# Conflicts: # README.md # backend/app/routes.py # docs/architecture/AI笔记软件技术栈说明-团队版-v2.3.md # docs/development/Knowledge与Retrieval-Core开发说明.md
48 KiB
第二阶段接口契约与开发规划
文档状态:接口冻结草案
更新日期:2026-09-03
依据:
../architecture/第二阶段团队分工表.md、../architecture/AI笔记软件技术栈说明-团队版-v2.3.md、后端接口契约-开发版.md
本文统一第二阶段新增能力的 HTTP、SSE、前端 Service、桌面 Host 和内部模块接口。文中标记为“计划新增”的路径尚未实现,不能据此判断当前服务已经支持;实现完成后以 FastAPI /openapi.json、TypeScript Wire DTO 和自动化测试共同作为最终依据。
人员分工和任务优先级不在本文重复维护,以第二阶段分工表为准。
1. 规划原则
- 第一阶段已经发布的路径和字段保持兼容,第二阶段优先增加可选字段、子资源和新事件。
- 前端只消费项目 Contract,不解析 Plugin Manifest、MCP 原始消息或模型厂商响应。
- Agent、Benchmark 和 Trace 共用同一运行事件,不建立测试专用的旁路执行协议。
- 长任务统一建模为 Job,创建接口快速返回,进度通过查询或 SSE 获取。
- 文件、附件、插件包和 Dataset 均使用受控 ID;HTTP 请求不接受任意本地绝对路径。
- Secret 只经专用写入接口提交,普通读取接口只返回是否配置和引用 ID。
- Mermaid、函数图像和导出器共享静态渲染 Contract,不从 Vue 组件 DOM 抓取结果。
- 所有第三方协议在 Adapter 边界转换,错误进入前端前映射为项目错误码。
2. 状态说明
| 标记 | 含义 |
|---|---|
| 已实现 | 接口已经落地并由当前 OpenAPI 与自动化测试覆盖 |
| 扩展 | 路径已存在,第二阶段增加字段、事件或行为 |
| 计划新增 | 第二阶段需要新增实现 |
| 内部 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 |
| MCP Server | GET/POST | /api/mcp/servers |
已实现(C.1) | 列出、创建独立 MCP Server 配置 |
| MCP Server | GET/PUT/DELETE | /api/mcp/servers/{server_id} |
已实现(C.1) | 读取、版本化修改、删除独立配置 |
| MCP Server | GET | /api/mcp/servers/{server_id}/tools |
已实现(C.1) | 获取映射后的 Tool 摘要 |
| MCP Server | POST | /api/mcp/servers/{server_id}/trust |
已实现(C.1) | 确认当前连接配置摘要 |
| MCP Server | POST | /api/mcp/servers/{server_id}/test |
已实现(C.1) | 临时连接、握手、发现工具后关闭 |
| MCP Server | POST | /api/mcp/servers/{server_id}/enable、disable |
已实现(C.1) | 控制连接与动态 Tool 生命周期 |
| MCP Server | PUT/DELETE | /api/mcp/servers/{server_id}/secrets/{key} |
已实现(C.1) | 按 kind 写入或删除加密环境变量/Header |
| 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(依赖 Agent Runtime 完成后交付) |
| 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 状态
第二阶段异步任务统一使用:
queued
running
completed
failed
cancelled
第一阶段 Transcription 曾使用 processing。迁移期间前端 Wire DTO 同时接受 processing 和 running,Service 统一映射为 running;所有消费者更新后,后端再停止返回旧值。
通用进度结构:
{
"phase": "transcribing",
"current": 42,
"total": 100,
"percent": 42.0,
"message": "正在转写音频"
}
percent 可以为 null,调用方不能以 100 代替 completed。终态 Job 必须有 completed_at;失败 Job 必须提供项目错误码,不直接暴露第三方堆栈。
4.3 分页和游标
普通资源列表继续使用:
{
"items": [],
"page": { "total": 0, "limit": 50, "offset": 0 }
}
事件和 Trace 使用序列游标:
{
"items": [],
"next_sequence": 120,
"has_more": false
}
after_sequence 表示只返回大于该值的事件。事件序号只保证在一个资源内单调递增,客户端不得假设从 0 或 1 开始。
4.4 SSE
SSE 事件统一包含 id、event 和 JSON data:
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 统一错误
沿用第一阶段结构:
{
"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。
请求:
{
"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只用于关联业务上下文,不进入模型参数。
响应:
{
"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
{
"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 |
事件类型:
Queued
ModelLoading
DiarizationStarted
DiarizationCompleted
TranscriptionStarted
Progress
Completed
Failed
Cancelled
取消必须向正在运行的模型任务传播。接口返回 accepted 只表示取消请求已记录,最终状态以 Job 或终止事件为准。
5.4 写入 Knowledge Core
POST /api/media/transcriptions/{job_id}/notes
{
"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 错误码
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
保留现有事件:
RunStarted
TextDelta
ThinkingDelta
ToolCall
ToolResult
PermissionRequired
Usage
Citation
RunCompleted
RunFailed
RunCancelled
第二阶段增加:
ModelCallStarted
ModelCallCompleted
ModelCallFailed
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
{
"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,
"errors": 0
},
"config_snapshot": {}
}
Trace API 返回事件事实,不返回前端树形布局。前端根据 parent ID 和 sequence 构建时间线或树,Benchmark 使用相同事件计算指标。
6.3 新增事件数据
ModelCallStarted:
{
"model_call_id": "model_call_01",
"provider_id": "deepseek",
"model": "deepseek-chat",
"step": 2
}
ModelCallCompleted:
{
"model_call_id": "model_call_01",
"duration_ms": 820,
"finish_reason": "tool_calls",
"input_tokens": 1200,
"output_tokens": 240
}
PermissionResolved:
{
"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 错误码
AGENT_RUN_NOT_FOUND
TRACE_NOT_AVAILABLE
TRACE_CURSOR_EXPIRED
TRACE_CURSOR_INVALID
7. MCP Bridge 与 Plugin Contribution
7.1 MCP 内部 Adapter
Agent Runtime 不使用 MCP 原始类型。MCP Bridge 必须完成:
MCP tools/list
→ JSON Schema 校验
→ 项目 ToolDefinition
→ Tool Registry
项目 ToolCall
→ MCP tools/call
→ MCP Content / Error
→ 项目 ToolResult
进入 Tool Registry 的名称使用 <plugin_id>.<tool_name> 命名空间。重复名称、无效 Schema、未声明权限和超过结果大小上限时拒绝注册。
内部接口示意:
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,按 MCP 2025-11-25 发起 initialize,并兼容 2025-06-18、2025-03-26 和 2024-11-05 协商结果。Host 负责 capability negotiation、分页 tools/list、进程生命周期、超时取消、stderr 隔离和异常退出后的 Tool 注销。stdio 消息使用 UTF-8 单行 JSON-RPC,并在完整行进入内存前执行有界读取;当前不实现 Streamable HTTP。
MCP Plugin 的 backend 增加:
backend:
type: mcp
transport: stdio
command: uvx
args: [--isolated, --from, example-mcp==1.2.3, example-mcp]
startup_timeout_seconds: 60
tool_timeout_seconds: 30
命令通过参数数组直接启动,不经过 Shell。带路径的 executable 必须位于 Plugin 包内;PATH 中的命令可以按名称引用。Python 包形式的 MCP 推荐使用固定版本的 uvx --isolated --from,但 uvx 只隔离依赖而不是文件/网络/系统调用安全沙箱,非 Python Server 不强制使用。开发模式首次运行未缓存的 uvx 包可能联网解析,因此 startup 示例使用 60 秒;生产安装阶段必须预取并验证,运行阶段不得临时解析依赖。子进程只继承运行所需的系统环境变量,不继承 OPENAI_API_KEY、APP_DB_PATH、Vault 路径等宿主状态。Secret 注入留给阶段 D 的专用引用接口。
在平台沙箱和可信命令许可完成前,APP_ENVIRONMENT != development 时启用 MCP Plugin 必须返回 403 MCP_TRUST_APPROVAL_REQUIRED,不得启动进程或注册 Tool。该门禁由后端执行,不能只依赖前端提示或文档约定。
远端 Tool 的可选项目权限放在 MCP _meta:
{ "_meta": { "notesagent/permission": "notes.read" } }
该权限必须属于项目已知权限并同时出现在 Plugin Manifest 中。发现结果必须与 contributes.tools 的命名空间 ID 完全一致;校验全部成功后才一次性发布到 Tool Registry。
当前 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
{
"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 状态:
stopped
starting
ready
unhealthy
error
POST /api/plugins/{plugin_id}/host/restart 返回 202 OperationResponse。重启期间先注销旧 Tool,发现和校验全部成功后再一次性发布新 Tool 集合,避免半注册状态。
当前实现还返回协商后的 protocol_version、server_name 和 server_version。单条协议消息上限为 2 MiB,单次 Tool Result 上限为 256 KiB;超限分别按 Host/Result 错误处理。Server 异常退出或发送无效 stdout 消息时,Host 进入 unhealthy,Plugin 进入 error,相关 Tool 立即注销。取消会同时通知 Server、移除 pending request 并唤醒本地等待线程。Restart 不得把 installed、disabled 或 permission_required Plugin 隐式启用,这些状态必须走 Enable。
7.3 Command Contribution 列表
GET /api/plugin-contributions/commands?location=command_palette
{
"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
{
"arguments": {},
"context": {
"note_id": "note_123",
"selection": "选中的文本"
}
}
宿主根据 Command 声明裁剪 Context,插件不能要求前端上传整个 Pinia 状态。响应:
{
"command_id": "example-plugin.open-search",
"status": "completed",
"effect": {
"type": "notification",
"payload": { "level": "success", "message": "命令已完成" }
}
}
允许的 effect 首批为 none、notification、navigate、refresh 和 job。每类 payload 也是契约的一部分:none 必须为空;notification 只接受 level(info/success/warning/error)和非空 message;navigate 只接受宿主路由名 route;refresh 只接受 workspace/commands/settings/plugins 范围;job 只接受受限格式的 job_id。后端拒绝未知字段和不匹配的 payload,前端仍须按判别联合穷尽处理,不得把 effect 当作任意代码执行。
当前宿主只注册已启用且已满足权限授权的 Plugin Command。执行前按 JSON Schema 校验参数、按 when 校验上下文,再根据 Command 声明裁剪 Context;单次执行默认超时 30 秒,effect 的 JSON 编码结果不得超过 64 KiB。宿主保留最多 500 条轻量审计事件,仅记录 Command、Plugin、状态、耗时和错误码,不记录 arguments、Context、effect 或 Secret。
需要 Secret 的 Command 必须在 commands.yaml 的内部 secrets 数组中声明对应 Setting Key,并在 Plugin Manifest 声明 secrets.use 权限。安装时宿主校验该字段确实属于当前 Plugin Settings Schema 的 secret 类型;只有权限已授予并启用后,运行时才向受控 handler 或 MCP Command Target 提供声明过的 Secret。未声明字段返回 PLUGIN_SECRET_ACCESS_DENIED,必填 Secret 未配置返回 PLUGIN_SECRET_REQUIRED。secrets 不属于前端 PluginCommand DTO,Secret 明文也不会并入普通 Settings 字典。
commands.yaml 中的执行目标必须在宿主白名单 handler 与当前 Plugin 命名空间的 mcp_tool 之间二选一。MCP Command Target 不注册为 Agent Tool;宿主用 _notesagent 保留包装传入 Command ID、参数、裁剪后的 Context、已校验的非敏感 Settings 和声明过的 Secret,并将 MCP structured result 再校验为白名单 effect。作为宿主协议标记,目标 MCP Tool 的 inputSchema 必须在顶层 properties 中直接声明 _notesagent: { type: object };不得用顶层组合或引用替代该标记。_notesagent 对象内部仍可使用完整 Draft 2020-12 约束、文档内引用和组合 Schema。启用阶段只检查协议标记,不尝试求解 Schema 或伪造业务值;宿主会保留完整 Schema,并在每次调用前用官方 Validator 校验真实信封。Command 与 Tool Schema 仅允许 #... 文档内引用,任何通过 $ref 或 $dynamicRef 指向文件、HTTP 或其他外部资源的 Schema 都会在注册前被拒绝。文档内引用遵循 Draft 2020-12 的嵌套 $id 与 Anchor 资源作用域,不能解析的引用不得进入运行时。
7.5 Settings Schema
GET /api/plugins/{plugin_id}/settings
{
"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 } }
}
字段类型首批固定为:
string
number
boolean
select
secret
Number 字段的 minimum 和 maximum 必须是有限数值;NaN、正无穷和负无穷均视为无效 Settings Schema。
7.6 更新 Settings 和 Secret
PUT /api/plugins/{plugin_id}/settings
{
"schema_version": 1,
"values": { "result_limit": 20 }
}
该接口拒绝 secret 字段。Schema 版本过期返回 PLUGIN_SETTINGS_VERSION_CONFLICT 并附当前版本。没有默认值的必填普通字段必须先通过该接口配置;否则 Plugin Enable 和 Command Execute 返回 PLUGIN_SETTINGS_REQUIRED,MCP 或内部 handler 不会收到残缺配置。
Secret 使用:
PUT /api/plugins/{plugin_id}/settings/{key}/secret
DELETE /api/plugins/{plugin_id}/settings/{key}/secret
写入请求:
{ "secret": "仅在本次请求中出现的明文" }
响应只返回:
{
"plugin_id": "example-plugin",
"key": "api_key",
"configured": true
}
Secret 明文不进入普通 Settings、日志、Trace、Benchmark Dataset 或前端持久化。
非敏感值按 plugin_id 写入 APP_DATA_DIR/plugins/settings.json。该文件只保存普通值、Schema 版本和确定性的定长 Secret Reference,格式为 plugin.<sha256(plugin_id\\0setting_key)>;Secret 本身由宿主凭据存储加密保存。卸载 Plugin 时同时清理它的 Settings 命名空间和 Secret Reference。当前开发阶段使用 Fernet 文件凭据存储,第三阶段接入桌面 Host 后应迁移到 Stronghold 或系统 Keychain。
plugin.* 为宿主保留凭据命名空间。/api/credentials/{credential_id}、Provider 持久配置、Provider 临时测试凭据和 Provider Resolver 均拒绝该前缀,防止通过 Provider 链路覆盖、删除或向外部 Base URL 发送 Plugin Secret。
7.7 Plugin/MCP 错误码
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
MCP_TRUST_APPROVAL_REQUIRED
MCP_TRUST_DIGEST_STALE
MCP_SANDBOX_REQUIRED
MCP_TRANSPORT_UNSUPPORTED
MCP_SERVER_NOT_FOUND
MCP_SERVER_NAME_INVALID
MCP_SERVER_ALREADY_ENABLED
MCP_SERVER_VERSION_CONFLICT
MCP_SERVER_LIMIT_REACHED
MCP_REGISTRY_WRITE_FAILED
MCP_REGISTRY_INVALID
MCP_CONNECTION_TEST_REQUIRED
MCP_CONFIG_INVALID
MCP_COMMAND_INVALID
MCP_URL_INVALID
MCP_HEADER_INVALID
MCP_HTTP_REQUEST_FAILED
MCP_HTTP_RESPONSE_INVALID
MCP_SECRET_REQUIRED
MCP_SECRET_NOT_DECLARED
MCP_SECRET_KIND_INVALID
MCP_SECRET_STORE_ERROR
MCP_ENVIRONMENT_INVALID
MCP_PERMISSION_INVALID
PLUGIN_COMMAND_NOT_FOUND
PLUGIN_COMMAND_CONFLICT
PLUGIN_COMMAND_INVALID
PLUGIN_COMMAND_ARGUMENT_INVALID
PLUGIN_COMMAND_CONTEXT_INVALID
PLUGIN_COMMAND_TIMEOUT
PLUGIN_COMMAND_EXECUTION_FAILED
PLUGIN_COMMAND_RESULT_INVALID
PLUGIN_COMMAND_RESULT_TOO_LARGE
PLUGIN_COMMAND_TARGET_SCHEMA_MISMATCH
PLUGIN_SETTINGS_SCHEMA_INVALID
PLUGIN_SETTINGS_VERSION_CONFLICT
PLUGIN_SETTINGS_FIELD_INVALID
PLUGIN_SETTINGS_REQUIRED
PLUGIN_SECRET_FIELD_NOT_FOUND
PLUGIN_SECRET_ACCESS_DENIED
PLUGIN_SECRET_REQUIRED
PLUGIN_SECRET_VALUE_INVALID
PLUGIN_SECRET_STORE_ERROR
PLUGIN_STORAGE_ERROR
CREDENTIAL_NAMESPACE_RESERVED
7.8 独立 MCP Server Registry(C.1)
独立 Server 不依附 Plugin Manifest,配置持久化于 APP_DATA_DIR/mcp/servers.json。transport 支持 stdio、streamable_http 和兼容旧服务的 sse。敏感环境变量与认证 Header 只以 mcp.* 引用进入加密凭据存储;读取响应以 secret_environment、secret_headers 的布尔值表示配置状态,不返回明文。动态工具使用 mcp.{server_id}.{remote_tool} 命名空间,来源标记为 mcp_server,仍通过统一 Tool Registry、Permission Manager 与 Agent Trace。
stdio 配置使用 command、args、environment 和 secret_environment_keys;HTTP/SSE 配置使用 url、headers 和 secret_header_keys,两组 Transport 字段不可混用。更新请求必须携带当前 version,成功后版本递增;过期版本返回 409 MCP_SERVER_VERSION_CONFLICT。GET /tools 返回 name、remote_name、description 和可选 permission。
创建或编辑配置后,调用方必须向 /trust 回传服务端计算的 command_digest。后端只接受与当前 Transport、连接参数、环境/Header 及权限完全一致的摘要;配置变化会撤销旧信任和测试结果。只有当前摘要通过 /test,才能调用 /enable。测试失败也会持久化时间和失败状态。
Secret 明文变化无法进入摘要,因此 Secret 写入和删除采用更严格规则:若 Server 已启用则先停用并注销 Tool,随后清除 tested_digest 和最近测试状态。调用方必须用新 Secret 再次执行 /test,不能沿用旧凭据的测试结果。
Streamable HTTP 支持 Session ID、MCP-Protocol-Version、JSON 或 SSE POST 响应、可选 GET 事件流及 Last-Event-ID;旧 SSE 按 endpoint 事件确定 POST 地址,并要求与初始 URL 同源。Secret 接口用 ?kind=environment 或 ?kind=header 区分类型。HTTP URL 不允许内嵌凭据或 Fragment,配置不得覆盖协议保留 Header。
stdio 命令始终以 executable 与 args 数组通过 shell=False 启动;普通环境变量和加密 Secret 显式注入,不继承 Provider Key、数据库或 Vault 路径。当前 Python Host 仅在 APP_ENVIRONMENT=development 时允许启动 stdio;其他环境返回 403 MCP_SANDBOX_REQUIRED。远程 HTTP Transport 不创建本机进程,但仍受摘要确认、成功测试、超时、消息限长与 Secret 隔离约束。
8. Provider Adapter 扩展
第二阶段不新增平行 Provider CRUD,继续使用第一阶段接口:
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 增加可选字段:
{
"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:
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 描述:
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 错误码
沿用并补齐:
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
{
"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
{
"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
{
"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
{
"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
{
"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:
{
"run_id": "benchmark_123",
"kind": "rag",
"dataset_id": "rag-core-v1",
"dataset_hash": "sha256:...",
"status": "queued",
"progress": null,
"metrics": null,
"config_snapshot": {},
"error": null,
"error_code": null,
"created_at": "2026-08-31T10:30:00Z",
"started_at": null,
"completed_at": null
}
status 取值:queued → running → completed | failed | cancelled。失败/取消时 error 与
error_code 只返回项目错误码与安全消息,不暴露第三方堆栈。
公共接口:
| 方法 | 路径 | 用途 |
|---|---|---|
| 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 |
获取结构化完整报告 |
SSE 事件流(RunStarted → CaseCompleted* → RunCompleted | RunFailed | RunCancelled):
GET /api/benchmarks/runs/{run_id}/events 支持 Last-Event-ID 与 ?after_sequence= 游标恢复,
RunCompleted / RunFailed / RunCancelled 为终止事件,收到后即断流。
9.7 指标 Contract
RAG:
{
"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,
"total_cases": 50,
"successful_cases": 48,
"failed_cases": 2,
"failure_rate": 0.04
}
失败样本按零分计入质量指标分母,total_cases / successful_cases / failed_cases /
failure_rate 让报告明确实际分母;延迟仅统计成功样本。
Agent:
{
"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 错误码
BENCHMARK_DATASET_NOT_FOUND
BENCHMARK_DATASET_INVALID
BENCHMARK_CONFIG_INVALID
BENCHMARK_INDEX_INCOMPATIBLE
BENCHMARK_CAPACITY_EXCEEDED
BENCHMARK_RUN_NOT_FOUND
BENCHMARK_RUN_FAILED
BENCHMARK_CASE_EVALUATION_FAILED
9.9 Retrieval Profile 与索引兼容
Benchmark、Search 和 RAG Engine 共享内部 RetrievalProfile,不得各自硬编码参数:
{
"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:
{
"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 重建,不能在查询时混用旧向量。
相关错误码:
EMBEDDING_MODEL_UNAVAILABLE
EMBEDDING_DIMENSION_MISMATCH
INDEX_MODEL_MISMATCH
VECTOR_INDEX_REBUILD_REQUIRED
10. Export Service
10.1 创建导出任务
POST /api/exports,返回 202 ExportJob。
{
"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。
响应:
{
"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:
{
"file_name": "操作系统复习.pdf",
"mime_type": "application/pdf",
"size": 1048576,
"sha256": "...",
"expires_at": "2026-09-01T10:30:00Z"
}
10.3 Document AST 内部 Contract
class DocumentExporter(Protocol):
async def export(
self,
document: Document,
options: ExportOptions,
) -> ExportResult: ...
Document Node 使用稳定判别字段:
{
"node_id": "node_01",
"type": "heading",
"attributes": { "level": 2 },
"children": [],
"text": "进程调度"
}
首批 node type:
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 和函数图像共享:
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 错误码
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 对应:
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
interface ThemePackageService {
selectPackage(): Promise<string | null>
inspectPackage(packageId: string): Promise<ThemePackageInspection>
install(packageId: string): Promise<InstalledTheme>
list(): Promise<InstalledTheme[]>
enable(themeId: string): Promise<InstalledTheme>
disable(themeId: string): Promise<void>
uninstall(themeId: string): Promise<void>
}
packageId 是 Host 通过文件选择器生成的临时句柄,不是前端传入的绝对路径。
11.3 Inspection
{
"package_id": "theme_package_123",
"manifest": {},
"preview_url": "app-theme-preview://theme_package_123",
"warnings": [],
"compatible": true
}
安装前检查:
Manifest Schema
Theme ID / Version
min_app_version
包内相对路径
CSS 语法与大小
禁止远程 URL、@import、脚本和越界资源
预览资源 MIME
预览运行在隔离容器,不能直接将未验证 CSS 注入主页面。
11.4 Theme 错误码
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
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
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:
interface FunctionPlotParseResult {
plot?: FunctionPlot
diagnostics: Array<{
severity: 'warning' | 'error'
code: string
message: string
line?: number
column?: number
}>
}
表达式解析使用白名单数学语法,不执行 eval、函数构造器、网络请求或对象属性访问。无法解析时保留原始 fenced block,并在预览中显示可定位诊断。
12.3 FunctionPlotRenderer
interface FunctionPlotRenderer {
renderInteractive(plot: FunctionPlot, target: HTMLElement): Promise<RenderHandle>
renderStatic(plot: FunctionPlot, options: StaticRenderOptions): Promise<StaticRenderResult>
}
interface RenderHandle {
update(plot: FunctionPlot): Promise<void>
resize(width: number, height: number): void
destroy(): void
}
编辑器、Markdown 预览和 Export Service 只能依赖上述 Model/Renderer,不直接依赖具体绘图库的数据结构。
12.4 可视化错误码
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 |
跨模块字段需要修改时:
- 先修改本文和对应 Pydantic/TypeScript Contract。
- 新字段优先可选并提供默认行为。
- 同一提交增加 Provider/Consumer 两侧契约测试。
- 若必须破坏兼容,增加版本字段或新路径并记录迁移窗口。
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 公共契约先行
先冻结并实现:
JobStatus / JobProgress
SSE sequence 与恢复规则
Agent Trace 新事件
Document AST
StaticRenderResult
Plugin Command / Settings Schema
Benchmark Dataset / Run
这些类型同时进入后端 Pydantic 和前端 Wire DTO,并增加序列化样例测试。
15.2 Adapter 和 Service
在公共 Contract 后分别实现:
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 集成验收链路
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. 实现文件建议
在不破坏现有目录的前提下,第二阶段可以逐步拆分:
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 层。