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:
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.
POST /analyze), reusing the same Pydantic schemas for request/response validation. Returns a CreditMemo directly — FastAPI serializes it to JSON automatically.FinancialData — company name, document type (a fixed enum: balance sheet, income statement, business plan, etc.), currency, and a list of LineItem (label, value, category) rather than hardcoded fields like revenue/total_assets — this is what lets one schema cover every document type. Also carries extraction_confidence and missing_fields so downstream steps know how much to trust it.RiskAssessment — risk level (enum) and score, a list of structured RatioResult, plus strengths/concerns as short factual bullet points for the memo agent to write from.CreditMemo — a MemoHeader (company name, document type, and date — all carried over from upstream data, not re-generated by the model) plus executive_summary, recommendations, key_findings, and caveats, 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:
| 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.ps1
macOS / Linux:
chmod +x setup.sh
./setup.sh
Both scripts will pause after creating .env so you can add your real OPENROUTER_API_KEY before the API starts.
uv installed:
pip install uv
.venv and installs everything from pyproject.toml/uv.lock):
git clone https://github.com/mgalen007/credit-analysis-agent
cd credit-analysis-agent
uv sync
.env.example to .env and 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 --reload
http://localhost:8000/docs
curl -X POST http://localhost:8000/analyze \
-F "file=@path/to/document.pdf"
uv run streamlit run client/streamlit_app.py
Upload a document, watch the analysis run, and view the extracted data, risk score, and memo.
Galen Mike