详细设计.md 16 KB


doc_id: DDS-202606-004 feature_id: FEAT-202606-006-agent-semantic-interaction type: detailed-design title: Agent 语义交互与命令治理详细设计 status: reviewing owner: 医梦研发团队 created_at: 2026-06-29 updated_at: 2026-07-01 reviewers:

  • 架构负责人
  • 后端负责人
  • 终端负责人 related_docs:
  • PRD-202606-002
  • ODS-202606-002
  • ADR-202606-001
  • API-202606-004 related_modules:
  • emoon-openplatform
  • emoon-ai-agent
  • emoon-ai-card
  • emoon-ai-mcp
  • emoon-system tags:
  • 详细设计
  • TaskState
  • SemanticResponse
  • Command
  • Presentation ---

Agent 语义交互与命令治理详细设计

实现范围

本设计描述后续实现所需的目标模块、领域对象、数据模型、状态机、接口、异常、安全和测试边界。 本轮只产出设计,不实施代码或数据库变更。

首个纵向样板仅覆盖挂号 Agent:

约束提取 → 号源查询 → 替代推荐 → 无状态展示
→ 候选选择 → 实时复核 → 临时锁号 → 最终挂号确认

设计原则

  1. 业务事实与表现形式分离。
  2. 只读规划与写命令分离。
  3. 模型建议与服务端状态分离。
  4. 展示快照与执行凭证分离。
  5. 明确交互与二次确认按风险分级。
  6. 不通过 Prompt 替代权限、状态、幂等和审计。

领域模型

TaskState

{
  "taskId": "task_001",
  "taskType": "REGISTRATION",
  "status": "ACTIVE",
  "currentStage": "CANDIDATE_SELECTION",
  "constraints": {
    "departmentId": "respiratory",
    "departmentName": "呼吸科",
    "doctorTitle": "主任医师",
    "visitDate": "2026-06-30",
    "timeRange": null,
    "campusId": null
  },
  "missingFields": ["candidateId"],
  "activeResultRef": "result_001",
  "activeCommandId": null,
  "version": 5
}

约束:

  • 只有服务端业务服务能修改确定性字段。
  • FastGPT 只通过白名单只读工具参数表达查询条件,服务端校验后才更新查询上下文。
  • 命令成功后由 Command Runtime 写入已锁号、已挂号等事实。
  • 同一会话最多一个 ACTIVE 前台任务;其他任务为 SUSPENDED

SemanticResponse

{
  "turnId": "turn_001",
  "answer": "明天呼吸科有 3 位主任医师可预约。",
  "task": {
    "taskId": "task_001",
    "taskType": "REGISTRATION",
    "status": "ACTIVE",
    "currentStage": "CANDIDATE_SELECTION",
    "version": 5
  },
  "resultRefs": [
    {
      "resultRef": "result_001",
      "version": 1,
      "resultType": "APPOINTMENT_SLOT_CANDIDATES",
      "expiresAt": "2026-06-30T09:35:00+08:00"
    }
  ],
  "availableActions": [
    {
      "actionType": "SELECT_CANDIDATE",
      "riskLevel": "L1",
      "confirmationRequired": false
    }
  ]
}

SemanticResponse 是薄传输 DTO,不是持久化领域对象,也不是要求 FastGPT 一次性输出的模型 JSON。 仅 turnIdanswer 必填;taskresultRefsavailableActions 按本轮结果可选。 风险和确认要求只存在于具体 Action/Command,Presentation 使用独立 SSE 事件。

ResultSnapshot

ResultSnapshot 是只读 MCP 结果的最小化、短期、版本化引用。

必须包含:

  • result_refresult_versionresult_type
  • tenant_idproject_idconversation_idtask_id
  • 可选 patient_binding_id
  • 最小化 result_json
  • source_tool_call_idtrace_id
  • expires_atcreated_at

禁止包含:

  • 完整 HIS 原始响应;
  • 非当前任务所需的患者资料;
  • 可直接执行写操作的密钥或令牌;
  • 无明确用途的长期医疗数据副本。

Presentation

Presentation 是 ResultSnapshot 的无状态投影:

{
  "presentationId": "prs_001",
  "viewModelType": "AppointmentSlotCandidates",
  "schemaVersion": "1.0",
  "resultRef": "result_001",
  "resultVersion": 1,
  "payload": {
    "groups": []
  },
  "interactions": [
    {
      "type": "SELECT_CANDIDATE",
      "candidateId": "slot_001"
    }
  ]
}

通用布局原语包括列表、分组、标签、摘要、按钮和确认区。 领域 ViewModel 包括 DoctorCandidateAppointmentSlotRegistrationConfirmation

模板和策略不得包含:

  • 工具名称或调用参数;
  • nextCard
  • 命令执行顺序;
  • TaskState 状态迁移;
  • 风险等级降级规则。

Command

{
  "commandId": "cmd_001",
  "commandType": "REGISTER_APPOINTMENT",
  "riskLevel": "L2",
  "status": "AWAITING_CONFIRMATION",
  "taskId": "task_001",
  "taskVersion": 7,
  "summary": {
    "department": "呼吸科",
    "doctor": "张明 主任医师",
    "visitTime": "2026-06-30 09:00",
    "feeYuan": "25.00"
  },
  "expiresAt": "2026-06-30T09:40:00+08:00"
}

命令参数由后端根据 TaskState、ResultSnapshot 和实时 MCP 结果准备。 终端不能提交或覆盖 commandType、HIS 参数、风险等级和金额。

数据模型

ai_result_snapshot

字段 类型建议 约束
id bigint 主键
result_ref varchar(96) 全局唯一
result_version int 同一引用递增
result_type varchar(64) 领域结果类型
tenant_id varchar(64) 必填、索引
project_id varchar(64) 必填
conversation_id varchar(96) 必填、索引
task_id varchar(96) 必填、索引
patient_binding_id varchar(96) 可空,不保存证件号
result_json json/text 最小化结果
source_tool_call_id varchar(96) 必填
trace_id varchar(96) 必填、索引
expires_at datetime 必填、索引
created_at datetime 必填

唯一键:(result_ref, result_version)

ai_command_instance

字段 类型建议 约束
id bigint 主键
command_id varchar(96) 全局唯一
command_type varchar(64) 后端注册类型
risk_level varchar(8) L1/L2/L3
status varchar(32) 状态机字段
tenant_id/project_id varchar(64) 权限边界
conversation_id/task_id/turn_id varchar(96) 链路关联
task_version int 防止旧任务确认
patient_binding_id varchar(96) 确认主体
command_payload json/text 后端生成,敏感字段最小化/加密
summary_json json/text 面向确认的脱敏摘要
idempotency_key varchar(128) 唯一或业务唯一
source_result_ref/version varchar/int 可空
expires_at datetime 必填
confirmed_at/executed_at datetime 可空
trace_id varchar(96) 必填、索引
created_at/updated_at datetime 必填

ai_command_audit_log

字段 类型建议 说明
audit_id varchar(96) 唯一
command_id varchar(96) 命令关联
event_type varchar(32) PREPARED/CONFIRMED/REJECTED/EXECUTING/SUCCEEDED/FAILED/UNKNOWN
actor_type/actor_id varchar(32/96) 患者、医护、系统
request_digest varchar(128) 请求摘要,不保存密钥
result_digest varchar(128) 结果摘要
tool_call_id varchar(96) 可空
trace_id varchar(96) 必填
occurred_at datetime 必填

Presentation MVP 不新增数据表

挂号纵向样板不创建 ai_presentation_templateai_presentation_policy

  • AppointmentSlotCandidatesV1RegistrationConfirmationV1 Schema 随代码版本管理;
  • 转换器只接受明确的领域 QueryResult,不接受任意 JSON;
  • Scene Profile 只配置终端是否支持卡片、最大展示条数和布局偏好;
  • ViewModel 主版本不兼容时新增类型或版本,不原地改变旧字段语义;
  • 不支持模板内动作脚本、工具名、表达式和 TaskState 迁移。

只有出现三个以上可复用领域 ViewModel、且医院确有无代码换肤需求时,才另立需求评估模板表。

状态机

TaskState

stateDiagram-v2
    [*] --> ACTIVE
    ACTIVE --> SUSPENDED: 切换业务任务
    SUSPENDED --> ACTIVE: 恢复且上下文仍有效
    ACTIVE --> COMPLETED: 业务目标完成
    ACTIVE --> CANCELLED: 用户取消
    ACTIVE --> EXPIRED: 任务超时
    SUSPENDED --> EXPIRED: 上下文或资源过期

Command

stateDiagram-v2
    [*] --> PREPARED
    PREPARED --> AWAITING_CONFIRMATION: L2/L3
    PREPARED --> EXECUTING: L1 明确交互授权
    AWAITING_CONFIRMATION --> EXECUTING: 确认通过
    AWAITING_CONFIRMATION --> REJECTED: 用户拒绝
    PREPARED --> EXPIRED: 到期
    AWAITING_CONFIRMATION --> EXPIRED: 到期
    EXECUTING --> SUCCEEDED
    EXECUTING --> FAILED
    EXECUTING --> UNKNOWN: 下游状态不明
    UNKNOWN --> SUCCEEDED: 幂等状态查询确认
    UNKNOWN --> FAILED: 幂等状态查询确认

禁止从 UNKNOWN 直接重新执行。

服务和对象职责

组件 职责 备注
FastGptAgentEngine 文本流、只读工具调用和引擎元数据适配 首期唯一正式引擎
AgentApplicationService 校验查询参数、更新查询上下文、保存结果引用、组装响应、解析 interaction 保持薄应用层,不做通用 Workflow
ResultSnapshotService 最小化、版本化、过期与访问控制 可作为 AgentApplicationService 内部协作者
PresentationPolicy 根据 ResultType 和终端能力选择代码内 ViewModel 先用简单策略,不建设独立规则引擎
CommandRuntime 固定命令策略、准备、确认、拒绝、执行、幂等和审计 命令类型与风险使用代码/配置白名单
McpToolService 工具注册、权限过滤、ToolCatalog、调用和审计 FastGPT 只获得只读集合

FastGPT 交互

ToolCatalog 生成与下发

医梦后端是工具目录权威来源:

{
  "name": "query_appointment_slots",
  "description": "按科室、日期、职称等条件查询可预约号源",
  "riskLevel": "READ_ONLY",
  "timeoutMs": 5000,
  "dataClassification": "INTERNAL",
  "inputSchema": {
    "type": "object",
    "required": ["departmentId", "visitDate"],
    "properties": {
      "departmentId": {"type": "string"},
      "visitDate": {"type": "string", "format": "date"},
      "doctorTitle": {"type": "string"},
      "timeRange": {"type": "string"}
    },
    "additionalProperties": false
  }
}

生成步骤:

  1. 从 MCP Tool Registry 读取工具定义;
  2. 只保留 READ_ONLY
  3. 按 tenant/project/agent/scene/终端权限过滤;
  4. 校验名称、说明、inputSchema、超时和数据分级;
  5. 由 FastGPT Adapter 注册或注入目标 App/Workflow;
  6. 发布时保存 ToolCatalog 版本和摘要,运行时记录实际版本。

FastGPT 具体采用 MCP ToolSet、HTTP ToolSet 或 Workflow 工具节点,由实施前能力验证确定。 该差异只存在于 Adapter,不改变医梦工具权限模型。

查询参数与 TaskState

取消单独的 propose_task_constraints 协议。FastGPT 调用只读工具时提交的参数即候选查询条件:

  1. AgentApplicationService 对参数做 JSON Schema、词典、日期和授权校验;
  2. 校验成功后写入 TaskState 的查询上下文并执行工具;
  3. 校验失败不向用户暴露内部“proposal 被拒绝”,最多修复一次;
  4. 无法修复时返回针对缺失或冲突字段的自然语言追问;
  5. 查询参数不能写入“已锁号、已挂号、已支付”等确定性状态。

FastGPT 接收:

  • 最小任务摘要;
  • 当前允许的只读工具;
  • 必要的会话文本;
  • traceId/turnId
  • 不包含完整 ResultSnapshot 或命令内部参数。

FastGPT 可产生:

  • 自然语言增量;
  • 只读工具调用。

用户的锁号或挂号意图由 AgentApplicationService 结合当前 TaskState 和明确交互识别; FastGPT 不输出可执行命令参数。

后端必须拒绝:

  • FastGPT 发起写工具;
  • FastGPT 直接设置 TaskState 确定性状态;
  • FastGPT 自定义风险等级或跳过确认;
  • FastGPT 返回的最终 cardData、commandToken 或价格作为可信事实。

挂号纵向样板

精确查询

  1. 提取日期、科室、职称。
  2. 服务端校验并更新 TaskState 约束。
  3. FastGPT 调用 ToolCatalog 中的 query_appointment_slots 只读工具。
  4. 保存 APPOINTMENT_SLOT_CANDIDATES ResultSnapshot。
  5. 文本说明精确结果;Presentation 可异步追加。

替代查询

  1. 精确结果为空。
  2. 服务端查询策略返回允许扩展维度。
  3. FastGPT 或后端依次调用只读工具。
  4. 每个候选标注 relaxedConstraint
  5. 患者明确选择后才更新 TaskState。

选择和锁号

  1. AgentApplicationService 将自然语言或点击选择解析为 candidateId。
  2. 校验 ResultSnapshot 未过期且版本匹配。
  3. 重新查询该号源实时状态、费用和患者资格。
  4. L1 选择行为视为授权,创建并执行 LOCK_APPOINTMENT_SLOT
  5. 成功后将 lockId 和有效期写入 TaskState。

最终挂号

  1. 后端准备 REGISTER_APPOINTMENT L2 命令。
  2. SSE 返回 command_prepared 脱敏摘要。
  3. 用户确认时携带 commandId、commandVersion 和幂等键。
  4. Command Runtime 复核身份、有效期、taskVersion 和 lockId。
  5. MCP 写工具执行挂号。
  6. 成功后 TaskState 进入 COMPLETED。

接口细节

外部接口以 API-202606-004 和 OpenAPI v2 为准。

SSE 事件:

message_delta
semantic_result
presentation
task_updated
command_prepared
command_completed
error
completed

presentation 不保证出现,也不阻塞 completed 前的业务结果。 命令状态可通过独立查询接口恢复。

异常处理

场景 处理
FastGPT 文本流失败 返回明确错误或已有文本;不产生未验证状态
Tool Call Schema 失败 拒绝该调用,可有限重试;文本可降级
查询超时 有限重试,说明无法获取实时数据,提供人工/传统入口
ResultSnapshot 过期 返回 RESULT_EXPIRED,重新查询
选择版本冲突 返回 RESULT_VERSION_CONFLICT
锁号失败 返回最新候选,不自动改选
L2 命令过期 返回 COMMAND_EXPIRED,重新准备
写命令超时 进入 UNKNOWN,按幂等键查询下游状态
展示生成失败 记录指标,继续文本/语音
任务切换 暂停旧任务,不续锁;提示剩余有效期

安全和合规

  • 复用 Bearer/HMAC 鉴权,并校验 projectId、partnerId、deviceId。
  • 所有 ResultSnapshot 和 Command 均做租户、项目、患者、会话绑定。
  • 确认主体必须与准备命令时的患者绑定一致。
  • FastGPT API Key、MCP 凭证和 HIS 密钥不得进入终端、日志或模型上下文。
  • 患者资料按最小必要字段传递,日志只保存摘要和标识。
  • Presentation 只展示当前终端权限允许的数据。
  • L3 命令要求医护或人工确认主体,患者端不能单独完成。

配置和部署

平台级配置:

  • 命令类型及固定风险等级;
  • 最低确认要求;
  • ResultSnapshot 最大有效期;
  • 写命令 UNKNOWN 处理策略;
  • FastGPT 引擎能力门禁。

租户/医院可配置:

  • 替代查询日期范围、职称和院区范围;
  • 返回候选数量;
  • 锁号有效期(不超过平台/HIS 上限);
  • 终端是否支持展示、最大条数和布局偏好。

测试策略

  • 单元:状态机、工具参数校验、版本校验、风险策略、简单展示策略。
  • 集成:FastGPT Adapter、只读 MCP、Command Runtime、HIS Mock 幂等。
  • 契约:OpenAPI v2、SSE 事件顺序、终端 interaction。
  • 安全:越权 ResultSnapshot、伪造命令、重复确认、过期确认。
  • 端到端:挂号完整路径、无结果替代、展示失败、写状态未知。
  • 架构:FastGPT 不可依赖写工具;Command 不依赖模型;Presentation 不推进 TaskState。

待评审项

  • FastGPT 目标部署版本的 Tool/Function Call 事件格式需要在实施前实测并记录。
  • 性能阈值需要在测试环境建立基线后填写到测试结果,不在设计阶段虚构。