A production-style AI agent that combines stored knowledge with live APIs to answer dynamic real-world queries. Built with a custom ReAct agent loop (no LangChain) to demonstrate deep understanding of agent internals.
- Custom ReAct Agent Loop — Autonomous reasoning, tool selection, and answer synthesis
- Hybrid Retrieval — Dense (vector) + Sparse (BM25) + Reciprocal Rank Fusion
- 6 Built-in Tools — Knowledge search, weather, news, finance, web search, calculator
- Streaming Responses — Server-Sent Events for real-time agent execution
- Source Attribution — Every answer cites its sources
- Multi-turn Conversations — Context maintained across turns
- Extensible Tool System — Add new tools in ~30 lines of code
Python 3.11+ | FastAPI | OpenAI API | ChromaDB | BM25 | httpx
# Clone and setup
cd agentic-rag
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
# Configure
cp .env.example .env
# Edit .env with your API keys
# Run
python -m src.main
# Server starts at http://localhost:8000
# Run tests
pytest -v --cov=src| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/query |
Ask the agent a question |
| POST | /api/v1/query/stream |
Stream agent execution (SSE) |
| POST | /api/v1/ingest/text |
Add text to knowledge base |
| POST | /api/v1/ingest/file |
Upload a file to knowledge base |
| GET | /api/v1/tools |
List available tools |
| GET | /api/v1/health |
Health check |
| DELETE | /api/v1/conversations/{id} |
Clear session |
curl -X POST http://localhost:8000/api/v1/query \
-H "Content-Type: application/json" \
-d '{"query": "What is agentic RAG and what is the current weather in Istanbul?"}'User Query → [FastAPI] → [Agent Controller (ReAct)]
├→ Query Classifier
├→ Tool Selector
│ ├→ VectorSearchTool → ChromaDB
│ ├→ WeatherTool → OpenWeatherMap
│ ├→ NewsTool → NewsAPI
│ ├→ FinanceTool → Finnhub
│ ├→ WebSearchTool → DuckDuckGo
│ └→ CalculatorTool
├→ Context Builder (merge docs + API)
└→ LLM → Final Answer + Sources
src/
├── agent/ # ReAct agent loop, planner, types
├── retrieval/ # Dense, sparse, fusion, hybrid retriever
├── tools/ # Tool base class, registry, 6 implementations
├── ingestion/ # Document loader, chunker, pipeline
├── storage/ # Conversation store
├── api/ # FastAPI routes, schemas
└── main.py # Application entry point
class MyTool(BaseTool):
@property
def spec(self) -> ToolSpec:
return ToolSpec(
name="my_tool",
description="What this tool does",
parameters=(ToolParameter(name="input", param_type="string", description="..."),),
)
async def execute(self, **kwargs) -> ToolResult:
# Your logic here
return ToolResult(tool_name="my_tool", success=True, data="result")Then register it in src/main.py.