Function Calling完全指南:让AI真正”能干活”的关键技术

6次阅读
没有评论

一、 开场:一个让我兴奋的技术趋势

大家好,我是老金。

最近几个月,我明显感觉到一个技术趋势正在加速:

Function Calling(函数调用)正在成为AI应用的标准能力。

OpenAI、Anthropic、Google、百度、阿里……几乎所有大模型厂商都在推进这个能力。

为什么?因为Function Calling是AI Agent从”能聊天”进化到”能干活”的关键桥梁

今天这篇文章,我想系统性地聊聊:Function Calling的技术原理、最佳实践,以及那些你可能遇到的坑

二、 什么是Function Calling?

2.1 核心概念

Function Calling是一种让LLM输出结构化函数调用的机制。

简单说:

  • 你告诉AI:”这里有一些可用的工具(函数)”
  • AI分析用户需求,决定调用哪个函数
  • AI输出结构化的函数调用(JSON格式)
  • 你执行函数,把结果返回给AI
  • AI基于结果继续回答用户

2.2 工作流程图

用户问题
    ↓
LLM分析 → 决定调用哪个函数
    ↓
输出结构化JSON:{"function": "get_weather", "args": {"city": "北京"}}
    ↓
你的代码执行函数调用
    ↓
返回结果给LLM
    ↓
LLM生成最终回答

2.3 为什么需要Function Calling?

没有Function Calling之前,让AI”调用工具”是这样的:

用户:帮我查一下北京今天天气

AI:好的,让我为你查询北京今天的天气... (AI输出了一堆自然语言描述,你需要用正则匹配提取关键信息)

有了Function Calling:

用户:帮我查一下北京今天天气

AI输出(结构化JSON): { "name": "get_weather", "arguments": "{"city": "北京", "date": "今天"}" }

你的代码:直接解析JSON,调用天气API

结构化输出 = 可靠的程序处理

三、 主流模型的Function Calling实现

3.1 OpenAI

import openai

定义可用函数

functions = [ { "name": "get_weather", "description": "获取指定城市的天气信息", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,如:北京、上海" }, "date": { "type": "string", "description": "日期,如:今天、明天、2024-01-01" } }, "required": ["city"] } }, { "name": "send_email", "description": "发送邮件", "parameters": { "type": "object", "properties": { "to": {"type": "string", "description": "收件人邮箱"}, "subject": {"type": "string", "description": "邮件主题"}, "body": {"type": "string", "description": "邮件正文"} }, "required": ["to", "subject", "body"] } } ]

调用API

response = openai.ChatCompletion.create( model="gpt-4", messages=[ {"role": "user", "content": "帮我查一下北京今天天气"} ], functions=functions, function_call="auto" # 让模型自己决定是否调用函数 )

处理函数调用

message = response.choices[0].message if message.get("function_call"): function_name = message["function_call"]["name"] arguments = json.loads(message["function_call"]["arguments"])

if function_name == "get_weather":
    result = get_weather(arguments["city"], arguments.get("date"))

    # 将结果返回给模型
    second_response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[
            {"role": "user", "content": "帮我查一下北京今天天气"},
            message,
            {
                "role": "function",
                "name": "get_weather",
                "content": json.dumps(result, ensure_ascii=False)
            }
        ]
    )
    print(second_response.choices[0].message.content)

3.2 Anthropic Claude

import anthropic

client = anthropic.Anthropic()

定义工具

tools = [ { "name": "get_weather", "description": "获取指定城市的天气信息", "input_schema": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名称"}, "date": {"type": "string", "description": "日期"} }, "required": ["city"] } } ]

调用API

response = client.messages.create( model="claude-3-opus-20240229", max_tokens=1024, messages=[ {"role": "user", "content": "帮我查一下北京今天天气"} ], tools=tools )

处理工具调用

for block in response.content: if block.type == "tool_use": if block.name == "get_weather": result = get_weather(**block.input)

        # 返回结果
        second_response = client.messages.create(
            model="claude-3-opus-20240229",
            max_tokens=1024,
            messages=[
                {"role": "user", "content": "帮我查一下北京今天天气"},
                {"role": "assistant", "content": response.content},
                {
                    "role": "user",
                    "content": [
                        {
                            "type": "tool_result",
                            "tool_use_id": block.id,
                            "content": json.dumps(result)
                        }
                    ]
                }
            ],
            tools=tools
        )
        print(second_response.content[0].text)

3.3 国产模型(以通义千问为例)

from dashscope import Generation

定义函数

functions = [ { "name": "get_weather", "description": "获取天气信息", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市"} }, "required": ["city"] } } ]

response = Generation.call( model="qwen-max", messages=[ {"role": "user", "content": "帮我查一下北京今天天气"} ], functions=functions )

处理逻辑类似...

四、 最佳实践

4.1 函数定义的艺术

好的函数定义能让AI更准确地调用,减少错误。

原则1:描述要具体

❌ 差的定义:
"city": {"type": "string"}

✅ 好的定义: "city": { "type": "string", "description": "城市名称,支持中文和拼音,如:北京、beijing、上海" }

原则2:使用枚举限制

"unit": {
    "type": "string",
    "enum": ["celsius", "fahrenheit"],
    "description": "温度单位"
}

原则3:设置合理的required

❌ 把所有参数都设为required
"required": ["city", "date", "unit", "detail_level"]

✅ 只把必须的参数设为required "required": ["city"]

4.2 错误处理

async def execute_function_call(function_name, arguments):
    """执行函数调用,带完善的错误处理"""
    try:
        # 1. 检查函数是否存在
        if function_name not in AVAILABLE_FUNCTIONS:
            return {
                "error": f"未知函数:{function_name}",
                "available_functions": list(AVAILABLE_FUNCTIONS.keys())
            }
    # 2. 验证参数
    func = AVAILABLE_FUNCTIONS[function_name]
    validate_arguments(func, arguments)

    # 3. 执行函数
    result = await func(**arguments)

    # 4. 验证返回值
    if result is None:
        return {"error": "函数执行返回空值"}

    return result

except ValidationError as e:
    return {"error": f"参数验证失败:{str(e)}"}
except Exception as e:
    logger.exception(f"函数 {function_name} 执行异常")
    return {"error": f"函数执行异常:{str(e)}"}

4.3 多函数调用处理

有时候AI会一次性调用多个函数:

async def handle_multiple_calls(tool_calls):
    """处理多个函数调用"""
    results = []
# 并行执行
tasks = []
for call in tool_calls:
    task = execute_function_call(call.name, call.arguments)
    tasks.append(task)

results = await asyncio.gather(*tasks, return_exceptions=True)

# 组织返回结果
function_results = []
for call, result in zip(tool_calls, results):
    if isinstance(result, Exception):
        function_results.append({
            "tool_call_id": call.id,
            "error": str(result)
        })
    else:
        function_results.append({
            "tool_call_id": call.id,
            "result": result
        })

return function_results

4.4 Token优化

函数定义会占用Token,需要注意优化:

class FunctionRegistry:
    def __init__(self):
        self.all_functions = {}  # 所有可用函数
        self.common_functions = []  # 常用函数(始终包含)
def get_functions_for_context(self, user_message, max_tokens=2000):
    """根据用户消息选择相关函数,控制Token消耗"""
    # 1. 始终包含常用函数
    selected = self.common_functions.copy()
    current_tokens = count_tokens(json.dumps(selected))

    # 2. 根据用户消息选择相关函数
    for name, func in self.all_functions.items():
        if name in [f["name"] for f in selected]:
            continue

        # 简单的相关性判断(可以换成向量检索)
        if self._is_relevant(user_message, func):
            func_tokens = count_tokens(json.dumps(func))
            if current_tokens + func_tokens <= max_tokens:
                selected.append(func)
                current_tokens += func_tokens

    return selected

def _is_relevant(self, message, func):
    """判断函数是否与消息相关"""
    keywords = func.get("description", "").split()
    return any(kw in message for kw in keywords)

五、 常见问题与解决方案

5.1 AI不调用函数

原因

  • 函数描述不够清晰
  • 用户问题与函数不匹配
  • 模型能力限制

解决方案

# 强制调用特定函数
response = openai.ChatCompletion.create(
    model="gpt-4",
    messages=[...],
    functions=functions,
    function_call={"name": "get_weather"}  # 强制调用
)

或者在系统提示中强调

system_prompt = """ 你可以使用以下工具:

  • get_weather: 获取天气信息

当用户询问天气相关问题时,请务必调用get_weather函数。 """

5.2 参数解析错误

原因:AI输出的JSON格式不正确。

解决方案

import json
from json_repair import repair_json

def parse_arguments(arguments_str): """解析函数参数,带修复功能""" try: return json.loads(arguments_str) except json.JSONDecodeError:

尝试修复JSON

    try:
        repaired = repair_json(arguments_str)
        return json.loads(repaired)
    except:
        return None

使用

arguments = parse_arguments(message["function_call"]["arguments"])
if arguments is None:

让AI重新输出

return "参数格式错误,请重新生成"

5.3 函数执行超时

async def execute_with_timeout(func, args, timeout=30):
    """带超时的函数执行"""
    try:
        result = await asyncio.wait_for(
            func(**args),
            timeout=timeout
        )
        return result
    except asyncio.TimeoutError:
        return {"error": f"函数执行超时({timeout}秒)"}

六、 写在最后

Function Calling是AI Agent能力的基石。

掌握它,你就能让AI从”聊天机器人”进化为”能干活的智能助手”。

最后分享几点心得:

  1. 定义要清晰:好的函数定义是成功的一半
  2. 错误处理要完善:AI不一定每次都正确,要有兜底方案
  3. 注意Token消耗:函数定义会占用上下文窗口
  4. 持续优化:根据实际调用情况优化函数设计

如果你在实践Function Calling时遇到问题,欢迎在评论区交流。

我是技术老金,我们下期见!


📌 往期精彩回顾

正文完
 0
技术老金
版权声明:本站原创文章,由 技术老金 于2026-03-29发表,共计6645字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)