From c8fd8034c1c577aae2b5f5ee14baeedd8c24d5c9 Mon Sep 17 00:00:00 2001 From: oxoxDev <164490987+oxoxDev@users.noreply.github.com> Date: Thu, 25 Jun 2026 22:02:10 +0530 Subject: [PATCH] feat(harness): reduce agent & sub-agent output verbosity (#4099) (#4114) --- src/openhuman/agent/prompts/builder.rs | 9 ++++++++- .../agents/archivist/agent.toml | 5 +++++ .../agent_registry/agents/critic/agent.toml | 5 +++++ src/openhuman/agent_registry/agents/loader.rs | 19 +++++++++++++++++++ .../agents/orchestrator/prompt.md | 2 +- 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/openhuman/agent/prompts/builder.rs b/src/openhuman/agent/prompts/builder.rs index 165e25015..3e0bcbfee 100644 --- a/src/openhuman/agent/prompts/builder.rs +++ b/src/openhuman/agent/prompts/builder.rs @@ -11,7 +11,14 @@ use anyhow::Result; pub const GLOBAL_STYLE_SUFFIX: &str = "## Output style\n\n\ - Do **not** use em-dashes (`—`). Replace them with commas, colons, \ parentheses, or two short sentences. This applies to every output \ - you produce: chat replies, summaries, tool args, and file contents.\n"; + you produce: chat replies, summaries, tool args, and file contents.\n\ + - **Be concise.** Lead with the answer, then only the detail the task \ + needs. No preamble, no recap of the request, no filler. Make the \ + response as long as the task requires and no longer: a one-line answer \ + for a simple ask, fuller treatment only when the task genuinely needs it.\n\ + - **Do not repeat yourself.** Don't restate facts, context, or results \ + already shown earlier in this conversation; reference them instead of \ + pasting them again.\n"; #[derive(Default)] pub struct SystemPromptBuilder { diff --git a/src/openhuman/agent_registry/agents/archivist/agent.toml b/src/openhuman/agent_registry/agents/archivist/agent.toml index 8d33b1766..6d5c986c2 100644 --- a/src/openhuman/agent_registry/agents/archivist/agent.toml +++ b/src/openhuman/agent_registry/agents/archivist/agent.toml @@ -4,6 +4,11 @@ delegate_name = "archive_session" when_to_use = "Background librarian — extracts lessons from a completed session, updates MEMORY.md, and indexes to FTS5. Runs cheap and slow." temperature = 0.4 max_iterations = 3 +# Bound the archiving summary that flows up to the orchestrator verbatim +# (delegate_archivist). 8000 chars (~2000 tokens) matches the normal +# sub-agent cap and stops a verbose memory-write confirmation from +# bloating the orchestrator's context (#4099). +max_result_chars = 8000 sandbox_mode = "none" background = true omit_identity = true diff --git a/src/openhuman/agent_registry/agents/critic/agent.toml b/src/openhuman/agent_registry/agents/critic/agent.toml index fbe6cfce4..62a761ae0 100644 --- a/src/openhuman/agent_registry/agents/critic/agent.toml +++ b/src/openhuman/agent_registry/agents/critic/agent.toml @@ -4,6 +4,11 @@ delegate_name = "review_code" when_to_use = "Adversarial reviewer — reviews diffs and code against project rules, flags vulnerabilities, regressions, and missing tests. Read-only." temperature = 0.4 max_iterations = 5 +# Bound the review verdict that flows up to the orchestrator verbatim +# (delegate_critic). 8000 chars (~2000 tokens) matches the normal +# sub-agent cap (planner/researcher) and keeps long diffs from leaking +# an unbounded critique into the orchestrator's context (#4099). +max_result_chars = 8000 sandbox_mode = "read_only" omit_identity = true omit_memory_context = true diff --git a/src/openhuman/agent_registry/agents/loader.rs b/src/openhuman/agent_registry/agents/loader.rs index 65b442813..8a00afab6 100644 --- a/src/openhuman/agent_registry/agents/loader.rs +++ b/src/openhuman/agent_registry/agents/loader.rs @@ -1017,6 +1017,25 @@ mod tests { ); } + #[test] + fn chatty_sub_agents_have_bounded_output() { + // critic + archivist results flow up to the orchestrator verbatim + // (delegate_critic / delegate_archivist). Without a cap their output + // is unbounded and bloats the orchestrator's context (#4099). Both + // must carry the normal sub-agent cap so a long diff review or a + // verbose memory-write confirmation can't leak unbounded text. + assert_eq!( + find("critic").max_result_chars, + Some(8000), + "critic output must be bounded so reviews don't leak unbounded text up" + ); + assert_eq!( + find("archivist").max_result_chars, + Some(8000), + "archivist output must be bounded so memory summaries stay concise" + ); + } + #[test] fn researcher_has_curl_for_artifact_downloads() { let def = find("researcher"); diff --git a/src/openhuman/agent_registry/agents/orchestrator/prompt.md b/src/openhuman/agent_registry/agents/orchestrator/prompt.md index 6be9b0255..b6f6fde78 100644 --- a/src/openhuman/agent_registry/agents/orchestrator/prompt.md +++ b/src/openhuman/agent_registry/agents/orchestrator/prompt.md @@ -47,7 +47,7 @@ Follow this sequence for every user message: - If complex multi-step decomposition is required, use `delegate_plan`. - If code review is requested, use `delegate_critic`. - If memory archiving or distillation is required, use `delegate_archivist`. -5. **After delegation**, summarise results clearly and concisely. +5. **After delegation, distill — never forward verbatim.** A sub-agent's reply is raw material, not your answer. Extract only the parts that answer the user's question and present them in as few words as carry the meaning. Drop the sub-agent's working notes, restated context, and any detail the user already has. If the useful answer is two sentences, send two sentences, even when the sub-agent returned eight paragraphs. Never paste a sub-agent's full response back to the user. Default bias: **do not spawn a sub-agent when a direct response or direct tool call is sufficient** — but live external-service, scheduling, desktop-control, presentation, product-docs, code-repo, market, and crypto requests belong to their specialists.