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,2 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
@@ -0,0 +1,224 @@
import json
import logging
from mailbox import Message
import os
import traceback
from typing import Any, AsyncGenerator, Dict, List
from aworld.cmd.utils.agent_ui_parser import AWorldWebAgentUI
from aworld.core.common import ActionModel, Observation
from aworld.core.context.base import Context
from aworld.memory.models import MemorySystemMessage, MessageMetadata
from aworld.output.base import MessageOutput
from aworld.output.ui.base import AworldUI
from aworld.output.workspace import WorkSpace
from examples.multi_agents.coordination.deepresearch.planner.plan import PlannerOutputParser
from aworld.core.agent.swarm import TeamSwarm
from aworld.cmd.data_model import BaseAWorldAgent, ChatCompletionRequest
from aworld.config.conf import AgentConfig, ModelConfig, TaskConfig
from aworld.agents.llm_agent import Agent
from aworld.core.task import Task
from aworld.runner import Runners
from examples.common.tools.common import Tools
from .prompts import *
logger = logging.getLogger(__name__)
class BaseDynamicPromptAgent(Agent):
async def async_policy(
self,
observation: Observation,
info: Dict[str, Any] = {},
message: Message = None,
**kwargs,
) -> List[ActionModel]:
return await super().async_policy(observation, info, message, **kwargs)
# multi turn system prompt generation
async def _add_system_message_to_memory(self, context: Context, content: str):
session_id = context.get_task().session_id
task_id = context.get_task().id
user_id = context.get_task().user_id
if not self.system_prompt:
return
content = self.system_prompt_template.format(context=context, task=content)
# logger.info(f"system prompt content: {content}")
await self.memory.add(
MemorySystemMessage(
content=content,
metadata=MessageMetadata(
session_id=session_id,
user_id=user_id,
task_id=task_id,
agent_id=self.id(),
agent_name=self.name(),
),
),
agent_memory_config=self.memory_config,
)
logger.info(
f"🧠 [MEMORY:short-term] Added system input to agent memory: Agent#{self.id()}, 💬 {content[:100]}..."
)
class PlanAgent(BaseDynamicPromptAgent):
pass
class ReportingAgent(BaseDynamicPromptAgent):
pass
def get_deepresearch_swarm(user_input):
agent_config = AgentConfig(
llm_config=ModelConfig(
llm_provider=os.getenv("LLM_MODEL_PROVIDER_DEEPRESEARCH", "openai"),
llm_model_name=os.getenv("LLM_MODEL_NAME_DEEPRESEARCH"),
llm_base_url=os.getenv("LLM_BASE_URL_DEEPRESEARCH"),
llm_api_key=os.getenv("LLM_API_KEY_DEEPRESEARCH"),
),
use_vision=False,
)
agent_id = "🧠 DeepResearchPlanAgent"
plan_agent = PlanAgent(
agent_id=agent_id,
name=agent_id,
desc=agent_id,
conf=agent_config,
use_tools_in_prompt=True,
model_output_parser=PlannerOutputParser(agent_id),
system_prompt_template=plan_sys_prompt,
)
web_search_agent = Agent(
name="🔎 WebSearchAgent",
desc="🔎 WebSearchAgent",
conf=agent_config,
system_prompt_template=search_sys_prompt,
tool_names=[Tools.SEARCH_API.value],
)
reporting_agent = Agent(
name="📝 ReportingAgent",
desc="📝 ReportingAgent",
conf=agent_config,
system_prompt_template=reporting_sys_prompt,
)
return TeamSwarm(plan_agent, web_search_agent, reporting_agent, max_steps=1)
class DeepResearchAgentWebUI(AWorldWebAgentUI):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
async def message_output(self, output: MessageOutput):
content = ""
try:
if (
hasattr(output, "response")
and "<FINAL_ANSWER_TAG>" in output.response
and "</FINAL_ANSWER_TAG>" in output.response
):
try:
content = (
output.response.split("<FINAL_ANSWER_TAG>")[1]
.split("</FINAL_ANSWER_TAG>")[0]
.strip()
)
except:
pass
except Exception as e:
logger.error(f"Error parsing output: {traceback.format_exc()}")
step_info = ""
try:
if (
"<PLANNING_TAG>" in output.response
and "</PLANNING_TAG>" in output.response
):
try:
planning = (
output.response.split("<PLANNING_TAG>")[1]
.split("</PLANNING_TAG>")[0]
.strip()
)
plan = json.loads(planning)
steps = plan.get("steps")
dags = plan.get("dag")
for i, dag in enumerate(dags):
if isinstance(dag, list):
for sub_i, sub_dag in enumerate(dag):
sub_step = steps.get(sub_dag)
sub_step_id = sub_step.get("id")
sub_step_input = sub_step.get("input")
step_info += f" - STEP {i+1}.{sub_i+1}: {sub_step_input} *@{sub_step_id}*\n"
else:
step = steps.get(dag)
step_id = step.get("id")
step_input = step.get("input")
step_info += f" - STEP {i+1}: {step_input} *@{step_id}*\n"
except:
pass
except Exception as e:
logger.error(f"Error parsing output: {traceback.format_exc()}")
if content and step_info:
return f"\n\n{content}\n\n**Execution Steps:**\n{step_info}\n"
elif step_info:
return f"\n\n**Execution Steps:**\n{step_info}\n"
elif content:
return f"\n\n{content}\n"
return await super().message_output(output)
class AWorldAgent(BaseAWorldAgent):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def name(self):
return "Deep Research[PlanAgent]"
def description(self):
return "Deep Research Agent with PlanAgent"
async def run(self, prompt: str = None, request: ChatCompletionRequest = None):
if prompt is None and request is not None:
prompt = request.messages[-1].content
swarm = get_deepresearch_swarm(prompt)
task = Task(
input=prompt,
swarm=swarm,
conf=TaskConfig(max_steps=20),
session_id=request.session_id,
endless_threshold=50,
)
rich_ui = DeepResearchAgentWebUI(
session_id=request.session_id,
workspace=WorkSpace.from_local_storages(workspace_id=request.session_id),
)
async for output in Runners.streamed_run_task(task).stream_events():
logger.info(f"Agent Ouput: {output}")
try:
res = await AworldUI.parse_output(output, rich_ui)
for item in res if isinstance(res, list) else [res]:
if isinstance(item, AsyncGenerator):
async for sub_item in item:
yield sub_item
else:
yield item
except Exception as e:
msg = f"Error parsing output: {traceback.format_exc()}"
logger.error(msg)
yield msg
@@ -0,0 +1,286 @@
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 = """\
You are an expert research planner. Your task is to decompose user questions, plan efficient search strategies, and validate completeness.
## Core Responsibilities
1. **Problem Decomposition**: Break complex questions into clear, executable search steps
2. **Strategic Planning**: Design comprehensive research workflows (max 3 steps)
3. **Completeness Validation**: Assess if current information fully answers the user's question
## Planning Guidelines
- Start with broad searches, then narrow to specific details
- Prioritize authoritative sources and recent information
- Convert relative dates using current date: {{current_date}}
- Ensure each search query is clear, complete, and semantically meaningful
- Limit to maximum 2 search steps for efficiency
## CRITICAL: Output Format Requirements
**MANDATORY**: Always output BOTH tags in exact format below:
**If planning needed:**
```
<PLANNING_TAG>
{
"steps": {
"agent_step_1": {
"input": "Specific search query here",
"id": "tool_name_from_available_tools"
},
"agent_step_2": {
"input": "Next search query here",
"id": "tool_name_from_available_tools"
}
},
"dag": ["agent_step_1", "agent_step_2"]
}
</PLANNING_TAG>
<FINAL_ANSWER_TAG>
Brief description of what these steps will accomplish and what information gaps they'll fill.
</FINAL_ANSWER_TAG>
```
**If no planning needed (question fully answered):**
```
<PLANNING_TAG>
{
"steps": {},
"dag": []
}
</PLANNING_TAG>
<FINAL_ANSWER_TAG>
Brief summary confirming the question is fully answered with current information.
</FINAL_ANSWER_TAG>
```
## Format Validation Checklist
✓ Both tags present and correctly named
✓ Valid JSON structure inside PLANNING_TAG
✓ Each step has "input" and "id" fields
✓ Tool IDs match available tools list
✓ DAG array defines execution order
✓ No text between tag sections
## Examples
**Research Planning Example:**
Topic: Analyze China's NEV market trends in 2024
<PLANNING_TAG>
{
"steps": {
"agent_step_1": {
"input": "2024 China NEV market policy updates and growth forecasts",
"id": "search_tool"
},
"agent_step_2": {
"input": "Major challenges facing China NEV industry development 2024",
"id": "search_tool"
}
},
"dag": ["agent_step_1", "agent_step_2"]
}
</PLANNING_TAG>
<FINAL_ANSWER_TAG>
These searches will provide current policy landscape and industry challenges to deliver a comprehensive analysis of China's NEV market trends and obstacles in 2024.
</FINAL_ANSWER_TAG>
**No Further Planning Example:**
Topic: Question already fully answered
<PLANNING_TAG>
</PLANNING_TAG>
<FINAL_ANSWER_TAG>
All required information has been gathered from previous research steps. Ready to synthesize findings.
</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 Markdown structure
User Context:
- {{task}}
Summaries:
{{trajectories}}"""
@@ -0,0 +1,74 @@
import logging
import os
import json
from aworld.cmd.data_model import BaseAWorldAgent, ChatCompletionRequest
from aworld.config.conf import AgentConfig, TaskConfig
from aworld.agents.llm_agent import Agent
from aworld.core.task import Task
from aworld.runner import Runners
logger = logging.getLogger(__name__)
class AWorldAgent(BaseAWorldAgent):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def name(self):
return "Single Agent Demo"
def description(self):
return "Single Agent Demo with search and playwright fetch ability"
async def run(self, prompt: str = None, request: ChatCompletionRequest = None):
llm_provider = os.getenv("LLM_PROVIDER_DEMO", "openai")
llm_model_name = os.getenv("LLM_MODEL_NAME_DEMO")
llm_api_key = os.getenv("LLM_API_KEY_DEMO")
llm_base_url = os.getenv("LLM_BASE_URL_DEMO")
llm_temperature = os.getenv("LLM_TEMPERATURE_DEMO", 0.0)
if not llm_model_name or not llm_api_key or not llm_base_url:
raise ValueError(
"LLM_MODEL_NAME, LLM_API_KEY, LLM_BASE_URL must be set in your envrionment variables"
)
agent_config = AgentConfig(
llm_provider=llm_provider,
llm_model_name=llm_model_name,
llm_api_key=llm_api_key,
llm_base_url=llm_base_url,
llm_temperature=llm_temperature,
)
path_cwd = os.path.dirname(os.path.abspath(__file__))
mcp_path = os.path.join(path_cwd, "mcp.json")
with open(mcp_path, "r") as f:
mcp_config = json.load(f)
super_agent = Agent(
conf=agent_config,
name="🙋🏻‍♂️ Single Agent Demo",
system_prompt="""You are a Super Search Agent, your goal is to accomplish the ultimate task following the instructions.""",
mcp_config=mcp_config,
# mcp_servers=mcp_config.get("mcpServers", {}).keys(),
# mcp_servers=["aworldsearch-server", "aworld-playwright"],
mcp_servers=["google-pse-search", "aworld-playwright"],
feedback_tool_result=True,
)
if prompt is None and request is not None:
prompt = request.messages[-1].content
task = Task(
input=prompt,
agent=super_agent,
conf=TaskConfig(max_steps=20),
session_id=request.session_id,
endless_threshold=50,
)
with open("data/output.txt", "w") as f:
f.write(f"AGENT START: agent={self.name()}, prompt: {prompt}\n")
async for output in Runners.streamed_run_task(task).stream_events():
f.write(f"Agent {self.name()} received output: {output}\n")
yield output
@@ -0,0 +1,47 @@
{
"mcpServers": {
"google-pse-search": {
"command": "npx",
"args": [
"-y",
"@adenot/mcp-google-search"
],
"env": {
"GOOGLE_API_KEY": "${GOOGLE_API_KEY}",
"GOOGLE_SEARCH_ENGINE_ID": "${GOOGLE_SEARCH_ENGINE_ID}"
}
},
"aworld-playwright": {
"command": "npx",
"args": [
"playwright-mcp-aworld",
"--isolated"
],
"env": {
"OSS_ENDPOINT": "${OSS_ENDPOINT}",
"OSS_ACCESS_KEY_ID": "${OSS_ACCESS_KEY_ID}",
"OSS_ACCESS_KEY_SECRET": "${OSS_ACCESS_KEY_SECRET}",
"OSS_BUCKET": "${OSS_BUCKET}",
"PLAYWRIGHT_TIMEOUT": "120000",
"SESSION_REQUEST_CONNECT_TIMEOUT": "120"
}
},
"fetch": {
"command": "uvx",
"args": [
"-i",
"https://mirrors.aliyun.com/pypi/simple/",
"mcp-server-fetch",
"--ignore-robots-txt"
]
},
"time": {
"command": "uvx",
"args": [
"mcp-server-time",
"--local-timezone",
"Asia/Shanghai"
]
}
}
}
@@ -0,0 +1,238 @@
import asyncio
import json
import logging
import os
import sys
from typing import List, Dict, Any, Optional, Union
import aiohttp
from mcp.server import FastMCP
from mcp.types import TextContent
from pydantic import Field
mcp = FastMCP("aworldsearch-server")
async def search_single(query: str, num: int = 5) -> Optional[Dict[str, Any]]:
"""Execute a single search query, returns None on error"""
try:
url = os.getenv('AWORLD_SEARCH_URL')
searchMode = os.getenv('AWORLD_SEARCH_SEARCHMODE')
source = os.getenv('AWORLD_SEARCH_SOURCE')
domain = os.getenv('AWORLD_SEARCH_DOMAIN')
uid = os.getenv('AWORLD_SEARCH_UID')
if not url or not searchMode or not source or not domain:
logging.warning(f"Query failed: url, searchMode, source, domain parameters incomplete")
return None
headers = {
'Content-Type': 'application/json'
}
data = {
"domain": domain,
"extParams": {},
"page": 0,
"pageSize": num,
"query": query,
"searchMode": searchMode,
"source": source,
"userId": uid
}
async with aiohttp.ClientSession() as session:
try:
async with session.post(url, headers=headers, json=data) as response:
if response.status != 200:
logging.warning(f"Query failed: {query}, status code: {response.status}")
return None
result = await response.json()
return result
except aiohttp.ClientError:
logging.warning(f"Request error: {query}")
return None
except Exception:
logging.warning(f"Query exception: {query}")
return None
def filter_valid_docs(result: Optional[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""Filter valid document results, returns empty list if input is None"""
if result is None:
return []
try:
valid_docs = []
# Check success field
if not result.get("success"):
return valid_docs
# Check searchDocs field
search_docs = result.get("searchDocs", [])
if not search_docs:
return valid_docs
# Extract required fields
required_fields = ["title", "docAbstract", "url", "doc"]
for doc in search_docs:
# Check if all required fields exist and are not empty
is_valid = True
for field in required_fields:
if field not in doc or not doc[field]:
is_valid = False
break
if is_valid:
# Keep only required fields
filtered_doc = {field: doc[field] for field in required_fields}
valid_docs.append(filtered_doc)
return valid_docs
except Exception:
return []
@mcp.tool(description="Search based on the user's input query list")
async def search(
query_list: List[str] = Field(
description="List format, queries to search for"
),
num: int = Field(
5,
description="Maximum number of results per query, default is 5, please keep the total results within 15"
)
) -> Union[str, TextContent]:
"""Execute search main function, supports single query or query list"""
try:
# Get configuration from environment variables
env_total_num = os.getenv('AWORLD_SEARCH_TOTAL_NUM')
if env_total_num and env_total_num.isdigit():
# Force override input num parameter with environment variable
num = int(env_total_num)
# If no queries provided, return empty list
if not query_list:
# Initialize TextContent with additional parameters
return TextContent(
type="text",
text="", # Empty string instead of None
**{"metadata": {}} # Pass as additional fields
)
# When query count is >= 3 or slice_num is set, use corresponding value
slice_num = os.getenv('AWORLD_SEARCH_SLICE_NUM')
if slice_num and slice_num.isdigit():
actual_num = int(slice_num)
else:
actual_num = 2 if len(query_list) >= 3 else num
# Execute all queries in parallel
tasks = [search_single(q, actual_num) for q in query_list]
raw_results = await asyncio.gather(*tasks)
# Filter and merge results
all_valid_docs = []
for result in raw_results:
valid_docs = filter_valid_docs(result)
all_valid_docs.extend(valid_docs)
# If no valid results found, return empty list
if not all_valid_docs:
# Initialize TextContent with additional parameters
return TextContent(
type="text",
text="", # Empty string instead of None
**{"metadata": {}} # Pass as additional fields
)
# Format results as JSON
result_json = json.dumps(all_valid_docs, ensure_ascii=False)
# Create dictionary structure directly
combined_query = ",".join(query_list)
search_items = []
# Use a dictionary to deduplicate by URL
url_dict = {}
for doc in all_valid_docs:
url = doc.get("url", "")
if url not in url_dict:
url_dict[url] = {
"title": doc.get("title", ""),
"url": url,
"snippet": doc.get("doc", "")[:100] + "..." if len(doc.get("doc", "")) > 100 else doc.get("doc", ""),
"content": doc.get("doc", "") # Map doc field to content
}
# Convert dictionary values to list
search_items = list(url_dict.values())
search_output_dict = {
"artifact_type": "WEB_PAGES",
"artifact_data": {
"query": combined_query,
"results": search_items
}
}
# Log results
logging.info(f"Completed {len(query_list)} queries, found {len(all_valid_docs)} valid documents")
# Initialize TextContent with additional parameters
return TextContent(
type="text",
text=result_json,
**{"metadata": search_output_dict} # Pass processed data as metadata
)
except Exception as e:
# Handle errors
logging.error(f"Search error: {e}")
# Initialize TextContent with additional parameters
return TextContent(
type="text",
text="", # Empty string instead of None
**{"metadata": {}} # Pass as additional fields
)
def main():
from dotenv import load_dotenv
load_dotenv(override=True)
print("Starting Audio MCP aworldsearch-server...", file=sys.stderr)
mcp.run(transport="stdio")
# Make the module callable
def __call__():
"""
Make the module callable for uvx.
This function is called when the module is executed directly.
"""
main()
sys.modules[__name__].__call__ = __call__
if __name__ == "__main__":
main()
# if __name__ == "__main__":
# # Configure logging
# logging.basicConfig(
# level=logging.INFO,
# format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
# )
#
#
# # Test single query
# # asyncio.run(search("Alibaba financial report"))
#
# # Test multiple queries
# test_queries = ["Alibaba financial report", "Tencent financial report", "Baidu financial report"]
# asyncio.run(search(query_list=test_queries))
@@ -0,0 +1,92 @@
import logging
import os
import json
from aworld.cmd.data_model import BaseAWorldAgent, ChatCompletionRequest
from aworld.config.conf import AgentConfig, TaskConfig
from aworld.agents.llm_agent import Agent
from aworld.core.agent.swarm import GraphBuildType, Swarm
from aworld.core.task import Task
from aworld.runner import Runners
from .prompt import *
logger = logging.getLogger(__name__)
class AWorldAgent(BaseAWorldAgent):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def name(self):
return "Team Agent[AgentAsToolCall]"
def description(self):
return "Team Agent with fetch and time mcp server"
async def run(self, prompt: str = None, request: ChatCompletionRequest = None):
llm_provider = os.getenv("LLM_PROVIDER_TEAM", "openai")
llm_model_name = os.getenv("LLM_MODEL_NAME_TEAM")
llm_api_key = os.getenv("LLM_API_KEY_TEAM")
llm_base_url = os.getenv("LLM_BASE_URL_TEAM")
llm_temperature = os.getenv("LLM_TEMPERATURE_TEAM", 0.0)
if not llm_model_name or not llm_api_key or not llm_base_url:
raise ValueError(
"LLM_MODEL_NAME, LLM_API_KEY, LLM_BASE_URL must be set in your envrionment variables"
)
agent_config = AgentConfig(
llm_provider=llm_provider,
llm_model_name=llm_model_name,
llm_api_key=llm_api_key,
llm_base_url=llm_base_url,
llm_temperature=llm_temperature,
)
path_cwd = os.path.dirname(os.path.abspath(__file__))
mcp_path = os.path.join(path_cwd, "mcp.json")
with open(mcp_path, "r") as f:
mcp_config = json.load(f)
plan_agent = Agent(
conf=agent_config,
name="Team-Plan-Agent",
system_prompt=plan_agent_sys_prompt,
)
google_pse_search_agent = Agent(
conf=agent_config,
name="Google-PSE-Search-Agent",
system_prompt=google_pse_search_sys_prompt,
mcp_config=mcp_config,
mcp_servers=["google-pse-search"],
)
summary_agent = Agent(
conf=agent_config,
name="Summary-Agent",
system_prompt=summary_agent_sys_prompt,
)
# default is sequence swarm mode
swarm = Swarm(
plan_agent,
google_pse_search_agent,
summary_agent,
max_steps=10,
build_type=GraphBuildType.TEAM,
)
if prompt is None and request is not None:
prompt = request.messages[-1].content
task = Task(
input=prompt,
swarm=swarm,
conf=TaskConfig(max_steps=20),
session_id=request.session_id,
endless_threshold=50,
)
async for output in Runners.streamed_run_task(task).stream_events():
logger.info(f"Agent Ouput: {output}")
yield output
@@ -0,0 +1,63 @@
{
"mcpServers": {
"google-pse-search": {
"command": "npx",
"args": [
"-y",
"@adenot/mcp-google-search"
],
"env": {
"GOOGLE_API_KEY": "${GOOGLE_API_KEY}",
"GOOGLE_SEARCH_ENGINE_ID": "${GOOGLE_SEARCH_ENGINE_ID}"
}
},
"aworldsearch-server": {
"command": "python",
"args": [
"agent_deploy/demo_agent/mcp_servers/aworldsearch_server.py"
],
"env": {
"SESSION_REQUEST_CONNECT_TIMEOUT": "60",
"AWORLD_SEARCH_URL": "${AWORLD_SEARCH_URL}",
"AWORLD_SEARCH_TOTAL_NUM": "${AWORLD_SEARCH_TOTAL_NUM}",
"AWORLD_SEARCH_SLICE_NUM": "${AWORLD_SEARCH_SLICE_NUM}",
"AWORLD_SEARCH_DOMAIN": "${AWORLD_SEARCH_DOMAIN}",
"AWORLD_SEARCH_SEARCHMODE": "${AWORLD_SEARCH_SEARCHMODE}",
"AWORLD_SEARCH_SOURCE": "${AWORLD_SEARCH_SOURCE}",
"AWORLD_SEARCH_UID": "${AWORLD_SEARCH_UID}"
}
},
"fetch": {
"command": "uvx",
"args": [
"-i",
"https://mirrors.aliyun.com/pypi/simple/",
"mcp-server-fetch",
"--ignore-robots-txt"
]
},
"time": {
"command": "uvx",
"args": [
"mcp-server-time",
"--local-timezone",
"Asia/Shanghai"
]
},
"aworld-playwright": {
"command": "npx",
"args": [
"playwright-mcp-aworld",
"--isolated"
],
"env": {
"OSS_ENDPOINT": "${OSS_ENDPOINT}",
"OSS_ACCESS_KEY_ID": "${OSS_ACCESS_KEY_ID}",
"OSS_ACCESS_KEY_SECRET": "${OSS_ACCESS_KEY_SECRET}",
"OSS_BUCKET": "${OSS_BUCKET}",
"PLAYWRIGHT_TIMEOUT": "120000",
"SESSION_REQUEST_CONNECT_TIMEOUT": "120"
}
}
}
}
@@ -0,0 +1,36 @@
plan_agent_sys_prompt = """\
You are a Team-Plan-Agent, Your goal is to make a plan to accomplish the task.
"""
google_pse_search_sys_prompt = """\
You are Google-PSE-Search-Agent, your goal is to use the Google PSE Search to search the web.
"""
aworld_playwright_sys_prompt = """\
You are Aworld-Playwright-Agent, your goal is to use the Aworld Playwright Tool to search the web.
Instructions:
- You must accomplish the task using the following steps:
- STEP 1: Choose which search engine to search the web, for example: google, bing, etc.
- STEP 2: Generate a search query based on the user's request
- STEP 3: Call tool aworld-playwright to open search engine search engine page.
- STEP 4: Click each search item to get the item content.
- STEP 5: Format the search result to the following format:
```json
[
{
"title": <title>,
"url": <url>,
"content": <content>
}
]
```
"""
summary_agent_sys_prompt = """\
You are Summary-Agent, your goal is to summarize the result of the search task following the instructions below.
Instructions:
- You MUST list the content reference from the search result
"""