1. The file is the problem
A single loan packet can run past 400 pages of PDFs: appraisals, tax returns, W-2s, title commitments, purchase contracts, bank statements. Borrowers upload scans namedIMG_4901_final.pdf. Loan officers spend their hours opening documents, renaming files to lender conventions, and checking policies by hand. Is this appraisal under 120 days old? Are all the required pages here?
The first automation attempt was one-off Ruby LLM calls. It worked, in the way a prototype works. Then three failures started compounding:
- Guessed answers. Models misclassified ambiguous pages and invented missing dates.
- Permission overreach. Scripts ran with ambient API tokens, unrestricted delete and overwrite across shared Drive and Box folders.
- Half-done states. A network timeout mid-tool-call left files unrecoverable.
Each failure alone is an annoyance. Together they tell you something: an agent inside a mortgage workflow is not a chat problem. It is a permissions problem.
2. A state machine, not a prompt
I re-architected Lois as a finite state machine in LangGraph, deployed on Amazon Bedrock AgentCore. Classification, extraction, policy validation, renaming: each a node with typed inputs, so a failure fails at a step you can name instead of somewhere inside a prompt you cannot.

3. Fail closed, always
The tenet I care most about from this project: an autonomous agent should never hold ambient broad API privileges. Not because the model is untrustworthy, but because a system you cannot bound is a system you cannot operate.
We enforced it through Composio integrations with Google Drive, Box, Salesforce, and SharePoint:
- Tenant scoping. Every tool payload carries an immutable tenant and borrower ID. The agent cannot traverse outside its own folder hierarchy.
- Partitioned permissions. Explicit read, write_new, append_metadata. Delete and purge are hard-blocked at the gateway, not politely discouraged in a prompt.
- Execution-time checks. Revoke access while a five-step graph is running and step three fails closed. No downstream side effects.
The distinction that matters here is between what a model can say and what application code will do. The model proposes a classification. Code owns authority. A guess can never become an irreversible write, because the boundary between the two is enforced in the gateway, not requested in the prompt.
4. Naming is compliance
Every wholesale lender enforces different file conventions. One wants[Year]_[BorrowerLastName]_[DocType]_[LoanNumber].pdf. Another wants[LoanNumber] - [DocCategory] - [FormID] - Final.pdf. Get it wrong and the loan package bounces.
Lois keeps a deterministic rule engine mapping classified documents onto each lender’s template. Zero compliance rejections during origination, not because the model names files cleverly, but because naming was never left to the model. The rules do the renaming. The model does the classifying. Neither does the other’s job.