2026-09-09 21:55:37 +08:00
|
|
|
|
# 业务组员 Agent 接入使用说明书
|
|
|
|
|
|
|
|
|
|
|
|
> 版本:v1.0|适用对象:客服、投顾、风控及其他业务 Agent 开发人员
|
2026-09-11 14:37:20 +08:00
|
|
|
|
>
|
|
|
|
|
|
> 📌 **接入请以 `docs/14-Agent组员统一接入说明书.md` 为准**(它是唯一推荐入口)。
|
|
|
|
|
|
> 本文是同一套教学序列的**早期版本**,保留作历史参考;两者冲突时以 `14` 为准。
|
2026-09-09 21:55:37 +08:00
|
|
|
|
|
|
|
|
|
|
## 1. 先记住三条原则
|
|
|
|
|
|
|
|
|
|
|
|
1. 业务 Agent 是 MVC+S 中的 Service,只负责业务意图和业务编排。
|
|
|
|
|
|
2. Agent 必须继承 `BaseAgent`,只能由公共 `AgentFactory` 创建。
|
|
|
|
|
|
3. 业务代码不得自行创建模型客户端、读取密钥、创建数据库连接或绕过工具、记忆、合规、审计流程。
|
|
|
|
|
|
|
|
|
|
|
|
当前业务范围是场内基金模拟交易;场外运营必须使用独立表和独立接口,不得写入场内交易表。
|
|
|
|
|
|
|
2026-09-09 23:40:35 +08:00
|
|
|
|
## 1.1 当前可用的公共行情工具
|
|
|
|
|
|
|
|
|
|
|
|
底座已经注册公共只读工具 `query_fund_quote`,可供客服、投顾和风控 Agent 使用。它已经接入
|
|
|
|
|
|
`ToolRegistry → ToolExecutor → AgentFactory`,会统一执行权限、角色、意图白名单、参数校验、
|
|
|
|
|
|
超时、审计、来源引用和行情降级处理。
|
|
|
|
|
|
|
|
|
|
|
|
注意:工具已在底座注册,不代表所有业务 Agent 自动拥有它。业务 Agent 必须在代码和发布配置中
|
|
|
|
|
|
显式声明后才能调用;当前没有注册的业务 Agent 仍不能直接提交运行。
|
|
|
|
|
|
|
2026-09-09 21:55:37 +08:00
|
|
|
|
## 2. 组员需要提交什么
|
|
|
|
|
|
|
|
|
|
|
|
每个 Agent 接入至少提交以下内容:
|
|
|
|
|
|
|
|
|
|
|
|
- `AgentDefinition`:类型、版本、角色、入口和支持的意图;
|
|
|
|
|
|
- 一个继承 `BaseAgent` 的类,只实现 `handle()`;
|
|
|
|
|
|
- 业务输入/输出 DTO(如有);
|
|
|
|
|
|
- 业务只读工具声明和处理器(如有);
|
|
|
|
|
|
- 在公共注册入口登记的一行 `factory.register(...)`;
|
|
|
|
|
|
- 单元测试、权限测试、低置信意图测试和业务边界测试。
|
|
|
|
|
|
|
|
|
|
|
|
不得修改 `BaseAgent.execute()`、`classify_intent()`、`generate_with_model()`、`call_tool()`、
|
|
|
|
|
|
鉴权、记忆召回和合规审查逻辑。
|
|
|
|
|
|
|
|
|
|
|
|
## 3. 接入流程
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
定义 AgentDefinition
|
|
|
|
|
|
→ 实现 BaseAgent.handle()
|
|
|
|
|
|
→ 在公共 bootstrap 注册
|
|
|
|
|
|
→ 配置 Agent 意图和工具白名单
|
|
|
|
|
|
→ 编写契约/业务测试
|
|
|
|
|
|
→ 运行全量验收
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3.1 定义 Agent
|
|
|
|
|
|
|
|
|
|
|
|
`agent_type` 使用小写蛇形命名,例如 `customer_service`、`advisor`、`risk`。
|
|
|
|
|
|
`supported_intents` 必须列出所有允许的意图;数据库配置不能扩大这份代码声明的权限。
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
class AdvisorAgent(BaseAgent):
|
|
|
|
|
|
definition = AgentDefinition(
|
|
|
|
|
|
agent_type="advisor",
|
|
|
|
|
|
version="1.0.0",
|
|
|
|
|
|
allowed_roles=("customer", "advisor", "operator", "admin"),
|
|
|
|
|
|
allowed_portals=("api",),
|
|
|
|
|
|
allowed_tools=("check_suitability",),
|
|
|
|
|
|
supported_intents=("fund_explanation", "risk_check", "general"),
|
|
|
|
|
|
)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3.2 实现 `handle()`
|
|
|
|
|
|
|
|
|
|
|
|
业务类只实现 `handle(request, context)`,返回 `CoreResult`。执行前的鉴权、配置、记忆召回和意图
|
|
|
|
|
|
分类由底座完成;执行后的引用校验、敏感信息脱敏、禁止表达审查和审计由底座完成。
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
async def handle(self, request: AgentRequest, context: RequestContext) -> CoreResult:
|
|
|
|
|
|
if request.message.strip() == "":
|
|
|
|
|
|
raise ValidationAgentError("业务消息不能为空")
|
|
|
|
|
|
return CoreResult(text="业务处理结果")
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
不要在 `handle()` 中读取 `request` 之外的客户端身份字段。客户身份以服务端生成的 `context` 为准。
|
|
|
|
|
|
|
2026-09-09 23:40:35 +08:00
|
|
|
|
### 3.3 接入基金行情工具
|
|
|
|
|
|
|
|
|
|
|
|
如果业务需要查询基金行情,在 `AgentDefinition` 中声明:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
allowed_tools=("query_fund_quote",)
|
|
|
|
|
|
supported_intents=("fund_quote", "general")
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
然后在配置中心对应的已发布版本中允许:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
namespace: agent_tools
|
|
|
|
|
|
config_key: <agent_type>:fund_quote
|
|
|
|
|
|
value_json: {"allowed_tools": ["query_fund_quote"]}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
业务代码通过底座方法调用:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
quote = await self.call_tool(
|
|
|
|
|
|
"query_fund_quote",
|
|
|
|
|
|
{"fund_codes": ["159511"], "limit": 20},
|
|
|
|
|
|
intent="fund_quote",
|
|
|
|
|
|
context=context,
|
|
|
|
|
|
)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
返回结果中的关键字段:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
quote_source: eastmoney | cache | degraded
|
|
|
|
|
|
is_intraday: 是否盘中行情
|
|
|
|
|
|
degraded: 是否降级
|
|
|
|
|
|
nav_date: 净值对应日期
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`degraded=true` 时只能进行说明和分析,不能把结果表述为成交、委托、持仓或实时保证。
|
|
|
|
|
|
组员不得直接导入 `hq.py`、调用东方财富 URL、导入 `httpx` 或自行读取行情缓存。
|
|
|
|
|
|
|
2026-09-09 21:55:37 +08:00
|
|
|
|
## 4. 注册 Agent
|
|
|
|
|
|
|
|
|
|
|
|
在 `app/service/agent/bootstrap.py` 的统一注册位置登记。HTTP 服务和 Worker 会使用同一个工厂。
|
|
|
|
|
|
构造器必须返回定义完全一致的 `BaseAgent` 实例。
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
factory.register(
|
|
|
|
|
|
AdvisorAgent.definition,
|
|
|
|
|
|
lambda _context: AdvisorAgent(AdvisorAgent.definition),
|
|
|
|
|
|
)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
注册后必须通过 `tests/contract/test_agent_factory_contract.py`。未注册类型受理时返回
|
|
|
|
|
|
`404 AGENT_TYPE_NOT_FOUND`。
|
|
|
|
|
|
|
|
|
|
|
|
## 5. 底座自动执行的完整顺序
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
输入校验
|
|
|
|
|
|
→ JWT/RBAC/入口/客户范围校验
|
|
|
|
|
|
→ 读取发布配置
|
|
|
|
|
|
→ 召回授权记忆
|
|
|
|
|
|
→ IntentClassifier 自动分类
|
|
|
|
|
|
→ 调用 handle()
|
|
|
|
|
|
→ 模型/工具/适当性治理
|
|
|
|
|
|
→ 引用、禁止表达和敏感信息审查
|
|
|
|
|
|
→ complete_run 同事务持久化、审计和 Outbox
|
|
|
|
|
|
→ SSE/查询返回结果
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-09-09 23:40:35 +08:00
|
|
|
|
### 公共基金行情工具
|
|
|
|
|
|
|
|
|
|
|
|
底座已提供只读工具 `query_fund_quote`,权限码为 `fund:quote:read`,允许角色包括
|
|
|
|
|
|
`customer`、`advisor`、`operator`、`risk_operator` 和 `admin`。该工具内部负责外部行情请求、
|
|
|
|
|
|
盘中/收盘判断、短缓存和降级标记。组员只需在自己的 `AgentDefinition.allowed_tools` 和发布的
|
|
|
|
|
|
意图白名单中声明它,然后通过 `self.call_tool(...)` 调用,不得导入外部行情脚本。
|
|
|
|
|
|
行情白名单和缓存 TTL 可由已发布的 `fund_market/default` 配置调整;配置缺失或非法时底座自动使用
|
|
|
|
|
|
安全默认值,组员无需读取或解析配置中心。
|
|
|
|
|
|
|
2026-09-09 21:55:37 +08:00
|
|
|
|
### 意图分类
|
|
|
|
|
|
|
|
|
|
|
|
组员不需要创建分类器或传 `endpoints`。底座从当前激活的 `model_endpoint_config` 读取模型端点,
|
|
|
|
|
|
在 `handle()` 前自动分类,并把结果写入 `CoreResult.intent`。低于阈值时
|
|
|
|
|
|
`needs_clarification=true`,不得把低置信结果当成确定意图。
|
|
|
|
|
|
|
|
|
|
|
|
### 模型生成
|
|
|
|
|
|
|
|
|
|
|
|
模型调用只能通过底座注入的 `generate_with_model(...)`。模型路由、fallback、密钥解析和错误处理
|
|
|
|
|
|
由底座负责;组员不得导入 `httpx`、供应商 SDK 或直接读取环境变量密钥。
|
|
|
|
|
|
|
|
|
|
|
|
### 工具调用
|
|
|
|
|
|
|
|
|
|
|
|
工具必须是只读工具,并声明 Pydantic 输入模型、权限码、允许角色和超时。Agent 调用格式:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
value = await self.call_tool(
|
|
|
|
|
|
"check_suitability",
|
|
|
|
|
|
{"customer_risk_level": 3, "product_risk_level": 3},
|
|
|
|
|
|
intent="risk_check",
|
|
|
|
|
|
context=context,
|
|
|
|
|
|
)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
底座会检查意图白名单、角色、权限和参数,记录脱敏摘要、来源引用及 `interaction_audit`。
|
|
|
|
|
|
工具不得执行下单、修改持仓、审核方案、关闭预警或改变客户资料。
|
|
|
|
|
|
|
|
|
|
|
|
### 适当性校验
|
|
|
|
|
|
|
|
|
|
|
|
必须调用公共 `SuitabilityService` 或已注册的 `check_suitability` 工具,不得复制 C1-C5/R1-R5
|
|
|
|
|
|
规则。客户风险等级低于产品风险等级、测评过期时必须拒绝;通过和拒绝都会审计。
|
|
|
|
|
|
|
|
|
|
|
|
## 6. 配置和权限
|
|
|
|
|
|
|
|
|
|
|
|
业务 Agent 的 `allowed_tools` 是代码上限,发布配置只能缩小权限。意图配置使用
|
|
|
|
|
|
`agent_intent_config`,工具白名单使用发布版本绑定的 `platform_config_item`。
|
|
|
|
|
|
|
|
|
|
|
|
所有写操作需要 JWT、权限和 `Idempotency-Key`;配置更新还需要 `If-Match`。业务 Agent 不得自行
|
|
|
|
|
|
绕过统一鉴权或创建管理接口。
|
|
|
|
|
|
|
|
|
|
|
|
## 7. 禁止事项
|
|
|
|
|
|
|
|
|
|
|
|
- 禁止覆盖 `execute()`、`validate_access()`、`resolve_config()`、`recall_memory()`、
|
|
|
|
|
|
`classify_intent()`、`generate_with_model()`、`call_tool()`;
|
|
|
|
|
|
- 禁止直接访问 SQLAlchemy Session、MySQL、Redis、Milvus、Neo4j Driver;
|
|
|
|
|
|
- 禁止把模型 API Key 写入代码、数据库明文、日志或接口响应;
|
|
|
|
|
|
- 禁止伪造 `SourceReference`、引用未授权记忆或客户端传来的客户身份;
|
|
|
|
|
|
- 禁止 Agent 代客下单、确认成交、审核方案、处置/关闭风险预警;
|
|
|
|
|
|
- 禁止修改已有表名和已有字段;数据库需求只能新增表或字段,并先对照基线。
|
|
|
|
|
|
|
|
|
|
|
|
## 8. 测试和提交前检查
|
|
|
|
|
|
|
|
|
|
|
|
业务 Agent 至少覆盖:
|
|
|
|
|
|
|
|
|
|
|
|
- 正常意图、低置信意图、模型格式错误;
|
|
|
|
|
|
- 未授权角色、入口、权限和客户范围;
|
|
|
|
|
|
- 工具白名单、参数错误、超时和失败关闭;
|
|
|
|
|
|
- 适当性通过、风险不匹配和测评过期;
|
|
|
|
|
|
- 禁止表达、敏感信息脱敏、来源引用和最终持久化。
|
|
|
|
|
|
|
|
|
|
|
|
提交前运行:
|
|
|
|
|
|
|
|
|
|
|
|
```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
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
接口字段以《05-接口文档.md》为准,数据库以《00-新数据库基线设计.md》和《02-数据库建表设计.md》为准。
|