Custom Python Agent
This example walks through a complete Kyvvu integration using the Python SDK decorator pattern. The agent is a customer support ticket router: it fetches a ticket, classifies it with an LLM, routes it down one of three specialized paths, generates a response, requests human approval, and closes the task. Seven steps, real branching logic, all logged.
The full source is in examples/custom-agent/ in the platform repo.
What this example covers
Initializing the
Kyvvuclient and registering an agentDecorating methods with
@kv.log_step()to log each stepHandling branching workflows (different paths, same audit trail)
Graceful handling of policy violations at registration time
Running the agent end-to-end
Setup
from kyvvu import Kyvvu
from kyvvu.exceptions import KyvvuPolicyViolationError
import os
kv = Kyvvu(
api_key=os.getenv("KV_DEMO_API_KEY"),
api_url=os.getenv("KV_DEMO_API_URL", "http://localhost:8000"),
)Kyvvu is the entry point for everything. One instance per agent process is the normal pattern. It holds the API connection, tracks the current task, and provides the @kv.log_step decorator.
Agent registration
Agents register themselves at startup by calling kv.register_agent(). This creates (or updates) the agent record in Kyvvu and triggers any agent_registration policies immediately.
A few things to note:
agent_keyis the stable identifier you choose. Kyvvu hashes it together with your API key to produce a uniqueagent_id. Re-registering with the sameagent_keyupdates the existing record rather than creating a duplicate.risk_classificationacceptsminimal,limited,high, orunacceptable. Policies can be scoped to specific risk levels — ahigh-risk agent may face stricter checks than alimited-risk one.Catching
KyvvuPolicyViolationErroris optional but recommended in production. If you don't catch it, the exception propagates and your agent won't start.
Logging steps with @kv.log_step
@kv.log_stepEvery meaningful operation in the agent is decorated with @kv.log_step(node_type). The decorator intercepts the function call, wraps it in a logged step, and forwards the return value unchanged. Your agent code does not change.
The decorator records the step's node_type, captures the function's inputs and outputs, timestamps the execution, and chains the log entry's hash to the previous step's hash. None of this requires any change to your function body.
Branching workflows
Real agents don't follow a straight line. This agent routes to one of three specialized paths based on the LLM classification — but the audit trail captures all of it regardless of which branch runs.
Each @kv.log_step call is independent. The SDK tracks which task is running and automatically assigns the correct step number — you don't manage sequencing yourself. Steps that don't run (e.g., create_jira_ticket on a billing ticket) simply aren't logged, which is exactly what you want in the audit trail.
Running the workflow
With the class defined, the execution loop is straightforward:
After this runs, the Kyvvu dashboard will show a complete task with all logged steps in order, their inputs and outputs, and the validated hash chain proving the log hasn't been tampered with.
What the dashboard shows
Once the agent has run, navigate to Logs in the dashboard and find the task. You'll see:
Each step listed in sequence with its
node_type, timestamp, and durationInput and output data captured for each step
Hash chain validation status — every step shows ✓ if the chain is intact
Any incidents generated by
step_executionortask_executionpolicies
If you have a policy configured (e.g., "every LLM_CALL must be preceded by a PII_CHECK"), violations appear as incidents linked directly to the offending step.
Key patterns to take away
One Kyvvu instance, module-level. Initialize kv once at the top of your agent module. All decorated methods share the same client and task context.
Register at startup, not per-request. kv.register_agent() is called once when the agent process starts. It's idempotent — safe to call on every restart.
END_NODE closes the task. The SDK tracks an open task from START_NODE until it sees END_NODE. Always end your workflow with an END_NODE-decorated method, even if it just clears state. A task without an END_NODE is flagged as unfinished.
Branching is fine. You don't need to tell Kyvvu about your routing logic. Decorate every meaningful operation and let the audit trail reflect what actually ran.
The decorator is transparent. @kv.log_step does not modify return values and adds negligible latency. What happens if a log call fails (network error, API down) is controlled by the on_error setting on the Kyvvu instance — see SDK Reference for details.
Next steps
LangChain agent: If you're using LangChain, the callback handler approach is even less intrusive — see LangChain Agent
Policies: Set up a
step_executionpolicy to enforce thatHUMAN_APPROVALalways precedesTOOL_CALLin your workflow — any task that skips the approval step will generate an incident automaticallyAudit reports: After a few runs, generate a PDF report from the dashboard under Reports → Generate
Last updated
