Getting Started

Note

This documentation is for adk-fluent v0.17.0 (PyPI).

This page gets you from zero to a working agent in 5 minutes. By the end, you’ll understand the builder pattern, the expression operators, and when to use each.

Note

Python and TypeScript — click a tab once Most samples ship with a Python / TypeScript tab. Click either — your choice syncs across every tab on the site and sticks as you navigate. The semantics are identical; only the operator syntax differs (Python uses >> / | / * / // / @; TypeScript uses .then() / .parallel() / .times() / .fallback() / .outputAs()). If you picked TypeScript, also see the TypeScript (adk-fluent-ts) landing page.

Install

pip install adk-fluent

Autocomplete works immediately – the package ships with .pyi type stubs for every builder. Type Agent("name"). and your IDE shows all available methods with type hints.

# adk-fluent-ts lives in the monorepo (not yet on npm)
git clone https://github.com/vamsiramakrishnan/adk-fluent.git
cd adk-fluent/ts
npm install
npm run build

Autocomplete works out of the box — the package is written in TypeScript, so hover-docs and type inference light up immediately in VS Code, JetBrains, Neovim (with tsserver), and any LSP-aware editor. See TypeScript (adk-fluent-ts) for install, imports, and the operator-mapping reference.

Your First Agent

from adk_fluent import Agent

agent = Agent("helper", "gemini-2.5-flash").instruct("You are a helpful assistant.").build()
# Returns a real google.adk.agents.llm_agent.LlmAgent — not a wrapper.

print(agent.ask("Hello, who are you?"))
# => Hi! I'm a helpful assistant. How can I help you today?
import { Agent } from "adk-fluent-ts";

const agent = new Agent("helper", "gemini-2.5-flash")
  .instruct("You are a helpful assistant.")
  .build();
// Returns a real @google/adk LlmAgent — not a wrapper.

console.log(await agent.ask("Hello, who are you?"));
// => Hi! I'm a helpful assistant. How can I help you today?

That’s a full agent. agent is a real native ADK LlmAgent — it works with adk web, adk run, adk deploy, or any ADK API.

Warning

Jupyter, FastAPI, or any running event loop? Python’s .ask() and .map() are sync and blocking — they raise RuntimeError inside an already-running event loop. Use await agent.ask_async(...) and await agent.map_async(...) instead. See Execution for the full async surface. (TypeScript is async-only; no equivalent footgun.)

Your First Pipeline

Chain agents sequentially with the Pipeline builder or the sequential operator — >> in Python, .then() in TypeScript.

from adk_fluent import Agent, Pipeline

# Builder style -- explicit, great for complex configurations
pipeline = (
    Pipeline("research")
    .step(Agent("searcher", "gemini-2.5-flash").instruct("Search for information."))
    .step(Agent("writer", "gemini-2.5-flash").instruct("Write a summary."))
    .build()
)

# Operator style -- concise, great for composing reusable parts
pipeline = (
    Agent("searcher", "gemini-2.5-flash").instruct("Search for information.")
    >> Agent("writer", "gemini-2.5-flash").instruct("Write a summary.")
).build()
import { Agent, Pipeline } from "adk-fluent-ts";

// Builder style -- explicit, great for complex configurations
const pipeline = new Pipeline("research")
  .step(new Agent("searcher", "gemini-2.5-flash").instruct("Search for information."))
  .step(new Agent("writer", "gemini-2.5-flash").instruct("Write a summary."))
  .build();

// Method-chain style -- concise, great for composing reusable parts
const pipeline2 = new Agent("searcher", "gemini-2.5-flash")
  .instruct("Search for information.")
  .then(new Agent("writer", "gemini-2.5-flash").instruct("Write a summary."))
  .build();

Both produce an identical SequentialAgent. The builder style shines when each step needs callbacks, tools, and context engineering. The operator / method-chain style excels at composing reusable sub-expressions.

Parallel Execution

Run agents concurrently with FanOut or the parallel operator — | in Python, .parallel() in TypeScript.

from adk_fluent import Agent, FanOut

fanout = (
    FanOut("parallel_research")
    .branch(Agent("web", "gemini-2.5-flash").instruct("Search the web."))
    .branch(Agent("papers", "gemini-2.5-flash").instruct("Search papers."))
    .build()
)

# Or with operators:
fanout = (
    Agent("web", "gemini-2.5-flash").instruct("Search the web.")
    | Agent("papers", "gemini-2.5-flash").instruct("Search papers.")
).build()
import { Agent, FanOut } from "adk-fluent-ts";

const fanout = new FanOut("parallel_research")
  .branch(new Agent("web", "gemini-2.5-flash").instruct("Search the web."))
  .branch(new Agent("papers", "gemini-2.5-flash").instruct("Search papers."))
  .build();

// Or with the method-chain operator:
const fanout2 = new Agent("web", "gemini-2.5-flash")
  .instruct("Search the web.")
  .parallel(new Agent("papers", "gemini-2.5-flash").instruct("Search papers."))
  .build();

Loops

Iterate a fixed number of times — * 3 in Python, .times(3) in TypeScript. Use Loop / loop_until / timesUntil when you need a predicate-driven exit.

from adk_fluent import Agent, Loop

loop = (
    Loop("refine")
    .step(Agent("writer", "gemini-2.5-flash").instruct("Write draft."))
    .step(Agent("critic", "gemini-2.5-flash").instruct("Critique."))
    .max_iterations(3)
    .build()
)

# Or with operators:
loop = (
    Agent("writer", "gemini-2.5-flash").instruct("Write draft.")
    >> Agent("critic", "gemini-2.5-flash").instruct("Critique.")
) * 3
import { Agent, Loop } from "adk-fluent-ts";

const loop = new Loop("refine")
  .step(new Agent("writer", "gemini-2.5-flash").instruct("Write draft."))
  .step(new Agent("critic", "gemini-2.5-flash").instruct("Critique."))
  .maxIterations(3)
  .build();

// Or with the method-chain operator:
const loop2 = new Agent("writer", "gemini-2.5-flash")
  .instruct("Write draft.")
  .then(new Agent("critic", "gemini-2.5-flash").instruct("Critique."))
  .times(3)
  .build();

Two Styles, Same Result

Every workflow can be expressed two ways. Both produce identical ADK objects:

# Builder style
pipeline = (
    Pipeline("research")
    .step(Agent("web", "gemini-2.5-flash").instruct("Search web.").writes("web_data"))
    .step(Agent("analyst", "gemini-2.5-flash").instruct("Analyze {web_data}."))
    .build()
)

# Operator style
pipeline = (
    Agent("web", "gemini-2.5-flash").instruct("Search web.").writes("web_data")
    >> Agent("analyst", "gemini-2.5-flash").instruct("Analyze {web_data}.")
).build()
// Builder style
const pipeline = new Pipeline("research")
  .step(
    new Agent("web", "gemini-2.5-flash")
      .instruct("Search web.")
      .writes("web_data"),
  )
  .step(new Agent("analyst", "gemini-2.5-flash").instruct("Analyze {web_data}."))
  .build();

// Method-chain style
const pipeline2 = new Agent("web", "gemini-2.5-flash")
  .instruct("Search web.")
  .writes("web_data")
  .then(new Agent("analyst", "gemini-2.5-flash").instruct("Analyze {web_data}."))
  .build();

The builder style shines for complex multi-step workflows where each step is configured with callbacks, tools, and context. The operator style excels at composing reusable sub-expressions:

# Reusable sub-expressions with operators
classify = Agent("classifier", "gemini-2.5-flash").instruct("Classify intent.").writes("intent")
resolve = Agent("resolver", "gemini-2.5-flash").instruct("Resolve {intent}.").tool(lookup_customer)
respond = Agent("responder", "gemini-2.5-flash").instruct("Draft response.")

support_pipeline = classify >> resolve >> respond
# Reuse classify in a different pipeline
escalation_pipeline = classify >> Agent("escalate", "gemini-2.5-flash").instruct("Escalate.")

Putting It Together

Here’s a real-world pipeline combining sequential, parallel, state flow, and context isolation:

S.capture → state Classifier C.none() .writes("intent") Resolver .tool(lookup) .tool(create_ticket) .writes("resolution") Responder {resolution} customer_message intent resolution PIPELINE FLOW WITH STATE DATA CONTRACTS
from adk_fluent import Agent, S, C

MODEL = "gemini-2.5-flash"

# Capture the user's message into state
# Classify with no history (C.none() = sees only the current message)
# Route to specialist agents
# Each agent writes to a named state key for explicit data contracts

support = (
    S.capture("customer_message")
    >> Agent("classifier", MODEL)
       .instruct("Classify intent: billing, technical, or general.")
       .context(C.none())
       .writes("intent")
    >> Agent("resolver", MODEL)
       .instruct("Resolve the {intent} issue for: {customer_message}")
       .tool(lookup_customer)
       .tool(create_ticket)
       .writes("resolution")
    >> Agent("responder", MODEL)
       .instruct("Draft a response summarizing: {resolution}")
)

This pipeline:

  • Captures the user message into state with S.capture() (State Transforms)

  • Isolates context with C.none() so the classifier sees only the current message (Context Engineering)

  • Flows data through named keys with .writes() (Data Flow)

  • Attaches tools with .tool() (Builders)

Test Without an API Key

You don’t need a Gemini API key to verify your agent logic works. .mock() replaces the LLM with canned responses:

from adk_fluent import Agent

agent = (
    Agent("helper", "gemini-2.5-flash")
    .instruct("You are a helpful assistant.")
    .mock(["Hello! I'm here to help."])
)

# Runs instantly, no API call, no cost
print(agent.ask("Hi there"))
# => Hello! I'm here to help.

This is how all 68 cookbook examples run in CI — every example uses .mock() so tests pass without credentials. Use .mock() during development, remove it when you’re ready for real LLM calls.

Tip

.test() — inline smoke tests For quick validation, chain .test() directly:

agent.test("What's 2+2?", contains="4")  # passes silently or raises AssertionError

See What the LLM Sees

One of the most powerful debugging tools: .llm_anatomy() shows the exact prompt, context, and tools the LLM receives.

from adk_fluent import Agent, C

agent = (
    Agent("classifier", "gemini-2.5-flash")
    .instruct("Classify the customer's intent.")
    .context(C.none())
    .writes("intent")
)

agent.llm_anatomy()
# System prompt:  Classify the customer's intent.
# Context:        none (C.none() — current turn only)
# Output key:     intent
# Tools:          (none)

This prevents the #1 debugging nightmare: “why is my agent producing garbage?” The answer is always in what the LLM sees.

Validate and Debug

# Catch config errors before runtime
agent = Agent("x", "gemini-2.5-flash").instruct("Help.").validate()

# See what the builder has configured
agent.explain()

# Generate a visual topology diagram
pipeline.to_mermaid()

# Full diagnostic report
pipeline.doctor()

See Error Reference for every error type with fix-it examples.

Async environments (Jupyter, FastAPI)

The warning earlier covers the one-line case: use ask_async() instead of ask(). The same rule applies to every sync method. Here are the three patterns you’ll actually use:

# One-shot
result = await agent.ask_async("What is the capital of France?")

# Streaming — yields text chunks as they arrive
async for chunk in agent.stream("Tell me a story"):
    print(chunk, end="")

# Multi-turn conversation in a persisted session
async with agent.session() as chat:
    print(await chat.send("Hi"))
    print(await chat.send("Tell me more"))

See Execution for the full async surface.

Choose Your Path

Now that you know the basics, adk-fluent offers three distinct pathways for agent building. All produce native ADK objects – they solve different problems at different abstraction levels.

Pipeline Path – Python-First Builders

Full Python control with expression operators (>> | * @ //) and 9 namespace modules. Build any topology with type-checked, IDE-friendly builders.

Best for: Custom workflows, complex routing, dynamic topologies, callback-heavy agents.

Expression Language
Skills Path – Declarative Agent Packages

Turn YAML + Markdown into executable agent graphs. Domain experts write prompts and topology; engineers inject tools and deploy. One file is docs AND runtime.

Best for: Stable topologies, reusable capability libraries, teams with non-Python domain experts.

Skills – Composable Agent Packages
Harness Path – Autonomous Coding Runtimes

Build Claude-Code-class autonomous agents with the H namespace. Five composable layers: intelligence, tools, safety, observability, and runtime.

Best for: Agents that need file/shell access, permissions, sandboxing, token budgets, multi-turn REPL.

Building Harnesses – From Agent to Autonomous Runtime

Not sure which? See the Decision Guide for a flowchart. All three compose together – a harness can load skills for domain expertise, and skills wire agents as pipelines internally.

What’s Next

User Guide

Deep dive into builders, operators, callbacks, context engineering, and all 9 namespace modules.

User Guide
Cookbook

68 recipes from simple agents to hero workflows like deep research and customer support triage.

Cookbook — Zero to Symphony
API Reference

Complete reference for all 135 builders with type signatures, ADK mappings, and examples.

API Reference
Reactive Agents

Build agents that respond to state changes with the R namespace – signals, predicates, priority scheduling, and Builder.on().

Reactor – reactive signals, priorities & interrupts

See also

  • Expression Language – all 9 operators with composition rules

  • Patterns – higher-order constructors (review_loop, map_reduce, cascade, fan_out_merge)

  • Reactor – signals, predicates, R.compile() for state-driven agent coordination

  • Testing.mock(), .test(), and check_contracts() for testing without API calls

  • Migration Guide – migrate existing native ADK code incrementally