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

终止条件:让循环知道何时完成、何时停下

我的学习 →

正在读取学习记录…

1. 完成条件和停止条件分别回答什么

完成条件描述目标是否达到:查询退款政策,需要找到相关资料。停止条件描述循环何时结束:找到资料、用完步数、查完来源、重复无进展,都能让循环退出。

把循环想成去图书馆找书:找到书是完成,图书馆闭馆是停止。两者都会让你离开,但你带回的结果不同。Agent 的返回值需要保留这个区别,界面才能告诉用户已找到,还是这次尚未找到。

每轮读取观察,找到证据则完成,达到步数或观察重复则停止
三个出口分别保存成功、步数到达和无进展的原因。

最大步数限制动作数量;无进展检查判断动作是否带来新信息。步数还有余额,也可以因重复提前结束。实际查询不同来源时,要把来源和参数一起比较,才能判断是否真正重复。

2. 运行三个有明确出口的循环

多个条件可能在同一步满足。下面的程序采用明确的判断顺序:先确认当前观察仍在步数额度内,再看是否已经找到资料;未找到资料时,若已到最后一步,记为步数用尽,否则检查是否连续重复。

当前情况结果为什么
第2步找到资料,上限也是2步completed最后一个允许步骤仍可以完成任务
第3步才找到资料,上限为2步max_steps第3步超出本次允许处理的范围
第2步重复,上限为5步no_progress还有步数,但这次查询没有带来新信息
第2步重复,上限为2步max_steps两个条件同时满足,本例优先记录额度已用尽

先区分“允许处理这一步”与“这一步处理后如何结束”,再沿代码阅读,会更容易理解两个步数判断的位置。

先在 PowerShell 创建并进入学习目录:

Bash
mkdir "F:\学习\l3-termination" -Force
cd "F:\学习\l3-termination"

保存下面的文件后,在这个终端运行 dir 核对文件名,再执行本节给出的运行命令。

在新文件夹创建 termination.py,写入完整代码:

先在 PowerShell 创建并进入学习目录:

Bash
mkdir "F:\学习\l3-termination" -Force
cd "F:\学习\l3-termination"

保存下面的文件后,在这个终端运行 dir 核对文件名,再执行本节给出的运行命令。

Python
"""成功、步数用尽和重复无进展,都有明确的停止原因。"""
import json

def run(observations, max_steps=3):
    if type(max_steps) is not int or max_steps < 1:
        raise ValueError("最大步数须为正整数")
    previous = None
    for step, text in enumerate(observations, start=1):
        # 先检查动作额度,再消费当前观察。
        if step > max_steps:
            return {"status": "stopped", "reason": "max_steps", "steps": max_steps}
        if isinstance(text, str) and text.startswith("退款期限") and text.endswith("天"):
            return {"status": "completed", "reason": "evidence_found", "steps": step}
        if step == max_steps:
            return {"status": "stopped", "reason": "max_steps", "steps": step}
        if text == previous:
            return {"status": "stopped", "reason": "no_progress", "steps": step}
        previous = text
    return {"status": "stopped", "reason": "sources_exhausted", "steps": len(observations)}

if __name__ == "__main__":
    for data, limit in ((["空", "退款期限7天"], 3),
                        (["空", "空", "空"], 2),
                        (["空", "空", "空"], 1)):
        print(json.dumps(run(data, limit), ensure_ascii=False))
Bash
ls
python termination.py

输出:

TXT
{"status": "completed", "reason": "evidence_found", "steps": 2}
{"status": "stopped", "reason": "max_steps", "steps": 2}
{"status": "stopped", "reason": "max_steps", "steps": 1}

第一组先“空”、后“退款期限7天”,第二步完成。第二组一直“空”,限制为2,第二步结束。第三组限制为1,消费一条空观察后结束。

enumerate 给观察编号,previous 保留上一条。检查顺序是成功、额度、重复。因此第二组在同一步既重复又到达额度时,记录 max_steps。成功放在额度前,允许最后一次合法动作带回证据。

3. 改变额度,观察停止原因

把第二组限制从2改成5,观察仍是三条“空”,会在第二步返回 no_progress:额度允许继续,但结果连续相同。把第一组限制改为2,仍会在第二步成功。

程序以“退款期限”开头、“天”结尾的固定格式识别本例证据。其他文字属于未命中;实际工具可返回结构化的相关性与来源字段。

要观察来源耗尽,把主程序循环替换为:

Python
print(json.dumps(run(["空"], 3), ensure_ascii=False))

运行 python termination.py,得到 sources_exhausted,表示给定来源已全部走完。增加来源与增加步数分别影响不同出口。恢复第2节的三组主程序输入,再运行对照三行原结果。

4. 保留检查用例

同一目录创建 test_termination.py:

下面的测试检查找到资料、重复、步数用尽和来源耗尽,以及最后允许的一步刚好成功的情况。

测试代码:test_termination.py
Python
"""检查termination示例的正常路径和边界结果。"""
import unittest
from termination import run
class Tests(unittest.TestCase):
    def test_success_at_last_allowed_step(self):
        self.assertEqual(run(["空", "退款期限7天"], 2)["status"], "completed")

    def test_evidence_beyond_limit_is_not_consumed(self):
        self.assertEqual(run(["空", "未知", "退款期限7天"], 2)["reason"], "max_steps")

    def test_simultaneous_repeat_and_limit_uses_limit_reason(self):
        self.assertEqual(run(["空", "空"], 2)["reason"], "max_steps")

    def test_done(self): self.assertEqual(run(["空","退款期限7天"])["status"],"completed")
    def test_limit(self): self.assertEqual(run(["空","空","空"],2)["reason"],"max_steps")
    def test_repeat(self): self.assertEqual(run(["空","空","空"],5)["reason"],"no_progress")
    def test_unrelated_text(self):
        self.assertEqual(run(["weather sunny"])["status"],"stopped")

if __name__ == "__main__":
    unittest.main()
Bash
python -m unittest test_termination -v

测试同时检查正常路径和改变条件后的结果。遇到导入错误时,先核对两个文件是否在同一目录;遇到断言失败时,比较预期值和实际值,再回到相应分支。

5. 根据原因安排后续操作

原因含义后续方向
evidence_found取得符合目标的资料生成并核对答复
max_steps动作额度已用完缩小任务或调整预算
no_progress重复观察没有新信息换查询方式或转人工
sources_exhausted当前来源已查完补充来源或结束

实际工具可能返回不同的无关文字,因此文本变化只是一个信号。更贴近任务的进展量包括新增相关资料条数、填满的必需字段数和已完成子任务数。写工具还可根据回执和业务状态判断进展。

调用方同时读取 status、reason、steps。成功展示结果和来源,额度停止展示已完成部分和待确认项,人工核对展示最后动作及原因。只显示“结束了”,会丢掉对读者最有用的信息。

一次请求也可能等待很久,步数少仍会超时。因此循环还要检查累计耗时,并为请求设置超时。Python 的 循环与分支教程可用于复习示例语法。

逐步追踪一次运行

以观察列表“空、退款期限7天”和最大步数2为例:

轮次当前观察目标判断额度判断最终动作
1空未取得退款证据1小于2保存previous,继续
2退款期限7天符合固定证据格式本轮在合法额度内完成并返回steps=2

再看“空、空、空”和额度5:第一轮保存空,第二轮仍为空,重复检查命中,提前停止。sources_exhausted则发生在列表走完之后。例如只有一条空、额度3,列表已经没有下一条,循环结束后返回这个原因。

一个真实查询还应记录动作身份,例如来源、关键词和结果摘要。如果来源从FAQ变成政策文档,即使都返回空,这仍然是一次不同的尝试。可将previous扩展为“来源+参数+结果”,或用新增证据数衡量进展。

目标条件也要贴近任务。这里的固定格式方便观察分支;接入实际检索时,可先让工具返回相关片段与出处,检查相关资料是否满足用户问题。最大步数控制资源,目标条件决定成果,两项各有输入与证据。

回顾与下一步

一个目标、一轮动作、一条观察、一个明确出口。下一篇 预算进一步处理Token、时间和费用。

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

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

正在读取本地记录…

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

练习 1 / 4 · v1.1.0

选择题 · 一个必需任务失败而另一个仍运行,协调者优先做什么

一个必需任务失败而另一个仍运行,协调者优先做什么?

对应知识点:L6-termination

选择一个答案
练习 2 / 4 · v1.1.0

简答题 · 解释机制

全部完成、预算耗尽和必需任务失败的结束理由怎样区分?

对应知识点:L6-termination

练习 3 / 4 · v1.1.0

代码题 · 核对给定数据

写一条断言,检查结束后所有任务都处于终态集合中。

对应知识点:L6-termination

练习 4 / 4 · v1.1.0

场景题 · 分析具体变化

可选资料查询失败,但必需订单和政策齐全,可以怎样收尾?

对应知识点:L6-termination

图片查看器
100%

文章配图 · 按 Esc 关闭