From bb8091f2e68709656a3ea1661d5c81d8ca3757b2 Mon Sep 17 00:00:00 2001 From: KiriAky 107 Date: Mon, 31 Aug 2026 20:26:53 +0800 Subject: [PATCH] =?UTF-8?q?docs(api):=20=E8=A7=84=E5=88=92=E7=AC=AC?= =?UTF-8?q?=E4=BA=8C=E9=98=B6=E6=AE=B5=E7=BB=9F=E4=B8=80=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=A5=91=E7=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- docs/AI笔记软件技术栈说明-团队版-v2.3.md | 2 + docs/后端接口契约-开发版.md | 6 +- docs/第二阶段接口契约-开发版.md | 1424 ++++++++++++++++++++++ 4 files changed, 1432 insertions(+), 3 deletions(-) create mode 100644 docs/第二阶段接口契约-开发版.md diff --git a/README.md b/README.md index e615c0c..30fc74e 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,7 @@ pnpm test | [第二阶段分工表](docs/第二阶段团队分工表.md) | 第二阶段人员职责、任务顺序、协作关系与验收项 | | [第一阶段测试验证操作手册](docs/第一阶段测试验证操作手册.md) | 自动化测试、接口主链路、前端人工验收与记录模板 | | [后端接口契约](docs/后端接口契约-开发版.md) | HTTP/SSE 接口、错误和当前实现状态 | +| [第二阶段接口契约](docs/第二阶段接口契约-开发版.md) | 第二阶段公共 DTO、计划接口、SSE、错误码与联调顺序 | | [AI Core 与 Agent Core](docs/AI-Core与Agent-Core开发说明.md) | Provider、Agent、Tool、Permission 与 Extension Core | | [Knowledge 与 Retrieval Core](docs/Knowledge与Retrieval-Core开发说明.md) | Block、索引、混合检索和 Citation | | [模型提供商与模型发现](docs/模型提供商与模型发现开发说明.md) | Provider 预设、模型发现和凭据边界 | @@ -152,6 +153,6 @@ pnpm test - API 默认监听 `127.0.0.1:8000`,前端默认监听 `127.0.0.1:5173`。 - 后端附件目录默认是 `backend/data/attachments`,可通过 `APP_ATTACHMENTS_PATH` 覆盖;该目录由桌面 Host 管理。 - 跨模块接口发生变化时,需要同步更新前后端类型和 `docs` 中的接口说明。 -- 当前前后端接口清单见 `docs/后端接口契约-开发版.md`,OpenAPI 以 `/openapi.json` 为准。 +- 当前已实现接口见 `docs/后端接口契约-开发版.md`,第二阶段规划接口见 `docs/第二阶段接口契约-开发版.md`;已实现能力以 `/openapi.json` 为准。 - 前端页面、交互、状态管理和第一阶段验收要求见 `docs/前端页面需求说明-开发版.md`。 - 分支、提交、Pull Request、Review 和冲突处理规范见 `docs/Git使用细则-团队开发版.md`。 diff --git a/docs/AI笔记软件技术栈说明-团队版-v2.3.md b/docs/AI笔记软件技术栈说明-团队版-v2.3.md index 2e7652a..9cd8bdb 100644 --- a/docs/AI笔记软件技术栈说明-团队版-v2.3.md +++ b/docs/AI笔记软件技术栈说明-团队版-v2.3.md @@ -1859,6 +1859,8 @@ POST /api/index/rebuild GET /health ``` +第一阶段已实现路径和第二阶段冻结草案分别见 `后端接口契约-开发版.md` 与 `第二阶段接口契约-开发版.md`。规划接口完成前不得在前端假定其可用;完成后以 OpenAPI、Pydantic Contract 和 TypeScript Wire DTO 的一致结果为准。 + HTTP 返回统一错误结构: ```json diff --git a/docs/后端接口契约-开发版.md b/docs/后端接口契约-开发版.md index f35d76f..9c45ded 100644 --- a/docs/后端接口契约-开发版.md +++ b/docs/后端接口契约-开发版.md @@ -1,6 +1,6 @@ # 后端接口契约(开发版) -> 更新日期:2026-08-30。本文档记录当前前后端联调使用的已实现接口;机器可读字段、校验规则和响应模型以 FastAPI 运行时生成的 OpenAPI 为准。 +> 更新日期:2026-08-31。本文档记录当前前后端联调使用的已实现接口;机器可读字段、校验规则和响应模型以 FastAPI 运行时生成的 OpenAPI 为准。第二阶段尚未实现的规划接口见 `第二阶段接口契约-开发版.md`,不要将规划路径视为当前服务能力。 ## 契约入口 @@ -159,7 +159,7 @@ RunCancelled ## 当前实现状态 -更新至 2026-08-30:后端 71 项回归测试通过。 +更新至 2026-08-31:后端 71 项回归测试通过。 - Chat、Agent Run、Agent Events、Tool 列表、Provider 配置生命周期、模型列表和连接测试已经接入 AI Core。 - Provider Adapter 当前包含 Mock、真正增量 SSE 的 OpenAI-Compatible Chat Completions,以及 Ollama JSONL Streaming。 @@ -168,3 +168,5 @@ RunCancelled - Plugin 启用前必须通过权限接口记录授权,未知权限默认拒绝。 - Attachment Tool 读取 Host 管理的 `attachments` 目录;音频接口读取 Host 生成的转写文本,真实本地语音模型在第二阶段接入。 - 接入业务模块时保持当前路径和 Contract,不在 Router 中直接实现数据库、Provider 或 Agent 逻辑。 + +第二阶段开发保持本文件中已有路径兼容,并按 `第二阶段接口契约-开发版.md` 增加子资源、可选字段和事件。接口完成后先更新 OpenAPI 与本文件,再将第二阶段文档中的状态改为已实现。 diff --git a/docs/第二阶段接口契约-开发版.md b/docs/第二阶段接口契约-开发版.md new file mode 100644 index 0000000..bccd59d --- /dev/null +++ b/docs/第二阶段接口契约-开发版.md @@ -0,0 +1,1424 @@ +# 第二阶段接口契约与开发规划 + +> 文档状态:接口冻结草案 +> 更新日期:2026-08-31 +> 依据:`第二阶段团队分工表.md`、`AI笔记软件技术栈说明-团队版-v2.3.md`、`后端接口契约-开发版.md` + +本文统一第二阶段新增能力的 HTTP、SSE、前端 Service、桌面 Host 和内部模块接口。文中标记为“计划新增”的路径尚未实现,不能据此判断当前服务已经支持;实现完成后以 FastAPI `/openapi.json`、TypeScript Wire DTO 和自动化测试共同作为最终依据。 + +人员分工和任务优先级不在本文重复维护,以第二阶段分工表为准。 + +--- + +## 1. 规划原则 + +1. 第一阶段已经发布的路径和字段保持兼容,第二阶段优先增加可选字段、子资源和新事件。 +2. 前端只消费项目 Contract,不解析 Plugin Manifest、MCP 原始消息或模型厂商响应。 +3. Agent、Benchmark 和 Trace 共用同一运行事件,不建立测试专用的旁路执行协议。 +4. 长任务统一建模为 Job,创建接口快速返回,进度通过查询或 SSE 获取。 +5. 文件、附件、插件包和 Dataset 均使用受控 ID;HTTP 请求不接受任意本地绝对路径。 +6. Secret 只经专用写入接口提交,普通读取接口只返回是否配置和引用 ID。 +7. Mermaid、函数图像和导出器共享静态渲染 Contract,不从 Vue 组件 DOM 抓取结果。 +8. 所有第三方协议在 Adapter 边界转换,错误进入前端前映射为项目错误码。 + +## 2. 状态说明 + +| 标记 | 含义 | +| --- | --- | +| 已实现 | 第一阶段接口已经存在,第二阶段保持兼容 | +| 扩展 | 路径已存在,第二阶段增加字段、事件或行为 | +| 计划新增 | 第二阶段需要新增实现 | +| 内部 Contract | 不直接暴露 HTTP,由两个模块共同遵守 | +| Host Contract | 由 Tauri/Rust Host 提供,Web 开发模式可使用 Mock Adapter | + +## 3. 第二阶段接口总览 + +| 域 | 方法 | 路径或 Contract | 状态 | 用途 | +| --- | --- | --- | --- | --- | +| Transcription | POST | `/api/media/transcriptions` | 扩展 | 创建真实音频转写任务 | +| Transcription | GET | `/api/media/transcriptions` | 计划新增 | 分页获取转写任务 | +| Transcription | GET | `/api/media/transcriptions/{job_id}` | 扩展 | 获取分段转写和任务状态 | +| Transcription | GET | `/api/media/transcriptions/{job_id}/events` | 计划新增 | 订阅模型加载、分离和转写进度 | +| Transcription | POST | `/api/media/transcriptions/{job_id}/cancel` | 计划新增 | 取消音频任务 | +| Transcription | POST | `/api/media/transcriptions/{job_id}/notes` | 计划新增 | 将 Transcript 写入 Knowledge Core | +| Agent Trace | GET | `/api/agent/runs/{run_id}/events` | 扩展 | 支持游标恢复并增加模型与权限事件 | +| Agent Trace | GET | `/api/agent/runs/{run_id}/trace` | 计划新增 | 分页读取可回放 Trace 快照 | +| Plugin Host | GET | `/api/plugins/{plugin_id}/host` | 计划新增 | 获取 MCP Host 健康状态 | +| Plugin Host | POST | `/api/plugins/{plugin_id}/host/restart` | 计划新增 | 重启异常 Host 并重新发现 Tool | +| Plugin Command | GET | `/api/plugin-contributions/commands` | 计划新增 | 获取前端可展示的 Command | +| Plugin Command | POST | `/api/plugin-contributions/commands/{command_id}/execute` | 计划新增 | 受控执行 Command | +| Plugin Settings | GET | `/api/plugins/{plugin_id}/settings` | 计划新增 | 获取 Schema 与非敏感配置 | +| Plugin Settings | PUT | `/api/plugins/{plugin_id}/settings` | 计划新增 | 更新非敏感配置 | +| Plugin Settings | PUT/DELETE | `/api/plugins/{plugin_id}/settings/{key}/secret` | 计划新增 | 写入或删除 Secret Reference | +| Provider | 现有路径 | `/api/providers/*`、`POST /api/chat` | 扩展 | 补齐协议能力和统一行为 | +| Retrieval | GET/POST | `/api/index/status`、`/api/index/rebuild` | 扩展 | 暴露 Embedding 兼容状态并安全重建向量 | +| Benchmark | GET | `/api/benchmarks/datasets` | 计划新增 | 枚举受控 Dataset | +| Benchmark | POST | `/api/benchmarks/rag/runs` | 计划新增 | 创建 RAG Benchmark | +| Benchmark | POST | `/api/benchmarks/agent/runs` | 计划新增 | 创建 Agent Benchmark | +| Benchmark | GET | `/api/benchmarks/runs` | 计划新增 | 分页获取 Benchmark Run | +| Benchmark | GET/POST | `/api/benchmarks/runs/{run_id}/*` | 计划新增 | 查询、订阅、取消和读取报告 | +| Export | POST | `/api/exports` | 计划新增 | 创建 HTML/PDF/DOCX 导出任务 | +| Export | GET | `/api/exports` | 计划新增 | 分页获取导出任务 | +| Export | GET | `/api/exports/{job_id}` | 计划新增 | 查询导出任务 | +| Export | GET | `/api/exports/{job_id}/file` | 计划新增 | 下载已完成产物 | +| Export | POST | `/api/exports/{job_id}/cancel` | 计划新增 | 取消导出任务 | +| Theme | Host Contract | `ThemePackageService` | 计划新增 | 导入、预览、启停和卸载主题包 | +| Renderer | 内部 Contract | `StaticRenderer` | 计划新增 | Mermaid/Function Plot 预览和导出复用 | + +--- + +## 4. 公共协议 + +### 4.1 JSON 与时间 + +- HTTP JSON 字段统一使用 `snake_case`。 +- TypeScript Wire DTO 与 JSON 保持同名,View Model 可在 Service 层转换。 +- 时间使用 UTC ISO 8601,例如 `2026-08-31T10:30:00Z`。 +- 耗时统一使用 `duration_ms`,音视频时间使用秒数浮点值。 +- ID 是不透明字符串,调用方不得解析前缀或依赖生成规则。 +- 枚举新增值时前端必须提供 unknown fallback,不能使整个页面渲染失败。 + +### 4.2 Job 状态 + +第二阶段异步任务统一使用: + +```text +queued +running +completed +failed +cancelled +``` + +第一阶段 Transcription 曾使用 `processing`。迁移期间前端 Wire DTO 同时接受 `processing` 和 `running`,Service 统一映射为 `running`;所有消费者更新后,后端再停止返回旧值。 + +通用进度结构: + +```json +{ + "phase": "transcribing", + "current": 42, + "total": 100, + "percent": 42.0, + "message": "正在转写音频" +} +``` + +`percent` 可以为 `null`,调用方不能以 100 代替 `completed`。终态 Job 必须有 `completed_at`;失败 Job 必须提供项目错误码,不直接暴露第三方堆栈。 + +### 4.3 分页和游标 + +普通资源列表继续使用: + +```json +{ + "items": [], + "page": { "total": 0, "limit": 50, "offset": 0 } +} +``` + +事件和 Trace 使用序列游标: + +```json +{ + "items": [], + "next_sequence": 120, + "has_more": false +} +``` + +`after_sequence` 表示只返回大于该值的事件。事件序号只保证在一个资源内单调递增,客户端不得假设从 0 或 1 开始。 + +### 4.4 SSE + +SSE 事件统一包含 `id`、`event` 和 JSON `data`: + +```text +id: 42 +event: Progress +data: {"sequence":42,"resource_id":"job_123","data":{"percent":50},"timestamp":"2026-08-31T10:30:00Z"} +``` + +- `id` 等于可恢复的事件 sequence。 +- 客户端重连时发送 `Last-Event-ID`;开发环境也可以使用 `?after_sequence=42`。 +- 服务端先回放缺失事件,再切换为实时事件。 +- 重放与实时交界处允许重复,客户端按资源 ID 与 sequence 去重。 +- `Completed`、`Failed`、`Cancelled` 是终止事件;正常断流但没有终止事件时,客户端按可重连处理。 +- 心跳使用 SSE 注释行,不创建业务事件。 + +### 4.5 统一错误 + +沿用第一阶段结构: + +```json +{ + "error": { + "code": "RESOURCE_NOT_FOUND", + "message": "Resource was not found.", + "details": {} + } +} +``` + +`message` 用于用户提示,`details` 只放可安全展示和定位的结构化信息。前端业务分支只判断 `code`。 + +--- + +## 5. Multimodal / Transcription + +### 5.1 创建转写任务 + +`POST /api/media/transcriptions`,扩展现有接口,成功返回 `202 Accepted`。 + +请求: + +```json +{ + "attachment_id": "attachment_123", + "language": null, + "diarization": true, + "word_timestamps": false, + "profile_id": "default", + "metadata": {} +} +``` + +约束: + +- `attachment_id` 必须属于当前 Vault 的 Host 管理附件。 +- `language = null` 表示自动识别。 +- `profile_id` 引用服务端模型配置,不允许请求直接传 CUDA 路径或任意模型目录。 +- `metadata` 只用于关联业务上下文,不进入模型参数。 + +响应: + +```json +{ + "job_id": "transcription_123", + "attachment_id": "attachment_123", + "status": "queued", + "progress": null, + "language": null, + "diarization_enabled": true, + "segments": [], + "text": null, + "warnings": [], + "error": null, + "created_at": "2026-08-31T10:30:00Z", + "started_at": null, + "updated_at": "2026-08-31T10:30:00Z", + "completed_at": null +} +``` + +### 5.2 Transcript Segment + +```json +{ + "segment_id": "segment_001", + "speaker": "SPEAKER_00", + "start_time": 12.4, + "end_time": 18.9, + "text": "这一部分介绍进程调度。", + "confidence": 0.93, + "words": [] +} +``` + +`speaker`、`confidence` 和 `words` 可以为空。Segment 按 `start_time` 排序且不得重叠为负时长。没有 pyannote 能力时任务可以完成,但必须在 `warnings` 中返回 `DIARIZATION_UNAVAILABLE`。 + +### 5.3 查询、事件和取消 + +| 方法 | 路径 | 响应 | +| --- | --- | --- | +| GET | `/api/media/transcriptions?status=&limit=&offset=` | `TranscriptionJobListResponse` | +| GET | `/api/media/transcriptions/{job_id}` | 完整 `TranscriptionJob` | +| GET | `/api/media/transcriptions/{job_id}/events` | `TranscriptionEvent` SSE | +| POST | `/api/media/transcriptions/{job_id}/cancel` | `OperationResponse` | + +事件类型: + +```text +Queued +ModelLoading +DiarizationStarted +DiarizationCompleted +TranscriptionStarted +Progress +Completed +Failed +Cancelled +``` + +取消必须向正在运行的模型任务传播。接口返回 accepted 只表示取消请求已记录,最终状态以 Job 或终止事件为准。 + +### 5.4 写入 Knowledge Core + +`POST /api/media/transcriptions/{job_id}/notes` + +```json +{ + "title": "操作系统课程录音", + "folder": "课程/操作系统", + "include_timestamps": true, + "include_speakers": true, + "template_id": null +} +``` + +仅 completed Job 可以转换。响应返回第一阶段 `Note` Contract。生成的 Block Citation 需要保留 `source_audio`、`start_time`、`end_time` 和 `speaker`,使搜索与 Agent 引用能够跳回音频。 + +### 5.5 Multimodal 错误码 + +```text +ATTACHMENT_NOT_FOUND +UNSUPPORTED_AUDIO_FORMAT +TRANSCRIPTION_PROFILE_NOT_FOUND +TRANSCRIPTION_MODEL_UNAVAILABLE +DIARIZATION_MODEL_UNAVAILABLE +TRANSCRIPTION_FAILED +TRANSCRIPTION_CANCELLED +TRANSCRIPT_NOT_READY +``` + +--- + +## 6. Agent Trace + +### 6.1 兼容现有 AgentEvent + +保留现有事件: + +```text +RunStarted +TextDelta +ThinkingDelta +ToolCall +ToolResult +PermissionRequired +Usage +Citation +RunCompleted +RunFailed +RunCancelled +``` + +第二阶段增加: + +```text +ModelCallStarted +ModelCallCompleted +PermissionResolved +``` + +`GET /api/agent/runs/{run_id}/events` 增加 `Last-Event-ID` 和 `after_sequence` 支持,不改变现有事件 envelope。 + +### 6.2 Trace 快照 + +`GET /api/agent/runs/{run_id}/trace?after_sequence=0&limit=200` + +```json +{ + "run_id": "run_123", + "status": "running", + "items": [], + "next_sequence": 42, + "has_more": false, + "summary": { + "model_calls": 2, + "tool_calls": 3, + "duration_ms": 1530, + "token_usage": 2048 + } +} +``` + +Trace API 返回事件事实,不返回前端树形布局。前端根据 parent ID 和 sequence 构建时间线或树,Benchmark 使用相同事件计算指标。 + +### 6.3 新增事件数据 + +`ModelCallStarted`: + +```json +{ + "model_call_id": "model_call_01", + "provider_id": "deepseek", + "model": "deepseek-chat", + "step": 2 +} +``` + +`ModelCallCompleted`: + +```json +{ + "model_call_id": "model_call_01", + "duration_ms": 820, + "finish_reason": "tool_calls", + "input_tokens": 1200, + "output_tokens": 240 +} +``` + +`PermissionResolved`: + +```json +{ + "request_id": "permission_01", + "permission": "notes.write", + "decision": "allow_once" +} +``` + +ToolCall 和 ToolResult 增加可选 `parent_model_call_id`、`duration_ms` 和经过截断/净化的摘要字段。完整敏感参数不进入 Trace。 + +### 6.4 Trace 保留和脱敏 + +- `run_id + sequence` 是事件幂等键。 +- 运行记录和事件由持久化 Repository 管理,不能只保存在 SSE 订阅队列。 +- API Key、Authorization Header、Secret Setting、完整附件正文默认脱敏。 +- 大型 Tool Result 保存摘要与受控 artifact 引用,不直接塞入事件 JSON。 +- Trace 被 Benchmark 引用时保存运行配置快照,避免后续 Provider 配置变更导致报告不可解释。 + +### 6.5 Trace 错误码 + +```text +AGENT_RUN_NOT_FOUND +TRACE_NOT_AVAILABLE +TRACE_CURSOR_EXPIRED +``` + +--- + +## 7. MCP Bridge 与 Plugin Contribution + +### 7.1 MCP 内部 Adapter + +Agent Runtime 不使用 MCP 原始类型。MCP Bridge 必须完成: + +```text +MCP tools/list +→ JSON Schema 校验 +→ 项目 ToolDefinition +→ Tool Registry + +项目 ToolCall +→ MCP tools/call +→ MCP Content / Error +→ 项目 ToolResult +``` + +进入 Tool Registry 的名称使用 `.` 命名空间。重复名称、无效 Schema、未声明权限和超过结果大小上限时拒绝注册。 + +内部接口示意: + +```python +class McpBridge(Protocol): + async def start(self, plugin: PluginManifest) -> "PluginHostStatus": ... + async def discover_tools(self, plugin_id: str) -> list[ToolDefinition]: ... + async def call_tool(self, plugin_id: str, call: ToolCall) -> ToolResult: ... + async def cancel(self, plugin_id: str, request_id: str) -> None: ... + async def stop(self, plugin_id: str) -> None: ... +``` + +首个实现支持本地 `stdio`。Host 负责 initialize、capability negotiation、进程生命周期、超时、取消、stderr 隔离和异常退出后的 Tool 注销。 + +当前 Web 开发接口 `POST /api/plugins/install` 使用 `package_path`。桌面 Host 接入后,`ExtensionInstallRequest` 增加 `package_id`,由文件选择器产生临时包句柄;`package_path` 只在明确的 development 环境保留并标记 deprecated,生产构建拒绝任意前端路径。 + +### 7.2 Plugin Host 状态 + +`GET /api/plugins/{plugin_id}/host` + +```json +{ + "plugin_id": "example-plugin", + "backend_type": "mcp", + "transport": "stdio", + "status": "ready", + "tools_count": 3, + "started_at": "2026-08-31T10:30:00Z", + "last_seen_at": "2026-08-31T10:31:00Z", + "error": null +} +``` + +Host 状态: + +```text +stopped +starting +ready +unhealthy +error +``` + +`POST /api/plugins/{plugin_id}/host/restart` 返回 `202 OperationResponse`。重启期间先注销旧 Tool,发现和校验全部成功后再一次性发布新 Tool 集合,避免半注册状态。 + +### 7.3 Command Contribution 列表 + +`GET /api/plugin-contributions/commands?location=command_palette` + +```json +{ + "items": [ + { + "command_id": "example-plugin.open-search", + "plugin_id": "example-plugin", + "title": "打开外部搜索", + "description": "在插件服务中搜索当前选区", + "icon": "search", + "locations": ["command_palette", "context_menu"], + "when": ["workspace.has_vault", "editor.has_selection"], + "parameters": { + "type": "object", + "properties": {} + }, + "enabled": true + } + ] +} +``` + +`icon` 只能引用宿主图标 ID 或插件包内已验证资源。`when` 是宿主支持的条件 token 列表,不执行插件提供的 JavaScript 表达式。 + +### 7.4 执行 Command + +`POST /api/plugin-contributions/commands/{command_id}/execute` + +```json +{ + "arguments": {}, + "context": { + "note_id": "note_123", + "selection": "选中的文本" + } +} +``` + +宿主根据 Command 声明裁剪 Context,插件不能要求前端上传整个 Pinia 状态。响应: + +```json +{ + "command_id": "example-plugin.open-search", + "status": "completed", + "effect": { + "type": "notification", + "payload": { "level": "success", "message": "命令已完成" } + } +} +``` + +允许的 effect 首批为 `none`、`notification`、`navigate`、`refresh` 和 `job`。前端仅执行白名单 effect;未知类型显示结果但不执行。 + +### 7.5 Settings Schema + +`GET /api/plugins/{plugin_id}/settings` + +```json +{ + "plugin_id": "example-plugin", + "schema_version": 1, + "fields": [ + { + "key": "result_limit", + "label": "结果数量", + "description": "每次最多返回的结果数", + "type": "number", + "required": true, + "default": 10, + "minimum": 1, + "maximum": 100, + "options": [] + }, + { + "key": "api_key", + "label": "API Key", + "type": "secret", + "required": true, + "options": [] + } + ], + "values": { "result_limit": 10 }, + "secrets": { "api_key": { "configured": false } } +} +``` + +字段类型首批固定为: + +```text +string +number +boolean +select +secret +``` + +### 7.6 更新 Settings 和 Secret + +`PUT /api/plugins/{plugin_id}/settings` + +```json +{ + "schema_version": 1, + "values": { "result_limit": 20 } +} +``` + +该接口拒绝 secret 字段。Schema 版本过期返回 `PLUGIN_SETTINGS_VERSION_CONFLICT` 并附当前版本。 + +Secret 使用: + +```text +PUT /api/plugins/{plugin_id}/settings/{key}/secret +DELETE /api/plugins/{plugin_id}/settings/{key}/secret +``` + +写入请求: + +```json +{ "secret": "仅在本次请求中出现的明文" } +``` + +响应只返回: + +```json +{ + "plugin_id": "example-plugin", + "key": "api_key", + "configured": true +} +``` + +Secret 明文不进入普通 Settings、日志、Trace、Benchmark Dataset 或前端持久化。 + +### 7.7 Plugin/MCP 错误码 + +```text +PLUGIN_HOST_UNAVAILABLE +PLUGIN_HOST_START_FAILED +PLUGIN_HOST_UNHEALTHY +MCP_INITIALIZE_FAILED +MCP_CAPABILITY_UNSUPPORTED +MCP_TOOL_SCHEMA_INVALID +MCP_TOOL_CALL_FAILED +MCP_TOOL_RESULT_TOO_LARGE +PLUGIN_COMMAND_NOT_FOUND +PLUGIN_COMMAND_CONTEXT_INVALID +PLUGIN_SETTINGS_SCHEMA_INVALID +PLUGIN_SETTINGS_VERSION_CONFLICT +PLUGIN_SECRET_FIELD_NOT_FOUND +``` + +--- + +## 8. Provider Adapter 扩展 + +第二阶段不新增平行 Provider CRUD,继续使用第一阶段接口: + +```text +GET /api/providers +GET /api/providers/presets +POST /api/providers +GET /api/providers/{provider_id} +PATCH /api/providers/{provider_id} +DELETE /api/providers/{provider_id} +GET /api/providers/{provider_id}/models +POST /api/providers/test +POST /api/chat +``` + +### 8.1 ModelInfo 扩展 + +`GET /api/providers/{provider_id}/models` 的 item 增加可选字段: + +```json +{ + "model": "example-model", + "display_name": "Example Model", + "capabilities": ["chat", "streaming", "tool_calling"], + "context_window": 128000, + "max_output_tokens": 8192, + "deprecated": false, + "metadata": {} +} +``` + +模型列表无法可靠获取 Capability 时返回保守集合,不能默认声称支持 Tool Calling 或 Vision。 + +### 8.2 Streaming 行为 + +继续使用现有 ModelEvent: + +```text +TextDelta +ThinkingDelta +ToolCallStart +ToolCallDelta +ToolCallEnd +Usage +Error +Done +``` + +第二阶段统一以下规则: + +- 每个事件包含单调递增 `sequence`。 +- `ToolCallDelta` 按 `tool_call_id` 聚合,完成时参数必须是合法 JSON Object。 +- `Usage` 字段统一为 `input_tokens`、`output_tokens`、`total_tokens`。 +- Model Stream 在成功或失败后都发送且只发送一个终止事件 `Done`;失败顺序为 `Error` 后 `Done`,`Done.data.status` 为 `completed`、`failed` 或 `cancelled`。 +- 浏览器取消 Fetch 或 SSE 后,服务端必须取消上游 Provider 请求。 +- 不支持 reasoning 的 Provider 不发送伪造 ThinkingDelta。 + +### 8.3 Provider 一致性测试 Contract + +每个 Adapter 使用相同 Case 描述: + +```yaml +id: provider-tool-call-basic +capabilities: + - chat + - streaming + - tool_calling +request_fixture: tool_call_weather +assertions: + terminal_event: Done + tool_calls: 1 + usage_required: false +``` + +测试报告记录 Provider 类型、模型、协议、运行时间和跳过原因。真实网络测试与离线 Adapter 单元测试分组,CI 默认不要求外部 API Key。 + +### 8.4 Provider 错误码 + +沿用并补齐: + +```text +PROVIDER_AUTH_FAILED +PROVIDER_RATE_LIMITED +PROVIDER_TIMEOUT +PROVIDER_UNAVAILABLE +PROVIDER_PROTOCOL_ERROR +PROVIDER_STREAM_INTERRUPTED +MODEL_NOT_FOUND +MODEL_CAPABILITY_MISMATCH +MODEL_CONTEXT_LENGTH_EXCEEDED +``` + +--- + +## 9. RAG / Agent Benchmark + +Benchmark Service 同时提供 Python 调用接口和本地 HTTP 接口。CLI、测试和前端报告页调用同一 Service,不各自实现指标。 + +### 9.1 Dataset 列表 + +`GET /api/benchmarks/datasets?kind=rag|agent` + +```json +{ + "items": [ + { + "dataset_id": "rag-core-v1", + "kind": "rag", + "version": "1.0.0", + "description": "基础中文笔记检索集", + "case_count": 50, + "content_hash": "sha256:..." + } + ] +} +``` + +Dataset 从仓库或受控导入目录注册。API 不接受调用方提交任意文件路径。 + +### 9.2 RAG Dataset Case + +```json +{ + "case_id": "rag-os-001", + "query": "死锁发生需要什么条件", + "expected_note_ids": ["note_deadlock"], + "expected_block_ids": ["block_conditions"], + "citation_required": true, + "tags": ["os", "deadlock"] +} +``` + +### 9.3 Agent Dataset Case + +```json +{ + "case_id": "agent-os-review-001", + "prompt": "查找死锁内容并创建三个复习任务", + "allowed_tools": ["rag.search", "notes.read", "tasks.create"], + "expected_tools": ["rag.search", "notes.read", "tasks.create"], + "expected_conditions": { + "citation_required": true, + "tasks_created": 3 + }, + "tags": ["os", "write"] +} +``` + +有写操作的 Agent Case 必须运行在隔离 Vault/数据库中,测试结束后清理 Fixture,禁止作用于用户真实知识库。 + +### 9.4 创建 RAG Benchmark + +`POST /api/benchmarks/rag/runs` + +```json +{ + "dataset_id": "rag-core-v1", + "modes": ["fts", "vector", "hybrid"], + "retrieval": { + "top_k": 10, + "rrf_k": 60, + "rerank": true, + "rerank_candidates": 20, + "score_threshold": 0.0 + }, + "repeat": 1, + "metadata": {} +} +``` + +配置快照必须记录 Embedding model ID/version/dimension、Reranker、索引版本、Dataset Hash 和运行环境。 + +### 9.5 创建 Agent Benchmark + +`POST /api/benchmarks/agent/runs` + +```json +{ + "dataset_id": "agent-core-v1", + "provider_id": "mock", + "model": "mock-1", + "skill_id": null, + "max_steps": 10, + "token_budget": 20000, + "concurrency": 1, + "metadata": {} +} +``` + +Benchmark Runner 通过正式 Agent Runtime 创建 Run,并从 Trace 计算结果,不能直接调用 Tool Executor 绕过权限和步骤控制。 + +### 9.6 Benchmark Run + +RAG 和 Agent 创建接口均返回 `202 BenchmarkRun`: + +```json +{ + "run_id": "benchmark_123", + "kind": "rag", + "dataset_id": "rag-core-v1", + "dataset_hash": "sha256:...", + "status": "queued", + "progress": null, + "metrics": null, + "config_snapshot": {}, + "error": null, + "created_at": "2026-08-31T10:30:00Z", + "started_at": null, + "completed_at": null +} +``` + +公共接口: + +| 方法 | 路径 | 用途 | +| --- | --- | --- | +| GET | `/api/benchmarks/runs?kind=&status=&limit=&offset=` | 分页获取运行记录 | +| GET | `/api/benchmarks/runs/{run_id}` | 获取状态和指标摘要 | +| GET | `/api/benchmarks/runs/{run_id}/events` | SSE 进度和 Case 结果 | +| POST | `/api/benchmarks/runs/{run_id}/cancel` | 取消运行 | +| GET | `/api/benchmarks/runs/{run_id}/report` | 获取结构化完整报告 | + +### 9.7 指标 Contract + +RAG: + +```json +{ + "hit_at_1": 0.72, + "hit_at_5": 0.91, + "recall_at_k": 0.88, + "mrr": 0.81, + "citation_hit_rate": 0.89, + "p50_latency_ms": 24.5, + "p95_latency_ms": 67.3 +} +``` + +Agent: + +```json +{ + "task_success_rate": 0.80, + "tool_selection_accuracy": 0.92, + "tool_argument_accuracy": 0.86, + "invalid_tool_call_rate": 0.02, + "average_steps": 4.3, + "average_latency_ms": 2300, + "average_token_usage": 3200 +} +``` + +报告同时返回逐 Case 结果、失败原因和 Trace Run ID,汇总指标不能覆盖失败样本。 + +### 9.8 Benchmark 错误码 + +```text +BENCHMARK_DATASET_NOT_FOUND +BENCHMARK_DATASET_INVALID +BENCHMARK_CONFIG_INVALID +BENCHMARK_INDEX_INCOMPATIBLE +BENCHMARK_RUN_NOT_FOUND +BENCHMARK_RUN_FAILED +``` + +### 9.9 Retrieval Profile 与索引兼容 + +Benchmark、Search 和 RAG Engine 共享内部 `RetrievalProfile`,不得各自硬编码参数: + +```json +{ + "profile_id": "default-v2", + "version": 2, + "fts_recall": 20, + "vector_recall": 20, + "rrf_k": 60, + "rerank": true, + "rerank_candidates": 20, + "context_top_k": 8, + "score_threshold": 0.0 +} +``` + +第二阶段扩展 `GET /api/index/status`: + +```json +{ + "status": "idle", + "pending_jobs": 0, + "active_job_id": null, + "embedding": { + "model_id": "bge-m3", + "model_version": "...", + "dimension": 1024, + "normalization": "l2" + }, + "vector_index": { + "model_id": "hash-v1", + "dimension": 128, + "compatible": false + }, + "last_completed_at": null, + "error_message": null +} +``` + +模型 ID、版本、维度或归一化方式不一致时,Vector/Hybrid 搜索返回 `INDEX_MODEL_MISMATCH`,前端降级到 FTS;`POST /api/index/rebuild` 使用现有 `scope = vectors` 重建,不能在查询时混用旧向量。 + +相关错误码: + +```text +EMBEDDING_MODEL_UNAVAILABLE +EMBEDDING_DIMENSION_MISMATCH +INDEX_MODEL_MISMATCH +VECTOR_INDEX_REBUILD_REQUIRED +``` + +--- + +## 10. Export Service + +### 10.1 创建导出任务 + +`POST /api/exports`,返回 `202 ExportJob`。 + +```json +{ + "source": { + "type": "note", + "note_id": "note_123" + }, + "format": "pdf", + "options": { + "theme_id": "light", + "include_title": true, + "include_metadata": false, + "page_size": "A4", + "code_theme": "github-light" + } +} +``` + +`source.type` 首批支持 `note` 和 `markdown`。`markdown` 来源用于尚未保存的预览,字段大小受限且不持久化到 Trace。`format` 固定为 `html`、`pdf`、`docx`。 + +响应: + +```json +{ + "job_id": "export_123", + "status": "queued", + "format": "pdf", + "progress": null, + "file": null, + "warnings": [], + "error": null, + "created_at": "2026-08-31T10:30:00Z", + "started_at": null, + "completed_at": null +} +``` + +### 10.2 查询、取消和下载 + +| 方法 | 路径 | 响应 | +| --- | --- | --- | +| GET | `/api/exports?status=&format=&limit=&offset=` | `ExportJobListResponse` | +| GET | `/api/exports/{job_id}` | `ExportJob` | +| POST | `/api/exports/{job_id}/cancel` | `OperationResponse` | +| GET | `/api/exports/{job_id}/file` | 文件流 | + +下载响应设置正确 `Content-Type`、经过清理的 `Content-Disposition` 文件名和 `Content-Length`。未完成、失败或过期 Job 不返回空文件。 + +完成 Job 的 file: + +```json +{ + "file_name": "操作系统复习.pdf", + "mime_type": "application/pdf", + "size": 1048576, + "sha256": "...", + "expires_at": "2026-09-01T10:30:00Z" +} +``` + +### 10.3 Document AST 内部 Contract + +```python +class DocumentExporter(Protocol): + async def export( + self, + document: Document, + options: ExportOptions, + ) -> ExportResult: ... +``` + +Document Node 使用稳定判别字段: + +```json +{ + "node_id": "node_01", + "type": "heading", + "attributes": { "level": 2 }, + "children": [], + "text": "进程调度" +} +``` + +首批 node type: + +```text +document +heading +paragraph +text +emphasis +strong +link +list +list_item +table +image +blockquote +code_block +math_inline +math_block +mermaid +function_plot +``` + +Exporter 对无法表示的节点添加 warning,不允许静默丢失。严重缺失由 `EXPORT_UNSUPPORTED_CONTENT` 失败。 + +### 10.4 Static Render Contract + +Mermaid 和函数图像共享: + +```ts +interface StaticRenderRequest { + kind: 'mermaid' | 'function_plot' + source: string + sourceHash: string + theme: 'light' | 'dark' + width?: number + height?: number +} + +interface StaticRenderResult { + mimeType: 'image/svg+xml' | 'image/png' + content: string | Uint8Array + width: number + height: number + warnings: string[] +} +``` + +渲染实现可以位于前端共享 Renderer、Host 或后端适配进程,但 Export Service 只依赖上述 Contract。SVG 进入导出器前必须净化;缓存键至少包含 source hash、主题和 renderer version。 + +### 10.5 Export 错误码 + +```text +EXPORT_SOURCE_NOT_FOUND +EXPORT_FORMAT_UNSUPPORTED +EXPORT_OPTIONS_INVALID +EXPORT_RENDER_FAILED +EXPORT_UNSUPPORTED_CONTENT +EXPORT_JOB_NOT_FOUND +EXPORT_FILE_EXPIRED +``` + +--- + +## 11. Theme Package Host Contract + +Theme 不经过 Python AI Core。正式桌面版通过 Tauri Host,Web 开发模式由相同 TypeScript Service 的 Mock Adapter 提供。 + +### 11.1 Theme Manifest + +`theme.yaml` 对应: + +```ts +interface ThemeManifest { + theme_id: string + name: string + version: string + author: string + description?: string + min_app_version: string + is_dark: boolean + css_entry: string + preview?: string +} +``` + +### 11.2 ThemePackageService + +```ts +interface ThemePackageService { + selectPackage(): Promise + inspectPackage(packageId: string): Promise + install(packageId: string): Promise + list(): Promise + enable(themeId: string): Promise + disable(themeId: string): Promise + uninstall(themeId: string): Promise +} +``` + +`packageId` 是 Host 通过文件选择器生成的临时句柄,不是前端传入的绝对路径。 + +### 11.3 Inspection + +```json +{ + "package_id": "theme_package_123", + "manifest": {}, + "preview_url": "app-theme-preview://theme_package_123", + "warnings": [], + "compatible": true +} +``` + +安装前检查: + +```text +Manifest Schema +Theme ID / Version +min_app_version +包内相对路径 +CSS 语法与大小 +禁止远程 URL、@import、脚本和越界资源 +预览资源 MIME +``` + +预览运行在隔离容器,不能直接将未验证 CSS 注入主页面。 + +### 11.4 Theme 错误码 + +```text +THEME_PACKAGE_NOT_FOUND +THEME_MANIFEST_INVALID +THEME_PACKAGE_INCOMPATIBLE +THEME_RESOURCE_OUTSIDE_PACKAGE +THEME_CSS_UNSAFE +THEME_ALREADY_INSTALLED +THEME_NOT_FOUND +THEME_BUILTIN_PROTECTED +``` + +--- + +## 12. Mermaid 与 Function Plot 前端 Contract + +### 12.1 MermaidRenderer + +```ts +interface MermaidRenderer { + render( + source: string, + options: { + theme: 'light' | 'dark' + mode: 'interactive' | 'static' + }, + ): Promise<{ + svg: string + width: number + height: number + warnings: string[] + }> +} +``` + +Renderer 返回净化后的 SVG 或明确错误,不直接操作调用组件之外的 DOM。组件销毁时释放事件监听和临时节点;编辑快速变化时以前一次请求的 AbortSignal 或 render version 丢弃旧结果。 + +### 12.2 FunctionPlot Model + +```ts +interface FunctionPlot { + version: 1 + expressions: Array<{ + expression: string + label?: string + color?: string + }> + domain?: [number, number] + range?: [number, number] + axes: { + xLabel?: string + yLabel?: string + grid: boolean + } +} +``` + +Markdown parser 将 fenced block 转换为 `FunctionPlotParseResult`: + +```ts +interface FunctionPlotParseResult { + plot?: FunctionPlot + diagnostics: Array<{ + severity: 'warning' | 'error' + code: string + message: string + line?: number + column?: number + }> +} +``` + +表达式解析使用白名单数学语法,不执行 `eval`、函数构造器、网络请求或对象属性访问。无法解析时保留原始 fenced block,并在预览中显示可定位诊断。 + +### 12.3 FunctionPlotRenderer + +```ts +interface FunctionPlotRenderer { + renderInteractive(plot: FunctionPlot, target: HTMLElement): Promise + renderStatic(plot: FunctionPlot, options: StaticRenderOptions): Promise +} + +interface RenderHandle { + update(plot: FunctionPlot): Promise + resize(width: number, height: number): void + destroy(): void +} +``` + +编辑器、Markdown 预览和 Export Service 只能依赖上述 Model/Renderer,不直接依赖具体绘图库的数据结构。 + +### 12.4 可视化错误码 + +```text +MERMAID_PARSE_FAILED +MERMAID_RENDER_FAILED +MERMAID_SVG_UNSAFE +FUNCTION_PLOT_PARSE_FAILED +FUNCTION_PLOT_EXPRESSION_UNSAFE +FUNCTION_PLOT_RENDER_FAILED +``` + +--- + +## 13. 跨模块 Contract 对照 + +| Contract | 提供模块 | 消费模块 | 稳定字段 | +| --- | --- | --- | --- | +| `TranscriptSegment` | Multimodal | Knowledge、Frontend | speaker、time range、text | +| `AgentEvent` / Trace | Agent Runtime | Frontend、Agent Benchmark | run_id、sequence、event、timestamp、data | +| `ToolDefinition` / `ToolResult` | Tool Registry | Agent、MCP Bridge | name、parameters、permission、success/error | +| `PluginCommand` | Extension Core | Command Palette、Context Menu | command_id、locations、when、parameters | +| `PluginSettingsSchema` | Extension Core | Settings UI | schema_version、fields、values、secret status | +| `ModelEvent` | Provider Adapter | Chat、Agent、Frontend | event、sequence、data、timestamp | +| `BenchmarkRun` | Benchmark Service | CLI、报告页 | dataset hash、config snapshot、metrics、status | +| `Document AST` | Export Service | HTML/PDF/DOCX Exporter | node_id、type、attributes、children/text | +| `StaticRenderResult` | Mermaid/Function Renderer | Export Service | mime、content、size、warnings | +| `ThemeManifest` | Theme Host | Theme UI | ID、version、compatibility、entry、preview | + +跨模块字段需要修改时: + +1. 先修改本文和对应 Pydantic/TypeScript Contract。 +2. 新字段优先可选并提供默认行为。 +3. 同一提交增加 Provider/Consumer 两侧契约测试。 +4. 若必须破坏兼容,增加版本字段或新路径并记录迁移窗口。 + +--- + +## 14. HTTP 状态码约定 + +| 状态码 | 使用场景 | +| --- | --- | +| 200 | 查询、同步更新、已完成命令 | +| 201 | 同步创建持久资源 | +| 202 | 创建异步 Job、请求取消或 Host 重启 | +| 204 | 删除成功且无响应正文 | +| 400 | 语义无效但 JSON 结构合法 | +| 401/403 | 本地 Session 或权限不足 | +| 404 | 资源不存在 | +| 409 | 状态冲突、依赖缺失、Schema 版本冲突 | +| 413 | 上传正文、结果或包超过限制 | +| 422 | Pydantic/Schema 校验失败 | +| 429 | 并发或速率容量已满 | +| 501 | Contract 已预留但当前宿主能力未实现 | +| 502/503/504 | 外部 Provider、MCP Host 或模型服务异常 | + +`501` 只用于明确存在于当前版本 Contract、但运行环境尚不具备的能力;未定义路由仍返回普通 404。 + +--- + +## 15. 接口冻结与开发顺序 + +这里描述依赖顺序,不代表人员优先级。 + +### 15.1 公共契约先行 + +先冻结并实现: + +```text +JobStatus / JobProgress +SSE sequence 与恢复规则 +Agent Trace 新事件 +Document AST +StaticRenderResult +Plugin Command / Settings Schema +Benchmark Dataset / Run +``` + +这些类型同时进入后端 Pydantic 和前端 Wire DTO,并增加序列化样例测试。 + +### 15.2 Adapter 和 Service + +在公共 Contract 后分别实现: + +```text +Whisper / Pyannote Adapter +MCP Bridge / Plugin Host +Provider 行为适配 +RAG / Agent Benchmark Runner +HTML / PDF / DOCX Exporter +Mermaid / FunctionPlot Renderer +ThemePackageService +``` + +Service 先以 Fake/Mock Adapter 完成状态机和错误测试,再接真实运行时,便于其他模块并行联调。 + +### 15.3 前后端联调 + +- 前端 Service 只依赖本文 Wire DTO。 +- 后端未完成时使用与 Contract 相同的 Mock,不在组件中硬编码第二套字段。 +- SSE 联调覆盖分片、重复事件、断线恢复、取消和终止状态。 +- Job 页面覆盖刷新后恢复,不依赖只存在于 Pinia 的进度。 +- Secret 输入在提交后立即清空,不进入浏览器持久化。 + +### 15.4 集成验收链路 + +```text +Audio Attachment +→ Transcription Job / Trace +→ Transcript Note +→ Index / RAG +→ Agent Run +→ MCP Tool +→ Agent Trace +→ Markdown with Mermaid / Function Plot +→ HTML / PDF / DOCX +→ Community Theme Preview +``` + +链路中每个箭头都必须通过本文定义的 Contract 或已有第一阶段接口,禁止测试脚本直接写数据库来伪造完成状态。 + +--- + +## 16. Definition of Done + +一个第二阶段接口完成需要同时满足: + +- [ ] Pydantic Request/Response Model 已实现并进入 OpenAPI; +- [ ] 前端 Wire DTO 与 Service 显式映射已实现; +- [ ] 成功、校验失败、资源不存在、状态冲突和运行时失败均有测试; +- [ ] 异步任务支持查询终态,取消语义明确; +- [ ] SSE 支持分片解析、sequence 去重、重连和唯一终止事件; +- [ ] 第三方错误已映射,响应和日志不包含 Secret 或堆栈; +- [ ] Mock/Fake 与真实 Adapter 遵守相同 Contract; +- [ ] 接口已在 `/docs` 和 `/openapi.json` 可见; +- [ ] 本文状态由“计划新增”更新为“已实现”或“扩展完成”; +- [ ] 相关开发说明、测试手册和问题修复文档已同步。 + +--- + +## 17. 实现文件建议 + +在不破坏现有目录的前提下,第二阶段可以逐步拆分: + +```text +backend/app/ +├── contracts.py # 现有公共 Contract;稳定后可按域拆分 +├── api/ +│ ├── media.py +│ ├── agent_trace.py +│ ├── plugin_contributions.py +│ ├── benchmarks.py +│ └── exports.py +├── media/ +│ ├── transcription.py +│ ├── whisper_adapter.py +│ └── diarization_adapter.py +├── extensions/ +│ ├── mcp_bridge.py +│ ├── command_registry.py +│ └── settings.py +├── benchmarks/ +│ ├── rag.py +│ ├── agent.py +│ └── reports.py +└── export/ + ├── document.py + ├── service.py + └── exporters/ + +frontend/src/ +├── contracts/ +├── services/ +│ ├── transcriptionService.ts +│ ├── pluginContributionService.ts +│ ├── benchmarkService.ts +│ ├── exportService.ts +│ └── themePackageService.ts +└── features/ + ├── agent-trace/ + ├── themes/ + └── export/ +``` + +目录调整应按实际代码规模渐进进行。Router 只做参数接收和错误映射,状态机、第三方 SDK 与文件处理继续放在 Service/Adapter 层。