news 2026/10/22 4:18:14

LLM上下文工程:模型注意力如何决定Agent成败

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LLM上下文工程:模型注意力如何决定Agent成败

做Agent有一个特别容易被低估的环节:上下文工程。这个词听起来像是把提示词调出花样的进阶版,但实际做下来你会发现,它决定了一个Agent是“看起来聪明”还是“真的能干”。我最早丢掉幻想,是在一个很简单的场景里:让模型从一段很长的材料里提取某个关键字段,它死活提取不对——不是没理解要求,而是它仿佛根本没“看到”那段材料。后来我把材料挪到对话末尾,问题瞬间消失了。那一刻我意识到,在LLM这里,“看到了什么”和我们以为的完全是两码事。

这一期是手搓Agent系列的第三篇,我只聊一个底层问题:一段文本被送进模型之后,到底经历了什么?模型的注意力会落在哪里?哪些位置、哪些格式,会决定一段信息的最终命运?把这个问题想清楚,你后面做工具调用、多轮记忆、知识库接入,都会顺很多。

1. 上下文工程是从哪里冒出来的?

1.1 没有上下文工程,Agent 处处碰壁

先把我对上下文工程的定义放这儿:它是关于“如何选择、组织、更新、压缩”送进模型的那批文本的设计方法。很多人刚开始把注意力全放在单条Prompt的措辞上,结果发现提示词写得再漂亮,一旦上下文里混了太多无关内容,或者信息位置不对,输出照样翻车。

我之前做了一个自动整理会议纪要的Agent。第一版实现特别简单,就是把“用户上传的材料 + 用户的要求 + 历史生成内容”一股脑追加进上下文。结果在处理一份三十页的材料时,模型会丢掉前面几页的信息,回答得像只看了最后几段。这不是模型蠢,是我没设计它的“视线范围”。

对比传统开发,一个函数只要你把参数传对了,它一定读得到;LLM不是这样的,它在一个固定长度的token流上做概率预测,哪些token最终会被“注意”到,是模型自己决定的,而且这个决定受位置、格式、周围干扰的强烈影响。所以想手搓Agent,上下文工程是绕不过去的一关。

1.2 模型和我们以为的“看到”不是一回事

这不是文字游戏。你和我“看到一句话”,是在理解含义;LLM“看到一句话”,是把文本拆成token,放进一个固定大小的窗口里参与大量的矩阵运算。它没有那种“翻开第一页、找到第五页某个段落”的阅读动作。它的全部视野,就是当前窗口里的token序列。

模型每生成一个词,都会回头对窗口里的所有token算一遍注意力分布。所以上下文里有没有某条信息、它放在哪儿、格式清不清晰,会直接决定后续生成的走向。把上下文工程理解为“给模型设计它可能关注到的信息布局”,这个角度就对了。

Agent侧的上下文构建,核心代码其实简单到有点让人放松警惕:

# agent_context.py class AgentContext: def __init__(self, system_prompt: str, max_tokens: int = 8000): self.messages = [{"role": "system", "content": system_prompt}] self.max_tokens = max_tokens def add_user_input(self, text: str): self.messages.append({"role": "user", "content": text}) def add_tool_result(self, name: str, result: str): # 工具返回值必须结构化地塞进上下文,不能直接往历史里丢 block = f"[工具 {name} 的返回]\n{result}\n[/工具结束]" self.messages.append({"role": "tool", "content": block}) def build(self): # 真正的工程点在于:build之前先做裁剪、摘要、去重 return self.messages

这个骨架想表达的是:系统提示词、用户需求、工具结果、历史对话,全都会被拼进同一个上下文里。很多人觉得API支持messages数组就万事大吉,其实消息一多,互相干扰才是灾难的源头。

2. 现场还原:在模型眼里,上下文的真实样貌

2.1 你的文本先过一道Tokenizer

你输入的文本,并不是以字符为单位被模型读取的。模型先把文本切成一串token,每个token可能是半个词、一个词根、一个汉字或一个小片段。中文和英文的切法不一样,中文经常一个字或一个词对应一个token。

这意味着“你写了多少字”和“模型实际处理多少token”不是一回事。我见过不少开发者觉得五万字的材料扔进去没事,结果一算token数早超了,或者严重挤占了输出空间。所以上下文工程的第一步,永远是估算token。先把要放进去的内容离线和估算一下,再决定哪些能留、哪些必须压缩。

所以工程上的结论是:你定义的“上下文”不是字符量,而是token量;你的信息布局,从一开始就要按token预算来做。不要等到线上运行才发现窗口被历史消息占满,模型连最基本的指令都快看不见了,那就很被动了。

2.2 位置编码带来的“先来后到”

Transformer靠位置编码记住顺序。现在主流模型一般用相对位置编码或旋转位置编码,目的是让模型感知token之间的相对前后关系。位置编码带来的直接结果就是:同一段内容放在上下文的不同位置,模型对它的利用程度是不同的。

我常用一个不一定严谨但很贴切的类比:人在听讲座时,对开头和结尾记得最牢,中间一段相对模糊。LLM不会“记得牢”或“模糊”得这么拟人,但注意力分布上确实有类似的倾向。

最明显的例子是,当你在长Prompt的中间某一段写了一个非常关键的限定条件,模型执行时经常会忽略;你要是把这条规则放在Prompt开头或结尾再强调一遍,执行成功率会显著提高。这个现象不是玄学,是注意力机制下不同位置的权重差异所致。后面我会写实验验证。

2.3 注意力不是“阅读”,而是“打分”

真正决定模型把注意力放在哪儿的机制叫自注意力。简单说,模型生成下一个token之前,会让序列里所有token两两计算一个相关度分数,分数高的位置更能影响输出。这个相关度是基于词义和上下文算出来的,不是像人眼那样去“找答案”。

所以上下文里塞了太多与当前任务无关的内容,会稀释有用的注意力。比如你放了二十条知识库片段,模型得自己判断哪些相关,判断错了就会漏掉关键信息。注意力机制本身不会给信息来源做可信度认证,它只做语义相关性打分。虽然现在模型能力一直在变强,但本质上还是概率模型,你不能假设它一定“看到”了你认为重要的地方。

这里也顺带提一句KV Cache。同一个上下文连续问多轮时,前面重复的token会被缓存、避免重复计算。上下文越长,KV Cache占用的空间越大。这也是我们不建议盲目把历史全塞进去的重要原因:上下文变长不只是贵和慢,注意力也可能被稀释得更厉害。

3. 三个关键现象:理解了这些,才算看懂了 LLM 的视线

3.1 注意力会“淹没在中间”

学理上有个知名的概括,叫“迷失在中间”。意思是:当需要从上下文的多段材料里找信息时,模型对开头和结尾的内容利用率更高,对中间内容的利用率偏低。原因和注意力机制里位置权重有关,也跟长序列内部互相干扰有关。

我自己做个了简单测试:把一个重要指令放在三段话的中间位置,模型经常漏掉;放在开头或结尾,几乎不会漏。材料越长,中间位置的信息越容易被忽略。所以做上下文工程,第一个基本原则就是:关键指令放两端,背景材料放中间。越重要的约束,越要靠后或靠前。

延伸出一个操作技巧:模型总是不遵循某条规则的时候,先别急着换措辞,把它从正文中间挪到系统提示词末尾试一次,很可能直接就解决了。这也是“上下文工程”和单纯“写提示词”最大的差别。

3.2 越靠后的内容越有“话语权”

和迷失在中间相辅相成的,是近因效应:模型会更倾向于利用靠近上下文末尾的内容。这跟人聊天很像,最后一句往往最容易被人接住。在Agent场景里,这意味着最新一次工具调用的结果、最近一轮的用户输入,应该放在消息序列比较靠后的位置;系统设定和背景知识可以放前面。

这一点在多轮Agent里尤其重要。很多时候模型不按工具返回结果回答,反而按之前的旧假设来推理,就是因为工具结果被放在了中间,旧假设却紧贴着用户当前问题。我后来把所有工具返回值放在用户输入的前面,让用户当前问题紧接着出现在最末尾,回答准确率明显提升。

通用经验:上下文信息的“话语权”排序,大致是底部大于顶部,顶部大于中间。设计消息队列时,把“当前要回答的问题”放在最后,“工具刚返回的数据”放在倒数第二,“系统设定”放开头,“背景知识、历史记录”放中间。如果光排序还不够,就搭配下一节的结构化标签。

3.3 历史中的事实不一定是模型凭据

这个现象特别容易造成误解:模型并不是像数据库一样“查询”历史,它只是生成时参考整个序列给出的概率。你把三个事实放在上下文里,模型可能只利用其中一个最显眼的。如果同时塞入多条相似信息,模型还可能记串。比如上下文中同时出现“订单A金额是100元”和“订单B金额是200元”,模型不一定能准确区分哪个对应哪个。

我的对策是:把事实放入上下文前,用结构化标签明确标识。像这样:

<订单信息> <订单编号>A001</订单编号> <金额>100元</金额> </订单信息>

模型对结构化标签的敏感度远高于散文式描述,有时候加一对尖括号比写十句“请注意以下信息很重要”更有效。因为标签把语义边界划清楚了,注意力在计算时更容易把同一个实体的属性关联起来。这属于极低成本、极高回报的上下文工程手段,强烈建议马上用起来。

4. 动手实验:看模型“视线”的有效姿势

4.1 简单实验:同一指令,换个位置

空谈理论不直观,建议你花半小时做个实验。固定同一个任务,准备三段不同的规则文本,分别放在上下文的开头、中间、结尾,然后各跑几遍,记录模型对规则的执行成功率。

我自己用这个办法验证过一个简单例子:让模型从一段材料中提取“项目启动时间”,材料里给了三个日期,规则明确要求“以最近的日期为准”。规则放开头时,执行成功率大概七成;放中间时大概五成;放结尾时接近八成。数值会随模型能力和随机性浮动,但“中间最差”的结论很稳定。

这个实验最大的意义在于,你会知道自己的Prompt该往哪儿调整,而不是盲目地“再写狠一点”。上下文工程设计的第一步,就是能预判模型关注力的重心。

4.2 用 logprobs 观察模型“选择”

如果只靠经验判断,也行;但想看更细,可以用logprobs。多数模型API支持返回每个token的生成概率。如果模型在某个关键位置生成了低概率的token,说明它没多大把握,或者上下文里没有足够支撑。

我调试Agent时,会先开logprobs跑一轮。如果模型在回答“项目启动时间是X”时,那个X的概率很低,基本可以断定:上下文里相关信息不足,或者位置没放对。用概率辅助判断,能帮我把“猜模型为什么错”变成“观察模型哪里没把握”,效率差很多。

4.3 用“小窗口自测”训练上下文嗅觉

我还常用一个训练上下文设计能力的小方法:把窗口人为调小,强迫自己只保留最有价值的内容。这个过程就像打包行李,装不下了你才认真思考哪些是真必需品。多做几次之后,会形成一种条件反射:先判断哪些信息是决策依据,哪些只是背景噪音。

具体操作上,我把候选信息分三级:核心指令、事实参数、装饰性描述。核心指令必须保留且放在显眼位置;事实参数按需放进结构块里;装饰性描述能删就删。这样整理出来的上下文,往往比“塞满完整对话历史”的效果更好。你现在手头要是有正在调的Agent,可以直接按这个方式重排一遍消息顺序。

5. 常见问题与避坑实录

5.1 常见误区速查表

误区实际情况正确思路
把上下文当成数据库模型不会像SQL一样精确查历史,注意力可能漏掉信息用结构标签、位置设计、压缩摘要保证关键信息不漏
以为模型会逐字阅读所有token注意力分布不均匀,中间的信息容易被忽略提到关键规则或数据时,放到两端
窗口越大越好上下文变长会稀释注意力,也增加延迟和成本按需裁剪,把窗口预算留给关键信息
所有token地位一样不同位置的注意力权重差别很大按话语权排序设计消息队列

这张表是我在多个项目里反复踩坑后总结的。每次模型表现不对劲,我先对照这张表排查一遍,很多时候问题不是模型不行,而是上下文结构不合理。

5.2 我踩过的坑和现在的做法

第一个坑是往上下文里硬塞知识库全文。某个内部项目里,我把一份几十页的材料整段塞进上下文,想的是“模型知道得越多,回答越准”。结果输出越来越偏向最后几页的内容,中间部分的专业名词甚至开始被它自创解释。后来改成只放摘要,需要细节再通过检索补充,效果立刻稳了。

第二个坑是历史消息无限增长。Agent多轮对话后,我把所有历史都保留在上下文中,首token延迟一路走高,更麻烦的是模型回答开始前后矛盾。后来加了两个机制:超过一定轮数的历史先做摘要,再老的消息直接丢弃。上下文稳定在可控范围,回答质量也恢复了不少。

第三个坑是工具结果格式混乱。不同工具的返回值五花八门,模型读起来很吃力。后来我统一用方括号标签包裹工具结果,再注明字段含义。前面提到了,模型对边界标记非常敏感,加完标签后准确率明显提升。你花十分钟统一一遍工具返回格式,可能比花一天换模型管用得多。

这些坑的底层教训是同一句话:上下文工程不是写提示词的技巧,它是Agent系统设计的一部分。上下文里的每一条消息,都应该经过“是否有必要”和“放哪里合适”这两道筛选。

5.3 给“上下文工程(上)”一个实用检查清单

最后给一个可以直接套用的检查清单。每次手搓Agent、调Agent之前照着过一遍,能省不少事:

  1. 预估当前上下文的token总数,确认是否超过窗口的85%。
  2. 检查关键指令是不是出现在开头或结尾,而不是埋在正文中间。
  3. 给工具返回值加上结构化标签,明确字段边界。
  4. 移除与当前任务无关的历史轮次,必要时改成摘要。
  5. 把用户当前问题放在消息序列最后,让它贴住模型的生成位置。
  6. 对多个同类型实体,尽量用列表或表格呈现,而不是散落在正文里。
  7. 对长材料,先做摘要和分层,不要整段堆入。
  8. 条件允许时,开logprobs观察关键生成位置的置信度。

这次把“LLM到底看到了什么”讲透之后,我最大的体会是:大多数Agent表现不好,不是因为模型不够聪明,是我们没把信息摆在它最容易注意到的位置。下次做Agent迭代,可以先不急着换模型、加RAG,试着重新设计上下文里的信息顺序,效果可能会让你意外。上下文工程(下)我准备聊更贴近实操的部分:多轮对话里上下文怎么压缩、记忆摘要怎么做、Agent状态机怎么维护。你也正在手搓Agent的话,可以先拿这份清单去试一轮,欢迎回来交流踩到的坑。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 4:17:43

一次硬盘数据丢失后的完整救援与归档复盘

2024年12月5日&#xff0c;我给这场突发事故起了个名字——“遗失的数字”。那天下午我本来只想从移动硬盘里找一份旧项目素材&#xff0c;结果发现一个以年份命名的文件夹还在&#xff0c;但里面的子目录只剩下一堆乱码空壳&#xff0c;实际文件少了一大半。数字资产的丢失不像…

作者头像 李华
网站建设 2026/10/22 4:17:43

C++视频人像分割推理:高性能实时抠图的工程实践

1. 项目概述&#xff1a;为什么C做视频人像分割不是“复古”&#xff0c;而是刚需你有没有遇到过这样的场景&#xff1a;在某次线上会议中&#xff0c;背景杂乱的书架、未收拾的床铺、甚至突然闯入的宠物&#xff0c;让本该专业的形象瞬间垮掉&#xff1b;又或者在做短视频内容…

作者头像 李华
网站建设 2026/10/22 4:15:39

少样本SFT过拟合防治:从症状到LoRA、标签平滑与KL约束的实战指南

做 few shot SFT 最怕的不是模型学不会&#xff0c;而是“学得太快”。我有一次拿六十来条人工清洗过的对话样本去微调一个开源底座模型&#xff0c;第一轮 epoch 还没跑完&#xff0c;训练 loss 就掉到了零点零几&#xff0c;看起来验证集也还行。等我把模型接到真实对话环境里…

作者头像 李华
网站建设 2026/10/22 4:14:54

Java高并发编程实战:锁、线程池与性能调优全解

做了这么多年并发编程&#xff0c;我最大的感受是&#xff1a;绝大多数线上故障&#xff0c;都不是业务逻辑多难&#xff0c;而是并发场景下的细节没想透。网上流传过很多所谓“大厂高并发笔记”&#xff0c;但大多是把八股文抄一遍&#xff0c;真正能落到实处的很少。这份笔记…

作者头像 李华
网站建设 2026/10/22 4:14:39

Hermes Agent能力深度解析:会话、工具、MCP与记忆实战

Hermes Agent能力篇&#xff1a;会话、工具、MCP、记忆这几年做智能体&#xff08;Agent&#xff09;项目&#xff0c;说实话最大的感受不是模型本身不够强&#xff0c;而是工程侧的东西太碎。你既要处理多轮会话的上下文管理&#xff0c;又要接各种外部工具&#xff0c;还得让…

作者头像 李华
网站建设 2026/10/22 4:14:38

企业级Agent平台核心能力拆解:从个人工具到可信执行体

当前行里&#xff0c;个人用Agent&#xff0c;最常见的是什么&#xff1f;一个是编程助手&#xff0c;一个是带知识库的问答机器人&#xff0c;再就是各种会议纪要、文档总结的小工具。这些工具确实提效&#xff0c;但一旦你想把它们复制到公司里跑真正的业务流程&#xff0c;问…

作者头像 李华