xing, flagging for human review, or queuing data for fine-tuning.
Implementation: TypeScript Feedback Middleware
The following implementation demonstrates a non-blocking feedback capture middleware for a Node.js/Express environment. It captures explicit and implicit signals and dispatches them to a message queue.
// types/feedback.ts
export type FeedbackSignal = 'thumbs_up' | 'thumbs_down' | 'edit' | 'copy' | 'share' | 'report';
export type FeedbackSource = 'explicit' | 'implicit';
export interface FeedbackEvent {
traceId: string;
userId: string;
modelId: string;
promptHash: string;
responseId: string;
signal: FeedbackSignal;
source: FeedbackSource;
timestamp: Date;
metadata: Record<string, any>;
// For 'edit' signals
originalText?: string;
editedText?: string;
}
// middleware/feedbackCollector.ts
import { Request, Response, NextFunction } from 'express';
import { v4 as uuidv4 } from 'uuid';
import { FeedbackEvent, FeedbackSignal, FeedbackSource } from '../types/feedback';
import { publishToQueue } from '../services/messageQueue';
import { redactPII } from '../services/piiRedaction';
export function feedbackMiddleware() {
return async (req: Request, res: Response, next: NextFunction) => {
const traceId = req.headers['x-trace-id'] as string || uuidv4();
const userId = req.user?.id || 'anonymous';
// Store context in res.locals for downstream capture
res.locals.feedbackContext = {
traceId,
userId,
modelId: req.body.model || 'default',
promptHash: hashString(JSON.stringify(req.body.prompt)),
responseId: uuidv4(),
timestamp: new Date(),
};
// Capture response to attach metadata
const originalJson = res.json.bind(res);
res.json = function(body: any) {
res.locals.feedbackContext.responseId = body.response_id || uuidv4();
return originalJson(body);
};
next();
};
}
// api/feedbackController.ts
import { Request, Response } from 'express';
import { publishToQueue } from '../services/messageQueue';
export async function captureFeedback(req: Request, res: Response) {
const { traceId, signal, metadata } = req.body;
const context = res.locals.feedbackContext;
// In a real app, context might be retrieved via traceId lookup service
const event: FeedbackEvent = {
traceId,
userId: req.user.id,
modelId: context?.modelId || 'unknown',
promptHash: context?.promptHash || '',
responseId: context?.responseId || '',
signal: signal as FeedbackSignal,
source: 'explicit',
timestamp: new Date(),
metadata: await redactPII(metadata || {}),
};
// Fire-and-forget to avoid latency impact
await publishToQueue('feedback-events', event);
res.status(202).json({ status: 'accepted' });
}
// api/responseEditController.ts
export async function captureResponseEdit(req: Request, res: Response) {
const { traceId, originalText, editedText } = req.body;
const event: FeedbackEvent = {
traceId,
userId: req.user.id,
modelId: 'unknown', // Lookup via traceId service
promptHash: '',
responseId: '',
signal: 'edit',
source: 'implicit',
timestamp: new Date(),
metadata: {},
originalText: await redactPII(originalText),
editedText: await redactPII(editedText),
};
await publishToQueue('feedback-events', event);
res.status(202).json({ status: 'accepted' });
}
Architecture Decisions
- Async Dispatch: Feedback capture must never block the inference response. The middleware attaches context, and the controller publishes to a queue (e.g., Kafka, SQS, RabbitMQ). This ensures zero impact on P99 latency.
- PII Redaction: Feedback data often contains sensitive user input. Redaction must occur at the ingestion edge or immediately upon queue consumption before storage.
- Prompt Hashing: Storing full prompts in feedback events increases storage costs and privacy risk. Hashing allows aggregation of feedback by prompt variant without storing raw text.
- Implicit vs. Explicit: Implicit signals (edits, copies) are higher volume but noisier. Explicit signals (thumbs down) are lower volume but higher fidelity. The pipeline must weight these differently during analysis.
Pitfall Guide
-
Feedback Bias:
- Mistake: Assuming feedback represents the general population. Users who provide feedback are often outliers (extremely satisfied or extremely frustrated).
- Mitigation: Instrument implicit signals to capture the silent majority. Use sampling strategies for explicit feedback requests (e.g., prompt after N interactions).
-
Label Noise in Automated Pipelines:
- Mistake: Treating a "thumbs down" as a definitive error label without context. Users may downvote for stylistic preferences or minor hallucinations that don't affect utility.
- Mitigation: Implement a scoring function that combines signal type, user tenure, and interaction depth. Use a secondary LLM call to classify the severity of negative feedback before adding to training sets.
-
Loop Collapse:
- Mistake: Retraining the model solely on its own feedback. This causes the model to overfit to its own output distribution, reducing diversity and amplifying biases.
- Mitigation: Maintain a balanced dataset. Mix feedback-derived data with diverse, high-quality external data. Use DPO (Direct Preference Optimization) or RLHF techniques that penalize deviation from the base model unless the reward signal is strong.
-
Privacy and Compliance Violations:
- Mistake: Storing raw user prompts and responses indefinitely in feedback logs.
- Mitigation: Implement data retention policies. Anonymize user IDs. Encrypt feedback at rest. Ensure the feedback pipeline is included in GDPR/CCPA data mapping.
-
Latency Leakage via Synchronous Processing:
- Mistake: Running enrichment or PII redaction synchronously in the feedback API endpoint.
- Mitigation: Offload all heavy processing to background workers. The feedback endpoint should only validate schema and publish to the queue.
-
Ignoring Distribution Shift:
- Mistake: Reacting only to feedback signals while ignoring shifts in input data distribution.
- Mitigation: Monitor input embeddings. If the centroid of user inputs drifts significantly from the training data, trigger a review even if feedback scores are stable.
-
Reward Hacking:
- Mistake: Users discovering they can manipulate the model by providing specific feedback patterns.
- Mitigation: Detect anomalous feedback patterns from single users or IPs. Implement rate limiting and anomaly detection on the feedback stream.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| High Volume, Low Risk (Chatbot) | Automated RAG Update + Prompt Tuning | Fast iteration; feedback can trigger index refreshes or prompt variant A/B tests immediately. | Low storage cost; moderate compute for embedding updates. |
| Low Volume, High Risk (Medical/Legal) | Human-in-the-Loop Queue | Safety requires expert review. Feedback triggers a review queue; model updates are manual. | High operational cost; low storage cost due to volume. |
| Model Degradation Detected | Fine-Tuning Pipeline | Feedback indicates fundamental knowledge gap. Requires dataset curation and fine-tuning run. | High compute cost; long lead time. |
| Stylistic Complaints | DPO/Preference Optimization | Feedback shows model tone/structure issues. DPO aligns model to preference data without full retraining. | Moderate compute; efficient data usage. |
Configuration Template
Use this JSON schema to validate feedback events at the ingestion edge. This ensures schema consistency across frontend and backend services.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "AIFeedbackEvent",
"type": "object",
"required": ["traceId", "userId", "signal", "timestamp"],
"properties": {
"traceId": {
"type": "string",
"format": "uuid",
"description": "Unique identifier linking feedback to the inference request."
},
"userId": {
"type": "string",
"description": "User identifier. Must be hashed or pseudonymized."
},
"signal": {
"type": "string",
"enum": ["thumbs_up", "thumbs_down", "edit", "copy", "report"],
"description": "The type of feedback signal."
},
"source": {
"type": "string",
"enum": ["explicit", "implicit"],
"description": "Whether the feedback was主动 provided or derived from behavior."
},
"timestamp": {
"type": "string",
"format": "date-time"
},
"metadata": {
"type": "object",
"description": "Additional context. PII must be redacted before submission.",
"properties": {
"responseId": { "type": "string" },
"modelVersion": { "type": "string" },
"editDistance": { "type": "number" },
"reason": { "type": "string" }
}
}
}
}
Quick Start Guide
-
Add Interceptor:
Copy the feedbackMiddleware into your API layer. Ensure it attaches traceId and responseId to the response context.
# Install dependencies
npm install uuid @types/uuid
-
Define Schema:
Create the FeedbackEvent interface in your shared types package. Implement the JSON schema validator in your feedback endpoint.
-
Setup Queue:
Configure a message queue (e.g., AWS SQS or Redis Streams). Create a producer function publishToQueue that serializes the event and sends it asynchronously.
-
Create Consumer:
Write a simple worker script that polls the queue, validates the event against the schema, runs PII redaction, and writes to your database.
// worker/feedbackConsumer.ts
import { consumeQueue } from '../services/messageQueue';
import { redactPII } from '../services/piiRedaction';
import { storeFeedback } from '../services/feedbackStore';
consumeQueue('feedback-events', async (event) => {
const sanitized = await redactPII(event);
await storeFeedback(sanitized);
});
-
Verify Pipeline:
Trigger an AI inference, capture a feedback signal, and check the consumer logs. Ensure the event appears in storage within seconds. Monitor queue depth to detect backpressure.
Conclusion
AI product feedback loops transform AI features from static tools into adaptive systems. By engineering robust ingestion, enrichment, and action pipelines, teams can reduce model drift, lower correction costs, and improve user retention. The infrastructure requires discipline around privacy, latency, and bias mitigation, but the operational returns justify the investment. Implement the loop, measure the decay, and close the gap.