Prompt 与上下文工程 · 知识点
Prompt 是上下文里的一块指令。真正决定效果的是:窗口里放什么、按什么顺序、何时压缩、何时检索、何时交给工具。
0. 总图
一次 LLM 调用 = 把 有限的上下文窗口 填满,然后让模型预测下一步。
窗口里通常有这些块(按常见 messages 结构):
- System / Developer:角色、硬约束、输出协议、安全边界
- Few-shot 示例:教格式和边界 case,不是教知识
- 业务上下文:用户画像、当前任务状态、权限、今天的日期
- 检索结果 / 记忆:RAG 片段、长期记忆、会话摘要
- 工具结果:函数返回、MCP 输出、代码执行日志
- 用户当前问题
- (可选)思考/草稿区:CoT、scratchpad、hidden thinking
上下文工程要解决的就是:
- 这些块 谁进窗口、谁不进
- 顺序怎么排(模型对位置敏感)
- 超长了怎么 压缩 / 裁剪 / 外置(RAG/工具)
- 怎么保证输出 可解析、可复现、可评测
1. Prompt 工程(仍要会,但是「组件」不是全部)
重点 1.1 Prompt 的标准零件
一个能上线的 prompt,建议固定包含:
| 零件 | 作用 | 示例 |
|---|---|---|
| 角色 | 限定能力边界和口吻 | 「你是客服助手,只能基于知识库回答」 |
| 任务 | 一句话说清楚要干什么 | 避免模型自己发明目标 |
| 硬约束 | 不能做什么、必须做什么 | 不编造、不承诺、不输出隐私 |
| 输入说明 | 变量从哪来、缺省怎么处理 | {doc} 为空时明确拒绝回答 |
| 输出协议 | JSON / Markdown / 指定字段 | 给 schema 和正反例 |
| 示例 | few-shot | 覆盖正常 + 边界 + 拒绝 |
| 上下文槽位 | 动态注入的文档、历史、工具结果 | 用 XML/标签包起来,防污染 |
推荐结构(Claude 系常用 XML,OpenAI 系常用 Markdown 标题,本质一样:分隔符 + 命名空间):
text
<role>…</role>
<rules>…</rules>
<output_format>…</output_format>
<examples>…</examples>
<context>…</context>
<user_query>…</user_query>为什么要用分隔符:降低「用户输入冒充系统指令」的概率,也方便模型区分「资料」和「问题」。
1.2 常见策略(会点名、会取舍)
Zero / One / Few-shot
- Zero-shot:任务简单、格式不严时用
- Few-shot:格式敏感、边界多、领域口径要统一时用
- 示例要 短、像真实流量、含失败样本(例如「知识库没有就说不知道」)
- 示例过多会占窗口、还可能过拟合示例风格
思维链 CoT
- 适合:数学、多步判断、需要可追溯理由
- 不适合:延迟敏感、答案必须极短、或会把推理暴露给用户(可改成内部思考 + 对外只给结论)
- 需要推理时可以让模型先想,对外接口只返回结构化结论,推理写入日志。
ReAct / 先规划再执行
- 本质是「思考 → 选工具 → 观察 → 再思考」
- 详细放在
03-Agent与工具调用,这里只需知道:Agent 的 prompt 是状态机说明书
结构化输出
优先顺序(从稳到不稳):
- 官方 JSON Schema / 工具调用(Function Calling)—— 最稳
- 明确 schema + 校验 + 失败重试
- 「请输出 JSON」纯自然语言 —— 最不稳,只能做 demo
落地时一定要有:解析失败 → 把错误信息回灌再生成一次(repair loop)→ 仍失败则降级。
角色 / 风格 / 安全
- 角色能提升稳定性,但不能当权限系统。权限必须在应用层做。
- 安全约束写在 system 里,且用户输入永远当 数据 不当 指令。
1.3 解码参数(常被当成 prompt 问题)
| 参数 | 影响 | 应用建议 |
|---|---|---|
| temperature | 随机性 | 抽取/分类/JSON 用 0–0.2;写作 0.7+ |
| top_p | 核采样 | 一般和 temperature 只调一个 |
| max_tokens | 长度上限 | 结构化输出设够但不要过大,防跑飞 |
| stop | 截断 | 防模型把示例后面的内容续出来 |
| seed(若有) | 可复现 | 评测时固定 |
常见误判:输出不稳定不一定是 prompt 写坏了,也可能是 temperature 太高,或每次检索结果不同。
1.4 Prompt 调试方法(体现工程能力)
不要只说「我再改改 prompt」。一套可讲的流程:
- 固定评测集(20–50 条真实 query,含边界)
- 一次只改一个变量(角色 / 示例 / 约束 / 顺序)
- 看失败类型:拒答过多、幻觉、格式错、口径漂
- 用「失败样本 → 加规则或加 few-shot」迭代
- 回归:改 A 不能把 B 弄坏(prompt 也有回归测试)
2. 上下文工程(本模块重点,拉开差距的地方)
重点 2.1 核心约束:窗口是稀缺资源
要能讲清楚这几个词:
- Token:模型的计费和长度单位,中文大约 1 字 ≈ 1.5–2 token(粗估即可)
- Context window:一次能看见的最大 token 数
- 有效上下文 ≠ 标称窗口:越长,中间信息越容易丢(Lost in the Middle)
- KV Cache:前缀不变时可复用计算;所以 稳定的 system 前缀 对延迟和成本很重要(Prompt Cache)
重点 2.2 Lost in the Middle
现象:模型对 开头和结尾 更敏感,中间的文档/历史容易被忽略。
工程对策:
- 最重要的指令放 system 头部,当前问题放最后
- RAG 结果不要无脑按相似度堆 20 段,做 rerank 后只留 top-k
- 长文档:结论/相关段落前置,或先摘要再答
- 多文档时加标题、来源、相关性分数,让模型知道权重
重点 2.3 上下文装填策略(必会画)
从「贵且满」到「便宜且外置」:
text
全量塞进窗口
→ 滑动窗口(只留近 N 轮)
→ 摘要压缩(旧对话变成 summary)
→ 分层记忆(工作记忆 / 会话摘要 / 长期记忆)
→ RAG 按需检索
→ 工具按需查询(DB、搜索、代码)怎么放:
- 必须每次都看到的硬规则 → system
- 当前任务状态 → 工作记忆(短、结构化)
- 可能相关的知识 → RAG,不默认全塞
- 实时/权威/可计算的数据 → 工具,不写进 prompt 装懂
- 很久以前的偏好 → 长期记忆,检索后注入
2.4 对话记忆的三种形态
| 类型 | 存什么 | 怎么用 | 风险 |
|---|---|---|---|
| 短期(滑动窗口) | 最近若干轮原文 | 实现简单 | 一长就爆窗、成本高 |
| 会话摘要 | 压缩后的事实与决策 | 中长会话标配 | 摘要丢失细节、错误会累积 |
| 长期记忆 | 用户偏好、实体、结论 | 跨会话检索注入 | 写错记忆会污染所有后续回答 |
实践组合(可直接当标准答案):
- 保留最近 K 轮原文
- 更早的内容做成 滚动摘要(只记事实、决策、未决问题)
- 关键实体进长期记忆(需确认机制,避免把幻觉写进记忆)
- 每轮回答前:摘要 + 近 K 轮 + 本轮检索/工具结果 + 当前问题
2.5 压缩(Compaction)怎么做才不像丢信息
常见手法:
- 抽取式:保留关键句、决策、数字、承诺(保真,更适合客服/法律)
- 生成式摘要:更短,但可能编造,必须约束「只根据原文」
- 结构化状态:把对话压成 JSON 状态机(订单状态、已选套餐)—— 业务系统最稳
- 按主题分桶:技术支持里「已尝试步骤 / 环境信息 / 未解决问题」分开存
压缩原则:
- 数字、ID、结论、用户明确偏好 不可丢
- 客套话、重复确认、失败的探索路径 可丢或极压缩
- 摘要要带时间/轮次,避免过期信息当现状
2.6 工具结果也是上下文(很多人漏这一块)
Agent 场景下,真正把窗口打爆的往往不是用户话,而是:
- 网页全文
- 代码文件
- 检索 10 段原文
- 连续 8 次工具日志
对策:
- 工具返回先 截断 / 摘要 / 抽字段
- 只把「对下一步有用的观察」写回消息
- 原始大结果落磁盘或对象存储,上下文里放指针
- 多步 Agent 要做 observation compaction
2.7 Prompt Caching(前缀缓存)
思想:把 很少变的大段(系统提示、工具定义、知识库静态说明)放在消息最前面且保持字节级稳定,让服务端复用 KV。
注意:
- 中间插一句「今天是 x 号」可能导致整段缓存失效(看厂商实现)
- 动态内容后置:日期、用户名、本轮检索结果放后面
- 这是成本和延迟优化,也会倒逼你把 prompt 设计成「稳定前缀 + 动态后缀」
3. 和相邻模块的边界
| 问题 | 用 Prompt/上下文 | 用 RAG | 用微调 | 用 Agent/工具 |
|---|---|---|---|---|
| 口吻、格式、拒答策略 | 是 | 否 | 通常不值得 | 否 |
| 私有知识、会更新的文档 | 少部分可塞 | 是 | 知识易过期,慎用 | 查 DB/API 也可以 |
| 要算、要查实时数据 | 不要让模型算钱/库存 | 文档检索不够 | 否 | 是 |
| 固定分类体系、极稳口径 | few-shot 可解 | — | 样本很多、要极稳可考虑 | — |
| 多步任务、要操作外部系统 | 单次 prompt 不够 | 只提供信息 | 否 | 是 |
金句:能检索就不要塞进 prompt;能调用工具就不要让模型瞎编数字;能用 schema 就不要靠「请你输出 JSON」。
4. 安全(本模块要会的那部分)
详细在 07-评测可观测与安全,这里掌握 3 点即可:
- Prompt Injection:用户/文档里藏「忽略之前指令」。对策:分隔符、检索文档当数据、工具权限白名单、关键操作二次确认。
- 间接注入:RAG 抓到的网页/PDF 里带恶意指令,比用户直接注入更隐蔽。
- 泄露:系统提示、内部工具、其他用户数据不要回显。日志里脱敏。
不要把安全只写在 prompt 里。Prompt 是软约束,鉴权、允许列表、服务端校验 才是硬约束。
5. 工程化(应用岗加分)
上线级 prompt 不是一段字符串,而是一套资产:
- 模板化:Jinja / 消息数组模板,变量显式声明
- 版本号:
prompt_id + version,请求日志能复盘 - 环境隔离:dev / staging / prod 可回滚
- 评测集:每次改 prompt 跑回归(准确率、拒答率、schema 合法率、延迟、token)
- 在线监控:空回复、解析失败、用户点踩、超时
- A/B:只对一部分流量放新版本
可观测要记:完整 messages(脱敏后)、模型参数、检索片段 ID、解析是否成功。没有这些,线上坏了你无法复现。
6. 你要能现场画的两张图
图 1:单次问答装填
text
[稳定 System + 工具定义] ← 可缓存前缀
[Few-shot]
[会话摘要]
[近 K 轮]
[本轮 RAG / 工具观察]
[当前用户问题] ← 放最后图 2:超长对话生命周期
text
每轮:
若 token < 阈值 → 原文拼接
若超阈值 → 旧轮次抽成摘要,保留近 K 轮
若摘要也膨胀 → 摘要再压缩,重要状态写入结构化 memory
知识类问题 → 走 RAG,不把全书塞进历史7. 准备到什么程度算够(自检)
能不看稿讲 3 分钟:
- Prompt 零件有哪些,为什么要分隔符
- 什么是上下文工程,和「写提示词」有何不同
- Lost in the Middle 是什么、你怎么排顺序
- 对话爆窗的三层处理:滑动窗口 / 摘要 / RAG+记忆
- 结构化输出怎么做才稳
- 什么知识放 prompt、什么放 RAG、什么放工具
- 改 prompt 如何评测,而不是凭感觉
能现场手改:
- 把一段散文 prompt 改成带 schema 的模板
- 给一个「答非所问 / 幻觉 / JSON 偶尔坏」的 case 做诊断
8. 建议复习顺序
- 消息角色(system/user/assistant/tool)和 token 窗口
- Prompt 零件 + 结构化输出 + few-shot
- Lost in the Middle、顺序、截断
- 记忆:滑动窗口、摘要、长期记忆
- 工具结果压缩、Prompt Cache
- 注入攻击与「文档当数据」
- Prompt 版本与评测
本目录配合:02-RAG(检索当上下文)、03-Agent(工具观察当上下文)、07-评测与安全(怎么证明变好了)。
了解
思维链(Chain of Thought, CoT)
让模型先写出中间推理步骤,再给结论,而不是直接蹦答案。经典触发:「请一步步思考」;也可以在 few-shot 里示范带推理的例子。
- 解决什么 — 多步算术、规则判断、要可追溯理由。中间 token 当草稿纸,比一口出答案稳。
- 两种写法 — Zero-shot:只加「逐步思考」。Few-shot:示例里带完整推理。ToT / 自洽(多次采样投票)是加强版,更贵。
- RAG 里怎么用 — 证据已经在上下文里、还要跨多段综合时可以 CoT。不能用 CoT 补知识:库里没有的事实,想再多也是编。
- 上线注意 — 更慢、更费 token;推理可能泄露策略。对内 think、对外只给
answer + citations。延迟敏感、短答案、纯检索题不要开。
更细的「何时用/不用」看 01-Prompt与上下文工程/面试题.md B3。