The sendFeedback() API allows you to provide feedback on LLM completions, enabling continuous prompt improvement and optimization.
Basic usage
Send feedback on a completion using its span ID:
import { sendFeedback, getCurrentSpan } from 'zeroeval';
// After getting a response from the LLM
const spanId = getCurrentSpan()?.spanId;
if (spanId) {
await sendFeedback({
promptSlug: 'customer-support',
completionId: spanId,
thumbsUp: true,
reason: 'Response was helpful and accurate'
});
}
The completionId is the UUID of the span that generated the LLM completion. Use getCurrentSpan() to get the active span’s ID.
Positive feedback
Indicate successful completions:
await sendFeedback({
promptSlug: 'code-reviewer',
completionId: spanId,
thumbsUp: true,
reason: 'Identified all the issues correctly'
});
Positive feedback helps the optimization system understand what works well and reinforces effective prompt patterns.
Negative feedback
Report problematic completions:
await sendFeedback({
promptSlug: 'summarizer',
completionId: spanId,
thumbsUp: false,
reason: 'Summary was too verbose',
expectedOutput: 'A concise 2-3 sentence summary focusing on key points'
});
Include expectedOutput to guide the optimization process toward desired outputs.
Add custom metadata for richer context:
await sendFeedback({
promptSlug: 'translator',
completionId: spanId,
thumbsUp: false,
reason: 'Translation was too literal',
metadata: {
sourceLanguage: 'en',
targetLanguage: 'es',
domain: 'technical',
userExpertise: 'beginner'
}
});
Metadata helps identify patterns in feedback and enables more targeted optimization.
Judge automation feedback
Provide feedback on automated judge evaluations:
await sendFeedback({
promptSlug: 'customer-support',
completionId: spanId,
thumbsUp: false,
judgeId: 'judge-automation-uuid',
reason: 'Judge incorrectly marked this as unhelpful'
});
This helps improve the accuracy of automated evaluation systems.
Scored judge feedback
For judges that return numerical scores:
await sendFeedback({
promptSlug: 'content-quality',
completionId: spanId,
thumbsUp: false,
judgeId: 'quality-scorer-uuid',
expectedScore: 8.5,
scoreDirection: 'too_low',
reason: 'Judge gave 6.0, but content quality was clearly higher'
});
Integration with prompt workflow
Combine prompts and feedback in your application:
import { OpenAI } from 'openai';
import { wrap, prompt, sendFeedback, getCurrentSpan } from 'zeroeval';
const openai = wrap(new OpenAI());
async function generateResponse(userQuery: string) {
// Get the prompt
const systemPrompt = await prompt({
name: 'customer-support',
content: 'You are a helpful customer service assistant.'
});
// Generate completion
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userQuery }
]
});
const answer = response.choices[0].message.content;
// Present to user and collect feedback
const userRating = await showToUserAndGetFeedback(answer);
// Send feedback
const spanId = getCurrentSpan()?.spanId;
if (spanId) {
await sendFeedback({
promptSlug: 'customer-support',
completionId: spanId,
thumbsUp: userRating >= 4, // 1-5 star rating
reason: userRating < 4 ? 'User rated below 4 stars' : 'User satisfied',
metadata: { stars: userRating }
});
}
return answer;
}
Async feedback collection
Capture the span ID for later feedback submission:
import { getCurrentSpan, sendFeedback } from 'zeroeval';
// During LLM call
const response = await openai.chat.completions.create({ /* ... */ });
const spanId = getCurrentSpan()?.spanId;
// Store for later
await db.storeCompletion({
id: responseId,
content: response.choices[0].message.content,
spanId: spanId
});
// Later, when user provides feedback
async function submitUserFeedback(responseId: string, isPositive: boolean) {
const completion = await db.getCompletion(responseId);
await sendFeedback({
promptSlug: 'customer-support',
completionId: completion.spanId,
thumbsUp: isPositive
});
}
Feedback options
All available options for sendFeedback():
interface SendFeedbackOptions {
promptSlug: string; // Required: prompt identifier
completionId: string; // Required: span UUID
thumbsUp: boolean; // Required: positive or negative
reason?: string; // Optional: explanation
expectedOutput?: string; // Optional: desired output
metadata?: Record<string, unknown>; // Optional: custom data
judgeId?: string; // Optional: judge automation ID
expectedScore?: number; // Optional: for scored judges
scoreDirection?: 'too_high' | 'too_low'; // Optional: score direction
}
Error handling
Handle feedback submission errors gracefully:
import { sendFeedback } from 'zeroeval';
import { PromptRequestError } from 'zeroeval/errors';
try {
await sendFeedback({
promptSlug: 'my-prompt',
completionId: spanId,
thumbsUp: true
});
} catch (error) {
if (error instanceof PromptRequestError) {
console.error('Feedback submission failed:', error.message);
// Log for retry or monitoring
} else {
throw error;
}
}
Feedback submission is non-blocking. If it fails, your application continues normally. Consider implementing retry logic for critical feedback.
Best practices
Collect feedback early and often
Integrate feedback collection at the point where users interact with LLM outputs for the most accurate signals.
Provide context in reasons
Specific reasons help identify patterns. Instead of “bad response”, use “included deprecated API in code example”.
Use expectedOutput for negative feedback
When giving negative feedback, include what the output should have been to guide optimization.
Add rich metadata
Include relevant context like user type, query category, or application state to enable sophisticated analysis.
Don't block on feedback
Submit feedback asynchronously to avoid impacting user experience if the API is slow or unavailable.
Programmatic feedback patterns
Automate feedback collection based on downstream metrics:
import { sendFeedback, getCurrentSpan } from 'zeroeval';
async function processUserRequest(query: string) {
const response = await generateLLMResponse(query);
const spanId = getCurrentSpan()?.spanId;
try {
// Attempt to execute the LLM's suggestion
const result = await executeAction(response.action);
// Success = positive feedback
if (spanId) {
await sendFeedback({
promptSlug: 'action-generator',
completionId: spanId,
thumbsUp: true,
reason: 'Action executed successfully'
});
}
} catch (error) {
// Failure = negative feedback
if (spanId) {
await sendFeedback({
promptSlug: 'action-generator',
completionId: spanId,
thumbsUp: false,
reason: `Action failed: ${error.message}`,
expectedOutput: 'A valid, executable action'
});
}
}
}