一个面向合同条款对比分析的多文档 AI 分析工作台,围绕项目空间、可复现异步工作流、BM25 + Chroma 混合检索、结构化结果、字段级 citation 和 Markdown 导出构建。
这不是一个聊天式 RAG Demo。这个项目的重点不是“问一句、答一句”,而是把多文档分析做成可追溯、可评审、可导出的工作台。
- 工作台而不是聊天窗口:入口是项目空间、文档就绪状态和显式分析任务,不是单轮对话。
- 双状态机:文档生命周期和分析任务生命周期分开建模,便于复现、排错和重试。
- 混合检索有证据支撑:
BM25 + Chroma不是拍脑袋组合,而是由批量评测里的互补召回结果支撑。 - 结构化结果 + 字段级 citation:结果不是停在自然语言段落,而是收口为固定字段及其来源证据。
- 评测口径能撑住追问:README 主口径优先采用监督式评测,而不是沿用更好看但更弱的旧口径。
很多文档 RAG 项目解决的是“怎么回答一个问题”。合同分析更常见的需求其实是另一类:
- 把多份文档放进同一个项目空间统一管理
- 明确知道每份文档是否已经解析、建索引并进入可分析状态
- 对固定字段做可追溯的结构化抽取
- 在两份文档之间做固定维度的对比
- 把结果导出成可以继续流转和复核的 Markdown 报告
所以这个项目从一开始就不是聊天机器人,而是一个AI 分析工作台。
项目空间
-> 文档上传
-> 文档生命周期状态机
-> BM25 + Chroma 检索
-> 分析任务状态机
-> 结构化结果 + 字段级 citation
-> Markdown 导出
核心实现入口:
- backend/app/models/entities.py:
project / document / ingestion_job / analysis_run四类核心实体 - backend/app/workers/runner.py:异步 ingestion 和 analysis 执行链路
- backend/app/services/indexing.py:chunk、BM25 sidecar、向量索引
- backend/app/services/retrieval.py:混合检索与融合逻辑
- backend/app/services/analysis.py:
summary / compare结果构建 - backend/app/services/exporting.py:Markdown 导出
| 项目 | 数值 |
|---|---|
| 默认场景 | 合同条款对比分析 |
| 真实文档 | 21 份合同 |
| 检索查询 | 189 个 |
| 历史无监督口径 | 已退出 README 主口径 |
| 当前主口径 | 优先采用监督式评测 |
这部分回答的是:为什么混合检索不是拍脑袋,以及当前结构化抽取链路覆盖到了什么程度。
| 指标 | 结果 | 说明 |
|---|---|---|
| BM25 独占召回片段 | 1180 |
说明词法检索能覆盖纯向量检索漏掉的固定术语场景 |
| Vector 独占召回片段 | 1225 |
说明语义检索能覆盖 BM25 漏掉的表达变体 |
| 字段覆盖率 | 78.3% |
结构化抽取并不完美,但已经具备可用性 |
| citation 覆盖率 | 100% |
每个输出字段都能回溯到来源证据 |
这部分回答的是:在更小但更可信的监督式语境里,top-k 命中能力和最终结果可接受度分别如何。
| 指标 | 结果 | 说明 |
|---|---|---|
| Recall@4 | Hybrid 47% / Vector 57% / BM25 37% |
小规模监督集上 Vector 更强,但三者并非可互相替代 |
| 可接受率 | 81.5% |
来自监督式 gold set 阶段,不是旧的无监督阶段 |
证据文件:
这些数字最重要的含义是:
1180 / 1225说明这个项目不能被写成“纯向量检索已经够了”。47 / 57 / 37说明监督式评测会暴露真实瓶颈,尤其是 top-k 命中能力。81.5%虽然没有旧口径好看,但更可信,也更适合公开展示。
聊天窗口会把生命周期、任务状态和导出产物都隐藏起来。合同分析更像一条工作流,而不是一次问答,所以这里优先做工作台。
文档 ingestion 失败和分析任务失败是两类不同问题。如果全部揉成一个“处理中”状态,排错、重试和复现都会变得模糊。拆成两套状态机后,问题边界更清楚。
批量评测已经说明两者覆盖的是不同场景。BM25 更擅长固定术语和精确短语,向量检索更擅长语义变体。只保留其中一个,都会丢失真实证据。
这个项目的目标不是“生成一段看起来不错的话”,而是让结果能被复核、对比和导出。固定字段加 citation,比自由文本更适合这个场景。
旧口径来自更早的无监督阶段,解释成本更高,也更容易被追问击穿。当前 README 明确选择监督式评测,是为了让公开主稿更稳。
导出不是装饰功能,而是 AI 结果重新回到人工工作流的交接点。所以它不是“顺手加一个下载按钮”,而是工作台闭环的一部分。
后端:
cd backend
uv sync
cp .env.example .env
# 至少填写 OPENAI_API_KEY,以及 DATABASE_URL 或 MYSQL_PASSWORD
uv run uvicorn app.main:app --reload前端:
cd frontend
npm install
cp .env.example .env
# 确认 VITE_API_BASE_URL 与后端端口一致,默认是 http://127.0.0.1:8000
npm run dev最小验证:
cd backend
uv run pytest环境变量示例:
首次运行前的最低配置要求:
- 后端必须提供
OPENAI_API_KEY - 数据库必须提供
DATABASE_URL,或至少提供MYSQL_PASSWORD - 前端默认读取
VITE_API_BASE_URL=http://127.0.0.1:8000
backend/app/:API、数据模型、检索、分析、worker 链路backend/scripts/:评测脚本、评测报告、gold setbackend/tests/:核心流程的集成测试frontend/src/:工作台前端samples/:样例说明和验证报告,不包含第三方原始合同
默认演示流程:
- 创建项目
- 上传一到两份合同
- 等待文档进入
ready - 运行单文档
summary或双文档compare - 查看结构化字段与 citation
- 导出 Markdown 报告
相关样例说明:
- samples/README.md
- samples/external_candidates/reports/demo_asset_table.md
- samples/external_candidates/reports/validation_report_2026-04-14.md
compare当前只支持两文档和固定字段,不是开放式 N 文档分析。- 监督式 gold set 刻意保持小规模,它的价值在于提高可信度和暴露失败模式,不在于充当 benchmark 级大样本结论。
- 小 gold set 上出现
Vector 57% > Hybrid 47%,README 保留了这个事实,而不是把它藏掉。 - 这个仓库适合作为 AI 应用作品展示,但这里没有把它包装成生产级合同审阅系统。
- 第三方原始合同文件不会随仓库公开分发。
2026.04
