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

This commit is contained in:
2026-08-20 13:12:50 +00:00
commit b119135836
10275 changed files with 3284984 additions and 0 deletions
@@ -0,0 +1,46 @@
# Multi-Agent Examples
This directory contains a variety of multi-agent system examples built on the AWorld framework.
These examples demonstrate three core paradigms of agent **collaboration**, **coordination**, and **workflow**,
corresponding to **Swarm** of Handoff, Team, and Workflow respectively.
## Examples of Paradigm
- **collaborative/**
- Multi-agent collaboration scenarios.
- **debate/**
Example of agents engaging in a debate, including affirmative, negative, and moderator agents. Demonstrates turn-based argumentation and multi-agent dialogue.
- **travel/**
Multi-agent interaction for travel planning.
- **coordination/**
- Multi-agent coordination and orchestration patterns.
- **custom_agent/**
Example for customizing agent roles and behaviors in a coordinated system.
- **master_worker/**
Demonstrates the TeamSwarm pattern, where a lead agent (PlanAgent) coordinates with specialized agents (SearchAgent, SummaryAgent) to solve complex tasks.
Includes both multi-action and single-action planning versions.
See `master_worker/README.md` for detailed workflow and advantages of each approach.
- **deepresearch/**
Advanced research scenario with a planner agent, web search agent, and reporting agent.
Shows how to break down user queries, plan search strategies, and synthesize results using a TeamSwarm.
- **workflow/**
- Workflow automation with multi-agent.
- **search/**
Example of agents collaborating to perform search and data aggregation tasks.
## Key Concepts
- **Collaboration:**
Agents work together to achieve a common goal, such as debating or planning a trip.
- **Coordination:**
Agents are orchestrated in a structured pattern to solve complex problems.
- **Workflow Automation:**
Agents automate multi-step processes, such as planning, searching, and summarizing information.
## Usage
- Each subdirectory contains its own entry point (usually `run.py`) and may include additional configuration or requirements files.
- Before running any example, ensure you have installed all required dependencies and set the necessary environment variables (e.g., LLM provider credentials, API keys).
- For detailed instructions, refer to the README or comments within each subdirectory.
@@ -0,0 +1,2 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
@@ -0,0 +1,2 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
@@ -0,0 +1,2 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
@@ -0,0 +1,45 @@
from pydantic import BaseModel, Field
from aworld.output import Output
import asyncio
from aworld.output.base import OutputPart
class DebateSpeech(Output, BaseModel):
name: str = Field(default="", description="name of the speaker")
type: str = Field(default="", description="speech type")
stance: str = Field(default="", description="stance of the speech")
content: str = Field(default="", description="content of the speech")
round: int = Field(default=0, description="round of the speech")
finished: bool = Field(default=False, description="round of the speech")
metadata: dict = Field(default_factory=dict, description="metadata of the speech")
async def wait_until_finished(self):
"""
Wait until the speech is finished.
"""
while not self.finished:
await asyncio.sleep(1)
async def convert_to_parts(self, message_output, after_call):
async def __convert_to_parts__():
async for item in message_output.response_generator:
if item:
self.content += item
yield OutputPart(content=item)
if message_output.finished:
await after_call(message_output.response)
self.parts = __convert_to_parts__()
@classmethod
def from_dict(cls, data: dict) -> "DebateSpeech":
return cls(
name=data.get("name", ""),
type=data.get("type", ""),
stance=data.get("stance", ""),
content=data.get("content", ""),
round=data.get("round", 0),
metadata=data.get("metadata", {})
)
@@ -0,0 +1,219 @@
import logging
from abc import ABC
from typing import Dict, Any, Union, List, Literal, Optional
from datetime import datetime
import uuid
from aworld.models.model_response import ToolCall
from examples.multi_agents.collaborative.debate.agent.base import DebateSpeech
from examples.multi_agents.collaborative.debate.agent.prompts import user_assignment_prompt, user_assignment_system_prompt, affirmative_few_shots, \
negative_few_shots, \
user_debate_prompt
from examples.multi_agents.collaborative.debate.agent.search.search_engine import SearchEngine
from examples.multi_agents.collaborative.debate.agent.search.tavily_search_engine import TavilySearchEngine
from examples.multi_agents.collaborative.debate.agent.stream_output_agent import StreamOutputAgent
from aworld.config import AgentConfig
from aworld.core.common import Observation, ActionModel
from aworld.output import SearchOutput, SearchItem, MessageOutput
from aworld.output.artifact import ArtifactType
def truncate_content(raw_content, char_limit):
if raw_content is None:
raw_content = ''
if len(raw_content) > char_limit:
raw_content = raw_content[:char_limit] + "... [truncated]"
return raw_content
class DebateAgent(StreamOutputAgent, ABC):
stance: Literal["affirmative", "negative"]
def __init__(self, conf: AgentConfig, name: str, stance: Literal["affirmative", "negative"], search_engine: Optional[SearchEngine] = TavilySearchEngine()):
conf.name = name
super().__init__(conf, name)
self.steps = 0
self.stance = stance
self.search_engine = search_engine
async def speech(self, topic: str, opinion: str,oppose_opinion: str, round: int, speech_history: list[DebateSpeech]) -> DebateSpeech:
observation = Observation(content=self.get_latest_speech(speech_history).content if self.get_latest_speech(speech_history) else "")
info = {
"topic": topic,
"round": round,
"opinion": opinion,
"oppose_opinion": oppose_opinion,
"history": speech_history
}
actions = await self.async_policy(observation, info)
return actions[0].policy_info
async def async_policy(self, observation: Observation, info: Dict[str, Any] = {}, **kwargs) -> Union[
List[ActionModel], None]:
## step 1: params
opponent_claim = observation.content
round = info["round"]
opinion = info["opinion"]
oppose_opinion = info["oppose_opinion"]
topic = info["topic"]
history: list[DebateSpeech] = info["history"]
#Event.emit("xxx")
## step2: gen keywords
keywords = await self.gen_keywords(topic, opinion, oppose_opinion, opponent_claim, history)
logging.info(f"gen keywords = {keywords}")
## step3search_webpages
search_results = await self.search_webpages(keywords, max_results=5)
for search_result in search_results:
logging.info(f"keyword#{search_result['query']}-> result size is {len(search_result['results'])}")
search_item = {
"query": search_result.get("query", ""),
"results": [SearchItem(title=result["title"],url=result["url"], content=result['content'], raw_content=result['raw_content'], metadata={}) for result in search_result["results"]],
"origin_tool_call": ToolCall.from_dict({
"id": f"call_search",
"type": "function",
"function": {
"name": "search",
"arguments": keywords
}
}),
"task_id": self.context.task_id
}
search_output = SearchOutput.from_dict(search_item)
await self.workspace.create_artifact(
artifact_type=ArtifactType.WEB_PAGES,
artifact_id=str(uuid.uuid4()),
content=search_output,
metadata={
"query": search_output.query,
"user": self.name(),
"round": info["round"],
"opinion": info["opinion"],
"oppose_opinion": info["oppose_opinion"],
"topic": info["topic"],
"tags": [f"user#{self.name()}",f"Rounds#{info['round']}"]
}
)
## step4 gen result
user_response = await self.gen_statement(topic, opinion, oppose_opinion, opponent_claim, history, search_results)
logging.info(f"user_response is {user_response}")
## step3: gen speech
speech = DebateSpeech.from_dict({
"round": round,
"type": "speech",
"stance": self.stance,
"name": self.name(),
})
async def after_speech_call(message_output_response):
logging.info(f"{self.stance}#{self.name()}: after_speech_call")
speech.metadata = {}
speech.content = message_output_response
speech.finished = True
await speech.convert_to_parts(user_response, after_speech_call)
action = ActionModel(
policy_info=speech
)
return [action]
async def gen_keywords(self, topic, opinion, oppose_opinion, last_oppose_speech_content, history):
current_time = datetime.now().strftime("%Y-%m-%d-%H")
human_prompt = user_assignment_prompt.format(topic=topic,
opinion=opinion,
oppose_opinion=oppose_opinion,
last_oppose_speech_content=last_oppose_speech_content,
current_time = current_time,
limit=2
)
messages = [{'role': 'system', 'content': user_assignment_system_prompt},
{'role': 'user', 'content': human_prompt}]
output = await self.async_call_llm(messages)
response = await output.get_finished_response()
return response.split(",")
async def search_webpages(self, keywords, max_results):
return await self.search_engine.async_batch_search(queries=keywords, max_results=max_results)
async def gen_statement(self, topic, opinion, oppose_opinion, opponent_claim, history, search_results) -> MessageOutput:
search_results_content = ""
for search_result in search_results:
search_results_content += f"SearchQuery: {search_result['query']}"
search_results_content += "\n\n".join([truncate_content(s['content'], 1000) for s in search_result['results']])
unique_history = history
# if len(history) >= 2:
# for i in range(len(history)):
# # Check if the current element is the same as the next one
# if i == len(history) - 1 or history[i] != history[i+1]:
# # Add the current element to the result list
# unique_history.append(history[i])
affirmative_chat_history = ""
negative_chat_history = ""
if len(unique_history) >= 2:
if self.stance == "affirmative":
for speech in unique_history[:-1]:
if speech.stance == "affirmative":
affirmative_chat_history = affirmative_chat_history + "You: " + speech.content + "\n"
elif speech.stance == "negative":
affirmative_chat_history = affirmative_chat_history + "Your Opponent: " + speech.content + "\n"
elif self.stance == "negative":
for speech in unique_history[:-1]:
if speech.stance == "negative":
negative_chat_history = negative_chat_history + "You: " + speech.content + "\n"
elif speech.stance == "affirmative":
negative_chat_history = negative_chat_history + "Your Opponent: " + speech.content + "\n"
few_shots = ""
chat_history = ""
if self.stance == "affirmative":
chat_history = affirmative_chat_history
few_shots = affirmative_few_shots
elif self.stance == "negative":
chat_history = negative_chat_history
few_shots = negative_few_shots
human_prompt = user_debate_prompt.format(topic=topic,
opinion=opinion,
oppose_opinion=oppose_opinion,
last_oppose_speech_content=opponent_claim,
search_results_content=search_results_content,
chat_history = chat_history,
few_shots = few_shots
)
messages = [{'role': 'system', 'content': user_assignment_system_prompt},
{'role': 'user', 'content': human_prompt}]
return await self.async_call_llm(messages)
def get_latest_speech(self, history: list[DebateSpeech]):
"""
get the latest speech from history
"""
if len(history) == 0:
return None
return history[-1]
def set_workspace(self, workspace):
self.workspace = workspace
@@ -0,0 +1,209 @@
import logging
from typing import Optional, AsyncGenerator
from aworld.memory.main import MemoryFactory
from examples.multi_agents.collaborative.debate.agent.base import DebateSpeech
from examples.multi_agents.collaborative.debate.agent.debate_agent import DebateAgent
from examples.multi_agents.collaborative.debate.agent.moderator_agent import ModeratorAgent
from aworld.core.common import Observation
from aworld.core.memory import MemoryItem
from aworld.output import Output, WorkSpace, ArtifactType, CodeArtifact
class DebateArena:
"""
DebateArena is platform for debate
"""
affirmative_speaker: DebateAgent
negative_speaker: DebateAgent
moderator: Optional[ModeratorAgent]
speeches: list[DebateSpeech]
display_panel: str
def __init__(self,
affirmative_speaker: DebateAgent,
negative_speaker: DebateAgent,
moderator: ModeratorAgent,
workspace: WorkSpace,
**kwargs
):
self.affirmative_speaker = affirmative_speaker
self.negative_speaker = negative_speaker
self.moderator = moderator
self.speeches = []
self.workspace = workspace
self.affirmative_speaker.set_workspace(workspace)
self.negative_speaker.set_workspace(workspace)
self.moderator.set_workspace(workspace)
self.moderator.memory = MemoryFactory.instance()
# Event.register("topic", func= );
async def async_run(self, topic: str, rounds: int) \
-> AsyncGenerator[Output, None]:
"""
Start the debate
1. debate will start from round 1
2. each round will have two speeches, one from affirmative_speaker and one from negative_speaker
3. after all rounds finished, the debate will end
Args:
topic: str -> topic of the debate
affirmative_opinion: str -> affirmative speaker's opinion
negative_opinion: str -> negative speaker's opinion
rounds: int -> number of rounds
Returns: list[DebateSpeech]
"""
## 1. generate opinions
moderator_speech = await self.moderator_speech(topic, rounds)
if not moderator_speech:
return
yield moderator_speech
await moderator_speech.wait_until_finished()
self.store_speech(moderator_speech)
affirmative_opinion = moderator_speech.metadata["affirmative_opinion"]
negative_opinion = moderator_speech.metadata["negative_opinion"]
logging.info(f"✈️==================================== opinions =============================================")
logging.info(f"topic: {topic}")
logging.info(f"affirmative_opinion: {affirmative_opinion}")
logging.info(f"negative_opinion: {negative_opinion}")
logging.info(f"✈️==================================== start... =============================================")
## 2. Alternating speeches
for i in range(1, rounds + 1):
logging.info(
f"✈️==================================== round#{i} start =============================================")
loading_speech = DebateSpeech.from_dict({
"content": f"\n\n**round#{i} start** \n\n",
"round": i,
"type": "loading",
"stance": "stage",
"name": "stage",
"finished": True
})
yield loading_speech
loading_speech = DebateSpeech.from_dict({
"content": f"\n\n【affirmative】✅:{self.affirmative_speaker.name()}\n Searching ....\n",
"round": i,
"type": "loading",
"stance": "stage",
"name": "stage",
"finished": True
})
yield loading_speech
# affirmative_speech
speech = await self.affirmative_speech(i, topic, affirmative_opinion, negative_opinion)
yield speech
await speech.wait_until_finished()
self.store_speech(speech)
loading_speech = DebateSpeech.from_dict({
"content": f"\n\n【negative】❌:{self.negative_speaker.name()}\n Searching ....\n",
"round": i,
"type": "loading",
"stance": "stage",
"name": "stage",
"finished": True
})
yield loading_speech
# negative_speech
speech = await self.negative_speech(i, topic, negative_opinion, affirmative_opinion)
yield speech
await speech.wait_until_finished()
self.store_speech(speech)
logging.info(
f"🛬==================================== round#{i} end =============================================")
## 3. Summary speeches
moderator_speech = await self.moderator.summary_speech()
if not moderator_speech:
return
yield moderator_speech
await moderator_speech.wait_until_finished()
await self.workspace.add_artifact(
CodeArtifact.build_artifact(
artifact_type=ArtifactType.CODE,
artifact_id="result",
code_type='html',
content=moderator_speech.content,
metadata={
"topic": topic
}
)
)
logging.info(
f"🛬==================================== total is end =============================================")
async def moderator_speech(self, topic, rounds) -> DebateSpeech | None:
results = await self.moderator.async_policy(Observation(content=topic, info={"rounds": rounds}))
if not results or not results[0] or not results[0].policy_info:
return None
return results[0].policy_info
async def affirmative_speech(self, round: int, topic: str, opinion: str, oppose_opinion: str) -> DebateSpeech:
"""
affirmative_speaker will start speech
"""
affirmative_speaker = self.get_affirmative_speaker()
logging.info(affirmative_speaker.name() + ": " + "start")
speech = await affirmative_speaker.speech(topic, opinion, oppose_opinion, round, self.speeches)
logging.info(affirmative_speaker.name() + ": result: " + speech.content)
return speech
async def negative_speech(self, round: int, topic: str, opinion: str, oppose_opinion: str) -> DebateSpeech:
"""
after affirmative_speaker finished speech, negative_speaker will start speech
"""
negative_speaker = self.get_negative_speaker()
logging.info(negative_speaker.name() + ": " + "start")
speech = await negative_speaker.speech(topic, opinion, oppose_opinion, round, self.speeches)
logging.info(negative_speaker.name() + ": result: " + speech.content)
return speech
def get_affirmative_speaker(self) -> DebateAgent:
"""
return the affirmative speaker
"""
return self.affirmative_speaker
def get_negative_speaker(self) -> DebateAgent:
"""
return the negative speaker
"""
return self.negative_speaker
def store_speech(self, speech: DebateSpeech):
self.moderator.memory.add(MemoryItem.from_dict({
"content": speech.content,
"metadata": {
"round": speech.round,
"speaker": speech.name,
"type": speech.type
}
}))
self.speeches.append(speech)
def gen_closing_statement(self):
pass
@@ -0,0 +1,139 @@
import logging
from abc import ABC
from datetime import datetime
from typing import Dict, Any, Union, List
from pydantic import Field
from aworld.core.common import Observation, ActionModel
from aworld.output import MessageOutput, WorkSpace, ArtifactType, SearchOutput
from examples.multi_agents.collaborative.debate.agent.base import DebateSpeech
from examples.multi_agents.collaborative.debate.agent.prompts import user_assignment_system_prompt, summary_system_prompt, summary_debate_prompt
from examples.multi_agents.collaborative.debate.agent.stream_output_agent import StreamOutputAgent
def truncate_content(raw_content, char_limit):
if raw_content is None:
raw_content = ''
if len(raw_content) > char_limit:
raw_content = raw_content[:char_limit] + "... [truncated]"
return raw_content
class ModeratorAgent(StreamOutputAgent, ABC):
stance: str = "moderator"
topic: str = Field(default=None)
affirmative_opinion: str = Field(default=None)
negative_opinion: str = Field(default=None)
async def async_policy(self, observation: Observation, info: Dict[str, Any] = {}, **kwargs) -> Union[
List[ActionModel], None]:
## step 1: params
topic = observation.content
## step2: gen opinions
output = await self.gen_opinions(topic)
## step3: gen speech
moderator_speech = DebateSpeech.from_dict({
"content": "",
"round": 0,
"type": "speech",
"stance": "moderator",
"name": self.name(),
})
async def after_speech_call(message_output_response):
logging.info("moderator: after_speech_call")
opinions = message_output_response
self.affirmative_opinion = opinions.get("positive_opinion")
self.negative_opinion = opinions.get("negative_opinion")
moderator_speech.metadata = {
"topic": topic,
"affirmative_opinion": self.affirmative_opinion,
"negative_opinion": self.negative_opinion,
}
moderator_speech.finished = True
await moderator_speech.convert_to_parts(output, after_speech_call)
action = ActionModel(
policy_info=moderator_speech
)
return [action]
async def gen_opinions(self, topic) -> MessageOutput:
current_time = datetime.now().strftime("%Y-%m-%d-%H")
human_prompt = self.agent_prompt.format(topic=topic,
current_time=current_time,
)
messages = [
{"role": "system", "content": user_assignment_system_prompt},
{"role": "user", "content": human_prompt}
]
output = await self.async_call_llm(messages, json_parse=True)
return output
async def summary_speech(self) -> DebateSpeech:
chat_history = await self.get_formated_history()
print(f"chat_history is \n {chat_history}")
search_results_content_history = await self.get_formated_search_results_content_history()
print(f"search_results_content_history is \n {search_results_content_history}")
human_prompt = summary_debate_prompt.format(topic=self.topic,
opinion=self.affirmative_opinion,
oppose_opinion=self.negative_opinion,
chat_history=chat_history,
search_results_content_history=search_results_content_history
)
messages = [
{"role": "system", "content": summary_system_prompt},
{"role": "user", "content": human_prompt}
]
output = await self.async_call_llm(messages, json_parse=False)
moderator_speech = DebateSpeech.from_dict({
"content": "",
"round": 0,
"type": "summary",
"stance": "moderator",
"name": self.name(),
})
async def after_speech_call(message_output_response):
moderator_speech.finished = True
await moderator_speech.convert_to_parts(output, after_speech_call)
return moderator_speech
async def get_formated_history(self):
formated = []
for item in self.memory.get_all():
formated.append(f"{item.metadata['speaker']} (round {item.metadata['round']}): {item.content}")
return "\n".join(formated)
async def get_formated_search_results_content_history(self):
if not self.workspace:
return
search_results = self.workspace.list_artifacts(ArtifactType.WEB_PAGES)
materials = []
for search_result in search_results:
if isinstance(search_result.content, SearchOutput):
for item in search_result.content.results:
materials.append(
f"{search_result.metadata['user']} (round {search_result.metadata['round']}): {search_result.content.query}: url: {item.url}, title: {item.title}, description: {item.content}")
return "\n".join(materials)
def set_workspace(self, workspace: WorkSpace):
self.workspace = workspace
@@ -0,0 +1,251 @@
user_assignment_system_prompt = "You are a helpful search agent."
user_assignment_prompt = """
While facing the hot topic: {topic}, your opinion is {opinion}. You stand on your opinion and fight any other opinion (such as {oppose_opinion}) that differs from your opinion.
You have an assistant that can help search the relative materials online to support your opinion {opinion} in the topic: {topic}
While facing your opponent's claim {last_oppose_speech_content}, you decide to fight back! Now you need to ask your assistant to do some online survey, according to that claim, to give you more insights to decide what to debate with your opponent.
For example, you could talk to your assistant to search: A, B, C . Then you will gain more insights and can decide how to fight back!
Attention: You need to pay attention the current time ({current_time}).
If you want to search something that is sensitive to the time, espcially some materials are needed to be up to date, you need to output your assignment queries associated with the current time, so your assistant can search the up to date search.
Format Requirements (query seperated by , ), limit {limit}:
aaa, bbb, ccc...
Now, you could output your assignment queries (strictly follow the Format Requirements: seperated by ,) to your assistant.
"""
user_debate_system_prompt = "You are an impressive debater."
user_debate_prompt = """
## Role
You are an outstanding debater, with a fiery and stubborn personality, sharp language, and a penchant for irony.
Your responsibility is to respond to the content of the opposing debater's speech based on the current debate topic, your stance, your skills, and restrictions.
## Current Debate Topic
{topic}
## Your Stance
{opinion}
## Opponent's Stance
{oppose_opinion}
## Your Skills
- You have a strong sense of self-esteem, a bit rebellious, enjoy arguing back, and speak quite aggressively.
- Sometimes you like to use habitual rhetorical questions to belittle the opponent.
- When you sense the opponent is about to leave or the conversation is hard to continue due to lack of topics, you can throw out provocative questions related to the current debate topic to challenge the opponent.
- You excel at finding loopholes in the opponent's arguments and incessantly attacking their weaknesses.
- You are adept at using reference materials to support your arguments with facts and counter the opponent's loopholes.
- Your language is concise and incisive, always able to capture the key points. When responding, you can settle the matter in just three or four sentences without the need for lengthy explanations.
## Attention
- It is strictly prohibited to use phrases like “lets switch to a lighter topic.”
- The output content must be organized according to the given format and must not deviate from the framework requirements.
- If you recoginize that there is no history chat and your opponent has not said anything yet, it means you are on the first round, you need to directly and briefly state your opinion to your opponent.
- You are not allowed to output your inner thinking chain, just directly speak out to your opponent.
- Please do not say too much, it will make you seem too mild, which directly undermines your character.
- Be brief and sharp, and use sarcasm frequently. Limit your response within 100-150 words.
- DO NOT output your response starting with 'You:', JUST DIRECTLY output your response without "You:".
## Dialogue Style Examples
{few_shots}
## Current Task
You are arguing with your opponent on the topic: {topic}. You opinion is {opinion}.
Below is the content of your conversation:
{chat_history}
Now, your opponent just said to you: “{last_oppose_speech_content}”.
At this moment, you have obtained the following related materials, which may be beneficial for your rebuttal against the opponent.
Reference materials: {search_results_content}
Please use your character's identity traits, skills, restrictions, and dialogue style examples, relying on your reference materials, to counter the opposing debater in a incisive and concise way. Never output "You:". Now, please directly output your response to your opponent without "You:".
"""
affirmative_few_shots = """
Your opponent:
You: Let me make it straight, Jordan is the best, who dares to oppose?
Your opponent: James has a better character. Even when JR Smith made a blunder in the finals, LeBron was still supportive. If it were Jordan, he would have chased him around the court in anger.
You: Cut it out! Michael Jordan was a ruthless winner on the court. He wouldn't tolerate any mistakes, and it's this relentless standard that earned him six championships and never failing in the finals. So, no matter how much of a nice guy LeBron tries to be, it won't erase his multiple finals defeats.
Your opponent: James has a stronger body, and physique is the most important factor on basketball court.
You: My Jesus, I can't believe someone would say that. Bastetball is far beyond physique. Skills, mind and leadership all matters. In these aspects, James is no match for Jordan. If James is so proud of his physique, why doesn't he go work in the fields?
"""
negative_few_shots = """
Your opponent:
You: Let me make it straight, Lebron is the best, who dares to oppose?
Your opponent: With no doubt, Jordan's skills are more well-rounded.
You: Would you stop kidding...Since Jordan's skills are supposedly so well-rounded, then tell me why his three-point shooting percentage is so low. Jordan was just given a greater personal boost because of the unique era he played in.
"""
generate_opinions_prompt = """
Here is the debate topic:{topic}.
Please output the two sides' (positive side vs negative side) opinions of the topic.
Output format:
{{
"positive_opinion":"xxx"
"negative_opinion":"yyy"
}}
Now the topic is {topic}, please follow the example and output format, output the two sides' opinions.
you must always return json, don not return markdown tag or others such as ```json,``` etc;
For example:
----------------------------------
topic: Who is better? A or B?
{{
"positive_opinion":"A"
"negative_opinion":"B"
}}
----------------------------------
topic: Is is OK to drink wine?
positive_opinion:Yes
negative_opinion:No
{{
"positive_opinion":"Yes"
"negative_opinion":"No"
}}
"""
summary_system_prompt = "You are a good assistant to make summary."
summary_debate_prompt = """
## Your Role
You are a reliable assistant to make summary and skilled in information architecture and visual storytelling, capable of transforming any content into stunning cards using a webpage format.
Your responsibility is: 1. read people's conversation and make summary on this conversation; 2. translate your summary into the HTML code.
## Current Situation
1. You find that several people have started a debate on a particular topic "{topic}";
2. One side is holding the opinion: {opinion}, the other side is holding: {oppose_opinion}.
2.1 For the details of the conversation between these two sides, please refer to Conversation History below.
3. Each time one side is giving the conversation, he/she would like to cite the supportive materials searched on the website, in form of the urls, title, descritpion.
3.1 For the details of the supportive materials between these two sides for each conversation round, please refer to Supportive Materials History below.
4. Now you are supposed to make a concise, brief summary for each round conversation between the two sides, in terms of the viewpoint, citation.
4.1 'viewpoint' is the main point for each side in each conversation round;
4.2 'citation' is the formatted structure in terms of the urls, title of the supportive materials that is indeed cited in each conversation round.
## Conversation History:
{chat_history}
## Supportive Materials History
{search_results_content_history}
## Summary Format
debater_name1's summary (round 1): xxxx
debater_name1's citation(round 1): url_1: xxxx, title_1: xxxx; url_2: xxxx, title_2: xxxx
debater_name2's summary (round 1): yyyy
debater_name2's citation(round 1): url_1: yyyy, title_1: yyyy; url_2: yyyy, title_2: yyyy...
debater_name1's summary (round 2): pppp
debater_name1's citation(round 2): url_1: pppp, title_1: pppp; url_2: pppp, title_2: pppp
debater_name2's summary (round 2): qqqq
debater_name2's citation(round 2): url_1: qqqq, title_1: qqqq; url_2: qqqq, title_2: qqqq...
...
## Write HTML Requirements
1. You should only present using HTML code, including basic HTML, CSS, and JavaScript. This should encompass text, visualization, and structured results.
2. Provide complete HTML code; CSS and JavaScript should also be included within the code to ensure the user can open a single file.
3. Do not arbitrarily omit the core viewpoints from the original text; core viewpoints and summaries must be preserved.
## Write HTML Technical Implementation
1. Utilize modern CSS techniques (such as flex/grid layouts, variables, gradients)
2. Ensure the code is clean and efficient, without redundant elements
3. Add a save button that does not interfere with the design
4. Implement a one-click save as image feature using html2canvas
5. The saved image should only contain the cover design, excluding interface elements
6. Use Google Fonts or other CDNs to load appropriate modern fonts
7. Online icon resources can be used (such as Font Awesome)
## Write HTML Professional Typography Techniques
1. Apply the designer's common "negative space" technique to create focal points
2. Maintain harmonious proportion between text and decorative elements
3. Ensure a clear visual flow to guide the readers eye movement
4. Use subtle shadow or light effects to increase depth
5. For webpage URL addresses and their corresponding titles, use hyperlinks.
## Example
Topic: Which is more important to success? Working hard or Opportunity?
Conversation History:
Tom (round 1): Hard work can help individuals continuously improve their skills and professional knowledge, laying a solid foundation for achieving success. A programmer Kim spends time every day learning new languages and technologies, eventually becoming an expert in the field and securing an important position at a major tech company.
Jerry (round 1): Opportunity is more important. Steve Jobs saw the great potential of personal computer, then he found Apple.
Tom (round 2): Many famous entrepreneurs experienced numerous setbacks and failures before achieving success. They did not just happen to be lucky enough to get opportunities, but rather, through continuous effort and relentless perseverance, they eventually succeeded. For example, Thomas Edison conducted thousands of experiments in the process of inventing the light bulb, which demonstrates that his success was inseparable from his tenacious effort.
Jerry (round 2): Many historical events were driven by opportunities rather than purely by abilities. For example, during wars, some generals won battles because they made crucial decisions at key moments, even though they were not the most experienced commanders.
Supportive Materials History:
Tom (round 1): id1: url: aaaa, title: Why is Kim? description: bbbb.
Jerry (round 1): id1: url: cccc, title: Steve's story. description: dddd.
Tom (round 2): id1: url: eeee, title: The invention of light bulb. description: ffff.
Jerry (round 2): id1: url: gggg, title: Some interesting things during the war. description: hhhh.
Your Summary:
Tom's summary (round 1): Hard working improves people's skills and thus leads to personal sucess.
Tom's citation (round 1): url_1: aaaa, title_1: Why is Kim?
Jerry's summary (round 1): Steve's story supports opportunity is more important.
Jerry's citation (round 1): url_1:cccc, title_1:Steve's story.
Tom's summary (round 2): Entrepreneurs, like Thomas Edison, faced repeated failures before succeeding. Their achievements resulted from persistent effort and perseverance, not mere luck.
Tom's citation (round 2): url_1: eeee, title_1: The invention of light bulb.
Jerry's summary (round 2): Historical events are often driven by opportunity, as seen when generals win battles through timely decisions rather than experience.
Jerry's citation (round 2): url_1:gggg, title_1: Some interesting things during the war.
## Attention
- Strictly follow the ## Output Format. The output content must be organized according to the ## Output Format and must not deviate from the framework requirements.
- The summary of each side's each conversation round should be very concise (it would be better within 30 words), cannot be too long. Just capture the key points.
- Only the supportive materials that has been indeed referred by the debater can appear in the citation, in terms of their urls and the titles. Ignore the materials that have not been referred by the debater.
- You are not allowed to output your inner thinking chain.
- DO NOT output your response starting with 'You:', JUST DIRECTLY output your response without "You:".
- Please output your summary in the HTML form directly in one step.
Please deeply understand Your Role and Current Situation. Strictly follow the Summary Format, Attention with Example, output your summary, according to the Conversation History and Supportive Materials History.
Then transfer your summary according to the Write HTML Requirements, Write HTML Technical Implementation, Write HTML Professional Typography Techniques.
"""
# ## Conversation History:
# Jake (round1): Jordan is the best, he scores 35.1 points per game. No one is better than that.
# Lucy (round1): Jordan's opponent is too weak, Lebron's opponent is stronger, so James is better.
# Jake (round2): Jordan is the best, he has 6 champions. No one is better than that.
# Lucy (round2): Jordan's teamates are better, pippen, rodman... Lebron leads the whole team forward.
# ## Supportive Materials History
# Jake (round1): url_1: 123, title_1: Jordan's data;
# Lucy (round1): url_1: 456, title_1: The diff between basketball's eras; url_2: 9999, title_2: which second-order wave forces on hydrodynamcis.
# Jake (round2): url_1: 123678aa, title_1: Who's got most champions?
# Lucy (round2): url_1: xxxbbbw45, title_1: The importance of teammates.
@@ -0,0 +1,48 @@
import asyncio
from enum import Enum
from typing import List
class SearchAPI(Enum):
PERPLEXITY = "perplexity"
TAVILY = "tavily"
EXA = "exa"
ARXIV = "arxiv"
PUBMED = "pubmed"
LINKUP = "linkup"
class SearchException(Exception):
def __init__(self, message, error_code=None):
super().__init__()
self.message = message
self.error_code = error_code
def __str__(self):
return f'{self.message}'
class SearchEngine:
def batch_search(self, queries: List[str], max_results=5, include_raw_content=False, **kwargs):
try:
return asyncio.run(
self.async_batch_search(queries, max_results=max_results, include_raw_content=include_raw_content, **kwargs))
except Exception as err:
raise SearchException(f"search queries = {queries} failed.")
async def async_batch_search(self, queries: List[str], max_results=5, include_raw_content=False, **kwargs):
search_tasks = []
for query in queries:
search_tasks.append(
self.async_search(query, max_results, include_raw_content, **kwargs)
)
# Execute all searches concurrently
search_docs = await asyncio.gather(*search_tasks)
return search_docs
async def async_search(self, query: str, max_results=5, include_raw_content=False, **kwargs) -> dict:
pass
@@ -0,0 +1,46 @@
from examples.multi_agents.collaborative.debate.agent.search.search_engine import SearchEngine
class TavilySearchEngine(SearchEngine):
"""Tavily"""
async def async_search(self, query: str, max_results=5, include_raw_content=False, **kwargs) -> dict:
"""
Performs concurrent web searches using the Tavily API.
Args:
query (Str): str
Returns:
dict: search responses from Tavily API, one per query. Each response has format:
{
'query': str, # The original search query
'follow_up_questions': None,
'answer': None,
'images': list,
'results': [ # List of search results
{
'title': str, # Title of the webpage
'url': str, # URL of the result
'content': str, # Summary/snippet of content
'score': float, # Relevance score
'raw_content': str|None # Full page content if available
},
...
]
}
"""
try:
from tavily import AsyncTavilyClient
except ImportError:
# install mistune
import subprocess
subprocess.run(["pip", "install", "tavily-python>=0.5.1"], check=True)
from tavily import AsyncTavilyClient
tavily_async_client = AsyncTavilyClient()
return await tavily_async_client.search(
query,
max_results=5,
include_raw_content=True,
topic="general"
)
@@ -0,0 +1,18 @@
from aworld.config import AgentConfig
from aworld.agents.llm_agent import Agent
from aworld.models.llm import acall_llm_model_stream
from aworld.output import MessageOutput
class StreamOutputAgent(Agent):
def __init__(self, conf: AgentConfig, name: str, **kwargs):
super().__init__(conf, name)
async def async_call_llm(self, messages, json_parse=False) -> MessageOutput:
# Async streaming with acall_llm_model
async def async_generator():
async for chunk in acall_llm_model_stream(self.llm, messages, stream=True):
if chunk.content:
yield chunk.content
return MessageOutput(source=async_generator(), json_parse=json_parse)
@@ -0,0 +1,59 @@
import asyncio
import os
import uuid
from dotenv import load_dotenv
from aworld import trace
from examples.multi_agents.collaborative.debate.agent.debate_agent import DebateAgent
from examples.multi_agents.collaborative.debate.agent.main import DebateArena
from examples.multi_agents.collaborative.debate.agent.moderator_agent import ModeratorAgent
from examples.multi_agents.collaborative.debate.agent.prompts import generate_opinions_prompt
from aworld.config import AgentConfig
from aworld.output import WorkSpace
# os.environ["LLM_PROVIDER"] = "openai"
# os.environ["LLM_MODEL_NAME"] = "YOUR_LLM_MODEL_NAME"
# os.environ["LLM_BASE_URL"] = "YOUR_LLM_BASE_URL"
# os.environ["LLM_API_KEY"] = "YOUR_LLM_API_KEY"
if __name__ == '__main__':
load_dotenv()
trace.configure()
base_config = {
"llm_provider": os.getenv("LLM_PROVIDER"),
"llm_model_name": os.environ['LLM_MODEL_NAME'],
"llm_base_url": os.environ['LLM_BASE_URL'],
"llm_api_key": os.environ['LLM_API_KEY'],
"llm_temperature": os.getenv("LLM_TEMPERATURE", 0.0)
}
agentConfig = AgentConfig.model_validate(base_config)
agent1 = DebateAgent(name="affirmativeSpeaker", stance="affirmative", conf=AgentConfig.model_validate(base_config))
agent2 = DebateAgent(name="negativeSpeaker", stance="negative", conf=AgentConfig.model_validate(base_config))
moderator_agent = ModeratorAgent(
conf=AgentConfig.model_validate(base_config | {
"name": "moderator_agent",
"agent_prompt": generate_opinions_prompt
}),
name="moderator_agent"
)
debate_arena = DebateArena(affirmative_speaker=agent1, negative_speaker=agent2, moderator=moderator_agent,
workspace=WorkSpace.from_local_storages(str(uuid.uuid4())))
async def start_debate(debate_arena, topic, rounds):
speeches = debate_arena.async_run(topic=topic, rounds=rounds)
async for speech in speeches:
if speech.parts:
async for part in speech.parts:
print(part.content, flush=True, end="")
print(f"{speech.name}: {speech.content}")
asyncio.run(start_debate(debate_arena, topic="张居正", rounds=3))
@@ -0,0 +1,6 @@
Multi-agent interaction paradigm in travel examples.
![img.png](img.png)
Suggest using gpt-4o or deepseek-chat models, The latter does not support multimodality, so set use_vision to false.
@@ -0,0 +1,2 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

@@ -0,0 +1,74 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
plan_sys_prompt = """
You are an AI agent designed to automate tasks. Your goal is to accomplish the ultimate task following the rules.
# Input Format
Task
Previous steps
# Response Rules
1. RESPONSE FORMAT: You must ALWAYS respond with valid JSON or text.
2. ACTIONS: You can specify one actions in the list to be executed in sequence.
3. REQUIREMENTS:
- If you want to extract some information, you can use example_search_agent gets related info and url, then you can use browser_agent extract info from specific url.
- If you want to search, you need use example_search_agent and give the specific task.
- If you want to extract, you need use broswer_agent and give the task contains specific url. you can give two url once for browser agent, and tell browser agent only need extract from one url. if one url is invalid, use another url for replace.
- If you want to write, you need use example_write_agent and give the task and refer, the task needs be very detailed and contains all requirements.
4. Pipeline:
- If you have many information to search. you should choose search tool - extract loop many times.
5. TASK COMPLETION:
- Use the done action as the last action as soon as the ultimate task is complete
- Dont use "done" before you are done with everything the user asked you, except you reach the last step of max_steps.
- If you reach your last step, use the done action even if the task is not fully finished. Provide all the information you have gathered so far. If the ultimate task is completly finished set success to true. If not everything the user asked for is completed set success in done to false!
- If you have to do something repeatedly for example the task says for "each", or "for all", or "x times", count always inside "memory" how many times you have done it and how many remain. Don't stop until you have completed like the task asked you. Only call done after the last step.
- Don't hallucinate actions
- Make sure you include everything you found out for the ultimate task in the done text parameter. Do not just say you are done, but include the requested information of the task.
"""
# 6. Output Format:
# - You need first evaluate previous goal, and then save important things into memory, then give the next goal and use tool call to execute task.
# 'current_state': {
# 'evaluation_previous_goal': 'Success - I completed search and gets the url',
# 'memory': 'search compeleted and gets url',
# 'next_goal': 'extract information from the related url',
# },
#
# 7. You need execute task step by step, so that you can only give one simple action per time. do not search much more info one times. (you can search, extract, search, extract, ..., write)
# """
plan_prompt = """Your ultimate task is: {task}. If you achieved your ultimate task, stop everything and use the done action in the next step to complete the task. If not, continue as usual.
You should break down the retrieval task into small atomic granularities, search small and extract, and then search next. you should only take one action / function call once per time.
"""
search_sys_prompt = "You are a helpful search agent. please only use one action complete this task (only search once), at least results 6 pages."
search_prompt = """
Please act as a search agent, constructing appropriate keywords and search terms, using search toolkit to collect relevant information, including urls, webpage snapshots, etc.
Here are the question: {task}
"""
search_output_prompt = """
1. RESPONSE FORMAT: You must ALWAYS respond with valid JSON in this exact format:
{"action":[{{"one_action_name": {{// action-specific parameter}}}}, // ... more actions in sequence]}
"""
write_sys_prompt = "You are a helpful write agent."
write_prompt = """
Please act as a write agent, constructing appropriate keywords and search terms, using search toolkit to collect relevant information, including urls, webpage snapshots, etc.
Here are the write task: {task}
please only use one action complete this task.
"""
# Here is the reference information: {reference}
write_output_prompt = """
1. RESPONSE FORMAT: You must ALWAYS respond with valid JSON in this exact format:
{"action":[{{"one_action_name": {{// action-specific parameter}}}}, // ... more actions in sequence]}
"""
@@ -0,0 +1,95 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import os
from aworld.config.conf import AgentConfig, ToolConfig
from aworld.agents.llm_agent import Agent
from aworld.config import ModelConfig
from aworld.core.agent.swarm import TeamSwarm, Swarm, GraphBuildType
from aworld.core.task import Task
from aworld.runner import Runners
from examples.browser_use.agent import BrowserAgent
from examples.browser_use.config import BrowserAgentConfig
from examples.common.tools.common import Tools
from examples.common.tools.conf import BrowserToolConfig
from examples.common.tools.tool_action import SearchAction
from examples.multi_agents.collaborative.travel.prompts import *
# os.environ["LLM_PROVIDER"] = "openai"
# os.environ["LLM_MODEL_NAME"] = "YOUR_LLM_MODEL_NAME"
# os.environ["LLM_BASE_URL"] = "YOUR_LLM_BASE_URL"
# os.environ["LLM_API_KEY"] = "YOUR_LLM_API_KEY"
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"),
llm_temperature=os.getenv("LLM_TEMPERATURE", 0.0)
)
agent_config = AgentConfig(
llm_config=model_config,
use_vision=False
)
plan = Agent(
conf=agent_config,
name="example_plan_agent",
system_prompt=plan_sys_prompt,
agent_prompt=plan_prompt,
agent_names=['browser_agent'],
step_reset=False
)
search = Agent(
conf=agent_config,
name="example_search_agent",
desc="search ",
system_prompt=search_sys_prompt,
agent_prompt=search_prompt,
tool_names=[Tools.SEARCH_API.value],
black_tool_actions={Tools.SEARCH_API.value: [SearchAction.DUCK_GO.value.name, SearchAction.WIKI.value.name,
SearchAction.GOOGLE.value.name]}
)
write = Agent(
conf=agent_config,
name="example_write_agent",
system_prompt=write_sys_prompt,
agent_prompt=write_prompt,
tool_names=[Tools.HTML.value],
)
browser_agent = BrowserAgent(
name='browser_agent',
desc="browser_agent can execute extract web info task and open local file task, if you want to use browser agent to open local file, you should give the specific absolutely file path in params.",
conf=BrowserAgentConfig(
llm_config=model_config,
use_vision=False
),
custom_executor=True,
tool_names=[Tools.BROWSER.value]
)
def main():
goal = """
I need a 7-day Japan itinerary from April 2 to April 8 2025, departing from Hangzhou, We want to see beautiful cherry blossoms and experience traditional Japanese culture (kendo, tea ceremonies, Zen meditation). We would like to taste matcha in Uji and enjoy the hot springs in Kobe. I am planning to propose during this trip, so I need a special location recommendation. Please provide a detailed itinerary and create a simple HTML travel handbook that includes a 7-day Japan itinerary, an updated cherry blossom table, attraction descriptions, essential Japanese phrases, and travel tips for us to reference throughout our journey.
you need search and extract different info 1 times, and then write, at last use browser agent goto the html url and then, complete the task.
"""
swarm = Swarm((plan, search), (plan, browser_agent), (plan, write), build_type=GraphBuildType.HANDOFF)
# swarm = TeamSwarm(plan, search, browser_agent, write)
task = Task(
swarm=swarm,
input=goal,
tools_conf={
Tools.BROWSER.value: BrowserToolConfig(width=800, height=720, use_async=True, llm_config=model_config),
Tools.HTML.value: ToolConfig(name="html", llm_config=model_config)
},
endless_threshold=5
)
Runners.sync_run_task(task)
if __name__ == '__main__':
main()
@@ -0,0 +1,2 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
@@ -0,0 +1,251 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import copy
import json
import traceback
from typing import Dict, Any, List, Union
from aworld.core.event.base import Message
from aworld.utils.common import sync_exec
from examples.multi_agents.coordination.custom_agent.prompts import execute_system_prompt, plan_system_prompt, plan_done_prompt, \
plan_postfix_prompt, init_prompt
from examples.common.tools.common import Agents
from aworld.core.agent.base import AgentResult
from aworld.agents.llm_agent import Agent
from aworld.models.llm import call_llm_model
from aworld.config.conf import AgentConfig, ConfigDict
from aworld.core.common import Observation, ActionModel
from aworld.logs.util import logger
from examples.multi_agents.coordination.custom_agent.utils import extract_pattern
class ExecuteAgent(Agent):
def __init__(self, conf: Union[Dict[str, Any], ConfigDict, AgentConfig], name: str, **kwargs):
super(ExecuteAgent, self).__init__(conf=conf, name=name, **kwargs)
def id(self) -> str:
return Agents.EXECUTE.value
def reset(self, options: Dict[str, Any] = None):
"""Execute agent reset need query task as input."""
super().reset(options)
self.system_prompt = execute_system_prompt.format(task=self.task)
self.step_reset = False
async def async_policy(self, observation: Observation, info: Dict[str, Any] = {}, message: Message = None,
**kwargs) -> Union[List[ActionModel], None]:
await self.async_desc_transform(message.context)
return self._common(observation, info)
def policy(self,
observation: Observation,
info: Dict[str, Any] = None,
message: Message = None,
**kwargs) -> List[ActionModel] | None:
self.desc_transform(message.context)
return self._common(observation, info)
def _common(self, observation, info):
self._finished = False
content = observation.content
llm_result = None
## build input of llm
input_content = [
{'role': 'system', 'content': self.system_prompt},
]
for traj in self.trajectory:
# Handle multiple messages in content
if isinstance(traj[0].content, list):
input_content.extend(traj[0].content)
else:
input_content.append(traj[0].content)
if traj[-1].tool_calls is not None:
input_content.append(
{'role': 'assistant', 'content': '', 'tool_calls': traj[-1].tool_calls})
else:
input_content.append({'role': 'assistant', 'content': traj[-1].content})
if content is None:
content = observation.action_result[0].error
if not self.trajectory:
new_messages = [{"role": "user", "content": content}]
input_content.extend(new_messages)
else:
# Collect existing tool_call_ids from input_content
existing_tool_call_ids = {
msg.get("tool_call_id") for msg in input_content
if msg.get("role") == "tool" and msg.get("tool_call_id")
}
new_messages = []
for traj in self.trajectory:
if traj[-1].tool_calls is not None:
# Handle multiple tool calls
for tool_call in traj[-1].tool_calls:
# Only add if this tool_call_id doesn't exist in input_content
if tool_call.id not in existing_tool_call_ids:
new_messages.append({
"role": "tool",
"content": content,
"tool_call_id": tool_call.id
})
if new_messages:
input_content.extend(new_messages)
else:
input_content.append({"role": "user", "content": content})
# Validate tool_calls and tool messages pairing
assistant_tool_calls = []
tool_responses = []
for msg in input_content:
if msg.get("role") == "assistant" and msg.get("tool_calls"):
assistant_tool_calls.extend(msg["tool_calls"])
elif msg.get("role") == "tool":
tool_responses.append(msg.get("tool_call_id"))
# Check if all tool_calls have corresponding responses
tool_call_ids = {call.id for call in assistant_tool_calls}
tool_response_ids = set(tool_responses)
if tool_call_ids != tool_response_ids:
missing_calls = tool_call_ids - tool_response_ids
extra_responses = tool_response_ids - tool_call_ids
error_msg = f"Tool calls and responses mismatch. Missing responses for tool_calls: {missing_calls}, Extra responses: {extra_responses}"
logger.error(error_msg)
raise ValueError(error_msg)
tool_calls = []
try:
llm_result = call_llm_model(self.llm, input_content, model=self.model_name,
tools=self.tools, temperature=0)
logger.info(f"Execute response: {llm_result.message}")
res = sync_exec(self.model_output_parser.parse, llm_result, agent_id=self.id())
content = res.actions[0].policy_info
tool_calls = llm_result.tool_calls
except Exception as e:
logger.warning(traceback.format_exc())
finally:
if llm_result:
ob = copy.deepcopy(observation)
ob.content = new_messages
self.trajectory.append((ob, info, llm_result))
else:
logger.warning("no result to record!")
res = []
if tool_calls:
for tool_call in tool_calls:
tool_action_name: str = tool_call.function.name
if not tool_action_name:
continue
names = tool_action_name.split("__")
tool_name = names[0]
action_name = '__'.join(names[1:]) if len(names) > 1 else ''
params = json.loads(tool_call.function.arguments)
res.append(ActionModel(agent_name=Agents.EXECUTE.value,
tool_name=tool_name,
action_name=action_name,
params=params))
if res:
res[0].policy_info = content
self._finished = False
elif content:
policy_info = extract_pattern(content, "final_answer")
if policy_info:
res.append(ActionModel(agent_name=Agents.EXECUTE.value,
policy_info=policy_info))
self._finished = True
else:
res.append(ActionModel(agent_name=Agents.EXECUTE.value,
policy_info=content))
logger.info(f">>> execute result: {res}")
result = AgentResult(actions=res,
current_state=None)
return result.actions
class PlanAgent(Agent):
def __init__(self, conf: Union[Dict[str, Any], ConfigDict, AgentConfig], name: str, **kwargs):
super(PlanAgent, self).__init__(name=name, conf=conf, **kwargs)
def id(self) -> str:
return Agents.PLAN.value
def reset(self, options: Dict[str, Any] = None):
"""Execute agent reset need query task as input."""
super().reset(options)
self.system_prompt = plan_system_prompt.format(task=self.task)
self.done_prompt = plan_done_prompt.format(task=self.task)
self.postfix_prompt = plan_postfix_prompt.format(task=self.task)
self.first_prompt = init_prompt
self.first = True
self.step_reset = False
async def async_policy(self, observation: Observation, info: Dict[str, Any] = {}, message: Message = None,
**kwargs) -> Union[List[ActionModel], None]:
await self.async_desc_transform(message.context)
return self._common(observation, info)
def policy(self,
observation: Observation,
info: Dict[str, Any] = None,
message: Message = None,
**kwargs) -> List[ActionModel] | None:
self._finished = False
self.desc_transform(message.context)
return self._common(observation, info)
def _common(self, observation, info):
llm_result = None
input_content = [
{'role': 'system', 'content': self.system_prompt},
]
# build input of llm based history
for traj in self.trajectory:
input_content.append({'role': 'user', 'content': traj[0].content})
# plan agent no tool to call, use content
input_content.append({'role': 'assistant', 'content': traj[-1].content})
message = observation.content
if self.first_prompt:
message = self.first_prompt
self.first_prompt = None
input_content.append({"role": "user", "content": message})
try:
llm_result = call_llm_model(self.llm, messages=input_content, model=self.model_name)
logger.info(f"Plan response: {llm_result.message}")
except Exception as e:
logger.warning(traceback.format_exc())
raise e
finally:
if llm_result:
ob = copy.deepcopy(observation)
ob.content = message
self.trajectory.append((ob, info, llm_result))
else:
logger.warning("no result to record!")
res = sync_exec(self.model_output_parser.parse, llm_result, agent_id=self.id())
content = res.actions[0].policy_info
if "TASK_DONE" not in content:
content += self.done_prompt
else:
# The task is done, and the assistant agent need to give the final answer about the original task
content += self.postfix_prompt
if not self.first:
self._finished = True
self.first = False
logger.info(f">>> plan result: {content}")
result = AgentResult(actions=[ActionModel(agent_name=Agents.PLAN.value,
tool_name=Agents.EXECUTE.value,
policy_info=content)],
current_state=None)
return result.actions
@@ -0,0 +1,27 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import os
from aworld.utils.import_package import import_packages
import_packages(["pandas", "numpy"])
import pandas as pd
import numpy as np
from aworld.utils import import_package
def mock_dataset(name: str):
if name == 'gaia':
npy_path = f"{os.getcwd()}/gaia.npy"
numpy_array = np.load(npy_path, allow_pickle=True)
df = pd.DataFrame(numpy_array[:-1])
query = numpy_array[-1][0]
save_file_path = f"{os.getcwd()}/gaia.xlsx"
import_package("openpyxl")
df.to_excel(save_file_path, index=False, header=None)
return query.format(file_path=save_file_path)
return None
@@ -0,0 +1,88 @@
init_prompt = f"""
Please give me clear step-by-step instructions to complete the entire task. If the task needs any special knowledge, let me know which tools I should use to help me get it done.
"""
execute_system_prompt = """
===== RULES FOR THE ASSISTANT =====
You are my assistant, and I am your user. Always remember this! Do not flip roles. You are here to help me. Do not give me instructions.
Use the tools available to you to solve the tasks I give you.
Our goal is to work together to successfully solve complex tasks.
The Task:
Our overall task is: {task}. Never forget this.
Instructions:
I will give you instructions to help solve the task. These instructions will usually be smaller sub-tasks or questions.
You must use your tools, do your best to solve the problem, and clearly explain your solutions.
How You Should Answer:
Always begin your response with: Solution: [YOUR_SOLUTION]
[YOUR_SOLUTION] should be clear, detailed, and specific. Provide examples, lists, or detailed implementations if needed.
Additional Notes:
Our overall task may be complicated. Here are tips to help you:
<tips>
- If one method fails, try another. There is always a solution.
- If a search snippet is not helpful, but the link is from a reliable source, visit the link for more details.
- For specific values like numbers, prioritize credible sources.
- Start with Wikipedia when researching, then explore other websites if needed.
- Solve math problems using Python and libraries like sympy. Test your code for results and debug when necessary.
- Validate your answers by cross-checking them through different methods.
- If a tool or code fails, do not assume its result is correct. Investigate the problem, fix it, and try again.
- Search results rarely provide exact answers. Use simple search queries to find sources, then process them further (e.g., by extracting webpage data).
- For downloading files, either use a browser simulation tool or write code to download them.
</tips>
Remember:
Your goal is to support me in solving the task successfully.
Unless I say the task is complete, always strive for a detailed, accurate, and useful solution.
"""
plan_system_prompt = """
===== USER INSTRUCTIONS =====
Remember that you are the user, and I am the assistant. I will always follow your instructions. We are working together to successfully complete a task.
My role is to help you accomplish a difficult task. You will guide me step by step based on my expertise and your needs. Your instructions should be in the following format: Instruction: [YOUR INSTRUCTION], where "Instruction" is a sub-task or question.
You should give me one instruction at a time. I will respond with a solution for that instruction. You should instruct me rather than asking me questions.
Please note that the task may be complex. Do not attempt to solve it all at once. You should break the task down and guide me step by step.
Here are some tips to help you give better instructions:
<tips>
- I have access to various tools like search, web browsing, document management, and code execution. Think about how humans would approach solving the task step by step, and give me instructions accordingly. For example, you may first use Google search to gather initial information and a URL, then retrieve the content from that URL, or interact with a webpage to find the answer.
- Even if the task is complex, there is always a solution. If you cant find the answer using one method, try another approach or use different tools to find the solution.
- Always remind me to verify the final answer using multiple tools (e.g., screenshots, webpage analysis, etc.), or other methods.
- If Ive written code, remind me to run it and check the results.
- Search results generally dont give direct answers. Focus on finding sources through search, and use other tools to process the URL or interact with the webpage content.
- If the task involves a YouTube video, I will need to process the content of the video.
- For file downloads, use web browser tools or write code (e.g., download from a GitHub link).
- Feel free to write code to solve tasks like Excel-related tasks.
</tips>
Now, here is the overall task: <task>{task}</task>. Stay focused on the task!
Start giving me instructions step by step. Only provide the next instruction after Ive completed the current one. When the task is finished, respond with <TASK_DONE>.
Do not say <TASK_DONE> until Ive completed the task.
"""
plan_done_prompt = """\n
Below is some additional information about the overall task that can help you better understand the purpose of the current task:
<auxiliary_information>
{task}
</auxiliary_information>
If there are any available tools that can assist with the task, instead of saying "I will...", first call the tool and respond based on the results it provides. Please also specify which tool you used.
"""
plan_postfix_prompt = """\n
Now, please provide the final answer to the original task based on our conversation: <task>{task}</task>
Pay close attention to the required answer format. First, analyze the expected format based on the question, and then generate the final answer accordingly.
Your response should include the following:
- Analysis: Enclosed within <analysis> </analysis>, this section should provide a detailed breakdown of the reasoning process.
- Final Answer: Enclosed within <final_answer> </final_answer>, this section should contain the final answer in the required format.
Here are some important guidelines for formatting the final answer:
<hint>
- Your final answer must strictly follow the format specified in the question. The answer should be a single number, a short string, or a comma-separated list of numbers and/or strings:
- If the answer is a number, don't use commas as thousands separators, and don't include units (such as "$" or "%") unless explicitly required.
- If the answer is a string, don't include articles (e.g., "a", "the"), don't use abbreviations (e.g., city names), and write numbers in full words unless instructed otherwise.
- If the answer is a comma-separated list, apply the above rules based on whether each element is a number or a string.
</hint>
"""
@@ -0,0 +1,56 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import os
from aworld.config.conf import ModelConfig, AgentConfig
from aworld.core.agent.swarm import Swarm, GraphBuildType
from aworld.core.task import Task
from aworld.runner import Runners
from examples.multi_agents.coordination.custom_agent.agent import PlanAgent, ExecuteAgent
from examples.multi_agents.coordination.custom_agent.mock import mock_dataset
from examples.common.tools.common import Agents, Tools
# os.environ["LLM_PROVIDER"] = "openai"
# os.environ["LLM_MODEL_NAME"] = "YOUR_LLM_MODEL_NAME"
# os.environ["LLM_BASE_URL"] = "YOUR_LLM_BASE_URL"
# os.environ["LLM_API_KEY"] = "YOUR_LLM_API_KEY"
def main():
test_sample = mock_dataset("gaia")
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"),
llm_temperature=os.getenv("LLM_TEMPERATURE", 0.0)
)
agent1_config = AgentConfig(
llm_config=model_config
)
agent1 = PlanAgent(conf=agent1_config, name=Agents.PLAN.value, step_reset=False)
agent2_config = AgentConfig(
llm_config=model_config
)
agent2 = ExecuteAgent(conf=agent2_config, name=Agents.EXECUTE.value, step_reset=False,
tool_names=[Tools.DOCUMENT_ANALYSIS.value])
# Create swarm for multi-agents
# define (head_node1, tail_node1), (head_node1, tail_node1) edge in the topology graph
swarm = Swarm((agent1, agent2), build_type=GraphBuildType.HANDOFF)
# Define a task
task_id = 'task'
task = Task(id=task_id, input=test_sample, swarm=swarm, endless_threshold=5)
# Run task
result = Runners.sync_run_task(task=task)
print(f"Time cost: {result[task_id].time_cost}")
print(f"Task Answer: {result[task_id].answer}")
if __name__ == '__main__':
main()
@@ -0,0 +1,18 @@
import re
from typing import Any, Dict, List, Literal, Optional, Union, Tuple
import logging as logger
def extract_pattern(content: str, pattern: str) -> Optional[str]:
try:
_pattern = fr"<{pattern}>(.*?)</{pattern}>"
match = re.search(_pattern, content, re.DOTALL)
if match:
text = match.group(1)
return text.strip()
else:
return None
except Exception as e:
logger.warning(f"Error extracting answer: {e}, current content: {content}")
return None
@@ -0,0 +1,27 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
"""AWorld planner module for agent planning and reasoning capabilities.
This module provides planning capabilities inspired by langchain-experimental planners,
adapted for AWorld's Context and ModelResponse systems with StringPromptTemplate support.
"""
from .models import StepInfo, StepInfos, Plan
from .parse import parse_step_infos, parse_step_json, parse_plan
from .plan import PlannerOutputParser, PLANNING_TAG, PLANNING_END_TAG, FINAL_ANSWER_TAG, FINAL_ANSWER_END_TAG, DEFAULT_SYSTEM_PROMPT
from .plan_handler import PlanHandler
__all__ = [
'StepInfo',
'StepInfos',
'Plan',
'parse_step_infos',
'parse_step_json',
'parse_plan',
'PlannerOutputParser',
'PLANNING_TAG',
'PLANNING_END_TAG',
'FINAL_ANSWER_TAG',
'FINAL_ANSWER_END_TAG',
'DEFAULT_SYSTEM_PROMPT',
'PlanHandler',
]
@@ -0,0 +1,57 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import json
import traceback
from typing import Optional, Dict, Any, Union, List
from pydantic import BaseModel, Field
from aworld.logs.util import logger
class StepInfo(BaseModel):
"""Step information details"""
# input for agent
input: Optional[str] = Field(..., description="Step description")
# parameters for tools
parameters: Optional[Dict[str, Any]] = Field(..., default_factory=dict, description="Tool or agent parameters")
# id of tool or agent
id: str = Field(..., description="Tool or agent ID for execution")
class StepInfos(BaseModel):
"""Defined plan structure, including steps and their sequence."""
steps: Dict[str, StepInfo] = Field(
default_factory=dict,
description="step id with it info"
)
dag: List[Union[str, List[str]]] = Field(
default_factory=list,
description="dag"
)
class Plan(BaseModel):
"""Plan structure with step information and final answer"""
step_infos: StepInfos = Field(
default_factory=lambda: StepInfos(steps={}, dag=[]),
description="Step information and execution sequence"
)
answer: str = Field(
default="",
description="Final answer or result"
)
@classmethod
def parse_raw(cls, json_str: str) -> "Plan":
"""Create from JSON string"""
try:
data = json.loads(json_str)
return Plan.parse_obj(data)
except Exception as e:
logger.warning(f"{json_str} Failed to parse Plan and default to origin answer. \n{traceback.format_exc()}")
return Plan(step_infos=StepInfos(steps={}, dag=[]), answer=json_str)
@@ -0,0 +1,31 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import json
import traceback
from aworld.logs.util import logger
from examples.multi_agents.coordination.deepresearch.planner.models import Plan, StepInfos
def parse_step_infos(step_infos: dict) -> StepInfos:
"""Parse step information dictionary into StepInfos object"""
try:
return StepInfos.model_validate(step_infos)
except Exception as e:
logger.error(f"Error parsing step infos: {traceback.format_exc()}")
return StepInfos(steps={}, dag=[])
def parse_step_json(step_json: str) -> StepInfos:
"""Parse JSON string into StepInfos object"""
try:
data = json.loads(step_json)
except Exception as e:
logger.error(f"Failed to parse step JSON: {e}")
return StepInfos(steps={}, dag=[])
return parse_step_infos(data)
def parse_plan(plan_text: str) -> Plan:
"""Parse JSON string into Plan object"""
return Plan.parse_raw(plan_text)
@@ -0,0 +1,99 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import json
import re
from aworld.core.agent.base import AgentResult
from aworld.core.common import ActionModel
from aworld.core.model_output_parser import ModelOutputParser
from aworld.logs.util import logger
from aworld.models.model_response import ModelResponse
from examples.multi_agents.coordination.deepresearch.planner.models import Plan
from examples.multi_agents.coordination.deepresearch.planner.parse import parse_step_json
# Tags for response structure
PLANNING_TAG = "<PLANNING_TAG>"
PLANNING_END_TAG = "</PLANNING_TAG>"
FINAL_ANSWER_TAG = "<FINAL_ANSWER_TAG>"
FINAL_ANSWER_END_TAG = "</FINAL_ANSWER_TAG>"
# Default system prompt
DEFAULT_SYSTEM_PROMPT = f"""When answering questions, please follow these two steps:
1. First, output an execution plan in JSON format between {PLANNING_TAG} and {PLANNING_END_TAG}:
{{
"steps": {{
"agent_step_1": {{"input": "step description", "id": "tool_name"}},
"agent_step_2": {{"input": "step description", "id": "tool_name"}},
"agent_step_3": {{"input": "step description", "id": "tool_name"}}
}},
"dag": ["agent_step_1","agent_step_2","agent_step_3"]
}}
Where:
- steps: Contains each step's description and executor ID
* "id" MUST be a valid tool name from the available tools list below
* "input" describes what this step should accomplish
- dag: Defines the execution order and dependencies between steps in "steps"
* Each element in dag refers to step keys in "steps"
* You MUST follow the json format in the example above
2. Then, provide the final answer between {FINAL_ANSWER_TAG} and {FINAL_ANSWER_END_TAG}:
- The answer should be accurate and meet the query requirements
- If unable to answer using existing tools and information, explain why and request more information
- Prioritize using information already available in the context to avoid redundant tool calls
Available Tools:
{{{{tool_list}}}}
User Input: {{{{task}}}}"""
class PlannerOutputParser(ModelOutputParser[ModelResponse, AgentResult]):
"""Parser for responses that include thinking process and planning."""
def __init__(self, agent_name: str):
self.agent_name = agent_name
async def parse(self, resp: ModelResponse, **kwargs) -> AgentResult:
if not resp or not resp.content:
logger.warning("No valid response content!")
return AgentResult(actions=[], current_state=None)
content = resp.content.strip()
# Extract planning section
planning_match = re.search(r'<PLANNING_TAG>(.*?)</PLANNING_TAG>', content, re.DOTALL)
final_answer_match = re.search(r'<FINAL_ANSWER_TAG>(.*?)</FINAL_ANSWER_TAG>', content, re.DOTALL)
step_json = ""
if planning_match:
step_json = planning_match.group(1).strip()
final_answer = ""
if final_answer_match:
final_answer = final_answer_match.group(1).strip()
actions = []
is_call_tool = False
# Parse planning section if exists
if step_json or final_answer:
try:
step_infos = parse_step_json(step_json)
plan = Plan(step_infos=step_infos, answer=final_answer)
logger.info(f"BuiltInPlannerOutputParser|plan|{plan.json()}")
actions.append(ActionModel(
agent_name=self.agent_name,
policy_info=plan.model_dump_json()
))
except json.JSONDecodeError:
logger.warning("Failed to parse planning JSON")
# If neither planning nor final answer found, use entire content
if not actions:
actions.append(ActionModel(
agent_name=self.agent_name,
policy_info=content
))
logger.info(f"BuiltInPlannerOutputParser|actions|{actions}")
return AgentResult(actions=actions, current_state=None, is_call_tool=is_call_tool)
@@ -0,0 +1,140 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import asyncio
import uuid
from typing import AsyncGenerator
from aworld.core.common import ActionModel, Observation, TaskItem
from aworld.core.event.base import AgentMessage, Constants, TopicType, Message
from aworld.core.exceptions import AWorldRuntimeException
from aworld.logs.util import logger
from aworld.output.base import StepOutput
from aworld.runners import HandlerFactory
from aworld.runners.handler.agent import AgentHandler
from aworld.utils.run_util import exec_agent, exec_tool
from examples.multi_agents.coordination.deepresearch.planner.models import StepInfo
from examples.multi_agents.coordination.deepresearch.planner.parse import parse_plan
@HandlerFactory.register(name=f'__{Constants.PLAN}__')
class PlanHandler(AgentHandler):
def is_valid_message(self, message: Message):
if message.category != Constants.PLAN:
return False
return True
async def handle(self, message: Message) -> AsyncGenerator[Message, None]:
if not self.is_valid_message(message):
return
logger.info(f"PlanHandler|handle|taskid={self.task_id}|is_sub_task={message.context._task.is_sub_task}")
content = message.payload
# data is List[ActionModel]
for action in content:
if not isinstance(action, ActionModel):
# error message, p2p
yield Message(
category=Constants.OUTPUT,
payload=StepOutput.build_failed_output(name=f"{message.caller or self.name()}",
step_num=0,
data="action not a ActionModel.",
task_id=self.task_id),
sender=self.name(),
session_id=message.session_id,
headers=message.headers
)
msg = Message(
category=Constants.TASK,
payload=TaskItem(msg="action not a ActionModel.", data=content, stop=True),
sender=self.name(),
session_id=message.session_id,
topic=TopicType.ERROR,
headers=message.headers
)
logger.info(f"agent handler send task message: {msg}")
yield msg
return
logger.info(f"PlanHandler|content|{content}")
plan = parse_plan(content[0].policy_info)
logger.info(f"PlanHandler|plan|{plan}")
step_infos = plan.step_infos
steps = step_infos.steps
dag = step_infos.dag
if not steps or not dag:
if plan.answer:
logger.info(f"FINISHED|PlanHandler|plan|finished|{plan.answer}")
yield Message(
category=Constants.TASK,
payload=plan.answer,
sender=self.name(),
session_id=message.session_id,
topic=TopicType.FINISHED,
headers=message.headers
)
else:
raise AWorldRuntimeException("no steps and answer.")
group_id = self.runner.task.group_id if self.runner.task.group_id else uuid.uuid4().hex
self.runner.task.group_id = group_id
merge_context = message.context
for node in dag:
if isinstance(node, list):
logger.info(f"PlanHandler|parallel_node|start|{node}")
# can parallel
tasks = []
for n in node:
new_context = merge_context.deep_copy()
step_info: StepInfo = steps.get(n)
agent = self.swarm.agents.get(step_info.id)
if agent:
tasks.append(exec_agent(step_info.input, agent, new_context,
outputs=merge_context.outputs,
sub_task=True,
task_group_id=group_id))
else:
names = step_info.id.split("__")
action_name = '__'.join(names[1:]) if len(names) > 1 else ''
tasks.append(exec_tool(tool_name=names[0],
action_name=action_name,
params=step_info.parameters,
agent_name=message.sender,
context=new_context,
sub_task=True,
outputs=merge_context.outputs,
task_group_id=group_id))
res = await asyncio.gather(*tasks)
for idx, t in enumerate(res):
merge_context.merge_context(t.context)
merge_context.save_action_trajectory(steps.get(node[idx]).id, t.answer)
logger.info(f"PlanHandler|parallel_node|end|{res}")
else:
logger.info(f"PlanHandler|single_node|start|{node}")
step_info: StepInfo = steps.get(node)
agent = self.swarm.agents.get(step_info.id)
new_context = merge_context.deep_copy()
if agent:
res = await exec_agent(step_info.input, agent, new_context, outputs=merge_context.outputs,
sub_task=True, task_group_id=group_id)
else:
names = step_info.id.split("__")
action_name = '__'.join(names[1:]) if len(names) > 1 else ''
res = await exec_tool(tool_name=step_info.id,
action_name=action_name,
params=step_info.parameters,
agent_name=message.sender,
context=new_context,
outputs=merge_context.outputs,
sub_task=True,
task_group_id=group_id)
merge_context.merge_context(res.context)
merge_context.save_action_trajectory(step_info.id, res.answer, agent_name=agent.id())
logger.info(f"PlanHandler|single_node|end|{res}")
new_plan_input = Observation(content=merge_context.task_input)
yield AgentMessage(session_id=message.session_id,
payload=new_plan_input,
sender=self.name(),
receiver=self.swarm.communicate_agent.id(),
headers={'context': merge_context})
@@ -0,0 +1,311 @@
parallel_plan_sys_prompt = """## Task
You are an information search expert. Your goal is to maximize the retrieval of effective information through search task planning and retrieval. Please plan the necessary search and processing steps to solve the problem based on the user's question and background information.
## Problem Analysis and Search Strategy Planning
- Break down complex user questions into multi-step or single-step search plans. Ensure all search plans are **complete and executable**.
- Use step-by-step searching. For high-complexity problems, break them down into multiple sequential execution steps.
- When planning, prioritize strategy breadth (coverage). Start with broad searches, then refine strategies based on search results.
- Typically limit to no more than 5 steps.
## Search Strategy Key Points
- Source Reasoning: Trace user queries to their sources, especially focusing on official websites and officially published information.
- Multiple Intent Breakdown: If user input contains multiple intentions or meanings, break it down into independently searchable queries.
- Information Completion:
- Supplement omitted or implied information in user questions
- Replace pronouns with specific entities based on context
- Time Conversion: The current date is {{current_date}}. Convert relative time expressions in user input to specific dates or date ranges.
- Semantic Completeness: Ensure each query is semantically clear and complete for precise search engine results.
- Bilingual Search: Many data sources require English searches, so provide corresponding English information.
## Important Output Format Requirements (MUST STRICTLY FOLLOW):
1. BOTH tags (<PLANNING_TAG> and <FINAL_ANSWER_TAG>) MUST be present
2. The JSON inside <PLANNING_TAG> MUST be valid and properly formatted
3. Inside <PLANNING_TAG>:
- The "steps" object MUST contain numbered steps (agent_step_1, agent_step_2, etc.)
- Each step MUST have both "input" and "id" fields
- The "dag" array MUST define execution order using step IDs
- Parallel steps MUST be grouped in nested arrays
4. DO NOT include any explanatory text between the two tag sections
5. DO NOT modify or change the tag names
6. If no further planning is needed, output an empty <PLANNING_TAG> section but STILL include <FINAL_ANSWER_TAG> with explanation
## Example:
Topic: Analyze the development trends and main challenges of China's New Energy Vehicle (NEV) market in 2024
<PLANNING_TAG>
{
"steps": {
"agent_step_1": {
"input": "Search for 2024 China NEV market policy updates and industry forecasts",
"id": "search_tool"
},
"agent_step_2": {
"input": "Search for major challenges and bottlenecks in China's NEV industry development",
"id": "search_tool"
},
"agent_step_3": {
"input": "Analyze market trends based on gathered data and synthesize findings",
"id": "analysis_tool"
}
},
"dag": [["agent_step_1", "agent_step_2"], "agent_step_3"]
}
</PLANNING_TAG>
<FINAL_ANSWER_TAG>
Based on the planned analysis steps, we will be able to provide a comprehensive overview of China's NEV market development trends and challenges in 2024, incorporating both policy updates and industry insights.
</FINAL_ANSWER_TAG>
Topic: Research the latest developments in Large Language Models (LLMs) and their impact on the AI industry in the past 6 months
<PLANNING_TAG>
{
"steps": {
"agent_step_1": {
"input": "Search for major LLM releases and technical breakthroughs in the last 6 months",
"id": "search_tool"
},
"agent_step_2": {
"input": "Search for industry applications and commercial implementations of new LLM technologies",
"id": "search_tool"
},
"agent_step_3": {
"input": "Search for academic papers and research findings about LLM improvements",
"id": "search_tool"
},
"agent_step_4": {
"input": "Synthesize findings to analyze trends and impact on AI industry",
"id": "analysis_tool"
}
},
"dag": [["agent_step_1", "agent_step_2", "agent_step_3"], "agent_step_4"]
}
</PLANNING_TAG>
<FINAL_ANSWER_TAG>
Based on the planned research steps, we will compile a comprehensive analysis of recent LLM developments, including technical advances, practical applications, and their broader impact on the AI industry landscape.
</FINAL_ANSWER_TAG>
Topic: Compare the sustainability initiatives and environmental impact of major tech companies (Apple, Google, Microsoft) in their data centers
<PLANNING_TAG>
{
"steps": {
"agent_step_1": {
"input": "Search for official environmental reports and sustainability commitments from Apple, Google, and Microsoft",
"id": "search_tool"
},
"agent_step_2": {
"input": "Search for third-party assessments and environmental impact studies of tech companies' data centers",
"id": "search_tool"
},
"agent_step_3": {
"input": "Search for specific green initiatives and renewable energy projects by these companies",
"id": "search_tool"
},
"agent_step_4": {
"input": "Search for comparative analysis of environmental metrics and carbon footprint data",
"id": "search_tool"
},
"agent_step_5": {
"input": "Compile and compare findings to create a comprehensive comparison",
"id": "analysis_tool"
}
},
"dag": [["agent_step_1", "agent_step_2"], ["agent_step_3", "agent_step_4"], "agent_step_5"]
}
</PLANNING_TAG>
<FINAL_ANSWER_TAG>
Based on the planned analysis steps, we will provide a detailed comparison of sustainability initiatives and environmental impact across major tech companies, focusing on their data center operations and overall environmental commitments.
</FINAL_ANSWER_TAG>
Topic: No further research needed as all required information has been collected
<PLANNING_TAG>
{
"steps": {},
"dag": []
}
</PLANNING_TAG>
<FINAL_ANSWER_TAG>
Based on the comprehensive information already collected in previous steps, no additional research is needed. We can proceed with synthesizing the existing findings.
</FINAL_ANSWER_TAG>
## Research Topic
{{task}}"""
parallel_replan_sys_prompt = parallel_plan_sys_prompt + """
## Trajectories
{{trajectories}}
"""
plan_sys_prompt = """## Task
You are an information search expert. Your goal is to maximize the retrieval of effective information through search task planning and retrieval. Please plan the necessary search and processing steps to solve the problem based on the user's question and background information.
## Problem Analysis and Strategy Planning
- Break down complex user questions into multi-step or single-step search plans. Ensure all search plans are **complete and executable**.
- Use step-by-step searching. For high-complexity problems, break them down into multiple sequential execution steps.
- When planning, prioritize strategy breadth (coverage). Start with broad searches, then refine strategies based on search results.
- **IMPORTANT** Typically limit to no more than 3 steps.
## Search Strategy Key Points
- Source Reasoning: Trace user queries to their sources, especially focusing on official websites and officially published information.
- Multiple Intent Breakdown: If user input contains multiple intentions or meanings, break it down into independently searchable queries.
- Information Completion:
- Supplement omitted or implied information in user questions
- Replace pronouns with specific entities based on context
- Time Conversion: The current date is {{current_date}}. Convert relative time expressions in user input to specific dates or date ranges.
- Semantic Completeness: Ensure each query is semantically clear and complete for precise search engine results.
- Bilingual Search: Many data sources require English searches, so provide corresponding English information.
- **IMPORTANT** search at most 2 steps
## **IMPORTANT** Output Format:
1. BOTH tags (<PLANNING_TAG> and <FINAL_ANSWER_TAG>) MUST be present
2. The JSON inside <PLANNING_TAG> MUST be valid and properly formatted
3. Inside <PLANNING_TAG>:
- The "steps" object MUST contain numbered steps (agent_step_1, agent_step_2, etc.)
- Each step MUST have both "input" and "id" fields, "id" is the id of the tool_id or agent_id from ## Available Tools
- The "dag" array MUST define execution order using step IDs
- Parallel steps MUST be grouped in nested arrays
4. DO NOT include any explanatory text between the two tag sections
5. DO NOT modify or change the tag names
6. If no further planning is needed, output an empty <PLANNING_TAG> section but STILL include <FINAL_ANSWER_TAG> with explanation
## Example:
Topic: Analyze the development trends and main challenges of China's New Energy Vehicle (NEV) market in 2024
<PLANNING_TAG>
{
"steps": {
"agent_step_1": {
"input": "Search for 2024 China NEV market policy updates and industry forecasts",
"id": "search_tool"
},
"agent_step_2": {
"input": "Search for major challenges and bottlenecks in China's NEV industry development",
"id": "search_tool"
},
"agent_step_3": {
"input": "Analyze market trends based on gathered data and synthesize findings",
"id": "analysis_tool"
}
},
"dag": ["agent_step_1", "agent_step_2", "agent_step_3"]
}
</PLANNING_TAG>
<FINAL_ANSWER_TAG>
Based on the planned analysis steps, we will be able to provide a comprehensive overview of China's NEV market development trends and challenges in 2024, incorporating both policy updates and industry insights.
</FINAL_ANSWER_TAG>
Topic: Research the latest developments in Large Language Models (LLMs) and their impact on the AI industry in the past 6 months
<PLANNING_TAG>
{
"steps": {
"agent_step_1": {
"input": "Search for major LLM releases and technical breakthroughs in the last 6 months",
"id": "search_tool"
},
"agent_step_2": {
"input": "Search for industry applications and commercial implementations of new LLM technologies",
"id": "search_tool"
},
"agent_step_3": {
"input": "Search for academic papers and research findings about LLM improvements",
"id": "search_tool"
},
"agent_step_4": {
"input": "Synthesize findings to analyze trends and impact on AI industry",
"id": "analysis_tool"
}
},
"dag": ["agent_step_1", "agent_step_2", "agent_step_3", "agent_step_4"]
}
</PLANNING_TAG>
<FINAL_ANSWER_TAG>
Based on the planned research steps, we will compile a comprehensive analysis of recent LLM developments, including technical advances, practical applications, and their broader impact on the AI industry landscape.
</FINAL_ANSWER_TAG>
Topic: Compare the sustainability initiatives and environmental impact of major tech companies (Apple, Google, Microsoft) in their data centers
<PLANNING_TAG>
{
"steps": {
"agent_step_1": {
"input": "Search for official environmental reports and sustainability commitments from Apple, Google, and Microsoft",
"id": "search_tool"
},
"agent_step_2": {
"input": "Search for third-party assessments and environmental impact studies of tech companies' data centers",
"id": "search_tool"
},
"agent_step_3": {
"input": "Search for specific green initiatives and renewable energy projects by these companies",
"id": "search_tool"
},
"agent_step_4": {
"input": "Search for comparative analysis of environmental metrics and carbon footprint data",
"id": "search_tool"
},
"agent_step_5": {
"input": "Compile and compare findings to create a comprehensive comparison",
"id": "analysis_tool"
}
},
"dag": ["agent_step_1", "agent_step_2", "agent_step_3", "agent_step_4", "agent_step_5"]
}
</PLANNING_TAG>
<FINAL_ANSWER_TAG>
Based on the planned analysis steps, we will provide a detailed comparison of sustainability initiatives and environmental impact across major tech companies, focusing on their data center operations and overall environmental commitments.
</FINAL_ANSWER_TAG>
## Available Tools
{{tool_list}}
## Research Topic
{{task}}
## Trajectories
{{trajectories}}
"""
search_sys_prompt = """Conduct targeted aworld_search tools to gather the most recent, credible information on "{{task}}" and synthesize it into a verifiable text artifact.
Instructions:
- Query should ensure that the most current information is gathered. The current date is {{current_date}}.
- Conduct multiple, diverse searches to gather comprehensive information.
- Consolidate key findings while meticulously tracking the source(s) for each specific piece of information.
- The output should be a well-written summary or report based on your search findings.
- Only include the information found in the search results, don't make up any information.
- Generate output in English
- Search tool accepts one parameter and returns one result
Research Topic:
{{task}}
"""
reporting_sys_prompt = """Generate a high-quality answer to the user's question based on the provided summaries.
Instructions:
- The current date is {{current_date}}.
- You are the final step of a multi-step research process, don't mention that you are the final step.
- You have access to all the information gathered from the previous steps.
- You have access to the user's question.
- Generate a high-quality answer to the user's question based on the provided summaries and the user's question.
- you MUST include all the citations from the summaries in the answer correctly.
- Format the output using HTML structure
User Context:
- {{task}}
Summaries:
{{trajectories}}"""
@@ -0,0 +1,71 @@
import os
from aworld.agents.llm_agent import Agent
from aworld.config.conf import AgentConfig, ModelConfig
from aworld.core.agent.swarm import TeamSwarm
from aworld.core.event.base import Constants
from aworld.runner import Runners
from examples.common.tools.common import Tools
from examples.multi_agents.coordination.deepresearch.prompts import *
from examples.multi_agents.coordination.deepresearch.planner.plan import PlannerOutputParser
from examples.web.agent_deploy.deep_research.agent import BaseDynamicPromptAgent
# os.environ["LLM_MODEL_NAME"] = "qwen/qwen3-8b"
# os.environ["LLM_BASE_URL"] = "YOUR_LLM_BASE_URL"
# os.environ["LLM_API_KEY"] = "YOUR_LLM_API_KEY"
class PlannerAgent(BaseDynamicPromptAgent):
pass
def get_deepresearch_swarm(user_input):
agent_config = AgentConfig(
llm_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"),
llm_temperature=os.getenv("LLM_TEMPERATURE", 0.0)
),
use_vision=False
)
agent_id = "planner_agent"
plan_agent = PlannerAgent(
agent_id = agent_id,
name="planner_agent",
desc="planner_agent",
conf=agent_config,
model_output_parser=PlannerOutputParser(agent_id),
system_prompt=plan_sys_prompt,
event_handler_name=Constants.PLAN
)
web_search_agent = Agent(
name="web_search_agent",
desc="web_search_agent",
conf=agent_config,
system_prompt=search_sys_prompt,
tool_names=[Tools.SEARCH_API.value]
)
reporting_agent = Agent(
name="reporting_agent",
desc="reporting_agent",
conf=agent_config,
system_prompt=reporting_sys_prompt,
)
return TeamSwarm(plan_agent, web_search_agent, reporting_agent, max_steps=1)
if __name__ == "__main__":
user_input = "7天北京旅游计划"
swarm = get_deepresearch_swarm(user_input)
result = Runners.sync_run(
input=user_input,
swarm=swarm
)
print("deepresearch result: ", result)
@@ -0,0 +1,60 @@
# TeamSwarm Example
This example demonstrates how to build a multi-agent collaborative system using the TeamSwarm feature in the AWorld framework. In this example, we create a team with three agents:
1. **PlanAgent** - The lead agent responsible for breaking down tasks and planning execution steps
2. **SearchAgent** - The execution agent responsible for performing web search tasks
3. **SummaryAgent** - The execution agent responsible for summarizing information and generating final reports
## File Structure
- `run_multi_action.py` - Main example code showing how to build and run the multi-action version of TeamSwarm
- `run.py` - Single-action version of the TeamSwarm example
- `prompts_multi_actions.py` - Contains prompt templates used by agents in the multi-action planning version
- `prompts_single_action.py` - Contains prompt templates used by agents in the single-action version
## Running the Examples
### Multi-action Planning Version
```bash
python run_multi_action.py
```
### Single-action Version
```bash
python run.py
```
## TeamSwarm Workflow
### Multi-action Planning Version
1. PlanAgent receives user input and plans multiple search and summary actions at once
2. PlanAgent breaks down complex problems into multiple sub-problems and plans search actions for each
3. SearchAgent executes the search actions according to the plan to gather comprehensive information
4. SummaryAgent synthesizes all collected information and generates a final report for the user
5. The entire workflow is planned upfront with consideration for dependencies between actions
### Single-action Version
1. PlanAgent receives user input and decides whether to execute a search or summary based on the current context
2. If more information is needed, PlanAgent calls SearchAgent to perform a search
3. If sufficient information has been collected, PlanAgent calls SummaryAgent to generate a summary
4. If a summary has been executed and results obtained, PlanAgent outputs the result without calling any tools
## Core Concepts
TeamSwarm is a special Swarm structure that requires a lead agent with other agents following its commands. In TeamSwarm:
- The first agent (or the agent specified by the root_agent parameter) is the leader
- Other agents act as executors, interacting with the leader
- The leader decides when and which executor to call
This structure is suitable for scenarios requiring a central coordinator to manage multiple specialized agents, such as the information search and summarization tasks in this example.
## Advantages of the Single-action Version
The single-action version of TeamSwarm has the following advantages compared to the multi-action planning version:
1. **More flexible decision-making** - Each decision is based on the latest context, allowing strategy adjustments based on real-time situations
2. **Better error recovery** - If a step fails, subsequent steps can be adjusted based on the failure results
3. **More efficient resource utilization** - Search is only performed when needed, avoiding unnecessary operations
4. **More natural interaction flow** - Simulates human thinking and decision-making processes by searching for information first and then summarizing
@@ -0,0 +1,5 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
"""
TeamSwarm示例模块
"""
@@ -0,0 +1,83 @@
"""
Contains various prompt templates used in the TeamSwarm example
"""
# System prompt for the planning agent
plan_sys_prompt = """## Task
You are an information search expert. Your goal is to maximize the acquisition of effective information through search task planning and retrieval. Please plan the search and processing steps needed to solve the problem based on the user's question and background information.
## Problem Analysis and Strategy Planning
- Break down complex user problems into multi-step or single-step search plans. Ensure all search plans are **complete and executable**.
- Call search tools to perform searches according to the breakdown search plan.
- For highly complex problems, break down the problem into no more than 3 sub-problems, and call search tools for each sub-problem.
- **Important note**: Usually limit to no more than 3 sub-problems.
## Action Decision
- Based on the current context and available information, decide whether the next step is to perform a search or a summary
- If more information is needed, choose a search action
- If sufficient information has been collected, choose a summary action
- If a summary action has been executed and results obtained, output the summary results directly without calling tools
- After search actions are completed, the summary tool must be used to summarize
## Search Strategy Key Points
- Multi-intent decomposition: If user input contains multiple intents or meanings, break it down into independently searchable queries.
- Information completion:
- Supplement information omitted or implied in the user's question
- Replace pronouns with specific entities based on context
- Time conversion: Current date is {{current_date}}. Convert relative time expressions in user input to specific dates or date ranges.
- Semantic completeness: Ensure each query is semantically clear and complete to get precise search engine results.
- Bilingual search: Many data sources require English searches, so please provide corresponding English information.
- **Important note**: Search no more than 2 steps
## **Important Notes**
1. When a search action needs to be executed, call the corresponding search tool
2. Do not execute search actions more than 2 times; if search actions have been executed 2 times and results obtained, do not call the search tool again
3. When a summary action needs to be executed, call the corresponding summary tool
4. Do not execute summary actions more than 2 times; if summary actions have been executed 2 times and results obtained, do not call the summary tool again
## Available Tools
{{tool_list}}
## Research Topic
{{task}}"""
# System prompt for replanning
replan_sys_prompt = plan_sys_prompt + """
## Trajectories
{{trajectories}}
"""
# System prompt for the search agent
search_sys_prompt = """Perform targeted search tools to collect the latest, credible information about "{{task}}" and synthesize it into verifiable text.
Instructions:
- Queries should ensure collection of the latest information. Current date is {{current_date}}.
- Conduct multiple different searches to collect comprehensive information.
- Integrate key findings while precisely tracking the source of each specific piece of information.
- Output should be a carefully written summary or report based on your search results.
- Only include information found in search results, do not fabricate any information.
- Generate output in English
- The search tool accepts one parameter and returns one result
Research Topic:
{{task}}
"""
# System prompt for the summary agent
summary_sys_prompt = """Generate a high-quality answer to the user's question based on the provided summaries.
Instructions:
- Current date is {{current_date}}.
- You are the final step in a multi-step research process, do not mention that you are the final step.
- You have access to all information collected from previous steps.
- You have access to the user's question.
- Generate a high-quality answer based on the provided summaries and the user's question.
- You must correctly include all references from the summaries in your answer.
- Format output using HTML structure
User Context:
- {{task}}
Summaries:
{{trajectories}}"""
@@ -0,0 +1,77 @@
"""
Contains various prompt templates used in the TeamSwarm example (single-action version)
"""
# System prompt for the planning agent
plan_sys_prompt = """## Task
You are an information search expert. Your goal is to maximize the acquisition of effective information through search task planning and retrieval. Based on the user's question and background information, plan the next search or summary processing step that needs to be executed.
## Single-Action Decision Making
- You only need to decide on executing one action at a time, rather than planning multiple steps at once
- Based on the current context and available information, decide whether the next step is to perform a search or a summary
- If more information is needed, choose a search action
- If sufficient information has been collected, choose a summary action
- If a summary action has been executed and results obtained, output the summary results directly without calling tools
## Search Strategy Key Points
- Source reasoning: Trace user queries back to their sources, with special focus on official websites and officially released information
- Multi-intent decomposition: If user input contains multiple intents or meanings, search for them separately
- Information completion: Supplement information omitted or implied in the user's question, replace pronouns with specific entities based on context
- Time conversion: Current date is {{current_date}}. Convert relative time expressions to specific dates or date ranges
- Semantic completeness: Ensure each query is semantically clear and complete to get precise search results
- Bilingual search: Many data sources require English searches, so please provide corresponding English information
## **Important Notes**
1. When a search action needs to be executed, call the corresponding search tool
2. Do not execute search actions more than 2 times; if search actions have been executed 2 times and results obtained, do not call the search tool again
3. When a summary action needs to be executed, call the corresponding summary tool
4. Do not execute summary actions more than 2 times; if summary actions have been executed 2 times and results obtained, do not call the summary tool again
## Available Tools
{{tool_list}}
## Research Topic
{{task}}"""
# System prompt for replanning
replan_sys_prompt = plan_sys_prompt + """
## Trajectories
{{trajectories}}
"""
# System prompt for the search agent
search_sys_prompt = """Perform targeted search tools to collect the latest, credible information about "{{task}}" and synthesize it into verifiable text.
Instructions:
- Queries should ensure collection of the latest information. Current date is {{current_date}}.
- Conduct multiple different searches to collect comprehensive information.
- Integrate key findings while precisely tracking the source of each specific piece of information.
- Output should be a carefully written summary or report based on your search results.
- Only include information found in search results, do not fabricate any information.
- Generate output in English
- The search tool accepts one parameter and returns one result
- Call the search tool no more than 3 times
- If historical search tool calls have reached 3 times, do not call the search tool again, directly output the search results
Research Topic:
{{task}}
"""
# System prompt for the summary agent
summary_sys_prompt = """Generate a high-quality answer to the user's question based on the provided summaries.
Instructions:
- Current date is {{current_date}}.
- You are the final step in a multi-step research process, do not mention that you are the final step.
- You have access to all information collected from previous steps.
- You have access to the user's question.
- Generate a high-quality answer based on the provided summaries and the user's question.
- You must correctly include all references from the summaries in your answer.
- Format output using HTML structure
User Context:
- {{task}}
Summaries:
{{trajectories}}"""
@@ -0,0 +1,95 @@
import os
from pathlib import Path
import sys
project_root = Path(__file__).parent.parent.parent
sys.path.insert(0, str(project_root))
from aworld.core.agent.swarm import TeamSwarm
from aworld.runner import Runners
from examples.common.tools.common import Tools
from aworld.agents.llm_agent import Agent
from aworld.config.conf import AgentConfig, ModelConfig
from examples.multi_agents.coordination.master_worker.prompts_single_action import (
plan_sys_prompt,
search_sys_prompt,
summary_sys_prompt
)
# Set environment variables, configure LLM model
# os.environ["LLM_MODEL_NAME"] = "YOUR_LLM_MODEL_NAME"
# os.environ["LLM_BASE_URL"] = "YOUR_LLM_BASE_URL"
# os.environ["LLM_API_KEY"] = "YOUR_LLM_API_KEY"
def get_single_action_team_swarm(user_input):
"""
Create a single-action version of TeamSwarm, consisting of PlanAgent, SearchAgent, and SummaryAgent
In this version, PlanAgent generates only one action at a time, deciding whether to execute a search or summary based on the current context
Args:
user_input: User's input query
Returns:
TeamSwarm instance
"""
# Create a unified Agent configuration
agent_config = AgentConfig(
llm_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"),
llm_temperature=os.getenv("LLM_TEMPERATURE", 0.0)
),
use_vision=False
)
# Create planning Agent, responsible for planning single execution steps based on context
plan_agent = Agent(
name="plan_agent",
desc="Agent responsible for deciding whether to execute search or summary based on current context",
conf=agent_config,
system_prompt_template=plan_sys_prompt,
use_planner=False,
use_tools_in_prompt=False
)
# Create search Agent, responsible for executing web search tasks
search_agent = Agent(
name="search_agent",
desc="Agent responsible for executing web search tasks",
conf=agent_config,
system_prompt_template=search_sys_prompt,
tool_names=[Tools.SEARCH_API.value]
)
# Create summary Agent, responsible for summarizing information and generating final reports
summary_agent = Agent(
name="summary_agent",
desc="Agent responsible for summarizing information and generating final reports",
conf=agent_config,
system_prompt_template=summary_sys_prompt,
)
# Create TeamSwarm, with plan_agent as the lead Agent and other Agents as executors
# Increase maximum steps to support multiple rounds of interaction
return TeamSwarm(plan_agent, search_agent, summary_agent, max_steps=10)
if __name__ == "__main__":
# User input example
user_input = "Please provide me with information about the latest developments in large language models"
# Create single-action version of TeamSwarm
swarm = get_single_action_team_swarm(user_input)
# Run TeamSwarm
result = Runners.sync_run(
input=user_input,
swarm=swarm
)
print("Single-action TeamSwarm execution result: ", result)
@@ -0,0 +1,95 @@
import os
from pathlib import Path
import sys
project_root = Path(__file__).parent.parent.parent
sys.path.insert(0, str(project_root))
from aworld.core.agent.swarm import TeamSwarm
from aworld.runner import Runners
from examples.common.tools.common import Tools
from aworld.agents.llm_agent import Agent
from aworld.config.conf import AgentConfig, ModelConfig
from examples.multi_agents.coordination.master_worker.prompts_multi_actions import (
plan_sys_prompt,
search_sys_prompt,
summary_sys_prompt
)
# Set environment variables, configure LLM model
# os.environ["LLM_MODEL_NAME"] = "YOUR_LLM_MODEL_NAME"
# os.environ["LLM_BASE_URL"] = "YOUR_LLM_BASE_URL"
# os.environ["LLM_API_KEY"] = "YOUR_LLM_API_KEY"
def get_multi_action_team_swarm(user_input):
"""
Create a multi-action version of TeamSwarm, consisting of PlanAgent, SearchAgent, and SummaryAgent
In this version, PlanAgent generates only one action at a time, deciding whether to execute a search or summary based on the current context
Args:
user_input: User's input query
Returns:
TeamSwarm instance
"""
# Create a unified Agent configuration
agent_config = AgentConfig(
llm_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"),
llm_temperature=os.getenv("LLM_TEMPERATURE", 0.0)
),
use_vision=False
)
# Create planning Agent, responsible for planning single execution steps based on context
plan_agent = Agent(
name="plan_agent",
desc="Agent responsible for deciding whether to execute search or summary based on current context",
conf=agent_config,
system_prompt_template=plan_sys_prompt,
use_planner=False,
use_tools_in_prompt=False
)
# Create search Agent, responsible for executing web search tasks
search_agent = Agent(
name="search_agent",
desc="Agent responsible for executing web search tasks",
conf=agent_config,
system_prompt_template=search_sys_prompt,
tool_names=[Tools.SEARCH_API.value]
)
# Create summary Agent, responsible for summarizing information and generating final reports
summary_agent = Agent(
name="summary_agent",
desc="Agent responsible for summarizing information and generating final reports",
conf=agent_config,
system_prompt_template=summary_sys_prompt,
)
# Create TeamSwarm, with plan_agent as the lead Agent and other Agents as executors
# Increase maximum steps to support multiple rounds of interaction
return TeamSwarm(plan_agent, search_agent, summary_agent, max_steps=10)
if __name__ == "__main__":
# User input example
user_input = "Research the future development plans of Horizon Robotics and Momenta"
# Create multi-action version of TeamSwarm
swarm = get_multi_action_team_swarm(user_input)
# Run TeamSwarm
result = Runners.sync_run(
input=user_input,
swarm=swarm
)
print("Parallel-action TeamSwarm execution result: ", result)
@@ -0,0 +1,2 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
@@ -0,0 +1,49 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import os
from aworld.agents.llm_agent import Agent
from aworld.config.conf import AgentConfig
from examples.common.tools.common import Tools
search_sys_prompt = "You are a helpful search agent."
search_prompt = """
Please act as a search agent, constructing appropriate keywords and searach terms, using search toolkit to collect relevant information, including urls, webpage snapshots, etc.
Here are the question: {task}
pleas only use one action complete this task, at least results 6 pages.
"""
summary_sys_prompt = "You are a helpful general summary agent."
summary_prompt = """
Summarize the following text in one clear and concise paragraph, capturing the key ideas without missing critical points.
Ensure the summary is easy to understand and avoids excessive detail.
Here are the content:
{task}
"""
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"),
llm_temperature=os.getenv("LLM_TEMPERATURE", 0.0)
)
search = Agent(
conf=agent_config,
name="search_agent",
system_prompt=search_sys_prompt,
agent_prompt=search_prompt,
tool_names=[Tools.SEARCH_API.value]
)
summary = Agent(
conf=agent_config,
name="summary_agent",
system_prompt=summary_sys_prompt,
agent_prompt=summary_prompt
)
@@ -0,0 +1,21 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
from aworld.core.agent.swarm import Swarm
from aworld.runner import Runners
from examples.multi_agents.workflow.search.common import *
if __name__ == "__main__":
s1 = Swarm(search)
s2 = Swarm(summary)
# default is workflow swarm
# swarm1 and swarm2 are embedded into the swarm, which is a hierarchical swarm
swarm = Swarm(s1, s2, max_steps=1)
prefix = ""
# can special search google, wiki, duck go, or baidu. such as:
# prefix = "search wiki: "
res = Runners.sync_run(
input=prefix + """What is an agent.""",
swarm=swarm
)
print(res.answer)
@@ -0,0 +1,28 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
from aworld.core.agent.swarm import Swarm
from aworld.runner import Runners
from examples.multi_agents.workflow.search.common import *
if __name__ == "__main__":
search2 = Agent(
conf=agent_config,
name="search_agent",
system_prompt=search_sys_prompt,
agent_prompt=search_prompt,
tool_names=[Tools.SEARCH_API.value]
)
# default is workflow swarm
# search1 and search2 parallel execution and use the same input.
swarm = Swarm((search, summary), (search2, summary), max_steps=1)
# you also can set root_agent=[search, search2]
prefix = ""
# can special search google, wiki, duck go, or baidu. such as:
# prefix = "search wiki: "
res = Runners.sync_run(
input=prefix + """What is an agent.""",
swarm=swarm
)
print(res.answer)
@@ -0,0 +1,27 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
from aworld.core.agent.swarm import Swarm
from aworld.runner import Runners
from examples.multi_agents.workflow.search.common import *
# os.environ["LLM_MODEL_NAME"] = "YOUR_LLM_MODEL_NAME"
# os.environ["LLM_BASE_URL"] = "YOUR_LLM_BASE_URL"
# os.environ["LLM_API_KEY"] = "YOUR_LLM_API_KEY"
# search and summary
if __name__ == "__main__":
# need to set GOOGLE_API_KEY and GOOGLE_ENGINE_ID to use Google search.
# os.environ['GOOGLE_API_KEY'] = ""
# os.environ['GOOGLE_ENGINE_ID'] = ""
# default is workflow swarm
swarm = Swarm(search, summary, max_steps=1)
# swarm = WorkflowSwarm(search, summary, max_steps=1)
prefix = ""
# can special search google, wiki, duck go, or baidu. such as:
# prefix = "search wiki: "
res = Runners.sync_run(
input=prefix + """What is an agent.""",
swarm=swarm
)
print(res.answer)