Agent observability
Add Caveman to AgentOps: compress a CrewAI crew
Keep AgentOps recording your crew. Wrap one CrewAI agent with Caveman Middleware, compare two tagged AgentOps sessions, and roll back by removing one wrapper.
- 01Start the Caveman runtime
- 02Wrap the heaviest agent
- 03Record two tagged sessions
- 04Compare or roll back
This guide adds Caveman Middleware to a CrewAI crew that AgentOps already records. AgentOps keeps doing what it does: same agentops.init(), same dashboard, same session replays. You wrap one agent, run the crew twice with tagged traces, and compare. For the background, read Caveman vs AgentOps.
CrewAI is the example because AgentOps instruments it out of the box. The same steps work with Caveman's AutoGen, LangChain, Agno and LiteLLM adapters. Entry points and version ranges are on the frameworks page.
What do you need before you start?
You need a crew AgentOps already records, and one agent whose tools return a lot of text: search results, logs, file reads, API payloads. Open a recent AgentOps replay and pick the agent whose calls carry the biggest tool results. That is the one to wrap first.
Check versions. caveman-middleware needs Python 3.13 or newer, and the CrewAI adapter accepts crewai >=1.15 <2. Outside that range it declines and passes calls through untouched.
Step 1: Start the Caveman runtime
The middleware sends candidates to a local runtime that compresses them and keeps the originals on disk:
npm install -g @caveman-ai/cli && caveman setup --install
CAVEMAN_MODE=compress caveman startPlain caveman start runs in record mode, which measures and changes nothing. Set compress on the runtime and switch the baseline on the client side instead, so the two runs differ in one setting.
Step 2: Wrap one agent, keep AgentOps as it is
pip install caveman-sdk "caveman-middleware[crewai]==0.1.0a1"with_caveman_agent takes the keyword arguments you already pass to CrewAI's Agent and returns new ones. Role, goal and backstory pass through untouched. It wraps the llm and adds the caveman_retrieve tool:
import os, uuid
import agentops
from crewai import Agent, LLM
from caveman_cloud.middleware import MiddlewareRuntime, Scope
from caveman_middleware.crewai import with_caveman_agent
agentops.init(auto_start_session=False) # reads AGENTOPS_API_KEY
mode = os.environ.get("CAVEMAN_CLIENT_MODE", "compress")
runtime = MiddlewareRuntime(mode=mode)
if mode != "off":
runtime.ready() # raises if the runtime is unreachable
run_id = str(uuid.uuid4())
analyst = Agent(**with_caveman_agent({
"role": "Log analyst",
"goal": "Find the root cause in the service logs",
"backstory": "You read logs and recover original evidence when needed.",
"llm": LLM(model="gpt-4.1-mini"),
"tools": [read_logs], # your existing tools
}, runtime=runtime, scope=Scope("triage", f"{run_id}-analyst", "main", "0")))Give each agent context its own Scope. The four parts key the stored originals, so a handle only resolves under the scope that created it.
The rest of the crew, tasks and process stay the same.
Step 3: Record two tagged sessions
Wrap the crew run in an AgentOps trace tagged with the mode:
trace = agentops.start_trace("triage", tags=[f"caveman-{mode}"])
result = crew.kickoff(inputs={"service": "checkout"})
agentops.end_trace(trace, end_state="Success")CAVEMAN_CLIENT_MODE=record python triage.py
CAVEMAN_CLIENT_MODE=compress python triage.pySame inputs, same model, same tools. The record run is your baseline: the runtime measures each call and sends the original bytes.
How do you check it worked?
Open the two sessions in AgentOps side by side and work through this list.
- The analyst's model calls. Search the
compressreplay forcaveman: shortened. AgentOps hooks CrewAI's LiteLLM-backedLLM.call; CrewAI 1.x routesgpt-4.1-minito a native OpenAI class, so if the replay shows no model spans, passis_litellm=TruetoLLM(...)(needslitellminstalled) or read the Caveman reports below instead. The tool span keeps the full original. - Recovery calls. Count
caveman_retrievetool calls. Each carries the original back and costs tokens. - Extra turns. Compare iterations, retries and delegations across every agent. If the writer asks the analyst twice, that cost belongs in the total.
- Tokens. Compare provider-reported input tokens for the whole session, every agent included. Where AgentOps records the model call, it takes usage from the provider response, so it reflects what was actually sent.
- The deliverable. Judge both final outputs with the acceptance check you already trust. AgentOps shows how the runs differed; your check decides whether the result is still correct.
For a per-call decision, pass on_report=print to MiddlewareRuntime. applied or reused means a tool result was shortened. skipped comes with a reason, such as no_candidate when the call had no eligible tool result.
What if the agents run on different machines?
A handle resolves only against the runtime that stored the original. If the analyst and the writer run in separate containers and both may call caveman_retrieve, point them at one shared runtime. That needs a token, TLS and explicit consent to send content off the machine, all covered in the deployment docs. Until that's set up, wrap only agents that share a host with the runtime.
How do you roll back?
Build the agent from the original dict again: Agent(role=..., llm=LLM(...), tools=[read_logs]). with_caveman_agent returns new options and mutates nothing. To switch off without a code change, set CAVEMAN_CLIENT_MODE=off. AgentOps was never modified, so both tagged sessions stay in the dashboard for reference.
Keep the runtime running until in-flight crews finish, because a handle the model already holds needs the stored original. Grants last 24 hours from their last use.
Where to go next
- Caveman vs AgentOps for which span shows which copy
- Python middleware docs for the AutoGen, LangChain and Agno adapters
- AgentOps docs for traces and tags
- Measure agent cost for a fair two-arm comparison
Try it on work you know
Keep your stack. Add the adapter.
Wrap the client or framework you already call, run one agent task you can check, and compare the tokens sent.
Open the middleware docs Explore comparisons ↗