提示词与上下文:系统提示分层、消息准备与压缩
30 秒导读: 模型每次生成前,gptme 都要临时拼出一份"喂给它的消息列表"。这份列表分两大块: 开头一串系统消息(身份 + 工具说明 + 用户/项目上下文,由
get_prompt组装,只在开会话时算一次) 和后面的对话历史(每轮都在长)。本章讲这两块怎么拼、怎么按"能不能被缓存"排序,以及当历史 吃掉太多 token 时,prepare_messages如何一步步把它裁回窗口里。
本章只讲"消息怎么被造出来喂给模型"。谁在什么时候调用它(主循环)见 01-agent-loop; 钩子如何在生成前后往里塞东西(active context、RAG)的挂载机制见 05-hooks-extensibility。
1. 这是什么(零基础也能懂)
一句话定义: 把"AI 该知道的一切"——它是谁、有哪些工具、在哪个项目、聊过什么——按顺序码成一串 消息,再确保这串消息不超过模型能吃的 token 上限。
解决什么问题: 大模型是"无状态"的——它不记得上一句,也不知道你的项目长什么样。每次请求你都得 把上下文重新讲一遍。但上下文会越堆越大(工具输出动辄几百行),而模型窗口是有限的。于是有两个 矛盾要同时解决:
- 要讲全:身份、工具、项目结构、Git 状态、历史……一样都不能漏。
- 要讲省:省钱(命中提示词缓存,provider 对没变的前缀打折)、省窗口(别撑爆 token 上限)。
它大概怎么用: 你在终端敲 gptme "帮我修一下这个 bug",gptme 在真正调模型之前,内部会:
① 开会话时:get_prompt(...) → [系统提示1, workspace提示, ...] # 一次性,尽量可缓存
② 每一轮生成前:prepare_messages(整段对话) → 裁剪过的消息列表 # 每轮都跑,保证不超窗口
一句话直觉: 把系统提示想成书的前言(写一次、基本不动、最该被缓存),把对话历史想成不断加页 的正文;当书太厚塞不进书包(窗口),就先撕掉最长的附录、再从最旧的正文开始丢。
2. 顶层全景(它大概怎么转)
整条链路分两个阶段,由两个入口函数把守:
会话开始(一次) 每轮生成前(反复)
────────────── ────────────────
get_prompt() prepare_messages(log.messages)
prompts/__init__.py:445 logmanager/manager.py:769
│ │
├─ 核心段 prompt_gptme/_tools/_user ├─ enrich 嵌入附件文件内容
├─ 可缓存段 prompt_workspace(项目/agent) ├─ reduce 截断最长消息直到达标
├─ ── 缓存边界标记 ── ├─ prune 丢弃过期的临时消息(thinking)
└─ 动态段 context_cmd / chat_history └─ limit 从最旧起丢,硬卡进窗口
│ │
▼ ▼
initial_msgs(存进 Log,pinned) msgs(临时,只为这一次请求)
怎么读这张图: 左边产出的 initial_msgs 会作为对话的开头存进日志;右边每轮把"日志全文"临时
加工一遍再发出去——右边不改磁盘上的日志,只裁一份副本。二者在 01 的主循环里
先后被调用(chat.py:546 调 prepare_messages)。
各部件一句话职责:
| 部件 | 干什么 | 在哪 |
|---|---|---|
get_prompt | 组装开场的一串系统消息 | prompts/__init__.py:445 |
prompt_gptme | 身份 + 行为准则(交互/非交互) | prompts/templates.py:88 |
prompt_tools / get_instructions | 把每个工具的说明写进 prompt | templates.py:307 / tools/base.py:492 |
prompt_workspace | 项目结构、Git 状态、AGENTS.md、选中文件 | prompts/workspace.py:176 |
prompt_skills_summary | 列出可按需 cat 加载的技能清单 | prompts/skills.py:13 |
prompt_chat_history | 跨会话的历史摘要(可选) | prompts/chat_history.py:20 |
prepare_messages | 生成前把整段对话裁进窗口 | logmanager/manager.py:769 |
reduce_log / limit_log | 截断最长消息 / 从旧到新硬删 | util/reduce.py:40 / :250 |
context/(自适应压缩) | 按任务类型决定压多狠 | context/adaptive_compressor.py |