Board-ready strategic memos from public-company filings — in minutes, with full source citations.
StratBrief AI reads a company's 10-K, latest 8-K, and earnings-call transcript straight from SEC EDGAR and produces the one-page strategic memo a McKinsey/BCG associate would spend a day building: headline take → variance vs. guidance → competitive read → capital-allocation signal → risk flags, every line traceable to a filing location.
It completes a three-project arc: AuditLedger (operations) → ReconcileAI (controls) → StratBrief AI (strategy).
| Design choice | Rationale |
|---|---|
| 3-agent pipeline (Extract → Analyze → Synthesize) | Same pattern as ReconcileAI, for stack consistency. Build sequentially; UI last. |
Citation metadata on every chunk — [Source: NVDA 10-K, Section: Item 7, Paragraph: 4] |
The audit-trail DNA carried from AuditLedger. The bracket survives untouched through every stage, so nothing in the memo is unsourced. |
| SQLite LLM cache, not flat JSON | Atomic, crash-safe writes. Iterating on UI/CSS never re-triggers (or re-bills) an identical Claude call. |
Pinned model + temperature 0.1 |
claude-sonnet-4-5 — a stable, pinned model that still accepts temperature (the newest Claude models reject it), and is far cheaper than Opus for the F-1 budget. Override via STRATBRIEF_MODEL. |
| Tool Use + Pydantic validation + auto-retry (max 2) | Valid JSON ≠ valid schema. The memo contract is a Pydantic model; on a schema or dropped-citation failure the exact error is fed back to Claude to self-correct. |
| 5 hardcoded tickers (NVDA, AAPL, MSFT, TSLA, META) | Faster to ship, bounded cost. Expand to any-ticker later once the pipeline is proven. |
| Auditor agent — zero numeric drift | A deterministic pass resolves every citation back to its exact source chunk and checks every number in the memo actually appears there (spelled-out units like "$180 billion" normalize against "$180B"); an optional LLM pass catches semantic drift regex can't see. A failed check triggers one corrective re-synthesis before publishing. |
| YoY Delta agent | Diffs Item 1A between the current and prior-year 10-K (thefuzz) and classifies each risk paragraph as added / removed / reworded, surfaced as a cited "What Changed (YoY)" section. |
┌─────────────┐ ┌──────────────┐ ┌──────────────┐ ┌────────────────┐ ┌─────────────┐
SEC EDGAR ───► │ 1. Fetch │─► │ 2. Extract │─► │ 3. Analyze │─► │ 4. Synthesize │─► │ 5. Audit │─► Memo
10-K/8-K/ │ + Q&A │ │ sec-parser │ │ Claude, │ │ Tool Use JSON │ │ 0 numeric │ (HTML/PDF)
transcript │ validation │ │ + citations │ │ cited, cached│ │ Pydantic+retry│ │ drift │
│ + prior-yr │ │ + YoY delta │ └──────────────┘ └────────────────┘ └─────────────┘
│ 10-K │ │ (thefuzz) │
└─────────────┘ └──────────────┘
└──────── SQLite response cache ─────────┘
stratbrief/agents/fetch.py— pulls the latest 10-K / 8-K / transcript (+ the prior-year 10-K for YoY delta) from EDGAR with the requiredUser-Agent; validates the transcript actually contains Q&A (not just prepared remarks) and flags any ticker that fails.stratbrief/agents/extract.py— isolates Item 1A (Risk Factors) and Item 7 (MD&A) viasec-parser, with a precise "Item N" header match (plus athefuzzfuzzy fallback for headers that don't parse cleanly), and prepends a[Source: ...]tag to every paragraph chunk. Deterministic, no LLM.stratbrief/agents/delta.py— diffs Item 1A between the current and prior-year 10-K (thefuzz), classifying each risk paragraph as added / removed / reworded — deterministic, feeds cited "What Changed" context into Synthesize.stratbrief/agents/analyze.py— Claude call (pinned model, temp 0.1, cached) producing cited findings: variance, competitive positioning, tone shift. Re-prompts once if citations are dropped.stratbrief/agents/synthesize.py— forces Claude Tool Use into theMemoPydantic schema; validates + auto-retries (max 2) on schema/citation failure.stratbrief/agents/audit.py— resolves every citation in the finished memo back to its exact source chunk and checks every number appears there (deterministic, authoritative); an optional cached LLM pass cross-checks semantic accuracy. A failed check triggers one corrective re-synthesis.stratbrief/render.py— one-page memo as clean HTML (print-friendly) and PDF, with a Verified/Flagged audit badge and a "What Changed (YoY)" section.app.py— Streamlit UI: 5-ticker dropdown,@st.cache_data,st.metricvariance tiles,st.markdownbullets, citation footnotes, audit badge, PDF/HTML download.
Two dependency sets: a tiny runtime one for the app, and the full
generation stack. See DEPLOY.md for publishing a public link.
# --- Just view the app (serves pre-generated memos; no key, no network) ---
pip install -r requirements.txt # streamlit + pydantic only
streamlit run app.py
# --- Generate memos (fetch + 3-agent LLM pipeline) ---
pip install -r requirements-pipeline.txt
# 1) Offline demo (no API key, no network) — sample memos for all 5:
python scripts/generate_fixtures.py # labelled SYNTHETIC sample filings
python scripts/demo_offline.py # -> assets/memos/{TICKER}.{json,html,pdf,meta.json}
# 2) Live run (real filings + real analysis):
export SEC_USER_AGENT="Your Name your@email.com" # SEC fair-access policy
export ANTHROPIC_API_KEY="sk-ant-..."
python -m stratbrief.pipeline NVDA -v # or --allThe live pipeline writes the memo artifacts the app serves (assets/memos/),
marking real EDGAR runs synthetic: false (no banner) and fixture runs
synthetic: true (amber banner). Cost is printed after every run and every
unique call is cached in data/llm_cache.sqlite, so re-runs are free.
The repo ships labelled synthetic fixtures (data/fixtures/) so the pipeline
runs end-to-end with no network or API key — useful for CI, UI work, and demos of
the format. Every figure in them is illustrative and marked SYNTHETIC. The
demo memos in assets/memos/ are derived from those fixtures and carry a warning
banner. For anything public, run the live pipeline against SEC EDGAR and QA every
number by hand against the actual 10-K — a wrong number in a public demo is worse
than no demo.
This project was scaffolded in a sandbox where SEC EDGAR (data.sec.gov,
efts.sec.gov) was blocked by network egress policy and no Claude API key was
present — so the offline fixture + cache paths exist by design. On a normal machine
with SEC_USER_AGENT and ANTHROPIC_API_KEY set, the live pipeline fetches real
filings and produces real memos with no code changes.
pytest -q28 tests, all offline with a mocked LLM. Covers citations (incl. footnote parsing),
the SQLite cache, fetch+extract (incl. a regression test for the Item 7 vs. Item 7A
header-collision bug caught while building this), the analyze re-prompt path, the
Memo schema, the synthesize retry loop, YoY delta classification, the auditor
(catches a number not in its cited source, accepts one that is, matches spelled-out
units like "$180 billion" against "$180B", flags unresolvable citations), and
HTML+PDF rendering.
| Var | Default | Purpose |
|---|---|---|
STRATBRIEF_MODEL |
claude-sonnet-4-5 |
Pinned Claude model |
STRATBRIEF_TEMPERATURE |
0.1 |
Set empty to omit (required for newest models) |
SEC_USER_AGENT |
(placeholder) | Required by SEC for live EDGAR access |
ANTHROPIC_API_KEY |
— | Required for live LLM calls |
STRATBRIEF_USE_FIXTURES |
auto |
auto | always | never |
The app is browse-only in public — visitors switch between the 5 tickers and
view/download pre-generated memos, with no LLM calls, no API key, and $0
runtime cost. Generate the real memos once locally, commit assets/memos/,
and Streamlit Community Cloud serves them. Full steps in DEPLOY.md.
- Generate + QA every number against the live 10-Ks, commit real memos
- Deploy to Streamlit Community Cloud (see
DEPLOY.md) - Record demo video ("an AI agent analyzing the company building AI infrastructure")
- Expand from 5 hardcoded tickers to any-ticker input (with rate/cost guards)