业务故事站
P1 AIP

可解释问答:决策证据链

NLQ 管"查数",决策证据链管"答疑":把"销售额到底怎么算""华东区下滑对业绩影响多大"这类问题变成一次 5 步链——问题拆解→三路检索→证据核验→答案合成→引用溯源,答案必须是 markdown + [n] 引用,每一步都写 ai_decision 审计事件。答得对不对,不用信 AI 的嘴,直接看引用和审计回放。看完这 4 个故事,你就能自己提一个决策问题并读懂每一步。

业务分析师 销售总监 管理员 决策问答 证据链 引用溯源 共 4 个故事

能 / 不能速览

✅ 这个主题能做
  • 决策/口径类问答:POST /decision/answer 同步执行 5 步链
  • 三路检索:RAG(metadata/knowledge/history/fewshot)+ 实体(aie_entities)+ 结构化 NLQ,融合去重后截断 12 条
  • 证据核验:LLM 批量判定 support / refute / irrelevant,refute/irrelevant 被丢弃
  • 答案只基于证据:markdown 三节结构(结论/分析/建议)+ [n] 引用编号
  • 引用溯源:cite 步骤确定性映射 [n] 到 URI(doc:// / object://aie/ / datasource://),ad_citations 落表
  • 每步 ai_decision 审计留痕(refID=trace_id),/admin/ai-audit 可回放整条决策链
⛔ 这个主题做不了
  • 结构化 NLQ 通道本批未注入(NLQRunner 传 nil,恒 skipped;数据源为空也 skipped)
  • RAG 非 metadata 通道的引用 URI 是内容寻址降级 doc://aip/<md5>,非真实文档 ID
  • 同步执行:整链多步 LLM,耗时随证据数与 LLM 速度增长,前端要放宽超时(300s)
  • 证据内容截断:进入 prompt/引用最多 600 字符;引用上限 10 条
  • verify 漏判的条目保守保留(按 support 处理),宁可多给证据不少给

适用角色

本主题面向三个角色:

  • 业务分析师:核心使用者,用决策问答替代"翻群聊/找数据工程师问口径",拿到带引用的答案。
  • 销售总监 / 业务经理:消费结论与引用;有疑问时要求管理员回放决策链,理解"凭什么这么答"。
  • 管理员:在 /admin/ai-audit 回放 ai_decision 审计,定位检索漏路或核验降级。

页面在 /decision,任意登录用户可提问(protected),审计回放页面为管理员入口。

能力速览(能做什么)

决策问答(POST /decision/answer)

同步执行 5 步链,返回 answer(markdown)+ steps(5 步明细)+ citations(引用清单)。

问题拆解(decompose)

LLM 把问题拆成 1~4 个完整中文疑问句,提升检索召回;坏 JSON 重试 1 次,失败降级为原始问题。

三路检索(retrieve)

RAG 四/五通道 + 实体直查 aie_entities + 结构化 NLQ,URI/内容 MD5 去重、分数降序、截断 12 条。

证据核验(verify)

LLM 批量判定每条候选证据 support/refute/irrelevant,非法输出降级 unverified(诚实标注)。

答案合成(synthesize)

仅基于证据生成三节 markdown,结论句末标 [n] 引用;无证据走"证据不足"兜底,不编造。

引用溯源(cite)+ 审计

确定性映射 [n]→引用(未引用证据按分数追加,上限 10);每步 ai_decision 审计留痕可回放。

调整指南(怎么调整)

  • 带数据源提问:question 之外加 data_source 字段;有数据源时结构化 NLQ 通道才可能执行(需注入 NLQRunner)。
  • 补知识底料:答案"证据不足"时,先补知识库文档或触发实体抽取,再重新提问。
  • 看步骤状态:steps[].status 是 ok/degraded/skipped,HTTP 200 不等于业务成功;LLM 降级看 verify 的 unverified 标注。
  • 改证据数量:融合后候选截断 12 条、引用上限 10 条是常量,证据多时答案只基于最强的 12 条。
  • 回放定位问题:答案不满意先看 /admin/ai-audit?trace_id=,定位是检索漏了路还是核验降了级。

做得好的场景

决策证据链在"口径答疑 + 可追溯"上收益最明显:
  • 几十秒出可溯源口径:以往问口径要翻群聊/找数据工程师,半天到一天;现在一条提问 + 证据引用,几十秒给到可溯源的答案。
  • 答案不是黑盒:5 步链全透明,[n] 引用指向 doc:// / object://aie/ 资源,点开即溯源。
  • 审计可回放:每一步一条 ai_decision 事件(refID=trace_id),"为什么这么答"可以逐步骤解释给管理层。
  • 诚实不编造:无证据就说"证据不足",LLM 不可用就标"未经推理与核验",降级结果绝不冒充推理结论。

限制与不足

以下是明确的边界,使用前先知道:
  • 结构化通道本批 skipped:NLQRunner 未注入(接线传 nil),数据源为空也 skipped;要启用需注入受控 NLQ 实现。
  • 引用 URI 内容寻址降级:RAG 非 metadata 通道的文档引用是 doc://aip/<md5>,不带真实 docID,可后续经 Retriever 适配层回填。
  • 同步耗时:/decision/answer 是多步 LLM 同步执行,随证据数增长;前端 timeout 300s,未任务化。
  • 证据截断:进入 prompt/引用最多 600 字符;verify 漏判的条目保守保留。
  • 依赖数据底料:知识库/实体库/数据源里都没有的内容,只能诚实回"证据不足"。

场景故事

故事 1 问"销售额的计算口径",5 步链走全 + [n] 引用可溯源
背景
王姐在 /decision 页面输入"本季度销售额的计算口径是什么",带上数据源 aip_demo_warehouse。前置已就绪:知识库有《销售指标口径说明》文档,实体抽取已确认 销售额 实体。她想看看系统能不能答清楚"含不含退款"这种只有文档里才有的口径。
传统做法对比
以前问口径要翻群聊、找数据工程师,半天到一天才能定论;现在一条提问 + 证据引用,几十秒给到可溯源的答案,审计轨迹还能回放"为什么这么答"。
角色
业务分析师(提问,带数据源名)。
操作步骤
  1. 进入 /decision 页面,输入问题并选数据源
  2. 提交后等待 5 步链同步完成(loading 等待)
  3. 查看 5 步进度、答案 markdown 与引用面板
  4. 点击引用 URI 溯源到文档 chunk / 实体
系统响应
返回答案 + 5 步明细 + 引用清单:
{
  "code": 0,
  "data": {
    "answer": { "id": "<trace_id>", "question": "本季度销售额的计算口径是什么", "status": "done",
      "answer": "## 结论\n\n本月销售额 = 订单金额 - 退款金额 [1],指已成交订单的实付金额 [2]。\n\n## 分析\n...\n\n## 建议\n...",
      "steps": "...", "citations": "...", "created_by": "u1", "created_at": "..." },
    "steps": [
      { "step": "decompose", "status": "ok", "detail": { "sub_questions": ["销售额如何定义", "是否含退款"], "llm_ok": true } },
      { "step": "retrieve", "status": "ok", "detail": { "rag": 2, "entity": 1, "nlq": "skipped_no_datasource", "candidates": 3, "fused": 3 } },
      { "step": "verify", "status": "ok", "detail": { "llm_ok": true, "llm_retries": 0, "supported": 2, "dropped": 1 } },
      { "step": "synthesize", "status": "ok", "detail": { "llm_ok": true, "evidence_used": 2, "answer_chars": 120 } },
      { "step": "cite", "status": "ok", "detail": { "citations": 2 } }
    ],
    "citations": [
      { "id": "<uuid>", "answer_id": "<trace_id>", "seq": 1, "uri": "doc://aip/3?chunk=2", "title": "销售指标口径说明", "snippet": "销售额 = 订单金额 - 退款金额", "score": 0.9, "source": "rag" },
      { "id": "<uuid>", "answer_id": "<trace_id>", "seq": 2, "uri": "object://aie/<entity_id>", "title": "销售额", "snippet": "实体「销售额」(类型: metric)", "score": 0.85, "source": "entity" }
    ]
  }
}
结果洞察
decompose 把问题拆成"销售额如何定义/是否含退款"两个子问题;retrieve 三路中 RAG 命中 2 条、实体命中 1 条,结构化 NLQ 因本批未注入恒 skipped;verify 把无关历史对话判为 irrelevant 丢弃;synthesize 只基于 2 条 support 证据生成三节 markdown,结论句末 [1][2] 对应引用面板。
调整建议
提问带 data_source 字段结构化通道才可能执行;答案不满意先看 steps 哪一步 degraded;要提升实体命中,前置去 /entity-extract 确认实体别名。
动手试一试
登录:http://127.0.0.1:18080,admin / admin1。页面路径:/decision。输入:问题"销售额的计算口径是什么",数据源 aip_demo_warehouse。预期结果:5 步全 ok,answer 三节结构带 [n],citations 面板 2 条可溯源(doc:// 与 object://aie/)。
限制提示
/decision/answer 是同步多步 LLM,耗时随证据数/LLM 速度增长,前端 timeout 300s;RAG 非 metadata 通道引用 URI 是内容寻址降级 doc://aip/<md5>,非真实文档 ID。
故事 2 管理层追问依据:ai-audit 回放整条决策链
背景
李经理是销售总监,对王姐给出的"销售额 = 订单金额 - 退款金额"有疑问:"这个口径哪来的?"王姐把答案的 trace_id 交给管理员,管理员在 /admin/ai-audit?trace_id= 回放决策链,逐步解释答案的依据。
传统做法对比
以前 AI 给个结论就是黑盒,追问只能再问人、靠人背书;现在每一步都有 ai_decision 审计事件(refID=trace_id,stepType=decompose/retrieve/verify/synthesize/cite),可逐步骤解释"为什么这么答"。
角色
管理员(回放审计)+ 销售总监(核验答案依据)。
操作步骤
  1. 提问完成后记录 answer.id(= trace_id)
  2. 管理员打开 /admin/ai-audit?trace_id=<trace_id>
  3. 核对 5 条 ai_decision 事件(stepType 逐个对应)
  4. 查看 retrieve 各通道命中数与 verify 结论,定位问题
系统响应
审计回放按 trace_id 返回决策链事件(GET /api/v1/ai-audit?trace_id=):
ref_type=ai_decision  event_type=DECISION_ANSWER  ref_id=<trace_id>
step1 decompose result=success  detail={"sub_questions":["销售额如何定义","是否含退款"]}
step2 retrieve result=success  detail={"rag":2,"entity":1,"nlq":"skipped_no_datasource","candidates":3,"fused":3}
step3 verify   result=success  detail={"llm_ok":true,"supported":2,"dropped":1}
step4 synthesize result=success detail={"evidence_used":2,"answer_chars":120}
step5 cite     result=success  detail={"citations":2}
结果洞察
每步经 LogEventRef(refType=ai_decision, refID=trace_id, stepType=步骤名) 打点,result 按步骤状态映射(ok→success / degraded→degraded / skipped→skipped)。李经理据此看到:答案基于 RAG 命中的《销售指标口径说明》chunk 与 销售额 实体,检索了 3 条候选、核验后剩 2 条 support——口径出处一目了然。这类回放还能定位"检索漏了哪个通道"或"证据核验是否降级"。
调整建议
提问时带数据源名,审计里能区分结构化通道是 skipped 还是执行;要留作汇报证据,记下 trace_id 即可随时回放。
动手试一试
登录:admin / admin1。操作:/decision 提一个问题,复制返回的 answer.id,在 /admin/ai-audit?trace_id=<id> 回放。预期结果:5 条 ai_decision 事件,stepType 与 5 步链一一对应,retrieve 步骤能看到各路命中数。
限制提示
审计回放依赖 ai_decision 事件写入;audit 未注入时不打点;HTTP 200 不等于业务成功——LLM 降级、通道 skipped、证据不足都以 steps[].status / 答案前缀为准。
故事 3 LLM 网关不可用:诚实降级,不把摘要冒充结论
背景
某次演示时 LLM 网关不可用,王姐不知道,照常提交了同一个口径问题。系统仍返回 200——没有报错,但答案前多了一段说明,步骤状态也变了。她要确认"这段答案能不能直接当结论用"。
传统做法对比
不少系统此时要么整链报错,要么把降级结果当推理结论返回,用户无从分辨;本模块 200 + 诚实标注——答案明确说"未经推理与核验",降级结果绝不冒充。
角色
业务分析师(识别降级,判断答案可用性)。
操作步骤
  1. 断开/失效 LLM key(或模拟网关 500)
  2. 在 /decision 再次提交问题
  3. 看答案前缀与 steps 状态(degraded / unverified)
  4. 确认降级后不要直接当推理结论使用
系统响应
HTTP 200,但步骤降级、答案带"未推理"前缀:
answer: "> 说明:LLM 服务暂不可用,以下为检索证据摘要(未经推理与核验),仅供参考。\n1. **销售指标口径说明**(来源: rag, URI: doc://aip/3?chunk=2)\n2. **销售额**(来源: entity, URI: object://aie/<id>)..."

steps: [
  { "step": "decompose", "status": "degraded", "detail": { "sub_questions": ["本季度销售额的计算口径是什么"], "llm_ok": false } },
  { "step": "verify", "status": "degraded", "detail": { "llm_ok": false, "unverified": true, "supported": 2, "dropped": 0 } },
  { "step": "synthesize", "status": "degraded", "detail": { "llm_ok": false, "evidence_used": 2, "answer_chars": 120 } }
]
结果洞察
降级三连:decompose 降级为原始问题(degraded);verify 全部证据按 support 处理并标注 unverified=true(诚实标注"未经核验");synthesize 输出证据摘要并加"未经推理与核验"前缀(fallbackEvidenceMarkdown)。链路不断、前端可渲染、答案明确标注——不会把降级结果冒充推理结论。llm 为 nil 时全链同样走这套降级。
调整建议
恢复 LLM key 后重新提问即恢复完整推理;看到 unverified=true 或"未推理"前缀时,结论需人工核对原始证据,不要直接引用。
动手试一试
登录:admin / admin1。操作:临时让 LLM 网关不可用,在 /decision 提问。预期结果:HTTP 仍 200,答案前缀出现"LLM 服务暂不可用…未经推理与核验",verify 步骤 detail.unverified=true。
限制提示
降级时 decompose/verify/synthesize 的 llm_ok 均为 false,审计事件 result 为 degraded;引用 URI 在降级路径仍按来源兜底生成(entity→object://aie/、rag 其他通道→doc://aip/<md5>)。
故事 4 查不到证据:系统诚实说"证据不足",不编造
背景
王姐问了一个知识库、实体库、数据源里都没有的问题:"虚拟订单的跨境结算周期是几天?"知识库文档没写,实体库里没有对应实体,演示库也没有这张表。她想看系统会不会硬编一个答案。
传统做法对比
有些问答系统会"编造"一个看似合理的答案;决策证据链 prompt 硬性要求:证据不足必须写"证据不足",三路检索均无命中时 retrieve 标 degraded,宁缺毋滥。
角色
业务分析师(提问冷门问题,验证诚实性)。
操作步骤
  1. 在 /decision 输入知识底料里不存在的问题
  2. 查看 retrieve 步骤状态与 detail
  3. 查看 answer 与 citations(空引用)
系统响应
三路均无命中,答案走 noEvidenceAnswer 兜底:
answer: "未找到与该问题相关的证据,无法给出有依据的结论。"
steps: [
  { "step": "retrieve", "status": "degraded", "detail": { "rag": 0, "entity": 0, "nlq": "skipped_no_datasource", "note": "三路检索均无命中或均失败", "candidates": 0, "fused": 0 } }
]
citations: []
结果洞察
retrieve 三路都空,步骤标 degraded 并注明"三路检索均无命中或均失败";synthesize 因无证据走 noEvidenceAnswer(诚实回答"未找到证据,无法给出有依据的结论",llm_ok=true);cite 生成空引用。全部证据被判 refute/irrelevant 时同理:答案会如实说明证据不足。宁可诚实,绝不编造。
调整建议
想让这类问题有答案,先补料:知识库写文档、触发实体抽取、确认数据源元数据;补完重新提问。
动手试一试
登录:admin / admin1。操作:/decision 输入"虚拟订单的跨境结算周期是几天"。预期结果:answer 为"未找到与该问题相关的证据…",retrieve 步骤 degraded、note 注明三路均无命中,citations 为空。
限制提示
检索三路中任一路失败/未注入只跳过该路(detail 标 skipped/failed),不阻断整链;verify 漏判的条目保守保留(宁可多给证据不少给),可能让个别无关证据进入答案。

常见问题

决策证据链和 NLQ 查数有什么区别?

NLQ 是"查数":Text2SQL 生成 SQL 并执行,返回表格;决策证据链是"答疑":检索知识/实体/数据证据,核验后合成带引用的 markdown 答案。NLQ 主链路对 knowledge_qa 意图短路返回占位提示,本模块补上了知识问答/决策问答的完整推理链路。

引用 [n] 是怎么映射的?

cite 步骤做确定性映射(无 LLM):解析答案中 [n] 标记(首次出现顺序去重)对应证据成为引用(seq 1..);未被引用的证据按分数降序追加;按 URI 或内容 MD5 去重,上限 10 条。

为什么 retrieve 里的结构化通道总是 skipped?

结构化 NLQ 通道依赖 NLQRunner 注入,本批接线传 nil;且仅当请求带 data_source 才执行。注入了自定义实现(适配 orchestrator 的 Text2SQL+executor)后通道才启用。

审计在哪儿回放?

管理员在 /admin/ai-audit?trace_id=<trace_id> 回放,trace_id 就是答案 ID。5 步链每步一条 ref_type=ai_decision 事件,stepType 为 decompose/retrieve/verify/synthesize/cite。

答案一定可靠吗?

不一定。HTTP 200 不等于业务成功:LLM 降级、通道 skipped、证据不足都以 steps[].status / detail / 答案前缀为准。答案只基于核验通过的证据合成,且带引用可自查,但证据本身质量取决于知识库/实体库/数据源底料。

主题小结

一句话:决策证据链 = decompose→retrieve→verify→synthesize→cite 五步链,把决策问题变成"带引用、可回放、不编造"的可解释答案。记住边界:结构化通道本批 skipped、RAG 文档引用是内容寻址降级、同步执行耗时较长、无底料只能诚实说"证据不足"。看步骤状态比看 HTTP 状态码更能判断答案质量。