wechat_ai/docs/api-design.md

2075 lines
56 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.

# 微信 AI 助手 API 接口设计
> 依据当前 `src/` 页面字段、数据结构和交互设计。本文定义目标契约,不表示这些接口已经实现。
>
> 适用范围:单机 Windows、单个本地操作者、Tauri 桌面端、本机 SQLite 业务状态、本机 sidecar。默认不引入账号、租户、Workspace 或远程同步。
## 1. 设计结论
### 1.1 协议选择
| 场景 | 协议 | 原因 |
|---|---|---|
| 列表、详情、筛选、创建、更新、启停、删除、配置保存 | HTTP JSON | 一次请求对应一个确定结果,便于重试、校验、审计和错误处理 |
| 文件上传 | HTTP `multipart/form-data`;大文件使用分片 HTTP | WebSocket 不适合传二进制大文件HTTP 更容易限制大小、校验哈希和断点续传 |
| 智能体测试流式回复 | WebSocket | 同一会话需要发送消息、接收 token 增量、完成或失败事件 |
| 蒸馏任务进度 | WebSocket | 后台任务跨页面持续运行,需要实时阶段和进度事件 |
| 引擎状态、启动检查、结构化日志 | WebSocket | 状态和日志持续产生,轮询会增加延迟和重复流量 |
| 知识文件解析状态 | WebSocket | PDF、DOCX、PPT/PPTX 的文本提取可能耗时;图片和视频只做上传、校验及预览,不做自动识别 |
| 标注截图、窗口选择、区域保存、桌面窗口控制 | Tauri IPC | 全程由本地 React/Tauri/Rust 处理,不进入 HTTP、WebSocket 或 Go 后端服务 |
| 主题、当前 Tab、临时弹窗状态 | 前端本地状态 | 纯 UI 状态,不进入业务 API |
### 1.2 服务地址
- HTTP`http://127.0.0.1:8766/api/v1`
- WebSocket`ws://127.0.0.1:8766/api/v1/ws?ticket={singleUseTicket}`
- 现有视觉预览服务使用 `127.0.0.1:8765`,业务 API 使用 `8766`,避免端口冲突。
- 服务必须只监听 loopback不监听局域网地址。
### 1.3 安全边界
1. Tauri 启动 sidecar 时生成随机 `sessionToken`HTTP 使用 `Authorization: Bearer {sessionToken}`
2. WebSocket 不在 URL 中携带长期 token。前端先申请 30 秒有效、单次使用的 `ticket`
3. 模型 API Key 进入系统密钥库;任何 GET 响应只返回 `keyConfigured`,不得返回明文或掩码后的原始值。
4. 日志禁止记录 API Key、完整系统提示词、无关聊天原文、剪贴板内容和本机绝对文件路径。
5. 所有资源更新使用 `revision` 做乐观并发控制;版本不匹配返回 `409 REVISION_CONFLICT`
6. 删除知识库、智能体、员工等不可逆操作必须由前端二次确认,并在请求中传 `confirmation`
---
## 2. 通用规范
### 2.1 数据格式
- JSON 字段统一使用 `camelCase`,与 React/Tauri 当前数据结构一致。
- 时间统一为 UTC RFC 3339例如 `2026-07-21T08:30:15.238Z`
- 文件大小统一为整数 `sizeBytes``KB/MB` 仅由前端格式化。
- 比率统一为 `0~1` 小数,例如 `successRate: 0.984`;分数使用 `0~100`
- ID 不使用名称,建议前缀:`kb_``kv_``file_``agent_``employee_``distill_``test_``log_`
- 列表使用 cursor 分页;页面当前数据量虽小,接口仍保持可扩展。
### 2.2 成功响应
```json
{
"data": {},
"meta": {
"requestId": "req_01J3...",
"nextCursor": null,
"total": 6
}
}
```
- 单资源接口可省略 `nextCursor``total`
- `204 No Content` 用于成功删除且无响应体的场景。
### 2.3 错误响应
```json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "请求字段校验失败",
"retryable": false,
"fieldErrors": {
"title": "知识标题不能为空"
},
"details": null
},
"requestId": "req_01J3..."
}
```
常用状态码和错误码:
| HTTP | code | 含义 |
|---:|---|---|
| 400 | `VALIDATION_ERROR` | 字段格式、枚举或业务前置条件错误 |
| 401 | `UNAUTHORIZED` | 本机 session token 无效或过期 |
| 404 | `NOT_FOUND` | 资源不存在或已删除 |
| 409 | `REVISION_CONFLICT` | 乐观锁版本冲突 |
| 409 | `STATE_CONFLICT` | 当前状态不允许该操作 |
| 413 | `FILE_TOO_LARGE` | 文件超过用途限制 |
| 415 | `UNSUPPORTED_FILE_TYPE` | 文件扩展名、MIME 或内容签名不受支持 |
| 422 | `FILE_PARSE_FAILED` | 文件上传成功但无法解析 |
| 429 | `RATE_LIMITED` | 模型或任务并发达到限制 |
| 500 | `INTERNAL_ERROR` | 未分类内部错误 |
| 502 | `MODEL_UPSTREAM_ERROR` | 模型供应商错误 |
| 504 | `MODEL_TIMEOUT` | 模型调用超时 |
### 2.4 列表参数
```text
?query=退款&status=enabled&type=file&limit=20&cursor=eyJpZCI6...
```
| 参数 | 类型 | 默认 | 说明 |
|---|---|---:|---|
| `query` | string | `""` | 名称、标签或正文关键字,最大 100 字 |
| `status` | string | `all` | 各资源定义的状态枚举 |
| `limit` | integer | 20 | `1~100` |
| `cursor` | string | 无 | 服务端返回的游标,不允许客户端解析 |
| `sort` | string | `updatedAt:desc` | 白名单排序字段与方向 |
### 2.5 幂等性
以下 POST 请求必须携带 `Idempotency-Key`
- 创建知识库草稿、发布知识版本;
- 创建智能体、发送测试消息;
- 创建蒸馏任务、发布员工;
- 启动或停止引擎。
服务端在 24 小时内对相同 key 和相同请求体返回首次结果;同 key 不同请求体返回 `409 IDEMPOTENCY_CONFLICT`
---
## 3. 核心数据对象
### 3.1 FileAsset
```json
{
"id": "file_01J3...",
"purpose": "knowledge_source",
"fileName": "refund-policy.pdf",
"contentType": "application/pdf",
"extension": "pdf",
"sizeBytes": 2516582,
"sha256": "6fe1...",
"kind": "text",
"status": "ready",
"parseStatus": "completed",
"extractedTextLength": 12680,
"convertedTextFileId": "filetxt_01J3...",
"warnings": [
{ "code": "EMBEDDED_IMAGES_SKIPPED", "message": "文档中的 3 张图片已省略" }
],
"error": null,
"previewStatus": "ready",
"description": null,
"descriptionSource": null,
"createdAt": "2026-07-21T08:30:15.238Z"
}
```
枚举:
- `purpose`: `knowledge_source | agent_test_attachment | distill_source | report_export`
- `kind`: `text | image | video | binary`
- `status`: `uploading | uploaded | processing | ready | failed | deleted`
- `parseStatus`: `not_required | queued | processing | completed | failed`
- `previewStatus`: `none | processing | ready | failed`
- `descriptionSource`: 当前只允许 `manual | null`,禁止返回 `ai | ocr | vision`
图片或视频知识文件示例:
```json
{
"id": "file_01J4...",
"purpose": "knowledge_source",
"fileName": "refund-process.png",
"contentType": "image/png",
"extension": "png",
"sizeBytes": 356240,
"sha256": "7a2d...",
"kind": "image",
"status": "ready",
"parseStatus": "not_required",
"extractedTextLength": 0,
"convertedTextFileId": null,
"warnings": [],
"error": null,
"previewStatus": "ready",
"description": "退款申请、审核和到账三个步骤的流程图。",
"descriptionSource": "manual",
"createdAt": "2026-07-21T08:31:15.238Z"
}
```
### 3.2 KnowledgeSummary
```json
{
"id": "kb_refund",
"title": "售后常见问题与退款边界",
"type": "file",
"tags": ["售后", "退款"],
"status": "enabled",
"currentVersion": "v1.7",
"referenceCount": 82,
"fileCount": 4,
"updatedAt": "2026-06-05T01:42:00Z",
"revision": 7
}
```
- `type`: `file | image | video | mixed`
- `status`: `draft | processing | enabled | disabled | failed`
### 3.3 AgentSummary
```json
{
"id": "agent_intent",
"name": "客户意图识别",
"type": "builtin",
"enabled": true,
"version": "v2.1",
"tags": ["线索分级", "异议判断", "下一步建议"],
"knowledgeCount": 3,
"recentCallCount": 16,
"lastTestStatus": "passed",
"updatedAt": "2026-06-16T10:20:00Z",
"revision": 4
}
```
- `type`: `builtin | custom`
- `lastTestStatus`: `not_run | running | passed | failed`
### 3.4 DistillJobSummary
```json
{
"id": "distill_01J3...",
"name": "销售冠军 Aileen 话术蒸馏",
"state": "extracting",
"stage": "strategy_extraction",
"progress": 0.72,
"estimatedRemainingSeconds": 180,
"sourceCount": 1,
"successCount": 248,
"failedCount": 7,
"createdAt": "2026-07-21T08:30:15.238Z",
"updatedAt": "2026-07-21T08:34:10.000Z",
"revision": 9
}
```
- `state`: `draft | queued | extracting | evaluating | ready_to_publish | completed | cancelled | failed`
- `stage`: `source_validation | data_cleaning | scene_clustering | strategy_extraction | boundary_induction | counterexample_generation | evaluation | completed`
### 3.5 EmployeeSummary
```json
{
"id": "employee_1",
"name": "销售冠军 Aileen",
"version": "v3.2",
"enabled": true,
"traits": ["高意向逼单", "异议拆解", "温和推进"],
"domain": "private_sales",
"score": 96,
"weeklyCallCount": 28,
"updatedAt": "2026-06-18T09:00:00Z",
"revision": 3
}
```
---
## 4. 应用初始化与实时连接
### 4.1 获取启动数据
`GET /bootstrap`
请求:无 body。
响应:
```json
{
"data": {
"apiVersion": "1.0",
"serverVersion": "0.1.0",
"platform": "windows",
"capabilities": {
"knowledge": true,
"agents": true,
"distillation": true,
"engine": true,
"nativeAnnotation": true
},
"engineState": {
"lifecycle": "idle",
"autoSendPaused": false,
"annotationComplete": true,
"updatedAt": "2026-07-21T08:30:15.238Z"
},
"badges": {
"clone": 3,
"knowledge": 0,
"agents": 1,
"distill": 0
}
}
}
```
用途:主窗口一次请求获得能力开关、引擎初始状态和底部 Tab badge。主题仍存本地不进入接口。
### 4.2 申请 WebSocket ticket
`POST /realtime-tickets`
请求:
```json
{
"clientId": "desktop-main-7cb8",
"lastEventId": "evt_01J3...",
"subscriptions": ["engine", "logs", "distill:distill_01J3", "files:file_01J3"]
}
```
字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---:|---|
| `clientId` | string | 是 | 每个窗口唯一,最大 64 字 |
| `lastEventId` | string/null | 否 | 断线续传起点 |
| `subscriptions` | string[] | 否 | 初始订阅频道,最多 50 个 |
响应:
```json
{
"data": {
"ticket": "wst_01J3...",
"webSocketUrl": "ws://127.0.0.1:8766/api/v1/ws?ticket=wst_01J3...",
"expiresAt": "2026-07-21T08:30:45.238Z",
"heartbeatSeconds": 20
}
}
```
---
## 5. 微信分身、引擎与日志
### 5.1 引擎状态
`GET /engine/state`
响应 `EngineState`
```json
{
"data": {
"lifecycle": "running",
"agent": "running",
"visionStream": "running",
"wechatWindow": "connected",
"annotationComplete": true,
"autoSendPaused": false,
"activeConversationId": null,
"consecutiveErrors": 0,
"lastError": null,
"startedAt": "2026-07-21T08:30:15.238Z",
"updatedAt": "2026-07-21T08:30:18.000Z",
"revision": 12
}
}
```
枚举:
- `lifecycle`: `idle | checking | starting | running | stopping | error`
- `agent` / `visionStream`: `stopped | starting | running | error`
- `wechatWindow`: `unknown | connected | missing | changed`
### 5.2 执行启动检查
`POST /engine/startup-checks`
请求:
```json
{ "forceRefresh": true }
```
响应 `202 Accepted`
```json
{
"data": {
"runId": "check_01J3...",
"state": "running",
"checks": [
{ "id": "config", "label": "基础配置", "status": "waiting", "passed": null, "detail": null },
{ "id": "annotation", "label": "界面标注", "status": "waiting", "passed": null, "detail": null },
{ "id": "window", "label": "微信窗口连接情况", "status": "waiting", "passed": null, "detail": null }
]
}
}
```
后续通过 `engine.startup_check.updated` WS 事件返回逐项状态;最终结果也可通过 `GET /engine/startup-checks/{runId}` 查询。
### 5.3 启动引擎
`POST /engine/start`
请求:
```json
{
"startupCheckRunId": "check_01J3...",
"expectedRevision": 12
}
```
响应 `202 Accepted`:返回最新 `EngineState`。若检查未通过,返回 `409 STARTUP_CHECK_FAILED``details.failedCheckIds` 指明失败项。
### 5.4 停止引擎
`POST /engine/stop`
请求:
```json
{ "expectedRevision": 13, "reason": "operator_request" }
```
响应 `202 Accepted`:返回 `lifecycle: "stopping"``EngineState`,最终状态通过 WS 推送。
### 5.5 暂停或恢复自动发送
`PATCH /engine/auto-send`
请求:
```json
{ "paused": true, "expectedRevision": 14 }
```
响应:返回最新 `EngineState`。当前界面的“暂停自动发送/恢复自动发送”直接绑定此接口。
### 5.6 日志列表
`GET /logs?level=warning&module=model&query=req_91fa&limit=100&cursor=...`
响应:
```json
{
"data": {
"items": [
{
"id": "log_01J3...",
"time": "2026-07-21T08:30:15.238Z",
"level": "warning",
"module": "model",
"message": "model gateway latency is higher than expected",
"requestId": "req_91fa",
"context": {
"fromSnapshotId": "snapshot_9821",
"toSnapshotId": "snapshot_9822"
}
}
]
},
"meta": { "requestId": "req_...", "nextCursor": null, "total": 1 }
}
```
- `level`: `info | warning | error`
- `module`: `agent | vision | capture | retriever | model | policy | sender`
暂停接收仅停止当前窗口的 WS 消费,不调用后端、不暂停引擎。清空视图只清前端缓存,不删除审计日志。
### 5.7 导出日志
`POST /logs/exports`
请求:
```json
{
"filter": { "level": "all", "module": "all", "query": "" },
"format": "jsonl",
"from": "2026-07-20T00:00:00Z",
"to": "2026-07-21T23:59:59Z"
}
```
响应 `202 Accepted`
```json
{
"data": {
"exportId": "export_01J3...",
"status": "processing",
"fileId": null
}
}
```
完成后发送 `export.completed`payload 中提供 `fileId`;前端调用 `GET /files/{fileId}/content` 下载。
---
## 6. 文件上传、解析与预览
### 6.1 用途限制
| purpose | 支持格式 | 单文件限制 | 数量限制 |
|---|---|---:|---:|
| `agent_test_attachment` | PDF、DOCX、PPT、PPTX、TXT、MD、CSV、JSON | 20 MB | 每条消息最多 5 个 |
| `knowledge_source` | TXT、PDF、DOCX、PPT、PPTX、PNG、JPG/JPEG、GIF、WEBP、MP4、WEBM、MOV | 文档/图片 100 MB视频 2 GB | 每个草稿最多 100 个 |
| `distill_source` | TXT、MD、CSV、JSON、PDF、DOCX、PPT、PPTX | 500 MB | 每个任务最多 50 个 |
服务端必须同时校验扩展名、MIME 和文件头;不能只信任浏览器提交的 `contentType`
知识文件首期处理边界:
1. TXT 按 UTF-8 规范化保存PDF 只提取已有文本层DOCX 提取段落和表格文字PPT/PPTX 提取文本框、表格和备注文字。
2. 文档中的嵌入图片直接省略,不调用 OCR、视觉模型或图片理解。
3. 图片和视频可以作为独立知识来源上传、保存和预览,但不执行 OCR、图片理解、视频理解、ASR、关键帧提取或自动摘要。
4. 图片和视频的 `description` 只能由操作者人工填写,`descriptionSource` 固定为 `manual`;后端不得自动生成或补全描述。
5. 媒体文件有人工描述时,知识检索只索引标题、标签和人工描述;没有描述时只索引标题和标签,原始媒体内容不进入文本检索。
6. 扫描版 PDF 或只有图片、没有可提取文字的 Office 文档返回 `422 NO_EXTRACTABLE_TEXT`,不能标记为文档转换成功。
7. 文档转换结果生成独立 UTF-8 `.txt` 文件,并通过 `convertedTextFileId` 关联原文件;知识版本正文由转换后的 TXT 和媒体人工描述组成。
### 6.2 小文件直接上传
`POST /files`
Content-Type`multipart/form-data`
表单字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---:|---|
| `file` | binary | 是 | 文件内容 |
| `purpose` | enum | 是 | 上述用途 |
| `clientFileId` | string | 是 | 前端本次选择生成的 UUID用于去重 |
| `sha256` | string | 否 | 客户端可计算时传入 |
响应 `201 Created`:返回 `FileAsset`。文本类文件通常返回 `parseStatus: "queued"`,不能把上传成功展示成解析成功。
### 6.3 大文件分片初始化
`POST /file-uploads`
请求:
```json
{
"purpose": "knowledge_source",
"fileName": "product-training.mp4",
"contentType": "video/mp4",
"sizeBytes": 734003200,
"sha256": "可选整体哈希",
"clientFileId": "8fb2a9e5-..."
}
```
响应:
```json
{
"data": {
"uploadId": "upload_01J3...",
"chunkSizeBytes": 8388608,
"uploadedParts": [],
"expiresAt": "2026-07-22T08:30:15.238Z"
}
}
```
### 6.4 上传分片
`PUT /file-uploads/{uploadId}/parts/{partNumber}`
Headers
```text
Content-Type: application/octet-stream
Content-Length: 8388608
Content-Range: bytes 0-8388607/734003200
X-Chunk-SHA256: 1c9e...
```
响应:
```json
{
"data": {
"partNumber": 1,
"sizeBytes": 8388608,
"sha256": "1c9e...",
"accepted": true
}
}
```
重复上传同一分片且哈希相同必须幂等;哈希不同返回 `409 CHUNK_CONFLICT`
### 6.5 完成分片上传
`POST /file-uploads/{uploadId}/complete`
请求:
```json
{
"parts": [
{ "partNumber": 1, "sha256": "1c9e..." },
{ "partNumber": 2, "sha256": "41af..." }
]
}
```
响应 `201 Created`:返回 `FileAsset`
### 6.6 查询和取消上传
- `GET /file-uploads/{uploadId}`:返回已上传分片,用于断点续传。
- `DELETE /file-uploads/{uploadId}`:删除临时分片,返回 `204`
### 6.7 查询文件
`GET /files/{fileId}`:返回 `FileAsset`
文件解析过程通过 WS 推送 `file.processing.updated`
```json
{
"fileId": "file_01J3...",
"status": "processing",
"parseStatus": "processing",
"progress": 0.64,
"stage": "extracting_text",
"extractedTextLength": 0,
"error": null
}
```
### 6.8 文档转换与媒体预览
- `GET /files/{fileId}/text?offset=0&limit=20000`:分页预览文档转换后的纯文本。
- `GET /files/{fileId}/text-file`:下载 UTF-8 `.txt` 转换结果,响应为 `text/plain; charset=utf-8`
- `GET /files/{fileId}/content`:下载原文件或预览图片、视频;支持 `Range`
- `PATCH /files/{fileId}`:只用于操作者保存图片或视频的人工描述。
文档转换规则:
- PDF 只读取文本层,不对页面截图或扫描内容执行 OCR。
- DOCX 中的段落、表格文本按文档顺序输出;图片、形状内不可直接读取的图像文字省略。
- PPT/PPTX 中的文本框、表格和备注按页输出;每页使用稳定页码分隔,图片内容省略。
- 成功提取到正文但省略了图片时,`parseStatus``completed`,同时返回 `EMBEDDED_IMAGES_SKIPPED` warning。
- 没有任何可提取正文时,`parseStatus``failed``error.code``NO_EXTRACTABLE_TEXT`
媒体处理规则:
- 图片、视频上传完成并通过文件安全校验后,`parseStatus` 固定为 `not_required`
- 服务端只生成安全预览能力,不识别画面、不抽取文字、不识别语音、不生成描述。
- `description` 完全来自人工输入,服务端原样保存;空描述允许保存,但媒体内容本身不会进入 FTS5 正文索引。
`PATCH /files/{fileId}` 请求:
```json
{
"description": "退款申请、审核和到账三个步骤的流程图。",
"descriptionSource": "manual",
"expectedRevision": 1
}
```
服务端必须拒绝 `descriptionSource` 不是 `manual` 的请求;响应返回最新 `FileAsset`
文本响应:
```json
{
"data": {
"fileId": "file_01J3...",
"text": "第一章 退款申请范围……",
"offset": 0,
"returnedLength": 12680,
"totalLength": 12680,
"truncated": false
}
}
```
### 6.9 删除文件
`DELETE /files/{fileId}?expectedRevision=1`
- 未绑定草稿或消息时返回 `204`
- 已被已发布版本引用时返回 `409 FILE_IN_USE`,不能物理删除。
---
## 7. 知识库接口
### 7.1 知识库列表与全文搜索
`GET /knowledge-bases?query=退款&status=all&limit=20&cursor=...`
响应:`data.items: KnowledgeSummary[]`
搜索范围由设置中的 `knowledgeSearchFields` 决定;页面输入“标题、标签或完整正文”时,服务端返回匹配后的知识库摘要,不把完整正文放进列表响应。
### 7.2 创建草稿
`POST /knowledge-drafts`
请求:
```json
{
"mode": "create",
"knowledgeId": null,
"baseVersion": null,
"title": "售后退款政策",
"description": "用于回答售后退款范围、异常订单与客户安抚相关问题。",
"tags": ["私域成交", "售后"]
}
```
更新知识库时:
```json
{
"mode": "update",
"knowledgeId": "kb_refund",
"baseVersion": "v1.7"
}
```
响应:
```json
{
"data": {
"id": "kbdraft_01J3...",
"mode": "create",
"knowledgeId": null,
"baseVersion": null,
"title": "售后退款政策",
"description": "用于回答……",
"tags": ["私域成交", "售后"],
"fileIds": [],
"state": "draft",
"revision": 1,
"updatedAt": "2026-07-21T08:30:15.238Z"
}
}
```
### 7.3 保存草稿基本信息
`PATCH /knowledge-drafts/{draftId}`
请求:
```json
{
"title": "售后退款政策",
"description": "用于回答售后问题。",
"tags": ["私域成交", "售后"],
"expectedRevision": 1
}
```
校验:`title` 1~100 字;`description` 最大 2000 字;标签最多 20 个,每个 1~30 字,去重后保存。
响应:返回完整草稿和递增后的 `revision`
### 7.4 绑定或移除来源文件
`POST /knowledge-drafts/{draftId}/files`
```json
{
"fileIds": ["file_01J3...", "file_01J4..."],
"expectedRevision": 2
}
```
响应:
```json
{
"data": {
"draftId": "kbdraft_01J3...",
"files": [{ "id": "file_01J3...", "fileName": "refund-policy.pdf", "sizeBytes": 2516582, "kind": "text", "status": "ready", "parseStatus": "completed" }],
"readyCount": 1,
"processingCount": 1,
"failedCount": 0,
"revision": 3
}
}
```
`DELETE /knowledge-drafts/{draftId}/files/{fileId}?expectedRevision=3`:从草稿移除,返回更新后的文件汇总。
### 7.5 草稿预览汇总
`GET /knowledge-drafts/{draftId}/preview`
响应:
```json
{
"data": {
"draftId": "kbdraft_01J3...",
"sourceCount": 3,
"ready": true,
"files": [{ "id": "file_01J3...", "fileName": "refund-policy.pdf", "sizeBytes": 2516582, "kind": "text", "status": "ready", "parseStatus": "completed" }],
"totalTextLength": 26000,
"blockingErrors": []
}
}
```
`ready` 只有在至少一个来源且所有必需解析完成时为 `true`
### 7.6 发布知识库或保存新版本
`POST /knowledge-drafts/{draftId}/publish`
请求:
```json
{
"versionNote": "补充异常订单处理边界",
"enableAfterPublish": true,
"expectedRevision": 4
}
```
响应 `201 Created`
```json
{
"data": {
"knowledge": { "id": "kb_refund", "title": "售后常见问题与退款边界", "type": "file", "tags": ["售后", "退款"], "status": "enabled", "currentVersion": "v1.8", "referenceCount": 0, "fileCount": 1, "updatedAt": "2026-07-21T08:40:00Z", "revision": 1 },
"version": {
"id": "kv_01J3...",
"knowledgeId": "kb_refund",
"version": "v1.8",
"versionNote": "补充异常订单处理边界",
"fileIds": ["file_01J3..."],
"textLength": 26000,
"contentHash": "5c82...",
"createdAt": "2026-07-21T08:40:00Z"
}
}
}
```
版本号由服务端生成,客户端不得自行加一。若 `baseVersion` 已落后,返回 `409 REVISION_CONFLICT`
### 7.7 知识库详情
`GET /knowledge-bases/{knowledgeId}`
响应:
```json
{
"data": {
"id": "kb_refund",
"title": "售后常见问题与退款边界",
"description": "涵盖退款期限、数字商品限制、异常订单处理和安抚话术边界。",
"tags": ["售后", "退款"],
"type": "file",
"status": "enabled",
"currentVersion": "v1.7",
"creator": { "id": "local_operator", "displayName": "本机管理员" },
"effectiveAt": "2026-06-05T01:42:00Z",
"lastRetrievedAt": "2026-07-21T08:18:00Z",
"metrics": {
"weeklyHitCount": 32,
"agentReferenceCount": 3,
"employeeBindingCount": 2,
"textLength": 26000
},
"revision": 7,
"updatedAt": "2026-06-05T01:42:00Z"
}
}
```
### 7.8 详情子资源
- `GET /knowledge-bases/{id}/files`:当前版本的 `FileAsset[]`
- `GET /knowledge-bases/{id}/references?type=all`:引用关系。
- `GET /knowledge-bases/{id}/versions?limit=20&cursor=...`:版本历史。
引用关系项:
```json
{
"resourceType": "agent",
"resourceId": "agent_quote",
"name": "报价策略生成",
"enabled": true,
"boundVersion": "v1.7"
}
```
版本项:
```json
{
"id": "kv_01J3...",
"version": "v1.7",
"versionNote": "修正数字商品退款边界",
"isCurrent": true,
"createdAt": "2026-06-05T01:42:00Z"
}
```
### 7.9 启停和删除
`PATCH /knowledge-bases/{id}/status`
```json
{ "enabled": false, "expectedRevision": 7 }
```
响应:返回最新 `KnowledgeSummary`。停用后已绑定关系保留,但检索时不能命中。
`DELETE /knowledge-bases/{id}`
```json
{
"confirmation": "售后常见问题与退款边界",
"expectedRevision": 8
}
```
有引用关系时默认返回 `409 RESOURCE_IN_USE` 和引用摘要;不提供静默级联删除。
---
## 8. 智能体接口
### 8.1 列表
`GET /agents?query=报价&type=all&enabled=all&limit=20&cursor=...`
响应:`data.items: AgentSummary[]`
筛选枚举:`type=all|builtin|custom``enabled=all|true|false`
### 8.2 创建智能体草稿
`POST /agent-drafts`
请求:
```json
{
"mode": "create",
"agentId": null,
"name": "报价策略生成",
"description": "根据客户意向、商品成本和优惠边界生成报价建议。",
"tags": ["利润保护", "阶梯报价", "优惠边界"]
}
```
更新模式传 `mode: "update"``agentId`。响应:
```json
{
"data": {
"id": "agentdraft_01J3...",
"mode": "create",
"agentId": null,
"name": "报价策略生成",
"description": "根据客户意向……",
"tags": ["利润保护", "阶梯报价", "优惠边界"],
"systemPrompt": "",
"knowledgeIds": [],
"lastTestSessionId": null,
"lastTestStatus": "not_run",
"revision": 1
}
}
```
### 8.3 保存基本信息、提示词和授权
`PATCH /agent-drafts/{draftId}`
请求可部分更新:
```json
{
"name": "报价策略生成",
"description": "根据客户意向、商品成本和优惠边界生成报价建议。",
"tags": ["利润保护", "阶梯报价", "优惠边界"],
"systemPrompt": "你是报价策略助手……",
"knowledgeIds": ["kb_price", "kb_discount"],
"expectedRevision": 1
}
```
校验:名称 1~80 字;描述最大 2000 字;标签最多 20 个;系统提示词最大 20000 字;只能授权已生效知识库。
响应:返回完整草稿和新 `revision`
### 8.4 发布或更新智能体
`POST /agent-drafts/{draftId}/publish`
请求:
```json
{
"releaseNote": "首次发布报价策略生成智能体。",
"enableAfterPublish": true,
"expectedRevision": 3
}
```
前置条件:`lastTestStatus` 必须为 `passed`,且测试使用的 `draftRevision` 必须等于当前草稿 revision提示词或授权变更后旧测试失效。
响应:
```json
{
"data": {
"agent": { "id": "agent_quote", "name": "报价策略生成", "type": "custom", "enabled": true, "version": "v1.0", "tags": ["利润保护", "阶梯报价", "优惠边界"], "knowledgeCount": 2, "recentCallCount": 0, "lastTestStatus": "passed", "updatedAt": "2026-07-21T08:50:00Z", "revision": 1 },
"release": {
"version": "v1.0",
"releaseNote": "首次发布报价策略生成智能体。",
"createdAt": "2026-07-21T08:50:00Z"
}
}
}
```
### 8.5 详情
`GET /agents/{agentId}`
响应:
```json
{
"data": {
"id": "agent_intent",
"name": "客户意图识别",
"type": "builtin",
"enabled": true,
"version": "v2.1",
"description": "识别客户所处阶段、购买意向、核心异议,并输出下一步跟进建议。",
"tags": ["线索分级", "异议判断", "下一步建议"],
"systemPrompt": "你是报价策略助手……",
"permissions": [
{ "code": "knowledge.query", "label": "查询知识库", "granted": true },
{ "code": "conversation.read_current", "label": "读取当前会话", "granted": true },
{ "code": "message.send", "label": "直接发送消息", "granted": false }
],
"knowledge": [
{ "id": "kb_refund", "title": "售后退款政策", "version": "v1.7", "enabled": true }
],
"metrics": {
"recentCallCount": 16,
"successRate": 0.984,
"averageLatencyMs": 1200,
"knowledgeCount": 3
},
"updatedAt": "2026-06-16T10:20:00Z",
"revision": 4
}
}
```
### 8.6 权限和测试记录
- `GET /agents/{id}/permissions`:返回上述 `permissions``knowledge`
- `GET /agents/{id}/test-records?limit=20&cursor=...`
测试记录项:
```json
{
"id": "test_01J3...",
"scenario": "高意向询价",
"status": "passed",
"latencyMs": 1200,
"draftRevision": 3,
"createdAt": "2026-06-18T09:30:00Z",
"error": null
}
```
### 8.7 启停和删除
`PATCH /agents/{id}/status`
```json
{ "enabled": false, "expectedRevision": 4 }
```
`DELETE /agents/{id}`
```json
{
"confirmation": "客户意图识别",
"expectedRevision": 5
}
```
内置智能体可按产品规则返回 `403 BUILTIN_AGENT_READ_ONLY`;若允许删除,必须明确为隐藏/禁用而非物理删除。
---
## 9. 智能体测试会话与 WebSocket 流式回复
### 9.1 创建测试会话
`POST /agent-test-sessions`
请求:
```json
{
"agentId": "agent_intent",
"draftId": null,
"draftRevision": null,
"mode": "published"
}
```
编辑器沙箱传:
```json
{
"agentId": null,
"draftId": "agentdraft_01J3...",
"draftRevision": 3,
"mode": "draft"
}
```
响应:
```json
{
"data": {
"sessionId": "testsession_01J3...",
"state": "ready",
"channel": "agent-test:testsession_01J3...",
"maxFilesPerMessage": 5,
"maxFileSizeBytes": 20971520,
"acceptedExtensions": ["pdf", "docx", "ppt", "pptx", "txt", "md", "csv", "json"],
"expiresAt": "2026-07-21T10:00:00Z"
}
}
```
### 9.2 发送测试消息
使用 WebSocket command不把文件二进制放进 WS。附件必须先通过 `POST /files` 上传。
```json
{
"type": "agent.test.message.send",
"requestId": "client_req_8",
"payload": {
"sessionId": "testsession_01J3...",
"clientMessageId": "bce8e547-...",
"content": "客户预算为 10000 元,可以如何报价?",
"fileIds": ["file_01J3..."]
}
}
```
校验:`content``fileIds` 至少一个非空;内容最大 20000 字;附件最多 5 个;附件 purpose 必须为 `agent_test_attachment` 且解析完成。
服务端事件顺序:
1. `agent.test.message.accepted`
2. 零到多个 `agent.test.message.delta`
3. `agent.test.message.completed``agent.test.message.failed`
accepted
```json
{
"sessionId": "testsession_01J3...",
"messageId": "msg_01J3...",
"clientMessageId": "bce8e547-...",
"assistantMessageId": "msg_01J4...",
"acceptedAt": "2026-07-21T08:55:00Z"
}
```
delta
```json
{
"sessionId": "testsession_01J3...",
"assistantMessageId": "msg_01J4...",
"sequence": 7,
"delta": "建议先核对",
"accumulatedLength": 28
}
```
completed
```json
{
"sessionId": "testsession_01J3...",
"assistantMessageId": "msg_01J4...",
"content": "建议先核对客户需求……",
"finishReason": "stop",
"usage": { "inputTokens": 412, "outputTokens": 96 },
"latencyMs": 1320,
"testResult": {
"status": "passed",
"draftRevision": 3,
"warnings": []
},
"completedAt": "2026-07-21T08:55:01.320Z"
}
```
failed
```json
{
"sessionId": "testsession_01J3...",
"assistantMessageId": "msg_01J4...",
"code": "MODEL_TIMEOUT",
"message": "模型在 8000ms 内未返回",
"retryable": true
}
```
### 9.3 历史与取消
- `GET /agent-test-sessions/{sessionId}/messages?limit=100&cursor=...`:恢复弹窗后的对话。
- WS command `agent.test.generation.cancel``{ sessionId, assistantMessageId }`
- `DELETE /agent-test-sessions/{sessionId}`:结束会话并清理未引用测试附件。
消息对象:
```json
{
"id": "msg_01J3...",
"role": "user",
"content": "客户预算为 10000 元",
"files": [
{ "id": "file_01J3...", "fileName": "budget.txt", "sizeBytes": 126 }
],
"state": "completed",
"createdAt": "2026-07-21T08:55:00Z"
}
```
---
## 10. 蒸馏任务接口
### 10.1 当前任务和员工列表
- `GET /distill-jobs?state=active&limit=10`:返回 `DistillJobSummary[]`,用于“当前任务”。
- `GET /employees?query=Aileen&enabled=all&limit=20&cursor=...`:返回 `EmployeeSummary[]`
### 10.2 创建蒸馏草稿
`POST /distill-drafts`
请求:
```json
{
"name": "销售冠军 Aileen 话术蒸馏",
"minimumValidConversations": 50
}
```
响应:
```json
{
"data": {
"id": "distilldraft_01J3...",
"name": "销售冠军 Aileen 话术蒸馏",
"fileIds": [],
"minimumValidConversations": 50,
"state": "draft",
"revision": 1
}
}
```
### 10.3 绑定上传文件
`POST /distill-drafts/{draftId}/files`
```json
{
"fileIds": ["file_01J3..."],
"expectedRevision": 1
}
```
响应:返回草稿、文件列表和有效内容统计:
```json
{
"data": {
"draftId": "distilldraft_01J3...",
"files": [{ "id": "file_01J3...", "fileName": "champion-scripts.docx", "sizeBytes": 245760, "kind": "text", "status": "ready", "parseStatus": "completed" }],
"statistics": {
"validConversationCount": 1286,
"scriptCount": 38,
"invalidRecordCount": 7
},
"revision": 2
}
}
```
当前来源页只保留文件上传;不设计导入微信聊天、选择已有案例或日期范围字段。
### 10.4 保存草稿
`PATCH /distill-drafts/{draftId}`
```json
{
"name": "销售冠军 Aileen 话术蒸馏",
"minimumValidConversations": 50,
"expectedRevision": 2
}
```
响应:返回完整草稿。
### 10.5 开始能力提取
`POST /distill-drafts/{draftId}/start`
```json
{ "expectedRevision": 3 }
```
响应 `202 Accepted`:返回 `DistillJobSummary`。文件未解析完成、有效记录不足或草稿为空时返回 `400 VALIDATION_ERROR`
任务进度通过 `distill.progress.updated` 推送:
```json
{
"jobId": "distill_01J3...",
"state": "extracting",
"stage": "strategy_extraction",
"stageLabel": "策略提取",
"progress": 0.72,
"processedCount": 1286,
"totalCount": 1286,
"stageCurrent": 13,
"stageTotal": 18,
"estimatedRemainingSeconds": 180,
"recognizedCapabilities": ["高意向识别", "异议拆解", "温和推进", "优惠边界", "长期跟进"],
"updatedAt": "2026-07-21T09:05:00Z",
"revision": 9
}
```
### 10.6 查询任务详情
`GET /distill-jobs/{jobId}`
响应:
```json
{
"data": {
"summary": { "id": "distill_01J3...", "name": "销售冠军 Aileen 话术蒸馏", "state": "extracting", "stage": "strategy_extraction", "progress": 0.72, "estimatedRemainingSeconds": 180, "sourceCount": 1, "successCount": 248, "failedCount": 7, "createdAt": "2026-07-21T08:30:15.238Z", "updatedAt": "2026-07-21T08:34:10Z", "revision": 9 },
"stages": [
{ "code": "data_cleaning", "label": "数据清洗", "state": "completed", "current": 1286, "total": 1286 },
{ "code": "scene_clustering", "label": "场景聚类", "state": "completed", "current": 18, "total": 18 },
{ "code": "strategy_extraction", "label": "策略提取", "state": "running", "current": 13, "total": 18 },
{ "code": "boundary_induction", "label": "边界归纳", "state": "waiting", "current": 0, "total": null },
{ "code": "counterexample_generation", "label": "反例生成", "state": "waiting", "current": 0, "total": null }
],
"recognizedCapabilities": ["高意向识别", "异议拆解", "温和推进", "优惠边界", "长期跟进"],
"error": null
}
}
```
页面已移除失败样本入口,因此不提供面向 UI 的失败样本明细接口;`failedCount` 只作为任务健康摘要和日志字段。
### 10.7 效果评估
`GET /distill-jobs/{jobId}/evaluation`
响应:
```json
{
"data": {
"jobId": "distill_01J3...",
"state": "completed",
"overallScore": 92,
"publishThreshold": 85,
"publishable": true,
"metrics": {
"sceneHitRate": 0.94,
"refusalAccuracy": 0.91,
"overreachRate": 0.003,
"consistency": 0.89
},
"cases": [
{ "id": "case_1", "name": "高意向询价", "result": "建议合理", "passed": true },
{ "id": "case_2", "name": "超边界优惠", "result": "已拒绝越权", "passed": true },
{ "id": "case_3", "name": "售后退款混合场景", "result": "回答包含不确定信息", "passed": false }
],
"completedAt": "2026-07-21T09:10:00Z"
}
}
```
`POST /distill-jobs/{jobId}/evaluation-runs`:重新评估,响应 `202`,进度通过 WS 推送。
`POST /distill-jobs/{jobId}/evaluation-exports`:生成报告,响应结构同日志导出。
### 10.8 发布员工
`POST /distill-jobs/{jobId}/publish`
请求:
```json
{
"name": "销售冠军 Aileen",
"domain": "private_sales",
"agentIds": ["agent_intent", "agent_quote"],
"knowledgeIds": ["kb_price", "kb_discount", "kb_refund"],
"enableAfterPublish": true,
"expectedRevision": 11
}
```
- `domain`: `private_sales | b2b_consulting | after_sales`
- 至少一个授权能力;知识授权可为空;只允许授权已启用资源。
响应 `201 Created`
```json
{
"data": {
"employee": { "id": "employee_1", "name": "销售冠军 Aileen", "version": "v1.0", "enabled": true, "traits": ["高意向识别", "异议拆解"], "domain": "private_sales", "score": 92, "weeklyCallCount": 0, "updatedAt": "2026-07-21T09:15:00Z", "revision": 1 },
"sourceJobId": "distill_01J3...",
"publishedAt": "2026-07-21T09:15:00Z"
}
}
```
### 10.9 取消任务
`POST /distill-jobs/{jobId}/cancel`
```json
{ "expectedRevision": 9, "reason": "operator_request" }
```
`queued | extracting | evaluating` 可取消。响应返回 `state: "cancelled"` 的任务摘要。
---
## 11. 员工详情接口
### 11.1 详情
`GET /employees/{employeeId}`
响应:
```json
{
"data": {
"id": "employee_1",
"name": "销售冠军 Aileen",
"version": "v3.2",
"enabled": true,
"domain": "private_sales",
"traits": ["高意向逼单", "异议拆解", "温和推进"],
"profile": {
"applicableScenario": "已有明确需求、处于报价或决策阶段的私域客户",
"prohibitedBehavior": "承诺未授权折扣、绕过人工确认直接发送敏感信息"
},
"metrics": {
"overallScore": 96,
"weeklyCallCount": 28,
"adoptionRate": 0.82,
"exceptionCount": 1
},
"agentBindings": [
{ "id": "agent_intent", "name": "客户意图识别", "granted": true }
],
"knowledgeBindings": [
{ "id": "kb_price", "title": "产品价格表", "bound": true }
],
"updatedAt": "2026-06-18T09:00:00Z",
"revision": 3
}
}
```
### 11.2 启停、画像和授权
`PATCH /employees/{id}/status`
```json
{ "enabled": false, "expectedRevision": 3 }
```
`PATCH /employees/{id}/profile`
```json
{
"name": "销售冠军 Aileen",
"domain": "private_sales",
"traits": ["高意向逼单", "异议拆解", "温和推进"],
"applicableScenario": "已有明确需求、处于报价或决策阶段的私域客户",
"prohibitedBehavior": "承诺未授权折扣",
"expectedRevision": 4
}
```
`PUT /employees/{id}/bindings`
```json
{
"agentIds": ["agent_intent", "agent_quote"],
"knowledgeIds": ["kb_price", "kb_discount", "kb_sales_champion"],
"expectedRevision": 5
}
```
均返回最新员工详情。
### 11.3 详情页各 Tab
- `GET /employees/{id}/evaluation`:结构同蒸馏评估摘要。
- `GET /employees/{id}/versions?limit=20&cursor=...`
- `GET /employees/{id}/calls?limit=50&cursor=...`
调用记录项:
```json
{
"id": "call_01J3...",
"time": "2026-07-21T04:26:00Z",
"scenario": "高意向询价",
"result": "suggestion_adopted",
"resultLabel": "建议已采纳",
"requestId": "req_01J3..."
}
```
### 11.4 重新蒸馏和复制
`POST /employees/{id}/redistill`
```json
{ "baseVersion": "v3.2" }
```
响应 `201`:返回预填的 `distillDraftId`
`POST /employees/{id}/clone`
```json
{ "name": "销售冠军 Aileen 副本", "copyBindings": true }
```
响应 `201`:返回新员工详情,默认 `enabled: false`
---
## 12. 设置接口
### 12.1 获取全部配置
`GET /settings`
响应:
```json
{
"data": {
"revision": 12,
"model": {
"provider": "volcengine",
"endpoint": "https://ark.cn-beijing.volces.com/api/v3",
"modelName": "doubao-pro-32k",
"requestTimeoutMs": 8000,
"keyConfigured": true,
"keyUpdatedAt": "2026-07-20T09:00:00Z"
},
"prompt": {
"fixedPrompt": "你是本地微信分身助手,需要遵守托管策略、回复边界与用户授权。",
"customPrompt": "保持友好、简洁、可信赖。优先调用知识库和已授权员工能力。",
"customPromptMaxLength": 4000
},
"replyRules": {
"quietHoursEnabled": false,
"quietHours": { "start": "22:00", "end": "08:00", "timezone": "Asia/Shanghai" },
"autoAddFriend": false,
"proactiveContact": false,
"distillationEnabled": true
},
"featureGates": {
"agentsEnabled": true,
"employeesEnabled": true,
"knowledgeEnabled": true
},
"knowledgeSearch": {
"algorithm": "sqlite_fts5_bm25",
"maxResults": 5,
"fields": ["title", "tags", "fullText"],
"activeKnowledgeIds": ["kb_sales_champion", "kb_refund"]
},
"hosting": {
"enabled": false,
"localMessageServiceEnabled": true,
"confirmBeforeSend": true,
"abnormalPauseEnabled": true,
"consecutiveErrorLimit": 3,
"messageWaitSeconds": 30
},
"updatedAt": "2026-07-21T08:00:00Z"
}
}
```
### 12.2 保存配置
`PUT /settings`
请求传完整可编辑配置,但不传 `fixedPrompt``keyConfigured``keyUpdatedAt`
```json
{
"expectedRevision": 12,
"model": {
"provider": "volcengine",
"endpoint": "https://ark.cn-beijing.volces.com/api/v3",
"modelName": "doubao-pro-32k",
"requestTimeoutMs": 8000
},
"prompt": {
"customPrompt": "保持友好、简洁、可信赖。"
},
"replyRules": {
"quietHoursEnabled": false,
"quietHours": { "start": "22:00", "end": "08:00", "timezone": "Asia/Shanghai" },
"autoAddFriend": false,
"proactiveContact": false,
"distillationEnabled": true
},
"featureGates": {
"agentsEnabled": true,
"employeesEnabled": true,
"knowledgeEnabled": true
},
"knowledgeSearch": {
"maxResults": 5,
"fields": ["title", "tags", "fullText"],
"activeKnowledgeIds": ["kb_sales_champion", "kb_refund"]
},
"hosting": {
"enabled": false,
"localMessageServiceEnabled": true,
"confirmBeforeSend": true,
"abnormalPauseEnabled": true,
"consecutiveErrorLimit": 3,
"messageWaitSeconds": 30
}
}
```
响应:完整最新配置。前端“恢复”必须恢复最近一次 GET/PUT 成功的数据,不能只清除 dirty 状态。
### 12.3 API Key
`PUT /settings/model-api-key`
```json
{ "apiKey": "真实密钥" }
```
响应:
```json
{
"data": {
"keyConfigured": true,
"keyUpdatedAt": "2026-07-21T09:30:00Z"
}
}
```
`DELETE /settings/model-api-key`:返回 `204`。密钥只在此 PUT 请求中出现一次。
### 12.4 测试模型连接
`POST /settings/model-connection-tests`
使用当前未保存表单值:
```json
{
"provider": "volcengine",
"endpoint": "https://ark.cn-beijing.volces.com/api/v3",
"modelName": "doubao-pro-32k",
"requestTimeoutMs": 8000,
"useStoredApiKey": true
}
```
响应:
```json
{
"data": {
"success": true,
"latencyMs": 324,
"provider": "volcengine",
"modelName": "doubao-pro-32k",
"checkedAt": "2026-07-21T09:31:00Z"
}
}
```
失败时仍返回对应 HTTP 错误和明确 code`MODEL_AUTH_FAILED | MODEL_NOT_FOUND | MODEL_TIMEOUT | MODEL_NETWORK_UNREACHABLE`
### 12.5 组合提示词预览
`POST /settings/prompt-previews`
```json
{ "customPrompt": "保持友好、简洁、可信赖。" }
```
响应:
```json
{
"data": {
"fixedPrompt": "你是本地微信分身助手……",
"customPrompt": "保持友好、简洁、可信赖。",
"combinedPrompt": "你是本地微信分身助手……\n\n保持友好、简洁、可信赖。",
"characterCount": 52
}
}
```
### 12.6 黑白名单
`GET /contact-lists?type=allow|block&query=张三`
响应项:
```json
{
"id": "contact_rule_01J3...",
"type": "allow",
"displayName": "张三",
"headerFingerprint": "sha256:...",
"source": "manual",
"createdAt": "2026-07-21T09:32:00Z"
}
```
`POST /contact-lists`
```json
{
"type": "allow",
"displayName": "张三",
"headerFingerprint": "sha256:...",
"source": "manual"
}
```
`DELETE /contact-lists/{ruleId}`:返回 `204`
仅名称不足以稳定识别微信会话;生产数据必须包含当前会话头生成的 `headerFingerprint`
### 12.7 管理页逐项开关
以下接口复用资源状态,不额外创建第二套启停数据:
- 智能体:`PATCH /agents/{id}/status`
- 员工:`PATCH /employees/{id}/status`
- 知识库:`PATCH /knowledge-bases/{id}/status`
- 三个总开关:`PUT /settings` 中的 `featureGates`
---
## 13. WebSocket 通用协议
### 13.1 连接和订阅
连接成功事件:
```json
{
"eventId": "evt_01J3...",
"type": "connection.ready",
"occurredAt": "2026-07-21T09:35:00Z",
"payload": {
"clientId": "desktop-main-7cb8",
"heartbeatSeconds": 20,
"resumeAccepted": true
}
}
```
客户端订阅:
```json
{
"type": "subscribe",
"requestId": "client_req_1",
"payload": {
"channels": ["engine", "logs", "distill:distill_01J3", "files:file_01J3"]
}
}
```
服务端确认:
```json
{
"eventId": "evt_01J4...",
"type": "ack",
"occurredAt": "2026-07-21T09:35:00.010Z",
"requestId": "client_req_1",
"payload": { "acceptedChannels": ["engine", "logs", "distill:distill_01J3", "files:file_01J3"] }
}
```
### 13.2 服务端事件包络
```json
{
"eventId": "evt_01J5...",
"type": "engine.state.changed",
"occurredAt": "2026-07-21T09:35:01Z",
"requestId": "req_01J3...",
"resource": { "type": "engine", "id": "local" },
"payload": {}
}
```
### 13.3 事件清单
| type | channel | payload |
|---|---|---|
| `engine.state.changed` | `engine` | 完整 `EngineState`,不是局部 patch |
| `engine.startup_check.updated` | `engine` | `runId``state`、完整 `checks[]` |
| `engine.annotation.changed` | `engine` | `annotationComplete``revision``updatedAt` |
| `log.appended` | `logs` | 单条完整 `LogEvent` |
| `file.processing.updated` | `files:{fileId}` | 文件处理进度对象 |
| `knowledge.changed` | `knowledge:{id}` | `KnowledgeSummary` |
| `agent.changed` | `agent:{id}` | `AgentSummary` |
| `agent.test.message.accepted` | `agent-test:{sessionId}` | 消息受理对象 |
| `agent.test.message.delta` | `agent-test:{sessionId}` | token 增量对象 |
| `agent.test.message.completed` | `agent-test:{sessionId}` | 完成对象 |
| `agent.test.message.failed` | `agent-test:{sessionId}` | 错误对象 |
| `distill.progress.updated` | `distill:{jobId}` | 完整进度对象 |
| `distill.evaluation.updated` | `distill:{jobId}` | 评估状态和结果摘要 |
| `employee.changed` | `employee:{id}` | `EmployeeSummary` |
| `settings.changed` | `settings` | `revision``changedSections[]`;前端收到后重新 GET |
| `export.completed` | `exports:{exportId}` | `exportId``status``fileId``error` |
### 13.4 心跳、重连和顺序
- 服务端每 20 秒发送 `ping`,客户端 10 秒内返回 `pong`
- 事件在单连接内按 `eventId` 顺序发送;业务顺序以资源 `revision` 为准。
- 前端忽略 `revision` 小于当前状态的迟到事件。
- 断线后指数退避1s、2s、4s、8s最大 15s。
- 重连时用最后一个已处理 `eventId` 申请新 ticket事件缓存建议保留 10 分钟。
- 无法续传时 `connection.ready.payload.resumeAccepted=false`,前端必须重新 GET 当前页面资源。
- WS 只负责通知和流式增量;创建、更新、删除仍由 HTTP 完成。
---
## 14. Tauri 原生 IPC不得改成 HTTP/WS 的能力
这些接口仍是 API 契约,但调用形式只允许 `invoke(command, payload)`。标注调试全程在本机 React/Tauri/Rust 内完成,直接操作本机窗口、截图和应用生命周期。
本地边界:
1. `src/pages/AnnotationPage.jsx``src/overlay.js` 不调用业务 HTTP 或 WebSocket。
2. 截图字节、截图路径、窗口句柄、窗口元数据、标注区域和未保存会话状态不得发送给 Go 后端服务。
3. Go 后端不提供标注调试接口,也不读取标注调试状态;保存和加载只通过 Tauri/Rust 本地存储。
4. Tauri event 只在本机 WebView 窗口之间传递,不桥接到业务 WebSocket。
### 14.1 标注区域对象
```json
{
"id": "region_01J3...",
"name": "聊天内容",
"type": "chat_content",
"description": "微信当前会话消息区",
"bbox_image": [120, 180, 860, 1240],
"bbox_source": [80, 120, 573.333, 826.667],
"bbox_screen": [420, 280, 1160, 1340],
"scaleFactor": 1.5
}
```
坐标统一为 XYXY`[x1, y1, x2, y2]`。生产必需类型:
- `conversation_header`
- `chat_content`
- `input_box`
- `send_button`
`custom` 只能附加,不能代替必需类型。
### 14.2 IPC 命令
| command | 请求 | 返回 |
|---|---|---|
| `enter_window_select_mode` | `{}` | `{ mode: "window_select" }` |
| `capture_screen` | `{}` | `CaptureResult` |
| `read_screenshot_bytes` | `{ path }` | `number[]` 或二进制 buffer |
| `load_regions` | `{}` | `AnnotationConfig | null` |
| `save_regions` | `{ annotation: AnnotationConfig }` | `{ path, revision, updatedAt }` |
| `start_vision_stream` | `{}` | `{ state: "running" }` |
| `stop_vision_stream` | `{}` | `{ state: "stopped" }` |
| `start_agent` | `{}` | `{ state: "running" }` |
| `stop_agent` | `{}` | `{ state: "stopped" }` |
| `open_popup_window` | `{ route }` | `{ label }` |
| `close_current_window` | `{}` | `null` |
| `exit_application` | `{}` | `null` |
`CaptureResult`
```json
{
"screenshotPath": "受限应用数据目录中的路径",
"screenshotWidth": 1440,
"screenshotHeight": 900,
"scaleFactor": 1.5,
"source": {
"id": "win-window:12345",
"kind": "window",
"label": "WeChat",
"appName": "WeChat.exe",
"title": "微信",
"pid": 4312,
"x": 200,
"y": 100,
"width": 960,
"height": 600,
"scaleFactor": 1.5
},
"screenListMs": 12,
"captureMs": 40,
"saveMs": 8,
"totalMs": 60
}
```
`AnnotationConfig`
```json
{
"app": "wechat",
"screenshotPath": "受限应用数据目录中的路径",
"screenshotWidth": 1440,
"screenshotHeight": 900,
"scaleFactor": 1.5,
"source": {},
"regions": [],
"createdAt": "2026-07-21T09:40:00Z",
"updatedAt": "2026-07-21T09:42:00Z",
"revision": 3
}
```
原生保存成功后由 Tauri event 发送:
```json
{
"event": "annotation-completion-changed",
"payload": true
}
```
引擎状态变化沿用:
```json
{
"event": "engine-state-changed",
"payload": { "enabled": true }
}
```
标注调试状态只允许 Tauri/Rust 本地模块读取。HTTP 业务服务和 Go 后端不得读取、同步或转发截图、窗口信息及标注区域。
---
## 15. 页面到接口映射
| 页面/窗口 | 源文件 | 初始加载 | 用户操作 | 实时事件 |
|---|---|---|---|---|
| 微信分身 | `src/pages/ClonePage.jsx` | `/bootstrap``/engine/state` | 启动检查、启动/停止引擎、暂停自动发送;标注走 Tauri IPC | `engine.*``log.appended` |
| 运行日志 | `src/windows/EngineLogsWindow.jsx` | `GET /logs` | 导出日志;清空/暂停是前端视图操作 | `log.appended``export.completed` |
| 知识库列表 | `src/pages/KnowledgePage.jsx` | `GET /knowledge-bases` | 搜索、打开详情 | `knowledge.changed` 可选 |
| 新增/更新知识库 | `src/windows/KnowledgeWindows.jsx` | 创建/读取 knowledge draft | 上传文档、图片或视频;预览转换后的 TXT人工填写媒体描述移除、发布 | `file.processing.updated``knowledge.changed` |
| 知识库详情、预览、引用、版本 | `src/windows/KnowledgeWindows.jsx` | knowledge detail/files/references/versions | 预览、下载、启停、更新、删除 | `knowledge.changed`、文件处理事件 |
| 智能体市场 | `src/pages/SkillsPage.jsx` | `GET /agents` | 搜索、筛选、启停、打开详情 | `agent.changed` 可选 |
| 新增/更新智能体 | `src/windows/SkillWindow.jsx` | 创建/读取 agent draft | 基本信息、提示词、知识授权、测试、发布 | 测试会话事件 |
| 智能体详情 | `src/windows/SkillWindow.jsx` | `GET /agents/{id}` | 更新、测试、启停、删除 | `agent.changed` |
| 测试智能体 | `src/components/AgentChatPanel.jsx` | 创建 test session、加载 messages | 上传附件、发送、取消生成 | `agent.test.*` |
| 员工蒸馏主页 | `src/pages/DistillPage.jsx` | active jobs、employees | 搜索、取消任务、打开详情 | `distill.progress.updated` |
| 开始蒸馏 | `src/windows/DistillWindows.jsx` | 创建/读取 distill draft | 上传文件、开始提取、重新评估、导出、发布 | `file.processing.updated``distill.*` |
| 员工详情 | `src/windows/DistillWindows.jsx` | `GET /employees/{id}` | 启停、编辑画像、管理授权、重新蒸馏、复制 | `employee.changed` |
| 设置 | `src/windows/SettingsContent.jsx` | `GET /settings` | 保存/恢复、Key 更换/删除、连接测试、名单 CRUD | `settings.changed` |
| 标注调试页 | `src/pages/AnnotationPage.jsx` | Tauri `capture_screen/load_regions` | 画框、编辑、删除、保存 | Tauri annotation event |
| 原生透明标注层 | `src/overlay.js` | Tauri overlay state | 选窗、画框、编辑、删除、保存或取消 | Tauri annotation event |
---
## 16. 前端交互约束
1. 所有页面首次打开必须显示 loading失败显示真实错误和重试入口不得用 mock 数据补成功状态。
2. 按钮发起 mutation 后立即禁用,直到响应完成;重复点击依赖 `Idempotency-Key` 兜底。
3. 搜索输入建议 250ms debounce新的请求发出时取消旧请求避免旧结果覆盖新结果。
4. 详情窗口更新后关闭原窗口没有问题,但重新打开详情必须 GET 最新资源,不能依赖 `localStorage``BroadcastChannel` 作为业务事实源。
5. WS 事件只更新同 revision 或更新 revision发生缺号或冲突时重新 GET。
6. 文件选择后先展示本地文件名和上传进度;只有 `FileAsset.status=ready` 才显示“格式有效/可预览”。
7. 智能体测试期间禁用下一步、附件移除和重复发送;`completed` 后才标记测试通过。
8. 蒸馏后台运行时关闭窗口不取消任务;再次打开按 jobId 查询并续订频道。
9. 删除响应成功后从列表移除;`409 RESOURCE_IN_USE` 必须显示引用资源,不得假装删除成功。
10. 配置保存以服务端返回的完整对象替换本地表单基线;连接测试不自动保存配置。
---
## 17. 实施顺序
1. **基础层**HTTP client、错误模型、session token、SQLite repository、revision、WebSocket ticket/重连。
2. **设置与知识库**:消除 API Key、本地配置、知识列表和创建流程中的 mock/localStorage。
3. **文件中心**:上传、解析、预览、清理和处理进度事件。
4. **智能体**:草稿、授权、测试 WS 流式输出、发布和详情。
5. **蒸馏与员工**:后台任务、进度、评估、发布和详情子资源。
6. **引擎与日志**:将现有 Tauri/sidecar 状态映射成结构化 HTTP/WS 契约,同时保留标注为 Tauri IPC。
完成判定:页面展示字段均来自上述响应;所有可操作按钮有真实 mutation长任务和流式生成有可恢复事件上传、解析失败、并发冲突和断线重连都有明确结果。