Python
Explore reference agent implementations in Python
An AI Agent is a system built on language models (LLMs or SLMs) that can solve complex tasks through structured reasoning and autonomous or human-assisted actions. The BeeAI Framework serves as the orchestration layer that enables agents to do this and more:
Unlike basic chatbots, agents built with this framework can perform multi-step reasoning, use tools to interact with external systems, maintain context across interactions, and adapt based on feedback. These capabilities make them ideal for planning, research, analysis, and complex execution tasks.
You can customize your agent’s behavior in several key ways:
The backend system manages your connection to different language model providers. The BeeAI Framework supports multiple LLM providers through a unified interface. Learn more about available backends and how to set their parameters in our backend documentation.
from beeai_framework.backend import ChatModelfrom beeai_framework.agents.requirement import RequirementAgent
# Using Ollama (local models)llm = ChatModel.from_name("ollama:granite3.3")
# Using OpenAIllm = ChatModel.from_name("openai:gpt-5-mini")
# Using Anthropicllm = ChatModel.from_name("anthropic:claude-sonnet-4")
agent = RequirementAgent( llm=llm, # ... other configuration)import { RequirementAgent } from "beeai-framework/agents/requirement/agent";import { ChatModel } from "beeai-framework/backend/chat";
# Using Ollama (local models)llm = await ChatModel.from_name("ollama:granite3.3")
# Using OpenAIllm = await ChatModel.from_name("openai:gpt-5-mini")
# Using Anthropicllm = await ChatModel.from_name("anthropic:claude-sonnet-4")
agent = new RequirementAgent({ llm, # ... other configuration})Backend Features:
The system prompt defines your agent’s behavior, personality, and capabilities. You can configure this through several parameters when initializing an agent:
agent = RequirementAgent( llm="ollama:granite4:micro", role="You are a helpful research assistant specializing in academic papers", instructions=[ "Always provide citations for your sources", "Focus on peer-reviewed research when possible", "Explain complex concepts in simple terms" ], notes=[ "Be especially careful about medical or legal advice", "If unsure about a fact, acknowledge the uncertainty" ], name="Research Assistant", description="An AI agent that helps with academic research tasks")const agent = new RequirementAgent({ llm: await ChatModel.from_name("ollama:granite4:micro"), role: "You are a helpful research assistant specializing in academic papers", instructions: [ "Always provide citations for your sources", "Focus on peer-reviewed research when possible", "Explain complex concepts in simple terms" ], notes: [ "Be especially careful about medical or legal advice", "If unsure about a fact, acknowledge the uncertainty" ], name: "Research Assistant", description: "An AI agent that helps with academic research tasks"});Prompt Parameters:
role: Defines the agent’s persona and primary functioninstructions: List of specific behavioral guidelinesnotes: Additional context or special considerationsname and description: Help identify the agent’s purpose and are helpful when using the HandoffTool, Serve module, and when you need to access agent metadata via agent.metaSetting instructions, notes, role will be integrated into the framework provided system prompt template. If you want to completely override the framework provided system prompt template, you can provide a custom prompt template.
When executing an agent, you can provide additional options to guide its behavior and execution settings:
response = await agent.run( Analyze the latest AI research trends", expected_output="A structured summary with key findings and recommendations", # expected_output can also receive a Pydantic Model or a JSON Schema backstory="The user is preparing for a conference presentation on AI trends", total_max_retries=5, max_retries_per_step=2, max_iterations=15,)
#structured outputs can be accessed like this:print(response.output_structured)const schema = z.object({ firstName: z.string().min(1), lastName: z.string().min(1)});
const response = await agent.run({ prompt: "Generate profile of a citizen.", expectedOutput: schema,});const person = response.state.resultconsole.info(person.firstName, person.lastName);Available Options:
expected_output: Guides the agent toward a specific unstructured or structured output format. output_structured is defined only when expected_output is a Pydantic model or a JSON schema. However, the text representation is always available via response.output.backstory: Provides additional context to help the agent understand the user’s situationtotal_max_retries: Controls the total number of retry attempts across the entire agent executionmax_retries_per_step: Limits retries for individual steps (like tool calls or model responses)max_iterations: Sets the maximum number of reasoning cycles the agent can performEnhance your agent’s capabilities by providing it with tools to interact with external systems. Learn more about beeai provided tools and creating custom tools in our tools documentation.
from beeai_framework.tools.search.duckduckgo import DuckDuckGoSearchToolfrom beeai_framework.tools.weather import OpenMeteoTool
agent = RequirementAgent( llm="ollama:granite4", tools=[ DuckDuckGoSearchTool(), OpenMeteoTool(), # Add more tools as needed ])import { OpenMeteoTool } from "beeai-framework/tools/weather/openMeteo";import { ChatModel } from "beeai-framework/backend/chat";import { RequirementAgent } from "beeai-framework/agents/requirement/agent";
const llm = await ChatModel.from_name("ollama:granite4");const agent = new RequirementAgent({ llm, tools: [ new OpenMeteoTool(), // weather tool ],});Memory allows your agent to maintain context across multiple interactions. Different memory types serve different use cases. Learn more about our built in options in the memory documentation.
from beeai_framework.memory import TokenMemory, UnconstrainedMemoryfrom beeai_framework.agents.requirement import RequirementAgent
agent = RequirementAgent( llm="ollama:granite4:micro", memory=TokenMemory(max_tokens=20*1024))import { TokenMemory } from "beeai-framework/memory/tokenMemory";import { ChatModel } from "beeai-framework/backend/chat";import { RequirementAgent } from "beeai-framework/agents/requirement/agent";
const agent = new RequirementAgent({ llm: await ChatModel.from_name("ollama:granite4:micro"), memory: new TokenMemory({ maxTokens: 20 * 1024 })});BeeAI Framework provides several agent implementations:
This agent provides the reliability needed for production scenarios through a rule system that defines execution constraints while keeping problem-solving flexibility intact. Unlike traditional approaches that require complex orchestration code, RequirementAgent uses a declarative interface where you define requirements and let the framework enforce them automatically.
Learn more about RequirementAgent in its dedicated page or in the blog post.
from beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.agents.requirement.requirements.conditional import ConditionalRequirementfrom beeai_framework.tools.search.duckduckgo import DuckDuckGoSearchToolfrom beeai_framework.tools.think import ThinkToolfrom beeai_framework.tools.weather import OpenMeteoToolfrom beeai_framework.backend import ChatModelfrom beeai_framework.middleware.trajectory import GlobalTrajectoryMiddleware
agent = RequirementAgent( llm=ChatModel.from_name("ollama:granite4:micro"), tools=[ ThinkTool(), # to reason OpenMeteoTool(), # retrieve weather data DuckDuckGoSearchTool() # search web ], instructions="Plan activities for a given destination based on current weather and events.", requirements=[ # Force thinking first ConditionalRequirement(ThinkTool, force_at_step=1), # Search only after getting weather and at least once ConditionalRequirement(DuckDuckGoSearchTool, only_after=[OpenMeteoTool], min_invocations=1), # Weather tool be used at least once but not consecutively ConditionalRequirement(OpenMeteoTool, consecutive_allowed=False, min_invocations=1), ])
# Run with execution loggingresponse = await agent.run("What to do in Boston?").middleware(GlobalTrajectoryMiddleware())print(f"Final Answer: {response.answer.text}")An agent that leverages a language model and a suite of tools to solve problems. The agent does not have any system prompt.
This design is ideal for:
import asyncio
from beeai_framework.agents.lite import LiteAgentfrom beeai_framework.backend import ChatModel, ChatModelOutput, ChatModelParameters, SystemMessagefrom beeai_framework.emitter import EventMetafrom beeai_framework.middleware.trajectory import GlobalTrajectoryMiddlewarefrom beeai_framework.tools.search.duckduckgo import DuckDuckGoSearchToolfrom beeai_framework.tools.think import ThinkToolfrom beeai_framework.tools.weather import OpenMeteoTool
async def main() -> None: agent = LiteAgent( llm=ChatModel.from_name("ollama:granite4:micro", ChatModelParameters(stream=True)), tools=[ThinkTool(), OpenMeteoTool(), DuckDuckGoSearchTool()], middlewares=[GlobalTrajectoryMiddleware()], )
# Optionally set a custom system prompt await agent.memory.add(SystemMessage("You are a helpful assistant."))
@agent.emitter.on("final_answer") def stream_final_answer(data: ChatModelOutput, meta: EventMeta) -> None: print(data.get_text_content()) # emits chunks
await agent.run("Hello")
if __name__ == "__main__": asyncio.run(main())The ReActAgent implements the ReAct (Reasoning and Acting) pattern, which structures agent behavior into a cyclical process of reasoning, action, and observation.
This pattern allows agents to reason about a task, take actions using tools, observe results, and continue reasoning until reaching a conclusion.
Let’s see how a ReActAgent approaches a simple question:
Input prompt: “What is the current weather in Las Vegas?”
First iteration:
thought: I need to retrieve the current weather in Las Vegas. I can use the OpenMeteo function to get the current weather forecast for a location.tool_name: OpenMeteotool_input: {"location": {"name": "Las Vegas"}, "start_date": "2024-10-17", "end_date": "2024-10-17", "temperature_unit": "celsius"}Second iteration:
thought: I have the current weather in Las Vegas in Celsius.final_answer: The current weather in Las Vegas is 20.5°C with an apparent temperature of 18.3°C.import asyncioimport loggingimport osimport sysimport tempfileimport tracebackfrom typing import Any
from dotenv import load_dotenv
from beeai_framework.agents.react import ReActAgentfrom beeai_framework.backend import ChatModel, ChatModelParametersfrom beeai_framework.emitter import EmitterOptions, EventMetafrom beeai_framework.errors import FrameworkErrorfrom beeai_framework.logger import Loggerfrom beeai_framework.memory import TokenMemoryfrom beeai_framework.tools import AnyToolfrom beeai_framework.tools.code import LocalPythonStorage, PythonToolfrom beeai_framework.tools.search.duckduckgo import DuckDuckGoSearchToolfrom beeai_framework.tools.search.wikipedia import WikipediaToolfrom beeai_framework.tools.weather import OpenMeteoToolfrom examples.helpers.io import ConsoleReader
# Load environment variablesload_dotenv()
# Configure logging - using DEBUG instead of tracelogger = Logger("app", level=logging.DEBUG)
reader = ConsoleReader()
def create_agent() -> ReActAgent: """Create and configure the agent with tools and LLM"""
# Other models to try: # "llama3.1" # "granite4.1:8b" # "deepseek-r1" # ensure the model is pulled before running llm = ChatModel.from_name( "ollama:granite4:micro", ChatModelParameters(temperature=0), )
# Configure tools tools: list[AnyTool] = [ WikipediaTool(), OpenMeteoTool(), DuckDuckGoSearchTool(), ]
# Add code interpreter tool if URL is configured code_interpreter_url = os.getenv("CODE_INTERPRETER_URL") if code_interpreter_url: tools.append( PythonTool( code_interpreter_url, LocalPythonStorage( local_working_dir=tempfile.mkdtemp("code_interpreter_source"), interpreter_working_dir=os.getenv("CODE_INTERPRETER_TMPDIR", "./tmp/code_interpreter_target"), ), ) )
# Create agent with memory and tools agent = ReActAgent(llm=llm, tools=tools, memory=TokenMemory(llm))
return agent
def process_agent_events(data: Any, event: EventMeta) -> None: """Process agent events and log appropriately"""
if event.name == "error": reader.write("Agent 🤖 : ", FrameworkError.ensure(data.error).explain()) elif event.name == "retry": reader.write("Agent 🤖 : ", "retrying the action...") elif event.name == "update": reader.write(f"Agent({data.update.key}) 🤖 : ", data.update.parsed_value) elif event.name == "start": reader.write("Agent 🤖 : ", "starting new iteration") elif event.name == "success": reader.write("Agent 🤖 : ", "success")
async def main() -> None: """Main application loop"""
# Create agent agent = create_agent()
# Log code interpreter status if configured code_interpreter_url = os.getenv("CODE_INTERPRETER_URL") if code_interpreter_url: reader.write( "🛠️ System: ", f"The code interpreter tool is enabled. Please ensure that it is running on {code_interpreter_url}", )
reader.write("🛠️ System: ", "Agent initialized with Wikipedia, DuckDuckGo, and Weather tools.")
# Main interaction loop with user input for prompt in reader: # Run agent with the prompt response = await agent.run( prompt, max_retries_per_step=3, total_max_retries=10, max_iterations=20, ).on("*", process_agent_events, EmitterOptions(match_nested=False))
reader.write("Agent 🤖 : ", response.last_message.text)
if __name__ == "__main__": try: asyncio.run(main()) except FrameworkError as e: traceback.print_exc() sys.exit(e.explain())import "dotenv/config.js";import { ReActAgent } from "beeai-framework/agents/react/agent";import { createConsoleReader } from "../helpers/io.js";import { FrameworkError } from "beeai-framework/errors";import { TokenMemory } from "beeai-framework/memory/tokenMemory";import { Logger } from "beeai-framework/logger/logger";import { PythonTool } from "beeai-framework/tools/python/python";import { LocalPythonStorage } from "beeai-framework/tools/python/storage";import { DuckDuckGoSearchTool } from "beeai-framework/tools/search/duckDuckGoSearch";import { WikipediaTool } from "beeai-framework/tools/search/wikipedia";import { OpenMeteoTool } from "beeai-framework/tools/weather/openMeteo";import { dirname } from "node:path";import { fileURLToPath } from "node:url";import { OllamaChatModel } from "beeai-framework/adapters/ollama/backend/chat";
Logger.root.level = "silent"; // disable internal logsconst logger = new Logger({ name: "app", level: "trace" });
// Other models to try:// "llama3.1:70b"// "granite4.1:8b"// "deepseek-r1:32b"// ensure the model is pulled before runningconst llm = new OllamaChatModel("granite4:micro");
const codeInterpreterUrl = process.env.CODE_INTERPRETER_URL;const __dirname = dirname(fileURLToPath(import.meta.url));
const codeInterpreterTmpdir = process.env.CODE_INTERPRETER_TMPDIR ?? "./examples/tmp/code_interpreter";const localTmpdir = process.env.LOCAL_TMPDIR ?? "./examples/tmp/local";
const agent = new ReActAgent({ llm, memory: new TokenMemory(), tools: [ new DuckDuckGoSearchTool(), // new WebCrawlerTool(), // HTML web page crawler new WikipediaTool(), new OpenMeteoTool(), // weather tool // new ArXivTool(), // research papers // new DynamicTool() // custom python tool ...(codeInterpreterUrl ? [ new PythonTool({ codeInterpreter: { url: codeInterpreterUrl }, storage: new LocalPythonStorage({ interpreterWorkingDir: `${__dirname}/../../${codeInterpreterTmpdir}`, localWorkingDir: `${__dirname}/../../${localTmpdir}`, }), }), ] : []), ],});
const reader = createConsoleReader();if (codeInterpreterUrl) { reader.write( "🛠️ System", `The code interpreter tool is enabled. Please ensure that it is running on ${codeInterpreterUrl}`, );}
try { for await (const { prompt } of reader) { const response = await agent .run( { prompt }, { execution: { maxRetriesPerStep: 3, totalMaxRetries: 10, maxIterations: 20, }, }, ) .observe((emitter) => { // emitter.on("start", () => { // reader.write(`Agent 🤖 : `, "starting new iteration"); // }); emitter.on("error", ({ error }) => { reader.write(`Agent 🤖 : `, FrameworkError.ensure(error).dump()); }); emitter.on("retry", () => { reader.write(`Agent 🤖 : `, "retrying the action..."); }); emitter.on("update", async ({ data, update, meta }) => { // log 'data' to see the whole state // to log only valid runs (no errors), check if meta.success === true reader.write(`Agent (${update.key}) 🤖 : `, update.value); }); emitter.on("partialUpdate", ({ data, update, meta }) => { // ideal for streaming (line by line) // log 'data' to see the whole state // to log only valid runs (no errors), check if meta.success === true // reader.write(`Agent (partial ${update.key}) 🤖 : `, update.value); });
// To observe all events (uncomment following block) // emitter.match("*.*", async (data: unknown, event) => { // logger.trace(event, `Received event "${event.path}"`); // });
// To get raw LLM input (uncomment following block) // emitter.match( // (event) => event.creator === llm && event.name === "start", // async (data: InferCallbackValue<GenerateEvents["start"]>, event) => { // logger.trace( // event, // [ // `Received LLM event "${event.path}"`, // JSON.stringify(data.input), // array of messages // ].join("\n"), // ); // }, // ); });
reader.write(`Agent 🤖 : `, response.result.text); }} catch (error) { logger.error(FrameworkError.ensure(error).dump());} finally { reader.close();}The ToolCallingAgent is optimized for scenarios where tool usage is the primary focus. It handles tool calls more efficiently and can execute multiple tools in parallel.
import asyncioimport loggingimport sysimport tracebackfrom typing import Any
from dotenv import load_dotenv
from beeai_framework.agents.tool_calling import ToolCallingAgentfrom beeai_framework.backend import ChatModelfrom beeai_framework.emitter import EventMetafrom beeai_framework.errors import FrameworkErrorfrom beeai_framework.logger import Loggerfrom beeai_framework.memory import UnconstrainedMemoryfrom beeai_framework.tools.weather import OpenMeteoToolfrom examples.helpers.io import ConsoleReader
# Load environment variablesload_dotenv()
# Configure logging - using DEBUG instead of tracelogger = Logger("app", level=logging.DEBUG)
reader = ConsoleReader()
def process_agent_events(data: Any, event: EventMeta) -> None: """Process agent events and log appropriately"""
if event.name == "start": reader.write("Agent (debug) 🤖 : ", "starting new iteration") elif event.name == "success": reader.write("Agent (debug) 🤖 : ", data.state.memory.messages[-1])
async def main() -> None: """Main application loop"""
# Create agent agent = ToolCallingAgent( llm=ChatModel.from_name("ollama:llama3.1"), memory=UnconstrainedMemory(), tools=[OpenMeteoTool()] )
# Main interaction loop with user input for prompt in reader: response = await agent.run(prompt).on("*", process_agent_events) reader.write("Agent 🤖 : ", response.last_message.text)
print("======DONE (showing the full message history)=======")
# pyrefly: ignore [unbound-name] messages = response.state.memory.messages for msg in messages: print(msg)
if __name__ == "__main__": try: asyncio.run(main()) except FrameworkError as e: traceback.print_exc() sys.exit(e.explain())import "dotenv/config.js";import { createConsoleReader } from "../../helpers/io.js";import { FrameworkError } from "beeai-framework/errors";import { TokenMemory } from "beeai-framework/memory/tokenMemory";import { Logger } from "beeai-framework/logger/logger";import { OpenMeteoTool } from "beeai-framework/tools/weather/openMeteo";import { OllamaChatModel } from "beeai-framework/adapters/ollama/backend/chat";import { ToolCallingAgent } from "beeai-framework/agents/toolCalling/agent";
Logger.root.level = "silent"; // disable internal logsconst logger = new Logger({ name: "app", level: "trace" });
// Other models to try:// "llama3.1:70b"// "granite4.1:8b"// "deepseek-r1:32b"// ensure the model is pulled before runningconst llm = new OllamaChatModel("granite4:micro");
const agent = new ToolCallingAgent({ llm, memory: new TokenMemory(), templates: { system: (template) => template.fork((config) => { config.defaults.instructions = "You are a helpful assistant that uses tools to answer questions."; }), }, tools: [ new OpenMeteoTool(), // weather tool ],});
const reader = createConsoleReader();
try { for await (const { prompt } of reader) { let messagesCount = agent.memory.messages.length + 1;
const response = await agent.run({ prompt }).observe((emitter) => { emitter.on("success", async ({ state }) => { const newMessages = state.memory.messages.slice(messagesCount); messagesCount += newMessages.length;
reader.write( `Agent (${newMessages.length} new messages) 🤖 :\n`, newMessages.map((msg) => `-> ${JSON.stringify(msg.toPlain())}`).join("\n"), ); });
// To observe all events (uncomment following block) // emitter.match("*.*", async (data: unknown, event) => { // logger.trace(event, `Received event "${event.path}"`); // }, { // matchNested: true // });
// To get raw LLM input (uncomment following block) // emitter.match( // (event) => event.creator === llm && event.name === "start", // async (data: InferCallbackValue<GenerateEvents["start"]>, event) => { // logger.trace( // event, // [ // `Received LLM event "${event.path}"`, // JSON.stringify(data.input), // array of messages // ].join("\n"), // ); // }, // ); });
reader.write(`Agent 🤖 : `, response.result.text); }} catch (error) { logger.error(FrameworkError.ensure(error).dump());} finally { reader.close();}For advanced use cases, you can create your own agent implementation by extending the BaseAgent class.
import asyncioimport sysimport tracebackfrom typing import Unpack
from pydantic import BaseModel, Field
from beeai_framework.adapters.ollama import OllamaChatModelfrom beeai_framework.agents import AgentMeta, AgentOptions, AgentOutput, BaseAgentfrom beeai_framework.backend import AnyMessage, AssistantMessage, ChatModel, SystemMessage, UserMessagefrom beeai_framework.context import RunContextfrom beeai_framework.emitter import Emitterfrom beeai_framework.errors import FrameworkErrorfrom beeai_framework.memory import BaseMemory, UnconstrainedMemoryfrom beeai_framework.runnable import runnable_entry
class State(BaseModel): thought: str final_answer: str
class CustomAgent(BaseAgent): def __init__(self, llm: ChatModel, memory: BaseMemory) -> None: super().__init__() self.model = llm self._memory = memory
@property def memory(self) -> BaseMemory: return self._memory
@memory.setter def memory(self, memory: BaseMemory) -> None: self._memory = memory
def _create_emitter(self) -> Emitter: return Emitter.root().child( namespace=["agent", "custom"], creator=self, )
@runnable_entry async def run(self, input: str | list[AnyMessage], /, **kwargs: Unpack[AgentOptions]) -> AgentOutput: async def handler(context: RunContext) -> AgentOutput: class CustomSchema(BaseModel): thought: str = Field(description="Describe your thought process before coming with a final answer") final_answer: str = Field( description="Here you should provide concise answer to the original question." )
response = await self.model.run( [ SystemMessage("You are a helpful assistant. Always use JSON format for your responses."), *(self.memory.messages if self.memory is not None else []), *([UserMessage(input)] if isinstance(input, str) else input), ], response_format=CustomSchema, max_retries=kwargs.get("total_max_retries", 3), signal=context.signal, ) assert isinstance(response.output_structured, CustomSchema)
result = AssistantMessage(response.output_structured.final_answer) await self.memory.add(result) if self.memory else None
return AgentOutput( output=[result], context={ "state": State( thought=response.output_structured.thought, final_answer=response.output_structured.final_answer, ) }, )
return await handler(RunContext.get())
@property def meta(self) -> AgentMeta: return AgentMeta( name="CustomAgent", description="Custom Agent is a simple LLM agent.", tools=[], )
async def main() -> None: agent = CustomAgent( llm=OllamaChatModel("granite3.3"), memory=UnconstrainedMemory(), )
response = await agent.run([UserMessage("Why is the sky blue?")]) print(response.context.get("state"))
if __name__ == "__main__": try: asyncio.run(main()) except FrameworkError as e: traceback.print_exc() sys.exit(e.explain())import { BaseAgent, BaseAgentRunOptions } from "beeai-framework/agents/base";import { AssistantMessage, Message, SystemMessage, UserMessage,} from "beeai-framework/backend/message";import { Emitter } from "beeai-framework/emitter/emitter";import { GetRunContext } from "beeai-framework/context";import { z } from "zod";import { AgentMeta } from "beeai-framework/agents/types";import { BaseMemory } from "beeai-framework/memory/base";import { UnconstrainedMemory } from "beeai-framework/memory/unconstrainedMemory";import { ChatModel } from "beeai-framework/backend/chat";import { OllamaChatModel } from "beeai-framework/adapters/ollama/backend/chat";
interface RunInput { message: Message;}
interface RunOutput { message: Message; state: { thought: string; final_answer: string; };}
interface RunOptions extends BaseAgentRunOptions { maxRetries?: number;}
interface AgentInput { llm: ChatModel; memory: BaseMemory;}
export class CustomAgent extends BaseAgent<RunInput, RunOutput, RunOptions> { public readonly memory: BaseMemory; protected readonly model: ChatModel; public emitter = Emitter.root.child({ namespace: ["agent", "custom"], creator: this, });
constructor(input: AgentInput) { super(); this.model = input.llm; this.memory = input.memory; }
protected async _run( input: RunInput, options: RunOptions, run: GetRunContext<this>, ): Promise<RunOutput> { const response = await this.model.createStructure({ schema: z.object({ thought: z .string() .describe("Describe your thought process before coming with a final answer"), final_answer: z .string() .describe("Here you should provide concise answer to the original question."), }), messages: [ new SystemMessage("You are a helpful assistant. Always use JSON format for you responses."), ...this.memory.messages, input.message, ], maxRetries: options?.maxRetries, abortSignal: run.signal, });
const result = new AssistantMessage(response.object.final_answer); await this.memory.add(result);
return { message: result, state: response.object, }; }
public get meta(): AgentMeta { return { name: "CustomAgent", description: "Custom Agent is a simple LLM agent.", tools: [], }; }
createSnapshot() { return { ...super.createSnapshot(), emitter: this.emitter, memory: this.memory, }; }
loadSnapshot(snapshot: ReturnType<typeof this.createSnapshot>) { Object.assign(this, snapshot); }}
const agent = new CustomAgent({ llm: new OllamaChatModel("granite3.3"), memory: new UnconstrainedMemory(),});
const response = await agent.run({ message: new UserMessage("Why is the sky blue?"),});console.info(response.state);Create a team of specialized agents that can collaborate:
import asyncio
from beeai_framework.agents.requirement import RequirementAgentfrom beeai_framework.agents.requirement.requirements.conditional import ConditionalRequirementfrom beeai_framework.backend import ChatModelfrom beeai_framework.errors import FrameworkErrorfrom beeai_framework.middleware.trajectory import GlobalTrajectoryMiddlewarefrom beeai_framework.tools import Toolfrom beeai_framework.tools.handoff import HandoffToolfrom beeai_framework.tools.search.wikipedia import WikipediaToolfrom beeai_framework.tools.think import ThinkToolfrom beeai_framework.tools.weather import OpenMeteoTool
async def main() -> None: knowledge_agent = RequirementAgent( llm=ChatModel.from_name("ollama:granite4:micro"), tools=[ThinkTool(), WikipediaTool()], requirements=[ConditionalRequirement(ThinkTool, force_at_step=1)], role="Knowledge Specialist", instructions="Provide answers to general questions about the world.", )
weather_agent = RequirementAgent( llm=ChatModel.from_name("ollama:granite4:micro"), tools=[OpenMeteoTool()], role="Weather Specialist", instructions="Provide weather forecast for a given destination.", )
main_agent = RequirementAgent( name="MainAgent", llm=ChatModel.from_name("ollama:granite4:micro"), tools=[ ThinkTool(), HandoffTool( knowledge_agent, name="KnowledgeLookup", description="Consult the Knowledge Agent for general questions.", ), HandoffTool( weather_agent, name="WeatherLookup", description="Consult the Weather Agent for forecasts.", ), ], requirements=[ConditionalRequirement(ThinkTool, force_at_step=1)], # Log all tool calls to the console for easier debugging middlewares=[GlobalTrajectoryMiddleware(included=[Tool])], )
question = "If I travel to Rome next weekend, what should I expect in terms of weather, and also tell me one famous historical landmark there?" print(f"User: {question}")
try: response = await main_agent.run(question, expected_output="Helpful and clear response.") print("Agent:", response.last_message.text) except FrameworkError as err: print("Error:", err.explain())
if __name__ == "__main__": asyncio.run(main())COMING SOONFor complex applications, you can create multi-agent workflows where specialized agents collaborate.
import asyncioimport sysimport traceback
from beeai_framework.backend import ChatModelfrom beeai_framework.emitter import EmitterOptionsfrom beeai_framework.errors import FrameworkErrorfrom beeai_framework.tools.search.wikipedia import WikipediaToolfrom beeai_framework.tools.weather import OpenMeteoToolfrom beeai_framework.workflows.agent import AgentWorkflow, AgentWorkflowInputfrom examples.helpers.io import ConsoleReader
async def main() -> None: llm = ChatModel.from_name("ollama:llama3.1") workflow = AgentWorkflow(name="Smart assistant")
workflow.add_agent( name="Researcher", role="A diligent researcher.", instructions="You look up and provide information about a specific topic.", tools=[WikipediaTool()], llm=llm, )
workflow.add_agent( name="WeatherForecaster", role="A weather reporter.", instructions="You provide detailed weather reports.", tools=[OpenMeteoTool()], llm=llm, )
workflow.add_agent( name="DataSynthesizer", role="A meticulous and creative data synthesizer", instructions="You can combine disparate information into a final coherent summary.", llm=llm, )
reader = ConsoleReader()
reader.write("Assistant 🤖 : ", "What location do you want to learn about?") for prompt in reader: await ( workflow.run( inputs=[ AgentWorkflowInput(prompt="Provide a short history of the location.", context=prompt), AgentWorkflowInput( prompt="Provide a comprehensive weather summary for the location today.", expected_output="Essential weather details such as chance of rain, temperature and wind. Only report information that is available.", ), AgentWorkflowInput( prompt="Summarize the historical and weather data for the location.", expected_output="A paragraph that describes the history of the location, followed by the current weather conditions.", ), ] ) .on( # Event Matcher -> match agent's 'success' events lambda event: isinstance(event.creator, ChatModel) and event.name == "success", # log data to the console lambda data, event: reader.write( "->Got response from the LLM", " \n->".join([str(message.content[0].model_dump()) for message in data.value.messages]), ), EmitterOptions(match_nested=True), ) .on( "success", lambda data, event: reader.write( f"->Step '{data.step}' has been completed with the following outcome." f"\n\n{data.state.final_answer}\n\n", data.model_dump(exclude={"data"}), ), ) ) reader.write("Assistant 🤖 : ", "What location do you want to learn about?")
if __name__ == "__main__": try: asyncio.run(main()) except FrameworkError as e: traceback.print_exc() sys.exit(e.explain())import "dotenv/config";import { createConsoleReader } from "examples/helpers/io.js";import { OpenMeteoTool } from "beeai-framework/tools/weather/openMeteo";import { WikipediaTool } from "beeai-framework/tools/search/wikipedia";import { AgentWorkflow } from "beeai-framework/workflows/agent";import { OllamaChatModel } from "beeai-framework/adapters/ollama/backend/chat";
const workflow = new AgentWorkflow("Smart assistant");const llm = new OllamaChatModel("llama3.1");
workflow.addAgent({ name: "Researcher", role: "A diligent researcher", instructions: "You look up and provide information about a specific topic.", tools: [new WikipediaTool()], llm,});workflow.addAgent({ name: "WeatherForecaster", role: "A weather reporter", instructions: "You provide detailed weather reports.", tools: [new OpenMeteoTool()], llm,});workflow.addAgent({ name: "DataSynthesizer", role: "A meticulous and creative data synthesizer", instructions: "You can combine disparate information into a final coherent summary.", llm,});
const reader = createConsoleReader();reader.write("Assistant 🤖 : ", "What location do you want to learn about?");for await (const { prompt } of reader) { const { result } = await workflow .run([ { prompt: "Provide a short history of the location.", context: prompt }, { prompt: "Provide a comprehensive weather summary for the location today.", expectedOutput: "Essential weather details such as chance of rain, temperature and wind. Only report information that is available.", }, { prompt: "Summarize the historical and weather data for the location.", expectedOutput: "A paragraph that describes the history of the location, followed by the current weather conditions.", }, ]) .observe((emitter) => { emitter.on("success", (data) => { reader.write( `Step '${data.step}' has been completed with the following outcome:\n`, data.state?.finalAnswer ?? "-", ); }); });
reader.write(`Assistant 🤖`, result.finalAnswer); reader.write("Assistant 🤖 : ", "What location do you want to learn about?");}