Files
NotesAgentic/docs/contracts/第二阶段接口契约-开发版.md
T

54 KiB
Raw Blame History

第二阶段接口契约与开发规划

阶段 F 实现更新(2026-09-04):新增持久化媒体任务、附件上传/清理、修订与笔记导出、本地模型管理、Token 用量及提供商请求 JSON。详细路径、字段语义和验证边界见 多模态管线与模型运行开发说明,以下旧阶段规划与实现不一致时以该说明和 OpenAPI 为准。

文档状态:接口冻结草案

更新日期:2026-09-03

依据:../architecture/第二阶段团队分工表.md../architecture/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. 状态说明

标记 含义
已实现 接口已经落地并由当前 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}/enabledisable 已实现(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 同时接受 processingrunningService 统一映射为 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 事件统一包含 idevent 和 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 去重。
  • CompletedFailedCancelled 是终止事件;正常断流但没有终止事件时,客户端按可重连处理。
  • 心跳使用 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": []
}

speakerconfidencewords 可以为空。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_audiostart_timeend_timespeaker,使搜索与 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-IDafter_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_idduration_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-182025-03-262024-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_KEYAPP_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_versionserver_nameserver_version。单条协议消息上限为 2 MiB,单次 Tool Result 上限为 256 KiB;超限分别按 Host/Result 错误处理。Server 异常退出或发送无效 stdout 消息时,Host 进入 unhealthyPlugin 进入 error,相关 Tool 立即注销。取消会同时通知 Server、移除 pending request 并唤醒本地等待线程。Restart 不得把 installeddisabledpermission_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 首批为 nonenotificationnavigaterefreshjob。每类 payload 也是契约的一部分:none 必须为空;notification 只接受 levelinfo/success/warning/error)和非空 messagenavigate 只接受宿主路由名 routerefresh 只接受 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_REQUIREDsecrets 不属于前端 PluginCommand DTOSecret 明文也不会并入普通 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 字段的 minimummaximum 必须是有限数值;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_REQUIREDMCP 或内部 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 RegistryC.1

独立 Server 不依附 Plugin Manifest,配置持久化于 APP_DATA_DIR/mcp/servers.jsontransport 支持 stdiostreamable_http 和兼容旧服务的 sse。敏感环境变量与认证 Header 只以 mcp.* 引用进入加密凭据存储;读取响应以 secret_environmentsecret_headers 的布尔值表示配置状态,不返回明文。动态工具使用 mcp.{server_id}.{remote_tool} 命名空间,来源标记为 mcp_server,仍通过统一 Tool Registry、Permission Manager 与 Agent Trace。

stdio 配置使用 commandargsenvironmentsecret_environment_keysHTTP/SSE 配置使用 urlheaderssecret_header_keys,两组 Transport 字段不可混用。更新请求必须携带当前 version,成功后版本递增;过期版本返回 409 MCP_SERVER_VERSION_CONFLICTGET /tools 返回 nameremote_namedescription 和可选 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 扩展

阶段 E 实施更新(2026-09-04):OpenAI Responses、Anthropic Messages、Chat Completions 与 Ollama Adapter 已接入;国内提供商 logo 预设、独立凭据输入、配置恢复、Embedding / 转写 / 声纹 API 路由已实现。真实本地语音模型仍属于阶段 F。实现细节见 模型提供商与模型发现开发说明

第二阶段不新增平行 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 扩展

以下为后续计划的可选字段;阶段 E 的 GET /api/providers/{provider_id}/models 实际 item 仍只包含 modeldisplay_namecapabilities

{
  "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
  • ToolCallDeltatool_call_id 聚合,完成时参数必须是合法 JSON Object。
  • Usage 字段统一为 input_tokensoutput_tokenstotal_tokens
  • Model Stream 在成功或失败后都发送且只发送一个终止事件 Done;失败顺序为 ErrorDoneDone.data.statuscompletedfailedcancelled
  • 浏览器取消 Fetch 或 SSE 后,服务端必须取消上游 Provider 请求。
  • 不支持 reasoning 的 Provider 不发送伪造 ThinkingDelta。

阶段 E 补充:取消或关闭迭代器直接关闭上游连接并传播取消,不向已断开的客户端继续发送 Done。内部带点号、长名称的工具映射为合法的 64 字符以内名称,响应恢复原命名空间,映射在请求内隔离。实际流中断错误码为 PROVIDER_STREAM_TRUNCATEDPROVIDER_INVALID_RESPONSE 用于无效结构/参数。上面的 Done.data.status 适用于真实 HTTP Adapter;开发 Mock 保留原有测试事件。

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

8.5 阶段 E 模型路由接口(已实现)

方法 路径 契约
GET /api/model-routing {config, local_backends}
PUT /api/model-routing 提交 ModelRoutingConfig,返回递增版本配置
POST /api/models/embeddings {texts: string[]}{vectors, source, model_id, dimensions, fallback_reason}
POST /api/media/speaker-matches {attachment_id, reference_attachment_id}{score, source, fallback_reason}

ModelRoutingConfig 包含 versionembeddingtranscriptionspeaker_matching。每个能力为 null 或 {provider_id, model, endpoint, dimensions?}。dimensions 仅 Embedding 使用,范围 116384endpoint 是选定 Provider 下不带查询的路径,不能传第二个 URL。PUT 不提交 GET 返回的 local_backends;版本冲突返回 409 MODEL_ROUTING_VERSION_CONFLICT。删除仍被引用的 Provider 返回 409 PROVIDER_IN_USE

本阶段远程能力仅接受 OpenAI Chat / Compatible HTTP 配置,默认路径分别是 /embeddings/audio/transcriptions/audio/speaker-matches。最后一个是本项目自定义 multipart 接口,不是公共 OpenAI 标准协议:请求 model、file、reference_file,响应有限 01 的 score。转写采用 multipart model、file、可选 language,必须返回非空 text。文件来自受控附件目录,限制 25 MiB。

TranscriptionJob 新增可选 source: api|local|sidecarfallback_reason。保留已有转写 Job 路径;diarization=true 返回失败 Job,错误为 DIARIZATION_NOT_IMPLEMENTED,不能静默忽略。

无配置时调用本地接口;有配置时 API 优先,网络/鉴权/限流/结果无效时回退。本地 Embedding 当前为 hash 占位;本地 ASR / 声纹后端尚未安装时返回 LOCAL_MODEL_NOT_INSTALLED,而非伪成功。远程 Embedding 独立索引并检查完整覆盖,模型变化后需重建;不与本地向量混算。

Provider PATCH 支持 provider_type;普通配置持久化到 SQLite,凭据继续独立加密。预设新增 logo_id、description、capabilities,前端图标随应用打包。

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 取值:queuedrunningcompleted | failed | cancelled。失败/取消时 errorerror_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 事件流(RunStartedCaseCompleted* → 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,前端降级到 FTSPOST /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 首批支持 notemarkdownmarkdown 来源用于尚未保存的预览,字段大小受限且不持久化到 Trace。format 固定为 htmlpdfdocx

响应:

{
  "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 HostWeb 开发模式由相同 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

跨模块字段需要修改时:

  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 公共契约先行

先冻结并实现:

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 层。

Benchmark Embedding 运行归属(阶段 E 集成修复)

config_snapshot.local_embedding 仅表示本地基线;config_snapshot.embedding{ "policy": "per_case", "details": "cases[].embedding" }。报告与 CaseCompleted 事件的逐样本 embedding 包含实际 sourceapi/local/not_used/unavailable)、model_id、dimensions,以及可选 version、fallback_reason、requested_route、route_version、attempted_space。requested_route 仅含提供商引用、模型、相对端点和维度,不包含 API Key 或凭据引用。FTS 不使用 Embedding,标记 not_used;远程失败或索引不完整回退时记录实际本地模型及原因。

阶段 F 收尾接口补充(2026-09-05

接口/字段 行为
POST /api/media/attachments 可选 Idempotency-Key Header,16–100 位字母、数字、下划线或连字符。同键同扩展名同内容返回同 attachment_id;同键同扩展名内容不一致返回 409 IDEMPOTENCY_CONFLICT。上传仍受 25 MiB 限制。
TranscriptNoteRequest.update_existing 默认 false;true 时将新修订安全写入相同导出选项对应的笔记。无基线返回 409 NOTE_UPDATE_BASELINE_MISSING;正文改变返回 409 NOTE_CONTENT_CONFLICT。同修订重复调用保持幂等。
本地模型 disk_bytes 权重目录实际字节数;无法读取为 null。与下载 bytes/total 分开。
GET /api/local-models/diagnostics scope=application_last_200_attempts,应用 SQLite 中最近 200 条诊断,包含调用及回退事件。未实际开始推理时不伪造 actual_device。
GET /api/usage 增加 audio_request_count、可空的 audio_secondsaudio_covered_requests,适用原有时间/提供商/模型/来源过滤。次数按 transcription/speaker_matching 实际 attempt;未报告时长不估算。
POST /api/providers/request-rules/validate 输入/输出 {version:1, request_overrides:[...]};最多 100 条,复用请求扩展校验,不保存提供商。
POST /api/providers/request-probe 输入 {provider:ProviderCreateRequest, stream:boolean};固定短消息真实聊天推理,45 秒超时。成功返回 success/stream/model/message;空响应 422、供应商错误 502、超时 504。只使用 credential_id,不接收明文密钥。

请求预览新增 capability 选择(chat/embedding/transcription/speaker_matching),仍只返回隐藏正文的请求体。实际扩展字段是否被供应商接受,以推理响应为准。