Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions docs/agents/custom-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@ Finally, you instantiate your `StoryFlowAgent` and use the `Runner` as usual.

## Full Code Example

```python
# Full runnable code for the StoryFlowAgent example
--8<-- "examples/python/snippets/agents/custom-agent/storyflow_agent.py"
```
???+ "Storyflow Agent"

```python
# Full runnable code for the StoryFlowAgent example
--8<-- "examples/python/snippets/agents/custom-agent/storyflow_agent.py"
```
11 changes: 6 additions & 5 deletions docs/agents/llm-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,13 @@ For more complex reasoning involving multiple steps or executing code:

## Putting It Together: Example

Here's the complete basic `capital_agent`:
??? "Code"
Here's the complete basic `capital_agent`:

```python
# Full example code for the basic capital agent
--8<-- "examples/python/snippets/agents/llm-agent/capital_agent.py"
```
```python
# Full example code for the basic capital agent
--8<-- "examples/python/snippets/agents/llm-agent/capital_agent.py"
```

_(This example demonstrates the core concepts. More complex agents might incorporate schemas, context control, planning, etc.)_

Expand Down
8 changes: 4 additions & 4 deletions docs/agents/workflow-agents/loop-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ Imagine a scenario where you want to iteratively improve a document:

In this setup, the `LoopAgent` would manage the iterative process. The `CriticAgent` could be **designed to return a "STOP" signal when the document reaches a satisfactory quality level**, preventing further iterations. Alternatively, the `max_iterations` parameter could be used to limit the process to a fixed number of cycles, or external logic could be implemented to make stop decisions. The **loop would run at most five times**, ensuring the iterative refinement doesn't continue indefinitely.

#### Full code
???+ "Full Code"

```py
--8<-- "examples/python/snippets/agents/workflow-agents/loop_agent_doc_improv_agent.py"
```
```py
--8<-- "examples/python/snippets/agents/workflow-agents/loop_agent_doc_improv_agent.py"
```
8 changes: 4 additions & 4 deletions docs/agents/workflow-agents/parallel-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ Imagine researching multiple topics simultaneously:

These research tasks are independent. Using a `ParallelAgent` allows them to run concurrently, potentially reducing the total research time significantly compared to running them sequentially. The results from each agent would be collected separately after they finish.

### Full code
???+ "Code"

```py
--8<-- "examples/python/snippets/agents/workflow-agents/parallel_agent_web_research.py"
```
```py
--8<-- "examples/python/snippets/agents/workflow-agents/parallel_agent_web_research.py"
```
8 changes: 4 additions & 4 deletions docs/agents/workflow-agents/sequential-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ SequentialAgent(sub_agents=[CodeWriterAgent, CodeReviewerAgent, CodeRefactorerAg

This ensures the code is written, *then* reviewed, and *finally* refactored, in a strict, dependable order. **The output from each sub-agent is passed to the next by storing them in state via [`output_key`](../llm-agents.md)**.

#### Full code
???+ "Code"

```py
--8<-- "examples/python/snippets/agents/workflow-agents/sequential-agent-code-development-agent.py"
```
```py
--8<-- "examples/python/snippets/agents/workflow-agents/sequential_agent_code_development_agent.py"
```
48 changes: 30 additions & 18 deletions docs/callbacks/types-of-callbacks.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,23 @@ These callbacks are available on *any* agent that inherits from `BaseAgent` (inc

**Purpose:** Ideal for setting up resources or state needed only for this specific agent's run, performing validation checks on the session state (callback\_context.state) before execution starts, logging the entry point of the agent's activity, or potentially modifying the invocation context before the core logic uses it.

```py
--8<-- "examples/python/snippets/callbacks/before_agent_callback.py"
```
??? "Code"

```py
--8<-- "examples/python/snippets/callbacks/before_agent_callback.py"
```

### After Agent Callback

**When:** Called *immediately after* the agent's `_run_async_impl` (or `_run_live_impl`) method successfully completes. It does *not* run if the agent was skipped due to `before_agent_callback` returning content or if `end_invocation` was set during the agent's run.

**Purpose:** Useful for cleanup tasks, post-execution validation, logging the completion of an agent's activity, modifying final state, or augmenting/replacing the agent's final output.

```py
--8<-- "examples/python/snippets/callbacks/after_agent_callback.py"
```
??? "Code"

```py
--8<-- "examples/python/snippets/callbacks/after_agent_callback.py"
```

## LLM Interaction Callbacks

Expand All @@ -39,9 +43,11 @@ These callbacks are specific to `LlmAgent` and provide hooks around the interact
**Return Value Effect:**
If the callback returns `None`, the LLM continues its normal workflow. If the callback returns an `LlmResponse` object, then the call to the LLM is **skipped**. The returned `LlmResponse` is used directly as if it came from the model. This is powerful for implementing guardrails or caching.

```py
--8<-- "examples/python/snippets/callbacks/before_model_callback.py"
```
??? "Code"

```py
--8<-- "examples/python/snippets/callbacks/before_model_callback.py"
```

### After Model Callback

Expand All @@ -55,9 +61,11 @@ If the callback returns `None`, the LLM continues its normal workflow. If the ca
* parsing structured data from the LLM response and storing it in `callback_context.state`
* or handling specific error codes.

```py
--8<-- "examples/python/snippets/callbacks/after_model_callback.py"
```
??? "Code"

```py
--8<-- "examples/python/snippets/callbacks/after_model_callback.py"
```

## Tool Execution Callbacks

Expand All @@ -74,9 +82,11 @@ These callbacks are also specific to `LlmAgent` and trigger around the execution
1. If the callback returns `None`, the tool's `run_async` method is executed with the (potentially modified) `args`.
2. If a dictionary is returned, the tool's `run_async` method is **skipped**. The returned dictionary is used directly as the result of the tool call. This is useful for caching or overriding tool behavior.

```py
--8<-- "examples/python/snippets/callbacks/before_tool_callback.py"
```
??? "Code"

```py
--8<-- "examples/python/snippets/callbacks/before_tool_callback.py"
```

### After Tool Callback

Expand All @@ -89,6 +99,8 @@ These callbacks are also specific to `LlmAgent` and trigger around the execution
1. If the callback returns `None`, the original `tool_response` is used.
2. If a new dictionary is returned, it **replaces** the original `tool_response`. This allows modifying or filtering the result seen by the LLM.

```py
--8<-- "examples/python/snippets/callbacks/after_tool_callback.py"
```
??? "Code"

```py
--8<-- "examples/python/snippets/callbacks/after_tool_callback.py"
```
2 changes: 1 addition & 1 deletion docs/guides/responsible-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ If none of these options satisfy your requirements, you can build your own code

### Evaluations

See [Evaluate Agents](../guides/evaluate-agents.md).
See [Evaluate Agents](../evaluate/index.md).

### VPC-SC Perimeters and Network Controls

Expand Down
196 changes: 99 additions & 97 deletions docs/sessions/memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,100 +71,102 @@ The typical workflow involves these steps:

This example demonstrates the basic flow using the `InMemory` services for simplicity.

```py
import asyncio
from google.adk.agents import LlmAgent
from google.adk.sessions import InMemorySessionService, Session
from google.adk.memory import InMemoryMemoryService # Import MemoryService
from google.adk.runners import Runner
from google.adk.tools import load_memory # Tool to query memory
from google.genai.types import Content, Part

# --- Constants ---
APP_NAME = "memory_example_app"
USER_ID = "mem_user"
MODEL = "gemini-2.0-flash-exp" # Use a valid model

# --- Agent Definitions ---
# Agent 1: Simple agent to capture information
info_capture_agent = LlmAgent(
model=MODEL,
name="InfoCaptureAgent",
instruction="Acknowledge the user's statement.",
# output_key="captured_info" # Could optionally save to state too
)

# Agent 2: Agent that can use memory
memory_recall_agent = LlmAgent(
model=MODEL,
name="MemoryRecallAgent",
instruction="Answer the user's question. Use the 'load_memory' tool "
"if the answer might be in past conversations.",
tools=[load_memory] # Give the agent the tool
)

# --- Services and Runner ---
session_service = InMemorySessionService()
memory_service = InMemoryMemoryService() # Use in-memory for demo

runner = Runner(
# Start with the info capture agent
agent=info_capture_agent,
app_name=APP_NAME,
session_service=session_service,
memory_service=memory_service # Provide the memory service to the Runner
)

# --- Scenario ---

# Turn 1: Capture some information in a session
print("--- Turn 1: Capturing Information ---")
session1_id = "session_info"
session1 = session_service.create_session(APP_NAME, USER_ID, session1_id)
user_input1 = Content(parts=[Part(text="My favorite project is Project Alpha.")])

# Run the agent
final_response_text = "(No final response)"
for event in runner.run(USER_ID, session1_id, user_input1):
if event.is_final_response() and event.content and event.content.parts:
final_response_text = event.content.parts[0].text
print(f"Agent 1 Response: {final_response_text}")

# Get the completed session
completed_session1 = session_service.get_session(APP_NAME, USER_ID, session1_id)

# Add this session's content to the Memory Service
print("\n--- Adding Session 1 to Memory ---")
memory_service.add_session_to_memory(completed_session1)
print("Session added to memory.")

# Turn 2: In a *new* (or same) session, ask a question requiring memory
print("\n--- Turn 2: Recalling Information ---")
session2_id = "session_recall" # Can be same or different session ID
session2 = session_service.create_session(APP_NAME, USER_ID, session2_id)

# Switch runner to the recall agent
runner.agent = memory_recall_agent
user_input2 = Content(parts=[Part(text="What is my favorite project?")])

# Run the recall agent
print("Running MemoryRecallAgent...")
final_response_text_2 = "(No final response)"
for event in runner.run(USER_ID, session2_id, user_input2):
print(f" Event: {event.author} - Type: {'Text' if event.content and event.content.parts and event.content.parts[0].text else ''}"
f"{'FuncCall' if event.get_function_calls() else ''}"
f"{'FuncResp' if event.get_function_responses() else ''}")
if event.is_final_response() and event.content and event.content.parts:
final_response_text_2 = event.content.parts[0].text
print(f"Agent 2 Final Response: {final_response_text_2}")
break # Stop after final response

# Expected Event Sequence for Turn 2:
# 1. User sends "What is my favorite project?"
# 2. Agent (LLM) decides to call `load_memory` tool with a query like "favorite project".
# 3. Runner executes the `load_memory` tool, which calls `memory_service.search_memory`.
# 4. `InMemoryMemoryService` finds the relevant text ("My favorite project is Project Alpha.") from session1.
# 5. Tool returns this text in a FunctionResponse event.
# 6. Agent (LLM) receives the function response, processes the retrieved text.
# 7. Agent generates the final answer (e.g., "Your favorite project is Project Alpha.").
```
???+ "Full Code"

```py
import asyncio
from google.adk.agents import LlmAgent
from google.adk.sessions import InMemorySessionService, Session
from google.adk.memory import InMemoryMemoryService # Import MemoryService
from google.adk.runners import Runner
from google.adk.tools import load_memory # Tool to query memory
from google.genai.types import Content, Part

# --- Constants ---
APP_NAME = "memory_example_app"
USER_ID = "mem_user"
MODEL = "gemini-2.0-flash-exp" # Use a valid model

# --- Agent Definitions ---
# Agent 1: Simple agent to capture information
info_capture_agent = LlmAgent(
model=MODEL,
name="InfoCaptureAgent",
instruction="Acknowledge the user's statement.",
# output_key="captured_info" # Could optionally save to state too
)

# Agent 2: Agent that can use memory
memory_recall_agent = LlmAgent(
model=MODEL,
name="MemoryRecallAgent",
instruction="Answer the user's question. Use the 'load_memory' tool "
"if the answer might be in past conversations.",
tools=[load_memory] # Give the agent the tool
)

# --- Services and Runner ---
session_service = InMemorySessionService()
memory_service = InMemoryMemoryService() # Use in-memory for demo

runner = Runner(
# Start with the info capture agent
agent=info_capture_agent,
app_name=APP_NAME,
session_service=session_service,
memory_service=memory_service # Provide the memory service to the Runner
)

# --- Scenario ---

# Turn 1: Capture some information in a session
print("--- Turn 1: Capturing Information ---")
session1_id = "session_info"
session1 = session_service.create_session(APP_NAME, USER_ID, session1_id)
user_input1 = Content(parts=[Part(text="My favorite project is Project Alpha.")])

# Run the agent
final_response_text = "(No final response)"
for event in runner.run(USER_ID, session1_id, user_input1):
if event.is_final_response() and event.content and event.content.parts:
final_response_text = event.content.parts[0].text
print(f"Agent 1 Response: {final_response_text}")

# Get the completed session
completed_session1 = session_service.get_session(APP_NAME, USER_ID, session1_id)

# Add this session's content to the Memory Service
print("\n--- Adding Session 1 to Memory ---")
memory_service.add_session_to_memory(completed_session1)
print("Session added to memory.")

# Turn 2: In a *new* (or same) session, ask a question requiring memory
print("\n--- Turn 2: Recalling Information ---")
session2_id = "session_recall" # Can be same or different session ID
session2 = session_service.create_session(APP_NAME, USER_ID, session2_id)

# Switch runner to the recall agent
runner.agent = memory_recall_agent
user_input2 = Content(parts=[Part(text="What is my favorite project?")])

# Run the recall agent
print("Running MemoryRecallAgent...")
final_response_text_2 = "(No final response)"
for event in runner.run(USER_ID, session2_id, user_input2):
print(f" Event: {event.author} - Type: {'Text' if event.content and event.content.parts and event.content.parts[0].text else ''}"
f"{'FuncCall' if event.get_function_calls() else ''}"
f"{'FuncResp' if event.get_function_responses() else ''}")
if event.is_final_response() and event.content and event.content.parts:
final_response_text_2 = event.content.parts[0].text
print(f"Agent 2 Final Response: {final_response_text_2}")
break # Stop after final response

# Expected Event Sequence for Turn 2:
# 1. User sends "What is my favorite project?"
# 2. Agent (LLM) decides to call `load_memory` tool with a query like "favorite project".
# 3. Runner executes the `load_memory` tool, which calls `memory_service.search_memory`.
# 4. `InMemoryMemoryService` finds the relevant text ("My favorite project is Project Alpha.") from session1.
# 5. Tool returns this text in a FunctionResponse event.
# 6. Agent (LLM) receives the function response, processes the retrieved text.
# 7. Agent generates the final answer (e.g., "Your favorite project is Project Alpha.").
```
Loading