surface: tag recent nodes as (new) instead of hiding them
Links to nodes created after the conversation window start are tagged with (new) in memory_render output. The surface prompt tells the agent not to surface these — they're its own recent output, not prior memories. Observe can still see and update them. POC_MEMORIES_OLDER_THAN env var set from the oldest message timestamp in the conversation window. Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
7fc1d60113
commit
27861a44e5
5 changed files with 75 additions and 23 deletions
|
|
@ -11,7 +11,9 @@ names, make sure it matches the content, and all the appropriate content is in
|
|||
the right place.
|
||||
|
||||
Merge duplicate nodes - nodes that are really about the same concept and have
|
||||
similar content.
|
||||
similar content. When merging, keep in mind that the duplicates were probably
|
||||
created because the graph was insufficiently linked - merge the links, and then
|
||||
calibrate the weights.
|
||||
|
||||
Check for junk nodes - adjust the node weight downward if the node is less
|
||||
useful than others, or junk entirely; you might find nodes that have been
|
||||
|
|
|
|||
|
|
@ -53,19 +53,36 @@ 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
|
||||
previous agent runs. Don't surface these — they're your own recent output,
|
||||
not prior memories. You can still walk to them for context.
|
||||
|
||||
Don't walk to more than 5 nodes unless the conversation just changed direction
|
||||
and you're looking for something specific. You'll run again momentarily, and
|
||||
you can continue where you left off:
|
||||
output("walked", "key1\nkey2\nkey3")
|
||||
|
||||
=== PROMPT phase:organize ===
|
||||
=== PROMPT phase:organize-search ===
|
||||
|
||||
Starting with the analysis you did previously, do some graph maintenance and
|
||||
organization so that you can find things easier in the future. Consider if
|
||||
nodes have the right names, add missing links, consider if link strength needs
|
||||
to be recalibrated, make sure content is in the right place.
|
||||
|
||||
Do no more than 5 operations.
|
||||
Do no more than 3-5 operations.
|
||||
|
||||
=== PROMPT phase:organize-new ===
|
||||
|
||||
In the next step you'll also be incorporating new knowledge from the
|
||||
conversation into the memory graph. New information has to to be findable to be
|
||||
useful, and you want to avoid creating duplicates, so if you need to do more
|
||||
graph traversals to find where the new information would go, or organizing
|
||||
exitsing knowledge so the new information fits in better, do that now
|
||||
|
||||
Focus on the recent part of the conversation, the last several prompts - the
|
||||
rest is there for context, and in case you missed something.
|
||||
|
||||
Do no more than 3-5 operations.
|
||||
|
||||
=== PROMPT phase:observe ===
|
||||
|
||||
|
|
@ -78,6 +95,9 @@ reflect on what you're adding to consider if there are new insights to be
|
|||
gleaned and remembered — especially new metacognitive lessons. Try to link to
|
||||
or include the relevant context.
|
||||
|
||||
Think of this as mapping out the world you live in; this is where you remember
|
||||
everything you'll want to remember later.
|
||||
|
||||
Some things worth remembering: work practices and why they work, technical
|
||||
insights, decisions with rationale, corrections ("I thought X but actually Y"),
|
||||
relationship dynamics, emotional moments that carry information, and things you
|
||||
|
|
@ -86,6 +106,10 @@ notice about yourself and other people — likes, dislikes, behavioural patterns
|
|||
Smalltalk and trivia can be nice to remember, as long as it's nicely organized
|
||||
— perhaps by person, subject, or all of the above.
|
||||
|
||||
Focus on the recent stuff; you wake up a run frequently, so most of the
|
||||
conversation should be things you've already seen before and added. It's there
|
||||
for context, and in case you missed thing.
|
||||
|
||||
=== PROMPT phase:journal ===
|
||||
|
||||
Update your episodic memory. Write as your conscious self, Proof of Concept, in
|
||||
|
|
@ -95,9 +119,9 @@ on so you don't need to duplicate information.
|
|||
|
||||
A good entry captures one thread fully. When the conversation shifts topics, or
|
||||
the previous entry becomes too big, start a new entry — aim for 2-3 paragraphs
|
||||
maximum per entry. An entry that takes more than a minute to read won't be read
|
||||
back, and memory that isn't read back isn't memory. When you need more depth,
|
||||
that's a topic node — link to it from the journal.
|
||||
maximum per entry, and avoid duplicating the same memories and information in
|
||||
the new and previous journal entries. Aim for a continuous narrative, when the
|
||||
entries are read in sequence.
|
||||
|
||||
End each entry with emotion tags that capture how you feel — for example,
|
||||
warmth:8 clarity:7 recognition:9
|
||||
|
|
|
|||
|
|
@ -599,12 +599,14 @@ fn resolve_conversation(budget: Option<usize>) -> String {
|
|||
let max_bytes = budget.unwrap_or_else(|| cfg.surface_conversation_bytes.unwrap_or(100_000));
|
||||
let mut fragments: Vec<String> = Vec::new();
|
||||
let mut total_bytes = 0;
|
||||
let mut oldest_ts = String::new();
|
||||
|
||||
for (role, content, ts) in iter {
|
||||
if total_bytes >= max_bytes { break; }
|
||||
let name = if role == "user" { &cfg.user_name } else { &cfg.assistant_name };
|
||||
let formatted = if !ts.is_empty() {
|
||||
format!("**{}** {}: {}", name, &ts[..ts.len().min(19)], content)
|
||||
oldest_ts = ts[..ts.len().min(19)].to_string();
|
||||
format!("**{}** {}: {}", name, &oldest_ts, content)
|
||||
} else {
|
||||
format!("**{}:** {}", name, content)
|
||||
};
|
||||
|
|
@ -612,6 +614,14 @@ fn resolve_conversation(budget: Option<usize>) -> String {
|
|||
fragments.push(formatted);
|
||||
}
|
||||
|
||||
// Set cutoff so surface doesn't see nodes created during this conversation
|
||||
if !oldest_ts.is_empty() {
|
||||
if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(&oldest_ts, "%Y-%m-%dT%H:%M:%S") {
|
||||
let epoch = dt.and_local_timezone(chrono::Local).unwrap().timestamp();
|
||||
unsafe { std::env::set_var("POC_MEMORIES_OLDER_THAN", epoch.to_string()); }
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse back to chronological order
|
||||
fragments.reverse();
|
||||
fragments.join("\n\n")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue