How We Reduced LLM Hallucinations in Financial Statement Parsers

Reducing Hallucinations in Parsers: Splitting Extraction and Calculation

Parsing tables, net margins, and line-item details from PDF invoices should be direct. But when a model gets creative and returns a "net total" that doesn't equal the sum of its parts, your downstream accounting databases quickly corrupt.

AI models excel at locating text, but they fail at basic math. In this technical review, we walk through how we combined schema validation libraries (Zod) with strict code validations to catch parsing anomalies at runtime.

1. The Two-Step Extraction Pattern

Rather than asking the model to parse and calculate figures in a single request, separate the operational stages:

// Schema validation forcing extraction before calculation
const LineItemSchema = z.object({
  description: z.string(),
  unitPrice: z.number(),
  quantity: z.number(),
  declaredTotal: z.number()
});

function validateInvoicedTotal(lineItems) {
  const calculatedSum = lineItems.reduce((acc, item) => acc + (item.unitPrice * item.quantity), 0);
  return lineItems.every(item => Math.abs(calculatedSum - item.declaredTotal) < 0.01);
}

If the validator detects a discrepancy between the calculated sum and the model's extracted total, the workflow triggers a second target API check or routes the record to the human review queue.

Conclusion

Lowering model hallucinations isn't solved by adding "please be accurate" to your prompt. It requires isolating raw extraction from mathematical operations and running strict validation rules in your code.

Ananya Iyer

Ananya Iyer

Head of AI & Engineering at AICraftGen. Former systems architect specializing in secure LLM pipelines and workflow orchestration.