If you ship multi-step AI agents with tools and LLMs, you eventually need more than print statements: traces, cost ceilings, tool permissions, and memory that does not leak across users.
This guide shows a practical Python integration pattern for AI agent observability and governance using the open Cartha platform and the cartha-sdk package.
Official product and dashboard: https://cartha.in
Docs: https://cartha.in/documentation
Pricing / trial: https://cartha.in/pricing
Login / API keys: https://cartha.in/login
What is an AI agent control plane?
An AI agent control plane sits beside your agents (CrewAI, LangGraph, OpenAI, custom code). You keep building agents as usual. The control plane:
- Records execution traces (tools, LLM calls, errors)
- Enforces hard budget breakers (stop runaway spend)
- Applies tool allow-lists (block unauthorized tools before they run)
- Stores scoped memory (
user/agent/team/org) with server-side isolation - Exposes an ops dashboard for agents, traces, memory, and costs
Cartha is built as that control plane for production agent systems—not a replacement for your agent framework.
Why Python AI agents need observability and budgets
Search traffic and real teams ask the same questions:
| Problem | What breaks without a control plane |
|---|---|
| Debugging | “Which tool failed?” needs step-level traces |
| Cost | Agent loops can burn API spend overnight |
| Safety | Support bots must not call wire-transfer tools |
| Multi-tenant apps | Customer A memory must never appear for Customer B |
| Audit | Compliance needs “what did the agent know?” |
Logging alone is not enough. You want server-enforced budgets, policies, and scopes—visible at cartha.in.
Prerequisites
- Python 3.10+
- Free workspace + API key from Cartha login
- Optional: OpenAI key if you use
wrap_openai()
Step 1 — Install Cartha SDK
pip install cartha-sdk
# optional
pip install openai
export CARTHA_API_KEY="cartha_..." # from https://cartha.in → Settings / Keys
export CARTHA_API_BASE="https://cartha.in" # same host as the product
Full install notes and examples live in the Cartha documentation (https://cartha.in/documentation).
───
Step 2 — Minimal Python integration (copy-paste)
This is the core AI agent instrumentation pattern: init → tool → trace → optional memory + OpenAI wrap.
```import os
import cartha
Connect to your Cartha workspace (https://cartha.in)
cartha.init(
api_key=os.environ["CARTHA_API_KEY"],
api_base=os.environ.get("CARTHA_API_BASE", "https://cartha.in"),
)
Auto LLM + cost steps for OpenAI chat completions
client = cartha.wrap_openai()
@cartha.tool()
def crm_lookup(user_id: str) -> dict:
"""Recorded as a tool step; blocked if not on allowed_tools."""
return {"user_id": user_id, "plan": "pro"}
@cartha.trace(
id="support_agent",
team="support",
budget_usd=0.50, # hard agent-run budget
allowed_tools=["crm_lookup"], # tool allow-list (authority)
)
async def handle_ticket(user_id: str, ticket: str) -> str:
# Scoped memory — user scope is isolated per customer
await cartha.remember(
user_id=user_id,
content=f"Ticket: {ticket}",
scope="user",
confidence=0.9,
)
hits = await cartha.recall(
user_id=user_id,
context=ticket,
scope=["user", "team"],
top_k=5,
)
data = crm_lookup(user_id)
r = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{
"role": "user",
"content": f"{ticket}\nCRM: {data}\nMemory: {hits}",
}],
)
return r.choices[0].message.content or ""
Run the function once, then open the Cartha dashboard (https://cartha.in):
• Agents — registration / heartbeats
• Traces — step-by-step run replay
• Memory — scoped store/recall
• Costs — spend feeding budget breakers
───
### Step 3 — Hard agent budgets (stop runaway spend)
```from cartha import BudgetExceeded
@cartha.trace(id="support_agent", team="support", budget_usd=0.01)
async def risky_run(user_id: str) -> str:
# ... LLM / cost events ...
return "ok"
try:
await risky_run("user_123")
except BudgetExceeded as e:
print("Cartha stopped the run:", e)
This is an agent-run budget (per @cartha.trace), not “hope the model stops.” Details: cartha.in/documentation (https://cartha.in/documentation).
───
Step 4 — Tool allow-lists (authority before execution)
@cartha.trace(
id="support_agent",
team="support",
allowed_tools=["crm_lookup"], # wire tools NOT listed
)
async def support_only(user_id: str):
return crm_lookup(user_id)
# calling a non-listed @cartha.tool → ToolNotAuthorized (before body runs)
Support can help customers; it cannot “accidentally” call privileged tools if those tools are outside the allow-list. That’s AI agent governance, not a prompt suggestion.
───
Step 5 — Scoped memory (multi-tenant isolation)
┌───────┬───────────────────────────────┐
│ Scope │ Who can use it │
├───────┼───────────────────────────────┤
│ user │ Memories for that user_id │
├───────┼───────────────────────────────┤
│ agent │ Bound to agent identity │
├───────┼───────────────────────────────┤
│ team │ Shared within a team │
├───────┼───────────────────────────────┤
│ org │ Org-wide (when policy allows) │
└───────┴───────────────────────────────┘
Isolation is enforced on the Cartha API, not only in client code. Product overview: https://cartha.in (https://cartha.in).
Top comments (0)