ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,393 @@
|
||||
# Building and Running Agents
|
||||
|
||||
In AWorld's design, both Workflows and Multi-Agent Systems (MAS) are complex systems built around Agents as the core
|
||||
component. Using the most common llm_agent as an example, this tutorial provides detailed guidance on:
|
||||
|
||||
1. How to quickly build an Agent
|
||||
2. How to customize an Agent
|
||||
This document is divided into two parts to explain AWorld's design philosophy.
|
||||
|
||||
## Part 1: Quick Agent Setup
|
||||
|
||||
### Declaring an Agent
|
||||
|
||||
```python
|
||||
from aworld.agents.llm_agent import Agent
|
||||
|
||||
# Assign a name to your agent
|
||||
agent = Agent(name="my_agent")
|
||||
```
|
||||
|
||||
### Configuring LLM
|
||||
|
||||
#### Method 1: Using Environment Variables
|
||||
|
||||
```python
|
||||
import os
|
||||
|
||||
## Set up LLM service using environment variables
|
||||
os.environ["LLM_PROVIDER"] = "openai" # Choose from: openai, anthropic, azure_openai
|
||||
os.environ["LLM_MODEL_NAME"] = "gpt-4"
|
||||
os.environ["LLM_API_KEY"] = "your-api-key"
|
||||
os.environ["LLM_BASE_URL"] = "https://api.openai.com/v1" # Optional for OpenAI
|
||||
```
|
||||
|
||||
#### Method 2: Using AgentConfig
|
||||
|
||||
```python
|
||||
import os
|
||||
from aworld.agents.llm_agent import Agent
|
||||
from aworld.config.conf import AgentConfig
|
||||
|
||||
agent_config = AgentConfig(
|
||||
llm_provider=os.getenv("LLM_PROVIDER", "openai"),
|
||||
llm_model_name=os.getenv("LLM_MODEL_NAME"),
|
||||
llm_base_url=os.getenv("LLM_BASE_URL"),
|
||||
llm_api_key=os.getenv("LLM_API_KEY"),
|
||||
)
|
||||
|
||||
agent = Agent(name="my_agent", conf=agent_config)
|
||||
```
|
||||
|
||||
#### Method 3: Using Shared ModelConfig
|
||||
|
||||
When multiple agents use the same LLM service, you can specify a shared ModelConfig:
|
||||
|
||||
```python
|
||||
import os
|
||||
from aworld.agents.llm_agent import Agent
|
||||
from aworld.config.conf import AgentConfig, ModelConfig
|
||||
|
||||
# Create a shared model configuration
|
||||
model_config = ModelConfig(
|
||||
llm_provider=os.getenv("LLM_PROVIDER", "openai"),
|
||||
llm_model_name=os.getenv("LLM_MODEL_NAME"),
|
||||
llm_base_url=os.getenv("LLM_BASE_URL"),
|
||||
llm_api_key=os.getenv("LLM_API_KEY"),
|
||||
)
|
||||
|
||||
# Use the shared model config in agent configuration
|
||||
agent_config = AgentConfig(
|
||||
llm_config=model_config,
|
||||
)
|
||||
|
||||
agent = Agent(name="my_agent", conf=agent_config)
|
||||
```
|
||||
|
||||
### Configuring Prompts
|
||||
|
||||
```python
|
||||
from aworld.agents.llm_agent import Agent
|
||||
import os
|
||||
from aworld.config.conf import AgentConfig, ModelConfig
|
||||
|
||||
model_config = ModelConfig(
|
||||
llm_provider=os.getenv("LLM_PROVIDER", "openai"),
|
||||
llm_model_name=os.getenv("LLM_MODEL_NAME"),
|
||||
llm_base_url=os.getenv("LLM_BASE_URL"),
|
||||
llm_api_key=os.getenv("LLM_API_KEY"),
|
||||
)
|
||||
|
||||
agent_config = AgentConfig(
|
||||
llm_config=model_config,
|
||||
)
|
||||
|
||||
# Define your system prompt
|
||||
system_prompt = """You are a helpful AI assistant that can assist users with various tasks.
|
||||
You should be polite, accurate, and provide clear explanations."""
|
||||
|
||||
agent = Agent(
|
||||
name="my_agent",
|
||||
conf=agent_config,
|
||||
system_prompt=system_prompt
|
||||
)
|
||||
```
|
||||
|
||||
### Configuring Tools
|
||||
|
||||
#### Local Tools
|
||||
|
||||
```python
|
||||
from aworld.agents.llm_agent import Agent
|
||||
import os
|
||||
from aworld.config.conf import AgentConfig, ModelConfig
|
||||
from aworld.core.tool.func_to_tool import be_tool
|
||||
|
||||
model_config = ModelConfig(
|
||||
llm_provider=os.getenv("LLM_PROVIDER", "openai"),
|
||||
llm_model_name=os.getenv("LLM_MODEL_NAME"),
|
||||
llm_base_url=os.getenv("LLM_BASE_URL"),
|
||||
llm_api_key=os.getenv("LLM_API_KEY"),
|
||||
)
|
||||
|
||||
agent_config = AgentConfig(
|
||||
llm_config=model_config,
|
||||
)
|
||||
|
||||
system_prompt = """You are a helpful agent with access to various tools."""
|
||||
|
||||
|
||||
# Define a local tool using the @be_tool decorator
|
||||
|
||||
@be_tool(tool_name='greeting_tool', tool_desc="A simple greeting tool that returns a hello message")
|
||||
def greeting_tool() -> str:
|
||||
return "Hello, world!"
|
||||
|
||||
|
||||
agent = Agent(
|
||||
name="my_agent",
|
||||
conf=agent_config,
|
||||
system_prompt=system_prompt,
|
||||
tool_names=['greeting_tool']
|
||||
)
|
||||
```
|
||||
|
||||
#### MCP (Model Context Protocol) Tools
|
||||
|
||||
```python
|
||||
from aworld.agents.llm_agent import Agent
|
||||
import os
|
||||
from aworld.config.conf import AgentConfig, ModelConfig
|
||||
|
||||
model_config = ModelConfig(
|
||||
llm_provider=os.getenv("LLM_PROVIDER", "openai"),
|
||||
llm_model_name=os.getenv("LLM_MODEL_NAME"),
|
||||
llm_base_url=os.getenv("LLM_BASE_URL"),
|
||||
llm_api_key=os.getenv("LLM_API_KEY"),
|
||||
)
|
||||
|
||||
agent_config = AgentConfig(
|
||||
llm_config=model_config,
|
||||
)
|
||||
|
||||
system_prompt = """You are a helpful agent with access to file system operations."""
|
||||
|
||||
# Configure MCP servers
|
||||
|
||||
mcp_config = {
|
||||
"mcpServers": {
|
||||
"GorillaFileSystem": {
|
||||
"type": "stdio",
|
||||
"command": "python",
|
||||
"args": ["examples/BFCL/mcp_tools/gorilla_file_system.py"],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
agent = Agent(
|
||||
name="my_agent",
|
||||
conf=agent_config,
|
||||
system_prompt=system_prompt,
|
||||
mcp_servers=list(mcp_config.get("mcpServers", {}).keys()),
|
||||
mcp_config=mcp_config
|
||||
)
|
||||
```
|
||||
|
||||
#### Agent as Tool
|
||||
|
||||
```python
|
||||
from aworld.agents.llm_agent import Agent
|
||||
import os
|
||||
from aworld.config.conf import AgentConfig, ModelConfig
|
||||
|
||||
model_config = ModelConfig(
|
||||
llm_provider=os.getenv("LLM_PROVIDER", "openai"),
|
||||
llm_model_name=os.getenv("LLM_MODEL_NAME"),
|
||||
llm_base_url=os.getenv("LLM_BASE_URL"),
|
||||
llm_api_key=os.getenv("LLM_API_KEY"),
|
||||
)
|
||||
|
||||
agent_config = AgentConfig(
|
||||
llm_config=model_config,
|
||||
)
|
||||
|
||||
system_prompt = """You are a helpful agent that can delegate tasks to other specialized agents."""
|
||||
|
||||
# Create a specialized tool agent
|
||||
tool_agent = Agent(name="tool_agent", conf=agent_config)
|
||||
|
||||
# Create the main agent that can use the tool agent
|
||||
agent = Agent(
|
||||
name="my_agent",
|
||||
conf=agent_config,
|
||||
system_prompt=system_prompt,
|
||||
agent_names=['tool_agent']
|
||||
)
|
||||
```
|
||||
|
||||
## Part 2: Customizing Agents
|
||||
|
||||
### Customizing Agent Input
|
||||
|
||||
Override the `init_observation()` function to customize how your agent processes initial observations:
|
||||
|
||||
```python
|
||||
async def init_observation(self, observation: Observation) -> Observation:
|
||||
# You can add extended information from other agents or third-party storage
|
||||
# For example, enrich the observation with additional context
|
||||
observation.metadata = {"timestamp": time.time(), "source": "custom"}
|
||||
return observation
|
||||
```
|
||||
|
||||
### Customizing Model Input
|
||||
|
||||
Override the `async_messages_transform()` function to customize how messages are transformed before being sent to the
|
||||
model:
|
||||
|
||||
```python
|
||||
async def async_messages_transform(self,
|
||||
image_urls: List[str] = None,
|
||||
observation: Observation = None,
|
||||
message: Message = None,
|
||||
**kwargs) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Transform input data into the format expected by the LLM.
|
||||
|
||||
Args:
|
||||
image_urls: List of images encoded using base64
|
||||
observation: Observation from the environment
|
||||
message: Event received by the Agent
|
||||
"""
|
||||
messages = []
|
||||
|
||||
# Add system context
|
||||
if hasattr(self, 'system_prompt'):
|
||||
messages.append({"role": "system", "content": self.system_prompt})
|
||||
|
||||
# Add user message
|
||||
if message and message.content:
|
||||
messages.append({"role": "user", "content": message.content})
|
||||
|
||||
# Add images if present
|
||||
if image_urls:
|
||||
for img_url in image_urls:
|
||||
messages.append({
|
||||
"role": "user",
|
||||
"content": [{"type": "image_url", "image_url": {"url": img_url}}]
|
||||
})
|
||||
|
||||
return messages
|
||||
```
|
||||
|
||||
### Customizing Model Logic
|
||||
Override the `invoke_model()` function to implement custom model logic:
|
||||
```python
|
||||
async def invoke_model(self,
|
||||
messages: List[Dict[str, str]] = [],
|
||||
message: Message = None,
|
||||
**kwargs) -> ModelResponse:
|
||||
"""Custom model invocation logic.
|
||||
You can use neural networks, rule-based systems, or any other business logic.
|
||||
"""
|
||||
|
||||
# Example: Use a custom model or business logic
|
||||
if self.use_custom_logic:
|
||||
# Your custom logic here
|
||||
response_content = self.custom_model.predict(messages)
|
||||
else:
|
||||
# Use the default LLM
|
||||
response_content = await self.llm_client.chat_completion(messages)
|
||||
|
||||
return ModelResponse(
|
||||
id=f"response_{int(time.time())}",
|
||||
model=self.model_name,
|
||||
content=response_content,
|
||||
tool_calls=None # Set if tool calls are present
|
||||
)
|
||||
```
|
||||
|
||||
### Customizing Model Output
|
||||
Create a custom `ModelOutputParser` class and specify it using the `model_output_parser` parameter:
|
||||
```python
|
||||
from aworld.models.model_output_parser import ModelOutputParser
|
||||
|
||||
|
||||
class CustomOutputParser(ModelOutputParser[ModelResponse, AgentResult]):
|
||||
async def parse(self, resp: ModelResponse, **kwargs) -> AgentResult:
|
||||
"""Custom parsing logic based on your model's API response format."""
|
||||
|
||||
# Extract relevant information from the model response
|
||||
content = resp.content
|
||||
tool_calls = resp.tool_calls
|
||||
|
||||
# Create your custom AgentResult
|
||||
result = AgentResult(
|
||||
content=content,
|
||||
tool_calls=tool_calls,
|
||||
metadata={"parsed_at": time.time()}
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
# Use the custom parser
|
||||
|
||||
agent = Agent(
|
||||
name="my_agent",
|
||||
conf=agent_config,
|
||||
model_output_parser=CustomOutputParser()
|
||||
)
|
||||
```
|
||||
### Customizing Agent Response
|
||||
Override the `async_post_run()` function to customize how your agent responds:
|
||||
```python
|
||||
from aworld.core.message import Message
|
||||
|
||||
class CustomMessage(Message):
|
||||
def __init__(self, content: str, custom_field: str = None):
|
||||
super().__init__(content=content)
|
||||
self.custom_field = custom_field
|
||||
|
||||
async def async_post_run(self,
|
||||
policy_result: List[ActionModel],
|
||||
policy_input: Observation,
|
||||
message: Message = None) -> Message:
|
||||
"""
|
||||
Customize the agent's response after processing.
|
||||
"""
|
||||
|
||||
# Process the policy result and create a custom response
|
||||
response_content = f"Processed {len(policy_result)} actions"
|
||||
custom_field = "custom_value"
|
||||
|
||||
return CustomMessage(
|
||||
content=response_content,
|
||||
custom_field=custom_field
|
||||
)
|
||||
```
|
||||
|
||||
### Custom Response Parsing
|
||||
If the framework doesn't support your response structure, you can create a custom response parser:
|
||||
```python
|
||||
from aworld.runners import HandlerFactory
|
||||
from aworld.runners.default_handler import DefaultHandler
|
||||
|
||||
# Define a custom handler name
|
||||
custom_name = "custom_handler"
|
||||
|
||||
|
||||
@HandlerFactory.register(name=custom_name)
|
||||
class CustomHandler(DefaultHandler):
|
||||
def is_valid_message(self, message: Message):
|
||||
"""Check if this handler should process the message."""
|
||||
return message.category == custom_name
|
||||
|
||||
|
||||
async def _do_handle(self, message: Message) -> AsyncGenerator[Message, None]:
|
||||
"""Custom message processing logic."""
|
||||
if not self.is_valid_message(message):
|
||||
return
|
||||
|
||||
# Implement your custom message processing logic here
|
||||
processed_message = self.process_custom_message(message)
|
||||
yield processed_message
|
||||
|
||||
|
||||
# Use the custom handler
|
||||
agent = Agent(
|
||||
name="my_agent",
|
||||
conf=agent_config,
|
||||
event_handler_name=custom_name
|
||||
)
|
||||
```
|
||||
**Important Note:** The `custom_name` variable value must remain consistent across your handler registration and agent
|
||||
configuration.
|
||||
@@ -0,0 +1,88 @@
|
||||
# Environment
|
||||
|
||||
Mainly providing MCP servers in an independent environment to support high concurrency applications of MCP servers.
|
||||
|
||||
## Add MCP Servers
|
||||
|
||||
If you only use the built-in MCP servers, you only need to deploy them.
|
||||
|
||||
If there is a new MCP server and you want to use it independently, you can use the same directory structure
|
||||
as [gaia-mcp-server](../../env/gaia-mcp-server), and refer to the code structure and implementation of
|
||||
[hello_world](../../env/gaia-mcp-server/mcp_servers/hello_world).
|
||||
|
||||
```
|
||||
your_mcp_server/
|
||||
.dockerignore
|
||||
Dockfile
|
||||
mcp_servers/
|
||||
.env
|
||||
.gitignore
|
||||
mcp_config.py
|
||||
build_mcp_tool_schema.py
|
||||
init_env.sh
|
||||
your_tool/
|
||||
src/
|
||||
.python-version
|
||||
pyproject.toml
|
||||
```
|
||||
|
||||
The `mcp_config` variable in `mcp_config.py` is a standard MCP configuration structure.
|
||||
|
||||
Before deployment, it is necessary to run `build_mcp_tool_schema.py` to generate `mcp_tool_schema.json`.
|
||||
|
||||
`.env` is the environment configuration file for MCP servers.
|
||||
|
||||
## Depolyment
|
||||
|
||||
### Local Docker Deployment
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
Ensure Docker and Docker Compose are properly installed and operational:
|
||||
|
||||
```bash
|
||||
# Verify Docker installation
|
||||
docker --version
|
||||
docker compose --version
|
||||
|
||||
# Verify Docker daemon is running
|
||||
docker ps
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
**Step 1: Launch VirtualPC MCP Server**
|
||||
|
||||
```bash
|
||||
sh run-docker.sh
|
||||
```
|
||||
|
||||
Monitor the terminal output for any errors during startup.
|
||||
|
||||
**Step 2: Connect to VirtualPC MCP Server**
|
||||
|
||||
Use the following configuration to connect to the VirtualPC MCP Server:
|
||||
|
||||
```json
|
||||
{
|
||||
"virtualpc-mcp-server": {
|
||||
"type": "streamable-http",
|
||||
"url": "http://localhost:8000/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer your token",
|
||||
"MCP_SERVERS": "readweb-server,browser-server"
|
||||
},
|
||||
"timeout": 6000,
|
||||
"sse_read_timeout": 6000,
|
||||
"client_session_timeout_seconds": 6000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: The Bearer token is your own. The `MCP_SERVERS` header specifies the MCP server
|
||||
scope for your current connection, which should be a subset of server names defined in
|
||||
`mcp_servers/mcp_config.py`.
|
||||
|
||||
### Kubernetes Cluster Deployment
|
||||
|
||||
For production deployments and RL training scenarios, Kubernetes cluster deployment is recommended.
|
||||
Detailed instructions will be provided in future updates.
|
||||
@@ -0,0 +1,18 @@
|
||||
# Installation
|
||||
## AWorld Agent
|
||||
|
||||
### Prerequisites
|
||||
- Python 3.10+
|
||||
|
||||
### Install
|
||||
```shell
|
||||
git clone https://github.com/inclusionAI/AWorld && cd AWorld
|
||||
|
||||
pip install .
|
||||
```
|
||||
|
||||
## AWorld Env
|
||||
TODO
|
||||
|
||||
## AWorld Train
|
||||
TODO
|
||||
@@ -0,0 +1,166 @@
|
||||
# Building and Running Multi-Agent Systems (MAS)
|
||||
In the AWorld framework, similar to Workflow Construction, the fundamental building block for MAS is the Agent. By introducing the Swarm concept, users can easily, quickly, and efficiently build complex Multi-Agent Systems. In summary:
|
||||
|
||||
1. **Workflow in AWorld**: Static, pre-defined execution flows
|
||||
2. **MAS in AWorld**: Dynamic, real-time decision-making execution flows
|
||||
|
||||
This design ensures unified underlying capabilities (i.e., Agent, Graph-based Topology) while maintaining extensibility.
|
||||
|
||||
## Quick MAS Construction
|
||||
Similar to Workflows, we can easily define communication networks between Agents through topology. The key difference is that by using `build_type=GraphBuildType.HANDOFF`, we allow dynamic decision-making for inter-agent calling relationships:
|
||||
|
||||
1. `agent1` can selectively decide to call `agent2` and `agent3`; the number of calls is also dynamic (once or multiple times)
|
||||
2. `agent2` can selectively decide to call `agent3`; the number of calls is also dynamic (once or multiple times)
|
||||
|
||||
```python
|
||||
from aworld.config.conf import AgentConfig
|
||||
from aworld.agents.llm_agent import Agent
|
||||
from aworld.core.agent.swarm import Swarm, GraphBuildType
|
||||
from aworld.runner import Runners
|
||||
|
||||
# Configure agents
|
||||
agent_conf = AgentConfig(...)
|
||||
agent1 = Agent(name="agent1", conf=agent_conf)
|
||||
agent2 = Agent(name="agent2", conf=agent_conf)
|
||||
agent3 = Agent(name="agent3", conf=agent_conf)
|
||||
|
||||
# Create swarm with dynamic handoff topology
|
||||
swarm = Swarm(
|
||||
topology=[(agent1, agent2), (agent2, agent3), (agent1, agent3)],
|
||||
build_type=GraphBuildType.HANDOFF
|
||||
)
|
||||
|
||||
# Run the swarm
|
||||
Runners.run(input="your question", swarm=swarm)
|
||||
```
|
||||
|
||||
### Specifying Entry Agent
|
||||
Since MAS is essentially a Graph by definition, different Agents can accept external input. We can specify which Agent receives the query using the `root_agent` parameter.
|
||||
|
||||
```python
|
||||
swarm = Swarm(
|
||||
topology=[(agent1, agent2), (agent2, agent3), (agent1, agent3)],
|
||||
build_type=GraphBuildType.HANDOFF,
|
||||
root_agent=[agent1]
|
||||
)
|
||||
```
|
||||
|
||||
### Dynamic Routing
|
||||
When the `policy()` function decides which agent to call next, for special cases, Agents may need customized routing based on specific business rules. You can override the handler in the corresponding Agent:
|
||||
|
||||
```python
|
||||
# Handler name consistency must be maintained
|
||||
agent = Agent(..., event_handler_name="your_handler_name")
|
||||
```
|
||||
|
||||
```python
|
||||
from aworld.core.handler import HandlerFactory, DefaultHandler
|
||||
from aworld.core.message import Message
|
||||
from typing import AsyncGenerator
|
||||
|
||||
@HandlerFactory.register(name="your_handler_name")
|
||||
class YourHandler(DefaultHandler):
|
||||
def is_valid_message(self, message: Message) -> bool:
|
||||
return message.category == "your_handler_name"
|
||||
|
||||
async def _do_handle(self, message: Message) -> AsyncGenerator[Message, None]:
|
||||
if not self.is_valid_message(message):
|
||||
return
|
||||
|
||||
# The type of data is generally ActionModel, but can be customized
|
||||
data = message.payload
|
||||
if "clause1" in data:
|
||||
# Handle clause1 logic
|
||||
pass
|
||||
elif "clause2" in data:
|
||||
# Handle clause2 logic
|
||||
pass
|
||||
```
|
||||
|
||||
You can refer to the implementation of `DefaultTaskHandler` in AWorld.
|
||||
|
||||
#### Two Examples of Overriding Routing: ReAct and Plan-Execute
|
||||
##### ReAct
|
||||
```python
|
||||
@HandlerFactory.register(name='react')
|
||||
class ReactHandler(AgentHandler):
|
||||
def is_valid_message(self, message: Message):
|
||||
if message.category != 'react':
|
||||
return False
|
||||
return True
|
||||
|
||||
async def _do_handle(self, message: Message) -> AsyncGenerator[Message, None]:
|
||||
yield message
|
||||
```
|
||||
|
||||
##### Plan-Execute
|
||||
Compared to ReAct, agent2 and agent3 can execute in parallel simultaneously.
|
||||
|
||||
```python
|
||||
from aworld.core.common import Observation
|
||||
from aworld.core.event.base import AgentMessage
|
||||
from aworld.logs.util import logger
|
||||
|
||||
@HandlerFactory.register(name='plan_execute')
|
||||
class PlanExecuteHandler(AgentHandler):
|
||||
def is_valid_message(self, message: Message):
|
||||
if message.category != 'plan_execute':
|
||||
return False
|
||||
return True
|
||||
|
||||
async def _do_handle(self, message: Message) -> AsyncGenerator[Message, None]:
|
||||
logger.info(f"PlanExecuteHandler|handle|taskid={self.task_id}|is_sub_task={message.context._task.is_sub_task}")
|
||||
content = message.payload
|
||||
|
||||
# Parse model plan
|
||||
plan = parse_plan(content[0].policy_info)
|
||||
logger.info(f"PlanExecuteHandler|plan|{plan}")
|
||||
|
||||
# Execute steps
|
||||
output, context = execution_steps(plan.steps)
|
||||
|
||||
# Send event message, notify the next processing agent
|
||||
new_plan_input = Observation(content=output)
|
||||
yield AgentMessage(
|
||||
session_id=message.session_id,
|
||||
payload=new_plan_input,
|
||||
sender=self.name(),
|
||||
receiver=self.swarm.communicate_agent.id(),
|
||||
headers={'context': context}
|
||||
)
|
||||
```
|
||||
|
||||
For more details, refer to the examples.
|
||||
|
||||
## Combination and Recursion of MAS and Workflow
|
||||
Same or different types of Swarms can be deeply nested, providing multi-level Swarms with different interaction mechanisms to support complex multi-agent interactions. For example, when creating a travel itinerary planner, using a combination of Workflow + MAS, where Workflow provides deterministic processes and MAS handles multi-source information retrieval and integration.
|
||||
|
||||
```python
|
||||
from aworld.config.conf import AgentConfig
|
||||
from aworld.agents.llm_agent import Agent
|
||||
from aworld.core.agent.swarm import Swarm, GraphBuildType
|
||||
|
||||
# Configure agents
|
||||
agent_conf = AgentConfig(...)
|
||||
|
||||
# Create five agents
|
||||
rewrite = Agent(name="rewrite", conf=agent_conf)
|
||||
plan = Agent(name="plan", conf=agent_conf)
|
||||
search = Agent(name="search", conf=agent_conf)
|
||||
summary = Agent(name="summary", conf=agent_conf)
|
||||
report = Agent(name="report", conf=agent_conf)
|
||||
|
||||
# Construct a MAS
|
||||
mas = Swarm(
|
||||
topology=[(plan, search), (plan, summary)],
|
||||
build_type=GraphBuildType.HANDOFF,
|
||||
root_agent=[plan]
|
||||
)
|
||||
|
||||
# Construct a combination of a workflow with the MAS team
|
||||
combination = Swarm(
|
||||
topology=[(rewrite, mas), (mas, report)],
|
||||
root_agent=[rewrite]
|
||||
)
|
||||
```
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
|
||||
We use the classic graph syntax to describe workflows in AWorld.
|
||||
The following are the basic scenarios for constructing agent workflows.
|
||||
|
||||
## Agent Native Workflow
|
||||
|
||||
### Sequential
|
||||
```python
|
||||
"""
|
||||
Sequential Agent Pipeline: agent1 → agent2 → agent3
|
||||
|
||||
Executes agents in sequence where each agent's output becomes
|
||||
the next agent's input, enabling multi-step collaborative processing.
|
||||
"""
|
||||
|
||||
swarm = Swarm([(agent1, agent2), (agent2, agent3)], root_agent=[agent1])
|
||||
result: TaskResponse = Runners.run(input=question, swarm=swarm)
|
||||
```
|
||||
|
||||
### Parallel
|
||||
```python
|
||||
"""
|
||||
Parallel Agent Execution with Barrier Synchronization
|
||||
|
||||
Input ──┬─→ agent1 ──┐
|
||||
│ ├──→ agent3 (barrier wait)
|
||||
└─→ agent2 ──┘
|
||||
|
||||
- agent1 and agent2 execute in parallel
|
||||
- agent3 acts as a barrier, waiting for both agents
|
||||
- agent3 processes combined outputs from agent1 and agent2
|
||||
"""
|
||||
|
||||
swarm = Swarm([(agent1, agent3), (agent2, agent3)], root_agent=[agent1, agent2])
|
||||
result: TaskResponse = Runners.run(input=question, swarm=swarm)
|
||||
```
|
||||
|
||||
### Parallel Multi-Path
|
||||
```python
|
||||
"""
|
||||
Parallel Multi-Path Agent Execution
|
||||
|
||||
Input ──→ agent1 ──┬──→ agent2 ──┐
|
||||
│ │
|
||||
└──→ agent3 ←─┘ (barrier wait for agent1 & agent2)
|
||||
|
||||
- Single input enters only through agent1
|
||||
- agent1 distributes to both agent2 and agent3
|
||||
- agent2 processes and feeds agent3
|
||||
- agent3 waits for both agent1 and agent2 completion
|
||||
- agent3 synthesizes outputs from both agent1 and agent2
|
||||
"""
|
||||
|
||||
swarm = Swarm([(agent1, agent2), (agent1, agent3), (agent2, agent3)], root_agent=[agent1])
|
||||
result: TaskResponse = Runners.run(input=question, swarm=swarm)
|
||||
```
|
||||
|
||||
## Task Native Workflow
|
||||
Task native workflow is further implemented for Isolating the agent runtimes and environments,
|
||||
in the distributed or other easy-to-overlap scenarios.
|
||||
Task native workflow is further implemented for isolating agent runtimes and environments,
|
||||
particularly useful in distributed or other scenarios where tool-isolation is required.
|
||||
```python
|
||||
task1 = Task(input="my question", agent=agent1)
|
||||
task2 = Task(agent=agent2)
|
||||
task3 = Task(agent=agent3)
|
||||
tasks = [task1, task2, task3]
|
||||
|
||||
result: Dict[str, TaskResponse] = Runners.run_task(tasks, RunConfig(sequence_dependent=True))
|
||||
```
|
||||
Reference in New Issue
Block a user