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

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 "步数用完了,没有得到答案。"循环只有三种结局:
- 模型不再调用工具,给出回答 → 正常结束。
- 达到
max_steps→ 强制结束,防止死循环和费用失控。 - 出现异常(网络、余额不足等)→ 交给外层重试或报错,下一课细讲。
工具怎么准备

接着用上一课的咖啡店:一个查资料的工具,一个计算器。
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 把工具做成标准插件,一次编写,多处使用。