Skip to content

第 7 章|把业务变成可运行系统

第 6 章结束时,澄川项目组已经画出了目标流程:系统读取已授权的会议记录,从批准资料中查找依据,准备 CRM 字段建议,由销售核对后写入沙盒。团队因此决定有条件建设一个受控 LLM 工作流。

第 6A 章把这些决定写进业务约束卡:触发收窄到已授权会议和已关联沙盒商机;AI 只准备四字段建议;销售确认后才允许写入;资料冲突阻塞字段;写入未知时禁止新开意图。卡上已有业务答案,但还没有可检查的系统实现。

一名工程师拿着这张卡,把五个实现问题逐项摊开:系统怎样确定“这次会议”和“这个商机”?两份资料冲突时谁有效?一条“更新建议”在系统里是什么对象?销售审阅期间 CRM 已经改变怎么办?写入可能成功、响应却超时,系统到底该显示成功还是失败?

提示词原型能生成一张像样的预览卡片,却落实不了卡上已经写明的版本、状态和禁止动作。这说明项目已从“边界写清了”进入另一类工作:把业务约束翻译成系统可以检查的契约,并回写到同一张卡的系统约束版。

本章继续使用“澄川工业客户跟进助手”教学案例。文中的组织、记录、编号、运行结果和评审决定均为教学材料,不代表真实企业数据或行业基线。

可运行,意味着边界可以被检查

“模型能回答”只证明一次生成发生过。可运行系统还得搞清楚:任务从哪里启动,能读什么数据,处理的是哪个业务对象,哪些工具会改动外部状态,当前跑到什么状态了,以及最后怎么核对外部环境是不是真的对了。

澄川把这些要求整理成六层契约:任务契约说明本次运行要完成什么;上下文契约限定证据从哪里来;对象契约保存业务含义与不变量;工具契约管理系统读写;状态契约规定任务怎样前进、停止和恢复;轨迹契约留下可审计证据。

text
一次业务任务
  ├─ 任务契约:目标、输入、完成状态、禁止范围
  ├─ 上下文契约:来源、版本、权限、冲突和缺失处理
  ├─ 对象契约:含义、关系、状态、动作和不变量
  ├─ 工具契约:输入、输出、权限、副作用和失败语义
  ├─ 状态契约:允许的转换、暂停、恢复与结束
  └─ 轨迹契约:版本、调用、人工决定和环境结果

这六层不是让团队一上来就建一套大而全的平台。它们更像一张检查表,帮你看设计有没有漏项。早期方案范围可以很小,但每一次输入、判断、动作和结果都得落到某一层上;哪一步归不进去,往往说明边界还没说清楚。

OpenAI 的智能体构建实践指南把模型、工具与指令视为智能体系统的基础组成,并建议从单一、可控的系统逐步增加复杂度。澄川的路径固定、工具有限,因此没有理由先建设一个能够自由规划的销售 Agent。

先把一次任务钉住

如果任务本身仍叫“帮助销售更新 CRM”,后面的对象和工具会不断膨胀。项目组为首个可运行方案写下了更窄的任务契约:给定一次已经结束且授权可读的客户会议,以及一个已经关联的沙盒商机,准备四类内部字段建议,显示来源和现有值;销售确认后,只把被批准的字段提交到沙盒,并核对最终状态。

允许的四类内容是客户需求、风险备注、下一步动作和下一步负责人。价格、预计成交日期、正式商机阶段以及任何对外消息均不在当前范围内。即使会议里出现“预算批准后尽快推进”,系统也不能把这句话转换成成交日期。

任务契约也把完成状态写清楚:预览生成不算完成,销售点击确认也不算完成。只有沙盒返回可验证的写入凭据,系统重新查询后确认目标记录已处于预期版本,任务才能进入 committed

任务契约还规定了几个提前结束的状态。会议无权访问时是 blocked;资料冲突影响字段时是 blocked;销售拒绝建议时是 rejected;销售取消任务时是 cancelled;写入结果无法确认时是 unknown。这些状态都不是“生成失败”的同义词,它们对应不同的负责人和下一步动作。

缩小任务以后,项目组才能判断每一份数据是否必要。系统不需要读取销售的全部邮箱、历史聊天和整个文档库,也不需要获得正式 CRM 的通用写权限。权限范围要随着任务范围一起缩小。

上下文不是附件集合,而是证据地图

澄川的第一版原型把会议转写、CRM 记录和产品文档一起塞进提示词。模型可以从中组织语言,却不知道哪份材料能证明哪类主张。出现冲突时,它倾向于给出一个连贯答案,业务人员却无法判断答案的权威性。

上下文契约要为每个来源写明四件事:它能支持什么,不能支持什么,由谁维护,在什么条件下失效。系统随后才知道怎样检索、怎样引用,以及何时必须停下。

会议转写可以证明“客户在会议中说过什么”,不能证明公司产品一定具备某项能力。它由会议组织者负责授权,关联会议编号与录制版本;转写缺段时,相关事实标记为缺失,系统不能自行补齐。

批准产品资料可以证明当前内部产品口径,不能证明客户已经接受某个条件。第 6 章中的资料冲突已由内容负责人处理:v2.1 被标记失效,v2.3 补上生效日期和适用配置。系统只能检索批准目录中的有效版本,并在建议旁保存文档编号、版本与片段位置。

CRM 当前记录可以提供已有字段、商机标识和记录版本,但它是一份会变化的状态,不是永久事实。预览读取时保存 record_version=42;提交前若版本已经改变,系统必须停止并重新生成差异。

销售输入代表授权用户的业务判断。它可以修改候选字段或拒绝提交,却不能绕过字段白名单和环境限制。销售在界面里输入“写入正式环境”也不会改变工具权限。

把这些约束压缩后,澄川的上下文地图如下。它总结的是前面的语义和责任,不代替来源治理。

来源可以支持负责人及版本缺失或冲突时
会议转写 M-082客户原话、问题与行动线索会议组织者;转写版本缺失片段标记待确认,不推断
批准产品资料 v2.3当前产品能力与适用条件产品内容负责人;生效日期冲突字段阻塞并升级
沙盒商机 OP-314当前 CRM 值与记录版本CRM 系统;版本 42版本变化后重载和重审
销售审阅输入接受、修改或拒绝候选当前登录销售;审批记录身份或权限失效时阻塞

上下文地图还要落实数据最小化。运行时只取与 M-082OP-314 和四个允许字段有关的材料。日志保存来源引用与必要摘要,不默认复制完整转写和工具载荷。授权、保留期限与删除要求属于系统设计的一部分,不能等上线后再补。

字段必须进入业务对象,才拥有语义

若系统只有一组 JSON 字段,它能检查类型,却不知道字段处于哪个业务过程。next_action_owner 是 AI 猜测、销售草稿,还是已经写入 CRM 的责任人?只看字符串无法回答。

澄川为首个方案定义了六个对象。MeetingRecord 表示一次授权会议及其转写版本;EvidenceRef 指向支撑某条建议的来源;FieldSuggestion 保存现有值、候选值、依据与不确定项;Approval 保存销售的接受、修改或拒绝;WriteReceipt 表示沙盒对副作用的确认。

把这些对象连接起来的是 CRMChangeSet。它不是“模型生成结果”的别名,而是一份等待审阅、可以被批准并能够追踪写入结果的变更单。

json
{
  "change_set_id": "CS-007",
  "opportunity_id": "OP-314",
  "base_record_version": 42,
  "proposed_changes": [
    {
      "field": "next_action",
      "value": "下周三前确认接口人名单",
      "evidence_ref": "M-082#t=31:20"
    }
  ],
  "status": "ready_for_review",
  "approval_id": null,
  "idempotency_key": "crm-CS-007-v1"
}

对象的关键价值在于不变量。禁止字段不能进入 proposed_changes;没有有效 Approval 的变更单不能提交;证据冲突会阻塞受影响字段;提交只能针对创建预览时读取的 CRM 版本;一个幂等键只能对应一份确定的变更内容。

这些约束应尽可能由确定性代码和存储层执行。提示词可以提醒模型不要生成预计成交日期,但服务端字段白名单才是控制边界。JSON Schema 可以检查结构是否完整,但不能证明业务含义正确;来源、状态、权限与人工决定仍需分别验证。

状态机把“然后”改成允许的转换

流程图通常用箭头连接步骤,工程系统还要说明每个箭头在什么条件下可以发生。澄川为 CRMChangeSet 定义了主路径:

text
draft
  → ready_for_review
  → approved
  → committing
  → committed

旁路状态包括 blockedrejectedcancelledunknown。来源冲突或 CRM 版本变化进入 blocked;销售明确拒绝进入 rejected;用户在提交前终止,则进入 cancelled;提交请求发出后无法确认结果,则进入 unknown

unknown 尤其重要。它说明系统不知道副作用是否已经发生,不能被简单归为普通失败。若此时直接重试,第一次请求可能已经成功,第二次请求就会造成重复写入。

每个状态还要带上允许动作。ready_for_review 可以接受、修改、拒绝或取消;approved 可以提交,不能继续改变字段;committing 不接受第二次提交;unknown 只允许查询结果、转人工或按恢复协议继续。状态转换由服务端校验,界面隐藏按钮只能改善体验,不能代替权限控制。

工具契约约束跨系统边界的动作

业务对象保存意义,工具负责读取或改变外部系统。一个叫 update_crm、接收任意字典的通用工具,看似灵活,实际上把字段范围、身份、环境和失败含义都留给了调用者猜测。

澄川首个方案只开放五个窄工具。get_meeting_context 只读授权会议;search_approved_product_docs 只检索有效资料;preview_crm_update 读取沙盒当前记录并计算差异;commit_crm_update 执行受控写入;get_crm_update_status 查询已提交动作的最终状态。

每个工具都要说明调用身份、输入结构、输出结构、权限、超时、错误码、副作用、幂等规则和审计字段。写入工具还必须说明“收到请求”“开始处理”和“外部状态已改变”之间的区别。

commit_crm_update 的请求可以设计为:

json
{
  "change_set_id": "CS-007",
  "opportunity_id": "OP-314",
  "expected_record_version": 42,
  "approved_fields": {
    "customer_need": "确认设备接口兼容范围",
    "risk_note": "客户预算仍待内部批准",
    "next_action": "下周三前确认接口人名单",
    "next_action_owner": "销售李岚"
  },
  "approval_token": "<short-lived-approval-token>",
  "idempotency_key": "crm-CS-007-v1",
  "environment": "sandbox"
}

服务端收到请求后仍要重新鉴权,核对审批是否属于当前变更单,检查字段白名单、环境和 expected_record_version。任何一项不匹配,都不能把“模型已经决定”当作放行理由。

成功响应应返回 receipt_id、新记录版本和实际差异;明确拒绝应返回稳定的错误类型,例如 STALE_RECORD_VERSIONFIELD_NOT_ALLOWED。若请求已经发出但服务端无法判断外部系统是否完成,则返回或记录 unknown,由状态查询工具接手。

Stripe 的幂等请求说明展示了一种常见机制:客户端为同一意图提供幂等键,服务端复用该键识别重复请求,降低重试产生重复操作的风险。这是一个具体 API 的实现示例,不意味着 CRM 天然具备同样能力;澄川必须在自己的适配器和存储中明确实现并测试这项契约。

幂等也不等于“可以无限重试”。如果同一个键对应的字段内容被改变,服务端应拒绝;如果状态查询仍无法确认结果,流程应保持 unknown 并转人工,而不是换一个新键再次提交。

最小方案要形成完整证据

项目组没有把所有客户、会议类型和 CRM 字段一起接入。首个运行方案只服务一种客户会议、一种已授权销售角色、一套批准资料、四个字段和一个沙盒环境,编排路径固定。

这个范围虽然小,却包含输入校验、带版本的上下文、结构化建议、销售审阅、一次沙盒写入、一次故障注入、运行轨迹和环境重置。它不是功能数量最少的演示,而是能够从输入追到最终环境状态的最小完整方案。

模型在这个方案中的工作也很窄:根据给定材料生成 FieldSuggestion。客户说“预算批准后尽快推进,下周三给接口人名单”,系统可以把预算待批放入风险候选,把“下周三确认接口人名单”放入下一步;由于句子没有给出可靠成交日期,相关字段不会出现。

销售随后对建议作业务判断。她把下一步负责人从模型候选的“客户接口人”改为“销售李岚”,因为客户接口人是交付对象,不是内部任务责任人。系统保存原建议、修改值和确认身份,使这次纠正可以进入下一章的评估材料。

正常运行:成功必须落到环境状态

正常样本的运行编号是 RUN-CC-007。工作流版本为 0.3,运行环境为 sandbox。系统首先验证当前销售有权访问 M-082OP-314,随后读取会议转写版本、批准资料 v2.3 和 CRM 记录版本 42。

生成组件返回四个允许字段的候选,每项都带来源。规则层拒绝空字段和范围外字段,并检查证据引用是否存在。preview_crm_update 返回当前值与建议值的差异,变更单进入 ready_for_review

销售修改下一步负责人,接受其余三项。系统据此生成 Approval,冻结变更内容,状态从 approved 进入 committing。写入工具使用 crm-CS-007-v1 提交,沙盒返回凭据 WR-731 和记录版本 43。

工作流没有一看到工具返回的“success”文本就结束任务。它重新查询 OP-314,确认四个被批准的字段与写入凭据一致,才将变更单标记为 committed

这次运行证明了任务可以按契约完成,也证明人工修改、资料版本和沙盒结果能够被还原。它没有证明字段建议在更多会议上可靠,没有证明节省时间,也没有证明销售愿意长期使用。这些问题需要下一章的评估集和真实采用阶段回答。

先兑现业务约束卡上的失败样本

第 6A 章交给本章的失败样本是资料冲突,不是写入超时。项目组先跑 RUN-CC-006,把 SAMPLE-FAIL-001 做成可复现运行。

系统读到两份仍在批准目录中、却对兼容范围结论相反的资料。冲突字段进入 blocked,其余三项草稿保留,任务交给内容负责人,模型不得自行挑选“更像答案”的一份。这不是新发明的规则,而是业务约束卡里已经写明的异常路径第一次被状态机执行。

超时和未知状态是本章新增的系统样本,用来补卡上“尚未覆盖超时”的缺口。下一节的 RUN-CC-008 只回答这个问题,不再回头改写资料冲突的业务含义。

异常运行:超时不等于失败

随后一次运行 RUN-CC-008 使用写入超时样本。销售已完成审阅,commit_crm_update 把请求发往沙盒,但在等待响应时超时。此时系统只知道请求曾经发出,不知道 CRM 是否已处理。

工作流把状态改为 unknown,冻结变更单,并禁止界面再次提交。它随后使用同一幂等键调用 get_crm_update_status。查询发现沙盒已经产生写入凭据 WR-744,目标记录版本也已增加,于是系统把这次运行协调为 committed,没有创建第二次写入。

如果查询没有找到凭据,仍不能只凭“没有返回”判断未执行。适配器需要区分“明确未执行”和“仍然未知”。只有前者在恢复协议允许时才能使用原幂等键重试;后者必须保留现场、通知负责人员并等待进一步核对。

第 4 章曾预演过一个后续版本事故:CRM 可能已写入、工具响应却超时,系统重试后创建了重复任务。第 7 章的异常运行说明首个方案已经定义了处理未知状态的方法。它不保证未来版本永远正确;如果后续改动绕过状态机或丢失幂等约束,同类事故仍会发生。契约的作用是让这类退化能够被测试和定位。

轨迹记录外部可观察事实

团队要复盘一次运行,不需要保存外界看不到的模型“内心推理”,而要记录系统实际观察到的事件。一次轨迹至少应串起任务身份、工作流版本、上下文版本、工具调用、状态转换、人工决定和最终环境结果。

text
RUN-CC-008
10:02:11  run.received          user=sales-17  workflow=0.3  env=sandbox
10:02:12  context.loaded        meeting=M-083@v1  docs=approved@v2.3
10:02:18  suggestions.ready     allowed_fields=4  blocked_fields=0
10:03:41  approval.recorded     approval=AP-008  human_edits=1
10:03:42  commit.started        key=crm-CS-008-v1  record_version=18
10:03:52  commit.timeout        state=unknown
10:03:54  status.queried        key=crm-CS-008-v1
10:03:55  receipt.found         receipt=WR-744  new_record_version=19
10:03:56  run.committed         final_state_verified=true

工具参数只记录审计所需的摘要与引用。短期审批令牌不能进入日志,完整会议转写也不应因为调试方便而被无限保留。若故障分析确实需要受限载荷,访问权限、保留期限和删除方式都要单独规定。

OpenAI Agents SDK 的追踪文档把模型生成、工具调用、护栏与自定义事件组织为可关联的运行轨迹,同时提醒追踪可能捕获敏感的模型和工具输入输出。无论采用什么框架,团队都应主动决定记录什么,而不是让默认日志替自己作数据治理决定。

轨迹还要能关联版本。只写“调用了模型”和“使用了提示词”不足以复现问题。澄川保存工作流版本、指令版本、模型别名、来源版本、工具适配器版本和评估样本编号;模型供应方版本无法完全固定时,也要保留当时可获得的标识与时间。

把六层契约回写到同一张卡

六层契约不是另一套主文档。它们是业务约束卡在第 7 章的升级页:任务契约落实触发与完成状态,上下文契约落实来源版本,对象与状态契约落实变更单,工具契约落实窄写入,轨迹契约落实可复查运行。

澄川回写后的增量见系统约束版填写示例。读者应能从业务约束版的缺口表走到系统约束版的对象名、工具名、幂等键和运行编号,而不需要再猜工程师“另外做了一套设计”。

回写时不得改写第 6 章已经批准的范围。四字段、沙盒、禁止正式环境和外部消息,仍然有效;本章只决定这些边界是否已经被实现到可以评估。

接口与边界是否可以进入系统评估

这一步判断当前最小方案是否已经具备可以测试的接口和边界。它不是行业认证,不回答系统是否足够准确、是否安全进入真实业务,也不批准正式环境权限。

澄川团队决定“进入沙盒评估”。项目组已经完成来源和版本规则,核心对象拥有状态与不变量,写入工具受到身份、字段、环境与幂等限制,资料冲突、正常写入和未知状态都留下了可复现轨迹。

这项决定附带明确边界:系统仍只运行在沙盒,AI 只提供建议和准备变更预览;正式 CRM 写入、价格、成交日期、商机阶段与对外消息均未获批准。当前方案能够进入沙盒评估,但这一点不能被改写成“可以开始真实试点”。

进入第 8 章后,团队要系统评价字段正确性、依据可追溯性、人工修改、越权阻塞、工具副作用、未知状态恢复和敏感数据处理。任何必须停止试点的高风险失败,都不能用平均分抵消。

如果当前方案只能展示一张预览,却无法验证外部状态;如果权限只写在提示词里;如果异常运行不能停在可恢复状态;或者日志无法说明用了哪份资料,团队应当返工。

五种容易把原型误当系统的做法

一是把提示词当数据库用。团队把全部背景、字段定义和规则塞进一段长提示,却没有来源版本和负责人。文档一更新,旧规则可能还藏在复制的提示里。正确做法是把稳定的语义放进对象和策略里,把会变化的知识交给带版本的来源管理。

二是开放一个万能写入工具。update_crm(payload) 让模型自己决定写哪个对象、哪个字段、哪个环境,提示注入或映射错一次就能把副作用放大。工具应该限定在明确的业务意图内,服务端再检查一遍权限、字段和状态。

三是拿聊天历史当状态。对话里出现“已经确认”不代表系统有了有效审批;模型说“更新成功”也不等于 CRM 真改了。业务状态需要持久化对象、允许的状态转换和外部凭据来证明。

四是把重试当恢复。读操作超时一般可以重试,但写操作超时可能已经成功了,只是响应没回来——这时候状态是未知的。工具契约必须区分有没有副作用,并提供幂等、状态查询或人工协调路径。

五是什么都记进日志求安心。完整转写、客户信息、工具密钥、审批令牌全进同一个日志,调试是方便了,泄露面也跟着大了。轨迹只留审计需要的内容,并遵守数据最小化、访问控制和保留期限。

架构复杂度也可能掩盖这些缺口。把检索、审核和写入分别命名为多个 Agent,并不会自动带来权限、状态或证据。澄川先采用一个固定工作流,等到任务确实需要动态路径且评估能够区分职责时,再讨论增加自主性。

把这套方法迁移到退款建议

设想客户支持团队希望让 AI 处理退款。较危险的设计是给模型订单查询和退款接口,让它根据对话自行决定金额并执行。问题不只在模型准确率,还在于对象、政策、身份和不可逆副作用都没有边界。

一个较小的方案可以把任务限定为“准备退款建议”。Ticket 保存客户请求和授权范围;PolicyCitation 指向当前生效的退款政策;RefundRecommendation 保存候选原因、金额上限和依据;Approval 保存客服主管的决定;RefundReceipt 留给以后获批的执行阶段。

首轮工具只读取工单、订单摘要和批准政策,再生成可审阅建议。实际退款接口不进入当前方案。若政策版本冲突,建议进入 blocked;订单状态在审阅期间变化,则作废旧预览。这样的系统已经可以评价检索与建议质量,却没有提前获得资金操作权限。

迁移时不应照抄 CRM 字段。需要保留的是方法:限定一次任务,声明来源语义,建立带状态的业务对象,把副作用隔离在窄工具里,再用正常和异常运行核对最终环境。

你的练习

继续使用上一章决定建设的场景,先打开同一张转化卡,把业务约束版升级为系统约束版。练习册中的上下文地图业务对象卡工具契约运行轨迹是这张卡的展开页,不是另一套互不相认的作业。若没有真实授权材料,请标明“教学模拟”;使用澄川时,直接复用 SAMPLE-NORMAL-001、SAMPLE-FAIL-001,并补一条写入超时样本。

先写一段任务契约,明确触发条件、输入、业务完成状态、允许动作和禁止范围。然后选择三至五个上下文来源,分别说明它们能证明什么、由谁维护、怎样标识版本,以及缺失或冲突时如何处理。

接着定义至少两个核心对象。不要停在字段名:写出对象的业务含义、关系、状态、允许动作、权限和两条不能被破坏的不变量。再为一个只读工具和一个有副作用的工具写完整契约。

最后运行一个正常样本和一个异常样本。异常可以是来源冲突、权限拒绝、记录版本变化或写入结果未知。保存状态转换、人工决定、工具结果和最终环境证据,并决定进入系统评估、返工、暂停或终止。

怎样判断答案是否合格

先把模型从设计里暂时拿掉,检查业务对象和状态是否仍然说得通。如果“模型认为已经完成”是唯一完成条件,系统还没有业务终点。

再检查每一个动词。“读取”“查询”“创建”“更新”“发送”分别由哪个身份、哪个工具、在哪个环境执行?输入不合法、没有权限、超时或只完成一半时,会进入哪个状态?答不出来的动词就是尚未设计的接口。

随后用一个被禁止的字段攻击自己的契约。若它能从生成结果一路穿过审批并进入工具参数,字段白名单只存在于文字说明。再模拟一次写入超时;如果系统立即换键重试,未知状态的恢复仍不合格。

最后请另一位同事只看运行轨迹,回答用了哪些资料版本、谁修改并批准了什么、工具有没有产生副作用、外部环境最终是什么。需要依赖讲解者记忆才能回答,说明证据链尚未闭合。

一份合格作业不需要覆盖整个组织。它应当让一个小任务可以运行、停止、恢复和复现,并明确说明尚未证明的质量、风险与业务价值。

本章小结:系统已经能跑,尚未证明值得试点

澄川把业务约束卡翻译成了六层契约,并回写为系统约束版。任务契约卡范围,上下文契约管来源和语义,对象契约存业务状态,工具契约约束副作用,状态契约处理停止和恢复,轨迹契约把版本、人工决定和最终环境串起来留证据。

RUN-CC-006 证明第 6A 章的资料冲突样本会被阻塞而不是被模型“圆过去”;RUN-CC-007 证明沙盒变更可以从会议证据走到外部状态;RUN-CC-008 证明写入超时可以先进入未知状态,再通过幂等键和结果查询完成协调。三次运行只证明接口和边界可测试,没有证明总体质量。

第 8 章将使用这些对象、轨迹和失败分支建立评估集,并把权限、人审、停止、恢复与事件响应写成试点前必须检查的条件。

下一章:用评估与治理证明系统可控

本章来源与框架边界

六层契约、系统评估前的检查、对象命名和运行编号是本书为连续案例整理的教学方法,不代表上述机构发布的统一标准。Stripe 文档仅作为幂等请求的实现示例;具体系统仍需根据自身 API、数据和风险设计恢复机制。