System Prompt
System HardeningAssemble Inputs as a Pipeline|305 LOC|4 tools
The model sees a constructed input pipeline, not one giant static string.
s00 > s01 > s02 > s03 > s04 > s05 > s06 > s07 > s08 > s09 > [ s10 ] > s11 > s12 > s13 > s14 > s15 > s16 > s17 > s18 > s19
系統提示詞不是一整塊大字串,而是一條可維護的組裝流水線。
這一章為什麼重要
很多初學者一開始會把 system prompt 寫成一大段固定文字。
這樣在最小 demo 裡當然能跑。
但一旦系統開始長功能,你很快會遇到這些問題:
- 工具列表會變
- skills 會變
- memory 會變
- 當前目錄、日期、模式會變
- 某些提醒只在這一輪有效,不該永遠塞進系統說明
所以到了這個階段,system prompt 不能再當成一塊硬編碼文字。
它應該升級成:
由多個來源共同組裝出來的一條流水線。
建議聯讀
- 如果你還習慣把 prompt 看成“神秘大段文字”,先回
s00a-query-control-plane.md,重新確認模型輸入在進模型前經歷了哪些控制層。 - 如果你想真正穩住“哪些內容先拼、哪些後拼”,建議把
s10a-message-prompt-pipeline.md放在手邊,這頁就是本章最關鍵的橋。 - 如果你開始把 system rules、工具說明、memory、runtime state 混成一個大塊,先看
data-structures.md,把這些輸入片段的來源重新拆開。
先解釋幾個名詞
什麼是 system prompt
system prompt 是給模型的系統級說明。
它通常負責告訴模型:
- 你是誰
- 你能做什麼
- 你應該遵守什麼規則
- 你現在處在什麼環境裡
什麼是“組裝流水線”
意思是:
- 不同資訊來自不同地方
- 最後按順序拼接成一份輸入
它不是一個死字串,而是一條構建過程。
什麼是動態資訊
有些資訊經常變化,例如:
- 當前日期
- 當前工作目錄
- 本輪新增的提醒
這些資訊不適合和所有穩定說明混在一起。
最小心智模型
最容易理解的方式,是把 system prompt 想成 6 段:
1. 核心身份和行為說明
2. 工具列表
3. skills 元資訊
4. memory 內容
5. CLAUDE.md 指令鏈
6. 動態環境資訊
然後按順序拼起來:
core
+ tools
+ skills
+ memory
+ claude_md
+ dynamic_context
= final system prompt
為什麼不能把所有東西都硬塞進一個大字串
因為這樣會有三個問題:
1. 不好維護
你很難知道:
- 哪一段來自哪裡
- 該修改哪一部分
- 哪一段是固定說明,哪一段是臨時上下文
2. 不好測試
如果 system prompt 是一大坨文字,你很難分別測試:
- 工具說明生成得對不對
- memory 是否被正確拼進去
- CLAUDE.md 是否被正確讀取
3. 不好做快取和動態更新
一些穩定內容其實不需要每輪大變。
一些臨時內容又只該活一輪。
這就要求你把“穩定塊”和“動態塊”分開思考。
最小實現結構
第一步:做一個 builder
class SystemPromptBuilder:
def build(self) -> str:
parts = []
parts.append(self._build_core())
parts.append(self._build_tools())
parts.append(self._build_skills())
parts.append(self._build_memory())
parts.append(self._build_claude_md())
parts.append(self._build_dynamic())
return "\n\n".join(p for p in parts if p)
這就是這一章最核心的設計。
第二步:每一段只負責一種來源
例如:
_build_tools()只負責把工具說明生成出來_build_memory()只負責拿 memory_build_claude_md()只負責讀指令檔案
這樣每一段的職責就很清楚。
這一章最關鍵的結構化邊界
邊界 1:穩定說明 vs 動態提醒
最重要的一組邊界是:
- 穩定的系統說明
- 每輪臨時變化的提醒
這兩類東西不應該混為一談。
邊界 2:system prompt vs system reminder
system prompt 適合放:
- 身份
- 規則
- 工具
- 長期約束
system reminder 適合放:
- 這一輪才臨時需要的補充上下文
- 當前變動的狀態
所以更清晰的做法是:
- 主 system prompt 保持相對穩定
- 每輪額外變化的內容,用單獨的 reminder 方式追加
一個實用的教學版本
教學版可以先這樣分:
靜態部分:
- core
- tools
- skills
- memory
- CLAUDE.md
動態部分:
- date
- cwd
- model
- current mode
如果你還想再清楚一點,可以加一個邊界標記:
=== DYNAMIC_BOUNDARY ===
它的作用不是神秘魔法。
它只是提醒你:
上面更穩定,下面更容易變。
CLAUDE.md 為什麼要單獨一段
因為它的角色不是“某一次任務的臨時上下文”,而是更穩定的長期說明。
教學倉裡,最容易理解的鏈條是:
- 使用者全域性級
- 專案根目錄級
- 當前子目錄級
然後全部拼進去,而不是互相覆蓋。
這樣讀者更容易理解“規則來源可以分層疊加”這個思想。
memory 為什麼要和 system prompt 有關係
因為 memory 的本質是:
把跨會話仍然有價值的資訊,重新帶回模型當前的工作環境。
如果儲存了 memory,卻從來不在系統輸入中重新呈現,那它就等於沒被真正用起來。
所以 memory 最終一定要進入 prompt 組裝鏈條。
初學者最容易混淆的點
1. 把 system prompt 講成一個固定字串
這會讓讀者看不到系統是如何長大的。
2. 把所有變化資訊都塞進 system prompt
這會把穩定說明和臨時提醒攪在一起。
3. 把 CLAUDE.md、memory、skills 寫成同一種東西
它們都可能進入 prompt,但來源和職責不同:
skills:可選能力或知識包memory:跨會話記住的資訊CLAUDE.md:長期規則說明
教學邊界
這一章先只建立一個核心心智:
prompt 不是一整塊靜態文字,而是一條被逐段組裝出來的輸入流水線。
所以這裡先不要擴到太多外層細節:
- 不要先講複雜的 section 註冊系統
- 不要先講快取與預算
- 不要先講所有外部能力如何追加 prompt 說明
只要讀者已經能把穩定規則、動態提醒、memory、skills 這些來源看成不同輸入段,而不是同一種“大 prompt”,這一章就已經講到位了。
如果你開始分不清 prompt、message、reminder
這是非常正常的。
因為到了這一章,系統輸入已經不再只有一個 system prompt 了。
它至少會同時出現:
- system prompt blocks
- 普通對話訊息
- tool_result 訊息
- memory attachment
- 當前輪 reminder
如果你開始有這類困惑:
- “這個資訊到底該放 prompt 裡,還是放 message 裡?”
- “為什麼 system prompt 不是全部輸入?”
- “reminder 和長期規則到底差在哪?”
建議繼續看:
這章和後續章節的關係
這一章像一個匯合點:
s05skills 會匯進來s09memory 會匯進來s07的當前模式也可能匯進來s19MCP 以後也可能給 prompt 增加說明
所以 s10 的價值不是“新加一個功能”,
而是“把前面長出來的功能組織成一份清楚的系統輸入”。
學完這章後,你應該能回答
- 為什麼 system prompt 不能只是一整塊硬編碼文字?
- 為什麼要把不同來源拆成獨立 section?
- system prompt 和 system reminder 的邊界是什麼?
- memory、skills、CLAUDE.md 為什麼都可能進入 prompt,但又不是一回事?
一句話記住:system prompt 的關鍵不是“寫一段很長的話”,而是“把不同來源的資訊按清晰邊界組裝起來”。