Multi-agent personal assistant that actually works.
4 API keys. 12 models. 48 concurrent slots. One answer.
I got tired of rate limits killing my Gemini calls halfway through complex tasks. So I built a system that spreads work across multiple API keys and models simultaneously, picks the right model for each subtask based on how hard it is, and falls back gracefully when things go wrong (because they always do).
The idea is simple: you throw a complex question at it, a Planner breaks it into pieces, a pool of agents work on those pieces concurrently across all available keys/models, and a Judge stitches everything together into one coherent answer. The whole thing is grounded in a local knowledge base via RAG so the agents don't just hallucinate random stuff.
- Semaphore-based concurrency β not
asyncio.gatherand pray. Actual mutual exclusion over 48 resource slots with a HashMap tracking every allocation in real-time. - Criticality-based routing β a task rated 9/10 gets Gemini 3.x. A boring summarization task gets Gemma 3 1B. The Planner decides, and the pool routes accordingly. If the top-tier model is rate-limited, the system auto-demotes down through the tiers instead of just crashing.
- Three separate RAG stages β BM25 for the Planner (keyword matching is actually better for planning docs), Vector search for the Executor (semantic similarity for technical content), and a Hybrid BM25+Vector with Reciprocal Rank Fusion for the Judge (because quality assessment needs both exact matches AND semantic understanding).
- Two-level cache β L1 is a straight SHA-256 hash lookup in SQLite (exact match, instant). L2 uses ChromaDB cosine similarity (so "what's the weather in NYC" and "NYC weather today" hit the same cache entry). Threshold is 0.90 to avoid false positives.
You ask something
β
βΌ
ββ Cache check ββββββββ HIT? β return immediately
β β MISS
β βΌ
β Planner (BM25 RAG)
β breaks query into N independent tasks
β β
β βΌ
β Executor (Vector RAG)
β N agents run concurrently through the resource pool
β Semaphore(48) controls access, criticality picks the model tier
β β
β βΌ
β Judge (Hybrid RAG)
β reads all agent outputs, synthesizes final answer
β β
β βΌ
βββ Save to SQLite + ChromaDB + Cache
| Tier | Models | When it's used |
|---|---|---|
| T0 | Gemini 3.1 Flash Lite, Gemini 3 Flash | Hard stuff β reasoning, complex analysis |
| T1 | Gemini 2.5 Flash / Lite | Middle ground β search grounding, general tasks |
| T2 | Gemma 4 31B / 26B | Decent β thinking tasks with generous rate limits |
| T3 | Gemma 3 (27B β 1B, six variants) | Workhorse β high throughput, repetitive subtasks |
The Planner assigns criticality 1β10 to each subtask. 9-10 starts at T0, 7-8 at T1, and so on. If a tier is exhausted, we automatically try the next one down.
.
βββ core/ # all the actual logic lives here
β βββ llm.py # builds genai clients from env or BYOK keys
β βββ resource_pool.py # the semaphore pool β this is where the magic happens
β βββ planner.py # query β structured agent plan (JSON schema output)
β βββ executor.py # runs agents concurrently through the pool
β βββ judge.py # synthesizes everything into a final answer
β βββ rag_engine.py # BM25, Vector, and Hybrid retrievers
β βββ knowledge_base_loader.py
β βββ storage.py # SQLite history + ChromaDB embeddings
β βββ cache.py # L1 hash + L2 semantic
β βββ tools.py # placeholder for future tool integrations
β
βββ api/ # FastAPI backend for serving as a service
β βββ server.py # CORS, lifespan hooks, route mounting
β βββ models.py # pydantic schemas
β βββ dependencies.py # singleton init (pool, storage, cache)
β βββ routes/ # query, health, history, cache endpoints
β
βββ ui/ # streamlit frontend
β βββ app.py # 4 tabs: Chat, Agents, History, Metrics
β βββ styles/custom.css # dark theme, glassmorphism
β βββ components/ # sidebar, chat, agent_viz, metrics
β
βββ cli/main.py # the original REPL interface
βββ tests/test_rag.py # RAG integration tests (7 test groups)
βββ knowledge_base/ # txt files organized by stage (planning/technical/evaluation/policy)
βββ util/ # dev utilities (model checker, reference data)
βββ data/ # runtime β SQLite db + ChromaDB (gitignored)
βββ run.py # unified launcher
βββ prompts.txt # collection of stress-test prompts
βββ requirements.txt
You'll need: Python 3.11+ and at least one Gemini API key from AI Studio.
git clone https://github.com/Swarno-Coder/VibeForge.git
cd VibeForge
python -m venv venv
# Windows:
.\venv\Scripts\activate
# Mac/Linux:
source venv/bin/activate
pip install -r requirements.txtCopy the env template and drop in your keys:
cp .env.example .envNUM_KEYS=4
GEMINI_API_KEY1=AIzaSy...
GEMINI_API_KEY2=AIzaSy...
GEMINI_API_KEY3=AIzaSy...
GEMINI_API_KEY4=AIzaSy...Works fine with just 1 key (you get 12 slots instead of 48). More keys = more concurrency.
Three modes, one launcher:
# Web UI β opens at localhost:8501
python run.py ui
# REST API β opens at localhost:8000, Swagger docs at /docs
python run.py api
# CLI β interactive terminal
python run.py cliDark themed Streamlit app with four tabs:
- Chat β type your query, watch the pipeline execute in real-time
- Agents β see which agents were spawned, what models they got, how many retries
- History β scroll through past conversations
- Metrics β resource pool utilization, cache hit rates, storage counts
There's a BYOK panel in the sidebar β you can paste in your own Gemini keys without touching the .env file. Useful for sharing the deployment with others.
Standard REST. Main endpoint:
# basic query
curl -X POST http://localhost:8000/api/query \
-H "Content-Type: application/json" \
-d '{"query": "Compare EU vs US AI regulation in 2026"}'
# with your own keys (BYOK)
curl -X POST http://localhost:8000/api/query \
-H "Content-Type: application/json" \
-d '{"query": "...", "api_keys": {"1": "AIzaSy..."}}'Other endpoints: GET /api/health, GET /api/history, GET /api/history/{id}, GET /api/cache/stats, DELETE /api/cache.
Full Swagger docs at http://localhost:8000/docs.
| Command | What it does |
|---|---|
/history |
print recent conversations |
/cachestats |
show L1/L2 hit rates |
/clearcache |
wipe the cache |
/exit |
quit |
Drop .txt files into the subdirectories under knowledge_base/:
knowledge_base/
βββ planning/ # Planner reads these via BM25
βββ technical/ # Executor reads these via vector search
βββ evaluation/ # Judge reads these via hybrid search
βββ policy/ # shared across executor + judge
Files get chunked automatically (500 words, 50 word overlap). The system also feeds its own answers back into the knowledge base over time β so it gets better the more you use it.
# unit tests β no API keys needed, tests RAG retrieval logic
python -m tests.test_rag
# full pipeline e2e β needs valid keys in .env
python -m tests.test_rag --e2e7 test groups covering knowledge base loading, BM25/Vector/Hybrid retrieval accuracy, context formatting, singleton initialization, and (optionally) full pipeline execution.
This was the hardest part to get right. Here's what happens when an agent tries to call a model:
- Agent calls
pool.acquire(name, criticality)β semaphore blocks if all 48 slots are in use - Pool finds a free slot in the agent's target tier (based on criticality)
- Agent makes the API call
- If it works β great, release the slot, done
- If 429/quota error β blacklist all keys for that model for 60 seconds, retry
- If JSON/parse error β blacklist just that slot for 60 seconds, retry
- After 2 failures on the same tier β auto-demote to the next tier down
- After 12 total failures β give up, return error message
pool.release()always runs (it's in afinallyblock)
The allocation HashMap (slot_id β {agent, model, key, tier, time}) lets you see exactly what's happening at any moment β which agent is using which key on which model.
| Package | Why |
|---|---|
google-genai |
Gemini SDK |
rich |
pretty terminal output |
pydantic |
schema validation |
python-dotenv |
env file parsing |
chromadb |
vector store for RAG + semantic cache |
sentence-transformers |
embedding model (all-MiniLM-L6-v2) |
fastapi + uvicorn |
REST API |
streamlit |
web UI |
Fork it, make a branch, open a PR. Keep commits clean. If you're touching resource_pool.py, write a test β that code is load-bearing.
MIT β see LICENSE.