Connect your agent
Put a test run's run ID and case ID on your agent's root span, so Judgment can score the trace.
Judgment scores a case from the trace your agent produces. To know which run and case a trace belongs to, it reads two attributes from the trace’s root span:
| Key | Value |
|---|---|
judgment.test.run_id |
The run ID. |
judgment.test.case_id |
The case ID. |
Judgment reads only these two attributes. Each case corresponds to exactly one trace, so run your agent once per case, with each run producing its own trace. If a case receives more than one trace, Judgment scores the first one to complete and ignores the rest.
Three ways to tag the root span
All three approaches are valid. Choose whichever fits your agent runtime and use case.
1. Set the attributes yourself
This is the simplest instrumentation, and needs no Judgment helper. With plain
OpenTelemetry, call span.set_attribute() / span.setAttribute() on your root
span with the two keys. With judgeval, call Tracer.set_attributes() /
Tracer.setAttributes() while the root span is the current span.
@Tracer.observe(span_type="agent")
def support_agent(case_input, run_id, case_id):
Tracer.set_attributes({
"judgment.test.run_id": run_id,
"judgment.test.case_id": case_id,
})
...const supportAgent = Tracer.observe(
async (caseInput: unknown, runId: string, caseId: string) => {
Tracer.setAttributes({
"judgment.test.run_id": runId,
"judgment.test.case_id": caseId,
});
// ...
},
);2. Wrap the agent call (helper)
Instead of setting the attributes directly, you can wrap the call with
Tracer.test_case() (Python) or Tracer.testCase() (TypeScript). Your agent’s
entry point does not change.
from judgeval import Tracer
with Tracer.test_case(run_id, case_id):
support_agent(case_input)import { Tracer } from "judgeval";
await Tracer.testCase(runId, caseId, () => supportAgent(caseInput));3. Send them in an HTTP header
When you call an agent that is already deployed, send the IDs as W3C baggage on the request:
baggage: judgment.test.run_id=<run_id>,judgment.test.case_id=<case_id>
Each case in a started run includes this header in its headers field, so you
can pass it through unchanged:
for case in run["cases"]:
httpx.post(agent_url, json=case["input"], headers=case["headers"])
Your server restores the baggage at its entry point, before any span starts.
Wrap the handler in judgeval’s Tracer.continue_trace() /
Tracer.continueTrace(): judgeval copies Judgment baggage onto every span it
starts, the root included. This is the same pattern as
distributed tracing. With another
OpenTelemetry SDK, extract the baggage and set the two attributes on your root
span yourself.
with Tracer.continue_trace(request.headers):
result = support_agent(payload)await Tracer.continueTrace(req.headers, () => supportAgent(payload));Common issues
- Send only
baggage. Do not send atraceparentfrom a span that Judgment never receives, such as one exported to another vendor. The agent’s top-level span would then reference a parent that never arrives, so the case would have no root span and would never be scored. - Use the test suite’s project. The trace must be sent to the same Judgment project as the test suite.