How to Use
Complete guide for developers and operators: connect the SDK, instrument agents, and use the dashboard. Base URL for the cloud product: https://cartha.in
1. What Cartha is
Cartha is an ops and governance layer for AI agent fleets. You keep your own agent code (OpenAI, tools, business logic). You add a few Cartha lines so every run is registered, traced, and (optionally) budgeted and memory-scoped.
- Not a chatbot builder — you bring the agents.
- Not only logs — scopes, hard budgets, policies, cost per task.
- SDK-first — Python package
cartha-sdk.
2. Quickstart (minimum)
2.1 Create an account & API key
- Open Login and sign in (Google / GitHub).
- Go to Keys (or Settings) and copy your API key (
cartha_...).
2.2 Install
pip install cartha-sdk # optional — auto LLM tracing: pip install openai
2.3 Environment variables
# macOS / Linux export CARTHA_API_KEY="cartha_..." export CARTHA_API_BASE="https://cartha.in" # Windows PowerShell $env:CARTHA_API_KEY="cartha_..." $env:CARTHA_API_BASE="https://cartha.in"
Use https://cartha.in — not api.cartha.in (that host may not resolve).
2.4 Minimum code
import cartha
cartha.init()
@cartha.trace(id="my_agent", team="support")
def run_agent(user_id: str, prompt: str):
# your existing logic
return "done"
run_agent("u1", "hello")After you call the function: the agent appears under Agents, a run appears under Traces, heartbeats keep “last seen” fresh while the process is alive.
Minimum integration ≈ 25% of product value (fleet + run shell). Use the easy path below for tools, LLM, cost, and budgets.
3. Easy full integration (recommended)
Use helpers so you rarely write tool_call / llm_call by hand.
import cartha
cartha.init()
client = cartha.wrap_openai() # auto: LLM steps + tokens + $
@cartha.tool() # auto: tool steps + failures
def crm_lookup(user_id: str) -> dict:
return {"plan": "pro", "duplicate": True}
@cartha.trace(id="support", team="support", budget_usd=0.5)
def handle(user_id: str, ticket: str) -> str:
cartha.remember_sync(user_id=user_id, content=ticket, scope="user")
hits = cartha.recall_sync(user_id=user_id, context=ticket, scope=["user", "team"])
data = crm_lookup(user_id)
r = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"{ticket}\n{data}\n{hits}"}],
)
return r.choices[0].message.content or ""
print(handle("user_42", "I was charged twice"))| You write | Cartha records |
|---|---|
| @cartha.trace(...) | Agent, heartbeat, run start/finish, nest, optional budget |
| @cartha.tool() | Tool steps (success + failure) |
| wrap_openai() + create() | LLM steps, tokens, estimated cost (feeds budgets) |
| remember / recall | Scoped memory (you choose what to store) |
4. Agents & traces in detail
@cartha.trace parameters
id— agent key in the dashboard. Optional; defaults to the function name. Use a stable id in production.team— team id for memory/cost grouping. Default:CARTHA_TEAM_IDordefault_team.budget_usd— hard spend ceiling for the run.allowed_tools— only these tool names may run.nest=True— child agents auto-link under the parent.
@cartha.trace(id="billing_agent", team="support", budget_usd=0.5, allowed_tools=["crm_lookup"])
def billing(...):
...Works on sync and async functions. Call the function for anything to appear — decorating alone does nothing.
5. Tools & LLM
Easy
@cartha.tool()
def crm_lookup(user_id: str) -> dict:
return db.get(user_id)
@cartha.tool("search_docs")
async def search(q: str) -> list:
return await index.search(q)
client = cartha.wrap_openai() # needs: pip install openai
r = client.chat.completions.create(model="gpt-4o-mini", messages=[...])Manual (any model / custom)
await cartha.tool_call(tool_name="crm_lookup", output=data, status="success")
await cartha.llm_call(
model="gpt-4o-mini",
input=prompt,
output=text,
tokens_in=100,
tokens_out=40,
total_cost_usd=0.002,
)
# sync:
cartha.tool_call_sync(...)
cartha.llm_call_sync(...)Without tool/LLM recording, traces stay thin (start/finish only) and budgets may not trip on real model spend.
6. Memory (scopes & denials)
Memory is enforced on the server (not only in the SDK). Prefer going only through Cartha APIs — do not give agents raw DB/index credentials that skip filters.
# Store
await cartha.remember(user_id="u1", content="Prefers email", scope="user", confidence=0.9)
# scopes: "user" | "agent" | "team" | "org"
# Recall (clamped to this agent's readable scopes)
hits = await cartha.recall(
user_id="u1",
context="contact preference",
scope=["user", "team"],
top_k=5,
)
# Sync
cartha.remember_sync(...)
cartha.recall_sync(...)- Team memory only shared if agents share the same
team=on@trace. - Org setting memory denial mode (Settings):
denied_hint(default) vssilent. - Admins can erase memory / RTBF via API (governance); see Memory & Settings in the app.
7. Budgets (circuit breaker)
@cartha.trace(id="billing", team="support", budget_usd=0.05)
def billing(...):
# each llm_call / wrap_openai create counts toward the cap
...
# On overspend:
from cartha import BudgetExceeded
try:
...
except BudgetExceeded as e:
return f"Stopped: {e}"Fail-closed: after the limit is crossed, the next spend raises — not an email hours later. Spend must be reported (via wrap_openai or llm_call / cost).
8. Multi-agent: nest & attenuated delegate
Nest (automatic)
Call one @cartha.trace agent from another — child runs link under the parent in the dashboard.
Delegate budget (parent → child)
@cartha.trace(id="boss", team="support", budget_usd=2.0, allowed_tools=["crm_lookup", "refund"])
async def boss(user_id: str, ticket: str):
grant = await cartha.delegate(
to_agent_id="worker",
task_description="Handle ticket",
budget_usd=0.5, # must be ≤ remaining
allowed_tools=["crm_lookup"], # subset of parent only
)
try:
return await worker(
user_id,
ticket,
cartha_budget_id=grant.get("budget_id"),
cartha_budget_max_usd=grant.get("budget_max_usd"),
cartha_budget_tools=grant.get("budget_allowed_tools"),
)
finally:
if grant.get("budget_id"):
await cartha.close_budget(grant["budget_id"])
@cartha.trace(id="worker", team="support")
async def worker(user_id: str, ticket: str, **kwargs):
...Child cannot get more money or more tools than the parent granted.
Cost per completed task (retries)
tid = cartha.task_context()
for attempt in range(3):
try:
await run_agent(..., cartha_task_id=tid)
break
except Exception:
continue
# Dashboard → Costs → outcomes rolls retries under one task_id9. Policies & human-in-the-loop
- Open Policies in the dashboard.
- Create a rule in plain language (e.g. block certain tools or PII).
- Set action: block or require human approval.
- In code, use
@cartha.tool/tool_call— policy is checked automatically.
from cartha import PolicyViolation
try:
await cartha.tool_call(tool_name="charge_refund", ...)
except PolicyViolation as e:
return f"Blocked: {e.policy_name} — {e.reason}"Pending approvals appear under Escalations. Trace Replay re-evaluates current policies on a past run.
10. Dashboard guide (operators)
| Page | What to do there |
|---|---|
| Home / Dashboard | Tasks today, cost, active agents, needs attention, recent runs |
| Agents | Fleet list, status, last seen, open agent detail |
| Traces | Filter/search runs, open waterfall, run-diff, replay, live tail, export |
| Memory | Search memories, audit, flows |
| Costs | Spend by agent, trends, cost per completed task |
| Policies | Create/edit rules, view policy events |
| Escalations | Approve/reject HITL requests |
| Intelligence | Org benchmarks / summaries |
| Keys | Create admin/agent keys; never commit keys to git |
| Settings | Org name, denial mode, GitHub repo, alerts, SDK snippet |
11. What you see with different integration levels
| Integration | Dashboard shows |
|---|---|
| init + @trace only | Agents, heartbeats, thin runs (start/finish), nest |
| + @tool + wrap_openai | Full timeline, tokens, costs, useful budgets |
| + remember/recall | Memory page, scope/denial behavior |
| + budget_usd / delegate | Hard stop, child envelopes |
| + policies in UI | Blocks, replay, escalations |
12. Integration checklist
- API key set; base URL is https://cartha.in
- cartha.init() at process start
- @cartha.trace on each agent (stable id + team)
- @cartha.tool on tools OR manual tool_call
- wrap_openai() OR manual llm_call with total_cost_usd
- remember/recall if you need shared context
- budget_usd on expensive agents
- Policies created in dashboard for guardrails
- Call the agent functions (decorator alone is not enough)
- Log in with the same org as the API key
13. FAQ
Do I need id and team on @trace?
Optional. Defaults: function name + default_team. Use explicit values in production.
Is tool_call required if I use @cartha.tool?
No — the decorator records it for you.
Why is cost still $0?
You must report spend via wrap_openai or llm_call/cost. Decorator alone does not read your OpenAI bill.
Sync vs async?
Both work. Prefer async + await for high concurrency; use *_sync helpers in sync code.
Can agents bypass memory scopes?
Not through the Cartha API. Never give tools raw database/index access that skips Cartha — enforcement lives on the server.
Ready to instrument your first agent?