AI Agent开发学习:用Python写一个能调用工具的智能体
AI Agent 不是某个具体框架,而是一种让大模型根据目标自主决定“下一步做什么”的程序结构。
学习 AI Agent 开发,最直接的方式是先跑通一个最小闭环:模型接收任务,判断需要调用哪个工具,程序执行工具并把结果交回模型,模型再决定继续调用还是给出最终答案。
本文用 Python 和 OpenAI 兼容接口实现一个支持天气查询和四则运算的命令行 Agent,环境为 Python 3.9 以上,需要可用的 API Key。
先理解 Agent 的循环结构
普通聊天是一次请求一次回复,Agent 则可能经历多轮“模型决策 → 工具执行 → 结果回传”。
这个循环里有两个关键角色:
- 模型:负责理解任务、选择工具、生成参数。
- 执行器:也就是你的 Python 代码,负责真正调用工具函数,并把返回值按协议格式送回模型。
工具本身用 JSON Schema 描述,告诉模型“有哪些工具、每个工具需要什么参数”。
模型不会直接执行代码,它只输出“我要调用哪个工具、参数是什么”,执行权始终在程序手里。
准备环境与项目文件
先创建虚拟环境并安装依赖。
不要直接装在系统 Python 里,避免版本冲突。
python -m venv agent-env
# Linux/macOS
source agent-env/bin/activate
# Windows
agent-env\Scripts\activate
pip install openai
新建 agent.py,并设置环境变量保存密钥:
export OPENAI_API_KEY="你的密钥"
# 如果使用兼容接口,再加一个 base_url 变量
export OPENAI_BASE_URL="https://你的接口地址/v1"
Windows 下用 set 代替 export。
密钥不要写死在代码里,更不要提交到 Git。
定义两个能被模型调用的工具
工具函数就是普通 Python 函数,重点是参数和返回值要简单、可序列化。
下面写一个天气查询(示例返回固定数据,实际可替换为真实 API)和一个计算器。
import json
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["OPENAI_API_KEY"],
base_url=os.environ.get("OPENAI_BASE_URL")
)
def get_weather(city: str) -> str:
data = {"北京": "晴,26℃", "上海": "多云,24℃"}
return data.get(city, f"未找到{city}的天气数据")
def calculate(expression: str) -> str:
try:
allowed = set("0123456789+-*/(). ")
if not set(expression) <= allowed:
return "表达式包含不允许的字符"
return str(eval(expression, {"__builtins__": {}}, {}))
except Exception as e:
return f"计算失败:{e}"
注意计算器用 eval 时必须限制字符集和内置函数,否则有安全风险。
生产环境建议换成安全的表达式解析库。
接着用 JSON Schema 描述这两个工具,作为请求参数传给模型:
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市的天气",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
}
},
{
"type": "function",
"function": {
"name": "calculate",
"description": "计算四则运算表达式",
"parameters": {
"type": "object",
"properties": {"expression": {"type": "string"}},
"required": ["expression"]
}
}
}
]
编写 Agent 主循环
主循环负责发送消息、检查模型是否要求调用工具、执行工具并追加结果。
设置最大轮数防止死循环。
available = {"get_weather": get_weather, "calculate": calculate}
def run_agent(user_input: str, max_steps: int = 5) -> str:
messages = [{"role": "user", "content": user_input}]
for _ in range(max_steps):
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
tools=tools,
tool_choice="auto"
)
msg = resp.choices[0].message
messages.append(msg)
if not msg.tool_calls:
return msg.content
for call in msg.tool_calls:
fn = available.get(call.function.name)
args = json.loads(call.function.arguments)
result = fn(**args) if fn else "未知工具"
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": str(result)
})
return "达到最大执行轮数,未得到最终答案"
if __name__ == "__main__":
print(run_agent("北京天气怎么样?顺便算一下(12+8)*3"))
运行 python agent.py,预期输出中会包含北京天气和计算结果 60。
如果只输出天气没算数,说明模型选择工具不稳定,可以调整提示词或在用户输入中明确要求分步处理。
常见报错与避坑说明
报错 tool_calls 为空或工具不触发:先检查 tools 参数是否传对,以及工具描述是否清楚。
模型对 description 依赖很强,把“查询天气”写成“根据城市名返回天气”会更容易命中。
报错 Invalid parameter: messages with role 'tool' must be a response to a preceding message with 'tool_calls':说明工具结果消息没有对应到正确的 tool_call_id。
每次追加 role=tool 消息时,必须带上模型返回的 call.id,且顺序不能乱。
报错 401 或连接超时:检查 OPENAI_API_KEY 是否生效,兼容接口的 base_url 是否以 /v1 结尾。
可以先用 curl 测一下接口连通性,再排查代码。
Agent 陷入重复调用:常见原因是工具总是返回错误信息,模型不断重试。
给工具返回值加上明确的成功/失败标记,并设置 max_steps 上限,避免消耗额度。
验证 Agent 是否真正可用
除了看最终输出,建议做三项检查:
- 在
run_agent里临时打印messages,确认工具调用和结果回传的顺序正确。 - 换一个需要两次工具调用的任务,例如“上海天气和北京天气分别是什么”,观察是否产生多个
tool_calls。 - 故意传入不存在的城市,确认工具返回兜底信息而不是让 Agent 崩溃。
完成这个最小闭环后,再学习 LangChain、AutoGen 等框架会更容易理解它们封装了什么。
AI Agent 开发学习的重点不是背 API,而是掌握“模型决策、程序执行、结果回传”这条链路,后续增加记忆、多 Agent 协作或接入真实业务系统,都是在这条链路上扩展。