fix(provider): 修复索引事务回滚与工具名分片并同步主分支

This commit is contained in:
2026-09-04 07:15:52 +08:00
30 changed files with 2232 additions and 98 deletions
+1
View File
@@ -30,6 +30,7 @@
- [AI Core 与 Agent Core 开发说明](development/AI-Core与Agent-Core开发说明.md)
- [Knowledge 与 Retrieval Core 开发说明](development/Knowledge与Retrieval-Core开发说明.md)
- [Benchmark 开发说明](development/Benchmark开发说明.md)
- [模型提供商与模型发现开发说明](development/模型提供商与模型发现开发说明.md)
- [MCP Bridge 与 Plugin Host 开发说明](development/MCP-Bridge与Plugin-Host开发说明.md)
- [独立 MCP Server 配置中心开发说明](development/独立MCP-Server配置中心开发说明.md)
@@ -5,7 +5,7 @@
> 适用范围:桌面客户端、本地知识库、RAG、Agent、Skill、多模型接入、多模态处理与可选云同步
> 目标读者:前端、Rust 桌面端、Python AI Core、算法、测试与后续接手项目的开发成员
> 实施状态更新:2026-09-02。本文同时包含目标架构、当前实现和第二阶段接口基线。第一阶段已完成 Vue Web 联调前端、FastAPI、Knowledge/Retrieval、Agent/Tool/Permission、Skill/Plugin 声明式运行时、Mock/OpenAI-Compatible/Ollama Provider、DeepSeek/OpenAI 预设、模型发现及开发阶段 Fernet 凭据存储。Web Workspace 已通过 FastAPI 接入后端配置的真实单 Vault;第二阶段 Agent Trace 持久化、分页快照、可恢复 SSE、stdio MCP Bridge、隔离 Plugin Host、Plugin Command 与 Plugin Settings/Secret Contract 已完成。阶段 E 已完成 Responses/Anthropic 协议、国内 logo 预设、Provider 配置恢复和 Embedding/转写/声纹 API 路由;本地语音模型仍为阶段 F 接口预留。后续继续接入真实音频处理、Benchmark、文档导出、主题包、Trace 可视化、Mermaid 和函数图像。Tauri/Rust Host、Stronghold、原生多 Vault 文件系统和 Sync Server 仍未实现。
> 实施状态更新:2026-09-04。本文同时包含目标架构、当前实现和第二阶段接口基线。第一阶段已完成 Vue Web 联调前端、FastAPI、Knowledge/Retrieval、Agent/Tool/Permission、Skill/Plugin 声明式运行时、Mock/OpenAI-Compatible/Ollama Provider、DeepSeek/OpenAI 预设、模型发现及开发阶段 Fernet 凭据存储。Web Workspace 已通过 FastAPI 接入后端配置的真实单 Vault;第二阶段 Agent Trace 持久化、分页快照、可恢复 SSE、stdio MCP Bridge、隔离 Plugin Host、Plugin Command 与 Plugin Settings/Secret Contract 已完成。阶段 E 已完成 Responses/Anthropic 协议、国内 logo 预设、Provider 配置恢复和 Embedding/转写/声纹 API 路由;本地语音模型仍为阶段 F 接口预留。RAG Benchmark 检索评测(Dataset 加载、异步运行、SSE 进度、指标聚合与报告)已完成,Agent Benchmark 暂缓。后续继续接入真实音频处理、文档导出、主题包、Trace 可视化、Mermaid 和函数图像。Tauri/Rust Host、Stronghold、原生多 Vault 文件系统和 Sync Server 仍未实现。
---
@@ -2100,9 +2100,13 @@ MRR
Citation Hit Rate
P50 Latency
P95 Latency
total_cases
successful_cases
failed_cases
failure_rate
```
Benchmark 参数、Embedding 模型、Reranker、数据集版本和运行环境需要一起记录,保证不同实验结果可以复现。
失败样本按零分计入质量指标分母,报告同时输出样本构成字段标明实际分母。Benchmark 参数、Embedding 模型、Reranker、数据集版本和运行环境需要一起记录,保证不同实验结果可以复现。
### 20.3 Agent Benchmark
@@ -62,9 +62,9 @@
| 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 | 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` | 计划新增 | 分页获取导出任务 |
@@ -912,7 +912,7 @@ Dataset 从仓库或受控导入目录注册。API 不接受调用方提交任
配置快照必须记录 Embedding model ID/version/dimension、Reranker、索引版本、Dataset Hash 和运行环境。
### 9.5 创建 Agent Benchmark
### 9.5 创建 Agent Benchmark(暂缓,未暴露接口)
`POST /api/benchmarks/agent/runs`
@@ -946,12 +946,16 @@ RAG 和 Agent 创建接口均返回 `202 BenchmarkRun`
"metrics": null,
"config_snapshot": {},
"error": null,
"error_code": null,
"created_at": "2026-08-31T10:30:00Z",
"started_at": null,
"completed_at": null
}
```
`status` 取值:`queued``running``completed` | `failed` | `cancelled`。失败/取消时 `error`
`error_code` 只返回项目错误码与安全消息,不暴露第三方堆栈。
公共接口:
| 方法 | 路径 | 用途 |
@@ -962,6 +966,10 @@ RAG 和 Agent 创建接口均返回 `202 BenchmarkRun`
| POST | `/api/benchmarks/runs/{run_id}/cancel` | 取消运行 |
| GET | `/api/benchmarks/runs/{run_id}/report` | 获取结构化完整报告 |
SSE 事件流(`RunStarted``CaseCompleted`* → `RunCompleted` | `RunFailed` | `RunCancelled`):
`GET /api/benchmarks/runs/{run_id}/events` 支持 `Last-Event-ID``?after_sequence=` 游标恢复,
`RunCompleted` / `RunFailed` / `RunCancelled` 为终止事件,收到后即断流。
### 9.7 指标 Contract
RAG
@@ -974,10 +982,17 @@ RAG
"mrr": 0.81,
"citation_hit_rate": 0.89,
"p50_latency_ms": 24.5,
"p95_latency_ms": 67.3
"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
```json
@@ -1001,8 +1016,10 @@ 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 与索引兼容
+76
View File
@@ -0,0 +1,76 @@
# Benchmark 开发说明
> 所属模块:Knowledge / Retrieval Core(后端,负责人 yxx)。RAG Benchmark 已交付;Agent Benchmark 暂缓,待 Agent Runtime 完成后在同一契约下补齐。
## 定位
Benchmark Service 用受控 Dataset 对检索引擎做可复现评测:创建即返回 queued、后台 asyncio.Task 执行、SSE 实时推送进度、结束后产出结构化报告。CLI、测试与前端报告页复用同一 Service,不各自实现指标。
## 接口
| 方法 | 路径 | 用途 |
| --- | --- | --- |
| GET | `/api/benchmarks/datasets?kind=rag` | 枚举受控目录下的 Dataset 元信息 |
| POST | `/api/benchmarks/rag/runs` | 创建 RAG Benchmark202 |
| 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` | 结构化完整报告 |
Agent Benchmark 的 `/api/benchmarks/agent/runs` 未暴露(暂缓),不在 OpenAPI 注册占位接口。
## Dataset
Dataset 来自 `settings.benchmark_datasets_path`(默认 `backend/data/benchmarks`),API 不接受调用方提交任意路径。按文件名 stem 精确匹配 `{dataset_id}.json`,与请求无关文件的损坏(JSON 语法错误、UTF-8 解码错误、顶层非对象)不会阻断加载;只有目标文件本身损坏才返回 `BENCHMARK_DATASET_INVALID`
RAG Case 结构:`case_id``query``expected_note_ids``expected_block_ids``citation_required``tags``citation_required=true` 时必须声明 `expected_block_ids`,否则无法计算 Citation Hit Rate。
## 运行生命周期
`queued → running → completed | failed | cancelled`
- 创建时校验索引兼容性:索引非空、Embedding model/dim 与当前引擎一致、vector/hybrid 时向量索引非空;不满足返回 `BENCHMARK_INDEX_INCOMPATIBLE`(409),避免把环境/索引错误误判为检索质量差。
- 内存注册表上限 `MAX_RUNS=100`,超限只淘汰终态 run;满容量且全为活动 run 时返回 `BENCHMARK_CAPACITY_EXCEEDED`429)。
- 失败/取消只向公开响应暴露项目错误码与安全消息,详细异常进入日志,不通过 HTTP/SSE 返回。
## 指标
RAG 按 (mode, case, repeat) 逐样本计算,再按 mode 聚合:
- 质量:`hit_at_1``hit_at_5``recall_at_k``mrr``citation_hit_rate`
- 延迟:`p50_latency_ms``p95_latency_ms`(仅统计成功样本);
- 样本构成:`total_cases``successful_cases``failed_cases``failure_rate`
失败样本按零分计入质量指标分母,报告据此可知实际分母,避免把执行失败误判为检索质量差。
## 事件与 SSE
事件流:`RunStarted → CaseCompleted* → RunCompleted | RunFailed | RunCancelled`
`GET /api/benchmarks/runs/{run_id}/events` 支持 `Last-Event-ID``?after_sequence=` 游标恢复(复用 Agent SSE 的解析逻辑),`RunCompleted` / `RunFailed` / `RunCancelled` 为终止事件,收到后断流。
## 错误码
```text
BENCHMARK_DATASET_NOT_FOUND
BENCHMARK_DATASET_INVALID
BENCHMARK_INDEX_INCOMPATIBLE
BENCHMARK_CAPACITY_EXCEEDED
BENCHMARK_RUN_NOT_FOUND
BENCHMARK_RUN_FAILED
BENCHMARK_CASE_EVALUATION_FAILED
```
## 配置快照
报告与运行记录保存 `config_snapshot`dataset hash/version、modes、retrieval 参数、Embedding model/version/dim、Reranker、索引元数据、App 版本与环境、Python 版本,保证不同实验结果可复现。
## 测试
```powershell
cd backend
uv run pytest -q
```
`tests/test_benchmark.py` 覆盖数据集注册与校验、指标纯函数、端到端运行、取消、索引兼容、容量与失败样本聚合;`tests/test_retrieval.py` 覆盖 FTS 阈值与分页 total 一致性。
@@ -198,7 +198,7 @@ cd backend
uv run pytest -q
```
当前后端完整测试共 136 个用例通过(单元 + 端到端)。测试通过 `tests/conftest.py` 的 autouse fixture 把
当前后端完整测试共 218 个用例通过(单元 + 端到端)。测试通过 `tests/conftest.py` 的 autouse fixture 把
数据目录/DB/Vault 重定向到临时目录,不读写真实 `backend/data`,任何本机状态下结果确定。
## 配置
@@ -232,4 +232,6 @@ rag.search
- Embedding / Reranker 为轻量实现,后续替换为真实模型(接口不变)。
- 小语料下 hybrid 检索召回偏宽(向量 Top-K 覆盖全部 block),可加相关性阈值收紧。
- 重建为同步 + 全量,后续接入增量索引与异步任务队列。
- 检索 Benchmark 建立
- RAG Benchmark 建立`POST /api/benchmarks/rag/runs` 创建即返回 queued、后台 Task 执行,
通过 SSE 实时推送进度,报告含逐 Case 结果与 `total_cases` / `successful_cases` / `failed_cases` / `failure_rate`
- Agent Benchmark 暂缓,待 Agent Runtime 完成后交付。
@@ -74,7 +74,9 @@ PUT body 只提交 `config` 的内容。`version` 为读取时的版本,成功
笔记索引始终保留现有 hash/sqlite-vec 本地基线,远程向量写入独立 `routed_block_vectors` 表。远程查询只搜索对应空间,并要求覆盖全部当前 Block。API 失败、索引缺失、不完整或损坏时使用完整本地索引。切换模型、URL、维度后应在设置中重建全部索引。旧空间与当前文本不会混合打分,删除笔记或重建索引会通过外键清理远程向量。
当前远程侧索引采用 SQLite JSON 向量和精确余弦扫描,复杂度 O(Block 数量 × 维度),适用于当前小型 Vault;后续大规模索引需替换为按空间隔离的 ANN。网络等待发生在数据库写事务之前,当前仍会增加保存或重建延迟,异步索引队列尚未接入。
当前远程侧索引采用 SQLite JSON 向量和精确余弦扫描,复杂度 O(Block 数量 × 维度),适用于当前小型 Vault;后续大规模索引需替换为按空间隔离的 ANN。网络等待发生在数据库写事务之前,当前仍会增加保存或重建延迟,异步索引队列尚未接入。全量重建先在内存中准备全部向量,再使用一个 SQLite 事务更新元数据、FTS、本地与远程向量及任务关联;取消或失败只回滚索引事务,不再覆盖整库文件。准备阶段保留旧索引可查询,代价是内存同时容纳本次重建的向量。
OpenAI Compatible 流中,工具名称可能分片返回。适配器在本轮输出结束后发送完整工具名及已缓冲参数,避免把名称片段当作工具 ID;文本与推理内容仍逐片发送。
无 API 时使用的 `HashEmbeddingProvider` 是确定性特征哈希占位实现,**不是已集成的小型语义模型**。真实本地 Embedding 可实现既有 `EmbeddingProvider` 接口注入。
@@ -94,7 +96,7 @@ PUT body 只提交 `config` 的内容。`version` 为读取时的版本,成功
流式事件依据:[OpenAI Responses streaming](https://platform.openai.com/docs/api-reference/responses-streaming)、[Anthropic streaming](https://platform.claude.com/docs/en/build-with-claude/streaming)。音频请求依据:[SiliconFlow transcription](https://docs.siliconflow.com/en/api-reference/audio/create-audio-transcriptions)。
自动化验证使用虚构凭据、本地附件、httpx.MockTransport 和可注入本地模型,覆盖流式 Tool/Usage/取消、错误映射、回退、索引空间隔离、版本冲突、重启恢复和界面凭据行为。没有使用真实 API Key 或向厂商发送推理请求。最终验证:后端全量 410 项、前端 76 项测试通过,Vue/TypeScript 类型检查和生产构建通过,浅色/深色预设页面与路由保存经过浏览器检查,git diff --check 通过。后端仅保留既有 Starlette 测试客户端弃用提示,前端保留既有大 bundle 提示。
自动化验证使用虚构凭据、本地附件、httpx.MockTransport 和可注入本地模型,覆盖流式 Tool/Usage/取消、错误映射、回退、索引空间隔离、版本冲突、重启恢复和界面凭据行为。没有使用真实 API Key 或向厂商发送推理请求。审阅修复并同步主分支后验证:后端全量 447 项、前端 76 项测试通过,Vue/TypeScript 类型检查和生产构建通过,浅色/深色预设页面与路由保存经过浏览器检查,git diff --check 通过。后端仅保留既有 Starlette 测试客户端弃用提示,前端保留既有大 bundle 提示。
```powershell
cd backend