You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

265 lines
13 KiB

# Riksdagen Sökplattform – Teknisk Översikt
En plattform för att utforska riksdagsdebatter och metadata. Backenden bygger på FastAPI, ArangoDB (med ArangoSearch), samt ChromaDB för vektorsök i chatten. Frontenden är en React/Vite-applikation skriven i TypeScript. LLM-funktionalitet kopplas in via `_llm`-paketet.
---
## 📋 Innehållsförteckning
1. [Systemöversikt](#systemöversikt)
2. [Projektstruktur](#projektstruktur)
3. [Backend-arkitektur](#backend-arkitektur)
4. [Frontend-arkitektur](#frontend-arkitektur)
5. [Dataflöden](#dataflöden)
6. [Viktiga Python- och TypeScript-komponenter](#viktiga-python--och-typescript-komponenter)
7. [ArangoDB och ArangoSearch](#arangodb-och-arangosearch)
8. [ChromaDB för vektorsök](#chromadb-för-vektorsök)
9. [LLM-integration](#llm-integration)
10. [Utvecklingsflöde](#utvecklingsflöde)
11. [Felsökning och tips](#felsökning-och-tips)
---
## 🏗 Systemöversikt
```
┌─────────────┐ HTTP ┌──────────────┐ Python ┌───────────────┐
│ Browser │ ─────────▶ │ FastAPI │ ───────────▶ │ ArangoDB │
│ (React) │ ◀───────── │ backend │ ◀─────────── │ (ArangoSearch) │
└─────────────┘ └──────┬───────┘ └───────────────┘
│ embeddings / metadata
┌──────────────┐
│ ChromaDB │
└──────┬───────┘
┌──────────────┐
│ _llm │ (OpenAI API-kompatibel klient)
└──────────────┘
```
- **ArangoDB**: primär databas. Sök görs via ArangoSearch-vyer och AQL; inga relationsfrågor används.
- **ChromaDB**: håller embeddings för chatten och mer avancerad semantisk sökning.
- **LLM**: genererar chattsvar med hjälp av `_llm.LLM` (kopplad till t.ex. vLLM/OpenAI API).
---
## 📁 Projektstruktur
```
riksdagen/
├── arango_client.py # Initierar Arango-klienten (används av backend & scripts)
├── backend/
│ ├── app.py # FastAPI-app, routerregistrering
│ ├── search.py # SearchService med ArangoSearch-frågor
│ ├── chat.py # Chat-endpoints (LLM + Chroma)
│ ├── vector.py # Hjälpfunktioner för vektorsök (omfattar Chroma-anrop)
│ └── ... # Övriga routrar, scheman och konfigurationer
├── frontend/
│ ├── src/
│ │ ├── App.tsx # Inträde, växlar mellan sök/chat-lägen
│ │ ├── api.ts # Axios-klient mot `/api/*`
│ │ ├── types.ts # TypeScript-typer för API-svar
│ │ ├── components/ # UI-komponenter (SearchPanel, ChatPanel m.fl.)
│ │ └── styles.css # Globala stilar
│ └── vite.config.ts
├── scripts/
│ ├── ingest_arguments.py # Exempel: läser in data till ArangoDB
│ └── build_embeddings.py # Skapar embeddings och för in i Chroma
├── _arango/
│ ├── _arango.py # Wrapper kring python-arango (förbättrad initiering)
│ ├── queries/ # Samling av AQL/ArangoSearch-frågor
│ └── helpers.py # Hjälpfunktioner för AQL-byggande
├── _chromadb/
│ ├── client.py # Instansierar ChromaDB-klient
│ ├── collections.py # Hanterar samlingar (persistens, insättning, sök)
│ └── embeddings.py # Hjälp för integrering mot embeddingsgenerator
├── _llm/
│ └── llm.py # LLM-wrapper (OpenAI API-kompatibelt gränssnitt)
└── README.md
```
> **Tips:** Utforska `_arango`, `_chromadb` och `_llm` för att förstå hur databaskopplingar och LLM-anrop är kapslade.
---
## 🔧 Backend-arkitektur
### `backend/app.py`
- Skapar FastAPI-instansen och exponerar endpoints under `/api`.
- Registrerar t.ex. `search_router`, `chat_router`, `meta_router`.
- Konfigurerar CORS så att frontenden kan nå backenden under utveckling och produktion.
### `backend/search.py`
- Innehåller klassen **`SearchService`**.
- `SearchService` använder `arango_client` och `_arango`-hjälpare för att köra ArangoSearch-frågor.
- Vanliga metoder (namn kan skilja något, läs filen för exakt signatur):
- `search(...)`: bygger en ArangoSearch AQL-query med filter (parti, år, talare) samt scoring via `BM25()` eller `TFIDF()`.
- `get_metadata(...)`: hämtar distinkta värden till frontendens filter.
- `get_document_by_id(...)`: används för detaljerad visning/snippets.
### `backend/chat.py`
- Hanterar endpointen `POST /api/chat`.
- Steg i chatthanteringen:
1. Plockar senaste användarfrågan.
2. Anropar ChromaDB (via `_chromadb`) för semantisk återhämtning.
3. Kompletterar kontext med Arango-sökresultat om relevant.
4. Bygger prompt och anropar `_llm.LLM`.
5. Returnerar AI-svaret plus metadata (t.ex. vilka dokument som användes).
- Har ofta även `POST /api/vector-search` som låter frontenden testa Chroma-resultat separat.
### `backend/vector.py` (eller motsvarande modul)
- Innehåller hjälpfunktioner för att:
- Normalisera text.
- Skicka query till ChromaDB och hämta top-k embeddings-matchningar.
- Eventuellt uppdatera/återskapa Chroma-samlingar.
---
## ⚛ Frontend-arkitektur
- **`App.tsx`**: växlar mellan sök- och chat-läge (t.ex. via `const [mode, setMode] = useState<'search' | 'chat'>('search')`).
- **`api.ts`**: axios-klient som exporterar:
- `searchTalks(params)``POST /api/search`.
- `chat(messages, strategy)``POST /api/chat`.
- `vectorSearch(query)``POST /api/vector-search`.
- `getMeta()``GET /api/meta`.
- **`components/SearchPanel.tsx`**:
- Kontrollerade inputs för fritext, år, parti m.m.
- Använder `react-query` (`useMutation/useQuery`) för att trigga backend-anrop.
- Visar resultat i `ResultsTable.tsx`.
- **`components/ChatPanel.tsx`**:
- Håller konversation i state (lista av `{ role, content }`).
- Skickar hela historiken till chat-endpointen.
- Renderar streamen av svar.
- **`types.ts`**:
- Speglar backendens Pydantic-scheman med TypeScript-interfaces (`SearchRequest`, `SearchResponse`, `TalkResult`, `ChatRequest`, etc.).
- Bra översättning mellan Python-modeller och TypeScript.
---
## 🔄 Dataflöden
### Sökning via ArangoSearch
1. **Frontend (`SearchPanel.tsx`)** samlar användarens filter och anropar `searchTalks` i `frontend/src/api.ts`.
2. **FastAPI (`backend/search.py`)** tar emot POST `/api/search`, instansierar `SearchService` och skickar vidare parametrar.
3. **`SearchService.search`** bygger en AQL-fråga mot ArangoSearch-vyn (t.ex. `talks_view`) där filter och scoring läggs till. Se `_arango/queries/search_view.py` för den faktiska queryn.
```aql
FOR doc IN talks_view
SEARCH ANALYZER(doc.anforande_text, "text_sv") %PHRASE(@query)%
FILTER (@party == null OR doc.party == @party)
FILTER (@year == null OR doc.year == @year)
SORT BM25(doc) DESC
LIMIT @offset, @limit
RETURN MERGE(doc, { snippet: doc.preview })
```
4. **ArangoDB** returnerar matchande dokument. Backend formaterar svaren (färger, snippet) och skickar JSON tillbaka.
5. **Frontend** renderar resultatet i `ResultsTable.tsx`, uppdaterar statistikpaneler och hanterar paginering.
### Chattflöde med LLM och Chroma
1. **Frontend (`ChatPanel.tsx`)** skickar konversationen till `POST /api/chat`.
2. **`backend/chat.py`**:
- Extraherar senaste användarfrågan.
- Hämtar relevanta chunkar via Chroma (`_chromadb.collections.get_collection(...).query(...)`).
- Optionellt kompletterar kontexten med `SearchService.search` för ArangoSearch-träffar.
- Bygger prompten, anropar `_llm.LLM(model="vllm")` och returnerar svaret.
3. **Frontend** lägger till AI-svaret i konversationen och visar källor/metadata för transparens.
---
## 🧩 Viktiga Python- och TypeScript-komponenter
| Fil | Funktion |
|-----|----------|
| `arango_client.py` | Initierar Arango-klienten med anslutningsuppgifter från miljön. |
| `_arango/_arango.py` | Wrapper som kapslar uppkoppling och hjälpfunktioner mot ArangoDB och views. |
| `_arango/queries/*` | Samling återskapbara AQL-snippets för sök, metadata och detaljhämtning. |
| `_chromadb/client.py` | Skapar ChromaDB-klienten (lokal DuckDB + Parquet som standard). |
| `_chromadb/collections.py` | Hjälper till att skapa/hämta Chroma-samlingar, lägga in chunkar och göra `query`. |
| `_llm/llm.py` | OpenAI-kompatibel klient. Använd `LLM(model='vllm')` och `chat(...)` för generering. |
| `backend/search.py` | Innehåller `SearchService` och API-routes för sök, bygger AQL mot ArangoSearch. |
| `backend/chat.py` | Chat- och vector-endpoints. Kopplar ihop Chroma-resultat med LLM-svar. |
| `frontend/src/api.ts` | Axios-klient för att prata med backenden (`searchTalks`, `chat`, `vectorSearch`, `getMeta`). |
| `frontend/src/components/*` | UI-komponenter i React/TypeScript för sök, resultat, chat, statistik. |
---
## 🗄 ArangoDB och ArangoSearch
- **Kollektioner**: rådata om tal, talare och debatter lagras i Arango-kollektioner.
- **Views**: exempelvis `talks_view` indexerar textfält med analysatorn `text_sv`. Alla fritextfrågor går via vyn.
- **AQL-byggstenar**:
- `FOR doc IN talks_view`
- `SEARCH` med `ANALYZER`, `PHRASE` eller token-matchning.
- `FILTER` för parti, år, kategori och talare.
- `SORT BM25(doc)` eller `SORT doc.datum` beroende på begärd sortering.
- `LIMIT @offset, @limit` för paginering.
- **Metadata**: hämtas via dedikerade queries i `_arango/queries/meta.py` (t.ex. `COLLECT party = doc.party RETURN DISTINCT party`).
---
## 🧠 ChromaDB för vektorsök
- Embeddings genereras via `scripts/build_embeddings.py` som:
1. Läser dokument från Arango via `arango_client`.
2. Delar text i chunkar (`_chromadb/embeddings.py`).
3. Beräknar embeddings (vanligen med `_llm` eller annan embeddingsgenerator).
4. Sparar chunkar + metadata i en Chroma-samling.
- Chatten hämtar liknande chunkar med `collection.query(query_texts=[...], n_results=top_k)` och använder resultaten i prompten.
---
## 🔧 Utvecklingsflöde
| Steg | Befallning | Kommentar |
|------|------------|-----------|
| Starta backend i utvecklingsläge | `uvicorn backend.app:app --reload --port 8000` | Kör i projektroten. |
| Starta frontend i dev-läge | `cd frontend && npm run dev` | Öppna http://localhost:5173. |
| Bygg frontend för produktion | `make frontend` | Skapar `frontend/dist`. |
| Ladda om nginx (om du serverar statiskt) | `sudo systemctl reload nginx` | Efter ny frontend-build. |
| Kör embeddings-script | `python -m scripts.build_embeddings --limit 500` | Uppdaterar Chroma-samlingen. |
| Kör Arango-inmatning | `python -m scripts.ingest_arguments` | Läser in tal/metadata i Arango. |
---
## 🧪 Felsökning och tips
- **Arango-anslutning**: kör `python arango_client.py` eller `arangosh --server.endpoint tcp://127.0.0.1:8529`.
- **Kontrollera AQL**: använd ArangoDB Web UI eller `arangosh` för att testa samma query som `SearchService` byggde.
- **Chroma-status**: kontrollera `persist_directory` i `_chromadb/client.py`; ta bort katalogen om du behöver återskapa samlingen.
- **Embeddings**: kör `python -m scripts.build_embeddings --limit 20` för att uppdatera Chroma när nytt material importeras.
- **LLM-svar**: logga prompt och kontext i `backend/chat.py` om svaren känns irrelevanta.
- **React/TypeScript**: använd `useQuery` för GET, `useMutation` för POST och typsätt API-kontrakt via `frontend/src/types.ts`.
---
## 🤖 LLM-integration
- `_llm/llm.py` är huvudgränssnittet. Grundmönster:
```python
from _llm import LLM
def generate_answer(prompt: str) -> str:
llm = LLM(model="vllm")
messages = [
{"role": "system", "content": "Du är en assistent som förklarar riksdagsdebatter sakligt."},
{"role": "user", "content": prompt},
]
response = llm.chat(messages=messages)
return response
```
- Miljövariabler (`CHAT_MODEL`, `LLM_BASE_URL`, `LLM_API_KEY`) styr målmodell och endpoint.
- Chat-endpointen täcker redan vanlig användning. För experiment: återanvänd samma mönster men bygg tydlig prompt av kontexten du hämtar.
> Undvik onödiga `try/except`. Fel bubbla upp så att du ser den faktiska orsaken (t.ex. nätverksproblem, fel API-nyckel).