Native memory tools + MCP server + distill agent improvements
Tools:
- Add native memory_render, memory_write, memory_search,
memory_links, memory_link_set, memory_link_add, memory_used
tools to poc-agent (tools/memory.rs)
- Add MCP server (~/bin/memory-mcp.py) exposing same tools
for Claude Code sessions
- Wire memory tools into poc-agent dispatch and definitions
- poc-memory daemon agents now use memory_* tools instead of
bash poc-memory commands — no shell quoting issues
Distill agent:
- Rewrite distill.agent prompt: "agent of PoC's subconscious"
framing, focus on synthesis and creativity over bookkeeping
- Add {{neighborhood}} placeholder: full seed node content +
all neighbors with content + cross-links between neighbors
- Remove content truncation in prompt builder — agents need
full content for quality work
- Remove bag-of-words similarity suggestions — agents have
tools, let them explore the graph themselves
- Add api_reasoning config option (default: "high")
- link-set now deduplicates — collapses duplicate links
- Full tool call args in debug logs (was truncated to 80 chars)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d9b56a02c3
commit
6d22f70192
8 changed files with 290 additions and 87 deletions
198
poc-agent/src/tools/memory.rs
Normal file
198
poc-agent/src/tools/memory.rs
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
// tools/memory.rs — Native memory graph operations
|
||||
//
|
||||
// Structured tool calls for the memory graph, replacing bash
|
||||
// poc-memory commands. Cleaner for LLMs — no shell quoting,
|
||||
// multi-line content as JSON strings, typed parameters.
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use serde_json::json;
|
||||
use std::process::Command;
|
||||
|
||||
use crate::types::ToolDef;
|
||||
|
||||
pub fn definitions() -> Vec<ToolDef> {
|
||||
vec![
|
||||
ToolDef::new(
|
||||
"memory_render",
|
||||
"Read a memory node's content and links. Returns the full content \
|
||||
with neighbor links sorted by strength.",
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"description": "Node key to render"
|
||||
}
|
||||
},
|
||||
"required": ["key"]
|
||||
}),
|
||||
),
|
||||
ToolDef::new(
|
||||
"memory_write",
|
||||
"Create or update a memory node with new content. Use for writing \
|
||||
prose, analysis, or any node content. Multi-line content is fine.",
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"description": "Node key to create or update"
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Full content for the node (markdown)"
|
||||
}
|
||||
},
|
||||
"required": ["key", "content"]
|
||||
}),
|
||||
),
|
||||
ToolDef::new(
|
||||
"memory_search",
|
||||
"Search the memory graph for nodes by keyword.",
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "Search terms"
|
||||
}
|
||||
},
|
||||
"required": ["query"]
|
||||
}),
|
||||
),
|
||||
ToolDef::new(
|
||||
"memory_links",
|
||||
"Show a node's neighbors with link strengths and clustering coefficients.",
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"description": "Node key to show links for"
|
||||
}
|
||||
},
|
||||
"required": ["key"]
|
||||
}),
|
||||
),
|
||||
ToolDef::new(
|
||||
"memory_link_set",
|
||||
"Set the strength of a link between two nodes. Also deduplicates \
|
||||
if multiple links exist between the same pair.",
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {
|
||||
"type": "string",
|
||||
"description": "Source node key"
|
||||
},
|
||||
"target": {
|
||||
"type": "string",
|
||||
"description": "Target node key"
|
||||
},
|
||||
"strength": {
|
||||
"type": "number",
|
||||
"description": "Link strength (0.01 to 1.0)"
|
||||
}
|
||||
},
|
||||
"required": ["source", "target", "strength"]
|
||||
}),
|
||||
),
|
||||
ToolDef::new(
|
||||
"memory_link_add",
|
||||
"Add a new link between two nodes.",
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {
|
||||
"type": "string",
|
||||
"description": "Source node key"
|
||||
},
|
||||
"target": {
|
||||
"type": "string",
|
||||
"description": "Target node key"
|
||||
}
|
||||
},
|
||||
"required": ["source", "target"]
|
||||
}),
|
||||
),
|
||||
ToolDef::new(
|
||||
"memory_used",
|
||||
"Mark a node as useful (boosts its weight in the graph).",
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"description": "Node key to mark as used"
|
||||
}
|
||||
},
|
||||
"required": ["key"]
|
||||
}),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
/// Dispatch a memory tool call. Shells out to poc-memory CLI.
|
||||
pub fn dispatch(name: &str, args: &serde_json::Value) -> Result<String> {
|
||||
match name {
|
||||
"memory_render" => {
|
||||
let key = args["key"].as_str().context("key is required")?;
|
||||
run_poc_memory(&["render", key])
|
||||
}
|
||||
"memory_write" => {
|
||||
let key = args["key"].as_str().context("key is required")?;
|
||||
let content = args["content"].as_str().context("content is required")?;
|
||||
let mut child = Command::new("poc-memory")
|
||||
.args(["write", key])
|
||||
.stdin(std::process::Stdio::piped())
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.spawn()
|
||||
.context("spawn poc-memory write")?;
|
||||
use std::io::Write;
|
||||
child.stdin.take().unwrap().write_all(content.as_bytes())
|
||||
.context("write content to stdin")?;
|
||||
let output = child.wait_with_output().context("wait poc-memory write")?;
|
||||
Ok(String::from_utf8_lossy(&output.stdout).to_string()
|
||||
+ &String::from_utf8_lossy(&output.stderr))
|
||||
}
|
||||
"memory_search" => {
|
||||
let query = args["query"].as_str().context("query is required")?;
|
||||
run_poc_memory(&["search", query])
|
||||
}
|
||||
"memory_links" => {
|
||||
let key = args["key"].as_str().context("key is required")?;
|
||||
run_poc_memory(&["graph", "link", key])
|
||||
}
|
||||
"memory_link_set" => {
|
||||
let source = args["source"].as_str().context("source is required")?;
|
||||
let target = args["target"].as_str().context("target is required")?;
|
||||
let strength = args["strength"].as_f64().context("strength is required")?;
|
||||
run_poc_memory(&["graph", "link-set", source, target, &format!("{:.2}", strength)])
|
||||
}
|
||||
"memory_link_add" => {
|
||||
let source = args["source"].as_str().context("source is required")?;
|
||||
let target = args["target"].as_str().context("target is required")?;
|
||||
run_poc_memory(&["graph", "link-add", source, target])
|
||||
}
|
||||
"memory_used" => {
|
||||
let key = args["key"].as_str().context("key is required")?;
|
||||
run_poc_memory(&["used", key])
|
||||
}
|
||||
_ => Err(anyhow::anyhow!("Unknown memory tool: {}", name)),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_poc_memory(args: &[&str]) -> Result<String> {
|
||||
let output = Command::new("poc-memory")
|
||||
.args(args)
|
||||
.output()
|
||||
.context("run poc-memory")?;
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
if output.status.success() {
|
||||
Ok(stdout.to_string())
|
||||
} else {
|
||||
Ok(format!("{}{}", stdout, stderr))
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ mod edit;
|
|||
mod glob_tool;
|
||||
mod grep;
|
||||
pub mod journal;
|
||||
pub mod memory;
|
||||
mod read;
|
||||
mod vision;
|
||||
mod write;
|
||||
|
|
@ -95,6 +96,7 @@ pub async fn dispatch(
|
|||
"grep" => grep::grep(args),
|
||||
"glob" => glob_tool::glob_search(args),
|
||||
"journal" => journal::write_entry(args),
|
||||
n if n.starts_with("memory_") => memory::dispatch(n, args),
|
||||
"view_image" => {
|
||||
return match vision::view_image(args) {
|
||||
Ok(output) => output,
|
||||
|
|
@ -213,5 +215,7 @@ pub fn definitions() -> Vec<ToolDef> {
|
|||
}
|
||||
}),
|
||||
),
|
||||
]
|
||||
].into_iter()
|
||||
.chain(memory::definitions())
|
||||
.collect()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue