Files
group_fqcd_jr/docs/16-Agent组员入门易懂版说明.md
T

426 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Agent 组员入门易懂版说明
> 📌 **接入请以 `docs/14-Agent组员统一接入说明书.md` 为准**(唯一推荐入口,含完整工具清单与权限口径)。
> 本文是同一套教学序列的**入门易懂版**(与 `11` / `15` 内容高度重合),保留用于新人快速上手;
> 两者冲突时以 `14` 为准。
> 适用对象:第一次接入本项目的客服、投顾、风控和运营开发人员,以及协助编码的 AI。
> 阅读目标:看完后,能够在不破坏底座的前提下写出一个业务 Agent。
## 1. 先用一句话理解这个项目
你们要做的是“业务 Agent”,底座已经负责公共工作。
可以把系统想成一个机场:
```text
Controller = 机场入口和检票口
AgentFactory = 登机调度台
BaseAgent = 统一登机流程
你的 Agent = 具体航班
ToolExecutor = 受控的机场服务柜台
Worker = 真正执行飞行任务的工作人员
```
你只需要设计“这个航班要去哪里、提供什么业务服务”,不需要重新建机场、重新做安检或自己买飞机。
## 2. 你真正需要写的代码
一个业务 Agent 通常只需要三部分:
1. 定义 Agent 的名字、角色和意图;
2. 写一个继承 `BaseAgent` 的类;
3. 在公共工厂注册一行代码。
最小结构如下:
```text
app/service/agent/implementations/your_agent.py # 业务 Agent
tests/unit/service/test_your_agent.py # 业务测试
app/service/agent/bootstrap.py # 注册一行
```
不要复制 `BaseAgent`,不要复制模型调用,不要复制工具权限代码。
## 3. 第一步:定义 Agent
```python
from app.core.contracts import AgentDefinition
class AdvisorAgent(BaseAgent):
definition = AgentDefinition(
agent_type="advisor",
version="1.0.0",
allowed_roles=("customer", "advisor", "operator", "admin"),
allowed_portals=("api",),
allowed_tools=("query_fund_quote",),
supported_intents=("fund_quote", "general"),
)
```
### 每一项是什么意思
| 字段 | 简单理解 | 示例 |
|---|---|---|
| `agent_type` | Agent 的唯一名字 | `advisor` |
| `version` | 业务 Agent 版本 | `1.0.0` |
| `allowed_roles` | 哪些角色可以使用 | `customer`、`advisor` |
| `allowed_portals` | 从哪里进入 | `api` |
| `allowed_tools` | 代码允许使用哪些工具 | `query_fund_quote` |
| `supported_intents` | 这个 Agent 能处理哪些问题类型 | `fund_quote`、`general` |
注意:`allowed_tools` 是最大权限。配置中心只能减少工具,不能增加工具。
## 4. 第二步:写 `handle()`
```python
from app.core.contracts import AgentRequest, CoreResult, RequestContext
from app.service.agent.base import BaseAgent
class AdvisorAgent(BaseAgent):
definition = AgentDefinition(
agent_type="advisor",
version="1.0.0",
allowed_roles=("customer", "advisor", "operator", "admin"),
allowed_portals=("api",),
allowed_tools=("query_fund_quote",),
supported_intents=("fund_quote", "general"),
)
async def handle(
self,
request: AgentRequest,
context: RequestContext,
) -> CoreResult:
return CoreResult(text=f"你刚才说的是:{request.message}")
```
### `request` 和 `context` 的区别
`request` 是用户说的话,例如:
```text
请查询基金 159511 的行情
```
`context` 是系统验证后的身份信息,例如:
```text
user_id、roles、permissions、trace_id、customer_ids
```
身份、客户范围和权限必须相信 `context`,不要相信用户自己在消息里写的客户编号。
## 5. 第三步:注册 Agent
在 `app/service/agent/bootstrap.py` 中添加:
```python
factory.register(
AdvisorAgent.definition,
lambda _context: AdvisorAgent(AdvisorAgent.definition),
)
```
这行代码的意思是:
```text
当系统收到 agent_type=advisor 时,
请使用 AdvisorAgent.definition 检查权限,
然后创建一个 AdvisorAgent 实例。
```
为什么必须在这里注册?因为 HTTP 服务和 Worker 都从这里拿 AgentFactory。如果你在别的地方注册,
可能出现“接口能找到,Worker 找不到”的问题。
## 6. 系统会自动做什么
你写完 `handle()` 后,系统会自动完成:
```text
检查请求
→ 检查 JWT、角色和权限
→ 读取配置
→ 读取客户记忆
→ 判断用户意图
→ 执行你的 handle()
→ 检查违规内容和敏感信息
→ 保存结果、审计和事件
```
所以你不需要在 `handle()` 里重复写:
- JWT 验证;
- 角色判断;
- 数据库 Session;
- 记忆读取;
- 模型密钥读取;
- 工具权限判断;
- 审计日志;
- SSE 推送。
## 7. 如何使用基金行情工具
底座已经提供:
```text
工具名:query_fund_quote
权限码:fund:quote:read
```
### 7.1 先在 AgentDefinition 声明
```python
allowed_tools=("query_fund_quote",)
supported_intents=("fund_quote", "general")
```
### 7.2 在 `handle()` 中调用
```python
async def handle(
self,
request: AgentRequest,
context: RequestContext,
) -> CoreResult:
quotes = await self.call_tool(
"query_fund_quote",
{
"fund_codes": ["159511"],
"limit": 20,
},
intent="fund_quote",
context=context,
)
return CoreResult(text=f"行情查询结果:{quotes}")
```
### 7.3 为什么必须用 `call_tool()`
因为 `call_tool()` 会自动检查:
```text
工具是否存在
→ 当前意图是否允许用它
→ 当前角色是否允许用它
→ 当前用户是否有权限
→ 参数是否正确
→ 是否超时
→ 是否写入审计
```
不要这样写:
```python
import httpx
response = httpx.get("https://api.fund.eastmoney.com/...")
```
也不要这样写:
```python
from hq import get_southern_fund_market
```
外部行情必须由底座统一处理。
### 7.4 如何理解行情结果
重点字段:
```text
nav 基金净值
nav_date 净值日期
daily_change 日涨幅
quote_source eastmoney / cache / degraded
is_intraday 是否盘中
degraded 是否降级
```
当 `degraded=true` 时,应该说“当前数据可能不是最新行情”,不能说“已经成交”或“保证收益”。
## 8. 如何使用适当性校验
投顾 Agent 不要自己写风险等级比较:
```python
if customer_level >= product_level:
allowed = True
```
必须使用公共工具:
```python
decision = await self.call_tool(
"check_suitability",
{
"customer_risk_level": 3,
"product_risk_level": 3,
"product_requires_disclosure": True,
},
intent="risk_check",
context=context,
)
```
返回结果主要看:
```text
allowed 是否允许
reason_code 原因
required_disclosure 是否需要风险揭示
requires_confirmation 是否需要二次确认
requires_recording 是否需要双录
```
风险不匹配或测评过期时,必须停止高风险流程并给出安全提示。
## 9. 如何调用模型
大多数 Agent 不需要自己调用模型,因为底座已经自动完成意图分类。
只有业务确实需要生成一段说明文字时,才使用:
```python
result = await self.generate_with_model(
approved_endpoints,
"请根据以下行情生成简短风险说明:...",
)
```
这里的 `approved_endpoints` 必须来自底座路由结果,不能自己写模型地址。
禁止:
- 自己读取 API Key;
- 自己创建 OpenAI 或其他供应商客户端;
- 把模型原始回答直接返回;
- 让模型决定是否下单或修改持仓。
## 10. 配置中心怎么配
工具白名单的配置格式:
```text
namespace: agent_tools
config_key: advisor:fund_quote
value_json: {"allowed_tools": ["query_fund_quote"]}
```
配置只能小于等于代码中的 `allowed_tools`。例如代码没有声明 `query_fund_quote`,仅靠数据库配置
不能让 Agent 获得它。
行情运行配置使用:
```text
namespace: fund_market
config_key: default
```
配置缺失或格式错误时,底座会使用安全默认值,不要在业务代码里直接读取配置表。
## 11. 常见错误和解决办法
### 错误一:`AGENT_TYPE_NOT_FOUND`
原因:没有在 `bootstrap.py` 注册 Agent。
解决:补充 `factory.register(...)`,再运行注册表契约测试。
### 错误二:`FORBIDDEN`
原因可能是:角色不在 `allowed_roles`、入口不在 `allowed_portals`、缺少权限或工具没有配置到当前意图。
解决:检查代码声明和已发布配置,不要在 Agent 中绕过检查。
### 错误三:`RECOVERABLE_ERROR`
常见原因:模型、行情、Redis 等外部依赖暂时不可用。让 Worker 按策略重试,不要把供应商异常原文展示给用户。
### 错误四:工具找不到
确认三件事:
```text
AgentDefinition.allowed_tools 是否声明
当前 supported_intents 是否包含调用意图
agent_tools/<agent_type>:<intent> 是否发布了工具白名单
```
### 错误五:引用校验失败
不要手写 `SourceReference`。只引用 `self.memories` 或本次 `call_tool()` 返回的数据。
## 12. 测试怎么写
最少要测试:
```text
正常请求
未授权角色
未授权入口
低置信意图
模型输出错误
工具参数错误
工具超时
行情降级
适当性不通过
禁止表达和敏感信息
```
提交前执行:
```powershell
python -m pytest -q -p no:cacheprovider
python -m ruff check app tests tools alembic
python -m mypy app
python tools/check_authoritative_docs.py
python tools/audit_schema.py
```
## 13. 哪些事情绝对不能做
- 不要覆盖 `BaseAgent.execute()`;
- 不要自己实现鉴权、意图分类、工具权限或审计;
- 不要直接访问 MySQL、Redis、Milvus、Neo4j;
- 不要直接调用东方财富接口;
- 不要代客下单、确认成交或修改持仓;
- 不要把场外运营数据写进场内交易表;
- 不要修改已有表名、字段名、类型和既有含义;
- 不要提交 `.env`、私钥、Token 或 API Key。
## 14. 开发完成后的交接模板
```text
Agent 名称:
支持意图:
新增工具:
修改文件:
配置项:
测试命令:
测试结果:
数据库是否变化:
是否涉及场外流程:
已知限制:
```
如果数据库有需求,必须额外说明:新增了哪些表/字段、如何证明没有修改旧表和旧字段,以及迁移回滚方式。
## 15. 最后记住
你的业务 Agent 越简单越好:
```text
声明能力
→ 接收 request/context
→ 调用公共工具
→ 组织业务结果
→ 返回 CoreResult
```
安全、权限、模型、记忆、审计、恢复和外部依赖由底座负责。不要为了“方便”绕过底座,否则代码无法通过
契约测试,也会给其他组员和生产运行带来不一致行为。