Agenthog setup
You (the AI coding agent reading this skill) are being asked to install and
configure AgentHog tracing in the user's project. Follow this skill end-to-end.
Do NOT skip the verification step.
1. Detect the runtime
Look at the project root to determine the language and primary framework:
- Python if you see , , , or files.
- TypeScript / Node if you see with or
TypeScript files, or .
If both, default to whichever the primary entrypoint uses. Ask the user if
genuinely ambiguous.
Identify the primary framework: FastAPI, Flask, Django, LangGraph, LangChain,
Express, Vercel AI SDK, Next.js, Hono, etc. The framework determines where
belongs and where the per-request loop is.
2. Gather configuration
Two values are required from the user. The endpoint is auto-defaulted — do
NOT ask the user about it unless one of the override conditions below
fires.
Required: and
These are secrets, resolved at
runtime from the environment — the SDK reads
/
from env vars (see the
examples below). Your job is to wire the code to those env vars and confirm the
values are present —
never to obtain, see, or store the secret yourself.
-
Already in the environment? (
,
set) — good, nothing to gather. Wire the code to read them and move on.
-
Not set? The user provides them — you never handle the secret. Offer two
ways and
prefer the first. A bare
is per-shell and dies when the
terminal closes — a common cause of "it traced once, then stopped," and it's
invisible to any other shell (including your own tool shell):
-
Preferred — (
): prompts for both
values, writes a gitignored
(
), and persists across restarts
and across shells. The user runs it and types the key; you do not. Because
it writes
, the values survive new terminals — no two-shells
mismatch.
-
Fallback — shell (ephemeral, fine for a quick one-off): the
user sets the values in the terminal where they'll run the app:
bash
export AGENTOS_API_KEY=<their key>
export AGENTOS_WORKSPACE_ID=<their workspace id>
The quickstart page gives a ready-to-paste
block. This lasts only
for that shell session — recommend
for anything persistent.
Get values from
https://app.theagentos.space (API key: Settings → API Keys;
workspace ID: sidebar chip / top-right). Once the user has done one of these,
confirm presence by name (below) and continue.
Confirm presence without ever reading the secret:
Check that the vars are
set, never their value — a boolean presence test,
never
or echoing the key into your context:
bash
# prints only yes/no; resolves env first, then a gitignored .env the app loads
python3 -c "import os; print('AGENTOS_API_KEY set:', bool(os.getenv('AGENTOS_API_KEY')))"
If it prints
, the user hasn't completed
/
yet —
ask them to finish, don't go hunting for the value.
Credential safety (do not deviate):
- Never accept the API key pasted into the chat. If a user offers to paste
it, decline and tell them to run (or it) instead —
you reference by name, never the value.
- Never search the filesystem or read files hunting for a key, and never
open to read the secret. Confirm presence by env-var name only (above).
- Never hardcode the key in source, and never print or echo it. The
code references via the environment, never a literal.
- The agent never writes the plaintext secret to disk.
(driven by the user typing their key) or the user does that. You may create or
update with a placeholder (Step 6); the real value is never
yours to handle.
Endpoint: default silently
resolves in this order without prompting the user:
- If is already set in the environment, use that.
- Otherwise, use the default:
https://api.theagentos.space
.
Only prompt the user about the endpoint if you have positive evidence
they want to override it — e.g. they said something like "I'm self-hosting" or
"we run agenthog on our own infra" in their request.
Otherwise: pick the default and move on. Asking for the endpoint on every
quickstart is annoying friction for the 95% who use the hosted cloud.
Never commit the plaintext API key, and don't write it to disk yourself —
the user places the real value (Step 6 wires the code + the
placeholder).
3. Install the SDK (a new-enough version is mandatory)
Minimum supported version: Python , TypeScript
. The ingestion API
rejects events from older SDKs
(HTTP 426). The Python floor is
0.9.0 because this skill depends on its
features end-to-end:
credentials (0.5.0), raw-HTTP LLM capture
for OpenAI-compatible endpoints + automatic flush-on-exit (0.6.0), the
/
decorator for tracing the agent's tools
(0.7.0), and
one-call tool capture (Step 5b, 0.9.0) —
which guarantees no tool is silently missed. Below the floor, setup produces
incomplete traces (missing tool steps, or none at all from a short-lived
process).
Install (or upgrade)
as part of setup, using the project's existing
package manager — the same one already managing its dependencies. Tell the user
what you're adding, and pin the floor so an existing older install is bumped
rather than left as-is. If you can't determine the package manager, ask before
installing.
Python
Detect the package manager (
,
,
,
) and install with the
version floor + upgrade:
bash
# uv (uv add always resolves to the newest allowed; the floor guarantees ≥0.9.0)
uv add 'agenthog>=0.9.0'
# poetry
poetry add 'agenthog@>=0.9.0'
# pip (-U upgrades an already-installed older copy)
pip install -U 'agenthog>=0.9.0'
For frameworks with first-class auto-instrumentation, install the extras:
bash
# OpenAI calls
uv add 'agenthog[openai]>=0.9.0'
# Anthropic
uv add 'agenthog[anthropic]>=0.9.0'
# LangChain / LangGraph
uv add 'agenthog[langchain]>=0.9.0'
# OpenTelemetry passthrough
uv add 'agenthog[otel]>=0.9.0'
TypeScript
bash
# pnpm (preferred)
pnpm add agenthog@latest
# npm
npm install agenthog@latest
# yarn
yarn add agenthog@latest
# bun
bun add agenthog@latest
Auto-instrumentation in TS is wired via the integrations import — no extras
required at install time. See
.
Verify the installed version satisfies the floor (do not skip)
After installing, confirm the version (Python
, TypeScript
).
If it isn't, upgrade and re-check before continuing — the rest of the setup (and
the ingestion API) assumes it.
bash
# Python — prints the installed version; exits non-zero if below the floor.
python -c "import agenthog, sys; from importlib.metadata import version; \
v=version('agenthog'); print('agenthog', v); \
sys.exit(0 if tuple(map(int, v.split('.')[:2])) >= (0,9) else 1)"
# TypeScript — same idea.
node -e "const v=require('agenthog/package.json').version; const [a,b]=v.split('.').map(Number); \
console.log('agenthog', v); process.exit((a>0||b>=3)?0:1)"
If the check reports a version below the floor (Python
, TypeScript
), upgrade it the same way you installed it (the package manager's
upgrade for
) and re-run the check. If the floor still can't be met
(e.g. the registry hasn't published it yet),
stop and tell the user rather
than proceeding on an unsupported version — their telemetry would be rejected at
ingest, or the trace would be missing tool steps.
4. Wire at app startup
The
call must run
once, before any LLM/tool call you want traced.
Place it in the app's primary entrypoint:
| Framework | Where to call init |
|---|
| FastAPI | , before |
| Flask | , before |
| Django | bottom, or an hook |
| LangGraph script | top of the script, before graph construction |
| Express | /, before |
| Next.js | (Next.js's official OTel/tracing hook) |
| Vercel AI SDK | wherever the AI client is constructed |
Python init pattern
python
import os
import agenthog
agenthog.init(
api_key=os.environ["AGENTOS_API_KEY"],
endpoint=os.environ.get("AGENTOS_ENDPOINT", "https://api.theagentos.space"),
workspace_id=os.environ["AGENTOS_WORKSPACE_ID"],
agent_id="<descriptive-agent-name>", # pick based on what this service is
)
# Auto-instrument supported LLM / framework libraries. Detects installed
# packages (openai, anthropic, langchain) and patches them. As of
# agenthog >= 0.6.0 it ALSO captures raw-HTTP calls to OpenAI-compatible
# /chat/completions endpoints made via `requests` or `httpx` — so agents that
# POST directly to NVIDIA, Together, Groq, OpenRouter, a local vLLM/Ollama,
# etc. (instead of through a vendor SDK) get traced with no code change.
agenthog.autoinstrument()
Provider coverage. (>= 0.6.0) covers the vendor SDKs
and raw-HTTP OpenAI-compatible calls. If the agent talks to a
custom or
non-OpenAI-shaped endpoint that neither path recognizes, the LLM call won't
be auto-captured — log it explicitly with
agenthog.emit("agent.llm_call", {...})
(or
for tools, Step 5b). When verifying (Step 7),
confirm the trace actually shows the LLM step, not just an empty
.
Flushing on exit is automatic in agenthog >= 0.6.0:
registers an
+
drain, so short-lived CLIs / scripts deliver their buffer
without a manual
. Only a hard kill (
,
) bypasses
it — call
before such an exit if you have one.
TypeScript init pattern
ts
import { init } from "agenthog";
init({
apiKey: process.env.AGENTOS_API_KEY!,
endpoint: process.env.AGENTOS_ENDPOINT ?? "https://api.theagentos.space",
workspaceId: process.env.AGENTOS_WORKSPACE_ID!,
agentId: "<descriptive-agent-name>",
});
// Auto-instrumentation is per-integration in TS. Import the wrapper for the
// LLM client you use. Examples:
// import "agenthog/integrations/openai";
// import "agenthog/integrations/anthropic";
// import "agenthog/integrations/vercelAI";
// import "agenthog/integrations/langchain";
Pick a
that describes what the service does, not just the repo
name. Examples:
,
,
.
5. Wrap the primary agent loop in
One
= one end-to-end execution of the agent (one user message → one
final response, or one cron trigger → one job complete). Spans nest inside it.
Python — context manager
python
with agenthog.start_task_run(user_id=user_id) as ctx:
# Auto-instrumented LLM calls in here are traced under one task_run_id.
# Tool calls are NOT auto-traced unless they go through a framework
# (LangChain/LangGraph) — see Step 5b for hand-written tool loops.
result = my_agent.run(user_input)
return result
Available kwargs:
,
,
,
,
. There is
no kwarg — attach attribution via
the explicit kwargs above instead.
For an HTTP handler (FastAPI/Flask), wrap the handler body:
python
@app.post("/chat")
async def chat(req: ChatRequest):
with agenthog.start_task_run(user_id=req.user_id):
return await my_agent.run(req.message)
TypeScript — callback
ts
import { startTaskRun } from "agenthog";
app.post("/chat", async (req, res) => {
await startTaskRun({ userId: req.body.userId }, async () => {
const out = await myAgent.run(req.body.message);
res.json(out);
});
});
Available
fields:
,
,
,
,
. No
field.
If the user's framework has a middleware concept (Express, Hono, FastAPI
middleware), prefer wiring
as a middleware so every request
gets a task_run automatically.
5b. Instrument the agent's tools (the full execution tree)
captures
LLM calls (vendor SDKs + raw-HTTP
OpenAI-compatible calls) — but
not the agent's
tools: ordinary functions
that hit an API, query a DB, geocode, run a computation. Left alone, the trace
shows only the LLM step, and evals that look for tool usage wrongly report "the
agent never used a tool / hallucinated." This is the same boundary in every
tracer — Arize/LangSmith auto-trace the full tree only on instrumented
frameworks; for hand-written tools they require an annotation too.
This step is what produces the full workflow tree (tool calls + LLM call), not
just the model step. Do it whenever the agent has tool/helper functions.
Preferred — one call over the tools module (Python, )
Point
at the module holding the agent's tool functions. It
wraps
every public function defined there as an
step in a
single call — so no tool can be silently missed the way per-function decorating
can. It
returns the list of wrapped names; keep that list for the Step 7
count check.
python
import agenthog
import weather # the module with the agent's tool functions
agenthog.init(...)
wrapped = agenthog.instrument_module(weather)
# wrapped == ['get_weather', 'get_air_quality', 'geocode']
It skips private helpers (leading
), imported names, classes, and constants.
Tune the selection when the defaults don't fit:
- A public function that isn't a tool →
instrument_module(weather, exclude=["build_prompt"])
.
- A tool named with a leading , or one that lives among imports →
instrument_module(weather, include=["_geocode"])
.
Functions you already decorated with
are detected and
not
double-wrapped.
Enumerate the agent's tool module(s) and pass each through
one call — this is what makes "all tools captured"
structural rather than a per-function judgment you can under-apply.
When tools are spread across many files, or you want per-function control — decorate each
Add
to each function the agent uses as a tool. One line each;
captures args →
, return →
, plus duration / status /
error, as an
step under the active
. Works on sync
and async functions; safe to leave in place before
. Decorate
every
tool — a missed one is silently absent from the trace (exactly what
prevents).
python
import agenthog
@agenthog.tool
def get_weather(city: str) -> dict | None:
...
@agenthog.tool(name="geocode") # custom step name
def _geocode(city: str) -> tuple[float, float] | None:
...
TypeScript has no decorator or yet (JS SDK is
pre-
; bulk capture, the decorator, raw-HTTP capture, and
auto-flush-on-exit are Python-only for now). For Node/TS agents, instrument
tools with the explicit
path in the Alternative section below,
and be aware the rest of the auto-capture story is thinner than Python until
the JS SDK catches up.
Identify the tool functions — typically the helpers the entrypoint calls
before/around the LLM call (data fetches, lookups, actions). Either path should
produce a multi-step trace like
task_run → get_weather → geocode → get_air_quality → llm_call
.
Alternative — log explicitly (older SDKs, or a raw tool-calling loop)
If you can't decorate (e.g. tools dispatched dynamically in a model
tool-calling loop, or the TS SDK which has neither
nor
), log each call:
python
result = run_tool(name, **args)
agenthog.get_default_client().log_tool_call(name=name, input=args, output=result)
ts
const result = await runTool(name, args);
await getDefaultClient()!.logToolCall({ name, input: args, output: result });
/
take
plus optional
,
,
,
,
. Skip tool instrumentation only when every tool
already runs through an auto-instrumented framework.
6. Document the config (placeholders only — never the real key)
Add three
placeholder lines to
(or create it). This file is
committed, so it must never contain a real key:
AGENTOS_API_KEY=agops_replace_me
AGENTOS_WORKSPACE_ID=ws_replace_me
AGENTOS_ENDPOINT=https://api.theagentos.space
For the real values:
- Python (used in Step 2): the gitignored is already
written and ignored — nothing more to do here except the committed
placeholders above (for teammates).
- Otherwise: tell the user to set the real values themselves — in their
shell environment, secret manager, or a gitignored . Do not write
the plaintext key to any file on their behalf. If they use a , confirm
it's listed in (add it if missing) so the secret can't be
committed.
Make the actually load (do not skip)
A
file does
not populate
/
on its own.
The SDK reads the keys from the environment at runtime (Step 4), so if the app
doesn't load
the user will set everything correctly and still see
no
trace with an empty/missing key. Wire the load step to match the runtime:
| Runtime | How reaches the environment |
|---|
| Next.js | Automatic — Next loads / . Nothing to add. |
| Node (non-Next) | Run with (Node 20+), or add as the first import. |
| Python — FastAPI / Flask / script | Add from dotenv import load_dotenv; load_dotenv()
before (add the dep), or use , or run via . |
| Any runtime | Or just export in the shell before launching: export AGENTOS_API_KEY=… AGENTOS_WORKSPACE_ID=…
. |
Pick the one matching the project. If you add
, place it at the
top of the entrypoint, before
runs. Confirm the var is actually populated
(Step 7's "Confirm
is loaded" check) before declaring done.
7. Verify (hard gate — do not skip, do not assume)
This step is mandatory and blocking. Setup is
not done until you have
seen a real trace from a real run. Wiring
+
does not by
itself produce a trace: if no LLM/tool call is captured, the trace is an empty
and nothing useful appears.
Never report success because the code
"looks wired up" — only because you observed the trace.
Check the trace is complete, not just present. It should contain the LLM
call
and a
step for each tool the agent used (Step 5b).
Count them: the number of distinct
steps must equal the number
of tools instrumented — the length of the list
returned, or
the number of functions you decorated with
. Fewer means a tool
was missed (or an execution path didn't run it); a trace with only the
and no tool steps means the tools weren't instrumented at all — go
back to Step 5b. Do not pass this gate on a count mismatch. The goal is the full
execution tree, the way Arize/LangSmith show it.
Run the app. Exercise one request that hits the wrapped handler. Then
verify via one of these paths, in order of simplicity:
Path A — Dashboard (always works)
Open in a browser:
https://app.theagentos.space/traces
Sign in if needed. A trace from the request you just exercised should
appear within a few seconds. Use this path by default.
Path B — CLI verifier (offline, cryptographic)
The
CLI ships as an installed entrypoint, NOT as
. There is no
. Invoke the entrypoint directly:
bash
# If agenthog[audit] is installed in the active env:
agenthog audit verify --workspace-id $AGENTOS_WORKSPACE_ID
# Or, ephemerally without modifying the project's deps:
uvx --with 'agenthog[audit]' agenthog audit verify --workspace-id $AGENTOS_WORKSPACE_ID
# Or, with uv run from outside any project (mirrors `uv run --no-project`):
uv run --no-project --with 'agenthog[audit]' agenthog audit verify \
--workspace-id $AGENTOS_WORKSPACE_ID
Do not run
python -m agenthog audit verify
— that fails with
'agenthog' is a package and cannot be directly executed
. The CLI is
invoked via the
console-script entrypoint, not the module
runner.
Note:
requires the
extra (pulls in
for Ed25519 verification). If you only installed
for tracing, add
for verification or use
Path A.
If no trace appears after 30 seconds
- Check the app's stdout for warning lines.
- Confirm is loaded (not empty, not the placeholder).
- Confirm the endpoint is reachable:
curl -I $AGENTOS_ENDPOINT
.
- Confirm the app actually called before the handler runs
(sometimes is wired after the handler import — fix the import
order so runs first at module load).
- Trace appears but is empty (no LLM step)? The model call wasn't
captured. covers the vendor SDKs and — on
agenthog >= 0.6.0 — raw-HTTP OpenAI-compatible calls.
A custom/non-OpenAI-shaped endpoint won't be auto-traced: log it with
agenthog.emit("agent.llm_call", {...})
. (On older SDKs, raw /
calls are never captured — upgrade to >= 0.6.0 or switch to the
OpenAI SDK with a custom .)
- Short-lived script and nothing arrives? On agenthog >= 0.6.0 the buffer
flushes on exit automatically; on older versions add
before the process exits, or upgrade.
8. Report back
Once a trace is verified visible, tell the user:
- What you installed (which extras)
- Where you put the call (file + line)
- Where you wrapped the loop (file + line)
- The trace_id of your verification trace (so they can open it directly)
Rules
- Never throw. The user's agent must keep running even if AgentHog is
misconfigured. The SDK guarantees its own calls don't raise; your wiring
should match that contract.
- Credentials stay in the environment. The API key is read from an env var
at runtime. Never hardcode it, never print or echo it, never write the
plaintext value to a file, and never scan the filesystem for it. Wire the
code to the env var and let the user place the secret (Step 2).
- Don't auto-instrument secrets. If the user has prompts containing PII
or API keys, mention the privacy controls () in
the report — don't change the default silently.
- Don't refactor unrelated code. Only touch what's necessary to wire
tracing. If the user wants broader observability work, let them ask
explicitly.
- Use uv if it's already in the project. Otherwise use whatever package
manager the project already uses. Don't introduce a new one.