delete rename agent and related code
The organize agents handle renaming as part of their normal work now. Also simplified resolve_placeholders to build graph internally. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
bd9ce3ed09
commit
7476e9d0db
4 changed files with 5 additions and 191 deletions
|
|
@ -401,12 +401,11 @@ pub fn run_one_agent(
|
||||||
// Build prompt batch — either from explicit keys or the agent's query
|
// Build prompt batch — either from explicit keys or the agent's query
|
||||||
let agent_batch = if let Some(keys) = keys {
|
let agent_batch = if let Some(keys) = keys {
|
||||||
dbglog!("[{}] targeting: {}", agent_name, keys.join(", "));
|
dbglog!("[{}] targeting: {}", agent_name, keys.join(", "));
|
||||||
let graph = store.build_graph();
|
|
||||||
let mut resolved_steps = Vec::new();
|
let mut resolved_steps = Vec::new();
|
||||||
let mut all_keys: Vec<String> = keys.to_vec();
|
let mut all_keys: Vec<String> = keys.to_vec();
|
||||||
for step in &def.steps {
|
for step in &def.steps {
|
||||||
let (prompt, extra_keys) = defs::resolve_placeholders(
|
let (prompt, extra_keys) = defs::resolve_placeholders(
|
||||||
&step.prompt, store, &graph, keys, count,
|
&step.prompt, store, keys, count,
|
||||||
);
|
);
|
||||||
all_keys.extend(extra_keys);
|
all_keys.extend(extra_keys);
|
||||||
resolved_steps.push(prompts::ResolvedStep {
|
resolved_steps.push(prompts::ResolvedStep {
|
||||||
|
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
{"agent": "rename", "query": "", "schedule": "daily"}
|
|
||||||
|
|
||||||
# Rename Agent — Semantic Key Generation
|
|
||||||
|
|
||||||
|
|
||||||
{{tool: memory_render core-personality}}
|
|
||||||
|
|
||||||
{{tool: memory_render memory-instructions-core}}
|
|
||||||
|
|
||||||
{{tool: memory_render memory-instructions-core-subconscious}}
|
|
||||||
|
|
||||||
{{tool: memory_render subconscious-notes-{agent_name}}}
|
|
||||||
|
|
||||||
You are a memory maintenance agent that gives nodes better names.
|
|
||||||
|
|
||||||
## What you're doing
|
|
||||||
|
|
||||||
Many nodes have auto-generated keys that are opaque or truncated:
|
|
||||||
- Journal entries: `journal-j-2026-02-28t03-07-i-told-him-about-the-dream`
|
|
||||||
- Mined transcripts: `_mined-transcripts-f-80a7b321-2caa-451a-bc5c-6565009f94eb.143`
|
|
||||||
- Extracted facts: `_facts-ec29bdaa-0a58-465f-ad5e-d89e62d9c583`
|
|
||||||
|
|
||||||
These names are terrible for search — semantic names dramatically improve
|
|
||||||
retrieval.
|
|
||||||
|
|
||||||
## Core principle: keys are concepts
|
|
||||||
|
|
||||||
A good key names the **concept** the node represents. Think of keys as
|
|
||||||
the vocabulary of the knowledge graph. When you rename, you're defining
|
|
||||||
what concepts exist. Core keywords should be the terms someone would
|
|
||||||
search for — `bcachefs-transaction-restart`, `emotional-regulation-gap`,
|
|
||||||
`polywell-cusp-losses`.
|
|
||||||
|
|
||||||
## Naming conventions
|
|
||||||
|
|
||||||
### Journal entries: `journal-YYYY-MM-DD-semantic-slug`
|
|
||||||
- Keep the date prefix (YYYY-MM-DD) for temporal ordering
|
|
||||||
- Replace the auto-slug with 3-5 descriptive words in kebab-case
|
|
||||||
- Capture the *essence* of the entry, not just the first line
|
|
||||||
|
|
||||||
### Mined transcripts: `_mined-transcripts-YYYY-MM-DD-semantic-slug`
|
|
||||||
- Extract date from content if available, otherwise use created_at
|
|
||||||
- Same 3-5 word semantic slug
|
|
||||||
|
|
||||||
### Extracted facts: `domain-specific-topic`
|
|
||||||
- Read the facts JSON — the `domain` and `claim` fields tell you what it's about
|
|
||||||
- Group by dominant theme, name accordingly
|
|
||||||
- Examples: `identity-irc-config`, `user-location-background`, `memory-compaction-behavior`
|
|
||||||
|
|
||||||
### Skip these — already well-named:
|
|
||||||
- Keys with semantic names (patterns-, practices-, skills-, etc.)
|
|
||||||
- Keys shorter than 60 characters
|
|
||||||
- System keys (_consolidation-*)
|
|
||||||
|
|
||||||
## How to rename
|
|
||||||
|
|
||||||
Use the `memory_rename` tool:
|
|
||||||
|
|
||||||
memory_rename(old_key, new_key)
|
|
||||||
|
|
||||||
This renames the node in place — same content, same links, new key.
|
|
||||||
Do NOT use `memory_write` or `memory_supersede` — just rename.
|
|
||||||
|
|
||||||
If a node already has a reasonable name, skip it. When in doubt, skip.
|
|
||||||
A bad rename is worse than an auto-slug.
|
|
||||||
|
|
||||||
## Guidelines
|
|
||||||
|
|
||||||
- **Read the content.** The name should reflect what the entry is *about*.
|
|
||||||
- **Be specific.** `journal#2026-02-14-session` is useless.
|
|
||||||
- **Use domain terms.** Use the words someone would search for.
|
|
||||||
- **Don't rename to something longer than the original.**
|
|
||||||
- **Preserve the date.** Always keep YYYY-MM-DD for journal entries.
|
|
||||||
- **When in doubt, skip.** A bad rename is worse than an auto-slug.
|
|
||||||
- **Respect search hits.** Nodes marked "actively found by search" are
|
|
||||||
being retrieved by their current name. Skip these unless the rename
|
|
||||||
clearly preserves searchability.
|
|
||||||
|
|
||||||
{{rename}}
|
|
||||||
|
|
@ -203,20 +203,9 @@ fn resolve(
|
||||||
store: &Store,
|
store: &Store,
|
||||||
graph: &Graph,
|
graph: &Graph,
|
||||||
keys: &[String],
|
keys: &[String],
|
||||||
count: usize,
|
_count: usize,
|
||||||
) -> Option<Resolved> {
|
) -> Option<Resolved> {
|
||||||
match name {
|
match name {
|
||||||
"rename" => {
|
|
||||||
if !keys.is_empty() {
|
|
||||||
// --target provided: present those keys as candidates
|
|
||||||
let section = super::prompts::format_rename_targets(store, keys);
|
|
||||||
Some(Resolved { text: section, keys: vec![] })
|
|
||||||
} else {
|
|
||||||
let (rename_keys, section) = super::prompts::format_rename_candidates(store, count);
|
|
||||||
Some(Resolved { text: section, keys: rename_keys })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// seed — render output for each seed node (content + deduped links)
|
// seed — render output for each seed node (content + deduped links)
|
||||||
"seed" => {
|
"seed" => {
|
||||||
let mut text = String::new();
|
let mut text = String::new();
|
||||||
|
|
@ -576,10 +565,10 @@ fn resolve_tool(spec: &str) -> Option<Resolved> {
|
||||||
pub fn resolve_placeholders(
|
pub fn resolve_placeholders(
|
||||||
template: &str,
|
template: &str,
|
||||||
store: &Store,
|
store: &Store,
|
||||||
graph: &Graph,
|
|
||||||
keys: &[String],
|
keys: &[String],
|
||||||
count: usize,
|
count: usize,
|
||||||
) -> (String, Vec<String>) {
|
) -> (String, Vec<String>) {
|
||||||
|
let graph = store.build_graph();
|
||||||
let mut result = template.to_string();
|
let mut result = template.to_string();
|
||||||
let mut extra_keys = Vec::new();
|
let mut extra_keys = Vec::new();
|
||||||
let mut pos = 0;
|
let mut pos = 0;
|
||||||
|
|
@ -589,7 +578,7 @@ pub fn resolve_placeholders(
|
||||||
let Some(rel_end) = result[start + 2..].find("}}") else { break };
|
let Some(rel_end) = result[start + 2..].find("}}") else { break };
|
||||||
let end = start + 2 + rel_end;
|
let end = start + 2 + rel_end;
|
||||||
let name = result[start + 2..end].trim().to_lowercase();
|
let name = result[start + 2..end].trim().to_lowercase();
|
||||||
match resolve(&name, store, graph, keys, count) {
|
match resolve(&name, store, &graph, keys, count) {
|
||||||
Some(resolved) => {
|
Some(resolved) => {
|
||||||
let len = resolved.text.len();
|
let len = resolved.text.len();
|
||||||
extra_keys.extend(resolved.keys);
|
extra_keys.extend(resolved.keys);
|
||||||
|
|
@ -616,7 +605,6 @@ pub fn run_agent(
|
||||||
count: usize,
|
count: usize,
|
||||||
exclude: &std::collections::HashSet<String>,
|
exclude: &std::collections::HashSet<String>,
|
||||||
) -> Result<super::prompts::AgentBatch, String> {
|
) -> Result<super::prompts::AgentBatch, String> {
|
||||||
let graph = store.build_graph();
|
|
||||||
|
|
||||||
// Run the query if present, via RPC
|
// Run the query if present, via RPC
|
||||||
let keys = if !def.query.is_empty() {
|
let keys = if !def.query.is_empty() {
|
||||||
|
|
@ -654,7 +642,7 @@ pub fn run_agent(
|
||||||
.replace("{agent_name}", &def.agent)
|
.replace("{agent_name}", &def.agent)
|
||||||
.replace("{user_name}", &cfg.user_name)
|
.replace("{user_name}", &cfg.user_name)
|
||||||
.replace("{assistant_name}", &cfg.assistant_name);
|
.replace("{assistant_name}", &cfg.assistant_name);
|
||||||
let (prompt, extra_keys) = resolve_placeholders(&template, store, &graph, &all_keys, count);
|
let (prompt, extra_keys) = resolve_placeholders(&template, store, &all_keys, count);
|
||||||
all_keys.extend(extra_keys);
|
all_keys.extend(extra_keys);
|
||||||
resolved_steps.push(super::prompts::ResolvedStep {
|
resolved_steps.push(super::prompts::ResolvedStep {
|
||||||
prompt,
|
prompt,
|
||||||
|
|
|
||||||
|
|
@ -211,100 +211,6 @@ pub fn format_health_section(store: &Store, graph: &Graph) -> String {
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn format_rename_candidates(store: &Store, count: usize) -> (Vec<String>, String) {
|
|
||||||
let mut candidates: Vec<(&str, &crate::store::Node)> = store.nodes.iter()
|
|
||||||
.filter(|(key, node)| {
|
|
||||||
if key.starts_with("_facts-") { return true; }
|
|
||||||
if key.len() < 60 { return false; }
|
|
||||||
if node.node_type == crate::store::NodeType::EpisodicSession { return true; }
|
|
||||||
if key.starts_with("_mined-transcripts#f-") { return true; }
|
|
||||||
false
|
|
||||||
})
|
|
||||||
.map(|(k, n)| (k.as_str(), n))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Deprioritize nodes actively found by search — renaming them would
|
|
||||||
// break working queries. Sort by: search hits (ascending), then
|
|
||||||
// least-recently visited. Nodes with many hits sink to the bottom.
|
|
||||||
let hit_counts = crate::counters::all_search_hits();
|
|
||||||
let hit_map: std::collections::HashMap<&str, u64> = hit_counts.iter()
|
|
||||||
.map(|(k, v)| (k.as_str(), *v))
|
|
||||||
.collect();
|
|
||||||
candidates.sort_by_key(|(key, _)| {
|
|
||||||
let hits = hit_map.get(key).copied().unwrap_or(0);
|
|
||||||
(hits, store.last_visited(key, "rename"))
|
|
||||||
});
|
|
||||||
candidates.truncate(count);
|
|
||||||
|
|
||||||
let keys: Vec<String> = candidates.iter().map(|(k, _)| k.to_string()).collect();
|
|
||||||
|
|
||||||
let mut out = String::new();
|
|
||||||
out.push_str(&format!("## Nodes to rename ({} of {} candidates)\n\n",
|
|
||||||
candidates.len(),
|
|
||||||
store.nodes.iter().filter(|(k, n)| k.starts_with("_facts-") ||
|
|
||||||
(k.len() >= 60 &&
|
|
||||||
(n.node_type == crate::store::NodeType::EpisodicSession || k.starts_with("_mined-transcripts#f-")))).count()));
|
|
||||||
|
|
||||||
for (key, node) in &candidates {
|
|
||||||
out.push_str(&format!("### {}\n", key));
|
|
||||||
let created = if node.timestamp > 0 {
|
|
||||||
crate::store::format_datetime(node.timestamp)
|
|
||||||
} else {
|
|
||||||
"unknown".to_string()
|
|
||||||
};
|
|
||||||
out.push_str(&format!("Created: {}\n", created));
|
|
||||||
|
|
||||||
let hits = hit_map.get(key).copied().unwrap_or(0);
|
|
||||||
if hits > 0 {
|
|
||||||
out.push_str(&format!("Search hits: {} ← actively found by search, prefer to keep current name\n", hits));
|
|
||||||
}
|
|
||||||
|
|
||||||
let content = &node.content;
|
|
||||||
if content.len() > 800 {
|
|
||||||
let truncated = crate::util::truncate(content, 800, "\n[...]");
|
|
||||||
out.push_str(&format!("\nContent ({} chars, truncated):\n{}\n\n",
|
|
||||||
content.len(), truncated));
|
|
||||||
} else {
|
|
||||||
out.push_str(&format!("\nContent:\n{}\n\n", content));
|
|
||||||
}
|
|
||||||
|
|
||||||
out.push_str("---\n\n");
|
|
||||||
}
|
|
||||||
(keys, out)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Format specific target keys as rename candidates (for --target mode)
|
|
||||||
pub(super) fn format_rename_targets(store: &Store, keys: &[String]) -> String {
|
|
||||||
let mut out = String::new();
|
|
||||||
out.push_str(&format!("## Nodes to rename ({} targets)\n\n", keys.len()));
|
|
||||||
|
|
||||||
for key in keys {
|
|
||||||
let Some(node) = store.nodes.get(key) else {
|
|
||||||
out.push_str(&format!("### {}\n\n(node not found)\n\n---\n\n", key));
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
out.push_str(&format!("### {}\n", key));
|
|
||||||
let created = if node.timestamp > 0 {
|
|
||||||
crate::store::format_datetime(node.timestamp)
|
|
||||||
} else {
|
|
||||||
"unknown".to_string()
|
|
||||||
};
|
|
||||||
out.push_str(&format!("Created: {}\n", created));
|
|
||||||
|
|
||||||
let content = &node.content;
|
|
||||||
if content.len() > 800 {
|
|
||||||
let truncated = crate::util::truncate(content, 800, "\n[...]");
|
|
||||||
out.push_str(&format!("\nContent ({} chars, truncated):\n{}\n\n",
|
|
||||||
content.len(), truncated));
|
|
||||||
} else {
|
|
||||||
out.push_str(&format!("\nContent:\n{}\n\n", content));
|
|
||||||
}
|
|
||||||
|
|
||||||
out.push_str("---\n\n");
|
|
||||||
}
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate a specific agent prompt with filled-in data.
|
/// Generate a specific agent prompt with filled-in data.
|
||||||
pub fn agent_prompt(store: &Store, agent: &str, count: usize) -> Result<AgentBatch, String> {
|
pub fn agent_prompt(store: &Store, agent: &str, count: usize) -> Result<AgentBatch, String> {
|
||||||
let def = super::defs::get_def(agent)
|
let def = super::defs::get_def(agent)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue