Foundation
Your first chat agent using Agent, Backend, and Tool modules
This step-by-step guide shows how to start with your first agent and progressively add tools, debugging, reasoning, knowledge, and more. Each section introduces a single capability, so you can follow the full path or jump to the parts most useful to you.
Here’s a quick map of the stages and modules:
Foundation
Your first chat agent using Agent, Backend, and Tool modules
Debugging
Add logging and monitoring to debug your agent’s behavior
Requirements
Add reasoning rules, guardrails, and user permissions
Knowledge
Ground your agent in data with RAG capabilities
Orchestration
Coordinate teams of specialized agents with Workflows
Production
Scale with caching and error handling
Integration
Expose agents as services (MCP, Agent Stack, A2A, IBM wxO)
pip install 'beeai-framework[wikipedia]'ollama pull granite3.3Let’s start with the simplest possible agent - one that can respond to messages.
import asynciofrom beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.backend import ChatModel
async def main(): agent = RequirementAgent( llm=ChatModel.from_name("ollama:granite3.3"), role="friendly AI assistant", instructions="Be helpful and conversational in all your interactions." )
response = await agent.run("Hello! What can you help me with?") print(response.last_message.text)
if __name__ == "__main__": asyncio.run(main())import { RequirementAgent } from "beeai-framework/agents/requirement/agent";import { ChatModel } from "beeai-framework/backend/chat";
const agent = new RequirementAgent({ llm: await ChatModel.fromName("ollama:granite3.3"), role: "friendly AI assistant", instructions: "Be helpful and conversational in all your interactions.",});
const response = await agent.run({ prompt: "Hello! What can you help me with?" });console.log(response.result.text);Try it:
simple_agent.pypython simple_agent.pyTroubleshooting
Verify it’s running: ollama list
Start the service: ollama serve
Pull the model: ollama pull granite3.3
List available models: ollama list
Create an alias: If your granite model doesn’t have the name granite3.3 give it the alias by trying this command in your terminal ollama cp <existing model name> <alias>
Update to the latest version: pip install --upgrade beeai-framework
Check Python version: python --version (must be >= 3.11)
Give your agent the ability to access real-world information, external systems, or running code by adding tools.
import asynciofrom beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.backend import ChatModelfrom beeai_framework.tools.search.wikipedia import WikipediaToolfrom beeai_framework.tools.weather import OpenMeteoTool
async def main(): agent = RequirementAgent( llm=ChatModel.from_name("ollama:granite3.3"), role="friendly AI assistant", instructions="Be helpful and conversational in all your interactions. Use your tools to find accurate, current information.", tools=[WikipediaTool(), OpenMeteoTool()], )
response = await agent.run("What's the current weather in New York and tell me about the history of the city?") print(response.last_message.text)
if __name__ == "__main__": asyncio.run(main())import { RequirementAgent } from "beeai-framework/agents/requirement/agent";import { ChatModel } from "beeai-framework/backend/chat";import { WikipediaTool } from "beeai-framework/tools/search/wikipedia";import { OpenMeteoTool } from "beeai-framework/tools/weather/openMeteo";
const agent = new RequirementAgent({ llm: await ChatModel.fromName("ollama:granite3.3"), role: "friendly AI assistant", instructions: "Be helpful and conversational in all your interactions. Use your tools to find accurate, current information.", tools: [new WikipediaTool(), new OpenMeteoTool()],});
const response = await agent.run({ prompt: "What's the current weather in New York and tell me about the history of the city?",});console.log(response.result.text);Try these prompts:
Knowing what your application is doing is essential from the very start. The BeeAI Framework is based on the event system; each component in the framework emits events throughout its execution. You can listen and alter these events to build custom logic.
The most simple way to see what’s happening in your application is by using GlobalTrajectoryMiddleware which listens to all events and prints them to the console.
import asynciofrom beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.backend import ChatModelfrom beeai_framework.tools.weather import OpenMeteoToolfrom beeai_framework.middleware.trajectory import GlobalTrajectoryMiddlewarefrom beeai_framework.tools import Tool
async def main(): agent = RequirementAgent( llm=ChatModel.from_name("ollama:granite3.3"), tools=[OpenMeteoTool()] )
response = await agent.run("What's the current weather in Paris?").middleware( GlobalTrajectoryMiddleware(included=[Tool])) # Only show tool executions
print(response.last_message.text)
if __name__ == "__main__": asyncio.run(main())import { RequirementAgent } from "beeai-framework/agents/requirement/agent";import { ChatModel } from "beeai-framework/backend/chat";import { OpenMeteoTool } from "beeai-framework/tools/weather/openMeteo";import { GlobalTrajectoryMiddleware } from "beeai-framework/middleware/trajectory";import { Tool } from "beeai-framework/tools/base";
const agent = new RequirementAgent({ llm: await ChatModel.fromName("ollama:granite3.3"), tools: [new OpenMeteoTool()],});
const response = await agent .run({ prompt: "What's the current weather in Paris?" }) .middleware(new GlobalTrajectoryMiddleware({ included: [Tool] })); // Only show tool executions
console.log(response.result.text);Sometimes you want to react to specific events. To see which events are emitted, you can use the on function.
import asynciofrom beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.backend import ChatModelfrom beeai_framework.tools.weather import OpenMeteoToolfrom beeai_framework.middleware.trajectory import GlobalTrajectoryMiddlewarefrom beeai_framework.tools import Tool
async def main(): agent = RequirementAgent( llm=ChatModel.from_name("ollama:granite3.3"), tools=[OpenMeteoTool()] )
response = await agent.run("What's the current weather in Paris?").on("*.*", lambda data, event: print(event.name, data))
print(response.last_message.text)
if __name__ == "__main__": asyncio.run(main())import { RequirementAgent } from "beeai-framework/agents/requirement/agent";import { ChatModel } from "beeai-framework/backend/chat";import { OpenMeteoTool } from "beeai-framework/tools/weather/openMeteo";
const agent = new RequirementAgent({ llm: await ChatModel.fromName("ollama:granite3.3"), tools: [new OpenMeteoTool()],});
const response = await agent .run({ prompt: "What's the current weather in Paris?" }) .observe((emitter) => { emitter.match("*.*", (data, event) => console.log(event.name, data)); });
console.log(response.result.text);Alternatively, you can listen for events on the class itself rather than for a specific run.
import asynciofrom beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.backend import ChatModelfrom beeai_framework.tools.weather import OpenMeteoToolfrom typing import Anyfrom beeai_framework.emitter import EventMeta
async def main(): agent = RequirementAgent( llm=ChatModel.from_name("ollama:granite3.3"), tools=[OpenMeteoTool()] )
@agent.emitter.on("*.*") async def handle_event(data: Any, event: EventMeta): print(event.name, data)
response = await agent.run("What's the current weather in Paris?") print(response.last_message.text)
if __name__ == "__main__": asyncio.run(main())import { RequirementAgent } from "beeai-framework/agents/requirement/agent";import { ChatModel } from "beeai-framework/backend/chat";import { OpenMeteoTool } from "beeai-framework/tools/weather/openMeteo";
const agent = new RequirementAgent({ llm: await ChatModel.fromName("ollama:granite3.3"), tools: [new OpenMeteoTool()],});
agent.emitter.match("*.*", (data, event) => { console.log(event.name, data);});
const response = await agent.run({ prompt: "What's the current weather in Paris?" });console.log(response.result.text);Logging is a way to provide visibility into the state of your application. Set the Logger level of granularity and place logging statements at key points throughout your agent process.
import asynciofrom beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.backend import ChatModelfrom beeai_framework.tools.search.wikipedia import WikipediaToolfrom beeai_framework.tools.weather import OpenMeteoToolfrom beeai_framework.middleware.trajectory import GlobalTrajectoryMiddlewarefrom beeai_framework.tools import Toolfrom beeai_framework.logger import Logger
async def main(): # You create the logger and decide what to log logger = Logger("my-agent", level="TRACE")
logger.info("Starting agent application")
agent = RequirementAgent( llm=ChatModel.from_name("ollama:granite3.3"), role="friendly AI assistant", instructions="Be helpful and conversational in all your interactions. Use your tools to find accurate, current information.", tools=[WikipediaTool(), OpenMeteoTool()] )
logger.debug("About to process user message")
# The `included` parameter filters what types of operations to trace: # - [Tool]: Show only tool executions (function calls, API calls, etc.) # - [ChatModel]: Show only LLM calls (model inference, token usage) # - [Tool, ChatModel]: Show both tools and LLM interactions # - [] or None: Show everything (agents, tools, models, requirements) response = await agent.run( "What's the weather in Paris and tell me about the Eiffel Tower?" ).middleware(GlobalTrajectoryMiddleware(included=[Tool]))
logger.info("Agent response generated")
print(response.last_message.text)
if __name__ == "__main__": asyncio.run(main())import { RequirementAgent } from "beeai-framework/agents/requirement/agent";import { ChatModel } from "beeai-framework/backend/chat";import { WikipediaTool } from "beeai-framework/tools/search/wikipedia";import { OpenMeteoTool } from "beeai-framework/tools/weather/openMeteo";import { GlobalTrajectoryMiddleware } from "beeai-framework/middleware/trajectory";import { Tool } from "beeai-framework/tools/base";import { Logger } from "beeai-framework/logger/logger";
// Set up pretty logging to the consoleLogger.defaults.pretty = true;
// You create the logger and decide what to logconst logger = new Logger({ name: "my-agent", level: "trace" });
logger.info("Starting agent application");
const agent = new RequirementAgent({ llm: await ChatModel.fromName("ollama:granite3.3"), role: "friendly AI assistant", instructions: "Be helpful and conversational in all your interactions. Use your tools to find accurate, current information.", tools: [new WikipediaTool(), new OpenMeteoTool()],});
logger.debug("About to process user message");
// The `included` parameter filters what types of operations to trace:// - [Tool]: Show only tool executions (function calls, API calls, etc.)// - [ChatModel]: Show only LLM calls (model inference, token usage)// - [Tool, ChatModel]: Show both tools and LLM interactions// - [] or undefined: Show everything (agents, tools, models, requirements)const response = await agent .run({ prompt: "What's the weather in Paris and tell me about the Eiffel Tower?" }) .middleware(new GlobalTrajectoryMiddleware({ included: [Tool] }));
logger.info("Agent response generated");
console.log(response.result.text);Logger Output: Traditional log messages with timestamps
2024-01-15 10:30:45 | INFO | my-agent - Starting agent application2024-01-15 10:30:45 | DEBUG | my-agent - About to process user message2024-01-15 10:30:47 | INFO | my-agent - Agent response generated successfullyLogging to the console is great for development, but it’s not enough for production monitoring. You can easily let the framework send traces and metrics to external platforms like Arize Phoenix, LangFuse, LangSmith, and more.
Set the OTEL_EXPORTER_OTLP_ENDPOINT environment variable. Some vendors also need API kesy like OTEL_EXPORTER_OTLP_HEADERS="authorization=Bearer <token>
export OTEL_EXPORTER_OTLP_ENDPOINT="https://your-otel-endpoint"$env:OTEL_EXPORTER_OTLP_ENDPOINT="https://your-otel-endpoint"from openinference.instrumentation.beeai import BeeAIInstrumentorfrom opentelemetry import trace as trace_apifrom opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporterfrom opentelemetry.sdk import trace as trace_sdkfrom opentelemetry.sdk.resources import Resourcefrom opentelemetry.sdk.trace.export import SimpleSpanProcessor # Use BatchSpanProcessor in prod
def setup_observability() -> None: tracer_provider = trace_sdk.TracerProvider(resource=Resource.create({})) tracer_provider.add_span_processor(SimpleSpanProcessor(OTLPSpanExporter())) trace_api.set_tracer_provider(tracer_provider) BeeAIInstrumentor().instrument() # auto-instruments BeeAI Framework
# Call this BEFORE creating/running any BeeAI agentssetup_observability()// Note: OpenTelemetry instrumentation for TypeScript is coming soon// For now, use standard OpenTelemetry setup with manual instrumentationRun your application and you should see traces and metrics in your selected dashboard.
RequirementAgentUse requirements to control the agent’s behavior. Let’s add the ThinkTool and set up a ConditionalRequirement to enforce rules on when and how tools should be used.
import asynciofrom beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.agents.requirement.requirements.conditional import ConditionalRequirementfrom beeai_framework.backend import ChatModelfrom beeai_framework.tools.search.wikipedia import WikipediaToolfrom beeai_framework.tools.weather import OpenMeteoToolfrom beeai_framework.tools.think import ThinkToolfrom beeai_framework.middleware.trajectory import GlobalTrajectoryMiddlewarefrom beeai_framework.tools import Toolfrom beeai_framework.logger import Logger
async def main(): logger = Logger("my-agent", level="TRACE") logger.info("Starting agent application")
agent = RequirementAgent( llm=ChatModel.from_name("ollama:granite3.3"), role="friendly AI assistant", instructions="Be helpful and conversational in all your interactions. Use your tools to find accurate, current information.", tools=[WikipediaTool(), OpenMeteoTool(), ThinkTool()], requirements=[ # Force agent to think before acting, and after each tool use ConditionalRequirement( ThinkTool, force_at_step=1, # Always think first force_after=Tool, # Think after using any tool consecutive_allowed=False # Don't think twice in a row ) ], middlewares=[GlobalTrajectoryMiddleware(included=[Tool])] ) logger.debug("About to process user message") response = await agent.run( "What's the weather in Paris and tell me about the Eiffel Tower?" ) logger.info("Agent response generated") logger.info(response.last_message.text)
if __name__ == "__main__": asyncio.run(main())import { RequirementAgent } from "beeai-framework/agents/requirement/agent";import { ConditionalRequirement } from "beeai-framework/agents/requirement/requirements/conditional";import { ChatModel } from "beeai-framework/backend/chat";import { WikipediaTool } from "beeai-framework/tools/search/wikipedia";import { OpenMeteoTool } from "beeai-framework/tools/weather/openMeteo";import { ThinkTool } from "beeai-framework/tools/think";import { GlobalTrajectoryMiddleware } from "beeai-framework/middleware/trajectory";import { Tool } from "beeai-framework/tools/base";import { Logger } from "beeai-framework/logger/logger";
const logger = new Logger({ name: "my-agent", level: "trace" });logger.info("Starting agent application");
const agent = new RequirementAgent({ llm: await ChatModel.fromName("ollama:granite3.3"), role: "friendly AI assistant", instructions: "Be helpful and conversational in all your interactions. Use your tools to find accurate, current information.", tools: [new WikipediaTool(), new OpenMeteoTool(), new ThinkTool()], requirements: [ // Force agent to think before acting, and after each tool use new ConditionalRequirement(ThinkTool, { forceAtStep: 1, // Always think first forceAfter: [Tool], // Think after using any tool consecutiveAllowed: false, // Don't think twice in a row }), ], middlewares: [new GlobalTrajectoryMiddleware({ included: [Tool] })],});
logger.debug("About to process user message");const response = await agent.run({ prompt: "What's the weather in Paris and tell me about the Eiffel Tower?",});logger.info("Agent response generated");logger.info(response.result.text);AskPermissionRequirementAdd user permission for when you want an action to be human validated before being executed:
import asynciofrom beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.agents.requirement.requirements.conditional import ConditionalRequirementfrom beeai_framework.agents.experimental.requirements.ask_permission import AskPermissionRequirementfrom beeai_framework.backend import ChatModelfrom beeai_framework.tools.search.wikipedia import WikipediaToolfrom beeai_framework.tools.weather import OpenMeteoToolfrom beeai_framework.tools.think import ThinkToolfrom beeai_framework.middleware.trajectory import GlobalTrajectoryMiddlewarefrom beeai_framework.tools import Toolfrom beeai_framework.logger import Logger
async def main(): logger = Logger("my-agent", level="TRACE") logger.info("Starting agent application")
agent = RequirementAgent( llm=ChatModel.from_name("ollama:granite3.3:8b"), role="friendly AI assistant", instructions="Be helpful and conversational in all your interactions. Use your tools to find accurate, current information.", tools=[WikipediaTool(), OpenMeteoTool(), ThinkTool()], requirements=[ ConditionalRequirement( ThinkTool, force_at_step=1, force_after=Tool, consecutive_allowed=False ), AskPermissionRequirement([OpenMeteoTool]) # Ask before using weather API ] ) logger.debug("About to process user message") response = await agent.run( "What's the weather in Paris?" ).middleware(GlobalTrajectoryMiddleware(included=[Tool])) logger.info("Agent response generated") print(response.last_message.text)
if __name__ == "__main__": asyncio.run(main())// Note: AskPermissionRequirement is not yet implemented in TypeScript// Coming soonIn the console, the output looks like:
Do you allow it? (yes/no): yesNow it’s time to integrate data. from a vector store using RAG (retrieval augmented generation)
Let’s give your agent access to a knowledge base of documents.
step1_knowledge_basefile_paths with your own filesimport asynciofrom beeai_framework.backend.document_loader import DocumentLoaderfrom beeai_framework.backend.embedding import EmbeddingModelfrom beeai_framework.backend.text_splitter import TextSplitterfrom beeai_framework.backend.vector_store import VectorStore
async def setup_knowledge_base(): # Create embedding model using Ollama embedding_model = EmbeddingModel.from_name("ollama:nomic-embed-text")
# Create vector store vector_store = VectorStore.from_name( "beeai:TemporalVectorStore", embedding_model=embedding_model )
# Setup text splitter for chunking documents text_splitter = TextSplitter.from_name( "langchain:RecursiveCharacterTextSplitter", chunk_size=1000, chunk_overlap=200 )
return vector_store, text_splitter
async def load_documents(vector_store, text_splitter, file_paths): """Load documents into the vector store""" all_chunks = []
for file_path in file_paths: try: # Load the document loader = DocumentLoader.from_name( "langchain:UnstructuredMarkdownLoader", file_path=file_path ) documents = await loader.load()
# Split into chunks chunks = await text_splitter.split_documents(documents) all_chunks.extend(chunks) print(f"Loaded {len(chunks)} chunks from {file_path}") except Exception as e: print(f"Failed to load {file_path}: {e}")
# Add all chunks to vector store if all_chunks: await vector_store.add_documents(all_chunks) print(f"Total chunks added: {len(all_chunks)}")
return vector_store if all_chunks else None
async def main(): # Setup the knowledge base vector_store, text_splitter = await setup_knowledge_base()
# Replace with your actual markdown files file_paths = [ "your_document1.md", #replace these documents with the path to your local document "your_document2.md", #replace these documents with the path to your local document ]
# Load documents loaded_vector_store = await load_documents(vector_store, text_splitter, file_paths)
if loaded_vector_store: print("Knowledge base ready!") return loaded_vector_store else: print("No documents loaded") return None
if __name__ == "__main__": # Run this first to setup your knowledge base asyncio.run(main())// Note: TypeScript does not yet have VectorStore, DocumentLoader, or TextSplitter equivalents.// Knowledge base / RAG setup via beeai-framework is currently Python-only.step1_knowledge_base file and uses the vector store setup in the previous stepfile_paths with your own pathsimport asynciofrom beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.backend import ChatModelfrom beeai_framework.tools.search.wikipedia import WikipediaToolfrom beeai_framework.tools.weather import OpenMeteoToolfrom beeai_framework.tools.search.retrieval import VectorStoreSearchTool
# Import the setup function from Step 1from step1_knowledge_base import setup_knowledge_base, load_documents
async def main(): # Setup knowledge base (from Step 1) vector_store, text_splitter = await setup_knowledge_base()
# Load your documents file_paths = [ "your_document1.md", #replace these documents with the path to your local document "your_document2.md", #replace these documents with the path to your local document ]
loaded_vector_store = await load_documents(vector_store, text_splitter, file_paths)
if not loaded_vector_store: print("No documents loaded - exiting") return
# Create RAG tool rag_tool = VectorStoreSearchTool(vector_store=loaded_vector_store)
# Create agent with RAG capabilities agent = RequirementAgent( llm=ChatModel.from_name("ollama:granite3.3"), tools=[WikipediaTool(), OpenMeteoTool(), rag_tool], instructions="""You are a knowledgeable assistant with access to: 1. A document knowledge base (use VectorStoreSearch for specific document queries) 2. Wikipedia for general facts 3. Weather information
When users ask about topics that might be in the documents, search your knowledge base first.""" )
# Test the RAG-enabled agent response = await agent.run("What information do you have in your knowledge base?") print(response.last_message.text)
if __name__ == "__main__": asyncio.run(main())// Note: VectorStoreSearchTool is not yet available in TypeScript.// RAG-enabled agents are currently Python-only in beeai-framework.Create a team of specialized agents that can collaborate:
import asynciofrom beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.backend import ChatModelfrom beeai_framework.memory import UnconstrainedMemoryfrom beeai_framework.tools.search.wikipedia import WikipediaToolfrom beeai_framework.tools.weather import OpenMeteoToolfrom beeai_framework.tools.handoff import HandoffToolfrom beeai_framework.tools.think import ThinkToolfrom beeai_framework.logger import Logger
async def main(): # Initialize logger logger = Logger("multi-agent-system", level="TRACE") logger.info("Starting multi-agent system")
# Create specialized agents logger.debug("Creating knowledge agent") knowledge_agent = RequirementAgent( llm=ChatModel.from_name("ollama:granite3.3"), tools=[ThinkTool(), WikipediaTool()], memory=UnconstrainedMemory(), instructions="Provide detailed, accurate information using available knowledge sources. Think through problems step by step." )
logger.debug("Creating weather agent") weather_agent = RequirementAgent( llm=ChatModel.from_name("ollama:granite3.3"), tools=[ThinkTool(), OpenMeteoTool()], memory=UnconstrainedMemory(), instructions="Provide comprehensive weather information and forecasts. Always think before using tools." )
# Create a coordinator agent that manages handoffs logger.debug("Creating coordinator agent") coordinator_agent = RequirementAgent( llm=ChatModel.from_name("ollama:granite3.3"), memory=UnconstrainedMemory(), tools=[ HandoffTool( target=knowledge_agent, name="knowledge_specialist", description="For general knowledge and research questions" ), HandoffTool( target=weather_agent, name="weather_expert", description="For weather-related queries" ), ], instructions="""You coordinate between specialist agents. - For weather queries: use weather_expert - For research/knowledge questions: use knowledge_specialist - For mixed queries: break them down and use multiple specialists
Always introduce yourself and explain which specialist will help.""" )
logger.info("Running query: What's the weather in Paris and tell me about its history?") try: response = await coordinator_agent.run("What's the weather in Paris and tell me about its history?") logger.info("Query completed successfully") print(response.last_message.text) except Exception as e: logger.error(f"Error during agent execution: {e}") raise
logger.info("Multi-agent system execution completed")
if __name__ == "__main__": asyncio.run(main())import { RequirementAgent } from "beeai-framework/agents/requirement/agent";import { OllamaChatModel } from "beeai-framework/adapters/ollama/backend/chat";import { UnconstrainedMemory } from "beeai-framework/memory/unconstrainedMemory";import { WikipediaTool } from "beeai-framework/tools/search/wikipedia";import { OpenMeteoTool } from "beeai-framework/tools/weather/openMeteo";import { HandoffTool } from "beeai-framework/tools/handoff";import { ThinkTool } from "beeai-framework/tools/think";
const llm = new OllamaChatModel("granite3.3");
// Create specialized agentsconst knowledgeAgent = new RequirementAgent({ llm, tools: [new ThinkTool(), new WikipediaTool()], memory: new UnconstrainedMemory(), instructions: "Provide detailed, accurate information using available knowledge sources. Think through problems step by step.",});
const weatherAgent = new RequirementAgent({ llm, tools: [new ThinkTool(), new OpenMeteoTool()], memory: new UnconstrainedMemory(), instructions: "Provide comprehensive weather information and forecasts. Always think before using tools.",});
// Create a coordinator agent that manages handoffsconst coordinatorAgent = new RequirementAgent({ llm, memory: new UnconstrainedMemory(), tools: [ new HandoffTool(knowledgeAgent, { name: "knowledge_specialist", description: "For general knowledge and research questions", }), new HandoffTool(weatherAgent, { name: "weather_expert", description: "For weather-related queries", }), ], instructions: `You coordinate between specialist agents. - For weather queries: use weather_expert - For research/knowledge questions: use knowledge_specialist - For mixed queries: break them down and use multiple specialists
Always introduce yourself and explain which specialist will help.`,});
const response = await coordinatorAgent.run({ prompt: "What's the weather in Paris and tell me about its history?",});console.log(response.result.text);Now it’s time for production-grade features.
Caching helps you cut costs, reduce latency, and deliver consistent results by reusing previous computations. In BeeAI Framework, you can cache LLM responses and tool outputs.
1. Caching LLM Calls
Configure a cache on your LLM to avoid paying for repeated queries:
import asynciofrom beeai_framework.backend import ChatModel, UserMessagefrom beeai_framework.cache import SlidingCache
async def main(): # LLM with caching enabled llm = ChatModel.from_name("ollama:granite3.3") llm.config(cache=SlidingCache(size=50)) # Cache up to 50 responses
# Must send a list of messages to the llm messages = [UserMessage("Hello, how are you?")]
# First call (miss) res1 = await llm.run(messages) # Second call with identical input (hit) res2 = await llm.run(messages)
print("First:", res1.last_message.text) print("Second (cached):", res2.last_message.text)
asyncio.run(main())import { ChatModel } from "beeai-framework/backend/chat";import { SlidingCache } from "beeai-framework/cache/slidingCache";import { UserMessage } from "beeai-framework/backend/message";
// LLM with caching enabledconst llm = await ChatModel.fromName("ollama:granite3.3");llm.config({ cache: new SlidingCache({ size: 50 }) }); // Cache up to 50 responses
// Must send a list of messages to the llmconst messages = [new UserMessage("Hello, how are you?")];
// First call (miss)const res1 = await llm.create({ messages });// Second call with identical input (hit)const res2 = await llm.create({ messages });
console.log("First:", res1.getTextContent());console.log("Second (cached):", res2.getTextContent());2. Caching Tool Outputs
Many tools query APIs or perform expensive lookups. You can attach a cache directly:
from beeai_framework.tools.weather import OpenMeteoToolfrom beeai_framework.cache import UnconstrainedCache
# Cache all results from the weather APIweather_tool = OpenMeteoTool(options={"cache": UnconstrainedCache()})import { OpenMeteoTool } from "beeai-framework/tools/weather/openMeteo";import { UnconstrainedCache } from "beeai-framework/cache/unconstrainedCache";
// Cache all results from the weather APIconst weatherTool = new OpenMeteoTool({ cache: new UnconstrainedCache(),});3. Using Cached Components in an Agent
from beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.tools.weather import OpenMeteoToolfrom beeai_framework.cache import UnconstrainedCachefrom beeai_framework.memory import UnconstrainedMemoryfrom beeai_framework.backend import ChatModelfrom beeai_framework.cache import SlidingCacheimport asyncio
async def main(): llm = ChatModel.from_name("ollama:granite3.3") llm.config(cache=SlidingCache(size=50)) # Cache up to 50 responses
weather_tool = OpenMeteoTool(options={"cache": UnconstrainedCache()})
agent = RequirementAgent( llm=llm, # LLM with cache tools=[weather_tool], # Tool with cache memory=UnconstrainedMemory(), instructions="Provide answers efficiently using cached results when possible." )
response1 = await agent.run("What's the weather in New York?") response2 = await agent.run("What's the weather in New York?") # Weather tool cache should hit
asyncio.run(main())import { RequirementAgent } from "beeai-framework/agents/requirement/agent";import { ChatModel } from "beeai-framework/backend/chat";import { OpenMeteoTool } from "beeai-framework/tools/weather/openMeteo";import { UnconstrainedCache } from "beeai-framework/cache/unconstrainedCache";import { UnconstrainedMemory } from "beeai-framework/memory/unconstrainedMemory";import { SlidingCache } from "beeai-framework/cache/slidingCache";
const llm = await ChatModel.fromName("ollama:granite3.3");llm.config({ cache: new SlidingCache({ size: 50 }) }); // Cache up to 50 responses
const weatherTool = new OpenMeteoTool({ cache: new UnconstrainedCache(),});
const agent = new RequirementAgent({ llm, tools: [weatherTool], // Tool with cache memory: new UnconstrainedMemory(), instructions: "Provide answers efficiently using cached results when possible.",});
await agent.run({ prompt: "What's the weather in New York?" });await agent.run({ prompt: "What's the weather in New York?" }); // Weather tool cache should hitMake your system robust with comprehensive error management:
import asyncioimport tracebackfrom beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.backend import ChatModelfrom beeai_framework.memory import UnconstrainedMemoryfrom beeai_framework.tools.weather import OpenMeteoToolfrom beeai_framework.errors import FrameworkError
async def main(): try: agent = RequirementAgent( llm=ChatModel.from_name("ollama:granite3.3"), memory=UnconstrainedMemory(), tools=[OpenMeteoTool()], instructions="You provide weather information." )
response = await agent.run("What's the weather in Invalid-City-Name?") print(response.last_message.text)
except FrameworkError as e: print(f"Framework error occurred: {e.explain()}") traceback.print_exc() except Exception as e: print(f"Unexpected error: {e}") traceback.print_exc()
if __name__ == "__main__": asyncio.run(main())import { RequirementAgent } from "beeai-framework/agents/requirement/agent";import { ChatModel } from "beeai-framework/backend/chat";import { UnconstrainedMemory } from "beeai-framework/memory/unconstrainedMemory";import { OpenMeteoTool } from "beeai-framework/tools/weather/openMeteo";import { FrameworkError } from "beeai-framework/errors";
try { const agent = new RequirementAgent({ llm: await ChatModel.fromName("ollama:granite3.3"), memory: new UnconstrainedMemory(), tools: [new OpenMeteoTool()], instructions: "You provide weather information.", });
const response = await agent.run({ prompt: "What's the weather in Invalid-City-Name?" }); console.log(response.result.text);} catch (e) { if (e instanceof FrameworkError) { console.error(`Framework error occurred: ${e.explain()}`); } else { console.error(`Unexpected error: ${e}`); }}Expose your agent as an MCP server:
from beeai_framework.adapters.mcp import MCPServerfrom beeai_framework.tools.weather import OpenMeteoToolfrom beeai_framework.tools.search.wikipedia import WikipediaTool
def main(): # Create an MCP server server = MCPServer()
# Register tools that can be used by MCP clients server.register_many([ OpenMeteoTool(), WikipediaTool() ])
# Start the server server.serve()
if __name__ == "__main__": main()import { MCPServer } from "beeai-framework/adapters/mcp/serve/server";import { OpenMeteoTool } from "beeai-framework/tools/weather/openMeteo";import { WikipediaTool } from "beeai-framework/tools/search/wikipedia";
// Create an MCP serverconst server = new MCPServer();
// Register tools that can be used by MCP clientsserver.registerMany([new OpenMeteoTool(), new WikipediaTool()]);
// Start the serverawait server.serve();Expose your agent as a Agent Stack server:
from beeai_framework.adapters.agentstack.serve.server import AgentStackServerfrom beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.backend import ChatModelfrom beeai_framework.memory import UnconstrainedMemoryfrom beeai_framework.middleware.trajectory import GlobalTrajectoryMiddlewarefrom beeai_framework.tools.search.wikipedia import WikipediaToolfrom beeai_framework.tools.weather import OpenMeteoTool
def main(): llm = ChatModel.from_name("ollama:granite3.3") agent = RequirementAgent( llm=llm, tools=[WikipediaTool(), OpenMeteoTool()], memory=UnconstrainedMemory(), middlewares=[GlobalTrajectoryMiddleware()], instructions="You are a helpful research assistant with access to Wikipedia and weather data." )
# Runs HTTP server that registers to Agent Stack server = AgentStackServer(config={"configure_telemetry": False}) server.register(agent) server.serve()
if __name__ == "__main__": main()// AgentStack server support is not yet available in TypeScript.// Use the Python SDK for this integration.Expose your agent as an A2A server:
from beeai_framework.adapters.a2a import A2AServer, A2AServerConfigfrom beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.backend import ChatModelfrom beeai_framework.memory import UnconstrainedMemoryfrom beeai_framework.serve.utils import LRUMemoryManagerfrom beeai_framework.tools.search.duckduckgo import DuckDuckGoSearchToolfrom beeai_framework.tools.weather import OpenMeteoTool
def main() -> None: llm = ChatModel.from_name("ollama:granite3.3") agent = RequirementAgent( llm=llm, tools=[DuckDuckGoSearchTool(), OpenMeteoTool()], memory=UnconstrainedMemory(), )
# Register the agent with the A2A server and run the HTTP server # we use LRU memory manager to keep limited amount of sessions in the memory A2AServer(config=A2AServerConfig(port=9999), memory_manager=LRUMemoryManager(maxsize=100)).register(agent).serve()
if __name__ == "__main__": main()import { A2AServer } from "beeai-framework/adapters/a2a/serve/server";import { RequirementAgent } from "beeai-framework/agents/requirement/agent";import { OllamaChatModel } from "beeai-framework/adapters/ollama/backend/chat";import { UnconstrainedMemory } from "beeai-framework/memory/unconstrainedMemory";import { OpenMeteoTool } from "beeai-framework/tools/weather/openMeteo";
const llm = new OllamaChatModel("granite3.3");const agent = new RequirementAgent({ llm, tools: [new OpenMeteoTool()], memory: new UnconstrainedMemory(),});
// Register the agent with the A2A server and run the HTTP serverawait new A2AServer().register(agent).serve();Expose your agent as an IBM watsonx Orchestrate server:
from beeai_framework.adapters.watsonx_orchestrate import WatsonxOrchestrateServer, WatsonxOrchestrateServerConfigfrom beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.backend import ChatModelfrom beeai_framework.memory import UnconstrainedMemoryfrom beeai_framework.serve.utils import LRUMemoryManagerfrom beeai_framework.tools.weather import OpenMeteoTool
def main() -> None: llm = ChatModel.from_name("ollama:granite3.3") agent = RequirementAgent( llm=llm, tools=[OpenMeteoTool()], memory=UnconstrainedMemory(), instructions="You are a weather agent that provides accurate weather information." )
config = WatsonxOrchestrateServerConfig(port=8080, host="0.0.0.0", api_key=None) # optional # use LRU memory manager to keep limited amount of sessions in the memory server = WatsonxOrchestrateServer(config=config, memory_manager=LRUMemoryManager(maxsize=100)) server.register(agent)
# start an API with /chat/completions endpoint which is compatible with Watsonx Orchestrate server.serve()
if __name__ == "__main__": main()// IBM watsonx Orchestrate server support is not yet available in TypeScript.// Use the Python SDK for this integration.Congratulations! You’ve built a complete AI agent system from a simple chat bot to a production-ready, multi-agent workflow with knowledge bases, caching, error handling, and service endpoints.
Each module page includes detailed guides, examples, and best practices. Here are some next steps:
The framework is designed to scale with you. Start simple, then grow your system step by step as your needs evolve. You now have all the building blocks to create sophisticated and reliable AI agent systems!