Decision Guide¶
This page answers the question every developer asks: “Which pattern should I use?”
Use it as a flowchart when you’re staring at a blank file and know what you want but not how to express it in adk-fluent.
Choosing a Pathway¶
Before picking a topology, pick a pathway. adk-fluent has three:
flowchart TD
START["What are you building?"] --> Q1{"Topology stable &<br>owned by non-Python users?"}
Q1 -->|Yes| SKILLS["<b>Skills Path</b><br>Skill('path/').ask('prompt')<br>YAML + Markdown → agent graph"]
Q1 -->|No| Q2{"Agent needs autonomous<br>file / shell / web access?"}
Q2 -->|Yes| HARNESS["<b>Harness Path</b><br>Agent('x').tools(H.workspace() + H.web())<br>5-layer architecture"]
Q2 -->|No| PIPELINE["<b>Pipeline Path</b><br>Agent('a') >> Agent('b') | Agent('c')<br>Full Python control, 9 namespace modules"]
SKILLS --> COMPOSE["All three compose together"]
HARNESS --> COMPOSE
PIPELINE --> COMPOSE
style SKILLS fill:#FFF3E0,stroke:#E65100,color:#1A1A1A
style HARNESS fill:#e0f2fe,stroke:#0ea5e9,color:#1A1A1A
style PIPELINE fill:#ecfdf5,stroke:#10b981,color:#1A1A1A
style COMPOSE fill:#f5f5f5,stroke:#757575,color:#1A1A1A
Pipeline |
Skills |
Harness |
|
|---|---|---|---|
Abstraction |
Low – full Python control |
High – YAML config |
Medium – composable layers |
Topology |
Any (unlimited) |
Fixed per skill file |
Agent + toolset |
Who writes it |
Engineers |
Domain experts + engineers |
Engineers |
File/shell access |
Optional |
No |
Yes (sandboxed) |
Multi-turn runtime |
No |
No |
Yes (REPL, memory) |
Reusability |
Code sharing |
SKILL.md sharing (30+ platforms) |
Per-domain |
All three compose. A harness loads skills, skills wire agents as pipelines, pipelines use the full expression algebra.
Choosing a Topology¶
flowchart TD
T0["How many agents?"] --> T1{"ONE agent?"}
T1 -->|Yes| SINGLE["Agent('name', 'model')<br>.instruct('...').build()"]
T1 -->|No| T2{"Run in ORDER?"}
T2 -->|Yes| SEQ["a >> b >> c<br><i>Pipeline</i>"]
T2 -->|No| T3{"Run INDEPENDENTLY?"}
T3 -->|Yes| PAR["a | b | c<br><i>FanOut</i>"]
T3 -->|No| T4{"REPEAT until condition?"}
T4 -->|Yes| LOOP["(a >> b) * until(pred)<br><i>Loop</i>"]
T4 -->|No| T5{"ROUTE based on state?"}
T5 -->|Yes| ROUTE["Route('key').eq('x', agent_x)<br>.otherwise(fallback)"]
T5 -->|No| T6{"FALLBACK chain?"}
T6 -->|Yes| FALL["fast_agent // strong_agent"]
T6 -->|No| COMPOSE["Compose them all:<br>a >> (b | c) >> (d >> e) * until(f) >> g"]
style SINGLE fill:#ecfdf5,stroke:#10b981,color:#1A1A1A
style SEQ fill:#FFF3E0,stroke:#E65100,color:#1A1A1A
style PAR fill:#e0f2fe,stroke:#0ea5e9,color:#1A1A1A
style LOOP fill:#ecfdf5,stroke:#10b981,color:#1A1A1A
style ROUTE fill:#fce4ec,stroke:#f472b6,color:#1A1A1A
style FALL fill:#f3e5f5,stroke:#a78bfa,color:#1A1A1A
style COMPOSE fill:#FFF3E0,stroke:#E65100,color:#1A1A1A
Choosing a Context Strategy¶
Situation |
Use |
Why |
|---|---|---|
Agent should see NO history |
|
Classifiers, routers, utility agents that shouldn’t be influenced by prior conversation |
Agent should see only USER messages |
|
Prevents leaking other agents’ internal reasoning |
Agent should see specific state keys |
|
Explicit data contracts; agent sees only what it needs |
Agent should see recent context only |
|
Keeps token budget manageable for long conversations |
Agent should see specific other agents |
|
Multi-agent workflows where you want selective visibility |
Default ADK behavior |
Don’t call |
Agent sees full conversation history |
See Context Engineering for composition rules (+ for union, | for pipe).
Choosing a Data Flow Strategy¶
flowchart LR
subgraph write["Producing Data"]
W1[".writes('key')"]
W2["S.capture('input')"]
W3["S.transform('key', fn)"]
W4["S.merge('a','b', into='c')"]
end
subgraph read["Consuming Data"]
R1[".reads('key')"]
R2["'{key}' in instruction"]
R3["S.guard(pred)"]
R4["S.default(key='val')"]
end
W1 -.-> R1
W1 -.-> R2
W2 -.-> R1
W3 -.-> R2
W4 -.-> R1
style write fill:#ecfdf5,stroke:#10b981,color:#1A1A1A
style read fill:#FFF3E0,stroke:#E65100,color:#1A1A1A
Situation |
Use |
Why |
|---|---|---|
Pass data to the next agent |
|
Named state key, explicit contract |
Read data from a previous agent |
|
Inject state into prompt template |
Capture user input into state |
|
Zero-cost function step before pipeline |
Transform data between agents |
|
No LLM call, pure function |
Merge multiple keys |
|
Combine parallel outputs |
Validate state invariants |
|
Fail fast if preconditions are broken |
Set defaults |
|
Ensure keys exist before reading |
See Data Flow and State Transforms.
Choosing an Output Strategy¶
Situation |
Use |
Why |
|---|---|---|
Free-form text |
Don’t add constraints |
Default LLM behavior |
Structured JSON |
|
Forces JSON conforming to schema; raises on parse failure |
Named state key |
|
Downstream agents read |
Contract annotation only |
|
No runtime effect; |
See Structured Data.
Choosing a Testing Strategy¶
Situation |
Use |
Why |
|---|---|---|
Quick smoke test |
|
Inline, no test file needed |
Deterministic tests (no API) |
|
Canned responses, no LLM calls |
Contract verification |
|
Static analysis of data flow |
Full harness |
|
Async send/receive with assertions |
See Testing.
Choosing a Middleware Strategy¶
Situation |
Use |
Why |
|---|---|---|
Retry on transient failures |
|
Exponential backoff, no retry logic in tools |
Log all agent events |
|
Structured logging for observability |
Track token usage |
|
Budget monitoring |
Circuit breaker |
|
Stop calling a failing model |
Cache responses |
|
Avoid redundant LLM calls |
Scope to specific agents |
|
Apply middleware selectively |
See Middleware.
Common Recipes by Goal¶
“I want to classify and route”¶
from adk_fluent import Agent, S, C
from adk_fluent._routing import Route
classifier = Agent("classifier", MODEL).instruct("Classify: a, b, or c").context(C.none()).writes("category")
pipeline = S.capture("input") >> classifier >> Route("category").eq("a", agent_a).eq("b", agent_b).otherwise(agent_c)
“I want parallel search then synthesis”¶
from adk_fluent import Agent, C
results = (
Agent("web", MODEL).instruct("Search web.").writes("web")
| Agent("papers", MODEL).instruct("Search papers.").writes("papers")
)
pipeline = results >> Agent("synth", MODEL).instruct("Synthesize {web} and {papers}.")
“I want write-review-revise loop”¶
from adk_fluent import Agent
loop = (
Agent("writer", MODEL).instruct("Write.").writes("draft")
>> Agent("critic", MODEL).instruct("Score 0-1.").writes("score")
).loop_until(lambda s: float(s.get("score", 0)) >= 0.8, max_iterations=3)
“I want to test without API calls”¶
from adk_fluent import Agent
from adk_fluent.testing import mock_backend, AgentHarness
harness = AgentHarness(
Agent("helper").instruct("Help."),
backend=mock_backend({"helper": "I can help!"})
)
response = await harness.send("Hi")
assert response.final_text == "I can help!"
See Testing
Still Not Sure?¶
Browse the Cookbook by use case – find a recipe that matches your domain
Read the Framework Comparison – see how adk-fluent compares to LangGraph and CrewAI
Check the Hero Workflows – 7 production-grade examples with full interplay breakdowns
Read the Error Reference – if you’re stuck on a specific error