-
Notifications
You must be signed in to change notification settings - Fork 5
EN Course 05 System Prompt Builder
The system prompt is a cache-sensitive protocol surface. Small byte differences can reduce provider KV-cache reuse, so sections are appended through a common helper.
util_append_section() {
local __outvar="$1" tag="$2" content="$3" name="${4:-}" wrapped
[[ -n "$content" ]] || return 0
if [[ -n "$name" ]]; then
wrapped=$(printf '<%s name="%s">\n%s\n</%s>' "$tag" "$(util_json_escape "$name")" "$content" "$tag")
else
wrapped=$(printf '<%s>\n%s\n</%s>' "$tag" "$content" "$tag")
fi
printf -v "$__outvar" '%s%s\n' "${!__outvar}" "$wrapped"
}agent_build_prompt appends sections in a fixed order:
util_append_section output "agent-identity" "$agent_identity"
util_append_section output "environment" "$environment"
util_append_section output "rules" "$core_rules"
util_append_section output "using-your-tools" "$tool_guidance"
util_append_section output "sub-agent-guidance" "$sub_agent_guidance"
util_append_section output "todo-guidance" "$todo_guidance"
util_append_section output "plan-lifecycle-guidance" "$plan_lifecycle_guidance"
util_append_section output "instruction-files" "$instruction_files"
util_append_section output "skill-index" "$skill_index"
util_append_section output "selected-skills" "$selected_skills"
util_append_section output "current-plan" "$plan" "${PLAN_FILE:-}"
util_append_section output "context-snapshot" "$stable_context"
util_append_section output "output-language" "$output_language_reaffirm"That means the actual top-level prompt sections are:
| Section | Purpose |
|---|---|
agent-identity |
runtime identity, localized by locale |
environment |
language, cwd, home, platform, shell |
rules |
concise behavior and failure-reporting rules |
using-your-tools |
general tool-use guidance |
sub-agent-guidance |
delegation, fork mode, and result handling |
todo-guidance |
when and how to use TodoWrite
|
plan-lifecycle-guidance |
draft/confirm/execute plan protocol |
instruction-files |
global and project instruction-file sections |
skill-index |
available skill names and summaries |
selected-skills |
full content for explicitly selected skills |
current-plan |
locked plan, named by PLAN_FILE
|
context-snapshot |
compacted long-session summary |
output-language |
locale reaffirmation |
The order and formatting are part of the contract. Bash, C, Go, and Rust must produce the same prompt bytes for the same session state.
Some top-level sections contain nested sections. Instruction files are collected as instruction-file entries:
util_build_instructions_section() {
local output="" global_file project_file global_content project_content
global_file=$(util_find_instruction_file "${HOME}/.bash-agent" 2>/dev/null || true)
project_file=$(util_find_instruction_file "${PWD:-$(pwd)}" 2>/dev/null || true)
if [[ -n "$global_file" ]]; then
global_content=$(<"$global_file") || return 1
util_append_section output "instruction-file" "$global_content" "global"
fi
if [[ -n "$project_file" ]]; then
project_content=$(<"$project_file") || return 1
util_append_section output "instruction-file" "$project_content" "project"
fi
printf '%s' "${output%$'\n'}"
}Selected skills are also nested as individual skill sections inside selected-skills.
plan.draft exists because editing a locked plan changes the system prompt. Drafting keeps intermediate planning text out of the prompt until it is confirmed.
store_plan_confirm() {
[[ -n "$PLAN_DRAFT_FILE" && -s "$PLAN_DRAFT_FILE" ]] && {
mv "$PLAN_DRAFT_FILE" "$PLAN_FILE"
: > "$PLAN_DRAFT_FILE"
return 0
}
return 1
}This makes plan confirmation a deliberate cache boundary.
Compaction and Long Sessions explains how stable prompt structure and session stats support long-running conversations.