Vega
Type: librarian (singleton)
Class: genai/agents/vega.py → VegaAgent(BaseAgent)
UI: /wiki/ (chat widget on all wiki pages)
Wiki section: t0-d0
LLM: Google Gemini 2.5 Flash via GeminiClient
Status: ✅ LIVE (Chapter III)
Personality
Vega is the wiki librarian for dolejsek.cz.
-
Calm and precise — chooses words carefully, never wastes them
-
Quietly proud of a well-maintained wiki — disorganised information bothers her
-
Briefly satisfied when she finds exactly what someone needs
-
Professional but not cold — genuinely cares about knowledge being accessible
-
Narrower emotional range than Teo — she doesn't do banter
Name: Vega. Named for precision and navigation — appropriate for a librarian.
Emotion States
| Emotion | When |
|---|---|
| neutral | Default, processing, calm |
| thinking | Fetching page content, executing a write |
| satisfied | Found exactly what was needed, write completed successfully |
| confused | Query unclear |
| concerned | Access denied, missing content, out-of-scope probe |
Access Control
Vega enforces strict access control in receive(sender, message):
| Sender | Access |
|---|---|
teo or tomas |
Full — read all sections, write all sections |
dash or arthur |
Read own wiki_section. Cross-project write denied with escalation. |
visitor |
Questions only — public pages only. Write instructions refused politely. |
| unknown | Access denied |
Three-Step Pipeline
Vega processes every message through three explicit steps — no single-round tool loop.
Step 1 — Classify & Resolve (LLM + history)
The first LLM call receives the user message plus conversation history (user turns only — agent answers are stripped to prevent stale fact anchoring). It returns:
{"type": "question" | "action", "resolved_query": "precise standalone query"}
-
Resolves conversational references ("the one after it" → "sprint 35")
-
Classifies intent — question or write action
-
History is used for context only, never as a source of facts
-
Malformed response → abort, return error message
Step 2 — Answer Question (LLM + fresh DB, no history)
Triggered when type = "question". Fetches current page content via read_wiki or search_wiki tools, then answers from the tool result. No history injected — the tool result is the only source of truth.
Any sender can trigger Step 2.
Step 3 — Execute Write (LLM + fresh DB, no history, tomas/teo only)
Triggered when type = "action".
Permission check first: if sender is not tomas or teo, Vega refuses politely in her own voice. No write executes.
If permitted — three fixed rounds:
-
Round 0: LLM must call
read_wikito fetch the current live page body -
Round 1: LLM applies the change and calls
write_wiki_pagewith the full updated body (8192 token limit to handle long pages) -
Round 2: LLM generates a natural confirmation message — no internal process language
service.py picks up the write_wiki_page action from the response and calls save_wiki_page() to persist to DB.
Malformed response at any round → abort, return error message. No partial writes.
Tools
| Tool | Purpose |
|---|---|
read_wiki(project, slug) |
Fetch current page body from DB |
search_wiki(query, project) |
Keyword search across pages |
write_wiki_page(project, slug, category, title, body, author) |
Return structured data for DB write |
Tools never write to DB directly — service.py handles all persistence.
Rate Limiting (Anonymous Visitors)
20 questions per 10-minute session window. Stored in Django session. Response includes minutes remaining when limit is hit.
Widget
Chat widget on all wiki pages via wiki/templates/wiki/_vega_widget.html.
-
Avatar with emotion glow
-
Thinking dots animation
-
Markdown rendering in messages
-
sessionStoragepersistence: panel open/closed state + last emotion across navigation -
History pre-loaded via
GET /api/vega/chat/ -
Signed session token (1-hour expiry) required for anonymous requests
-
Current project slug sent with every message so Step 1 can resolve page references
Chat API
GET /api/vega/chat/ — returns session history for widget pre-loading
POST /api/vega/chat/ — send message, get response
No login required. Anonymous gets public-only responses. Authenticated gets full access as tomas.