LLM Agent 工具调用框架:从 ReAct 到 Function Calling
1. 引言大语言模型本身只能生成文本但通过 Agent 框架它可以调用工具、搜索互联网、执行代码、操作数据库——成为一个真正的智能体。本文将从原理到实现完整构建一个 LLM Agent 系统。核心概念ReActReasoning 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 工具实现importrequestsimportsubprocessimportjsondefget_weather(city:str,unit:strcelsius)-str:查询天气# 使用 wttr.in 免费 APIresprequests.get(fhttps://wttr.in/{city}?formatj1)dataresp.json()currentdata[current_condition][0]tempcurrent[temp_C]ifunitcelsiuselsecurrent[temp_F]desccurrent[weatherDesc][0][value]windcurrent[windspeedKmph]returnjson.dumps({city:city,temperature:f{temp}°{CifunitcelsiuselseF},description:desc,wind_speed:f{wind}km/h,},ensure_asciiFalse)defsearch_web(query:str,num_results:int5)-str:搜索网页# 使用 DuckDuckGo 搜索fromduckduckgo_searchimportDDGSwithDDGS()asddgs:resultslist(ddgs.text(query,max_resultsnum_results))returnjson.dumps(results,ensure_asciiFalse)defexecute_python(code:str)-str:安全执行 Python 代码importsysfromioimportStringIO old_stdoutsys.stdout sys.stdoutStringIO()try:exec(code,{__builtins__:__builtins__})outputsys.stdout.getvalue()returnoutputifoutputelse代码执行成功无输出exceptExceptionase:returnf执行错误:{str(e)}finally:sys.stdoutold_stdout# 工具注册表TOOL_REGISTRY{get_weather:get_weather,search_web:search_web,execute_python:execute_python,}4. Agent 核心实现fromopenaiimportOpenAIclassLLMAgent:LLM Agent 核心引擎def__init__(self,modelgpt-4o):self.clientOpenAI()self.modelmodel self.toolstools# 上面定义的工具 schemaself.max_iterations10defrun(self,user_message:str)-str:运行 Agent 循环messages[{role:system,content:你是一个有用的 AI 助手。你可以使用工具来回答问题。每次使用工具后观察结果然后决定是否需要继续使用工具。如果已经有足够信息直接回答用户。},{role:user,content:user_message},]foriterationinrange(self.max_iterations):# 调用 LLMresponseself.client.chat.completions.create(modelself.model,messagesmessages,toolsself.tools,tool_choiceauto,)messageresponse.choices[0].message messages.append(message)# 检查是否有工具调用ifnotmessage.tool_calls:# 没有工具调用返回最终回答returnmessage.content# 执行所有工具调用fortool_callinmessage.tool_calls:func_nametool_call.function.name func_argsjson.loads(tool_call.function.arguments)print(f 调用工具:{func_name}({func_args}))# 执行工具iffunc_nameinTOOL_REGISTRY:resultTOOL_REGISTRY[func_name](**func_args)else:resultf错误未知工具{func_name}print(f 结果:{result[:200]}...)# 将结果加入消息messages.append({role:tool,tool_call_id:tool_call.id,content:str(result),})return达到最大迭代次数未能得出最终答案。5. 使用示例agentLLMAgent()# 单步工具调用answeragent.run(北京今天天气怎么样)print(answer)# 多步推理answeragent.run(帮我计算一下如果我现在投资 10 万元年化收益 8%复利计算 10 年后本息合计是多少)print(answer)# 复杂任务answeragent.run(搜索一下 2024 年诺贝尔物理学奖得主是谁然后用 Python 计算一下他们从出生到获奖分别经历了多少天。)print(answer)6. 并行工具调用# OpenAI 支持并行工具调用# LLM 可以在一次响应中返回多个 tool_callsdefrun_parallel(agent,user_message:str)-str:支持并行工具调用的 Agentmessages[{role:system,content:你可以同时调用多个工具。},{role:user,content:user_message},]responseagent.client.chat.completions.create(modelagent.model,messagesmessages,toolsagent.tools,parallel_tool_callsTrue,# 启用并行调用)messageresponse.choices[0].messageifmessage.tool_calls:# 并行执行所有工具importconcurrent.futureswithconcurrent.futures.ThreadPoolExecutor()asexecutor:futures{}fortcinmessage.tool_calls:funcTOOL_REGISTRY[tc.function.name]argsjson.loads(tc.function.arguments)futures[tc.id]executor.submit(func,**args)# 收集结果messages.append(message)fortcinmessage.tool_calls:resultfutures[tc.id].result()messages.append({role:tool,tool_call_id:tc.id,content:str(result),})# 第二轮 LLM 调用整合结果responseagent.client.chat.completions.create(modelagent.model,messagesmessages,toolsagent.tools,)returnresponse.choices[0].message.content7. 安全考虑# 工具调用的安全最佳实践classSafeAgent(LLMAgent):带安全限制的 Agentdef__init__(self,**kwargs):super().__init__(**kwargs)self.allowed_tools{get_weather,search_web}# 白名单self.max_tool_calls_per_turn5def_validate_tool_call(self,func_name,func_args):验证工具调用安全性iffunc_namenotinself.allowed_tools:raisePermissionError(f工具{func_name}不在白名单中)# 代码执行需要额外检查iffunc_nameexecute_python:codefunc_args.get(code,)dangerous[os.system,subprocess,eval(,exec(,import os]forpatternindangerous:ifpatternincode:raisePermissionError(f代码包含危险操作:{pattern})8. 总结LLM Agent 的核心是思考-行动-观察循环工具定义清晰的 JSON Schema 描述是 LLM 正确调用工具的前提Agent LoopLLM 决定调用什么工具 → 执行 → 将结果反馈给 LLM → 继续推理安全控制工具白名单、代码沙箱、调用次数限制并行调用独立的工具调用可以并行执行提升效率