피드백 수집하기
피드백 수집하기
실제 사용자 피드백을 여러분의 평가 파이프라인에 통합해요.
출처: 문서
본문
개요
Confident AI는 여러분의 LLM 앱과 상호작용하는 최종 사용자로부터 피드백을 수집할 수 있게 해요. 챗봇 답변 뒤의 엄지 위/아래, 지원 대화 끝의 스타 평점, "이게 도움이 됐나요?" 프롬프트 — 이 모든 것이 여러분의 AI가 프로덕션에서 실제로 어떻게 수행되는지에 대한 신호이고, 무언가 회귀했다는 가장 이른 경고인 경우가 많아요. 최종 사용자 피드백은 다음에 남길 수 있어요:
- Traces
- Spans, 그리고
- Threads
사용자 피드백의 어노테이션을 보내면, 그것을 데이터셋에 통합할 기회를 얻어요. 그래서 사용자가 지적한 대화가 다음에 평가할 테스트 케이스가 돼요.
사용자 피드백은 Confident API로, 또는 python이나 typescript를 쓰는 사람은 DeepEval로 수집할 수 있어요. 여러분 앱의 트레이싱(
confident-trace)은 피드백이 어느 트레이스, 스팬, 스레드에 속하는지 식별하는 데만 관여해요.
최종 사용자에게 피드백을 받는 대신 앱 내에서 자체 팀이 트레이스를 어노테이션하게 하려면 annotation queues를 보세요.
어떻게 동작하나요
피드백을 수집하려면 다음이 필요해요:
- 사용자가 평가(엄지 위/아래 또는 5성 시스템), 선택적으로 예상 결과/출력, 설명을 입력할 커스텀 UI를 설정
- 피드백을 남길 트레이스 UUID, 스팬 UUID, 또는 스레드 ID를 수집
- Confident API를 통해 피드백을 Confident AI로 보내기
스레드 ID는 LLM 트레이싱 중에 여러분이 제공하는 것이므로(모르겠다면 여기 클릭), 트레이스·스팬보다 스레드에 피드백 수집을 설정하는 편이 일반적으로 더 쉬워요.
채팅 제품을 만들고 있다면 multi-turn 피드백부터 시작하세요. ID 룩업이 전혀 필요 없어요 — 스레드 ID를 여러분이 설정하니까 이미 알고 있거든요.
Single-Turn 피드백 수집하기
OpenTelemetry 식별자 얻기
애플리케이션 스팬이 활성인 동안 트레이스와 스팬 ID를 읽고, 응답과 함께 저장해서 피드백 UI가 나중에 올바른 요청을 참조하게 해요.
Python
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()
Typescript
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();
}
Node preload(node --import tsx --import confident-trace/register src/index.ts)로 엔트리 포인트를 실행하는 것을 기억하세요.
ID를 어딘가 저장할 방법을 찾아야 해요 — 예를 들어 데이터베이스의 메시지 행의 컬럼 — 사용자가 피드백 버튼을 클릭할 때 나중에 쓰려고요.
트레이스/스팬 어노테이션 보내기
별도 워크플로에서 — 보통 엄지 위/아래 버튼 뒤의 핸들러 — 수집한 OpenTelemetry ID로 DeepEval을 사용해 피드백을 보내요. 어노테이션 API는 여전히 이 필드들을 trace_uuid와 span_uuid라고 부릅니다.
Python
from deepeval.annotation import send_annotation
send_annotation(
trace_uuid=TRACE_ID,
rating=1,
# span_uuid=SPAN_ID, # you can only set trace_uuid or span_uuid
)
from deepeval.annotation.api import AnnotationType
from deepeval.annotation import send_annotation
send_annotation(
trace_uuid=TRACE_ID,
type=AnnotationType.FIVE_STAR_RATING,
rating=5
# span_uuid=SPAN_ID, # you can only set trace_uuid or span_uuid
)
Typescript
import { sendAnnotation } from "deepeval/annotation";
sendAnnotation({
traceUuid: TRACE_ID,
rating: 1,
// spanUuid: SPAN_ID, // you can only set traceUuid or spanUuid
});
import { sendAnnotation, AnnotationType } from "deepeval/annotation";
sendAnnotation({
traceUuid: TRACE_ID,
type: AnnotationType.FIVE_STAR_RATING,
rating: 5,
// spanUuid: SPAN_ID, // you can only set traceUuid or spanUuid
});
엄지 위/아래 평점 또는 5성 평점 중 하나를 보낼 수 있어요.
Multi-Turn 피드백 수집하기
스레드 ID 설정하기
스레드 ID를 정의하고 추적되는 LLM 앱이 모든 관련 트레이스를 이 스레드에 연관시키도록 구성해요. 트레이스에 애플리케이션 스팬 안에서 설정해요 — update_trace / updateTrace가 현재 트레이스의 가장 바깥쪽 스팬에 쓰므로 요청 어디에서든 동작해요.
Python
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()
Typescript
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();
}
스레드 ID는 사용자 정의이므로, 대화를 시작할 때 하나만 설정하고 호출 간에 재사용하면 돼요. 장기 실행 서버에서는 시작 시
init()을 한 번, 종료 시shutdown()을 한 번 호출해요 — 위 예시는 단기 스크립트를 보여줘요. I/O 관례와turn()헬퍼는 threads를 보세요.
스레드 어노테이션 보내기
정의한 스레드 ID로 스레드 수준 피드백을 Confident API에 게시해요. 스레드 ID는 여러분 것이므로 룩업할 것이 없어요 — 유일하게 기억할 점은 그 스레드의 트레이스가 먼저 수집되어야 한다는 것인데, 보통 응답을 보낸 후 몇 초 기다리는 것을 의미해요.
Python
from deepeval.annotation import send_annotation
send_annotation(
thread_id=THREAD_ID,
rating=1,
)
from deepeval.annotation.api import AnnotationType
from deepeval.annotation import send_annotation
send_annotation(
thread_id=THREAD_ID,
type=AnnotationType.FIVE_STAR_RATING,
rating=5
)
Typescript
import { sendAnnotation } from "deepeval/annotation";
sendAnnotation({
threadId: THREAD_ID,
rating: 1,
});
import { sendAnnotation, AnnotationType } from "deepeval/annotation";
sendAnnotation({
threadId: THREAD_ID,
type: AnnotationType.FIVE_STAR_RATING,
rating: 5,
});
single-turn 피드백과 마찬가지로 엄지 위/아래 평점 또는 5성 평점 중 하나를 보낼 수 있어요.
다음 단계
피드백이 흘러들기 시작하면 사용해 보세요:
Annotation Queues
플랫폼 안에서 구조화된 검토용으로 트레이스, 스팬, 스레드를 팀에 라우팅해요.
Eval Alignment
인간 피드백으로 메트릭이 실제 사용자와 얼마나 잘 맞는지 확인하고 조정해요.