基于 LangChain/LangGraph + MCP + ChromaDB 的医疗辅助诊断系统,支持双模式:单 Agent 通用对话 与 4 阶段结构化诊断流水线(病历采集 → 鉴别诊断 → 证据检索 → 报告生成)。
- 4 阶段多 Agent 协作:病历采集 → 鉴别诊断 → 证据检索 → 报告生成,各阶段独立 ReAct 子图,专属提示词 + 工具子集
- 提示词 100% 确定性切换:阶段路由由 StateGraph 的边硬编码决定,LLM 不参与"进入哪个阶段"的决策,只决定阶段内的工具调用和内容生成
- 患者校验人工节点:未指定患者 ID 时通过 LangGraph
interrupt()暂停,UI 展示有效患者范围(P1001~P1021),用户手动输入 ID 校验后恢复执行 - 中间产物可见:侧边栏实时展示 SOAP 病历摘要、鉴别诊断表、指南证据包,推理链条透明
- Stage 3 零 LLM 确定性检索:对鉴别诊断候选并行调用 RAG(ThreadPoolExecutor),按诊断自动路由到对应专科知识库
- 报告逐 token 流式输出:Stage 4 报告实时打字机效果
- 智能医学问答:基于医学知识库(RAG),提供专业的医学知识检索和问答
- 辅助诊断:获取患者基本信息、生命体征、就诊科室,辅助临床判断
- 诊断报告生成:正则匹配报告意图,确定性切换报告提示词
- 多轮对话记忆:基于 LangGraph Checkpointer 保留对话历史;
trim_history中间件超出 token 预算自动裁剪最早消息,防多轮膨胀 - 输出安全护栏:
medical_guardrail中间件检测到具体用药/剂量内容时,确定性附加免责声明(不依赖模型自觉遵守 prompt)
- MCP 工具封装:7 个专业医疗工具通过 MCP(Model Context Protocol)streamable-http 传输提供服务;子进程动态端口启动,双重检查锁保证多会话下单例安全
- 专科 RAG 过滤:知识库按 8 个专科组织(子目录名作 department 元数据),检索可按专科过滤
- 混合检索 + 重排序:BM25(jieba 中文分词)与向量双路召回经 EnsembleRetriever 融合,gte-rerank 精排名次与召回名次二次 RRF 融合,解决单一 rerank 模型对关键词型查询的排序偏差
- 检索质量可度量:
tests/内置 30 条专科评测集(含 10 条同义改写难例),一键对比基线与混合检索命中率 - 双模型提供方:推理模型支持 Kimi(月之暗面)/ 通义千问切换,嵌入固定用阿里 text-embedding-v4
- 无闪屏流式 UI:Streamlit fragment 局部刷新,生成过程页面稳定不闪烁
- 手动停止生成:关闭事件流生成器,真正中断底层 LLM 流(非仅停止显示)
- 容错兜底:工具失败自动降级、阶段递归限制防死循环、结构化解析本地优先 + LLM parser 兜底、LLM 调用统一超时 + 自动重试
- 日志监控:完整的工具调用和模型调用日志
| 类别 | 技术 |
|---|---|
| LLM框架 | LangChain + LangGraph(StateGraph 父子图、interrupt/resume、Checkpointer、中间件体系) |
| 推理模型 | Kimi(ChatOpenAI,sk-kimi- key 自动走 api.kimi.com/coding/v1)/ 通义千问(ChatQwen) |
| 嵌入模型 | 阿里 text-embedding-v4(DashScope) |
| 检索增强 | BM25(rank_bm25 + jieba)混合召回 + gte-rerank 重排(RRF 双序融合) |
| 工具协议 | MCP(FastMCP + langchain-mcp-adapters,streamable-http) |
| 向量数据库 | ChromaDB(department 元数据过滤) |
| Web界面 | Streamlit(fragment 局部刷新) |
| 配置管理 | PyYAML + python-dotenv |
mediagent/
├── agent/
│ ├── react_agent.py # 单 Agent 核心(MCP客户端、流式事件、对话记忆)
│ ├── pipeline/ # 4阶段诊断流水线
│ │ ├── pipeline_agent.py # PipelineAgent: 组装3个ReAct子图+确定性节点为StateGraph
│ │ ├── pipeline_state.py # PipelineState + SOAP/DDx/Evidence Pydantic 模型
│ │ ├── stage_nodes.py # 阶段节点: validate(interrupt)/stage_1/2/3(纯代码并行RAG)/4
│ │ └── tool_sets.py # 各阶段工具子集
│ └── tools/
│ ├── agent_tools.py # 医疗工具集(7个工具,全部基于真实数据)
│ └── middleware.py # 中间件(工具监控/日志/动态Prompt/记忆裁剪/安全护栏)
├── config/
│ ├── agent.yml # Agent 配置
│ ├── chroma.yml # ChromaDB 配置
│ ├── pipeline.yml # 流水线配置(各阶段递归限制、prompt cache开关)
│ ├── prompts.yml # 提示词路径配置
│ └── rag.yml # 模型配置(chat_provider: kimi/dashscope)
├── data/ # 知识库(按专科分子目录, 子目录名即 department 元数据)
│ ├── external/
│ │ └── patient_records.csv # 患者病历数据(21位患者 P1001~P1021)
│ ├── 心血管内科/ 呼吸内科/ 消化内科/ 内分泌科/
│ ├── 肾内科/ 血液科/ 骨科/ 通用/
├── model/
│ └── factory.py # 模型工厂(按 key 前缀自动选择 Kimi 端点)
├── prompts/
│ ├── main_prompt.txt # 单 Agent 主提示词
│ ├── pipeline_stage1/2/4.txt # 流水线各阶段提示词(Stage3为纯代码节点, 无prompt)
│ ├── rag_summarize.txt # RAG 总结提示词
│ └── report_prompt.txt # 诊断报告提示词
├── rag/
│ ├── rag_service.py # RAG 服务(混合召回 + rerank 的检索编排)
│ ├── reranker.py # gte-rerank 重排 + 召回/精排 RRF 双序融合
│ └── vector_store.py # 向量存储 + BM25语料(递归加载、department元数据、md5去重)
├── tests/
│ ├── eval_dataset.json # 30条专科检索评测集(含10条同义改写难例)
│ └── run_eval.py # 评测脚本(--compare 对比朴素向量基线)
├── utils/
│ ├── config_handler.py # 配置加载器
│ ├── prompt_loader.py # 提示词加载器
│ ├── prompt_cache.py # Kimi Context Caching(实验性)
│ └── ... # 日志/路径/文件工具
├── scripts/
│ └── generate_pdfs.py # txt → PDF 批量转换(reportlab 中文字体)
├── app.py # Streamlit 入口(双模式、fragment局部刷新、interrupt交互)
├── mcp_server.py # MCP 服务端(7个医疗工具,streamable-http)
└── requirements.txt
cd mediagent
python -m venv .venv
# Windows:
.venv\Scripts\activate
# Linux/Mac:
source .venv/bin/activate
pip install -r requirements.txt编辑 .env 文件:
# 推理模型二选一(在 config/rag.yml 中切换 chat_provider)
MOONSHOT_API_KEY=你的Kimi密钥 # chat_provider=kimi(sk-kimi- 开头自动走 coding 端点)
DASHSCOPE_API_KEY=你的通义千问密钥 # chat_provider=dashscope,且嵌入模型必需
注意:嵌入模型固定使用阿里 text-embedding-v4,无论选哪个推理模型都需要
DASHSCOPE_API_KEY。
首次运行前,将医学知识库文档加载到 ChromaDB:
python -m rag.vector_store支持 data/ 下按专科分子目录递归加载,同名 txt+PDF 自动去重,md5 增量更新。
streamlit run app.py浏览器访问 http://localhost:8501。MCP 工具服务随 Agent 自动启动,无需手动操作。
无需手动切换模式:输入框统一接收问题,由确定性意图规则(正则,不经 LLM 判断)自动路由:
- 命中诊断/报告意图(如"诊断分析"、"病情分析"、"出具诊断报告")→ 4 阶段流水线,侧边栏实时显示阶段进度与中间产物
- 普通医学问答 → 单 Agent 模式,侧边栏实时显示工具执行轨迹(检索知识库、查询病历等)
P1001 胸痛3天,请帮我做诊断分析 → 流水线模式
高血压的诊断标准是什么 → 单Agent模式
流水线执行流程:
- 患者校验(纯代码):正则提取患者 ID,不存在则报错终止;未指定则
interrupt()暂停,UI 显示有效范围(P1001~P1021),手动输入 ID 后继续 - Stage 1 病历采集:ReAct Agent 调用患者工具,输出 SOAP 结构化病历(```json 本地提取,失败才走 LLM parser)
- Stage 2 鉴别诊断:ReAct Agent 结合 RAG 生成 3-5 个鉴别诊断(含可能性/依据/关键证据)
- Stage 3 证据检索:零 LLM 确定性节点,对每个候选诊断并行检索对应专科知识库
- Stage 4 报告生成:综合前三阶段产出结构化 Markdown 报告,逐 token 流式输出
侧边栏实时显示阶段进度条和中间产物(SOAP 摘要、鉴别诊断表、证据包)。
7 个医疗工具通过 MCP 封装(mcp_server.py,streamable-http 传输),Agent 启动时自动拉起 MCP 服务。所有工具基于真实数据实现,无随机模拟:
| 工具 | 功能 | 参数 |
|---|---|---|
| rag_summarize | 医学知识库检索并 LLM 总结 | query, department(可选) |
| rag_retrieve | 轻量检索,返回带溯源的原文片段 | query, department(可选) |
| list_patient_ids | 获取所有已建档患者ID列表 | 无 |
| get_patient_vitals | 获取患者最新就诊生命体征 | patient_id |
| get_patient_department | 根据患者最新病情推断就诊科室 | patient_id |
| get_visit_date | 获取患者最新就诊日期 | patient_id |
| fetch_patient_history | 获取患者病历 | patient_id, visit_date(可选) |
chat_provider: kimi # 推理模型提供方: kimi / dashscope
kimi_chat_model: kimi-k2-0905-preview # Kimi 模型
dashscope_chat_model: qwen3-max # 通义千问模型
embedding_model_name: text-embedding-v4 # 嵌入模型(固定阿里)
llm_timeout: 60 # LLM 调用超时(秒)
llm_max_retries: 2 # LLM 调用自动重试次数stage_recursion_limits: # 各阶段最大工具调用轮数(防死循环; stage_3为纯代码节点无需配置)
stage_1: 12
stage_2: 12
stage_4: 8
structured_output_fallback: true # 结构化解析失败时启用 LLM parser 兜底
enable_prompt_cache: false # Kimi Context Caching(实验性)collection_name: mediagent
persist_directory: rag/chroma_db
k: 6 # 最终返回分片数(重排精选后)
fetch_k: 15 # 召回阶段候选数(BM25与向量各自召回后融合)
enable_hybrid: true # BM25+向量混合召回开关
enable_rerank: true # gte-rerank 重排开关(RRF双序融合)
rerank_model: gte-rerank-v2
rerank_threshold: 0.0 # rerank相关性分数阈值, 0表示不过滤
data_path: data
allow_knowledge_file_type: ["txt", "pdf"]
chunk_size: 200
chunk_overlap: 20内置 30 条专科评测集(8 个专科,含 10 条同义改写难例,如"慢阻肺"→COPD 指南、"近三个月平均血糖"→HbA1c):
python tests/run_eval.py # 评测当前配置(混合检索+重排, 按科室过滤)
python tests/run_eval.py --compare # 与优化前配置(朴素向量top-3)对比
python tests/run_eval.py --compare --no-filter # 全库检索(无科室过滤), 检验开放检索下的真实增益当前结果(k=6, fetch_k=15):
| 检索范围 | 混合检索+重排 | 朴素向量 top-3(基线) |
|---|---|---|
| 按科室过滤 | 100%(30/30) | 93.3%(28/30) |
| 全库无过滤 | 100%(30/30) | 93.3%(28/30) |
报告自动写入 tests/eval_report.md(带过滤)/ tests/eval_report_nofilter.md(全库)。
为拆开"召回预算"与"检索方法"各自的贡献,全库无过滤下对四个臂对比:
| 方案 | 命中率 | 说明 |
|---|---|---|
| 向量 top-3(基线) | 93.3% | 优化前线上配置 |
| 向量 top-6 | 100% | 只扩大预算 → 预算贡献是主要增益 |
| 混合 top-6(无 rerank) | 93.3% | 只加混合召回 → BM25 拉入同文档相邻分片反而稀释目标 |
| 混合 + rerank top-6 | 100% | rerank 修复混合召回的排序稀释 → 等预算下 rerank 贡献 +6.7pp |
诚实结论:本知识库规模下,命中率增益主要来自召回预算扩大;rerank 的可量化价值体现在 "混合召回必须配重排才不退化",混合检索+重排的鲁棒性优势需更大型语料才能进一步显现。 调参过程:k/fetch_k 偏小(3/10 或 5/10)时全库检索漏掉 2 条关键词型用例——召回侧已含 目标分片,但 rerank 对药品名/指标类查询的排序偏差将其挤出 top-k;扩大召回预算 + RRF 双序融合后解决。
- 工具失败兜底:工具异常返回错误消息而非抛出,模型感知后兜底回复
- LLM 调用容错:统一配置超时(60s)与自动重试(2 次),网络抖动不再直接终止流水线
- 阶段递归限制:每阶段独立
recursion_limit(pipeline.yml 可配),超限强制退出 - 结构化解析降级:```json 本地正则提取 → Pydantic 校验 → LLM parser 兜底(开关可控)→
stage_error终止(绝不跑错阶段) - MCP 服务守护:持久后台事件循环避免连接反复重建;双重检查锁防多会话并发重复拉起子进程;子进程随主程序退出自动回收
- 知识库一致性:MD5 增量去重 + 文件更新时按来源清理旧分片,避免新旧知识并存
- 本系统为AI辅助诊断工具,不能替代医生的专业诊断和判断
- 所有诊疗决策需由执业医师结合临床实际情况作出
- 药物建议需在医生指导下使用;系统内置输出安全护栏,检测到具体用药/剂量内容时自动附加免责声明
- 涉及急重症症状时,系统会建议立即就医
MIT License
v3.1.0 | 最后更新 2026-08-03
- RAG 检索升级:BM25(jieba) + 向量混合召回,gte-rerank 精排与召回名次 RRF 二次融合
- 新增检索质量评测集(30 条专科用例含同义改写难例)与评测脚本,命中率 93.3% → 100%
- 新增输出安全护栏中间件(用药/剂量内容自动附加免责声明)
- 新增长对话记忆裁剪中间件(trim_messages,防 token 线性膨胀)
- LLM 调用统一超时 + 自动重试
- MCP 工具单例加载加双重检查锁,修复多会话并发初始化竞态
- "停止生成"改为真正关闭事件流(中断底层 LLM 连接)
- 清理死代码/死配置(stage_recursion_limits 与 structured_output_fallback 配置真正生效)
- 知识库更新时按来源清理旧分片,修复 MD5 去重"只增不改"
- 新增 4 阶段诊断流水线(多 Agent 协作,StateGraph 父子图架构)
- 阶段路由纯代码控制,提示词切换 100% 确定性
- 患者校验 interrupt 人工节点(范围提示 + 手动输入校验)
- 侧边栏中间产物展示(SOAP/鉴别诊断/证据包)
- Stage 3 零 LLM 确定性并行专科检索
- 知识库按 8 专科组织,RAG 支持 department 元数据过滤
- 新增 rag_retrieve 轻量检索工具(带溯源引用)
- 推理模型支持 Kimi(按 key 前缀自动选择端点)
- UI 改用 Streamlit fragment 局部刷新,消除闪屏
- 报告生成逐 token 流式输出
- 工具全面 MCP 化(FastMCP + streamable-http)
- 多轮对话记忆(LangGraph Checkpointer)
- 逐 token 流式输出、实时状态展示、手动停止
- 工具全部基于真实病历数据实现
- 首个版本:ReAct 单 Agent 架构(LangChain)
- 医学知识库 RAG 问答(ChromaDB + text-embedding)
- 患者病历查询、生命体征、就诊科室等基础工具
- 诊断报告生成(提示词切换)
- Streamlit Web 界面