Skip to content
Open
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
292 changes: 292 additions & 0 deletions docs/guide/integrations/crewai.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
---
title: CrewAI Integration Guide
author: ruirui6946
date: 2026-06-23
tags:
- integration
- crewai
lang: en-US
---

# CrewAI Integration Guide

## Integration Target and Version

[CrewAI](https://www.crewai.com/) is a framework for orchestrating role-based AI agents. This guide connects CrewAI's official `E2BPythonTool` to Cube Sandbox, so agents can execute Python inside isolated MicroVMs instead of on the host.

The integration uses Cube Sandbox's E2B-compatible API. Existing CrewAI code keeps using `E2BPythonTool`; the only infrastructure change is pointing `E2B_API_URL` at CubeAPI and selecting a Cube template.

- Tested CrewAI version: `1.14.7`
- Python: `3.10+`
- Cube client: `e2b-code-interpreter`, installed by `crewai-tools[e2b]`
- Integration type: isolated Python execution tool

## Prerequisites

1. Deploy Cube Sandbox by following one of the [deployment guides](../bare-metal-deploy.md).
2. Create a code-interpreter template containing Python and the packages your agent needs. Cube template IDs use the `tpl-<hex>` format:

```bash
cubemastercli tpl create-from-image \
--image cube-sandbox-cn.tencentcloudcr.com/cube-sandbox/sandbox-code:latest \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing international registry note. Readers outside China should use cube-sandbox-int.tencentcloudcr.com/... instead. Every other English doc in the repo (e.g., quickstart.md, bare-metal-deploy.md) adds a note like:

Use cube-sandbox-int.tencentcloudcr.com/cube-sandbox/sandbox-code:latest (recommended for international access). If you are in mainland China, use cube-sandbox-cn.tencentcloudcr.com/cube-sandbox/sandbox-code:latest instead.

Please add the same note here for consistency.

--writable-layer-size 1G \
--expose-port 49999 \
--expose-port 49983 \
--probe 49999
```

Use `cube-sandbox-int.tencentcloudcr.com/cube-sandbox/sandbox-code:latest` (recommended for international access). If you are in mainland China, use `cube-sandbox-cn.tencentcloudcr.com/cube-sandbox/sandbox-code:latest` instead.

3. Install the dependencies:

```bash
pip install "crewai>=1.14.7,<2" "crewai-tools[e2b]>=1.14.7,<2" python-dotenv
```

4. Configure CubeAPI and your LLM:

```bash
export E2B_API_URL="http://<cube-api-host>:3000"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

http:// transmits the API key in plaintext. Using http:// (not HTTPS) means the E2B_API_KEY credential is sent unencrypted on every API call. Please add a security note that http:// is only acceptable for local development on a trusted machine. For production deployments, either configure TLS on CubeAPI and use https://, or use http://127.0.0.1:3000 (loopback) to limit network exposure.

export E2B_API_KEY="<cube-api-key>"
export CUBE_TEMPLATE_ID="tpl-xxxxxxxxxxxxxxxxxxxxxxxx"

export OPENAI_API_KEY="<your-llm-api-key>"
export MODEL="openai/gpt-4o-mini"
# Optional for an OpenAI-compatible provider:
# export OPENAI_BASE_URL="https://your-provider.example/v1"
```

`E2B_API_URL` must point to the Cube API Server, normally on port `3000`. Do not point it at CubeProxy.

::: warning Protect the Cube API key
The `http://` examples above are only suitable for local development on a trusted machine. Production deployments should either configure TLS on CubeAPI and use `https://`, or bind CubeAPI to loopback and use `http://127.0.0.1:3000` so `E2B_API_KEY` is not sent across the network in plaintext.
:::

## Integration Steps

### 1. Create the Cube-backed CrewAI tool

CrewAI already provides `E2BPythonTool`, so no custom `BaseTool` implementation is required:

```python
import os

from crewai_tools import E2BPythonTool

cube_python = E2BPythonTool(
template=os.environ["CUBE_TEMPLATE_ID"],
persistent=False,
)
```

With `persistent=False`, every tool call receives a fresh Cube MicroVM that is destroyed after execution. This is the safest default for agent-generated code.

### 2. Attach the tool to an agent

```python
import os

from crewai import Agent, Crew, LLM, Process, Task
from crewai_tools import E2BPythonTool

api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
raise RuntimeError("Missing required environment variable: OPENAI_API_KEY")

llm_options = {
"model": os.getenv("MODEL", "openai/gpt-4o-mini"),
"api_key": api_key,
}
if os.getenv("OPENAI_BASE_URL"):
llm_options["base_url"] = os.environ["OPENAI_BASE_URL"]

cube_python = E2BPythonTool(
template=os.environ["CUBE_TEMPLATE_ID"],
persistent=False,
)

analyst = Agent(
role="Sandboxed data analyst",
goal="Use isolated Python execution to produce reproducible answers",
backstory=(
"You verify every numerical result by running Python in Cube Sandbox. "
"Never execute generated code on the host."
),
tools=[cube_python],
llm=LLM(**llm_options),
verbose=os.getenv("CREWAI_VERBOSE", "").lower() == "true",
)

task = Task(
description=(
"Use the sandbox Python tool to simulate 10,000 rolls of two fair dice "
"with random seed 7. Report the estimated probability that the sum is 8 "
"and compare it with the exact probability 5/36."
),
expected_output=(
"A short report containing the simulated probability, exact probability, "
"absolute error, and the Python method used."
),
agent=analyst,
)

try:
result = Crew(
agents=[analyst],
tasks=[task],
process=Process.sequential,
).kickoff()
except Exception as exc:
raise RuntimeError(
"Crew execution failed. Check LLM credentials, CubeAPI connectivity, "
"and sandbox execution timeouts."
) from exc

print(result)
```

### 3. Verify Cube before involving an LLM

When debugging connectivity, invoke the tool directly:

```python
import json

result = cube_python.run(
code=(
"import json\n"
"print(json.dumps({'runtime': 'cube', 'sum': sum(range(10))}, sort_keys=True))"
),
timeout=30,
)
payload = json.loads(str(result).strip().splitlines()[-1])
if (
not isinstance(payload, dict)
or payload.get("runtime") != "cube"
or payload.get("sum") != 45
):
raise RuntimeError(f"Unexpected Cube smoke test payload: {payload!r}")
print(json.dumps(payload, sort_keys=True))
```

This isolates CubeAPI, template, and SDK configuration from any LLM or CrewAI orchestration issue.

## Near-Zero Migration from E2B

If a crew already uses `E2BPythonTool`, the Python code does not change:

```diff
from crewai_tools import E2BPythonTool

tool = E2BPythonTool(
- template="base",
+ template=os.environ["CUBE_TEMPLATE_ID"],
)
```

Only the environment changes:

```diff
-E2B_API_URL=https://api.e2b.dev
+E2B_API_URL=http://<cube-api-host>:3000
```

The agent, task, and tool-calling logic stay the same while execution moves to Cube's MicroVM isolation.

## Going Further

### Persist state across tool calls

Use persistent mode when an agent must reuse imports, variables, or generated files:

```python
cube_python = E2BPythonTool(
template=os.environ["CUBE_TEMPLATE_ID"],
persistent=True,
sandbox_timeout=300,
)

try:
# Use cube_python in one or more agents.
result = crew.kickoff()
finally:
cube_python.close()
```

Persistent sandboxes increase the effect of prompt injection because state survives between calls. Keep timeouts short and do not inject broad credentials.

### Restrict network access

Cube extends the E2B create API with network policy controls. For workloads that need these controls, create the sandbox directly or expose the same arguments from a small custom CrewAI tool:

```python
from e2b_code_interpreter import Sandbox

with Sandbox.create(
template=os.environ["CUBE_TEMPLATE_ID"],
allow_internet_access=False,
network={"allow_out": ["10.0.1.0/24", "api.example.com", "*.example.org"]},
) as sandbox:
execution = sandbox.run_code("print('isolated execution')")
```

`allow_out` accepts IPv4/CIDR targets and DNS domain targets, including leading `*.` wildcard domains. Wildcards match subdomains such as `api.example.org`, not the apex domain `example.org`. Domain targets are learned from DNS A-record answers into temporary IP allow entries, so use `allow_internet_access=False` or an explicit deny-all fallback when the allowlist must be strict. `deny_out` remains an IPv4/IP-CIDR policy.

### Mount host data

Host mounts are a Cube-specific extension encoded in sandbox metadata:

::: warning Validate host mounts
Treat `hostPath` values as privileged configuration. Validate them against a small allowlist before passing them to `Sandbox.create()`, prefer `readOnly: true`, and do not let prompt-controlled agent input construct host-mount metadata. A host mount exposes that host filesystem path to the sandbox despite MicroVM isolation for other paths; read-write mounts can also modify host state from inside the sandbox.
:::

```python
import json

mounts = json.dumps([
{
"hostPath": "/srv/agent-input",
"mountPath": "/mnt/input",
"readOnly": True,
}
])

with Sandbox.create(
template=os.environ["CUBE_TEMPLATE_ID"],
metadata={"host-mount": mounts},
) as sandbox:
execution = sandbox.run_code(
"from pathlib import Path; print(list(Path('/mnt/input').iterdir()))"
)
```

The host path must already exist on the Cubelet node. Prefer read-only mounts for agent inputs.
Comment on lines +235 to +263

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Host mount documentation lacks security guidance on arbitrary host filesystem access (severity: medium)

The guide shows how to mount arbitrary host paths via hostPath but provides minimal security guidance. There is no warning that:

  • hostPath values should be validated and allowlisted before being passed to Sandbox.create()
  • Host mounts bypass the MicroVM isolation boundary for the mounted paths
  • An attacker who controls sandbox metadata (e.g., through prompt injection against an agent) could specify arbitrary hostPath values like /etc/kubernetes/, /var/lib/kubelet/pki/, or /root/.ssh/
  • Read-write mounts allow modifying host filesystem state from within a sandbox

Suggestion: Add a ::: warning admonition block similar to the TLS warning, covering allowlisting, read-only preference, and the isolation-bypass risk.

Comment on lines +235 to +263

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Host mount documentation lacks security guidance on arbitrary host filesystem access (severity: medium)

The guide shows how to mount arbitrary host paths but provides minimal security guidance. There is no warning that:

  • hostPath values should be validated and allowlisted before being passed to Sandbox.create()
  • Host mounts bypass the MicroVM isolation boundary for the mounted paths
  • An attacker who controls sandbox metadata (e.g., through prompt injection against an agent that creates sandboxes) could specify arbitrary hostPath values like /etc/kubernetes/, /var/lib/kubelet/pki/, or /root/.ssh/
  • Read-write mounts allow modifying host filesystem state from within a sandbox

Given the CrewAI context where an LLM agent might construct sandbox parameters based on user prompts, this is a real attack surface.

Suggestion: Add a warning block similar to the TLS warning above, covering allowlisting, read-only preference, and the isolation-bypass risk.


### Bound execution time

There are two different timeout controls:

- `sandbox_timeout` on `E2BPythonTool` controls the sandbox idle lifetime in persistent mode.
- `timeout` passed to `tool.run(...)` controls an individual code execution.

Use per-execution timeouts in ephemeral mode. When `persistent=True`, also set a short `sandbox_timeout` so an idle persistent sandbox cannot stay alive indefinitely.

## Caveats

- The template must include Cube's `envd` service. A plain image such as `python:3.12-slim` is not a valid Cube template by itself.
- Cube template IDs are generated IDs such as `tpl-...`, not Docker image names.
- The LLM API key belongs to the CrewAI process. Only pass task-scoped secrets into the sandbox.
- Treat code and output generated from untrusted prompts as untrusted, even though the MicroVM protects the host.
- Use ephemeral mode unless the task explicitly needs state across calls.

## Runnable Example

The complete bilingual example is available under [`examples/crewai-integration`](https://github.com/TencentCloud/CubeSandbox/tree/master/examples/crewai-integration). Run `smoke_test.py` first to validate Cube, then run `main.py` to start the CrewAI agent.

## References

- [CrewAI E2B Sandbox Tools](https://docs.crewai.com/v1.15.2/en/tools/ai-ml/e2bsandboxtools.md)
- [CrewAI custom tools](https://docs.crewai.com/v1.15.2/en/learn/create-custom-tools.md)
- [Cube Sandbox Python examples](https://github.com/TencentCloud/CubeSandbox/tree/master/examples/code-sandbox-quickstart)
Comment on lines +286 to +290

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CrewAI reference URLs may need updating (severity: medium)

The URLs https://docs.crewai.com/en/tools/ai-ml/e2bsandboxtools and https://docs.crewai.com/en/learn/create-custom-tools use the /en/ language prefix. CrewAI's documentation site was restructured — the current live site likely serves these pages without the /en/ prefix.

Suggestion: Verify these URLs resolve correctly and update to the current path structure. Also applies to the Chinese guide at docs/zh/guide/integrations/crewai.md lines 263-264.

Comment on lines +286 to +290

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CrewAI reference URLs may need updating (severity: medium)

The reference URLs https://docs.crewai.com/en/tools/ai-ml/e2bsandboxtools and https://docs.crewai.com/en/learn/create-custom-tools use the /en/ language prefix. CrewAI's documentation site restructured — the current live site likely serves these pages without the /en/ prefix (e.g., https://docs.crewai.com/tools/e2bsandboxtools).

Also applies to docs/zh/guide/integrations/crewai.md at lines 263-264.

Suggestion: Verify these URLs resolve correctly and update to the current path structure.

- [Cube network policy example](https://github.com/TencentCloud/CubeSandbox/tree/master/examples/network-policy)
- [Cube host-mount example](https://github.com/TencentCloud/CubeSandbox/tree/master/examples/host-mount)
2 changes: 1 addition & 1 deletion docs/guide/integrations/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ lang: en-US

| Title | Author | Date | Tags |
| --- | --- | --- | --- |
| _Add your article here_ | - | - | - |
| [CrewAI Integration Guide](./crewai.md) | ruirui6946 | 2026-06-23 | integration, crewai |
Loading
Loading