OCRE AI: A Domain-Specific AI System for Roman Coin Identification

OCRE AI is an open-source applied-AI system for identifying Roman coins, built on the Online Coins of the Roman Empire dataset — more than 43,000 documented coin types spanning from Augustus (31 BC) to Zeno (AD 491).

It gives collectors, students, and researchers several ways into a specialized historical catalog: browse and filter structured records, search in natural language, upload a coin photo for AI-assisted identification, and chat with an AI numismatics expert that can reason over catalog data.

The project is also a practical case study in domain-specific AI architecture: combining structured data, semantic search, full-text search, vision models, and agentic workflows without turning the product into a generic chatbot.

The project is open source under the MIT license: github.com/ilich/ocre-ai.

Why I built it

Roman coin identification is a good test case for applied AI because the problem is both visual and textual. A user may have an unclear inscription, a worn portrait, a partial legend, a denomination, a metal, a mint, or only a photo. The answer usually depends on matching several weak signals against a large expert catalog.

That makes it a better architecture problem than a simple "ask the model" demo. The system needs search, ranking, filtering, provenance, image interpretation, and a user experience that lets people inspect candidates rather than blindly accept a generated answer.

OCRE AI is my way of exploring how AI can make a specialist dataset more usable while keeping the authoritative catalog at the center of the product.

What it demonstrates

OCRE AI demonstrates several patterns that matter in real applied-AI products:

  • Domain-specific retrieval beats generic chat — the AI experience is grounded in the OCRE catalog instead of relying on model memory.
  • Hybrid search handles messy user intent — full-text search catches exact historical terms, while vector search captures semantic similarity.
  • Image understanding becomes a search input — an uploaded photo is described by a vision model and then routed through the same catalog search pipeline.
  • Agents need tools, not just answers — the chat agent can describe images, rewrite ambiguous questions, and search the catalog before responding.
  • Product workflows still matter — authentication, saved collections, profile pages, filtering, pagination, and deployment shape turn the AI feature into a usable application.

Product experience

The application has four main user workflows:

  • Catalog browsing — filter the full OCRE dataset by denomination, material, manufacturer, authority, object type, and other structured fields.
  • Natural-language search — type a plain-language description and get ranked candidate matches from the catalog.
  • Image-assisted identification — upload a coin photo, generate a model-produced description, and search the catalog from that description.
  • AI chat and collection management — ask a conversational expert for help, then save interesting coins to a personal collection.

This is important because many AI prototypes stop at a single impressive interaction. OCRE AI treats AI as one layer inside a fuller product workflow: search, inspect, compare, save, and return.

Search architecture

The catalog endpoint supports two modes, chosen automatically by whether a search query is present:

  • List mode — standard MongoDB filtering, sorting, and pagination for direct catalog browsing.
  • Hybrid search — a query embedding is generated with the configured embedding model, then MongoDB Atlas vector search and full-text search are combined with $rankFusion, merging and re-ranking results before pagination.

That hybrid approach matters in historical data. Exact names, legends, mints, and authorities should still match precisely, but users often describe coins in approximate language. The system needs both lexical precision and semantic tolerance.

Uploaded coin photos follow the same path after one extra step: OpenAI vision produces a textual description of the image, and that description feeds the hybrid search pipeline. A written question and a photo therefore converge into the same ranked catalog results.

AI chat

The chat endpoint is powered by a pydantic-ai agent with three tools at its disposal:

  • Describe an uploaded image — convert visual evidence into searchable text.
  • Search the coin catalog — pull candidate records from the OCRE dataset.
  • Rewrite ambiguous questions — improve retrieval when the user's wording is underspecified.

This lets the agent answer conversationally while remaining connected to real catalog data. It can identify a coin from a description, cross-reference an emperor or mint, explain an inscription or portrait, and help a user understand why a candidate match is plausible.

System architecture

OCRE AI is a full-stack application rather than a standalone model wrapper.

Backend — Python 3.13, FastAPI, Beanie and PyMongo over MongoDB, pydantic-ai for the agent and embeddings, OpenAI-compatible models for chat, embeddings, and vision, Argon2 password hashing, PyJWT authentication, Loguru structured logging, and request tracing.

Frontend — React 19 with React Router 7 in SPA mode, MUI for components, TailwindCSS for layout utilities, Zustand for client state, React Hook Form with Zod validation, strict TypeScript, ESLint, and Prettier.

Data and search — MongoDB Atlas Local with dedicated Atlas Search indexes for full-text search and vector search. The default embedding model uses 1536-dimensional vectors from text-embedding-3-small.

Deployment shape — Docker Compose can run MongoDB, Mailpit, the FastAPI backend, and the React frontend behind nginx. The backend is not exposed directly to the browser; frontend traffic goes through nginx, which reverse-proxies /api/* to the backend.

Quality gates include Ruff formatting and linting, strict mypy, pytest with coverage, and Locust performance tests for backend workflows.

Design tradeoffs

OCRE AI does not try to make the language model the source of truth. The authoritative data remains the OCRE catalog, and the model is used to translate messy human inputs — natural language, ambiguity, and images — into better retrieval paths.

That design keeps the product more inspectable. A user can browse records, compare candidate matches, and see catalog-backed results instead of receiving only a fluent generated answer. For specialist domains, that distinction matters: AI should improve access to evidence, not hide the evidence behind a confident paragraph.

The system also keeps conventional product concerns in scope. Authentication, password reset, saved collections, API boundaries, search indexes, local development, and deployment are not glamorous AI features, but they are the difference between a demo and an application that can be operated and improved.

What this project reflects

OCRE AI reflects how I approach applied AI work: start with a real domain, preserve the value of the underlying data, use models where they remove friction, and design the surrounding system so users can inspect, correct, and build trust in the result.

The same pattern applies outside numismatics. Many organizations have valuable specialist data locked inside catalogs, documents, support histories, compliance records, research archives, or operational systems. The opportunity is not to add a chatbot on top; it is to design a reliable retrieval and workflow layer that makes the data usable.

Running it locally

Full stack via Docker Compose

The whole stack is orchestrated with Docker Compose profiles:

cp .env.docker.example .env.docker   # fill in OPENAI_API_KEY, SECRET_KEY, etc.

docker compose --profile full up     # mongodb + mailpit + backend + frontend

Once it's running, seed the coin catalog and generate embeddings:

docker compose --profile full exec backend make seed
docker compose --profile full exec backend make embed

The frontend is then available at http://localhost, with the backend reachable only through nginx's /api/* proxy.

Backend (FastAPI)

Requires Python 3.13 and uv, with MongoDB Atlas Local running (docker compose --profile dev up from the repo root):

cd apps/backend
cp .env.example .env   # fill in your values
make install           # sync dependencies from uv.lock
make run                # http://localhost:8000, --reload, docs at /docs

Other useful targets: make seed (load the OCRE dataset), make embed (generate embeddings, requires OPENAI_API_KEY), and make test (format, lint, type-check, run pytest with coverage).

Frontend (React)

Requires Node.js 20+ and pnpm, with the backend reachable at http://localhost:8000:

cd apps/frontend
cp .env.example .env   # optional — VITE_API_BASE_URL defaults to http://localhost:8000
pnpm install
pnpm dev                # http://localhost:5173, with HMR

Other useful scripts: pnpm build (production build), pnpm typecheck, and pnpm lint.

Get involved

OCRE AI is open source and under active development. Clone the repository, bring up the Docker Compose stack, and browse the catalog — issues and pull requests are welcome at github.com/ilich/ocre-ai.