diff --git a/python/docs/src/guides/azure-openai-with-aad-auth.md b/python/docs/src/cookbook/azure-openai-with-aad-auth.md similarity index 100% rename from python/docs/src/guides/azure-openai-with-aad-auth.md rename to python/docs/src/cookbook/azure-openai-with-aad-auth.md diff --git a/python/docs/src/core-concepts/memory.md b/python/docs/src/cookbook/buffered-memory.md similarity index 75% rename from python/docs/src/core-concepts/memory.md rename to python/docs/src/cookbook/buffered-memory.md index efd33f2cb..d132385b3 100644 --- a/python/docs/src/core-concepts/memory.md +++ b/python/docs/src/cookbook/buffered-memory.md @@ -1,16 +1,4 @@ -# Memory - -Memory is a collection of data corresponding to the conversation history -of an agent. -Data in meory can be just a simple list of all messages, -or one which provides a view of the last N messages. - -To create a custom memory implementation, you need to subclass the -{py:class}`agnext.components.memory.ChatMemory` protocol class and implement -all its methods. -For example, you can use [LLMLingua](https://github.com/microsoft/LLMLingua) -to create a custom memory implementation that provides a compressed -view of the conversation history. +# Buffered Memory Here is an example of a custom memory implementation that keeps a view of the last N messages: diff --git a/python/docs/src/guides/extracting-results-with-an-agent.md b/python/docs/src/cookbook/extracting-results-with-an-agent.md similarity index 100% rename from python/docs/src/guides/extracting-results-with-an-agent.md rename to python/docs/src/cookbook/extracting-results-with-an-agent.md diff --git a/python/docs/src/guides/termination-with-intervention.md b/python/docs/src/cookbook/termination-with-intervention.md similarity index 100% rename from python/docs/src/guides/termination-with-intervention.md rename to python/docs/src/cookbook/termination-with-intervention.md diff --git a/python/docs/src/guides/type-routed-agent.md b/python/docs/src/cookbook/type-routed-agent.md similarity index 100% rename from python/docs/src/guides/type-routed-agent.md rename to python/docs/src/cookbook/type-routed-agent.md diff --git a/python/docs/src/core-concepts/agent.md b/python/docs/src/core-concepts/agent.md deleted file mode 100644 index c43ef9f97..000000000 --- a/python/docs/src/core-concepts/agent.md +++ /dev/null @@ -1,69 +0,0 @@ -# Agent - -An agent in AGNext is an entity that can react to, send, and publish -messages. Messages are the only means through which agents can communicate -with each other. - -## Messages - -Messages are typed, and serializable (to JSON) objects that agents use to communicate. The type of a message is used to determine which agents a message should be delivered to, if an agent can handle a message and the handler that should be invoked when the message is received by an agent. If an agent is invoked with a message it is not able to handle, it must raise {py:class}`~agnext.core.exceptions.CantHandleException`. - -Generally, messages are one of: - -- A subclass of Pydantic's {py:class}`pydantic.BaseModel` -- A dataclass - -Messages are purely data, and should not contain any logic. - -```{tip} -It is *strongly* recommended that messages are Pydantic models. This allows for easy serialization and deserialization of messages, and provides a clear schema for the message. -``` - - - -## Communication - -There are two forms of communication in AGNext: - -- **Direct communication**: An agent sends a direct message to another agent. -- **Broadcast communication**: An agent publishes a message to all agents in the same namespace. - -### Message Handling - -When an agent receives a message the runtime will invoke the agent's message handler ({py:meth}`agnext.core.Agent.on_message`) which should implement the agents message handling logic. If this message cannot be handled by the agent, the agent should raise a {py:class}`~agnext.core.exceptions.CantHandleException`. For the majority of custom agent's {py:meth}`agnext.core.Agent.on_message` will not be directly implemented, but rather the agent will use the {py:class}`~agnext.components.TypeRoutedAgent` base class which provides a simple API for associating message types with message handlers. - -### Direct Communication - -Direct communication is effectively an RPC call directly to another agent. When sending a direct message to another agent, the receiving agent can respond to the message with another message, or simply return `None`. To send a message to another agent, within a message handler use the {py:meth}`agnext.core.BaseAgent.send_message` method. Awaiting this call will return the response of the invoked agent. If the receiving agent raises an exception, this will be propagated back to the sending agent. - -To send a message to an agent outside of agent handling a message the message should be sent via the runtime with the {py:meth}`agnext.core.AgentRuntime.send_message` method. This is often how an application might "start" a workflow or conversation. - -### Broadcast Communication - -Broadcast communication is effectively the publish-subscribe model. -As part of the agent's implementation it must advertise the message types that it would like to receive when published ({py:attr}`agnext.core.Agent.subscriptions`). If one of these messages is published, the agent's message handler will be invoked. The key difference between direct and broadcast communication is that broadcast communication is not a request/response model. When an agent publishes a message it is one way, it is not expecting a response from any other agent. In fact, they cannot respond to the message. - -To publish a message to all agents, use the {py:meth}`agnext.core.BaseAgent.publish_message` method. This call must still be awaited to allow the runtime to deliver the message to all agents, but it will always return `None`. If an agent raises an exception while handling a published message, this will be logged but will not be propagated back to the publishing agent. - -To publish a message to all agents outside of an agent handling a message, the message should be published via the runtime with the {py:meth}`agnext.core.AgentRuntime.publish_message` method. - -If an agent publishes a message type for which it is subscribed it will not receive the message it published. This is to prevent infinite loops. - -```{note} -Currently an agent does not know if it is handling a published or direct message. So, if a response is given to a published message, it will be thrown away. -``` diff --git a/python/docs/src/core-concepts/cancellation.md b/python/docs/src/core-concepts/cancellation.md deleted file mode 100644 index 6792e941e..000000000 --- a/python/docs/src/core-concepts/cancellation.md +++ /dev/null @@ -1 +0,0 @@ -# Cancellation \ No newline at end of file diff --git a/python/docs/src/core-concepts/namespace.md b/python/docs/src/core-concepts/namespace.md deleted file mode 100644 index 16f83c039..000000000 --- a/python/docs/src/core-concepts/namespace.md +++ /dev/null @@ -1,19 +0,0 @@ -# Namespace - -Namespace allow for defining logical boundaries between agents. - -Namespaces are strings, and the default is `default`. - -Two possible use cases of agents are: - -- Creating a multi-tenant system where each tenant has its own namespace. For - example, a chat system where each tenant has its own set of agents. -- Security boundaries between agent groups. For example, a chat system where - agents in the `admin` namespace can communicate with agents in the `user` - namespace, but not the other way around. - -The {py:class}`agnext.core.AgentId` is used to address an agent, it is the combination of the agent's namespace and its name. - -When getting an agent reference ({py:meth}`agnext.core.AgentRuntime.get`) or proxy ({py:meth}`agnext.core.AgentRuntime.get_proxy`) from the runtime the namespace can be specified. Agents have an ID property ({py:attr}`agnext.core.Agent.id`) that returns the agent's id. Additionally, the register method takes a factory that can optionally accept the ID as an argument ({py:meth}`agnext.core.AgentRuntime.register`). - -By default, there are no restrictions and are left to the application to enforce. The runtime will however automatically create agents in a namespace if it does not exist. diff --git a/python/docs/src/core-concepts/runtime.md b/python/docs/src/core-concepts/runtime.md deleted file mode 100644 index becfdb24e..000000000 --- a/python/docs/src/core-concepts/runtime.md +++ /dev/null @@ -1,36 +0,0 @@ -# Agent Runtime - -Agent runtime is the execution environment for agents in AGNext. -Similar to the runtime environment of a programming language, the -agent runtime provides the necessary infrastructure to facilitate communication -between agents, manage agent states, and provide API for monitoring and -debugging multi-agent interactions. - -Further readings: - -1. {py:class}`agnext.core.AgentRuntime` -2. {py:class}`agnext.application.SingleThreadedAgentRuntime` - -## Agent Registration - -Agents are registered with the runtime using the -{py:meth}`agnext.core.AgentRuntime.register` method. The process of registration -associates some name, which is the `type` of the agent with a factory function -that is able to create an instance of the agent in a given namespace. The reason -for the factory function is to allow automatic creation of agents when they are -needed, including automatic creation of agents for not yet existing namespaces. - -Once an agent is registered, a reference to the agent can be retrieved by -calling {py:meth}`agnext.core.AgentRuntime.get` or -{py:meth}`agnext.core.AgentRuntime.get_proxy`. There is a convenience method -{py:meth}`agnext.core.AgentRuntime.register_and_get` that both registers a type -and gets a reference. - -A byproduct of this process of `register` + `get` is that -{py:class}`agnext.core.Agent` interface is a purely implementation contract. All -agents must be communicated with via the runtime. This is a key design decision -that allows the runtime to manage the lifecycle of agents, and to provide a -consistent API for interacting with agents. Therefore, to communicate with -another agent the {py:class}`agnext.core.AgentId` must be used. There is a -convenience class {py:meth}`agnext.core.AgentProxy` that bundles an ID and a -runtime together. \ No newline at end of file diff --git a/python/docs/src/core-concepts/tools.md b/python/docs/src/core-concepts/tools.md deleted file mode 100644 index e609c3c38..000000000 --- a/python/docs/src/core-concepts/tools.md +++ /dev/null @@ -1,8 +0,0 @@ -# Tools - -Tools can be used together with agents powered by the OpenAI's ChatCompletion or the Assistant API. -AGNext provides the {py:mod}`agnext.components.tools` module with a suite of built-in -tools and utilities for creating and running custom tools. - -See [examples](https://github.com/microsoft/agnext/tree/main/python/examples#tool-use-examples) -for how to use the built-in code execution tool and creating custom tools. diff --git a/python/docs/src/getting-started/core-concepts.md b/python/docs/src/getting-started/core-concepts.md new file mode 100644 index 000000000..691d865a7 --- /dev/null +++ b/python/docs/src/getting-started/core-concepts.md @@ -0,0 +1,456 @@ +# Core Concepts + +## What is Multi-Agent Application? + +A wide variety of software applications can be modeled as a collection of independent +agents that communicate with each other through messages: +sensors on a factory floor, +distributed services powering web applications, +business workflows involving multiple stakeholders, +and more recently, generative artificial intelligence (AI) models (e.g., GPT-4) that can write code and interact with +other software systems. +We refer to them as multi-agent applications. + +In a multi-agent application, agents can live in the same process, on the same machine, +or on different machines and across organizational boundaries. +They can be implemented using different AI models, instructions, and programming languages. +They can collaborate and work toward a common goal. + +Each agent is a self-contained unit: +developers can build, test and deploy it independently, and reuse it for different scenarios. +Agents are composable: simple agents can form complex applications. + +## AGNext Overview + +AGNext is a framework for building multi-agent applications. +It provides a runtime envionment to facilitate communication between agents, +manage their identities and lifecycles, and enforce boundaries. +It also provides a set of common patterns and components to help developers build +AI agents that can work together. + +AGNext is designed to be unopinionated and extensible. +It does not prescribe an abstraction for agents or messages, rather, it provides +a minimal base layer that can be extended to suit the application's needs. +Developers can build agents quickly by using the provided components including +type-routed agent, AI model clients, tools for AI models, code execution sandboxes, +memory stores, and more. +Developers can also make use of the provided multi-agent patterns to build +orchestrated workflows, group chat systems, and more. + +The API consists of the following modules: + +- {py:mod}`agnext.core` - The core interfaces that defines agent and runtime. +- {py:mod}`agnext.application` - Implementations of the runtime and other modules (e.g., logging) for building applications. +- {py:mod}`agnext.components` - Independent agent-building components: agents, models, memory, and tools. + +## Agent and Agent Runtime + +An agent in AGNext is an entity that can react to, send, and publish +messages. Messages are the only means through which agents can communicate +with each other. + +An agent runtime is the execution environment for agents in AGNext. +Similar to the runtime environment of a programming language, the +agent runtime provides the necessary infrastructure to facilitate communication +between agents, manage agent lifecycles, enforce security boundaries, and support monitoring and +debugging. +For local development, developers can use {py:class}`~agnext.application.SingleThreadedAgentRuntime`, +which can be embedded in a Python application. + +```{note} +Agents are not directly instantiated and managed by application code. +Instead, they are created by the runtime when needed and managed by the runtime. +``` + +### Implementing an Agent + +To implement an agent, developer must subclass the {py:class}`~agnext.core.BaseAgent` class, +declare the message types it can handle in the {py:attr}`~agnext.core.AgentMetadata.subscriptions` metadata, +and implement the {py:meth}`~agnext.core.BaseAgent.on_message` method. +This method is invoked when the agent receives a message. For example, +the following agent handles string messages and simply prints message it receives: + +```python +from agnext.core import BaseAgent, CancellationToken + +class MyAgent(BaseAgent): + def __init__(self): + super().__init__("MyAgent", subscriptions=[str]) + + async def on_message(self, message: str, cancellation_token: CancellationToken) -> None: + print(f"Received message: {message}") +``` + +For convenience, developers can subclass the {py:class}`~agnext.components.TypeRoutedAgent` class +which provides an easy-to use API to implement different message handlers for different message types. +See the section on message handlers below. + +### Registering Agents + +To make an agent available to the runtime, developers can use the +{py:meth}`~agnext.core.AgentRuntime.register` method. +The process of registration associates a name and a factory function +that creates an instance of the agent in a given namespace. +The factory function is used to allow automatic creation of agents when they are needed. + +For example, to register an agent with the {py:class}`~agnext.application.SingleThreadedAgentRuntime`, +the following code can be used: + +```python +from agnext.application import SingleThreadedAgentRuntime + +runtime = SingleThreadedAgentRuntime() +runtime.register("my_agent", lambda: MyAgent()) +``` + +Once an agent is registered, a reference to the agent can be retrieved by +calling {py:meth}`~agnext.core.AgentRuntime.get` or +{py:meth}`~agnext.core.AgentRuntime.get_proxy`. For example, to +send a message to the agent we just registered: + +```python +agent = runtime.get("my_agent") +await runtime.send_message("Hello, World!", agent) +``` + +There is a convenience method +{py:meth}`~agnext.core.AgentRuntime.register_and_get` that both registers an agent +and gets a reference. + +```{note} +Because the runtime manages the lifecycle of agents, a reference to an agent, +whether it is {py:class}`~agnext.core.AgentId` or {py:class}`~agnext.core.AgentProxy`, +is only used to communicate with the agent or retrieve its metadata (e.g., description). +``` + +### Running the Agent Runtime + +The above code snippets will not actually produce any output because the +runtime is not running. +The local embedded runtime {py:class}`~agnext.application.SingleThreadedAgentRuntime` +can be called to process messages until there are no more messages to process. + +```python +await runtime.process_until_idle() +``` + +It can also be called to process a single message: + +```python +await runtime.process_next() +``` + +Other runtime implementations will have their own way of running the runtime. + +## Messages + +Agents communicate with each other via messages. +Messages are serializable objects, they can be defined using: + +- A subclass of Pydantic's {py:class}`pydantic.BaseModel`, or +- A dataclass +- A built-in serializable Python type (e.g., `str`). + +For example: + +```python +from dataclasses import dataclass + +@dataclass +class TextMessage: + content: str + source: str + +@dataclass +class ImageMessage: + url: str + source: str +``` + +```{note} +Messages are purely data, and should not contain any logic. +``` + +### Message Handlers + +When an agent receives a message the runtime will invoke the agent's message handler +({py:meth}`~agnext.core.Agent.on_message`) which should implement the agents message handling logic. +If this message cannot be handled by the agent, the agent should raise a +{py:class}`~agnext.core.exceptions.CantHandleException`. +For convenience, the {py:class}`~agnext.components.TypeRoutedAgent` base class +provides a simple API for associating message types with message handlers, +so developers do not need to implement the {py:meth}`~agnext.core.Agent.on_message` method. + +For example, the following type-routed agent responds to `TextMessage` and `ImageMessage` +using different message handlers: + +```python +from agnext.application import SingleThreadedAgentRuntime +from agnext.components import TypeRoutedAgent, message_handler +from agnext.core import CancellationToken + +class MyAgent(TypeRoutedAgent): + @message_handler + async def on_text_message(self, message: TextMessage, cancellation_token: CancellationToken) -> None: + print(f"Hello, {message.source}, you said {message.content}!") + + @message_handler + async def on_image_message(self, message: ImageMessage, cancellation_token: CancellationToken) -> None: + print(f"Hello, {message.source}, you sent me {message.url}!") + +async def main() -> None: + runtime = SingleThreadedAgentRuntime() + agent = runtime.register_and_get("my_agent", lambda: MyAgent("My Agent")) + await runtime.send_message(TextMessage(content="Hello, World!", source="User"), agent) + await runtime.send_message(ImageMessage(url="https://example.com/image.jpg", source="User"), agent) + runtime.process_until_idle() + +import asyncio +asyncio.run(main()) +``` + +## Communication + +There are two types of communication in AGNext: + +- **Direct communication**: An agent sends a direct message to another agent. +- **Broadcast communication**: An agent publishes a message to all agents in the same namespace. + +### Direct Communication + +To send a direct message to another agent, within a message handler use +the {py:meth}`agnext.core.BaseAgent.send_message` method, +from the runtime use the {py:meth}`agnext.core.AgentRuntime.send_message` method. + +Awaiting this method call will return the a `Future[T]` object where `T` is the type +of response of the invoked agent. +The future object can be awaited to get the actual response. + +```{note} +If the invoked agent raises an exception while the sender is awaiting on +the future, the exception will be propagated back to the sender. +``` + +#### Request/Response + +Direct communication can be used for request/response scenarios, +where the sender expects a response from the receiver. +The receiver can respond to the message by returning a value from its message handler. +You can think of this as a function call between agents. + +For example, consider the following type-routed agents: + +```python +from agnext.application import SingleThreadedAgentRuntime +from agnext.components import TypeRoutedAgent, message_handler +from agnext.core import CancellationToken, AgentId + +class InnerAgent(TypeRoutedAgent): + @message_handler + async def on_str_message(self, message: str, cancellation_token: CancellationToken) -> str: + return f"Hello from inner, {message}" + +class OuterAgent(TypeRoutedAgent): + def __init__(self, inner_agent_id: AgentId): + super().__init__("OuterAgent") + self.inner_agent_id = inner_agent_id + + @message_handler + async def on_str_message(self, message: str, cancellation_token: CancellationToken) -> None: + print(f"Received message: {message}") + # Send a direct message to the inner agent and receves a response future. + response_future = await self.send_message(f"Hello from outer, {message}", self.inner_agent_id) + # Wait for the response to be ready. + response = await response_future + print(f"Received inner response: {response}") + +async def main() -> None: + runtime = SingleThreadedAgentRuntime() + inner = runtime.register_and_get("inner_agent", lambda: InnerAgent("InnerAgent")) + outer = runtime.register_and_get("outer_agent", lambda: OuterAgent("OuterAgent", inner)) + await runtime.send_message("Hello, World!", outer) + runtime.process_until_idle() + +import asyncio +asyncio.run(main()) +``` + +In the above example, upone receving a message, +the `OuterAgent` sends a direct string message to the `InnerAgent` and receives +a string message in response. The following output will be produced: + +```text +Received message: Hello, World! +Received inner response: Hello from inner, Hello from outer, Hello, World! +``` + +```{note} +To get the response after sending a message, the sender must await on the +response future. So you can also write `response = await await self.send_message(...)`. +``` + +#### Send, No Reply + +In many scenarios, the sender does not need a response from the receiver. +In this case, the sender does not need to await on the response future, +and the receiver does not need to return a value from the message handler. +In the following example, the `InnerAgent` does not return a value, +and the `OuterAgent` does not await on the response future: + +```python +from agnext.application import SingleThreadedAgentRuntime +from agnext.components import TypeRoutedAgent, message_handler +from agnext.core import CancellationToken, AgentId + +class InnerAgent(TypeRoutedAgent): + @message_handler + async def on_str_message(self, message: str, cancellation_token: CancellationToken) -> None: + # Just print the message. + print(f"Hello from inner, {message}") + +class OuterAgent(TypeRoutedAgent): + def __init__(self, inner_agent_id: AgentId): + super().__init__("OuterAgent") + self.inner_agent_id = inner_agent_id + + @message_handler + async def on_str_message(self, message: str, cancellation_token: CancellationToken) -> None: + print(f"Received message: {message}") + # Send a direct message to the inner agent and move on. + await self.send_message(f"Hello from outer, {message}", self.inner_agent_id) + # No need to wait for the response, just do other things. + +async def main() -> None: + runtime = SingleThreadedAgentRuntime() + inner = runtime.register_and_get("inner_agent", lambda: InnerAgent("InnerAgent")) + outer = runtime.register_and_get("outer_agent", lambda: OuterAgent("OuterAgent", inner)) + await runtime.send_message("Hello, World!", outer) + runtime.process_until_idle() + +import asyncio +asyncio.run(main()) +``` + +In the above example, the `OuterAgent` sends a direct string message to the `InnerAgent` +but does not await on the response future. The following output will be produced: + +```text +Received message: Hello, World! +Hello from inner, Hello from outer, Hello, World! +``` + +### Broadcast Communication + +Broadcast communication is effectively the publish/subscribe model. +As part of the base agent ({py:class}`~agnext.core.BaseAgent`) implementation, +it must advertise the message types that +it would like to receive when published ({py:attr}`~agnext.core.AgentMetadata.subscriptions`). +If one of these messages is published, the agent's message handler will be invoked. + +The key difference between direct and broadcast communication is that broadcast +communication cannot be used for request/response scenarios. +When an agent publishes a message it is one way only, it cannot receive a response +from any other agent, even if a receiving agent sends a response. + +```{note} +An agent receiving a message does not know if it is handling a published or direct message. +So, if a response is given to a published message, it will be thrown away. +``` + +To publish a message to all agents in the same namespace, +use the {py:meth}`agnext.core.BaseAgent.publish_message` method. +This call must still be awaited to allow the runtime to deliver the message to all agents, +but it will always return `None`. +If an agent raises an exception while handling a published message, +this will be logged but will not be propagated back to the publishing agent. + +The following example shows a `BroadcastingAgent` that publishes a message +upong receiving a string message. A `ReceivingAgent` that prints the message +it receives. + +```python +from agnext.application import SingleThreadedAgentRuntime +from agnext.components import TypeRoutedAgent, message_handler +from agnext.core import CancellationToken + +class BroadcastingAgent(TypeRoutedAgent): + @message_handler + async def on_str_message(self, message: str, cancellation_token: CancellationToken) -> None: + # Publish a message to all agents in the same namespace. + await self.publish_message(f"Publishing a message: {message}!") + +class ReceivingAgent(TypeRoutedAgent): + @message_handler + async def on_str_message(self, message: str, cancellation_token: CancellationToken) -> None: + print(f"Received a message: {message}") + +async def main() -> None: + runtime = SingleThreadedAgentRuntime() + broadcaster = runtime.register_and_get("broadcasting_agent", lambda: BroadcastingAgent("Broadcasting Agent")) + runtime.register("receiving_agent", lambda: ReceivingAgent("Receiving Agent")) + await runtime.send_message("Hello, World!", broadcaster) + runtime.process_until_idle() + +import asyncio +asyncio.run(main()) +``` + +Running the above code will produce the following output produced by the `ReceivingAgent`: + +```text +Received a message: Publishing a message: Hello, World! +``` + +To publish a message to all agents outside of an agent handling a message, +the message should be published via the runtime with the +{py:meth}`agnext.core.AgentRuntime.publish_message` method. + +```python +await runtime.publish_message("Hello, World! From the runtime!", namespace="default") +runtime.process_until_idle() +``` + +Running the above code will produce the following output: + +```text +Received a message: Hello, World! From the runtime! +Received a message: Publishing a message: Hello, World! From the runtime! +``` + +The first output is from the `ReceivingAgent` that received a message published +by the runtime. The second output is from the `ReceivingAgent` that received +a message published by the `BroadcastingAgent`. + +```{note} +If an agent publishes a message type for which it is subscribed it will not +receive the message it published. This is to prevent infinite loops. +``` + +## Namespace + +Namespace allow for defining logical boundaries between agents. + +Namespaces are strings, and the default is `default`. + +Two possible use cases of agents are: + +- Creating a multi-tenant system where each tenant has its own namespace. For + example, a chat system where each tenant has its own set of agents. +- Security boundaries between agent groups. For example, a chat system where + agents in the `admin` namespace can communicate with agents in the `user` + namespace, but not the other way around. + +The {py:class}`~agnext.core.AgentId` is used to address an agent, +it is the combination of the agent's namespace and its name. + +When getting an agent reference ({py:meth}`agnext.core.AgentRuntime.get`) or +proxy ({py:meth}`agnext.core.AgentRuntime.get_proxy`) from the runtime the +namespace can be specified. +Agents have an ID property ({py:attr}`agnext.core.Agent.id`) that returns the +agent's id. +Additionally, the register method takes a factory that can optionally accept +the ID as an argument ({py:meth}`agnext.core.AgentRuntime.register`). + +By default, there are no restrictions and are left to the application to +enforce. The runtime will however automatically create agents in a +namespace if it does not exist. diff --git a/python/docs/src/getting-started/tutorial.md b/python/docs/src/getting-started/tutorial.md deleted file mode 100644 index 4f50eccc5..000000000 --- a/python/docs/src/getting-started/tutorial.md +++ /dev/null @@ -1 +0,0 @@ -# Tutorial diff --git a/python/docs/src/guides/components.md b/python/docs/src/guides/components.md new file mode 100644 index 000000000..a934f24b7 --- /dev/null +++ b/python/docs/src/guides/components.md @@ -0,0 +1,37 @@ +# Agent Components + +AGNext provides a suite of components to help developers build agents. + +## Type-Routed Agent + +The {py:class}`~agnext.components.TypeRoutedAgent` base class provides +developer with a simple decorator {py:meth}`~agnext.components.message_handler` +for associating message types with message handlers. + +## Model Clients + +AGNext provides the {py:mod}`agnext.components.models` module with a suite of built-in +model clients for using ChatCompletion API. + +## Tools + +Tools can be used together with agents powered by the OpenAI's ChatCompletion or the Assistant API. +AGNext provides the {py:mod}`agnext.components.tools` module with a suite of built-in +tools and utilities for creating and running custom tools. + +See [examples](https://github.com/microsoft/agnext/tree/main/python/examples#tool-use-examples) +for how to use the built-in code execution tool and creating custom tools. + +## Memory + +Memory is a collection of data corresponding to the conversation history +of an agent. +Data in meory can be just a simple list of all messages, +or one which provides a view of the last N messages. + +To create a custom memory implementation, you need to subclass the +{py:class}`agnext.components.memory.ChatMemory` protocol class and implement +all its methods. +For example, you can use [LLMLingua](https://github.com/microsoft/LLMLingua) +to create a custom memory implementation that provides a compressed +view of the conversation history. diff --git a/python/docs/src/core-concepts/logging.md b/python/docs/src/guides/logging.md similarity index 100% rename from python/docs/src/core-concepts/logging.md rename to python/docs/src/guides/logging.md diff --git a/python/docs/src/core-concepts/patterns.md b/python/docs/src/guides/patterns.md similarity index 89% rename from python/docs/src/core-concepts/patterns.md rename to python/docs/src/guides/patterns.md index 4e640ae03..20042adba 100644 --- a/python/docs/src/core-concepts/patterns.md +++ b/python/docs/src/guides/patterns.md @@ -8,7 +8,6 @@ multi-agent systems out-performing single agent systems at complex tasks like software development. You can implement any multi-agent pattern using AGNext agents, which -communicate with each other using messages through the agent runtime -(see {doc}`/core-concepts/runtime` and {doc}`/core-concepts/agent`). +communicate with each other using messages through the agent runtime. See [examples](https://github.com/microsoft/agnext/tree/main/python/examples#pattern-examples) for how to implement patterns like reflection and group chat. diff --git a/python/docs/src/core-concepts/worker_protocol.md b/python/docs/src/guides/worker-protocol.md similarity index 100% rename from python/docs/src/core-concepts/worker_protocol.md rename to python/docs/src/guides/worker-protocol.md diff --git a/python/docs/src/index.rst b/python/docs/src/index.rst index ea40f3764..2f0928604 100644 --- a/python/docs/src/index.rst +++ b/python/docs/src/index.rst @@ -3,18 +3,16 @@ AGNext AGNext is a framework for building multi-agent applications. -At a high level, it provides a framework for inter-agent communication and a -suite of independent components for building and managing agents. It models agents as -independent actors communicating via messages. You can implement agents in -different languages and run them on different machines across organizational boundaries. +At a high level, it provides a framework for inter-agent communication and a +suite of independent components for building and managing agents. +You can implement agents in +different programming languages and run them on different machines across organizational boundaries. You can also implement agents using other agent frameworks and run them in AGNext. -:doc:`Agents ` are hosted by and managed by a :doc:`runtime `. -AGNext supports both RPC-like direct messaging and event based -communication between agents, allowing for a :doc:`diverse set of agent patterns -`. +Please read :doc:`Core Concepts ` for +a detailed overview of AGNext's architecture and design. -AGNext's developer API consists of the following layers: +AGNext's API consists of the following modules: - :doc:`core ` - The core interfaces that defines agent and runtime. - :doc:`application ` - Implementations of the runtime and other modules (e.g., logging) for building applications. @@ -29,30 +27,26 @@ that demonstrate how to use AGNext. :hidden: getting-started/installation - getting-started/tutorial - -.. toctree:: - :caption: Core Concepts - :hidden: - - core-concepts/runtime - core-concepts/agent - core-concepts/patterns - core-concepts/memory - core-concepts/tools - core-concepts/cancellation - core-concepts/logging - core-concepts/namespace - core-concepts/worker_protocol + getting-started/core-concepts .. toctree:: :caption: Guides :hidden: - guides/type-routed-agent - guides/azure-openai-with-aad-auth - guides/termination-with-intervention - guides/extracting-results-with-an-agent + guides/components + guides/patterns + guides/logging + guides/worker-protocol + +.. toctree:: + :caption: Cookbook + :hidden: + + cookbook/type-routed-agent + cookbook/azure-openai-with-aad-auth + cookbook/termination-with-intervention + cookbook/buffered-memory + cookbook/extracting-results-with-an-agent .. toctree::