Skip to content

feat(adk): declarative graph workflow agents for ADK Go 2.x - #3344

Open
dimetron wants to merge 16 commits into
kagent-dev:mainfrom
dimetron:main
Open

dimetron wants to merge 16 commits into
kagent-dev:mainfrom
dimetron:main

Conversation

@dimetron

@dimetron dimetron commented Oct 24, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This PR proposes declarative graph workflows for ADK Go 2.x in kagent. A workflow is a YAML/JSON AgentSystem document (apiVersion: adk.declarative/v0.2) that describes ADK workflow.Workflow graphs and LlmAgents with no Go routing code. The controller validates it, and the Go ADK builds the equivalent ADK v2 graph at startup.

The proposal has three parts:

  1. go/adk/pkg/declarative: loader, validator and builder that map the document 1:1 onto ADK Go v2 (workflow.Start, NewFunctionNode, JoinNode, AgentNode, NamedToolNode, ParallelWorker, dynamic nodes, RequestInput, routes).
  2. API plumbing:
    • AgentTemplate.spec.workflowFrom (ConfigMap key);
    • a validated ADKWorkflowConfig in the ADK config proto;
    • BYO harness compilation;
    • subagent isolation (Shared | Isolated).
  3. CEL, MCP and hooks, so common logic stays in YAML: classification, routing, data shaping, guardrails and remote tools.

Document model

apiVersion: adk.declarative/v0.2
kind: AgentSystem
mcp_servers:
  - { name: crm, url: "http://crm-mcp:8080/mcp" }
agents:
  - name: triage
    agent_class: Workflow
    nodes:
      - id: classify
        type: cel
        vars:
          - { name: text, expr: user.content.lowerAscii() }
        routes:                                   # ordered rules, first match wins
          - { route: BILLING, when: "vars.text.matches(r'\\b(invoice|refund)\\b')" }
          - { route: QUESTION, when: "vars.text.endsWith('?')" }
      - { id: lookup, type: tool, server: crm, tool: lookup_customer,
          retry: { max_attempts: 3, when: error.timeout } }
      - { id: reply, type: cel, output: "input.output.with(c, 'Plan: ' + c.plan)" }
      - { id: answer, type: llm, model: default, mode: single_turn,
          instruction_from: { config_map: { name: prompts, key: answer } }, tools: [crm] }
      - { id: fallback, type: cel, output: "'Please rephrase.'" }
    edges:
      - { from: START, to: classify }
      - { from: classify, to: lookup, route: BILLING }
      - { from: lookup, to: reply }
      - { from: classify, to: answer, route: QUESTION }
      - { from: classify, to: fallback, route: DEFAULT }

Node types → ADK Go 2.x

type ADK construct Purpose
function workflow.NewFunctionNode Registered Go function; may return routes
cel function node CEL vars, output, route/routes, state_delta
tool NamedToolNode Registered tool, harness tool, or remote MCP tool (server:)
llm inline LlmAgent node Single-turn model call with tools, prompts and hooks
agent AgentNode Another agent of the document
join JoinNode Fan-in barrier after fan-out
workflow nested Workflow Sub-graph with its own START (max depth 8)
parallel_worker ParallelWorker Map a function, tool or agent over a list (max_concurrency)
dynamic dynamic node Go function that schedules children under max_iters
hitl RequestInput Pause for a human reply

Edges support routing (route, DEFAULT) and fan-out/fan-in. timeout and retry map to NodeConfig; retry.when is a CEL predicate. As in ADK, START is the only sentinel: a node with no outgoing edges ends its branch.

CEL

  • Context: expressions see input, vars, state, session, user and invocation.
  • Libraries: the cel-go standard library plus the strings, regex, lists, math, sets, base64 and optionals extensions.
  • Helpers: json, toJson, x.with(name, body) and default(a.b, fallback) use the same names and semantics as agentgateway CEL.
  • vars and routes: vars (ordered named values) and routes (an ordered first-match rule table) replace nested cel.bind and ternary chains.
  • Compile-time checks: every expression is type-checked when the document is validated, including vars.<name> references, and runs under a cost limit.

Callbacks / guardrails

before_model, after_model, before_tool and after_tool are lists of rules. Each rule is either a registered Go hook or a CEL rule:

  • when + reject: short-circuit with a message.
  • accept: let the call through.

Prompts, MCP and harness tools

  • Prompt sources: instruction_from / global_instruction_from read from a ConfigMap. The controller inlines the text and records it in revision provenance, so a ConfigMap change rolls a new revision. Secret and env sources are rejected.
  • Remote MCP: mcp_servers declares Streamable HTTP MCP servers (headers, tool filter, URL from env). An llm node lists a server in tools:; a tool node calls a single server tool.
  • Harness tools: the local tool library adds read_file, write_file, list_files and bash, which work in the agent workspace.

Delivery in kagent

  • AgentTemplate.spec.workflowFrom references a ConfigMap key. It is mutually exclusive with systemPrompt/systemPromptFrom, a CEL validation rule enforces this, and only a BYO Harness may use it in this first step.
  • The controller validates the document with the same Go package the runtime uses. It sends the canonical JSON and its SHA-256 in ADKWorkflowConfig, which is included in provenance. Invalid documents are reported as validation errors; a missing ConfigMap is retried.
  • Subagents gain isolation: Isolated. In that mode the parent calls the subagent as a tool in a new session seeded with a copy of its state, which parallel fan-out to one subagent needs.

Examples and docs

  • go/adk/examples/declarative: support triage built from CEL classification, remote MCP CRM lookup, harness write_file and an LLM node with tool hooks.
  • go/adk/examples/workflowpatterns: 22 patterns, each implemented twice, as Go and as YAML, and tested for identical output. The catalog in docs/architecture/workflow-patterns.md is generated from them, and a test fails if it is stale.
  • docs/architecture/workflow-agents.md: reference for the document format.

Testing

  • Unit tests for the loader, validator, builder, CEL, MCP (in-process loopback server), hooks, harness tools and isolation.
  • Go/YAML parity tests over the 22 patterns.
  • Translator tests for ConfigMap inlining, Secret rejection and provenance; envtest CRD validation tests; E2E workflow_test.go and subagent_isolation_test.go.
  • make -C go lint is clean.

Not in this PR / open questions

  • Managed harnesses: the kagent harness rejects workflow for now; only BYO images run documents. Should managed kagent runtimes run documents next?
  • CRD: the document is embedded through a ConfigMap rather than as typed CRD fields. This keeps the public API small and avoids exposing ADK internals.
  • Prompt sources: only ConfigMaps; Secret prompt sources are deferred.
  • Python ADK: not covered; the schema is language-neutral, so a Python loader can follow.

Related

dimetron and others added 3 commits October 25, 2026 00:11
Add declarative ADK Go v2 workflow agents (adk.declarative AgentSystem
documents) with a registry for functions, tools, toolsets, models and
node types; runnable workflow design patterns in Go and YAML; controller
validation of workflow documents for BYO agents (spec.workflowFrom);
session isolation for fan-out and Shared subagents; and architecture docs.

Supersedes #2, rebased onto main and reconciled with runtime-level
ask_user disabling.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Dmytro Rashko <dimetron@me.com>
…d model/tool hooks

- cel node type and CEL route/state_delta with ADK context (state, session,
  user, invocation) and cel-go standard extensions plus json.encode/decode
- mcp_servers with url/header sources; tool nodes and LLM tools from remote MCP
- instruction_from/global_instruction_from (env; ConfigMap/Secret via controller)
- before/after model and tool callbacks with accept/reject hooks
- CEL regexp classification example

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Dmytro Rashko <dimetron@me.com>
feat(adk): declarative workflow agents for the Go ADK
@dimetron
dimetron requested review from a team and supreme-gg-gg as code owners October 24, 2026 22:33
@dimetron
dimetron marked this pull request as draft October 24, 2026 22:34
dimetron and others added 6 commits October 25, 2026 00:42
…atterns

- CEL retry predicate (retry.when) over error {message, timeout, canceled}
- Harness tool library: read_file, write_file, list_files, bash (opt-in)
- Close MCP sessions behind filtered toolsets; MCP, hook, prompt and CEL tests
- Declarative example runs CEL + remote MCP + harness tools end to end
- Go-vs-YAML parity patterns cel_router and mcp_tool; regenerated docs
- Controller inlines ConfigMap prompt sources, rejects Secret/env sources
- Document MCP servers, callbacks, prompt sources, retry.when, harness tools

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Dmytro Rashko <dimetron@me.com>
feat(adk): declarative CEL nodes, remote MCP tools, prompt sources and model/tool hooks
…ateway helpers

Make CEL node expressions easier to write without hiding CEL:

- vars: ordered named values read as vars.<name>, replacing nested cel.bind.
- routes: ordered {route, when} rule table; first match wins, no match takes
  DEFAULT. Mutually exclusive with route.
- Helpers follow agentgateway names and semantics: json(text), toJson(value),
  x.with(name, body) and default(a.b[k], fallback). json.decode/json.encode
  are renamed to json/toJson.

Examples, docs and the generated pattern catalog use the new forms.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Dmytro Rashko <dimetron@me.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Dmytro Rashko <dimetron@me.com>
feat(adk): simplify declarative CEL with vars, route rules and agentgateway helpers
As in ADK, a node without outgoing edges ends its branch. An edge to END now explains this instead of reporting an unknown node.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Dmytro Rashko <dimetron@me.com>
@dimetron
dimetron marked this pull request as ready for review October 25, 2026 00:01
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Dmytro Rashko <dimetron@me.com>
@dimetron dimetron changed the title ADK 2.x declarative agents proposal feat(adk): declarative graph workflow agents for ADK Go 2.x Oct 25, 2026
@github-actions github-actions Bot added the enhancement New feature or request label Oct 25, 2026
dimetron and others added 4 commits October 25, 2026 02:14
… subagents

A cel node shared one error variable across concurrent runs, and the
standalone check stopped at an isolated subagent's direct children.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Dmytro Rashko <dimetron@me.com>
A Workflow agent may set max_concurrency to cap how many nodes of its
graph run at once. workflowagent.Config takes no engine options, so a
capped graph is built with workflow.WithMaxConcurrency behind a small
agent that runs or resumes it.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Dmytro Rashko <dimetron@me.com>
docs(adk): workflows have no END node; list cel routes
…s atomically

retry.when replaced ADK's default predicate, so input validation errors
were retried when the expression matched; they are now never retried.
A DynamicFunc may run children concurrently, so DynamicEnv claims its
max_iters budget atomically.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Dmytro Rashko <dimetron@me.com>
fix(adk): keep ADK retry defaults in retry.when and count dynamic runs atomically
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Oct 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant