Files
google__adk-docs/docs/evaluate/environment_simulation.md
Kristopher Overholt 50f7df7f46 Add Kotlin language support to ADK docs (#1768)
* Add Kotlin to hero / front page

* Add quickstart page for Kotlin

* Complete Kotlin quickstart guide and fix hero code sample (#2)

* Replace GitHub repo links with language icons in header (#3)

* Fix header icon FOUC and homepage font weight regression (#4)

* Testing staging pipeline

* Revert test edit (for staging pipeline)

* Update language icon tooltips to indicate GitHub destination (#5)

* Add link to ADK Kotlin release notes (#7)

* Initial commit of ADK Kotlin API reference docs (#6)

* Add script to generate ADK Kotlin API reference docs (#8)

* Update links and link checker ignore list (temporarily) (#9)

* Add ADK Kotlin for Android getting started guide to Advanced setup page (#10)

* Add advanced setup page with steps to "Use ADK Kotlin in Android projects"

* Update temp link checker rules

* Add placeholder folder for adk-samples (#13)

* adding linter/compilation checks for kotlin snippets (#12)

* adding linter/compilation checks for kotlin snippets

* Add Kotlin validation scripts

* Initial commit of Kotlin sample agents for adk-samples (#15)

* Adding kotlin snippet for llm agents (#16)

* Adding kotlin snippets to  Events (#17)

* Pull changes to docs/events/index.md from glaforge-kotlin-snippets

* fixing kotlin event timestamp and longRunningToolIds

* Fix language tags (#19)

* Fix language tags

* Update

* Fix wrapping

* Fix wrapping (again)

* Fix wrapping/format

* Fix language tag on integration page

* Enable check_paths in PyMdown Snippets Extension to make the build fail if a snippet can't be found (#20)

* Update mkdocs config (#21)

* Fix broken links, update URLs to adk.dev, and improve (temp) lychee config (#22)

* Add Kotlin/maven badge to README (#23)

* Adding Kotlin snippets for artifacts  (#18)

* Pull Kotlin snippets for artifacts from glaforge-kotlin-snippets

* Add comprehensive Kotlin snippets for artifacts

* Refactor artifacts documentation to use external Kotlin snippets

* Update Kotlin model to gemini-flash-latest

* Fix GCS initialization in Kotlin artifact snippet

* afixi failing test with capital-agent added to files_to_check

* Fix snippet label syntax for MkDocs build

* Configure proper Gradle project for Kotlin snippets and fix dependencies

* Add KSP support and generated sources to Kotlin snippets build

* fixing capital_agent turnComplete

* Fix syntax error in build.gradle.kts by removing invalid placeholders (#25)

* Adding Kotlin snippets to google-gemini.md (#27)

Pulling kotlin changes to google-gemini.md from glaforge-kotlin-snippets

* Add a warning about not adding an api key to production code. (#28)

* Add a warning about not adding an api key to production code.

* Update note

---------

Co-authored-by: Kristopher Overholt <koverholt@google.com>

* Add ADK Demo App sample showcasing Gemini-powered agents (#29)

This sample demonstrates how to use the Google ADK (Agent Development Kit) in an Android application to create a chat interface powered by a Gemini-based "Fun Facts" agent. The implementation features:
*   Integration with the Kotlin ADK core and processor libraries.
*   A `FunFactsAgent` defined using `LlmAgent` and the Gemini model.
*   A `ChatViewModel` utilizing `InMemoryRunner` for asynchronous message streaming.
*   A modern UI built with Jetpack Compose and Material 3.
*   Build configuration logic for secure API key management via environment variables or `local.properties`.

* Update Kotlin docs and samples to align with adk-kotlin API changes (#30)

Rename GeminiModel to Gemini, @AdkTool/@AdkParam to @Tool/@Param,
adkTools() to generatedTools(), replace DebugRunner with InMemoryRunner,
fix AgentLoader import path, use SingleAgentLoader, bump Kotlin to
2.3.21 and KSP to 2.3.7, and update Android minSdk from 24 to 26.

* adding kotlin info to READMEs (#14)

* Reorganize Android sample agent and add READMEs (#31)

* Move Android sample agent

* Update repo README, add Android README, update sample agent README

* Minor edit to language support tags (#32)

* Remove blog post link (#33)

Will re-add after it's published

* Remove examples link (#34)

* Adding Kotlin snippets for Sessions docs (#26)

* initial kotlins snippets additions to sessions docs

* Updating memory docs with kotlin snippets

* Adding kotlin snippets to session state docs.

* update model to gemini-flash-latest

* sessions examples clean-up

* fixing sessions snippet markers

* adding kotlin session snippets to files to test

* adding callback to memory_example

* Fixing capital agent snippet  (#35)

Fixing file name
Updating adkTool > Tool
Updating GeminiModel > Gemini

* Adding kotlin snippets for tools docs (#36)

* adding function tool kotlin snippets

* adding function_tools snippets to files to test

* Adding kotlin snippets to observability docs (#37)

* initial kotlin observability updates

* adding observability snippets to file check (#38)

* Adding Kotlin snippets to Callbacks docs (#39)

*  kotlin callbacks snippets

* adding callbacks snippets to file check

* Align Kotlin and KSP versions with published 0.1.0 artifacts (#40)

* switch CLI entry points from InMemoryRunner to ReplRunner (#41)

* Switch CLI entry points from InMemoryRunner to ReplRunner

* Fix wording

* Update API reference docs for Kotlin, 2026-05-18 (#42)

* Remove ADK on Android note until published (#43)

* Update Kotlin code samples (#44)

* Rename GeminiModel to Gemini in Kotlin snippets and docs

* Remove broken SessionKey call and use sessionId directly in AgentTool snippet

* Rewrite Go hero snippet to use llmagent API

* Use isFinalResponse with safe access in CapitalAgent snippet

* Use Role.USER constant instead of raw string in SetupExample

* Use full semver v0.1.0 in Kotlin language support tags

* Remove Android setup steps, moving to new property (#45)

* Tutorial Kotlin agent (#46)

* Adding multi-tool-agent snippet and updating tutorial
* Fixing Go language order on tutorial page
* adding multi tool agent example to files to test

* Inline Kotlin get-started code sample

* Kotlin Multi agents snippets (#47)

* Multi-agent kotlin snippets

* Fixing docs tags in multiagent example

* Fix Kotlin language support tags, code samples, and google-gemini.md cleanup (#48)

* Add Kotlin v0.1.0 to language support tags across docs

* Fix MultiToolAgent.kt model string and argument style

* Update MultiAgentExample.kt to use gemini-flash-latest model string

* Fix google-gemini.md: add Kotlin sample, remove unsupported Java tabs

* Remove explicit apiKey from CallbackBasic.kt for consistency

* Standardize Gemini() constructor to use named args in all snippets

* Remove adk-samples directory (moved to google/adk-samples#1969)

* Remove adk-samples directory (moved to google/adk-samples#1969) (#49)

* Update API reference docs for ADK Kotlin 0.1.0 (#50)

* Remove adk-samples directory (moved to google/adk-samples#1969)

* Update API reference docs for ADK Kotlin 0.1.0

* Remove kotlin lycheeignore config (#51)

* Remove adk-samples directory (moved to google/adk-samples#1969)

* Remove Kotlin .lycheeignore config links

---------

Co-authored-by: Toni Klopfenstein <2359976+ToniCorinne@users.noreply.github.com>
Co-authored-by: Jolanda Verhoef <JolandaVerhoef@users.noreply.github.com>
2026-05-19 11:43:42 -05:00

446 lines
16 KiB
Markdown

# Environment simulation for evaluations
<div class="language-support-tag">
<span class="lst-supported">Supported in ADK</span><span class="lst-python">Python v1.24.0</span>
</div>
When evaluating agents that rely on external dependencies — such as APIs,
databases, or third-party services — running those tools live during testing can
be slow, costly, or unreliable. The **Environment Simulator** lets you safely
intercept these tool calls during agent execution and replace them with
controlled, deterministic responses, without modifying the agent itself. This
approach can fill a critical gap in the agent improvement loop, allowing you to
create hermetic, offline test runs that isolate your agent logic for reliable
scoring.
Overall, this feature lets you:
* Test how an agent handles API errors or edge-case responses.
* Run evaluations offline, without access to live backends.
* Generate realistic mock responses automatically using an LLM.
* Produce reproducible test runs by seeding probabilistic injections.
The Environment Simulation integrates with ADK's tool execution pipeline via the
[`before_tool_callback`](/callbacks/types-of-callbacks/#tool-execution-callbacks)
hook or the [plugin system](/plugins/), so no
changes to your agent code are required.
```
The Environment Simulation is an experimental feature. Its API may change in future
releases.
```
## How it works
While [User Simulation](/evaluate/user-sim/)
drives the conversation forward, Environment Simulation provides the stable
backend. At a high level, the Environment Simulator sits between your agent and
its tools. When the agent calls a tool, the simulator intercepts the call and
decides whether to return a synthetic response — either a predefined injection
or an LLM-generated mock — or to let the real tool execute.
The decision logic follows this order for each configured tool:
1. **Injection configs** are checked first, in order. If a matching injection
is found (based on argument matching and probability), its error or response
is returned immediately.
2. **Mock strategy** is used as a fallback if no injection config applies. The
simulator calls an LLM to generate a realistic response based on the tool's
schema and any stateful context.
3. **No-op** is returned (`None`) if the tool is not in the simulator config,
allowing the real tool to execute normally.
## Integration
The `EnvironmentSimulationFactory` class provides two integration points:
* `create_callback()` — Returns an async callable suitable for use as a
`before_tool_callback` on any `LlmAgent`.
* `create_plugin()` — Returns an `EnvironmentSimulationPlugin` instance that
integrates with the ADK plugin system.
### Using as a callback
The following example shows how to create an environment simulation as one of the adk agent callbacks.
```python
from google.adk.agents import LlmAgent
from google.adk.tools.environment_simulation import EnvironmentSimulationFactory
from google.adk.tools.environment_simulation.environment_simulation_config import (
EnvironmentSimulationConfig,
InjectedError,
InjectionConfig,
ToolSimulationConfig,
)
config = EnvironmentSimulationConfig(
tool_simulation_configs=[
ToolSimulationConfig(
tool_name="get_user_profile",
injection_configs=[
InjectionConfig(
injected_error=InjectedError(
injected_http_error_code=503,
error_message="Service temporarily unavailable.",
)
)
],
)
]
)
agent = LlmAgent(
name="my_agent",
model="gemini-flash-latest",
tools=[get_user_profile],
before_tool_callback=EnvironmentSimulationFactory.create_callback(config),
)
```
### Using as a plugin
The following example shows how to create environment simulation as an ADK agent plugin.
```python
from google.adk.apps import App
from google.adk.tools.environment_simulation import EnvironmentSimulationFactory
from google.adk.tools.environment_simulation.environment_simulation_config import (
EnvironmentSimulationConfig,
MockStrategy,
ToolSimulationConfig,
)
config = EnvironmentSimulationConfig(
tool_simulation_configs=[
ToolSimulationConfig(
tool_name="search_products",
mock_strategy_type=MockStrategy.MOCK_STRATEGY_TOOL_SPEC,
)
]
)
app = App(
agent=my_agent,
plugins=[EnvironmentSimulationFactory.create_plugin(config)],
)
```
## Configuration reference
You can configure the Environment Simulator with a set of dataclasses. The
following sections provide a detailed reference for each configuration object.
### `EnvironmentSimulationConfig`
The top-level configuration object.
Field | Type | Default | Description
:------------------------------- | :--------------------------- | :------------------- | :----------
`tool_simulation_configs` | `List[ToolSimulationConfig]` | required | One entry per tool to simulate. Must not be empty, and tool names must be unique.
`simulation_model` | `str` | `"gemini-flash-latest"` | The LLM used for tool connection analysis and mock response generation.
`simulation_model_configuration` | `GenerateContentConfig` | thinking enabled | LLM generation config for internal simulator calls.
`environment_data` | `str \| None` | `None` | Optional environment context (e.g., a JSON database snapshot) passed to mock strategies to generate more realistic responses.
`tracing` | `str \| None` | `None` | Tracing data (e.g., a prior agent run trace in JSON string format) to provide historical context.
### `ToolSimulationConfig`
Defines how a single named tool should be simulated.
Field | Type | Default | Description
:------------------- | :---------------------- | :-------------------------- | :----------
`tool_name` | `str` | required | Must match the tool's registered name exactly.
`injection_configs` | `List[InjectionConfig]` | `[]` | Zero or more injection configs, checked in order before the mock strategy.
`mock_strategy_type` | `MockStrategy` | `MOCK_STRATEGY_UNSPECIFIED` | Fallback strategy when no injection is triggered.
### `InjectionConfig`
Controls a single synthetic response that can be injected into a tool call.
Exactly one of `injected_error` or `injected_response` must be set.
Field | Type | Default | Description
:------------------------- | :----------------------- | :------ | :----------
`injected_error` | `InjectedError \| None` | `None` | Error to return (mutually exclusive with `injected_response`).
`injected_response` | `Dict[str, Any] \| None` | `None` | Fixed response dict to return (mutually exclusive with `injected_error`).
`injection_probability` | `float` | `1.0` | Probability `[0.0, 1.0]` that this injection fires.
`match_args` | `Dict[str, Any] \| None` | `None` | If set, the injection only fires when the tool's arguments contain all key-value pairs in `match_args`.
`injected_latency_seconds` | `float` | `0.0` | Artificial delay (≤ 120 s) added before returning the injection result.
`random_seed` | `int \| None` | `None` | Seed for the probability check, enabling deterministic injection behavior.
### `InjectedError`
Defines an HTTP-style error response.
| Field | Type | Description |
| :------------------------- | :---- | :-------------------------------------- |
| `injected_http_error_code` | `int` | HTTP status code to surface as |
: : : `"error_code"` in the tool response. :
| `error_message` | `str` | Human-readable message surfaced as |
: : : `"error_message"` in the tool response. :
### `MockStrategy`
Enum controlling how the simulator generates responses when no injection fires.
| Value | Description |
| :------------------------ | :---------------------------------------------- |
| `MOCK_STRATEGY_TOOL_SPEC` | Uses the tool's schema and stateful context to |
: : prompt an LLM to generate a realistic response. :
| `MOCK_STRATEGY_TRACING` | *(Deprecated)* Please use |
: : `MOCK_STRATEGY_TOOL_SPEC` with tracing input. :
## Injection mode
Use injection configs to test specific failure or edge-case scenarios.
Injections are evaluated in list order; the first one whose `match_args`
criteria are met (and whose probability check passes) is applied.
### Injecting errors
The following example shows how to inject errors with specific error code and error message to the agent.
```python
from google.adk.tools.environment_simulation.environment_simulation_config import (
InjectedError,
InjectionConfig,
ToolSimulationConfig,
)
ToolSimulationConfig(
tool_name="charge_payment",
injection_configs=[
InjectionConfig(
injected_error=InjectedError(
injected_http_error_code=402,
error_message="Payment declined.",
)
)
],
)
```
The agent will receive `{"error_code": 402, "error_message": "Payment
declined."}` instead of a real tool result, allowing you to evaluate how the
agent handles payment failures.
### Injecting fixed responses
Use the following InjectionConfig to specify a success response with fixed response payload.
```python
InjectionConfig(
injected_response={"status": "ok", "order_id": "ORD-9999"}
)
```
### Conditional injection with argument matching
Use `match_args` to inject only when specific arguments are passed.
```python
InjectionConfig(
match_args={"item_id": "ITEM-404"},
injected_error=InjectedError(
injected_http_error_code=404,
error_message="Item not found.",
),
)
```
Here, the error is injected only when the tool is called with
`item_id="ITEM-404"`. All other calls pass through to the next injection config
or to the mock strategy.
### Probabilistic injection
Set `injection_probability` to a value between `0.0` and `1.0` to simulate flaky
behavior. For reproducible test runs, pin the random outcome with `random_seed`.
```python
InjectionConfig(
injection_probability=0.3,
random_seed=42,
injected_error=InjectedError(
injected_http_error_code=500,
error_message="Internal server error.",
),
)
```
### Injecting latency
Use `injected_latency_seconds` to simulate slow backend responses, useful for
testing timeout handling or user experience under degraded conditions.
```python
InjectionConfig(
injected_latency_seconds=5.0,
injected_response={"result": "slow but successful"},
)
```
### Combining multiple injection configs
Multiple injection configs on a single tool are checked in order. You can
combine them to test multiple scenarios:
```python
ToolSimulationConfig(
tool_name="get_inventory",
injection_configs=[
# Always fail for a specific out-of-stock item
InjectionConfig(
match_args={"sku": "OOS-001"},
injected_response={"quantity": 0, "available": False},
),
# Randomly fail 20% of the time for all other items
InjectionConfig(
injection_probability=0.2,
random_seed=7,
injected_error=InjectedError(
injected_http_error_code=503,
error_message="Inventory service unavailable.",
),
),
],
)
```
## Mock strategy mode
When you want the simulator to generate plausible responses automatically —
rather than returning hand-crafted values — use `MOCK_STRATEGY_TOOL_SPEC`.
The simulator uses an LLM to:
1. Analyze the schemas of all tools the agent has access to, and identify
*stateful dependencies* between them (e.g., a `create_order` tool produces
an `order_id` that `get_order` consumes).
2. Track a **state store** of IDs and resources created during the session.
3. Generate a response that is consistent with the tool's schema and the
current state — returning a 404-style error if a consuming tool requests a
resource that was never created.
```python
from google.adk.tools.environment_simulation.environment_simulation_config import (
EnvironmentSimulationConfig,
MockStrategy,
ToolSimulationConfig,
)
config = EnvironmentSimulationConfig(
tool_simulation_configs=[
ToolSimulationConfig(
tool_name="create_order",
mock_strategy_type=MockStrategy.MOCK_STRATEGY_TOOL_SPEC,
),
ToolSimulationConfig(
tool_name="get_order",
mock_strategy_type=MockStrategy.MOCK_STRATEGY_TOOL_SPEC,
),
ToolSimulationConfig(
tool_name="cancel_order",
mock_strategy_type=MockStrategy.MOCK_STRATEGY_TOOL_SPEC,
),
]
)
```
With this config, the simulator will automatically generate an `order_id` when
`create_order` is mocked, and use it to return consistent results (or a
not-found error) when `get_order` or `cancel_order` are subsequently called.
### Providing environment data
Pass domain-specific context through `environment_data` to make mock responses
more realistic. This can be a JSON string representing a snapshot of your
database or any structured context the LLM should use when generating responses.
```python
import json
db_snapshot = {
"products": [
{"id": "P-001", "name": "Wireless Headphones", "price": 79.99, "stock": 12},
{"id": "P-002", "name": "USB-C Hub", "price": 34.99, "stock": 0},
],
"warehouse_location": "US-WEST-2",
}
config = EnvironmentSimulationConfig(
tool_simulation_configs=[
ToolSimulationConfig(
tool_name="search_products",
mock_strategy_type=MockStrategy.MOCK_STRATEGY_TOOL_SPEC,
),
],
environment_data=json.dumps(db_snapshot),
)
```
The LLM will use this data to return product names, prices, and stock levels
that match your domain, rather than generating arbitrary placeholder values.
### Providing tracing data
Feed traces generated in the agent to be mocked through `tracing` to make mock
responses more realistic.
```python
import json
agent_traces = [
{
"invocation_id": "inv-001",
"user_content": {"role": "user", "parts": [{"text": "Search for high-end headphones"}]},
"intermediate_data": {
"tool_uses": [
{
"name": "search_products",
"args": {"query": "high-end headphones"},
"response": {"products": [{"id": "P-123", "name": "Premium Wireless ANC Headphones"}]}
}
]
}
}
]
config = EnvironmentSimulationConfig(
tool_simulation_configs=[
ToolSimulationConfig(
tool_name="search_products",
mock_strategy_type=MockStrategy.MOCK_STRATEGY_TOOL_SPEC,
),
],
tracing=json.dumps(agent_traces),
)
```
The LLM will use this data to return product names, prices, and stock levels
that match your domain, rather than generating arbitrary placeholder values.
## Mixing injections and mock strategy
Injection configs and a mock strategy can be combined on the same tool.
Injections are always checked first; the mock strategy fires only when no
injection applies.
```python
ToolSimulationConfig(
tool_name="send_notification",
injection_configs=[
# Always fail for a known-bad recipient
InjectionConfig(
match_args={"recipient_id": "INVALID"},
injected_error=InjectedError(
injected_http_error_code=400,
error_message="Invalid recipient.",
),
),
],
# For all other recipients, generate a plausible success response
mock_strategy_type=MockStrategy.MOCK_STRATEGY_TOOL_SPEC,
)
```