agents: always use API backend, remove tools field from .agent files

- Remove is_split special case in daemon — split now goes through
  job_consolidation_agent like all other agents
- call_for_def uses API whenever api_base_url is configured, regardless
  of tools field (was requiring non-empty tools to use API)
- Remove "tools" field from all .agent files — memory tools are always
  provided by the API layer, not configured per-agent
- Add prompt size guard: reject prompts over 800KB (~200K tokens) with
  clear error instead of hitting the model's context limit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kent Overstreet 2026-03-20 14:26:39 -04:00
parent 9d476841b8
commit 6069efb7fc
9 changed files with 17 additions and 31 deletions

View file

@ -1,4 +1,4 @@
{"agent":"calibrate","query":"all | not-visited:calibrate,7d | sort:degree desc | limit:1","model":"sonnet","schedule":"daily","tools":["Bash(poc-memory:*)"]} {"agent":"calibrate","query":"all | not-visited:calibrate,7d | sort:degree desc | limit:1","model":"sonnet","schedule":"daily"}
# Calibrate Agent — Link Strength Assessment # Calibrate Agent — Link Strength Assessment

View file

@ -1,4 +1,4 @@
{"agent":"distill","query":"all | type:semantic | sort:degree | limit:10","model":"sonnet","schedule":"daily","tools":["Bash(poc-memory:*)"]} {"agent":"distill","query":"all | type:semantic | sort:degree | limit:10","model":"sonnet","schedule":"daily"}
{{node:core-personality}} {{node:core-personality}}

View file

@ -1,4 +1,4 @@
{"agent":"evaluate","query":"key ~ '_consolidate' | sort:created | limit:10","model":"sonnet","schedule":"daily","tools":["Bash(poc-memory:*)"]} {"agent":"evaluate","query":"key ~ '_consolidate' | sort:created | limit:10","model":"sonnet","schedule":"daily"}
# Evaluate Agent — Agent Output Quality Assessment # Evaluate Agent — Agent Output Quality Assessment

View file

@ -1,4 +1,4 @@
{"agent":"linker","query":"all | not-visited:linker,7d | sort:isolation*0.7+recency(linker)*0.3 | limit:5","model":"sonnet","schedule":"daily","tools":["Bash(poc-memory:*)"]} {"agent":"linker","query":"all | not-visited:linker,7d | sort:isolation*0.7+recency(linker)*0.3 | limit:5","model":"sonnet","schedule":"daily"}
# Linker Agent — Relational Binding # Linker Agent — Relational Binding

View file

@ -1,4 +1,4 @@
{"agent":"observation","query":"","model":"sonnet","schedule":"daily","tools":["Bash(poc-memory:*)"]} {"agent":"observation","query":"","model":"sonnet","schedule":"daily"}
# Observation Agent — Transcript Mining # Observation Agent — Transcript Mining
{{node:core-personality}} {{node:core-personality}}

View file

@ -1,4 +1,4 @@
{"agent":"organize","query":"all | not-visited:organize,86400 | sort:degree*0.5+isolation*0.3+recency(organize)*0.2 | limit:5","model":"sonnet","schedule":"weekly","tools":["Bash(poc-memory:*)"]} {"agent":"organize","query":"all | not-visited:organize,86400 | sort:degree*0.5+isolation*0.3+recency(organize)*0.2 | limit:5","model":"sonnet","schedule":"weekly"}
{{node:core-personality}} {{node:core-personality}}

View file

@ -1189,7 +1189,6 @@ pub fn run_daemon() -> Result<(), String> {
let mut remaining = count; let mut remaining = count;
let is_rename = *agent_type == "rename"; let is_rename = *agent_type == "rename";
let is_split = *agent_type == "split";
// Targeted run: one task for a specific node // Targeted run: one task for a specific node
if let Some(ref key) = target_key { if let Some(ref key) = target_key {
@ -1212,29 +1211,6 @@ pub fn run_daemon() -> Result<(), String> {
remaining = 0; remaining = 0;
} }
if is_split {
let store = crate::store::Store::load().ok();
let candidates = store.as_ref()
.map(|s| super::prompts::split_candidates(s))
.unwrap_or_default();
let to_split: Vec<String> = candidates.into_iter()
.take(count)
.collect();
for key in &to_split {
let key = key.clone();
let task_name = format!("c-split-{}:{}", key, today);
choir_rpc.spawn(task_name)
.resource(&llm_rpc)
.retries(1)
.init(move |ctx| {
job_split_one(ctx, key.clone())
})
.run();
spawned += 1;
}
remaining = 0;
}
while remaining > 0 { while remaining > 0 {
let batch = remaining.min(batch_size); let batch = remaining.min(batch_size);
let agent = agent_type.to_string(); let agent = agent_type.to_string();

View file

@ -138,6 +138,16 @@ fn run_one_agent_inner(
else { format!("{} tools", def.tools.len()) }; else { format!("{} tools", def.tools.len()) };
log(&format!("prompt {}KB, model={}, {}, {} nodes", log(&format!("prompt {}KB, model={}, {}, {} nodes",
prompt_kb, def.model, tools_desc, agent_batch.node_keys.len())); prompt_kb, def.model, tools_desc, agent_batch.node_keys.len()));
// Guard: reject prompts that would exceed model context.
// Rough estimate: 1 token ≈ 4 bytes. Reserve 16K tokens for output.
let max_prompt_bytes = 800_000; // ~200K tokens, leaves room for output
if agent_batch.prompt.len() > max_prompt_bytes {
return Err(format!(
"prompt too large: {}KB (max {}KB) — seed nodes may be oversized",
prompt_kb, max_prompt_bytes / 1024,
));
}
for key in &agent_batch.node_keys { for key in &agent_batch.node_keys {
log(&format!(" node: {}", key)); log(&format!(" node: {}", key));
} }

View file

@ -188,7 +188,7 @@ pub(crate) fn call_haiku(agent: &str, prompt: &str) -> Result<String, String> {
/// otherwise falls back to claude CLI subprocess. /// otherwise falls back to claude CLI subprocess.
pub(crate) fn call_for_def(def: &super::defs::AgentDef, prompt: &str) -> Result<String, String> { pub(crate) fn call_for_def(def: &super::defs::AgentDef, prompt: &str) -> Result<String, String> {
let config = crate::config::get(); let config = crate::config::get();
if config.api_base_url.is_some() && !def.tools.is_empty() { if config.api_base_url.is_some() {
super::daemon::log_verbose(&def.agent, "llm-backend", super::daemon::log_verbose(&def.agent, "llm-backend",
&format!("API: {}", config.api_base_url.as_deref().unwrap_or("?"))); &format!("API: {}", config.api_base_url.as_deref().unwrap_or("?")));
let log = |msg: &str| eprintln!("[{}] {}", def.agent, msg); let log = |msg: &str| eprintln!("[{}] {}", def.agent, msg);