281 lines
8.9 KiB
Markdown
281 lines
8.9 KiB
Markdown
# 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 是:
|
||
|
||
```text
|
||
[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 必须改成只喂客户显示名:
|
||
|
||
从:
|
||
|
||
```text
|
||
来源: raw_source_label
|
||
```
|
||
|
||
改成:
|
||
|
||
```text
|
||
来源: 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`
|