AI Agent开发指南:用Function Calling构建智能助手

普通的 LLM 调用是“一问一答”:你问它答,它不能查数据库、不能调外部 API、不能执行代码。但真实世界的任务往往需要多步操作——比如“查一下北京明天天气,如果下雨就帮我订一把伞”。这就需要 AI Agent:让模型不仅能说话,还能自主决定调用哪些工具、按什么顺序调用,直到完成任务。

本文将从 Agent 的核心概念讲起,用 Function Calling 从零实现一个能查天气、算数学、查数据库的智能助手。读完你就能理解 LangChain Agent、AutoGPT 这类框架背后的本质。

一、什么是 AI Agent

Agent 的定义有很多版本,但本质就一句话:一个以 LLM 为大脑、能调用外部工具完成多步任务的系统。它和普通聊天机器人的核心区别在于“自主决策”。

一个 Agent 通常由四个部分组成:

二、Function Calling:Agent 的基石

Function Calling(函数调用)是让 LLM 能“调用工具”的标准机制。它的流程是:

  1. 你把可用工具的名称、描述、参数 schema 告诉模型。
  2. 模型判断是否需要调用工具,如果需要,返回一个结构化的工具调用请求(工具名 + 参数 JSON)。
  3. 你的代码执行这个工具,把结果回传给模型。
  4. 模型基于结果继续思考,要么再调一个工具,要么给出最终答案。

关键点:模型不会真的执行你的函数,它只是输出“我想调用这个函数,参数是这些”。真正的执行由你的代码完成。这种设计保证了安全性——模型无法直接访问你的数据库或发邮件。

三、定义工具:用 JSON Schema 描述

每个工具需要用 JSON Schema 描述清楚:名字、做什么、接受什么参数。描述越清晰,模型调用越准。

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "查询指定城市的实时天气。当用户问天气相关问题时调用。",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": { "type": "string", "description": "城市名,如'北京'" },
                    "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "温度单位" }
                },
                "required": ["city"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "calculate",
            "description": "计算一个数学表达式。当需要精确数值计算时调用,不要自己心算。",
            "parameters": {
                "type": "object",
                "properties": {
                    "expression": { "type": "string", "description": "合法的数学表达式,如 '12 * 8 + 5'" }
                },
                "required": ["expression"]
            }
        }
    }
]

写描述的两个技巧:一是说明什么时候该用(“当用户问天气时调用”),二是说明什么时候不该用(“不要自己心算”)。这能显著减少误调用。

四、实现工具函数

import json

# 工具的实际实现
def get_weather(city, unit="celsius"):
    # 实际项目里这里调用真实天气 API
    weather_db = {"北京": 28, "上海": 26, "广州": 31}
    temp = weather_db.get(city, 25)
    if unit == "fahrenheit":
        temp = temp * 9 / 5 + 32
    return json.dumps({"city": city, "temp": temp, "unit": unit}, ensure_ascii=False)

def calculate(expression):
    try:
        return str(eval(expression))
    except Exception as e:
        return f"计算失败: {e}"

# 工具名到函数的映射
tool_map = {"get_weather": get_weather, "calculate": calculate}

五、Agent 的核心循环

这是 Agent 最关键的部分:一个“思考-行动-观察”的循环。模型每次回复后,我们检查它是否要求调用工具;如果是,执行工具把结果送回去,让模型继续;如果模型给出最终答案(没有工具调用),循环结束。

from openai import OpenAI

client = OpenAI(api_key="sk-your-enlyai-key", base_url="https://enlyai.com/v1")

def run_agent(user_query, model="gpt-5.5", max_steps=8):
    messages = [
        {"role": "system", "content": "你是一个能调用工具的智能助手。遇到需要查天气或计算的问题,请调用对应工具,不要凭空猜测。"},
        {"role": "user", "content": user_query}
    ]

    for step in range(max_steps):
        resp = client.chat.completions.create(
            model=model,
            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_name = call.function.name
            fn_args = json.loads(call.function.arguments)
            print(f"[调用工具] {fn_name}({fn_args})")

            result = tool_map[fn_name](**fn_args)
            # 把工具结果作为 tool 角色消息回传
            messages.append({
                "role": "tool",
                "tool_call_id": call.id,
                "content": result
            })

    return "达到最大步数,任务未完成。"

# 测试:模型会先查天气,再综合回答
print(run_agent("北京和上海哪个更热?请给出具体温度。"))

运行这段代码,你会看到模型先调用两次 get_weather(北京、上海),拿到结果后对比给出“广州更热”的结论。这就是 Agent 的自主决策能力——你只给了任务,模型自己规划了执行路径。

六、多轮对话:让 Agent 有记忆

真实助手需要支持多轮对话,用户可能基于上一轮结果追问。只要把完整的 messages 历史保留下来传给模型即可:

conversation = [
    {"role": "system", "content": "你是智能助手,可调用工具。"}
]

def chat(user_input):
    conversation.append({"role": "user", "content": user_input})
    # 复用 run_agent 的循环逻辑,传入已有 conversation
    answer = run_agent_with_history(conversation)
    return answer

# 第一轮
print(chat("北京今天多少度?"))
# 第二轮,模型能记住上文
print(chat("那换成华氏度呢?"))  # 模型知道"那"指北京

注意控制历史长度:对话轮数多了,token 会膨胀。生产环境建议保留最近 10-20 轮,或用摘要压缩更早的历史。

七、ReAct 模式:思考过程可见

上面用的是原生 Function Calling。另一种经典模式是 ReAct(Reasoning + Acting):让模型在每一步先输出“Thought(思考)”,再输出“Action(行动)”。好处是推理过程透明,便于调试;缺点是更费 token。

2026 年主流模型(GPT-5.5、Claude Opus 4.8)的原生 Function Calling 已经足够稳定,新项目优先用 Function Calling;只有在模型不支持 function calling、或需要强可解释性时才用 ReAct。

八、生产环境最佳实践

  1. 限制最大步数:Agent 可能陷入“调用-失败-再调用”死循环,max_steps 必须设上限(通常 8-15)。
  2. 工具要做参数校验:模型可能传错参数类型,工具函数里要 try/except 并返回友好错误,而不是抛异常崩溃。
  3. 工具结果要简洁:返回大段 HTML 或超长文本会撑爆上下文,工具内部应做摘要或截断。
  4. 记录每一步:把 Thought、Action、Observation 全部记日志,出问题时能复盘模型为什么走偏。
  5. 危险工具要二次确认:发邮件、删数据、付款这类工具,不要让 Agent 自动执行,应返回“需确认”让用户拍板。
  6. 用便宜模型做简单决策:路由、参数提取用 GPT-5.4-mini,复杂规划才用 GPT-5.5,能省大量成本。

九、用 EnlyAI 统一接入多模型

Agent 开发中你往往要在多个模型间切换:规划用 Claude Opus 4.8(推理强),工具调用用 GPT-5.5(function calling 稳),简单判断用 GPT-5.4-mini(便宜)。如果分别对接各家平台,密钥和 SDK 管理很麻烦。通过 EnlyAI 统一接入,一个 key 调用所有模型

from openai import OpenAI

client = OpenAI(api_key="sk-your-enlyai-key", base_url="https://enlyai.com/v1")

# 同一套 Agent 代码,只改 model 即可切换底层模型
def adaptive_agent(query):
    # 简单问题用 mini 省钱,复杂问题用 GPT-5.5
    if len(query) < 20:
        return run_agent(query, model="gpt-5.4-mini")
    return run_agent(query, model="gpt-5.5")

# 也可以用 Claude 做规划
print(run_agent("帮我分析最近一周的销售数据趋势", model="claude-opus-4-8"))

统一接入还能实现故障转移:某个模型服务波动时,代码里改一个字符串就能切到备用模型,不用改 SDK、不用换密钥。

总结

AI Agent 的本质并不神秘:LLM 当大脑,Function Calling 当手脚,一个循环把“思考-行动-观察”串起来。本文从工具定义到核心循环给出了完整可运行的代码,你已经具备了自己实现一个 LangChain Agent 的能力——因为那些框架做的,无非是把这套循环封装得更优雅。

真正决定 Agent 好坏的,是工具描述写得清不清楚、循环控制稳不稳定、错误处理健不健壮。把这些细节打磨好,再配合 EnlyAI 统一接入多模型的能力,你就能构建出既聪明又省钱的智能助手。

想用一套代码调用所有大模型构建 Agent?

EnlyAI 提供 OpenAI 兼容的统一接口,一个 key 调用 GPT-5.5、Claude Opus 4.8、Gemini 3.5 Pro,原生支持 Function Calling,注册即送免费额度。

立即注册 EnlyAI →