Short records of the non-obvious choices. Format: Context / Decision / Consequences.
- Context: We need vector similarity search plus relational data (issues, classifications, analyses).
- Decision: Use PostgreSQL + pgvector for both, rather than adding a dedicated vector store (Milvus/Qdrant/etc.).
- Consequences: One datastore to run, back up, and reason about. Joins between vectors and relational rows are trivial. At very large scale a dedicated store may win, but for this workload a single Postgres is simpler and sufficient.
- Context: The demo must be cheap and reproducible by anyone.
- Decision: Embed locally with
sentence-transformers(all-MiniLM-L6-v2). - Consequences: No API key, no cost, deterministic. First run downloads the model (cached in a volume). Quality is lower than large hosted embeddings but more than enough to demonstrate semantic retrieval.
- Context: Reviewers may not have an API key; LLM calls can fail.
- Decision: Define
LLMProvideras aProtocol; ship Claude and OpenAI adapters plus aRuleFallback. The factory returns the fallback when no key is configured, and the analyzer degrades to it if a live call errors. - Consequences:
docker compose upworks offline. The pipeline shape is identical across providers. Tests run without secrets.
- Context: Issues have no category; the case library may not cover every phrasing.
- Decision: Vote with semantic KNN over the labeled case library, backed by a
small keyword ruleset, and record the deciding
source. - Consequences: Handles cold-start phrasings while staying mostly semantic.
The
sourcefield makes decisions explainable.
- Context: ~200 issues + ~40 cases.
- Decision: Do not create an ivfflat/hnsw index. A sequential scan is fast here and gives perfect recall; an ivfflat index built on a tiny table hurts recall.
- Consequences: Simpler setup, exact results. The index statement to add at
scale is documented in
migrations/001_init.sql.
- Context: Avoid drift between an init SQL file and the ORM models.
- Decision: Create tables from
app/models.pyon startup;001_init.sqlonly runsCREATE EXTENSION vector. - Consequences: One place defines the schema. For a production system you would switch to versioned migrations (e.g. Alembic).