TaskPilot is a multi-tool AI agent designed to answer user questions by dynamically selecting the appropriate tool or combination of tools based on the nature of each query.
The system is built around three core principles:
- Retrieval-Augmented Generation (RAG) for grounded, citation-backed answers from uploaded documents.
- Hierarchical summarization for handling long-document distillation.
- Live web search for current, public information beyond the document corpus.
┌─────────────────────────────────────────────────────────┐
│ Browser Client │
│ React + TypeScript + Vite │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ src/services/api.ts (centralised HTTP client) │ │
│ └─────────────────────────────────────────────────┘ │
└──────────────────────────┬──────────────────────────────┘
│ HTTP / REST
▼
┌─────────────────────────────────────────────────────────┐
│ FastAPI Backend │
│ │
│ ┌──────────┐ ┌─────────────┐ ┌───────────────────┐ │
│ │ /api/ │ │ /api/ │ │ /api/ │ │
│ │ health │ │ documents │ │ chat │ │
│ └──────────┘ └─────────────┘ └────────┬──────────┘ │
│ │ │
│ ┌────────────────────────────────────────▼────────┐ │
│ │ LangGraph Agent │ │
│ │ │ │
│ │ Input query │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ Router Node (Gemini decides) │ │
│ │ │ │ │
│ │ ┌───┴──────────────┬───────────────────┐ │ │
│ │ ▼ ▼ ▼ │ │
│ │ RAG Tool Summarizer Tool Web Search │ │
│ │ │ │ Tool (Tavily) │ │
│ │ └──────────────────┴───────────────────┘ │ │
│ │ │ │ │
│ │ Final Answer │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
┌────────────┴────────────┐
▼ ▼
┌─────────────────────┐ ┌───────────────────────────┐
│ FAISS Vector Store │ │ Local File Storage │
│ data/vector_store/ │ │ data/uploads/ │
│ Sentence Transformers └───────────────────────────┘
│ all-MiniLM-L6-v2 │
└─────────────────────┘
- Single-page application with sophisticated dark theme
- Modular components (
ChatWindow,DocumentUploader,ToolBadge) - Custom React Hooks (
useChat,useDocuments) for state management - Communicates exclusively through
src/services/api.ts VITE_API_BASE_URLenvironment variable controls the backend origin- Vite dev-server proxy eliminates CORS friction during development
app/main.py— Application factory. Creates the FastAPI instance, registers middleware, mounts routers, and handles the application lifespan.app/core/config.py—Settingsclass (Pydantic Settings). Single source of truth for all environment-driven configuration.app/core/logging.py— Centralised logging setup. Secrets are never logged.app/api/health.py—GET /api/healthliveness probe.app/api/documents.py— Upload, list, delete, and semantic search routes.app/api/chat.py— Agent chat route with configuration/provider error handling.app/models/schemas.py— Pydantic response models shared across routes.app/services/— Business logic layer between API routes and AI components.app/agent/— LangGraphStateGraphdefinition and typed state.app/tools/— Individual tool implementations.app/rag/— Vector store management, loaders, and chunking.
The agent uses a StateGraph with conditional routing. The implementation has one
agent node and one tool node; the LLM's AIMessage.tool_calls determines whether the
conditional edge enters the tool node or reaches END:
graph = StateGraph(AgentState)
graph.add_node("agent", agent_node)
graph.add_node("tools", tool_node)
graph.add_conditional_edges("agent", should_continue)
graph.add_edge("tools", "agent")- User uploads PDF/DOCX
langchain.document_loadersparses the fileRecursiveCharacterTextSplitterchunks the textsentence-transformers/all-MiniLM-L6-v2produces embeddings locally- Vectors stored persistently in FAISS at
data/vector_store/ - At query time: top-K chunks retrieved and injected into the LLM context
| Tool | Implementation | Phase |
|---|---|---|
| RAG Retriever | FAISS + LangChain | 2 |
| Doc Summarizer | LangChain map-reduce | 2 |
| Web Search | Tavily API | 3 |
User message
│
▼
POST /api/chat
│
▼
LangGraph agent receives query + conversation history
│
▼
Router node (Gemini) classifies intent:
- "Answer directly" → LLM generates response
- "Need documents" → RAG Retriever tool
- "Need summary" → Summarizer tool
- "Need web info" → Web Search tool
- "Multi-step" → chain multiple tools
│
▼
Tool(s) called → results returned
│
▼
Final answer synthesized with citations
│
▼
HTTP `ChatResponse` returned to frontend
AgentState.messages uses LangGraph's add_messages reducer so new turns append to
the checkpointed conversation. active_document_ids is supplied on every API request
and is overwritten for that invocation, so a previous document selection does not leak
into a later turn. tool_trace and sources are reset per response while messages
remain persistent. The session ID becomes the checkpointer thread_id; New Chat clears
the frontend session ID and starts a new thread on the next request.
Tools create source objects directly. The tool node appends them to AgentState.sources.
AgentService validates and deduplicates them, then returns them in ChatResponse.
Document sources use (document_id, chunk_id) for deduplication; web sources use URL.
The trace contains only observable tool name, status, and description. No model scratchpad,
system prompt, or provider secret is returned.
- API keys are loaded exclusively from environment variables — never hardcoded.
- Secrets are filtered from logs.
.envfiles are git-ignored.- The backend can boot without AI API keys in Phase 1.
- AI key validation occurs lazily when AI functionality is first used.
- Upload extensions are allowlisted, filenames are sanitized, stored names are UUID-prefixed, and the configured upload-size limit is enforced while streaming the file to disk.
- Uploaded text is treated as untrusted content by summarization prompts; it is not executed.
- External source links use
target="_blank"withrel="noopener noreferrer".
| Phase | Scope | Status |
|---|---|---|
| 1 | Repository structure, FastAPI, config, logging, tests, React shell | Complete |
| 2 | Document upload, PDF/DOCX parsing, FAISS RAG pipeline | Complete |
| 3 | LangGraph agent, Gemini integration, Tavily web search, chat UI | Complete |
| 4 | Conversation memory, structured sources, citations UI | Complete |
# Backend
cd backend
python -m venv .venv && .venv\Scripts\activate
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000
# Frontend
cd frontend
npm install && npm run devTaskPilot uses an explicit LangGraph StateGraph for multi-tool LLM routing:
flowchart TD
U[User Request] --> A[Agent Node]
A --> C{LLM requested tools?}
C -->|No| E[END]
C -->|Yes| T[Tool Execution]
T --> R[RAG Retriever]
T --> S[Document Summarizer]
T --> W[Web Search]
R --> A
S --> A
W --> A
- AgentState: typed dict tracking
messages,active_document_ids,tool_trace, andsources. - Message Flow: Human messages feed into the Gemini LLM. If the LLM generates
tool_calls, a conditional edge (should_continue) routes to thetool_node. - Tool Execution: Tools run asynchronously and append
ToolMessageresults to the state. - Checkpointer: State is preserved across turns via
langgraph-checkpoint-sqlite(orMemorySaver), enabling conversation continuity persession_id. Graph and checkpointer creation is lazy so importing the API does not initialize them. - Source Tracking: Isolated from internal model reasoning; actual metadata (document chunks or web URLs) is tracked in the
sourcesstate field explicitly by the tool nodes. - Loop Protection: Built-in LangGraph recursion limits prevent runaway multi-tool loops.
The FastAPI application uses AgentService as a clean bridge to the LangGraph compiled state graph:
- Client: Sends
POST /api/chatwithmessage,session_id, anddocument_ids. - FastAPI: Validates payload schemas and invokes
run_agent(). - AgentService:
- Generates/validates
session_idwhich strictly maps to LangGraphthread_idfor memory. - Converts HTTP
document_idsinto request-scopedactive_document_idsfor the state, actively overriding stale scope from previous turns. - Invokes
agent_graph. - Extracts the final text string from the model's message.
- Deduplicates sources by URL/chunk.
- Derives
tools_usedfrom the observabletool_trace.
- Generates/validates
- AgentState Memory: SQLite persistence safely bridges context seamlessly across separate HTTP requests.