PMFlow/docs/superpowers/specs/2026-04-15-answer-presentat...

8.9 KiB
Raw Blame History

Answer Presentation Policy Design

Goal

为 query.answer 建立一层独立的“回答展示契约”,把运行时证据、审计元数据和客户可见文案彻底拆开。后续内部查询与外部查询的用户可见回复默认只展示面向客户的中文化内容,不再把 Project database / Semantic facts / Knowledge Graph / Feishu history 这类机器标签或内部术语直接暴露给客户。

Why Now

当前问题不是某一个英文 label 写得不好,而是整条回答链没有区分:

  • 机器内部证据
  • 后台审计证据
  • 客户可见展示

现在英文和内部术语会从四个出口漏到客户侧:

  1. retrieval / deterministic query 分支直接写英文 source_label
  2. 来源摘要 直接拼接这些 source_label
  3. citations / evidence_pack / sources_used 直接复用原始 label
  4. 生成式回答 prompt 的 context block 里也是 来源: {item.source_label},模型会把英文来源再复述进正文

所以这不是“翻译一下来源摘要”能解决的问题,而是展示契约缺失。

Confirmed Baseline

  • 客户可见回答默认走 customer_safe 展示契约。
  • customer_safe 视图只展示中文化后的来源显示名和客户可读结论。
  • raw_source_label / source_type / source_id / chunk_id / tool_trace 继续保留,但只属于 audit/debug 视图。
  • 不做“看到什么翻译什么”的散点补丁;统一由单点展示策略产出 display_label。
  • 不强行翻译业务对象真名。项目名、产品名、人员名如果本身是英文,可以保留。
  • internal / external 来源过滤都必须 fail-closed;过滤空了就不显示客户来源摘要,不允许把原始机器来源兜回来。
  • 展示层整治必须覆盖正文生成 prompt、来源摘要、payload citations/evidence pack 三层,不能只改末尾渲染。

Scope

In Scope

  • query.answer 的客户可见回答展示
  • internal / external 的来源显示策略
  • answer prompt 上下文里的来源显示名
  • response payload 的 customer/audit 双视图拆分
  • 来源摘要、citations、evidence_pack 的客户安全化

Out Of Scope

  • internal DSL / semantic facts 的查询逻辑
  • external provider / MCP 执行层
  • requirement-plan / draft confirm / card rendering
  • 前端反馈中心、训练中心这类管理后台数据模型

Current State

1. 内部来源头是中文,但来源 label 不是

backend/app/services/adaptive_rag_service/mixin_query_route_gateway.py

  • internal route 会统一加 内部知识库查询:
  • 但 来源摘要 是直接拼 label
  • 当前没有 customer-safe label 层

2. 多条 retrieval 分支直接写英文机器 label

例如:

  • Project database
  • User database
  • Semantic facts
  • Knowledge Graph

这些 label 当前分散在 deterministic query、structured db retrieval、graph retrieval 等多条分支里,不是单点控制。

3. _source_label() 也在直接产出客户可见英文 / 内部术语

backend/app/services/adaptive_rag_service/mixin_conversation_utils_a_p1.py

  • Document[...]
  • Feishu history
  • PMFlow records
  • URL[...]

这些 label 现在会被直接透传给最终回答和 citations。

4. payload 层会把原始 label 继续写进 citations / evidence pack

backend/app/services/adaptive_rag_service/mixin_answer_payload.py

  • _build_citations_from_sources() 直接读取 item.label / metadata.source_label
  • _build_evidence_pack() 再把 source_label 聚合进用户可见 summary.evidence

5. 模型正文也会吃到原始来源 label

backend/app/services/adaptive_rag_service/mixin_deep_guard_answer.py

当前 context block 是:

[1] 来源: {item.source_label}
相关度: ...
内容: ...

这意味着即使最后渲染层不显示英文,模型正文也可能主动复述这些英文来源。

Root Cause

根因不是“英文太多”,而是运行时把一份字段同时拿去做了三种用途:

  1. 检索/审计定位
  2. 内部 debug 与治理
  3. 客户可见展示

只要这三种用途继续共用同一个 source_label,后面不管补多少翻译映射,都会继续漏。

Design Decision

新增单点 Answer Presentation Policy,让回答证据从一开始就分成两套字段:

  • 机器字段
  • 展示字段

统一数据结构建议为:

字段 用途
source_type 机器类型
source_id 机器主键/引用
raw_source_label 原始 label,仅审计使用
display_label 客户可见显示名
display_category 如 项目数据/成员数据/文档资料/语义事实
target_url 可外显链接(若允许)
permission_scope 可见范围
audience_visibility customer / admin_only

后续所有客户可见回答只消费 display_* 字段,绝不直接消费 raw_source_label。

Answer Presentation Contract

建议把回答展示视图显式分成两类:

customer_safe

给客户看的默认视图,只包含:

  • 中文结论
  • 中文来源显示名
  • 客户可读的证据摘要
  • 必要时的外部 URL

不允许包含:

  • raw_source_label
  • tool_trace
  • chunk_id
  • 内部表名、内部 source type
  • Project database / Semantic facts / Knowledge Graph / Feishu history 等内部术语

admin_debug

给后台治理或排障看的视图,保留:

  • raw_source_label
  • source_type
  • source_id
  • chunk_id
  • tool_trace
  • reason_code
  • 原始 citations

Source Display Registry

新增单点 Source Display Registry,统一把机器来源映射为客户可见显示名。

示意映射:

机器来源 客户显示名
db + projects 项目数据
db + users 成员数据
db + semantic_facts 语义事实
graph 关联知识
doc 文档资料
feishu 飞书消息
pmflow 系统记录
url 外部网页
system guard 系统校验

这个 registry 只负责展示,不改变检索与审计逻辑。

Mandatory Flow

回答展示主链改成:

  1. retrieval / deterministic query 产出机器证据
  2. Answer Presentation Policy 归一化出 raw_source_label + display_label
  3. prompt context block 只使用 display_label
  4. 来源摘要 只使用 display_label
  5. response payload 拆成 customer_citations 与 audit_citations
  6. 前台默认只消费 customer_safe
  7. 治理后台按需消费 admin_debug

Prompt Hygiene Rule

生成式回答 prompt 必须改成只喂客户显示名:

从:

来源: raw_source_label

改成:

来源: display_label

否则就算前端不展示英文,模型正文仍会继续输出英文内部术语。

Failure Semantics

  • 某来源无法映射 display label:客户视图不展示原始 label,只显示更高层级中文类别,或直接不列该来源
  • source filter 后为空:不显示来源摘要,不回退原始机器来源
  • payload 构建失败:保留最小结论,不允许把 raw audit fields 透给客户

Rollout

Phase A

先写入基线文档,不动运行时代码。

Phase B

落地 Source Display Registry + customer_safe/admin_debug 双视图。

Phase C

把 prompt context、来源摘要、citations/evidence_pack 全部切到展示契约。

Phase D

补回归测试,锁住“客户侧不再出现英文机器标签/内部术语”。

Acceptance Criteria

  • internal query.answer 的客户可见回复默认纯中文展示壳
  • 来源摘要 不再出现 Project database / Semantic facts / Knowledge Graph / Feishu history
  • 模型正文不再因为 prompt context 泄露英文来源 label
  • audit/debug 仍可看到原始 raw_source_label/source_type/source_id
  • external/internal 的 source filter 都是 fail-closed

Planned File Changes

新增模块

  • backend/app/services/adaptive_rag_service/source_presentation.py

运行时接入

  • backend/app/services/adaptive_rag_service/mixin_deep_guard_answer.py
  • backend/app/services/adaptive_rag_service/mixin_query_route_gateway.py
  • backend/app/services/adaptive_rag_service/mixin_answer_payload.py
  • backend/app/services/adaptive_rag_service/mixin_conversation_utils_a_p1.py
  • backend/app/services/adaptive_rag_service/mixin_retrieval_router_db.py
  • backend/app/services/adaptive_rag_service/mixin_retrieval_graph.py
  • backend/app/services/adaptive_rag_service/mixin_deterministic_query_count.py
  • backend/app/services/adaptive_rag_service/mixin_deterministic_query_list.py

回归测试

  • backend/tests/test_answer_presentation_policy.py
  • backend/tests/test_internal_query_source_display_contract.py
  • backend/tests/test_external_query_source_display_contract.py
  • backend/tests/test_adaptive_rag_prompt_source_hygiene.py

文档基线

  • AGENTS.md
  • docs/architecture/RUNTIME_GUARDRAILS.md
  • docs/architecture/ARCHITECTURE_INVENTORY.md
  • docs/superpowers/specs/2026-04-15-answer-presentation-policy-design.md