Balance sheets in, balanced judgment out
An AI-powered agent that reads financial documents (loan applications, business plans, financial statements) and produces structured, validated credit analysis — extraction, ratio calculations, risk scoring, and a summary memo — built with PydanticAI, Google Gemini, FastAPI, and Streamlit.
Feed it a financial document (PDF) and it will:
- Extract structured financial data — as generalized line items (label, value, category) rather than a fixed set of fields, so it handles balance sheets, income statements, business plans, and other document types without needing a separate schema per type.
- Analyze that data using deterministic tools (ratio calculator, currency conversion) to compute things like debt-to-equity and current ratio, then produce a risk score.
- Draft a memo — a structured
CreditMemo(header, executive summary, recommendation, key findings, caveats), not free-form text.
The pipeline is a chain of small, focused agents rather than one agent trying to do everything — each agent's output is a typed, validated Pydantic model that feeds into the next step.
Document (PDF)
│
▼
┌────────────────────────┐
│ Extraction Agent │ no tools · reads the document faithfully
│ nemotron-3-ultra │ → FinancialData (line items)
└─────────┬──────────────┘
│
▼
┌────────────────────────┐
│ Analysis Agent │ tools: calculate_ratio, convert_currency
│ nemotron-3-utlra │ → RiskAssessment (score, ratios, findings)
└─────────┬──────────────┘
│
▼
┌─────────────────────────┐
│ Memo Agent │ no tools · writes from already-validated data
│ nemotron-3-ultra │ → CreditMemo (header, narrative, recommendation)
└─────────────────────────┘
Each agent is defined with PydanticAI, using Nemotron as the underlying model and a Pydantic schema as its output_type, so the output is validated (and automatically retried/corrected by PydanticAI) rather than hoped-for. All agents are now using nemotron-3-ultra as an alternative to Gemini.
- Agents (core) — the PydanticAI agents themselves: extraction, analysis, memo drafting. This is the heart of the project and works standalone (e.g. from a script) before anything else exists.
- API (FastAPI) — a thin layer exposing the agent pipeline over HTTP (
POST /analyze), reusing the same Pydantic schemas for request/response validation. Returns aCreditMemodirectly — FastAPI serializes it to JSON automatically. - Frontend (Streamlit) — a simple UI for uploading a document and viewing the extracted data, risk score, and memo.
FinancialData— company name, document type (a fixed enum: balance sheet, income statement, business plan, etc.), currency, and a list ofLineItem(label, value, category) rather than hardcoded fields likerevenue/total_assets— this is what lets one schema cover every document type. Also carriesextraction_confidenceandmissing_fieldsso downstream steps know how much to trust it.RiskAssessment— risk level (enum) and score, a list of structuredRatioResult, plusstrengths/concernsas short factual bullet points for the memo agent to write from.CreditMemo— aMemoHeader(company name, document type, and date — all carried over from upstream data, not re-generated by the model) plusexecutive_summary,recommendations,key_findings, andcaveats, which are the parts that actually need the model's judgment.
| Agent | Tools | Notes |
|---|---|---|
| Extraction | none | Reads the uploaded document only — no web search, to avoid the model "filling in" figures from outside the document |
| Analysis | calculate_ratio (plain, no deps), convert_currency (uses an HTTP client via deps) |
Benchmark lookups deferred to a later version, pending a trustworthy data source |
| Memo | none | Returns a CreditMemo directly; persistence (saving the memo) is handled by the calling code, not a tool |
Built in this order:
- Core PydanticAI agents (extraction → analysis → memo)
- FastAPI layer wrapping the agent pipeline
- Streamlit frontend
| Layer | Tool |
|---|---|
| Agents | PydanticAI |
| Models | OpenRouter (NVIDIA) — nemotron-3-ultra (extraction, analysis, memo) |
| Validation | Pydantic v2 |
| API | FastAPI |
| Frontend | Streamlit |
| Package management | uv |
.
├── agents/
│ ├── extraction.py # Extraction agent + schema
│ ├── analysis.py # Analysis agent + tools (ratios, currency conversion)
│ └── memo.py # Memo-drafting agent
├── schemas/
│ └── models.py # Shared Pydantic models used across agents
├── config/
│ └── models.py # Per-agent model configuration
├── api/
│ └── main.py # FastAPI app (POST /analyze)
├── client/
│ └── streamlit_app.py # Streamlit frontend
├── .env.example # Required environment variables
├── setup.sh # Setup script (macOS/Linux)
├── setup.ps1 # Setup script (Windows)
├── pyproject.toml
├── uv.lock
└── README.md
The fastest way to get running is the setup script for your OS — it installs dependencies, sets up your .env file, and starts the API.
Windows (PowerShell):
.\setup.ps1macOS / Linux:
chmod +x setup.sh
./setup.shBoth scripts will pause after creating .env so you can add your real OPENROUTER_API_KEY before the API starts.
-
Make sure you have
uvinstalled:pip install uv
-
Clone the repo, then sync dependencies (creates
.venvand installs everything frompyproject.toml/uv.lock):git clone https://github.com/mgalen007/credit-analysis-agent cd credit-analysis-agent uv sync -
Copy
.env.exampleto.envand add your Gemini API key / OpenRouter API Key:cp .env.example .env
GEMINI_API_KEY=your_key_here OPENROUTER_API_KEY=your_key_here
uv run uvicorn api.main:app --reloadhttp://localhost:8000/docs
curl -X POST http://localhost:8000/analyze \
-F "file=@path/to/document.pdf"uv run streamlit run client/streamlit_app.pyUpload a document, watch the analysis run, and view the extracted data, risk score, and memo.
Galen Mike