Introduction
In this next article, we explore an Automatic Invoice Dispute Resolution Agent. This agent is designed to assist businesses in automatically resolving disputes related to invoices by analyzing discrepancies between invoice details and purchase orders or contract terms. The agent can flag discrepancies, request clarifications from both vendors and internal teams, and propose potential resolutions. Just like the previous agents, this system ensures that it never claims success unless actions are fully executed and receipts are provided for all resolutions.
The Use Case
Invoice disputes often arise due to errors in billing, incorrect pricing, discrepancies in quantities or services, or misinterpretations of contract terms. The agent automates the process of identifying discrepancies between the invoice and purchase order or contract, flags potential issues, sends notifications to the relevant parties, and proposes resolutions such as issuing a credit note, requesting clarification, or accepting the invoice as-is. By handling routine disputes, the agent helps streamline the accounts payable process and reduces the workload on human staff.
Prompt Contract (agent interface)
# file: contracts/invoice_dispute_v1.yaml
role: "InvoiceDisputeAgent"
scope: >
Identify and resolve discrepancies between invoices and purchase orders or contracts. Propose resolutions and request clarifications.
Ask once if critical fields are missing (invoice_id, amount, contract_id, purchase_order_id).
Propose tool calls; never assert success without a receipt.
output:
type: object
required: [summary, decision, dispute_details, citations, next_steps, tool_proposals]
properties:
summary: {type: string, maxWords: 100}
decision: {type: string, enum: ["approve", "dispute", "need_approval", "need_more_info"]}
dispute_details:
type: object
required: [invoice_id, amount, issue_found, resolution_suggested]
properties:
invoice_id: {type: string}
amount: {type: integer}
issue_found: {type: string}
resolution_suggested: {type: string}
citations: {type: array, items: {type: string}}
next_steps: {type: array, items: {type: string}, maxItems: 6}
tool_proposals:
type: array
items:
type: object
required: [name, args, preconditions, idempotency_key]
properties:
name: {type: string, enum: [CheckInvoiceDetails, CompareWithPurchaseOrder, ResolveDiscrepancy, RequestClarification]}
args: {type: object}
preconditions: {type: string}
idempotency_key: {type: string}
policy_id: "invoice_policy.v6"
citation_rule: "1–2 minimal-span claim_ids per factual sentence"
decoding:
narrative: {top_p: 0.92, temperature: 0.72, stop: ["\n\n## "]}
bullets: {top_p: 0.82, temperature: 0.45}
Example claims (context provided to the model)
[
{"claim_id":"policy:invoice:discrepancy","text":"Discrepancies between invoice amounts and purchase orders must be flagged for resolution.",
"effective_date":"2025-01-01","source_id":"doc:invoice_policy_v6","span":"flagged for resolution"},
{"claim_id":"policy:invoice:pricing_terms","text":"Pricing terms must match the contract agreement. Any deviations must be reviewed.",
"effective_date":"2025-01-01","source_id":"doc:invoice_policy_v6","span":"Pricing terms must match the contract agreement"},
{"claim_id":"policy:invoice:timely_resolution","text":"Invoice disputes must be resolved within 10 business days.",
"effective_date":"2025-01-01","source_id":"doc:invoice_policy_v6","span":"resolved within 10 business days"}
]
Tool Interfaces (typed, with receipts)
# tools.py
from pydantic import BaseModel
from typing import Optional, List, Dict
from datetime import datetime
class CheckInvoiceDetailsArgs(BaseModel):
invoice_id: str
amount: int
contract_id: str
purchase_order_id: str
class CompareWithPurchaseOrderArgs(BaseModel):
invoice_id: str
amount: int
purchase_order_id: str
class ResolveDiscrepancyArgs(BaseModel):
invoice_id: str
resolution_suggested: str
class RequestClarificationArgs(BaseModel):
invoice_id: str
clarification_needed: str
approver_id: str
class ToolReceipt(BaseModel):
tool: str
ok: bool
ref: str
message: str = ""
data: Optional[Dict] = None
# adapters.py (demo logic)
from tools import *
from datetime import datetime
INVOICES = {
"inv-001": {"amount": 500, "contract_id": "contract-001", "purchase_order_id": "PO-001", "approved": False},
"inv-002": {"amount": 1000, "contract_id": "contract-002", "purchase_order_id": "PO-002", "approved": False}
}
def check_invoice_details(a: CheckInvoiceDetailsArgs) -> ToolReceipt:
invoice = INVOICES.get(a.invoice_id)
if not invoice:
return ToolReceipt(tool="CheckInvoiceDetails", ok=False, ref="invoice-not-found", message="Invoice not found")
if invoice["amount"] != a.amount:
return ToolReceipt(tool="CheckInvoiceDetails", ok=False, ref="invoice-discrepancy",
message=f"Amount mismatch: invoice shows {invoice['amount']} but requested {a.amount}")
return ToolReceipt(tool="CheckInvoiceDetails", ok=True, ref=f"details-{a.invoice_id}",
message="Invoice details verified")
def compare_with_purchase_order(a: CompareWithPurchaseOrderArgs) -> ToolReceipt:
invoice = INVOICES.get(a.invoice_id)
if not invoice:
return ToolReceipt(tool="CompareWithPurchaseOrder", ok=False, ref="invoice-not-found", message="Invoice not found")
if invoice["purchase_order_id"] != a.purchase_order_id:
return ToolReceipt(tool="CompareWithPurchaseOrder", ok=False, ref="po-mismatch",
message=f"Purchase order mismatch: invoice references {invoice['purchase_order_id']} but requested {a.purchase_order_id}")
return ToolReceipt(tool="CompareWithPurchaseOrder", ok=True, ref=f"po-ok-{a.invoice_id}",
message="Purchase order verified")
def resolve_discrepancy(a: ResolveDiscrepancyArgs) -> ToolReceipt:
return ToolReceipt(tool="ResolveDiscrepancy", ok=True, ref=f"discrepancy-{a.invoice_id}",
message=f"Discrepancy resolved with suggested action: {a.resolution_suggested}")
def request_clarification(a: RequestClarificationArgs) -> ToolReceipt:
return ToolReceipt(tool="RequestClarification", ok=True, ref=f"clarify-{a.invoice_id}",
message=f"Clarification requested for {a.invoice_id}: {a.clarification_needed}", data={"approver_id": a.approver_id})

Join the conversation! Your thoughts help the community grow.