医疗信息化多 Skill 组合LangChain 实现
本文介绍了一个门诊病历自动化处理系统,该系统通过封装3个串联Skill实现全流程自动化处理: PDF病历解析Skill:提取PDF格式门诊记录的文本内容 医疗术语结构化Skill:将非结构化文本转为标准化字段(症状、诊断、用药、医嘱) 诊断编码映射Skill:将诊断结果映射为ICD-10国际医疗编码 系统采用LangChain Agent实现流程调度,无需人工干预。
·
针对门诊病历自动化处理场景,封装 3 个串联 Skill:
- PDF 病历解析 Skill:提取 PDF 格式门诊记录的文本内容
- 医疗术语结构化 Skill:将非结构化文本转为标准化字段(症状、诊断、用药、医嘱)
- 诊断编码映射 Skill:将诊断结果映射为 ICD-10 国际医疗编码
通过 LangChain Agent 实现全自动流程调度,无需人工干预。
前置依赖安装
pip install langchain langchain-openai python-dotenv pypdf2 pydantic
完整代码实现
1. 环境配置与基础导入
import os
import re
from dotenv import load_dotenv
from pypdf2 import PdfReader
from pydantic import BaseModel, Field
from typing import Type, List, Optional
# LangChain 核心组件
from langchain_openai import ChatOpenAI
from langchain.tools import BaseTool
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents import create_openai_tools_agent, AgentExecutor
# 加载环境变量(配置 OpenAI API Key)
load_dotenv()
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
2. 封装 3 个医疗专属 Skill
Skill 1:PDF 病历解析 Skill(提取文本)
# 定义输入输出 Schema(符合 Skills 范式的标准化约束)
class PDFMedicalRecordInput(BaseModel):
pdf_path: str = Field(description="PDF 格式门诊病历的本地文件路径,例如:./patient_record.pdf")
class PDFMedicalRecordOutput(BaseModel):
text_content: str = Field(description="从 PDF 中提取的完整门诊病历文本")
page_count: int = Field(description="PDF 文件的总页数")
class PDFMedicalRecordParserSkill(BaseTool):
name = "pdf_medical_record_parser_skill"
description = "专门用于解析 PDF 格式的医疗门诊病历,提取其中的文本内容,输入为本地 PDF 文件路径"
args_schema: Type[BaseModel] = PDFMedicalRecordInput
return_direct: bool = False
def _run(self, pdf_path: str) -> str:
"""核心执行逻辑:读取 PDF 并提取文本"""
try:
reader = PdfReader(pdf_path)
page_count = len(reader.pages)
text_content = ""
for page in reader.pages:
text_content += page.extract_text() + "\n"
# 按 Schema 格式返回结果
result = PDFMedicalRecordOutput(
text_content=text_content.strip(),
page_count=page_count
)
return result.model_dump_json(indent=2)
except FileNotFoundError:
return f"错误:未找到 PDF 文件 {pdf_path}"
except Exception as e:
return f"PDF 解析失败:{str(e)}"
def _arun(self, pdf_path: str):
raise NotImplementedError("异步解析暂未支持")
Skill 2:医疗术语结构化 Skill(标准化字段提取)
class MedicalTextStructuringInput(BaseModel):
raw_text: str = Field(description="从 PDF 提取的原始门诊病历文本,包含症状、诊断、用药、医嘱等信息")
output_format: str = Field(default="json", description="输出格式,支持 json 或 markdown")
class MedicalStructuredData(BaseModel):
patient_symptom: str = Field(description="患者症状描述,如发热、咳嗽、乏力")
diagnosis_result: str = Field(description="医生的诊断结论,如急性上呼吸道感染")
medication_list: List[str] = Field(description="用药清单,如阿莫西林胶囊、布洛芬混悬液")
doctor_advice: str = Field(description="医嘱内容,如多喝水、休息 3 天")
class MedicalTextStructuringSkill(BaseTool):
name = "medical_text_structuring_skill"
description = "将非结构化的门诊病历文本转换为标准化字段,包含症状、诊断、用药、医嘱,支持指定输出格式"
args_schema: Type[BaseModel] = MedicalTextStructuringInput
return_direct: bool = False
def _run(self, raw_text: str, output_format: str = "json") -> str:
"""核心执行逻辑:基于正则和规则提取标准化字段"""
# 模拟医疗文本提取逻辑(真实场景可替换为医疗 NLP 模型)
symptom_pattern = r"症状[::](.*?)(?=诊断|$)"
diagnosis_pattern = r"诊断[::](.*?)(?=用药|医嘱|$)"
medication_pattern = r"用药[::](.*?)(?=医嘱|$)"
advice_pattern = r"医嘱[::](.*)"
symptom = re.search(symptom_pattern, raw_text, re.DOTALL)
diagnosis = re.search(diagnosis_pattern, raw_text, re.DOTALL)
medication = re.search(medication_pattern, raw_text, re.DOTALL)
advice = re.search(advice_pattern, raw_text, re.DOTALL)
structured_data = MedicalStructuredData(
patient_symptom=symptom.group(1).strip() if symptom else "未提取到症状",
diagnosis_result=diagnosis.group(1).strip() if diagnosis else "未提取到诊断",
medication_list=medication.group(1).strip().split("、") if medication else [],
doctor_advice=advice.group(1).strip() if advice else "未提取到医嘱"
)
# 按指定格式输出
if output_format == "json":
return structured_data.model_dump_json(indent=2)
else:
return f"""# 结构化门诊病历
- 症状:{structured_data.patient_symptom}
- 诊断:{structured_data.diagnosis_result}
- 用药:{', '.join(structured_data.medication_list)}
- 医嘱:{structured_data.doctor_advice}"""
def _arun(self, raw_text: str, output_format: str = "json"):
raise NotImplementedError("异步结构化暂未支持")
Skill 3:诊断编码映射 Skill(ICD-10 匹配)
class MedicalCodeMappingInput(BaseModel):
diagnosis: str = Field(description="标准化的诊断结论,如急性支气管炎、高血压")
class MedicalCodeOutput(BaseModel):
diagnosis: str = Field(description="输入的诊断结论")
icd10_code: str = Field(description="对应的 ICD-10 编码")
code_description: str = Field(description="ICD-10 编码的官方描述")
# 模拟 ICD-10 编码库(真实场景可对接医保编码数据库)
ICD10_DATABASE = {
"急性上呼吸道感染": ("J06.900", "急性上呼吸道感染,未特指"),
"急性支气管炎": ("J20.900", "急性支气管炎,未特指"),
"高血压": ("I10.x00", "原发性高血压"),
"糖尿病": ("E11.900", "2型糖尿病,未特指并发症")
}
class MedicalCodeMappingSkill(BaseTool):
name = "medical_code_mapping_skill"
description = "将医疗诊断结论映射为 ICD-10 国际标准编码,输入为标准化诊断结果"
args_schema: Type[BaseModel] = MedicalCodeMappingInput
return_direct: bool = False
def _run(self, diagnosis: str) -> str:
"""核心执行逻辑:匹配 ICD-10 编码"""
diagnosis = diagnosis.strip()
if diagnosis in ICD10_DATABASE:
code, desc = ICD10_DATABASE[diagnosis]
result = MedicalCodeOutput(
diagnosis=diagnosis,
icd10_code=code,
code_description=desc
)
return result.model_dump_json(indent=2)
else:
return f"未找到【{diagnosis}】对应的 ICD-10 编码,请检查诊断名称是否准确"
def _arun(self, diagnosis: str):
raise NotImplementedError("异步编码映射暂未支持")
3. 组合 Skill 并创建 Agent 调度器
def create_medical_agent():
# 1. 初始化大模型(支持工具调用)
llm = ChatOpenAI(
model="gpt-3.5-turbo",
temperature=0, # 固定输出,适合工具调用场景
max_tokens=1024
)
# 2. 组装医疗 Skill 工具箱
medical_skills = [
PDFMedicalRecordParserSkill(),
MedicalTextStructuringSkill(),
MedicalCodeMappingSkill()
]
# 3. 定义 Agent Prompt:明确 Skill 调用逻辑和顺序
prompt = ChatPromptTemplate.from_messages([
("system", """你是医疗信息化智能助手,负责处理门诊病历的自动化解析任务,可调用以下 3 个技能,需按顺序执行:
1. 第一步:调用 PDF 病历解析技能,提取 PDF 文本内容;
2. 第二步:调用医疗术语结构化技能,将提取的文本转为标准化字段;
3. 第三步:调用诊断编码映射技能,将诊断结果转为 ICD-10 编码。
请严格按照步骤执行,每一步的输出作为下一步的输入,最终返回完整的处理报告。"""),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad") # 存储 Agent 思考过程
])
# 4. 创建 Agent 并绑定 Skill
agent = create_openai_tools_agent(llm, medical_skills, prompt)
agent_executor = AgentExecutor(
agent=agent,
tools=medical_skills,
verbose=True, # 开启详细日志,查看 Skill 调用过程
handle_parsing_errors="请检查输入格式,确保提供正确的 PDF 文件路径"
)
return agent_executor
# 初始化医疗 Agent
medical_agent = create_medical_agent()
4. 测试多 Skill 组合调用
if __name__ == "__main__":
# 测试输入:指定 PDF 病历路径
test_input = "帮我处理这份门诊病历 PDF:./patient_record.pdf,要求完成结构化和 ICD-10 编码映射"
# 执行 Agent 调度
result = medical_agent.invoke({"input": test_input})
# 输出最终结果
print("\n===== 最终处理报告 =====")
print(result["output"])
关键执行逻辑说明
- Skill 调用顺序:Agent 严格按照 Prompt 要求,先调用
PDFMedicalRecordParserSkill提取文本 → 再用MedicalTextStructuringSkill结构化 → 最后用MedicalCodeMappingSkill生成编码,实现流水线式处理。 - 数据流转:前一个 Skill 的输出自动作为后一个 Skill 的输入,无需手动传递参数,完全符合通用
Skills的组合调用范式。 - 日志可视化:开启
verbose=True后,可在控制台看到每个 Skill 的调用过程、参数传递和结果返回,便于调试。
医疗场景适配优化建议
- 替换真实医疗 NLP 模型:将示例中的正则提取逻辑,替换为医疗领域预训练模型(如 BERT-医疗版),提升结构化准确率。
- 对接医保数据库:将
ICD10_DATABASE替换为医院/医保局的官方编码接口,实现实时编码查询。 - 添加权限控制:在 Skill 中加入用户鉴权逻辑,仅允许授权人员访问病历数据,符合医疗数据隐私规范。
更多推荐

所有评论(0)