Quick Start
Let’s build your first AI agent in under 5 minutes.
What You’ll Build
A simple agent that responds to messages with streaming AI responses. By the end, you’ll have a working /api/chat endpoint.
Install
pip install django-ai-sdkThe Haystack runtime is a plain dependency, not an extra: the adapters, pipelines and generators are Haystack components. Everything else is opt-in, so you install only what you use:
| Extra | Brings | Cost |
|---|---|---|
qdrant / chroma |
a vector store for RAG, plus fastembed | chroma also pulls kubernetes, grpc and uvicorn[standard] |
rag |
both vector stores | |
mcp, files |
MCP integrations, document extraction | small |
anthropic, mistral, ollama, openrouter, huggingface |
one hosted provider each (see Generators) | small |
providers |
all five of those | small |
transformers |
runs a Hugging Face model locally | pulls torch (~500 MB) |
all |
everything above |
Each optional feature lives in its own module that imports its package at the top,
and those modules are loaded on first use - so a Qdrant project never installs
chromadb, and an OpenAI project never installs mistral. Reach for a feature whose
extra is missing and you get the import error naming the package, e.g.
No module named 'haystack_integrations.document_stores.qdrant'; the table above
says which extra provides it.
Streaming needs an ASGI server, but the SDK does not choose one for you: install
daphne, uvicorn or hypercorn yourself. Nothing the demo project happens to use -
allauth, DRF, django-cors-headers, a django-tasks backend - is a dependency of the
SDK either.
The haystack extra pulls in Haystack and the components agents run on. That’s all you need to start.
Want the full set of extras (MCP, vector stores, document parsing, providers)?
pip install "django-ai-sdk[all]"Ready-made HTTP endpoints are separate, so you only pull in the framework you use:
pip install "django-ai-sdk[ninja]" # or: django-ai-sdk[drf]See Views and Routing.
Configure Django
Add the app to your INSTALLED_APPS:
# settings.py
INSTALLED_APPS = [
# ... your apps
"django_ai_sdk",
]Run migrations:
python manage.py migrateThis creates tables for conversation storage. You won’t need to think about them: the SDK handles it automatically.
Create an Agent
An agent is just a Python class with personality:
# agents.py
from django.conf import settings
from django_ai_sdk import Agent
from django_ai_sdk.adapters.base import Stream
from django_ai_sdk.agents import ToolAgent, ToolAgentConfig, auto_register
from django_ai_sdk.common import prompt
from django_ai_sdk.generators import openai_responses_chat
from django_ai_sdk.protocols.vercel import VercelProtocolHandler
from django_ai_sdk.storage.db import DbStorageAdapter
@auto_register
class ShakespeareAgent(Agent):
"""An agent that speaks like Shakespeare."""
name = "Shakespeare Bot"
model = settings.AI_SDK_DEFAULT_MODEL
instructions = prompt(
"You are a helpful assistant who speaks in Shakespearean English. "
"Use thee, thou, and other Elizabethan expressions. "
"Be poetic but always answer the user's question."
)
protocol = VercelProtocolHandler
storage_adapter = DbStorageAdapter
llm = openai_responses_chat
async def get_pipeline_adapter(self, thread_id=None, user=None):
generator = self.get_llm()
tool_agent = ToolAgent(
config=ToolAgentConfig(
model=self.get_model(),
system_prompt=self.get_system_prompt(),
tools=await self.get_tools(thread_id=thread_id or "", user=user),
),
generator=generator,
)
return Stream(
pipeline=tool_agent.pipeline(),
generator=generator,
storage_adapter=await self.get_storage_adapter(thread_id),
)Three things to note:
- name: What to call your agent
- instructions: How it should behave (the system prompt)
- get_pipeline_adapter: Returns the
Streamthat powers streaming chat - llm: The generator factory
get_llm()builds - it defaults toopenai_responses_chat, so you can leave it out for OpenAI
openai_responses_chat works with any endpoint that speaks the OpenAI Responses API. Point OPENAI_API_URL at a local server (vLLM, llama.cpp) or leave it unset for OpenAI, and use openai_chat for a Chat Completions-only endpoint. Other providers get their own factory - see Generators. Every AI_SDK_* setting is listed in the Settings Reference.
That’s it. No complex configuration. No framework setup.
Register Your Agent
Your agent must be registered before it can be used. Add it to AI_SDK_AGENTS in your settings.py:
# settings.py
AI_SDK_AGENTS = [
"your_app.agents.ShakespeareAgent",
]The SDK loads these classes at startup. Each agent gets a stable UUID derived from its module and class name, so you can retrieve it anywhere:
from django_ai_sdk.agents.services import AgentService
# Get an agent by its stable ID
agent = await AgentService.get(agent_id)Agents are also registered automatically by the @auto_register decorator: AI_SDK_AGENTS just makes sure the module is imported.
Wire It to a View
Connect your agent to Django Ninja:
# views.py
from ninja import Router
from django_ai_sdk.agents.services import AgentService
from django_ai_sdk.views.schemas import ChatRequest
router = Router()
@router.post("/chat")
async def chat(request, payload: ChatRequest):
"""Chat with Shakespeare Bot."""
agent = await AgentService.get(payload.agent_id)
return await agent.as_view(payload.messages, user=request.user)The as_view() method handles everything:
- Protocol conversion (Vercel AI SDK Data Stream Protocol in,
ChatMessages internally) - Building the pipeline adapter and streaming to the model
- Returning an SSE
StreamingHttpResponse
Add to URLs
# urls.py
from ninja import NinjaAPI
from your_app.views import router
api = NinjaAPI()
api.add_router("/", router)
urlpatterns = [
path("api/", api.urls),
]Test It
Start your server:
python manage.py runserverSend a message:
curl -X POST http://localhost:8000/api/chat \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{
"messages": [{"role": "user", "parts": [{"type": "text", "text": "Hello!"}]}],
"agent_id": "your-agent-uuid"
}'You’ll see a streaming response:
data: {"type":"start","messageId":"msg_abc123"}
data: {"type":"text-start","id":"text_001"}
data: {"type":"text-delta","id":"text_001","delta":"Hark!"}
data: {"type":"text-delta","id":"text_001","delta":" Good"}
data: {"type":"text-delta","id":"text_001","delta":" morrow"}
data: {"type":"text-delta","id":"text_001","delta":" to"}
data: {"type":"text-delta","id":"text_001","delta":" thee!"}
data: {"type":"text-end","id":"text_001"}
data: {"type":"finish"}
data: [DONE]You did it! Your agent is live and streaming responses.
as_view() converted the protocol messages to internal ChatMessages, get_pipeline_adapter() built a ToolAgent pipeline and returned a Stream, Haystack streamed chunks normalized into StreamEvents, and the events were serialized to the Vercel protocol over SSE. All in about 50 lines of code.