# GraphTutor-基于 LangGraph 的多智能体智能辅导系统 **Repository Path**: git_bai/work ## Basic Information - **Project Name**: GraphTutor-基于 LangGraph 的多智能体智能辅导系统 - **Description**: 这是一个基于 LangGraph 和RAG的多智能体智能辅导系统 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 12 - **Forks**: 1 - **Created**: 2026-06-02 - **Last Updated**: 2026-10-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # GraphTutor — 基于 LangGraph 的多智能体 RAG 辅导系统
#### 对抗性规划 + HIL 人工审批
#### React Flow 交互式 DAG 视图
---
## 快速启动
### 环境要求
- Python 3.11+
- Node.js 18+ 和 npm
- uv(Python 依赖同步与锁文件管理)
- DeepSeek API Key + SiliconFlow API Key
- PostgreSQL(可选,不配置时自动降级为无状态模式)
仓库提供 `.python-version` 和 `.nvmrc`,使用 `uv` / `nvm` 时会自动选择推荐的 Python 3.11 和 Node.js 20。
### 1. 克隆项目
```bash
git clone https://github.com/chipfighter/gaokao_tutor.git
cd gaokao_tutor
```
### 2. 后端
```bash
# 安装 uv(如果尚未安装)
python -m pip install uv
# 按 uv.lock 同步隔离环境,避免使用系统 Python/Anaconda 里的包
python -m uv sync --extra dev --locked
# 配置 API Key
cp .env.example .env
# 编辑 .env,填入 DEEPSEEK_API_KEY 和 SILICONFLOW_API_KEY
# 私有访问登录。默认账号 admin、密码 123456。
# 暴露到公网前必须修改 AUTH_PASSWORD,并生成一个长随机 AUTH_SECRET。
# AUTH_USERNAME=admin
# AUTH_PASSWORD=123456
# AUTH_SECRET=replace_with_a_long_random_string
# AUTH_SESSION_HOURS=12
# HTTPS 部署时设置 AUTH_COOKIE_SECURE=true
# 可选:官方政策 / 招生数据 MCP。优先使用 MCP,失败时自动回退到通用 Web search。
# 二选一配置即可:
# POLICY_MCP_URL=http://127.0.0.1:8765/mcp
# POLICY_MCP_COMMAND="python path/to/policy_mcp_server.py"
# POLICY_MCP_TOOL=policy_search
# POLICY_MCP_TIMEOUT_SECONDS=10
# 可选:试卷/讲义解析 MCP。未配置时 PDF/DOCX 回退本地文本提取,
# 图片回退现有视觉 OCR;扫描版 PDF 建议配置支持 OCR 的 MCP。
# DOCUMENT_MCP_URL=http://127.0.0.1:8766/mcp
# DOCUMENT_MCP_COMMAND="python path/to/document_mcp_server.py"
# PDF_PARSE_MCP_TOOL=pdf_parse
# DOCX_PARSE_MCP_TOOL=docx_parse
# IMAGE_OCR_MCP_TOOL=image_ocr_plus
# QUESTION_SEGMENTER_MCP_TOOL=question_segmenter
# 构建知识库索引
python -m uv run python scripts/build_index.py
# 启动后端
python -m uv run python -m uvicorn app:app --host 127.0.0.1 --port 8002
```
`POST /documents/parse` 接收一个 PDF/DOCX 或多张图片,依次调用对应解析工具和
`question_segmenter`,统一返回题目级结构:
```json
{
"questions": [
{
"number": "17",
"subject": "math",
"stem": "...",
"options": [],
"figures": [],
"detected_knowledge_points": ["导数", "函数零点"],
"source_pages": [3]
}
],
"parser": "mcp:pdf_parse",
"segmenter_used": false
}
```
### 3. 前端
```bash
cd frontend
npm install
# 前端统一使用 npm:仅保留 frontend/package-lock.json,避免提交 frontend/pnpm-lock.yaml
# 创建前端环境配置
echo "NEXT_PUBLIC_API_URL=http://localhost:8002" > .env
# 启动开发服务器
npm run dev
```
访问 `http://localhost:3000` 查看完整界面。
### 4. Docker Compose(生产部署)
```bash
docker compose up -d
# 可选:启用 Jaeger 追踪
docker compose --profile observability up -d
```
### 5. 上线配置
上线前至少确认这些环境变量:
```bash
# 私有登录
AUTH_USERNAME=admin
AUTH_PASSWORD=replace_with_a_strong_password
AUTH_SECRET=replace_with_a_long_random_string
AUTH_COOKIE_SECURE=true
# 用户级每日限额
QUOTA_ENABLED=true
QUOTA_DAILY_REQUESTS=200
QUOTA_DAILY_TOKENS=300000
QUOTA_DAILY_UPLOADS=50
QUOTA_DAILY_RETRIES=30
# 上传安全
DOCUMENT_PARSE_MAX_FILE_MB=25
DOCUMENT_PARSE_MAX_FILES=12
OCR_MAX_IMAGE_MB=8
UPLOAD_TASK_TIMEOUT_SECONDS=180
UPLOAD_MAX_UNCOMPRESSED_MB=100
UPLOAD_MAX_COMPRESSION_RATIO=100
UPLOAD_AV_COMMAND=
UPLOAD_AV_TIMEOUT_SECONDS=30
```
`UPLOAD_AV_COMMAND` 留空时只启用本地静态检查:扩展名/MIME/文件签名一致性、PDF 活动内容拦截、DOCX 宏与压缩包异常检查。生产环境建议接入杀毒命令,例如 `clamscan --no-summary`。
每日 quota 按认证用户记录在 `data/quota/daily.json`,上传审计记录在 `data/audit/uploads.jsonl`。这两个目录是运行时数据,默认不提交到 Git。当前实现适合单机部署;多实例部署应迁移到 Redis 或 PostgreSQL。
---
## SSE 事件协议
| 事件类型 | 描述 | 示例载荷 |
| -------- | ---- | -------- |
| `thread_id` | 会话标识(流开始) | `{"type":"thread_id","thread_id":"abc..."}` |
| `node_event` | 节点生命周期 | `{"type":"node_event","node":"drafter","status":"start","duration_ms":1234}` |
| `token` | 流式 Token(逐字推送) | `{"type":"token","content":"你"}` |
| `text` | 非流式节点完整输出 | `{"type":"text","content":"...","node":"plan_output"}` |
| `usage` | Token 用量统计 | `{"type":"usage","node":"drafter","input_tokens":500,"output_tokens":200}` |
| `interrupt` | HIL 中断暂停 | `{"type":"interrupt","draft":"...","thread_id":"..."}` |
| `done` | 流完成标记 | `{"type":"done"}` |
| `error` | 错误信息 | `{"type":"error","message":"..."}` |
---
## 项目结构
```text
gaokao_tutor/
├── app.py # FastAPI SSE 端点 + 生命周期管理
├── Dockerfile # 多阶段构建(前端 + 后端)
├── docker-compose.yml # 一键部署(后端 + PostgreSQL + Jaeger)
├── config/
│ ├── settings.yaml # 运行参数(温度、超时、重试上限)
│ └── prompts/ # 16 套 XML 提示词模板
├── src/
│ ├── graph/
│ │ ├── builder.py # StateGraph 构建与编译(19 个节点)
│ │ ├── state.py # TutorState TypedDict(26 个字段 + context_reducer)
│ │ ├── supervisor.py # 意图路由 + 关键词提取(Structured Output)
│ │ ├── academic.py # 并行 RAG+Web 检索 → 答案生成 → 幻觉评估 → 重试
│ │ ├── planner.py # 政策搜索 + 情绪/资源情报收集
│ │ ├── plan_adversarial.py # 对抗性起草/审查循环 + HIL 反馈路由 + 计划微调
│ │ ├── emotional.py # 情绪支持
│ │ └── llm.py # LLM 工厂 + streaming + 跨厂商容灾降级
│ ├── rag/
│ │ ├── loader.py # 文档加载器(PDF/MD/TXT + 自定义分块)
│ │ ├── indexer.py # 向量索引构建(ChromaDB / NumPy 降级)
│ │ ├── retriever.py # 混合检索:向量 + BM25 + Reranker
│ │ ├── section_splitter.py # 中文试卷章节感知分割器
│ │ ├── simple_store.py # 纯 NumPy 向量存储(onnxruntime 不可用时降级)
│ │ └── reranker.py # BGE-Reranker API 封装
│ ├── tools/ # LangChain Tool 封装(RAG 检索 / Web 搜索)
│ ├── tracing/ # OpenTelemetry 初始化 + @traced_node + SQLite 导出
│ ├── config/ # YAML 配置加载 + XML 提示词缓存
│ ├── database/ # PostgreSQL Checkpointer 管理
│ └── schemas.py # Pydantic 请求模型
├── frontend/
│ ├── app/page.tsx # 主页面:SSE 消费、HIL 反馈状态管理
│ └── components/
│ ├── chat-area.tsx # 消息气泡 + Markdown/GFL 渲染
│ ├── plan-review.tsx # HIL 计划审阅(编辑/反馈/导出 Markdown)
│ ├── right-panel.tsx # React Flow 交互式 DAG + 节点轨迹 + 日志
│ └── left-sidebar.tsx # 对话历史管理
├── data/ # 知识库:高考试卷(语文/数学)
├── scripts/ # 离线索引构建脚本
└── tests/ # 27 个测试文件,覆盖所有节点/工具/RAG/HIL
```
---
## 测试
换电脑或交接开发前,先阅读 [`docs/PROJECT_HANDOFF.md`](docs/PROJECT_HANDOFF.md);实际换机当天可直接按 [`docs/NEW_MACHINE_CHECKLIST.md`](docs/NEW_MACHINE_CHECKLIST.md) 核对。
```bash
# 一键工程基线:环境、后端测试/覆盖率、静态检查、前端构建
python -m uv run python scripts/run_baseline.py
# 单元测试(无需在线 API,全部 Mock)
OTEL_TRACING_ENABLED=false python -m uv run python -m pytest tests/ --ignore=tests/test_integration.py -v --tb=short
# 只检查测试收集,适合排查依赖是否可复现
python -m uv run python -m pytest --collect-only -q
# 评测 harness(golden case 与执行逻辑分离)
python -m uv run python scripts/run_eval.py --suite quality_gate --output artifacts/eval/
python -m uv run python scripts/run_eval.py --suite rag --output artifacts/eval/
python -m uv run python scripts/run_eval.py --suite routing --output artifacts/eval/
python -m uv run python scripts/run_eval.py --suite hallucination --output artifacts/eval/
python -m uv run python scripts/run_eval.py --suite planning --output artifacts/eval/
# 前端构建检查
cd frontend && npm run build
```
`quality_gate` 是上线前固定质量门禁,会聚合 `routing`、`rag`、`hallucination` 三类核心指标:Supervisor routing accuracy、RAG Recall@K/MRR/Hit Rate、幻觉评估 pass rate/faithful recall/hallucination recall。单项 suite 仍可独立运行,便于定位失败来源。
RAG golden 评测集人工从 `data/chinese/`、`data/math/`、`data/english/` 的高考语文试卷、数学知识点、英语知识点资料中构建,覆盖精确试卷召回、章节召回、宽泛主题检索、概念/公式/方法/模板/策略检索等查询类型。`scripts/run_eval.py --suite rag` 会输出整体 Recall@K、Precision@K、MRR、Hit Rate、平均延迟,并按 `subject`、`topic`、`query_type`、`difficulty` 生成 breakdown,便于发现具体薄弱维度。`scripts/run_eval.py --suite hallucination` 会用 golden 上下文与回答样例校验 `evaluate_hallucination` 对忠实回答和编造回答的判别能力。
提交前可先运行不依赖 API 密钥的 Golden Dataset 校验:
```bash
python -m uv run python scripts/validate_golden_datasets.py
python -m uv run pytest tests/test_eval_harness.py -q --tb=short
```
这两项也由 CI 的 `golden-dataset-gate` job 执行:前者检查每个 YAML 的 schema、元数据、阈值和 case ID,后者回归评测 harness 的加载、覆盖率和阈值逻辑。这样数据集格式错误会在进入在线评测前被发现。
评测 JSON 和 Markdown 报告会额外输出 `cost_latency`,包含 `total_tokens`、`node_tokens`、`wall_time_ms`、`node_latency_ms`、`fallback_used`、`tool_rounds`、`retry_count`、`adv_round` 等字段,用于量化 RAG、reranker、Web search 和 Agent tool loop 的成本与延迟变化。
评测支持 `--seed` 参数(默认 `42`)。报告会保存 Git commit、Python 版本、运行平台和 `config/` 文件哈希,方便复现和比较不同实验配置;敏感环境变量不会写入报告。
两份同一数据集版本的评测结果可以横向比较:
```bash
python -m uv run python scripts/compare_eval_reports.py \
--baseline artifacts/eval/baseline.json \
--variant artifacts/eval/variant.json
```
对比结果统一写入 `artifacts/experiments/`,同时生成 JSON 和 Markdown;报告中的 `delta` 定义为 `variant - baseline`,如果 suite 或数据集版本不一致会直接拒绝比较。
多次评测结果可以聚合成本和延迟:
```bash
python -m uv run python scripts/aggregate_eval_costs.py --input artifacts/eval
```
聚合报告按 suite 输出 token、总耗时、平均耗时、P95、重试次数、工具轮次和 fallback rate,结果写入 `artifacts/experiments/`。
前端 `frontend/next.config.mjs` 显式设置了 `turbopack.root` 和 `outputFileTracingRoot`,确保 Next 在用户目录存在其他 lockfile 时仍以 `frontend/` 作为 workspace 和 standalone tracing 根目录。
HTTP 入口的离线 contract smoke 可运行:
```bash
python -m uv run pytest tests/test_e2e_contract.py -q --tb=short
```
该测试真实请求 `/stream`、`/resume` 和 `/documents/parse`,只替换模型/解析器实现,不需要 API key;它验证 SSE 响应和上传任务状态。
前端浏览器 smoke 使用 Playwright 验证登录、提问和 SSE 答案渲染:
```bash
cd frontend
npx playwright install chromium
npm run test:e2e
```
浏览器测试通过网络拦截提供确定性的 API/SSE 响应,因此无需启动真实后端或配置模型密钥,并已接入 CI 的 `frontend-build` job。
---
## 许可证
[MIT](./LICENSE)