乐于分享
好东西不私藏

让 AI 调用外部工具,该怎么设计?

让 AI 调用外部工具,该怎么设计?

让 AI 调用外部工具,该怎么设计?

一个"只会说不会做"的 AI

你做了一个 AI 助手,用户问它:"今天北京天气怎么样?"

AI 回答:"作为 AI,我无法获取实时天气信息。建议您查看天气预报网站或使用手机天气应用。"

用户又问:"我的订单到哪了?"

AI 回答:"我无法查询您的订单信息。请联系客服或登录官网查看。"

两次对话,两次"我不能"。

用户失望地离开了。你做了个只能聊天的 AI,不能查天气、不能查订单、不能调 API、不能做任何"实事"。

这不是 AI 的能力问题。大语言模型本身确实不能访问外部数据——它的知识来自训练数据,它不能上网、不能查数据库、不能调接口。

但你可以教它"用工具"。

这就是今天要聊的主题——Tool Use(工具调用),也叫 Function Calling。


Tool Use 是什么?

一句话:让 AI 决定"调用哪个工具"和"传什么参数",然后由你的代码去执行。

流程是这样的:

用户: "北京今天天气怎么样?"        ↓AI: 我需要调用 get_weather 工具,参数是 city="北京"        ↓你的代码: 调用天气 API,拿到结果 {"temp": 28"weather""晴"}        ↓AI: 北京今天天气晴朗,气温 28°C。

AI 不直接调用工具。 AI 只负责"决定"调用哪个工具、传什么参数。真正的执行由你的代码完成。AI 的角色是"决策者",你的代码是"执行者"。

这个设计非常优雅——AI 不需要有执行能力,只需要有"判断力"。


怎么告诉 AI 有哪些工具?

你需要给 AI 提供一份"工具说明书",告诉它每个工具的名称、用途和参数。

这份说明书的格式叫Tool Description,是一个 JSON Schema。

tools = [    {        "type""function",        "function": {            "name""get_weather",            "description""查询指定城市的当前天气信息,包括温度、天气状况、湿度",            "parameters": {                "type""object",                "properties": {                    "city": {                        "type""string",                        "description""城市名称,如'北京'、'上海'"                    }                },                "required": ["city"]            }        }    },    {        "type""function",        "function": {            "name""get_order_status",            "description""查询用户的订单状态,返回物流信息和预计送达时间",            "parameters": {                "type""object",                "properties": {                    "order_id": {                        "type""string",                        "description""订单号,如 ORD-20240101-001"                    }                },                "required": ["order_id"]            }        }    }]

关键要素:

  1. name:工具名称,要简洁明确。get_weather 比weather_tool 好。
  2. description:工具用途,这是 AI 决定是否调用的依据。写清楚"什么时候该用这个工具"。
  3. parameters:参数的 JSON Schema,包括类型、描述、是否必填。

description 写得好不好,直接影响 AI 的工具选择准确率。 下面展开说。


Tool Description 怎么写才好?

这是 Tool Use 最容易被忽视、也最影响效果的环节。

原则一:描述"什么时候用",而不是"怎么实现"

# 差:只描述了实现细节"description""调用天气 API 获取数据"# 好:描述了使用场景"description""当用户询问某个城市的天气、气温、是否下雨等问题时调用。只支持中国大陆城市。"

原则二:参数描述要包含格式和约束

# 差:参数描述太简单"order_id": {"type""string""description""订单号"}# 好:包含格式说明"order_id": {"type""string""description""订单号,格式为 ORD-YYYYMMDD-XXX,如 ORD-20240101-001"}

原则三:用 enum 限制选项

# 用 enum 限制 AI 的选择范围"unit": {    "type""string",    "enum": ["celsius""fahrenheit"],    "description""温度单位"}

原则四:在 description 中写明"不适用"的情况

"description": "查询订单物流状态。不支持查询退款进度,退款请用 get_refund_status 工具。"

这能有效防止 AI 调错工具。


完整实战:多工具 AI 助手

来看一个完整的实现——一个能查天气、查订单、查汇率的 AI 助手。

"""多工具 AI 助手pip install openai"""import jsonfrom openai import OpenAIclient = OpenAI()# ===== 第一步:定义工具 =====tools = [    {        "type""function",        "function": {            "name""get_weather",            "description""查询指定城市的当前天气。当用户问天气、气温、是否下雨时调用。只支持中国大陆城市。",            "parameters": {                "type""object",                "properties": {                    "city": {"type""string""description""城市名称,如'北京'、'上海'"}                },                "required": ["city"]            }        }    },    {        "type""function",        "function": {            "name""get_order_status",            "description""查询订单的物流状态和预计送达时间。需要用户提供订单号。",            "parameters": {                "type""object",                "properties": {                    "order_id": {"type""string""description""订单号,格式 ORD-YYYYMMDD-XXX"}                },                "required": ["order_id"]            }        }    },    {        "type""function",        "function": {            "name""convert_currency",            "description""货币汇率转换。支持 USD、EUR、CNY、JPY、GBP。",            "parameters": {                "type""object",                "properties": {                    "amount": {"type""number""description""金额"},                    "from_currency": {"type""string""enum": ["USD""EUR""CNY""JPY""GBP"], "description""源货币"},                    "to_currency": {"type""string""enum": ["USD""EUR""CNY""JPY""GBP"], "description""目标货币"}                },                "required": ["amount""from_currency""to_currency"]            }        }    }]# ===== 第二步:实现工具函数 =====def get_weather(city):    """模拟天气 API"""    mock_data = {        "北京": {"temp"28"weather""晴""humidity"45},        "上海": {"temp"31"weather""多云""humidity"72},    }    return mock_data.get(city, {"error"f"暂不支持 {city} 的天气查询"})def get_order_status(order_id):    """模拟订单 API"""    return {        "order_id": order_id,        "status""配送中",        "carrier""顺丰速运",        "eta""2024-01-15"    }def convert_currency(amount, from_currency, to_currency):    """模拟汇率 API"""    rates = {"USD"1"CNY"7.2"EUR"0.92"JPY"148"GBP"0.79}    result = amount / rates[from_currency] * rates[to_currency]    return {"amount"round(result, 2), "from": from_currency, "to": to_currency}# 工具映射表tool_map = {    "get_weather": get_weather,    "get_order_status": get_order_status,    "convert_currency": convert_currency,}# ===== 第三步:对话循环 =====def chat(user_input, history=None):    if history is None:        history = [{"role""system""content""你是一个贴心的 AI 助手,可以帮用户查天气、查订单、算汇率。"}]    history.append({"role""user""content": user_input})    # 第一轮:让 AI 决定是否调用工具    response = client.chat.completions.create(        model="gpt-4o-mini",        messages=history,        tools=tools,        tool_choice="auto"  # 让 AI 自动决定是否调用    )    msg = response.choices[0].message    # 如果 AI 决定调用工具    if msg.tool_calls:        history.append(msg)        for tool_call in msg.tool_calls:            func_name = tool_call.function.name            func_args = json.loads(tool_call.function.arguments)            # 执行工具            result = tool_map[func_name](**func_args)            # 把工具结果返回给 AI            history.append({                "role""tool",                "tool_call_id": tool_call.id,                "content": json.dumps(result, ensure_ascii=False)            })        # 第二轮:AI 基于工具结果生成最终回答        final_response = client.chat.completions.create(            model="gpt-4o-mini",            messages=history        )        answer = final_response.choices[0].message.content    else:        # AI 不需要调用工具,直接回答        answer = msg.content    history.append({"role""assistant""content": answer})    return answer# ===== 测试 =====print(chat("北京今天天气怎么样?"))# 输出:北京今天天气晴朗,气温 28°C,湿度 45%,适合出行。print(chat("帮我查一下订单 ORD-20240101-001 到哪了"))# 输出:您的订单 ORD-20240101-001 目前正在配送中,由顺丰速运承运,预计 1 月 15 日送达。print(chat("100 美元换成人民币是多少?"))# 输出:100 美元约合 720 人民币。

整个流程分两步:

  1. AI 根据用户问题 + 工具列表,决定调用哪个工具、传什么参数
  2. 你的代码执行工具,把结果返回给 AI,AI 生成最终回答

工具太多反而效果差?

一个常见的误区:给 AI 注册越多工具越好。

事实是:工具数量超过 10-15 个时,AI 的工具选择准确率会明显下降。

原因很简单——AI 需要在所有工具中"挑选"最合适的那个。工具越多,干扰越多,选错的概率越大。

解决方案一:分层工具

把工具分成"类别",先选类别,再选具体工具。

# 第一层:AI 先选类别categories = [    {"name""weather""description""天气相关查询"},    {"name""order""description""订单和物流查询"},    {"name""finance""description""金融和汇率查询"},]# 第二层:根据类别加载对应工具def get_tools_for_category(category):    tool_sets = {        "weather": [get_weather_tool, get_forecast_tool],        "order": [get_order_status_tool, get_order_detail_tool, cancel_order_tool],        "finance": [convert_currency_tool, get_stock_price_tool],    }    return tool_sets.get(category, [])

解决方案二:动态工具加载

根据用户的意图,动态决定加载哪些工具。

def detect_intent(user_input):    """简单的意图检测"""    if any(w in user_input for w in ["天气""气温""下雨"]):        return "weather"    elif any(w in user_input for w in ["订单""物流""快递"]):        return "order"    elif any(w in user_input for w in ["汇率""换算""美元"]):        return "finance"    return "general"# 根据意图加载工具intent = detect_intent(user_input)tools = get_tools_for_category(intent)

解决方案三:工具描述优化

确保每个工具的 description 足够精确,让 AI 能快速判断是否适用。模糊的描述是工具选择错误的主要原因。


生产环境的注意事项

1. 工具执行要有超时和重试。 外部 API 可能慢或挂,不能让 AI 一直等。

import httpxdef call_api_with_timeout(url, timeout=5):    try:        resp = httpx.get(url, timeout=timeout)        return resp.json()    except httpx.TimeoutException:        return {"error""请求超时,请稍后重试"}    except Exception as e:        return {"error"f"请求失败: {str(e)}"}

2. 工具结果要做格式校验。 AI 传来的参数可能不符合预期,要验证后再执行。

3. 敏感操作要加确认。 比如"取消订单"这种操作,不能让 AI 直接执行,要让用户确认。

4. 记录工具调用日志。 每次调用什么工具、传了什么参数、返回了什么结果,都要记录。出了问题好排查。


行动清单

  1. 从 3 个工具开始:先选最核心的 3 个工具,跑通流程后再逐步扩展。

  2. 把 description 当产品文档写:给每个工具写清楚"什么时候用"和"什么时候不用",这是影响准确率的第一因素。

  3. 加参数校验:AI 传来的参数不一定合法,执行前要验证。

  4. 监控工具调用成功率:记录每次调用是成功还是失败,失败原因是什么。这是优化工具描述的数据基础。


下篇预告

Tool Use 让 AI 能"做事"了。但你可能注意到一个问题——AI 的输出是自然语言,格式不稳定。

你让它返回 JSON,它有时候返回 JSON,有时候返回一段解释文字,有时候返回一个带注释的代码块。你写了解析逻辑,三天两头报错。

下一篇,我们来解决这个痛点——《怎么让 AI 稳定输出 JSON 并直接入库?》