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

37 KiB
Raw Blame History

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

文档状态:接口冻结草案

更新日期:2026-08-31

依据:../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. 状态说明

标记 含义
已实现 第一阶段接口已经存在,第二阶段保持兼容
扩展 路径已存在,第二阶段增加字段、事件或行为
计划新增 第二阶段需要新增实现
内部 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 状态

第二阶段异步任务统一使用:

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

{
  "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 集合,避免半注册状态。

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。前端仅执行白名单 effect;未知类型显示结果但不执行。

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

7.6 更新 Settings 和 Secret

PUT /api/plugins/{plugin_id}/settings

{
  "schema_version": 1,
  "values": { "result_limit": 20 }
}

该接口拒绝 secret 字段。Schema 版本过期返回 PLUGIN_SETTINGS_VERSION_CONFLICT 并附当前版本。

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 或前端持久化。

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
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,继续使用第一阶段接口:

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
  • ToolCallDeltatool_call_id 聚合,完成时参数必须是合法 JSON Object。
  • Usage 字段统一为 input_tokensoutput_tokenstotal_tokens
  • Model Stream 在成功或失败后都发送且只发送一个终止事件 Done;失败顺序为 ErrorDoneDone.data.statuscompletedfailedcancelled
  • 浏览器取消 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,
  "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

{
  "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

{
  "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_RUN_NOT_FOUND
BENCHMARK_RUN_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 层。