feat: subagent dispatch (delegate skill)#137
Open
hlgreenblatt wants to merge 1 commit into
Open
Conversation
Adds the (delegate "<goal>") skill — a bounded child-LLM loop that
runs against a configurable provider/model/endpoint (per-persona
JSON config) and returns a single-string digest to the parent.
Lets a deployment pair its foundation-model parent with narrow
specialist subagents (often smaller, cheaper, or local) that
execute defined sub-tasks within tight tool budgets.
Implementation in src/subagent.py is self-contained — Python stdlib
plus lib_llm_ext.AIProvider (instantiated fresh per dispatch from
the persona's JSON config; does not mutate _provider_registry).
JSON config schema documented at memory/personas-subagent/README.md.
Per-persona binding lets one deployment route different sub-tasks
to different models (e.g. local Ollama for code, frontier API
for synthesis).
v1 tool registry: search, read-file, write-file, append-file,
shell (restricted — no apostrophes, 30s timeout, 4 KB output cap),
plus tavily-search and technical-analysis when uagents is
importable. v1 excludes remember, query, episodes, pin, metta,
send, delegate — enforced at dispatch time with a structured
error string back to the parent.
Three forms of (delegate ...) in src/skills.metta:
- 1-arg (delegate "<goal>") — matches OmegaClaw's
single-arg-per-skill
convention; uses
"researcher" persona
- 3-arg (delegate "<g>" "<tools>" "<p>") — explicit persona,
default max_turns=8
- 4-arg (delegate "<g>" "<tools>" "<p>" <n>) — full control
Measured against a working deployment dispatching to a local
Ollama-served Granite 4.1 30B specialist for the test goal
"what is 7 plus 13":
Parent (frontier API): ~15,000 input tokens / call median
Subagent (local Ollama): ~500 input tokens / call median
Per-call ratio: 30.5× (median), 17.6× (mean)
Workload reduction: ~4.7× same-model (modeled
counterfactual: parent runs all 13
iterations itself = ~195K tokens; with
dispatch = ~41K tokens)
Three new env vars, all with defaults — see
docs/reference-skills-subagent.md§Configuration:
OMEGACLAW_SUBAGENT_PERSONA_DIR
OMEGACLAW_SUBAGENT_MAX_TURNS
OMEGACLAW_SUBAGENT_MAX_DIGEST_CHARS
Zero changes to the agent loop, lib_llm_ext, or any existing skill
body. Zero new pip dependencies.
7 files, +1106 / -0 lines:
src/subagent.py (new, 663 lines)
src/skills.metta (+26 lines: catalog + 3 rules)
memory/personas-subagent/.gitkeep (new, marker)
memory/personas-subagent/README.md (new, schema)
memory/personas-subagent/prompt-researcher.txt (new, example)
docs/reference-skills-subagent.md (new, reference)
docs/tutorial-09-subagents.md (new, walkthrough)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
(delegate "<goal>")— a bounded child-LLM loop that runs against aconfigurable provider/model/endpoint (per-persona JSON config) and returns
a single-string digest to the parent. Lets a deployment pair its
foundation-model parent (good at parsing intent + routing) with narrow
specialist subagents (often smaller, cheaper, or local) that execute
defined sub-tasks within tight tool budgets.
The persona config (
memory/personas-subagent/<key>.json) names aprompt file plus a
(provider, model, base_url, api_key_env)tuple,so different personas can bind to different LLM endpoints. The
dispatcher constructs a fresh
AIProvider(fromlib_llm_ext) percall — stays inside the existing class abstraction, does not mutate
_provider_registry.Architecture, in one sentence
Right-sized model for the task, foundation model for the routing.
The architectural payoff (sovereignty, specialist quality, marginal
cost approaching zero on local subagents) matters independently of
the token math; the token math (below) validates that the
plumbing actually delivers the claimed shape.
Measured
End-to-end verification ran against a working OmegaClaw deployment
dispatching to a local Ollama-served Granite 4.1 30B specialist over
the
(delegate "what is 7 plus 13")test goal.Counterfactual workload analysis (same task without dispatch — parent
would run the 13 iterations itself): ~195K parent input tokens vs.
~41K with dispatch = ~4.7× workload-token reduction same-model.
Multiplied by the per-token price ratio when the parent is a frontier
API and the subagent is local Ollama: order-of-magnitude per-workload
cost reduction.
What's in this PR
7 files, +1,106 / -0 lines, all additive. No changes to the agent
loop,
lib_llm_ext, or any existing skill body.src/subagent.py(new)AIProviderper dispatch, mini-loop with strict(emit "<digest>")termination, response-cleanup (<think>strip, fence strip, line-leading s-expr parse), tool registry:search,read-file,write-file,append-file, restrictedshell, plustavily-search/technical-analysiswhenuagentsis available. ~660 lines.src/skills.mettagetSkills+ three positional rules (1-arg, 3-arg, 4-arg). 1-arg form matches OmegaClaw's single-arg-per-skill convention; 3- and 4-arg forms for explicit persona / max-turns control.memory/personas-subagent/.gitkeep(new)memory/personas-subagent/README.md(new)memory/personas-subagent/prompt-researcher.txt(new)(emit "...")protocol.docs/reference-skills-subagent.md(new)docs/reference-skills-remote-agents.md.docs/tutorial-09-subagents.md(new)docs/tutorial-03-writing-a-custom-skill.md.Configuration
Three optional env vars, all with safe defaults:
OMEGACLAW_SUBAGENT_PERSONA_DIR./memory/personas-subagent<key>.json+ persona prompt files.OMEGACLAW_SUBAGENT_MAX_TURNS8OMEGACLAW_SUBAGENT_MAX_DIGEST_CHARS2000v1 scope and explicit exclusions
Tools the subagent cannot call in v1:
remember,query,episodes— would couple the subagent into the parent's memory state. Subagents are stateless; v2 may add a tagged write variant.pin,metta— mutate parent atomspace state.send— would let the subagent talk to the user channel directly, defeating the digest-return invariant.delegate— no subagent → subagent recursion in v1.These are enforced at dispatch time: any v1-excluded skill in the
tools argument returns a structured error string and the subagent
doesn't run.
No new dependencies
The dispatcher uses Python stdlib + the existing
openaipackage(via
lib_llm_ext.AIProvider). JSON persona configs use stdlibjson. Nopyyaml. No new entry inrequirements.txt.Test plan
python3 -m py_compile src/subagent.py— passespython3 -c "import sys; sys.path.insert(0,'src'); import subagent"— passes (withoutlib_llm_extavailable, since the import is deferred to dispatch time)src/skills.mettaparens balanced (verified: 99 opens, 99 closes)(subagent error: persona config '<key>.json' not found...)(delegate "x" "remember" "researcher" 4)returns(subagent error: skill(s) ['remember'] are not callable by subagents in v1...)Anti-claims
This PR does not:
lib_llm_ext, or any skill body. Purely additive.Related — one-line dependency
The subagent's
delegatedispatch returns a string viapy-call,which means it surfaces a pre-existing bug in
src/loop.metta:65:the safety-net check
(== "(" (first_char $resp))fails onString-vs-atom type mismatch for valid Python-string returns from
py-call, causing the agent's actual output to be substituted forthe reminder string. The bug is documented in a downstream fork's
implementation notes and the one-line fix is well-tested:
Now open as #138 for independent review and merge ordering. The
fix is unrelated to subagent functionality and benefits any new
py-call-bridged skill. The subagent dispatch in this PR willhit the same bug if #138 doesn't land first.
Architectural framing + measurement methodology in
docs/reference-skills-subagent.md and docs/tutorial-09-subagents.md.