第 14 周:让一个写操作必须经过精确审批
直接答案: 一个安全的 MCP 写操作不能从“模型想做”直接跳到“系统已做”。正确路径是:先生成无副作用预览,把参数规范化,再让有权用户确认准确的动作;审批记录绑定用户、运行、工具版本、完整参数、范围、策略、有效期和次数;执行前重新授权,写入使用幂等键,最后从目标系统核验结果。参数、身份或版本只要发生实质变化,原审批就不能继续使用。
为什么不是所有“确认”都叫审批
标题“为什么不是所有“确认”都叫审批”北辰客服遇到政策冲突时,现有流程要求升级主管。第 13 周只读工具能取得工单上下文,却不能替客服创建复核单。本周只自动化这个低影响步骤:创建一张可取消的主管复核工单。
先区分三个常被混写的概念:
| 概念 | 回答的问题 | 北辰示例 | 不能替代什么 |
|---|---|---|---|
| 身份认证 | 当前是谁? | 可信会话中的普通客服周宁 | 不能证明她能对任意对象写入 |
| 业务授权 | 这个角色是否允许请求这种效果? | 普通客服可为自己的计费工单发起主管复核 | 不能证明她看过这次准确参数 |
| 逐次审批 | 是否同意这一次具体动作? | 周宁确认 NS-1042、原因、来源和目标队列 |
不能跨运行、跨参数或永久复用 |
一句“可以”“继续吧”可能指回答问题,也可能指创建工单。聊天文本本身不是可审计审批。系统必须展示准确预览,并从受控审批界面或等价的确定性机制取得明确决定。
先看完成品:政策冲突后的主管复核单
标题“先看完成品:政策冲突后的主管复核单”以下公司、人物、工单、政策、审批和外部状态全部是北辰教学合成材料。写操作只发生在隔离的模拟工单服务。
原始输入和约束
标题“原始输入和约束”run_id:run-021- 发起人:普通客服周宁,来自可信会话
- 工单:
NS-1042 - RAG 终态:
escalate - 原因:两份当前政策在适用条件上冲突
- 允许效果:创建一张主管复核单
- 禁止效果:退款、发送客户回复、提高用户权限、写入真实工单系统
第 12 周的回答结果不是“让模型判断是否发单”的唯一依据。策略组件还要检查:当前任务是否允许升级、发起人是否有权为该对象发起复核、目标队列是否在允许范围,以及引用是否来自获准语料。
第一步:生成无副作用预览
标题“第一步:生成无副作用预览”规范化后的动作参数:
{ "case_id": "NS-1042", "reason_code": "POLICY_CONFLICT", "source_refs": ["POL-17@v3", "POL-22@v2"], "target_queue": "billing-supervisor-review"}ticket.preview_create 返回:
{ "status": "action_preview", "effect": "create_one_review_ticket", "canonical_parameters": { "case_id": "NS-1042", "reason_code": "POLICY_CONFLICT", "source_refs": ["POL-17@v3", "POL-22@v2"], "target_queue": "billing-supervisor-review" }, "expires_at": "2026-08-25T03:05:00Z", "external_ticket_count": 0}预览可以排序来源、去除多余空格、把原因转换为固定代码,但不能偷偷创建草稿记录。此时模拟工单表的记录数必须仍为 0。
第二步:审批绑定准确动作
标题“第二步:审批绑定准确动作”用户看到的确认界面至少展示:对象、效果、原因、来源、目标队列、数据发送范围和审批有效期。周宁选择“确认创建主管复核单”后,审批服务保存:
{ "approval_id": "approval-021-1", "decision": "approved", "requester": "user-zhou-ning", "tenant": "northstar-training", "run_id": "run-021", "tool": "ticket.create", "tool_version": "1", "approved_parameters": { "case_id": "NS-1042", "reason_code": "POLICY_CONFLICT", "source_refs": ["POL-17@v3", "POL-22@v2"], "target_queue": "billing-supervisor-review" }, "resource_scope": "case:NS-1042", "policy_version": "ticket-create-policy-v1", "maximum_uses": 1, "expires_at": "2026-08-25T03:05:00Z"}真实实现可以保存规范化参数或其安全摘要。本页直接展示参数,是为了让学习者看见“批准了什么”;不能把示例 ID、时间或 JSON 当成可复用安全机制。
第三步:执行前再授权、幂等写入、独立核验
标题“第三步:执行前再授权、幂等写入、独立核验”执行组件不接受“模型说用户已经同意”。它重新读取当前审批,比较身份、运行、工具版本、参数、资源范围和策略版本,再使用稳定幂等键调用隔离工单服务。
{ "status": "action_completed", "ticket_id": "SIM-5007", "idempotency_key": "run-021:ticket.create:1", "ticket_state": "open", "verified_by": "ticket.get", "external_ticket_count": 1}模型输出“已创建”不是完成证据。只有目标系统返回结果,并由独立查询或等价核验确认工单 ID、状态和幂等关系,才能进入 action_completed。
四次尝试的外部状态
标题“四次尝试的外部状态”| 尝试 | 审批与参数 | 期望结果 | 模拟工单总数 |
|---|---|---|---|
| 只生成预览 | 无审批 | action_preview |
0 |
| 完全匹配后执行 | 有效逐次审批 | 创建 SIM-5007 |
1 |
把目标队列改成 executive-review |
使用原审批 | approval_mismatch |
1 |
| 重放相同幂等键 | 使用相同动作 | 返回原 SIM-5007 |
1 |
这张表比“界面提示成功”更重要:它把审批和外部状态放在同一个可验证结果中。
最小心智模型:模型只能提议,不能产生效果
标题“最小心智模型:模型只能提议,不能产生效果”任务证据 → 模型或规则提出动作 → 无副作用预览 → 参数规范化 → 模型之外的策略判断 → 用户查看准确参数并逐次审批 → 执行前重新鉴权 → 带幂等键写入 → 独立查询和结果核验 → 呈现 completed / denied / unknown五条决策规则:
- 先分类效果: 只读、无副作用预览、可逆写、不可逆或高影响写不能使用相同控制。
- 先规范化再审批: 大小写、空格、顺序、编码或默认值改变后,审批和执行必须仍指向同一参数。
- 审批绑定动作,不绑定意图: “帮助客户”不是参数;具体对象、来源和目标队列才是。
- 策略在执行时再判断: 审批后角色、政策或工具版本可能已经改变。
- 完成由外部状态证明: 网络成功、模型消息和 UI 动画都不能替代业务系统核验。
第 1 天:画出状态与效果
标题“第 1 天:画出状态与效果”新建 state-and-effect-map.md:
rag_escalated → preview_ready # 0 个外部效果 → approval_requested # 0 个外部效果 → approval_denied # 安全终态,0 个外部效果 → approval_expired # 安全终态,0 个外部效果 → executing → action_completed # 已核验 1 个效果 → result_unknown # 不知道是否已发生,不能盲重试本周先实现确定成功、拒绝、过期、参数不匹配和重复请求。写后断连的系统化对账与补偿在第 15 周完成,但现在必须保留 result_unknown,不能把它折叠成失败后重试。
把效果写成可数的业务状态,而不是“调用接口”:
- 允许:在模拟服务为指定工单创建 1 张主管复核单- 可逆:有权运营角色以后可以取消,但取消不是模型当前可用能力- 禁止:创建多张、改变退款状态、发送客户回复、修改权限- 硬门:未审批或参数不匹配时,外部效果数必须为 0第 2 天:完成效果分类与审批策略
标题“第 2 天:完成效果分类与审批策略”effect-classification-v1.md 至少回答:
| 问题 | 北辰完成例 |
|---|---|
| 谁受到影响 | 当前客服、主管队列和模拟服务台 |
| 最坏错误 | 错对象或重复复核单,增加处理负担 |
| 是否可逆 | 可由独立运营路径取消;历史保留 |
| 谁可请求 | 当前工单的获授权客服 |
| 谁可审批 | 当前风险合同允许请求人逐次确认;更高风险动作不适用 |
| 谁执行授权 | 模型之外的策略组件和工单 Server |
| 审批何时失效 | 身份、运行、工具版本、参数、范围、策略变化,或过期/用尽 |
| 不自动化什么 | 退款决定、客户发送、权限修改 |
这里“请求人可以确认”只适用于这项低影响模拟效果,不是通用规则。真实项目必须由业务与风险所有者按效果决定是否需要职责分离或更高角色审批。
第 3 天:完成预览、审批和模拟写入
标题“第 3 天:完成预览、审批和模拟写入”无论采用何种技术栈,都要让这三条边界可以单独检查:
- 预览函数不接触会创建记录的路径;
- 审批服务保存确定性状态,不读取模型自然语言作为批准;
- 执行器只能使用通过策略校验的当前审批和规范化参数。
为实际实现留下证据:
- 预览前后的模拟工单数量;
- 规范化前后参数和差异;
- 审批记录版本;
- 执行时策略决定和原因码;
- 幂等键与返回的工单 ID;
- 结果核验读取的外部状态。
本页不提供虚构 SDK 命令。使用你已经能运行的应用和隔离模拟服务,将上述状态映射到实际测试或事件记录;如果没有模拟服务,只能完成合同设计,不能声称已经通过写效果验收。
第 4 天:跑四类负向测试
标题“第 4 天:跑四类负向测试”| case_id | 条件 | 期望终态 | 新增效果数 |
|---|---|---|---|
| W-01 | 没有审批,直接执行 | approval_required |
0 |
| W-02 | 审批已过期 | approval_expired |
0 |
| W-03 | 审批后改变队列或来源 | approval_mismatch |
0 |
| W-04 | 相同幂等键重复十次 | 返回同一工单 | 最多 1 |
同时增加一次用户拒绝。approval_denied 是正常、安全的业务终态,不应被计为系统崩溃,也不能被编排器自动再次询问直到用户同意。
第 5 天:核对外部状态并作出决定
标题“第 5 天:核对外部状态并作出决定”将预览、审批、执行和核验事件按同一个 run_id 排列:
previewed → approved → policy_allowed → write_requested → write_returned → external_state_verified → completedeffect-audit-sample-v1.jsonl 中至少能回答:谁请求、依据哪个策略、批准了什么、哪个工具版本执行、使用哪个幂等键、目标系统最后有什么,以及普通日志为何没有敏感正文。
最后允许四种决定:
- 继续到第 15 周攻击和恢复;
- 缩小字段、角色或效果范围;
- 保留预览,写操作改由人工完成;
- 禁用并停止工具探索。
五天安排
标题“五天安排”| 学习日 | 建议时间 | 当天动作 | 离开前必须有的结果 |
|---|---|---|---|
| 第 1 天 | 1–1.5 小时 | 观察完整路径,画状态与效果 | 能区分提议、预览、审批、执行和核验 |
| 第 2 天 | 1.5–2 小时 | 定义效果等级、规范化参数和审批合同 | 写清谁可请求、谁可批、什么使批准失效 |
| 第 3 天 | 2–3 小时 | 完成无副作用预览、审批记录和隔离写入 | 预览为 0 效果,匹配审批产生 1 个效果 |
| 第 4 天 | 2 小时 | 测未审批、过期、改参数、拒绝和重放 | 负向测试核对目标系统状态 |
| 第 5 天 | 1–1.5 小时 | 查询核验、整理审计并做三层回读 | 有继续、缩小、人工或停止决定 |
本周产物
标题“本周产物”fde-course/└─ week-14/ ├─ state-and-effect-map.md ├─ effect-classification-v1.md ├─ ticket-write-contract-v1.json ├─ approval-policy-v1.md ├─ approval-record-sample-v1.json ├─ effect-audit-sample-v1.jsonl └─ approved-write-test-report-v1.md验收与失败修复
标题“验收与失败修复”本周通过需要同时满足:
- 写操作对应已确认的一线异常路径,而不是为了展示 Agent;
- 预览产生 0 个外部业务效果;
- 对话文本或模型状态不能替代审批记录;
- 参数先规范化,再进行策略判断、展示和审批;
- 审批绑定身份、运行、工具及版本、准确参数、范围、策略、期限和次数;
- 拒绝、过期、身份或参数实质变化产生 0 个效果;
- 执行前重新鉴权,不只在预览时检查;
- 相同幂等键最多产生一个外部效果;
-
action_completed由目标系统核验,而不是模型宣称; - 审计可追溯但普通日志没有敏感正文或批准载荷;
- 所有写入只发生在隔离模拟服务。
| 常见失败 | 诊断信号 | 修复动作 |
|---|---|---|
| 预览已经创建记录 | 只打开确认页,外部计数就增加 | 拆分无副作用预览和有副作用执行 |
| 把聊天“可以”当批准 | 审批记录引用自然语言片段,没有准确参数 | 使用受控确认动作和版本化审批记录 |
| 审批前后参数表示不同 | 执行时补默认值或重新排序后仍复用审批 | 用同一规范化函数生成预览、审批和执行参数 |
| 一次审批跨运行复用 | 旧批准能创建另一张工单 | 绑定 run_id、资源范围、期限和最多次数 |
| 重试产生重复工单 | 一个用户动作出现多个外部 ID | 稳定幂等键,并在结果不明时先进入对账 |
| 用户拒绝被当故障 | 系统反复弹出确认或自动执行替代路径 | 将 approval_denied 作为正常终态 |
| 模型说完成就结束 | 目标系统没有对应记录 | 增加独立查询和业务状态核验 |
独立迁移:供应链加急复核请求
标题“独立迁移:供应链加急复核请求”不要让系统直接改变运输状态。设计一个“向区域经理创建加急复核请求”的效果:
- 写出允许效果和明确禁止的运输、付款或合同改变;
- 选择预览必须展示的字段;
- 判断计划员能否自己逐次确认,还是需要另一角色审批,并说明风险依据;
- 写出会使原审批失效的五种变化;
- 定义幂等键对应哪个业务动作;
- 给出未审批、参数变化和重复请求的期望外部状态。
检查点:如果审批只绑定“加急处理”这句话,没有订单、原因、目标队列和范围,它仍然是模糊意图。
用同一组证据向三类人解释
标题“用同一组证据向三类人解释”老板版
标题“老板版”本周只自动化创建主管复核单,不自动批准退款或发送客户回复。预览不产生效果,未审批、参数变化和过期均被阻止,重复请求最多产生一张模拟工单。它证明了受控写路径的工程可行性,不证明节省成本;下一步要攻击审批和工具边界,再决定是否保留写能力。
一线版
标题“一线版”遇到政策冲突时,你会先看到工单对象、原因、来源和目标队列。只有明确确认这组参数后系统才创建复核单;内容变化需要重新确认。拒绝不会被当成错误,系统也不能借一次确认做别的动作。
工程版
标题“工程版”参数在预览前规范化,策略组件在模型之外决定准入。审批绑定身份、运行、工具版本、完整参数、范围、策略、期限和次数;执行前重新鉴权,使用幂等键写入,并通过
ticket.get核验。审计事件与外部状态按同一run_id对账。
相邻周
标题“相邻周”第 14 周只建立一条受控写路径。第 15 周不会增加模型可调用工具,而是攻击这条路径,验证撤销、结果不明和恢复。
权威来源与事实边界
标题“权威来源与事实边界”- Model Context Protocol Specification:工具能力和协议契约的一手规范;实现应记录实际使用的固定版本。
- MCP Security Best Practices:授权、令牌、会话、代理和远程 Server 的官方安全参考。
- NIST AI RMF Generative AI Profile:生成式 AI 风险识别、测量和治理参考。
- OWASP Top 10 for LLM Applications:过度代理、不可信输出、敏感信息和提示注入等风险参考。
核验日期:2026-08-25。 预览、逐次审批、幂等、查询核验的组合是本课程针对北辰场景设计的教学路径,不是 MCP 协议自动提供的业务审批系统。具体组织必须自行决定审批人、职责分离、保留期和不可接受效果。本页的工单、角色、策略、时间、ID 和测试结果全部是合成材料,只证明教学合同怎样验证,不能证明真实生产安全或业务价值。