🐸 青蛙编程网
首页 / AI / 智能体 Agent / 2. 环境搭建与第一个 Agent

2. 环境搭建与第一个 Agent

这是什么:工具调用(function calling) = 模型不直接执行代码,而是「点名」要你调哪个函数、传什么参数。

为什么用:只靠模型自己回答,它既查不了资料也动不了你的文件;给它工具,模型才算有了手。

如何用:装好 SDK 后,用三段代码把「挂工具 → 执行 → 回传结果」串起来,做出会查天气的 Agent。

2.1 环境准备

按 虚拟环境 venv 建环境,装 LLM 教程用过的同一个 SDK:

mkdir agent-lab && cd agent-lab
python -m venv .venv
.venv\Scripts\Activate.ps1        # (macOS/Linux: source .venv/bin/activate)

pip install openai

Key 仍从环境变量读,沿用 LLM 教程 的约定:

$env:LLM_API_KEY = "你的key"
$env:LLM_BASE_URL = "https://api.openai.com/v1"
$env:CHAT_MODEL = "gpt-4o-mini"

前提:模型必须支持 function calling(工具调用)。不支持的模型看不到 tools 参数,工具会被静默忽略。

2.2 工具调用是怎么发生的

模型不会真的执行你的函数——它只会说「我要调这个函数,参数是这些」。整个过程分三步:

你  ──▶  模型:给你一个工具清单
模型 ──▶  你:我要调 get_weather(city="北京")
你  ──▶  模型:工具执行完了,结果是「晴,26℃」
模型 ──▶  你:最终回答(基于结果)

关键点:第 3 步是模型自己决定要不要调、调哪个。你只负责「把工具挂上」和「把结果送回去」。

2.3 第一个 Agent

新建 first_agent.py:

import json
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["LLM_API_KEY"],
    base_url=os.environ.get("LLM_BASE_URL", "https://api.openai.com/v1"),
)
CHAT_MODEL = os.environ.get("CHAT_MODEL", "gpt-4o-mini")

# ① 真正干活的函数。真实场景这里会请求天气 API
def get_weather(city: str) -> str:
    fake = {"北京": "晴,26℃", "上海": "多云,29℃", "广州": "雷阵雨,31℃"}
    return fake.get(city, f"没查到 {city} 的天气")

# ② 用 JSON Schema 告诉模型:这个工具叫什么、干什么、要什么参数
TOOLS = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "查询指定城市的当前天气。城市名用中文。",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "城市名,如 '北京'"}
            },
            "required": ["city"]
        }
    }
}]

messages = [{"role": "user", "content": "北京现在天气怎么样?"}]

# ③ 第一次请求:把工具清单给模型
resp = client.chat.completions.create(
    model=CHAT_MODEL,
    messages=messages,
    tools=TOOLS,
)
msg = resp.choices[0].message

# ④ 模型要调工具了
if msg.tool_calls:
    call = msg.tool_calls[0]
    args = json.loads(call.function.arguments)
    result = get_weather(**args)

    # ⑤ 把「模型的决定」和「工具的结果」都写回上下文
    messages.append(msg)
    messages.append({
        "role": "tool",
        "tool_call_id": call.id,
        "content": result,
    })

    # ⑥ 再问一次,这次它会拿结果回答
    resp = client.chat.completions.create(
        model=CHAT_MODEL,
        messages=messages,
        tools=TOOLS,
    )
    print(resp.choices[0].message.content)

运行:

python first_agent.py

你会看到(示意):

北京现在天气晴,26℃,适合外出。
代码 作用
get_weather() 真正干活的地方,你随便换
TOOLS 工具说明书,模型靠它决定调不调
msg.tool_calls 模型要调工具时才有;不需要工具时是 None
json.loads(call.function.arguments) 参数是 JSON 字符串,要解出来
role: "tool" + tool_call_id 工具结果的回传格式,id 必须对上
第二次请求 把结果送回去,模型才会给出最终答案

2.4 工具结果要写成给人看的句子

工具返回值是给模型看的,模型读不懂 {'code': 0, 'data': {...}} 这种结构。直接返回自然语言,它才知道怎么复述:

def get_weather(city: str) -> str:
    return f"{city}:晴,26℃,东南风 2 级"     # ✅ 模型能直接用
    # return {"city": city, "temp": 26}       # ❌ 模型得自己猜怎么转述

2.5 常见翻车现场

报错 / 现象 原因 修法
msg.tool_calls 一直是 None 模型不支持 function calling 换个支持工具调用的模型
模型调了工具但没收到结果 漏了 role: "tool" 那条消息 严格按 ⑤ 写回
报 tool_call_id 不匹配 手工拼的 id 对不上 用 call.id 原样回传
工具参数传错类型 Schema 里没写清类型 给每个参数写 type 和 description
模型调了个不存在的函数 工具清单里有重名或描述不清 检查 TOOLS 里的 name

2.6 动手改

  1. 给 TOOLS 再加一个 get_time(city) 工具(返回当前时间),问一句「北京几点 北京天气怎么样」,看它会不会两个都调
  2. 把 get_weather 的返回值改成字典,看模型回答会不会变得别扭
  3. 把 TOOLS 里的 description 删掉再跑一次,感受「工具说明写不清」的后果

2.7 小结

  • 模型不执行函数,它只要求你执行——工具调用是「模型说、你来干」
  • 工具挂上 tools,模型自己决定调不调、调哪个
  • 执行完必须把 assistant 消息和 role: "tool" 消息都写回上下文
  • 工具的 description 是写给模型看的说明书,比代码本身更重要
  • 工具返回值用自然语言,模型更好消化

下一章:Agent 的循环,让它自己转起来干多步的活 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。