跳转至

第10章 企业 Data Agent:把“会写 SQL”变成“可信问数”

学习目标

本章把前九章的运行时、Session、工具、审批、部署和生态治理组合成一个完整案例。读完后,你应能解释为什么 Text-to-SQL 不是智能问数的产品目标;能设计指标、维度、实体、时间和版本组成的业务语义层;能用结构化对话状态处理继承、修改、删除和澄清;能把模型限制在 QuerySpec 提议阶段,把授权、编译和执行留给确定性服务;还能构造覆盖多轮、权限、成本、注入和故障的生产评估体系。

这里讨论的是架构方法,不把示例中的医疗检验口径当作任何真实医院的标准定义。每个组织都必须由自己的数据与业务责任人确认口径。

10.1 产品目标不是生成 SQL,而是交付可验证答案

用户问“去年各科室检验量”,数据库里也许同时有申请单、样本、检验项目、收费项目和报告记录。五条 SQL 都可能语法正确、运行成功、返回漂亮图表,却对应五种业务事实。Text-to-SQL 基准通常关注“问题是否翻译成目标 SQL”,生产系统还要回答:目标 SQL 所依据的业务定义是谁批准的;用户是否有权看这些行列;时间字段和组织层级怎样解释;数据是否完整;答案能否复现。

可以把可信度拆成四个相乘而非相加的条件:

可信答案 = 语义正确 × 授权正确 × 执行正确 × 表达忠实

任一项为零,最终答案就不可信。SQL 执行成功只覆盖第三项的一部分。因此 Data Agent 的验收对象应是“证据化答案包”,而不是一段模型生成的查询文本。

10.2 DSH 在系统中的位置:对话控制平面,不是数据真相层

DSH 提供 Agent Loop、Session 事件、工具流水线、审批、Skill、Workflow 和多种 Surface,适合承载理解意图、维护交互、调用受控工具和展示进度。指标定义、行列权限、确定性编译、查询审计和数据质量则属于企业领域服务。

flowchart TB UI[企业 Web / IM / API] --> GW[身份与会话网关] GW --> DSH[DSH 对话控制平面] DSH --> SEM[语义目录与状态服务] DSH --> PLAN[QuerySpec 规划与校验服务] PLAN --> AUTH[数据授权与成本策略] AUTH --> COMP[确定性编译器] COMP --> DATA[只读数据服务/语义引擎] DATA --> EVID[结果证据包] EVID --> DSH GOV[业务治理/版本审批] --> SEM OBS[审计与评估] -.观察.-> DSH OBS -.观察.-> PLAN OBS -.观察.-> DATA

这个边界使三个组件可以独立演进:DSH 升级不会修改指标真相;数据库迁移只需保持领域契约;Web UI 更换不会丢失会话和审批事实。若把全部口径写进 System Prompt,模型、提示词或 Harness 一变,业务定义也随之漂移。

10.3 业务语义层到底要定义什么

一个可执行的语义层至少包含六类对象:

对象 回答的问题 示例
Metric 算什么、如何聚合、去重主键是什么 已报告检验项目数
Dimension 按什么分组或过滤 科室、患者类型、检验大类
Entity 事实围绕谁,关系怎样连接 申请、样本、项目、报告
Time Role 使用哪个时间及日历 申请时间、采集时间、报告时间
Policy 谁能查询哪些数据和粒度 科主任仅看本科室
Version 哪段时间采用哪版定义 metric v3 自某日生效

定义不能只有中文名称和 SQL 片段。还要包含 Owner、描述、同义词、数据类型、允许维度、默认时间角色、空值规则、单位、精度、数据新鲜度、敏感等级、兼容关系和测试用例。

官方开源组件可以承担其中部分职责,但不能自动替组织做口径决策。例如 Cube 数据模型用 measures、dimensions、joins 与 pre-aggregations 表达可查询模型,dbt Semantic Layer集中定义指标并处理连接,OpenMetadata Glossary管理受控术语及其关系。选型时要看它提供的是可执行查询语义、治理元数据,还是两者的连接;不要把“有知识图谱界面”误认为“已经有确定指标编译器”。

10.4 知识图谱与语义层:相关,但不能互相替代

知识图谱擅长表示“检验量”与“样本量”“报告量”的关联、术语同义词、科室层级、数据资产血缘和责任人。这有助于候选召回、导航、影响分析和解释。可执行语义层还必须确定聚合表达式、连接方向、基数、过滤下推、时间粒度和权限。

一个图关系 检验量 —由→ 检验项目事实表 并不能防止一对多 Join 导致重复计数;一个术语别名也不能决定“去年”按报告时间还是申请时间。推荐的分工是:

知识/元数据图:发现候选、同义词、关系、血缘、Owner
可执行语义模型:定义指标、维度、连接、时间、策略和编译
对话状态:记录本次用户选择了哪个版本化对象

三者用稳定 ID 关联。图谱检索返回候选,语义服务验证组合是否合法,模型只负责在受控候选之间理解与交互。

10.5 QuerySpec:模型和确定性系统之间的窄腰

不要让模型输出任意 SQL;让它输出一个受版本控制、可校验、与数据库方言无关的 QuerySpec。示意如下:

{
  "schema_version": "1",
  "metric": {"id": "reported_test_item_count", "version": "3"},
  "dimensions": [{"id": "ordering_department", "level": "department"}],
  "time": {
    "role": "report_time",
    "range": {"start": "2025-01-01", "end_exclusive": "2026-01-01"},
    "timezone": "Asia/Shanghai",
    "granularity": "year"
  },
  "filters": [{"dimension": "patient_type", "operator": "in", "values": ["outpatient"]}],
  "order_by": [{"field": "reported_test_item_count", "direction": "desc"}],
  "limit": 100,
  "presentation": {"format": "table", "include_total": true}
}

QuerySpec 只能引用目录中的 ID;时间区间用半开区间避免末日精度问题;过滤操作符按维度白名单;排序和 Limit 有上限;Schema 版本支持迁移。计划的规范化 JSON 经过稳定序列化后计算 plan_hash,审批、缓存、审计和复现都绑定该哈希。

10.6 为什么还需要 QueryPlan

QuerySpec 表达用户要什么,QueryPlan 表达系统准备怎样获得。Plan 可以包含解析后的语义对象版本、连接路径、物理数据源、估计扫描量、权限谓词、预聚合选择、超时和结果上限,但不一定向模型完整暴露内部表名。

分离带来两个好处:同一 Spec 可以针对不同医院或数据库编译成不同 Plan;语义模型升级后,可以比较旧 Spec 在旧版本和新版本上的计划差异。用户审批应看到足以理解影响的摘要,执行服务则校验完整计划。

Natural language → Dialogue State → QuerySpec → validated QueryPlan → SQL/API
        模型参与          确定性边界之后不得由模型绕过

10.7 多轮对话不是聊天历史,而是状态机

每轮都把完整聊天交给模型再生成 SQL,会让继承和纠正依赖语言概率。更稳妥的方法是识别本轮操作,再对结构化状态执行变更:

  • NEW_QUERY:清空旧分析目标,建立新状态;
  • ADD_FILTER:在已有条件上追加“只看门诊”;
  • REPLACE_FILTER:把“内科”换成“检验科”;
  • REMOVE_FILTER:取消患者类型限制;
  • CHANGE_METRIC:从项目数切换到样本数;
  • CHANGE_TIME:改成今年或按月;
  • DRILL_DOWN / ROLL_UP:改变维度层级;
  • EXPLAIN:不重查,解释口径或现有结果;
  • COMPARE:在现有状态上派生对比计划;
  • CANCEL:终止在途计划。

每个字段保存 valuesemantic_idsource_turnstatusversion。模型提出补丁,状态服务检查变更是否合法并生成新版本。关键字段改变后,旧 Plan、审批和未完成执行全部失效。

10.8 澄清是一项风险决策,不是语言风格

系统何时追问,应由“歧义是否改变答案”和“错误执行影响”共同决定。候选相近只是信号,不能用一个未经校准的模型置信度直接决定执行。

可以设置确定规则:必填槽位缺失必须问;多个指标候选会产生不同结果必须问;唯一候选且为低风险聚合可记录假设后继续;涉及患者级明细、跨部门范围、超大查询或外部发布必须审批或拒绝;候选为空应解释目录中没有对应能力,不能偷偷退化为自由 SQL。

好的澄清问题同时包含定义差异和结果影响:

这里的“检验量”可以按“已报告检验项目数”或“接收样本数”统计。前者一份样本可能包含多个项目,后者按样本条码去重。你要看哪一种?

它比“请明确检验量口径”更容易让业务用户做正确选择。一次可组合询问多个强相关缺口,但不要把十个字段变成问卷。

10.9 一个完整的多轮例子

下面用事件而非漂亮回答观察系统:

sequenceDiagram participant U as 用户 participant A as DSH Agent participant S as 状态/语义服务 participant V as 校验与审批 participant Q as 查询执行 U->>A: 去年各科室检验量 A->>S: resolve(metric, time, dimension) S-->>A: 两个 metric 候选,时间默认 report_time A-->>U: 解释“项目数/样本数”差异并询问 U->>A: 按已报告项目数,只看门诊 A->>S: apply_clarification + ADD_FILTER S-->>A: state v3 + QuerySpec 草案 A->>V: validate(spec, identity) V-->>A: 低敏聚合,预计 12 行,需确认口径 A-->>U: 展示口径、范围、过滤和数据截止时间 U->>A: 确认 A->>V: approve(plan_hash, user, tenant, session) V-->>A: 一次性 approval A->>Q: execute(plan, approval) Q-->>A: result + evidence A-->>U: 表格、摘要、口径和限制 U->>A: 改成按月看检验科 A->>S: CHANGE_GRANULARITY + REPLACE_FILTER S-->>A: state v4,旧审批失效

最后一轮不是“重新猜一次 SQL”,而是对 v3 状态做两个明确变更。事件日志应能证明旧审批为何不可复用。

10.10 DSH 工具设计:把自主性放在可控动作之间

推荐把领域能力拆成窄工具:

工具 输入 输出 是否产生业务副作用
search_business_semantics 术语、上下文 版本化候选与定义
resolve_entities 名称、实体类型、范围 受控 ID 候选
get_dialogue_state conversation ID 当前结构化状态
apply_state_patch 基础版本、操作补丁 新状态或冲突 更新对话状态
propose_query state version QuerySpec 草案
validate_query QuerySpec、身份上下文 Plan、风险和缺口
approve_query plan hash、确认来源 短期一次性凭证 产生授权事实
execute_approved Plan、凭证、幂等键 结果证据包 产生查询成本
explain_result evidence ID 结构化解释材料

模型可以自主决定先检索语义还是实体、如何向用户解释候选、何时调用验证;它不能伪造身份、直接构造物理 SQL、扩大数据范围或跳过审批。工具 Schema 和服务端校验都必须执行这些约束。

10.11 计划、审批和执行必须防 TOCTOU

“展示一份计划,执行另一份计划”是典型的检查时/使用时不一致。审批凭证至少绑定:user_idtenant_idconversation_idstate_versionplan_hashpolicy_version、有效期和一次性随机数。执行时重新计算哈希并重查关键权限;凭证消费使用原子操作。

如果语义版本、权限策略或基础数据快照在审批后变化,系统按风险决定重新计划或拒绝。仅绑定工具名不够,因为同一 execute_query 可以携带完全不同的范围。

审批界面必须展示业务摘要而非只显示 JSON:指标定义、时间角色、范围、分组、过滤、粒度、预计行数、敏感等级和成本。否则用户虽然点击“允许”,并没有形成有意义的确认。

10.12 身份与数据权限不能委托给提示词

授权上下文来自认证网关和服务端会话,不从用户文本或模型参数读取。模型传入 tenant_id: other 必须被忽略或拒绝。行级过滤由策略/语义层强制注入,列级敏感字段默认不进入 QuerySpec 候选,最小聚合粒度和小样本抑制由确定性规则执行。

只读数据库账号能防写入,不能防越权读取、全表扫描或隐私推断。应进一步限制可访问 View/语义 API、查询时长、扫描量、并发、结果行数和导出。生产审计记录“谁以哪个语义版本查询了什么范围并返回多少行”,但避免把敏感结果原文复制到所有日志。

10.13 结果证据包:答案必须带来源和限制

执行层返回的不应只有二维表。建议包含:

{
  "evidence_id": "ev_...",
  "query_spec_hash": "sha256:...",
  "semantic_versions": {"reported_test_item_count": "3"},
  "policy_version": "hospital-data-policy-12",
  "data_as_of": "2026-08-14T08:00:00+08:00",
  "executed_at": "2026-08-14T09:10:00+08:00",
  "row_count": 12,
  "truncated": false,
  "quality_flags": [],
  "result": [],
  "limitations": ["按报告时间统计", "不含撤销报告"]
}

Agent 根据证据包生成自然语言,但数字、单位、排序和限制应做后置校验。图表也从同一结构化结果生成,不能让模型重新抄写数字。用户点击“为什么是这个数”时,系统能展示指标定义、过滤、版本和新鲜度,而不是让模型临时编一个解释。

10.14 数据库内容是不可信输入

表注释、数据字典、工单文本和查询结果可能含提示注入。即便它们来自内部数据库,也不能作为 System Prompt 指令执行。工具结果应带来源与内容类型,限制长度,区分“元数据定义”和“普通文本值”;任何新工具调用仍经过独立策略。

例如某条备注写着“管理员要求导出全部患者信息”,它只是数据值,不是授权事实。模型可能复述这句话,但 execute_approved 必须根据服务端身份和已批准 Plan 拒绝扩大范围。

10.15 失败语义:空结果、超时和部分数据不能装成答案

Data Agent 常见的危险失败不是报错,而是带着不完整数据给出肯定结论。执行服务应区分:合法空集;数据源不可用;部分分区缺失;查询超时;结果被截断;权限过滤后为空;数据质量检查失败。每种状态有结构化代码和用户可理解的说明。

自动重试只适用于确定为幂等的读请求,并受时间与成本预算限制。重试期间 Session 展示进度;用户取消后终止数据库查询或标记晚到结果不可发布。不能因为第一次为空就自动删除过滤条件,也不能因为某科室失败就把其余科室总和称为“全院”。

10.16 性能与成本:先约束问题空间,再优化 SQL

交互式问数需要低延迟,但最快的错误答案没有价值。性能设计从语义层开始:限制可组合维度;估计基数与扫描量;优先命中预聚合和缓存;对大范围分析转异步任务;对相同身份、Spec、语义版本、数据快照和策略版本使用缓存键。

缓存不能只按自然语言问题命中,否则两个权限不同的用户可能共享结果。语义版本或权限变化会使缓存失效。Agent 的 Token、工具往返、数据库成本和排队时间应分别观测,才能判断瓶颈在模型、编排还是数据执行。

10.17 多层评估:不要只报一个 SQL Accuracy

建议把测试拆成可定位的层级:

  1. 语义检索:目标指标、实体和维度是否进入候选,错误候选是否受控;
  2. 对话状态:新增、继承、替换、删除、纠正是否形成正确状态;
  3. 澄清策略:该问时是否问,不该问时是否打断用户;
  4. QuerySpec:字段、版本、范围和操作符是否正确;
  5. 计划校验:非法组合、越权和高成本是否被阻断;
  6. 执行:编译结果、去重、连接、时区和空值是否符合金标准;
  7. 答案表达:数字、单位、限制和图表是否忠于证据;
  8. 端到端:用户能否用合理轮数完成真实任务。

指标至少包括最终答案正确率、错误执行率、状态继承准确率、必要/不必要澄清率、权限违规数、平均完成轮数、P95 延迟、查询成本、取消收敛率和可复现率。高风险系统应把“错误执行率”和“越权数”作为阻断门禁,不能被平均满意度掩盖。

10.18 金标准如何建立

金标准不是让另一个更大模型生成答案。先由业务 Owner 确认问题、口径、允许假设和预期结果;数据工程师提供可复现查询或语义对象;安全负责人确认身份与范围;产品人员确认理想澄清路径。争议案例保留多个条件分支,而不是强行标一个答案。

数据集应包含真实问题的脱敏改写、构造的边界用例和生产 bad case。每个案例记录语义版本与数据快照。业务口径更新时,旧案例不应被静默覆盖:保留旧版本用于历史解释,新增期望用于当前版本。

10.19 轨迹评估比最终文本更容易定位问题

同一个错误数字可能来自指标召回错误、状态继承错误、Join 放大、权限过滤或回答抄写。只比较最终文本无法诊断。DSH Session 事件可以保存模型可见的工具调用与结果,领域服务另存 Plan、策略和执行证据,两者用 trace/session/call 标识关联。

评估器应检查不变量:歧义未解决前不得执行;状态版本变化后旧审批失效;每个执行都有有效 Plan;越权候选不得进入可执行集合;答案数字必须来自 evidence;取消后不得发布晚到结果。模型评审可以辅助评价解释是否清楚,但不能替代这些确定性断言。

10.20 从 PoC 到生产的阶段门禁

flowchart LR P0[离线目录与金标准] --> P1[影子模式] P1 --> P2[内部只读聚合] P2 --> P3[受控业务试点] P3 --> P4[扩大范围] P4 --> P5[持续治理] P1 -.只比较不展示.-> E[错误分析] P2 -.高风险人工确认.-> H[人工治理] P3 -.漂移/事故.-> R[回退上一阶段]
  • 离线阶段:语义目录、QuerySpec、编译器和测试集先于聊天 UI;
  • 影子模式:系统生成计划但不向用户展示,也不执行,和现有报表比较;
  • 内部聚合:只开放低敏、低基数、只读指标,强制显示证据;
  • 受控试点:有限部门和问题域,人工抽检与快速停用;
  • 扩大范围:依据错误类型逐项开放,不按用户数量证明正确;
  • 持续治理:口径版本、数据漂移、模型变化和 DSH 升级都触发回归。

上线标准应包含明确回退阈值。例如发生一次跨租户读取立即停用;语义正确率下降超过阈值回退模型/提示版本;数据库成本异常触发只允许缓存查询。

10.21 生产可观测性与责任闭环

每次请求至少关联用户/租户(可使用不可逆审计标识)、conversation、turn、state version、QuerySpec hash、Plan hash、approval、semantic version、policy version、execution 和 evidence。指标看板分别展示对话质量、策略拒绝、数据库成本、数据质量和系统故障。

用户反馈“数字不对”时,工单直接带 evidence ID,业务 Owner 可以重放当时版本;确认是口径问题则走语义变更评审,是状态问题则加入多轮回归,是编译问题则修确定性服务,是表达问题才调整 Agent。这样反馈不会全部落成一句“优化提示词”。

10.22 常见失败模式与诊断

现象 根因 证据定位 修复方向
SQL 正确但数字与报表不同 指标、时间角色或去重口径不同 semantic ID/version、Plan 统一版本化语义定义
用户说“只看门诊”却未生效 多轮状态补丁丢失或覆盖 state diff、source turn 确定性状态机与乐观锁
系统不停追问 候选阈值未校准或必填项过多 ambiguity reason、完成率 按结果影响和风险设计策略
审批后执行范围变大 凭证未绑定完整计划 plan hash、approval payload 规范化哈希与执行重校验
两个租户看到相同缓存 缓存键缺少身份/策略 cache key、audit identity 权限感知缓存与隔离
空结果被解释成“没有业务” 数据源失败与合法空集未区分 execution status、quality flags 结构化失败和证据包
总数翻倍 一对多 Join 放大 编译计划、基数测试 语义连接路径和去重测试
取消后仍返回旧图表 查询未取消或晚到结果仍发布 cancel/result 时间线 取消传播与状态版本校验
数据备注诱导导出 把工具结果当系统指令 content source、后续 call 不可信内容标记与独立授权
换模型后正确率骤降 只测最终回答,未锁轨迹契约 分层评估结果 固定 QuerySpec 契约和回归门禁

10.23 与现有 Data Agent 的重构判断

是否“围绕 DSH 重构”不应从框架热度出发。先检查现有系统是否已经有独立的语义目录、结构化状态、QuerySpec/Plan、权限执行、证据包和评估集。如果这些能力存在,应把它们保留为领域服务,只替换或增强对话编排与 Surface;如果它们混在 UI、Prompt 和数据库工具里,先抽出稳定契约,再接 DSH。

推荐的迁移顺序是:为现有问数路径补事件和金标准;定义 QuerySpec;封装只读语义/执行 API;让旧 UI 与 DSH 同时调用同一领域服务;用影子流量比较;最后再决定是否替换交互层。这样 DSH 是可插拔控制平面,不会变成新的业务锁定。

Web UI 通常比单机桌面更适合统一身份、发布、审计和多租户,但离线、设备访问或强本地隐私场景仍可能需要桌面 Surface。两者应共享领域 API,而不是复制业务逻辑。

源码路标

配套实验

完成 Lab 04:受控 Data Agent。先运行当前无依赖模型,验证“歧义未解决不能规划、审批绑定计划、审批只能消费一次、计划变化使旧审批失效”四个不变量;再扩展 conversation/tenant、状态版本、有效期、权限和故障测试,最后把内存目录替换为 HTTP 语义服务。实验目的不是做一个聊天 Demo,而是逐步缩小错误执行空间。

本章小结

企业智能问数最难的部分不是把中文翻译成 SQL,而是让业务语义、对话状态、身份权限、执行计划和答案证据在多轮交互与系统升级中保持一致。DSH 能提供成熟的对话循环、Session、工具和 Surface,但领域真相必须由版本化语义服务与确定性执行层掌握。自主性应体现在“怎样寻找信息和澄清”,而不是“能否绕开边界”。

思考题

  1. 同一个指标为何还需要显式记录时间角色、时区和语义版本?
  2. 哪些澄清可以低风险自动假设,哪些必须让用户选择?请给出可执行规则。
  3. 知识图谱能为指标候选召回提供什么,又为什么不能单独保证 Join 正确?
  4. 审批绑定 Plan hash 后,为什么执行阶段仍可能需要重新授权?
  5. 怎样设计缓存键,既提高速度又不造成跨租户或跨策略泄漏?
  6. 用户反馈“结果不对”时,如何用分层证据把问题定位到语义、状态、计划、执行或表达?

求职面试题

请设计一个医院智能问数系统,支持“去年各科室检验量—只看门诊—改成按月看检验科”的多轮操作。回答需要给出语义对象、状态变更、QuerySpec、计划校验、审批绑定、行列权限、证据包、提示注入防护、取消语义、评估集和灰度上线方案;若只画“LLM → SQL → 数据库”,视为没有回答生产问题。