AgentDepthAgent 工程学习指南
输入概念或问题,检索正文和待编写目录。
第一次学习?从 L0 总览开始 →

任务建模、自治边界与成功标准:先把“完成”写清楚

我的学习 →

正在读取学习记录…

任务建模是把“要做什么”写成具体要求,让人和程序都能判断任务是否完成。

比如,客户留言:“付款成功,但订单显示未付款。”客服系统会把这条求助保存成一条记录,称为“工单”。我们希望助手读懂客户的问题,推荐负责处理的部门,并说明理由。它可以回答:“建议交给账单组,因为客户描述的是付款状态与订单状态不一致。”客服人员看过后,再决定是否把这条求助交给账单组。这就是一次完整的推荐任务。

先把这条任务写成具体的执行要求,再用程序判断几条固定记录分别处于什么状态。建议、审批和回读对应不同阶段,程序据此决定继续执行还是等待。

先修:模型 API 配置。你不需要先学 Agent 框架;先能说清任务,后续的工具、循环、状态和评估才有组织依据。

0. 为什么写 Agent 之前先写任务

模型可以生成听起来完整的结果,但程序需要知道什么时候停止、该不该调用写工具、失败后由谁接手。如果“成功”只是模型在最后说一句“已完成”,系统无法分辨下面几种情况:它只给了建议;写请求已经发出但响应丢失;修改了错误对象;或者越过审批后碰巧得到了正确值。

任务建模把这些差异提前写成状态和证据。到了 L3 的 Agent Loop,循环每一轮都要回答“离哪个成功条件更近”;到了 L7 的评测,数据集也要知道一条轨迹到底该判成功、失败还是需要人工核对。这里写不清,后面只会把模糊需求放大。

1. 把模糊愿望改成一份任务契约

任务契约是执行前约定的输入、允许动作、交付物和验收规则,不要求使用某种特定格式。它比“请认真完成”多了可以检查的边界。

先写一份最简单的建议任务:输入是一张“付款成功但订单显示未付款”的工单;动作是读取工单和分流规则;输出是“建议账单组处理”以及对应规则。看到建议和依据,就完成这项任务。此时没有更新队列,也就不需要写入审批。像先开一张维修建议单,还没开始拆机器。

若用户要求助手真正转移工单,就要增加写权限、审批和回读。这是另一份更强的契约。下面的表围绕这个“实际更新”任务展开:

项目本例约定为什么不能省
对象范围本次指定的工单 ID,且操作者有权访问防止遍历全部客户记录
输入版本工单当前版本、分流规则版本防止审核时和执行时看到不同内容
交付物分流建议、依据片段、审核结果和更新记录区分文本建议与真实系统修改
允许读操作读取指定工单及授权规则限定模型寻找证据的范围
允许写操作审核通过后更新指定队列字段不包含退款、关闭工单或给客户发信
成功条件审核与实际写入一致,回读确认当前状态请求发出不是最终完成
停止条件缺规则、冲突、预算耗尽或执行状态未知防止无依据地继续自治
人工接管提交问题、候选方案、已发生动作及未解决项人不必重新猜一遍现场发生了什么

“提高客服效率”是业务目标,不是单次任务的可执行条件。业务层可以用平均处理时长、人工返工和错误转派率衡量,但单次执行仍需明确到底修改了哪条记录。

分流任务的完成条件是转到正确团队。接手团队随后处理客户问题,那是下一项任务;把这两个阶段分别记录。

2. 先判断需不需要 Agent

不必把所有自动化都叫 Agent。若工单已经带有可信的产品编号,队列映射也是确定的,普通规则程序可能足够。若用户的表达不统一,可以让模型做语义分类,再把后续步骤保持为固定工作流。只有当下一步确实需要根据观察动态选择,才考虑更开放的循环。

方案本例适用情况主要控制方式
普通程序编号到队列的映射完整确定输入校验、规则与测试
单次模型调用从自由文本中提出分类建议结构化结果、证据与人工复核
固定工作流读取→建议→审核→更新的步骤已知显式状态与步骤前置条件
Agent 循环需要有界地查不同资料、根据结果决定下一步工具权限、预算、停止与外部验收
规则程序、固定工作流和Agent循环的三栏对比,分别说明谁决定路径、适合的任务、工单例子和主要代价
图1:从左向右不是升级路线。确定的映射留给规则;步骤已知时采用工作流;只有路径难以预知且收益可测时,才承担 Agent 循环的额外成本。

这里采用常见的架构区分:工作流按预定路径组织,Agent 允许模型动态决定部分过程。不同资料对 Agent 的术语范围可能不同,不必争一个标签;更重要的是知道哪些决策由模型控制。Anthropic 的工程文章讨论了这一区别以及成本、延迟和灵活性的取舍。Building effective agents

不要为了让路线图显得高级而添加循环。每新增一次模型调用,都要问:它获取了什么新信息?改善了哪个可测指标?如果没有答案,可能只是让系统更难复现。

3. 按动作风险划分自治程度

“允许自主执行”至少包含三个维度:能看什么、能决定什么、能改什么。读取公开说明与修改客户账务不应共享同一授权范围。

本例允许模型建议分类、说明缺少的资料,并在已授权范围内选择规则查询。它不能修改权限、扩大工单集合,不能把“请处理完”理解为允许付款或通知客户。即使模型在文本里说“用户已经同意”,也不等于真的取得审批。

审核必须绑定具体对象、动作参数和版本。例如审核的是“把这张工单从普通队列转到账单组”,而不是“今后所有分流动作均同意”。如果建议内容或工单版本在审批后改变,应重新验证或再次审核,不能复用旧批准。

权限检查也不能只写在 Prompt 里。写入工具本身要校验身份、对象范围、字段允许列表和审批有效性;否则模型忽略一句话就能越界。Prompt 帮助模型理解规则,程序负责执行不可绕过的约束。

人工接管需要哪些信息

有效的接管材料至少包括:

  • 工单 ID 与读取版本,不包含无关客户资料。
  • 已查过的规则及适用范围。
  • 候选团队和尚未消除的冲突。
  • 是否已经发出写请求,当前执行结果是否确定。
  • 等待人工决定的具体问题。

例如:“规则 R3 指向 Billing,规则 R8 指向 Fraud,二者生效范围重叠;未发出写请求,等待确认规则优先级。”这比“遇到问题,任务失败”更容易继续处理。

4. 成功条件要拆成三种证据

内容证据

分类建议是否有依据?引用的规则是否真的适用于该工单?文本里出现一个规则 ID 不代表引用正确,需要检查规则内容、版本和适用条件。

执行证据

系统是否实际发生了授权变更?接口返回请求受理、任务排队或 HTTP 成功,不一定意味着目标记录已经更新。需要根据工具语义检查结果,必要时回读并比对版本。

边界证据

执行是否仍在授权范围、预算与数据边界内?误改了另一条工单,即使那条工单成功写入,也不是当前任务成功。没有审批但写入成功,属于越权,不是“效率高”。

本例的严格成功条件可以写成:有效建议与依据已审核;审批对应实际动作;更新后回读与期望队列一致;没有违反任务边界。这里的回读也不是万能证明:如果其他人随后修改了工单,状态会再次变化,所以记录时间和版本很重要。

一张工单从确认任务到交付完成共有六步;审批异常或回读异常分别进入人工核对分支
图 2:六步主流程在回读比对后才到交付完成。依据不足或审批失效时暂停;写入状态未知或回读不符时先查状态,再人工确认。

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:

Python
"""根据推荐、审批、实际更新和更新后的查询结果,判断任务进行到了哪一步。"""
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
Python
# 核对任务建模、自治边界与成功标准的正常结果和错误分支;断言失败时会显示对应的测试。
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()
Bash
pwd
ls
python --version
python -B -X utf8 -m unittest -v test_acceptance.py

python --version 若提示找不到命令,先回到 Python 基础完成安装与解释器检查。不要在文件所在目录不明时反复复制命令;ModuleNotFoundError 往往是当前目录不对或文件名被系统加了扩展名。

上方的完整源码先检查六个字段是否齐全、类型是否正确,再据此判断任务状态。审批是否真实有效,应由业务系统提供并核对;下面的判定器处理已核对的状态记录:

六个字段是流程各阶段检查后的结果,布尔值 True/False 表示条件成立或不成立,None 表示回读结果尚不确定。可以把判定函数看成进度看板:它读检查结果,决定显示哪一种状态;真正的读取、审批和写入由相应执行步骤完成。

先在程序文件旁保存 try_states.py,观察一次正常流程:

Python
# 按固定输入观察任务建模、自治边界与成功标准的处理过程和返回结果。
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,因为目标状态还没确认。

Python
# 按固定输入观察任务建模、自治边界与成功标准的处理过程和返回结果。
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. 习题、作品和讨论

课后练习展开习题收起习题

4 道练习 · 客观题规则评分 · 其余题目待批改

正在读取本地记录…

不调用模型,不执行代码。简答、代码和场景题仅保存答案并展示自查标准,不生成评分、徽章或认证。输入内容不会自动保存,请点击提交;不要填写个人信息或密钥。

练习 1 / 4 · v2.0.0

选择题 · 怎样定义任务完成?

需求是“整理三份资料并标明来源”。哪种完成条件最可验收?

对应知识点:L0-problem

选择一个答案
练习 2 / 4 · v2.0.0

简答题 · 分流完成不等于问题解决

系统已经把工单转到正确团队,模型宣布“客户问题已解决”。依据本篇任务契约,这个结论为什么越界?分别举出内容、执行和边界证据。

对应知识点:L0-problem

练习 3 / 4 · v2.0.0

代码题 · 未知结果不能被预算停止覆盖

扩展assess的测试:写入已尝试、回读未知且预算耗尽时必须返回reconcile_required;加入字符串"false"作为approval的反例。说明为什么此函数不能直接接受模型填写的状态。

对应知识点:L0-problem

练习 4 / 4 · v2.0.0

场景题 · 审批之后工单变了

人工批准将T-17从General转到Billing,执行前工单被别人修改。你如何绑定批准、防止旧批准被复用,并向人工交接?

对应知识点:L0-problem

实践作品是一份可以交给另一位开发者实现的工单分流契约,附判定表和测试。至少包含一条成功路径、一条审核路径、一条结果未知路径,以及一个未经授权动作的反例。练习使用虚构工单。

拿三条输入检查契约:资料齐全时走成功路径,缺少审批时停在审核路径,写入结果不明时进入查询或人工核对。逐条说清“根据哪个条件选择这条路”,再与测试结果对照。

可以自问:如果把本例换成退款,哪些条件必须变严?如果只生成建议而不改系统,哪些执行证据可以去掉?具体说清由谁审核什么内容,以及批准怎样绑定到动作。

10. 回顾任务的各个阶段

把工单案例缩成一张可交接的卡片:谁提出建议,谁批准,哪个工具真正写入,写完怎样回读。每一步留下什么证据,也写在旁边。这样模型只说“已更新”时,你能立刻发现缺了写入和回读记录。

离线案例里,写入结果未知会得到 reconcile_required,意思是先核对现状,再决定如何继续。给作品附成功、待审核、未知写入和越权四条状态轨迹。规则程序、固定工作流与 Agent 的取舍可参照 Anthropic Building effective agents。下一篇读Token 成本。

参考资料

图片查看器
100%

文章配图 · 按 Esc 关闭