Policy Badger is a structured policy question-answering app backed by PostgreSQL. It answers user questions by planning a constrained database search, building safe SQL, retrieving rows from policies_v2, and formatting a grounded answer from those rows.
The current system is centered on the src/policy_badger/engine/ and src/policy_badger/ingestion/ packages.
User question
-> planner JSON search spec
-> allowlisted SQL query builder
-> PostgreSQL policies_v2
-> formatter answer grounded in returned rows
Main implementation:
src/policy_badger/web/app.pyis the authenticated FastAPI web app.src/policy_badger/engine/service.pyorchestrates question answering.src/policy_badger/engine/planner.pyasks the LLM for a JSON search spec only.src/policy_badger/engine/query_builder.pybuilds parameterized SQL from validated fields and filters.src/policy_badger/engine/db.pyconnects to PostgreSQL usingDB_*environment variables.src/policy_badger/engine/formatter.pywrites the final answer using only retrieved rows.src/policy_badger/ingestion/ingests policy documents, stages extracted policy candidates, proposes schema changes, and publishes accepted rows.
.
├── apphosting.yaml
├── Dockerfile
├── requirements.txt
├── src/
│ └── policy_badger/
│ ├── engine/
│ │ └── migrations/
│ ├── ingestion/
│ ├── pdf/
│ └── web/
│ └── static/
├── scripts/
└── tests/
Run locally:
PYTHONPATH=src uvicorn policy_badger.web.app:app --reloadThe FastAPI app provides:
GET /loginandPOST /loginfor session login.GET /logoutto clear the session.GET /for the web UI insrc/policy_badger/web/static/index.html.POST /askfor authenticated policy questions.GET /healthfor health checks.
Users are configured with either:
APP_USERS, a JSON object like{"alice": "password1", "bob": "password2"}.- Or legacy single-user fallback values
APP_USERandAPP_PASSWORD.
Set APP_SECRET_KEY in deployed environments.
The policy engine is intentionally split into small responsibilities:
planner.pyproduces a validated JSON search specification. It does not answer questions and does not execute SQL.query_builder.pyconverts the spec into safe, parameterized PostgreSQL queries using allowlisted fields only.db.pyopens PostgreSQL connections withpsycopg2.service.pycoordinates planning, querying, relaxed fallback queries, text fallback queries, and final formatting.formatter.pyasks the LLM to answer using only retrieved rows.
The main public API is:
from policy_badger.engine.service import answer_policy_question
result = answer_policy_question("What is the PTO approval policy?")The adaptive ingestion system turns source policy documents into structured policy rows.
High-level flow:
Document input
-> ingestion batch
-> document registration
-> section chunking
-> semantic policy extraction
-> staging rows
-> schema gap detection
-> migration proposals
-> approved migrations
-> publish into policies_v2
Important files:
src/policy_badger/ingestion/pipeline.pywires the ingestion workflow.src/policy_badger/ingestion/policy_extractor.pyextracts structured candidate policy JSON.src/policy_badger/ingestion/schema_dictionary.pyloads canonical policy field definitions.src/policy_badger/ingestion/gap_detector.pyandschema_planner.pyidentify schema gaps.src/policy_badger/ingestion/migration_generator.pyandmigration_applier.pygenerate and apply approved schema changes.src/policy_badger/ingestion/publisher.pyvalidates staged records and publishes them topolicies_v2.src/policy_badger/engine/migrations/0001_adaptive_ingestion_foundation.sqlcreates the ingestion support tables.
Run adaptive ingestion from the CLI:
python scripts/adaptive_ingest.py path/to/policy.docxInspect database status:
python scripts/db_status.pyRequired for the web app and policy engine:
APP_SECRET_KEY=change-me
APP_USER=Test
APP_PASSWORD=Test123!
OPENAI_API_KEY=...
DB_HOST=...
DB_PORT=5432
DB_NAME=cal_policy_db
DB_USER=postgres
DB_PASSWORD=...Optional for adaptive extraction and mirroring:
USE_LLM_SEMANTIC_EXTRACTION=1
SEMANTIC_EXTRACT_MODEL=gemini-2.0-flash
VERTEX_PROJECT_ID=...
VERTEX_LOCATION=us-central1
BQ_POLICY_DATASET=...
BQ_POLICY_EVENTS_TABLE=policy_events
BQ_POLICY_CURRENT_TABLE=policies_currentThe current deployment path is Docker/FastAPI:
docker build -t policy-badger .
docker run --env-file .env -p 8080:8080 policy-badgerThe Dockerfile runs:
uvicorn policy_badger.web.app:app --host 0.0.0.0 --port ${PORT:-8080}apphosting.yaml contains the App Hosting / Cloud Run-style runtime and secret configuration.
Run active tests:
pytest -qCompile active Python modules:
python -m compileall -q src scripts tests