
来自github开源资料——《从零开始学Agent》构建一个实用的搜索与计算 Agent能够回答需要实时信息和数学推理的复杂问题项目目标构建一个 Agent能够 搜索互联网获取实时信息 精确执行数学计算 组合多个工具完成复杂任务 给出有来源依据的回答设计思路多步工具组合——让Agent真正发挥作用的地方三个互补的工具工具职责何时调用何时不调用search_web搜索互联网获取实时信息实时数据、新闻、人物背景数学计算、单位换算calculate精确计算数学表达式大数运算、科学计算直接查得到的事实unit_converter常见单位换算km/mile、°C/°F、kg/pound货币、汇率设计上有三个关键决策贯穿整个 Agent工具边界互斥每个工具有适用 / 不适用两个描述避免模型在多个工具间随机选择错误信息返回字符串而非抛异常模型收到错误后能自我修正如换关键词、换格式Agent 循环设置硬上限MAX_STEPS 8防止模型陷入无限循环把 Token 烧光工具层实现tool.py# ── 工具1搜索基于 DuckDuckGo 免费 API无需 API Key────────importrequestsdefsearch_web(query:str,num_results:int5)-str:调用 DuckDuckGo Instant Answer API返回文本摘要。try:urlhttps://api.duckduckgo.com/params{q:query,format:json,no_html:1}datarequests.get(url,paramsparams,timeout10).json()results[]ifdata.get(AbstractText):results.append(f即时答案{data[AbstractText]})fortopicindata.get(RelatedTopics,[])[:num_results]:ifisinstance(topic,dict)andtopic.get(Text):results.append(f•{topic[Text][:200]})return\n.join(results)ifresults \elsef未找到结果建议换更具体的关键词。exceptExceptionase:returnf搜索失败{e}。请检查网络连接。# ── 工具2精确计算用受限 eval 兼顾安全与功能─────────────importmathdefcalculate(expression:str)-str:用受限的 eval 环境执行数学表达式避免任意代码执行。safe_dict{__builtins__:{},# 关键禁用所有内置函数sqrt:math.sqrt,log:math.log,sin:math.sin,cos:math.cos,pi:math.pi,e:math.e,abs:abs,round:round,# ... 其他数学函数按需添加}try:resulteval(expression,safe_dict)returnf{expression}{result}exceptZeroDivisionError:return计算错误除以零exceptExceptionase:returnf计算错误{e}。请确认表达式格式正确。# ── 工具3单位换算 ──────────────────────────────────────────# ── 通用单位换算表 ──_LENGTH{m:1.0,meter:1.0,meters:1.0,km:1000.0,kilometer:1000.0,kilometers:1000.0,cm:0.01,centimeter:0.01,mm:0.001,millimeter:0.001,mile:1609.344,miles:1609.344,yard:0.9144,yards:0.9144,foot:0.3048,feet:0.3048,inch:0.0254,inches:0.0254,}_WEIGHT{kg:1.0,kilogram:1.0,kilograms:1.0,g:0.001,gram:0.001,grams:0.001,mg:1e-6,milligram:1e-6,pound:0.45359237,pounds:0.45359237,lb:0.45359237,lbs:0.45359237,ounce:0.028349523125,ounces:0.028349523125,oz:0.028349523125,ton:1000.0,tons:1000.0,}_AREA{m2:1.0,sqm:1.0,square meter:1.0,km2:1_000_000.0,sqkm:1_000_000.0,square kilometer:1_000_000.0,cm2:0.0001,sqcm:0.0001,hectare:10_000.0,hectares:10_000.0,acre:4046.8564224,acres:4046.8564224,ft2:0.09290304,sqft:0.09290304,square foot:0.09290304,}_VOLUME{l:1.0,liter:1.0,liters:1.0,ml:0.001,milliliter:0.001,milliliters:0.001,m3:1000.0,cubic meter:1000.0,gallon:3.785411784,gallons:3.785411784,quart:0.946352946,quarts:0.946352946,cup:0.2365882365,cups:0.2365882365,floz:0.0295735295625,fluid ounce:0.0295735295625,}defunit_converter(value:float,from_unit:str,to_unit:str)-str:通用单位换算支持长度、重量、面积、体积、温度。from_unitfrom_unit.strip().lower()to_unitto_unit.strip().lower()# ── 温度单独处理 ──temp_units{celsius,c,fahrenheit,f,kelvin,k}iffrom_unitintemp_unitsandto_unitintemp_units:return_convert_temperature(value,from_unit,to_unit)# ── 长度 ──iffrom_unitin_LENGTHandto_unitin_LENGTH:resultvalue*_LENGTH[from_unit]/_LENGTH[to_unit]returnf{value}{from_unit}{result:g}{to_unit}# ── 重量 ──iffrom_unitin_WEIGHTandto_unitin_WEIGHT:resultvalue*_WEIGHT[from_unit]/_WEIGHT[to_unit]returnf{value}{from_unit}{result:g}{to_unit}# ── 面积 ──iffrom_unitin_AREAandto_unitin_AREA:resultvalue*_AREA[from_unit]/_AREA[to_unit]returnf{value}{from_unit}{result:g}{to_unit}# ── 体积 ──iffrom_unitin_VOLUMEandto_unitin_VOLUME:resultvalue*_VOLUME[from_unit]/_VOLUME[to_unit]returnf{value}{from_unit}{result:g}{to_unit}# ── 未知单位 ──return(f不支持的单位换算{from_unit}→{to_unit}。f支持长度 (m/km/mile/ft/inch)、重量 (kg/g/pound/ounce)、f面积 (m2/km2/acre/hectare)、体积 (l/ml/gallon)、温度 (°C/°F/K)。)def_convert_temperature(value:float,from_unit:str,to_unit:str)-str:from_unitfrom_unit.lower()to_unitto_unit.lower()iffrom_unitto_unit:returnf{value}{from_unit}{value}{to_unit}# 先转成 celsiusiffrom_unitcelsius:cvalueeliffrom_unitfahrenheit:c(value-32)*5/9eliffrom_unitkelvin:cvalue-273.15else:returnf不支持的温度单位{from_unit}# 再从 celsius 转到目标单位ifto_unitcelsius:resultcelifto_unitfahrenheit:resultc*9/532elifto_unitkelvin:resultc273.15else:returnf不支持的温度单位{to_unit}returnf{value}{from_unit}{result:.6g}{to_unit}TOOLS[{type:function,function:{name:search_web,description:在互联网上搜索实时信息。 适合用于 - 查询最新新闻、事件、价格等实时数据 - 获取人物、地点、事件的背景信息 - 查找技术文档和教程 不适合用于 - 数学计算使用 calculate 工具 - 单位换算使用 unit_converter 工具 - 你已经知道答案的问题,parameters:{type:object,properties:{query:{type:string,description:搜索关键词建议简洁精准。},num_results:{type:integer,description:返回结果数量默认5最大10,default:5,minimum:1,maximum:10}},required:[query],additionalProperties:False# 关键禁止额外字段}}},{type:function,function:{name:calculate,description:精确计算数学表达式。 支持基本运算,-,*,/,**、数学函数sqrt/sin/cos/log等、常量pi/e 不适合 - 需要查询的事实如光速是多少→ 用 search_web - 单位换算 → 用 unit_converter - 大段文本处理,parameters:{type:object,properties:{expression:{type:string,description:数学表达式使用 Python 语法。乘方用 **不用 ^}},required:[expression],additionalProperties:False}}},{type:function,function:{name:unit_converter,description:单位换算支持长度、重量、温度、面积等常见单位转换。 适合m/km/mile、kg/pound、°C/°F 等 不适合货币汇率、时区转换,parameters:{type:object,properties:{value:{type:number,description:要换算的数值},from_unit:{type:string,description:原始单位如 m, km, celsius},to_unit:{type:string,description:目标单位如 mile, pound, fahrenheit}},required:[value,from_unit,to_unit],additionalProperties:False}}}]Agent循环——决策与执行的调度器importosimportjsonfromdotenvimportload_dotenvfromopenaiimportOpenAIfromtoolimportTOOLS,search_web,calculate,unit_converter load_dotenv()clientOpenAI(api_keyos.getenv(DEEPSEEK_API_KEY),base_urlhttps://api.deepseek.com,)MODELos.getenv(DEEPSEEK_MODEL,deepseek-v4-flash)TOOL_FUNCTIONS{search_web:search_web,calculate:calculate,unit_converter:unit_converter,}SYSTEM_PROMPT 你是一个可以使用工具的 Agent。 你可以回答需要实时信息、数学推理、单位换算的复杂问题。 规则 1. 如果问题涉及最新新闻、价格、实时数据、当前事实优先调用 search_web。 2. 如果问题涉及精确数学计算必须调用 calculate不要心算。 3. 如果问题涉及单位换算调用 unit_converter。 4. 可以多步调用工具先搜索再计算再总结。 5. 工具返回错误时根据错误信息尝试修正参数不要无限重试。 6. 最终回答要清楚说明哪些信息来自搜索哪些结果来自计算。 7. 如果搜索结果不足要明确说明不确定性不要编造。 defrun_agent(user_query:str,max_steps:int6)-str:messages[{role:system,content:SYSTEM_PROMPT},{role:user,content:user_query},]for_inrange(max_steps):responseclient.chat.completions.create(modelMODEL,messagesmessages,toolsTOOLS,tool_choiceauto,)assistant_messageresponse.choices[0].message# 关键把 SDK 对象转成 dict再塞回 messagesmessages.append(assistant_message.model_dump(exclude_noneTrue))# 没有工具调用说明模型已经给出最终答案ifnotassistant_message.tool_calls:returnassistant_message.contentorfortool_callinassistant_message.tool_calls:tool_nametool_call.function.name raw_argstool_call.function.argumentstry:argsjson.loads(raw_args)exceptjson.JSONDecodeError:tool_resultf工具调用失败参数不是合法 JSON。收到{raw_args}else:tool_funcTOOL_FUNCTIONS.get(tool_name)iftool_funcisNone:tool_resultf工具调用失败未知工具{tool_name}else:try:tool_resulttool_func(**args)exceptTypeErrorase:tool_result(f工具调用失败参数不匹配。f工具名{tool_name}参数{args}错误{e})exceptExceptionase:tool_result(f工具执行异常{e}。f请尝试调整参数或换用其他工具。)messages.append({role:tool,tool_call_id:tool_call.id,content:str(tool_result),})returnAgent 达到最大工具调用轮数仍未完成任务。请缩小问题范围或增加 max_steps。if__name____main__:ifnotos.getenv(DEEPSEEK_API_KEY):raiseRuntimeError(请先在 .env 中配置 DEEPSEEK_API_KEY)whileTrue:queryinput(\nUser ).strip()ifquery.lower()in{exit,quit}:breakanswerrun_agent(query)print(\nAgent)print(answer)环境中执行python .\agent.py User地球到月球多远光速飞过去要几秒 Agent## 地球到月球的距离 光速飞行时间### 距离地球到月球的平均距离约为 **384,400 公里**。 由于月球绕地球的轨道是椭圆形距离会变化|位置|距离||------|------||**近地点**最近|约 **363,300 公里**||**平均距离**|约 **384,400 公里**||**远地点**最远|约 **405,500 公里**|### 光速飞行时间光速约为 **299,792 公里/秒**通过计算可得|情况|时间||------|------||**近地点**|约 **1.21 秒**||**平均距离**|约 **1.28 秒**||**远地点**|约 **1.35 秒**|所以光从地球飞到月球大约只需要 **1.28 秒**平均而言✨这也是为什么月球通信会有约 **2.56 秒的往返延迟**一去一回。工具学习前沿进展Toolformer: Language Models Can Teach Themselves to Use Tools.https://arxiv.org/abs/2302.04761设计原则让模型通过“预测未来”来学习。Toolformer 提出了一个非常反直觉但巧妙的思路如果调用工具能帮我预测下一个单词那这个工具就是有用的。Gorilla: Large Language Model Connected with Massive APIshttps://arxiv.org/abs/2305.15334更关注一个工程问题当工具数量非常多时模型能不能准确选择 API并生成正确的参数核心问题API幻觉在真实业务中Agent 面对的不是一两个工具而可能是几十、几百甚至上千个 API。此时模型容易出现三类错误调用不存在的 API把训练语料中见过的函数名和当前系统的工具混淆。参数结构错误字段名、类型、必填项不符合真实 schema。版本不匹配API 已更新但模型仍按旧文档调用。Gorilla 的价值在于它把 API 文档、检索和模型生成结合起来让模型在调用前先找到最相关的 API 文档再基于文档生成调用代码或参数。最新论文速递https://haozhe-xing.github.io/agent_learning/zh/chapter_tools/06_paper_readings.html#-%E6%9C%80%E6%96%B0%E8%AE%BA%E6%96%87%E9%80%9F%E9%80%92可以查看关于工具学习的最新进展