门诊分诊 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": []
  }'

返回结果中要重点看 actionaudit_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文档翻译】

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐