十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

LLM不会“跳”:大模型应用如何用RAG与Function Calling搭台阶

LLM不会“跳”:大模型应用如何用RAG与Function Calling搭台阶 如果你最近在做一个 LLM 应用或者正准备把大模型接进业务系统我建议你先理解一句话LLMs can’t jump。这个说法不是某个模型的 bug也不是某个框架的缺陷而是一个很容易被忽略的事实大模型擅长生成下一个 token但它并不会主动“跳”到一个正确的结果也不会像搜索引擎一样在可行路径里做选择。它更像一个知识丰富但需要有人给它铺好台阶的助手而不是一个能自己翻山越岭的规划器。这篇文章特别适合在捣鼓 RAG、Agent、Function Calling、MCP 或者多步工作流的开发者。你可能会遇到“Demo 能跑但真实任务总是断”“模型好像会调用工具但调用完就乱写结果”“让它做三步以上的事情它中途就忘了目标”这类问题。这些问题几乎都能归结到同一个原因把不该让模型跳的“跳跃”交给了模型。最值得关注的不是换更大的模型而是把每一次跳跃拆成可验证、可回退、可重试的台阶。1. 先理解“不会跳”到底是什么意思1.1 模型生成的是文本不是搜索过程LLM 的工作方式是根据已有上下文逐个预测下一个 token 的概率分布。这个机制决定了它能写出很通顺的文字、给出看起来合理的回答但它没有真正执行“搜索”“回溯”“多路径比较”这些规划行为。所谓“跳”在任务中往往表现为从问题直接跳到结论从原因直接跳到结果从指令直接跳到动作中间缺少验证和纠错。举个例子。你让一个模型处理“从 A 仓库调货到 B 门店如果 B 门店缺货就自动从 C 供应商补货”这个需求。模型可能直接回答“从 A 调货如果不够就从 C 补货”。这个回答看起来正确但它没有真的去查 A 有没有货也没有判断 C 供应商的补货周期。换成 Agent 场景问题更明显模型可能声称“我已调用库存接口”但实际接口返回错误它还是继续往下写。这就是“跳”出来的幻觉。所以第一件事是调整预期LLM 是语言理解与生成组件不是决策引擎更不是可靠的执行引擎。凡是需要读状态、走流程、做判断的环节都应该放到代码层或工具层。1.2 “会说话”和“会做事”之间的距离模型能输出一段像模像样的 JSON不代表它能正确完成一次工具调用。真正让模型“做事情”的是外围代码定义工具、传参、执行请求、解析返回结果、判断成功或失败。这一整套链路里任何一个环节都可能出错而且大多数错误不是模型“蠢”而是你默认它能自己处理。Function Calling 也好MCP 也罢本质都是给模型一扇门让它把“意图”转成“结构化调用”。但门后面的路需要你去走。模型可能把参数填成残缺的也可能填一个不存在的城市名更可能为了凑格式而编造返回内容。开发时一定要把它当作“会说话但经常马虎的实习生”而不是“全自动机器人”。我见过不少翻车项目都有共同姿态把所有业务逻辑塞进一个大 prompt让模型“一步步思考”最后直接执行模型输出的 JSON。结果通常是格式偶尔错、字段偶尔缺、参数偶尔幻觉批量跑起来之后错误五花八门。更稳的做法是把任务拆成小步每一步输入是什么、输出是什么、需要哪些工具、失败怎么办都在代码里显式定义。1.3 跳跃失败会以什么形式出现多步任务做到中间断掉模型不再按流程走。工具调用之后模型没有读取真实返回值直接编造结果。上下文一长模型把最初的约束条件忘了。模型输出一个看似合理的 JSON但字段名和工具定义不一致。批量任务里几条数据成功、几条失败失败原因各不相同。这些都不是偶发问题而是架构设计里缺少“台阶”导致的。下面几章我会从 RAG、工具连接、编排、精度和排查这五个方向把台阶补法讲清楚。2. 那些看起来会跳的能力底层靠的是外部模块2.1 RAG 补齐的是“记忆跳跃”但检索链路要自己搭很多人以为 RAG 是让模型“记住新知识”。准确说RAG 是把知识的存储、索引、检索放到外部系统里模型只负责根据检索出来的片段生成答案。生成前的那一段检索就是一次“记忆跳跃”的台阶。常见的 RAG 链路包括文档切分、向量化、向量索引、相似度检索、重排、拼装上下文。每一步都可能出问题。比如项目启动时报“文本向量 API 未配置”之类的错误通常不是模型坏了而是 embedding 服务的 API Key、模型名或者网络配置没处理好。遇到这种情况先做一次最小验证拿同一句话调用向量接口确认返回的是一个固定维度的向量数组。如果这一步都通过不了后面检索质量好坏无从谈起。我把 RAG 的一条经验写在这里不要一上来就追求高精度召回先把“能不能稳定拿到向量、能不能稳定搜出前三名”跑通再优化切分和重排。很多 RAG 项目跑出来的效果差不是模型不好而是文档切分切坏了或者检索阈值太低导致模型看到一堆不相关片段。2.2 Function Calling 与 MCP工具连接要能“被验证”Function Calling 是目前让 LLM 接触外部世界最主流的方式之一。你给模型一个工具清单模型根据用户问题选择合适的工具并输出一个结构化参数。这里最容易被忽略的是工具调用完成后的返回结果必须经过代码校验再交给模型继续处理。举个例子定义查询天气的工具时参数 schema 可以写成这样{ name: get_weather, description: 根据城市名查询当前天气, parameters: { type: object, properties: { city: { type: string, description: 城市名例如北京 } }, required: [city] } }模型输出{city: 北京}只是第一步。你的代码要去调用真实天气接口拿到返回结果后再拼进下一轮对话。如果你把模型当成已经执行成功的人直接让它根据“假想的结果”继续回答错误就会一路向下传播。Function Calling 里最常见的错误是模型输出空参数、参数与 schema 不匹配、或者返回了工具里不存在的字段。处理方案不是责怪模型而是给代码加一层“参数校验 失败重试 重试上限”。MCP 做的事情是把“模型连接到外部工具”这件事标准化。你可以用 MCP client 把数据库、文件系统、网络服务等能力暴露给 LLM。但连接之后权限问题马上出现。不要给模型一个万能工具让它可以执行任意命令、删除文件、写数据库。正确的做法是只暴露当前任务真正需要的最小动作集并在执行前再做一次白名单校验。2.3 模型路径和配置文件启动失败的第一个排查点很多时候你以为“模型能力不行”其实项目根本没有加载到模型。本地部署 LLM 时模型文件路径、量化版本、配置文件都特别容易出问题。比如在 ComfyUI 这类工具里extra_model_paths.yaml如果路径没有写对模型目录扫描不到启动时就不会加载指定的模型。表现可能是“界面正常但模型列表为空”也可能是“加载时报错”。这类问题有个通用处理思路配置文件尽量使用绝对路径。启动日志里确认扫描到了哪个目录有没有识别出模型文件。如果路径包含中文、空格或特殊字符先改成简单路径测试。确认模型文件完整不是下载了一半。这些看起来和 LLM 本身无关但在实际排查中占了相当大的比例。判断标准只有一个启动日志里能看到模型成功加载并且在显存或内存中占用符合预期。2.4 为什么需要编排框架如果你只是调用一次 API 做文本分类不一定要引入框架。但如果你要做“检索 - 判断 - 工具调用 - 结果再生成”这种多步骤任务纯手写状态管理和重试逻辑会很痛苦。编排框架比如 Spring AI、LangChain 这类层次或者自己写的 Workflow 引擎能帮你把链路组织起来。但我对框架一直有个提醒框架会掩盖细节。很多框架默认做了重试、缓存、日志出错时你反而不知道是哪一步失败的。所以哪怕用了框架也要保留每一步的原始输入输出日志。框架最大的价值不是“自动把 LLM 变得更强”而是让“先检索、再调用、后生成”这个台阶结构更清楚。3. 把“跳跃”换成台阶一个多步任务的落地流程3.1 先给任务分层接到一个需求时不要直接写 prompt。先画一条链路把整个任务分成节点。每个节点问两个问题这一步需要理解自然语言吗这一步需要读取或修改外部状态吗整体可以分成六层层级职责由谁完成输入规范化清理、校验、格式转换代码意图识别判断用户想做什么LLM参数抽取从文本中提取结构化参数LLM工具执行请求外部系统、数据库、API代码结果校验判断工具返回是否有效代码输出生成把结果整理成自然语言LLM模型只承担意图识别、参数抽取和输出生成其他环节不要让它“代劳”。3.2 先跑通一条最小数据不要一次上几十条数据。先选一条最简单、最典型的样本把整条链路手动跑一遍。运行大模型时先确认几个关键参数# 伪代码示例具体参数以你使用的 SDK 为准 response client.chat.completions.create( modelyour-model-name, messages[ {role: user, content: 查询北京的天气} ], tools[weather_tool_schema], temperature0.2, max_tokens300, )这里我特别建议把temperature调低。多步任务和工具调用场景不需要太多随机性稳定比“有创意”重要。max_tokens也不要给太大给够当前步骤输出空间即可。输出太长既浪费资源也更容易在中间出现截断。拿到返回后先人工确认三件事模型有没有选择正确的工具参数是否完整返回内容是否能被正常解析。如果这三步都正常再去接真实工具调用。3.3 工具调用后的返回必须重新回到对话里工具执行完你应该把结果作为一个新的消息加入消息列表再让模型基于真实结果继续回答。如果工具调用失败不要假装成功。要么把错误信息返回给模型让它在重试次数内调整参数要么直接停止流程进入人工异常分支。这一步最容易踩的坑是“模型已经生成了一段回答但你发现工具调用了结果还没写进上下文里”。这会导致模型基于空状态瞎编。我习惯的写法是每次工具返回都带上一个唯一标识回答结束后还能回溯到具体是哪个工具、哪个参数、哪个返回码。3.4 批量任务先想清楚幂等和失败重试批量跑 LLM 任务不能只看“能不能并发”。要提前想清楚几件事每个任务有没有唯一 ID方便日志追踪。输出文件怎么命名避免重复跑的时候互相覆盖。失败任务要不要重试最大重试几次间隔多久。如果任务执行到一半进程崩溃重启后能不能接着跑还是从零开始。我见过很多批量场景翻车原因不是模型不行而是任务没有幂等设计。第一次超时重试结果重复写入了一遍或者进程重启后已经生成的文件被新任务覆盖。要避免这些问题建议先加一个任务状态表待处理、处理中、成功、失败、已跳过。每一步更新状态而不是靠“跑完再说”。4. 资源、精度与推理引擎低配环境更要理解边界4.1 模型体积、显存与上下文长度本地跑 LLM第一约束通常是显存或内存。模型越大约聪明这是事实但资源占用也会快速上升。低配机器不代表不能用你要做的是把模型变小、上下文变短、并发数降下来。如果显存不够可以选量化版本。量化会让模型体积和资源占用降低但输出质量可能下降。判断标准不是“跑起来没有”而是“同一批测试数据量化前后结果是否稳定”。如果只是做代码补全、文本分类这类容错性高的任务量化的影响通常可控但在工具参数抽取、结构化学术写作这类对格式和细节要求很高的任务里要谨慎测试。上下文长度也是一个隐藏变量。输入越长显存占用越高单次请求耗时越长。很多项目不是模型跑不动而是每次请求都把所有历史记录塞进去导致上下文越来越长速度越来越慢。建议按任务裁剪上下文而不是无脑拼接。4.2 fp16、fp32、bf16 怎么选讨论这个问题时先理解三种格式的差异格式精度动态范围资源占用使用场景fp32高常规最大小模型、调试、对数值敏感的任务fp16中容易溢出中很多训练场景常见但部分推理可能不稳定bf16较低很大中大模型训练和推理常见数值范围更安全实际开发中不要因为你看到某个帖子说 bf16 好就立刻切换。先用同一批测试数据跑一遍比较输出完整性和稳定性。如果模型在 fp16 下出现 NaN、数值溢出或结果异常可以试试 bf16 或 fp32。很多推理框架已经默认做了最优处理你不需要手动改但要会看日志里实际加载的精度是哪个。4.3 本地引擎、远程 API 与 Mac 场景远程 API 的好处是部署简单、开箱即用适合快速验证坏处是数据要离开本地环境长链路调用可能有网络延迟和成本问题。本地推理适合隐私敏感、离线或长期重复调用的场景但需要你处理模型文件、依赖库、推理引擎兼容性这些问题。如果你在 Mac 上做本地推理先关注推理引擎对 Apple 芯片的支持。同一个模型在不同引擎上的表现可能差别很大有的算子不支持有的导出格式不兼容有的只能跑 CPU 速度很慢。遇到“模型能加载但推理速度很慢”或者“某个操作不支持”时先不要怀疑模型型号去看引擎日志支持的算子和推荐格式。低配置环境就不要追求高并发一次只跑一个请求把超时时间放宽比花几个小时调并发参数更有意义。5. 常见失败现象与排查链路5.1 输出为空、直接报错、模型不加载这是一类“看似模型问题实际环境问题”的现象。排查顺序很重要先看日志有没有明确报错记录下错误码和行号。确认模型路径、模型名称是否正确模型文件是否完整。确认 API Key、API 地址、网络连通性远程调用还要看配额。确认显存、内存、磁盘空间是否充足。确认依赖库版本和推理框架版本是否匹配。如果项目在本地启动时报“模型加载失败”但又没有详细日志我会先运行一个不带复杂依赖的小脚本只加载模型打印一行输出。这个最小化测试能快速排除配置和依赖问题。5.2 Function Calling 失败或返回垃圾参数如果你定义的工具调用经常失败按这个顺序查现象优先排查常见原因模型没有调用工具工具 schema 说明是否清晰工具名或描述太长模型没理解参数缺失工具 schema 的 required 字段字段命名不直观或中文语义有歧义参数值幻觉返回结果校验逻辑没有把工具真实返回回传模型在编调用结果报错工具执行代码、网络、权限工具地址不对、鉴权失败超时或重复调用幂等设计、超时设置同一条任务重试了多次但没去重我习惯在工具调用的入口处打印一条完整 JSON 日志包含模型原始输出、解析后参数、工具执行状态、返回结果。排查时直接看这一条日志比反复猜要快得多。5.3 多步任务卡住或跑飞多步任务最常见的两个问题一个是模型在某一步陷入反复生成相似内容没有进展另一个是流程跑着跑着超出了预设步骤资源被浪费。应对方法是显式设置循环上限和超时时间。比如 Agent 循环最多执行 5 轮每轮最多 30 秒超时直接终止并进入降级分支。另外要检查状态变量。很多多步任务跑飞是因为上一步的结果没有正确保存下一步只能在“记忆模糊”的前提下继续。务必把每一步的输出显式存储下来并在下一步开始时确认依赖字段存在。5.4 RAG 检索结果差RAG 效果不好时很多人第一时间换模型。我建议先检查检索链路输入文本是否被正确切分是否有长文本截断问题。embedding API 是否配置正确返回向量的维度是否一致。检索数量是否合理太多会塞满上下文太少会漏信息。是否有重排环节重排模型是否生效。阈值设置是否太严或太松。如果连续几轮检索结果都和问题无关先用一个最短的问题验证把问题直接和目标文档的一小段进行向量相似度测试。相似度正常再看文档切分相似度不正常先检查向量模型和索引。5.5 工具权限别给模型“能拆家”的能力最后一定要提权限问题。LLM 应用落地时最容易出现的安全风险不是模型本身而是你给模型的工具权限过大。一个能读文件、写数据库、执行系统命令的 Agent一旦被注入恶意提示词风险会非常高。我的原则是权限最小化只暴露当前任务必须用到的工具。工具本身再加一层白名单校验例如只允许操作特定目录、特定表、特定接口。高风险操作先走人工确认即使模型已经“决定”要做也要由代码拦截。记录工具调用日志方便事后审计。这不是为了限制能力而是为了让你敢把 Agent 放到真实业务里。6. 落地判断什么场景该用 LLM什么场景别硬上6.1 适合用 LLM 的场景文本摘要、改写、翻译、分类。从非结构化文本中抽取关键信息比如从邮件里提取会议时间、负责人。自然语言到结构化查询的转换比如把“查一下上个月销量”变成 SQL。代码补全和解释。知识库问答但需要和 RAG 链路配合。这些场景的共性是对“语言理解”要求高对“精确状态修改”要求低即使偶尔生成有误差也容易被人工或代码发现。6.2 不建议硬上的场景精确数值计算尤其是金融、计费、库存扣减。强一致性的多步骤状态流转比如订单状态机。高并发、低延迟请求比如每秒几千次的实时过滤。不可失败的关键路径比如直接执行删除或支付操作。这些场景更应该用规则引擎、状态机、脚本或业务代码实现。LLM 可以让用户交互更自然但不应该成为唯一执行者。6.3 我的最小落地清单如果你准备开始做 LLM 应用我会建议按这张清单过一遍明确这一步是不是真的需要 LLM。定义输入和输出的 schema用代码校验。先用一条小样本跑通全链路。工具调用后必须校验返回结果并回传给模型。配置日志记录每一次请求、工具调用和错误。设置超时、重试次数和任务状态表。批量跑之前先测试幂等和失败恢复。只给模型必要的最小工具权限。把这些经验沉淀成团队内部的 wiki 或检查清单不要只存在某一个人的笔记里。LLM 项目真正难的不是让模型“变聪明”而是把模型放进一套可靠的工程框架里。每次遇到“它怎么又乱跳了”的时候先回来看这条链路输入有没有校验工具有没有真实执行结果有没有验证流程有没有超时。你会发现多数问题不是模型跳不过去而是我们忘了给它搭阶梯。
返回列表