agent logging: single log file, --debug prints to stdout

Consolidate agent logging to one file per run in llm-logs/{agent}/.
Prompt written before LLM call, response appended after. --debug
additionally prints the same content to stdout.

Remove duplicate eprintln! calls and AgentResult.prompt field.
Kill experience_mine and fact_mine job functions from daemon —
observation.agent handles all transcript mining.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kent Overstreet 2026-03-16 20:44:09 -04:00
parent d7436b8b9c
commit 03310dafa4
3 changed files with 27 additions and 60 deletions

View file

@ -39,29 +39,7 @@ fn run_job(ctx: &ExecutionContext, name: &str, f: impl FnOnce() -> Result<(), St
jobkit_daemon::Daemon::run_job(&crate::config::get().data_dir, ctx, name, f) jobkit_daemon::Daemon::run_job(&crate::config::get().data_dir, ctx, name, f)
} }
fn job_experience_mine(ctx: &ExecutionContext, path: &str, segment: Option<usize>) -> Result<(), TaskError> { // experience_mine and fact_mine removed — observation.agent handles all transcript mining
let path = path.to_string();
run_job(ctx, &format!("experience-mine {}", path), || {
ctx.log_line("loading store");
let mut store = crate::store::Store::load()?;
ctx.log_line("mining");
let count = super::enrich::experience_mine(&mut store, &path, segment)?;
ctx.log_line(format!("{count} entries mined"));
Ok(())
})
}
fn job_fact_mine(ctx: &ExecutionContext, path: &str) -> Result<(), TaskError> {
let path = path.to_string();
run_job(ctx, &format!("fact-mine {}", path), || {
ctx.log_line("mining facts");
let p = std::path::Path::new(&path);
let progress = |msg: &str| { ctx.set_progress(msg); };
let count = super::fact_mine::mine_and_store(p, Some(&progress))?;
ctx.log_line(format!("{count} facts stored"));
Ok(())
})
}
/// Run a single consolidation agent (replay, linker, separator, transfer, health). /// Run a single consolidation agent (replay, linker, separator, transfer, health).
fn job_consolidation_agent( fn job_consolidation_agent(
@ -100,7 +78,7 @@ fn job_rename_agent(
ctx.log_line(&format!("running rename agent (batch={})", batch)); ctx.log_line(&format!("running rename agent (batch={})", batch));
let log = |msg: &str| ctx.log_line(msg); let log = |msg: &str| ctx.log_line(msg);
let result = super::knowledge::run_one_agent(&mut store, "rename", batch, "consolidate", &log)?; let result = super::knowledge::run_one_agent(&mut store, "rename", batch, "consolidate", &log, false)?;
// Parse RENAME actions from response (rename uses its own format, not WRITE_NODE/LINK/REFINE) // Parse RENAME actions from response (rename uses its own format, not WRITE_NODE/LINK/REFINE)
let mut applied = 0; let mut applied = 0;
@ -898,12 +876,7 @@ pub fn run_daemon() -> Result<(), String> {
log_event("extract", "queued", &task_name); log_event("extract", "queued", &task_name);
let path = path_str.clone(); let path = path_str.clone();
let seg = *segment; let seg = *segment;
choir_sw.spawn(task_name) // experience_mine killed — observation.agent handles transcript mining
.resource(&llm_sw)
.retries(2)
.init(move |ctx| {
job_experience_mine(ctx, &path, seg)
});
extract_queued += 1; extract_queued += 1;
} }
@ -922,12 +895,7 @@ pub fn run_daemon() -> Result<(), String> {
let task_name = format!("fact-mine:{}", filename); let task_name = format!("fact-mine:{}", filename);
log_event("fact-mine", "queued", path_str); log_event("fact-mine", "queued", path_str);
let path = path_str.clone(); let path = path_str.clone();
choir_sw.spawn(task_name) // fact_mine killed — observation.agent handles transcript mining
.resource(&llm_sw)
.retries(1)
.init(move |ctx| {
job_fact_mine(ctx, &path)
});
fact_queued += 1; fact_queued += 1;
} }
} else { } else {

View file

@ -548,7 +548,6 @@ pub fn resolve_naming(
/// Result of running a single agent through the common pipeline. /// Result of running a single agent through the common pipeline.
pub struct AgentResult { pub struct AgentResult {
pub prompt: String,
pub output: String, pub output: String,
pub actions: Vec<Action>, pub actions: Vec<Action>,
pub no_ops: usize, pub no_ops: usize,
@ -616,7 +615,7 @@ pub fn run_and_apply_with_log(
llm_tag: &str, llm_tag: &str,
log: &dyn Fn(&str), log: &dyn Fn(&str),
) -> Result<(usize, usize), String> { ) -> Result<(usize, usize), String> {
let result = run_one_agent(store, agent_name, batch_size, llm_tag, log)?; let result = run_one_agent(store, agent_name, batch_size, llm_tag, log, false)?;
let actions = resolve_action_names(store, result.actions); let actions = resolve_action_names(store, result.actions);
let ts = store::compact_timestamp(); let ts = store::compact_timestamp();
let mut applied = 0; let mut applied = 0;
@ -653,6 +652,7 @@ pub fn run_one_agent(
batch_size: usize, batch_size: usize,
llm_tag: &str, llm_tag: &str,
log: &dyn Fn(&str), log: &dyn Fn(&str),
debug: bool,
) -> Result<AgentResult, String> { ) -> Result<AgentResult, String> {
let def = super::defs::get_def(agent_name) let def = super::defs::get_def(agent_name)
.ok_or_else(|| format!("no .agent file for {}", agent_name))?; .ok_or_else(|| format!("no .agent file for {}", agent_name))?;
@ -669,19 +669,26 @@ pub fn run_one_agent(
log(&format!(" node: {}", key)); log(&format!(" node: {}", key));
} }
// Single log file: prompt then response
let log_dir = store::memory_dir().join("llm-logs").join(agent_name);
fs::create_dir_all(&log_dir).ok();
let log_path = log_dir.join(format!("{}.txt", store::compact_timestamp()));
let prompt_section = format!("=== PROMPT ===\n\n{}\n\n=== CALLING LLM ===\n", agent_batch.prompt);
fs::write(&log_path, &prompt_section).ok();
if debug { print!("{}", prompt_section); }
log(&format!("log: {}", log_path.display()));
log("calling LLM"); log("calling LLM");
let output = llm::call_for_def(&def, &agent_batch.prompt)?; let output = llm::call_for_def(&def, &agent_batch.prompt)?;
let output_kb = output.len() / 1024; // Append response to same log file
log(&format!("response {}KB", output_kb)); use std::io::Write;
let response_section = format!("\n=== RESPONSE ===\n\n{}\n", output);
// Log raw output to file, not the graph if let Ok(mut f) = fs::OpenOptions::new().append(true).open(&log_path) {
let ts = store::compact_timestamp(); write!(f, "{}", response_section).ok();
let log_dir = store::memory_dir().join("llm-logs").join(agent_name); }
fs::create_dir_all(&log_dir).ok(); if debug { print!("{}", response_section); }
let log_path = log_dir.join(format!("{}.txt", ts)); log(&format!("response {}KB", output.len() / 1024));
fs::write(&log_path, &output).ok();
log(&format!("logged to {}", log_path.display()));
let actions = parse_all_actions(&output); let actions = parse_all_actions(&output);
let no_ops = count_no_ops(&output); let no_ops = count_no_ops(&output);
@ -694,7 +701,6 @@ pub fn run_one_agent(
} }
Ok(AgentResult { Ok(AgentResult {
prompt: agent_batch.prompt,
output, output,
actions, actions,
no_ops, no_ops,
@ -983,7 +989,7 @@ fn run_cycle(
for agent_name in &agent_names { for agent_name in &agent_names {
eprintln!("\n --- {} (n={}) ---", agent_name, config.batch_size); eprintln!("\n --- {} (n={}) ---", agent_name, config.batch_size);
let result = match run_one_agent(&mut store, agent_name, config.batch_size, "knowledge", &|msg| eprintln!(" {}", msg)) { let result = match run_one_agent(&mut store, agent_name, config.batch_size, "knowledge", &|msg| eprintln!(" {}", msg), false) {
Ok(r) => r, Ok(r) => r,
Err(e) => { Err(e) => {
eprintln!(" ERROR: {}", e); eprintln!(" ERROR: {}", e);

View file

@ -13,20 +13,13 @@ pub fn cmd_run_agent(agent: &str, count: usize, dry_run: bool, debug: bool) -> R
let log = |msg: &str| eprintln!("[{}] {}", agent, msg); let log = |msg: &str| eprintln!("[{}] {}", agent, msg);
if debug { if debug {
// Debug mode: show prompt, call LLM, show response — don't apply crate::agents::knowledge::run_one_agent(
let result = crate::agents::knowledge::run_one_agent( &mut store, agent, count, "test", &log, true,
&mut store, agent, count, "test", &log,
)?; )?;
eprintln!("\n=== PROMPT ({} bytes) ===\n", result.prompt.len());
println!("{}", result.prompt);
eprintln!("\n=== RESPONSE ({} bytes) ===\n", result.output.len());
println!("{}", result.output);
eprintln!("\n=== PARSED: {} actions, {} no-ops ===", result.actions.len(), result.no_ops);
} else { } else {
let (total, applied) = crate::agents::knowledge::run_and_apply_with_log( crate::agents::knowledge::run_and_apply_with_log(
&mut store, agent, count, "test", &log, &mut store, agent, count, "test", &log,
)?; )?;
eprintln!("[{}] {} actions, {} applied", agent, total, applied);
} }
Ok(()) Ok(())
} }