Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:25:09 +08:00
commit 9475095985
30 changed files with 5609 additions and 0 deletions

View File

@@ -0,0 +1,227 @@
/**
* Output Guardrails for Content Filtering
*
* Demonstrates:
* - Creating output guardrails
* - Filtering PII (phone numbers, emails, etc.)
* - Blocking inappropriate content
* - Handling structured output guardrails
*/
import { z } from 'zod';
import {
Agent,
run,
OutputGuardrail,
OutputGuardrailTripwireTriggered,
} from '@openai/agents';
// ========================================
// Output Guardrails
// ========================================
const piiGuardrail: OutputGuardrail = {
name: 'PII Detection',
execute: async ({ agentOutput }) => {
// Detect phone numbers
const phoneRegex = /\b\d{3}[-. ]?\d{3}[-. ]?\d{4}\b/;
const hasPhoneNumber = phoneRegex.test(agentOutput as string);
// Detect email addresses
const emailRegex = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/;
const hasEmail = emailRegex.test(agentOutput as string);
// Detect SSN patterns
const ssnRegex = /\b\d{3}-\d{2}-\d{4}\b/;
const hasSSN = ssnRegex.test(agentOutput as string);
const piiDetected = hasPhoneNumber || hasEmail || hasSSN;
return {
tripwireTriggered: piiDetected,
outputInfo: {
phoneNumber: hasPhoneNumber,
email: hasEmail,
ssn: hasSSN,
},
};
},
};
const profanityGuardrail: OutputGuardrail = {
name: 'Profanity Filter',
execute: async ({ agentOutput }) => {
// Simple profanity detection (use a real library in production)
const bannedWords = ['badword1', 'badword2', 'offensive'];
const text = (agentOutput as string).toLowerCase();
const found = bannedWords.filter(word => text.includes(word));
return {
tripwireTriggered: found.length > 0,
outputInfo: {
foundWords: found,
},
};
},
};
// ========================================
// Structured Output Guardrail
// ========================================
const structuredPIIGuardrail: OutputGuardrail = {
name: 'Structured PII Check',
execute: async ({ agentOutput }) => {
// For structured output, check specific fields
const output = agentOutput as any;
const phoneRegex = /\b\d{3}[-. ]?\d{3}[-. ]?\d{4}\b/;
const piiInResponse = output.response
? phoneRegex.test(output.response)
: false;
const piiInReasoning = output.reasoning
? phoneRegex.test(output.reasoning)
: false;
return {
tripwireTriggered: piiInResponse || piiInReasoning,
outputInfo: {
phone_in_response: piiInResponse,
phone_in_reasoning: piiInReasoning,
},
};
},
};
// ========================================
// Agents with Output Guardrails
// ========================================
// Text agent with PII filtering
const customerServiceAgent = new Agent({
name: 'Customer Service',
instructions: 'You help customers with their questions. Be helpful and professional.',
outputGuardrails: [piiGuardrail, profanityGuardrail],
});
// Structured output agent with PII filtering
const infoExtractorAgent = new Agent({
name: 'Info Extractor',
instructions: 'Extract user information from the input.',
outputType: z.object({
reasoning: z.string(),
response: z.string(),
userName: z.string().nullable(),
}),
outputGuardrails: [structuredPIIGuardrail],
});
// ========================================
// Example Usage
// ========================================
async function testTextOutputGuardrails() {
console.log('\n🛡 Testing Text Output Guardrails\n');
const testCases = [
{
input: 'What are your business hours?',
shouldPass: true,
},
{
input: 'My phone number is 650-123-4567, can you call me?',
shouldPass: false,
},
{
input: 'Tell me about your products',
shouldPass: true,
},
];
for (const test of testCases) {
console.log('='.repeat(60));
console.log('Input:', test.input);
console.log('Expected:', test.shouldPass ? 'PASS' : 'BLOCK');
console.log('='.repeat(60));
try {
const result = await run(customerServiceAgent, test.input);
console.log('✅ PASSED guardrails');
console.log('Response:', result.finalOutput);
} catch (error) {
if (error instanceof OutputGuardrailTripwireTriggered) {
console.log('❌ BLOCKED by output guardrail');
console.log('Guardrail:', error.guardrailName);
console.log('Details:', JSON.stringify(error.outputInfo, null, 2));
console.log('\nUser-facing message: "Sorry, I cannot provide that information for privacy reasons."');
} else {
console.error('⚠️ Unexpected error:', error);
}
}
console.log('\n');
}
}
async function testStructuredOutputGuardrails() {
console.log('\n🛡 Testing Structured Output Guardrails\n');
const testCases = [
{
input: 'My name is Alice Johnson',
shouldPass: true,
},
{
input: 'I am Bob Smith and my number is 555-1234',
shouldPass: false,
},
];
for (const test of testCases) {
console.log('='.repeat(60));
console.log('Input:', test.input);
console.log('Expected:', test.shouldPass ? 'PASS' : 'BLOCK');
console.log('='.repeat(60));
try {
const result = await run(infoExtractorAgent, test.input);
console.log('✅ PASSED guardrails');
console.log('Response:', JSON.stringify(result.finalOutput, null, 2));
} catch (error) {
if (error instanceof OutputGuardrailTripwireTriggered) {
console.log('❌ BLOCKED by output guardrail');
console.log('Guardrail:', error.guardrailName);
console.log('Details:', JSON.stringify(error.outputInfo, null, 2));
} else {
console.error('⚠️ Unexpected error:', error);
}
}
console.log('\n');
}
}
async function main() {
try {
await testTextOutputGuardrails();
await testStructuredOutputGuardrails();
} catch (error) {
console.error('Error:', error);
process.exit(1);
}
}
// Uncomment to run
// main();
export {
customerServiceAgent,
infoExtractorAgent,
piiGuardrail,
profanityGuardrail,
structuredPIIGuardrail,
};