6 Context Sharing
Tools often need shared state. Let’s add context injection.
Note
Code Reference: code/v0.3/src/agentsilex/
agent.pyrunner.py
6.1 The Problem
Consider a shopping assistant with multiple tools:
@tool
def add_to_cart(item: str, price: float) -> str:
"""Add item to cart."""
cart.append({"item": item, "price": price}) # What cart?
return f"Added {item}"
@tool
def get_total() -> str:
"""Get cart total."""
return sum(item["price"] for item in cart) # Same problem!Tools need to share state, but:
- Global variables are bad
- Each tool is a separate function
- We need something to pass through
6.2 Solution: Context Dict
Pass a mutable dictionary through the Runner that tools can read and write:
# Usage
runner = Runner(session, context={"user_id": "user123"})
result = runner.run(agent, "Add milk to cart")
# After execution, context may be modified:
print(runner.context) # {"user_id": "user123", "cart": [...]}6.3 Detecting Context Parameters
First, we need to detect if a function accepts context (agent.py):
import inspect
def has_context_param(func):
sig = inspect.signature(func)
return "context" in sig.parametersSimple introspection — check if context is in the function signature.
6.4 Updated ToolsSet.execute_function_call
class ToolsSet:
# ... get_specification unchanged ...
def execute_function_call(self, context: dict, call_spec):
tool = self.registry.get(call_spec.function.name)
if not tool:
raise ValueError(f"Tool {call_spec.function.name} not found")
args = json.loads(call_spec.function.arguments)
# Inject context if the tool supports it
if has_context_param(tool.function):
args["context"] = context
result = tool(**args)
return {"role": "tool", "tool_call_id": call_spec.id, "content": str(result)}Key change: if the tool function has a context parameter, we inject it automatically.
6.5 Updated Runner
class Runner:
def __init__(self, session: Session, context: dict | None = None):
self.session = session
# Context dict passed to tools - can be read and written
self.context = context or {}
def run(self, agent: Agent, prompt: str, context: dict | None = None) -> RunResult:
# ... setup ...
while loop_count < 10 and not should_stop:
# ... LLM call ...
# Execute tools with context
tools_response = [
current_agent.tools_set.execute_function_call(self.context, call_spec)
for call_spec in response_message.tool_calls
if not call_spec.function.name.startswith(HANDOFF_TOOL_PREFIX)
]
# ... rest unchanged ...Context is passed to execute_function_call, which injects it into tools that need it.
6.7 Example: Shopping Cart
from agentsilex import Agent, Runner, Session, tool
@tool
def add_to_cart(item: str, price: float, context: dict) -> str:
"""Add an item to the shopping cart."""
cart = context.setdefault("cart", [])
cart.append({"item": item, "price": price})
return f"Added {item} (${price:.2f}) to cart"
@tool
def get_cart(context: dict) -> str:
"""Show current cart contents."""
cart = context.get("cart", [])
if not cart:
return "Cart is empty"
items = [f"- {item['item']}: ${item['price']:.2f}" for item in cart]
total = sum(item["price"] for item in cart)
return "\n".join(items) + f"\nTotal: ${total:.2f}"
@tool
def clear_cart(context: dict) -> str:
"""Clear the shopping cart."""
context["cart"] = []
return "Cart cleared"
agent = Agent(
name="shopping_assistant",
model="gpt-4o",
instructions="You are a shopping assistant. Help users manage their cart.",
tools=[add_to_cart, get_cart, clear_cart],
)
# Create runner with initial context
session = Session()
context = {"user_id": "user123"}
runner = Runner(session, context=context)
# Multi-turn shopping
runner.run(agent, "Add milk for $3.50")
runner.run(agent, "Add bread for $2.00")
result = runner.run(agent, "What's in my cart?")
print(result.final_output)
# - milk: $3.50
# - bread: $2.00
# Total: $5.50
# Context was modified by tools
print(runner.context)
# {"user_id": "user123", "cart": [{"item": "milk", "price": 3.5}, ...]}6.8 What the LLM Sees
For add_to_cart, the LLM sees:
{
"type": "function",
"function": {
"name": "add_to_cart",
"description": "Add an item to the shopping cart.",
"parameters": {
"type": "object",
"properties": {
"item": {"type": "string"},
"price": {"type": "number"}
},
"required": ["item", "price"]
}
}
}Notice: context is not in the parameters. The LLM only knows about item and price.
6.9 Use Cases
| Use Case | Context Data |
|---|---|
| Shopping cart | {"cart": [...]} |
| User preferences | {"user_id": "...", "preferences": {...}} |
| API clients | {"db": connection, "http_client": client} |
| Accumulated state | {"search_results": [...], "selected": ...} |
6.10 Key Design Decisions
| Decision | Why |
|---|---|
context param name reserved |
Clear convention, easy to detect |
| Hidden from LLM | LLM doesn’t need to know about internal state |
| Mutable dict | Tools can both read and write |
| Optional for tools | Tools opt-in by adding context param |
TipCheckpoint
cd code/v0.3Context sharing is now available! Tools can share state.