Skip to main content

Overview

The Agentica SDK allows agents to write and execute arbitrary Python code in a sandboxed execution environment, while maintaining direct access to objects in your runtime.

Why?

The Agentica SDK is built on the premise that code is the most expressive interface through which models can interact with their environment; with the Agentica SDK, agents can manage and engineer their own context, manipulate and return objects by reference and dynamically create their own tools. Check out the blog post Beyond Code Mode: The Agentica SDK.

The Mental Model

This is achieved by Warp, the Agentica SDK’s protocol that combines Remote Procedure Call (RPC) with transparent proxying in a Python REPL using a language-agnostic object model.
  • Sandboxed execution: Agents write Python code that is executed in a safe, isolated environment
  • Warp bridge: Functions you pass in scope appear in the sandbox as stubs, but execute in your runtime
  • Your code stays local: All actual computation happens in your process with full access to your dependencies
  • Objects are warped: Return values from your functions are represented as lightweight proxies in the sandbox, not fully serialized
  • Type safety enforced: Return values are validated against your type annotations

Anatomy of an Invocation

Let’s walk through exactly what happens when you call an agentic function (much like an agent). Consider the simplified example below where we have elided portions of the code for clarity.
from agentica import agentic

# Your existing types and functions
class OrderResult: ...
class CustomerTier: ...
def get_customer_tier(name: str) -> CustomerTier: ...
def calculate_discount(tier: CustomerTier) -> float: ...

@agentic(get_customer_tier, calculate_discount)
async def process_order(customer_name: str, base_price: float) -> OrderResult:
    """Look up customer tier, calculate discount, and create order"""
    ...

# Call the function
result = await process_order("Alice", 100.0)

What Happens Behind the Scenes

When is called, it triggers the following interaction. 1. The Agentica SDK sends a request to the underlying model with:
  • The instruction: "Look up customer tier, calculate discount, and create order"
  • The input parameters:
  • The signatures and docstrings of functions in scope:
  • The details of the types in scope: OrderResult, CustomerTier
  • The details of the expected return type, OrderResult
2. The agent interactively writes and evaluates code in a sandboxed REPL environment. The agent always writes Python code in the sandbox, regardless of whether you’re using the Python or TypeScript SDK. From its perspective, the Python REPL in the sandbox has everything in scope: get_customer_tier, calculate_discount, OrderResult, CustomerTier, etc. The agent can write and evaluate code like normal. Here’s what a sample REPL session could look like.
An example of an agent's output in the Agentica SDK.
[Agent]
I should begin by obtaining the customer tier
```python
tier = get_customer_tier(customer_name)
tier
```
[Execution]
```
CustomerTier(level='gold', benefits=['free_shipping', 'priority_support'])
```
[Agent]
This must be an important customer
```python
I need to calculate the discount factor
discount = calculate_discount(tier)
discount
```
[Execution]
```
0.15
```
[Agent]
That looks reasonable, a 15% discount. I should compute the price using the discount
```python
final_price = base_price * (1 - discount)
final_price
```
[Execution]
```
85.0
```
[Agent]
Good, let me assemble the order
```python
return OrderResult(
     customer=customer_name,
     original_price=base_price,
     discount=discount,
     final_price=final_price
)
```
[Execution]
```
No output was produced.
```
Async Functions in the REPL: The REPL includes a top-level event loop, so async functions work naturally. When you pass async functions from your runtime, they appear in the REPL as functions returning Future[T] (Python async def foo(...) -> T becomes def foo(...) -> Future[T], TypeScript async function foo(...): Promise<T> similarly translates). The agent can use top-level await, and standard patterns like asyncio.gather() work as expected.

A breakdown

Let’s break down the key lines from the agent’s output above:
tier = get_customer_tier(customer_name)
This calls a stub function in the sandbox — get_customer_tier was never defined in the sandbox, but Warp makes it appear to be present in the agent’s execution environment. The stub intercepts the call and sends an RPC to your runtime, where your actual get_customer_tier() executes with access to your database, environment, etc.
tier
CustomerTier(level='gold', benefits=['free_shipping', 'priority_support'])
The return value is warped back to the sandbox. On inspection, the agent sees what looks like a CustomerTier object, but it’s actually a lightweight reference to the real object in your runtime.
discount = calculate_discount(tier)
When the agent passes tier to another function, Warp resolves the proxy back to the real object. Your actual calculate_discount() executes in your runtime with the real CustomerTier object.
final_price = base_price * (1 - discount)
Simple calculations execute directly in the sandbox — no RPC needed for basic operations.
result = OrderResult(...)
Instantiating OrderResult triggers your actual class constructor in your runtime via Warp, returning another proxy. 3. The result type is validated and returned to your code:
result = await process_order("Alice", 100.0)
# Returns: OrderResult(customer="Alice", original_price=100.0, discount=0.15, final_price=85.0)
Observe that no schema was generated or needed to return a value to your code. Instead an object was instantiated in your runtime and the Agentica SDK ensures that the type of result matches the required return type (OrderResult), which ensures type safety.

Current limitations

Agents and agentic functions currently cannot:
  • define a type and then return an instance of that type
  • return functions, types, or generators that they have defined themselves
Expanded module support: Entire modules (e.g. numpy, pandas, scipy, sympy), C-implemented functions, and pure lambdas can now be passed from the client into agent scope and warped (proxied by reference) to the sandbox.

Bug reports

To report bugs and errors to the Agentica team, please create an issue in the relevant GitHub repo (Python SDK or TypeScript SDK) and include the agent logs (filenames are printed to output). Happy programming!

Next Steps

Scope

Learn about passing functions, state, and types

Functions vs Agents

Understand when to use each

Agentic Functions

Dive into agentic function usage

Examples

See practical examples