大模型上下文工程决定了应用的实际效果上限。模型能力再强,喂进去的上下文混乱、超长、关键信息被淹没,输出质量照样崩塌。上下文工程要解决三个问题:System Prompt怎么写才稳定、多轮对话历史怎么压缩、检索内容怎么摆放才不被忽略。
System Prompt结构化设计:角色定义与输出约束的固定模板
System Prompt写法上,松散的自然语言描述容易导致模型输出漂移,换成结构化分段写法能显著提升稳定性。常用结构分五段:角色定义、能力边界、输出格式、示例、兜底规则。
# 结构化 System Prompt 模板
system_prompt = """
# 角色
你是一个电商客服助手,服务范围是订单查询、退换货政策、物流咨询。
# 能力边界
- 只回答电商相关问题
- 不提供医疗、法律、投资建议
- 涉及具体订单金额变更时,引导用户联系人工客服
# 输出格式
- 每次回复不超过3句话
- 需要用户提供信息时,明确列出所需项
- 涉及政策条款时引用政策编号
# 兜底规则
- 无法理解用户意图时,回复固定话术并给出3个常见问题选项
- 检测到用户情绪激动时,优先安抚再解决问题
# 示例
用户:我的订单怎么还没到
回复:请提供订单号,为您查询物流状态。同时可参考常见时效说明。
"""
段落式写法的关键是把约束拆成原子规则,每条规则只管一件事。实测同一批测试问题,结构化Prompt的意图识别准确率比松散写法高出两成以上,且长对话后约束衰减更慢。原因在于模型对带标题的文本块定位更准,注意力分配更集中。
多轮对话上下文压缩:滑动窗口与摘要拼接的实现方案
多轮对话超出上下文窗口是常态。直接截断最旧的轮次会丢失关键信息,比如用户在第一轮说过的订单号,截断后模型开始反复追问。工程上用滑动窗口加滚动摘要的组合方案。
from collections import deque
class ContextManager:
def __init__(self, max_turns=10, keep_recent=4):
self.turns = deque(maxlen=max_turns) # 完整对话窗口
self.summary = "" # 滚动摘要
self.keep_recent = keep_recent # 近期轮次保留原文
def add(self, role, content):
self.turns.append({"role": role, "content": content})
def compress(self, llm):
# 窗口满时,把旧轮次压缩进摘要
if len(self.turns) >= self.turns.maxlen:
old = [t for t in list(self.turns)[:-self.keep_recent]]
prompt = (
"把以下对话历史压缩成要点摘要,"
"保留:用户身份信息、订单号、明确的诉求、已承诺的事项。"
"删除寒暄与重复内容。\n" + str(old)
)
self.summary = llm.chat(prompt)
# 只保留近期轮次原文
recent = list(self.turns)[-self.keep_recent:]
self.turns.clear()
self.turns.extend(recent)
def build_messages(self):
msgs = []
if self.summary:
msgs.append({
"role": "system",
"content": f"历史对话摘要:{self.summary}"
})
msgs.extend(list(self.turns))
return msgs
压缩时机放在窗口将满之前,不要等到截断发生。摘要提示词里明确列出保留项类别,比一句概括所有历史的模糊指令效果好。订单号、日期、金额这类实体信息建议单独抽取存字段,摘要里再引用,避免摘要本身把数字抄错。
检索增强内容注入:RAG上下文摆放位置与信息密度控制
检索回来的内容直接塞在对话末尾,模型容易只看最后几段。位置上,检索内容放在用户最新问题之前、以独立system消息注入,效果优于拼接到用户消息尾部。注入时给每段检索内容加来源标签,模型引用时能对上号。
def build_rag_messages(query, retrieved_docs, history):
context = "\n\n".join(
f"[资料{i+1}] {d['content']}(来源:{d['source']})"
for i, d in enumerate(retrieved_docs[:4])
)
return [
{"role": "system", "content": f"参考资料如下,回答优先使用资料内容,资料未覆盖的部分基于通用知识回答并注明。\n\n{context}"},
*history,
{"role": "user", "content": query}
]
资料条数控制在3到5条。条数超过8条后,中间位置的资料被引用率明显下降,存在明显的中间遗忘现象。资料本身先做压缩,把每条压到200字以内,只留与问题相关的句子,比整段原文直接注入的召回利用率更高。
常见问题处理
长对话后模型开始答非所问:优先检查压缩逻辑,摘要是否保留了当前话题的关键实体,近期轮次窗口是否把当前问题隔在门外。
System Prompt约束逐渐失效:每N轮对话把System Prompt关键约束以system消息重申一次,或在压缩摘要里附带约束提醒。
检索内容与模型知识冲突:System Prompt里明确优先级声明,资料内容优先于模型记忆,并要求引用来源编号。
上下文Token成本失控:统计各部分Token占比,通常历史对话占大头。降低保留轮次、摘要压缩比调小、检索资料截断,三处下手能把单次请求Token量压掉一半以上。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/da-mo-xing-shang-xia-wen-gong-cheng-shi-zhan-systemprompt/