Agent+电子病历:病史整理、结构化录入与摘要生成如何落地
电子病历系统里大量信息仍然来自自由文本:主诉、现病史、既往史、过敏史、体格检查记录等,医生录入耗时,后续质控和检索也困难。本文从工程实现角度,拆解一个 Agent 辅助病史整理、结构化录入和摘要生成的最小可落地方案。本文仅讨论技术架构和工程流程示例,不提供诊断、治疗、分诊或用药建议,所有规则都应由医疗专业人员和机构规范确认。
问题背景:自由文本为什么难以直接入库
在电子病历场景中,开发者经常遇到三个问题。
第一,自由文本格式不稳定。同一个字段可能写成“否认药物过敏史”“青霉素过敏”“过敏史:不详”,简单正则很难覆盖全部表达。
第二,结构化字段有强约束。例如chief_complaint、present_illness、past_history、allergy_history等字段需要类型明确、来源可追溯、可被人工修改。
第三,模型输出不能直接覆盖病历。Agent 可以提升整理效率,但必须保留审核边界:哪些字段来自原文、哪些是模型归纳、哪些需要人工确认,都要在数据结构里体现。
因此,合理目标不是“自动写病历”,而是构建一个“候选结构化结果生成器 + 审核工作台”。
技术目标与边界
本文示例技术栈如下:
- Python + FastAPI:提供病历解析和审核接口
- PostgreSQL:保存原始文本、抽取结果、审核状态
- vector store:存储历史模板、科室常用表达、字段示例
- LLM API:完成字段抽取、摘要生成和一致性检查
系统目标可以拆成四步:
- 接收原始病历文本,做脱敏和分段。
- Agent 根据字段 Schema 抽取结构化候选值。
- 生成病史摘要,并标注来源片段。
- 审核工作台展示差异,由人工确认后回填。
需要特别注意:示例中的风险提示、字段完整性检查、升级规则都只是可配置工程规则,真实项目必须由医疗专业人员和机构制度确认。
方案总览:把 Agent 拆成可控流程
Agent 不建议设计成一个黑盒接口。更稳妥的方式是拆成多个节点,每个节点只做一件事。
核心设计点是“候选结果”和“最终病历”分离。模型只写入draft_record,人工审核后才写入confirmed_record。这样可以避免模型生成内容直接污染正式病历数据。
一个简化的数据结构如下:
emr_raw_note - id - patient_visit_id - raw_text - deidentified_text - created_at emr_draft_extract - id - raw_note_id - field_name - field_value - evidence_text - confidence - status: pending / accepted / rejected / edited emr_confirmed_record - id - patient_visit_id - structured_json - reviewer_id - reviewed_at实现步骤一:定义字段 Schema
不要让模型自由发挥字段名。先定义固定 Schema,抽取任务围绕 Schema 进行。
frompydanticimportBaseModel,FieldfromtypingimportOptional,ListclassEvidenceField(BaseModel):value:Optional[str]=Field(default=None,description="抽取出的字段值")evidence:Optional[str]=Field(default=None,description="原文依据片段")confidence:float=Field(default=0.0,ge=0.0,le=1.0)classEmrExtractResult(BaseModel):chief_complaint:EvidenceField present_illness:EvidenceField past_history:EvidenceField allergy_history:EvidenceField family_history:EvidenceField medication_history:EvidenceField missing_fields:List[str]=[]need_human_review:List[str]=[]这里的evidence很关键。审核人员看到的不应只是模型结论,还要能回到原始文本。confidence也不要理解为医学准确率,它只是工程上的模型自评或规则综合评分,不能替代人工判断。
实现步骤二:FastAPI 接口与 Agent 调用
下面是一个最小示例,演示如何把原始文本传入 LLM,并要求返回符合 Schema 的 JSON。实际项目中还应加入鉴权、审计日志、敏感信息处理和异常重试。
importjsonfromfastapiimportFastAPIfrompydanticimportBaseModelfromopenaiimportOpenAI app=FastAPI()client=OpenAI(api_key="YOUR_LLM_API_KEY")classExtractRequest(BaseModel):patient_visit_id:strraw_text:strSYSTEM_PROMPT=""" 你是电子病历结构化助手,只能根据输入文本抽取字段。 不得生成诊断、治疗、分诊或用药建议。 如果原文没有明确依据,字段 value 填 null,并加入 missing_fields。 每个字段必须返回 evidence,表示原文依据片段。 输出严格 JSON,不要输出解释。 """defbuild_user_prompt(text:str)->str:returnf""" 请从以下电子病历自由文本中抽取结构化字段: 字段包括 chief_complaint, present_illness, past_history, allergy_history, family_history, medication_history。 原文:{text}返回 JSON 格式: {{ "chief_complaint": {{"value": "", "evidence": "", "confidence": 0.0}}, "present_illness": {{"value": "", "evidence": "", "confidence": 0.0}}, "past_history": {{"value": "", "evidence": "", "confidence": 0.0}}, "allergy_history": {{"value": "", "evidence": "", "confidence": 0.0}}, "family_history": {{"value": "", "evidence": "", "confidence": 0.0}}, "medication_history": {{"value": "", "evidence": "", "confidence": 0.0}}, "missing_fields": [], "need_human_review": [] }} """@app.post("/emr/extract")defextract_emr(req:ExtractRequest):response=client.chat.completions.create(model="gpt-4o-mini",temperature=0.1,messages=[{"role":"system","content":SYSTEM_PROMPT},{"role":"user","content":build_user_prompt(req.raw_text)}])content=response.choices[0].message.content result=json.loads(content)validated=EmrExtractResult(**result)return{"patient_visit_id":req.patient_visit_id,"draft_extract":validated.model_dump(),"status":"pending_review"}这个接口只返回待审核草稿,不直接写入正式病历。工程上建议把 LLM 原始响应、Prompt 版本、模型版本、调用时间都记录下来,方便追溯和回放。
实现步骤三:摘要生成不要脱离证据
摘要生成常见问题是“写得像病历,但找不到出处”。建议摘要 Agent 只基于已抽取字段和证据片段生成,不直接基于完整原文自由发挥。
摘要 Prompt 可以限制为:
根据已审核或待审核的结构化字段生成病史摘要。 不得新增输入中不存在的信息。 如果字段状态为 pending,需要在摘要元数据中标记 pending_source=true。 不得输出诊断、治疗、分诊或用药建议。摘要结果建议保存为:
{"summary":"患者主诉及病史摘要文本","source_fields":["chief_complaint","present_illness","past_history"],"pending_source":true,"generated_at":"2026-05-30T09:00:00"}这样审核工作台可以明确提示:摘要中包含未确认字段,不能直接作为最终内容使用。
审核工作台:人工校对边界怎么设计
审核界面至少需要展示四列信息:
- 原文片段
- 模型抽取字段
- 置信度或规则提示
- 人工操作:接受、修改、驳回
建议增加字段级状态,而不是整份病历一个状态。因为一段文本中,主诉可能很明确,过敏史可能缺失,既往史可能需要人工确认。
常见操作流程如下:
- 医生或质控人员打开待审核记录。
- 系统高亮每个字段对应的 evidence。
- 审核人员逐字段确认。
- 修改记录写入审计日志。
- 全部必要字段确认后,才允许回填正式 EMR。
对于“缺失字段提示”“冲突字段提示”等规则,应写成可配置策略。例如:如果过敏史字段为空,系统只提示“该字段缺失,需人工确认”,不能推断患者不存在过敏史。
向量库在这里解决什么问题
vector store 不建议直接存患者隐私文本,除非完成脱敏、权限控制和合规评估。更常见的用途是存储以下内容:
- 字段填写样例
- 科室常用病史模板
- 机构认可的术语规范
- Prompt few-shot 示例
抽取时先检索相似模板,再拼入 Prompt,可以提升字段格式稳定性。例如同一机构希望“过敏史”统一写成“否认 / 明确药物或食物 / 不详”,就可以通过模板约束输出。
踩坑与调试建议
第一,JSON 解析失败要单独处理。LLM 偶尔会输出多余文本,生产环境建议使用 JSON schema response 或函数调用能力。
第二,不要只看字段是否非空,还要看 evidence 是否能支持 value。可以写一个一致性检查 Agent,专门判断“字段值是否能从证据片段推出”。
第三,Prompt 要版本化。字段定义、规则说明、示例文本变化后,历史结果必须能追溯到当时的 Prompt 版本。
第四,脱敏要前置。日志、向量库、调试平台中都不应裸存敏感身份信息。真实项目还需结合机构安全规范、访问控制和审计要求。
性能与扩展取舍
如果每份病历都串行调用多个 Agent,延迟会比较高。工程上可以把流程拆成同步和异步两部分:字段抽取同步返回,摘要生成和一致性检查放入队列异步执行。
对于短文本门诊记录,可以一次性抽取全部字段。对于较长住院记录,建议先分段,再按字段路由到不同抽取任务,最后合并结果。合并时要保留来源段落,避免摘要阶段丢失依据。
成本方面,可以将模板检索、规则校验、字段完整性检查放在本地完成,只把需要语义理解的部分交给 LLM。这样既能减少 token,也便于做稳定性测试。
总结与下一步
Agent 辅助电子病历落地的关键,不是让模型替代人工录入,而是把自由文本整理成可审核、可追溯、可回填的候选结构化数据。推荐的工程路径是:固定 Schema、证据片段绑定、草稿与正式数据隔离、字段级审核、Prompt 与模型版本可追溯。
下一步可以继续完善三块能力:脱敏与权限控制、审核工作台交互、字段一致性评估。只要把医疗专业判断留在人工和机构规范内,Agent 就能作为提升录入效率和文本整理质量的工程组件。
本文文献检索、文献挖掘以及文献翻译采用的是【超能文献| AI文献检索|AI文档翻译】。
