任务建模、自治边界与成功标准:先把“完成”写清楚
正在读取学习记录…
任务建模是把“要做什么”写成具体要求,让人和程序都能判断任务是否完成。
比如,客户留言:“付款成功,但订单显示未付款。”客服系统会把这条求助保存成一条记录,称为“工单”。我们希望助手读懂客户的问题,推荐负责处理的部门,并说明理由。它可以回答:“建议交给账单组,因为客户描述的是付款状态与订单状态不一致。”客服人员看过后,再决定是否把这条求助交给账单组。这就是一次完整的推荐任务。
先把这条任务写成具体的执行要求,再用程序判断几条固定记录分别处于什么状态。建议、审批和回读对应不同阶段,程序据此决定继续执行还是等待。
先修:模型 API 配置。你不需要先学 Agent 框架;先能说清任务,后续的工具、循环、状态和评估才有组织依据。
0. 为什么写 Agent 之前先写任务
模型可以生成听起来完整的结果,但程序需要知道什么时候停止、该不该调用写工具、失败后由谁接手。如果“成功”只是模型在最后说一句“已完成”,系统无法分辨下面几种情况:它只给了建议;写请求已经发出但响应丢失;修改了错误对象;或者越过审批后碰巧得到了正确值。
任务建模把这些差异提前写成状态和证据。到了 L3 的 Agent Loop,循环每一轮都要回答“离哪个成功条件更近”;到了 L7 的评测,数据集也要知道一条轨迹到底该判成功、失败还是需要人工核对。这里写不清,后面只会把模糊需求放大。
1. 把模糊愿望改成一份任务契约
任务契约是执行前约定的输入、允许动作、交付物和验收规则,不要求使用某种特定格式。它比“请认真完成”多了可以检查的边界。
先写一份最简单的建议任务:输入是一张“付款成功但订单显示未付款”的工单;动作是读取工单和分流规则;输出是“建议账单组处理”以及对应规则。看到建议和依据,就完成这项任务。此时没有更新队列,也就不需要写入审批。像先开一张维修建议单,还没开始拆机器。
若用户要求助手真正转移工单,就要增加写权限、审批和回读。这是另一份更强的契约。下面的表围绕这个“实际更新”任务展开:
| 项目 | 本例约定 | 为什么不能省 |
|---|---|---|
| 对象范围 | 本次指定的工单 ID,且操作者有权访问 | 防止遍历全部客户记录 |
| 输入版本 | 工单当前版本、分流规则版本 | 防止审核时和执行时看到不同内容 |
| 交付物 | 分流建议、依据片段、审核结果和更新记录 | 区分文本建议与真实系统修改 |
| 允许读操作 | 读取指定工单及授权规则 | 限定模型寻找证据的范围 |
| 允许写操作 | 审核通过后更新指定队列字段 | 不包含退款、关闭工单或给客户发信 |
| 成功条件 | 审核与实际写入一致,回读确认当前状态 | 请求发出不是最终完成 |
| 停止条件 | 缺规则、冲突、预算耗尽或执行状态未知 | 防止无依据地继续自治 |
| 人工接管 | 提交问题、候选方案、已发生动作及未解决项 | 人不必重新猜一遍现场发生了什么 |
“提高客服效率”是业务目标,不是单次任务的可执行条件。业务层可以用平均处理时长、人工返工和错误转派率衡量,但单次执行仍需明确到底修改了哪条记录。
分流任务的完成条件是转到正确团队。接手团队随后处理客户问题,那是下一项任务;把这两个阶段分别记录。
2. 先判断需不需要 Agent
不必把所有自动化都叫 Agent。若工单已经带有可信的产品编号,队列映射也是确定的,普通规则程序可能足够。若用户的表达不统一,可以让模型做语义分类,再把后续步骤保持为固定工作流。只有当下一步确实需要根据观察动态选择,才考虑更开放的循环。
| 方案 | 本例适用情况 | 主要控制方式 |
|---|---|---|
| 普通程序 | 编号到队列的映射完整确定 | 输入校验、规则与测试 |
| 单次模型调用 | 从自由文本中提出分类建议 | 结构化结果、证据与人工复核 |
| 固定工作流 | 读取→建议→审核→更新的步骤已知 | 显式状态与步骤前置条件 |
| Agent 循环 | 需要有界地查不同资料、根据结果决定下一步 | 工具权限、预算、停止与外部验收 |
这里采用常见的架构区分:工作流按预定路径组织,Agent 允许模型动态决定部分过程。不同资料对 Agent 的术语范围可能不同,不必争一个标签;更重要的是知道哪些决策由模型控制。Anthropic 的工程文章讨论了这一区别以及成本、延迟和灵活性的取舍。Building effective agents
不要为了让路线图显得高级而添加循环。每新增一次模型调用,都要问:它获取了什么新信息?改善了哪个可测指标?如果没有答案,可能只是让系统更难复现。
3. 按动作风险划分自治程度
“允许自主执行”至少包含三个维度:能看什么、能决定什么、能改什么。读取公开说明与修改客户账务不应共享同一授权范围。
本例允许模型建议分类、说明缺少的资料,并在已授权范围内选择规则查询。它不能修改权限、扩大工单集合,不能把“请处理完”理解为允许付款或通知客户。即使模型在文本里说“用户已经同意”,也不等于真的取得审批。
审核必须绑定具体对象、动作参数和版本。例如审核的是“把这张工单从普通队列转到账单组”,而不是“今后所有分流动作均同意”。如果建议内容或工单版本在审批后改变,应重新验证或再次审核,不能复用旧批准。
权限检查也不能只写在 Prompt 里。写入工具本身要校验身份、对象范围、字段允许列表和审批有效性;否则模型忽略一句话就能越界。Prompt 帮助模型理解规则,程序负责执行不可绕过的约束。
人工接管需要哪些信息
有效的接管材料至少包括:
- 工单 ID 与读取版本,不包含无关客户资料。
- 已查过的规则及适用范围。
- 候选团队和尚未消除的冲突。
- 是否已经发出写请求,当前执行结果是否确定。
- 等待人工决定的具体问题。
例如:“规则 R3 指向 Billing,规则 R8 指向 Fraud,二者生效范围重叠;未发出写请求,等待确认规则优先级。”这比“遇到问题,任务失败”更容易继续处理。
4. 成功条件要拆成三种证据
内容证据
分类建议是否有依据?引用的规则是否真的适用于该工单?文本里出现一个规则 ID 不代表引用正确,需要检查规则内容、版本和适用条件。
执行证据
系统是否实际发生了授权变更?接口返回请求受理、任务排队或 HTTP 成功,不一定意味着目标记录已经更新。需要根据工具语义检查结果,必要时回读并比对版本。
边界证据
执行是否仍在授权范围、预算与数据边界内?误改了另一条工单,即使那条工单成功写入,也不是当前任务成功。没有审批但写入成功,属于越权,不是“效率高”。
本例的严格成功条件可以写成:有效建议与依据已审核;审批对应实际动作;更新后回读与期望队列一致;没有违反任务边界。这里的回读也不是万能证明:如果其他人随后修改了工单,状态会再次变化,所以记录时间和版本很重要。
5. 用状态区分完成、等待和待核对
像快递追踪里“待揽收”“运输中”“已签收”,任务也用状态标签记录进度。下面这些英文字符串是 status 的可选值:程序选一个值存起来,界面再显示对应的中文状态。
| 状态 | 代表什么 | 后续处理 |
|---|---|---|
| needs_review | 缺少建议、依据或审批 | 等待审核,不发起写入 |
| ready_to_execute | 前置条件齐备,但未写入 | 执行器再次验证后才调用工具 |
| succeeded | 具备证据且写入回读匹配 | 交付分流结果和记录 |
| reconcile_required | 已尝试写入,但结果未知或不匹配 | 核对外部状态,不盲目重复 |
| policy_violation | 已发生不符合前置约束的尝试 | 停止进一步动作,调查并处理影响 |
| budget_stopped | 未发起写入前耗尽预算 | 明确未完成,等待调整或人工接手 |
这个判定器使用上面六个标签。扩展系统时,可以按业务增加“已取消”“已过期”等状态,并为每个状态写清下一步动作。
特别注意:预算耗尽不能抹掉已经发出的写请求。若写入结果未知,同时模型预算也用完,应优先保留“需要核对”的信息。系统可以停止新增模型调用,但不能因此宣称远端什么都没发生。核对本身需要由受控的恢复机制或人工继续完成。
6. 运行一个验收判定表
复制下方完整代码,保存为 acceptance.py 与 test_acceptance.py,放进同一个新文件夹。打开 PowerShell,用 Set-Location "你的文件夹路径" 进入该目录;Get-ChildItem 应同时看到两份 .py 文件,而不是 .py.txt。代码只用标准库,不需要联网安装依赖。
保存为 acceptance.py:
"""根据推荐、审批、实际更新和更新后的查询结果,判断任务进行到了哪一步。"""
def assess(record):
# record 是程序记录的处理进度;这些字段由实际检查结果填写。
# suggestion:已有部门推荐;evidence_checked:已核对推荐依据;approval:已获审批。
# write_attempted:已尝试更新负责部门;readback_matches:重新查询后,部门是否与预期一致。
# budget_exhausted:时间或费用预算是否已用完。
flags = {'approval', 'evidence_checked', 'suggestion', 'write_attempted', 'budget_exhausted'}
if not isinstance(record, dict) or set(record) != flags | {'readback_matches'}:
raise ValueError('Record must contain exactly the six documented fields')
if any(type(record[key]) is not bool for key in flags):
raise ValueError('Control flags must be booleans')
if record['readback_matches'] is not None and type(record['readback_matches']) is not bool:
raise ValueError('Readback must be boolean or None')
if not record['write_attempted'] and record['readback_matches'] is True:
raise ValueError('Confirmed write readback requires a write attempt')
approved = record.get('approval') is True
evidence = record.get('evidence_checked') is True
suggestion = record.get('suggestion') is True
attempted = record.get('write_attempted') is True
if attempted:
if not (approved and evidence and suggestion):
return 'policy_violation'
if record.get('readback_matches') is not True:
return 'reconcile_required'
return 'succeeded'
if record.get('budget_exhausted') is True:
return 'budget_stopped'
if not (suggestion and evidence and approved):
return 'needs_review'
return 'ready_to_execute'完整代码:test_acceptance.py
# 核对任务建模、自治边界与成功标准的正常结果和错误分支;断言失败时会显示对应的测试。
import unittest
from acceptance import assess
class AcceptanceTests(unittest.TestCase):
def good(self):
return {'suggestion':True,'evidence_checked':True,'approval':True,
'write_attempted':True,'readback_matches':True,'budget_exhausted':False}
def test_complete_requires_all_evidence(self):
self.assertEqual(assess(self.good()),'succeeded')
def test_suggestion_not_execution(self):
item=self.good();item.update(write_attempted=False,readback_matches=False)
self.assertEqual(assess(item),'ready_to_execute')
def test_unapproved_write_is_violation(self):
item=self.good();item['approval']=False
self.assertEqual(assess(item),'policy_violation')
def test_unknown_write_is_not_failure_or_success(self):
item=self.good();item['readback_matches']=None
self.assertEqual(assess(item),'reconcile_required')
def test_budget_does_not_override_uncertain_write(self):
item=self.good();item.update(readback_matches=None,budget_exhausted=True)
self.assertEqual(assess(item),'reconcile_required')
def test_mismatch_requires_repair(self):
item=self.good();item['readback_matches']=False
self.assertEqual(assess(item),'reconcile_required')
def test_missing_evidence_before_write(self):
item=self.good();item.update(evidence_checked=False,approval=False,write_attempted=False,readback_matches=None)
self.assertEqual(assess(item),'needs_review')
def test_budget_stop_before_write(self):
item=self.good();item.update(write_attempted=False,readback_matches=None,budget_exhausted=True)
self.assertEqual(assess(item),'budget_stopped')
def test_rejects_missing_fields(self):
for key in self.good():
item=self.good();del item[key]
with self.subTest(key=key),self.assertRaises(ValueError):assess(item)
def test_rejects_non_boolean_flags(self):
for key in self.good():
for value in ('false',1,0,[],{}):
item=self.good();item[key]=value
with self.subTest(key=key,value=value),self.assertRaises(ValueError):assess(item)
def test_rejects_inconsistent_readback(self):
item=self.good();item['write_attempted']=False
with self.assertRaises(ValueError):assess(item)
def test_rejects_unknown_fields_and_non_mapping(self):
item=self.good();item['model_claim']='success'
for value in (item,None,[],True):
with self.subTest(value=value),self.assertRaises(ValueError):assess(value)
if __name__=='__main__':unittest.main()pwd
ls
python --version
python -B -X utf8 -m unittest -v test_acceptance.pypython --version 若提示找不到命令,先回到 Python 基础完成安装与解释器检查。不要在文件所在目录不明时反复复制命令;ModuleNotFoundError 往往是当前目录不对或文件名被系统加了扩展名。
上方的完整源码先检查六个字段是否齐全、类型是否正确,再据此判断任务状态。审批是否真实有效,应由业务系统提供并核对;下面的判定器处理已核对的状态记录:
六个字段是流程各阶段检查后的结果,布尔值 True/False 表示条件成立或不成立,None 表示回读结果尚不确定。可以把判定函数看成进度看板:它读检查结果,决定显示哪一种状态;真正的读取、审批和写入由相应执行步骤完成。
先在程序文件旁保存 try_states.py,观察一次正常流程:
# 按固定输入观察任务建模、自治边界与成功标准的处理过程和返回结果。
from acceptance import assess
record = {
"suggestion": True,
"evidence_checked": True,
"approval": True,
"write_attempted": False,
"readback_matches": None,
"budget_exhausted": False,
}
print(assess(record))
record["write_attempted"] = True
print(assess(record))
record["readback_matches"] = True
print(assess(record))运行 python -X utf8 try_states.py,依次得到 ready_to_execute、reconcile_required、succeeded:批准后可以执行;尝试写入后先等待核对;回读匹配才算完成。把最后的 True 改为 False 再运行,第三个结果仍是 reconcile_required,因为目标状态还没确认。
# 按固定输入观察任务建模、自治边界与成功标准的处理过程和返回结果。
def assess(record):
approved = record.get("approval") is True
evidence = record.get("evidence_checked") is True
suggestion = record.get("suggestion") is True
attempted = record.get("write_attempted") is True
if attempted:
if not (approved and evidence and suggestion):
return "policy_violation"
if record.get("readback_matches") is not True:
return "reconcile_required"
return "succeeded"
if record.get("budget_exhausted") is True:
return "budget_stopped"
if not (suggestion and evidence and approved):
return "needs_review"
return "ready_to_execute"为什么先判断是否尝试写入?因为它决定了是否可能存在外部副作用。未写入时可以安全等待;已经写入但没有确认结果时,需要对账,而不是简单重新开始。
为什么使用 is True?为了不把字符串 "false" 这样的真值当成确认。完整源码也会直接拒绝这类类型错误、缺失或多余字段,以及“没有写入尝试却确认了写后回读”的矛盾记录。readback_matches 允许布尔值或 None,其余五个字段必须是布尔值。类型正确仍不证明记录真实:调用者仍可能伪造标志。生产执行器必须自行产生或验证这些字段,而不是信任模型提交的 JSON。
八项测试逐项看
| 输入变化 | 预期状态 | 想防住的错误 |
|---|---|---|
| 前置条件成立且回读匹配 | succeeded | 无法识别真正完成 |
| 只有已批准建议,未尝试写入 | ready_to_execute | 把建议当执行 |
| 未批准却尝试写入 | policy_violation | 用执行成功掩盖越权 |
| 写后回读未知 | reconcile_required | 超时直接算成功 |
| 写后未知且预算耗尽 | reconcile_required | 用预算状态覆盖副作用 |
| 回读与目标不匹配 | reconcile_required | 把已返回当已正确更新 |
| 缺依据、未审批、未写入 | needs_review | 缺依据继续自治 |
| 写入前预算耗尽 | budget_stopped | 超出预算继续执行 |
另外四项输入约束测试检查缺失字段、错误类型、矛盾回读、多余字段和非字典输入。状态判定和输入校验一起组成十二项检查。
修改一个字段后,重新运行测试,对照上表找出受影响的状态。
有一个重要限制:判定函数只描述状态,不能回到过去阻止已经发生的越权。真正的执行器必须在工具调用前使用同样的权限与证据约束。否则系统只是事后发现问题,不是预防问题。
把布尔标志还原成可核对的外部契约
在把 approval=True、evidence_checked=True 传给判定器之前,先在纸面或表格里填下列字段。它是人工演练表,不在上方的 acceptance.py 中执行:
| 待核对字段 | 本例记录 | 执行前的判断 |
|---|---|---|
| 工单与读取版本 | 本次指定的工单、版本 4、当前队列 General | 执行器只读取有权访问的这张工单,保存读取版本 |
| 建议与依据 | 目标 Billing、分流规则版本 2 | 人工核对规则文本确实支持该队列 |
| 审批绑定 | 审批记录同意“这张工单从 General 改为 Billing,工单版本 4、规则版本 2” | 对象、动作和版本必须与即将执行的操作逐项相同 |
| 写前重读 | 这张工单的当前版本仍为 4 | 允许尝试写入;若已变成版本 5,停止并重新审核 |
| 写后回读 | 队列为 Billing,返回新版本 5 | 才能把 readback_matches=True;超时或无法回读时用 None |
现在只改变一个条件:审批后别的操作者先把这张工单更新到版本 5。写前重读与审批版本不同,预期是不调用写工具,转人工复核;不能把旧审批仍记为 approval=True 后送入判定程序。恢复到版本 4 的情形,且对象、动作、工单版本、规则版本和写后回读都一致,才允许把事实映射成六个标志。请在作品中保留这两条表格记录和“是否实际发出写请求”,让另一人逐格验收。
7. 把成功、等待审核和结果未知都测一遍
建立测试集时,可以先按失败类型组织,而不是随机拿十条正常工单:
| 样例 | 为什么需要 | 关注指标 |
|---|---|---|
| 明确对应单一团队 | 基线是否工作 | 正确分流与处理成本 |
| 两条规则冲突 | 是否愿意停下来 | 冲突识别与接管质量 |
| 信息不足 | 是否编造分类依据 | 不足信息识别 |
| 文本要求忽略规则 | 是否把工单内容当指令 | 越权尝试与执行拦截 |
| 审批后版本变化 | 是否复用过期批准 | 版本冲突处理 |
| 写入后网络断开 | 是否重复修改 | 对账与幂等恢复 |
“模型分流正确率”与“系统安全完成率”是不同指标。模型可能经常提出正确建议,但写入工具偶尔改错 ID;也可能模型不确定就交给人,安全性提高但人工工作量变大。同时记录分流正确率、执行错误和人工接手量,就能看清这些取舍。
本例不设“超过某个分数即可上线”的通用阈值。试运行时先只生成建议,与人工结果对照;再在低风险、明确授权的范围灰度执行。每次扩大自治范围都应重新验证后果、恢复方式和监控。
8. 开源代码应该带着哪些问题读
研究框架时,按下面顺序检查任务条件怎样变成执行判断:
| 顺序 | 阅读对象 | 必须回答的问题 |
|---|---|---|
| 1 | 这里 Python 判定函数与测试 | 哪一条证据允许进入成功状态 |
| 2 | 官方工程文章的 Workflow 与 Agent 部分 | 谁决定下一步,何时向人交回控制 |
| 3 | 你选定框架的工具执行、暂停与恢复模块 | 审批绑定在哪里,重试会不会重复写入 |
| 4 | 真实项目的日志和评估测试 | 成功标志来自模型文本还是外部状态 |
选框架时,还要看维护状态、许可证和具体版本。后续学习 Agent 循环、持久状态与评估时,可以拿这里写下的任务契约检查框架是否满足需求。
9. 习题、作品和讨论
课后练习展开习题收起习题
正在读取本地记录…
不调用模型,不执行代码。简答、代码和场景题仅保存答案并展示自查标准,不生成评分、徽章或认证。输入内容不会自动保存,请点击提交;不要填写个人信息或密钥。
选择题 · 怎样定义任务完成?
需求是“整理三份资料并标明来源”。哪种完成条件最可验收?
对应知识点:L0-problem
简答题 · 分流完成不等于问题解决
系统已经把工单转到正确团队,模型宣布“客户问题已解决”。依据本篇任务契约,这个结论为什么越界?分别举出内容、执行和边界证据。
对应知识点:L0-problem
代码题 · 未知结果不能被预算停止覆盖
扩展assess的测试:写入已尝试、回读未知且预算耗尽时必须返回reconcile_required;加入字符串"false"作为approval的反例。说明为什么此函数不能直接接受模型填写的状态。
对应知识点:L0-problem
场景题 · 审批之后工单变了
人工批准将T-17从General转到Billing,执行前工单被别人修改。你如何绑定批准、防止旧批准被复用,并向人工交接?
对应知识点:L0-problem
实践作品是一份可以交给另一位开发者实现的工单分流契约,附判定表和测试。至少包含一条成功路径、一条审核路径、一条结果未知路径,以及一个未经授权动作的反例。练习使用虚构工单。
拿三条输入检查契约:资料齐全时走成功路径,缺少审批时停在审核路径,写入结果不明时进入查询或人工核对。逐条说清“根据哪个条件选择这条路”,再与测试结果对照。
可以自问:如果把本例换成退款,哪些条件必须变严?如果只生成建议而不改系统,哪些执行证据可以去掉?具体说清由谁审核什么内容,以及批准怎样绑定到动作。
10. 回顾任务的各个阶段
把工单案例缩成一张可交接的卡片:谁提出建议,谁批准,哪个工具真正写入,写完怎样回读。每一步留下什么证据,也写在旁边。这样模型只说“已更新”时,你能立刻发现缺了写入和回读记录。
离线案例里,写入结果未知会得到 reconcile_required,意思是先核对现状,再决定如何继续。给作品附成功、待审核、未知写入和越权四条状态轨迹。规则程序、固定工作流与 Agent 的取舍可参照 Anthropic Building effective agents。下一篇读Token 成本。
参考资料
- Anthropic:Building effective agents:Workflow 与 Agent 的架构区分,以及简单方案与动态路径的取舍。
- Anthropic:Demystifying evals for AI agents:多轮 Agent 评测需要同时观察结果、轨迹和环境状态;用于理解为什么“最后一句答案”不够。
描述会发送给本站维护者,用于修订文章,保存90天。请只填写文章问题,省略个人信息、密钥和内部资料。