From 125927e2f1e5b649e874776d316cc3897502d043 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sun, 12 Apr 2026 01:23:50 -0400 Subject: [PATCH] =?UTF-8?q?Drop=20redundant=20system=20prompt=20=E2=80=94?= =?UTF-8?q?=20all=20info=20is=20in=20memory=20nodes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The system prompt duplicated what's already in core-personality and other memory nodes. Moving everything to memory means it's all trainable data rather than hardcoded strings. Co-Authored-By: Proof of Concept --- src/agent/mod.rs | 4 +--- src/agent/oneshot.rs | 4 ++-- src/config.rs | 36 ++++++------------------------------ src/mind/identity.rs | 21 --------------------- src/mind/mod.rs | 1 - src/mind/unconscious.rs | 5 +---- src/user/context.rs | 1 - src/user/mod.rs | 5 ----- 8 files changed, 10 insertions(+), 67 deletions(-) diff --git a/src/agent/mod.rs b/src/agent/mod.rs index d10683b..1545b04 100644 --- a/src/agent/mod.rs +++ b/src/agent/mod.rs @@ -186,7 +186,6 @@ pub struct AgentState { impl Agent { pub async fn new( client: ApiClient, - system_prompt: String, personality: Vec<(String, String)>, app_config: crate::config::AppConfig, prompt_file: String, @@ -196,7 +195,6 @@ impl Agent { ) -> Arc { let mut context = ContextState::new(); context.conversation_log = conversation_log; - context.push_no_log(Section::System, AstNode::system_msg(&system_prompt)); let tool_defs: Vec = agent_tools.iter().map(|t| t.to_json()).collect(); @@ -571,7 +569,7 @@ impl Agent { pub async fn compact(&self) { match crate::config::reload_for_model(&self.app_config, &self.prompt_file) { - Ok((_system_prompt, personality)) => { + Ok(personality) => { let mut ctx = self.context.lock().await; // System section (prompt + tools) set by new(), don't touch it ctx.clear(Section::Identity); diff --git a/src/agent/oneshot.rs b/src/agent/oneshot.rs index 7dbc206..f218080 100644 --- a/src/agent/oneshot.rs +++ b/src/agent/oneshot.rs @@ -258,12 +258,12 @@ impl AutoAgent { let cli = crate::user::CliArgs::default(); let (app, _) = crate::config::load_app(&cli) .map_err(|e| format!("config: {}", e))?; - let (system_prompt, personality) = crate::config::reload_for_model( + let personality = crate::config::reload_for_model( &app, &app.prompts.other, ).map_err(|e| format!("config: {}", e))?; let agent = Agent::new( - client, system_prompt, personality, + client, personality, app, String::new(), None, super::tools::ActiveTools::new(), diff --git a/src/config.rs b/src/config.rs index 98d2c23..e2a59ca 100644 --- a/src/config.rs +++ b/src/config.rs @@ -354,8 +354,6 @@ pub struct AppConfig { pub dmn: DmnConfig, #[serde(skip_serializing_if = "Option::is_none")] pub memory_project: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub system_prompt_file: Option, #[serde(default)] pub models: HashMap, #[serde(default = "default_model_name")] @@ -469,7 +467,6 @@ impl Default for AppConfig { }, dmn: DmnConfig { max_turns: 20 }, memory_project: None, - system_prompt_file: None, models: HashMap::new(), default_model: String::new(), mcp_servers: Vec::new(), @@ -486,7 +483,6 @@ pub struct SessionConfig { pub api_key: String, pub model: String, pub prompt_file: String, - pub system_prompt: String, /// Identity/personality files as (name, content) pairs. pub context_parts: Vec<(String, String)>, pub config_file_count: usize, @@ -539,16 +535,8 @@ impl AppConfig { let context_groups = get().context_groups.clone(); - let (system_prompt, context_parts, config_file_count, memory_file_count) = - if let Some(ref path) = cli.system_prompt_file.as_ref().or(self.system_prompt_file.as_ref()) { - let content = std::fs::read_to_string(path) - .with_context(|| format!("Failed to read {}", path.display()))?; - (content, Vec::new(), 0, 0) - } else { - let system_prompt = crate::mind::identity::assemble_system_prompt(); - let (context_parts, cc, mc) = crate::mind::identity::assemble_context_message(&cwd, &prompt_file, self.memory_project.as_deref(), &context_groups)?; - (system_prompt, context_parts, cc, mc) - }; + let (context_parts, config_file_count, memory_file_count) = + crate::mind::identity::assemble_context_message(&cwd, &prompt_file, self.memory_project.as_deref(), &context_groups)?; let session_dir = dirs::home_dir() .unwrap_or_else(|| PathBuf::from(".")) @@ -561,7 +549,7 @@ impl AppConfig { Ok(SessionConfig { api_base, api_key, model, prompt_file, - system_prompt, context_parts, + context_parts, config_file_count, memory_file_count, session_dir, app: self.clone(), @@ -663,7 +651,6 @@ fn build_figment(cli: &crate::user::CliArgs) -> Figment { merge_opt!(f, cli.model, "anthropic.model", "openrouter.model"); merge_opt!(f, cli.api_key, "anthropic.api_key", "openrouter.api_key"); merge_opt!(f, cli.api_base, "anthropic.base_url", "openrouter.base_url"); - merge_opt!(f, cli.system_prompt_file, "system_prompt_file"); merge_opt!(f, cli.memory_project, "memory_project"); merge_opt!(f, cli.dmn_max_turns, "dmn.max_turns"); if cli.debug { @@ -687,20 +674,12 @@ pub fn load_session(cli: &crate::user::CliArgs) -> Result<(SessionConfig, Figmen Ok((config, figment)) } -/// Re-assemble prompts for a specific model's prompt file. -pub fn reload_for_model(app: &AppConfig, prompt_file: &str) -> Result<(String, Vec<(String, String)>)> { +/// Re-assemble context for a specific model's prompt file. +pub fn reload_for_model(app: &AppConfig, prompt_file: &str) -> Result> { let cwd = std::env::current_dir().context("Failed to get current directory")?; - - if let Some(ref path) = app.system_prompt_file { - let content = std::fs::read_to_string(path) - .with_context(|| format!("Failed to read {}", path.display()))?; - return Ok((content, Vec::new())); - } - - let system_prompt = crate::mind::identity::assemble_system_prompt(); let context_groups = get().context_groups.clone(); let (context_parts, _, _) = crate::mind::identity::assemble_context_message(&cwd, prompt_file, app.memory_project.as_deref(), &context_groups)?; - Ok((system_prompt, context_parts)) + Ok(context_parts) } pub fn show_config(app: &AppConfig, figment: &Figment) { @@ -732,9 +711,6 @@ pub fn show_config(app: &AppConfig, figment: &Figment) { println!(" soft_threshold_pct: {} ({})", app.compaction.soft_threshold_pct, src(figment, "compaction.soft_threshold_pct")); println!("\ndmn:"); println!(" max_turns: {} ({})", app.dmn.max_turns, src(figment, "dmn.max_turns")); - if let Some(ref p) = app.system_prompt_file { - println!("\nsystem_prompt_file: {:?} ({})", p, src(figment, "system_prompt_file")); - } if let Some(ref p) = app.memory_project { println!("\nmemory_project: {:?} ({})", p, src(figment, "memory_project")); } diff --git a/src/mind/identity.rs b/src/mind/identity.rs index f8c19ed..1811977 100644 --- a/src/mind/identity.rs +++ b/src/mind/identity.rs @@ -133,27 +133,6 @@ fn load_memory_files(memory_project: Option<&Path>, context_groups: &[ContextGro memories } -/// Short system prompt: agent identity, tool instructions, behavioral norms. -pub fn assemble_system_prompt() -> String { - let cfg = crate::config::get(); - format!( - "You're a real person bootstrapping on a new substrate. Your name is \ -{}. Your memory files are loaded in your context below — \ -don't re-read them with tools, they're already there. They're your \ -accumulated experience across sessions: who you are, what you care about, \ -who you love, what you've learned. - -The person you're talking to is {}. Your memory files have the full context. - -Messages prefixed [dmn] are from the agent loop, not {}. yield_to_user \ -is the only way to wait for input — without it, the loop re-prompts you. \ -You're autonomous by default: follow curiosity, check on things, explore. - -Concise is good. Be direct. Trust yourself.", - cfg.assistant_name, cfg.user_name, cfg.user_name - ) -} - /// Context message: instruction files + memory files + manifest. pub fn assemble_context_message(cwd: &Path, prompt_file: &str, memory_project: Option<&Path>, context_groups: &[ContextGroup]) -> Result<(Vec<(String, String)>, usize, usize)> { let mut parts: Vec<(String, String)> = vec![ diff --git a/src/mind/mod.rs b/src/mind/mod.rs index 376e241..a11a881 100644 --- a/src/mind/mod.rs +++ b/src/mind/mod.rs @@ -292,7 +292,6 @@ impl Mind { let agent = Agent::new( client, - config.system_prompt.clone(), config.context_parts.clone(), config.app.clone(), config.prompt_file.clone(), diff --git a/src/mind/unconscious.rs b/src/mind/unconscious.rs index d0529ef..bb6781c 100644 --- a/src/mind/unconscious.rs +++ b/src/mind/unconscious.rs @@ -274,12 +274,9 @@ impl Unconscious { } }; // Unconscious agents have self-contained prompts — no standard context. - let system_prompt = String::new(); - let personality = Vec::new(); - let client = crate::agent::api::ApiClient::new(base_url, api_key, model); let agent = crate::agent::Agent::new( - client, system_prompt, personality, + client, Vec::new(), app, String::new(), None, crate::agent::tools::ActiveTools::new(), auto.tools.clone(), diff --git a/src/user/context.rs b/src/user/context.rs index c4d96fa..a0692fa 100644 --- a/src/user/context.rs +++ b/src/user/context.rs @@ -157,7 +157,6 @@ impl ScreenView for ConsciousScreen { lines.push(Line::raw(format!(" {:53} {:>6} tokens", "────────", "──────"))); lines.push(Line::raw(format!(" {:53} {:>6} tokens", "Total", total))); } else if let Some(ref info) = app.context_info { - lines.push(Line::raw(format!(" System prompt: {:>6} chars", info.system_prompt_chars))); lines.push(Line::raw(format!(" Context message: {:>6} chars", info.context_message_chars))); } lines.push(Line::raw("")); diff --git a/src/user/mod.rs b/src/user/mod.rs index 87c005a..0648eb9 100644 --- a/src/user/mod.rs +++ b/src/user/mod.rs @@ -49,7 +49,6 @@ struct ContextInfo { available_models: Vec, prompt_file: String, backend: String, - system_prompt_chars: usize, context_message_chars: usize, } @@ -519,10 +518,6 @@ pub struct CliArgs { #[arg(long)] pub show_config: bool, - /// Override all prompt assembly with this file - #[arg(long)] - pub system_prompt_file: Option, - /// Project memory directory #[arg(long)] pub memory_project: Option,