Skip to content
A · AI 应用开发入门第 6 课⏱ 20 分钟

Agent 循环:让模型自己一步步完成任务

学完你能
  • 写出一个最小的 Agent 循环,并说清楚它什么时候停
  • 处理多个工具调用、工具报错和步数上限
  • 知道 Agent 什么时候值得用、什么时候用普通流程就够了
Yui 和 Kai 引导模型循环调用工具完成任务。
Yui 和 Kai 引导模型循环调用工具完成任务。AI 生成配图

A4 里模型调用一次工具就回答了。真实任务常常要好几步:先查资料,再算数,再查另一个东西……每一步做什么取决于上一步的结果。把「调用工具 → 看结果 → 决定下一步」放进循环,就是 Agent。

最小的 Agent 循环 ​

模型选工具,执行结果回传,直到停止。
模型选工具,执行结果回传,直到停止。AI 生成配图
python
import json, os
from openai import OpenAI

client = OpenAI(base_url="https://hivegpt.cn/v1", api_key=os.environ["HIVEGPT_API_KEY"])

def run_agent(question, tools, functions, max_steps=6):
    messages = [
        {"role": "system", "content": "你可以使用工具。需要信息就先查,需要计算就用计算器,不要自己心算。"},
        {"role": "user", "content": question},
    ]
    for step in range(max_steps):
        resp = client.chat.completions.create(model="gpt-5.5", messages=messages, tools=tools)
        msg = resp.choices[0].message
        if not msg.tool_calls:                 # 不再调用工具:这就是最终回答
            return msg.content
        messages.append(msg)                   # 先把模型这一步(含 tool_calls)放回对话
        for call in msg.tool_calls:            # 一步里可能有多个调用
            try:
                args = json.loads(call.function.arguments)
                result = functions[call.function.name](**args)
            except Exception as e:             # 出错也要回传,让模型自己调整
                result = f"调用失败:{e}"
            print(f"第 {step + 1} 步:{call.function.name}({args}) -> {result}")
            messages.append({"role": "tool", "tool_call_id": call.id, "content": str(result)})
    return "步数用完了,没有得到答案。"

循环只有三种结局:

  1. 模型不再调用工具,给出回答 → 正常结束。
  2. 达到 max_steps → 强制结束,防止死循环和费用失控。
  3. 出现异常(网络、余额不足等)→ 交给外层重试或报错,下一课细讲。

工具怎么准备 ​

咖啡店工具箱包含查询资料和计算器。
咖啡店工具箱包含查询资料和计算器。AI 生成配图

接着用上一课的咖啡店:一个查资料的工具,一个计算器。

python
DOCS = {
    "菜单": "拿铁 28 元,美式 22 元,燕麦拿铁 32 元;大杯加 4 元。",
    "会员规则": "会员每消费 1 元积 1 分,每 100 分可抵扣 5 元。",
    "营业时间": "周一到周五 8:00–21:00,周末 9:00–22:00。",
}

def search_docs(query):
    hits = [f"{k}:{v}" for k, v in DOCS.items() if any(w in k + v for w in query.split())]
    return "\n".join(hits) or "没有找到相关资料"

def calculator(expression):
    allowed = set("0123456789+-*/(). ")
    if not set(expression) <= allowed:
        raise ValueError("只支持数字和 + - * / ( )")
    return eval(expression, {"__builtins__": {}})   # 示例用;正式项目请用安全的表达式解析库

TOOLS = [
    {"type": "function", "function": {"name": "search_docs", "description": "查询小蜂咖啡的菜单、会员规则、营业时间等资料",
        "parameters": {"type": "object", "properties": {"query": {"type": "string", "description": "关键词,空格分隔"}}, "required": ["query"]}}},
    {"type": "function", "function": {"name": "calculator", "description": "计算四则运算表达式,例如 28*3-10",
        "parameters": {"type": "object", "properties": {"expression": {"type": "string"}}, "required": ["expression"]}}},
]

print(run_agent("我买 3 杯大杯拿铁,用 200 积分抵扣,还要付多少钱?", TOOLS,
                {"search_docs": search_docs, "calculator": calculator}))

动手:看 Agent 一步步做 ​

下面的运行框会真的跑这个循环:每一步把模型想调用的工具显示出来,工具在你的浏览器里执行(资料就是上面那几条),结果再发回模型。每一步都是一次模型调用,从你选的 Key 按用量计费。

▶ 运行 Agent 循环
示例(之前运行的结果)
第 1 步 · search_docs({"query":"拿铁 大杯"}) → 菜单:拿铁 28 元……大杯加 4 元。
第 2 步 · search_docs({"query":"积分 抵扣"}) → 会员规则:……每 100 分可抵扣 5 元。
第 3 步 · calculator({"expression":"(28+4)*3-200/100*5"}) → 86
第 4 步 · 最终回答:3 杯大杯拿铁共 96 元,200 积分抵 10 元,还需支付 86 元。

试试换个问题:「周六晚上 9 点半还能去吗?」「一杯燕麦拿铁比美式贵百分之多少?」看它调用的工具有什么不同。

让 Agent 更可靠 ​

  • 工具描述要具体:写清楚「什么时候用」「参数是什么格式」,模型选错工具多半是描述含糊。
  • 工具结果要简短:把几万字的网页原样塞回去,既贵又容易让模型迷失;先截取、摘要。
  • 报错也是结果:参数不对时把错误信息回传,模型通常会改正后重试。
  • 记录每一步:打印或存下每步的工具名、参数、结果和 token,出问题时才能复盘。
  • 有副作用的工具要确认:下单、发消息、删数据前,让用户确认(A4 讲过)。

什么时候不用 Agent ​

Agent 灵活,但每一步都是一次模型调用:更慢、更贵、结果也更难预测。如果步骤是固定的(比如「先检索,再回答」),直接按顺序写代码更好——A5 的 RAG 就不需要 Agent。步骤要根据中间结果临时决定时,才值得用循环。

小结 ​

  • Agent = Function Calling + 循环:模型选工具,你执行,结果交回,直到它不再调用工具。
  • 一定要有步数上限;工具出错也要回传;每一步都记日志。
  • 流程固定就写普通代码,需要临场决策才用 Agent。

下一课:工具一多,每个应用都要重复接一遍。MCP 把工具做成标准插件,一次编写,多处使用。

代码示例在页面里运行时使用 HiveGPT 的模型接口。延伸阅读来自 JavaGuide(Apache-2.0),版权归原作者。