323 lines
15 KiB
Markdown
323 lines
15 KiB
Markdown
# API 与编排接口设计
|
||
|
||
> 版本: v1.0 | 日期: 2026-07-30 | 状态: 初版
|
||
>
|
||
> 本文档定义 **Web 后端(FastAPI Orchestrator)的 REST API 端点、WebSocket 事件通道、编排调用链与部署拓扑**。是 `docs/design.md` §8(Web UI)与 `docs/agent-runtime-design.md` §3(编排能力)的接口级展开。
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
1. [定位与架构](#1-定位与架构)
|
||
2. [REST API 端点清单](#2-rest-api-端点清单)
|
||
3. [WebSocket 事件通道](#3-websocket-事件通道)
|
||
4. [编排调用链](#4-编排调用链)
|
||
5. [任务队列抽象](#5-任务队列抽象)
|
||
6. [部署拓扑](#6-部署拓扑)
|
||
7. [错误码约定](#7-错误码约定)
|
||
8. [与各文档的关系](#8-与各文档的关系)
|
||
|
||
---
|
||
|
||
## 1. 定位与架构
|
||
|
||
```
|
||
浏览器 (React UI)
|
||
│ REST API (JSON) + WebSocket (实时事件)
|
||
▼
|
||
FastAPI Orchestrator(单进程,默认)
|
||
│
|
||
├── SessionManager(会话状态机,见 agent-runtime §3)
|
||
├── Agent 调度(Parser / Impact / Writer / QA)
|
||
├── TaskQueue 抽象(默认 InMemory,可切 Redis/Valkey)
|
||
└── RagService 调用(规则检索)
|
||
```
|
||
|
||
**架构决策**(与用户确认):
|
||
- **编排形态**:抽象 `TaskQueue` 接口 + 双实现。默认 `InMemoryQueue`(开发/测试/演示零依赖);部署时可切换 `RedisQueue` / `ValkeyQueue`(Redis 协议兼容,`redis-py` 客户端通用)。
|
||
- **许可说明**:Redis 内部使用合法(RSALv2/SSPLv1 仅限制「提供 Redis 托管服务给第三方」);若需完全开源无限制,使用 Valkey(BSD-3),代码无需改动。
|
||
- 所有 API 返回 JSON;长任务(解析/影响调查/生成)采用「异步启动 + 轮询/推送」模式。
|
||
|
||
---
|
||
|
||
## 2. REST API 端点清单
|
||
|
||
### 2.1 会话管理
|
||
|
||
| Method | Path | 说明 | 请求体 | 响应 | 触发状态转移 |
|
||
|--------|------|------|--------|------|-------------|
|
||
| POST | `/api/sessions` | 创建会话 | `{user_id}` | `{session_id, status, locked_rule_version}` | `→ uploading` |
|
||
| GET | `/api/sessions/{id}` | 获取会话详情 | — | 会话对象(状态/步骤/进度摘要) | 只读 |
|
||
| DELETE | `/api/sessions/{id}` | 删除会话 | — | `{deleted: true}` | 终态清理 |
|
||
| GET | `/api/sessions` | 会话列表(按用户) | `?user_id=` | `[{session_id, status, updated_at}]` | 只读 |
|
||
|
||
### 2.2 文件上传
|
||
|
||
| Method | Path | 说明 | 请求体 | 响应 | 触发状态转移 |
|
||
|--------|------|------|--------|------|-------------|
|
||
| POST | `/api/sessions/{id}/files` | 上传文件(multipart) | `file` + `file_type`(requirements/template/rules/existing_system) | `{file_id, file_name, size}` | `uploading` 保持 |
|
||
| POST | `/api/sessions/{id}/start-parse` | 开始解析(全部文件上传完成后) | `{}` | `{task_id}`(解析异步执行) | `uploading → parsing` |
|
||
| GET | `/api/sessions/{id}/parse-result` | 获取解析结果 | — | `{structured_summary, sheets, template_sections}` | 只读 |
|
||
|
||
### 2.3 解析确认
|
||
|
||
| Method | Path | 说明 | 请求体 | 响应 | 触发状态转移 |
|
||
|--------|------|------|--------|------|-------------|
|
||
| POST | `/api/sessions/{id}/confirm-parse` | 确认解析结果 | `{sheet_fixes?, template_fixes?}` | `{ok: true}` | `awaiting_parse_confirm → impact_running` |
|
||
| POST | `/api/sessions/{id}/reparse` | 修正后重新解析 | `{sheet_overrides}` | `{task_id}` | `awaiting_parse_confirm → parsing` |
|
||
|
||
### 2.4 影响调查
|
||
|
||
| Method | Path | 说明 | 请求体 | 响应 | 触发状态转移 |
|
||
|--------|------|------|--------|------|-------------|
|
||
| POST | `/api/sessions/{id}/start-impact` | 启动影响调查 | `{}` | `{task_id}` | `impact_running` 保持 |
|
||
| GET | `/api/sessions/{id}/impact-result` | 获取影响调查结果 | — | ImpactReport JSON | 只读 |
|
||
| POST | `/api/sessions/{id}/impact-edits` | 逐条修正关联 | `{edits: [{type, id, action, data}]}` | `{ok: true, correction_history_id}` | `awaiting_impact_confirm` 保持 |
|
||
| POST | `/api/sessions/{id}/confirm-impact` | 确认影响调查 | `{}` | `{ok: true}` | `awaiting_impact_confirm → writing` |
|
||
| POST | `/api/sessions/{id}/reject-impact` | 打回(重推或回到解析) | `{target: "impact" \| "parse"}` | `{ok: true}` | `awaiting_impact_confirm → impact_running \| awaiting_parse_confirm` |
|
||
|
||
### 2.5 生成(Writer + QA)
|
||
|
||
| Method | Path | 说明 | 请求体 | 响应 | 触发状态转移 |
|
||
|--------|------|------|--------|------|-------------|
|
||
| POST | `/api/sessions/{id}/generate` | 启动章节生成 | `{}` | `{task_ids: [逐章]}` | `writing` 保持 |
|
||
| GET | `/api/sessions/{id}/generation-status` | 各章生成进度 | — | `[{chapter_id, status, duration_ms}]` | 只读 |
|
||
| GET | `/api/sessions/{id}/chapters/{chapter_id}` | 获取单章内容块 | — | ContentBlock JSON | 只读 |
|
||
| POST | `/api/sessions/{id}/regenerate-chapter` | 单章重生成 | `{chapter_id, reason}` | `{task_id}` | `writing` 保持 |
|
||
| POST | `/api/sessions/{id}/run-qa` | 启动 QA 校验 | `{}` | `{task_id}` | `writing → qa` |
|
||
| GET | `/api/sessions/{id}/qa-result` | QA 校验结果 | — | QAReport JSON | 只读 |
|
||
| POST | `/api/sessions/{id}/writer-fix` | QA 发现问题后反馈 Writer 重生成 | `{issues: [...]}` | `{task_ids}` | `qa → writing` |
|
||
| POST | `/api/sessions/{id}/rollback-to-impact` | 回退到影响调查(writing 阶段用户要求调整关联/不确定处) | `{}` | `{ok: true}` | `writing → awaiting_impact_confirm` |
|
||
|
||
### 2.6 结果与下载
|
||
|
||
| Method | Path | 说明 | 请求体 | 响应 | 触发状态转移 |
|
||
|--------|------|------|--------|------|-------------|
|
||
| GET | `/api/sessions/{id}/result/preview` | HTML 预览 | — | `{html}` | 只读 |
|
||
| GET | `/api/sessions/{id}/result/download` | 下载 docx | — | `application/vnd.openxmlformats...`(文件流) | 只读 |
|
||
| GET | `/api/sessions/{id}/result/qa-report` | 下载 QA 报告 | — | `application/json` | 只读 |
|
||
| GET | `/api/sessions/{id}/result/impact-report` | 下载影响调查书 | — | `application/json` | 只读 |
|
||
|
||
### 2.7 规则管理
|
||
|
||
| Method | Path | 说明 | 请求体 | 响应 | 触发状态转移 |
|
||
|--------|------|------|--------|------|-------------|
|
||
| GET | `/api/rules/versions` | 规则手册版本列表 | — | `[{version_id, active, created_at, doc_count}]` | 只读 |
|
||
| POST | `/api/rules/update` | 触发规则手册重建 | `{files, categories}` | `{version_id \| "no_change"}` | 不涉及会话 |
|
||
| POST | `/api/rules/rollback` | 回退到指定版本 | `{version_id}` | `{ok: true}` | 不涉及会话 |
|
||
| GET | `/api/rules/conflicts` | 待决策的规则冲突 | `?session_id=` | `[{conflict_id, topic, chunks}]` | 只读 |
|
||
| POST | `/api/rules/conflicts/{id}/resolve` | 冲突决策 | `{session_id, decision}` | `{ok: true}` | 继续该章生成 |
|
||
|
||
### 2.8 设置与状态
|
||
|
||
| Method | Path | 说明 | 请求体 | 响应 |
|
||
|--------|------|------|--------|------|
|
||
| GET | `/api/settings` | 获取配置(脱敏) | — | `{models, vector_store, limits}` |
|
||
| GET | `/api/health` | 健康检查 | — | `{status: "ok", version}` |
|
||
| POST | `/api/sessions/{id}/cancel` | 取消当前任务 | `{task_id?}` | `{ok: true}` |
|
||
| GET | `/api/sessions/{id}/logs` | 获取会话事件日志 | `?event_type=` | `[{event_type, ...}]` |
|
||
|
||
---
|
||
|
||
## 3. WebSocket 事件通道
|
||
|
||
### 3.1 端点
|
||
|
||
```
|
||
WS /api/ws/sessions/{id}
|
||
```
|
||
|
||
### 3.2 事件类型(服务端 → 客户端)
|
||
|
||
| 事件 | 载荷 | 时机 |
|
||
|------|------|------|
|
||
| `status_change` | `{from, to, at}` | 状态机转移时 |
|
||
| `progress` | `{task_id, chapter_id?, percent?, detail}` | 任务进度更新 |
|
||
| `tool_call` | `{tool, args_summary, status, duration_ms}` | 工具调用(runtime §5.7)|
|
||
| `llm_call` | `{model, prompt_version, status, duration_ms, input_tokens, output_tokens}` | LLM 调用(runtime §6.1)|
|
||
| `conflict_pending` | `{conflict_id, topic, chapter_id}` | 规则冲突待用户决策 |
|
||
| `qa_completed` | `{summary: {pass, fail, warnings}}` | QA 完成 |
|
||
| `error` | `{code, message, options: ["retry","skip","abort"]}` | 任务失败(异常 UX)|
|
||
| `done` | `{download_url}` | 全部完成 |
|
||
|
||
### 3.3 客户端 → 服务端
|
||
|
||
| 事件 | 载荷 | 说明 |
|
||
|------|------|------|
|
||
| `ping` | — | 心跳(保持连接)|
|
||
| `request_status` | — | 请求当前完整状态(断线重连时同步)|
|
||
|
||
---
|
||
|
||
## 4. 编排调用链
|
||
|
||
### 4.1 正常流程时序
|
||
|
||
```
|
||
客户端 Orchestrator Agent / 基础设施
|
||
│ POST /sessions │ │
|
||
│──────────────────────►│ 创建会话 → uploading │
|
||
│ POST /files │ │
|
||
│──────────────────────►│ │
|
||
│ POST /start-parse │ │
|
||
│──────────────────────►│ 投递解析任务 ────────────────►│ Parser
|
||
│◄───WS progress────────│ │
|
||
│◄───WS status_change───│ parsing → awaiting_parse_confirm
|
||
│ POST /confirm-parse │ │
|
||
│──────────────────────►│ → impact_running │
|
||
│ POST /start-impact │ 投递影响调查 ────────────────►│ Impact
|
||
│◄───WS status_change───│ → awaiting_impact_confirm │
|
||
│ POST /confirm-impact │ │
|
||
│──────────────────────►│ → writing │
|
||
│ POST /generate │ 投递逐章任务 ────────────────►│ Writer
|
||
│◄───WS progress(章)────│ │
|
||
│ POST /run-qa │ 投递 QA 任务 ────────────────►│ QA
|
||
│◄───WS qa_completed────│ → done(或 qa → writing 反馈)│
|
||
│ GET /result/download │ │
|
||
│──────────────────────►│ 返回 docx 文件流 │
|
||
```
|
||
|
||
### 4.2 Orchestrator 职责边界
|
||
|
||
- **不做**业务逻辑(解析/推理/生成/校验在各 Agent)
|
||
- **做**:状态机推进、任务调度、事件收集与推送、异常路由(重试/跳过/中断)、规则版本锁定、会话持久化
|
||
|
||
### 4.3 进程内调用 vs 队列
|
||
|
||
```
|
||
默认(InMemoryQueue):
|
||
Orchestrator 进程内 asyncio 任务池消费队列
|
||
状态与任务结果共享内存(FastAPI 进程内)
|
||
|
||
切换(RedisQueue/ValkeyQueue):
|
||
Orchestrator 投递 → Redis/Valkey Stream
|
||
worker 进程消费 → 执行 → 状态写回 SQLite / 事件回传
|
||
需额外部署 worker 容器(见 §6)
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 任务队列抽象
|
||
|
||
### 5.1 接口定义
|
||
|
||
```python
|
||
class TaskQueue(ABC):
|
||
"""统一任务队列抽象(内存 / Redis / Valkey 实现)"""
|
||
|
||
@abstractmethod
|
||
def enqueue(self, task: TaskSpec) -> TaskHandle: ...
|
||
# TaskSpec = {task_id, session_id, step, chapter_id?, payload, idempotency_key}
|
||
|
||
@abstractmethod
|
||
def poll(self, session_id: str) -> list[TaskHandle]: ...
|
||
|
||
@abstractmethod
|
||
def update_status(self, handle: TaskHandle, status: str, result: Any = None) -> None: ...
|
||
# status: pending | running | completed | failed
|
||
|
||
@abstractmethod
|
||
def get(self, task_id: str) -> TaskHandle | None: ...
|
||
|
||
@abstractmethod
|
||
def cancel(self, task_id: str) -> bool: ...
|
||
|
||
@abstractmethod
|
||
def close(self) -> None: ...
|
||
```
|
||
|
||
### 5.2 实现类
|
||
|
||
| 实现 | 依赖 | 使用场景 | 说明 |
|
||
|------|------|---------|------|
|
||
| `InMemoryQueue` | 无 | 开发 / 测试 / 演示(默认)| asyncio 任务池,进程内状态 |
|
||
| `RedisQueue` | redis-py | 生产部署 | Redis Streams(RSALv2,内部使用合法)|
|
||
| `ValkeyQueue` | redis-py(兼容)| 生产部署(零许可风险)| Valkey 兼容 Redis 协议,代码同 RedisQueue |
|
||
|
||
### 5.3 幂等去重
|
||
|
||
- 任务幂等键:`(session_id, step, chapter_id)`(runtime §7.3)
|
||
- 重复 enqueue 同一幂等键 → 已完成直接返回缓存结果;进行中则返回原 handle
|
||
- `InMemoryQueue` 与 `RedisQueue` 行为一致(单测以 MockAdapter 风格覆盖双实现)
|
||
|
||
---
|
||
|
||
## 6. 部署拓扑
|
||
|
||
### 6.1 默认(单容器,InMemoryQueue)
|
||
|
||
```
|
||
docker-compose.yml(最小):
|
||
services:
|
||
api:
|
||
build: .
|
||
ports: ["8000:8000"]
|
||
env_file: .env
|
||
volumes:
|
||
- ./data:/data # 用户数据 + 规则手册
|
||
- ./config:/config # inference.yaml / rag.yaml
|
||
command: uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||
```
|
||
|
||
### 6.2 生产(Redis/Valkey 可选)
|
||
|
||
```
|
||
docker-compose.yml(扩展):
|
||
services:
|
||
api: # FastAPI 编排 + REST/WS
|
||
...
|
||
command: uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||
worker: # 任务消费(仅切换队列时启用)
|
||
build: .
|
||
command: python -m app.worker
|
||
depends_on: [queue]
|
||
queue: # Redis 或 Valkey 二选一
|
||
image: valkey/valkey:8 # 零许可风险(或 redis:7,内部使用合法)
|
||
volumes: ["queue_data:/data"]
|
||
```
|
||
|
||
### 6.3 目录结构
|
||
|
||
```
|
||
/data/
|
||
├── users/{user_id}/ # 用户隔离(web-ui-design §4.3)
|
||
│ ├── uploads/
|
||
│ └── outputs/
|
||
├── shared/
|
||
│ ├── rules-handbook/ # 规则手册(RAG 层)
|
||
│ │ ├── chroma/ # Chroma 持久化
|
||
│ │ ├── manifest.json
|
||
│ │ └── v1/ v2/ ... # 版本目录
|
||
│ └── templates/ # 公共模板
|
||
└── db/ # SQLite(sessions/events/snapshots)
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 错误码约定
|
||
|
||
| 错误码 | HTTP | 含义 | 用户选项 | 来源 |
|
||
|--------|------|------|---------|------|
|
||
| `FILE_TYPE_INVALID` | 400 | 文件类型不支持 | 更换文件 | — |
|
||
| `FILE_TOO_LARGE` | 400 | 超过 100MB 限制 | 压缩/分割 | — |
|
||
| `STATE_TRANSITION_INVALID` | 409 | 非法状态转移(如 uploading 直接 generate)| 提示正确流程 | — |
|
||
| `LLM_TIMEOUT` | 502 | LLM 调用超时 | retry / skip / abort | exceptions.LLMTimeoutError |
|
||
| `LLM_NOT_CONFIGURED` | 503 | LLM Key 未配置 | 配置 Key | exceptions.LLMNotConfiguredError |
|
||
| `LLM_PARSE_ERROR` | 502 | 结构化输出解析失败 | retry | exceptions.LLMResponseError |
|
||
| `EMBEDDING_FAILED` | 503 | Embedding 服务故障(降级为 BM25)| 继续(降级提示)| — |
|
||
| `RULES_HANDBOOK_MISSING` | 404 | 规则手册不存在 | 上传规则文档 | — |
|
||
| `CHAPTER_NOT_FOUND` | 404 | 章节不存在 | — | — |
|
||
| `CONFLICT_PENDING` | 409 | 规则冲突待用户决策 | 决策后继续 | — |
|
||
| `INTERNAL_ERROR` | 500 | 未知错误 | 重试/联系支持 | — |
|
||
|
||
---
|
||
|
||
## 8. 与各文档的关系
|
||
|
||
| 文档 | 关系 |
|
||
|------|------|
|
||
| `docs/design.md` §8 | Web UI 页面设计;本文档为后端 API 的接口级展开 |
|
||
| `docs/agent-runtime-design.md` §3 | 状态机与任务队列抽象;本文档定义 REST/WS 入口 |
|
||
| `docs/web-ui-design.md` | 前端各页面调用本文档的端点 |
|
||
| `docs/config-design.md` | 部署配置(env / yaml / docker-compose) |
|