RAG 让 LLM 能「读」Function Calling 让 LLM 能「做」。这篇带你从零实现一个​带工具调用能力的聊天机器人​——它能查天气、搜新闻、做算术还能把结果整理成漂亮的格式返回。 目录项目目标定义工具集核心循环决策 → 执行 → 观察完整代码实现扩展方向项目目标最终效果 用户「北京今天天气怎么样顺便帮我算下 23 * 47」 机器人 北京今天晴气温 12~22°C适合出行 23 × 47 1081 用户「搜一下最新的 AI 新闻」 机器人[调用搜索工具] 找到以下最新资讯 1. OpenAI 发布 GPT-5 ... 2. Google DeepMind 宣布 ... 关键能力 ✅ 自动判断是否需要调用工具 ✅ 正确提取函数参数 ✅ 执行工具并将结果整合进回答 ✅ 多轮工具调用一个问题可能需要调多个工具定义工具集# bot/tools.pyimportjsonimportrandomfromdatetimeimportdatetimefromtypingimportCallable# 工具注册表名称 → (函数, 描述, 参数Schema)TOOLS_REGISTRY:dict[str,dict]{}deftool(name:str,description:str,params_schema:dict):装饰器注册一个工具函数defdecorator(func:Callable):TOOLS_REGISTRY[name]{function:func,description:description,parameters:params_schema,}returnfuncreturndecoratortool(nameget_weather,description获取指定城市的当前天气,params_schema{type:object,properties:{city:{type:string,description:城市名},},required:[city]})defget_weather(city:str)-str:模拟天气 API实际替换为真实 APIweathers[晴,多云,阴,小雨]temp_lowrandom.randint(-5,20)temp_highrandom.randint(temp_low5,temp_low18)returnjson.dumps({city:city,weather:random.choice(weathers),temp_low:temp_low,temp_high:temp_high,updated_at:datetime.now().isoformat()},ensure_asciiFalse)tool(namecalculate,description执行数学计算表达式,params_schema{type:object,properties:{expression:{type:string,description:数学表达式如 23*47},},required:[expression]})defcalculate(expression:str)-str:try:# 安全起见只允许数字和基本运算符allowedset(0123456789-*/().% )ifnotall(cinallowedforcinexpression):returnjson.dumps({error:不允许的表达式字符})resulteval(expression)# 生产环境用 ast.literal_eval 更安全returnjson.dumps({expression:expression,result:result})exceptExceptionase:returnjson.dumps({error:str(e)})tool(namesearch_news,description搜索最新新闻按关键词,params_schema{type:object,properties:{keyword:{type:string,description:搜索关键词},limit:{type:integer,description:返回条数默认5},},required:[keyword]})defsearch_news(keyword:str,limit:int5)-str:模拟搜索实际接入搜索 API 或 Tavilyfake_news[f{keyword}相关重大进展报道,f专家解读{keyword}领域新趋势,f{month}月{keyword}行业数据发布,]returnjson.dumps([{title:n,source:模拟源}forninfake_news[:limit]],ensure_asciiFalse)print(f已注册{len(TOOLS_REGISTRY)}个工具:{list(TOOLS_REGISTRY.keys())})核心循环 完整实现# bot/main.pyfromopenaiimportOpenAIimportjsonfromtoolsimportTOOLS_REGISTRY clientOpenAI()defbuild_tools_definition():转换为 OpenAI Function Calling 格式return[{type:function,function:{name:name,description:info[description],parameters:info[parameters]}}forname,infoinTOOLS_REGISTRY.items()]defexecute_tool(name:str,args:dict):安全执行工具并返回结果ifnamenotinTOOLS_REGISTRY:returnjson.dumps({error:f未知工具:{name}})try:funcTOOLS_REGISTRY[name][function]resultfunc(**args)returnresultexceptExceptionase:returnjson.dumps({error:f工具执行错误:{e}})defchat_with_tools(user_message:str,max_turns5):带 Function Calling 的多轮对话循环messages[{role:system,content:你是一个有帮助的助手。使用可用工具来获取实时信息和执行操作。}]messages.append({role:user,content:user_message})tools_defbuild_tools_definition()forturninrange(max_turns):responseclient.chat.completions.create(modelgpt-4o-mini,messagesmessages,toolstools_def,temperature0.2,)msgresponse.choices[0].message# 情况A直接回答ifnotmsg.tool_calls:returnmsg.content# 情况B调用工具messages.append(msg)# 记录 LLM 的决策fortcinmsg.tool_calls:func_nametc.function.name func_argsjson.loads(tc.function.arguments)print(f 调用工具:{func_name}({func_args}))tool_resultexecute_tool(func_name,func_args)print(f 结果:{tool_result[:100]}...)# 结果喂回 LLMmessages.append({role:tool,tool_call_id:tc.id,content:tool_result})return达到最大轮次限制# CLI 交互if__name____main__:print( 函数调用机器人已启动(输入 quit 退出)\n)whileTrue:queryinput(你: )ifquery.lower()quit:breakanswerchat_with_tools(query)print(f:{answer}\n)# 运行python-mbot.main❌ 常见误区❌ 工具描述写得越详细越好 — 太长的描述反而干扰 LLM 决策简洁精准最好❌ 所有工具一次性注册 — 按场景分组加载减少选择困难❌ 忽略工具的错误处理 — 工具可能失败必须有 fallback 和异常捕获