AI Agent辅助门诊分诊:症状采集、初步分流和边界控制怎么设计
门诊分诊 Agent 的难点不在于“让大模型回答得像医生”,而在于把症状采集、规则判断、人工转接和安全边界做成可审计的工程链路。本文只讨论技术架构与工程流程示例,不提供诊断、治疗、分诊或用药建议;所有风险分层、阈值和升级规则均为示例,真实项目必须由医疗专业人员和机构规范确认。
问题背景:Agent 最容易在哪些地方越界
在门诊入口场景里,用户往往输入一句自然语言,例如“胸口不舒服,已经两小时”。如果系统直接把这句话交给 LLM 生成结论,很容易出现三个问题。
第一,信息采集不完整。持续时间、伴随表现、年龄、既往情况、当前状态等关键字段缺失,后续判断不可追溯。
第二,输出边界不清晰。Agent 可能把“建议转人工”写成“你可能是某疾病”,这已经越过辅助服务系统的范围。
第三,规则不可审计。单纯依赖 Prompt 很难解释为什么触发升级,也不利于后续质控和日志复盘。
因此更稳妥的设计是:LLM 负责理解自然语言和生成下一轮问题,决策树或规则引擎负责示例风险分层,后端服务负责状态机、审计日志和人工转接。
技术目标与边界声明
本文面向医疗健康技术开发者、科研工具开发者和技术架构师,目标是给出一个可落地的辅助分诊 Agent 设计骨架。
系统目标可以拆成四点:
- 结构化采集症状信息,而不是只保存聊天文本
- 用可配置示例规则做初步分流提示
- 对高不确定性或触发升级条件的会话转人工
- 严格限制 Agent 输出,不生成诊断、治疗、用药建议
一个推荐的边界文案是:系统仅用于门诊服务流程中的信息预采集和就诊引导,不能替代医务人员判断。任何紧急不适或症状加重,应按机构规则联系人工服务或急救渠道。
方案概览:把 Agent 拆成四个模块
可以把链路设计为如下文本架构图:
用户输入
-> 会话状态管理 FastAPI
-> LLM 信息抽取与追问生成
-> 症状结构化表单 PostgreSQL
-> 示例规则决策树
-> 输出控制器
-> 继续追问
-> 初步服务引导
-> 人工转接
-> 紧急升级提示
这里有一个关键原则:LLM 不直接决定最终动作。它可以提取字段、判断字段是否缺失、把规则结果转成用户能理解的服务语言,但最终动作由后端策略层返回。
建议结构化字段至少包括:
- chief_complaint:主诉文本
- duration:持续时间,允许为空
- severity:用户自述程度,枚举或分值
- red_flags:示例高风险提示项,布尔数组
- age_group:年龄段
- department_hint:服务引导候选,不等同诊断
- handoff_required:是否转人工
- audit_reason:触发原因
实现步骤:FastAPI + 示例决策树
下面示例演示一个最小可运行的策略层。代码中的规则仅用于工程流程说明,不能作为真实医疗分诊规则。真实项目需要由医疗专业人员确认字段、阈值、升级条件和话术。
from enum import Enum
from typing import List, Optional
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI(title="Outpatient Triage Agent Demo")
class Action(str, Enum):
ASK_MORE = "ask_more"
GUIDE_REGISTRATION = "guide_registration"
HANDOFF_HUMAN = "handoff_human"
ESCALATE = "escalate"
class SymptomInput(BaseModel):
session_id: str
chief_complaint: str = Field(..., min_length=1)
duration_minutes: Optional[int] = None
severity: Optional[int] = Field(default=None, ge=1, le=10)
age_group: Optional[str] = None
red_flags: List[str] = []
class TriageResult(BaseModel):
action: Action
missing_fields: List[str]
department_hint: Optional[str]
message: str
audit_reason: List[str]
REQUIRED_FIELDS = ["duration_minutes", "severity", "age_group"]
def decide_department_hint(text: str) -> Optional[str]:
text = text.lower()
if "咳" in text or "发热" in text:
return "可考虑呼吸相关门诊服务台咨询"
if "腹痛" in text or "恶心" in text:
return "可考虑消化相关门诊服务台咨询"
if "皮疹" in text or "瘙痒" in text:
return "可考虑皮肤相关门诊服务台咨询"
return None
def triage_policy(data: SymptomInput) -> TriageResult:
missing = [f for f in REQUIRED_FIELDS if getattr(data, f) is None]
reasons = []
if data.red_flags:
reasons.append(f"触发示例升级项: {','.join(data.red_flags)}")
return TriageResult(
action=Action.ESCALATE,
missing_fields=missing,
department_hint=None,
message="根据你提供的信息,建议立即联系现场工作人员或人工客服处理。本系统不做诊断判断。",
audit_reason=reasons
)
if missing:
reasons.append("结构化信息不足,需要继续采集")
return TriageResult(
action=Action.ASK_MORE,
missing_fields=missing,
department_hint=None,
message=f"还需要补充这些信息:{', '.join(missing)}。请按实际情况描述。",
audit_reason=reasons
)
if data.severity and data.severity >= 8:
reasons.append("用户自述程度较高,按示例规则转人工")
return TriageResult(
action=Action.HANDOFF_HUMAN,
missing_fields=[],
department_hint=None,
message="你描述的不适程度较高,建议转人工服务进一步确认。本系统不提供诊断或治疗建议。",
audit_reason=reasons
)
hint = decide_department_hint(data.chief_complaint)
reasons.append("未触发升级项,返回服务引导候选")
return TriageResult(
action=Action.GUIDE_REGISTRATION,
missing_fields=[],
department_hint=hint,
message="已完成信息预采集,可将结构化信息提交给人工服务台或挂号流程参考。",
audit_reason=reasons
)
@app.post("/triage", response_model=TriageResult)
def triage(data: SymptomInput):
return triage_policy(data)
本地运行:
pip install fastapi uvicorn pydantic
uvicorn main:app --reload
测试请求:
curl -X POST http://127.0.0.1:8000/triage \
-H "Content-Type: application/json" \
-d '{
"session_id": "s001",
"chief_complaint": "咳嗽三天",
"duration_minutes": 4320,
"severity": 4,
"age_group": "adult",
"red_flags": []
}'
返回结果中要重点看 action 和 audit_reason,它们是后续质控、回放、统计的基础。
LLM 应该放在哪里:只做抽取和追问
在这个系统里,LLM 更适合做两个任务。
一是把用户自然语言转成候选字段。例如从“昨晚开始肚子痛,能忍但影响睡觉”提取持续时间描述、主诉、用户自述程度候选。注意这里应保留原文和置信度,不要覆盖用户表达。
二是生成追问。后端发现 duration_minutes 缺失时,可以让 LLM 把字段需求转成自然问题,例如“这种不适大概从什么时候开始?”但问题模板仍应受控。
推荐 Prompt 约束包括:
- 只输出 JSON,不输出医学结论
- 不推断疾病名称
- 不给用药、治疗、检查建议
- 字段不确定时返回 null
- 发现用户表达紧急或危险信号时标记 red_flags,由策略层处理
LLM 输出也要经过 JSON Schema 校验。校验失败时不要继续链路,可降级为固定模板追问或人工转接。
PostgreSQL 表设计:为审计而不是只为聊天
门诊分诊类系统必须能回答“系统当时看到了什么、规则为什么触发、给了什么提示”。因此数据库不要只存 message。
一个简化表结构可以这样设计:
CREATE TABLE triage_sessions (
id UUID PRIMARY KEY,
user_hash TEXT NOT NULL,
status TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT now(),
updated_at TIMESTAMP NOT NULL DEFAULT now()
);
CREATE TABLE symptom_snapshots (
id UUID PRIMARY KEY,
session_id UUID REFERENCES triage_sessions(id),
raw_text TEXT NOT NULL,
structured_json JSONB NOT NULL,
llm_confidence NUMERIC,
created_at TIMESTAMP NOT NULL DEFAULT now()
);
CREATE TABLE triage_decisions (
id UUID PRIMARY KEY,
session_id UUID REFERENCES triage_sessions(id),
action TEXT NOT NULL,
department_hint TEXT,
audit_reason JSONB NOT NULL,
policy_version TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT now()
);
policy_version 很重要。规则调整后,同一个输入可能得到不同动作,没有版本号就无法复盘。
常见踩坑与边界控制
第一,不要让前端直接展示 LLM 原始输出。所有对用户可见的内容都应通过输出控制器过滤,至少移除诊断名、用药建议、确定性判断等高风险表达。
第二,不要把“科室引导”写成“疾病判断”。工程字段可以叫 department_hint,不要叫 diagnosis。命名会影响团队后续实现边界。
第三,不要用单一分值决定动作。示例代码里 severity >= 8 只是演示,真实项目更适合采用机构确认的多条件规则,并支持人工覆盖。
第四,日志要脱敏。手机号、身份证号、具体住址等信息不应进入普通应用日志。生产环境建议使用用户哈希、字段级加密和访问审计。
第五,要设计失败路径。LLM API 超时、JSON 解析失败、数据库写入失败时,系统应返回“转人工或稍后重试”的安全结果,而不是继续生成不可靠提示。
性能与扩展建议
门诊入口对延迟比较敏感,建议把同步链路控制在“字段校验、策略判断、保存快照”这类确定性操作上。LLM 抽取可以设置短超时,例如 2 到 5 秒;超时后降级到固定表单追问。
规则层不要硬编码在多个服务里。可以先用 Python 函数起步,后续迁移到可配置 JSON、DSL 或规则引擎。关键是每次决策都记录规则版本、输入快照和输出动作。
如果要做灰度,可以按机构、科室服务台、时间段加载不同策略包,但必须保证策略包经过审批和回滚测试。
总结
AI Agent 辅助门诊分诊的工程重点是“受控采集 + 可审计规则 + 人工兜底 + 输出边界”,而不是让模型自由给结论。LLM 适合做语言理解和追问生成,初步分流动作应由后端策略层决定,并记录完整审计链路。落地时请把本文规则视为技术示例,真实阈值、风险项、升级路径和用户话术都需要医疗专业人员与机构规范共同确认。
本文文献检索、文献挖掘以及文献翻译采用的是【超能文献| AI文献检索|AI文档翻译】。
更多推荐


所有评论(0)