Python 3.12+ LangGraph Streamlit Google Gemini License: MIT UV
Production-ready multi-agent AI orchestration — a Supervisor intelligently routes tasks across 4 specialist agents, powered by Google Gemini 2.0 and LangGraph's supervisor pattern.
| Feature | Detail |
|---|---|
| 🎯 Supervisor orchestration | Structured-output routing with full reasoning traces |
| 🔍 Real-time agent trace | Watch every routing decision and tool call live in the UI |
| 🤖 4 specialist agents | Researcher, Coder, Analyst, Writer — each with bound tools |
| 🛠️ 4 production tools | Web search, Wikipedia, AST calculator, sandboxed Python executor |
| 🌊 Streaming UI | graph.stream() with incremental Streamlit updates |
| 🔒 Safe execution | AST-based math eval + restricted Python sandbox with timeout |
| 📦 UV workspace | Modern Python packaging with uv |
| 🏗️ TypedDict state | Fully typed LangGraph state with add_messages reducer |
User Query
│
▼
┌─────────────────┐
│ Supervisor │ ◄─ Reads full history
│ (Gemini LLM) │ Uses RouterOutput
│ next + reason │ (Pydantic structured)
└────────┬────────┘
│
┌─────────────────┼──────────────────┬─────────────────┐
▼ ▼ ▼ ▼
┌───────────────┐ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ Researcher │ │ Coder │ │ Analyst │ │ Writer │
│ (Gemini) │ │ (Gemini) │ │ (Gemini) │ │ (Gemini) │
└───────┬───────┘ └──────┬──────┘ └──────┬───────┘ └──────┬───────┘
│ │ │ │
┌───────┴───────┐ ┌─────┴──────┐ ┌──────┴──────┐ (synthesis
│ web_search │ │run_python │ │safe_ │ no tools)
│ wiki_lookup │ │ _code │ │calculator │
└───────────────┘ └────────────┘ └─────────────┘
│ │ │ │
└────────────────┴─────────────────┴────────────────┘
│
back to Supervisor
│
FINISH → END
Key design decisions:
- Supervisor uses
with_structured_output(RouterOutput)— guarantees valid routing every time (no string parsing, no hallucinations) add_messagesreducer — new messages are appended, not overwritten; full history always available- ReAct agents via
create_react_agent— each specialist runs its own Thought → Action → Observation loop - Sandboxed execution — the Coder uses thread-based timeouts + restricted builtins; the Analyst uses AST-parsed eval
- Python 3.12+
- UV (
pip install uv) - Google API key from Google AI Studio
# Clone the repository git clone https://github.com/Hritikd/gemma4-multi-agent.git cd gemma4-multi-agent # Install dependencies with UV uv pip install -e . # Configure environment cp .env.example .env # Edit .env and add your GOOGLE_API_KEY
streamlit run app.py
Open http://localhost:8501 and start asking questions!
# Run all demos uv run python examples/demo.py # Run a specific demo (1-4) uv run python examples/demo.py 2 # Run a custom query uv run python examples/demo.py "Explain transformer attention mechanisms and implement scaled dot-product attention"
Edit .env to configure:
GOOGLE_API_KEY=your_google_api_key_here GEMINI_MODEL=gemini-2.0-flash-exp # or gemini-1.5-flash, gemini-1.5-pro MAX_AGENT_STEPS=20 # max supervisor routing steps
You can also configure everything in the Streamlit sidebar at runtime.
The brain of the system. Uses ChatGoogleGenerativeAI.with_structured_output(RouterOutput) to produce a guaranteed valid routing decision:
class RouterOutput(BaseModel): next: Literal["researcher", "coder", "analyst", "writer", "FINISH"] reasoning: str # Explains WHY this agent was chosen
Routing logic:
- Reads the full message history via
MessagesPlaceholder - Produces a
RouterOutputwithnextandreasoningfields - LangGraph conditional edges route to the named node
- Loops until
FINISHor max steps
Specialty: Real-world knowledge retrieval
Tools: web_search (DuckDuckGo) + wikipedia_lookup
When used: Facts, current events, definitions, background research
Pattern: LangGraph create_react_agent with system prompt
researcher = create_react_agent( llm, tools=[web_search, wikipedia_lookup], state_modifier=RESEARCHER_SYSTEM_PROMPT, )
Example tasks:
- "What are the latest advances in fusion energy?"
- "Who invented the transformer architecture?"
- "Explain the CAP theorem in distributed systems"
Specialty: Python code generation and execution
Tools: run_python_code (sandboxed executor)
When used: Algorithms, scripts, data processing, any task requiring working code
Safety: Thread timeout (max 30s) + restricted builtins + whitelist of safe modules
Available modules in sandbox:
math, random, statistics, itertools, functools, collections, string, re, json, datetime
Example tasks:
- "Write a binary search tree implementation"
- "Create a web scraper for HackerNews"
- "Build a Fibonacci visualizer with ASCII art"
Specialty: Mathematical analysis and structured reasoning
Tools: safe_calculator (AST-based eval)
When used: Numerical computations, statistical analysis, comparisons
Safety: Pure AST parsing — no eval() with arbitrary code, no imports
Available functions:
sqrt, sin, cos, tan, log, log10, log2, exp, abs, round, ceil, floor, factorial, gcd, lcm, hypot, degrees, radians
Example tasks:
- "Compare compound interest at 5%, 7%, and 10% over 30 years"
- "Calculate the time complexity ratios of O(n log n) vs O(n2) for n=1000"
- "Analyze break-even point: fixed costs 50ドルk, variable cost 15ドル/unit, price 35ドル/unit"
Specialty: Final response synthesis and polish
Tools: None (synthesis only)
When used: Always last — reads all prior agent outputs and produces the definitive answer
Output format: Rich markdown with headers, code blocks, tables, bullet points
The Writer sees the entire conversation history and transforms raw agent outputs into a polished, user-facing response.
Uses DuckDuckGo via duckduckgo-search. No API key required. Returns formatted results with titles, URLs, and snippets.
Uses wikipedia-api. Fetches article summaries with URL attribution. Handles non-existent pages gracefully with suggestions.
AST-based expression evaluator. Supports all standard arithmetic, bitwise operations, and math functions. Zero risk of code injection — uses ast.parse() + custom tree walker, never eval() on raw strings.
safe_calculator("sqrt(144) + 2**8") # → 268 safe_calculator("factorial(10) / 1000") # → 3628.8 safe_calculator("log10(1000000)") # → 6.0
Executes Python in a restricted environment:
- Thread-based timeout (max 30 seconds)
- Restricted builtins (no
open,exec,__import__, etc.) - Captures stdout/stderr separately
- Returns structured output with execution results
User: Explain quantum entanglement and its role in quantum computing
Supervisor → researcher (web search + Wikipedia)
Supervisor → analyst (explain qubit math)
Supervisor → writer (synthesize)
Final: [Polished 800-word explanation with math notation and diagrams]
User: Write a web scraper for HackerNews top posts
Supervisor → researcher (understand HN API)
Supervisor → coder (write and execute scraper)
Supervisor → writer (format with explanation)
Final: [Working Python code + output + explanation of design decisions]
User: If I invest 500ドル/month for 20 years at 8%, how much do I have?
Supervisor → analyst (compound interest formula)
Supervisor → writer (format with comparison table)
Final: [Detailed analysis with 500ドル/1000ドル/2000ドル/month comparison table]
The supervisor pattern is a specific multi-agent topology in LangGraph where one "manager" agent controls routing:
# 1. Build the StateGraph graph = StateGraph(TeamState) # 2. Add supervisor and specialist nodes graph.add_node("supervisor", supervisor_node) graph.add_node("researcher", researcher_node) # 3. Conditional routing from supervisor graph.add_conditional_edges( "supervisor", route_next, # reads state["next_agent"] {"researcher": "researcher", ..., END: END} ) # 4. All specialists loop back to supervisor graph.add_edge("researcher", "supervisor") # 5. Compile and stream compiled = graph.compile() for state in compiled.stream({"messages": [HumanMessage(content=query)]}): # Handle each state update
Why this pattern?
- Clean separation of concerns — each agent is an expert
- Full observability — every routing decision is logged with reasoning
- Extensible — adding a new agent is adding one node + one conditional edge
- Reliable — structured output guarantees no invalid routing
- Create
agents/my_new_agent.py:
def create_my_agent(llm): return create_react_agent( llm, tools=[my_tool], state_modifier="You are the MyAgent specialist...", )
- Update
agents/supervisor.py— add to theLiteraltype:
next: Literal["researcher", "coder", "analyst", "writer", "my_agent", "FINISH"]
- Add to
graph/workflow.py:
graph.add_node("my_agent", my_agent_node) graph.add_edge("my_agent", "supervisor") # Add "my_agent": "my_agent" to conditional_edges map
- Create
tools/my_tool.pywith@tooldecorator - Import in the relevant agent's file and add to
tools=[...]
gemma4-multi-agent/
├── app.py # Streamlit UI entry point
├── pyproject.toml # UV project config + dependencies
├── .env.example # Environment variable template
├── graph/
│ ├── state.py # TeamState TypedDict with add_messages
│ └── workflow.py # LangGraph StateGraph construction
├── agents/
│ ├── supervisor.py # Supervisor + RouterOutput Pydantic model
│ ├── researcher.py # Web search + Wikipedia specialist
│ ├── coder.py # Python execution specialist
│ ├── analyst.py # Math + data analysis specialist
│ ├── writer.py # Final response synthesis
│ └── base.py # Shared utilities
├── tools/
│ ├── search.py # DuckDuckGo web search
│ ├── wikipedia.py # Wikipedia API lookup
│ ├── calculator.py # AST-based safe math eval
│ └── code_runner.py # Sandboxed Python executor
└── examples/
└── demo.py # CLI demo script (no Streamlit)
Contributions welcome! Please:
- Fork the repo
- Create a feature branch:
git checkout -b feat/my-feature - Commit with conventional commits:
git commit -m "feat: add new agent" - Push and open a PR
Areas for contribution:
- New specialist agents (data viz, SQL, API caller)
- Additional tools (Arxiv search, GitHub API, Wolfram Alpha)
- Persistent memory with LangGraph checkpointers
- Docker deployment
- Unit tests
MIT License — see LICENSE for details.
Built with ❤️ using LangGraph · Google Gemini · Streamlit
⭐ Star this repo if you found it useful!