Skip to content

Commit 664b5dd

Browse files
llm-obs: SDK documentation end-user feedback (#39133)
* llm-obs: SDK documentation end-user feedback * llm-obs: use dedicated Feedback data source in Analyze feedback * Apply suggestions from code review Co-authored-by: Eva Parish <eva.parish@datadoghq.com> --------- Co-authored-by: Eva Parish <eva.parish@datadoghq.com>
1 parent 6570cf8 commit 664b5dd

3 files changed

Lines changed: 302 additions & 2 deletions

File tree

hugo/content/en/llm_observability/evaluations/end_user_feedback.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ Use `feedback_join_key` when feedback is not tied to a single span, trace, or se
110110

111111
## Analyze feedback
112112

113-
To create a dashboard widget for feedback, create the widget as you would for an evaluation and add the filter `@event_kind:feedback`. To search and filter spans and traces by feedback in the Trace Explorer, see [Feedback queries][6].
113+
To create a dashboard widget for feedback, create the widget as you would for an evaluation and select the dedicated **Feedback** data source. To search and filter spans and traces by feedback in the Trace Explorer, see [Feedback queries][6].
114114

115-
{{< img src="llm_observability/evaluations/feedback_widget_query.png" alt="The Datadog widget editor configured to count all evaluations filtered by @event_kind:feedback." style="width:100%;" >}}
115+
{{< img src="llm_observability/evaluations/feedback_widget_query.png" alt="The Datadog widget editor with the Feedback data source selected, showing a count of all feedback." style="width:100%;" >}}
116116

117117
## Further Reading
118118

hugo/content/en/llm_observability/instrumentation/sdk.md

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2691,6 +2691,306 @@ public class MyJavaClass {
26912691
{{% /tab %}}
26922692
{{< /tabs >}}
26932693

2694+
### Submitting end-user feedback
2695+
2696+
End-user feedback captures input from the users of your LLM application, such as thumbs-up or thumbs-down ratings, whether a user accepted an agent's change, and free-text comments. Unlike an evaluation, feedback carries the identity of the submitter and can target a span, a trace, a session, or a customer-defined entity. For more information, see [End-User Feedback](/llm_observability/evaluations/end_user_feedback/).
2697+
2698+
{{< tabs >}}
2699+
{{% tab "Python" %}}
2700+
Use `LLMObs.submit_feedback()` to submit end-user feedback associated with a span, trace, session, or customer-defined entity.
2701+
2702+
The `LLMObs.submit_feedback()` method accepts the following arguments:
2703+
2704+
{{% collapse-content title="Arguments" level="h4" expanded=false id="submit-feedback-arguments" %}}
2705+
`label`
2706+
: required - _string_
2707+
<br />The name of the feedback metric. Must not contain a `.`.
2708+
2709+
`metric_type`
2710+
: required - _string_
2711+
<br />The type of the feedback. Must be `categorical`, `score`, `boolean`, `json`, or `text`.
2712+
2713+
`value`
2714+
: required - _string, numeric type, boolean, or dict_
2715+
<br />The value of the feedback. Must be a string (`metric_type==categorical` or `metric_type==text`), integer or float (`metric_type==score`), boolean (`metric_type==boolean`), or dict (`metric_type==json`).
2716+
2717+
`submitter`
2718+
: required - _dictionary_
2719+
<br />A dictionary that identifies who submitted the feedback. Must contain a non-empty `id` (string), and can contain an optional `type` (string), such as `user`.
2720+
2721+
`span`
2722+
: optional - _dictionary_
2723+
<br />A dictionary that identifies the span associated with this feedback. Use [`LLMObs.export_span()`](#exporting-a-span) to generate this dictionary.
2724+
2725+
`span_id`
2726+
: optional - _string_
2727+
<br />The ID of the span associated with this feedback.
2728+
2729+
`trace_id`
2730+
: optional - _string_
2731+
<br />The ID of the trace associated with this feedback.
2732+
2733+
`session_id`
2734+
: optional - _string_
2735+
<br />The ID of the session associated with this feedback.
2736+
2737+
`feedback_join_key`
2738+
: optional - _string_
2739+
<br />A customer-defined key associated with this feedback, such as an incident ID or a ticket ID. To connect the feedback to your spans, first annotate them with a `feedback_join_key` tag holding the same value. See [Enriching spans](#enriching-spans).
2740+
2741+
**Note**: Exactly one of `span`, `span_id`, `trace_id`, `session_id`, or `feedback_join_key` is required. Supplying more than one, or none, raises a `ValueError`.
2742+
2743+
`ml_app`
2744+
: optional - _string_
2745+
<br />The name of the ML application. If not provided, this defaults to the ML application configured for the SDK.
2746+
2747+
`timestamp_ms`
2748+
: optional - _integer_
2749+
<br />The Unix timestamp in milliseconds when the feedback was generated. If not provided, this defaults to the current time.
2750+
2751+
`tags`
2752+
: optional - _dictionary_
2753+
<br />A dictionary of string key-value pairs that users can add as tags regarding the feedback. For more information about tags, see [Getting Started with Tags](/getting_started/tagging/).
2754+
2755+
`assessment`
2756+
: optional - _string_
2757+
<br />An assessment of this feedback. Accepted values are `pass` and `fail`.
2758+
2759+
`reasoning`
2760+
: optional - _string_
2761+
<br />A text explanation of the feedback.
2762+
{{% /collapse-content %}}
2763+
2764+
#### Example
2765+
2766+
{{< code-block lang="python" >}}
2767+
from ddtrace.llmobs import LLMObs
2768+
from ddtrace.llmobs.decorators import llm
2769+
2770+
@llm(model_name="claude", name="invoke_llm", model_provider="anthropic")
2771+
def llm_call():
2772+
completion = ... # user application logic to invoke LLM
2773+
span_context = LLMObs.export_span(span=None)
2774+
2775+
# submitting feedback for a trace
2776+
LLMObs.submit_feedback(
2777+
label="thumbs",
2778+
metric_type="categorical",
2779+
value="down",
2780+
submitter={"id": "user-123", "type": "user"},
2781+
trace_id=span_context["trace_id"],
2782+
assessment="fail",
2783+
)
2784+
2785+
# connecting the span to a customer-defined entity
2786+
LLMObs.annotate(tags={"feedback_join_key": "incident-123"})
2787+
2788+
# submitting feedback for that entity
2789+
LLMObs.submit_feedback(
2790+
label="user_comment",
2791+
metric_type="text",
2792+
value="The investigation missed the customer impact.",
2793+
submitter={"id": "user-123", "type": "user"},
2794+
feedback_join_key="incident-123",
2795+
)
2796+
return completion
2797+
{{< /code-block >}}
2798+
2799+
{{% /tab %}}
2800+
2801+
{{% tab "Node.js" %}}
2802+
Use `llmobs.submitFeedback()` to submit end-user feedback associated with a span, trace, session, or customer-defined entity.
2803+
2804+
The `llmobs.submitFeedback()` method accepts an options object with the following properties:
2805+
2806+
{{% collapse-content title="Arguments" level="h4" expanded=false id="submit-feedback-arguments" %}}
2807+
`label`
2808+
: required - _string_
2809+
<br />The name of the feedback metric. Must not contain a `.`.
2810+
2811+
`metricType`
2812+
: required - _string_
2813+
<br />The type of the feedback. Must be one of `categorical`, `score`, `boolean`, `json`, or `text`.
2814+
2815+
`value`
2816+
: required - _string, number, boolean, or object_
2817+
<br />The value of the feedback. Must be a string (for `categorical` and `text` metric types), number (for `score`), boolean (for `boolean`), or a JSON object (for `json`).
2818+
2819+
`submitter`
2820+
: required - _object_
2821+
<br />An object that identifies who submitted the feedback. Must contain a non-empty `id` (string), and can contain an optional `type` (string), such as `user`.
2822+
2823+
`span`
2824+
: optional - _object_
2825+
<br />The span context of the span to attach the feedback to. This should be the output of [`llmobs.exportSpan()`](#exporting-a-span).
2826+
2827+
`spanId`
2828+
: optional - _string_
2829+
<br />The ID of the span to attach the feedback to.
2830+
2831+
`traceId`
2832+
: optional - _string_
2833+
<br />The ID of the trace to attach the feedback to.
2834+
2835+
`sessionId`
2836+
: optional - _string_
2837+
<br />The ID of the session to attach the feedback to.
2838+
2839+
`feedbackJoinKey`
2840+
: optional - _string_
2841+
<br />A customer-defined key to attach the feedback to, such as an incident ID or a ticket ID. Set the same key on your spans to connect the feedback to them.
2842+
2843+
**Note**: Exactly one of `span`, `spanId`, `traceId`, `sessionId`, or `feedbackJoinKey` is required. Supplying more than one, or none, throws an error.
2844+
2845+
`mlApp`
2846+
: optional - _string_
2847+
<br />The name of the ML application. If not provided, this defaults to the ML application configured for the SDK.
2848+
2849+
`timestampMs`
2850+
: optional - _number_
2851+
<br />The Unix timestamp in milliseconds when the feedback was generated. If not provided, this defaults to the current time.
2852+
2853+
`tags`
2854+
: optional - _object_
2855+
<br />An object of string key-value pairs that users can add as tags regarding the feedback. For more information about tags, see [Getting Started with Tags](/getting_started/tagging/).
2856+
2857+
`assessment`
2858+
: optional - _string_
2859+
<br />An assessment of this feedback. Accepted values are `pass` and `fail`.
2860+
2861+
`reasoning`
2862+
: optional - _string_
2863+
<br />A text explanation of the feedback.
2864+
{{% /collapse-content %}}
2865+
2866+
#### Example
2867+
2868+
{{< code-block lang="javascript" >}}
2869+
function llmCall () {
2870+
const completion = ... // user application logic to invoke LLM
2871+
const spanContext = llmobs.exportSpan()
2872+
2873+
// submitting feedback for a trace
2874+
llmobs.submitFeedback({
2875+
label: 'thumbs',
2876+
metricType: 'boolean',
2877+
value: true,
2878+
submitter: { id: 'user-123', type: 'user' },
2879+
traceId: spanContext.traceId,
2880+
assessment: 'pass'
2881+
})
2882+
2883+
// connecting the span to a customer-defined entity
2884+
llmobs.annotate({
2885+
tags: { feedback_join_key: 'incident-123' }
2886+
})
2887+
2888+
// submitting feedback for that entity
2889+
llmobs.submitFeedback({
2890+
label: 'user_comment',
2891+
metricType: 'text',
2892+
value: 'This answer was helpful.',
2893+
submitter: { id: 'user-123', type: 'user' },
2894+
feedbackJoinKey: 'incident-123'
2895+
})
2896+
return completion
2897+
}
2898+
llmCall = llmobs.wrap({ kind: 'llm', name: 'invokeLLM', modelName: 'claude', modelProvider: 'anthropic' }, llmCall)
2899+
{{< /code-block >}}
2900+
{{% /tab %}}
2901+
2902+
{{% tab "Java" %}}
2903+
Use `LLMObs.submitFeedback()` to submit end-user feedback associated with a span, trace, session, or customer-defined entity. Build the feedback with `LLMObs.Feedback.builder()`.
2904+
2905+
The builder accepts the following methods:
2906+
2907+
{{% collapse-content title="Arguments" level="h4" expanded=false id="submit-feedback-arguments" %}}
2908+
`label(String label)`
2909+
: required
2910+
<br />The name of the feedback metric. Must not contain a `.`.
2911+
2912+
`categoricalValue(String)`, `scoreValue(double)`, `booleanValue(boolean)`, `jsonValue(Map<String, Object>)`, or `textValue(String)`
2913+
: required
2914+
<br />The value of the feedback. Set exactly one of these methods, which also determines the metric type.
2915+
2916+
`submitter(String id, String type)` or `submitter(Submitter submitter)`
2917+
: required
2918+
<br />Identifies who submitted the feedback. The `id` must be a non-empty string. The `type` is an optional qualifier, such as `user`.
2919+
2920+
`span(LLMObsSpan span)`, `spanId(String)`, `traceId(String)`, `sessionId(String)`, or `feedbackJoinKey(String)`
2921+
: required
2922+
<br />The entity to attach the feedback to. Set exactly one of these methods. Use `feedbackJoinKey` for a customer-defined entity, such as an incident ID or a ticket ID, and set the same key on your spans to connect the feedback to them.
2923+
2924+
`mlApp(String mlApp)`
2925+
: optional
2926+
<br />The name of the ML application. If not provided, this defaults to the ML application configured for the tracer.
2927+
2928+
`timestampMs(long timestampMs)`
2929+
: optional
2930+
<br />The Unix timestamp in milliseconds when the feedback was generated. If not provided, this defaults to the current time.
2931+
2932+
`tags(Map<String, Object> tags)` or `tag(String key, Object value)`
2933+
: optional
2934+
<br />Key-value pairs used to tag the feedback. For more information about tags, see [Getting Started with Tags](/getting_started/tagging/).
2935+
2936+
`assessment(Assessment assessment)`
2937+
: optional
2938+
<br />An assessment of this feedback. Accepted values are `LLMObs.Feedback.Assessment.PASS` and `LLMObs.Feedback.Assessment.FAIL`.
2939+
2940+
`reasoning(String reasoning)`
2941+
: optional
2942+
<br />A text explanation of the feedback.
2943+
{{% /collapse-content %}}
2944+
2945+
**Note**: `LLMObs.submitFeedback()` validates the feedback and throws an `IllegalArgumentException` when Agent Observability is enabled and the feedback is invalid, such as when the target, value, or submitter is missing. When Agent Observability is disabled, or the Agent is not attached, the call is a no-op.
2946+
2947+
#### Example
2948+
2949+
{{< code-block lang="java" >}}
2950+
import datadog.trace.api.llmobs.LLMObs;
2951+
2952+
public class MyJavaClass {
2953+
public String invokeChat(String userInput) {
2954+
LLMObsSpan llmSpan = LLMObs.startLLMSpan("my-llm-span-name", "my-llm-model", "my-company", "maybe-ml-app-override", "session-141");
2955+
String chatResponse = "N/A";
2956+
try {
2957+
chatResponse = ... // user application logic to invoke LLM
2958+
} catch (Exception e) {
2959+
llmSpan.addThrowable(e);
2960+
throw new RuntimeException(e);
2961+
} finally {
2962+
// connecting the span to a customer-defined entity
2963+
llmSpan.setTag("feedback_join_key", "incident-123");
2964+
llmSpan.finish();
2965+
2966+
// submitting feedback for a trace
2967+
LLMObs.submitFeedback(
2968+
LLMObs.Feedback.builder()
2969+
.traceId(llmSpan.getTraceId().toString())
2970+
.label("thumbs")
2971+
.booleanValue(true)
2972+
.submitter("user-123", "end_user")
2973+
.assessment(LLMObs.Feedback.Assessment.PASS)
2974+
.reasoning("answered the question")
2975+
.build());
2976+
2977+
// submitting feedback for that entity
2978+
LLMObs.submitFeedback(
2979+
LLMObs.Feedback.builder()
2980+
.feedbackJoinKey("incident-123")
2981+
.label("user_comment")
2982+
.textValue("The answer missed the customer impact.")
2983+
.submitter("user-123", "end_user")
2984+
.assessment(LLMObs.Feedback.Assessment.FAIL)
2985+
.build());
2986+
}
2987+
return chatResponse;
2988+
}
2989+
}
2990+
{{< /code-block >}}
2991+
{{% /tab %}}
2992+
{{< /tabs >}}
2993+
26942994
## Span processing
26952995

26962996
To modify input and output data on spans, you can configure a processor function. The processor function has access to span tags to enable conditional input/output modification. Processor functions can either return the modified span to emit it, or return `None`/`null` to prevent the span from being emitted entirely. This is useful for filtering out spans that contain sensitive data or meet certain criteria.
-105 KB
Loading

0 commit comments

Comments
 (0)