Uh oh!
There was an error while loading. Please reload this page.
[superseded] Agent-as-tool delegation — see activeagent#360 - #7
Conversation
A tool is a Ruby method the model can call; a delegation is another agent
the model can call. The callee keeps its own instructions, templates, model
and budget, so a specialist agent stays specialist and the generalist
orchestrating it never inherits its prompt.
Three declarations, each where the knowledge lives:
- The contract lives on the sub-agent. `delegation :action, description:`
declares the description the calling model reads, a JSON Schema for the
inputs (block DSL, a plain hash, or any class responding to
`to_json_schema`), and optionally a `returns` schema. A declared `returns`
becomes the sub-agent's response_format; its answer is parsed and checked
against the required keys before the caller sees it, so callers receive
data rather than text to re-parse.
- The budget lives at the call site, because only the caller knows what the
work is worth. `max_calls`, `max_tokens`, `max_cost`, `max_duration` and a
per-call `timeout`, set per delegation and/or agent-wide with
`delegation_budget`. Exhausting one returns a structured result the model
can reason about instead of raising mid-conversation (`on_exceeded: :raise`
is available). Ledgers live on the agent instance, so a budget is scoped to
one generation with nothing to reset.
- The backend lives at the call site too. `backend: :ollama` or
`backend: { provider: :anthropic, model: "claude-haiku-4-5" }` moves a
delegation to different silicon without touching the sub-agent. Provider
swaps go through a cached subclass configured by `generate_with` rather
than merging a hash over stale provider config, and the subclass reports
its parent's name so template lookup still resolves to the original views.
Cost budgets price themselves through the existing ModelPricing, which
already resolves rates from RubyLLM's registry with a static fallback, so
`max_cost` works with no configuration and mock models correctly cost $0.
Delegated tools are passed explicitly via `delegated_tools`, matching how
HasTools passes `tools` — no monkeypatching, and it survives ActiveAgent
upgrades. `auto_delegate!` opts into merging them onto every action; it is
opt-in because it prepends ActiveAgent's private prepare_prompt_parameters,
wrapping the method from the outside but still coupled to an internal.
Delegated generations inherit the parent's trace id via a before_generation
callback, so a delegation tree reads as one trace.
Tests run against a real ActiveAgent::Base and drive the genuine provider
tool loop. They need their own harness and rake task: the unit harness mocks
Rails, which makes `require "active_agent"` raise in the same process, so
`rake` now runs the two suites separately. A test-only Fake provider gives
the backend-swap tests a second service without pulling in a vendor SDK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RbmULyN7NyPwv62s7eWRmD⏳ Superconductor is working — View implementation I'll get back to you soon! |
TonsOfFun
commented
Aug 14, 2026
Closing — superseded by activeagent#360. Confirmed by diff that this is not a variant of that implementation, it is the same one:
Delegation is execution, and Two things from this branch are worth keeping and are not carried by #360:
A docs page covering how activeagent's delegation meets this gem's persistence (delegated runs as #6 is unaffected — it was rebased off Generated by Claude Code |
I recommend closing this in favour of activeagent#360. Opening it as a draft so the decision is explicit rather than silent — the work is real and passing, it is just in the wrong repo.
Why I think it belongs in activeagent
docs/framework/v2-extraction-roadmap.mdin activeagent draws the line:Delegation is execution, not persistence. And the roadmap lists it explicitly as an activeagent v2 item:
I built the core version first, was asked to move it here, and only found the roadmap afterwards while researching the records extraction. Flagging it rather than quietly letting whichever PR merges first decide.
What this branch contains, if you want it anyway
The same feature adapted to this gem's conventions, and the adaptation surfaced two things worth keeping regardless of where delegation lands:
SolidAgent::ModelPricingis better than theDelegation::PricingI wrote for core. It resolves rates from RubyLLM's registry with a static fallback and a blended default, somax_costworks with no configuration — and mock models correctly price at $0, so a cost budget never trips by accident in tests. If delegation stays in activeagent, that gem needs an equivalent or the two should share one.HasToolssets the convention that tools are passed explicitly. So this version usesprompt(tools: delegated_tools)rather than auto-merging, which drops the core patch entirely — no monkeypatching, survives ActiveAgent upgrades.auto_delegate!is opt-in and documented as coupled to a private method.Also here: an integration harness (
test/integration/,rake test:integration) running against a realActiveAgent::Baseand driving the genuine provider tool loop, plus a test-onlyFakeprovider giving backend-swap tests a second service without pulling in a vendor SDK. 55 integration tests, 0 failures; 220 unit unchanged. That harness is useful independent of delegation — if this PR closes, it is worth salvaging.If you close this
#6 (the records extraction) was deliberately rebased off
mainrather than stacked on this branch, so closing this one costs nothing there. The one casualty istest/integration/, which lives only here — say the word and I will lift it onto a standalone branch.Generated by Claude Code