An AI-powered knowledge assistant for the EPFL Rocket Team.
The goal is to allow members to ask questions in natural language and receive answers based on the team's existing documentation, procedures, technical reports, tutorials, and other knowledge stored in Wiki.js.
The system is designed for a team of approximately 250 members and is built around the existing Wiki.js documentation rather than requiring the team to maintain a separate AI-specific knowledge base.
The EPFL Rocket Team has a large amount of documentation spread across many technical and management areas.
Instead of manually searching through hundreds of Wiki.js pages, a team member can ask a question such as:
What is our biggest rocket?
or:
How do I access the team's servers?
The system searches the team's existing documentation and finds the most relevant information.
The current system provides semantic search through a REST API. The next step is to pass the retrieved documentation to a large language model to generate complete answers.
The original documentation remains the source of truth.
The AI assistant is intended to make information easier to find, not to replace the Wiki.js documentation.
┌──────────────────────┐
│ Wiki.js │
│ Team documentation │
└──────────┬───────────┘
│
│ Git repository
▼
┌──────────────────────┐
│ Local Git clone │
│ Markdown files │
└──────────┬───────────┘
│
│ Ingestion
▼
┌──────────────────────┐
│ Chunking │
│ Split documents │
│ into smaller parts │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ SQLite │
│ Documents + chunks │
└──────────┬───────────┘
│
┌─────────────┴─────────────┐
▼ ▼
┌────────────────┐ ┌────────────────┐
│ SQLite FTS5 │ │ Embeddings │
│ Keyword search │ │ Semantic search│
└────────┬───────┘ └────────┬───────┘
│ │
└─────────────┬─────────────┘
▼
┌──────────────────────┐
│ Search results │
│ Relevant chunks │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ FastAPI │
│ REST API │
└──────────┬───────────┘
│
▼
https://rag.epfl-rocket-team.ch
The planned question-answering layer will extend this architecture:
User question
│
▼
┌────────────────────┐
│ FastAPI API │
└─────────┬──────────┘
│
▼
┌────────────────────────────┐
│ Hybrid retrieval │
│ │
│ Keyword search + semantic │
│ search + reranking │
└─────────────┬──────────────┘
│
▼
Relevant chunks
│
▼
Mistral API
│
▼
Answer
│
▼
Sources and citations
The team's Wiki.js installation contains the original documentation.
The Wiki.js content is also available as a Git repository containing Markdown files.
This project uses the Git repository as its local source of documentation.
This has several advantages:
- The original Wiki.js documentation remains unchanged.
- The AI system can be updated automatically.
- Git provides a history of document changes.
- The ingestion system can process only changed files.
- The entire knowledge base can be rebuilt from the source repository.
The Wiki.js repository is cloned locally and periodically updated using Git.
The documentation is stored primarily as Markdown files.
Examples include:
management/2025_management/2025_M_IT.md
icarus/avionics/2025_I_AV_SW_MAXON_SUM.md
tutorials/beginners_guide.md
competition/firehorn/...
The directory structure itself contains useful information about the documentation.
For example:
icarus/
└── avionics/
└── software/
└── Drivers/
└── 2025_I_AV_SW_DRIVERS_GNSS.md
The path can help identify the context of a document.
The ingestion process reads Markdown files from the local Wiki.js Git repository.
For every document, the system:
- Reads the Markdown file.
- Extracts the document title.
- Identifies headings.
- Splits the document into smaller chunks.
- Generates an embedding for each chunk.
- Stores the chunks in the database.
- Associates each chunk with its original document and URL.
The system supports incremental updates.
When a Wiki.js document changes:
Wiki.js
│
▼
Git commit
│
▼
git pull
│
▼
Changed Markdown file
│
▼
Re-index changed document
│
▼
Update database
There is no need to reprocess the entire Wiki.js documentation every time one page changes.
The ingestion system tracks the previously indexed Git commit and detects changed files.
Large documents are split into smaller pieces called chunks.
For example:
Document
│
├── Introduction
│
├── Hardware
│
├── Software
│
├── Testing
│
└── Results
can become:
Chunk 1: Introduction
Chunk 2: Hardware
Chunk 3: Software
Chunk 4: Testing
Chunk 5: Results
The goal is to make each chunk small enough to retrieve efficiently while retaining enough context to be useful.
Each chunk stores metadata such as:
- Document path
- Document title
- Heading path
- Original URL
- Content
- Embedding
SQLite is currently used as the local database.
The database stores:
Information about original Markdown files.
The smaller pieces created during ingestion.
Numerical vector representations of chunks used for semantic similarity search.
An SQLite FTS5 index is used to search the text efficiently.
The database can be rebuilt from the Git repository if necessary.
This means the database itself is derived data rather than the primary source of truth.
The system uses SQLite FTS5 for full-text search.
For example, a query such as:
it team
can retrieve relevant documentation containing those terms.
The search results include:
- Title
- Section
- File path
- Wiki.js URL
- Relevant content
The system also uses embeddings to find conceptually similar documentation.
Instead of only looking for matching words, the system converts text into numerical representations called embeddings.
Conceptually:
"What is our biggest rocket?"
│
▼
Query embedding
│
▼
Compare with chunk embeddings
│
▼
Find semantically similar
content
The current embedding model is configured through the EMBEDDING_MODEL environment variable.
The default model is:
all-MiniLM-L6-v2
The embedding model is used both during ingestion and when generating query embeddings.
Embeddings are normalized, allowing cosine similarity to be used for ranking.
The system currently exposes both retrieval mechanisms:
User query
│
┌─────────┴─────────┐
▼ ▼
Keyword search Semantic search
FTS5 Embeddings
│ │
└─────────┬─────────┘
▼
Search results
The current API endpoint returns semantic search results.
The keyword search functionality is also implemented in the database layer and can be combined with semantic results in a future retrieval-ranking stage.
A future hybrid retrieval system will combine both methods:
Keyword search
+
Semantic search
│
▼
Result fusion / reranking
│
▼
Best relevant chunks
This should provide better results than either method alone.
The search system is exposed through a FastAPI REST API.
The production API is available at:
https://rag.epfl-rocket-team.ch
GET /Example response:
{
"name": "EPFL Rocket Team RAG API",
"status": "running"
}GET /healthExample response:
{
"status": "ok"
}POST /searchRequest:
{
"query": "How do I access the servers?",
"limit": 5
}Example using curl:
curl -X POST \
"https://rag.epfl-rocket-team.ch/search" \
-H "Content-Type: application/json" \
-d '{
"query": "How do I access the servers?",
"limit": 5
}'The API returns relevant documentation chunks together with their similarity scores and source URLs.
Example:
{
"query": "How do I access the servers?",
"results": [
{
"similarity": 0.4461,
"id": 21359,
"path": "competition/firehorn/ground-segment/control-station/2024_C_GS_GSC_DJF.md",
"title": "Introduction",
"heading_path": "Software Design > Software Components Design > Aspects of Each Component > Server",
"content": "...",
"url": "https://rocket-team.epfl.ch/..."
}
]
}FastAPI automatically generates interactive API documentation at:
/docs
The next major layer is a question-answering endpoint.
The planned flow is:
User asks a question
│
▼
Generate query
embedding
│
▼
Search knowledge base
│
▼
Retrieve relevant chunks
│
▼
Build context
│
▼
Send context to Mistral API
│
▼
Generate answer
│
▼
Return answer + sources
The planned API will look approximately like:
POST /askRequest:
{
"query": "How do I access the servers?"
}Response:
{
"query": "How do I access the servers?",
"answer": "According to the documentation...",
"sources": [
{
"title": "How to Flash Boards",
"url": "https://rocket-team.epfl.ch/..."
}
]
}The LLM will receive retrieved documentation as context and should answer based primarily on that context rather than relying only on its internal knowledge.
The planned LLM backend is the Mistral API, using a Ministral model such as:
ministral-14b-2512
The API key is stored in environment variables and is not committed to the repository.
- Clone Wiki.js documentation repository
- Read Markdown documentation
- Process thousands of Markdown files
- Split documents into chunks
- Store document metadata
- Store chunk metadata
- Store source URLs
- Use SQLite as the database
- Implement SQLite FTS5 full-text search
- Search the documentation from the command line
- Preserve original document paths
- Support incremental document ingestion
- Generate embeddings for document chunks
- Generate query embeddings
- Implement semantic similarity search
- Expose semantic search through FastAPI
- Add health-check endpoint
- Deploy the API behind
rag.epfl-rocket-team.ch - Generate automatic OpenAPI documentation
- Implement hybrid keyword + semantic retrieval
- Improve retrieval ranking
- Add result reranking
- Add
/askquestion-answering endpoint - Add Mistral LLM backend
- Return generated answers with citations
- Build a web chat interface
- Add authentication
- Automate Wiki.js Git updates
- Add monitoring and logging
- Add retrieval-quality evaluation
Potential future improvements include:
- Automatic document classification
- Automatic metadata extraction
- Automatic tagging
- Knowledge graphs
- Search analytics
- User feedback on answers
- Retrieval quality evaluation
- Local LLM support
- Browser-based LLM support
- Model comparison and evaluation
- MCP server integration
- Integration with Flowise or other AI orchestration systems
These are ideas for future development and are not necessarily part of the current architecture.
- Python 3.12+
uv- Git
The project uses uv for Python environment and dependency management.
Configuration is provided through environment variables.
Example:
DATABASE_PATH=data/rag.db
EMBEDDING_MODEL=all-MiniLM-L6-v2The Mistral API key is kept outside the repository:
MISTRAL_API_KEY=...Environment files containing secrets must not be committed to Git.
Install dependencies:
uv syncRun ingestion:
uv run python ingest.pyRun command-line search:
uv run python search.pyRun the API locally:
uv run uvicorn api:app \
--host 0.0.0.0 \
--port 8000The API will then be available locally at:
http://127.0.0.1:8000
Interactive API documentation:
http://127.0.0.1:8000/docs
The update workflow is:
1. Pull changes from the Wiki.js Git repository
2. Detect changed Markdown files
3. Generate embeddings for changed files
4. Re-index changed files
5. Remove deleted documents
6. Update the search index
The ingestion system tracks the previously indexed Git commit.
For example:
Every 10 minutes
│
▼
git pull
│
▼
Detect changes
│
▼
Generate embeddings
│
▼
Update index
This can eventually be automated using a cron job, system service, container restart workflow, or scheduled deployment.
The AI system should not become a second independent documentation system.
The original Wiki.js documentation remains authoritative.
The project should use the simplest architecture that solves the problem.
New technologies should only be added when they provide a clear benefit.
For example:
SQLite
is currently sufficient for the first version.
A distributed vector database may become useful later, but is not necessary simply because the project is an AI project.
Every answer should eventually be traceable to the original Wiki.js documentation.
A user should be able to ask:
Where did this answer come from?
and receive a link to the relevant source page.
The EPFL Rocket Team changes members every year.
The system should therefore be:
- Easy to understand
- Easy to deploy
- Easy to maintain
- Easy to rebuild
- Well documented
A future IT team should be able to understand and operate the project without relying on the original developers.
This project has progressed from an initial ingestion prototype into a working semantic search service.
The current system:
Wiki.js Git repository
│
▼
Incremental ingestion
│
▼
Markdown chunking
│
▼
Embedding generation
│
▼
SQLite database
│
▼
Semantic search
│
▼
FastAPI REST API
│
▼
rag.epfl-rocket-team.ch
The next major milestone is to combine the retrieval system with a Mistral language model to provide complete answers grounded in the EPFL Rocket Team's documentation.
The long-term goal is to make the team's existing knowledge significantly easier to discover while keeping the original Wiki.js documentation as the authoritative source.