🚀 Your First Decision Graph
A hands-on tutorial with the Decision Trace SDK
Modern systems don’t just produce outputs — they make decisions.
Refund approvals. Risk scoring. Feature flags. AI tool selection.
Behind every outcome is a chain of reasoning.
Most systems throw that reasoning away.
The Decision Trace SDK is built around a simple idea:
Decisions should be observable, inspectable, and composable.
In this tutorial, you’ll build your first decision graph — not just log events, but model real reasoning as a structured trace.
By the end, you’ll understand:
- how to represent decisions as graphs
- how to link decisions together
- how to capture evidence and outcomes
- how to inspect and debug decision flows
No prior knowledge of the SDK required.
The problem: a simple refund system
Let’s start with a realistic scenario.
A refund pipeline has 3 steps:
- classify → is this refund eligible?
- approve → should we approve it?
- execute → actually process the refund
Most systems log:
That’s useless when debugging.
What we want is:
- why was it eligible?
- what evidence was used?
- which policy approved it?
- what decision led to execution?
We’re going to model this as a decision graph.
Each step is a decision node.
Each node records:
- evidence
- reasoning
- outcome
- relationships to other decisions
That structure is the core of the SDK.
Step 1 — Install and setup
Install the SDK:
Start a local collector:
By default this runs at:
Now we’re ready to emit decisions.
Step 2 — Writing your first decision
Let’s model the classification step.
from decision_trace import decision
from decision_trace.exporters.http import HttpExporter
exporter = HttpExporter("http://127.0.0.1:8711")
with decision(
decision_type="refund.classify.v1",
actor={"type": "agent", "id": "classifier"},
tenant_id="demo",
environment="dev",
exporter=exporter,
validate=True,
) as classify:
classify.evidence("order_value", 120)
classify.evidence("days_since_purchase", 3)
classify.outcome("eligible")
Let’s break this down.
decision_type
This is your schema contract.
It defines what kind of decision this is.
Versioning is built in — you can evolve logic safely over time.
evidence
Evidence is structured reasoning input.
Not logs. Not strings.
Data that explains why the decision happened.
Think:
inputs to reasoning
outcome
This is the decision result.
Every decision ends with an explicit outcome.
No ambiguity. No guessing later.
Step 3 — Linking decisions into a graph
Now we extend the pipeline.
We want approval to depend on classification.
The SDK supports this via:
- trace_id → ties decisions into one session
- parent_decision_id → links nodes
with decision(
decision_type="refund.approve.v1",
actor={"type": "agent", "id": "approver"},
tenant_id="demo",
environment="dev",
exporter=exporter,
trace_id=classify._trace_id,
parent_decision_id=classify._decision_id,
validate=True,
) as approve:
approve.evidence("policy_version", "2026-01")
approve.outcome("approved")
This creates a parent-child relationship:
Now we add execution:
with decision(
decision_type="refund.execute.v1",
actor={"type": "system", "id": "executor"},
tenant_id="demo",
environment="dev",
exporter=exporter,
trace_id=classify._trace_id,
parent_decision_id=approve._decision_id,
validate=True,
) as execute:
execute.outcome("refund_sent")
We now have a full decision graph.
Not a log stream.
A reasoning structure.
Step 4 — Inspecting the trace
Run:
You’ll see a tree:
Each node contains:
- evidence
- timestamps
- actor metadata
- versioned schema
- parent/child links
This is your reasoning audit trail.
Step 5 — Why this is powerful
Once you have decision graphs, you unlock:
Debugging
Why was a refund approved?
→ inspect evidence chain
Analytics
Which policy versions approve more refunds?
→ aggregate decision outcomes
AI explainability
Why did an agent choose this action?
→ decision tree replay
Governance
What reasoning led to this financial action?
→ audit-ready decision lineage
Step 6 — Common design patterns
Here are 3 patterns you’ll use often.
Pattern 1: Policy gate
with decision("policy.check.v1", exporter=exporter) as gate:
gate.evidence("user_age", 17)
gate.outcome("denied")
Use for rule enforcement.
Pattern 2: Human approval
with decision("human.review.v1", exporter=exporter) as review:
review.evidence("ticket_id", "A123")
review.outcome("approved")
Use for manual interventions.
Pattern 3: AI tool chain
with decision("ai.plan.v1", exporter=exporter) as plan:
plan.outcome("use_refund_agent")
Use for LLM orchestration or agent routing.
Step 7 — Production thinking
The SDK is designed for real systems.
When moving beyond local experiments:
- enable schema contracts
- redact sensitive evidence
- isolate decision failures from business logic
- deploy collectors behind auth
- version decision types intentionally
Treat decisions like APIs.
They evolve. They deserve contracts.
Mental model recap
You are not logging.
You are modeling reasoning.
Each decision is:
- structured
- versioned
- linked
- inspectable
- composable
Over time, your system becomes a graph of explainable actions.
That graph is more valuable than raw logs.
It’s institutional memory.
Where to go next
Explore:
- decision contracts
- graph analytics
- replay tooling
- governance workflows
- AI decision explainability
The SDK is a foundation.
Decision graphs are the architecture.