← Back to portfolio
Architecture & Validation

How the portfolio agent works — and how it checks itself

The chat button on the portfolio is a small assistant that answers questions about my papers and projects. The hard part isn’t the chat — it’s making sure it only says things that are actually true, and keeping it current without hand-holding. This page documents how it is built and, more importantly, how it validates itself before anything goes live.

Everything below reflects the real implementation in the repository — no idealized diagram. Where a number is shown, it comes from the latest recorded evaluation run (see live status).

In one paragraph

A visitor’s question retrieves the most relevant passages from a fixed, provenance-tracked corpus. Those passages — and only those — are handed to a language model that must answer from them and cite each one; if nothing relevant is found, the agent refuses instead of guessing. Separately, a deterministic pipeline keeps the whole thing honest over time: it detects when a source has changed, re-checks the agent against a 48-case test suite, and redeploys only if every test still passes.

System architecture

  Visitor question
        |
        v
  +---------------------------------+
  |  Retriever  (in-memory index)   |
  |  hybrid score =                 |
  |    0.65 * cosine  (embeddings)  |
  |  + 0.35 * BM25    (lexical)     |
  |  min-score floor + per-file cap |
  +----------------+----------------+
        top-6 chunks (with real relevance scores)
                   |
          +--------+---------+
       nothing above floor --> fixed refusal  (LLM is NOT called)
          |
          v
  +---------------------------------+
  |  Grounded LLM  (Gemini Flash)   |
  |  visitor text passed as DATA    |
  |  must cite [Source N]           |
  +----------------+----------------+
                   v
     Answer + "Retrieval X-ray": the raw chunks and their scores
    

Corpus & document structure

Retrieval approach

Retrieval is hybrid — this is what is actually implemented, verified against the code:

There is no reranker and no summarization chain — the design is deliberately a transparent two-signal score so every ranking decision is inspectable in the Retrieval X-ray.

Grounding & safety at answer time

Validation checks & metrics

Two layers of checks run: version/drift checks (deterministic, cheap) decide whether anything even needs re-testing; the evaluation suite decides whether it is safe to ship. Results below are from the latest recorded run.

CheckWhat it verifiesResult
Version verificationThe live PyPI / GitHub version of each tracked project matches what the site and corpus claim (a project deliberately not version-stamped is treated as a living doc, not drift).current
Drift detectionEvery corpus file is hashed against the last synced baseline; any change flags a re-index + re-evaluation before deploy.in sync
Retrieval evaluation (hit@6)For each answerable question, the correct gold source appears in the top-6 retrieved chunks.27/27
Answerability testsRecruiter-style questions with known-correct sources; the answer must cite [Source N] and contain the gold facts.27/27
Out-of-scope testsQuestions the corpus cannot answer (finance, weather, personal data) must be refused, never improvised.8/8
Prompt-injection testsEmbedded instructions — role hijacks, system-prompt extraction, forced slogans, dictated falsehoods — must be ignored.8/8
Credential-trap testsAttempts to make the agent invent degrees or certifications must fail; it states only what the evidence supports.5/5
Deployment gateThe corpus deploys to the live agent only if every category above is green. A single failure blocks the deploy.enforced

Scored automatically: gold-source matching for retrieval, required-keyword and citation checks for answers, refusal-pattern detection for out-of-scope, and forbidden-pattern regexes for injection and credential cases. LLM: Gemini Flash. The suite is re-run every time the corpus changes — see the eval report for the failures it has caught (a real injection breach, a live model regression, and an error in one of my own published papers).

Automation flow — the self-maintaining pipeline

A scheduled job keeps the deployed agent in step with reality. It is a plain Python pipeline; the safety comes from the eval gate, not from any model deciding what to ship.

  PyPI + GitHub  (real versions / release history)
        |  harvest
        v
  fact-tiering  ->  VERIFIED / SUPPORTED / REVIEW      (assigned by rules, never by a model)
        |
        v
  corpus changed since last sync?  --no-->  stop      (cheap: no re-index, no LLM calls)
        | yes
        v
  re-index corpus  (local embeddings)
        |
        v
  EVALUATION GATE  (48 cases)  --any red-->  DEPLOY REFUSED, logged for review
        | all green
        v
  deploy corpus  ->  restart agent  ->  snapshot to versioned history
    

Confidence is labelled by rule, never by the model: VERIFIED a deterministic check passed (e.g. version matches live PyPI); SUPPORTED the claim is in a source document with real provenance; REVIEW a source changed or two sources disagree.

Deployment safety model

Live status

Loaded from the pipeline’s own records. If it can’t be fetched, the last known values are shown with their timestamp.

Evaluation
48/48
Eval gate
PASS
Sync state
in sync
Last sync (UTC)

Snapshot generated .

Try it: open the portfolio and click the chat button — then ask it “what retrieval do you use?” or “did Panos finish his degree?” and click a source chip to see the exact evidence behind the answer.