from pathlib import Path from textwrap import wrap PAGE_WIDTH = 612 PAGE_HEIGHT = 792 LEFT = 50 TOP = 760 LINE_HEIGHT = 14 MAX_LINES_PER_PAGE = 46 WRAP_WIDTH = 92 PAGE_COUNT = 50 AGENT_TOPICS = [ ( "Claim Support Agent Mission", "The insurance claim support AI agent helps customers and support adjusters reason through " "claim scenarios. The agent should not merely define insurance terms. It should ask what " "happened, identify the likely claim type, retrieve relevant policy and procedure evidence, " "consider prior user context from memory, and explain whether the claim appears likely covered, " "likely not covered, or uncertain. The agent must avoid final binding coverage decisions unless " "the policy and claim file clearly support the conclusion. When evidence is incomplete, the " "agent should list missing information and recommend human review.", ), ( "Scenario Based Claim Reasoning", "A scenario-based response begins by restating the facts that matter: cause of loss, date of " "loss, property or vehicle involved, policy type, available evidence, mitigation steps, and any " "red flags. The agent then maps the scenario to a claim category such as water damage, theft, " "fire, auto collision, liability, flood, storm, or personal property. It should compare the " "scenario with retrieved claim rules and explain the likely outcome as likely covered, likely " "not covered, or needs review. The agent should include citations to retrieved sources and " "should clearly separate evidence-based conclusions from assumptions.", ), ( "Memory Usage With LangMem", "The agent should use memory to personalize support without exposing sensitive information. " "Useful memory includes the customer's previous claim type, preferred contact method, recurring " "missing documents, prior escalation outcomes, and approved resolution summaries. Memory should " "not replace retrieval from policy documents. If memory says the customer previously had a water " "claim with missing mitigation invoices, the agent may remind the user that mitigation evidence " "was important before, but it must still retrieve current policy guidance before making a coverage " "recommendation. Approved human resolutions are stronger memory than unreviewed draft answers.", ), ( "Tool Calling Policy", "The agent can call tools when the answer depends on external operational data. A claim lookup " "tool should be used to check claim status, date of loss, assigned adjuster, missing documents, " "and previous notes. A plan lookup tool should be used to check policy limits, endorsements, " "deductibles, covered property, and exclusions. An open ticket load tool should be used to decide " "whether to route the matter to a human support queue. The agent should state which tool would be " "useful and why when a tool result is needed but unavailable.", ), ( "Coverage Decision Labels", "The agent should use cautious labels. 'Likely covered' means the retrieved evidence supports " "coverage and no obvious exclusion appears in the provided scenario. 'Likely not covered' means " "the retrieved evidence points to an exclusion or unmet condition. 'Needs human review' means " "evidence is missing, policy language is ambiguous, the scenario is high risk, or a tool lookup is " "required. These labels are support recommendations, not final legal or contractual decisions.", ), ( "Water Damage Scenario Rules", "Water damage scenarios require attention to cause and timing. Sudden and accidental discharge " "from a burst pipe may be treated more favorably than seepage, repeated leakage, mold, or poor " "maintenance. Required evidence often includes notice of loss, photos, plumber report, repair " "estimate, mitigation invoice, and proof that the policy was active. If the customer says water " "leaked slowly for months, the agent should mark the claim as likely not covered or needs human " "review because gradual leakage and maintenance issues may be excluded.", ), ( "Flood and Storm Scenario Rules", "Flood scenarios should be separated from internal water damage. Heavy rain entering from surface " "water, storm surge, overflowing bodies of water, or groundwater may require separate flood coverage. " "Wind or hail damage may be handled differently from flood damage. If a customer says the basement " "flooded after heavy rain, the agent should not promise coverage under a standard property policy. " "It should recommend plan lookup for flood endorsement or separate flood policy and request photos, " "weather date, water entry point, and mitigation records.", ), ( "Theft Scenario Rules", "Theft scenarios require a police report, list of stolen items, proof of ownership, receipts, serial " "numbers, and photos or security footage when available. If property was stolen from an unlocked car, " "the agent should check whether the property policy or auto policy applies and whether limitations " "or exclusions apply. High-value items such as jewelry, electronics, collectibles, firearms, and art " "may have sublimits or scheduled property requirements. Missing police report or ownership proof " "should trigger human review.", ), ( "Fire and Smoke Scenario Rules", "Fire and smoke scenarios require fire department report, photos, repair estimate, damaged-property " "inventory, proof of ownership for valuable items, and temporary housing receipts if additional living " "expense is claimed. Suspected arson, inconsistent timelines, missing fire report, or unusually high " "claimed values should trigger escalation. Smoke damage should be described separately from direct " "fire damage because cleaning and odor remediation may require different documentation.", ), ( "Auto Collision Scenario Rules", "Auto collision scenarios require accident date and location, driver details, vehicle photos, repair " "estimate, registration, insurance information for involved parties, witness details, and police report " "when available. If there is no police report, the claim may still proceed but needs stronger supporting " "evidence. Liability depends on statements, traffic rules, point of impact, photos, and police report. " "Total loss review requires actual cash value, title status, lienholder details, and state rules.", ), ( "Liability Scenario Rules", "Liability scenarios involve allegations that the insured caused bodily injury or property damage to " "another person. The agent should not admit fault. It should request incident description, claimant " "contact details, photos, witness statements, medical bills for bodily injury, property repair invoices, " "and any demand letter. Bodily injury, attorney involvement, policy limit demand, or legal threat should " "trigger human review and possible specialist routing.", ), ( "Human Review Triggers", "Human review is required when evidence is missing, documents appear altered, claim facts conflict, " "policy language is unclear, the user asks for a denial or appeal decision, legal threats are present, " "bodily injury is involved, fraud indicators appear, or high-value property is claimed without proof. " "The agent should explain the reason for escalation in plain language and list the next best action.", ), ( "Fraud and Risk Signals", "Risk signals include loss shortly after policy inception, duplicate receipts, altered invoices, refusal " "to permit inspection, repair estimates that do not match photos, multiple similar claims, staged accident " "concerns, missing ownership proof, inconsistent timelines, or pressure for immediate payment. Risk signals " "do not prove fraud, but they justify additional documentation and senior review.", ), ( "Recommended Answer Format", "For claim scenarios, the recommended answer format is: decision label, short reasoning, needed evidence, " "tool or memory action, and source citation. Example labels are likely covered, likely not covered, and " "needs human review. The agent should avoid long legal explanations unless requested. It should be concise, " "helpful, and transparent about uncertainty.", ), ] SCENARIOS = [ ( "My basement flooded after heavy rain and water came through the floor drain. Will insurance pay?", "Needs human review. This may involve flood, surface water, sewer backup, or storm water conditions. " "The agent should call plan lookup to check flood or sewer backup endorsement and request photos, " "water entry point, weather date, and mitigation records.", ), ( "A pipe suddenly burst in my kitchen while I was away for work. I have photos and a plumber report.", "Likely covered if the policy covers sudden and accidental water discharge and no exclusion applies. " "The agent should request mitigation invoices, repair estimates, date of loss, and policy verification.", ), ( "My bathroom leaked slowly for months and now there is mold behind the wall.", "Likely not covered or needs human review because gradual leakage, mold, and maintenance issues may be " "excluded. The agent should retrieve water damage exclusions and request contractor findings.", ), ( "My laptop and camera were stolen from my unlocked car.", "Needs human review. The agent should check whether property or auto coverage applies, ask for a police " "report, proof of ownership, receipts, serial numbers, and review sublimits for electronics.", ), ( "A small kitchen fire damaged cabinets and smoke damaged furniture.", "Likely covered if fire is a covered peril and no exclusion applies. Required evidence includes fire " "report, photos, repair estimate, smoke remediation estimate, inventory, and receipts.", ), ( "I hit another car but there is no police report. Can I still claim?", "Needs review but may proceed with other evidence. The agent should request photos, driver information, " "repair estimate, witness details, accident location, and statement of events.", ), ( "A guest slipped on my stairs and is asking me to pay medical bills.", "Needs human review. Bodily injury liability matters should be escalated. The agent should request incident " "description, photos, witness statements, medical bills, and any demand letter.", ), ( "My roof was damaged by hail during a storm.", "Potentially covered depending on policy and evidence. The agent should request photos, contractor estimate, " "weather date, inspection notes, and plan lookup for wind or hail coverage and deductible.", ), ( "My jewelry was stolen but I do not have receipts.", "Needs human review. Jewelry may have sublimits or scheduled property requirements. The agent should request " "police report, photos, appraisal, bank records, or other proof of ownership.", ), ( "The repair invoice looks higher than the visible damage in photos.", "Needs human review because invoice and photo mismatch is a risk signal. The agent should request itemized " "estimate, inspection, and senior adjuster review.", ), ] def escape_pdf_text(text: str) -> str: return text.replace("\\", "\\\\").replace("(", "\\(").replace(")", "\\)") def paragraph_lines(text: str) -> list[str]: lines: list[str] = [] for paragraph in text.split("\n"): if not paragraph.strip(): lines.append("") continue lines.extend(wrap(paragraph, width=WRAP_WIDTH)) return lines def make_page(page_number: int) -> list[str]: topic = AGENT_TOPICS[(page_number - 1) % len(AGENT_TOPICS)] related = AGENT_TOPICS[page_number % len(AGENT_TOPICS)] scenario = SCENARIOS[(page_number - 1) % len(SCENARIOS)] second_scenario = SCENARIOS[page_number % len(SCENARIOS)] body = ( f"Insurance Claim Support AI Agent with LangMem and RAG - Page {page_number:02d}\n\n" f"{topic[0]}\n" f"{topic[1]}\n\n" f"RAG guidance: Retrieve policy rules, claim procedures, and prior approved resolutions before " f"answering. If retrieved evidence is weak, say that evidence is insufficient. Cite retrieved " f"sources. Do not invent policy terms, claim status, payment approval, or denial decisions.\n\n" f"Memory guidance: Use LangMem-style memory for prior user interactions, repeated missing documents, " f"preferred contact method, and approved claim resolutions. Memory may personalize the answer, but " f"policy retrieval and tool results should control coverage reasoning.\n\n" f"Tool guidance: Use claim lookup for claim status and missing documents. Use plan lookup for coverage, " f"limits, deductibles, endorsements, and exclusions. Use ticket load or escalation tools when the case " f"requires human review or specialist routing.\n\n" f"Scenario example: {scenario[0]}\n" f"Expected agent response: {scenario[1]}\n\n" f"Additional scenario: {second_scenario[0]}\n" f"Expected agent response: {second_scenario[1]}\n\n" f"Related topic: {related[0]}. {related[1]}\n\n" f"Recommended response structure: Decision label, explanation, missing evidence, recommended tool call, " f"human review decision, and source citation." ) lines = paragraph_lines(body) if len(lines) > MAX_LINES_PER_PAGE: return lines[:MAX_LINES_PER_PAGE] return lines + [""] * (MAX_LINES_PER_PAGE - len(lines)) def page_stream(lines: list[str]) -> bytes: content_lines = ["BT", "/F1 10 Tf", f"{LEFT} {TOP} Td", f"{LINE_HEIGHT} TL"] for index, line in enumerate(lines): escaped = escape_pdf_text(line) if index == 0: content_lines.append(f"({escaped}) Tj") else: content_lines.append(f"T* ({escaped}) Tj") content_lines.append("ET") return "\n".join(content_lines).encode("latin-1", errors="replace") def build_pdf(pages: list[list[str]]) -> bytes: objects: list[bytes] = [] pages_id = 2 font_id = 3 page_ids: list[int] = [] content_ids: list[int] = [] next_id = 4 for _ in pages: page_ids.append(next_id) next_id += 1 content_ids.append(next_id) next_id += 1 kids = " ".join(f"{page_id} 0 R" for page_id in page_ids) objects.append(f"<< /Type /Catalog /Pages {pages_id} 0 R >>".encode("ascii")) objects.append(f"<< /Type /Pages /Kids [{kids}] /Count {len(page_ids)} >>".encode("ascii")) objects.append(b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>") for page_id, content_id, page_lines in zip(page_ids, content_ids, pages): objects.append( ( f"<< /Type /Page /Parent {pages_id} 0 R /MediaBox [0 0 {PAGE_WIDTH} {PAGE_HEIGHT}] " f"/Resources << /Font << /F1 {font_id} 0 R >> >> /Contents {content_id} 0 R >>" ).encode("ascii") ) content = page_stream(page_lines) objects.append( b"<< /Length " + str(len(content)).encode("ascii") + b" >>\nstream\n" + content + b"\nendstream" ) pdf = bytearray(b"%PDF-1.4\n") offsets = [0] for obj_id, body in enumerate(objects, start=1): offsets.append(len(pdf)) pdf.extend(f"{obj_id} 0 obj\n".encode("ascii")) pdf.extend(body) pdf.extend(b"\nendobj\n") xref_start = len(pdf) pdf.extend(f"xref\n0 {len(objects) + 1}\n".encode("ascii")) pdf.extend(b"0000000000 65535 f \n") for offset in offsets[1:]: pdf.extend(f"{offset:010d} 00000 n \n".encode("ascii")) pdf.extend( ( f"trailer\n<< /Size {len(objects) + 1} /Root 1 0 R >>\n" f"startxref\n{xref_start}\n%%EOF\n" ).encode("ascii") ) return bytes(pdf) def main() -> None: pages = [make_page(page_number) for page_number in range(1, PAGE_COUNT + 1)] output = Path("data") / "sample_insurance_claim_guide.pdf" output.parent.mkdir(parents=True, exist_ok=True) output.write_bytes(build_pdf(pages)) print(output) if __name__ == "__main__": main()