-
Notifications
You must be signed in to change notification settings - Fork 23
docs: add Google ADK integration guide #295
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
max-sudolabs
wants to merge
5
commits into
e2b-dev:main
Choose a base branch
from
max-sudolabs:max-sudolabs/e2b-adk-plugin
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+335
−0
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
b144bb9
docs: add Google ADK integration guide
max-sudolabs 918d04b
Update docs/agents/google-adk.mdx
max-sudolabs a58d522
Update docs/agents/google-adk.mdx
max-sudolabs 78a92a6
Update docs/agents/google-adk.mdx
max-sudolabs 760192c
docs: explain Google ADK integration paths
max-sudolabs File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,317 @@ | ||
| --- | ||
| title: "Google ADK" | ||
| description: "Give Google ADK agents a secure E2B sandbox to run code, commands, and files in." | ||
| icon: "/images/icons/google-adk.svg" | ||
| --- | ||
|
|
||
| [Google ADK](https://google.github.io/adk-docs/) (Agent Development Kit) is | ||
| Google's framework for building agents, with Gemini as the default model. The | ||
| [E2B](https://e2b.dev/) integration gives ADK workloads an isolated remote | ||
| workspace for running code and commands instead of executing them on your host. | ||
|
|
||
| ## Choose an integration | ||
|
|
||
| <CardGroup cols={2}> | ||
| <Card title="Native ADK environment" icon="box" href="#native-google-adk-environment"> | ||
| Use Google's built-in E2B environment for direct shell and file operations | ||
| </Card> | ||
| <Card title="Advanced E2B plugin" icon="plug" href="#advanced-e2b-plugin"> | ||
| Give an ADK agent ready-made code, command, file, and background-process tools | ||
| </Card> | ||
| </CardGroup> | ||
|
|
||
| Choose the **native environment** when your application directly controls the | ||
| sandbox. Choose the **advanced plugin** when the agent itself should decide when | ||
| to run code, manage files, or start a service. | ||
|
|
||
| ## Native: Google ADK environment | ||
|
|
||
| Google ADK includes the experimental | ||
| [`E2BEnvironment`](https://github.com/google/adk-python/tree/main/src/google/adk/integrations/e2b) | ||
| for direct command execution and file access through ADK's environment API. No | ||
| additional integration package is required. | ||
|
|
||
| ```bash | ||
| pip install "google-adk[e2b]>=2.3.0" | ||
| ``` | ||
|
|
||
| Create and close the environment explicitly in your application: | ||
|
|
||
| ```python | ||
| import asyncio | ||
|
|
||
| from google.adk.integrations.e2b import E2BEnvironment | ||
|
|
||
|
|
||
| async def main() -> None: | ||
| env = E2BEnvironment() | ||
| await env.initialize() | ||
| try: | ||
| await env.write_file("hello.py", 'print("Hello from E2B")\n') | ||
| result = await env.execute("python hello.py") | ||
| print(result.stdout) | ||
| finally: | ||
| await env.close() | ||
|
|
||
|
|
||
| asyncio.run(main()) | ||
| ``` | ||
|
|
||
| This path exposes the sandbox as a low-level workspace: your application calls | ||
| `execute`, `read_file`, and `write_file` itself. | ||
|
|
||
| ## Advanced: E2B plugin | ||
|
|
||
| The [`e2b-adk`](https://pypi.org/project/e2b-adk/) plugin exposes sandbox | ||
| operations as tools the model can call. It adds stateful code execution, shell | ||
| commands, file management, and background processes backed by the | ||
| [E2B Code Interpreter](/docs). The plugin creates one sandbox lazily, shares it | ||
| across tool calls, and closes it with the ADK runner. | ||
|
|
||
| To use E2B with ADK: | ||
|
|
||
| 1. Create an `E2BPlugin`. | ||
| 2. Hand `plugin.get_tools()` to your `Agent` and register the plugin on the `App`. | ||
| 3. Run the agent — every tool call executes inside the sandbox, and the plugin | ||
| creates and tears the sandbox down for you. | ||
|
|
||
| ### Install the dependencies | ||
|
|
||
| Install the plugin. It pulls in Google ADK and the E2B Code Interpreter SDK. | ||
|
|
||
| ```bash | ||
| pip install e2b-adk | ||
| ``` | ||
|
|
||
| You need an E2B API key for the sandbox and a Gemini key for the model. | ||
|
|
||
| ```bash | ||
| export E2B_API_KEY="..." | ||
| export GOOGLE_API_KEY="..." | ||
| ``` | ||
|
|
||
| ## Example: Data analysis agent | ||
|
|
||
| A good fit for a sandbox is analysis the model shouldn't do in its head. Here the | ||
| agent is given a raw dataset and a question. Rather than eyeballing the numbers, | ||
| it writes the data to a file, runs real pandas against it, and reports figures it | ||
| actually computed — the sandbox is a calculator it can't fool. | ||
|
|
||
| ### Create the plugin and agent | ||
|
|
||
| The plugin owns the sandbox. Pass its tools to the `Agent` and register it on the | ||
| `App`; the instruction is what makes the agent *run* code rather than guess. | ||
|
|
||
| ```python | ||
| from google.adk.agents import Agent | ||
| from google.adk.apps import App | ||
|
|
||
| from e2b_adk import E2BPlugin | ||
|
|
||
| INSTRUCTION = """You are a data analyst. You never guess numbers — you compute | ||
| them. Save any data you are given to a file with write_file, analyse it by | ||
| running real pandas with run_code, fix and re-run if the code errors, and report | ||
| the figures you computed. Never report a number you have not verified by running | ||
| code.""" | ||
|
|
||
| plugin = E2BPlugin() | ||
| agent = Agent( | ||
| model="gemini-2.5-flash", | ||
| name="data_analyst", | ||
| instruction=INSTRUCTION, | ||
| tools=plugin.get_tools(), | ||
| ) | ||
| app = App(name="data_analysis", root_agent=agent, plugins=[plugin]) | ||
| ``` | ||
|
|
||
| ### Run the analysis | ||
|
|
||
| Run the agent inside an `InMemoryRunner`. The sandbox is created lazily on the | ||
| first tool call, kept alive while the agent works, and killed when the | ||
| `async with` block exits — you never manage it yourself. | ||
|
|
||
| ```python | ||
| from google.adk.runners import InMemoryRunner | ||
|
|
||
| DATASET = """month,region,marketing_spend,revenue | ||
| 2024-01,North,12000,48000 | ||
| 2024-02,North,15000,61000 | ||
| 2024-03,North,9000,37000 | ||
| 2024-04,North,18000,72000 | ||
| 2024-01,South,8000,26000 | ||
| 2024-02,South,11000,30000 | ||
| 2024-03,South,14000,33000 | ||
| 2024-04,South,17000,38000""" | ||
|
|
||
| async with InMemoryRunner(app=app) as runner: | ||
| # run_debug prints the conversation as it runs. | ||
| await runner.run_debug( | ||
| f"Here is marketing spend and revenue as CSV:\n\n{DATASET}\n\n" | ||
| "Save it to sales.csv, then tell me which region converts spend into " | ||
| "revenue more efficiently and which month performed best." | ||
| ) | ||
| ``` | ||
|
|
||
| The agent writes `sales.csv` with `write_file`, then uses `run_code` to load it | ||
| with pandas, compute revenue-per-dollar and per-month totals, and answer in | ||
| prose — every number backed by an execution in the sandbox: | ||
|
|
||
| ```text | ||
| data_analyst > North converts marketing spend into revenue more efficiently — | ||
| about $4.0 of revenue per $1 of spend versus roughly $2.5 for South. The best | ||
| single month was 2024-04 in North: $72,000 revenue from $18,000 spend. | ||
| ``` | ||
|
|
||
| Pass `verbose=True` to `run_debug` to also print each tool call and its result | ||
| as the agent works. | ||
|
|
||
| ### Full example | ||
|
|
||
| ```python expandable | ||
| import asyncio | ||
|
|
||
| from google.adk.agents import Agent | ||
| from google.adk.apps import App | ||
| from google.adk.runners import InMemoryRunner | ||
|
|
||
| from e2b_adk import E2BPlugin | ||
|
|
||
| INSTRUCTION = """You are a data analyst. You never guess numbers — you compute | ||
| them. Save any data you are given to a file with write_file, analyse it by | ||
| running real pandas with run_code, fix and re-run if the code errors, and report | ||
| the figures you computed. Never report a number you have not verified by running | ||
| code.""" | ||
|
|
||
| DATASET = """month,region,marketing_spend,revenue | ||
| 2024-01,North,12000,48000 | ||
| 2024-02,North,15000,61000 | ||
| 2024-03,North,9000,37000 | ||
| 2024-04,North,18000,72000 | ||
| 2024-01,South,8000,26000 | ||
| 2024-02,South,11000,30000 | ||
| 2024-03,South,14000,33000 | ||
| 2024-04,South,17000,38000""" | ||
|
|
||
|
|
||
| async def main() -> None: | ||
| plugin = E2BPlugin(metadata={"example": "data-analysis"}) | ||
| agent = Agent( | ||
| model="gemini-2.5-flash", | ||
| name="data_analyst", | ||
| instruction=INSTRUCTION, | ||
| tools=plugin.get_tools(), | ||
| ) | ||
| app = App(name="data_analysis", root_agent=agent, plugins=[plugin]) | ||
|
|
||
| async with InMemoryRunner(app=app) as runner: | ||
| # run_debug prints the conversation as it runs. | ||
| await runner.run_debug( | ||
| f"Here is marketing spend and revenue as CSV:\n\n{DATASET}\n\n" | ||
| "Save it to sales.csv, then tell me which region converts spend " | ||
| "into revenue more efficiently and which month performed best." | ||
| ) | ||
|
|
||
|
|
||
| asyncio.run(main()) | ||
| ``` | ||
|
|
||
| ## Example: Code generation agent | ||
|
|
||
| The same tools support a code generator that verifies its own work. The | ||
| instruction tells the agent to execute every snippet in the sandbox before | ||
| returning it, so what you get back has already run and passed its tests — no | ||
| untested code reaches the user. | ||
|
|
||
|
|
||
| ```python expandable | ||
| import asyncio | ||
|
|
||
| from google.adk.agents import Agent | ||
| from google.adk.apps import App | ||
| from google.adk.runners import InMemoryRunner | ||
|
|
||
| from e2b_adk import E2BPlugin | ||
|
|
||
| INSTRUCTION = """You are a code generator that returns verified, working code. | ||
| For every request: (1) write the function, (2) write tests, (3) EXECUTE in the | ||
| sandbox with run_code, (4) if it fails, fix and re-run until tests pass, | ||
| (5) return ONLY the final function. Never return code you haven't executed.""" | ||
|
|
||
|
|
||
| async def main() -> None: | ||
| plugin = E2BPlugin(metadata={"example": "code-generator"}) | ||
| agent = Agent( | ||
| model="gemini-2.5-flash", | ||
| name="codegen", | ||
| instruction=INSTRUCTION, | ||
| tools=plugin.get_tools(), | ||
| ) | ||
| app = App(name="codegen", root_agent=agent, plugins=[plugin]) | ||
|
|
||
| async with InMemoryRunner(app=app) as runner: | ||
| # run_debug prints the conversation as it runs. | ||
| await runner.run_debug( | ||
| "Write a Python function group_by(items, key) that groups a list " | ||
| "by a key function." | ||
| ) | ||
|
|
||
|
|
||
| asyncio.run(main()) | ||
| ``` | ||
|
|
||
| ## Tools | ||
|
|
||
| `plugin.get_tools()` returns six tools, all sharing the plugin's single sandbox | ||
| so state persists across calls. Each returns a dict with a `success` flag and | ||
| reports failures in the result rather than raising, so a bad call never aborts | ||
| the agent run. `success` means the tool *ran*: code that raised or a command | ||
| that exited non-zero still returns `success: True` with the failure captured in | ||
| `error` / `exit_code` — only a call that could not run at all returns | ||
| `success: False`. | ||
|
|
||
| | Tool | Does | | ||
| |------|------| | ||
| | `run_code` | Run code in a stateful kernel (variables persist across calls) | | ||
| | `run_command` | Run a shell command | | ||
| | `write_file` / `read_file` | Write and read files in the sandbox | | ||
| | `list_files` | List a directory | | ||
| | `start_background_command` | Start a long-running process, with an optional preview URL for a port | | ||
|
|
||
| ## Configuration | ||
|
|
||
| `E2BPlugin` accepts keyword-only options, all optional. Anything you don't set | ||
| falls back to the E2B SDK's own default — the plugin overrides none of them. | ||
|
|
||
| ```python | ||
| plugin = E2BPlugin( | ||
| api_key=None, # defaults to the E2B_API_KEY env var | ||
| template=None, # E2B sandbox template | ||
| metadata=None, # dict[str, str] attached to the sandbox | ||
| envs=None, # environment variables inside the sandbox | ||
| timeout=None, # sandbox timeout in seconds (re-applied on every tool call) | ||
| lifecycle=None, # what happens on timeout — see below | ||
| # ...every other AsyncSandbox.create() option is forwarded verbatim | ||
| ) | ||
| ``` | ||
|
|
||
| The plugin keeps the sandbox alive while the agent is working: every tool call | ||
| pushes the expiry window forward by `timeout` (E2B's default is 300s). An idle | ||
| gap longer than `timeout` still expires the sandbox under E2B's default | ||
| lifecycle — pass | ||
| `lifecycle={"on_timeout": {"action": "pause"}, "auto_resume": True}` to pause | ||
| and auto-resume across idle gaps instead. See the | ||
| [repository README](https://github.com/e2b-dev/e2b-adk-plugin#configuration) | ||
| for the full option list. | ||
|
|
||
| ## Reference examples | ||
|
|
||
| Complete, runnable scripts live in the plugin repository. | ||
|
|
||
| <CardGroup cols={2}> | ||
| <Card title="Data analysis agent" icon="chart-line" href="https://github.com/e2b-dev/e2b-adk-plugin/blob/main/examples/data_analysis.py"> | ||
| Compute real answers from a dataset with pandas in the sandbox | ||
| </Card> | ||
| <Card title="Code generator" icon="code" href="https://github.com/e2b-dev/e2b-adk-plugin/blob/main/examples/code_generator.py"> | ||
| Return only code that has been executed and tested | ||
| </Card> | ||
| </CardGroup> | ||
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can advise here newer model
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but the intention was that this is ready to copy-paste with even free-tier gemini api-key, otherwise it would throw erros if user doesn't have already paid api key. So should I change the model?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense, leave it