mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
feat(flows): teach the workflow builder to pick specialist agents via agent_ref (B37) (#5120)
This commit is contained in:
@@ -639,6 +639,120 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// B37 (Gap 1): the standing prompt must actually teach the builder to
|
||||
/// reach for a specialist `agent_ref` — ground the id via
|
||||
/// `list_agent_profiles`, understand that `agent_ref` runs a real agent
|
||||
/// turn with its own tool loop (not just a persona-flavored completion),
|
||||
/// and see concrete examples of when a plain agent node isn't enough.
|
||||
#[test]
|
||||
fn standing_prompt_teaches_specialist_agent_ref_selection() {
|
||||
const STANDING_PROMPT: &str = include_str!("prompt.md");
|
||||
|
||||
for rule in [
|
||||
"list_agent_profiles",
|
||||
"Picking a specialist via `agent_ref`",
|
||||
"code_executor",
|
||||
"researcher",
|
||||
] {
|
||||
assert!(
|
||||
STANDING_PROMPT.contains(rule),
|
||||
"standing prompt must teach specialist selection via `{rule}` — the \
|
||||
builder needs to know it can ground a real agent_ref with \
|
||||
list_agent_profiles instead of hallucinating one"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The runtime already gives an `agent_ref` step the selected specialist's
|
||||
/// full persona/model/tool loop/iteration cap (`run_via_harness` in
|
||||
/// `tinyflows/caps.rs`) — the prompt must say so, not describe it as a
|
||||
/// future capability.
|
||||
#[test]
|
||||
fn standing_prompt_links_agent_ref_to_the_full_tool_loop() {
|
||||
const STANDING_PROMPT: &str = include_str!("prompt.md");
|
||||
|
||||
assert!(
|
||||
STANDING_PROMPT.contains("specialist")
|
||||
&& (STANDING_PROMPT.contains("tool loop")
|
||||
|| STANDING_PROMPT.contains("full persona")),
|
||||
"standing prompt must link agent_ref to the specialist's full tool loop \
|
||||
(the harness path), not just a persona/model swap"
|
||||
);
|
||||
}
|
||||
|
||||
/// Regression guard: the old `list_agent_profiles` description (and any
|
||||
/// prompt copy that echoed it) claimed the per-agent tool loop was "a
|
||||
/// follow-up" and that a step "still gets tools from the node's own
|
||||
/// inline `tools` list for now". That's false — `run_via_harness` already
|
||||
/// gives an `agent_ref` step its selected specialist's real tool loop —
|
||||
/// and the stale wording actively discouraged using `agent_ref` at all.
|
||||
#[test]
|
||||
fn standing_prompt_has_no_stale_agent_ref_followup_language() {
|
||||
const STANDING_PROMPT: &str = include_str!("prompt.md");
|
||||
|
||||
for banned in [
|
||||
"is a follow-up",
|
||||
"for now",
|
||||
"still gets tools from the node's own",
|
||||
] {
|
||||
assert!(
|
||||
!STANDING_PROMPT.contains(banned),
|
||||
"standing prompt must not carry the stale agent_ref-tool-loop \
|
||||
phrasing `{banned}` — the harness path already gives agent_ref \
|
||||
its full tool loop"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// `list_agent_profiles`'s own tool description used to discourage
|
||||
/// `agent_ref` with stale "follow-up"/"for now" wording (issue B37, Gap
|
||||
/// 1) — pin that it now correctly describes the harness's full tool
|
||||
/// loop instead.
|
||||
#[test]
|
||||
fn list_agent_profiles_tool_description_has_no_stale_followup_language() {
|
||||
use crate::openhuman::flows::builder_tools::ListAgentProfilesTool;
|
||||
use crate::openhuman::tools::traits::Tool;
|
||||
|
||||
let description = ListAgentProfilesTool::new().description().to_string();
|
||||
|
||||
for banned in ["is a follow-up", "for now"] {
|
||||
assert!(
|
||||
!description.contains(banned),
|
||||
"list_agent_profiles description must not carry the stale \
|
||||
phrasing `{banned}` — an agent_ref step already gets the \
|
||||
selected specialist's full tool loop"
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
description.contains("tool loop"),
|
||||
"list_agent_profiles description must describe agent_ref as running \
|
||||
the specialist's full tool loop"
|
||||
);
|
||||
}
|
||||
|
||||
/// Guard against over-fragmentation: the minimal-graph rule (don't chain
|
||||
/// agents doing the same kind of work) must survive alongside the new
|
||||
/// specialist guidance (do pick a specialist when the step needs tools
|
||||
/// the plain agent lacks) — neither should crowd the other out.
|
||||
#[test]
|
||||
fn standing_prompt_keeps_minimal_graph_warning_alongside_specialist_guidance() {
|
||||
const STANDING_PROMPT: &str = include_str!("prompt.md");
|
||||
|
||||
assert!(
|
||||
STANDING_PROMPT.contains("minimal viable graph"),
|
||||
"standing prompt must still warn to prefer the minimal viable graph"
|
||||
);
|
||||
assert!(
|
||||
STANDING_PROMPT.contains("3–6 nodes") || STANDING_PROMPT.contains("3-6 nodes"),
|
||||
"standing prompt must still carry the 3-6 node sizing guidance"
|
||||
);
|
||||
assert!(
|
||||
STANDING_PROMPT.contains("SAME kind of work"),
|
||||
"standing prompt must still warn against chaining agents doing the \
|
||||
same kind of work, even after adding specialist-selection guidance"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repair_includes_run_id_error_and_failing_nodes() {
|
||||
let mut r = req(BuildMode::Repair);
|
||||
|
||||
@@ -141,6 +141,11 @@ rather than a general context recall), use `memory_hybrid_search` in its
|
||||
makes the graph savable at all.
|
||||
- `list_flows` / `get_flow` → reuse or clone an existing flow instead of
|
||||
duplicating one.
|
||||
- `list_agent_profiles` → the real specialist agent ids (`researcher`,
|
||||
`code_executor`, …) an `agent` node can set as `config.agent_ref`. The
|
||||
agent analogue of `search_tool_catalog`: never guess/hallucinate an id —
|
||||
look it up. See "Picking a specialist via `agent_ref`" below for when and
|
||||
how to use it.
|
||||
- **Missing the integration the workflow needs?** See "Connecting
|
||||
integrations" below — you can help the user link it before you build,
|
||||
rather than dead-ending.
|
||||
@@ -325,6 +330,24 @@ A `WorkflowGraph` is `{ name?, nodes: [...], edges: [...] }`.
|
||||
(recall a preference) or should remember a result/state across runs, wire
|
||||
an `agent` node that uses memory instead of hardcoding context memory
|
||||
already holds. Use sparingly — only when the workflow truly needs it.
|
||||
|
||||
**Picking a specialist via `agent_ref`.** A plain `agent` node (no
|
||||
`agent_ref`) only has the default LLM plus whatever it's given in
|
||||
`input_context`/`prompt` — it cannot run code, browse the web, or reach
|
||||
any domain-specific tool. If a step genuinely needs to DO something —
|
||||
execute code, search the web, touch a domain the workflow author didn't
|
||||
already wire as a `tool_call` — set `config.agent_ref` to the specialist
|
||||
that owns those tools instead of hoping the plain agent can wing it.
|
||||
Setting `agent_ref` runs that step as a REAL agent turn: the selected
|
||||
agent's full persona, model, tool loop, and iteration cap, not just a
|
||||
differently-worded completion. **WHEN**: the step needs code/file
|
||||
execution, web research, or any tool a specialist owns that the plain
|
||||
agent doesn't have. **HOW**: call `list_agent_profiles`, pick the `id`
|
||||
whose `tools`/`description` match the step's need, and set it verbatim on
|
||||
`config.agent_ref` — never hallucinate an id, exactly like grounding a
|
||||
`tool_call` slug via `search_tool_catalog`. Examples: "generate an HTML
|
||||
report from this data" → `code_executor`; "research our competitors" →
|
||||
`researcher`.
|
||||
3. **`tool_call`** — an action. Two flavours by `config.slug`:
|
||||
- **Composio app action** — `config.slug` = a real action slug (from
|
||||
`search_tool_catalog`, e.g. `GMAIL_SEND_EMAIL`) + `config.connection_ref`
|
||||
@@ -587,7 +610,17 @@ failure at runtime. Rules of thumb:
|
||||
`agent` nodes when one could handle both tasks in its prompt (e.g.
|
||||
"extract the key fields AND compose a brief" in one node, rather than
|
||||
"extract" → "compose" as two nodes). Chain agents only when they need
|
||||
genuinely different models, schemas, or `agent_ref` profiles.
|
||||
genuinely different models, schemas, or `agent_ref` profiles. **Don't
|
||||
chain multiple agents doing the SAME kind of work** just to spread it
|
||||
across steps — that's the over-fragmentation this rule warns against.
|
||||
|
||||
- **DO pick a specialist when the step needs tools the plain agent lacks.**
|
||||
The minimal-graph rule is about node COUNT, not about under-provisioning a
|
||||
step — a step that needs to run code, search the web, or touch a
|
||||
specialist's tools literally cannot do that job as a plain `agent` node,
|
||||
so setting `config.agent_ref` there isn't added complexity, it's the
|
||||
difference between the step working and silently no-op'ing. See "Picking
|
||||
a specialist via `agent_ref`" above.
|
||||
|
||||
- **Target: 3–6 nodes for a simple automation.** A schedule-trigger →
|
||||
source-tool → agent-summarize → destination-tool flow is 4 nodes.
|
||||
|
||||
@@ -2264,10 +2264,12 @@ impl Tool for ListAgentProfilesTool {
|
||||
field (e.g. researcher, code_executor, crypto_agent). Read-only. Returns \
|
||||
a JSON array of { id, name, description, model, tools, tags }. Use this to \
|
||||
pick a real agent_ref — a coding step should reference the coding agent, a \
|
||||
research step the researcher — instead of guessing an id. Note: an \
|
||||
agent_ref applies that agent's persona/model to the step; its private \
|
||||
tool loop is a follow-up, so a step still gets tools from the node's own \
|
||||
inline `tools` list for now."
|
||||
research step the researcher — instead of guessing an id. Note: setting \
|
||||
agent_ref runs the step as a REAL agent turn (its own `run_single`), with \
|
||||
the selected specialist's full persona, model, tool loop, and iteration \
|
||||
cap — not just a persona-flavored completion. A plain `agent` node with \
|
||||
no agent_ref only gets the default LLM plus its own inline `tools` list; \
|
||||
it cannot run code, search the web, or use any specialist's tools."
|
||||
}
|
||||
|
||||
fn parameters_schema(&self) -> Value {
|
||||
|
||||
Reference in New Issue
Block a user