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