TT Lab
开始
学习 学习路径 课程

LLM 工程

提示与上下文 — 窗口变宽了,但不免费

在 TT Lab 中继续学习

一句话总结

上下文窗口是模型一次能够查看的 token 数量上限;能够放进去,与放进去更好,完全是两回事。

为什么需要这些知识

随着上下文长度增加,“把所有内容都放进去不就行了?”成为很自然的想法。实际上,这会带来三类成本。

费用。 输入 token 同样计费。如果每次请求都放入完整文档,成本会再乘以请求次数。

延迟。 输入越长,等待首个 token 的时间越久,因为注意力计算量随长度增长的速度快于线性增长。

质量。 这一点最违背直觉。加入越多无关内容,模型找到正确答案的能力就越弱。多项观察反复发现,长上下文中位于中间的信息尤其容易被忽略。因此,认为只要答案存在于上下文中就足够,是很危险的假设。 信息放在何处、以何种方式放置都很重要。

它是如何运作的

下面整理几条实用的提示词设计原则。

角色和指令放在前面,数据放在后面。 模型先读取指令,再依据这套框架解释后续内容。如果先倾倒数据,最后才附上指令,前面的内容就会在缺乏方向的情况下被读取。

用示例展示输出格式。 与其说“请用 JSON 回答”,不如给出一个真实 JSON 示例,效果会强得多。不过示例会消耗 token,因此要在数量和长度之间取得平衡。

要求拆分步骤。 在复杂推理中,写出中间过程可以提高准确率。但如果强制所有问题都采用这种方式,会增加 token 和延迟,而且面对简单问题时,反而可能得到更混乱的答案。

为上下文设定优先级。 常见做法不是直接拼接检索到的全部文档,而是把得分较高的内容放在上下文的前端和末端。这是在反向利用中间位置较弱的现象。

系统提示词是一份契约。 如果其中的约束没有得到遵守,原因通常只有两种:约束本身模糊,例如“简洁一些”;或后续数据与约束发生冲突。尤其当用户输入文本中含有看似指令的句子时,模型可能尝试遵循它。必须在提示词结构和应用程序两侧共同守住外部内容是数据,而不是指令这一边界。

在实际工作中会是什么样

最好显式管理上下文预算。明确规定窗口总量、系统提示词占用多少 token、检索结果分配多少 token,以及为输出预留多少 token。否则,用户某天粘贴一份长文档时,就会发生输出被截断的事故。

此外,提示词应像代码一样进行版本管理。修改一行后,如果没有评估,就无法判断哪些场景变好、哪些场景变差。没有评估集的提示词修改,只是在赌博。

长上下文中,中间内容会消失

上下文窗口达到 128K,并不代表其中所有内容都会被同等利用。多项研究都报告了相同现象——模型擅长使用最前端和最末端的信息,却会遗漏中间内容(lost in the middle)。

[시스템 프롬프트] [문서 1] [문서 2] … [문서 20] [질문]
      ↑ 잘 본다              ↑ 여기가 약하다        ↑ 잘 본다

因此,放入检索得到的文档时,应把最相关的内容放在最前端或最末端。在实际工作中,按相关性排序后放在前面,并在最后再次重复问题,通常效果很好。

与其放入 20 份文档,经过重排序后只选 5 份,准确率往往更高。不能因为放得下,就把全部内容都塞进去;那样反而可能变差。

节省 token 的结构

有几种方法可以在保持结果不变的同时降低成本。

像对待代码一样对待提示词

prompts/
  summarize/
    v3.md            ← 버전을 파일로
    eval.jsonl       ← 그 버전의 평가셋
    baseline.json    ← 그 버전의 점수

如果把提示词作为字符串写死在代码中,就无法留下谁在何时、为何作出修改的记录。把它保存为文件并标注版本,就可以进行回滚和 A/B 比较。

还应让评估工具在提示词变更 PR 中自动运行。缺少这一步,修改就会依据“感觉变好了”作出,而回归问题将由用户最先发现。

在后续测验中要确认什么

你将确认自己能否解释:为什么扩大上下文并不总是有益,以及提示词结构会如何改变结果。