Files
2025-11-30 08:24:06 +08:00

251 lines
6.6 KiB
TypeScript

// Human-in-the-Loop (HITL) Agent
import { Agent } from "agents";
interface Env {
// Add bindings
}
interface ApprovalRequest {
id: string;
type: 'booking' | 'payment' | 'data_change' | 'high_value';
data: any;
confidence: number;
requestedAt: number;
status: 'pending' | 'approved' | 'rejected';
reviewedBy?: string;
reviewedAt?: number;
}
interface HITLState {
pendingApprovals: ApprovalRequest[];
approvalHistory: ApprovalRequest[];
autoProcessedCount: number;
humanReviewCount: number;
}
export class HITLAgent extends Agent<Env, HITLState> {
initialState: HITLState = {
pendingApprovals: [],
approvalHistory: [],
autoProcessedCount: 0,
humanReviewCount: 0
};
async onRequest(request: Request): Promise<Response> {
const url = new URL(request.url);
// POST /process - Process request (auto or human review)
if (url.pathname === "/process") {
const { data, type } = await request.json();
const result = await this.processRequest(data, type);
return Response.json(result);
}
// GET /pending - Get pending approvals
if (url.pathname === "/pending") {
return Response.json({
pending: this.state.pendingApprovals,
count: this.state.pendingApprovals.length
});
}
// POST /approve/:id - Approve a request
if (url.pathname.startsWith("/approve/")) {
const id = url.pathname.split("/")[2];
const { reviewedBy } = await request.json();
const result = await this.approveRequest(id, reviewedBy);
return Response.json(result);
}
// POST /reject/:id - Reject a request
if (url.pathname.startsWith("/reject/")) {
const id = url.pathname.split("/")[2];
const { reviewedBy, reason } = await request.json();
const result = await this.rejectRequest(id, reviewedBy, reason);
return Response.json(result);
}
// GET /stats - Get approval statistics
if (url.pathname === "/stats") {
return Response.json({
autoProcessed: this.state.autoProcessedCount,
humanReviewed: this.state.humanReviewCount,
pending: this.state.pendingApprovals.length,
approvalRate: this.calculateApprovalRate()
});
}
return new Response("Not Found", { status: 404 });
}
// Process request (decide auto vs human review)
async processRequest(data: any, type: ApprovalRequest['type']) {
// Step 1: Analyze request
const analysis = await this.analyzeRequest(data, type);
// Step 2: Decide if human review needed
const needsHumanReview = this.needsHumanReview(analysis);
if (!needsHumanReview) {
// High confidence - process automatically
const result = await this.autoProcess(data, type);
this.setState({
...this.state,
autoProcessedCount: this.state.autoProcessedCount + 1
});
return {
status: 'auto_processed',
result
};
}
// Low confidence - request human review
const approvalRequest: ApprovalRequest = {
id: crypto.randomUUID(),
type,
data,
confidence: analysis.confidence,
requestedAt: Date.now(),
status: 'pending'
};
this.setState({
...this.state,
pendingApprovals: [...this.state.pendingApprovals, approvalRequest],
humanReviewCount: this.state.humanReviewCount + 1
});
// Send notification to human reviewers
await this.notifyReviewers(approvalRequest);
return {
status: 'pending_review',
approvalId: approvalRequest.id,
reason: analysis.reason
};
}
// Analyze request to determine confidence
async analyzeRequest(data: any, type: string) {
// Implement your analysis logic here
// Could use AI model, rules engine, etc.
// Example logic:
let confidence = 0.5;
let reason = '';
if (type === 'high_value' && data.amount > 10000) {
confidence = 0.3;
reason = 'High value transaction requires human approval';
} else if (type === 'booking' && data.urgent) {
confidence = 0.6;
reason = 'Urgent booking may need verification';
} else {
confidence = 0.9;
reason = 'Standard request';
}
return { confidence, reason };
}
// Determine if human review is needed
needsHumanReview(analysis: { confidence: number }): boolean {
const CONFIDENCE_THRESHOLD = 0.8;
return analysis.confidence < CONFIDENCE_THRESHOLD;
}
// Process automatically (high confidence)
async autoProcess(data: any, type: string) {
console.log('Auto-processing:', type, data);
// Implement auto-processing logic
return {
processed: true,
timestamp: Date.now()
};
}
// Approve a pending request
async approveRequest(id: string, reviewedBy: string) {
const approval = this.state.pendingApprovals.find(a => a.id === id);
if (!approval) {
return { error: 'Approval request not found' };
}
// Update approval
approval.status = 'approved';
approval.reviewedBy = reviewedBy;
approval.reviewedAt = Date.now();
// Process the approved request
const result = await this.autoProcess(approval.data, approval.type);
// Update state
this.setState({
...this.state,
pendingApprovals: this.state.pendingApprovals.filter(a => a.id !== id),
approvalHistory: [...this.state.approvalHistory, approval]
});
return {
success: true,
result
};
}
// Reject a pending request
async rejectRequest(id: string, reviewedBy: string, reason: string) {
const approval = this.state.pendingApprovals.find(a => a.id === id);
if (!approval) {
return { error: 'Approval request not found' };
}
// Update approval
approval.status = 'rejected';
approval.reviewedBy = reviewedBy;
approval.reviewedAt = Date.now();
(approval.data as any).rejectionReason = reason;
// Update state
this.setState({
...this.state,
pendingApprovals: this.state.pendingApprovals.filter(a => a.id !== id),
approvalHistory: [...this.state.approvalHistory, approval]
});
return { success: true };
}
// Notify human reviewers
async notifyReviewers(approval: ApprovalRequest) {
console.log('Notifying reviewers for:', approval.id);
// Implement notification logic:
// - Send email
// - Push notification
// - Slack message
// - Update dashboard
}
// Calculate approval rate
calculateApprovalRate(): number {
const approved = this.state.approvalHistory.filter(a => a.status === 'approved').length;
const total = this.state.approvalHistory.length;
return total > 0 ? approved / total : 0;
}
}
export default HITLAgent;