Collect Feedback
Incorperate real user feedback into your evaluation pipeline
Overview
Confident AI allows you to collect feedback from end users that are interacting with your LLM app. A thumbs up/down after a chatbot reply, a star rating at the end of a support conversation, a "was this helpful?" prompt — all of these are signals about how your AI is actually performing in production, and they're often the earliest warning you'll get that something regressed. End user feedback can be left on:
- Traces
- Spans, and
- Threads
When you send an annotation of a user feedback, you'll get the opportunity to incorporate them into a dataset, so the conversations your users flagged become the test cases you evaluate against next.
How It Works
To collect feedback, you need to:
- Setup a custom UI for users to enter their rating (thumbs up/down or 5 star system), and optionally expected outcome/output, and explanation
- Either collect the trace UUID, span UUID, or thread ID you'd like to leave feedback for
- Send the feedback to Confident AI via the Confident AI SDK or the Confident API
Since the thread ID is something you provide (click here if unsure) during LLM tracing, it is generally easier to setup feedback collection on threads than on traces and spans.
Collect Single-Turn Feedback
Get the OpenTelemetry identifiers
Read the trace and span IDs while your application span is active, and store them alongside the response so your feedback UI can refer back to the correct request later.
main.py from openai import OpenAI from confident_trace import init, span, shutdown from opentelemetry import trace init() client = OpenAI() def llm_app(query: str): with span("llm_app", type="agent"): res = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": query}] ).choices[0].message.content current_span = trace.get_current_span() context = current_span.get_span_context() trace_id = f"{context.trace_id:032x}" if context.is_valid else None span_id = f"{context.span_id:016x}" if context.is_valid else None return res, trace_id, span_id try: output, TRACE_ID, SPAN_ID = llm_app("Write me a poem.") finally: shutdown()src/index.ts import OpenAI from "openai"; import { init, withSpan } from "confident-trace"; import { trace } from "@opentelemetry/api"; const runtime = init(); const openai = new OpenAI(); const llmApp = async (query: string) => { return withSpan({ name: "llm_app", type: "agent" }, async () => { const res = await openai.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: query }], }); const { traceId, spanId } = trace.getActiveSpan()!.spanContext(); return { output: res.choices[0].message.content, traceId, spanId }; }); }; try { const { output, traceId: TRACE_ID, spanId: SPAN_ID } = await llmApp("Write me a poem."); } finally { await runtime.shutdown(); }Remember to launch your entry point with the Node preload (
node --import tsx --import confident-trace/register src/index.ts).Send annotation for trace/span
In a separate workflow — typically the handler behind your thumbs up/down button — send the feedback with the Confident AI SDK using the OpenTelemetry IDs you collected. The annotation API still names these fields
trace_uuidandspan_uuid.For a trace:
main.py from confident_ai import ConfidentAI from confident_ai.annotations import TraceAnnotationInput client = ConfidentAI() result = client.annotations.create( annotation=TraceAnnotationInput( trace_uuid="3f9c2a1e-5b7d-4c8e-9f01-2a3b4c5d6e7f", value="<ANNOTATION-VALUE-INPUT>" ), )For a span:
main.py from confident_ai import ConfidentAI from confident_ai.annotations import SpanAnnotationInput client = ConfidentAI() result = client.annotations.create( annotation=SpanAnnotationInput( span_uuid="9d2c6f7e-1a3b-4c5d-8e9f-0a1b2c3d4e5f", value="<ANNOTATION-VALUE-INPUT>" ), )Set
valueto the user's rating. A thumbs rating, the default, takestruefor thumbs up andfalsefor thumbs down. For a 5 star rating, setfield_typetoFIVE_STAR_RATINGandvalueto a whole number from 1 to 5. Theexpected_outputyour user wanted and theirexplanationare optional.
Collect Multi-Turn Feedback
Setup thread ID
Define a thread ID and configure your traced LLM app to associate all related traces to this thread. Set it on the trace inside your application span —
update_trace/updateTracewrites to the outermost span of the current trace, so it works from anywhere in the request.main.py from openai import OpenAI from confident_trace import init, span, update_trace, shutdown init() THREAD_ID = "YOUR-THREAD-ID" client = OpenAI() def llm_app(query: str) -> str: with span("llm_app", type="agent"): response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": query}] ).choices[0].message.content update_trace(thread_id=THREAD_ID, input=query, output=response) return response try: llm_app("Write me a poem.") finally: shutdown()src/index.ts import OpenAI from "openai"; import { init, withSpan, updateTrace } from "confident-trace"; const runtime = init(); const THREAD_ID = "YOUR-THREAD-ID"; const openai = new OpenAI(); const llmApp = async (query: string) => { return withSpan({ name: "llm_app", type: "agent" }, async () => { const res = await openai.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: query }], }); const output = res.choices[0].message.content; updateTrace({ threadId: THREAD_ID, input: query, output }); return output; }); }; try { await llmApp("Write me a poem."); } finally { await runtime.shutdown(); }Send annotation for thread
Post the thread-level feedback with the Confident AI SDK using the thread IDs you defined. Because the thread ID is yours, there's nothing to look up — the only thing to keep in mind is that the traces for that thread need to have been ingested first, which usually means waiting a few seconds after the response is sent.
main.py from confident_ai import ConfidentAI from confident_ai.annotations import ThreadAnnotationInput client = ConfidentAI() result = client.annotations.create( annotation=ThreadAnnotationInput( thread_id="thread-42", value="<ANNOTATION-VALUE-INPUT>" ), )As with single-turn feedback,
valueistrueorfalsefor a thumbs rating, or a whole number from 1 to 5 withfield_typeset toFIVE_STAR_RATING. Theexpected_outcomeyour user wanted and theirexplanationare optional.
Next Steps
Once feedback is flowing in, put it to work:
Annotation Queues
Route traces, spans, threads, and test cases to your team for structured review inside the platform.
Eval Alignment
Use human feedback to check and tune how well your metrics agree with real users.
Last updated on