前阵子在一篇论文标题里看到一个非常扎眼的判断:LLMs Can't Jump。当时我正好在测试一批真实业务文本,任务是让模型从合同第三段找到某个条款,再结合第五段的附件编号,判断两份材料之间是否存在冲突。单看每一段,模型回答都算正常;但只要把需求改成“先定位、再比对、最后判断”这种多步逻辑链,输出就开始飘。有时候只给条款,不管附件;有时候两个都提到了,但逻辑关系完全反了。
我一开始以为是提示词写得不够细,又以为是 temperature 设得太高导致随机性变大。调了半天,效果有改善,但极不稳定。直到看到这个标题,才意识到问题可能不在提示词,也不在解码参数,而在于模型本身不擅长从一个中间结论“跳”到另一个中间结论。
这篇文章想展开的,就是把“不能跳”这个判断翻译成工程语言。它不是一个学术冷笑话,而是非常接近真实使用边界的描述。真正重要的不是去责怪模型为什么不会跳,而是知道它在什么任务上会摔跤,然后设计流程去替它铺台阶。
1. 先理解“跳”到底是什么:不是长文本,是长依赖
1.1 从“走”到“跳”:一个关于中间状态的比喻
先说清楚这里说的“跳”指什么。
我见过很多开发者把模型表现差归因于“上下文不够长”。但“上下文窗口不够”和“不能跳”是两个完全不同的问题。上下文窗口解决的是“信息是否存在”,而“跳”解决的是“信息之间能否建立路径”。
打个比方。一个人沿着一条路往前走,每一步都踩在当前能看到的位置,这叫“走”。如果这条路中间断了几截,需要从一块石头直接跃到远处另一块石头上,中间没有落脚点,这就叫“跳”。人的直觉可以目测距离、估算力度,然后一次性完成跳跃。但语言模型不一样,它更擅长的是每一步都有明确依赖的推进方式,而不是直接跨越多个不可见的中间状态。
在自然语言处理里,这种“跨越”往往表现为:
- 答案依赖前文很远处的某个细节,且这个细节没有被后续内容重新提及。
- 任务成功路径上必须由模型自己生成多个中间结果,再基于这些中间结果往下走。
- 多个条件之间存在顺序、互斥或依赖关系,必须保持全局一致。
- 最终答案是否正确,需要回溯到很前面的信息才能验证。
这些场景都不是“字面距离长”,而是“逻辑依赖远”。你可以把一万字的上下文塞进去,模型也读得到,但它在生成每一步内容时,并不保证还记得前面的每一个细节,更不保证能自动完成多步逻辑推导。
1.2 上下文窗口变大,不等于模型能建立远距离关联
一个很常见的误解是:模型支持 128K 上下文,那我把整个项目文档放进去,它应该能自己搞定跨章节的规划了吧?
实际用下来会发现,上下文窗口变大的直接收益,是模型能“看到”更多内容,但不等于它能“组织”这些内容。它是逐 token 生成下一个字的,每一步都在做局部决策:当前 token 和它已经生成的内容之间要保持语义连贯。至于最初的第 2000 个 token 和现在的第 50000 个 token 之间该建立什么关系,模型没有机制显式保证。
所以在长上下文任务里,模型的输出质量往往会出现一段一段都挺合理、整体却合不上的现象。它会写出一段很有说服力的总结,但里面引用的事实可能来自错误段落;它会给出一个看起来完整的方案,但步骤之间的依赖是乱的。
这就是“不能跳”的直接表现:模型可以在单点信息上表现良好,可以在小范围内保持局部连贯,但当你要求它跨越多个中间状态去完成一次完整推理时,它并不会自动生成一条稳固的路径。它更倾向于输出“看起来像是那么回事”的路径,而不是真正按逻辑一步步逼近答案。
想明白这一点,再去看各种调参技巧,会发现很多努力都花在了错误方向上。
2. 为什么模型在需要“跳”的任务上总是翻车
2.1 下一个词的目标函数,天然偏好局部连贯
语言模型的核心训练目标,是给定之前的 token 序列,预测下一个 token 的概率分布。这个目标天然会更关注紧邻的、局部的关系,因为它在训练时看到的数据窗口本身就有比较强的局部连续性。
不是说模型完全没有能力处理远距离依赖,而是这种能力不是通过显式推理链路实现的。它更像是在海量文本里学到了某种“统计捷径”:当出现 A 类上下文时,输出 B 类内容是合理的。但在真正需要多步计算的逻辑任务里,单靠统计捷径是不够的。
举个例子,如果有人问你:“张三在第三段提出了一个条件,第五段里对同一个条件做了例外处理,那么针对李四的方案,最终应该采用哪个版本?”这个问题的答案依赖三段文本之间的实体对齐、关系抽取和逻辑优先级判断。模型如果只是通过局部概率生成,很容易在生成过程中把“例外”和“条件”的主次关系弄反。它不是不懂这句话,而是没有一种显式的“先建立约束图,再按约束图生成答案”的机制。
换句话说,模型在大多数情况下不是一个“推理引擎”,而是一个“续写引擎”。对于短依赖、单步逻辑的任务,续写引擎表现已经足够好;但对于多步逻辑,续写引擎会倾向于补全一个表面连贯的故事,而不是严格推进一条推理链。
2.2 一步偏了,后续不会自动纠偏
多步推理任务和单步问答之间最大的区别,是错误会被复制和放大。
如果让模型一步到位回答一个简单问题,即使判断有误,错误也只发生在单点。但如果任务要求它先提取关键实体,再判断实体之间的关系,再基于关系给出结论,那么每一步的错误都会进入下一步。
更麻烦的是,模型生成过程没有“回退”机制。它不会像人在做证明题那样,发现中间某一步不合理就回头修正。它只会基于已经生成的错误内容继续往下走,直到生成一个自洽但错误的结论。
我在实际项目里见过最典型的场景,是让模型从多份文档里抽取信息并生成对比表。单份文档抽取效果不错,但一旦要求它把多份文档的字段做交叉比对,它就经常出现这样的问题:A 文档的值是对的,B 文档的值也是对的,但两者之间的差异判断写反了。原因很简单:模型在生成对比结论时,需要同时记住两个来源的值、判断差值、还要写出结论。这个链路一旦超出它的“局部续写舒适区”,就会开始用统计倾向代替真实计算。
所以,说“颜色模型不能跳”,本质是在说:在成功路径较长、中间状态较多、每一步都必须严格依赖前一步的任务里,当前这类自回归模型存在结构性的脆弱点。
3. 四个信号:先判断你的任务是否要求模型去“跳”
3.1 用一张表判断任务的“跳度”
在开始写提示词、调参数、接 API 之前,我建议先做一次任务诊断。这个诊断不复杂,就是回答四个问题。
| 信号 | 判断标准 | 如果命中 |
|---|---|---|
| 远距线索 | 答案依赖的某个关键信息,出现位置与当前生成位置距离很远,且没有被显式重述 | 需要接入检索、重述或分步处理 |
| 中间状态 | 成功路径上,模型必须自己生成多个中间结果,再基于这些结果继续 | 先问能不能拆成多个独立子任务 |
| 顺序依赖 | 最终结论的正确性,取决于多个中间结果之间的先后或互斥关系 | 要用外部流程控制顺序,不能只靠模型 |
| 错误隐蔽 | 中间某一步出错时,最终答案看起来仍然合理,难以直接发现 | 需要把中间过程显式输出,再做校验 |
这四个信号如果都不命中,那么当前任务大概率是模型擅长的“近距离行走”,可以直接用。如果命中一个,可以先用提示词和上下文重述去缓解。如果命中两个以上,就要认真考虑,是否应该改变任务结构,而不是继续堆提示词。
3.2 为什么这条判断链路要放在调参之前
很多人遇到模型输出不对的第一反应是改 temperature、top_p,或者换更大的模型。这些做法不是没用,而是没有解决核心问题。
在“远距线索”这类问题上,调低 temperature 只会让模型更保守,但不会让它更擅长跨段关联。在“顺序依赖”这类问题上,换一个更聪明的模型可能会提高成功率,但很难做到稳定。因为问题不在随机性,而在任务结构本身超出了自回归模型当前的强项区间。
所以我的建议是,先把任务诊断做在前面。诊断完成之后,再决定是否需要换提示词思路、拆分子任务、加检索或引入编排框架。否则你很可能花了大量时间在一个走不通的方向上调参,最后得出“大模型不行”的结论。实际上不是模型不行,而是任务类型选错了。
这一步做完了,下一步才是真正的工程改造。
4. 工程应对:别逼它跳,替它修台阶
4.1 拆步骤:把一次多跳,拆成多次单跳
在所有工程手段里,最直接、最有效、也最容易被忽略的,是任务拆解。
既然模型不擅长一次性跨越多步,那就不要让它一次完成多步。把“从合同定位条款、比对附件、判断冲突”这个任务拆成几步:
- 第一步,让模型只做“条款定位”。输入是全文,输出是目标条款编号和原文摘录。
- 第二步,让模型只做“附件信息抽取”。输入是附件内容,输出是相关字段。
- 第三步,把前两步的结果拼成新上下文,再让模型做“是否冲突”的判断。
每一次调用,模型都只需要完成一段很短的单跳推理。它不需要同时记住所有材料的细节,也不需要自己维护一个庞大的中间状态。你替它把中间状态显式存了下来,再喂给它。
这样做表面上增加了几次 API 调用,但换来了两个非常重要的收益:一是每步输出可以单独检查,哪一步错了一目了然;二是每步的上下文可以根据需求定制,不需要把全部文本一股脑塞进去。
我在日常项目里会把这个模式叫“写台阶”:把模型不能跳的那段距离,用代码搭出可以踩的中间层。模型负责走,代码负责铺路。
4.2 接外部记忆:RAG、向量化接口与 MCP
拆步骤解决的是“长依赖”问题。还有一个问题是:当关键信息埋在很长的上下文里,模型不一定能准确找到。这时候你需要把“信息检索”从模型生成的过程中剥离出来,交给专门的检索模块。
常见做法是把文档切块,做向量化,存入向量库。用户提问时,先从向量库检索出最相关的几个片段,再把这些片段拼到提示词里。这个流程通常被叫做 RAG(Retrieval-Augmented Generation)。
实际项目里,向量化接口的配置比想象中更容易出问题。很多人遇到过“文本向量 API 未配置”之类的报错,这通常不是模型本身的问题,而是应用层没有把 embedding 接口的密钥、服务地址和模型名称配置完整。落地时建议先用一条只有几百字的样本文档,走通“切块—向量化—检索—拼接—生成”的完整链路,再开始处理真实长文档。不要等到项目上了生产环境,才发现检索结果全是无关片段。
除此之外,MCP 这类工具接入协议也在快速变成基础设施。它的核心价值,不是让模型自己变得更强,而是让模型能调用外部工具去获取当前数据、执行特定命令,比如查数据库、读文件、调业务接口。从“不能跳”的角度理解 MCP,它的真正作用是:把模型原本需要在内部完成的跨步骤操作,转变成可观察、可控制的外部调用。
这里要提醒一点:MCP 本身不解决逻辑问题。它解决了“模型拿不到最新数据”和“模型无法执行真实操作”的问题,但如果你的流程本身要求模型在多步之间保持严格逻辑顺序,还是需要外部编排来管住路径。
4.3 用编排框架管理依赖和顺序
当任务步骤变多之后,你不可能继续用脚本里的 if-else 去维护每一步的依赖关系。这正是 LLM 应用会把“编排框架”作为标配的原因。
编排框架做的事情,是让你把“先做什么、再做什么、什么时候需要用户确认、哪类错误要重试”这些过程逻辑显式写出来,而不是让模型自由发挥。它有点像一个项目经理:模型负责回答问题、生成内容、做局部判断;框架负责安排先后顺序、保存中间结果、决定何时调用外部工具。
有人可能会问:那 Agent 呢?Agent 不是可以让模型自己决定调用哪些工具吗?
Agent 确实提供了更大的灵活性,但灵活性也意味着不确定性。在严格多跳任务里,我更推荐把关键路径用编排框架固定下来,只在路径内部的单点判断上让模型做选择。也就是说,让 Agent 在台阶上自由移动,但不要让它决定整条台阶的走向。
实际接触过 Spring AI、LangChain 这类框架的人会发现,它们解决的问题高度重合:统一模型接入、管理对话记忆、串联工具调用、处理模型输出格式。它们解决的不是模型能力问题,而是工程可维护性问题。用上框架之后,你的应用更容易从“演示脚本”变成“可维护系统”。
4.4 别忽略精度选择和资源边界
在本地部署场景里,还有一个容易被忽略的坑:模型推理精度。
现在很多推理引擎默认用 fp16 或 bf16 来节省显存,速度也比 fp32 快不少。但精度降低会带来数值上的微小差异,在多数生成任务里没有感觉,在部分计算密集型任务里却可能造成输出不一致。
如果发现同样的输入,模型偶尔会出现完全不同的结果,可以先检查推理引擎是否做了量化或半精度转换。建议先把推理引擎切换回 fp32 或者更高的精度,重新跑一遍样例。如果高精度下问题消失,那说明当前任务对数值精度敏感,你需要根据显存和速度重新权衡。
反过来,如果只是做大规模文本分类、抽取、总结这类任务,fp16 或 bf16 通常够用,没必要为所有任务都上 fp32。这里的原则是:先确认精度是否是问题来源,再决定要不要牺牲性能。
5. 当输出不符合预期时,按这个顺序排查
5.1 现象分类:漏、错序、矛盾、幻觉
模型输出不符合预期,不是所有问题都叫“模型不够聪明”。我习惯先把问题分成四类:
- 遗漏:该提到的关键信息没提到。
- 错序:提到了所有信息,但先后或主次关系不对。
- 矛盾:不同步骤的结论之间互相冲突。
- 幻觉:生成了原文完全没有的内容。
这四种现象对应的处理方式不一样。遗漏问题通常要先检查“输入里有没有这个信息”以及“模型有没有真正注意这个信息”;错序问题往往和任务结构有关;矛盾问题常见于多步长依赖;幻觉问题则需要结合检索结果和生成约束去控制。
先分类,再定位,才能避免“乱调参”。如果一口气把 temperature、top_p、模型版本都换了,你根本不知道哪一种改动真正解决了问题。
5.2 五层排查链路:从输入到工具边界
我常用的一套排查链路,按顺序从前往后走:
第一层:看现象。先明确它到底是漏、错序、矛盾还是幻觉。如果现象本身都描述不清楚,后面的排查没有意义。
第二层:看输入。检查关键信息是否真的在上下文中,是否因为截断、编码或切块被拆分,是否和无关信息混在一起。很多看似离谱的输出,根因是输入里根本没有模型需要的信息。
第三层:看任务结构。问自己:这个任务是不是要求模型一次跨越多个中间状态?如果是,先用第四章的拆步骤方法,把任务拆成多条单跳任务,再重新测试。
第四层:看参数和环境。确认模型版本、temperature、top_p、上下文窗口设置是否合理,同时检查推理精度、显存占用和后端日志。如果用了 fp16 或量化,可以切回 fp32 验证一次。
第五层:看工具边界。如果已经接入了向量库、MCP、编排框架,问题可能出在工具配置上。比如文本向量接口未配置、MCP 服务超时、编排节点的输入字段名对不上。这层问题最容易让人误判为模型问题,因为你看到的是最终输出不对,但实际断点在工具链路上。
这套排查链路不是万能公式,但它能让你在面对“模型输出很奇怪”时,不会第一反应就归因到“模型智力不行”。
6. 适用边界与长期判断:不能跳不是缺陷,而是设计起点
6.1 适合与不适合的任务边界
既然模型存在“不能跳”的特性,那么在选型时就应该把它考虑进去。
适合直接让模型处理的任务,通常具备几个特征:单步判断、局部上下文、短逻辑链、输出允许一定弹性。比如文本润色、摘要生成、翻译、意图识别、单文档信息抽取、代码片段生成、聊天对话。这些任务即使模型偶尔出错,也容易在单点修正。
不适合直接处理的任务,往往正是“跳度”较高的类型:跨文档对比、多步骤规划、依赖严格顺序的计算、需要反复核实外部事实然后做出决策的业务流程、以及要求输出结果可被审计的正式报告。这类任务如果直接让模型端到端生成,即使成功率做到 80%,在真实生产环境里也仍然不可用,因为你无法确定哪一次输出是正确的那一次。
但注意,这不等于这些任务不能用 LLM 方案。它只是意味着,你应该把模型放在“单点决策”的位置上,而不是“全流程决策”的位置上。
6.2 未来真正值得关注的方向:不是让模型跳得更高,而是让系统不依赖跳动
回到标题本身。LLMs Can't Jump,这个命题真正有价值的地方,在于它提醒我们不要把模型的边界当成一条可以靠堆参数量无限推高的直线。模型能力的演进不会停止,但对大多数工程团队来说,真正长期有效的策略,不是等待模型某一天彻底解决多跳推理,而是从一开始就把系统设计成“不依赖模型跳”的形态。
这意味着:
- 任务结果可以被分解、被检查、被回溯。
- 外部检索和工具调用承担记忆与操作职能。
- 编排框架控制流程,模型只负责局部生成与判断。
- 关键路径上的每一步都有独立验证,而不是把希望押在一个大 Prompt、一次大输出里。
说到底,这和技术选型的本质是一样的:你选择的不只是一个模型,而是一套工作方式。理解模型擅长走、不擅长跳,你的选择就会变得更聪明:在它擅长的地方放权,在它不擅长的地方搭台阶。
比起反复问“为什么模型又错了”,我更建议先问自己:“我是不是又一次在期待它跳过去?”想清楚这个,很多工程问题会变得简单。