
很多开发者会在技术社区分享自己做的工具标题里最常出现的一句话是这是我做的 LLM 工具而且我日常离不开它。这句话看起来像表达成就感实际说的是另一件事——当 LLM 相关的工作流稳定下来之后真正留下的是那些能复用、能批量、能查日志的小工具而不是某个炫酷的 Demo。这篇文章就把我自己攒下来的一套 LLM Tools 按实际使用频率拆开讲重点覆盖 CLI 问答助手、本地 RAG 问答、批量文档处理、Agent 工具调用管理这几块。适合正在把 LLM 嵌入日常开发流程、而不是只停留在网页聊天窗口的读者。最值得关注的是我判断一个工具“能不能留下”的标准以及遇到报错时的排查顺序。1. 先想清楚为什么自己造 LLM Tools而不是直接用 Web 平台1.1 现成平台解决的是“对话”自己造工具解决的是“流程”网页聊天平台解决的是临时提问。比如你有一段代码看不懂把代码复制进去等它给出解释这个场景用 Web 界面完全够。但到了真实开发里需求会变每天要总结一批 git 提交记录给十几篇文章写摘要把多个报告整理成统一格式。这些操作在网页里复制粘贴等于重复劳动。更麻烦的是你每次都要重新组织 prompt得到的结果格式可能还不一样后续还要手工整理。所以我会想自己做一个小工具本质上是给模型的 API 调用包了一层“流程外壳”。这个外壳负责组装输入、调用模型、校验输出、写日志。调用哪家公司、哪个模型不重要重要的是每次执行都能得到可预期的结果。这个需求不是官方 Web 平台能解决的。商业平台也有 API但直接裸调 API 并不等于“工具”。裸调 API 只解决了“能请求模型”这个问题上下文怎么组装、批量任务怎么调度、失败要不要重试、结果存到哪里这些才是工具的活。1.2 我给自己定的三条标准可复用、可批量、可观测做一个工具之前我会先拿三条标准问自己可复用同一个 prompt、同一组参数能不能通过一个命令或一个脚本反复执行而不是每次手动拼字符串。可批量输入一个文件列表或目录能不能一次性处理完并且给每个文件单独输出。可观测单次调用有没有日志记录失败时能不能快速定位是哪一步出了问题。如果不符合这三条我不会急着写代码会先看看有没有现成方案。很多项目做着做着就放弃了原因不是模型不行而是第一版目标定得太大既要界面又要任务编排又要知识库最后运维成本超过收益。模型可以看成发动机工具是整车。发动机再强没有方向盘、仪表盘和刹车也没法当日常代步工具。这个类比放在 LLM 工具上非常合适。不要只想“模型能力够不够”要先想“我造的这辆车能不能安全开到目的地”。我一直建议从最小闭环开始先做一个命令行工具能读 prompt、能调用模型、能写日志再慢慢加功能。2. CLI 问答助手把每次模型调用变成一个可复用命令2.1 场景反复复制提示词实在太烦开发时最常见的需求是让模型解释一段代码、给一个报错信息找思路、生成某个函数的注释。如果每次都要打开网页、复制代码、粘贴、等回答、再手工整理一次两次还行次数多了会很烦。我自己最常用的工具就是一个命令行问答助手逻辑非常简单读取你给的 prompt按需拼接文件内容作为上下文把请求发给模型然后把结果输出到终端或指定文件。这个工具解决的问题很直接把“和模型对话”变成“执行一条命令”。命令是固定的prompt 是可配置的结果是可以落盘的。2.2 设计模型配置、上下文文件、输出落盘工具第一版只做了三件事配置、调用、输出。配置通过环境变量完成export LLM_API_KEYyour-key export LLM_MODELyour-model-name export LLM_BASE_URLhttps://api.example.com/v1不建议把密钥写死在代码里。环境变量能避免密钥进入版本库也方便切换模型服务。模型名、API 地址、密钥从环境变量读取这样代码本身不绑定单一厂商。调用方式类似这样llm-ask 解释下面这段代码 --ctx src/main.py --out answer.md--ctx表示把文件内容拼进 prompt--out指定输出路径。默认不传--out时直接打印到终端适合快速验证。我再加一个--log参数把每次请求的 prompt 原文、模型名、耗时、返回结果写入日志文件。这个日志一开始觉得多余等到排查问题时才意识到多重要。输出落盘比从终端复制更可靠。批量处理时结果直接写到文件里后面可以用脚本统一检查。2.3 最基础的调用骨架下面是一个简化示例用通用 HTTP 接口演示不绑定具体 SDKimport os import requests def ask_llm(prompt: str, model: str None, max_tokens: int 1024): api_key os.getenv(LLM_API_KEY) base_url os.getenv(LLM_BASE_URL, https://api.example.com/v1) model model or os.getenv(LLM_MODEL, your-model-name) payload { model: model, messages: [{role: user, content: prompt}], max_tokens: max_tokens, } headers {Authorization: fBearer {api_key}} with open(llm.log, a) as f: f.write(fPROMPT: {prompt[:200]}\n) resp requests.post( f{base_url}/chat/completions, jsonpayload, headersheaders, timeout60, ) resp.raise_for_status() return resp.json()[choices][0][message][content]这个骨架已经具备“配置、调用、日志”三个要素。实际使用时要补上超时重试、token 统计、JSON 解析和异常分类。不过第一步能跑通就行不要一开始就追求完美。2.4 怎么判断这个工具好不好用我一般会先跑一条样例确认三件事请求能不能返回、返回内容完整不完整、日志有没有正常记录。如果同一个 prompt 每次输出差异很大先看温度参数。温度越高输出越随机做工具时通常把温度调低一些让结果更稳定。具体调到多少要看场景做创意生成可以高一点做格式化和数据提取要低一点。这个工具最大的价值不是“接入模型”而是“每次调用都被记录下来”。后续不管是排查问题还是统计成本都有据可查。如果只是学习默认配置够用如果要长期使用就要把日志、输出目录和任务队列提前整理好。3. 本地 RAG 问答让模型基于我的资料回答而不是凭空生成3.1 为什么总绕不开 RAGLLM 训练用的是公开数据它不知道你项目里的内部约定、历史决策、团队命名规则。你问它“这个项目里为什么要用某种模式”它只能给通用答案不一定贴合实际情况。一种办法是把资料作为上下文直接塞进 prompt。这个方案在小文档场景下没问题但文档一多token 会超限成本也高。RAG 的做法是先检索再生成从本地文档库里找出和问题最相关的几段内容再把这几段拼进 prompt最后让模型基于这些片段回答。这样既控制了 token 数量又让模型有资料可依。如果你只有一个几十行的 README不需要 RAG直接贴进去更省事。RAG 解决的是“资料多到不能全塞进去”的场景。3.2 落地顺序加载、切块、向量化、检索、生成我实现 RAG 工具时按五步走加载文档读取.md、.txt、.pdf以及代码文件。切块把长文档切成固定长度的小块每个块之间留一点重叠区间避免把一句话切到两个块里。向量化用 embedding 模型把每个块转换成向量。检索把问题也转成向量和库里所有向量算相似度取 top-k。生成把 top-k 文本和问题拼进 prompt调用模型回答。切块大小要结合你的文档特点来调。块太小会丢失上下文块太大会混入无关内容。重叠区间一般按块大小的 10% 到 20% 设置不能完全没有否则边缘语义容易被切断。简化版流程长这样def rag_answer(question, docs, top_k3): chunks split_chunks(docs) index build_index(chunks) hits index.search(question, top_k) context \n---\n.join(hit.text for hit in hits) prompt f基于以下资料回答问题\n{context}\n\n问题{question} return ask_llm(prompt)这只是最简流程。实际要处理文件类型识别、增量索引、向量库持久化、文档更新后重新索引等一堆问题。3.3 检索质量怎么看这是最容易踩坑的地方。我一般不会直接把检索结果丢给模型而是先把 top-k 片段打印出来自己看一眼检索是否相关。如果检索出的片段和问题明显不相关问题不在生成环节而在切块或向量化。此时可以调整切块大小、增加重叠、换 embedding 模型或者增大 top-k。如果片段相关但模型回答时没有引用这些片段说明 prompt 里的指令不够强。比如模型可能习惯性地用泛知识回答。针对这种情况我会在 prompt 里强调如果资料中没有相关内容就明确说不知道不要自己补充。另外要注意向量相似度不等于语义正确的检索。对于代码仓库、内部缩写很多的文档纯向量检索不一定比全文搜索好用。我自己的习惯是“向量检索 关键词过滤”结合先缩小范围再精排。3.4 RAG 不是万能药文档特别少、关键词明确时用 grep 或全文搜索更快、更可控。语义检索解决的是“你不知道该搜什么关键词”的场景。RAG 工具也需要注意索引更新。文档改了索引不重建检索结果就会滞后。我一般会为本地知识库维护一个“文档版本号”每次更新资料后强制重建索引。本地资料库变大以后启动时间和内存占用都会上升。低配机器要控制文档数量和向量维度不然光是加载索引就会卡很久。把 RAG 工具理解成“给模型配一个可检索的资料库”会更容易设计。它的价值在于让回答有依据但不等于所有资料问题都能被它解决。4. 批量文档处理单条能跑通之后真正的工程问题才出现4.1 单任务和批量任务的区别很多人第一次写批量脚本时会以为单条能跑通批量也能跑通。实际不是这样。批量任务要考虑四个问题输入列表要处理哪些文件从目录递归读取还是从列表文件读取。失败重试某个文件请求超时或返回异常是重试还是跳过。输出命名多个文件都叫 result.txt 会互相覆盖必须根据输入文件名生成唯一输出名。断点续跑跑到第 50 个文件时中断重新启动能不能跳过已经处理过的文件。如果这些问题没有设计好批量跑到一半报错前面 49 个结果还在但后面全部停掉整体效率很低。4.2 队列、并发和重试怎么设计我的建议是不要一上来就开最大并发。先跑单条看单次请求耗时、失败率、限流情况再逐步加并发。推荐顺序单条样例、小批量测试、全量处理。小批量测试可以选 3 到 5 个有代表性的文件包括一个空文件、一个长文件、一个特殊字符多的文件。这样可以提前暴露问题。重试策略要分类处理网络错误、超时可以重试但要加退避时间。429 限流降低并发等待后再继续不能立即重试。JSON 解析失败、输入格式错误不要反复重试先看日志定位问题。伪代码其实很直接for file in input_files: if output_exists(file): continue for attempt in range(3): try: result process(file) write_output(file, result) break except TimeoutError: continue这里的output_exists判断就是幂等设计。已经处理过的文件跳过重新跑就不会重复消耗 API 额度。4.3 输出文件和日志规范输出目录应该按照输入目录镜像文件名加上_summary或_output后缀避免覆盖源文件。每条任务至少记录这些日志信息源文件路径、输出路径、状态、耗时、输出长度。状态包括 success、failed、skipped。我还会把失败文件的路径单独写到一个failures.txt里方便处理完后统一排查。批量任务最怕的不是报错而是报错信息不足以定位是哪个文件出了问题。如果任务跑完发现某些结果明显不对第一件事不是改模型而是回去看输入文件和 prompt 拼接。很多时候是某个文件编码不对或者内容格式超出了预期。批量处理这件事真正体现工程能力的地方不是“能调用模型”而是“出问题之后能不能快速恢复”。日志和幂等设计就是恢复的抓手。5. Agent 场景Skills 和 Agent Tools 的区别以及编排框架到底解决什么5.1 工具调用和技能包的边界LLM 应用进阶一点就会碰到 Agent。这个领域里最容易混淆的两个概念是 Skills 和 Agent Tools。Agent Tools 是模型可以动态调用的原子能力。比如“搜索网页”“读取文件”“执行命令”“计算表达式”。模型在推理过程中决定什么时候调用哪个工具以及填什么参数。Skills 更像是一个预设的技能包把多个原子能力组合成一个稳定的流程。比如“生成周报”这个技能可以拆成读取 git log、调用 LLM 做摘要、按模板格式化输出。每一步是确定的组合顺序也是确定的。一个常见误区是把所有能力都叫 Agent Tools结果模型不知道什么时候该调用哪个。工具越多模型越容易选错。我自己的做法是确定性流程放进 Skills需要模型现场判断的才注册成 Tools。5.2 工具注册和 prompt 管理给模型注册工具时描述必须写清楚“什么时候用、参数是什么”。描述太模糊模型就不会正确调用。参数最好用 JSON Schema 定义让模型生成结构化调用参数。工具参数和 prompt 是强相关的。同一个工具如果描述改了模型调用行为也会变。所以我一般会维护一个prompts/目录每个模板一个文件模板里用变量占位而不是把 prompt 硬编码在代码里。示例模板结构你是项目助理负责处理代码仓库相关任务。 可用工具 - search_code: 在仓库中搜索代码片段参数 keyword - read_file: 读取指定文件内容参数 path - summarize_markdown: 把内容整理成 markdown 格式参数 content 任务{task}模板版本化非常重要。改了一次 prompt可能影响所有下游任务。没有版本管理的话你很难知道某个结果是用哪个 prompt 版本生成的。我自己踩过的坑是把所有工具一次性注册进去结果模型频繁调用不相关的工具既浪费 token又拖慢响应。后来调整策略按场景拆分工具集一次只给模型注册当前场景相关的几个工具准确率高了很多。5.3 编排框架的价值和边界编排框架能解决记忆、路由、错误恢复这些问题适合多步骤、多工具、需要长期记忆的复杂场景。比如一个任务需要先搜索资料、再做规划、再逐步执行框架能帮你管理状态。但并不是所有场景都需要框架。固定流程用脚本写更简单出问题也好排查。框架加进来之后反而多了一层抽象报错信息不容易直接对应到具体业务逻辑。真正让 Agent 稳定的不是框架本身而是每一步的输入输出都有约束和验证。工具返回的数据要校验调用参数要校验模型生成的中间决策也要记录。自动调用工具在真实环境中一定会出错。比如工具返回了空结果、参数格式变了、模型生成了不存在的参数。所以 Agent 场景一定要保留人工兜底。我会给 Agent 加一个“审批模式”高风险操作不自动执行只输出建议由人来确认。不要为了 Agent 而 Agent。简单的单步任务、固定流程任务传统脚本可能更合适。6. 精度、成本和稳定性LLM 工具上线前要盯住的三个问题6.1 fp16、fp32、bf16本地推理时绕不开的精度选择这三个是模型权重的浮点精度本地部署模型时一定会遇到。fp32 精度最高、显存占用最大、推理速度相对较慢。fp16 省显存、速度快但数值范围比 fp32 小某些场景下更容易出现溢出。bf16 的动态范围和 fp32 接近尾数精度低一些但整体稳定性好是大模型训练和推理里很常见的格式。对普通工具开发者来说如果调用 API精度通常由服务端决定不需要你管。但如果是本地部署模型就要根据显存和输出质量做取舍。精度显存占用推理速度稳定性常见使用场景fp32高较慢高小模型、关键任务、训练调试fp16中较快中显存有限的推理bf16中较快较高大模型本地推理、多数量化前检查判断标准很简单同一个 prompt 多跑几次看输出是否稳定同时记录显存占用和单次推理耗时。如果输出不稳定先不要急着换模型可以换个精度再试。低显存机器也能跑本地模型但要把并发数、输入长度、上下文窗口都调小。不要指望 8G 显存跑一个超大模型还能保持全量精度。6.2 成本控制缓存、模型分级、批量化调用 API 做工具成本是绕不开的问题。我从三个方向控制成本。第一是缓存。相同或相似请求直接命中缓存不再重复调用模型。比如公司内部文档摘要文档不变结果就不需要重新生成。缓存键可以用输入内容的 hash 加上 prompt 模板版本。第二是模型分级。简单任务用便宜的小模型复杂任务才用大模型。比如判断文件是否包含关键词小模型就行需要深度分析的长文档才用大模型。这个策略能省下不少成本。第三是批量化。多个文件可以打包到一个上下文里处理减少 API 往返次数。不过批量化会受上下文长度限制要先估算 token。如果一批文件加起来太长就分组处理。成本不是不花钱而是让每一分钱都花在必要的地方。我会在日志里记录每次请求的 token 数定期统计哪些任务消耗最多。6.3 稳定输出JSON 解析、超时、重试、日志如果工具需要解析模型输出最稳定的做法是让模型输出 JSON并给出明确 schema。但即使这样也不能保证每次都是合法 JSON。我会做一个容错解析先尝试json.loads如果失败尝试从文本中提取 JSON 片段再不行就标记失败并重试。超时、重试、日志三个东西不要混在一起。超时是单次请求的等待时间重试是超时后的策略日志是整个过程的记录。每次请求至少记录这些字段模型名、prompt 版本、输入长度、输出长度、token 数、耗时、状态码。有了这些才能定位是模型问题、网络问题还是工具自身问题。稳定输出比单次效果更重要。一次返回完美答案下次报错对工具来说是致命的。工具的价值在于可预期。7. 排查链路工具不工作的时候我按这个顺序查7.1 先看现象再查输入再查配置工具报错时我一般按这个顺序排查先看现象是启动报错、请求超时、输出为空还是输出内容不对。再看输入文件路径是否正确、文件编码对不对、文件是否为空、prompt 拼接是否漏了内容。再看配置API key、base_url、模型名、端口、输出目录写权限。再看参数temperature、max_tokens、top_k、超时时间、并发数。最后看依赖SDK 版本、Python 版本、系统缺库、内存和显存占用。这里最容易被忽略的是输入文件和权限问题。很多报错看起来像模型不支持实际是文件编码不对或者输出目录没有写权限。排查时不要急着改代码先把日志打开。没有日志就只能靠猜效率很低。7.2 常见错误和修复方向现象先查什么处理方向401/403API key、模型访问权限检查密钥和账号权限429限流降低并发加退避重试超时网络、单次生成长度调大 timeout减少 max_tokens输出为空输入文件、prompt 拼接检查文件内容和代码路径JSON 解析失败输出约束、解析容错强化 schema做容错解析本地显存溢出模型体积、并发数换量化模型降低 batch403 不一定只是密钥错误也可能是当前账号没有访问该模型的权限。429 不一定要换模型先退避重试。超时可能不是网络慢而是模型生成了太长内容把 timeout 调大或者限制输出长度会更有效。本地推理显存溢出时优先换量化版模型然后降低并发。不要一上来就加内存显存瓶颈不是内存扩容能解决的。7.3 哪些功能不要过度期待本地小模型的复杂推理能力有限适合摘要、分类、格式转换不适合长链条推理。如果你的任务是几十步的复杂规划小模型大概率会中途出错。RAG 不能解决全部资料检索问题。如果资料本身缺失检索做得再好也没有用。它解决的是“资料存在但不知道去哪找”的问题。Agent 自动调用工具在真实环境一定会出错。工具返回空结果、参数变了、权限不足这些都会发生。所以高风险管理场景一定要人工兜底。“支持某功能”不等于“支持所有格式”。测试时一定要用真实数据不要只用整洁的样例。真实数据里的特殊字符、超长段落、错误编码才是工具真正要面对的挑战。踩过几次之后我发现很多问题不是模型不够强而是输入、参数和日志没有处理好。先把单条任务跑稳再把批量跑通最后才考虑 Agent 化这条路对我来说是走得通的。做工具不是比功能列表多而是比在真实环境里能不能稳定用。你的工具如果能做到可复用、可批量、可观测就算没有花哨界面也值得长期留着。