# 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. 今天和昨天这些失败案例都变成自动化绿灯