Skip to content

System Prompt

源码版本rust-v0.145.0

codex assembles "what is said to the model" from several pieces of static text, templates, and context fragments. The prompts crate provides constants and templates; core picks the appropriate base instructions for the model at session startup, then layers in AGENTS.md, permissions instructions, and personality fragments to form the final system prompt. This page covers that assembly chain.

Responsibilities

  1. Maintain per-model default base instructions and personality templates, selecting and filling by model slug: codex-rs/protocol/src/openai_models.rs:478-500
  2. Expose constant prompts for tools / approval (apply_patch, compaction, review, permissions, realtime): codex-rs/prompts/src/lib.rs:1-27
  3. Compose project-level developer instructions from AGENTS.md and user instructions: codex-rs/core/src/agents_md.rs:53-80
  4. Render sandbox / approval policy into model-visible permissions instructions: codex-rs/prompts/src/permissions_instructions.rs:62-117
  5. Settle base_instructions at session startup and turn switches, for client / compact to read: codex-rs/core/src/session/mod.rs:615-631

Design motivation

Base instructions cannot be hardcoded in core. Different models (gpt-5.1, gpt-5.2-codex, etc.) have different default personalities and tool descriptions; the same model may also switch personalities (friendly / pragmatic / none). Putting templates in models-manager and core/templates/, with ModelMessages.instructions_template replacing the placeholder at runtime, lets adding a new model only require adding one markdown file — core stays untouched.

Another motivation is provenance. AGENTS.md may come from the user level, project root, or subdirectories; on multi-layer stacking, LoadedAgentsMd keeps the source of each entry and tags output by environment when needed, so the model knows which directory each instruction came from. Permission instructions are the same — they are not plain strings but ContextualUserFragments with <permissions instructions> markers, so the context manager can recognize and strip them during compaction or replay.

Key files

Base instructions has a three-level fallback — user config first, then recovering from conversation history, then the model default.

rust
let base_instructions = config
    .base_instructions
    .clone()
    .or_else(|| conversation_history.get_base_instructions().map(|s| s.text))
    .unwrap_or_else(|| model_info.get_model_instructions(config.personality));

Personality template replacement: if the model has instructions_template, always replace via the template; without a template, fall back to base_instructions.

rust
pub fn get_model_instructions(&self, personality: Option<Personality>) -> String {
    if let Some(model_messages) = &self.model_messages
        && let Some(template) = &model_messages.instructions_template
    {
        let personality_message = model_messages
            .get_personality_message(personality)
            .unwrap_or_default();
        template.replace(PERSONALITY_PLACEHOLDER, personality_message.as_str())
    } else {
        // ...
        self.base_instructions.clone()
    }
}

Permissions instructions are not plain strings but a ContextualUserFragment with markers, so the context manager can recognize and strip them during compaction or replay.

rust
impl ContextualUserFragment for PermissionsInstructions {
    fn role(&self) -> &'static str { "developer" }
    fn markers(&self) -> (&'static str, &'static str) { Self::type_markers() }
    fn type_markers() -> (&'static str, &'static str) {
        ("<permissions instructions>", "</permissions instructions>")
    }
    fn body(&self) -> String { PermissionsInstructions::body(self) }
}

Data flow

Boundaries and failures

Summary

The system prompt in codex is not a hardcoded string but a three-level fallback (model default → config override → history snapshot) plus marker-tagged fragments like AGENTS.md, permissions instructions, and personality. The prompts crate only stores constants and templates; the assembly logic lives in core, and the BaseInstructions callers receive is already a settled string. Understanding this chain is key to debugging "why did the model answer this way" in Agent main loop and "which sections get stripped and replayed" in compaction.