---
title: "A knowledge base with cited answers"
url: "https://edoardogabrielli.com/en/projects/port-authority-knowledge-base"
lang: "en"
updated: "2026-11-02"
---

# A knowledge base with cited answers

> Collection of institutional documents, hybrid search and answers with cited sources: a RAG system built for a port authority.

A system that collects rules, documents and news from institutional websites, indexes them and answers questions by saying which documents each statement comes from. I built it at the software company where I work as a developer, for a port authority. The authority is not named.

## What it is for and what it can do

For a public body that needs to find rules and news scattered across many institutional sites. ETT offers it among its artificial intelligence solutions. It gives a single place to search, with answers that can be checked against the source.

- Collects documents and news from institutional sites and keeps them up to date.
- Combines keyword search and search by meaning.
- Answers in natural language with cited sources, to open and check.
- Caps the monthly AI spending.
- Measures answer quality on a set of test questions.

[See ETT's AI solutions](https://ettsolutions.com/prodotti/piattaforme/intelligenza-artificiale/)

## Context

People working at a port authority have to follow rules, studies and environmental news published on many different sites, in several languages and formats. The information exists but is scattered. They needed one place to search, with answers whose origin can be checked.

The project has been under development since February 2026. I wrote most of it, with one colleague.

## Collecting the documents

The collector is a separate service. For each source (a website, an RSS feed, a news section) it finds pages through sitemaps and feeds and opens them with a real browser, because some sites protect themselves with JavaScript checks. It extracts the main text from the pages, while PDFs and office files go through Apache Tika.

A language model helps decide which pages to follow and which to drop as irrelevant. The instructions for each site are saved and reused, so the site is not analysed again. Collected documents arrive as drafts, and a person reviews them before publication. Collections repeat at intervals and a check avoids reprocessing pages already seen.

![The page of a source to collect, with the steps of the guided setup and the latest runs.](https://edoardogabrielli.com/media/originals/2026/11/22f6762e83e64b929c3c46ea67cb5319/rag-porto-fonte.webp)

## Searching and answering

Each published document is split into pieces of about a thousand characters, with a 150-character overlap so sentences are not cut in half. For each piece I compute a 768-value vector (produced by an embedding model, with a cache so the same text is not paid for twice) and a keyword representation (BM25). Everything is stored in Qdrant.

Search is hybrid: it merges the two rankings, so “docking” also finds “mooring” and an exact code or acronym is not lost. An optional reranking step puts the best results on top. A smaller model rewrites follow-up questions (“and that one?”) before searching, so the chat works over several turns.

The answer is written by a language model (Gemini) that receives only the retrieved pieces and must cite them by number. The system shows only the documents actually cited, and the answer is streamed.

![The assistant answer with numbered references and the list of documents it comes from.](https://edoardogabrielli.com/media/originals/2026/11/0b7d2db423574bb8b95d67ea11a4721c/rag-porto-risposta.webp)

![The report of a collection run: pages found, new, discarded by the filter and sent as drafts.](https://edoardogabrielli.com/media/originals/2026/11/fc2efc0179b7470d86f1c31f5daa9c27/rag-porto-raccolta.webp)

The report of a collection run: pages found, new, discarded and sent as drafts. Names and addresses are replaced with sample values.

![The three-dimensional map showing how the retrieved documents relate to the question.](https://edoardogabrielli.com/media/originals/2026/11/fa72ff6d468f4ce6822dffbe54947a6c/rag-porto-spazio3d.webp)

The 3D map shows how the retrieved documents relate to the question.

## How I evaluate it

An answer that looks right is not enough, so there is a small evaluation setup. A set of 19 questions with known answers, split into categories (keywords, meaning, several documents, filters, questions with no answer), measures whether the right documents are found (recall, precision, MRR, NDCG). Three automatic checks with a judge model then rate whether the answer is faithful to the pieces, relevant and complete. It runs the same chain as production and lets me compare two configurations, for example with and without reranking, on numbers rather than gut feeling.

I tested load with 10,137 documents (17,220 pieces in Qdrant, with random vectors to avoid spending API calls): text search goes from 160 ms to 308 ms for one user at a time. With 50 users at once the median search time is 1.9 s. The bottleneck was database connections, not the amount of data.

## What I did

The search and answer service, the collector, the Django part that connects them and most of the interface, including the 3D map built with three.js. I also wrote the tests (503 in the backend suite in September 2026), the load and browser-compatibility tests and the documentation for the client. Since 2026 the service has run on a staging server of the authority.

## Limits and status

The system is being tested on a staging environment and is not yet in full production. The evaluation has only 19 questions: it catches regressions, it does not give a correctness percentage. Answers depend on a paid external service, with a monthly spending cap and the option to switch provider. Extraction of video subtitles is switched off for legal caution. The project belongs to the company and is not public.

## Stack

Python, Django, FastAPI, Celery, PostgreSQL with PostGIS, Qdrant, Redis, Playwright, Apache Tika, Nuxt (Vue, TypeScript), three.js, Docker.

[← All projects](https://edoardogabrielli.com/en/projects)