首页/知识库/工具调用:Function Calling 实战

工具调用:Function Calling 实战

✍️ AI Master📅 创建 2026-04-12📖 18 min 阅读
💡

文章摘要

让大模型调用外部工具,扩展 Agent 的能力边界

1为什么需要工具调用:大模型的能力边界

大语言模型虽然强大,但本质上是一个「文本到文本」的函数。它接收文本输入,生成文本输出,所有知识都固化在训练时的参数中。这意味着模型存在三个根本性局限:无法获取训练数据之后的最新信息(知识截止)、无法执行实际操作(如发送邮件、查询数据库)、无法进行精确计算(大模型做数学运算经常出错)。

Function Calling 的核心思想是让模型成为「大脑」而非「全栈工人」。模型不需要自己知道天气数据——它只需要决定「我应该调用天气查询工具」并构造正确的参数。这种分工让各系统各司其职:模型负责意图理解和决策,外部工具负责执行和数据获取。2023年6月OpenAI首次推出Function Calling API后,Anthropic、Google等厂商纷纷跟进,这已经成为构建AI Agent的标准范式。

python
# 大模型的三个根本性局限
# 1. 知识截止:无法获取训练后的新信息
print(model.predict("2026年世界杯冠军是谁?"))
# → 模型不知道,因为训练数据只到2024年

# 2. 无法执行操作:不能实际发邮件
print(model.predict("帮我给张三发一封邮件"))
# → 模型只能生成邮件草稿,无法真正发送

# 3. 数学计算不准
print(model.predict("2847 × 9361 = ?"))
# → 经常给出错误答案,不如计算器可靠
python
# Function Calling 的解决方案:让模型决定调用工具
def handle_user_query(user_input: str) -> str:
    """增强型对话处理:模型决定何时调用工具"""
    # 第一步:让模型判断是否需要工具调用
    response = llm.generate_with_tools(
        prompt=user_input,
        tools=[get_weather, send_email, calculate]
    )
    
    if response.tool_call:
        # 第二步:执行工具获取真实结果
        result = execute_tool(response.tool_call)
        # 第三步:将结果反馈给模型生成最终回答
        return llm.generate(user_input, tool_result=result)
    else:
        return response.text  # 普通对话直接回复
能力纯大模型Function Calling说明

实时信息

❌ 知识截止

✅ 通过API获取

新闻、天气、股票

外部操作

❌ 无法执行

✅ 调用工具执行

发邮件、写文件

精确计算

❌ 经常出错

✅ 计算器/代码

数学、数据处理

知识问答

✅ 训练知识

✅ 训练知识

历史、常识

意图理解

✅ 强项

✅ 强项

理解用户需求

核心思维转变:模型不是「什么都知道」的百科全书,而是「知道该问谁」的调度中心。

不要试图让大模型做精确计算或实时查询,这违背了工具调用的设计初衷。

2OpenAI Function Calling API 深入解析

OpenAI的Function Calling API通过一套结构化的协议,让模型能够在对话过程中声明对工具的调用需求。其工作流程分为三个清晰的阶段:第一阶段,开发者在API请求中通过tools参数定义可用工具的JSON Schema,包括工具名称、描述和参数定义;第二阶段,模型根据用户输入自主决定是否调用工具以及调用哪个工具,并返回一个包含tool_calls结构的响应;第三阶段,开发者执行实际的工具调用,将结果以tool role消息的形式追加到对话历史中,让模型基于工具返回的真实数据生成最终回答。

理解这个三阶段循环至关重要——它不是一次性的调用,而是一个可迭代的循环。模型在收到工具执行结果后,仍然可以继续发起新的工具调用。比如用户问「北京和上海哪个城市人口更多」,模型可能需要先调用人口查询API获取北京的数据,再调用一次获取上海的数据,最后才能进行比较。OpenAI的API设计天然支持这种多轮工具调用模式。

python
import openai

client = openai.OpenAI()

# 第一阶段:定义工具并发起请求
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "北京今天天气怎么样?"}],
    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"]
            }
        }
    }]
)
python
# 第二阶段:解析模型的调用决策
tool_call = response.choices[0].message.tool_calls[0]
func_name = tool_call.function.name  # "get_weather"
func_args = json.loads(tool_call.function.arguments)
# → {"city": "北京", "unit": "celsius"}

# 第三阶段:执行工具并将结果注入对话
weather_result = get_weather_api(**func_args)
messages = [
    {"role": "user", "content": "北京今天天气怎么样?"},
    response.choices[0].message,  # 模型的tool_call响应
    {
        "role": "tool",
        "tool_call_id": tool_call.id,
        "content": json.dumps(weather_result)
    }
]

# 让模型基于工具结果生成最终回答
final = client.chat.completions.create(
    model="gpt-4o", messages=messages
)
print(final.choices[0].message.content)
消息角色用途必须字段可选字段

system

设定系统行为

content

name

user

用户输入

content

name

assistant

模型响应

content 或 tool_calls

name

refusal

tool

工具返回结果

content, tool_call_id

name

始终在tools参数中定义所有可用工具,即使本次对话可能用不到——模型需要完整的工具清单来做决策。

tool role消息中的tool_call_id必须与模型返回的tool_call.id完全一致,否则API会报错。

3工具描述与 Schema 设计:决定模型能否正确调用

工具定义的质量直接决定了模型调用的准确率。Function Calling不是魔法——模型完全依赖你提供的JSON Schema来理解工具的用途、参数的含义和约束条件。一个描述模糊的工具定义会导致模型传入错误的参数类型、遗漏必填参数,甚至完全忽略这个工具。

设计优秀的工具Schema需要遵循几个关键原则:工具名称应该是动词短语(如get_weather而非weather),清晰表达工具的行为;description字段要用一句话说明工具做什么、什么时候用,避免过于笼统;每个参数的description要具体到取值范围和格式要求;对于枚举类型参数,务必列出所有合法值;必填参数只声明那些没有合理默认值的参数,降低模型调用门槛。

实践中最常见的错误是参数描述不够精确。比如将参数描述写为「城市名称」,模型可能传入「北京」、「北京市」、「beijing」等各种变体。更好的做法是「城市中文名,如北京、上海、广州」,甚至加上格式校验和归一化处理。

json
// 好的工具定义示例
{
  "name": "search_database",
  "description": "从用户数据库中查询信息,适用于需要检索用户资料、订单记录或统计数据的场景",
  "parameters": {
    "type": "object",
    "properties": {
      "query_type": {
        "type": "string",
        "enum": ["user_profile", "order_history", "statistics"],
        "description": "查询类型:user_profile查用户资料,order_history查订单,statistics查统计数据"
      },
      "user_id": {
        "type": "string",
        "description": "用户ID,格式为UUID,如 '550e8400-e29b-41d4-a716-446655440000'"
      },
      "date_range": {
        "type": "string",
        "description": "日期范围,格式 YYYY-MM-DD 至 YYYY-MM-DD,如 '2024-01-012024-12-31'"
      }
    },
    "required": ["query_type", "user_id"]
  }
}
python
# 使用 Pydantic 定义工具 Schema(推荐做法)
from pydantic import BaseModel, Field
from enum import Enum

class QueryType(str, Enum):
    USER_PROFILE = "user_profile"
    ORDER_HISTORY = "order_history"
    STATISTICS = "statistics"

class SearchDatabaseArgs(BaseModel):
    query_type: QueryType = Field(
        description="查询类型:user_profile查用户资料,order_history查订单,statistics查统计数据"
    )
    user_id: str = Field(
        description="用户ID,格式为UUID"
    )
    date_range: str | None = Field(
        default=None,
        description="日期范围,格式 YYYY-MM-DD 至 YYYY-MM-DD"
    )

# 自动转换为 OpenAI 格式
schema = SearchDatabaseArgs.model_json_schema()
tool_def = {
    "type": "function",
    "function": {
        "name": "search_database",
        "description": "从用户数据库中查询信息",
        "parameters": schema
    }
}
设计原则好的做法坏的做法影响

工具命名

get_weather(动词开头)

weather(名词)

模型不理解行为

参数描述

温度单位,可选celsius/fahrenheit

单位

模型传入非法值

枚举值

列出所有合法选项

不写enum约束

模型自由发挥

必填声明

仅声明无默认值的参数

所有参数都required

调用成功率下降

类型声明

精确类型:string+pattern

仅声明string

格式错误

使用 Pydantic 定义工具参数,既能获得自动类型校验,又能直接转换为 OpenAI 所需的 JSON Schema,一举两得。

不要在参数描述中包含代码示例或过长的说明,模型会提取这些内容作为参数值传入。

4多工具选择与路由:让模型成为聪明的调度员

当系统中只有一个工具时,问题很简单——模型只需要决定「调不调用」。但当可用工具从2个增加到10个甚至更多时,模型面临着真正的挑战:从众多工具中选出最合适的一个,并且传入正确的参数。这就是多工具路由问题。

OpenAI的模型在设计时就考虑了多工具场景。当你传入多个工具定义时,模型会基于用户输入的语义和工具的描述进行匹配选择。但模型的选择质量高度依赖于工具描述的区分度——如果两个工具的description过于相似,模型可能会混淆它们。比如同时定义了get_weather和get_forecast,如果描述不够清晰地区分「当前天气」和「未来预报」,模型在面对「明天天气」时可能选错工具。

解决多工具路由问题的策略包括:为每个工具提供明确的使用场景说明;在工具description中加入排除说明(「不适用于查询历史天气数据」);按功能分组管理工具;对于高度相似的工具,考虑合并为一个工具并通过参数区分行为。

python
# 多工具定义与路由示例
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "获取指定城市当前的实时天气状况。不适用于查询历史天气或未来天气预报。",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "城市名称"},
                },
                "required": ["city"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_weather_forecast",
            "description": "获取指定城市未来7天的天气预报。不适用于查询当前实时天气。",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "城市名称"},
                    "days": {"type": "integer", "description": "预报天数,1-7"},
                },
                "required": ["city", "days"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_historical_weather",
            "description": "查询指定日期的历史天气记录。不适用于查询当前或未来天气。",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "城市名称"},
                    "date": {"type": "string", "description": "日期,格式YYYY-MM-DD"},
                },
                "required": ["city", "date"]
            }
        }
    }
]
python
# 工具路由质量检查
def evaluate_tool_selection(user_input: str, 
                            selected_tool: str,
                            expected_tool: str) -> dict:
    """评估模型的工具选择是否正确"""
    return {
        "input": user_input,
        "selected": selected_tool,
        "expected": expected_tool,
        "correct": selected_tool == expected_tool,
    }

# 测试用例
test_cases = [
    ("北京现在多少度?", "get_current_weather"),
    ("下周去上海要带伞吗", "get_weather_forecast"),
    ("去年今天北京下雪了吗", "get_historical_weather"),
    ("深圳明天和后天天气", "get_weather_forecast"),
]

for query, expected in test_cases:
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": query}],
        tools=tools
    )
    selected = response.choices[0].message.tool_calls[0].function.name
    result = evaluate_tool_selection(query, selected, expected)
    print(f"{'✅' if result['correct'] else '❌'} {query} → {selected}")
路由策略适用场景工具数量准确率

直接多工具传入

工具少于5个,功能差异明显

1-5

~90%

分组工具定义

工具5-15个,可按功能分组

5-15

~85%

两阶段路由

工具超过15个,先分类再选择

15+

~95%

Few-shot示例

工具描述难以区分时

任意

~92%

在工具description中加入「不适用于...」的排除说明,能显著提升模型的路由准确率。

一次传入超过20个工具时,模型的选择质量会明显下降,建议使用两阶段路由策略。

5工具执行与结果注入:从调用到反馈的完整链路

工具调用的执行阶段是将模型的意图转化为实际结果的关键环节。这个阶段看似简单——解析参数、调用函数、返回结果——但其中隐藏着许多工程细节需要考虑。首先是参数验证:模型生成的参数虽然通常符合Schema定义,但仍可能出现边界情况,比如传入不存在的城市名或超出范围的数值。其次是错误处理:外部API可能超时、返回异常格式或完全不可用,你的代码需要优雅地处理这些失败场景。

结果注入环节同样重要。工具返回的数据需要以模型能够理解的格式传递回去。如果原始数据过于庞大(比如搜索返回100条结果),直接注入会超出上下文窗口,也会让模型难以提取关键信息。正确的做法是对工具结果进行适当的格式化和摘要,只保留模型生成回答所需的关键信息。比如天气API可能返回温度、湿度、风速、气压、紫外线指数等十几个字段,但如果用户只问了温度,只需注入温度相关的数据即可。

python
import requests
from typing import Any

def execute_tool_call(tool_call) -> str:
    """执行工具调用并返回结果字符串"""
    func_name = tool_call.function.name
    args = json.loads(tool_call.function.arguments)
    
    # 参数验证
    if func_name == "get_weather":
        city = args.get("city", "")
        if not city:
            return json.dumps({"error": "缺少城市参数"})
        
        # 执行 + 错误处理
        try:
            resp = requests.get(
                f"https://api.weather.com/v1/{city}",
                timeout=5
            )
            resp.raise_for_status()
            data = resp.json()
            # 结果格式化:只保留关键字段
            return json.dumps({
                "temperature": data["temp"],
                "description": data["description"],
                "humidity": data["humidity"]
            }, ensure_ascii=False)
        except requests.Timeout:
            return json.dumps({"error": "天气服务超时,请稍后重试"})
        except requests.HTTPError as e:
            return json.dumps({"error": f"天气服务错误: {e}"})
python
# 完整的多轮工具调用循环
def chat_with_tools(user_message: str, 
                    tools: list,
                    tool_registry: dict,
                    max_tool_calls: int = 5) -> str:
    """支持多轮工具调用的完整对话循环"""
    messages = [{"role": "user", "content": user_message}]
    
    for _ in range(max_tool_calls):
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=tools
        )
        msg = response.choices[0].message
        
        if not msg.tool_calls:
            return msg.content  # 模型决定不再调用工具
        
        # 执行所有工具调用
        messages.append(msg)
        for tc in msg.tool_calls:
            func_name = tc.function.name
            if func_name in tool_registry:
                result = tool_registry[func_name](tc)
            else:
                result = json.dumps({"error": f"未知工具: {func_name}"})
            
            messages.append({
                "role": "tool",
                "tool_call_id": tc.id,
                "content": result
            })
    
    return "已达到最大工具调用次数"
错误类型处理策略返回给模型用户体验

参数错误

验证参数,返回错误说明

参数校验错误信息

模型修正参数重试

API超时

设置timeout,返回超时提示

服务暂时不可用

模型告知用户稍后

API限流

捕获429,返回限流信息

请求过于频繁

模型建议降低频率

数据过大

截断/摘要后返回

关键信息摘要

模型基于摘要回答

工具不存在

检查registry,返回提示

未知工具错误

模型选择其他工具

工具结果注入前务必进行格式化和精简——模型不需要看到完整的API原始响应,只需要与用户问题相关的关键信息。

永远不要将未处理的异常堆栈直接注入对话,这会污染上下文并可能导致模型产生混乱的回复。

6安全与沙箱机制:防止工具调用带来的风险

赋予模型调用外部工具的能力,本质上是赋予了它执行代码和操作外部系统的权限。这带来了显著的安全风险:如果模型被恶意提示注入攻击(Prompt Injection),攻击者可能诱导模型调用危险工具,比如删除文件、发送敏感数据或执行系统命令。因此,工具调用的安全防护不是可选项,而是必须实现的底线。

安全防护需要从多个层面构建:在工具层面,实现严格的参数校验和权限控制,确保每次调用都符合预期范围;在执行层面,使用沙箱环境隔离工具执行,限制网络访问和文件系统操作;在审计层面,记录所有工具调用的完整日志,包括调用时间、参数、结果和执行者身份;在业务层面,对敏感操作(如删除、转账、发送消息)增加人工确认环节,不让模型全自动执行。

另一个重要但常被忽视的风险是数据泄露。模型可能将工具返回的敏感数据(如用户个人信息、内部统计数据)传递给不应该看到这些信息的用户。解决方案是实现数据分级和脱敏机制——在工具结果注入对话之前,根据当前用户的权限级别过滤敏感字段。

python
# 安全工具执行器:参数校验 + 权限控制 + 审计日志
import logging
from functools import wraps

logger = logging.getLogger("tool_executor")

def safe_tool_executor(func):
    """装饰器:为工具函数添加安全防护"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        tool_name = func.__name__
        logger.info(f"[TOOL_CALL] {tool_name} args={args} kwargs={kwargs}")
        
        # 参数校验
        validated = validate_params(tool_name, args, kwargs)
        if not validated["valid"]:
            logger.warning(f"[BLOCKED] {tool_name}: {validated['reason']}")
            return {"error": f"参数校验失败: {validated['reason']}"}
        
        # 权限检查
        if not check_permission(tool_name, kwargs.get("user_id")):
            logger.warning(f"[DENIED] {tool_name} user={kwargs.get('user_id')}")
            return {"error": "权限不足,无法执行此操作"}
        
        # 敏感操作需要二次确认
        if is_sensitive_operation(tool_name):
            if not request_confirmation(tool_name, kwargs):
                return {"error": "操作已取消:需要用户确认"}
        
        try:
            result = func(*args, **kwargs)
            logger.info(f"[SUCCESS] {tool_name}")
            return result
        except Exception as e:
            logger.error(f"[ERROR] {tool_name}: {e}")
            return {"error": "工具执行失败"}
    
    return wrapper
python
# 数据脱敏:根据用户权限过滤工具返回结果
def sanitize_tool_result(tool_name: str, 
                         result: dict, 
                         user_role: str) -> dict:
    """根据用户角色脱敏工具返回数据"""
    # 定义每个工具的字段权限级别
    field_permissions = {
        "get_user_profile": {
            "name": "all",
            "email": "authenticated",
            "phone": "admin",
            "address": "admin",
            "id_number": "admin",
            "password_hash": "never",  # 永远不返回
        },
        "search_database": {
            "order_id": "all",
            "amount": "authenticated",
            "payment_method": "admin",
            "card_last_four": "admin",
        }
    }
    
    if tool_name not in field_permissions:
        return result  # 未定义的工具不过滤
    
    rules = field_permissions[tool_name]
    sanitized = {}
    
    for field, value in result.items():
        required_role = rules.get(field, "never")
        role_levels = ["all", "authenticated", "admin"]
        
        if user_role == "never":
            continue  # 永远不返回
        if required_role == "all":
            sanitized[field] = value
        elif role_levels.index(user_role) >= role_levels.index(required_role):
            sanitized[field] = value
    
    return sanitized
安全层防护措施防护目标实现难度

参数校验

类型/范围/格式检查

防止非法参数

⭐ 简单

权限控制

RBAC角色权限验证

越权操作

⭐⭐ 中等

沙箱执行

容器/进程隔离

系统级攻击

⭐⭐⭐ 困难

数据脱敏

按角色过滤字段

数据泄露

⭐⭐ 中等

审计日志

全量记录调用链

事后追溯

⭐ 简单

人工确认

敏感操作需审批

误操作/恶意调用

⭐⭐ 中等

永远不要信任模型的参数输入——即使模型通常表现良好,也必须对所有工具参数进行服务端校验。

Prompt Injection攻击可能让模型绕过你的安全逻辑,所有敏感操作必须在代码层面强制校验,不能依赖模型的自我约束。

7实战:天气查询 + 计算器 + 搜索 Agent

现在我们将前面学到的知识整合起来,构建一个完整的多工具Agent。这个Agent具备三种核心能力:实时天气查询(外部API调用)、精确数学计算(避免大模型计算错误)、互联网搜索(获取最新信息)。这三个工具覆盖了Function Calling最常见的应用场景——实时数据获取、精确计算、知识扩展。

我们将使用OpenAI的Function Calling API作为核心框架,配合requests库调用外部服务,构建一个端到端的Agent系统。代码结构清晰分层:工具定义层描述每个工具的Schema,工具实现层封装实际的执行逻辑,Agent协调层管理完整的对话循环,包括工具选择、执行、结果注入和最终回答生成。

这个实战项目的价值在于它是可扩展的——你可以轻松地添加更多工具(数据库查询、邮件发送、文件操作等),而Agent的核心循环代码不需要修改。这正是Function Calling架构的优雅之处:工具即插即用,Agent的核心逻辑保持不变。

python
# ===== 工具定义 =====
WEATHER_TOOL = {
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "查询指定城市的当前天气状况",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "城市名称,如北京、上海"}
            },
            "required": ["city"]
        }
    }
}

CALCULATOR_TOOL = {
    "type": "function",
    "function": {
        "name": "calculate",
        "description": "执行数学计算,支持加减乘除、幂运算、开方",
        "parameters": {
            "type": "object",
            "properties": {
                "expression": {
                    "type": "string", 
                    "description": "数学表达式,如 '2 + 2'、'3 ** 4'、'sqrt(144)'"
                }
            },
            "required": ["expression"]
        }
    }
}

SEARCH_TOOL = {
    "type": "function",
    "function": {
        "name": "web_search",
        "description": "搜索互联网获取最新信息,适用于查询实时新闻、最新事件或训练数据之外的知识",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {"type": "string", "description": "搜索关键词"},
                "num_results": {"type": "integer", "description": "返回结果数量,默认5"}
            },
            "required": ["query"]
        }
    }
}

ALL_TOOLS = [WEATHER_TOOL, CALCULATOR_TOOL, SEARCH_TOOL]
python
# ===== 工具实现 + Agent 核心 =====
import openai
import requests
import math

client = openai.OpenAI()

TOOL_REGISTRY = {
    "get_weather": lambda args: _get_weather(args["city"]),
    "calculate": lambda args: {"result": _calc(args["expression"])},
    "web_search": lambda args: _search(args["query"], args.get("num_results", 5)),
}

def _get_weather(city: str) -> dict:
    resp = requests.get(f"https://wttr.in/{city}?format=j1", timeout=5)
    data = resp.json()["current_condition"][0]
    return {"city": city, "temp": data["temp_C"], "desc": data["weatherDesc"][0]["value"]}

def _calc(expr: str) -> str:
    allowed = set("0123456789+-*/.() ")
    if not all(c in allowed for c in expr):
        return "错误:表达式包含非法字符"
    return str(eval(expr, {"__builtins__": {}, "sqrt": math.sqrt, "pow": pow}))

def _search(query: str, n: int = 5) -> list:
    resp = requests.get(f"https://api.duckduckgo.com/?q={query}&format=json")
    return [{"title": r["Text"], "url": r["FirstURL"]} for r in resp.json().get("Results", [])[:n]]

# Agent 协调层
def agent_run(user_input: str, max_rounds: int = 5) -> str:
    messages = [{"role": "user", "content": user_input}]
    for _ in range(max_rounds):
        resp = client.chat.completions.create(
            model="gpt-4o", messages=messages, tools=ALL_TOOLS
        )
        msg = resp.choices[0].message
        if not msg.tool_calls:
            return msg.content
        messages.append(msg)
        for tc in msg.tool_calls:
            result = TOOL_REGISTRY[tc.function.name](
                json.loads(tc.function.arguments)
            )
            messages.append({
                "role": "tool",
                "tool_call_id": tc.id,
                "content": json.dumps(result, ensure_ascii=False)
            })
    return "已达到最大调用次数"

# 测试
print(agent_run("北京天气怎么样?"))
print(agent_run("2的32次方等于多少?"))
print(agent_run("今天有什么重要国际新闻?"))
工具输入输出示例错误处理

天气查询

{"city": "北京"}

{"temp": 22, "desc": "晴"}

超时/城市不存在

计算器

{"expr": "2+2"}

{"result": 4}

非法字符/除零

网络搜索

{"query": "AI新闻"}

[{"title": ..., "url": ...}]

API限流/无结果

Agent架构的核心价值在于可扩展性——新增工具只需添加定义和实现,核心循环代码零修改。

计算器工具中的eval函数必须严格限制允许的字符集合,否则存在代码注入风险。

继续你的 AI 学习之旅

浏览更多 AI 知识库文章,或者探索 GitHub 上的优质 AI 项目