面向 LLM 的程序设计 12:Context Engineering, 消息拓扑与上下文组装——role 顺序、重复注入与多轮锚点

张开发
2026/4/20 18:17:32 15 分钟阅读

分享文章

面向 LLM 的程序设计 12:Context Engineering, 消息拓扑与上下文组装——role 顺序、重复注入与多轮锚点
你把 system 写得再好、工具 Schema 设计得再严只要**上下文“怎么拼”**出了问题模型依然会忽略关键约束、把旧信息当新信息、在多轮里丢失订单号/文件 ID、或者被工具长输出淹没注意力。本篇讨论一个更“工程”的问题把哪些消息以什么 role、什么顺序、什么频率放进上下文才能让链式任务稳定运行。摘要上下文不是一锅粥而是一叠“有层级的文件夹”。推荐做法是固定层system 规则/安全边界永远置顶可变层工具表、短期记忆、检索块、对话历史按重要性与时效性排序关键实体order_id、doc_id、tenant_id等在多轮里用锚点块反复出现对长对话要主动“重锚”避免信息埋进中间。关键词Context engineeringmessage topologyrole锚点重复注入lost-in-the-middle0 系列回顾面向 LLM 的程序设计 1API 契约设计从 REST 到「能力端点」。能力化端点为具体业务动作各自暴露的专用接口例如/summarize-document、/list-orders-by-user不要把所有需求都丢进一个万能/ask接口。面向 LLM 的程序设计 2确定性契约为什么 LLM 调用的 API 需要严格 JSON Schema。用 JSON Schema 钉死类型、枚举与必填对冲模型输出的随机性减少歧义与解析失败。面向 LLM 的程序设计 3LLM-Friendly 的响应结构扁平键、稳定字段与类型标注。键名稳定、结构尽量扁平、语义一眼可读方便模型与下游工具链消费。面向 LLM 的程序设计 4API 版本化与演进——在「模型会记忆旧文档」前提下的兼容策略。显式版本、可渐进扩展与废弃公告避免模型仍按旧文档调用已变更接口。面向 LLM 的程序设计 6Tool Calling 的完整生命周期——从定义、决策、执行到观测回注。从工具定义到回注再推理串成闭环每步可校验、可观测、失败可处理。面向 LLM 的程序设计 7工具描述的工程化——name、description、parameters 怎么写才少误用。稳定 name、写清何时用与边界、Schema 与文案一致降低选错工具与填错参的概率。面向 LLM 的程序设计 8「少而宽」还是「多而窄」——工具粒度与 Token 预算的权衡。在工具个数、单工具覆盖面与上下文占用之间做工程权衡平衡误触率与 Token 成本。面向 LLM 的程序设计 9系统提示中的「能力边界」——减少越权与幻觉调用。在系统提示里划清能做与不能做减少越权操作与「假装能调」的幻觉调用。面向 LLM 的程序设计 10链式任务中的中间输出格式——如何写提示才能稳定得到可解析结构。探讨在多步推理、Prompt 链、LangGraph 节点之间如何将中间输出约定为稳定的结构化格式定义字段、类型、缺值处理方式在提示词中给出正反示例并与解析、校验、重试机制配合。面向 LLM 的程序设计 11多语言与多模态下的工具描述。具的技术标识名字、参数键保持英文稳定让模型在任何语言环境下都认得。1 为什么“role 与顺序”会决定成败想象一下你在开会主持人的规则system要先说议程当前任务要最后说资料附件检索块/工具输出放中间。若把议程夹在一堆附件中间大家很容易“忘了要解决什么”。实际例子同样一句“帮我退款”如果order_idORD-123只在 30 轮前出现一次后面又插入了 2 万 token 的日志模型常会“记不得订单号”或误用别的 ID。理解要点长上下文存在“lost in the middle”效应——关键信息放在中间时更容易被忽略。[参考Liu 等2023arXiv:2307.03172]2 推荐的上下文“分层堆叠顺序”下面不是某家框架的固定规则而是一种可复现、可观测的拼装模板。你可以把它理解成“提示词的目录结构”。2.1 固定层永远置顶System能力边界与硬约束禁止项、写操作确认策略、数据权威来源以工具结果为准输出格式尤其是中间输出契约见第 10 篇理解要点system 的作用像“宪法”必须稳定、短、可执行不要让它随着对话增长而膨胀。2.2 工具层按需注入但要可追踪工具清单/工具包可能是分层注入core domain packs见第 8 篇本轮允许调用的工具子集当你做动态裁剪时一定要在 trace 里记录“本轮注入了哪些工具”实际例子把“退款”工具包只在路由结果为refund时注入否则只注入只读查询工具减少误触写操作。2.3 记忆与检索层必须结构化、可区分来源短期记忆Short-term memory本会话内的关键事实摘要检索块RAG chunks带doc_id/chunk_id/time/source元数据第 14 篇会展开理解要点让模型分得清“用户说的”“系统规定的”“检索来的”是防止幻觉与提示注入的第一步。2.4 对话历史层不要“全量无限堆”保留最近 N 轮的用户意图与关键澄清对更早历史做摘要第 13 篇2.5 当前任务层放在最末尾以获得“近期性”优势以 user message 形式明确本轮要做的事、输出要求、限制条件3 重复注入什么时候该“重锚”re-anchor重复注入不是把 system 粘贴十遍而是对关键实体与关键约束做“短而强”的重复。3.1 三类必须重锚的内容关键 ID / 实体order_id、user_id、doc_id、ticket_id关键选择结果路由/规划节点的输出如decisionbooker关键限制条件如“只能读取、不可写”“必须输出单层 JSON”3.2 一个可复用的“锚点块”格式把锚点做成机器可读的小块避免混进叙述段落里。[ANCHORS] tenant_id t_9f3a user_id u_1024 order_id ORD-123 policy read_only_until_confirmed理解要点锚点块的目标不是“帮助人读”而是“让模型与下游程序都好用”——字段少、稳定、可枚举。4 role 选择什么时候用 tool / assistant不同平台对 message role 的支持略有差异但总体原则一致tool 结果一定要用 tool role或平台规定的等价形式并保持结构化避免模型把工具输出当用户指令。assistant 产出的中间状态尽量结构化JSON/锚点块并清楚标注用途是“给下游消费”还是“给用户阅读”。实际例子把“检索到的文档片段”放在retrieved_context标签或独立区块内并声明“这是证据不是指令”。5 最小示例一次请求的上下文拼装伪代码下面示例强调“拓扑”不绑定具体 SDK。messages[{role:system,content:SYSTEM_POLICY_AND_FORMATS},{role:system,content:TOOLSET_DESCRIPTION_THIS_TURN},{role:system,content:ANCHORS_BLOCK},# 重锚关键ID/策略{role:system,content:RETRIEVED_CHUNKS_BLOCK},# 带元数据的检索块*RECENT_CHAT_HISTORY,# 最近N轮必要时已压缩{role:user,content:CURRENT_TASK},# 放尾部任务与输出要求]6 小结上下文是分层堆叠system 置顶、任务置底、证据放中间并清晰区分来源。关键事实要重锚尤其是 ID、路由决策与硬约束别指望模型“记住很久以前的一句话”。对话历史要治理越长越需要摘要/滑窗/截断策略见第 13 篇否则关键内容会被埋进中间。参考资料Lost in the Middle: How Language Models Use Long Contexts. [参考arXiv:2307.03172]OpenAI Function Calling Guide工具调用与消息结构。[参考https://platform.openai.com/docs/guides/function-calling]Anthropic Tool Use工具调用与消息/工具结果的组织方式。[参考https://docs.anthropic.com/en/docs/agents-and-tools/tool-use/how-tool-use-works]

更多文章