```typescript
interface FactualClaim {
text: string;
type: 'statistic' | 'date' | 'name' | 'event' | 'definition' | 'comparison';
verifiable: boolean;
specificity: 'low' | 'medium' | 'high';
}
function extractFactualClaims(content: string): FactualClaim[] {
const claims: FactualClaim[] = [];
// Statistics
const statPatterns = [
/(\d+(?:\.\d+)?%)\s+(?:of\s+)?[\w\s]+/g,
/(\d+(?:,\d{3})*(?:\.\d+)?)\s+(people|users|companies|countries)/g,
/increased?\s+by\s+(\d+(?:\.\d+)?%?)/g,
];
for (const pattern of statPatterns) {
const matches = content.matchAll(pattern);
for (const match of matches) {
claims.push({
text: match[0],
type: 'statistic',
verifiable: true,
specificity: 'high',
});
}
}
// Specific dates
const datePattern = /(?:in|on|since)\s+(\d{4}|\w+\s+\d{1,2},?\s*\d{4})/g;
const dateMatches = content.matchAll(datePattern);
for (const match of dateMatches) {
claims.push({
text: match[0],
type: 'date',
verifiable: true,
specificity: 'high',
});
}
// Named entities with claims
const namedEntityPattern = /([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)\s+(?:is|was|are|were|has|have)\s+/g;
const entityMatches = content.matchAll(namedEntityPattern);
for (const match of entityMatches) {
claims.push({
text: match[0] + content.slice(match.index! + match[0].length).split(/[.!?]/)[0],
type: 'name',
verifiable: true,
specificity: 'medium',
});
}
return claims;
}
async function verifyFactualClaim(
claim: FactualClaim,
context: VerificationContext
): Promise {
// Check against provided ground truth
if (context.groundTruth) {
const contradiction = findContradiction(claim, context.groundTruth);
if (contradiction) {
return {
verified: false,
confidence: 0.95,
reason: Contradicts ground truth: ${contradiction},
finding: {
type: 'incorrect_fact',
severity: 'confirmed',
},
};
}
}
// Check for impossible claims
const impossibility = checkLogicalImpossibility(claim);
if (impossibility) {
return {
verified: false,
confidence: 0.99,
reason: impossibility,
finding: {
type: 'logical_impossibility',
severity: 'confirmed',
},
};
}
// Check temporal validity
const temporalError = checkTemporalValidity(claim);
if (temporalError) {
return {
verified: false,
confidence: 0.9,
reason: temporalError,
finding: {
type: 'temporal_error',
severity: 'likely',
},
};
}
return { verified: null, confidence: 0, reason: 'Unable to verify' };
}
function checkLogicalImpossibility(claim: FactualClaim): string | null {
// Percentages over 100% (unless explicitly about growth)
if (claim.type === 'statistic') {
const percentMatch = claim.text.match(/(\d+(?:\.\d+)?)%/);
if (percentMatch) {
const value = parseFloat(percentMatch[1]);
if (value > 100 && !claim.text.includes('growth') && !claim.text.includes('increase')) {
return Percentage ${value}% exceeds 100% without growth context;
}
}
}
// Negative counts
const negativeCount = claim.text.match(/-(\d+)\s+(people|users|items)/);
if (negativeCount) {
return Negative count: ${negativeCount[0]};
}
return null;
}
function checkTemporalValidity(claim: FactualClaim): string | null {
if (claim.type !== 'date') return null;
const yearMatch = claim.text.match(/\d{4}/);
if (yearMatch) {
const year = parseInt(yearMatch[0]);
const currentYear = new Date().getFullYear();
if (year > currentYear + 1) {
return Future date ${year} treated as historical fact;
}
// Check for anachronisms (would need domain knowledge)
// e.g., "invented the internet in 1850"
}
return null;
}
```