整个项目的代码由 Claude Code Sonnet 4.6 模型生成。本人只负责选择技术方向、Agent框架、RAG向量化和检索实现方法、部分法律条文爬虫下载等内容。
美国合同法律领域智能问答系统。项目聚焦美国合同法、加州通用法规、联邦破产法与美国宪法相关问答。用户以中文提问,系统通过 RAG 检索英文法律条文与判例,由大语言模型生成附带精确法律引用的中文回答。
覆盖领域: 合同法 · 商法 · 破产法 · 消费者保护法 · 加州法规(BPC/CCP/Corp/Lab/Fam)· 联邦破产法(USC Title 11)· 美国宪法 · 加州宪法
| 特性 | 说明 |
|---|---|
| 双执行模式 | ReAct Agent 自由编排 / Workflow 固定三步流水线,通过 .env 热切换 |
| 两阶段 RAG 检索 | ChromaDB 向量召回(top_k=20)→ BGE-Reranker 精排(top_n=6) |
| 查询变换 | multi_query / rewrite / HyDE / sub_query 四种策略,LLM 自动选择 |
| RRF 融合 | 多路查询结果通过 Reciprocal Rank Fusion(k=60)合并 |
| 引用权威度加权 | 判例 final_score = 0.85 × rerank_score + 0.15 × log(cite_count) |
| 流式输出 | FastAPI SSE 实时推送 token,前端逐字渲染 |
| 多轮对话 | thread_id 隔离的独立会话上下文(react: InMemorySaver;workflow: 进程内字典保留最近 4 轮) |
| Token 追踪 | 实时展示本次 / 累计 token 用量 |
| 双 LLM 提供商 | Qwen(默认)/ Gemini,通过 ACTIVE_LLM_PROVIDER 切换,共用 OpenAI 兼容接口 |
| 法律边界控制 | 超出覆盖范围的问题由 Router 或 System Prompt 自动过滤拒答 |
| Reranker 降级 | Reranker 不可用时自动回退到单阶段向量检索,不中断服务 |
浏览器 (static/index.html)
│ POST /api/chat [SSE]
▼
FastAPI (src/main.py)
│
▼
ChatService (src/services/chat_service.py)
│ 根据 EXECUTION_MODE 分派
├── [react] LawAgent (src/agents/law_agent.py)
│ └── 6 × @tool (src/tools/law_tools.py)
│
└── [workflow] WorkflowOrchestrator (src/agents/orchestrator.py)
├── Step 1: QueryRouter (src/agents/router.py)
├── Step 2: asyncio.gather (并行调 law_tools)
└── Step 3: AnswerSynthesizer
└── LLM 单次合成
- 由 Agent 自主选择工具。
- 适合探索式、多步工具调用场景。
用户输入
→ LawAgent (ReAct, recursion_limit=50)
→ 意图识别 → 选择并调用 RAG 工具(可多次)
→ 工具返回检索证据
→ LLM 生成中文回答(流式)
→ SSE 推送 meta / token / usage / [DONE]
- 先路由,再固定并行检索,最后一次性合成答案。
- 适合对延迟、可控性与可解释性要求更高的场景。
用户输入
→ QueryRouter ── 1次 LLM 调用 (temperature=0.0, max_tokens=200)
输出 RoutePlan { scope, collections[2], query }
→ asyncio.gather ── 2个 Collection 并行检索(耗时 = max 而非 sum)
→ AnswerSynthesizer ── 1次 LLM 合成(evidence + 对话历史)
→ SSE 推送
原始查询
→ QueryTransformer ── 方法选择 + 子查询生成(multi_query 示例:3 条扩展查询)
→ ChromaDB 向量检索 (top_k=20 per query)
→ RRF 融合 ── 多路结果合并(k=60)
→ BGE-Reranker ── Cross-Encoder 精排 → top_n=6
→ cite_count 加权 ── 仅判例 collection
→ ResultFormatter ── 格式化为 LLM 可读法律引用文本
| 组件 | 选型 / 版本 |
|---|---|
| 语言 | Python 3.12+ |
| Web 框架 | FastAPI + Uvicorn |
| Agent 框架 | LangChain 1.2 + LangGraph 1.0 |
| Agent 类型 | ReAct (create_agent) |
| 多轮记忆 | LangGraph InMemorySaver(react)/ 进程内字典(workflow) |
| 向量数据库 | ChromaDB PersistentClient(余弦相似度) |
| 嵌入模型 | BGE-M3(BAAI/bge-m3,1,024 维,本地离线,GPU) |
| 精排模型 | BGE-Reranker-v2-m3(Cross-Encoder,可降级) |
| 向量索引框架 | LlamaIndex(HuggingFaceEmbedding 封装 BGE-M3) |
| LLM | Qwen3.5-plus(默认)/ Gemini 2.5 Pro,OpenAI 兼容接口 |
| 流式传输 | Server-Sent Events (SSE) |
| 前端 | 原生 HTML/CSS/JS,marked.js v9,highlight.js v11 |
| 配置管理 | python-dotenv + Pydantic |
| 代理绕过 | httpx.Client(trust_env=False) |
| GPU | RTX 5060 Ti(单卡 cuda:0) |
总量:167,370 Chunks(判例 148,554 + 条文 18,816)
存储路径:D:/Embeddings_Data/chroma_law_db
| Collection | 内容 | Chunks |
|---|---|---|
cases_contract |
民事/合同类判例(5,800 条) | ~124,000 |
cases_commercial |
商业/破产类判例(5,454 条) | ~16,000 |
cases_regulatory |
监管/宪法类判例(4,573 条) | ~8,554 |
statutes_contract |
Civil Code + Commercial Code | ~6,800 |
statutes_ca_general |
BPC / CCP / Corp / Fam / Lab / Cal. Const. | ~10,700 |
statutes_federal |
USC Title 11 + U.S. Constitution | ~1,316 |
判例(JSONL,共 15,827 条原始案例)
| 源文件 | 记录数 | Collection |
|---|---|---|
civ_state_opinions.jsonl |
2,800 | cases_contract |
civ_opinions.jsonl |
3,000 | cases_contract |
ca_com_opinions.jsonl |
4,235 | cases_commercial |
bankrupt_opinions.jsonl |
1,219 | cases_commercial |
bpc_opinions.jsonl |
2,000 | cases_regulatory |
ca_const_opinions.jsonl |
2,573 | cases_regulatory |
条文(TXT/MD/TEXT,共 3,407 个文件)
| 法典 | 文件数 |
|---|---|
| Civil Code (CIV) | 674 |
| Commercial Code (COM) | 59 |
| Business & Professions Code (BPC) | 1,077 |
| Code of Civil Procedure (CCP) | 618 |
| Corporations Code (CORP) | 402 |
| Family Code (FAM) | 241 |
| Labor Code (LAB) | 293 |
| California Constitution | 33 |
| USC Title 11 (Bankruptcy) | 9 |
| U.S. Constitution | 1 |
- Conda 环境
lang(Python 3.12+,已包含 LangChain、LangGraph、FastAPI、ChromaDB、LlamaIndex 等) - GPU(RTX 5060 Ti 或同等级别),BGE-M3 和 Reranker 模型已下载到本地
.env文件配置完整(见下方)
git clone <repo-url>
cd law-ai-assistant
cp .env.example .env # 填入 API Key 等配置# Git Bash / Linux
bash scripts/run.sh
# Windows PowerShell
./scripts/run.ps1
# 直接运行(必须用 -m 模块方式,src/ 下使用绝对导入)
conda run -n lang python -m src.main
# 切换为 workflow 模式启动
EXECUTION_MODE=workflow conda run -n lang python -m src.main| 场景 | 地址 |
|---|---|
| 本机 | http://127.0.0.1:8010 |
| 局域网 | http://192.168.x.x:8010 |
| 健康检查 | http://127.0.0.1:8010/health |
局域网访问:需在 Windows 防火墙放行 8010 端口:
netsh advfirewall firewall add rule name="LawAgent 8010" dir=in action=allow protocol=TCP localport=8010
# 约 40 分钟,单卡 cuda:0
./scripts/build_index.ps1.env 文件完整配置说明:
# ── LLM 提供商选择 ──────────────────────────────────────────
ACTIVE_LLM_PROVIDER=qwen # qwen | gemini
# ── Qwen(阿里云 DashScope)────────────────────────────────
QWEN_API_KEY=sk-xxxxxxxxxxxxxxxx
QWEN_API_BASE=https://dashscope.aliyuncs.com/compatible-mode/v1
QWEN_MODEL_NAME=qwen3.5-plus
QWEN_TEMPERATURE=0.3
QWEN_MAX_TOKENS=8000
QWEN_TIMEOUT=60
# ── Gemini(中转,可选)────────────────────────────────────
GEMINI_API_KEY=xxxxxxxxxxxxxxxx
GEMINI_API_BASE=https://wuxuai.com/v1
GEMINI_MODEL_NAME=gemini-2.5-pro
GEMINI_TEMPERATURE=0.3
GEMINI_MAX_TOKENS=8000
GEMINI_TIMEOUT=60
# ── FastAPI ────────────────────────────────────────────────
FASTAPI_HOST=0.0.0.0
FASTAPI_PORT=8010
# ── 执行模式 ───────────────────────────────────────────────
EXECUTION_MODE=workflow # react | workflow
# ── ChromaDB ───────────────────────────────────────────────
CHROMA_DB_PATH=D:/Embeddings_Data/chroma_law_db_0327
# ── 嵌入模型(BGE-M3)─────────────────────────────────────
EMBEDDING_MODEL_PATH=D:/Embeddings_Model/models--BAAI--bge-m3/snapshots/5617a9f61b028005a4858fdac845db406aefb181
EMBEDDING_DEVICES=["cuda:0"]
# ── Reranker(BGE-Reranker-v2-m3)────────────────────────
RERANKER_MODEL_PATH=D:/Embeddings_Model/bge-reranker-v2-m3
RERANKER_DEVICE=cuda:0
RERANKER_ENABLED=true
# ── 检索参数 ───────────────────────────────────────────────
RETRIEVAL_TOP_K=20 # 向量召回数量
RERANKER_TOP_N=6 # 精排后保留数量
RETRIEVAL_THRESHOLD_CASE=0.43 # 判例相似度阈值
RETRIEVAL_THRESHOLD_STATUTE=0.35 # 条文相似度阈值law-ai-assistant/
├── .env # 配置(不提交 Git)
├── scripts/
│ ├── run.sh # Linux / Git Bash 启动脚本
│ ├── run.ps1 # Windows PowerShell 启动脚本
│ ├── build_index.ps1 # 离线索引构建脚本
│ └── eval.ps1 # RAG 评测脚本
├── src/
│ ├── main.py # FastAPI 入口:路由注册 + 生命周期
│ ├── config/
│ │ └── config.py # 全局配置:从 .env 读取所有常量
│ ├── schemas/
│ │ └── chat.py # Pydantic 请求/响应模型
│ ├── services/
│ │ └── chat_service.py # 模式分派 + SSE 帧封装 + 全局单例
│ ├── agents/
│ │ ├── law_agent.py # ReAct Agent + TokenUsageTracker + 流式输出
│ │ ├── orchestrator.py # WorkflowOrchestrator(Router → 并行 → 合成)
│ │ └── router.py # QueryRouter:1次 LLM → RoutePlan JSON
│ ├── tools/
│ │ └── law_tools.py # 6 个 @tool(QueryTransformer + RRF + Reranker)
│ ├── rag/
│ │ ├── index_manager.py # LawIndexManager 单例:ChromaDB + BGE-M3 + 两阶段检索
│ │ ├── reranker.py # BGE-Reranker-v2-m3 精排(可降级)
│ │ ├── query_transformer.py # 四种查询变换策略
│ │ ├── result_formatter.py # 检索结果 → LLM 可读法律引用文本
│ │ ├── data_loader.py # StatuteLoader + CaseLoader(TXT/JSONL 解析)
│ │ └── build_index.py # 离线全量索引构建
│ ├── prompts/
│ │ └── law_prompts.py # SYSTEM_PROMPT v3.2 / ROUTER_PROMPT / SYNTHESIZER_PROMPT
│ └── core/
│ └── llm.py # build_llm 工厂函数(供 QueryRouter 使用)
├── static/
│ ├── index.html # 单页前端(ChatGPT 风格)
│ ├── css/style.css
│ └── js/app.js # SSE 消费 + Markdown 渲染 + 会话管理
├── tests/
│ └── rag_eval.py # 5 套件 RAG 评测,自动生成 Markdown 报告
├── logs/
│ └── app.log # UTF-8 中文运行日志
└── docs/
└── project_summary.md # 架构决策与技术细节记录
| 端点 | 方法 | 说明 |
|---|---|---|
/ |
GET | 前端页面 |
/health |
GET | 健康检查 {"status": "ok"} |
/api/session |
POST | 创建新会话,返回 thread_id |
/api/chat |
POST | SSE 流式对话 |
{
"message": "加州非竞争协议是否有效?",
"thread_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}data: {"type": "meta", "thread_id": "abc123"}
data: {"type": "token", "token": "根据加州商业与职业法典..."}
data: {"type": "usage", "this_turn": 3241, "session_total": 9872}
data: [DONE]
| 帧类型 | 触发时机 | 关键字段 |
|---|---|---|
meta |
对话开始 | thread_id |
token |
每个生成 token | token |
usage |
对话结束 | this_turn, session_total |
[DONE] |
流结束 | — |
| 策略 | 适用场景 | 说明 |
|---|---|---|
NONE |
简单直接的查询 | 原始查询直通,零额外开销 |
REWRITE |
查询表达不够精确 | LLM 改写为更标准的法律表述 |
MULTI_QUERY |
复杂或多维度问题 | 生成 3 条扩展查询,RRF 融合 |
HYDE |
查询与文档风格差异大 | LLM 生成假设文档,answer-to-answer 检索 |
SUB_QUERY |
复合型复杂问题 | 拆解为独立子查询并行检索 |
策略由 LLM 在单次调用中自动选择并生成,失败时降级为 NONE。
条文检索使用 searching relevant legal provisions,判例检索使用 searching relevant court opinions,提升 BGE-M3 非对称检索精度。
ChromaDB 向量召回 top_k=20 阈值: 条文 0.35 / 判例 0.43
↓
BGE-Reranker 精排 top_n=6
↓
cite_count 加权 final_score = 0.85 × rerank + 0.15 × log(cite_count+1) / log(max_cite+1)
| 问题类型 | 并行检索耗时 | 总响应时间 | 路由结果 |
|---|---|---|---|
| 加州非竞争协议 | 21.6s | 56.8s | BPC + 消费者保护判例 |
| 违约损害赔偿 | 10.4s | 64.5s | 合同法条文 + 合同判例 |
| 破产未到期合同 | 7.8s | 38.0s | 联邦法规 + 破产判例 |
Q1 耗时较长原因:两库均触发 multi_query(各 3 条),共 6 次向量检索。
通过 .env 中的 EXECUTION_MODE 切换,无需修改代码,对外 SSE 接口完全兼容。
| 维度 | react 模式 | workflow 模式 |
|---|---|---|
| 架构 | ReAct Agent 自由调用工具(最多 20 次) | 固定三步:Router → 并行检索 → 合成 |
| LLM 调用次数 | 不固定(意图识别 + 每次工具决策 + 生成) | 固定 2 次(Router + Synthesizer) |
| 工具调用控制 | Agent 自主决定,可能重复调用 | Router 精确控制:至多 2 个 Collection,各 1 次 |
| 对话历史 | LangGraph InMemorySaver(完整轮次) | 进程内字典,最近 4 轮 |
| 适合场景 | 调试、需要灵活工具编排 | 生产推荐,行为可预测 |
| Token 统计精度 | 完整(含工具调用) | 部分(Router token 暂未计入) |
评测脚本:tests/rag_eval.py,5 个测试套件,自动生成 Markdown 报告。
./scripts/eval.ps1| 套件 | 测试内容 | 结果(chroma_law_db_0327) |
|---|---|---|
| coverage | 6 个 collection 各有召回 | 6 PASS |
| threshold | 阈值过滤噪声能力 | 3 WARN(联邦库数量偏少) |
| reranker | 精排前后排序改善 | 2 PASS |
| perf | 检索延迟基准 | 2 PASS 1 WARN |
| metadata | source 元数据完整性 | (statutes_federal section_range 全空,已知数据特性) |
- 仅供参考 — 本系统提供法律信息,不构成法律建议,具体事务请咨询持牌律师。
- API Key 安全 —
.env已加入.gitignore,禁止将 Key 提交至代码仓库。 - 会话持久化 — 服务重启后对话上下文清空;浏览器 localStorage 仅保留会话标题列表。
- 索引重建 — 法律数据为静态文件,新增数据需重新运行
build_index.ps1(约 40 分钟)。 - 代理环境 — 已配置
httpx.Client(trust_env=False)绕过本机系统代理,确保 API 直连。
本项目仅用于学习与研究目的。法律数据来源于公开法律文献,使用前请确认符合当地法律法规。