LLM Agent 工具调用框架:从 ReAct 到 Function Calling

1. 引言

大语言模型本身只能生成文本,但通过 Agent 框架,它可以调用工具、搜索互联网、执行代码、操作数据库——成为一个真正的"智能体"。本文将从原理到实现,完整构建一个 LLM Agent 系统。

核心概念:

  • ReAct:Reasoning + Acting 的循环模式
  • Function Calling:让 LLM 结构化地调用外部函数
  • Tool Schema:工具的 JSON Schema 描述
  • Agent Loop:思考 → 调用工具 → 观察结果 → 继续思考

2. ReAct 模式原理

用户提问 → LLM 思考(Thought) → 选择动作(Action) → 执行工具 → 观察结果(Observation)
                                         ↑                                    |
                                         └────────── 循环直到得到最终答案 ────────┘

示例交互:

用户: 北京今天的天气怎么样?

Thought: 用户想知道北京今天的天气,我需要调用天气查询工具。
Action: get_weather(city="北京")
Observation: 北京今天晴,气温 15-25°C,北风 3-4 级。

Thought: 我已经获得了天气信息,可以回答用户了。
Answer: 北京今天天气晴朗,气温 15-25°C,北风 3-4 级,适合外出。

3. 工具定义

3.1 工具 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": "search_web",
            "description": "搜索互联网获取最新信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "搜索关键词"
                    },
                    "num_results": {
                        "type": "integer",
                        "description": "返回结果数量",
                        "default": 5
                    }
                },
                "required": ["query"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "execute_python",
            "description": "执行 Python 代码并返回结果",
            "parameters": {
                "type": "object",
                "properties": {
                    "code": {
                        "type": "string",
                        "description": "要执行的 Python 代码"
                    }
                },
                "required": ["code"]
            }
        }
    }
]

3.2 工具实现

import requests
import subprocess
import json

def get_weather(city: str, unit: str = "celsius") -> str:
    """查询天气"""
    # 使用 wttr.in 免费 API
    resp = requests.get(f"https://wttr.in/{city}?format=j1")
    data = resp.json()
    current = data["current_condition"][0]
    temp = current["temp_C"] if unit == "celsius" else current["temp_F"]
    desc = current["weatherDesc"][0]["value"]
    wind = current["windspeedKmph"]
    return json.dumps({
        "city": city,
        "temperature": f"{temp}°{'C' if unit == 'celsius' else 'F'}",
        "description": desc,
        "wind_speed": f"{wind} km/h",
    }, ensure_ascii=False)

def search_web(query: str, num_results: int = 5) -> str:
    """搜索网页"""
    # 使用 DuckDuckGo 搜索
    from duckduckgo_search import DDGS
    with DDGS() as ddgs:
        results = list(ddgs.text(query, max_results=num_results))
    return json.dumps(results, ensure_ascii=False)

def execute_python(code: str) -> str:
    """安全执行 Python 代码"""
    import sys
    from io import StringIO
    old_stdout = sys.stdout
    sys.stdout = StringIO()
    try:
        exec(code, {"__builtins__": __builtins__})
        output = sys.stdout.getvalue()
        return output if output else "代码执行成功(无输出)"
    except Exception as e:
        return f"执行错误: {str(e)}"
    finally:
        sys.stdout = old_stdout

# 工具注册表
TOOL_REGISTRY = {
    "get_weather": get_weather,
    "search_web": search_web,
    "execute_python": execute_python,
}

4. Agent 核心实现

from openai import OpenAI

class LLMAgent:
    """LLM Agent 核心引擎"""

    def __init__(self, model="gpt-4o"):
        self.client = OpenAI()
        self.model = model
        self.tools = tools  # 上面定义的工具 schema
        self.max_iterations = 10

    def run(self, user_message: str) -> str:
        """运行 Agent 循环"""
        messages = [
            {"role": "system", "content":
                "你是一个有用的 AI 助手。你可以使用工具来回答问题。"
                "每次使用工具后,观察结果,然后决定是否需要继续使用工具。"
                "如果已经有足够信息,直接回答用户。"},
            {"role": "user", "content": user_message},
        ]

        for iteration in range(self.max_iterations):
            # 调用 LLM
            response = self.client.chat.completions.create(
                model=self.model,
                messages=messages,
                tools=self.tools,
                tool_choice="auto",
            )

            message = response.choices[0].message
            messages.append(message)

            # 检查是否有工具调用
            if not message.tool_calls:
                # 没有工具调用,返回最终回答
                return message.content

            # 执行所有工具调用
            for tool_call in message.tool_calls:
                func_name = tool_call.function.name
                func_args = json.loads(tool_call.function.arguments)

                print(f"  🔧 调用工具: {func_name}({func_args})")

                # 执行工具
                if func_name in TOOL_REGISTRY:
                    result = TOOL_REGISTRY[func_name](**func_args)
                else:
                    result = f"错误:未知工具 {func_name}"

                print(f"  📋 结果: {result[:200]}...")

                # 将结果加入消息
                messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call.id,
                    "content": str(result),
                })

        return "达到最大迭代次数,未能得出最终答案。"

5. 使用示例

agent = LLMAgent()

# 单步工具调用
answer = agent.run("北京今天天气怎么样?")
print(answer)

# 多步推理
answer = agent.run(
    "帮我计算一下,如果我现在投资 10 万元,年化收益 8%,"
    "复利计算 10 年后本息合计是多少?"
)
print(answer)

# 复杂任务
answer = agent.run(
    "搜索一下 2024 年诺贝尔物理学奖得主是谁,然后用 Python "
    "计算一下他们从出生到获奖分别经历了多少天。"
)
print(answer)

6. 并行工具调用

# OpenAI 支持并行工具调用
# LLM 可以在一次响应中返回多个 tool_calls

def run_parallel(agent, user_message: str) -> str:
    """支持并行工具调用的 Agent"""
    messages = [
        {"role": "system", "content": "你可以同时调用多个工具。"},
        {"role": "user", "content": user_message},
    ]

    response = agent.client.chat.completions.create(
        model=agent.model,
        messages=messages,
        tools=agent.tools,
        parallel_tool_calls=True,  # 启用并行调用
    )

    message = response.choices[0].message

    if message.tool_calls:
        # 并行执行所有工具
        import concurrent.futures
        with concurrent.futures.ThreadPoolExecutor() as executor:
            futures = {}
            for tc in message.tool_calls:
                func = TOOL_REGISTRY[tc.function.name]
                args = json.loads(tc.function.arguments)
                futures[tc.id] = executor.submit(func, **args)

            # 收集结果
            messages.append(message)
            for tc in message.tool_calls:
                result = futures[tc.id].result()
                messages.append({
                    "role": "tool",
                    "tool_call_id": tc.id,
                    "content": str(result),
                })

    # 第二轮 LLM 调用,整合结果
    response = agent.client.chat.completions.create(
        model=agent.model,
        messages=messages,
        tools=agent.tools,
    )
    return response.choices[0].message.content

7. 安全考虑

# 工具调用的安全最佳实践

class SafeAgent(LLMAgent):
    """带安全限制的 Agent"""

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.allowed_tools = {"get_weather", "search_web"}  # 白名单
        self.max_tool_calls_per_turn = 5

    def _validate_tool_call(self, func_name, func_args):
        """验证工具调用安全性"""
        if func_name not in self.allowed_tools:
            raise PermissionError(f"工具 {func_name} 不在白名单中")

        # 代码执行需要额外检查
        if func_name == "execute_python":
            code = func_args.get("code", "")
            dangerous = ["os.system", "subprocess", "eval(", "exec(", "import os"]
            for pattern in dangerous:
                if pattern in code:
                    raise PermissionError(f"代码包含危险操作: {pattern}")

8. 总结

LLM Agent 的核心是 思考-行动-观察 循环:

  1. 工具定义:清晰的 JSON Schema 描述是 LLM 正确调用工具的前提
  2. Agent Loop:LLM 决定调用什么工具 → 执行 → 将结果反馈给 LLM → 继续推理
  3. 安全控制:工具白名单、代码沙箱、调用次数限制
  4. 并行调用:独立的工具调用可以并行执行,提升效率
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大鱼>

一分也是爱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值