提示词之外,真正起作用的是上下文
前两年大家聊的是提示词工程:怎么措辞、加不加”请一步步思考”、要不要给角色设定。这套东西现在依然有用,但重要性明显下降了——模型本身变强之后,措辞的边际收益变小了。
真正决定结果的,变成了另一个问题:你让它看到什么。
上下文不是一个筐
一个常见的误解是”上下文窗口越大越好,全都塞进去就行”。
实际用起来不是这样。信息塞得越多,模型抓住重点的能力反而会下降——不是因为它笨,而是注意力被摊薄了。中间位置的信息尤其容易被忽略,这现象在公开的评测里被反复提到过,我自己用起来也有明显体感:一份很长的文档,开头和结尾的内容它会认真对待,中段经常像没看见。
所以上下文工程的第一原则不是”多”,而是”准”。
几个真正管用的手段
一、结构化的小抄,胜过全量代码。
一个项目的规则文件(用什么测试框架、代码风格、哪些目录别动、错误处理走哪个封装)通常几百字,但它对结果的影响比丢进去十个源文件还大。因为它回答的是”在这个项目里该怎么做”,而不是”代码长什么样”。
二、按需检索,而不是预先塞满。
让模型自己去读文件、搜索代码,比一次性把整个仓库塞进上下文效果好。听起来反直觉——它自己去读不是更慢吗?但慢的代价远小于”在一堆无关代码里迷失”的代价。
三、工具的返回值要有界。
如果一个查文件内容的工具返回整个文件,一个查数据库的工具返回一万行,那上下文很快就被垃圾填满。好的工具设计是返回相关片段、返回摘要、返回可继续查询的句柄。
四、该压缩的时候压缩。
长会话跑到后面,早期的探索过程大部分是废信息。把历史压成要点(做过什么、结论是什么、还有什么没解决),能显著改善后面的表现。这件事可以让人工做,也可以让模型自己做。
五、任务隔离。
多个不相关的任务混在一个会话里,上下文会互相污染:前一个任务的假设被带进后一个任务,而且很难发现。拆成不同的会话或者不同的执行单元,各自有干净的上下文,比在一个长会话里硬撑要好。
一个具体的对比
同样一个”给现有函数加上重试逻辑”的任务,我给过两种上下文:
第一种:把相关模块的完整代码、配置文件、两个测试文件全贴进去,大概几千行。
第二种:只给那个函数的代码、所在文件的 import 部分、一个已有的重试封装的签名、以及一句”这个项目不允许新增依赖”。
第二种的结果明显更接近我要的。第一种的结果也能跑,但它用了自己习惯的写法,跟项目的约定不一致,我还得改回去。
差别不在模型,在那次调用里我给了它多少相关的信息。
我还没想清楚的地方
压缩会不会丢关键细节。 把长会话压成要点,肯定会丢掉一些东西。丢掉的是”当时看起来不重要”的部分,但事后回看,恰恰有些细节是关键。这个取舍目前基本靠感觉。
长上下文的真实有效长度。 各家宣称的窗口大小和”实际能有效利用的长度”不是一回事。这个差距有多大、在不同任务上怎么变,公开的评测结论不一致,我也没找到可靠的判断方法。
上下文和微调的边界。 有些任务,把规则写进上下文效果好;有些任务,似乎需要更深的东西。这个分界线在哪,我目前只能靠试。
先看上下文,再调措辞
把”模型不行”和”上下文没给对”分开看,是很实用的一种思维习惯。
同一套模型,同样的任务,换一个干净的上下文,效果经常像换了个工具。这也解释了为什么同一个模型在不同团队手里口碑差别那么大——差别往往不在提示词的水平,而在他们喂进去的东西是否相关、结构化、并且足够小。
至于那些玄学的提示词技巧,我的态度是:在上下文没搞对之前,调措辞基本属于自我安慰。