| name | agents-towards-production |
| description | Build production-ready GenAI agents with stateful workflows, vector memory, deployment, and orchestration using LangGraph and LangChain |
| triggers | ["how do I build a production-ready AI agent","show me how to deploy an agent with LangGraph","how to add memory to my AI agent","create a multi-agent system with coordination","how do I add web search to my agent","deploy an agent with Docker and FastAPI","implement agent observability and monitoring","build a RAG agent for production"] |
Agents Towards Production
Skill by ara.so — AI Agent Skills collection.
This skill enables you to build production-grade GenAI agents from prototype to enterprise deployment. The repository provides 28+ end-to-end tutorials covering stateful workflows, vector memory, real-time web search, Docker deployment, FastAPI endpoints, security guardrails, GPU scaling, browser automation, multi-agent coordination, observability, evaluation, and UI development.
What It Does
Agents Towards Production is a comprehensive tutorial collection for building real-world AI agents that scale. It covers:
- Agent Frameworks: LangGraph, LangChain for stateful workflows and orchestration
- Memory Systems: Vector storage with Redis, Mem0 for persistent agent memory
- RAG Integration: Retrieval-augmented generation with Contextual AI
- Web Access: Real-time search APIs (Tavily), web scraping (Bright Data)
- Deployment: Docker, FastAPI, GPU scaling, production infrastructure
- Security: Guardrails, OAuth2, human-in-the-loop controls (Arcade)
- Multi-Agent: Coordination, orchestration, distributed workflows
- Observability: Monitoring, evaluation, debugging production agents
- UI Development: Browser automation, user interfaces
Installation
Clone the repository:
git clone https://github.com/NirDiamant/agents-towards-production.git
cd agents-towards-production
Install dependencies (each tutorial has its own requirements):
pip install langchain langgraph langchain-openai langchain-community
pip install redis langchain-redis mem0ai
pip install contextual-client chromadb sentence-transformers
pip install tavily-python brightdata-sdk
pip install fastapi uvicorn docker pydantic
pip install langsmith weave opentelemetry
Key Tutorials and Usage Patterns
1. Basic LangGraph Agent
Create a stateful agent with LangGraph:
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, END
from typing import TypedDict, List
import os
class AgentState(TypedDict):
messages: List[dict]
current_step: str
llm = ChatOpenAI(
model="gpt-4",
api_key=os.getenv("OPENAI_API_KEY")
)
def process_input(state: AgentState):
"""Process user input"""
messages = state["messages"]
response = llm.invoke(messages)
return {
"messages": messages + [{"role": "assistant", "content": response.content}],
"current_step": "completed"
}
workflow = StateGraph(AgentState)
workflow.add_node("process", process_input)
workflow.set_entry_point("process")
workflow.add_edge("process", END)
app = workflow.compile()
result = app.invoke({
"messages": [{"role": "user", "content": "Hello, how can you help me?"}],
"current_step": "start"
})
print(result["messages"][-][])
2. Agent with Vector Memory (Redis)
Add persistent memory to your agent:
from langchain_redis import RedisVectorStore, RedisConfig
from langchain_openai import OpenAIEmbeddings
from langchain.schema import Document
import os
redis_config = RedisConfig(
index_name="agent_memory",
redis_url=os.getenv("REDIS_URL", "redis://localhost:6379"),
distance_metric="COSINE"
)
embeddings = OpenAIEmbeddings(api_key=os.getenv("OPENAI_API_KEY"))
vector_store = RedisVectorStore(
config=redis_config,
embedding=embeddings
)
def store_memory(user_id: str, conversation: str, metadata: dict = None):
"""Store conversation in vector memory"""
doc = Document(
page_content=conversation,
metadata={"user_id": user_id, **(metadata or {})}
)
vector_store.add_documents([doc])
def retrieve_memory(user_id: str, query: str, k: int = 3):
"""Retrieve relevant past conversations"""
results = vector_store.similarity_search(
query,
k=k,
filter={"user_id": user_id}
)
return [doc.page_content for doc in results]
store_memory(
user_id="user123",
conversation="User asked about Python tutorials. Agent provided resources.",
metadata={: , : }
)
memories = retrieve_memory(, )
(, memories)
3. RAG Agent with Contextual AI
Build a retrieval-augmented generation agent:
from contextual_client import ContextualClient
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
import os
contextual = ContextualClient(api_key=os.getenv("CONTEXTUAL_API_KEY"))
kb = contextual.create_knowledge_base(
name="product_docs",
description="Product documentation and FAQs"
)
documents = [
{"content": "Our API supports REST and GraphQL endpoints.", "metadata": {"type": "api"}},
{"content": "Authentication uses OAuth2 with JWT tokens.", "metadata": {"type": "auth"}},
]
contextual.index_documents(knowledge_base_id=kb.id, documents=documents)
def rag_query(question: str):
"""Query with retrieval-augmented generation"""
results = contextual.search(
knowledge_base_id=kb.id,
query=question,
top_k=3
)
context = "\n".join([r.content for r in results])
prompt = ChatPromptTemplate.from_template("""
Answer the question based on the following context:
Context: {context}
Question: {question}
Answer:
""")
llm = ChatOpenAI(model="gpt-4", api_key=os.getenv("OPENAI_API_KEY"))
chain = prompt | llm
response = chain.invoke({: context, : question})
response.content
answer = rag_query()
(answer)
4. Web Search Agent with Tavily
Add real-time web search capabilities:
from tavily import TavilyClient
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
import os
tavily = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
def web_search_agent(query: str):
"""Agent with real-time web search"""
search_results = tavily.search(
query=query,
search_depth="advanced",
max_results=5,
include_domains=None,
exclude_domains=None
)
context = "\n\n".join([
f"Source: {r['url']}\n{r['content']}"
for r in search_results.get('results', [])
])
prompt = ChatPromptTemplate.from_template("""
Based on the following web search results, answer the user's question:
Search Results:
{context}
Question: {question}
Provide a comprehensive answer with sources:
""")
llm = ChatOpenAI(model="gpt-4", api_key=os.getenv("OPENAI_API_KEY"))
chain = prompt | llm
response = chain.invoke({"context": context, "question": query})
return {
"answer": response.content,
"sources": [r['url'] for r in search_results.get(, [])]
}
result = web_search_agent()
(result[])
(, result[])
5. Multi-Agent Coordination
Coordinate multiple specialized agents:
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from typing import TypedDict, List, Literal
import os
class MultiAgentState(TypedDict):
messages: List[dict]
task: str
current_agent: str
results: dict
llm = ChatOpenAI(model="gpt-4", api_key=os.getenv("OPENAI_API_KEY"))
def research_agent(state: MultiAgentState):
"""Research specialist"""
prompt = f"Research the following: {state['task']}"
response = llm.invoke([{"role": "user", "content": prompt}])
state["results"]["research"] = response.content
return state
def analysis_agent(state: MultiAgentState):
"""Analysis specialist"""
research = state["results"].get("research", "")
prompt = f"Analyze this research:\n{research}"
response = llm.invoke([{"role": "user", "content": prompt}])
state["results"]["analysis"] = response.content
state
():
analysis = state[].get(, )
prompt =
response = llm.invoke([{: , : prompt}])
state[][] = response.content
state
() -> [, , , ]:
state[]:
state[]:
state[]:
workflow = StateGraph(MultiAgentState)
workflow.add_node(, research_agent)
workflow.add_node(, analysis_agent)
workflow.add_node(, synthesis_agent)
workflow.set_entry_point()
workflow.add_edge(, )
workflow.add_edge(, )
workflow.add_edge(, END)
app = workflow.()
result = app.invoke({
: [],
: ,
: ,
: {}
})
(, result[][])
6. FastAPI Deployment
Deploy your agent as a REST API:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, END
from typing import List, Dict
import os
import uvicorn
app = FastAPI(title="Production Agent API")
class ChatRequest(BaseModel):
message: str
user_id: str
session_id: str
class ChatResponse(BaseModel):
response: str
session_id: str
metadata: Dict
class AgentState(BaseModel):
messages: List[Dict]
user_id: str
session_id: str
llm = ChatOpenAI(model="gpt-4", api_key=os.getenv("OPENAI_API_KEY"))
def create_agent():
"""Create agent workflow"""
def process(state: dict):
messages = state["messages"]
response = llm.invoke(messages)
state[].append({
: ,
: response.content
})
state
workflow = StateGraph()
workflow.add_node(, process)
workflow.set_entry_point()
workflow.add_edge(, END)
workflow.()
agent = create_agent()
():
:
result = agent.invoke({
: [{: , : request.message}],
: request.user_id,
: request.session_id
})
ChatResponse(
response=result[][-][],
session_id=request.session_id,
metadata={: request.user_id}
)
Exception e:
HTTPException(status_code=, detail=(e))
():
{: }
__name__ == :
uvicorn.run(app, host=, port=)
7. Docker Deployment
Deploy with Docker:
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . .
# Expose port
EXPOSE 8000
# Environment variables (set via docker run or docker-compose)
ENV OPENAI_API_KEY=""
ENV REDIS_URL="redis://redis:6379"
# Run application
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
version: '3.8'
services:
agent:
build: .
ports:
- "8000:8000"
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
- REDIS_URL=redis://redis:6379
- TAVILY_API_KEY=${TAVILY_API_KEY}
depends_on:
- redis
restart: unless-stopped
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
restart: unless-stopped
volumes:
redis_data:
Deploy:
docker-compose up -d
docker-compose logs -f agent
docker-compose up -d --scale agent=3
8. Agent with Observability
Add monitoring and observability:
from langsmith import Client
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, END
import os
from datetime import datetime
langsmith_client = Client(api_key=os.getenv("LANGSMITH_API_KEY"))
class ObservableAgent:
def __init__(self, project_name: str):
self.project_name = project_name
self.llm = ChatOpenAI(
model="gpt-4",
api_key=os.getenv("OPENAI_API_KEY")
)
def create_workflow(self):
"""Create observable workflow"""
def process_with_tracing(state: dict):
run_id = langsmith_client.create_run(
name="agent_process",
run_type="chain",
inputs={"messages": state["messages"]},
project_name=self.project_name
)
try:
response = self.llm.invoke(state["messages"])
state["messages"].append({
"role": "assistant",
: response.content
})
langsmith_client.update_run(
run_id=run_id.,
outputs={: response.content},
end_time=datetime.now()
)
Exception e:
langsmith_client.update_run(
run_id=run_id.,
error=(e),
end_time=datetime.now()
)
state
workflow = StateGraph()
workflow.add_node(, process_with_tracing)
workflow.set_entry_point()
workflow.add_edge(, END)
workflow.()
agent = ObservableAgent(project_name=)
app = agent.create_workflow()
result = app.invoke({
: [{: , : }]
})
Configuration
Key environment variables:
export OPENAI_API_KEY="your-openai-key"
export ANTHROPIC_API_KEY="your-anthropic-key"
export REDIS_URL="redis://localhost:6379"
export PINECONE_API_KEY="your-pinecone-key"
export TAVILY_API_KEY="your-tavily-key"
export BRIGHTDATA_API_KEY="your-brightdata-key"
export CONTEXTUAL_API_KEY="your-contextual-key"
export LANGSMITH_API_KEY="your-langsmith-key"
export LANGSMITH_PROJECT="your-project-name"
export ARCADE_API_KEY="your-arcade-key"
export REDIS_HOST="localhost"
export REDIS_PORT="6379"
export API_PORT="8000"
Common Patterns
Agent with Tools
from langchain.tools import Tool
from langchain.agents import initialize_agent, AgentType
from langchain_openai import ChatOpenAI
import os
def calculator(expression: str) -> str:
"""Evaluate mathematical expression"""
try:
return str(eval(expression))
except:
return "Invalid expression"
def web_search(query: str) -> str:
"""Search the web"""
return f"Results for: {query}"
tools = [
Tool(
name="Calculator",
func=calculator,
description="Evaluate mathematical expressions"
),
Tool(
name="WebSearch",
func=web_search,
description="Search the web for information"
)
]
llm = ChatOpenAI(model="gpt-4", api_key=os.getenv("OPENAI_API_KEY"))
agent = initialize_agent(
tools,
llm,
agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
verbose=True
)
result = agent.run("What is 25 * 17? Then search for information about AI agents.")
Streaming Responses
from langchain_openai import ChatOpenAI
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
llm = ChatOpenAI(
model="gpt-4",
api_key=os.getenv("OPENAI_API_KEY"),
streaming=True,
callbacks=[StreamingStdOutCallbackHandler()]
)
for chunk in llm.stream("Tell me about AI agents"):
print(chunk.content, end="", flush=True)
Error Handling and Retries
from tenacity import retry, stop_after_attempt, wait_exponential
from langchain_openai import ChatOpenAI
import os
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=4, max=10)
)
def call_llm_with_retry(prompt: str):
"""Call LLM with automatic retries"""
llm = ChatOpenAI(model="gpt-4", api_key=os.getenv("OPENAI_API_KEY"))
return llm.invoke([{"role": "user", "content": prompt}])
try:
response = call_llm_with_retry("Hello, how are you?")
print(response.content)
except Exception as e:
print(f"Failed after retries: {e}")
Troubleshooting
Common Issues
Agent not responding:
import os
assert os.getenv("OPENAI_API_KEY"), "OPENAI_API_KEY not set"
from langchain.globals import set_verbose
set_verbose(True)
Memory not persisting:
import redis
r = redis.from_url(os.getenv("REDIS_URL", "redis://localhost:6379"))
r.ping()
from langchain_redis import RedisVectorStore
Rate limiting:
from langchain.llms import OpenAI
from langchain.callbacks import get_openai_callback
with get_openai_callback() as cb:
print(f"Total Tokens: {cb.total_tokens}")
print(f"Total Cost: ${cb.total_cost}")
Docker deployment issues:
docker-compose logs -f agent
curl http://localhost:8000/health
docker-compose exec agent env | grep API_KEY
LangGraph state not updating:
from typing import TypedDict
from langgraph.graph import StateGraph
class State(TypedDict):
messages: list
def node(state: State):
return {"messages": state["messages"] + [new_message]}
Additional Resources
Best Practices
- Always use environment variables for API keys and secrets
- Implement retry logic for external API calls
- Add observability from day one (LangSmith, traces)
- Use typed state in LangGraph for better debugging
- Implement health checks in production deployments
- Store conversation history in vector databases for context
- Set token limits to control costs
- Test with different LLM models for cost/performance tradeoffs
- Add input validation to prevent prompt injection
- Monitor usage and costs in production