# 企业级 Agent 从 0 到 1(三):推理循环——让模型自己决定停,它就停不下来

(二)结尾我留了个尾巴:单次调用对付一问一答还行,稍微复杂点就废了。我当时没当回事,结果第二天自己手痒试了下"查上周缺陷数、跟前一周比趋势"------直接歇菜。这活儿得先查、再算、再总结,一串好几步,一次调用根本兜不住。

所以这篇就是怎么让 agent 自己多步跑起来。我一开始想的就是不就是个 while 套 API 嘛,写出来才知道坑都在后头。

一、先写最土的循环

上来别想那么多,把历史丢回去让它接着说,说完 FINISH 就停:

复制代码
```python
def run(q):
    hist = [sys_prompt, {"role": "user", "content": q}]
    while True:                      # 危险:这玩意儿没退出条件
        out = llm(hist)
        if "FINISH" in out:
            return out
        hist.append({"role": "assistant", "content": out})
        hist.append({"role": "user", "content": "[Observation] 工具结果"})
```

跑起来,两个问题啪就来了。

二、跑飞:它根本不会自己停

我让它"汇总下上周发版都改了啥"。它先翻 git log,翻完去查 Jira,查完去翻文档,然后折回去又翻 git log 看另一分支......我盯着日志数到 30 多步它还在转,最后手动 Ctrl-C 了。

我当场就懵了。后来想明白一件事:模型没有成本概念。它每步只管"下一步干啥",根本不在乎烧了多少 token、转了多少轮。普通 while 好歹有退出条件,模型的循环没有------你指望它自觉收手,它就一直觉得"再确认一下比较稳"。

取舍很直接:软终止(它说 FINISH)必须有硬上限兜着。硬上限怎么定?

  • 固定 12 步最省事,但简单任务浪费、复杂任务不够,否了
  • 按 tier 设(small 短、top 长)------我起步用的这个
  • 按历史步数 P95 × 1.5 最准,但得先攒数据,跑两周再回校准

我先 tier 起步,等手上有步数分布了再换 P95。既不至于一上来被数据卡死,也不至于定个死数后面跟着遭罪。

三、打转:停是停了,但来回兜圈子

这个比跑飞还烦。有次让它"把测试环境配置同步到预发"。它调了 kubectl apply -f config.yaml,返回 Error: already exists。它说"我反思一下",然后------又调了一次 kubectl apply -f config.yaml。还是 already exists。连着 4 次,每次"反思"完还是同一个动作。我看着日志真笑不出来。

原因很简单:没反思,或者反思来得太晚,它错了也不知道错,接着错。

取舍:反思时机我选"失败触发为主 + 终局强制 + 复杂任务定期体检"。每步都反思成本直接翻倍,大部分步本来就没错,纯浪费,否了;失败才反思 ROI 最高;终局强制是防"忙活半天做完了但答非所问";定期体检(每几步或到里程碑)防它静默跑偏。这里有个经验------如果等"失败后才反思"已经白白烧了前面好几步 token,就把触发提前到"连续两步没进展就反思",进度检测比等失败更早发现跑偏。

四、白反思:最阴的一种

就是上面那个打转,它明明"反思"了,文本里写着"我应该用 get_pod_logs 而不是 describe_pod",结果 action 字段填的还是 describe_pod。文本说改了,手上没改。

这种最坑------你以为它在纠偏,其实原地踏步,还给你一种"我在治理"的幻觉。

取舍:反思必须结构化 + 强制分支,我让它只回 JSON:

复制代码
```
{"verdict": "retry | change_strategy | escalate", "new_params": {...}}
```

关键在 retry 必须带新参数,绝不允许"用相同参数重试"------参数错了必然再错。这条我当企业级硬约束,比不反思还坑的就是这种假反思。

五、烧钱:反思不是免费的

调试那阵我图省事开了每步反思,一个本来 5 步能完的任务烧了平时三倍的钱。后来才反应过来------反思"什么内容"决定花多少钱,查个参数对错犯不着上顶级模型。

取舍:分层。执行层(工具选对没、参数对没)small 7B 就够;规划层(跑偏没、有更短路径没)才上 top 档。这么分下来 80% 的反思走便宜档,只有真出事才烧 top。

企业级坑:不可逆操作谁来拍板

代码助手一个 git push 把没 review 的代码推上去了,或者误删文件、对外发消息------个人项目你一个人 git 随便推,但团队里模型误操作影响的是别人。我把 git push / 删除 / 外发 / 支付这些在工具定义里打标,循环层统一拦截,轮不到模型"自主决定"。

六、一个能跑的骨架

把上面几个决策点落进同一段代码:

python 复制代码
```
import requests

API = "https://api.deepseek.com/v1/chat/completions"
KEY = "sk-xxx"

# 硬上限按 tier:简单短、复杂长。设计预期值,跑两周用 P95×1.5 回校准
HARD_LIMIT = {"small": 5, "mid": 12, "top": 20}
TIER = "mid"

def llm(messages, model="deepseek-chat"):
    r = requests.post(API, headers={"Authorization": f"Bearer {KEY}"},
                      json={"model": model, "messages": messages, "temperature": 0.3})
    return r.json()["choices"][0]["message"]["content"]

SYS = ("你是研发团队助手。多步任务按 Thought→Action→ActionInput→Observation 循环,"
       "完成说 FINISH。禁止自行执行 git push / 删除 / 外发,这些必须停下来等人工确认。")

def reflect(hist):
    """失败/打转时触。执行层检查用便宜模型够。返回结构化决策。"""
    p = hist + [{"role": "user", "content":
        "检查:工具选对没?参数对没?只回JSON:"
        "{\"verdict\":\"retry|change|escalate\",\"new_params\":{}}"}]
    return llm(p, model="deepseek-chat")

def run(q):
    hist = [{"role": "system", "content": SYS}, {"role": "user", "content": q}]
    steps = 0
    while steps < HARD_LIMIT[TIER]:
        steps += 1
        out = llm(hist)
        print(f"[step {steps}] {out[:100]}")
        if "FINISH" in out:
            return out
        # 解析 Action 调真实工具,观察结果回灌 hist
        # 失败检测:工具返回 error -> reflect()
        # 打转检测:连续两步同 Action -> reflect()
        refl = reflect(hist)
        if "retry" in refl and "new_params" not in refl:
            return "【反思无效,转人工】"   # retry 不带新参数,当失败处理
        hist.append({"role": "assistant", "content": out})
        hist.append({"role": "user", "content": "[Observation] 工具结果(接真实调用)"})
    return "【超出步数上限,转人工】"       # 撞硬上限不无限循环

print(run("查上周缺陷数,跟前一周比,说趋势"))
```

while True 换成了带硬上限的 whilereflect 负责失败/打转时的结构化反思,retry 不带新参数直接转人工------几个点对应上面几个坑。打转和失败的具体检测要去解析 Action,这里用注释标了位置。

七、这章的坑,记一下

  • 循环要软终止 + 硬上限,硬上限先按 tier,跑两周用 P95×1.5 回校准,别拍固定值。
  • 反思失败触发为主、终局强制必做retry 必须带新参数,禁止原地重放。
  • 企业级坑:不可逆操作代码硬控,不交给模型。
  • 遗留问题:循环有了,工具还没接。它现在"想干活"却"没手",下一步得给它接工具。

你们做 agent 的时候,循环上限是按固定步数还是按任务复杂度来定?我这儿只是引出问题,实际还是根据业务来决定,我的理念一直是,用最合适的技术能力,做最合适的事情,不为了堆技术而炫技。有得有舍,你换来了高可用,就一定会牺牲成本或者延迟,这账谁都绕不开。你如何选择的呢?


相关推荐
ZGG00313 分钟前
MCP 02:MCP 的各类比对——和 Function Calling、A2A、Skill 到底什么关系
人工智能·后端·python
IT_陈寒14 分钟前
Vite热更新突然失效?可能是这个配置在捣鬼
前端·人工智能·后端
不懂的浪漫14 分钟前
企业 Agent Runtime | 项目知识需要生命周期
人工智能·软件工程·coding agent
空堂与归17 分钟前
Anthropic 推出 Claude Fable 5.1 与 Mythos 5.1:同一模型,两套安全策略,缓存读取降价 75%
人工智能·ai·clude
智慧大脑搬运工20 分钟前
# 美丽蓝天政策申报|散煤与锅炉炉窑淘汰收官:技术替代路线、政策红线与数字化监管(2026)
人工智能
dllmayday21 分钟前
基于python-can仿真生成blf
python
轻松,带微笑23 分钟前
AI Agent正在重构知识付费SaaS,真正改变的不是功能数量
人工智能·重构
Dawson Zhu27 分钟前
Agent 持续进化:从学习信号到参数更新的工程化路径
人工智能·语言模型·架构·aigc·agi