Diagnosis-Ready Tracing
A trace can be valid OpenTelemetry and still be too weak for diagnosis. Provon needs enough structured evidence to reconstruct the goal, execution path, failure, recovery, and term
On this page Browse sections
Use this guide after sending your first trace. For boundaries, see Trace model and instrumentation. For the field-level contract, use the Tracing attribute reference.
Start With One Representative Run#
Instrument one real workflow before broadening coverage:
- Choose a run with a clear user goal, at least one model call, and one tool or retrieval step.
- Capture the complete run, including a failure or negative result when possible.
- Open the trace in the Workbench and compare it with the application's execution.
- Fix missing boundaries, ownership, inputs, results, and errors.
- Confirm that the conversation view tells the same story across turns.
- Run the relevant diagnostic Rule and inspect the evidence it can support.
Volume does not compensate for weak structure. One complete trace is a better integration test than thousands of model-call records without tool results or a terminal answer.
Define Stable Boundaries#
Use one trace for one bounded unit of work:
- one agent run;
- one conversation turn;
- one request from user input to terminal answer;
- one workflow execution with a defined end.
Use a new trace for each later turn or independent run. Keep gen_ai.conversation.id stable when
those traces belong to the same user goal or interaction.
Do not keep an OTel trace open for an unbounded conversation. Long-lived traces are difficult to flush, sample, inspect, and retry, and they obscure which turn introduced a failure.
Make The Root Span Reviewable#
The root span should answer these questions at a glance:
- What goal or constraint entered the system?
- What terminal answer or action left the system?
- Did the bounded run complete successfully?
Use gen_ai.input.messages and gen_ai.output.messages for readable user and assistant content.
Use child spans for model, retrieval, tool, delegation, and verification work.
Do not use an HTTP server span as the only root-level explanation when the important unit of work is an agent run. Infrastructure spans can remain in the trace, but the agent operation must be identifiable.
Treat Names As A Query Contract#
Use stable, verb-first names:
invoke-support-agent
classify-intent
retrieve-policy
execute-refund-check
generate-answer
verify-answerKeep request IDs, users, models, retry numbers, and timestamps out of span names. Put dynamic values in attributes instead.
| Avoid | Prefer |
|---|---|
request-01J7... |
invoke-support-agent |
gpt-5-mini-call |
generate-answer plus gen_ai.request.model |
search-retry-2 |
search-knowledge-base plus retry.number=2 |
tool-user-123-order-456 |
execute-order-lookup plus bounded attributes |
Stable names keep searches, dashboards, and diagnostic targeting meaningful across releases.
Capture Execution Facts, Not Conclusions#
Record objective evidence on the span where it happened:
- the model and provider used;
- the messages or input that drove the operation;
- tool arguments and the result actually returned;
- retrieved documents or bounded evidence references;
- token, cost, and latency data when available;
- span status and
error.type; - retry and recovery attempts as separate spans;
- the final answer or explicit non-completion.
Do not write derived labels such as hallucinated=true or root_cause=bad_prompt into execution
spans as if they were observed facts. Provon Rules derive diagnostic conclusions from the evidence
and publish them as Findings.
Preserve Causality#
Parent-child structure should match execution ownership:
invoke-agent
|-- retrieve-context
|-- generate-plan
|-- execute-tool attempt=1 status=ERROR
|-- execute-tool attempt=2 status=OK
`-- verify-answer- Propagate W3C trace context across service and queue boundaries.
- Keep each failed attempt; do not replace it with the successful retry.
- Use the same
gen_ai.tool.call.idto correlate a requested tool call with its result. - Set the failed span status to
ERRORand include a stableerror.type. - Keep verification after the action it verifies.
Timestamps help order events, but they do not replace parent span IDs, tool call IDs, and explicit participant identity.
Attribute Every Participant#
For multi-agent systems, set gen_ai.agent.id on every agent-owned span. Add
gen_ai.agent.name and gen_ai.agent.version when available.
Do not flatten several agents into one anonymous orchestration span. A diagnosis must be able to distinguish the participant that delegated work, the participant that acted, and the participant that produced the terminal answer.
See Multi-agent and distributed tracing for topology and handoff patterns.
Keep Useful Evidence, Remove Noise#
Keep a span when it changes how a reviewer understands the run:
- agent and workflow boundaries;
- model inference;
- retrieval;
- tool execution;
- external side effects;
- validation and verification;
- retry, fallback, and recovery;
- errors and policy decisions.
Consider filtering repetitive framework, database, and HTTP internals when they do not explain agent behavior. Preserve them when they establish latency, ownership, or a failure boundary.
Do not duplicate the same prompt or response across every child span. Put evidence on the operation that owns it and use the root span for the bounded run's user-facing input and output.
Bound Structured Content#
Prompts, tool results, retrieved documents, and raw payloads can be both large and sensitive:
- redact credentials and personal data before export;
- truncate or summarize unbounded tool output;
- retain stable references when full documents live elsewhere;
- avoid inline binary content;
- choose payload retention before production traffic;
- test whether the retained projection still supports the enabled Rules.
Redaction after ingestion cannot prevent sensitive data from entering the telemetry pipeline.
Quality Levels#
| Level | Evidence | Result |
|---|---|---|
| Visible | IDs, timestamps, names, parent relationships | Trace tree and timing |
| Searchable | Service, operation, conversation, user, agent, model, status | Filters, summaries, and conversation views |
| Diagnosable | Goal, ordered messages, tool inputs/results, failures, retries, final state | Supported diagnostic Rules and Findings |
Aim for diagnosable traces on workflows that feed Findings. Infrastructure-only telemetry can remain visible or searchable without carrying private model content.
Common Failure Patterns#
| Pattern | Why it fails |
|---|---|
| One span per model request only | Tool, retrieval, orchestration, and verification are absent |
| One trace for an entire long-lived chat | Boundaries, flushing, and per-turn causality become unclear |
| New conversation ID on every turn | Cross-turn evidence cannot be reconstructed |
| One participant identity for all agents | Ownership and handoff failures become ambiguous |
| Successful retry overwrites the failed span | Recovery evidence disappears |
| Error text without error span status | Error filters and deterministic signals can miss it |
| Request-specific span names | Searches and aggregates fragment |
| Full raw payload copied to every span | Cost, noise, and privacy exposure increase |
| Gateway and application both record one call | Duplicate evidence can distort counts and diagnosis |
Review Checklist#
- The trace covers one bounded run or turn.
- The root input states the real user goal.
- The root output records the terminal answer or explicit failure.
- Span names are stable and low-cardinality.
- Model, retrieval, tool, and verification work appear in causal order.
- Every agent-owned span has an explicit participant ID.
- Tool calls, results, failures, and retries remain distinct.
- Errors use span status
ERRORanderror.type. - Related traces share one
gen_ai.conversation.id. - Sensitive and unbounded content is removed before export.
- The Workbench trace and conversation views match the application's execution.
- A known failure can support a Finding without relying on unstated assumptions.