你真的了解上下文工程么? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
pmpmp
V2EX    程序员

你真的了解上下文工程么?

  •  
  •   pmpmp 2 天前 769 次点击

    全文逐字手打,多年不写长文了,累..

    如果你关注人工智能技术,尤其是 agent 技术,上下文工程( context engineering )可能是你在这半年听到的最多的词之一了吧?各种营销派、技术流从各种角度的解释和解读估计已经把你彻底搞麻了,甚至你都开始自我怀疑了:是我太笨了么?为啥我怎么也搞不清楚这玩意?

    其实,只是因为信息太杂乱了,我希望通过这篇文章,给你讲清楚到底什么是上下文工程,文章有点长,需要点耐心,读完大概需要 30 分钟,如果你没有时间,你可以先关注这个 github 的项目 https://github.com/zhixiangxue/chak-ai ,这篇文章里面谈到的所有话题我都把他浓缩在这里面了(很好用,不信你去试试,是不是打中了你的痛点,hhh ),我会定期维护更新这个项目,前几天 V2EX 的评论区提到的需求,刚刚发布了,嗯,真维护,hhh 。如果对你有帮助,请给个 star 哦~

    先给你吃一颗定心丸:上下文工程不是什么神奇的技术,来来回回就那么几件事情(这几件事情的技术栈确实是很深的),但是其本质其实很简单,我们抓住事物的本质就不怕乱了,乱七八糟的东西都是都是围绕这个本质转的。

    搞清楚了下面这几个问题,我们大概就能搞懂上下文工程了:为什么会有上下文工程?什么叫上下文?为什么叫工程?怎么做?效果是什么?没有它会怎么样?

    开始吧

    首先我们先问自己一个问题:为什么会有上下文工程?

    如果你只做过简单的 llm chatbot ,你可能是无法理解为什么会出现这玩意的,但是如果你做过稍微复杂一点的 llm chatbot 或者是 agent ,你就很能理解了,因为多数时候你都会对着电脑破口大骂:这什么垃圾模型,跟个傻 X 似的。

    为什么会这样呢?

    不是因为模型太傻,而是你没有给模型精准的上下文,它就很难按照你的想法/意图输出东西。模型的上下文空间是一个稀缺资源,模型的注意力和能力固然有先天缺陷(这不在我们的讨论范围内),但是现状就是这样,想让模型干活,就必须给它精准的上下文。太多了不行,它无法集中注意力,太少了也不行,它无法知晓细节,这其实跟人是一样的。

    这件事情发生在一轮对话中可能还好,因为对模型的输入是你人为可以控制的,精心的设计提示词就行了(这就是所谓的提示词工程),但是,如果是多轮呢?粗暴一点想,假设 1000 轮对话,中途你怎么干预这个上下文的窗口中的信息呢?麻烦了吧?

    所以就有了上下文工程这个说法:所谓的上下文工程,其实就是在每一轮的对话中,用一些巧妙的办法,给 llm 注入恰当的、准确的上下文信息,让它能更准确的进行推理(回复)。

    那么问题来了,上下文是什么?你说上下文不就是 messages 么? NONONO ,上下文是一个广义的概念,千万不要把他理解成只是来来回回对话的 Message ,它还可能包含工具和参数,因为:

    • 不是所有的信息都是你能“告诉 llm”的,有很多信息得它自己动态的挖掘和探索的,这就需要工具(我在这里先挖个坑吧,一提到工具,可能马上就能想到 MCP ,MCP 也是大家津津乐道的话题,但是什么是 MCP ,跟 llm 的关系我估计真正能讲清楚的人也不多,所以用起来更是云里雾里,下回我说说这个)。
    • llm 的其他的参数叫不叫上下文?我觉得也应该叫上下文,因为这也是对 llm 的输入,我举个简单的例子,假设对话的气氛活跃起来了,发现用户脑回路清奇的时候,你是不是得让 llm 回答的时候跳跃一些多给些情绪价值?不然用户会觉得这个机器人不解风情啊,这时候 temperature 是不是也应该作为一种“上下文信息”让模型感知到?

    收一收吧

    简单点说,所谓的上下文工程,就是在任何一轮的对话中,你都能用一种适合你业务的、巧妙的、无需人为干预的、通用的办法,告诉 llm 这个对话的重点信息、来龙去脉 、背景知识、可用的工具等等“关键信息”,从而达到让 llm 的输出更可靠的一种手段,毕竟,llm 只是一个推理引擎,它又不是神仙,你不给它优质的信息,它就无法给你优质的答案

    而之所谓称之为“工程”,就是因为这件事情是非常麻烦的,它甚至都没有任何“标准解决方案”(至少到今天为止是没有的),顶多只有一些“最佳实践”,而且它可以用的手段非常多,组合起来更是无边无际。

    简单的事情叫做“工作”,麻烦的事情才能称之为“工程”,一旦麻烦的事情被搞定了,就离“工业”不远了。

    OK ,到这里为止,我们已经知道了,什么是上下文,什么是上下文工程了,它不是一个枯燥晦涩的定义,就这么一个朴素的东西。

    好,那么下一个问题:怎么做到呢?

    别急,升级打怪到了这里,我先把下一关的怪物给你展示出来,它们是:RAG 、知识库、graph 、短期记忆、长期记忆、剪枝、摘要、向量数据库、嵌入、召回、reranking 、chunk 、MCP 等等,以及与之相关的各种你可能都不知道怎么发音的奇怪工具和论文。

    慌不慌?别慌,给你再吃颗定心丸:我在下面的故事里面,会把他们全都给你串起来,这些“怪物”虽然很烦,但是他们的目的都是一样的,就是为了吓死你,哦不,都是围绕“怎么才能喂给 llm 准确的信息”这个问题的一系列解决方案,他们没有谁好谁坏的问题,只有合不合适的问题,要客观看待,不要被营销号带节奏,也不要被某些大佬的只言片语带到沟里去,搞技术嘛,要客观全面的看待问题。

    下面我们开始由浅入深的聊聊

    在做 chatbot 或者 agent 的时候,里面的一个核心任务就是周而复始的和 llm 对话,而对话一旦多了,马上遇到的第一个问题就是:超出了上下文窗口大小

    怎么办?

    还能怎么办?缩短它呗。怎么缩短?其实有这么几种办法:

    • 硬截断:前面的消息不要了,只保留最新鲜的,这种简单粗暴的方式,肯定不是什么好主意,但也未必是个坏主意,具体看业务,有些业务前面的信息确实就什么用了,你搞那么花里胡哨的方法还不如这样的 ROI 高
    • 弹性的总结和摘要:当消息快满了时候或者话题转变的时候,可以总结一下前面的消息,得出一个富含精华信息的摘要,用这个摘要替代前面的冗长信息,这种方法就稍微智能了一点,既能保证窗口"永远"不会被充满,又节省 token ,但是他也是有明显的局限性的,比如会丢失细节,但是你又不知道会丢失哪些细节,可能某些细节会直接决定后续对话的质量
    • 总结摘要+遗忘:有些信息随着对话的轮次就不重要了,比如话题已经转向了,那这些过时的信息就应该被剔除掉,为其他的重要信息腾出空间来,但是缺点也很明显:你怎么知道后面的对话中用户不会话锋一转又回来了呢?
    • 其他:比如分层记忆(这个层怎么分就看你的需求了,可以按照时间来分,也可以按照重要性来分,都可以);甚至比如用 deepseek-ocr 来做一层前置都可以;

    这些手段可以灵活组合,千奇百怪,脑洞大开,但是本质上都是在解决“上下文窗口大小”的问题,也只是从“短期记忆”的角度来解决问题(所谓的的短期记忆其实就是你的 agent 在运行的过程中,在内存中存储的、用于随时可以和 llm 交互的那些数据)

    当你发现好不容易这些问题都解决了之后,新的问题又出现了:llm 的回答并不准确

    为什么呢?这是众所周知的,llm 其实就是一个“八音盒”,出厂的时候有哪些数据就有,没有的就是没有,数据都没有,你怎么指望它回答准确呢?那它只能给你胡编乱造了(就是幻觉)。

    怎么办呢?有几种办法:

    • 重新训练模型:如果你只是需要 llm 具备一些额外的、相对静态的知识,你可以重新训练一个 llm 出来,这个不在我们今天的讨论范围内,而且,这种事情是一个深坑,轻易不要尝试
    • RAG:Retrieval Augmented Generation ,RAG 这个话题很大,这里不展开,但是我想说的是所谓的检索增强其实也是一个很广义的概念,检索什么呢?知识库、搜索引擎、数据库、记忆系统(本质其实就是数据库)等等,都算,而且 RAG 可不是一个什么过时的技术,那都是卖记忆系统的那帮人渲染出来的营销话术,混淆视听,无商不奸,搞技术的要客观看待。RAG 包含的技术也很多,以后有机会专门写一篇吧,前面提到的,分块、嵌入、查询召回、reranking (重排)、向量数据库、甚至传统的基于关键词的搜索技术其实都算是 RAG 的领域。RAG 从技术层面叫做“检索增强”,从功能层面讲,如果是检索的是知识库,其实你也可以叫它长期记忆(至少他是长期记忆的手段之一),所以 RAG 是多面的,你能武断的说他过时了么?

    当你用了这些技术之后(当然你得用好,用的不好只会适得其反),你会发现,llm 的回答的质量明显的提升了,可是还没等你高兴多久呢,新的问题又出现了:llm 无法处理真实的业务

    经过你的一通折腾,你的 chatbot 变成了一个挺有意思的“陪聊玩具”,但是真实业务的上下文是非常复杂的,为什么呢?因为这些信息被分割到了各个地方,一个经典的例子就是客服,当网线那头的客户火冒三丈的跟你拍桌子反应问题的时候,你的 bot 如何知道怎么处理?我举一个常见的场景吧,这个场景对人类来说很简单,但是对 AI 来说就要死了。

    客户 A:[一张烂果的图片]

    你看,日常的真实情况就是这么朴实无华,AI 就是 CPU 干烧了也没用,为什么呢?因为根本没有任何的上下文,前面提到的那些“奇技淫巧”瞬间失灵了,怎么办?没有上下文,就要自己创造上下文。

    怎么创造?调用工具。

    客服 AI 的祖传技能之一就是搜集情报的能力,一个客诉过来了,要能准确的从 N 个系统中找出和这个客诉一切可能的背景信息,把这些准确的背景信息作为上下文传给 llm 在进行一步步的问题解决。

    那么,如何让 llm 知道要调用工具、以及调用什么工具呢?这个问题稍微有点复杂,因为前面埋过一个坑了,到时候我们聊 MCP 的时候,会把这个事情从头到尾一五一十的扒清楚,这里按下不表。

    好,到此为止,你的 AI 又 get 了一个新技能:使用工具。你可能觉得你的 AI 已经快无敌了,可是现实总是很残酷 你的 AI 兜兜转转可能又回到了问题的原点,上下文窗口被各种信息挤满了,有效的信号越来越弱,它,又变成一个笨蛋了,而且还是一个很慢的笨蛋...

    为什么呢?因为llm 记住的信息质量不高。你要知道,无论是从消息列表、外部知识/数据、工具结果来的数据,它们未必真的是因为质量不好,只是他们对于 llm 来说不够友好。

    这话什么意思呢,上海交通大学和 GAIR 实验室发表了个很有意思的论文 《上下文工程 2.0:上下文工程的上下文》,它里面用了一个精辟的方式来解释这个现象:熵(所谓熵,其实就是无序混乱的程度)。

    人类的沟通是高熵的,也就是很混乱、很跳跃,比如一句话里面有很多隐藏信息(例如指代),但是这些对人类都不是问题,因为人的大脑可以快速处理这些乱七八糟的背景信息,甚至是隐含在表情、肢体动作、语速、语气里面的隐形信息。

    但是,LLM 是低熵的,它无法接受乱七八糟的上下文,只能理解明确的、毫不含糊的指令。

    高熵和低熵的对话,无异于鸡同鸭讲,即使你上了很多的手段,你依然发现这些问题还是存在,这就回到了我们前面说到的一个问题 如何让数据对 llm 友好呢?这就涉及到另一个宏大的话题 记忆。

    你要为你的 llm 构造一个对它友好的记忆系统,让信息经过过滤提纯之后,成为对 llm 友好的数据,也就是把高熵的信息转为低熵的信息。

    写累了,我们总结一下,上下文工程怎么做呢?本质上就是想办法组织和构造对 llm 友好的、精准的信息和工具,才能让 llm 发挥它的特长,手段有很多,这些手段组合起来才可能达到一个更好的效果,但是怎么组合并没有标准答案。

    所以,大神 Karpathy 才精辟的总结到:上下文工程是一门微妙的艺术与科学,旨在填入恰到好处的信息,为下一步推理做准备。

    玄之又玄,众妙之门。

    那么,有了上下文工程之后的效果是什么呢?其实很难量化定义(因为如何评估效果又是一个大话题),但是从用户体验上来说,就会产生或多或少的“啊哈时刻”,你会觉得你面对的这个“对话框”有时候真的很聪明。反过来,如果没有它,你会很明显的感觉到你面对的这个“对话框”很机械很傻很笨很蠢很烂很垃圾...

    最后

    当我们要构建一个智能系统来解决业务问题的时候,我们要清醒的认知到:模型决定了这个系统的下限,而上下文工程能力决定了这个系统的上限。作为 AI 应用的开发者,应该尽快放弃幻想准备战斗,上下文工程是必修课,因为...没有其他选项。

    虽然上下文工程非常复杂,但是我们能不能做一些事情让它变的稍微简单一些呢?是可以的,这就是我开发并开源 Chak 的初衷

    Chak 是一个极简的多模型 LLM 客户端,内置了上下文管理和工具调用,极简,开箱即用。

    chak 不是另一个 liteLLM 、one-api 或 OpenRouter ,而是一个为你主动管理对话上下文(短期记忆)和工具调用的 LLM 客户端库。你只需专注于对话,让 chak 帮你处理一部分上下文工程。

    地址: https://github.com/zhixiangxue/chak-ai

    如果觉得好用的话,给个 star 吧,欢迎提 issue ,欢迎贡献

    我始终认为,先让和 llm 的对话变得智能轻松简单起来,才能谈 agent

    以上,全文完,感谢阅读。

    4 条回复    2025-11-15 21:43:06 +08:00
    Scarb
        1
    Scarb  
       1 天前
    写了很多,但是不知道我的理解对不对,实际只有一个功能:自动化上下文压缩(包括总结和 LRU )。其他的 LangChain 也都能做到。
    但是自动化上下文压缩感觉是一个伪需求,各个 Agent 使用场景大不相同,每个都应该有自定义的压缩方式。自动化压缩很容易把 Agent 需要的内容丢掉。
    pmpmp
        2
    pmpmp  
    OP
       1 天前
    @Scarb 感谢阅读

    是这样的,不能说是个伪需求,我觉得应该这样看:

    1. 是不是用得到,有些场景可能是短平快的,通常不需要压缩模型表现也会很好,压缩了反而会丢细节
    2. 如果用得到(多数情况下其实无法人为判断是否会用到),就要看具体的对话性质,需要保留哪些需要使用什么样的策略,只有业务方最清楚,不过 chak 提供了几种通用的、开箱即用的方式,不过也是支持自定义的,继承个基类自己实现就行了
    3. 即使用了,也是不够的,压缩摘要只是前置的一种手段,孤立的手段并不能构成绝对有效的结果


    作为一个框架,其实这些都是基操,我搞这 chak 也是因为 langchain 其实对于我搞过的很多场景来说还是太重了,一个简单的 mvp 结果要搞半天,但是 langchain 或者 langgraph 的 node 里面如果使用 chak ,还是非常方便的,否则我就得自己在 graph 里面反复的造轮子,管理上下文、管理工具调用

    langchain 和 langgraph 很强大,有时候杀鸡不需要用牛刀,语法太复杂学习曲线太高了,没有必要,就像 urllib 能搞定所有的 http 请求,但是太麻烦了,requests 就满足了绝大多数的需求了,接口简单易用凭直觉就能快速用起来


    一切看场景吧,定位不一样哈,chak 显然是搞不定也不会搞复杂的编排的,这不是 goal
    zzhirong
        3
    zzhirong  
       1 天前   1
    话说最近刚好看了一篇相关论文[Context Engineering 2.0: The Context of Context Engineering]( https://arxiv.org/abs/2510.26493), 里面总结了当前 LLM 面临的一些挑战(有限的窗口, 如何过滤有用的上下文)以及目前应对措施(收集, 处理, 存储, 使用等).

    我之前有种 LLM 类似编译器, 而自然语言扮演编译器角色的错觉. 仔细想又发现没这么简单, LLM 作为编译器最大的问题就是它很多时候都是一个黑盒, 相同的输入, 不同的输出, 你可以去接近控制它, 但是你没办法精确控制它, 编程语言可以精确描述你需要的库以及你想做什么(精确的上下文), 而 prompt 更像是一种许愿(类似于声明式编程).
    zololiu
        4
    zololiu  
       2 小时 2 分钟前   1
    希望 V2EX 上这类高质量的分享文章多一点哇!
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2370 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 23ms UTC 15:45 PVG 23:45 LAX 07:45 JFK 10:45
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86