Resolve subconscious prompt placeholders in Mind
Lightweight resolver handles {{seen_current}}, {{seen_previous}}, and
{{input:KEY}} using the session_id and output_dir directly instead of
env vars. Runs in trigger_subconscious before creating AutoAgent.
Removes {{memory_ratio}} from surface-observe prompt — redundant with
existing budget mechanisms.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
2678d64b77
commit
e2e0371726
2 changed files with 85 additions and 7 deletions
|
|
@ -72,6 +72,76 @@ impl SubconsciousAgent {
|
||||||
conversation_bytes.saturating_sub(self.last_trigger_bytes) >= interval
|
conversation_bytes.saturating_sub(self.last_trigger_bytes) >= interval
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve {{placeholder}} templates in subconscious agent prompts.
|
||||||
|
/// Handles: seen_current, seen_previous, input:KEY.
|
||||||
|
fn resolve_prompt(template: &str, session_id: &str, output_dir: &std::path::Path) -> String {
|
||||||
|
let mut result = String::with_capacity(template.len());
|
||||||
|
let mut rest = template;
|
||||||
|
while let Some(start) = rest.find("{{") {
|
||||||
|
result.push_str(&rest[..start]);
|
||||||
|
let after = &rest[start + 2..];
|
||||||
|
if let Some(end) = after.find("}}") {
|
||||||
|
let name = after[..end].trim();
|
||||||
|
let replacement = match name {
|
||||||
|
"seen_current" => resolve_seen_list(session_id, ""),
|
||||||
|
"seen_previous" => resolve_seen_list(session_id, "-prev"),
|
||||||
|
_ if name.starts_with("input:") => {
|
||||||
|
let key = &name[6..];
|
||||||
|
std::fs::read_to_string(output_dir.join(key))
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Unknown placeholder — leave as-is
|
||||||
|
result.push_str("{{");
|
||||||
|
result.push_str(&after[..end + 2]);
|
||||||
|
rest = &after[end + 2..];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
result.push_str(&replacement);
|
||||||
|
rest = &after[end + 2..];
|
||||||
|
} else {
|
||||||
|
// Unclosed {{ — pass through
|
||||||
|
result.push_str("{{");
|
||||||
|
rest = after;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push_str(rest);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_seen_list(session_id: &str, suffix: &str) -> String {
|
||||||
|
if session_id.is_empty() { return "(no session)".to_string(); }
|
||||||
|
|
||||||
|
let path = crate::store::memory_dir()
|
||||||
|
.join("sessions")
|
||||||
|
.join(format!("seen{}-{}", suffix, session_id));
|
||||||
|
|
||||||
|
let entries: Vec<(String, String)> = std::fs::read_to_string(&path).ok()
|
||||||
|
.map(|content| {
|
||||||
|
content.lines()
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.filter_map(|line| {
|
||||||
|
let (ts, key) = line.split_once('\t')?;
|
||||||
|
Some((ts.to_string(), key.to_string()))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if entries.is_empty() { return "(none)".to_string(); }
|
||||||
|
|
||||||
|
let mut sorted = entries;
|
||||||
|
sorted.sort_by(|a, b| b.0.cmp(&a.0));
|
||||||
|
let mut seen = std::collections::HashSet::new();
|
||||||
|
sorted.into_iter()
|
||||||
|
.filter(|(_, key)| seen.insert(key.clone()))
|
||||||
|
.take(20)
|
||||||
|
.map(|(ts, key)| format!("- {} ({})", key, ts))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
}
|
||||||
/// Which pane streaming text should go to.
|
/// Which pane streaming text should go to.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum StreamTarget {
|
pub enum StreamTarget {
|
||||||
|
|
@ -470,6 +540,12 @@ impl Mind {
|
||||||
.sum::<u64>()
|
.sum::<u64>()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Get session_id for placeholder resolution
|
||||||
|
let session_id = {
|
||||||
|
let ag = self.agent.lock().await;
|
||||||
|
ag.session_id.clone()
|
||||||
|
};
|
||||||
|
|
||||||
// Collect which agents to trigger (can't hold lock across await)
|
// Collect which agents to trigger (can't hold lock across await)
|
||||||
let to_trigger: Vec<(usize, Vec<AutoStep>, Vec<crate::agent::tools::Tool>, String, i32)> = {
|
let to_trigger: Vec<(usize, Vec<AutoStep>, Vec<crate::agent::tools::Tool>, String, i32)> = {
|
||||||
let mut subs = self.subconscious.lock().await;
|
let mut subs = self.subconscious.lock().await;
|
||||||
|
|
@ -481,9 +557,14 @@ impl Mind {
|
||||||
let sub = &mut subs[i];
|
let sub = &mut subs[i];
|
||||||
sub.last_trigger_bytes = conversation_bytes;
|
sub.last_trigger_bytes = conversation_bytes;
|
||||||
|
|
||||||
|
// The output dir for this agent — used for input: placeholders
|
||||||
|
// and the output() tool at runtime
|
||||||
|
let output_dir = crate::store::memory_dir()
|
||||||
|
.join("agent-output").join(&sub.name);
|
||||||
|
|
||||||
let steps: Vec<AutoStep> = sub.def.steps.iter().map(|s| {
|
let steps: Vec<AutoStep> = sub.def.steps.iter().map(|s| {
|
||||||
// TODO: resolve remaining placeholders (seen_current, input:walked, etc.)
|
let prompt = resolve_prompt(&s.prompt, &session_id, &output_dir);
|
||||||
AutoStep { prompt: s.prompt.clone(), phase: s.phase.clone() }
|
AutoStep { prompt, phase: s.phase.clone() }
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
let all_tools = crate::agent::tools::memory_and_journal_tools();
|
let all_tools = crate::agent::tools::memory_and_journal_tools();
|
||||||
|
|
|
||||||
|
|
@ -57,11 +57,8 @@ and analysis on the search — how useful was it, do memories need reorganizing?
|
||||||
Decide which memories, if any, should be surfaced to your conscious self:
|
Decide which memories, if any, should be surfaced to your conscious self:
|
||||||
output("surface", "key1\nkey2\nkey3")
|
output("surface", "key1\nkey2\nkey3")
|
||||||
|
|
||||||
When deciding what to surface, consider how much of the context window is
|
You generally shouldn't surface more than 1-2 memories at a time, and make
|
||||||
currently used by memories. It is currently {{memory_ratio}}, and you should
|
sure they're not already in context.
|
||||||
try to keep it under 40%. Only exceed that if you found something significantly
|
|
||||||
better than what was previously surfaced. You generally shouldn't surface more
|
|
||||||
than 1-2 memories at a time, and make sure they're not already in context.
|
|
||||||
|
|
||||||
Links tagged (new) are nodes created during the current conversation by
|
Links tagged (new) are nodes created during the current conversation by
|
||||||
previous agent runs. Don't surface these — they're your own recent output,
|
previous agent runs. Don't surface these — they're your own recent output,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue