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 的核心是 思考-行动-观察 循环:
- 工具定义:清晰的 JSON Schema 描述是 LLM 正确调用工具的前提
- Agent Loop:LLM 决定调用什么工具 → 执行 → 将结果反馈给 LLM → 继续推理
- 安全控制:工具白名单、代码沙箱、调用次数限制
- 并行调用:独立的工具调用可以并行执行,提升效率


4万+

被折叠的 条评论
为什么被折叠?



