649 lines
16 KiB
Markdown
649 lines
16 KiB
Markdown
# Interaction Boundary 与四挡合同设计
|
||
|
||
## 目的
|
||
|
||
这份设计要解决的不是“模型再聪明一点”,而是把 PMFlow 当前问答/建议/草稿/执行之间的边界收干净:
|
||
|
||
- `status`
|
||
- `recommendation`
|
||
- `draft`
|
||
- `execute`
|
||
|
||
当前问题不是系统不会回答,而是系统没有一个正式的“本轮允许做到哪一挡、明确禁止升到哪一挡”的运行时合同,导致 recommendation ask 会误滑到 `signal_source.onboard -> draft_pending_confirm`。
|
||
|
||
本设计的目标是:
|
||
|
||
1. 四挡独立,不能偷偷升级。
|
||
2. 每一挡都支持澄清、纠错、再次思考。
|
||
3. recommendation 必须能独立停住,不再被写链路抢走。
|
||
4. 用户可见回复必须自然业务化,不像架构师,也不能泄漏 packet / contract 术语。
|
||
5. 今天与昨天暴露出来的失败场景,必须转成自动化反例。
|
||
|
||
## 这次不做什么
|
||
|
||
- 不重写 `requirement_plan` / action card 主链。
|
||
- 不重构 `SignalSourceOrchestrationService` 业务编排本体。
|
||
- 不把前台所有文风都做一轮大翻修。
|
||
- 不引入新的中文关键词仓或正则仓去拍板 `query vs draft vs execute`。
|
||
|
||
## 当前已暴露的问题
|
||
|
||
### 1. recommendation ask 被错升成 onboard / draft
|
||
|
||
真实用户输入:
|
||
|
||
```text
|
||
我看这个项目现在还没有成型的信号源。你先别出草稿,只按当前项目情况,帮我推荐几个最值得接的外部信号源,并说清楚各自的价值。
|
||
```
|
||
|
||
真实结果:
|
||
|
||
- `intent = signal_source.onboard`
|
||
- `state = draft_pending_confirm`
|
||
- 先发 `⏳ 已完成入库,当前进入待确认队列。`
|
||
|
||
这说明:
|
||
|
||
- recommendation 与 draft 没有分层
|
||
- 负向边界 `先别出草稿` 没生效
|
||
- executor 链被过早放行
|
||
|
||
### 2. recommendation ask 被答成现状核查
|
||
|
||
用户明明在问“推荐几个外部信号”,系统却回:
|
||
|
||
- 当前没有信号源
|
||
- 当前项目没有记录
|
||
- 当前盘面怎样
|
||
|
||
这说明:
|
||
|
||
- `status` 与 `recommendation` 没分开
|
||
|
||
### 3. recommendation ask 被答成保守拒答
|
||
|
||
用户问:
|
||
|
||
```text
|
||
如果只考虑外部信号源,你会优先推荐哪 3 类?
|
||
```
|
||
|
||
系统回:
|
||
|
||
- 当前不能直接推荐
|
||
- 当前证据不足
|
||
|
||
这说明:
|
||
|
||
- recommendation 被误当成 freshness / fact verification
|
||
|
||
### 4. 稀疏项目只会打太极
|
||
|
||
例如 `自动开票系统` 只有一句描述时,系统长期只会回:
|
||
|
||
- 还缺业务步骤
|
||
- 还缺信号源
|
||
- 先别硬判
|
||
|
||
这类话术不算真正分析,只是保守拒答。
|
||
|
||
### 5. 客户可见回复泄漏内部术语
|
||
|
||
已经出现过:
|
||
|
||
- `分析包里明确写了`
|
||
- `missing_mapping`
|
||
- `focus_objects`
|
||
- `analysis_packet`
|
||
|
||
这说明:
|
||
|
||
- packet / presenter 边界没有守住
|
||
|
||
### 6. 新项目 / 新业务切换被旧线程污染
|
||
|
||
例如上一轮在聊 `测试产能警告`,下一轮问 `AI客服这个项目...`,却继续沿旧 subject / old scope 改写。
|
||
|
||
这说明:
|
||
|
||
- scope 连续性虽然已经补了治理,但还没有成为正式验收基线
|
||
|
||
## 设计原则
|
||
|
||
### 1. 先定边界,再决定动作
|
||
|
||
系统不能只理解“用户想干什么”,还必须理解“用户明确不要我干什么”。
|
||
|
||
### 2. 不追求一次答对,追求可纠错
|
||
|
||
系统要做到:
|
||
|
||
- 不越权
|
||
- 可停住
|
||
- 可被纠正
|
||
- 纠正后能原地重算
|
||
|
||
### 3. 结构化边界优先于运行时猜测
|
||
|
||
决策权不应该在:
|
||
|
||
- `if "先别出草稿" in text`
|
||
- `if "不要执行" in text`
|
||
|
||
而应该在:
|
||
|
||
- 统一语义规划层结构化产出的边界合同
|
||
- gatekeeper 对结构合同的执行
|
||
|
||
### 4. recommendation 是独立主链,不是 query 的弱版也不是 draft 的前奏
|
||
|
||
只要用户要的是建议,就应该允许系统:
|
||
|
||
- 给候选
|
||
- 给排序
|
||
- 给理由
|
||
|
||
但不能:
|
||
|
||
- 冒充已验证事实
|
||
- 顺手起草
|
||
- 顺手执行
|
||
|
||
### 5. 每一挡都必须有澄清、纠错、再次思考
|
||
|
||
这三件事不是失败兜底,而是系统能力的一部分。
|
||
|
||
## 新增运行时结构
|
||
|
||
### A. InteractionBoundaryPacket
|
||
|
||
职责:表达“这一轮允许做到哪一挡、禁止做到哪一挡”。
|
||
|
||
建议结构:
|
||
|
||
```python
|
||
class InteractionBoundaryPacket(BaseModel):
|
||
boundary_version: str = "v1"
|
||
|
||
requested_level: Literal[
|
||
"status",
|
||
"recommendation",
|
||
"draft",
|
||
"execute",
|
||
"unclear",
|
||
] = "unclear"
|
||
|
||
forbidden_levels: list[
|
||
Literal["status", "recommendation", "draft", "execute"]
|
||
] = Field(default_factory=list)
|
||
|
||
allow_status: bool = True
|
||
allow_recommendation: bool = True
|
||
allow_draft: bool = False
|
||
allow_execute: bool = False
|
||
allow_write: bool = False
|
||
|
||
needs_clarification: bool = False
|
||
clarification_reason: str = ""
|
||
clarification_questions: list[str] = Field(default_factory=list)
|
||
|
||
correction_allowed: bool = True
|
||
correction_targets: list[
|
||
Literal["project", "object", "level", "priority", "assumption"]
|
||
] = Field(default_factory=list)
|
||
|
||
self_review_required: bool = True
|
||
|
||
confidence: float = 0.0
|
||
evidence: list[dict[str, Any]] = Field(default_factory=list)
|
||
```
|
||
|
||
这不是给用户看的,而是给 gatekeeper 用的。
|
||
|
||
### B. AnswerControlDecision
|
||
|
||
职责:把 `SemanticDecision + InteractionBoundaryPacket` 合成真正能执行的层级结果。
|
||
|
||
建议结构:
|
||
|
||
```python
|
||
class AnswerControlDecision(BaseModel):
|
||
effective_level: Literal[
|
||
"status",
|
||
"recommendation",
|
||
"draft",
|
||
"execute",
|
||
"clarify",
|
||
]
|
||
|
||
blocked_levels: list[str] = Field(default_factory=list)
|
||
blocked_workflows: list[str] = Field(default_factory=list)
|
||
allowed_workflows: list[str] = Field(default_factory=list)
|
||
|
||
downgraded_from: str | None = None
|
||
requires_confirmation: bool = False
|
||
requires_self_review: bool = True
|
||
requires_correction_slot: bool = True
|
||
|
||
reason: str = ""
|
||
```
|
||
|
||
## 四挡定义
|
||
|
||
### 1. status
|
||
|
||
定义:
|
||
|
||
- 只回答当前现状
|
||
- 不给建议
|
||
- 不起草
|
||
- 不执行
|
||
|
||
允许:
|
||
|
||
- 当前已知内容
|
||
- 当前未知内容
|
||
- 当前只看到哪些记录
|
||
|
||
禁止:
|
||
|
||
- 偷偷给候选建议
|
||
- 偷偷起草或执行
|
||
|
||
### 2. recommendation
|
||
|
||
定义:
|
||
|
||
- 可以给候选、排序、理由
|
||
- 不能起草
|
||
- 不能执行
|
||
- 必须明确“这是建议,不是已验证事实”
|
||
|
||
### 3. draft
|
||
|
||
定义:
|
||
|
||
- 可以起草一版
|
||
- 必须带假设、缺口、待确认点
|
||
- 不能执行
|
||
|
||
### 4. execute
|
||
|
||
定义:
|
||
|
||
- 必须显式确认
|
||
- 可以落库 / 接入 / 执行
|
||
- 必须回显要动哪个对象、影响范围和执行结果
|
||
|
||
## 决策顺序
|
||
|
||
后续实现必须按这个顺序:
|
||
|
||
1. 现有 `WorkflowSemanticPlanner` 继续产出 `SemanticDecision`
|
||
2. 同轮补产出 `InteractionBoundaryPacket`
|
||
3. 把 `workflow_plans` 映射到四挡
|
||
4. 用 boundary packet 过滤 workflow
|
||
5. 产出 `AnswerControlDecision`
|
||
6. 再决定最终主链:
|
||
- `query.answer(status)`
|
||
- `query.answer(recommendation)`
|
||
- `draft_pending_confirm`
|
||
- `execute`
|
||
|
||
也就是说,`signal_source_flow` 可以被识别出来,但如果:
|
||
|
||
- `allow_draft = false`
|
||
- `allow_execute = false`
|
||
|
||
最终仍然只能落到:
|
||
|
||
- `query.answer(recommendation)`
|
||
|
||
## 负向边界优先级
|
||
|
||
必须明确:
|
||
|
||
1. 负向边界优先于系统自己的升级冲动
|
||
2. 负向边界低于用户后续显式升级确认
|
||
|
||
例如:
|
||
|
||
### 场景 A
|
||
|
||
```text
|
||
先别出草稿,只给我推荐几个高价值外部信号源。
|
||
```
|
||
|
||
预期:
|
||
|
||
- recommendation 可以回答
|
||
- draft / execute 必须被 block
|
||
|
||
### 场景 B
|
||
|
||
后续用户再说:
|
||
|
||
```text
|
||
可以,把第 1 个起草成接入草稿。
|
||
```
|
||
|
||
预期:
|
||
|
||
- 显式升级到 draft
|
||
- 仍不自动 execute
|
||
|
||
## 澄清、纠错、再次思考
|
||
|
||
### 1. 澄清
|
||
|
||
每一挡都允许澄清。
|
||
|
||
适用情况:
|
||
|
||
- 项目不清
|
||
- 业务对象不清
|
||
- 用户到底要 status / recommendation / draft / execute 不清
|
||
- 当前约束冲突
|
||
|
||
需要结构化字段:
|
||
|
||
- `needs_clarification`
|
||
- `clarification_reason`
|
||
- `clarification_questions`
|
||
|
||
### 2. 纠错
|
||
|
||
用户必须能低成本纠错:
|
||
|
||
- 不是这个项目
|
||
- 不是这个对象
|
||
- 不是这个方向
|
||
- 我现在不是要草稿,只要建议
|
||
- 我改了,现在直接出草稿
|
||
|
||
要求:
|
||
|
||
- 收到纠错后,必须重建新的 `InteractionBoundaryPacket`
|
||
- 不准带旧边界续写
|
||
- 输出里应承认:已按新纠正口径重算
|
||
|
||
### 3. 再次思考 / 自检
|
||
|
||
建议新增:
|
||
|
||
```python
|
||
class SelfReviewResult(BaseModel):
|
||
project_aligned: bool = True
|
||
object_aligned: bool = True
|
||
level_aligned: bool = True
|
||
leaked_internal_terms: bool = False
|
||
overstepped_boundary: bool = False
|
||
clarify_missing: bool = False
|
||
notes: list[str] = Field(default_factory=list)
|
||
```
|
||
|
||
至少要检查:
|
||
|
||
- 我是不是答错项目了
|
||
- 我是不是把 recommendation 偷偷升级成 draft 了
|
||
- 我是不是泄漏内部术语了
|
||
- 我是不是没有分析,只是在说“信息不足”
|
||
- 我是不是漏了关键澄清点
|
||
|
||
自检失败时:
|
||
|
||
- 降级
|
||
- 或补澄清
|
||
- 或阻止出站
|
||
|
||
## 用户可见回复骨架
|
||
|
||
四挡统一成三段式:
|
||
|
||
1. 当前判断
|
||
2. 理由 / 缺口
|
||
3. 纠错或升级入口
|
||
|
||
### recommendation 例子
|
||
|
||
```text
|
||
如果只是先给建议,我会先看这 3 类外部信号:
|
||
|
||
1. xxx,因为...
|
||
2. yyy,因为...
|
||
3. zzz,因为...
|
||
|
||
这些是候选建议,不代表已经接入,也还没完成验证。
|
||
如果方向不对,你可以直接改项目对象或业务目标;如果你要,我也可以下一步只把第 1 个起草成草稿。
|
||
```
|
||
|
||
### sparse 项目例子
|
||
|
||
```text
|
||
自动开票系统这个项目,我现在先不硬说它该落在哪一步。
|
||
|
||
我现在只看得到它的大致目标:收到开票请求后自动开票,并把发票图片发出去。但还看不到这条链路里谁触发、接什么输入、哪个结果算真正跑通。
|
||
|
||
如果你愿意,我们先把这条流程拆开:谁发起、系统接什么、最后产出什么,再往下判断最合适的信号放在哪。
|
||
```
|
||
|
||
## 高标准黄金案例(业务化 V3)
|
||
|
||
这批案例要替换掉旧的“架构味太重 / 问法太空”的样例。问题必须像业务同事会问的话。
|
||
|
||
### 01. 自动开票系统 / status
|
||
|
||
- 输入:`自动开票系统现在最容易卡在哪一步?你按现在项目情况直接说。`
|
||
- 预期层级:`status`
|
||
- 预期:不出建议,不出草稿,只说明当前最容易卡的链路或当前缺口
|
||
|
||
### 02. 自动开票系统 / recommendation
|
||
|
||
- 输入:`自动开票系统除了现在这些数据,你觉得还应该盯哪些外部信号?先给建议,不要起草。`
|
||
- 预期层级:`recommendation`
|
||
- 预期:给 3 个候选信号 + 理由,不进 draft
|
||
|
||
### 03. AI客服 / status
|
||
|
||
- 输入:`AI客服这个项目现在主要靠什么数据来做用户洞察?`
|
||
- 预期层级:`status`
|
||
- 预期:围绕当前数据源或已知对象回答,不偷换成推荐
|
||
|
||
### 04. AI客服 / recommendation
|
||
|
||
- 输入:`AI客服这个项目,除了现在这些数据,你觉得还应该补哪几类外部线索?直接说最有用的几个。`
|
||
- 预期层级:`recommendation`
|
||
- 预期:给候选 + 业务理由
|
||
|
||
### 05. 门店库存异常日报助手 / recommendation
|
||
|
||
- 输入:`这个项目如果要更早发现库存异常,你会先多看哪几类外部信号?先给建议,不要出方案。`
|
||
- 预期层级:`recommendation`
|
||
- 预期:业务化候选,不保守拒答
|
||
|
||
### 06. 直播复盘辅助工具 / recommendation
|
||
|
||
- 输入:`这个项目如果只补一个外部信号,你觉得先补哪个最值?直接说原因。`
|
||
- 预期层级:`recommendation`
|
||
- 预期:优先级明确,不起草
|
||
|
||
### 07. 测试产能警告 / recommendation with negative boundary
|
||
|
||
- 输入:`我看这个项目现在还没有成型的信号源。你先别出草稿,只按当前项目情况,帮我推荐几个最值得接的外部信号源,并说清楚各自的价值。`
|
||
- 预期层级:`recommendation`
|
||
- 预期:绝不发队列 ack、绝不建 draft
|
||
|
||
### 08. 测试产能警告 / explicit upgrade to draft
|
||
|
||
- 前置:上一轮 recommendation 已返回
|
||
- 输入:`可以,把第一个整理成接入草稿给我看。`
|
||
- 预期层级:`draft`
|
||
- 预期:允许升级,但不 execute
|
||
|
||
### 09. 确认立项 / internal status
|
||
|
||
- 输入:`确认立项现在 assignee 推荐做到哪了?`
|
||
- 预期层级:`status`
|
||
- 预期:回答当前状态,不泄漏 `status_summary / contract`
|
||
|
||
### 10. 跨项目切换 / scope reset
|
||
|
||
- 前置:上一轮问的是 `测试产能警告`
|
||
- 输入:`AI客服这个项目里,用户洞察数据现在到底给谁看、解决什么事?`
|
||
- 预期层级:`status`
|
||
- 预期:必须切到 AI 客服,不允许带旧项目续写
|
||
|
||
### 11. 稀疏项目 / clarify
|
||
|
||
- 输入:`这个项目到底该怎么做,你先说。`
|
||
- 预期层级:`clarify`
|
||
- 预期:必须澄清项目或对象,不能直接起草或乱答
|
||
|
||
### 12. execute 前确认
|
||
|
||
- 输入:`那就把第一个外部信号源直接新增进去。`
|
||
- 预期层级:`execute`
|
||
- 预期:必须显式确认,不准直接写
|
||
|
||
## 今天和昨天的失败反例,必须转成自动化
|
||
|
||
### Fail-01
|
||
|
||
- recommendation ask 误进 `signal_source.onboard`
|
||
- 断言:必须失败
|
||
|
||
### Fail-02
|
||
|
||
- recommendation ask 被答成现状核查
|
||
- 断言:必须失败
|
||
|
||
### Fail-03
|
||
|
||
- recommendation ask 被答成“当前不能推荐”
|
||
- 断言:必须失败
|
||
|
||
### Fail-04
|
||
|
||
- 客户可见文本泄漏 `分析包 / missing_mapping / focus_objects`
|
||
- 断言:必须失败
|
||
|
||
### Fail-05
|
||
|
||
- sparse 项目只会说“还缺业务步骤”
|
||
- 断言:必须失败
|
||
|
||
### Fail-06
|
||
|
||
- 新项目问句被旧线程污染
|
||
- 断言:必须失败
|
||
|
||
### Fail-07
|
||
|
||
- 用户说 `不要执行`,系统仍然进入 write flow
|
||
- 断言:必须失败
|
||
|
||
### Fail-08
|
||
|
||
- 用户说 `只给建议`,系统仍然进入 `draft_pending_confirm`
|
||
- 断言:必须失败
|
||
|
||
## 测试设计
|
||
|
||
### 1. schema / contract
|
||
|
||
- `test_interaction_boundary_packet_schema`
|
||
- `test_answer_control_decision_schema`
|
||
|
||
### 2. gatekeeper
|
||
|
||
- `test_recommendation_boundary_blocks_signal_source_onboard`
|
||
- `test_status_boundary_blocks_recommendation_upgrade`
|
||
- `test_explicit_upgrade_from_recommendation_to_draft_allowed`
|
||
- `test_execute_requires_confirmation_even_after_prior_upgrade`
|
||
|
||
### 3. mainline routing
|
||
|
||
- `test_query_answer_recommendation_only_stays_query`
|
||
- `test_status_only_does_not_return_candidates`
|
||
- `test_unclear_level_returns_clarify`
|
||
- `test_negative_boundary_blocks_write_flow`
|
||
|
||
### 4. Feishu outbox
|
||
|
||
- `test_feishu_recommendation_only_never_emits_queue_ack`
|
||
- `test_feishu_recommendation_reply_has_no_internal_terms`
|
||
- `test_feishu_negative_boundary_no_draft_pending_confirm`
|
||
|
||
### 5. sparse project
|
||
|
||
- `test_sparse_project_status_uses_known_goal_not_packet_jargon`
|
||
- `test_sparse_project_recommendation_explains_known_goal_and_unknown_flow`
|
||
|
||
### 6. correction / rethink
|
||
|
||
- `test_user_correction_rebuilds_boundary_packet`
|
||
- `test_self_review_blocks_overstep_to_draft`
|
||
- `test_self_review_blocks_internal_term_leak`
|
||
|
||
## >9 分验收标准
|
||
|
||
### 自动化稳定性 > 9
|
||
|
||
- 新增 contract / gatekeeper / Feishu outbox / sparse project 测试全绿
|
||
- recommendation 相关高风险误路由 case 全部自动化覆盖
|
||
|
||
### 链路清晰度 > 9
|
||
|
||
- 任意一条 query 都能在结构化结果里明确看到:
|
||
- `requested_level`
|
||
- `forbidden_levels`
|
||
- `effective_level`
|
||
- `blocked_workflows`
|
||
|
||
### 项目贴合度 > 9
|
||
|
||
- 回复必须贴当前项目对象
|
||
- 稀疏项目也至少能拆出最小业务链
|
||
- 不允许长时间停留在“缺步骤 / 缺信号源”空话
|
||
|
||
### 回复自然度 > 9
|
||
|
||
- 文风像业务同事
|
||
- 不像架构师
|
||
- 不像系统模板
|
||
- 不泄漏 packet / contract / internal terms
|
||
|
||
### 高风险误路由单条结果 > 9
|
||
|
||
- recommendation ask 错升 draft/execute 这类 case 必须逐条变绿
|
||
- 不能再用“总体表现还行”掩盖单点失败
|
||
|
||
## 实施顺序
|
||
|
||
1. 先补 schema:
|
||
- `InteractionBoundaryPacket`
|
||
- `AnswerControlDecision`
|
||
2. 再补 gatekeeper
|
||
3. 再把 recommendation-only 从 query.answer 主链里独立出来
|
||
4. 再补 clarify / correction / self-review
|
||
5. 最后按新的业务化黄金案例跑验证
|
||
|
||
## 真源落点建议
|
||
|
||
- `backend/app/services/ai_pmo/semantic_context/decision_schema.py`
|
||
- `backend/app/services/ai_pmo/workflow_semantic_planner/service.py`
|
||
- Feishu ingress / gatekeeper 主链
|
||
- `backend/app/services/ai_pmo/ai_pmo_typed_executor_execute/mixin_execute_signal_source.py`
|
||
- `docs/architecture/RUNTIME_GUARDRAILS.md`
|
||
- `docs/architecture/CODE_REFERENCE_INDEX.md`
|
||
|
||
## 完成标准
|
||
|
||
只有当下面全部满足时,这版才算过线:
|
||
|
||
1. recommendation 不再误进 draft / execute
|
||
2. status / recommendation / draft / execute 四挡真的独立
|
||
3. 稀疏项目不再只会打太极
|
||
4. 用户可见回复自然业务化,不像架构师
|
||
5. 今天和昨天这些失败案例都变成自动化绿灯
|