# Cookbook Practical recipes for common Atomic Agents use cases. ## Quick Reference | Recipe | Description | |--------|-------------| | [Basic Chatbot](#basic-chatbot) | Simple conversational agent | | [Chatbot with Memory](#chatbot-with-memory) | Agent that remembers context | | [Custom Output Schema](#custom-output-schema) | Structured responses | | [Multi-Provider Agent](#multi-provider-agent) | Switch between LLM providers | | [Agent with Tools](#agent-with-tools) | Agent using external tools | | [Streaming Chatbot](#streaming-chatbot) | Real-time response streaming | | [Research Agent](#research-agent) | Multi-step research workflow | | [RAG Agent](#rag-agent) | Retrieval-augmented generation | ## Basic Chatbot A minimal chatbot implementation. ```python """ Basic Chatbot Recipe A simple conversational agent that responds to user messages. Requirements: - pip install atomic-agents openai - Set OPENAI_API_KEY environment variable """ import os import instructor import openai from atomic_agents import AtomicAgent, AgentConfig, BasicChatInputSchema, BasicChatOutputSchema from atomic_agents.context import ChatHistory def create_basic_chatbot(): """Create a basic chatbot agent.""" client = instructor.from_openai(openai.OpenAI()) agent = AtomicAgent[BasicChatInputSchema, BasicChatOutputSchema]( config=AgentConfig( client=client, model="gpt-5-mini", history=ChatHistory() ) ) return agent def chat_loop(agent): """Interactive chat loop.""" print("Chatbot ready! Type 'quit' to exit.\n") while True: user_input = input("You: ").strip() if user_input.lower() in ['quit', 'exit', 'q']: print("Goodbye!") break if not user_input: continue response = agent.run(BasicChatInputSchema(chat_message=user_input)) print(f"Bot: {response.chat_message}\n") if __name__ == "__main__": agent = create_basic_chatbot() chat_loop(agent) ``` ## Chatbot with Memory Agent that maintains conversation history across turns. ```python """ Chatbot with Memory Recipe Demonstrates conversation history and context retention. Requirements: - pip install atomic-agents openai - Set OPENAI_API_KEY environment variable """ import os import instructor import openai from atomic_agents import AtomicAgent, AgentConfig, BasicChatInputSchema, BasicChatOutputSchema from atomic_agents.context import ChatHistory, SystemPromptGenerator def create_memory_chatbot(): """Create chatbot with memory and custom personality.""" client = instructor.from_openai(openai.OpenAI()) # Initialize history with a greeting history = ChatHistory() greeting = BasicChatOutputSchema( chat_message="Hello! I'm your personal assistant. I'll remember our conversation. How can I help?" ) history.add_message("assistant", greeting) # Custom system prompt system_prompt = SystemPromptGenerator( background=[ "You are a friendly, helpful personal assistant.", "You have an excellent memory and always remember details from the conversation.", "You refer back to previous messages when relevant." ], steps=[ "Review the conversation history for context.", "Provide helpful, personalized responses.", "Remember any names, preferences, or facts the user shares." ], output_instructions=[ "Be conversational and friendly.", "Reference previous context when appropriate.", "Ask follow-up questions to engage the user." ] ) agent = AtomicAgent[BasicChatInputSchema, BasicChatOutputSchema]( config=AgentConfig( client=client, model="gpt-5-mini", history=history, system_prompt_generator=system_prompt ) ) return agent def save_history(agent, filename="chat_history.json"): """Save conversation history to file.""" import json history_data = agent.history.dump() with open(filename, 'w') as f: json.dump(history_data, f, indent=2) print(f"History saved to {filename}") def load_history(agent, filename="chat_history.json"): """Load conversation history from file.""" import json try: with open(filename, 'r') as f: history_data = json.load(f) agent.history.load(history_data) print(f"History loaded from {filename}") except FileNotFoundError: print("No previous history found") if __name__ == "__main__": agent = create_memory_chatbot() # Demonstrate memory print("Testing memory...") response1 = agent.run(BasicChatInputSchema(chat_message="My name is Alice and I love Python")) print(f"Bot: {response1.chat_message}\n") response2 = agent.run(BasicChatInputSchema(chat_message="What's my name and favorite language?")) print(f"Bot: {response2.chat_message}\n") # Save for later save_history(agent) ``` ## Custom Output Schema Agent with structured output including metadata. ```python """ Custom Output Schema Recipe Agent that returns structured responses with confidence and sources. Requirements: - pip install atomic-agents openai - Set OPENAI_API_KEY environment variable """ import os from typing import List from pydantic import Field import instructor import openai from atomic_agents import AtomicAgent, AgentConfig, BasicChatInputSchema, BaseIOSchema from atomic_agents.context import ChatHistory, SystemPromptGenerator class StructuredOutputSchema(BaseIOSchema): """Structured response with metadata.""" answer: str = Field(..., description="The main answer to the question") confidence: float = Field( ..., ge=0.0, le=1.0, description="Confidence score from 0.0 to 1.0" ) key_points: List[str] = Field( default_factory=list, description="Key points summarizing the answer" ) follow_up_questions: List[str] = Field( default_factory=list, description="3 suggested follow-up questions" ) def create_structured_agent(): """Create agent with structured output.""" client = instructor.from_openai(openai.OpenAI()) system_prompt = SystemPromptGenerator( background=[ "You are a knowledgeable assistant that provides structured responses.", "You always assess your confidence in answers." ], steps=[ "Analyze the question thoroughly.", "Formulate a clear, accurate answer.", "Identify 3-5 key points.", "Assess your confidence (0.0-1.0).", "Generate 3 relevant follow-up questions." ], output_instructions=[ "Provide accurate, well-researched answers.", "Be honest about confidence level.", "Key points should be concise bullet points.", "Follow-up questions should explore the topic deeper." ] ) agent = AtomicAgent[BasicChatInputSchema, StructuredOutputSchema]( config=AgentConfig( client=client, model="gpt-5-mini", history=ChatHistory(), system_prompt_generator=system_prompt ) ) return agent def display_response(response: StructuredOutputSchema): """Pretty-print the structured response.""" print(f"\n{'='*60}") print(f"Answer: {response.answer}") print(f"\nConfidence: {response.confidence:.0%}") print(f"\nKey Points:") for point in response.key_points: print(f" - {point}") print(f"\nFollow-up Questions:") for i, q in enumerate(response.follow_up_questions, 1): print(f" {i}. {q}") print(f"{'='*60}\n") if __name__ == "__main__": agent = create_structured_agent() response = agent.run(BasicChatInputSchema( chat_message="What are the main benefits of using Python for data science?" )) display_response(response) ``` ## Multi-Provider Agent Switch between different LLM providers dynamically. ```python """ Multi-Provider Agent Recipe Agent that can use different LLM providers based on configuration. Requirements: - pip install atomic-agents openai anthropic groq - Set API keys for providers you want to use """ import os from enum import Enum from typing import Optional import instructor from atomic_agents import AtomicAgent, AgentConfig, BasicChatInputSchema, BasicChatOutputSchema from atomic_agents.context import ChatHistory class Provider(Enum): OPENAI = "openai" ANTHROPIC = "anthropic" GROQ = "groq" OLLAMA = "ollama" def get_client(provider: Provider): """Get instructor client for specified provider.""" if provider == Provider.OPENAI: from openai import OpenAI return instructor.from_openai(OpenAI()), "gpt-5-mini" elif provider == Provider.ANTHROPIC: from anthropic import Anthropic return instructor.from_anthropic(Anthropic()), "claude-3-5-haiku-20241022" elif provider == Provider.GROQ: from groq import Groq return instructor.from_groq(Groq(), mode=instructor.Mode.JSON), "mixtral-8x7b-32768" elif provider == Provider.OLLAMA: from openai import OpenAI client = instructor.from_openai( OpenAI(base_url="http://localhost:11434/v1", api_key="ollama"), mode=instructor.Mode.JSON ) return client, "llama3" raise ValueError(f"Unknown provider: {provider}") def create_agent(provider: Provider) -> AtomicAgent: """Create agent with specified provider.""" client, model = get_client(provider) return AtomicAgent[BasicChatInputSchema, BasicChatOutputSchema]( config=AgentConfig( client=client, model=model, history=ChatHistory() ) ) class MultiProviderAgent: """Agent that can switch between providers.""" def __init__(self, default_provider: Provider = Provider.OPENAI): self.current_provider = default_provider self.agent = create_agent(default_provider) def switch_provider(self, provider: Provider): """Switch to a different provider.""" self.current_provider = provider self.agent = create_agent(provider) print(f"Switched to {provider.value}") def run(self, message: str) -> str: """Run agent with current provider.""" response = self.agent.run(BasicChatInputSchema(chat_message=message)) return response.chat_message if __name__ == "__main__": agent = MultiProviderAgent(Provider.OPENAI) # Use OpenAI print(f"Using: {agent.current_provider.value}") response = agent.run("Hello! What model are you?") print(f"Response: {response}\n") # Switch to Groq (if available) try: agent.switch_provider(Provider.GROQ) response = agent.run("Hello! What model are you?") print(f"Response: {response}") except Exception as e: print(f"Could not switch to Groq: {e}") ``` ## Agent with Tools Agent that uses tools to extend capabilities. ```python """ Agent with Tools Recipe Agent that uses a calculator tool for mathematical operations. Requirements: - pip install atomic-agents openai sympy - Set OPENAI_API_KEY environment variable """ import os from pydantic import Field import instructor import openai from atomic_agents import AtomicAgent, AgentConfig, BaseTool, BaseToolConfig, BaseIOSchema from atomic_agents.context import ChatHistory, SystemPromptGenerator # Define Calculator Tool class CalculatorInputSchema(BaseIOSchema): """Input for calculator.""" expression: str = Field(..., description="Mathematical expression to evaluate") class CalculatorOutputSchema(BaseIOSchema): """Output from calculator.""" result: float = Field(..., description="Calculation result") expression: str = Field(..., description="Original expression") class CalculatorTool(BaseTool[CalculatorInputSchema, CalculatorOutputSchema]): """Simple calculator tool.""" def run(self, params: CalculatorInputSchema) -> CalculatorOutputSchema: try: # Safe evaluation using sympy from sympy import sympify result = float(sympify(params.expression)) return CalculatorOutputSchema( result=result, expression=params.expression ) except Exception as e: raise ValueError(f"Could not evaluate: {params.expression}. Error: {e}") # Agent output that can use tools class AgentOutputSchema(BaseIOSchema): """Agent response that may include tool usage.""" message: str = Field(..., description="Response message") needs_calculation: bool = Field( default=False, description="Whether a calculation is needed" ) calculation_expression: str = Field( default="", description="Expression to calculate if needed" ) def create_tool_agent(): """Create agent with tool capability.""" client = instructor.from_openai(openai.OpenAI()) calculator = CalculatorTool() system_prompt = SystemPromptGenerator( background=[ "You are a helpful assistant with calculation capabilities.", "When the user asks for calculations, indicate what needs to be calculated." ], steps=[ "Determine if the request involves mathematical calculation.", "If yes, set needs_calculation to true and provide the expression.", "Provide a helpful response message." ], output_instructions=[ "For math questions, extract the expression to calculate.", "Always be helpful and explain your response." ] ) agent = AtomicAgent[BasicChatInputSchema, AgentOutputSchema]( config=AgentConfig( client=client, model="gpt-5-mini", history=ChatHistory(), system_prompt_generator=system_prompt ) ) return agent, calculator def process_with_tools(agent, calculator, user_message: str) -> str: """Process message, using tools as needed.""" # Get agent response response = agent.run(BasicChatInputSchema(chat_message=user_message)) # Check if calculation is needed if response.needs_calculation and response.calculation_expression: try: calc_result = calculator.run( CalculatorInputSchema(expression=response.calculation_expression) ) return f"{response.message}\n\nCalculation: {calc_result.expression} = {calc_result.result}" except ValueError as e: return f"{response.message}\n\nCalculation error: {e}" return response.message if __name__ == "__main__": agent, calculator = create_tool_agent() # Test with calculation result = process_with_tools( agent, calculator, "What is 15% of 250?" ) print(result) ``` ## Streaming Chatbot Real-time streaming responses. ```python """ Streaming Chatbot Recipe Chatbot that streams responses in real-time. Requirements: - pip install atomic-agents openai rich - Set OPENAI_API_KEY environment variable """ import os import asyncio import instructor from openai import AsyncOpenAI from rich.console import Console from rich.live import Live from atomic_agents import AtomicAgent, AgentConfig, BasicChatInputSchema, BasicChatOutputSchema from atomic_agents.context import ChatHistory console = Console() def create_streaming_agent(): """Create agent configured for streaming.""" # Use async client for streaming client = instructor.from_openai(AsyncOpenAI()) agent = AtomicAgent[BasicChatInputSchema, BasicChatOutputSchema]( config=AgentConfig( client=client, model="gpt-5-mini", history=ChatHistory() ) ) return agent async def stream_response(agent, message: str): """Stream agent response with live display.""" console.print(f"\n[bold blue]You:[/bold blue] {message}") console.print("[bold green]Bot:[/bold green] ", end="") with Live("", refresh_per_second=10, console=console) as live: current_text = "" async for partial in agent.run_async_stream( BasicChatInputSchema(chat_message=message) ): if partial.chat_message: current_text = partial.chat_message live.update(current_text) console.print() # Newline after response async def streaming_chat_loop(agent): """Interactive streaming chat loop.""" console.print("[bold]Streaming Chatbot[/bold]") console.print("Type 'quit' to exit.\n") while True: user_input = console.input("[bold blue]You:[/bold blue] ").strip() if user_input.lower() in ['quit', 'exit', 'q']: console.print("Goodbye!") break if not user_input: continue await stream_response(agent, user_input) if __name__ == "__main__": agent = create_streaming_agent() asyncio.run(streaming_chat_loop(agent)) ``` ## Research Agent Multi-step research workflow. ```python """ Research Agent Recipe Agent that performs multi-step research by generating queries and synthesizing results. Requirements: - pip install atomic-agents openai - Set OPENAI_API_KEY environment variable """ import os from typing import List from pydantic import Field import instructor import openai from atomic_agents import AtomicAgent, AgentConfig, BaseIOSchema from atomic_agents.context import ChatHistory, SystemPromptGenerator, BaseDynamicContextProvider # Schemas class ResearchQuerySchema(BaseIOSchema): """Input for generating research queries.""" topic: str = Field(..., description="Research topic") num_queries: int = Field(default=3, ge=1, le=5) class GeneratedQueriesSchema(BaseIOSchema): """Output with generated search queries.""" queries: List[str] = Field(..., description="Generated search queries") reasoning: str = Field(..., description="Why these queries were chosen") class SynthesisInputSchema(BaseIOSchema): """Input for synthesizing research.""" original_topic: str = Field(..., description="Original research topic") query: str = Field(..., description="Ask a question about the research") class SynthesisOutputSchema(BaseIOSchema): """Synthesized research output.""" summary: str = Field(..., description="Research summary") key_findings: List[str] = Field(..., description="Key findings") confidence: float = Field(..., ge=0.0, le=1.0) # Context Provider for Research Results class ResearchResultsProvider(BaseDynamicContextProvider): """Provides research results as context.""" def __init__(self): super().__init__(title="Research Results") self.results: List[dict] = [] def add_result(self, query: str, result: str): self.results.append({"query": query, "result": result}) def clear(self): self.results = [] def get_info(self) -> str: if not self.results: return "No research results available yet." output = [] for i, r in enumerate(self.results, 1): output.append(f"Query {i}: {r['query']}") output.append(f"Result: {r['result']}") output.append("") return "\n".join(output) class ResearchAgent: """Multi-step research agent.""" def __init__(self): self.client = instructor.from_openai(openai.OpenAI()) self.results_provider = ResearchResultsProvider() # Query generation agent self.query_agent = AtomicAgent[ResearchQuerySchema, GeneratedQueriesSchema]( config=AgentConfig( client=self.client, model="gpt-5-mini", system_prompt_generator=SystemPromptGenerator( background=["You generate effective search queries for research."], steps=["Analyze the topic.", "Generate diverse, specific queries."], output_instructions=["Queries should cover different aspects."] ) ) ) # Synthesis agent self.synthesis_agent = AtomicAgent[SynthesisInputSchema, SynthesisOutputSchema]( config=AgentConfig( client=self.client, model="gpt-5-mini", system_prompt_generator=SystemPromptGenerator( background=["You synthesize research findings into clear summaries."], steps=["Review the research results.", "Identify key patterns.", "Synthesize findings."], output_instructions=["Be comprehensive but concise."] ) ) ) self.synthesis_agent.register_context_provider("research", self.results_provider) def generate_queries(self, topic: str, num_queries: int = 3) -> List[str]: """Generate research queries for a topic.""" response = self.query_agent.run( ResearchQuerySchema(topic=topic, num_queries=num_queries) ) print(f"Generated queries: {response.queries}") print(f"Reasoning: {response.reasoning}") return response.queries def add_research_result(self, query: str, result: str): """Add a research result (from search, database, etc.).""" self.results_provider.add_result(query, result) def synthesize(self, topic: str, question: str) -> SynthesisOutputSchema: """Synthesize research into a summary.""" return self.synthesis_agent.run( SynthesisInputSchema(original_topic=topic, query=question) ) if __name__ == "__main__": researcher = ResearchAgent() # Step 1: Generate queries topic = "Benefits of renewable energy" queries = researcher.generate_queries(topic) # Step 2: Simulate adding research results # (In practice, you'd search and add real results) researcher.add_research_result( queries[0], "Solar energy has seen 89% cost reduction since 2010." ) researcher.add_research_result( queries[1], "Wind power now provides 10% of global electricity." ) # Step 3: Synthesize synthesis = researcher.synthesize(topic, "What are the main benefits?") print(f"\n{'='*60}") print(f"Summary: {synthesis.summary}") print(f"\nKey Findings:") for finding in synthesis.key_findings: print(f" - {finding}") print(f"\nConfidence: {synthesis.confidence:.0%}") ``` ## RAG Agent Retrieval-augmented generation pattern. ```python """ RAG Agent Recipe Agent that retrieves relevant context before generating responses. Requirements: - pip install atomic-agents openai chromadb - Set OPENAI_API_KEY environment variable """ import os from typing import List from pydantic import Field import instructor import openai from atomic_agents import AtomicAgent, AgentConfig, BasicChatInputSchema, BaseIOSchema from atomic_agents.context import ChatHistory, SystemPromptGenerator, BaseDynamicContextProvider class RAGOutputSchema(BaseIOSchema): """RAG agent output with sources.""" answer: str = Field(..., description="Answer based on retrieved context") sources_used: List[int] = Field( default_factory=list, description="Indices of sources used (1-indexed)" ) confidence: float = Field(..., ge=0.0, le=1.0) class RetrievedContextProvider(BaseDynamicContextProvider): """Provides retrieved documents as context.""" def __init__(self): super().__init__(title="Retrieved Documents") self.documents: List[str] = [] def set_documents(self, docs: List[str]): self.documents = docs def clear(self): self.documents = [] def get_info(self) -> str: if not self.documents: return "No relevant documents found." output = [] for i, doc in enumerate(self.documents, 1): output.append(f"[Document {i}]: {doc}") return "\n\n".join(output) class SimpleVectorStore: """Simple in-memory vector store for demonstration.""" def __init__(self): self.documents: List[str] = [] def add_documents(self, docs: List[str]): self.documents.extend(docs) def search(self, query: str, top_k: int = 3) -> List[str]: """Simple keyword-based search (replace with real embeddings).""" query_words = set(query.lower().split()) scored = [] for doc in self.documents: doc_words = set(doc.lower().split()) score = len(query_words & doc_words) scored.append((score, doc)) scored.sort(reverse=True) return [doc for _, doc in scored[:top_k]] class RAGAgent: """Retrieval-Augmented Generation agent.""" def __init__(self): self.client = instructor.from_openai(openai.OpenAI()) self.vector_store = SimpleVectorStore() self.context_provider = RetrievedContextProvider() self.agent = AtomicAgent[BasicChatInputSchema, RAGOutputSchema]( config=AgentConfig( client=self.client, model="gpt-5-mini", history=ChatHistory(), system_prompt_generator=SystemPromptGenerator( background=[ "You are a helpful assistant that answers questions based on provided documents.", "Only use information from the retrieved documents to answer." ], steps=[ "Review the retrieved documents carefully.", "Find relevant information to answer the question.", "Cite which documents you used." ], output_instructions=[ "Base your answer only on the provided documents.", "If the documents don't contain the answer, say so.", "Always cite your sources by document number." ] ) ) ) self.agent.register_context_provider("documents", self.context_provider) def add_documents(self, documents: List[str]): """Add documents to the knowledge base.""" self.vector_store.add_documents(documents) def query(self, question: str, top_k: int = 3) -> RAGOutputSchema: """Query with retrieval-augmented generation.""" # Retrieve relevant documents relevant_docs = self.vector_store.search(question, top_k) self.context_provider.set_documents(relevant_docs) # Generate response response = self.agent.run(BasicChatInputSchema(chat_message=question)) return response if __name__ == "__main__": rag = RAGAgent() # Add knowledge base rag.add_documents([ "Python was created by Guido van Rossum and first released in 1991.", "Python emphasizes code readability with significant whitespace.", "Python supports multiple programming paradigms including procedural, object-oriented, and functional.", "The Python Package Index (PyPI) hosts over 400,000 packages.", "Python is widely used in data science, machine learning, and web development." ]) # Query response = rag.query("Who created Python and when?") print(f"Answer: {response.answer}") print(f"Sources used: {response.sources_used}") print(f"Confidence: {response.confidence:.0%}") ``` ## Summary These recipes demonstrate common patterns: | Pattern | Key Components | Use Case | |---------|---------------|----------| | Basic Chatbot | AtomicAgent, ChatHistory | Simple Q&A | | Memory | ChatHistory persistence | Context retention | | Custom Schema | BaseIOSchema subclass | Structured output | | Multi-Provider | Provider switching | Flexibility | | Tools | BaseTool | Extended capabilities | | Streaming | run_async_stream | Real-time UX | | Research | Multiple agents | Complex workflows | | RAG | Context providers | Knowledge-augmented | Combine these patterns to build sophisticated AI applications.