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.
 
 
 
 
 
Lasse Server bd922c498f Add routing and talk detail view; refactor search view and results table 2 months ago
.github/instructions Initial commit 2 months ago
_chromadb Initial commit 2 months ago
backend Refactor search response structure and add get_talk endpoint 2 months ago
frontend Add routing and talk detail view; refactor search view and results table 2 months ago
scripts Initial commit 2 months ago
templates Initial commit 2 months ago
var/chroma_repair Initial commit 2 months ago
.gitignore Initial commit 2 months ago
Makefile Initial commit 2 months ago
README.md Updated readme 2 months ago
__init__.py Initial commit 2 months ago
admin.py Initial commit 2 months ago
all_talks_to_db.py Initial commit 2 months ago
arango_client.py Initial commit 2 months ago
bootstrap.py Initial commit 2 months ago
chat_design.excalidraw Initial commit 2 months ago
config.py Initial commit 2 months ago
documents2db.py Initial commit 2 months ago
download_documents.py Initial commit 2 months ago
favicon.png Initial commit 2 months ago
info.py Initial commit 2 months ago
prompts.md Initial commit 2 months ago
requirements.txt Initial commit 2 months ago
talks2db.py Initial commit 2 months ago
utils.py Initial commit 2 months ago

README.md

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
  2. Projektstruktur
  3. Backend-arkitektur
  4. Frontend-arkitektur
  5. Dataflöden
  6. Viktiga Python- och TypeScript-komponenter
  7. ArangoDB och ArangoSearch
  8. ChromaDB för vektorsök
  9. LLM-integration
  10. Utvecklingsflöde
  11. 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.
    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:

    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).