Files
group_fqcd_jr/docs/05-接口文档.md
T
张胜宇 f68b052a69 fix: 端点编号去重与 milvus-lite 降为可选依赖
按 qyqy 在 PR #7 评审中的要求处理两项:

- docs/05-接口文档.md §19:客服画像候选的两个端点由 A034/A035 改为 A039/A040。
  原编号与 qyqy 侧登录 / RBAC 只读接口(A034-A038)重复;docs 守卫脚本只校验
  文档文件名编号、不校验 §19 端点编号,因此该重复会静默遗留。改后全表 55 个
  端点编号唯一。
- pyproject.toml / requirements.txt:milvus-lite 由主 dependencies 挪到
  [project.optional-dependencies] dev;requirements.txt 只保留说明性注释,
  不再作为生效依赖。理由:它仅用于本地开发(Docker Milvus 未运行时的本地
  持久化向量库),进主依赖会让生产环境多背一个包。

验证:pytest tests/unit tests/contract -> 1275 passed, 2 skipped, 0 failed;
ruff check app tests tools alembic 通过;mypy app 通过(244 个源文件)。
2026-09-12 11:53:35 +08:00

1172 lines
53 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.
# MVC+S Agent 平台接口文档
> 版本:v1.0
> 状态:评审稿
> 修订日期:2026-09-09
> 适用对象:前端、后端、测试、运维、业务 Agent 开发人员和编码 Agent
> 数据库约束:允许新增表和字段,禁止修改已有表名及已有字段定义
## 1. 文档目的
本文是 Agent 平台 HTTP 接口的唯一权威规范,定义运行面、会话面、平台管理面、SSE 传输、JWT 鉴权、统一信封、错误映射、幂等、分页、审计及业务扩展规则。
内部 Python 类型、Service Protocol、领域事件字段和数据库 DDL 不在本文重复定义。本文通过权威源矩阵引用已有设计,避免同一契约出现两个定义。
当前业务能力只针对场内基金模拟交易。场外基金运营使用独立业务表和接口,不得写入场内模拟交易表。
## 2. 权威源与边界
### 2.1 权威源矩阵
| 契约项 | 契约编号 | 唯一权威源 | 本文职责 |
|---|---|---|---|
| Agent 请求与上下文 | `AGENT-REQ-001` | 01 §5.2 | 定义 HTTP 投影和映射规则 |
| Agent 结果 | `AGENT-RESULT-001` | 01 §5.3 | 定义对外字段和序列化 |
| Service Protocol | `AGENT-SERVICE-001` | 01 §5.4 | 仅登记依赖方向 |
| Agent 错误体系 | `AGENT-ERROR-001` | 01 §5.5 | 定义 HTTP 状态码映射 |
| 领域事件 | `AGENT-EVENT-001` | 01 §5.4、02 §8.2 | 定义触发接口和触发时机 |
| SSE 事件载荷 | `AGENT-SSE-001` | 01 §12 | 定义 HTTP 传输方式 |
| Agent 执行顺序 | `AGENT-FLOW-001` | 01 §6、03 §3-§5 | 保证接口不绕过执行顺序 |
| 数据库实体 | `DB-BASE-001` | 00、02 | 仅定义资源与实体映射 |
| 请求幂等 | `DB-IDEMPOTENCY-001` | 02 §8.3 | 定义 HTTP 幂等语义 |
| Agent 运行 | `DB-RUN-001` | 02 中的 `agent_run` | 定义运行资源语义 |
| 领域状态机 | `DOMAIN-FLOW-001` | 对应业务流程文档 | 仅登记入口和 Agent 边界 |
### 2.2 冲突裁决
1. 本文的认证、路径、Header、HTTP 信封、分页、错误码映射和版本规则优先于业务接口文档。
2. 业务字段、业务状态机和业务校验以对应业务文档为准,本文不得复制后形成第二权威源。
3. 内部 DTO、Service Protocol、事件字段和数据库字段以矩阵指定文档为准。
4. 同一契约出现两处定义时,非权威定义必须删除并改为引用,不允许两份定义同时进入实现。
5. OpenAPI 是本文 HTTP 契约的机器可读投影。OpenAPI 与本文冲突时先停止发布并修正文档或生成逻辑。
### 2.3 MVC+S 依赖边界
```text
Controller -> Application Service -> Domain Service/Repository -> Model
|
`-> View
AgentRunService -> RunRepository/IdempotencyService/RunDispatchPort
Run Worker -> AgentExecutor -> AgentFactory -> BaseAgent
Run Query Service -> RunRepository/ConversationRepository -> JSON/SSE View
```
- Controller 不调用 Agent、ORM、Redis、Milvus、Neo4j 或消息中间件。
- Worker 不依赖 HTTP、Controller 或 SSE View。
- BaseAgent 返回传输无关的执行结果,不生成 HTTP 数据帧。
- JSON 查询和 SSE 订阅使用同一个 `RunQueryService` 权威投影。
- HTTP DTO 与内部 Agent DTO 分离,通过显式 Mapper 转换。
- Redis 只承担缓存和实时通知;MySQL 保存权威运行状态和最终结果。
## 3. 通用 HTTP 约定
### 3.1 基础地址与命名
- 业务接口前缀:`/api/v1`。
- 平台管理接口前缀:`/api/v1/admin`。
- 内部运维接口前缀:`/internal`。
- HTTP 路径使用 kebab-case,资源名称使用复数。
- `agent_type` 使用 snake_case,格式为 `[a-z][a-z0-9_]{1,31}`。
- 请求和响应编码统一为 UTF-8。
- JSON 请求使用 `Content-Type: application/json`。
### 3.2 通用请求 Header
| Header | 是否必填 | 规则 |
|---|---|---|
| `Authorization` | 是 | `Bearer <JWT>`,运维接口除外 |
| `Idempotency-Key` | 写接口按本文要求 | 8-64 个可打印 ASCII 字符 |
| `Content-Type` | 有请求体时是 | `application/json` |
| `Accept` | 否 | 默认 `application/json`;SSE 为 `text/event-stream` |
| `If-Match` | 管理面更新和状态转换是 | 使用服务端上次返回的 ETag |
| `traceparent` | 否 | 符合 W3C Trace Context;非法值由服务端替换 |
所有响应返回 `X-Trace-ID`。客户端不能通过请求正文或 `metadata` 指定用户、角色、客户范围、模型、工具、合规策略或追踪标识。
### 3.3 成功信封
单资源:
```json
{
"data": {},
"meta": {
"trace_id": "trace-uuid"
}
}
```
列表资源:
```json
{
"data": [],
"meta": {
"trace_id": "trace-uuid",
"next_cursor": "opaque-cursor",
"has_more": true
}
}
```
业务接口不得增加其他顶层字段。SSE、文件下载和运维健康检查不使用业务 JSON 信封。
### 3.4 错误信封
```json
{
"error": {
"code": "IDEMPOTENCY_CONFLICT",
"message": "幂等键已用于不同请求",
"retryable": false,
"field_errors": []
},
"meta": {
"trace_id": "trace-uuid"
}
}
```
`field_errors` 元素格式为:
```json
{
"field": "metadata.ui_entry",
"reason": "格式不正确"
}
```
错误响应禁止包含异常堆栈、SQL、模型原文、供应商响应、内部网络地址和密钥引用。
### 3.5 HTTP 状态码
| 状态码 | 使用场景 |
|---|---|
| `200` | 查询、幂等更新或同步操作成功 |
| `201` | 同步创建资源成功 |
| `202` | 异步任务或取消请求已受理 |
| `204` | 操作成功且无响应体 |
| `304` | 条件查询内容未变化 |
| `400` | JSON、Header 或查询参数无法解析 |
| `401` | JWT 缺失、无效、过期或已吊销 |
| `403` | 角色、权限、适当性或数据范围拒绝 |
| `404` | 资源不存在,或为防止越权枚举而隐藏资源 |
| `409` | 幂等冲突、版本冲突或非法状态转换 |
| `413` | 请求体或上传文件超过大小限制 |
| `422` | 已解析请求不满足字段或业务输入约束 |
| `429` | 频率、并发或配额限制 |
| `500` | 未分类内部错误 |
| `502` | 上游模型或工具返回无效响应 |
| `503` | 必需依赖不可用 |
| `504` | 上游调用超时且无法降级 |
### 3.6 核心错误码
| 错误码 | HTTP | 是否可重试 | 说明 |
|---|---:|---|---|
| `AGENT_INPUT_INVALID` | 422 | 否 | 请求内容不满足 Agent 输入约束 |
| `AGENT_PERMISSION_DENIED` | 403 | 否 | 角色、入口或数据范围不允许 |
| `AUTHENTICATION_REQUIRED` | 401 | 否 | Token 缺失或无效 |
| `SESSION_NOT_FOUND` | 404 | 否 | 会话不存在或已被资源隐藏 |
| `SESSION_NOT_ACCESSIBLE` | 404 | 否 | 当前身份不能访问会话 |
| `AGENT_TYPE_NOT_FOUND` | 404 | 否 | Agent 未注册 |
| `IDEMPOTENCY_CONFLICT` | 409 | 否 | 同一幂等键对应不同正文 |
| `RESOURCE_VERSION_CONFLICT` | 409 | 是 | `If-Match` 版本过期 |
| `RUN_NOT_FOUND` | 404 | 否 | 运行不存在或不可见 |
| `RUN_NOT_CANCELLABLE` | 409 | 否 | 运行已进入不可取消阶段 |
| `RUN_CANCELLED` | 409 | 否 | 运行已取消 |
| `INVALID_CURSOR` | 400 | 否 | 游标非法、过期或与过滤条件不符 |
| `RATE_LIMITED` | 429 | 是 | 频率、并发或配额限制 |
| `DEPENDENCY_UNAVAILABLE` | 503 | 是 | 必需依赖不可用 |
| `UPSTREAM_TIMEOUT` | 504 | 是 | 上游超过时间预算 |
| `AGENT_INTERNAL_ERROR` | 500 | 视情况 | 未分类内部错误 |
### 3.7 数据格式
- 时间统一使用 UTC 的 RFC 3339,例如 `2026-09-09T08:30:00.000000Z`。
- 金额、价格、数量和置信度使用十进制字符串,禁止使用 JSON 浮点数。
- 数据库 `BIGINT` 对外序列化为字符串,避免前端整数精度丢失。
- 布尔值使用 `true/false`,不得使用 `0/1`。
- 空集合返回 `[]`,空对象返回 `{}`,无值返回 `null`。
- 未特别说明的文本字段去除首尾空白,禁止不可见控制字符。
### 3.8 游标分页
列表接口统一使用 `cursor` 和 `limit`。`limit` 默认 20、最小 1、最大 100。游标是不透明字符串,绑定用户、查询条件、排序字段和方向,客户端不得解析或修改。
默认排序为 `created_at DESC, id DESC`。默认不返回总条数;确需精确统计时使用对应领域的独立统计接口。
### 3.9 接口版本
SSE 实现补充:运行未完成时建立的连接,在观察到最终事务提交后分块输出 delta;
已完成运行的新连接使用 replace 返回完整结果。两者都是结果级恢复,不提供事件游标续传。
块长度通过 .env 的 SSE_CHUNK_CHARACTERS 控制,按 Unicode 字符切分,空结果也输出一个 delta。
- 兼容变更:新增可选字段、新增接口、新增可识别的错误码。
- 破坏性变更:删除或重命名字段、改变字段类型或含义、把可选字段改为必填、改变既有状态语义。
- 破坏性变更必须升级主路径,例如 `/api/v2`。
- 客户端必须对未知枚举值提供兜底行为。
- 废弃接口返回 `Deprecation`、`Sunset` 和替代接口链接。
- 废弃期不得少于两个发布周期且不得少于 90 天。
## 4. JWT 鉴权与授权
### 4.1 Token 要求
```http
Authorization: Bearer <access-token>
```
JWT 至少包含 `sub`、`jti`、`iss`、`aud`、`iat`、`nbf` 和 `exp`。服务端必须验证签名算法白名单、签发方、受众、时间窗口和吊销状态。
JWT 只证明身份和基础授权范围。服务端每次请求重新加载有效用户状态、角色、权限、客户归属和数据范围;不得完全信任 Token 中的历史角色信息。
### 4.2 权限处理
实现补充(2026-09-09):统一 REST 入口在服务端设置 `portal=api`;Agent 声明须显式允许
此入口,metadata、Header 和 JWT 中的 portal/roles 均不作为授权依据。用户状态须为基线的
`正常`,角色状态为 `active`,角色分配在有效期内。每次请求直读 RBAC,撤权立即生效;
暂不缓存授权数据。运行同时要求 `agent:run` 权限及 AgentDefinition 的角色、入口交集。
权限范围按 permission_code 分别保存,禁止把另一权限的 `all` 扩散到全部资源。
管理面需 admin/super_admin 角色及第 19 节对应操作权限;拒绝追加审计。
- 客户只能访问自己的运行、会话、消息、反馈、转人工请求和安全记忆投影。
- 客服只能访问进入客服队列或分配给自己的会话和工单。
- 投顾只能访问 `sys_customer_assignment` 中归属自己的客户。
- 风控人员只能访问其角色和数据范围允许的风险记录。
- 普通管理员默认无权查看未脱敏会话正文和敏感审计详情。
- 越权资源统一返回 `404`,避免泄露资源是否存在;明确功能无权限时返回 `403`。
### 4.3 SSE 鉴权
SSE 订阅必须携带 Bearer Token。浏览器客户端使用支持自定义 Header 的 `fetch` 流式实现或合规 SSE 客户端,不使用无法设置 `Authorization` Header 的原生 `EventSource`。
连接建立时验证 JWT 和资源权限。Token 在连接期间过期不强制截断已经通过鉴权的短连接,但重连必须重新鉴权。
## 5. 幂等与并发
### 5.1 幂等范围
必须携带 `Idempotency-Key` 的接口包括:
- 创建 Agent 运行。
- 取消 Agent 运行。
- 创建或关闭会话。
- 创建转人工请求。
- 业务写接口。
- 配置审核、激活、回滚、停用和归档。
通用幂等范围为 `user_id + HTTP method + normalized_path + idempotency_key`。Agent 运行同时校验 `agent_type`。请求哈希使用规范化后的路径、查询参数和 JSON 正文计算,不包含 Authorization、trace Header 和传输时间。
### 5.2 重复请求
- 同一范围、同一键、同一哈希:返回原资源和当前状态。
- 同一范围、同一键、不同哈希:返回 `409 IDEMPOTENCY_CONFLICT`。
- 原异步任务仍在执行:返回原 `run_id`,不得创建第二个任务。
- Worker 租约超时:接管原 `run_id`,不得生成新的运行记录。
- 已完成请求:返回原结果资源地址。
### 5.3 乐观并发
管理面草稿更新和状态转换必须携带 `If-Match`。ETag 是服务端根据资源版本生成的不透明值。版本不一致返回 `409 RESOURCE_VERSION_CONFLICT`。
已审核、已激活、已停用或已归档的不可变版本不得原地编辑。
## 6. Agent 运行接口
### 6.1 运行状态
```text
queued -> running -> succeeded
-> failed
queued/running -> cancel_requested -> cancelled
```
- `queued`:受理事务已提交,等待 Worker。
- `running`:Worker 持有有效租约并正在执行。
- `cancel_requested`:已收到取消请求,等待 Worker 到达安全停止点。
- `succeeded`:最终消息、审计、幂等完成状态和 Outbox 已原子提交。
- `failed`:不可恢复错误及审计已经提交。
- `cancelled`:在最终事务开始前成功取消。
### 6.2 创建运行
```http
POST /api/v1/agent-runs
Authorization: Bearer <token>
Idempotency-Key: <key>
Content-Type: application/json
```
请求:
```json
{
"agent_type": "customer_service",
"session_id": "session-uuid",
"message": "场内基金卖出后资金什么时候可用?",
"end_session": false,
"metadata": {
"locale": "zh-CN",
"client_version": "1.0.0",
"ui_entry": "customer_chat"
}
}
```
`message` 长度 1-8000,不能只包含空白。`metadata` 只允许 `locale`、`client_version` 和 `ui_entry`,当前 `locale` 只允许 `zh-CN`。会话必须属于当前用户或在员工数据范围内,Agent 必须已注册且允许当前角色和入口。
返回 `202 Accepted`:
```json
{
"data": {
"run_id": "run-uuid",
"trace_id": "trace-uuid",
"status": "queued",
"status_url": "/api/v1/agent-runs/run-uuid",
"events_url": "/api/v1/agent-runs/run-uuid/events",
"created_at": "2026-09-09T08:30:00.000000Z"
},
"meta": {
"trace_id": "trace-uuid"
}
}
```
受理事务必须原子保存用户消息、`request_idempotency`、`agent_run` 和 `agent.run_requested` Outbox。事务失败时不得返回 `run_id`。
主要错误:`AGENT_INPUT_INVALID`、`AGENT_PERMISSION_DENIED`、`SESSION_NOT_FOUND`、`SESSION_NOT_ACCESSIBLE`、`AGENT_TYPE_NOT_FOUND`、`IDEMPOTENCY_CONFLICT`、`RATE_LIMITED`。
### 6.3 查询运行
```http
GET /api/v1/agent-runs/{run_id}
Authorization: Bearer <token>
```
返回 `200 OK`:
```json
{
"data": {
"run_id": "run-uuid",
"trace_id": "trace-uuid",
"session_id": "session-uuid",
"agent_type": "customer_service",
"status": "succeeded",
"result_version": 1,
"result": {
"message_id": "12345",
"reply": "最终安全回复",
"intent": "faq",
"confidence": "0.9400",
"source_references": [],
"suggestions": [],
"transfer_required": false,
"transfer_reason": null,
"degraded": false,
"degradation_reason": null
},
"error": null,
"created_at": "2026-09-09T08:30:00.000000Z",
"started_at": "2026-09-09T08:30:01.000000Z",
"completed_at": "2026-09-09T08:30:04.000000Z"
},
"meta": {
"trace_id": "trace-uuid"
}
}
```
- 未完成时 `result` 和 `error` 均为 `null`。
- 成功时 `result` 来自已持久化助手消息,不能从 Worker 内存拼装。
- 失败时 `result` 为 `null`,`error` 只包含安全错误码、消息和可重试标志。
- 取消时不创建助手成功消息。
- `result_version` 用于客户端重连去重,MVP 成功结果固定为 1。
主要错误:`RUN_NOT_FOUND`、`AGENT_PERMISSION_DENIED`。
### 6.4 订阅运行结果
```http
GET /api/v1/agent-runs/{run_id}/events
Authorization: Bearer <token>
Accept: text/event-stream
```
成功响应 Header:
```http
Content-Type: text/event-stream; charset=utf-8
Cache-Control: no-cache, no-transform
X-Accel-Buffering: no
X-Trace-ID: trace-uuid
```
传输顺序:
```text
运行未完成:start -> 注释心跳 -> 等待
正常完成:start -> tools(可选) -> delta(一个或多个) -> done -> 关闭
安全替换:start -> replace -> done -> 关闭
不可恢复:start -> error -> 关闭
```
帧示例:
```text
event: start
data: {"trace_id":"trace-uuid","session_id":"session-uuid"}
: keepalive
event: delta
data: {"trace_id":"trace-uuid","text":"安全文本片段"}
event: done
data: {"trace_id":"trace-uuid","intent":"faq","confidence":"0.9400","sources":[],"suggestions":[]}
```
事件名称和业务载荷以 `AGENT-SSE-001` 为准。注释心跳不是业务事件。`tools`、`delta`、`replace` 和 `done` 只能在最终持久化事务成功后发送。
MVP 采用结果级恢复,不实现事件级游标续传。`Last-Event-ID` 可以被忽略。重连时服务端根据 `agent_run` 和 `conversation_message` 重新发送完整最终结果,客户端按 `run_id + result_version` 去重。
若错误发生在响应 Header 发送前,返回统一 JSON 错误;发送后使用 `error` 事件并关闭连接。`done` 和 `error` 都是终止事件,之后不得继续发送数据。
主要错误:`RUN_NOT_FOUND`、`AGENT_PERMISSION_DENIED`、`SSE_NOT_ACCEPTABLE`。
### 6.5 取消运行
```http
POST /api/v1/agent-runs/{run_id}/cancellations
Authorization: Bearer <token>
Idempotency-Key: <key>
```
请求:
```json
{
"reason": "user_cancelled"
}
```
返回 `202 Accepted`:
```json
{
"data": {
"run_id": "run-uuid",
"status": "cancel_requested",
"cancel_requested_at": "2026-09-09T08:30:02.000000Z"
},
"meta": {
"trace_id": "trace-uuid"
}
}
```
重复取消返回同一状态。运行已成功、失败或进入最终提交事务时返回 `409 RUN_NOT_CANCELLABLE`。取消成功后,`request_idempotency` 使用 `failed + RUN_CANCELLED` 表示原请求已终止,不扩展其既有状态枚举。
## 7. 会话、消息、反馈与转人工接口
### 7.1 创建会话
```http
POST /api/v1/conversations
Authorization: Bearer <token>
Idempotency-Key: <key>
Content-Type: application/json
```
请求:
```json
{
"agent_type": "customer_service"
}
```
返回 `201 Created`:
```json
{
"data": {
"session_id": "session-uuid",
"agent_type": "customer_service",
"portal": "customer_chat",
"status": "active",
"clarification_round": 0,
"created_at": "2026-09-09T08:20:00.000000Z"
},
"meta": {"trace_id": "trace-uuid"}
}
```
服务端根据请求入口确定 `portal`,通过 AgentFactory 校验 Agent、角色和入口。客户端不得提交 `user_id`、`portal` 或澄清轮次。
### 7.2 查询会话
```http
GET /api/v1/conversations/{session_id}
Authorization: Bearer <token>
```
返回会话的 `session_id`、`agent_type`、`portal`、`status`、`clarification_round`、`message_count`、`last_active_at`、`started_at` 和 `ended_at`。权限不满足时按资源隐藏规则返回 `404`。
### 7.3 查询会话消息
```http
GET /api/v1/conversations/{session_id}/messages?limit=20&cursor=<cursor>
Authorization: Bearer <token>
```
返回 `conversation_message` 的对客投影:`message_id`、`role`、`content`、`intent`、`confidence`、`source_references`、`created_at` 和脱敏后的 `tool_calls`。不返回内部 Prompt、模型原文、权限判断详情、未脱敏工具参数或其他客户数据。
用户消息的 `intent`、`confidence` 和 `source_references` 按现有流程可以为空;助手消息必须来自已经完成合规和持久化的结果。
### 7.4 结束会话
```http
POST /api/v1/conversations/{session_id}/closures
Authorization: Bearer <token>
Idempotency-Key: <key>
```
请求体可为空。服务端条件更新会话状态为 `ended`,触发记忆提取事件的判定由公共底座完成。已结束会话重复调用返回当前会话状态;已转人工会话不得通过此接口绕过客服状态机。
### 7.5 创建用户反馈
```http
POST /api/v1/conversation-messages/{message_id}/feedback
Authorization: Bearer <token>
Idempotency-Key: <key>
Content-Type: application/json
```
请求:
```json
{
"rating": -1,
"feedback_type": "inaccurate",
"feedback_content": "回答没有说明到账条件"
}
```
`rating` 只能为 `1` 或 `-1`,正文最多 1000 字符。消息必须属于当前用户;同一用户对同一消息只能有一条有效反馈,重复提交相同内容返回原反馈,内容不同返回 `409 FEEDBACK_ALREADY_EXISTS`。
### 7.6 用户申请转人工
```http
POST /api/v1/conversations/{session_id}/handover-requests
Authorization: Bearer <token>
Idempotency-Key: <key>
Content-Type: application/json
```
请求:
```json
{
"reason_code": "user_requested",
"reason_detail": "希望人工解释"
}
```
客户端只能提交 `reason_code=user_requested` 和有限长度的补充说明,不能提交 `priority`、`assigned_to`、置信度、会话摘要或来源引用。返回 `202 Accepted`:
```json
{
"data": {
"handover_id": "ticket-uuid",
"session_id": "session-uuid",
"status": "pending",
"created_at": "2026-09-09T08:35:00.000000Z"
},
"meta": {"trace_id": "trace-uuid"}
}
```
该接口只负责创建通用转人工请求。客服工单分配、接单、解决和关闭由客服领域 Service 处理,不允许 Controller 直接写 `svc_handover_ticket`。
### 7.7 查询转人工请求
```http
GET /api/v1/handover-requests/{handover_id}
Authorization: Bearer <token>
```
客户只能查看自己的 `status`、创建时间和安全提示;客服坐席可按权限查看队列和分配信息。工单状态 `pending -> assigned -> processing -> resolved -> closed`,取消规则和字段以 02 §7.2 及客服业务文档为准。
## 8. 记忆与知识引用接口
### 8.1 查询记忆画像
```http
GET /api/v1/users/me/memory-profile
GET /api/v1/customers/{customer_id}/memory-profile
Authorization: Bearer <token>
```
客户只能查询自己的画像;投顾、客服或授权员工必须通过客户归属和数据范围校验。返回经过字段策略过滤的当前画像、有效期、来源摘要和版本,不返回模型原始推理、Prompt、其他客户信息或未解决冲突的内部详情。
记忆提取没有客户端写接口。`memory.extraction_requested` 由 `complete_run()` 与最终结果在同一事务写入 Outbox,再由 Worker 调用内部 `MemoryService`。更正、遗忘和监管删除属于独立隐私流程,本接口不临时复用 `memory_conflict`。
### 8.2 解析知识引用
```http
GET /api/v1/knowledge-references/{reference_token}
Authorization: Bearer <token>
```
`reference_token` 为服务端签发的短期不透明令牌,绑定用户、知识版本和过期时间。返回允许展示的标题、版本、生效日期、来源机构和摘要;客户端不能通过修改令牌枚举 `doc_id` 或读取草稿知识。
知识上传、审核、发布、失效和 Milvus 同步由知识业务文档定义。Agent 只能使用工具返回的 `SourceReference`,不得自行构造引用。
### 8.3 知识库文档管理
```http
POST /api/v1/knowledge/upload
GET /api/v1/knowledge/list
DELETE /api/v1/knowledge/{knowledge_id}
Authorization: Bearer <token>
```
需求来源:老师《需求文档-修改版》Phase 1 验收第 7 条「知识库管理接口可正常上传/查询/删除文档」与 F1.2。与 §8.2 的 `/knowledge-references/{reference_token}`(引用解析,K001)是**两个不同的资源面**:本节是**管理面文档生命周期**,凭 `knowledge:manage` 操作,不解析令牌、不按客户归属过滤;K001 是**读侧引用解析**,凭 `knowledge:reference:read`,返回可用展示的引用片段。两者不复用同一 prefix 语义。
三个端点均需 `knowledge:manage`(现只授给管理员角色)。`created_by` 取自认证上下文,**调用方不能指定**。三个端点共用 router 级限流;底座没有匿名路径,认证先于限流。
**上传**
```json
{
"filename": "理财产品销售管理办法.md",
"content_base64": "<文档字节的标准 base64>",
"knowledge_type": "policy"
}
```
- `filename` 只能是文件名本身(不得含路径与分隔符)、长度 ≤ 256。
- `knowledge_type` 取 `faq` / `product` / `policy` 之一,**由服务端映射集合**,调用方不得指定集合名;非法取值返回 `422`。
- `content_base64` 严格解码;非法 base64 返回 `422 AGENT_INPUT_INVALID`。
- 老师原文写 `multipart/form-data`,**一期实现为 JSON**(理由见下)。
成功 `201` 返回本次产生的知识行(一份文档切多块即为多行):
```json
{
"knowledge_ids": [109, 110],
"filename": "理财产品销售管理办法.md",
"knowledge_type": "policy",
"created_by": "9003",
"chunk_count": 2
}
```
**查询列表**
```http
GET /api/v1/knowledge/list?limit=20&offset=0&knowledge_type=policy
```
`limit` 默认 20、上限 100;`offset` ≥ 0;`knowledge_type` 可选。默认只列**未过期**行。响应只给 `content_preview`(前 200 字符)与 `content_length`,**不返回整篇 `content_text`** —— 正文是检索侧素材,管理面列表不得变成"直读知识正文"的旁路:
```json
{
"items": [
{
"knowledge_id": 109,
"knowledge_type": "policy",
"title": "### 第一条 目的…",
"source_file": "理财产品销售管理办法.md",
"collection": "fin_policy_collection",
"version": "v1",
"status": "active",
"review_status": "published",
"created_by": 9003,
"tags": {"chunk_index": 2, "heading_path": ["理财产品销售管理办法"]},
"content_preview": "### 第一条 目的…",
"content_length": 457
}
],
"count": 1
}
```
**删除**
```http
DELETE /api/v1/knowledge/{knowledge_id}
```
删除**不改物理行**,而是标记 `fin_knowledge_meta.status='expired'` 并**在同一事务**投递
`knowledge.vector_delete_requested`(`aggregate_id` 与 payload 均为 `str(knowledge_id)`),
由知识向量 Worker 消费后删除 Milvus 向量;本地文件随后尽力归档,**归档失败不回滚已提交的删除**(只记日志)。
成功 `200`:
```json
{
"knowledge_id": 109,
"status": "expired",
"vector_delete_event": "knowledge.vector_delete_requested"
}
```
不存在的 `knowledge_id` 返回 `404`,**不静默成功**。
**两处口径说明(避免误读)**
1. **请求体形状**:老师原文为 `multipart/form-data`,一期用 JSON + `content_base64`。
理由是底座其余写接口(admin 配置面、conversations、agent-runs)**全是 JSON**,错误信封(§3.4)
与 `ValidationAgentError` 的 422 口径都建立在 JSON body 上;一期单独引 multipart 等于新开一条
无测试覆盖的上传失败路径。后续接前端表单时新增一个 multipart 端点复用同一 Service 即可
(Service 只吃 `filename` + `content: bytes`,与传输形状无关)。
2. **删除的 404 错误码**:现实现复用 `SESSION_NOT_FOUND`(底座通用资源不存在码),语义上偏会话面;
若后续要求精确语义,应新增 `KNOWLEDGE_NOT_FOUND`。**本文按现状登记**,不做美化。
### 8.4 公共只读工具索引(Agent 面)
工具**不是 HTTP 接口**,但它们决定 Agent 能读什么,且必须由 `AgentFactory` 注入的
`ToolExecutor` 统一执行(鉴权、数据范围、审计不得绕过)。登记口径:
工具名 / 必需权限 / 只读 / 越权行为。
| 工具名 | 必需权限 | 只读 | 越权与失败行为 |
|---|---|---|---|
| `check_suitability` | `suitability:read` | 是 | 除管理员外不得查他人风险测评;测评过期/缺失一律拒绝(失败关闭) |
| `query_fund_quote` | `fund:quote:read` | 是 | 只读行情,不得改写为成交、委托或持仓语义 |
| `query_knowledge` | `knowledge:query` | 是 | 集合名由服务端按意图映射,调用方不得指定;维度不符失败关闭 |
| `query_customer_profile` | `memory:read:self`(查他人为 `memory:read:customer`) | 是 | 越范围按"不存在"处理且不泄露存在性;无当前画像**抛错**而不返回空画像 |
**工具的白名单是两段式**:代码里的 `allowed_tools` 是**上限**,实际可用范围还要与
当前 `active` 的 `config_release` 中 `namespace=agent_tools`、`config_key=<agent_type>:<intent>`
发布的工具白名单**取交集**;**缺发布配置时交集为空、工具失败关闭**。
`query_customer_profile` 的返回值遵循 §8.1 的字段策略:只投影画像属性白名单
(`investor_type` / `investment_horizon` / `trading_frequency` / `preferred_asset_class` /
`risk_tags` / `customer_tier` / `behavior_score` / `total_asset` /
`assessment_valid_until` / `assessment_expired`),
**不返回 `real_name`、`birth_date`、`mobile_masked`、`trade_account` 等 PII**;
`assessment_expired` 按**当前时间**重算,不采信快照里的历史布尔值。
### 8.5 客服画像候选(Phase 2)
> 编号说明:本节为客服二期新增,**不占用 §8.1–§8.4 既有号段**,以避免破坏 `AGENTS.md`、`docs/09`、`docs/14` 对「§8.3 知识库管理三端点」「§8.4 公共只读工具索引」的既有引用。
客服 Agent 不读取或直接修改正式画像。已登录用户明确陈述长期偏好、约束或目标时,系统
异步生成 `memory_unit.status='candidate'` 候选;访客不会生成候选。候选不进入客服召回,
必须经过用户确认和管理员审核后才能晋升为 `active`。
```text
GET /api/v1/users/me/memory-candidates
POST /api/v1/users/me/memory-candidates/{candidate_id}/decisions
GET /api/v1/admin/customer-profile-candidates
POST /api/v1/admin/customer-profile-candidates/{candidate_id}/reviews
```
用户确认请求体为 `{ "decision": "confirmed" | "rejected" }`,需要
`memory:candidate:confirm`;确认只将状态改为 `verified`。管理员审核请求体复用
`ReviewPayload`,需要管理员角色和 `memory:candidate:review`;`approved` 会在事务内
处理同键旧记忆冲突并将候选改为 `active`,`rejected` 将其改为 `rejected`。接口只返回
结构化候选值,不返回对话证据摘录、密码、验证码或其他原始敏感内容。
## 9. 平台管理面接口
管理面只操作草稿、审核、激活、停用、回滚和归档流程,不提供绕过版本控制的通用 CRUD。所有更新和状态转换都需要 `If-Match`;创建、审核、激活、回滚和停用需要 `Idempotency-Key`。
### 9.1 配置发布
```text
POST /api/v1/admin/config-releases
GET /api/v1/admin/config-releases
GET /api/v1/admin/config-releases/{release_id}
POST /api/v1/admin/config-releases/{release_id}/validations
POST /api/v1/admin/config-releases/{release_id}/reviews
POST /api/v1/admin/config-releases/{release_id}/activations
POST /api/v1/admin/config-releases/{release_id}/rollbacks
```
创建草稿请求至少包含 `release_no`、`title`、`change_summary`。验证接口执行 Schema、权限上限、引用完整性、路由备用端点、Prompt、模板和意图配置校验;未通过验证不得审核或激活。
审核请求:
```json
{
"decision": "approved",
"comment": "已完成审核"
}
```
创建人可以审核自己创建的版本(允许自审,不要求审核人不同于创建人);审核这一状态机节点不可跳过,未提交审核的草稿不能直接审核或激活。自审时 `reviewer_id` 留空——库约束 `chk_config_release_separation`(`reviewer_id IS NULL OR reviewer_id <> created_by`)属于数据库基线,不能把创建人写进 `reviewer_id`;自审人身份由 `interaction_audit` 的审核记录承担。他人复核仍照常写入 `reviewer_id`。激活必须是审核通过的完整批次,且原子切换当前生效版本。回滚不修改历史记录,而是创建新的发布记录并填写 `rollback_of_release_id`。
### 9.2 平台配置项
```text
POST /api/v1/admin/config-releases/{release_id}/platform-config-items
GET /api/v1/admin/config-releases/{release_id}/platform-config-items
PUT /api/v1/admin/config-releases/{release_id}/platform-config-items/{item_id}
```
配置项必须按 `namespace + item_key` 唯一。`value_json` 必须通过对应 Pydantic Schema 校验,不接受任意权限扩大、模型端点、工具白名单或客户范围参数。
### 9.3 模型端点
```text
POST /api/v1/admin/model-endpoints
GET /api/v1/admin/model-endpoints
GET /api/v1/admin/model-endpoints/{endpoint_id}
PUT /api/v1/admin/model-endpoints/{endpoint_id}
POST /api/v1/admin/model-endpoints/{endpoint_id}/reviews
POST /api/v1/admin/model-endpoints/{endpoint_id}/activations
POST /api/v1/admin/model-endpoints/{endpoint_id}/disablements
```
请求只允许提交 `endpoint_code`、供应商标识、能力、敏感级别、超时、成本和 `secret_ref`。不得提交或返回 API 明文密钥。健康检查失败可以熔断流量,但不能自动把数据库配置改成 `active`。
### 9.4 模型路由
```text
POST /api/v1/admin/config-releases/{release_id}/model-routing-rules
GET /api/v1/admin/config-releases/{release_id}/model-routing-rules
PUT /api/v1/admin/config-releases/{release_id}/model-routing-rules/{rule_id}
```
路由规则请求使用:
```json
{
"rule_code": "customer_service_answer",
"agent_type": "customer_service",
"task_type": "answer_generation",
"model_policy": "balanced",
"primary_endpoint_id": "1001",
"fallbacks": [
{"endpoint_id": "1002", "fallback_order": 1},
{"endpoint_id": "1003", "fallback_order": 2}
],
"max_attempts": 3,
"latency_budget_ms": 15000
}
```
HTTP 不接受或返回旧的 `fallback_endpoint_ids` JSON 字段。备用链以 `model_routing_fallback` 为权威关系表,服务端校验主端点不在备用链、顺序不重复、端点存在且总尝试数不超过 3。
### 9.5 Prompt、意图和回复模板
```text
POST /api/v1/admin/prompt-templates
GET /api/v1/admin/prompt-templates
GET /api/v1/admin/prompt-templates/{prompt_id}
POST /api/v1/admin/agent-intent-configs
GET /api/v1/admin/agent-intent-configs
PUT /api/v1/admin/agent-intent-configs/{config_id}
POST /api/v1/admin/reply-templates
GET /api/v1/admin/reply-templates
PUT /api/v1/admin/reply-templates/{template_id}
POST /api/v1/admin/negative-word-rules
GET /api/v1/admin/negative-word-rules
PUT /api/v1/admin/negative-word-rules/{rule_id}
```
这些资源使用各自的草稿、审核、激活和归档状态。由于现有表没有统一 `release_id` 外键,接口不得声称它们与 `config_release` 自动原子绑定;发布服务必须在激活前校验引用版本一致。
模板和意图配置的同一业务组合最多一个 `active` 版本,数据库生成列唯一约束是最终保障。已激活版本不可原地编辑。
### 9.6 审计查询
```http
GET /api/v1/admin/audit-records
Authorization: Bearer <token>
```
支持 `trace_id`、`run_id`、用户、Agent 类型、事件类型、结果、时间范围和游标过滤。接口只读,不提供修改和删除。`audit:read-sensitive` 才能查看未脱敏详情。
### 9.7 客服转人工队列(只读)
```http
GET /api/v1/admin/customer-service/handover-tickets?limit=20&cursor={ticket_id}
GET /api/v1/admin/customer-service/handover-tickets/{ticket_no}
Authorization: Bearer <token>
```
两个接口均要求 `admin` 或 `super_admin` 角色和 `handover:read` 权限。列表仅返回工单号、
会话标识、来源 Agent、优先级、转接原因、状态和时间;详情才追加二次脱敏后的转接原因、
会话摘要、意图置信度和受控知识来源。接口不得返回客户标识、原始会话正文、账户数据、
联系方式、工单分配信息或处理结论。当前仅支持查看,不支持接单、分配、处理、解决或关闭。
## 10. SSE 与领域事件映射
### 10.1 SSE 事件
事件名称和载荷以 `AGENT-SSE-001` 为准:`start`、`tools`、`delta`、`replace`、`done`、`error`。本文只定义传输和发送时机,不重新定义字段。
### 10.2 Outbox 事件
| 事件 | 产生位置 | 消费者 |
|---|---|---|
| `agent.run_requested` | 创建运行受理事务 | Agent Worker |
| `agent.run_cancel_requested` | 取消状态事务 | Agent Worker |
| `conversation.completed` | `complete_run()` 最终事务 | 会话投影、通知 |
| `conversation.transfer_requested` | `complete_run()` 或客户转人工申请事务 | 客服转人工消费者;写入 `handover.queue_ready` 审计,不改变工单 `pending` 状态 |
| `memory.extraction_requested` | `complete_run()` 最终事务 | 记忆提取 Worker |
| `agent.run_failed` | 失败状态事务 | 监控和告警 |
| `config.release_activated` | 配置激活事务 | 缓存失效、实例刷新 |
| `config.release_rolled_back` | 回滚激活事务 | 缓存失效、监控 |
| `model.endpoint_disabled` | 端点停用事务 | 模型路由缓存 |
Outbox 消费者按 `event_id` 幂等。失败事件保留并重试,超过阈值进入死信状态并告警;不能删除未完成事件。
## 11. 审计分界
### 11.1 必须写 `interaction_audit`
1. 委托、成交、资金、持仓和风险预警处置等受监管业务状态变化。
2. 配置发布、审核、激活、停用和回滚。
3. 越权、适当性、跨客户和其他权限决策拒绝。
4. 工单全生命周期、方案审核和人工处置。
5. Agent 运行的输入摘要、结果、模型/Prompt 版本、合规动作、降级原因和耗时。
`request_idempotency` 的完成状态随 `complete_run()` 与业务结果同一事务写审计。
### 11.2 只写运行日志或指标
心跳和租约续期、幂等抢占重试、缓存读写与失效、SSE 游标与断流、健康检查和普通限流拒绝只写运行日志或指标。若限流同时判定为攻击、越权或安全策略命中,则另写安全审计。
## 12. 业务接口索引与 Agent 边界
公共接口文档只登记入口、归属和 Agent 禁止边界,不预先定义领域载荷和状态机。
| 业务域 | 接口入口 | 归属文档 | Agent 边界 |
|---|---|---|---|
| 客服工单 | `/api/v1/customer-service/handover-tickets/**` | 客服业务文档 | 可生成摘要和转人工请求,不分配、接单、解决或关闭工单 |
| 投顾方案 | `/api/v1/advisory-plans/**` | 投顾业务文档 | 只生成分析草案,不代替投顾审核发布 |
| 场内模拟交易 | `/api/v1/sim-orders/**` | 交易业务文档 | 只读查询,不创建、确认或撤销委托 |
| 风控扫描 | `/api/v1/risk/**` | 风控业务文档 | 可解释规则结果,不启动人工处置 |
| 风险预警 | `/api/v1/risk/**` | 风控业务文档 | 只读分析,不确认、升级或关闭预警 |
| 场外基金运营 | 不属于当前系统 | 独立运营系统 | 不读写场内交易表 |
风控模块落地时把扫描、预警、证据、通知和日报收在同一个 Controller 下,入口为
`/api/v1/risk/**`(早期规划写作 `/risk-scans/**`、`/risk-alerts/**`,以本节的实际入口为准)。
具体端点清单、权限与字段映射由风控业务文档登记,见第 19 节末尾。
B 类业务写接口必须复用 JWT、响应信封、错误码、幂等、统一 `RequestContext`、事务和审计规则。Controller 只路由、校验、映射和调用 Service;禁止直接访问 Model。
## 13. A 类和 B 类扩展
### 13.1 A 类:Agent 能力扩展
组员新增 Agent、意图或 `handle()` 分支时,继续使用 `/api/v1/agent-runs`,不新增 Controller。允许修改 Agent 实现、启动注册和测试数据;不得复制或覆盖 BaseAgent 的鉴权、记忆、模型、工具、合规、审计、SSE 和事件逻辑。
### 13.2 B 类:业务 API 扩展
新增模拟下单、方案审核或预警处置等领域接口时,新增该业务域的 Controller、Application Service、DTO、Repository 和测试。B 类接口必须登记:资源路径、权限码、幂等范围、事务边界、审计类别、归属文档和 Agent 只读边界。
### 13.3 组员 Agent 交付清单
1. `<agent_type>_agent.py`:继承 `BaseAgent`,只实现 `handle()`。
2. `AgentDefinition`:角色、入口、意图、工具、合规策略、模型策略和记忆视图。
3. 启动注册新增一行,不修改工厂实现。
4. 正常、低置信、工具失败、模型失败和越权用例。
5. 每个来源引用必须来自工具或知识服务返回值。
6. 不覆盖 `run_stream()`、执行模板、记忆沉淀或事件广播方法。
## 14. 内部契约索引
本文不复制以下签名。实现和测试必须引用指定权威源:
| 契约 | 权威源 |
|---|---|
| `AgentRequest`、`RequestContext`、配置 DTO | 01 §5.2,`AGENT-REQ-001` |
| `IntentResult`、`SourceReference`、`AgentResult` | 01 §5.3,`AGENT-RESULT-001` |
| `ModelGateway`、`MemoryService`、`ComplianceService`、`AuditService` | 01 §5.4,`AGENT-SERVICE-001` |
| `AgentError` 分类 | 01 §5.5,`AGENT-ERROR-001` |
| `BaseAgent` 执行顺序和 `complete_run()` | 01 §6,`AGENT-FLOW-001` |
| `DomainEvent` 和 Outbox | 01 §5.3/§6.2、02 §8.2,`AGENT-EVENT-001` |
| SSE 事件载荷 | 01 §12,`AGENT-SSE-001` |
| 表和字段 | 00、02,`DB-BASE-001` |
HTTP 层使用 DTO Mapper 投影内部对象。Controller 和 View 不得直接返回 ORM Model 或内部异常对象。
## 15. 运维与安全接口
```text
GET /internal/health/live
GET /internal/health/ready
GET /internal/metrics
```
这些接口不使用业务 JSON 信封,不暴露数据库地址、模型密钥、Token、完整客户资料或异常堆栈,只允许内网和监控系统访问。JWT 签发、刷新、注销由统一身份认证模块负责,Agent 平台不重复实现。
> **实现现状(2026-09-11 更新)**:上面这句原本是"平台不做签发"的依据,实际落地时确认了
> 平台**必须**有一个登录入口 —— 否则客户 / 员工 / 管理员三种身份无法区分(各 Agent 的
> `allowed_roles` 早就分开了,缺的只是"怎么证明你是谁")。因此平台现在提供
> **`POST /api/v1/auth/tokens`**(账号密码换访问令牌,见 §19 的 A034),
> 这是本文档 §11 那句的**唯一例外**。
>
> 边界仍然守住:平台**只做登录**,**刷新与注销仍归统一身份认证模块**
> (`app/core/security.py` 已留好 `RevocationStore` 协议,接上 Redis 即可)。
> 令牌里只放 `sub`,角色 / 权限 / 数据范围一律由 `IdentityService` 每次请求查库解析,
> 所以权限变更立即生效,不受令牌有效期影响。
## 16. 验收与契约测试
### 16.1 HTTP 通用测试
- 所有 JSON 接口使用统一成功或错误信封。
- JWT 缺失、过期、签发方错误、受众错误和吊销均被拒绝。
- 同键同正文返回原资源,同键不同正文返回 `409`。
- 跨客户查询返回 `404`,且响应不泄露资源是否存在。
- 分页游标绑定过滤条件,非法游标返回 `400 INVALID_CURSOR`。
- Controller 不直接导入 ORM、数据库客户端、Agent 实现或消息客户端。
### 16.2 Agent 运行测试
- 受理事务同时创建用户消息、幂等记录、`agent_run` 和 `agent.run_requested` Outbox。
- Worker 租约过期后使用同一 `run_id` 安全接管。
- 最终消息、审计、幂等完成状态和领域事件在同一事务提交。
- `memory.extraction_requested` 不得由请求线程在提交后直接调用。
- SSE 仅在最终事务成功后发送 `tools`、`delta`、`replace` 和 `done`。
- 断流重连可以根据 `run_id` 获取完整结果;MVP 不声称支持事件级续传。
### 16.3 管理面测试
- 创建人可以审核自己创建的配置;审核状态机不可跳过。
- 未通过校验、审核或版本检查的配置不能激活。
- 已激活版本不可原地编辑。
- 模板和意图组合最多一个 `active` 版本。
- 备用端点必须存在、顺序唯一,且主端点不能出现在备用链。
- 激活、回滚、停用和权限拒绝都写审计。
### 16.4 静态检查
```text
检查 OpenAPI 与本文路径、方法和必填 Header 一致
检查每个接口都有权限码、幂等说明、错误码和审计分类
检查 Controller 不直接访问 Model
检查 Agent 子类未覆盖公共执行模板
检查契约编号唯一且引用存在
```
## 17. 实施顺序
1. 创建 `agent_run` 表及 Alembic 迁移,核对 00 基线和 02 新表,不修改已有表名和已有字段定义。
2. 实现 HTTP DTO、统一信封、JWT 依赖、错误映射、Trace 中间件和游标工具。
3. 实现 `AgentRunApplicationService`、幂等、运行租约和 `RunDispatchPort`。
4. 将 Agent 执行从 HTTP/SSE 中解耦为 Worker 可调用的 `AgentExecutor`。
5. 实现 JSON 状态查询和结果级恢复 SSE。
6. 实现会话、消息、反馈和公共转人工入口。
7. 实现管理面配置、模型路由、Prompt、意图、模板和禁止表达接口。
8. 生成 OpenAPI 并执行契约测试、安全测试、故障恢复和迁移演练。
## 18. 完成标准
- 所有公共 HTTP 接口具有稳定路径、请求、响应、权限、错误、幂等和审计定义。
- `run_id` 可在服务重启后查询、恢复和获取完整结果。
- JSON 和 SSE 不共享传输实现,但共享运行查询投影和权限检查。
- A 类 Agent 不需要修改公共 Controller、Factory、BaseAgent 或 SSE View。
- B 类业务接口遵守统一 MVC+S 骨架和 Agent 只读边界。
- 内部契约和数据库结构没有在本文形成第二权威定义。
- 数据库迁移不修改已有表名和已有字段定义(当前现库 **52 张表**;本行原写"49 张"是早期快照,
已于 2026-09-10 按实测更正 —— 表的**数量**会随新增表变化,因此这里只保留"不修改已有表名与字段定义"这一硬约束)。
## 19. 接口总目录
下表是 v1 接口的实现清单。除特别标注外,成功响应均使用第 3.3 节信封,错误响应均使用第 3.4 节信封。
| 编号 | 方法与路径 | 权限 | 幂等 | 成功状态 | 审计 |
|---|---|---|---|---|---|
| R001 | `POST /api/v1/agent-runs` | `agent:run` | 必须 | `202` | Agent 运行 |
| R002 | `GET /api/v1/agent-runs/{run_id}` | 资源所有者/数据范围 | 否 | `200` | 否 |
| R003 | `GET /api/v1/agent-runs/{run_id}/events` | 资源所有者/数据范围 | 否 | SSE | 否 |
| R004 | `POST /api/v1/agent-runs/{run_id}/cancellations` | `agent:cancel` | 必须 | `202` | 取消操作 |
| C001 | `POST /api/v1/conversations` | `conversation:create` | 必须 | `201` | 会话创建 |
| C002 | `GET /api/v1/conversations/{session_id}` | 会话所有者/数据范围 | 否 | `200` | 否 |
| C003 | `GET /api/v1/conversations/{session_id}/messages` | 会话所有者/数据范围 | 否 | `200` | 否 |
| C004 | `POST /api/v1/conversations/{session_id}/closures` | `conversation:close` | 必须 | `200` | 会话结束 |
| C005 | `POST /api/v1/conversations/{session_id}/handover-requests` | `handover:create` | 必须 | `202` | 转人工请求 |
| C006 | `GET /api/v1/handover-requests/{handover_id}` | 客户/客服数据范围 | 否 | `200` | 否 |
| C007 | `POST /api/v1/conversation-messages/{message_id}/feedback` | `conversation:feedback` | 必须 | `201` | 反馈创建 |
| M001 | `GET /api/v1/users/me/memory-profile` | `memory:read:self` | 否 | `200` | 敏感访问 |
| M002 | `GET /api/v1/customers/{customer_id}/memory-profile` | `memory:read:customer` | 否 | `200` | 敏感访问 |
| M003 | `GET /api/v1/users/me/memory-candidates` | `memory:read:self` | 否 | `200` | 候选查询 |
| M004 | `POST /api/v1/users/me/memory-candidates/{candidate_id}/decisions` | `memory:candidate:confirm` | 必须 | `200` | 用户确认/拒绝 |
| A039 | `GET /api/v1/admin/customer-profile-candidates` | `memory:candidate:review` | 否 | `200` | 候选审核列表 |
| A040 | `POST /api/v1/admin/customer-profile-candidates/{candidate_id}/reviews` | `memory:candidate:review` | 必须 | `200` | 候选审核 |
| K001 | `GET /api/v1/knowledge-references/{reference_token}` | `knowledge:reference:read` | 否 | `200` | 否 |
| K002 | `POST /api/v1/knowledge/upload` | `knowledge:manage` | 否 | `201` | 知识文档变更 |
| K003 | `GET /api/v1/knowledge/list` | `knowledge:manage` | 否 | `200` | 否 |
| K004 | `DELETE /api/v1/knowledge/{knowledge_id}` | `knowledge:manage` | 否 | `200` | 知识文档失效 |
| A001 | `POST /api/v1/admin/config-releases` | `config:write` | 必须 | `201` | 配置草稿 |
| A002 | `GET /api/v1/admin/config-releases` | `config:read` | 否 | `200` | 否 |
| A003 | `GET /api/v1/admin/config-releases/{release_id}` | `config:read` | 否 | `200` | 否 |
| A004 | `POST /api/v1/admin/config-releases/{release_id}/validations` | `config:write` | 必须 | `200` | 配置校验 |
| A005 | `POST /api/v1/admin/config-releases/{release_id}/reviews` | `config:review` | 必须 | `200` | 配置审核 |
| A006 | `POST /api/v1/admin/config-releases/{release_id}/activations` | `config:activate` | 必须 | `200` | 配置激活 |
| A007 | `POST /api/v1/admin/config-releases/{release_id}/rollbacks` | `config:activate` | 必须 | `201` | 配置回滚 |
| A008 | `POST /api/v1/admin/config-releases/{release_id}/platform-config-items` | `config:write` | 必须 | `201` | 配置变更 |
| A009 | `GET /api/v1/admin/config-releases/{release_id}/platform-config-items` | `config:read` | 否 | `200` | 否 |
| A010 | `PUT /api/v1/admin/config-releases/{release_id}/platform-config-items/{item_id}` | `config:write` | 必须 | `200` | 配置变更 |
| A011 | `POST /api/v1/admin/model-endpoints` | `model-endpoint:manage` | 必须 | `201` | 端点创建 |
| A012 | `GET /api/v1/admin/model-endpoints` | `config:read` | 否 | `200` | 否 |
| A013 | `GET /api/v1/admin/model-endpoints/{endpoint_id}` | `config:read` | 否 | `200` | 否 |
| A014 | `PUT /api/v1/admin/model-endpoints/{endpoint_id}` | `model-endpoint:manage` | 必须 | `200` | 端点变更 |
| A015 | `POST /api/v1/admin/model-endpoints/{endpoint_id}/reviews` | `config:review` | 必须 | `200` | 端点审核 |
| A016 | `POST /api/v1/admin/model-endpoints/{endpoint_id}/activations` | `model-endpoint:manage` | 必须 | `200` | 端点激活 |
| A017 | `POST /api/v1/admin/model-endpoints/{endpoint_id}/disablements` | `model-endpoint:manage` | 必须 | `200` | 端点停用 |
| A018 | `POST /api/v1/admin/config-releases/{release_id}/model-routing-rules` | `config:write` | 必须 | `201` | 路由变更 |
| A019 | `GET /api/v1/admin/config-releases/{release_id}/model-routing-rules` | `config:read` | 否 | `200` | 否 |
| A020 | `PUT /api/v1/admin/config-releases/{release_id}/model-routing-rules/{rule_id}` | `config:write` | 必须 | `200` | 路由变更 |
| A021 | `POST /api/v1/admin/prompt-templates` | `config:write` | 必须 | `201` | Prompt 变更 |
| A022 | `GET /api/v1/admin/prompt-templates` | `config:read` | 否 | `200` | 否 |
| A023 | `GET /api/v1/admin/prompt-templates/{prompt_id}` | `config:read` | 否 | `200` | 否 |
| A024 | `POST /api/v1/admin/agent-intent-configs` | `config:write` | 必须 | `201` | 意图配置 |
| A025 | `GET /api/v1/admin/agent-intent-configs` | `config:read` | 否 | `200` | 否 |
| A026 | `PUT /api/v1/admin/agent-intent-configs/{config_id}` | `config:write` | 必须 | `200` | 意图配置 |
| A027 | `POST /api/v1/admin/reply-templates` | `config:write` | 必须 | `201` | 回复模板 |
| A028 | `GET /api/v1/admin/reply-templates` | `config:read` | 否 | `200` | 否 |
| A029 | `PUT /api/v1/admin/reply-templates/{template_id}` | `config:write` | 必须 | `200` | 回复模板 |
| A030 | `POST /api/v1/admin/negative-word-rules` | `config:write` | 必须 | `201` | 禁止表达 |
| A031 | `GET /api/v1/admin/negative-word-rules` | `config:read` | 否 | `200` | 否 |
| A032 | `PUT /api/v1/admin/negative-word-rules/{rule_id}` | `config:write` | 必须 | `200` | 禁止表达 |
| A033 | `GET /api/v1/admin/audit-records` | `audit:read` | 否 | `200` | 否 |
| A034 | `POST /api/v1/auth/tokens` | 公开(登录前无身份) | 否 | `200` | 登录成功/失败 |
| A035 | `GET /api/v1/admin/roles` | `audit:read` | 否 | `200` | 否 |
| A036 | `GET /api/v1/admin/roles/{role_code}` | `audit:read` | 否 | `200` | 否 |
| A037 | `GET /api/v1/admin/roles/{role_code}/permissions` | `audit:read` | 否 | `200` | 否 |
| A038 | `GET /api/v1/admin/users/{user_id}/roles` | `audit:read` | 否 | `200` | 否 |
| O001 | `GET /internal/health/live` | 内网 | 否 | `200` | 否 |
| O002 | `GET /internal/health/ready` | 内网 | 否 | `200/503` | 否 |
| O003 | `GET /internal/metrics` | 监控系统 | 否 | `200` | 否 |
> **A034 – A038 的两点说明**:
>
> - `POST /api/v1/auth/tokens` 是平台内**唯一的登录入口**(§11 已注明这是"平台不重复实现
> 签发"的唯一例外;**刷新与注销仍归统一身份认证模块**)。
> - A035 – A038 是 RBAC 的**只读**查询,供管理员回答"谁能访问什么""这个人为什么 403"。
> 它们复用 `audit:read` 而**不新增** `rbac:read`:这份清单本身就是审计材料,且复用是
> 零数据改动、立刻可用(新增权限码得先改 `sys_permission`,而它目前由
> `seed_test_rbac.py` 以 DELETE 重建语义管理)。
> **权限变更(提权 / 降权)尚无接口** —— 那条写路径必须带三条红线
> (审计留痕、禁止自我提权、保护内置角色),需要单独评审,不是遗漏。
业务域接口 `/customer-service/handover-tickets/**`、`/advisory-plans/**`、`/sim-orders/**`、`/risk-scans/**` 和 `/risk-alerts/**` 的具体方法、请求体、领域状态机和错误码分别由对应业务文档登记;它们仍必须遵守本文第 3-5、11 和 12 节。
## 20. 变更流程
任何新增或修改接口必须同时更新:
1. 本文对应章节和第 19 节目录。
2. OpenAPI 机器可读定义。
3. 权限码、审计类别、幂等说明和错误码清单。
4. 请求、响应、越权、重复提交、故障恢复和安全测试。
5. 对应业务文档的入口索引;业务载荷不得复制到公共文档。
涉及内部契约时,先修改权威源文档,再更新本文索引。禁止只修改 OpenAPI 或只修改某个业务文档造成第二权威源。