题干
- 什么是 Context Engineering?与 Prompt Engineering 有什么差别?
- 长 Prompt / 长上下文 的设计有哪些坑?
- Memory 在 agent 中怎么分层设计?
- Context Window 满了应该怎么办?截断 / 摘要 / RAG?
- 动态上下文(按场景切换不同 system prompt / tool 注册)的工程做法?
- Lost-in-the-Middle / 位置偏置 是什么?怎么缓解?
题解
1. Context Engineering vs Prompt Engineering
text
Prompt Engineering:
优化单个 prompt 输入, 关心措辞 / 格式 / few-shot
Context Engineering:
把 prompt 看作 "整个上下文工程":
- system / user / assistant 多轮消息
- tool 描述 + 工具结果
- 长期 memory (跨 session)
- 检索到的 RAG 文档
- 用户偏好 / 历史 / profile
- 元数据 / 时间戳 / 来源
更系统:把上下文视作数据结构,设计加载/替换/淘汰策略。
2. 长 Prompt 的工程原则
text
① 简洁
减少冗余 token, 每次调用都直接省钱
② 层次
结构化分块: role / task / format / constraints / examples
③ 顺序敏感
模型对末尾指令权重最大, 重要约束放最后
④ 引用而非重复
重复长文本用 "@doc_3" 引用而非 copy paste
⑤ 格式稳定
优先 markdown / JSON / XML 等清晰结构
⑥ 标签显式
```<document id=3>...</document>```远比 "以下是文档" 强
⑦ 显式提醒
长 prompt 中 "按上述 system 角色回答" 在结尾再加一句
3. Lost-in-the-Middle 与位置偏置
实验 (Liu et al. 2023) 发现:
text
信息放在 prompt 中部时, 模型 recall 显著下降
首部 / 尾部 recall 较好
工程应对:
text
① 重要信息放在最前 / 最后
② 把文档按重要性排序: 最相关在前
③ 标注 ID 让模型即使在中部也"看得到"
④ 使用 CoT/Reasoning 强化检索来源 awareness
⑤ chunk 太小 → 关键信息常落中部 → 加 chunk 级 summary
4. Memory 分层
经典三层架构:
text
短期 (In-context memory):
- 当前 session 的对话 / tool 结果
- 直接放在 prompt / messages
- 上限: context window (4k~1M)
中期 (Session memory):
- 当前 session 的要点摘要 / 任务进度
- 摘要压缩写入 message
- 不再扩展为新一轮
长期 (User memory / Profile):
- 用户偏好 / 历史 / 知识库
- 在系统提示中以"UserProfile" 段落形式注入
- 可来自 RAG / 向量库 / 数据库
工程实现
python
# 三层 memory 的典型组装
def build_prompt(user_msg, session, profile, rag):
msgs = []
# 1) System with profile + role
msgs.append({
"role": "system",
"content": f"""
<role>你是...</role>
<user_profile>{profile.summary}</user_profile>
<task>{task}</task>
"""
})
# 2) 长期: RAG docs
for d in rag.retrieve(user_msg, k=5):
msgs.append({"role": "system", "content": f"<doc id={d.id}>{d.text}</doc>"})
# 3) 中期: session summary
msgs.append({"role": "system", "content": f"<session_summary>{session.summary}</session_summary>"})
# 4) 短期: recent dialog
msgs.extend(session.last_messages)
# 5) 当前 user message
msgs.append({"role": "user", "content": user_msg})
return msgs
5. 上下文窗口满时的处理
text
方法:
① 滑动窗口: 保留 system + 后 N 轮, 中间 summary
② Recursive Summary:
每隔 K 轮, 把历史摘要为 memory_note
③ Token-aware: 按 token 数截断, 不按消息数
④ Selective: 仅保留高重要性消息 (例如最近 / 工具结果 / 用户意图)
⑤ RAG for chat: 把历史向量化, 需要时再检索
工程经验:
text
- 100k+ context 是真的 (NIAH 评测), 但 RAG 任务上仍不如 RAG
- 让模型 "看 100k 上下文" ≠ "有效使用"
- RAG + 短上下文 (8k~32k) 是更稳的方案
- 用 Gemini 1M / Claude 200k 时, 注意 latency 上升
6. Context Caching
text
- Anthropic Prompt Caching / OpenAI cached prompt / Qwen Context Cache
- 把 system + 长期 RAG 文档 标记为 cacheable
- 价格折半 5~10×
- 端到端 prompt 模板不变, SDK 标记 cache 段
应用:
text
- 多轮对话 system 段固定
- RAG docs 同一来源多次 query
- Agent system + tool registry 固定
7. 动态上下文
按场景切换:
text
- 根据用户意图动态加载 tool
- 根据任务类型动态切换 prompt 模板
- 根据语种 / domain 加 prefix
实现:
python
def dynamic_context(user_msg):
intent = classify(user_msg)
template = TEMPLATE[intent] # 模板
tools = TOOL_REGISTRY[intent] # 工具子集
docs = rag.search(user_msg, k=5)
return template.format(tools=tools, docs=docs), tools
8. 工具 / Memory 的注入位置
text
- system message: 适合静态 / 长效信息
- user message: 适合当前 query 相关文档
- assistant message: 适合中间思考 / 摘要
- tool result message: 工具返回值 (原生多模态模型可附带图像 / 音频)
注意:
- 工具描述很占 token
- 把工具拆成"按需"
- 实在多, 让模型先选择子集 (router LLM)
9. 多模态上下文
text
- 图像: base64 + tag
- 音频: 文本转写 / embedding
- 视频: 抽帧 + 时间戳
- 文件: 转 PDF / 解析文本
工程:
- 把多模态内容塞 system message (允许)
- 用 native multimodal 模型更省
- 多模态 attention 计算开销大, 注意 token 估算
10. 上下文可观测性
text
- 每次 LLM 调用记录: token 数, 段分布
- 用占比堆叠图看 system vs RAG vs history 比例
- 警惕: 一个动态上下文"看起来合理", 实际 token 爆炸
- 工具: Langfuse / Helicone / Helicone OpenLLMetry / 自研 trace
11. 实战模板 (Context Engineering Blueprint)
text
1) profile / role
2) task / instructions
3) format / output schema
4) constraints / safety
5) few-shot (≥ 1, ≤ 3)
6) long-term memory
7) RAG docs
8) conversation history (summary + recent)
9) current user query
10) tool definitions (按需)
把每段用 XML / 标签包裹,模型对 <section> 类的边界识别率高。
12. 常见误区
| 误区 | 修正 |
|---|---|
| 上下文越长越好 | "用到" 比"塞进去" 重要 |
| 越详细越准 | 模型注意力被稀释, 重点不清 |
| 摘要就够 | 摘要丢失细节, 关键 token 必须保留 |
| Memory 不用结构化 | 必须有 schema, 不然跨 session 难组合 |
| Prompt 工程 = Context 工程 | Prompt 是局部, Context 是系统 |
13. 与 Long Context 的关系
text
- Long Context 提供 "能力上限" (1M context)
- Context Engineering 提供 "利用率"
- 真正的产品既要 "1M context" 也需要 "Memory + RAG + 结构化"
代码示例
python
# 一个 token-aware 历史摘要器
def trim_history(messages, max_tokens, summarizer):
tokens = count_tokens(messages)
if tokens <= max_tokens:
return messages
# 保留 system + 末尾若干, 中间摘要
head = messages[:1]
tail = messages[-6:]
middle = messages[1:-6]
summary = summarizer("请用 200 字概括以下对话:\n" + render(middle))
new = head + [{"role": "system",
"content": f"<conversation_summary>{summary}</conversation_summary>"}] + tail
return new
python
# 动态工具注册
def select_tools(query, registry, llm):
names = list(registry.keys())
prompt = (
"用户问: " + query + "\n"
"可用工具: " + ", ".join(names) + "\n"
"选 1~3 个最相关的, 输出逗号分隔:"
)
return registry.choose(llm(prompt))
面试延伸
- Context Caching 的命中率提升策略有哪些?
- Lost-in-the-Middle 是因为位置编码还是 attention 偏置?
- 在多 agent 系统中,每个 agent 的 context window 怎么设计?
- 当 RAG 文档与对话历史同时存在,如何防止信息冗余 / 冲突?