You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/agents/custom-agents.md
+90Lines changed: 90 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -49,6 +49,15 @@ The core of any custom agent is the method where you define its unique asynchron
49
49
* **Reactive Stream (`Flowable`):** It must return an `io.reactivex.rxjava3.core.Flowable<Event>`. This `Flowable` represents a stream of events that will be produced by the custom agent's logic, often by combining or transforming multiple `Flowable` from sub-agents.
50
50
* **`ctx` (InvocationContext):** Provides access to crucial runtime information, most importantly `ctx.session().state()`, which is a `java.util.concurrent.ConcurrentMap<String, Object>`. This is the primary way to share data between steps orchestrated by your custom agent.
51
51
52
+
=== "Go"
53
+
54
+
In Go, you implement the `Run` method as part of a struct that satisfies the `agent.Agent` interface. The actual logic is typically a method on your custom agent struct.
* **Iterator:** The `Run` method returns an iterator (`iter.Seq2`) that yields events and errors. This is the standard way to handle streaming results from an agent's execution.
58
+
* **`ctx` (InvocationContext):** The `agent.InvocationContext` provides access to the session, including state, and other crucial runtime information.
59
+
* **Session State:** You can access the session state through `ctx.Session().State()`.
60
+
52
61
**Key Capabilities within the Core Asynchronous Method:**
53
62
54
63
=== "Python"
@@ -123,6 +132,46 @@ The core of any custom agent is the method where you define its unique asynchron
123
132
* **Conditional:** `Flowable.defer()` to choose which `Flowable` to subscribe to based on a condition, or `filter()` if you're filtering events within a stream.
124
133
* **Iterative:** Operators like `repeat()`, `retry()`, or by structuring your `Flowable` chain to recursively call parts of itself based on conditions (often managed with `flatMapPublisher` or `concatMap`).
125
134
135
+
=== "Go"
136
+
137
+
1. **Calling Sub-Agents:** You invoke sub-agents by calling their `Run` method.
138
+
139
+
```go
140
+
// Example: Running one sub-agent and yielding its events
141
+
for event, err := range someSubAgent.Run(ctx) {
142
+
if err != nil {
143
+
// Handle or propagate the error
144
+
return
145
+
}
146
+
// Yield the event up to the caller
147
+
yield(event, nil)
148
+
}
149
+
```
150
+
151
+
2. **Managing State:** Read from and write to the session state to pass data between sub-agent calls or make decisions.
152
+
```go
153
+
// The `ctx` (`agent.InvocationContext`) is passed directly to your agent's `Run` function.
if val, ok := previousResult.(string); ok && val == "some_value" {
162
+
// ... call a specific sub-agent ...
163
+
} else {
164
+
// ... call another sub-agent ...
165
+
}
166
+
167
+
// Store a result for a later step
168
+
if err := ctx.Session().State().Set("my_custom_result", "calculated_value"); err != nil {
169
+
// Handle error
170
+
}
171
+
```
172
+
173
+
3. **Implementing Control Flow:** Use standard Go constructs (`if`/`else`, `for`/`switch` loops, goroutines, channels) to create sophisticated, conditional, or iterative workflows involving your sub-agents.
174
+
126
175
## Managing Sub-Agents and State
127
176
128
177
Typically, a custom agent orchestrates other agents (like `LlmAgent`, `LoopAgent`, etc.).
@@ -158,6 +207,14 @@ Let's illustrate the power of custom agents with an example pattern: a multi-sta
We define the `StoryFlowAgent` struct and a constructor. In the constructor, we store the necessary sub-agents and tell the `BaseAgent` framework about the top-level agents this custom agent will directly orchestrate.
### Part 2: Defining the Custom Execution Logic { #part-2-defining-the-custom-execution-logic }
@@ -191,6 +248,20 @@ Let's illustrate the power of custom agents with an example pattern: a multi-sta
191
248
3. Then, the `sequentialAgent's` Flowable executes. It calls the `grammar_check` then `tone_check`, reading `current_story` and writing `grammar_suggestions` and `tone_check_result` to the state.
192
249
4. **Custom Part:** After the sequentialAgent completes, logic within a `Flowable.defer` checks the "tone_check_result" from `invocationContext.session().state()`. If it's "negative", the `storyGenerator` Flowable is *conditionally concatenated* and executed again, overwriting "current_story". Otherwise, an empty Flowable is used, and the overall workflow proceeds to completion.
193
250
251
+
=== "Go"
252
+
253
+
The `Run` method orchestrates the sub-agents by calling their respective `Run` methods in a loop and yielding their events.
1. The initial `storyGenerator` runs. Its output is expected to be in the session state under the key `"current_story"`.
261
+
2. The `revisionLoopAgent` runs, which internally calls the `critic` and `reviser` sequentially for `max_iterations` times. They read/write `current_story` and `criticism` from/to the state.
262
+
3. The `postProcessorAgent` runs, calling `grammar_check` then `tone_check`, reading `current_story` and writing `grammar_suggestions` and `tone_check_result` to the state.
263
+
4. **Custom Part:** The code checks the `tone_check_result` from the state. If it's "negative", the `story_generator` is called *again*, overwriting the `current_story` in the state. Otherwise, the flow ends.
264
+
194
265
---
195
266
196
267
### Part 3: Defining the LLM Sub-Agents { #part-3-defining-the-llm-sub-agents }
@@ -212,6 +283,12 @@ These are standard `LlmAgent` definitions, responsible for specific tasks. Their
### Structuring Data (`input_schema`, `output_schema`, `output_key`)
255
283
256
284
For scenarios requiring structured data exchange with an `LLM Agent`, the ADK provides mechanisms to define expected input and desired output formats using schema definitions.
@@ -262,6 +290,7 @@ For scenarios requiring structured data exchange with an `LLM Agent`, the ADK pr
262
290
***`output_key` (Optional):** Provide a string key. If set, the text content of the agent's *final* response will be automatically saved to the session's state dictionary under this key. This is useful for passing results between agents or steps in a workflow.
263
291
* In Python, this might look like: `session.state[output_key] = agent_response_text`
264
292
* In Java: `session.state().put(outputKey, agentResponseText)`
293
+
* In Golang, within a callback handler: `ctx.State().Set(output_key, agentResponseText)`
265
294
266
295
=== "Python"
267
296
@@ -311,6 +340,14 @@ For scenarios requiring structured data exchange with an `LLM Agent`, the ADK pr
311
340
.build();
312
341
```
313
342
343
+
=== "Golang"
344
+
345
+
The input and output schema is a `google.genai.types.Schema` object.
{ title="This feature is currently available for Python. Java support is planned/ coming soon."}
@@ -543,6 +588,12 @@ call_agent("If it's raining in New York right now, what is the current temperatu
Service account credentials or API keys are powerful credentials. Never expose them publicly. Use a secret manager like [Google Secret Manager](https://cloud.google.com/secret-manager) to store and access them securely in production.
0 commit comments