Skip to content

The Grand Tour

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:

  • Python 3.11+
  • BeeAI Framework: pip install 'beeai-framework[wikipedia]'
  • Ollama running locally: Download Ollama
  • Model downloaded: ollama pull granite3.3

Let’s start with the simplest possible agent - one that can respond to messages.

import asyncio
from beeai_framework.agents.requirement import RequirementAgent
from 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())

Try it:

  1. Save as simple_agent.py
  2. Run python simple_agent.py
  3. Test different prompts

Troubleshooting

Ollama not responding?

Verify it’s running: ollama list
Start the service: ollama serve

Model not found?

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>

Import errors?

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 asyncio
from beeai_framework.agents.requirement import RequirementAgent
from beeai_framework.backend import ChatModel
from beeai_framework.tools.search.wikipedia import WikipediaTool
from 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())

Try these prompts:

  • “What’s the weather in different cities around the world?”
  • “Tell me about quantum computing and the current weather in CERN’s location”
  • “Compare the weather in New York and London, then tell me about their geographical similarity”

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 asyncio
from beeai_framework.agents.requirement import RequirementAgent
from beeai_framework.backend import ChatModel
from beeai_framework.tools.weather import OpenMeteoTool
from beeai_framework.middleware.trajectory import GlobalTrajectoryMiddleware
from 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())

Sometimes you want to react to specific events. To see which events are emitted, you can use the on function.

import asyncio
from beeai_framework.agents.requirement import RequirementAgent
from beeai_framework.backend import ChatModel
from beeai_framework.tools.weather import OpenMeteoTool
from beeai_framework.middleware.trajectory import GlobalTrajectoryMiddleware
from 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())

Alternatively, you can listen for events on the class itself rather than for a specific run.

import asyncio
from beeai_framework.agents.requirement import RequirementAgent
from beeai_framework.backend import ChatModel
from beeai_framework.tools.weather import OpenMeteoTool
from typing import Any
from 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())

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 asyncio
from beeai_framework.agents.requirement import RequirementAgent
from beeai_framework.backend import ChatModel
from beeai_framework.tools.search.wikipedia import WikipediaTool
from beeai_framework.tools.weather import OpenMeteoTool
from beeai_framework.middleware.trajectory import GlobalTrajectoryMiddleware
from beeai_framework.tools import Tool
from 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())

Logger Output: Traditional log messages with timestamps

2024-01-15 10:30:45 | INFO | my-agent - Starting agent application
2024-01-15 10:30:45 | DEBUG | my-agent - About to process user message
2024-01-15 10:30:47 | INFO | my-agent - Agent response generated successfully

Logging 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>

Terminal window
export OTEL_EXPORTER_OTLP_ENDPOINT="https://your-otel-endpoint"
from openinference.instrumentation.beeai import BeeAIInstrumentor
from opentelemetry import trace as trace_api
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk import trace as trace_sdk
from opentelemetry.sdk.resources import Resource
from 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 agents
setup_observability()

Run your application and you should see traces and metrics in your selected dashboard.

Use 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 asyncio
from beeai_framework.agents.requirement import RequirementAgent
from beeai_framework.agents.requirement.requirements.conditional import ConditionalRequirement
from beeai_framework.backend import ChatModel
from beeai_framework.tools.search.wikipedia import WikipediaTool
from beeai_framework.tools.weather import OpenMeteoTool
from beeai_framework.tools.think import ThinkTool
from beeai_framework.middleware.trajectory import GlobalTrajectoryMiddleware
from beeai_framework.tools import Tool
from 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())

Request User Permission with the AskPermissionRequirement

Section titled “Request User Permission with the AskPermissionRequirement”

Add user permission for when you want an action to be human validated before being executed:

import asyncio
from beeai_framework.agents.requirement import RequirementAgent
from beeai_framework.agents.requirement.requirements.conditional import ConditionalRequirement
from beeai_framework.agents.experimental.requirements.ask_permission import AskPermissionRequirement
from beeai_framework.backend import ChatModel
from beeai_framework.tools.search.wikipedia import WikipediaTool
from beeai_framework.tools.weather import OpenMeteoTool
from beeai_framework.tools.think import ThinkTool
from beeai_framework.middleware.trajectory import GlobalTrajectoryMiddleware
from beeai_framework.tools import Tool
from 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())

In the console, the output looks like:

Do you allow it? (yes/no): yes

Now 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.

Setup the Vector Store, Pre-process, and Load the Documents

Section titled “Setup the Vector Store, Pre-process, and Load the Documents”
  1. Create a new file and name it step1_knowledge_base
  2. Copy the following code into the file and replace the file_paths with your own files
import asyncio
from beeai_framework.backend.document_loader import DocumentLoader
from beeai_framework.backend.embedding import EmbeddingModel
from beeai_framework.backend.text_splitter import TextSplitter
from 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())
  1. Create a new file that imports the helper functions from the step1_knowledge_base file and uses the vector store setup in the previous step
  2. Copy the following code into a new file and replace the file_paths with your own paths
import asyncio
from beeai_framework.agents.requirement import RequirementAgent
from beeai_framework.backend import ChatModel
from beeai_framework.tools.search.wikipedia import WikipediaTool
from beeai_framework.tools.weather import OpenMeteoTool
from beeai_framework.tools.search.retrieval import VectorStoreSearchTool
# Import the setup function from Step 1
from 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())
  1. Add some markdown files with information about your company/project
  2. Ask questions that should be answered from your documents
  3. Compare how responses differ with vs. without the knowledge base or when using different pre-processing strategies

Create a team of specialized agents that can collaborate:

import asyncio
from beeai_framework.agents.requirement import RequirementAgent
from beeai_framework.backend import ChatModel
from beeai_framework.memory import UnconstrainedMemory
from beeai_framework.tools.search.wikipedia import WikipediaTool
from beeai_framework.tools.weather import OpenMeteoTool
from beeai_framework.tools.handoff import HandoffTool
from beeai_framework.tools.think import ThinkTool
from 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())
  1. Ask the coordinator mixed questions: “What’s the weather in Paris and tell me about its history?”
  2. Test how it decides which agent to use
  3. Try complex queries that need multiple specialists

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 asyncio
from beeai_framework.backend import ChatModel, UserMessage
from 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())

2. Caching Tool Outputs

Many tools query APIs or perform expensive lookups. You can attach a cache directly:

from beeai_framework.tools.weather import OpenMeteoTool
from beeai_framework.cache import UnconstrainedCache
# Cache all results from the weather API
weather_tool = OpenMeteoTool(options={"cache": UnconstrainedCache()})

3. Using Cached Components in an Agent

from beeai_framework.agents.requirement import RequirementAgent
from beeai_framework.tools.weather import OpenMeteoTool
from beeai_framework.cache import UnconstrainedCache
from beeai_framework.memory import UnconstrainedMemory
from beeai_framework.backend import ChatModel
from beeai_framework.cache import SlidingCache
import 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())

Make your system robust with comprehensive error management:

import asyncio
import traceback
from beeai_framework.agents.requirement import RequirementAgent
from beeai_framework.backend import ChatModel
from beeai_framework.memory import UnconstrainedMemory
from beeai_framework.tools.weather import OpenMeteoTool
from 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())

Expose your agent as an MCP server:

from beeai_framework.adapters.mcp import MCPServer
from beeai_framework.tools.weather import OpenMeteoTool
from 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()

Expose your agent as a Agent Stack server:

from beeai_framework.adapters.agentstack.serve.server import AgentStackServer
from beeai_framework.agents.requirement import RequirementAgent
from beeai_framework.backend import ChatModel
from beeai_framework.memory import UnconstrainedMemory
from beeai_framework.middleware.trajectory import GlobalTrajectoryMiddleware
from beeai_framework.tools.search.wikipedia import WikipediaTool
from 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()

Expose your agent as an A2A server:

from beeai_framework.adapters.a2a import A2AServer, A2AServerConfig
from beeai_framework.agents.requirement import RequirementAgent
from beeai_framework.backend import ChatModel
from beeai_framework.memory import UnconstrainedMemory
from beeai_framework.serve.utils import LRUMemoryManager
from beeai_framework.tools.search.duckduckgo import DuckDuckGoSearchTool
from 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()

Expose your agent as an IBM watsonx Orchestrate server:

from beeai_framework.adapters.watsonx_orchestrate import WatsonxOrchestrateServer, WatsonxOrchestrateServerConfig
from beeai_framework.agents.requirement import RequirementAgent
from beeai_framework.backend import ChatModel
from beeai_framework.memory import UnconstrainedMemory
from beeai_framework.serve.utils import LRUMemoryManager
from 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()

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:

  1. Explore Modules: Dive deeper into specific modules that interest you
  2. Scale Your System: Add more agents, tools, and knowledge bases
  3. Custom Tools: Build your own tools for domain-specific functionality

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!