ResearchAI is a multi-chat research assistant. You type a question, and it answers using Gemini, using your uploaded notes when relevant and calling tools such as the current time or a calculator when needed. Uploaded .txt and .md files are split, embedded, and stored so later questions can retrieve the matching passages before the model replies.
Browser
→ chat page, /chat/, /upload/
→ views.py
├─ upload: save file → ingest_document()
│ load → split → embed → Chroma
└─ chat: load history → generate_response()
→ LangGraph
START → retrieve → model
model → tools → model (only if the model requested a tool)
model → END (when there is no tool call)
.env.example # environment variable template; copy to .env
.gitignore # ignores .env, the database, uploads, and Chroma data
manage.py # Django command entry point
Procfile # production start command for Railway
requirements.txt # pinned Python dependencies
config/ # Django project package
├── settings.py # settings, API keys, Chroma path, upload limit
├── urls.py # mounts the chat app at /
├── wsgi.py # production WSGI entry used by gunicorn
├── asgi.py # ASGI entry; not used by the current server
├── templates/chat.html # chat page markup
└── static/
├── css/chat.css # page styles
└── js/
├── chat.js # page behavior, including drag and drop
├── api.js # /chat/ and /upload/ requests
├── state.js # chat list saved in the browser
└── markdown.js # render assistant markdown
chat/ # the application
├── views.py # page, chat API, and upload API
├── urls.py # /, /chat/, and /upload/ routes
├── models.py # Conversation and Message tables
├── admin.py # registers models in the Django admin
├── apps.py # Django app config
├── file_upload.py # checks and saves an uploaded file
├── migrations/ # database schema changes
├── langchain_backend/ # live answer and ingestion path
│ ├── services.py # LangGraph and generate_response()
│ ├── nodes.py # retrieve, model, and tool nodes
│ ├── tools.py # current time and calculator
│ ├── rag.py # search existing Chroma data
│ └── ingestion.py # load, split, embed, and store a file
└── manual_backend/ # earlier handwritten version, not used by the page
├── services.py # handwritten agent loop and generate_response()
├── tools.py # current time and calculator declarations
├── document_loader.py # read and split a .txt or .md file
├── embeddings.py # turn text into Gemini vectors
├── vector_store.py # save chunk vectors in Chroma
├── retrieval.py # search Chroma and filter by distance
└── ingestion.py # load, split, embed, and store a file
doc/example/ # sample notes for trying retrieval
├── sample_research_notes.txt # plain-text sample
└── sample_research_notes.md # Markdown sample
## 1.2 Workflow
<aside>
💡
Basic app → LLM → Conversation feature→ Tools → RAG → LangChain → LangGraph → Deployment
</aside>
Example: We are making a little teaching robot helper.
1. **Basic app:** You build a little box where you can type and see an answer.
2. **LLM API:** You connect the box to the “robot brain.”
3. **Conversation features:** Now the robot remembers what you said a moment ago.
4. **Tool calling:** Now the robot gets hands. It can use tools, like a calculator, a database, or another API
5. **RAG:** Now you give the robot a bookshelf. It can look inside your PDFs or documents before answering.
6. **LangChain:** LangChain is like a box of Lego pieces that helps you connect the robot brain, tools, documents, and prompts more easily.
7. **LangGraph:** Now you can give the robot a real map: `start → search → check → maybe search again → answer`. It helps with more complicated workflows.