任何一个 Agent 跑起来之后你很快就会撞上一个尴尬场景它聊起天来头头是道但一问今天实时热搜是什么它只能编让它算一下斐波那契数列第 50 项它开始一本正经地胡说让它把这段数据画成图它只能给你描述一张根本不存在的图。说白了大模型只有口才没有手和眼。而真正让 Agent 从聊天机器人变成干活系统的是外挂工具。在众多工具里Retrieval、精确计算和代码执行这三类能力是刚需对应到具体实现上我这些年用得最顺手的三件套就是SearxNG、WolframAlpha 与代码解释器。这篇内容不是我抄文档的笔记而是我带着这三个工具在真实 Agent 项目里反复调试后沉淀下来的拆解。会聊到它们各自适合干什么、怎么接入、怎么编排以及大量只在踩坑时才说得清的细节。无论你是用现成框架搭 Agent还是想自己从零写工具调用这份东西都能帮你少走半个月弯路。1. Agent 工具箱的定位光有大模型口才还不够先搞清楚一个问题为什么 Agent 一定要配工具箱大模型本身有海量训练知识但也因此有天然的三个短板时间截止训练数据、无法实时获取外部信息生成式模型不擅长精确数值计算乘法都可能错只能输出文本无法真正操作系统、读写文件、运行代码。工具就是补短板用的。但补的时候要讲究搭配不是工具越多越好而是让每种工具负责它最擅长的能力边界。我偏好把工具按能力类型分成三类信息获取类负责查事实、查动态、查详情代表就是搜索工具。知识与计算类负责精确推导、单位换算、数学求解代表是 WolframAlpha。执行操作类负责写代码、跑脚本、处理文件、生成图表代表是代码解释器。三者的关系不是替代而是互补。搜索能搜到一大堆网页但搜不出积分结果WolframAlpha 能告诉你精确答案但没法帮你把答案持续计算一百遍代码解释器什么都能算但你得自己给它写对算法和逻辑。把三者组合起来Agent 才真正具备检索线索 → 精确推理 → 动手验证的完整能力闭环。1.1 三个工具各自解决什么问题维度SearxNGWolframAlpha代码解释器能力本质元搜索聚合知识引擎与符号计算通用代码执行环境主要用途找网页、找最新信息、找事实线索数学计算、单位换算、实体知识、科学与工程数据写任意代码、处理数据、生成图、文件转码输出特点网页列表、摘要、链接结构化答案、步骤、图表文本输出、文件产物、可视化需要联网是实时是调 API通常隔离网络更安全适合场景开放性事实查询精确、可验证的定量问题需要跑逻辑或生成文件的任务这是我在工具选型时的初始判断表。实际使用中三者的边界经常有重叠。比如代码解释器理论上也能拉取数据但联网权限通常被关掉所以拉数据还得靠搜索类工具WolframAlpha 也能画图但自定义能力远不如代码解释器。清晰定位后面编排才不会乱。1.2 工具调用的底层逻辑Function Calling 与协议Agent 调用工具技术上并不是真的会按按钮而是通过一层约定。主流方式是Function Calling把工具描述、参数 schema 作为 JSON 发给模型模型根据任务选择调用哪个工具并生成符合 schema 的参数然后你的程序实际执行该工具把结果再塞回对话上下文让模型继续推理。在较新的 Agent 框架里这套东西已经具体化为类似MCPModel Context Protocol的协议让工具以标准服务的形式暴露给 Agent。但不管协议怎么变思维方式不变给每个工具写清楚什么时候用、什么时候别用的描述。给每个工具定义严格的输入参数格式。工具的返回值必须结构完整方便模型二次读取。我见过不少 Agent 翻车不是因为模型笨而是工具描述写得太含糊。比如把 SearxNG 描述成搜索工具模型就不知道该传什么关键词、该怎么处理结果。后面我会给实际可用的工具描述模板。2. SearxNG自托管的元搜索聚合器让 Agent 有一双实时眼睛先说信息获取这块。很多人的第一反应是直接调某搜索引擎的 API 不行吗可以但有几个现实问题免费额度有限、商用要付费、接口权限经常变、有些区域不可用而且会把 Agent 的检索行为绑定到单一服务商。我在项目里最终选的是SearxNG——一个开源的自托管元搜索引擎。它不是自己爬网页而是同时查询上游多个搜索来源再把结果聚合去重后返回给你。自托管意味着你拥有完整的控制权可以统一监控、可以按需禁用某些上游、可以缓存结果最重要的是可以只对 Agent 开放内网接口不用让每个请求都算在外部付费账户头上。对于高频检索的 Agent 场景这笔账算下来非常划算。2.1 Docker 部署 SearxNG最小可行配置SearxNG 的部署开销很小。跑在公司服务器或者自己的 Linux 机器上都行最省事的方式是 Docker Compose。下面是我实际线下验证过的最小配置services: searxng: image: searxng/searxng:latest container_name: searxng ports: - 8888:8080 environment: - SEARXNG_BASE_URLhttp://localhost:8888 - SEARXNG_SECRET请改成随机长字符串 volumes: - ./searxng-data:/etc/searxng restart: unless-stopped启动后访问http://localhost:8888能看到界面即成功。这里两个细节SEARXNG_SECRET务必换成一个强随机串否则服务会拒绝启动或者暴露敏感配置。我习惯用openssl rand -hex 32生成。容器内端口对宿主是 8080映射到外部 8888 只是示例按你自己的端口规划改。之后通过修改searxng-data/settings.yml可以调整上游引擎、限流策略、日志级别。默认的配置已经聚合了谷歌、必应等来源但某些来源可能请求频繁被拒所以要在配置里按需启用。提示不要直接暴露到公网还不用认证。SearxNG 本身没有完善的用户认证机制建议通过防火墙或网关注掉外部访问只给 Agent 所在的内网放行避免被刷成公共代理。2.2 用 JSON 格式接入 Agent 工具SearxNG 对机器调用非常友好只要在请求 URL 里加上formatjson就会返回 JSON。一个典型的查询请求长这样curl http://localhost:8888/search?qAgenttoolintegrationformatjsoncategoriesgeneral返回结构大致如下{ results: [ { title: Agent Tool Integration Guide, url: https://example.com/guide, publishedDate: null, content: This guide explains how to integrate external tools... } ], number_of_results: 1000, query: Agent tool integration }实际接入时我不建议直接把整个 JSON 原样塞给模型。里面有一些无关字段和噪声Token 消耗很快。更好的做法是做一层浅加工只提取关键字段压缩成干净的字符串列表async def search_web(query: str, max_results: int 5) - str: 调用 SearxNG 并提取标题、链接、摘要 params {q: query, format: json, categories: general} async with httpx.AsyncClient(timeout10) as client: resp await client.get(http://localhost:8888/search, paramsparams) data resp.json() rows [] for item in data.get(results, [])[:max_results]: title item.get(title) or url item.get(url) or content (item.get(content) or )[:250] rows.append(f- {title}\n 链接: {url}\n 摘要: {content}) return \n.join(rows) if rows else 没有搜索到有效结果这个函数返回的是一个干净文本块模型读起来很舒适。如果直接把原始 JSON 丢给它模型经常会被无关字段带偏我甚至见过模型引用infobox里的非官方摘要当结论。预处理这层不能省。2.3 为什么选 SearxNG 而不是直接调多个 Search API直接调单一搜索引擎官方 API优点是大厂结果质量高、稳定缺点也很明显配额与成本免费层请求数量有限Agent 任务动不动要搜索几十次配额很快就会见底。一旦转成付费单次请求成本需要精打细算。单点问题如果该搜索引擎调整策略、某个地区不可用、或者开放平台抽风Agent 的整个检索链路就断了。隐私与合规Agent 每次搜索都会把用户的完整意图发到第三方服务。自托管 SearxNG 后查询只落在自己的上游出口至少中间少了一个集中记录。这是很多企业在内网部署 Agent 时的硬性要求。我当然不是说你完全不需要官方 API。如果你有预算并且需求就是某一家检索结果质量特别高的垂直场景那直接调 API 也没问题。但对于我这种家常便饭式的 Agent 检索需求SearxNG 既省钱又灵活还能统一做缓存和限流性价比高出一大截。3. WolframAlpha为 Agent 补上精确计算与符号推理的短板如果说搜索给 Agent 的是眼界那 WolframAlpha 给的就是算力——不是 GPU 那种算力而是符号计算和结构化知识。很多问题比如一个物体的动量是多少根号下 1234567 等于多少3 英尺加 40 厘米是多少米让大模型直接做它不会老老实实告诉你说自己不确定而是会流畅地编一个近似答案。这时候让 Agent 求助于 WolframAlpha它通过查询计算服务获得精确答案再把答案交还给模型复述准确率瞬间拉满。WolframAlpha 的底层是 Wolfram 语言和庞大的知识库。它能求微积分、解方程、画函数图像、查元素周期表、换算单位、计算天气统计……这些是大模型无法稳定内置的能力。而且它的查询支持自然语言意思是temperature in Shanghai这种短语它能看懂甚至 integrate x^2 也可以直接处理。3.1 两种 API 的选择Short Answers 还是 Full ResultsWolframAlpha 的开放 API 主要分两类更细的说分为 Short Answers API、Full Results API 以及较新的 LLM-based API。做一个快速对比。Short Answers API适合只想要一句话答案的场景curl https://api.wolframalpha.com/v1/result?appid你的APPIDiintegrate%20x%5E2返回x^3/3就这么干净。没有步骤没有解释就是一个可复用的精确结果。拿来当 Agent 的计算器再合适不过。Full Results API返回一个 XML或 JSON结构包含输入解析、计算结果、图表、甚至分步骤推理。适合要给用户展示过程或者答案本身很复杂、需要让模型进一步决策的场景。curl https://api.wolframalpha.com/v2/query?appid你的APPIDinputintegrate%20x%5E2formatplaintext返回里会有plaintext: | x^3/3 |我实际项目里的经验是如果你只关心数值或符号结果对不对用 Short Answers 就够了响应更快、解析成本更低。但如果你需要 Agent 解释为什么是这个答案、或者要展示图表就得上 Full Results。我把这两种 API 分别封装成两个工具让 Agent 自己选——短答案版用于快速计算完整版用于需要证据链的场景。3.2 如何引导 Agent 正确构造 WolframAlpha 查询WolframAlpha 虽然能理解自然语言但和搜索引擎的理解逻辑是两回事。搜索是模糊匹配、给一堆链接WolframAlpha 是严格解析输入并对应到它内部的符号表达式。用户说帮我算一下这个函数的导数f 等于 x 平方加一这种长句子直接丢给 WolframAlpha 常常解析失败。让 Agent 先把问题压缩成短查询比如derivative of x^21。这个压缩能力不需要我们写复杂代码写在工具描述里让模型自己学会即可。我用的工具描述模板长这样工具名: wolfram_query 用途: 当回答中涉及精确数学公式、单位换算、科学常数、统计计算时使用。 输入: query应当是一个短表达式例如: - integrate x^2 - 2 meters in feet - density of iron - sqrt(1234567) 约束: 不要将自然语言长句塞入如果问题是概念性解释使用搜索工具。这里最关键的约束是不要拿 WolframAlpha 做解释概念的事。比如什么是导数这个问题它即使能返回一些定义也不如搜索来的丰富。Agent 有时候会误用工具所以我们必须在描述里把什么时候不该用它写清楚。还有一点要处理查询解析失败的返回。WolframAlpha 在听不懂时通常会返回类似 Wolfram|Alpha didnt understand your query 的提示。这时候你的工具层要有容错逻辑捕获这个错误把返回值转换成本次查询未识别建议换个表达方式再让模型重新构造查询而不是直接把原始错误甩给用户。3.3 几个打动我的实际案例拿它的单位换算来说。我问普通人3 英里每小时等于多少米每秒他可能还要打开计算器换算半天但 WolframAlpha 一两秒就给出精确答案1.34112 m/s。这种确定性计算能力是生成式模型给不了的。另一个让我印象深刻的场景是化学相关。学生问 Agent100 克碳酸钙和足量盐酸反应能生成多少摩尔二氧化碳。这种问题本质是化学计量计算大模型能记住公式但代入具体数字时经常换算错误。Agent 在识别到这种需要精确系数的问题时转而调用 WolframAlpha 的0.1 mol CaCO3 HCl查询得到的摩尔数就完全可信了。所以把 WolframAlpha 的定位理解为精确知识计算器更准确。它不适合检索但凡是算得准的问题它是最可信的。4. 代码解释器给 Agent 一个可以动手实验的沙箱搜索负责找信息WolframAlpha 负责算已知的公式但现实世界的问题哪有那么规矩数据要清洗、逻辑要反复验证、图表要生成、批量任务要跑循环。这时候最锋利的工具是代码解释器——也就是让 Agent 能写 Python 代码、传给一个沙箱环境执行、再把结果收回来继续推理。这东西是你 Agent 的瑞士军刀。它能做的远不止算数处理 CSV / Excel / JSON 数据筛选排序聚合。用 matplotlib 画折线图、柱状图、热力图。调用正则、文件解析、网页内容解析等标准库做文本处理。把不同格式的数据文件转来转去html 转 pdf、md 转 docx、图片改尺寸。代码解释器是整个三件套里身手最灵活的一个也因此最难缠——代码是 Agent 写的能不能跑通、跑完之后怎么把产物传给下一步都要你做好设计。4.1 沙箱的两种实现方式子进程与容器代码解释器本质上就是一个 Python 运行环境但绝不能把 Agent 生成的代码直接扔到你的主进程里跑。轻则把工作目录搞得乱七八糟重则删掉库文件、读走敏感信息。我强烈建议单独开一个隔离环境。最简单的方案是subprocess 虚拟环境。在本地用一个 venv 隔离依赖执行方式像这样import subprocess def run_code(code: str, timeout: int 30): with open(agent_script.py, w, encodingutf-8) as f: f.write(code) result subprocess.run( [venv/bin/python, agent_script.py], capture_outputTrue, textTrue, timeouttimeout, cwd./sandbox_workspace, ) return { stdout: result.stdout[-4000:], stderr: result.stderr[-2000:], exit_code: result.returncode, }但这样只能挡住手滑级别的风险挡不住恶意代码。如果 Agent 接收的是公开数据甚至用户上传文件那代码可能被诱导做危险操作。更稳妥的方案是Docker 容器沙箱每次执行新任务启动一个一次性容器任务结束直接销毁。docker run --rm --networknone --memory256m --cpus1 \ -v ./workspace:/workspace -w /workspace \ python:3.11-slim python /workspace/agent_script.py这里我用了--networknone切断网络这是安全基准之一。大部分代码解释器任务不需要联网联网应该走 SearxNG 工具而不是让沙箱里的代码随便请求。--memory和--cpus限制资源占用避免一段死循环代码拖垮整台服务器。4.2 会话内状态管理让 Agent 记住上一次的结果代码解释器最容易踩的坑是模型以为环境是连续的实际上每次执行可能都是全新状态。比如 Agent 第一步加载数据到 df第二步打印 df 的前三行如果第二步脚本是全新的df根本不存在。解决方案有两种让每一步代码生成都自包含。在提示词里要求 Agent每次生成的代码必须包含必要的 import、定义和文件读取不要依赖上一次的变量。用工作目录持久化中间文件。脚本读取和写入同一个工作目录比如/workspace/data.csv第二步脚本重新pd.read_csv再处理。这样即使变量不保留文件还在。我用的是第二种为主因为更直观。代码解释器的工具描述里明确写一句工作目录是 /workspace上一次任务生成的临时文件都在里面模型就会习惯性地把中间结果落盘到目录里然后下一步再读出来。这避免了多次执行之间的记忆问题也让用户事后能看到中间文件。4.3 安全边界与资源配额设计给 Agent 开放代码执行能力就是把一个会编程的实习生请进你的系统你要给他画好活动范围。我整理的硬性边界直接抄作业超时控制单次执行不超过 30 秒超时直接 kill。我设过 60 秒结果 Agent 写了个死循环查半天都没响应体验很糟后来统一改 30 秒。内存限制Docker 里用--memory256m跑数据量大的任务可以放宽到 512m但别超过 1g。** CPU 限制**--cpus1足够太多并发容易把服务器挤爆。网络隔离默认--networknone。如果某个任务确实需要联网单独开放白名单域名。文件操作白名单限制只能读取/workspace不允许访问系统目录。标准输出截断stdout超过 4000 字符后截断防止模型上下文被刷爆。注意代码解释器是能动手的工具也是风险最高的工具。即便关了网络恶意代码也可能通过读文件、大量文件 IO 来搞破坏所以不要把非可信来源的数据直接喂给它也别让它运行在拥有敏感数据权限的环境里。最理想是单独的虚拟机或专用容器权限最小化。5. 三件套的协同编排从问题到答案的完整流程三个工具单独都能干活但它们各自的强项只有在配合起来时才最有威力。我常用一个任务来测试 Agent 的编排能力统计某个城市近 30 天的平均日照时长并画一张趋势图。这个任务信息是动态的、计算是精确的、输出是可视化的单靠任何一个工具都完不成。下面是编排流程。5.1 任务拆解与工具路由Agent 接到问题后第一步不是直奔代码解释器而是先做计划。我在系统提示词里做了这样的工具路由逻辑先检索近 30 天日照时长不是模型训练数据里能直接查到的所以先用 SearxNG 搜天气数据或历史日照记录找到可引用的来源尽量拿到数字或数据表。再计算如果数据里有复杂的换算要求比如把天文学的日照公式系数代进来就用 WolframAlpha 验算公式是否正确。最后执行把搜集到的数据整理成脚本用代码解释器跑出趋势图。对应到 Agent 实现上就是让模型按 ReAct 模式循环思考 → 调用工具 → 观察结果 → 再思考。5.2 上下文传递的设计工具之间传递信息关键路径有三个搜索 → 代码搜索得到的数据往往是文本不能直接拿去画图。需要 Agent 自己做一个信息抽取的中间步骤把搜索到的一些数转成结构化的数组。我在系统提示词里写了一句话当你收集到数值数据先用代码解释器的 json.dumps 把中间结果写成/workspace/sunshine_data.json供后续脚本读取。WolframAlpha → 代码如果查询的结果是某个公式系数比如某个常数代码解释器脚本里要能引用这个数值。正确做法也是把它写进临时文件或者直接内嵌到生成的 Python 代码里我发现直接把数值内嵌到代码里更不容易出错省得读文件的代码平白多出错机会。代码 → 用户代码解释器返回的stdout里只有文字描述图片需要落盘到/workspace/output.png。你的工具层要负责把文件转成链接或 base64才能真正让用户看到图。我在代码解释器执行后会检查/workspace下是否有新增的图片文件自动追加到回复内容里。这个流程跑通后你会感觉到 Agent 不再是一个回答问题的机器而是一个完成项目的助手它先自己做功课查资料再动手分析最后把成果交付到你手上。6. 实战中的坑与优化来自数十次真实调用的教训每个工具单独跑的时候都挺好一旦组合起来麻烦事就排队上门。我在几次 Agent 调试中踩了不少坑挑几个最有代表性的讲。6.1 搜索结果的脏数据污染问题SearxNG 聚合的结果质量并不稳定。默认返回里经常夹杂一些 SEO 垃圾站点、内容农场、甚至页面乱码。给模型喂这种数据它可能在推理过程中被带偏引用一个根本不存在的专家结论。我的优化手段结果置信度过滤优先抓含数字、日期、官方域名的结果排掉 URL 里带明显垃圾特征如/out/、partner的。摘要截取前 250 字符搜索引擎摘要中前面通常是页面自述后面经常是无关噪声。短摘要可以有效过滤掉大部分文本垃圾。模型二次确认让 Agent 在引用搜索内容时必须附上来源链接。如果后续推理中的结论和链接内容对不上触发重新检索。这个优化不是让检索变慢而是让检索后的信息更干净模型拿到的结论可信度明显提升。6.2 WolframAlpha 用自然语言长句提交导致的失败第一次接入时我图省事直接把用户问题原封不动转给 WolframAlpha结果一半请求都返回 didnt understand your query。帮我算一下从 2020 年 1 月 1 日到今天一共有几天这种带背景的长句子WolframAlpha 根本听不懂得改写成days since 2020-01-01。优化方式是在工具函数里加查询重写提示如果请求方返回未识别让 Agent 用英文短查询重试。目前我用的是一个简单的二次试错循环def wolfram_query_with_retry(query: str): resp call_wolfram(query) if didnt understand in resp: better_query llm_shorten_query(query) # 让模型压缩查询词 return call_wolfram(better_query) return resp这个循环往往一次重试就成功代价极低但成功率提升显著。6.3 代码解释器的路径混乱与依赖缺失Agent 生成代码时经常会出现路径硬编码错误。比如它写open(data.csv)但执行环境的工作目录是/workspace用户上传的文件却在/workspace/uploads。一旦路径没对齐Agent 就会对着一个 FileNotFoundError 循环尝试非常浪费时间。我的解决方法是在代码解释器工具描述里放一个标准开卡模板要求 Agent 每个脚本开头都这样写import os, sys BASE /workspace # 所有相对路径必须基于 BASE # 如果文件在子目录比如 uploads, 请写 os.path.join(BASE, uploads, data.csv)同时工具层在返回报错时额外做一个自动告警如果stderr里出现 No such file 或 ModuleNotFoundError就给模型追加一条提示工作目录为 /workspace请先ls /workspace查看已有文件再用绝对路径操作。这个微小的反馈机制能省掉我过去一大半的排错时间。依赖缺失是另一个高频问题。沙箱是精简 Python 镜像默认没有 pandas、matplotlib。所以我在代码解释器工具描述里明确写了可选依赖清单pandas, numpy, matplotlib, openpyxl, requests虽然网络隔离requests 可能用不上并且做了一个启动时自动 pip install 最小集。如果 Agent 尝试 import 一个未安装的库我会让返回信息里包含可选装以下库引导它改用已提供的库。6.4 编排顺序让工具按成本从小到大调用这是我个人的核心心得。工具调用是有成本的SearxNG 查询几秒单位成本低。WolframAlpha 查询几秒到十几秒且 API 配额有限。代码解释器执行可快可慢但每次执行都可能消耗 token 来接收输出修正错误还要再跑。所以我在提示词里强制约束工具的调用顺序优先搜索获取线索能用 WolframAlpha 短答案解决就别上代码解释器代码解释器只在需要处理多步骤数据/可视化/批量计算时调用。这能极大减少 token 消耗和任务失败率。实际测试中一个复杂问题如果一开始就上代码解释器经常出现数据都没拿到就急着画图的空跑按成本次序调用后同样的任务平均减少 2-3 次无谓的工具调用。工具不在多在于能找到各自的定位并且配合得当。SearxNG、WolframAlpha 与代码解释器这三件套一个管看一个管算一个管做正好覆盖了 Agent 日常任务里最高频的三种需求。如果你刚开始搭 Agent 工具箱我建议先把这三样跑通再考虑加语音、加数据库操作之类的扩展。最后再分享一个我在实际维护中的习惯给每个工具加一层非常薄的日志记录记录每次调用的参数、耗时和返回码。它不能直接提升 Agent 能力但在你排查为什么 Agent 给了一个奇怪回答的时候日志是最有效的事后证据。工具栈越复杂日志越值钱。
阅读完成 · 觉得有帮助?