All skills
google avatar

/adk-agent-builder

@29933ce
by googlegoogle/adk-python22k stars
4,084

Builds ADK (Agent Development Kit) Python agents: LLM agents with tools, graph workflows of function and agent nodes, conditional routing, fan-out and join, schema-validated delegation between agents, human-in-the-loop pauses, and pytest coverage for all of it. Use when asked to create an agent or a workflow, add a tool to one, branch or loop between nodes, run steps in parallel, pause for user approval, or test an agent. Don't use for explaining how ADK works internally or designing its core components (use `adk-architecture`), for an agent that already runs but misbehaves (use `adk-debug`), for authoring a sample under `contributing/` (use `adk-sample-creator`), or for naming, typing, and formatting conventions (use `adk-style`).

Use this Skill: https://skilld.dev/gh/google/adk-python/adk-agent-builder

This session only. Nothing lands on disk.

referencesdynamic-nodes.md

≈971 tokens on demand. Your agent reads this file only when SKILL.md points to it.

Dynamic Node Scheduling

await ctx.run_node(...) runs another node from inside a node and returns its output. It turns graph control flow into ordinary Python: loops, conditionals, and early exits, written as loops, conditionals, and early exits.

from google.adk import Agent, Context, Event, Workflow
from google.adk.workflow import FunctionNode, node

Example

class Feedback(BaseModel):
  grade: str


generate_headline = Agent(
    name='generate_headline',
    instruction='Write a headline about the topic "{topic}".',
)

evaluate_headline = Agent(
    name='evaluate_headline',
    mode='single_turn',
    instruction='Grade whether the headline is tech-related.',
    output_schema=Feedback,
)


@node(rerun_on_resume=True)
async def orchestrate(ctx: Context, node_input: str) -> str:
  yield Event(state={'topic': node_input})
  while True:
    headline = await ctx.run_node(generate_headline)
    feedback = Feedback.model_validate(
        await ctx.run_node(evaluate_headline, node_input=headline)
    )
    if feedback.grade == 'tech-related':
      yield headline
      break


root_agent = Workflow(name='root_agent', edges=[('START', orchestrate)])

ctx.run_node arguments

await ctx.run_node(
    node,                     # a function, Agent, BaseTool, or BaseNode
    node_input=None,
    *,
    use_as_output=False,
    run_id=None,
    use_sub_branch=False,
    override_branch=None,
)
Argument Effect
use_as_output The child's output becomes the parent's output; the parent's own output events are suppressed
run_id Names this execution instead of auto-numbering it
use_sub_branch Appends node_name@run_id to the branch, isolating events from sibling runs
override_branch Uses a specific branch instead of the parent's

Rules the framework enforces

The calling node needs rerun_on_resume=True. Calling run_node without it raises immediately. The reason is resumption: a dynamically scheduled child may interrupt for user input, and the only way the parent can receive the answer is to be re-run from the top.

An explicit run_id must contain a non-digit. Auto-generated ids are plain numbers ("1", "2", ...), so an all-digit custom id would collide with one. ValueError names the offending id.

use_as_output=True at most once per parent execution. A second call raises Node {path} already has a use_as_output delegate. (A Workflow calling run_node is exempt.)

await the call directly. Wrapping it in asyncio.create_task() leaves the child unsupervised: its errors are swallowed and it is not cancelled when the parent is interrupted.

Imperative workflows

Standard Python replaces routed edges entirely:

async def orchestrator(ctx: Context, node_input: str):
  res_a = await ctx.run_node(step_a, node_input=node_input)
  if 'success' in res_a:
    return await ctx.run_node(step_b, node_input=res_a)
  return await ctx.run_node(step_c, node_input=res_a)

Three traps in this style

A raw function's parameters bind from state, not from node_input. Node parameter binding defaults to 'state', so a value passed as run_node(fn, node_input=x) reaches the function only through a parameter literally named node_input.

def my_worker(node_input: str):  # this name, or the value never arrives
  return f'Done: {node_input}'

A child that itself calls run_node is a parent too, so it also needs rerun_on_resume=True. Raw functions default to False, so wrap it:

inner = FunctionNode(func=inner_orchestrator, rerun_on_resume=True)

A generator cannot return a value. In a node that uses yield, produce the result with yield Event(output=...); return value is a syntax error in an async generator and silently ignored in a sync one.

Source: SKILL.md on GitHub

No alerts7d3 checks · Risk SAFE
  • Gen Agent Trust Hub7d

    This skill provides comprehensive documentation and reference material for building agents using the Google Agent Development Kit (ADK). It covers workflow orchestration, tool usage, and includes guidance on security best practices like input validation and secure credential management. No security issues were detected.

  • Socket7d

    No alerts

  • Snyk7d

    Risk: LOW · No issues

Signed by skilld at 29933ce. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated 2 months ago

README badge

README badge for google/adk-python/adk-agent-builder