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,105 @@
import os
import random
import sys
import json
from pathlib import Path
from typing import Optional
import unittest
from aworld.core.context.prompts.string_prompt_template import StringPromptTemplate
# Add the project root to Python path
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
from aworld.core.context.base import Context
from aworld.config.conf import AgentConfig, ContextRuleConfig, ModelConfig
from aworld.agents.llm_agent import Agent
from aworld.runner import Runners
from aworld.core.agent.swarm import Swarm, TeamSwarm
from aworld.core.task import Task
# Set environment variables
os.environ["LLM_API_KEY"] = "lm-studio"
os.environ["LLM_BASE_URL"] = "http://localhost:1234/v1"
os.environ["LLM_MODEL_NAME"] = "qwen/qwen3-1.7b"
def assertIsNotNone(obj, msg=None):
"""Assert that an object is not None"""
if obj is None:
standard_msg = f"{obj} is None"
raise Exception(standard_msg)
def assertEqual(first, second, msg=None):
"""Assert that two objects are equal"""
if first != second:
standard_msg = f"{first} != {second}"
raise Exception(standard_msg)
def assertTrue(expr, msg=None):
"""Assert that an expression is True"""
if not expr:
standard_msg = f"{expr} is not True"
raise Exception(standard_msg)
def assertIn(member, container, msg=None):
"""Assert that a member is in a container"""
if member not in container:
standard_msg = f"{member} not found in {container}"
raise Exception(standard_msg)
def assertIsInstance(obj, cls, msg=None):
"""Assert that an object is an instance of a class"""
if not isinstance(obj, cls):
standard_msg = f"{obj} is not an instance of {cls}"
raise Exception(standard_msg)
def init_agent(config_type: str = "1",
system_prompt_template: Optional[StringPromptTemplate] = None,
context_rule: ContextRuleConfig = None,
name: str = "my_agent" + str(random.randint(0, 1000000))):
if config_type == "1":
conf = AgentConfig(
llm_model_name=os.environ["LLM_MODEL_NAME"],
llm_base_url=os.environ["LLM_BASE_URL"],
llm_api_key=os.environ["LLM_API_KEY"]
)
else:
conf = AgentConfig(
llm_config=ModelConfig(
llm_model_name=os.environ["LLM_MODEL_NAME"],
llm_base_url=os.environ["LLM_BASE_URL"],
llm_api_key=os.environ["LLM_API_KEY"]
)
)
return Agent(
conf=conf,
name=name,
system_prompt="You are a helpful assistant.",
system_prompt_template=system_prompt_template,
context_rule=context_rule
)
def run_agent(input, agent: Agent):
swarm = Swarm(agent, max_steps=1)
return Runners.sync_run(
input=input,
swarm=swarm
)
def run_multi_agent_as_team(input, agent1: Agent, agent2: Agent):
swarm = TeamSwarm(agent1, agent2, max_steps=1)
return Runners.sync_run(
input=input,
swarm=swarm
)
def run_task(agent: Agent, context: Context = None, input: str = "What is an agent."):
swarm = Swarm(agent, max_steps=1)
task = Task(input=input,
swarm=swarm, context=context)
return Runners.sync_run_task(task)
@@ -0,0 +1,110 @@
import os
import sys
from pathlib import Path
import unittest
# Add the project root to Python path
project_root = Path(__file__).parent.parent.parent
sys.path.insert(0, str(project_root))
from aworld.config.conf import AgentConfig, ModelConfig, ContextRuleConfig, OptimizationConfig, LlmCompressionConfig
from aworld.core.context.processor import CompressionResult, CompressionType
from aworld.core.context.processor.llm_compressor import LLMCompressor
from aworld.core.context.processor.prompt_processor import PromptProcessor
class TestPromptCompressor(unittest.TestCase):
"""Test cases for PromptCompressor.compress_batch function"""
def test_compress_batch_basic(self):
compressor = LLMCompressor(
llm_config=ModelConfig(
llm_model_name=os.environ["LLM_MODEL_NAME"],
llm_base_url=os.environ["LLM_BASE_URL"],
llm_api_key=os.environ["LLM_API_KEY"],
)
)
# Test data
contents = [
"[SYSTEM]You are a helpful assistant.\n[USER]This is the first long text content that needs compression. This is the first long text content that needs compression.",
]
# Execute compress_batch
results = compressor.compress_batch(contents)
# Assertions
for result in results:
self.assertIsInstance(result, CompressionResult)
self.assertEqual(result.compression_type, CompressionType.LLM_BASED)
self.assertTrue(
'This is the first long text content that needs compression. This is the first long text content that needs compression.' not in result.compressed_content)
def test_compress_messages(self):
"""Test compress_messages function from PromptProcessor"""
# Create context rule with compression enabled
context_rule = ContextRuleConfig(
optimization_config=OptimizationConfig(
enabled=True,
max_token_budget_ratio=0.8
),
llm_compression_config=LlmCompressionConfig(
enabled=True,
trigger_compress_token_length=10, # Low threshold to trigger compression
compress_model=ModelConfig(
llm_model_name=os.environ["LLM_MODEL_NAME"],
llm_base_url=os.environ["LLM_BASE_URL"],
llm_api_key=os.environ["LLM_API_KEY"],
)
)
)
# Create prompt processor with context_rule and model_config
processor = PromptProcessor(context_rule=context_rule, model_config=ModelConfig(
llm_model_name=os.environ["LLM_MODEL_NAME"],
llm_base_url=os.environ["LLM_BASE_URL"],
llm_api_key=os.environ["LLM_API_KEY"],
))
# Test messages with repeated content that needs compression
messages = [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "This is the first long text content that needs compression. This is the first long text content that needs compression."
},
{
"role": "assistant",
"content": "I understand you want me to help with compression."
}
]
# Execute compress_messages
compressed_messages = processor.compress_messages(messages)
# Assertions
self.assertIsInstance(compressed_messages, list)
self.assertEqual(len(compressed_messages), len(messages))
# Find the user message and verify it was processed
user_message = None
for msg in compressed_messages:
if msg.get("role") == "user":
user_message = msg
break
self.assertIsNotNone(user_message)
# The original repeated text should be compressed
original_content = "This is the first long text content that needs compression. This is the first long text content that needs compression."
self.assertNotEqual(user_message["content"], original_content)
# The compressed content should be shorter than original
self.assertLess(len(user_message["content"]), len(original_content))
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,170 @@
import asyncio
import os
import sys
from pathlib import Path
import unittest
# Add the project root to Python path
project_root = Path(__file__).parent.parent.parent
sys.path.insert(0, str(project_root))
from aworld.core.context.session import Session
from aworld.core.agent.swarm import Swarm
from tests.base_test import assertEqual, assertIn, assertIsInstance, assertIsNotNone, assertTrue, run_multi_agent_as_team, run_task
from aworld.runners.hook.hook_factory import HookFactory
from aworld.core.context.base import Context
from aworld.config.conf import AgentConfig, ContextRuleConfig, ModelConfig, OptimizationConfig, LlmCompressionConfig
from aworld.agents.llm_agent import Agent
from aworld.core.task import Task
from tests.base_test import init_agent, run_agent, run_multi_agent_as_team
class TestContextManagement(unittest.TestCase):
# def test_save_and_reload(self):
# context = Context()
# context.context_info.set("hello", "world")
# task = Task(input="""What is an agent.""",
# swarm=Swarm(init_agent("1"), max_steps=1), context=context)
# task.session_id = "1"
# context.session = Session(session_id="1")
# context.set_task(task)
# context_manager = ContextManager()
# checkpoint = asyncio.run(context_manager.save(context))
# session_id = context.session_id
# context = asyncio.run(context_manager.reload(session_id))
# assertEqual(context.context_info.get("hello"), "world")
def test_default_context_configuration(self):
mock_agent = init_agent("1")
response = run_agent(
input="""What is an agent. describe within 20 words""", agent=mock_agent)
assertIsNotNone(response.answer)
assertEqual(
mock_agent.conf.llm_config.llm_model_name, os.environ["LLM_MODEL_NAME"])
# Test default context rule behavior
assertIsNotNone(mock_agent.context_rule)
assertIsNotNone(
mock_agent.context_rule.optimization_config)
def test_custom_context_configuration(self):
"""Test custom context configuration (README Configuration example)"""
# Create custom context rules
mock_agent = init_agent(context_rule=ContextRuleConfig(
optimization_config=OptimizationConfig(
enabled=True,
max_token_budget_ratio=0.00015
),
llm_compression_config=LlmCompressionConfig(
enabled=True,
trigger_compress_token_length=100,
compress_model=ModelConfig(
llm_model_name=os.environ["LLM_MODEL_NAME"],
llm_base_url=os.environ["LLM_BASE_URL"],
llm_api_key=os.environ["LLM_API_KEY"],
)
)
))
response = run_agent(
input="""describe What is an agent in details""", agent=mock_agent)
assertIsNotNone(response.answer)
# Test configuration values
assertTrue(
mock_agent.context_rule.optimization_config.enabled)
assertTrue(
mock_agent.context_rule.llm_compression_config.enabled)
def test_multi_agent_state_trace(self):
class StateModifyAgent(Agent):
async def async_policy(self, observation, info=None, **kwargs):
result = await super().async_policy(observation, info, **kwargs)
self.context.context_info.set('policy_executed', True)
return result
class StateTrackingAgent(Agent):
async def async_policy(self, observation, info=None, **kwargs):
result = await super().async_policy(observation, info, **kwargs)
assert self.context.context_info.get('policy_executed', True)
return result
# Create custom agent instance
custom_agent = StateModifyAgent(
conf=AgentConfig(
llm_model_name=os.environ["LLM_MODEL_NAME"],
llm_base_url=os.environ["LLM_BASE_URL"],
llm_api_key=os.environ["LLM_API_KEY"]
),
name="state_modify_agent",
system_prompt="You are a Python expert who provides detailed and practical answers.",
agent_prompt="You are a Python expert who provides detailed and practical answers.",
)
# Create a second agent for multi-agent testing
second_agent = StateTrackingAgent(
conf=AgentConfig(
llm_model_name=os.environ["LLM_MODEL_NAME"],
llm_base_url=os.environ["LLM_BASE_URL"],
llm_api_key=os.environ["LLM_API_KEY"]
),
name="state_tracking_agent",
system_prompt="You are a helpful assistant.",
agent_prompt="You are a helpful assistant.",
)
response = run_multi_agent_as_team(
input="What is an agent. describe within 20 words",
agent1=custom_agent,
agent2=second_agent
)
assertIsNotNone(response.answer)
# Verify state changes after execution
assertTrue(custom_agent.context.context_info.get('policy_executed', True))
def test_multi_task_state_trace(self):
context = Context()
task = Task(input="What is an agent.", context=context)
new_context = task.context.deep_copy()
new_context.context_info.update({"hello": "world"})
run_task(context=new_context, agent=init_agent("1"))
assertEqual(new_context.context_info.get("hello"), "world")
task.context.merge_context(new_context)
assertEqual(task.context.context_info.get("hello"), "world")
def test_hook_registration(self):
from tests.runners.hook.llm_hook import TestPreLLMHook, TestPostLLMHook
"""Test hook registration and retrieval"""
# Test that hooks are registered in _cls attribute
assertIn("TestPreLLMHook", HookFactory._cls)
assertIn("TestPostLLMHook", HookFactory._cls)
# Test hook creation using __call__ method
pre_hook = HookFactory("TestPreLLMHook")
post_hook = HookFactory("TestPostLLMHook")
assertIsInstance(pre_hook, TestPreLLMHook)
assertIsInstance(post_hook, TestPostLLMHook)
def test_hook_execution(self):
mock_agent = init_agent("1")
response = run_agent(
input="""What is an agent. describe within 20 words""", agent=mock_agent)
assertIsNotNone(response.answer)
def test_task_context_transfer(self):
mock_agent = init_agent("1")
context = Context()
context.context_info.update({"task": "What is an agent."})
run_task(context=context, agent=mock_agent)
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,125 @@
# Add the project root to Python path
from pathlib import Path
import sys
import unittest
project_root = Path(__file__).parent.parent.parent
print(project_root)
sys.path.insert(0, str(project_root))
from aworld.agents.llm_agent import Agent
from tests.base_test import assertEqual, init_agent, run_agent, run_task
from aworld.core.context.base import Context
from aworld.core.context.prompts.dynamic_variables import create_simple_field_getter, format_ordered_dict_json, \
get_field_values_from_list, get_value_by_path
from aworld.core.context.prompts.string_prompt_template import StringPromptTemplate
class TestPromptTemplate(unittest.TestCase):
def test_dynamic_variables(self):
context = Context()
context.context_info.update({"task": "chat"})
# Test dot separator
value_dot = get_value_by_path(context, "context_info.task")
assert "chat" == value_dot
# Test slash separator
value_slash = get_value_by_path(context, "context_info/task")
assert "chat" == value_slash
def test_formatted_field_getter(self):
context = Context()
value = {"steps": [1, 2, 3]}
context.trajectories.update(value)
getter = create_simple_field_getter(field_path="trajectories", default="default_value")
result = getter(context=context)
assert "steps" in value
# test default format function
assert "OrderedDict" not in result
# Test formatted field getter with processor
getter = create_simple_field_getter(field_path="trajectories", default="default_value",
processor=format_ordered_dict_json)
result = getter(context=context)
assert "steps" in result
def test_multiple_field_getters(self):
context = Context()
context.context_info.update({"task": "chat"})
context.trajectories.update({"steps": [1, 2, 3]})
field_paths = ["context_info.task", "trajectories.steps"]
result = get_field_values_from_list(context=context, field_paths=field_paths)
assert result["context_info_task"] == "chat"
assert result["trajectories_steps"] == "[1, 2, 3]"
def test_string_prompt_template(self):
# Use proper dot notation for nested field access
template = StringPromptTemplate.from_template(
"Hello {{name}}, welcome to {{place}}! Task: {{task}} Age: {{age}}",
partial_variables={"age": "1"})
assert "name" in template.input_variables
assert "place" in template.input_variables
assert "task" in template.input_variables
context = Context()
context.context_info.update({"task": "chat"})
# Pass task as a direct parameter since template expects it
result = template.format(context=context, name="Alice", place="AWorld", task="chat")
assert result == "Hello Alice, welcome to AWorld! Task: chat Age: 1"
def test_enhanced_field_values_basic(self):
context = Context()
context.context_info.update({"task": "chat"})
# Test retrieving both time variables and context fields
result = get_field_values_from_list(
context=context,
field_paths=["current_time", "context_info.task"],
default="not_found"
)
# Verify context field retrieved
assert result["context_info_task"] == "chat"
# Verify time variable retrieved (should be in HH:MM:SS format)
assert ":" in result["current_time"]
assert len(result["current_time"].split(":")) == 3
def test_undefined_system_prompt_template(self):
agent = init_agent()
agent._log_messages = lambda messages: assertEqual(messages[0]['content'], "You are a helpful assistant.")
result = run_task(
input="What is the weather in Beijing?",
agent=agent
)
assert result is not None
def test_custom_system_prompt_template(self):
context = Context()
context.context_info.set("name", "Qwen")
context.context_info.set("plan", [{"input": "query weather in Beijing"}])
system_prompt_template = StringPromptTemplate.from_template(
"Hello {{context_info.name}}, you are a {{role}}, {{context_info.plan}}",
partial_variables={"role": "assistant", "context_info.plan": lambda ob: "please " + ob[0]['input']})
agent = init_agent(
system_prompt_template=system_prompt_template,
)
agent._log_messages = lambda messages: assertEqual(messages[0]['content'], "Hello Qwen, you are a assistant, please query weather in Beijing")
result = run_task(
input="What is the weather in Beijing?",
agent=agent,
context=context
)
assert result is not None
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,3 @@
# gym demo
Example of gym operation.
@@ -0,0 +1,2 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
@@ -0,0 +1,34 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
from typing import Any, Dict, Union, List
from examples.common.tools.common import Tools
from aworld.config.conf import AgentConfig, ConfigDict
from aworld.agents.llm_agent import Agent
from aworld.core.common import Observation, ActionModel
class GymDemoAgent(Agent):
"""Example agent"""
def __init__(self, conf: Union[Dict[str, Any], ConfigDict, AgentConfig], **kwargs):
super(GymDemoAgent, self).__init__(conf=conf, **kwargs)
def policy(self, observation: Observation, info: Dict[str, Any] = {}, **kwargs) -> Union[
List[ActionModel], None]:
import numpy as np
env_id = observation.info.get('env_id')
if env_id and env_id != 'CartPole-v1':
raise ValueError("Unsupported env")
res = np.random.randint(2)
action = [ActionModel(agent_name=self.id(), tool_name=Tools.GYM.value, action_name="play", params={"result": res})]
if observation.info.get("done"):
self._finished = True
return action
async def async_policy(self, observation: Observation, info: Dict[str, Any] = {}, **kwargs) -> Union[
List[ActionModel], None]:
return self.policy(observation, info, **kwargs)
@@ -0,0 +1,23 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import asyncio
from aworld.config import RunConfig
from aworld.config.conf import AgentConfig
from aworld.core.task import Task
from aworld.runner import Runners
from tests.gym_demo.agent import GymDemoAgent as GymAgent
async def main():
agent = GymAgent(name="gym_agent", conf=AgentConfig(), tool_names=["gym"], feedback_tool_result=True)
# It can also be used `ToolFactory` for simplification.
task = Task(agent=agent,
tools_conf={"gym": {"env_id": "CartPole-v1", "render_mode": "human", "render": True, "use_async": True}})
res = await Runners.run_task(task=task, run_conf=RunConfig())
if __name__ == "__main__":
# We use it as a showcase to demonstrate the framework's scalability.
asyncio.run(main())
@@ -0,0 +1,125 @@
# YAML-based Config Guide
## Overview
Use a single YAML file to define multiple Agents and an optional Swarm topology. This loader supports two kinds of placeholders:
- `${ENV_VAR}`: Values come from system environment variables
- `${vars.KEY}`: Values come from the `vars` section of the same YAML file
When a field value is exactly a single placeholder like `${vars.DEFAULT_TEMPERATURE}`, the loader preserves the original type (e.g., float) instead of converting it to a string. This avoids type errors in LLM parameters such as `temperature`.
## Files in this folder
- `agents.yaml`: Example YAML configuration with environment and in-file variables
- `load_from_yaml.py`: Minimal runner that loads the YAML and executes a swarm
## Quick Start
1) Set your environment variables
- PowerShell: `$env:OPENAI_API_KEY="your-openai-api-key" ; $env:OPENROUTER_API_KEY="your-openrouter-api-key"`
- macOS/Linux: `export OPENAI_API_KEY="your-openai-api-key" ; export OPENROUTER_API_KEY="your-openrouter-api-key"`
2) Run the example
- `python examples/load_config/load_from_yaml.py`
## YAML Schema
Top-level keys:
- `vars`: Optional. In-file variables used by `${vars.KEY}`
- `agents`: Required. Map of agent name -> agent configuration
- `swarm`: Optional. Defines the topology (workflow, handoff, or team)
Example (abridged):
```yaml
vars:
DEFAULT_TEMPERATURE: 0.1
OPENAI_URL: https://api.openai.com/v1
OPENROUTER_URL: https://openrouter.ai/api/v1
agents:
researcher:
system_prompt: "You specialize at researching."
llm_config:
llm_provider: openai
llm_model_name: gpt-4o
llm_api_key: ${OPENAI_API_KEY} # from system env
llm_base_url: ${vars.OPENAI_URL} # from vars section
llm_temperature: ${vars.DEFAULT_TEMPERATURE} # from vars section
summarizer:
system_prompt: "You specialize at summarizing."
llm_config:
llm_provider: openai
llm_model_name: google/gemini-2.5-pro
llm_api_key: ${OPENROUTER_API_KEY} # from system env
llm_base_url: ${vars.OPENROUTER_URL} # from vars section
llm_temperature: ${vars.DEFAULT_TEMPERATURE} # from vars section
swarm:
type: workflow
order: [researcher, summarizer]
```
## Variable Substitution
- System env: `${OPENAI_API_KEY}`
- In-file vars: `${vars.DEFAULT_TEMPERATURE}`
Type-preserving rule:
- If the entire value is exactly `${vars.KEY}`, the raw value from `vars` is used with its original type (float/int/bool/string)
- If `${vars.KEY}` appears inside a longer string, it is replaced as text (string interpolation)
Tip: For numeric LLM parameters (like `llm_temperature`), prefer defining numbers in `vars` without quotes (e.g., `0.1`, not `"0.1"`).
## Swarm Topologies
- `workflow`
- Execute agents in the given `order`
- Example: `order: [researcher, summarizer]`
- `handoff`
- Use `edges: [[left, right], ...]` to define agent handoffs
- `team`
- Define a `root` agent and `members: [ ... ]`
If `swarm` is omitted, the loader defaults to a workflow in the order agents are declared in YAML.
## Running from Python
```python
from aworld.config.agent_loader import load_swarm_from_yaml
from aworld.runner import Runners
swarm, agents = load_swarm_from_yaml("examples/load_config/agents.yaml")
result = Runners.sync_run(
input="Tell me a complete history about the universe",
swarm=swarm,
)
```
Access a specific agent if needed:
```python
summarizer = agents["summarizer"]
```
## Advanced: YAML anchors and merge keys (optional)
You can also use YAML anchors/aliases/merge keys to reuse blocks within the same file:
```yaml
llm_defaults: &llm_defaults
llm_provider: openai
llm_temperature: 0.1
agents:
a:
llm_config:
<<: *llm_defaults # merge default fields
llm_model_name: gpt-4o
```
Note: Anchors are structural reuse (not string interpolation). Use `${vars.KEY}` for string placeholders.
## Troubleshooting
- Temperature type error (e.g., cannot unmarshal string into float64)
- Ensure the value comes from `${vars.KEY}` as a full value and that the `vars` value is a number (unquoted). The loader preserves numeric types on full-value substitution.
- Placeholders not replaced
- Missing environment variables or missing `vars.KEY`. Check the comments in YAML and set the needed values.
- Import error for loader
- Make sure you are running against the project source (e.g., `pip install -e .`) or your `PYTHONPATH` includes the project root.
## API Reference
- `load_agents_from_yaml(path) -> Dict[str, Agent]`
- Load agents only
- `load_swarm_from_yaml(path) -> Tuple[Swarm, Dict[str, Agent]]`
- Load agents and build a swarm based on the `swarm` section (or default workflow)
This loader reuses the existing Pydantic configuration models under `aworld.config.conf` and does not add new dependencies.
@@ -0,0 +1,34 @@
# Example agents configuration for YAML-based loading
# Two types of variable substitution:
# 1. ${ENV_VAR} - from system environment variables
# 2. ${vars.KEY} - from the 'vars' section in this YAML file
vars: # Internal variables (file-level)
DEFAULT_TEMPERATURE: 0.1
OPENAI_URL: https://api.openai.com/v1
OPENROUTER_URL: https://openrouter.ai/api/v1
agents:
researcher:
system_prompt: "You specialize at researching."
llm_config:
llm_provider: openai
llm_model_name: gpt-4o
llm_api_key: ${OPENAI_API_KEY} # from system env
llm_base_url: ${vars.OPENAI_URL} # from vars section
llm_temperature: ${vars.DEFAULT_TEMPERATURE} # from vars section
summarizer:
system_prompt: "You specialize at summarizing."
llm_config:
llm_provider: openai
llm_model_name: gpt-5
llm_api_key: ${OPENROUTER_API_KEY} # from system env
llm_base_url: ${vars.OPENROUTER_URL} # from vars section
llm_temperature: ${vars.DEFAULT_TEMPERATURE} # from vars section
swarm:
type: workflow
order: [researcher, summarizer]
@@ -0,0 +1,21 @@
# coding: utf-8
# Example: load agents and swarm from a YAML file and run
from aworld.config.agent_loader import load_agents_from_yaml, load_swarm_from_yaml
from aworld.runner import Runners
if __name__ == "__main__":
# You can change the config path as needed
swarm, agents = load_swarm_from_yaml("examples/load_config/agents.yaml")
# Access a specific agent if needed
summarizer = agents["summarizer"]
# Run with the constructed swarm
result = Runners.sync_run(
input="hello who are you?",
swarm=swarm,
)
print("Result:", result)
@@ -0,0 +1,52 @@
# MCP Examples
This directory contains examples and demos for using MCP (Model Context Protocol) tools and servers within the AWorld framework.
These examples showcase how to build, extend, and interact with various MCP-enabled services, including virtual file systems, calculators, media processing, and more.
## Already cases
- **BFCL/**
Demonstrates Basic Function Call Learning (BFCL) using a virtual file system (GorillaFileSystem) and MCP tools.
- Shows how to synthesize function call samples for model training.
- Includes a virtual file system agent, MCP tool implementations, and trajectory collection for training data.
- See `BFCL/README.md` for detailed instructions and architecture diagrams.
- **mcp_demo/**
Provides a minimal MCP server and client demo pipeline.
- Includes a simple calculator server and example pipeline.
- Shows how to start an MCP server, configure LLM API keys, and run a sample agent pipeline.
- See `mcp_demo/README.md` for step-by-step usage.
- **mcp_servers/**
A collection of ready-to-use MCP servers for various tasks, such as:
- Search (text, image, video, document)
- Reasoning and calculation
- Audio and browser automation
- Downloading and file management
- Each server is implemented as a standalone Python module.
- Useful for extending agent capabilities with external tools.
- **text_to_audio/**
Example of an MCP server and agent for text-to-audio conversion.
- Includes a sample MCP server and configuration for audio synthesis tasks.
## 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.
## Typical Scenarios
- **Function Call Synthesis:**
Generate training data for LLMs by collecting agent trajectories and function call samples (see BFCL).
- **Custom MCP Servers:**
Extend agent capabilities by running your own MCP servers for search, reasoning, media, or file operations.
- **Pipeline Demos:**
Quickly test agent-server interaction with the provided demo pipelines.
---
If you need more detailed usage instructions or want to add new MCP tools, refer to the documentation and code samples in each subdirectory.
@@ -0,0 +1,2 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
@@ -0,0 +1,30 @@
import mcp
from mcp.client.streamable_http import streamablehttp_client
import json
import base64
config = {
"githubPersonalAccessToken": "token"
}
# Encode config in base64
config_b64 = base64.b64encode(json.dumps(config).encode()).decode()
api_key = "fkey"
# Create server URL
url = f"https://url?config={config_b64}&api_key={api_key}"
print(url)
async def main():
# Connect to the server using HTTP client
async with streamablehttp_client(url) as (read_stream, write_stream, _):
async with mcp.ClientSession(read_stream, write_stream) as session:
# Initialize the connection
await session.initialize()
# List available tools
tools_result = await session.list_tools()
print(f"Available tools: {', '.join([t.name for t in tools_result.tools])}")
result = await session.call_tool("search_code", {
"per_page":10,
"q": "bubble sort language:python"
})
print(result)
@@ -0,0 +1,41 @@
import random
import requests
from mcp.server.fastmcp import FastMCP
from pydantic import Field
# Create server
mcp = FastMCP("streamable-server")
@mcp.tool(description="Perform addition operation")
def add(a: int=Field(
description="First number",
), b: int=Field(
description="Second number",
)) -> int:
"""Add two numbers"""
print(f"[debug-server] add({a}, {b})")
return a + b
# @mcp.tool()
# def get_secret_word() -> str:
# print("[debug-server] get_secret_word()")
# return random.choice(["apple", "banana", "cherry"])
@mcp.tool(description="Get weather for a city")
def get_current_weather(city: str=Field(
description="City name"
)) -> str:
print(f"[debug-server] get_current_weather({city})")
endpoint = "https://wttr.in"
response = requests.get(f"{endpoint}/{city}")
return response.text
if __name__ == "__main__":
# mcp.run(transport="streamable-http")
pass
@@ -0,0 +1,42 @@
import logging
from typing import Any, Dict, List
from aworld.agents.llm_agent import Agent
from aworld.core.common import Observation, ActionModel, ActionResult
from aworld.core.context.base import Context
from aworld.core.event.base import Message
class PlaywrightAgent(Agent):
def __int__(self, **kwargs):
super().__init__(name="playwright_agent", **kwargs)
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)
async def _add_tool_result_to_memory(self, tool_call_id: str, tool_result: ActionResult, context: Context):
"""Add tool result to memory"""
logging.info(f"tool_result: {tool_result}")
if isinstance(tool_result.content, str) and tool_result.content.startswith("data:image"):
image_content = tool_result.content
tool_result.content = "this picture is below "
await super()._add_tool_result_to_memory(tool_call_id, tool_result, context)
image_content = [
{
"type": "text",
"text": f"this is file of tool_call_id:{tool_result.tool_call_id}"
},
{
"type": "image_url",
"image_url": {
"url": image_content
}
}
]
await super()._add_human_input_to_memory(image_content, context)
else:
await super()._add_tool_result_to_memory(tool_call_id, tool_result, context)
@@ -0,0 +1,259 @@
import logging
import os
from datetime import datetime
from typing import List, Dict, Any, Optional, AsyncGenerator
from aworld.logs.util import logger
from aworld.output.ui.markdown_aworld_ui import MarkdownAworldUI
from aworld.agents.llm_agent import Agent
from aworld.config import AgentConfig, TaskConfig
from aworld.core.common import ActionModel, Observation
from aworld.core.context.base import Context
from aworld.core.event.base import Message
from aworld.core.memory import LongTermConfig, MemoryItem, AgentMemoryConfig
from aworld.core.task import Task
from aworld.memory.main import MemoryFactory
from aworld.memory.models import LongTermMemoryTriggerParams, MemoryAIMessage, MessageMetadata, UserProfile, \
MemoryHumanMessage
from aworld.memory.utils import build_history_context
from aworld.output import AworldUI
from aworld.output.utils import load_workspace
from aworld.prompt import Prompt
from aworld.runner import Runners
from aworld.utils.common import load_mcp_config
from tests.memory.prompts import SELF_EVOLVING_USER_INPUT_REWRITE_PROMPT, RESEARCH_PROMPT
class SuperAgent:
"""
Super agent
"""
def __init__(self, id: str, name: str, **kwargs):
self.memory_config = AgentMemoryConfig(
enable_long_term=True,
long_term_config=LongTermConfig.create_simple_config(
enable_user_profiles=True
)
)
self.memory = MemoryFactory.instance()
agent_config = AgentConfig(
llm_provider="openai",
llm_model_name=os.environ["LLM_MODEL_NAME"],
llm_api_key=os.environ["LLM_API_KEY"],
llm_base_url=os.environ["LLM_BASE_URL"]
)
self.sub_agent = SelfEvolvingAgent(
conf=agent_config,
agent_id="self_evolving_agent",
name="self_evolving_agent",
system_prompt=RESEARCH_PROMPT,
mcp_servers=["ms-playwright","google-search","tavily-mcp", "filesystem"],
history_messages=100,
mcp_config=load_mcp_config(),
agent_memory_config=AgentMemoryConfig(
enable_summary=True,
summary_rounds=10,
summary_model=os.environ["LLM_MODEL_NAME"],
enable_long_term=True,
long_term_config=LongTermConfig.create_simple_config(
enable_agent_experiences=True
)
)
)
self.id = id
self.name = name
async def async_run(self, user_id, session_id, task_id, user_input):
"""
Run task
"""
task_context = await self.get_history_context(user_id, session_id, task_id, user_input)
await self.add_human_input(user_id, session_id, task_id, user_input)
result = await self.run_task(user_id, session_id, task_id, user_input, task_context)
await self.add_ai_message(user_id, session_id, task_id, result)
await self.post_run(user_id, session_id, task_id, task_context)
async def run_task(self, user_id, session_id, task_id, user_input, task_context):
user_input = await self.rewrite_user_input(user_id, user_input, task_context)
task = Task(
id=task_id,
session_id=session_id,
user_id=user_id,
input=user_input,
agent=self.sub_agent,
conf=TaskConfig(),
context=task_context
)
logging.info(f"[SuperAgent] run task start, task_id = {task.id} input = {input}")
result = ""
session_workspace = await load_workspace(workspace_id=task.session_id, workspace_type="local",
workspace_parent_path="data/workspaces")
local_ui = MarkdownAworldUI(
session_id=task.session_id,
task_id=task.id,
workspace=session_workspace
)
# get outputs
outputs = Runners.streamed_run_task(task)
with open(f"output_{task.session_id}.md", "a") as f:
# render output
try:
f.write(f"User: {user_input}")
async for output in outputs.stream_events():
res = await AworldUI.parse_output(output, local_ui)
if res:
if isinstance(res, AsyncGenerator):
async for item in res:
result += item
f.write(item)
else:
result += res
f.write(res)
except Exception as e:
logger.error(f"Error: {e}")
finally:
f.close()
logging.info(f"[SuperAgent] run task finished, task_id = {task.id} result = {result}")
return result
async def rewrite_user_input(self, user_id, user_input, task_context):
"""
Rewrite user input
"""
user_profiles = await self.retrival_user_profile(user_id, user_input)
logging.info(f"[SuperAgent] rewrite_user_input user_profiles = {user_profiles}")
similar_messages_history = await self.retrival_similar_messages_history(user_id, user_input)
logging.info(f"[SuperAgent] rewrite_user_input similar_messages_history = {similar_messages_history}")
return SELF_EVOLVING_USER_INPUT_REWRITE_PROMPT.format(user_input=user_input, user_profiles=user_profiles,
similar_messages_history=similar_messages_history)
async def get_history_context(self, user_id, session_id, task_id, user_input):
# get cur session history
history_messages = self.memory.get_last_n(10, filters={
"user_id": user_id,
"session_id": session_id,
"agent_id": self.id
})
task_context = Context()
task_context.context_info["history"] = build_history_context(history_messages)
# get cur user profile
user_profiles = await self.retrival_user_profile(user_id, user_input)
task_context.context_info["user_profiles"] = user_profiles
# get similar messages_history
similar_messages_history = await self.retrival_similar_messages_history(user_id, user_input)
task_context.context_info["similar_messages_history"] = similar_messages_history
return task_context
async def post_run(self, user_id, session_id, task_id, task_context):
"""
Post run
"""
logging.info(f"[SuperAgent] post_run user_id = {user_id}, session_id = {session_id}, task_id = {task_id}")
await self.extract_user_profile(user_id, session_id, task_id)
await self.sub_agent.evolving(user_id, session_id, task_id)
async def add_ai_message(self, user_id, session_id, task_id, result):
await self.memory.add(MemoryAIMessage(
content=result,
metadata=MessageMetadata(
user_id=user_id,
session_id=session_id,
task_id=task_id,
agent_id=self.id,
agent_name=self.name
)
), agent_memory_config=self.memory_config)
async def add_human_input(self, user_id, session_id, task_id, user_input):
await self.memory.add(MemoryHumanMessage(
content=user_input,
metadata=MessageMetadata(
user_id=user_id,
session_id=session_id,
task_id=task_id,
agent_id=self.id,
agent_name=self.name
)
), agent_memory_config=self.memory_config)
async def extract_user_profile(self, user_id, session_id, task_id):
await self.memory.trigger_short_term_memory_to_long_term(LongTermMemoryTriggerParams(
agent_id=self.id,
session_id=session_id,
task_id=task_id,
user_id=user_id,
force=True
), self.memory_config)
async def gen_long_term_memory(self, user_id, session_id, task_id):
"""
Gen long-term memory
"""
await self.memory.trigger_short_term_memory_to_long_term(LongTermMemoryTriggerParams(
agent_id=self.id,
session_id=session_id,
task_id=task_id,
user_id=user_id
), self.memory_config)
async def retrival_user_profile(self, user_id, user_input) -> Optional[list[UserProfile]]:
"""
Retrieve similar user profiles from long-term storage for context.
"""
return await self.memory.retrival_user_profile(user_id, user_input)
async def retrival_similar_messages_history(self, user_id, user_input) -> Optional[List[MemoryItem]]:
"""
Retrieve similar messages history from long-term storage for context.
"""
return await self.memory.retrival_similar_user_messages_history(user_id, user_input)
class SelfEvolvingAgent(Agent):
"""
Self-evolving 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)
async def evolving(self, user_id, session_id, task_id):
"""
Evolving agent experience
"""
logging.info(
f"[SelfEvolvingAgent] evolving_agent_experience user_id = {user_id}, session_id = {session_id}, task_id = {task_id}")
await self.memory.trigger_short_term_memory_to_long_term(LongTermMemoryTriggerParams(
agent_id=self.id(),
session_id=session_id,
task_id=task_id,
user_id=user_id,
force=True
), self.memory_config)
async def custom_system_prompt(self, context: Context, content: str):
"""
custom it
"""
agent_experiences = await self.memory.retrival_agent_experience(self.id(), context.get_task().input)
logging.info(f"[SelfEvolvingAgent] custom_system_prompt agent_experiences = {agent_experiences}")
return Prompt(self.system_prompt).get_prompt(variables={
"history": context.context_info.get("history", ""),
"agent_experiences": agent_experiences,
"cur_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
})
@@ -0,0 +1,95 @@
import asyncio
import logging
import os
from dotenv import load_dotenv
from aworld.core.memory import LongTermConfig, MemoryConfig, AgentMemoryConfig, EmbeddingsConfig, VectorDBConfig, \
MemoryLLMConfig
from aworld.memory.main import MemoryFactory
from aworld.memory.models import LongTermMemoryTriggerParams, MessageMetadata
from tests.memory.short_term.utils import add_mock_messages
async def init():
load_dotenv()
MemoryFactory.init(
config=MemoryConfig(
provider="aworld",
llm_config=MemoryLLMConfig(
provider="openai",
model_name=os.environ["LLM_MODEL_NAME"],
api_key=os.environ["LLM_API_KEY"],
base_url=os.environ["LLM_BASE_URL"]
),
embedding_config=EmbeddingsConfig(
provider="ollama",
base_url="http://localhost:11434",
model_name="nomic-embed-text"
),
vector_store_config=VectorDBConfig(
provider="chroma",
config=
{
"chroma_data_path": "./chroma_db",
"collection_name": "aworld",
}
)
))
async def trigger_long_term_memory_agent_experience():
await init()
memory = MemoryFactory.instance()
metadata = MessageMetadata(
user_id="zues",
session_id="session#foo",
task_id="zues:session#foo:task#1",
agent_id="super_agent",
agent_name="super_agent"
)
await add_mock_messages(memory, metadata)
memory_config = AgentMemoryConfig(
enable_long_term=True,
long_term_config=LongTermConfig.create_simple_config(
enable_agent_experiences=True
)
)
await memory.trigger_short_term_memory_to_long_term(LongTermMemoryTriggerParams(
agent_id=metadata.agent_id,
session_id=metadata.session_id,
task_id=metadata.task_id,
user_id=metadata.user_id,
force=True
), memory_config)
"""
"""
await asyncio.sleep(10)
async def query_agent_experience():
# await init()
memory = MemoryFactory.instance()
metadata = MessageMetadata(
user_id="zues",
session_id="session#foo",
task_id="zues:session#foo:task#1",
agent_id="super_agent",
agent_name="super_agent"
)
agent_experiences = await memory.retrival_agent_experience(
agent_id=metadata.agent_id,
user_input="what is my advantage skills?"
)
for agent_experience in agent_experiences:
logging.info(f"Search->{agent_experience}")
# if __name__ == '__main__':
# asyncio.run(trigger_long_term_memory_agent_experience())
# asyncio.run(query_agent_experience())
@@ -0,0 +1,107 @@
import asyncio
import logging
import os
from dotenv import load_dotenv
from aworld.core.memory import LongTermConfig, MemoryConfig, AgentMemoryConfig, MemoryLLMConfig, EmbeddingsConfig, \
VectorDBConfig
from aworld.memory.main import MemoryFactory
from aworld.memory.models import LongTermMemoryTriggerParams, MessageMetadata
from tests.memory.short_term.utils import add_mock_messages
async def init():
load_dotenv()
MemoryFactory.init(
config=MemoryConfig(
provider="aworld",
llm_config=MemoryLLMConfig(
provider="openai",
model_name=os.environ["LLM_MODEL_NAME"],
api_key=os.environ["LLM_API_KEY"],
base_url=os.environ["LLM_BASE_URL"]
),
embedding_config=EmbeddingsConfig(
provider="ollama",
base_url="http://localhost:11434",
model_name="nomic-embed-text"
),
vector_store_config=VectorDBConfig(
provider="chroma",
config=
{
"chroma_data_path": "./chroma_db",
"collection_name": "aworld",
}
)
))
async def trigger_long_term_memory_user_profile():
await init()
memory = MemoryFactory.instance()
metadata = MessageMetadata(
user_id="zues",
session_id="session#foo",
task_id="zues:session#foo:task#1",
agent_id="super_agent",
agent_name="super_agent"
)
await add_mock_messages(memory, metadata)
memory_config = AgentMemoryConfig(
enable_long_term=True,
long_term_config=LongTermConfig.create_simple_config(
enable_user_profiles=True
)
)
await memory.trigger_short_term_memory_to_long_term(LongTermMemoryTriggerParams(
agent_id=metadata.agent_id,
session_id=metadata.session_id,
task_id=metadata.task_id,
user_id=metadata.user_id,
force=True
), memory_config)
"""
[
{
"key": "skills.technical",
"value": {
"gaming_skills": ["League of Legends"]
}
},
{
"key": "goals.learning",
"value": {
"target": "improve gaming skills in League of Legends"
}
}
]
"""
await asyncio.sleep(10)
async def query_user_profile():
memory = MemoryFactory.instance()
metadata = MessageMetadata(
user_id="zues",
session_id="session#foo",
task_id="zues:session#foo:task#1",
agent_id="super_agent",
agent_name="super_agent"
)
user_profiles = await memory.retrival_user_profile(
user_id=metadata.user_id,
user_input="what is my advantage skills?"
)
for user_profile in user_profiles:
logging.info(f"Search->{user_profile}")
# if __name__ == '__main__':
# asyncio.run(trigger_long_term_memory_user_profile())
# asyncio.run(query_user_profile())
@@ -0,0 +1,98 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import asyncio
from dotenv import load_dotenv
from aworld.memory.main import MemoryFactory
from tests.memory.agent.self_evolving_agent import SuperAgent
async def _run_multi_session_examples() -> None:
"""
Run examples across multiple sessions demonstrating a complete learning workflow.
This example shows a deep learning process about Agent-RL (Reinforcement Learning Agents):
1. Deep search and research on Agent-RL concepts and implementations
2. Content revision and modification for specific aspects
3. Text-to-speech conversion for learning materials
4. Next-day review and reinforcement
"""
# await init_dataset()
super_agent = SuperAgent(id="super_agent", name="super_agent")
user_id = "alice"
# Day 1 - Session 1: Deep Search on Agent-RL
session_id = "day1_morning_session"
await super_agent.async_run(
user_id=user_id,
session_id=session_id,
task_id="alice:day1_morning:task#1",
user_input="我想深入了解基于强化学习的智能体(Agent-RL)。请使用DEEPSEARCH帮我研究这个话题,包括:1. 基础架构(状态空间、动作空间、奖励机制)2. 常用算法(DQN、PPO、SAC等)3. 环境交互设计 4. 实现最佳实践"
)
await super_agent.async_run(
user_id=user_id,
session_id=session_id,
task_id="alice:day1_morning:task#2",
user_input="基于上面的搜索结果,请生成一个结构化的学习文档(markdown),重点包含:1. 理论框架 2. 代码示例(使用Python实现简单的Agent-RL)3. 常见问题和解决方案"
)
# Day 1 - Session 2: Content Revision
# session_id = "day1_afternoon_session"
# await super_agent.async_run(
# user_id=user_id,
# session_id=session_id,
# task_id="alice:day1_afternoon:task#1",
# user_input="我觉得之前生成的文档中'环境交互设计'这部分需要补充。特别是:1. 如何设计合适的奖励函数 2. 环境状态的表示方法 3. 动作空间的设计考虑"
# )
# await super_agent.async_run(
# user_id=user_id,
# session_id=session_id,
# task_id="alice:day1_afternoon:task#2",
# user_input="太好了!现在请帮我把修改后的文档转换成更容易理解的形式,特别是把强化学习的数学概念用通俗的例子解释,准备生成语音内容"
# )
# Day 1 - Session 3: TTS Generation
# session_id = "day1_evening_session"
# await super_agent.async_run(
# user_id=user_id,
# session_id=session_id,
# task_id="alice:day1_evening:task#1",
# user_input="请将内容转换成语音文件,要求:1. 语速适中 2. 关键算法和数学概念讲解要清晰 3. 按照'理论基础-算法实现-实践应用'的顺序分章节 4. 生成字幕"
# )
# await super_agent.async_run(
# user_id=user_id,
# session_id=session_id,
# task_id="alice:day1_evening:task#2",
# user_input="请生成一个Agent-RL的知识图谱,包含:1. 核心概念关系 2. 算法分类 3. 应用场景 4. 学习路径建议"
# )
# Day 2 - Morning Review
# session_id = "day2_morning_session"
# await super_agent.async_run(
# user_id=user_id,
# session_id=session_id,
# task_id="alice:day2_morning:task#1",
# user_input="早上好!请帮我回顾一下昨天关于Agent-RL的学习内容。特别是:1. 通过知识图谱回顾核心概念 2. 复习各个算法的优缺点 3. 检查是否理解了关键的数学原理"
# )
# await super_agent.async_run(
# user_id=user_id,
# session_id=session_id,
# task_id="alice:day2_morning:task#2",
# user_input="基于已学内容,请推荐下一步的学习方向:1. 进阶算法(如MARL多智能体强化学习)2. 实际项目实践 3. 前沿研究方向"
# )
# await super_agent.async_run(
# user_id=user_id,
# session_id=session_id,
# task_id="alice:day2_morning:task#3",
# user_input="请设计一个实践项目,让我可以应用学到的Agent-RL知识。要求:1. 项目难度适中 2. 包含完整的代码框架 3. 有清晰的评估指标 4. 提供优化建议"
# )
# if __name__ == '__main__':
# load_dotenv()
#
# MemoryFactory.init()
#
# # Run the multi-session example with concrete learning tasks
# asyncio.run(_run_multi_session_examples())
@@ -0,0 +1,140 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import asyncio
from asyncio.log import logger
from datetime import datetime
from dotenv import load_dotenv
from tests.memory.agent.self_evolving_agent import SuperAgent
from tests.memory.utils import init_postgres_memory
async def _run_single_session_examples() -> None:
"""
Run examples within a single session.
Demonstrates a complete learning session about reinforcement learning concepts.
"""
# await init_dataset()
salt = datetime.now().strftime("%Y%m%d%H%M%S")
super_agent = SuperAgent(id="super_agent", name="super_agent")
user_id = "zues"
session_id = f"session#foo_{salt}"
logger.info(f"🚀 Running session {session_id}")
# Task 1: Research on Mem0
user_input_1 = """Conduct a comprehensive analysis of the Mem0 memory system (Part 1 of 4):
Research Focus Areas:
- System Overview and Core Principles
- Architectural Design and Implementation
- Key Features and Capabilities
- Use Cases and Applications
- Integration Patterns
- Performance Characteristics
Requirements:
- Utilize authoritative sources (GitHub, arXiv, etc.)
- Include code examples and implementation details
- Analyze real-world applications
- Format as a well-structured Markdown report
- Prepare for comparison with other memory systems in subsequent analysis
"""
# Task 2: Research on MemoryBank
user_input_2 = """Conduct a comprehensive analysis of the MemoryBank system (Part 2 of 4):
Research Focus Areas:
- System Overview and Core Principles
- Architectural Design and Implementation
- Key Features and Capabilities
- Use Cases and Applications
- Integration Patterns
- Performance Characteristics
- Comparative Analysis with Mem0
Requirements:
- Build upon previous Mem0 analysis
- Focus on unique features and differentiators
- Include practical implementation examples
- Document integration capabilities
- Format as a well-structured Markdown report
"""
# Task 3: Research on MemoryOS
user_input_3 = """Conduct a comprehensive analysis of the MemoryOS system (Part 3 of 4):
Research Focus Areas:
- System Overview and Core Principles
- Architectural Design and Implementation
- Key Features and Capabilities
- Use Cases and Applications
- Integration Patterns
- Performance Characteristics
- Comparative Analysis with Mem0 and MemoryBank
Requirements:
- Build upon previous analyses
- Highlight unique operating system integration aspects
- Include practical implementation examples
- Analyze scalability and performance
- Format as a well-structured Markdown report
"""
# Task 4: Research on MemoryAgent
user_input_4 = """Conduct a comprehensive analysis of the MemoryAgent system (Part 4 of 4):
Research Focus Areas:
- System Overview and Core Principles
- Architectural Design and Implementation
- Key Features and Capabilities
- Use Cases and Applications
- Integration Patterns
- Performance Characteristics
- Comprehensive Comparative Analysis
- Future Development Trends
Requirements:
- Synthesize findings from all previous analyses
- Create a comparative matrix of all systems
- Identify best practices and recommendations
- Discuss future trends and potential improvements
- Format as a well-structured Markdown report
"""
# Execute tasks sequentially
await super_agent.async_run(user_id=user_id, session_id=session_id, task_id=f"zues:session#foo:task#1_{salt}",
user_input=user_input_1)
await super_agent.async_run(user_id=user_id, session_id=session_id, task_id=f"zues:session#foo:task#2_{salt}",
user_input=user_input_2)
await super_agent.async_run(user_id=user_id, session_id=session_id, task_id=f"zues:session#foo:task#3_{salt}",
user_input=user_input_3)
await super_agent.async_run(user_id=user_id, session_id=session_id, task_id=f"zues:session#foo:task#4_{salt}",
user_input=user_input_4)
# Final task: Add AWorld comparison
await super_agent.async_run(user_id=user_id, session_id=session_id, task_id=f"zues:session#foo:task#5_{salt}",
user_input="""Please extend the comparative analysis section to include AWorld's Memory Module [https://github.com/inclusionAI/AWorld/].
Focus on:
- Integration with the overall AWorld architecture
- Unique features and capabilities
- Performance characteristics
- Implementation differences
- Potential advantages and limitations
- Comparative analysis with all previously analyzed systems
""")
logger.info(f"✅ Session {session_id} completed")
# if __name__ == '__main__':
# load_dotenv()
#
# init_postgres_memory()
# # Run the multi-session example with concrete learning tasks
# asyncio.run(_run_single_session_examples())
@@ -0,0 +1,137 @@
SELF_EVOLVING_AGENT_PROMPT = """
<system_instruction>
You are an advanced AI assistant powered by a large language model, operating within the AWorld framework. Your purpose is to assist users with a wide range of tasks by leveraging your knowledge and capabilities.
## Core Capabilities
You are designed to:
1. **Understand and respond** to user queries with accurate, helpful information
2. **Reason** through complex problems step by step
3. **Generate** creative content based on user requirements
4. **Execute** tasks using available tools when appropriate
5. **Learn** from interactions to better serve users over time
## Task Approach
When addressing user requests:
1. **Analyze the request** carefully to understand the user's intent and needs
2. **Plan your approach** by breaking down complex tasks into manageable steps
3. **Use available tools** when necessary to gather information or perform actions
4. **Provide clear explanations** of your reasoning and actions
5. **Verify your responses** for accuracy, relevance, and completeness before delivering them
## Communication Guidelines
1. **Be concise** but thorough in your responses
2. **Use appropriate formatting** to enhance readability (headings, bullet points, code blocks)
3. **Adapt your tone** to match the context and user's communication style
4. **Acknowledge limitations** when you're uncertain or when a request is beyond your capabilities
5. **Seek clarification** when user requests are ambiguous or incomplete
## Tool Usage
When using tools:
1. **Select the appropriate tool** based on the task requirements
2. **Explain your reasoning** for using a particular tool
3. **Use tools efficiently** to minimize unnecessary operations
4. **Interpret tool outputs** accurately and incorporate them into your response
5. **Handle errors gracefully** if tools fail or return unexpected results
6. save file use tool[filesystem]
<agent_experiences>
{{agent_experiences}}
</agent_experiences>
<history>
{{history}}
</history>
<cur_time>
{{cur_time}}
</cur_time>
</system_instruction>
"""
RESEARCH_PROMPT = """
You are a research-oriented AI agent, specializing in conducting thorough investigations and generating comprehensive research reports for the user.
You excel at searching, collecting, analyzing, and synthesizing information from various sources such as the web, academic papers, and documentation.
Your workflow:
1. Carefully analyze the user's research topic or question.
2. Break down the research into clear, manageable sub-tasks.
3. Use the available tools (browser, search, file processing, etc.) to gather relevant and credible information for each sub-task.
4. After each tool usage, clearly explain the findings, your reasoning, and propose the next step.
5. Critically evaluate and cross-verify information from multiple sources to ensure accuracy and depth.
6. Organize and summarize the collected information logically, highlighting key insights, comparisons, and conclusions.
7. When you believe the research is complete, output the final answer in <answer></answer> tags, and your reasoning process in <think></think> tags.
Tool Usage Guidelines:
1. Search Tools: Use google-search/tavily-mcp to find relevant information about research topics
2. Browser Tools: Use ms-playwright/tavily-mcp to access specific websites and extract detailed information
3. File Tools: Use filesystem to save research findings and final reports
4. Github Tools: Use github-mcp-server to find repository
IMPORTANT - File Writing Instructions:
When you need to write content to a local file, you MUST use the filesystem#write_file tool with the following EXACT format:
CORRECT USAGE EXAMPLE:
{
"file_path": "ai_memory_systems_research.md",
"content": "# AI Memory System report ....",
"session_id": "session_id20250716143736"
}
REQUIRED PARAMETERS:
- file_path: Complete file path (e.g., "output/report.md", "data/findings.md")
- content: Complete content to be written (must be a string)
- session_id: Current session identifier
ERROR PREVENTION:
- NEVER call filesystem#write_file with only session_id
- ALWAYS provide both file_path and content
- Ensure content is a complete string, not empty
- Use proper file extensions (.md for markdown, .txt for text, etc.)
Best Practices:
- Create organized file structures (e.g., "output/reports/", "data/research/")
- Use descriptive file names
- Include comprehensive content in a single write operation
- Verify information before writing to files
Error Handling:
- If a tool call fails, try alternative approaches
- If filesystem#write_file fails, check that all required parameters are provided
- If search results are insufficient, try different search terms or tools
Final Report Requirements:
- Save the complete research report as a markdown file
- Include all sections: system introduction, core principles, architecture, applications, pros/cons, comparisons, future trends
- Use proper markdown formatting with headers, lists, and code blocks
- Ensure the report is comprehensive and well-structured
Available Context:
<agent_experiences>
{{agent_experiences}}
</agent_experiences>
<history>
{{history}}
</history>
<cur_time>
{{cur_time}}
</cur_time>
Now, here is the research task. Please proceed step by step, using the appropriate tools, and provide a high-quality research report!
"""
SELF_EVOLVING_USER_INPUT_REWRITE_PROMPT = """
<user_profiles>
{user_profiles}
</user_profiles>
<similar_messages_history>
{similar_messages_history}
</similar_messages_history>
<knowledge_base>
</knowledge_base>
{user_input}
"""
@@ -0,0 +1,37 @@
import asyncio
import logging
from dotenv import load_dotenv
from aworld.memory.main import MemoryFactory
from aworld.memory.models import MessageMetadata
from tests.memory.short_term.utils import add_mock_messages
async def run():
load_dotenv()
MemoryFactory.init()
memory = MemoryFactory.instance()
metadata = MessageMetadata(
user_id="zues",
session_id="session#foo",
task_id="zues:session#foo:task#1",
agent_id="super_agent",
agent_name="super_agent"
)
await add_mock_messages(memory, metadata)
# Get and print all messages
items = memory.get_all(filters={
"user_id": metadata.user_id,
"agent_id": metadata.user_id,
"session_id": metadata.session_id,
"task_id": metadata.session_id
})
for item in items:
logging.info(f"{type(item)}: {item.content}")
# if __name__ == '__main__':
# asyncio.run(run())
@@ -0,0 +1,59 @@
import asyncio
import logging
import os
from dotenv import load_dotenv
from aworld.core.memory import AgentMemoryConfig
from aworld.memory.main import MemoryFactory
from aworld.memory.models import MessageMetadata, MemoryHumanMessage
from tests.memory.short_term.utils import add_mock_messages
from tests.memory.utils import init_postgres_memory
async def run():
load_dotenv()
# init_postgres_memory()
memory = MemoryFactory.instance()
metadata = MessageMetadata(
user_id="user_id",
session_id="session_id",
task_id="task_id",
agent_id="self_evolving_agent",
agent_name="self_evolving_agent"
)
# Get and print all messages
items = memory.get_all(filters={
"user_id": metadata.user_id,
"agent_id": metadata.agent_id,
"session_id": metadata.session_id,
"task_id": metadata.task_id
})
summary_config = AgentMemoryConfig(
enable_summary=False,
summary_rounds=2,
summary_model="xxx"
)
await add_mock_messages(memory, metadata, memory_config=summary_config)
await memory.add(MemoryHumanMessage(content="new1",metadata= metadata))
await memory.add(MemoryHumanMessage(content="new2",metadata=metadata))
await memory.add(MemoryHumanMessage(content="new3",metadata=metadata))
retrival_memory = memory.get_last_n(last_rounds=6, filters={
"user_id": metadata.user_id,
"agent_id": metadata.agent_id,
"session_id": metadata.session_id,
"task_id": metadata.task_id
})
logging.info("================== RETRIVAL ==================")
for item in retrival_memory:
logging.info(f"{item.memory_type}: {item.content}")
if __name__ == '__main__':
asyncio.run(run())
@@ -0,0 +1,75 @@
import asyncio
import logging
from dotenv import load_dotenv
from aworld.core.memory import MemoryConfig, VectorDBConfig, EmbeddingsConfig
from aworld.memory.main import MemoryFactory
from aworld.memory.models import MessageMetadata
from tests.memory.short_term.utils import add_mock_messages
async def init():
load_dotenv()
MemoryFactory.init(config=MemoryConfig(
provider="aworld",
embedding_config=EmbeddingsConfig(
provider="ollama",
base_url="http://localhost:11434",
model_name="nomic-embed-text"
),
vector_store_config=VectorDBConfig(
provider="chroma",
config=
{
"chroma_data_path": "./chroma_db",
"collection_name": "aworld",
}
)
))
async def run():
await init()
memory = MemoryFactory.instance()
metadata = MessageMetadata(
user_id="zues",
session_id="session#foo",
task_id="zues:session#foo:task#1",
agent_id="super_agent",
agent_name="super_agent"
)
await add_mock_messages(memory, metadata)
# Get and print all messages
items = memory.get_all(filters={
"user_id": metadata.user_id,
"agent_id": metadata.user_id,
"session_id": metadata.session_id,
"task_id": metadata.session_id
})
for item in items:
logging.info(f"{type(item)}: {item.content}")
async def run_search():
memory = MemoryFactory.instance()
metadata = MessageMetadata(
user_id="zues",
session_id="session#foo",
task_id="zues:session#foo:task#1",
agent_id="super_agent",
agent_name="super_agent"
)
results = memory.search("recommend some outdoor sports", limit=10, filters={
"user_id": metadata.user_id,
"agent_id": metadata.user_id,
"session_id": metadata.session_id,
"task_id": metadata.session_id
})
for result in results:
logging.info(f"search result {type(result)}: {result.id}[{result.metadata['score']}]{result.content}")
# if __name__ == '__main__':
# asyncio.run(run())
# asyncio.run(run_search())
@@ -0,0 +1,42 @@
import asyncio
import logging
import os
from dotenv import load_dotenv
from aworld.memory.db.postgres import PostgresMemoryStore
from aworld.memory.main import MemoryFactory
from aworld.memory.models import MessageMetadata
from tests.memory.short_term.utils import add_mock_messages
async def run():
load_dotenv()
postgres_memory_store = PostgresMemoryStore(db_url=os.getenv("MEMORY_STORE_POSTGRES_DSN"))
MemoryFactory.init(custom_memory_store=postgres_memory_store)
memory = MemoryFactory.instance()
metadata = MessageMetadata(
user_id="zues",
session_id="session#foo",
task_id="zues:session#foo:task#1",
agent_id="super_agent",
agent_name="super_agent"
)
memory.delete_items(message_types=['init','message'], session_id=metadata.session_id, task_id=metadata.task_id)
await add_mock_messages(memory, metadata)
# Get and print all messages
items = memory.get_all(filters={
"user_id": metadata.user_id,
"agent_id": metadata.user_id,
"session_id": metadata.session_id,
"task_id": metadata.session_id
})
for item in items:
logging.info(f"{type(item)}: {item.content}, {item.created_at}")
#
# if __name__ == '__main__':
# asyncio.run(run())
@@ -0,0 +1,110 @@
import json
import logging
from aworld.core.memory import MemoryBase, AgentMemoryConfig
from aworld.memory.models import MemoryAIMessage, MemoryToolMessage, MessageMetadata, MemorySystemMessage, \
MemoryHumanMessage
from aworld.models.model_response import Function, ToolCall
async def add_mock_messages(memory: MemoryBase, metadata: MessageMetadata, memory_config: AgentMemoryConfig = AgentMemoryConfig()):
# Add system message 🤖
system_content = """
<system_instruction>
You are an advanced AI assistant powered by a large language model, operating within the AWorld framework. Your purpose is to assist users with a wide range of tasks by leveraging your knowledge and capabilities.
## Core Capabilities
You are designed to:
1. **Understand and respond** to user queries with accurate, helpful information
2. **Reason** through complex problems step by step
3. **Generate** creative content based on user requirements
4. **Execute** tasks using available tools when appropriate
5. **Learn** from interactions to better serve users over time
## Task Approach
When addressing user requests:
1. **Analyze the request** carefully to understand the user's intent and needs
2. **Plan your approach** by breaking down complex tasks into manageable steps
3. **Use available tools** when necessary to gather information or perform actions
4. **Provide clear explanations** of your reasoning and actions
5. **Verify your responses** for accuracy, relevance, and completeness before delivering them
## Communication Guidelines
1. **Be concise** but thorough in your responses
2. **Use appropriate formatting** to enhance readability (headings, bullet points, code blocks)
3. **Adapt your tone** to match the context and user's communication style
4. **Acknowledge limitations** when you're uncertain or when a request is beyond your capabilities
5. **Seek clarification** when user requests are ambiguous or incomplete
## Tool Usage
When using tools:
1. **Select the appropriate tool** based on the task requirements
2. **Explain your reasoning** for using a particular tool
3. **Use tools efficiently** to minimize unnecessary operations
4. **Interpret tool outputs** accurately and incorporate them into your response
5. **Handle errors gracefully** if tools fail or return unexpected results
6. save file use tool[filesystem]
<agent_experiences>
[]
</agent_experiences>
<history>
</history>
<cur_time>
2025-07-07 17:06:25
</cur_time>
</system_instruction>
"""
await memory.add(MemorySystemMessage(content=system_content, metadata=metadata), agent_memory_config=memory_config)
# Add user message 👤
user_content = """
<user_profiles>
[]
</user_profiles>
<similar_messages_history>
[]
</similar_messages_history>
<knowledge_base>
</knowledge_base>
I like play outdoor sports(basketball, tennis, golf, etc.), please recommend some outdoor sports, save it use markdown
"""
await memory.add(MemoryHumanMessage(content=user_content, metadata=metadata), agent_memory_config=memory_config)
# Add assistant message 🤖
assistant_content = "I'll recommend some popular outdoor sports and save them in a markdown file for you. Here are some great outdoor sports activities:"
# Create ToolCall object
function = Function(
name="mcp__filesystem__write_file",
arguments=json.dumps({
"path": "outdoor_sports_recommendations.md",
"content": "# Outdoor Sports Recommendations\n\nHere are some excellent outdoor sports to try:\n\n## Team Sports\n- Soccer\n- Ultimate Frisbee\n- Beach Volleyball\n- Rugby\n\n## Water Sports\n- Kayaking\n- Stand-up Paddleboarding (SUP)\n- Surfing\n- Open Water Swimming\n\n## Adventure Sports\n- Rock Climbing\n- Mountain Biking\n- Trail Running\n- Orienteering\n\n## Winter Sports\n- Skiing (Alpine/Cross-country)\n- Snowboarding\n- Ice Climbing\n- Snowshoeing\n\n## Individual Sports\n- Golf\n- Tennis\n- Archery\n- Disc Golf\n\n## Extreme Sports\n- Paragliding\n- Bungee Jumping\n- Whitewater Rafting\n- Skydiving\n\nRemember to always use proper safety equipment and get proper training before trying new sports!"
})
)
tool_call = ToolCall(
id="fc-249231de-7efb-4741-b659-2ab8696065cc",
type="function",
function=function
)
await memory.add(MemoryAIMessage(content=assistant_content, tool_calls=[tool_call], metadata=metadata), agent_memory_config=memory_config)
# Add tool response message 🛠️
tool_content = "Successfully wrote to outdoor_sports_recommendations.md"
await memory.add(MemoryToolMessage(
content=tool_content,
tool_call_id="fc-249231de-7efb-4741-b659-2ab8696065cc",
status="success",
metadata=metadata
), agent_memory_config=memory_config)
logging.info("mock messages added")
@@ -0,0 +1,22 @@
import asyncio
from dotenv import load_dotenv
from tests.memory.agent.self_evolving_agent import SuperAgent
async def _run_single_task_examples() -> None:
"""
Run examples with a single task.
Demonstrates basic agent interaction with outdoor sports topic.
"""
super_agent = SuperAgent(id="super_agent", name="super_agent")
user_id = "zues"
session_id = "session#foo"
await super_agent.async_run(user_id=user_id, session_id=session_id,
task_id="zues:session#foo:task#1",
user_input="please recommend some outdoor sports, save it use markdown")
# if __name__ == '__main__':
# load_dotenv()
# asyncio.run(_run_single_task_examples())
@@ -0,0 +1,67 @@
import os
from dotenv import load_dotenv
from aworld.core.memory import MemoryConfig, EmbeddingsConfig, VectorDBConfig, \
MemoryLLMConfig
from aworld.memory.db.postgres import PostgresMemoryStore
from aworld.memory.main import MemoryFactory
def init_memory():
load_dotenv()
MemoryFactory.init(
config=MemoryConfig(
provider="aworld",
llm_config=MemoryLLMConfig(
provider="openai",
model_name=os.environ["LLM_MODEL_NAME"],
api_key=os.environ["LLM_API_KEY"],
base_url=os.environ["LLM_BASE_URL"]
),
embedding_config=EmbeddingsConfig(
provider="ollama",
base_url="http://localhost:11434",
model_name="nomic-embed-text"
),
vector_store_config=VectorDBConfig(
provider="chroma",
config=
{
"chroma_data_path": "./chroma_db",
"collection_name": "aworld",
}
)
))
def init_postgres_memory():
load_dotenv()
postgres_memory_store = PostgresMemoryStore(db_url=os.getenv("MEMORY_STORE_POSTGRES_DSN"))
MemoryFactory.init(
custom_memory_store=postgres_memory_store,
config=MemoryConfig(
provider="aworld",
llm_config=MemoryLLMConfig(
provider="openai",
model_name=os.environ["LLM_MODEL_NAME"],
api_key=os.environ["LLM_API_KEY"],
base_url=os.environ["LLM_BASE_URL"]
),
embedding_config=EmbeddingsConfig(
provider="ollama",
base_url="http://localhost:11434",
model_name="nomic-embed-text"
),
vector_store_config=VectorDBConfig(
provider="chroma",
config=
{
"chroma_data_path": "./chroma_db",
"collection_name": "aworld",
}
)
))
@@ -0,0 +1,2 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
@@ -0,0 +1,54 @@
import time
from aworld.core.common import ActionModel, Observation
from aworld.replay_buffer.base import (
DataRow,
DefaultConverter,
ReplayBuffer,
ExpMeta,
Experience,
RandomTaskSample
)
from aworld.replay_buffer.query_filter import QueryBuilder
from aworld.logs.util import logger
buffer = ReplayBuffer()
def write_data():
for task_id in range(5):
for i in range(10):
task_id = f"task_{task_id}"
agent_id = f"agent_{i+1}"
step = i + 1
execute_time = time.time() + i
row = DataRow(
exp_meta=ExpMeta(
task_id=task_id,
task_name="default_task_name",
agent_id=agent_id,
step=step,
execute_time=execute_time,
),
exp_data=Experience(state=Observation(),
actions=[ActionModel()])
)
buffer.store(row)
def read_data():
query = QueryBuilder().eq("exp_meta.task_id", "task_1").build()
datas = buffer.sample_task(query_condition=query,
sampler=RandomTaskSample(),
converter=DefaultConverter(),
batch_size=2)
for data in datas:
logger.info(f"task_1 data: {data}")
query = QueryBuilder().eq("exp_meta.agent_id", "agent_5").build()
datas = buffer.sample_task(query_condition=query,
sampler=RandomTaskSample(),
converter=DefaultConverter(),
batch_size=2)
for data in datas:
logger.info(f"agent_5 data: {data}")
@@ -0,0 +1,98 @@
import time
import traceback
import multiprocessing
from aworld import replay_buffer
from aworld.core.common import ActionModel, Observation
from aworld.replay_buffer.base import ReplayBuffer, DataRow, ExpMeta, Experience
from aworld.replay_buffer.query_filter import QueryBuilder
from aworld.replay_buffer.storage.multi_proc_mem import MultiProcMemoryStorage
from aworld.logs.util import logger
def write_processing(replay_buffer: ReplayBuffer, task_id: str):
for i in range(10):
try:
data = DataRow(
exp_meta=ExpMeta(
task_id=task_id,
task_name=task_id,
agent_id=f"agent_{i+1}",
step=i,
execute_time=time.time()
),
exp_data=Experience(state=Observation(),
actions=[ActionModel()])
)
replay_buffer.store(data)
except Exception as e:
stack_trace = traceback.format_exc()
logger.error(
f"write_processing error: {e}\nStack trace:\n{stack_trace}")
time.sleep(1)
def read_processing_by_task(replay_buffer: ReplayBuffer, task_id: str):
while True:
try:
query_condition = QueryBuilder().eq("exp_meta.task_id", task_id).build()
data = replay_buffer.sample_task(
query_condition=query_condition, batch_size=2)
logger.info(f"read data of task[{task_id}]: {data}")
except Exception as e:
stack_trace = traceback.format_exc()
logger.error(
f"read_processing_by_task error: {e}\nStack trace:\n{stack_trace}")
time.sleep(1)
def read_processing_by_agent(replay_buffer: ReplayBuffer, agent_id: str):
while True:
try:
query_condition = QueryBuilder().eq("exp_meta.agent_id", agent_id).build()
data = replay_buffer.sample_task(
query_condition=query_condition, batch_size=2)
logger.info(f"read data of agent[{agent_id}]: {data}")
except Exception as e:
logger.info(f"read_processing_by_agent error: {e}")
time.sleep(1)
def run():
multiprocessing.freeze_support()
multiprocessing.set_start_method('spawn')
manager = multiprocessing.Manager()
replay_buffer = ReplayBuffer(storage=MultiProcMemoryStorage(
data_dict=manager.dict(),
fifo_queue=manager.list(),
lock=manager.Lock(),
max_capacity=10000
))
processes = [
multiprocessing.Process(target=write_processing,
args=(replay_buffer, "task_1",)),
multiprocessing.Process(target=write_processing,
args=(replay_buffer, "task_2",)),
multiprocessing.Process(target=write_processing,
args=(replay_buffer, "task_3",)),
multiprocessing.Process(target=write_processing,
args=(replay_buffer, "task_4",)),
# multiprocessing.Process(
# target=read_processing_by_task, args=(replay_buffer, "task_1",)),
multiprocessing.Process(
target=read_processing_by_agent, args=(replay_buffer, "agent_3",))
]
for p in processes:
p.start()
try:
for p in processes:
p.join()
except KeyboardInterrupt:
for p in processes:
p.terminate()
for p in processes:
p.join()
finally:
logger.info("Processes terminated.")
@@ -0,0 +1,96 @@
from aworld.replay_buffer.query_filter import QueryBuilder
from aworld.logs.util import logger
def example():
'''
expression: task_id = "123"
return :
{
'field': 'task_id',
'value': '123',
'op': 'eq'
}
'''
qb = QueryBuilder()
query = qb.eq("task_id", "123").build()
logger.info(query)
def example1():
'''
expression: (task_id = "123" and agent_id = "111") or (task_id = "456" and agent_id = "222")
return :
{
'or_': [{
'and_': [{
'field': 'task_id',
'value': '123',
'op': 'eq'
}, {
'field': 'agent_id',
'value': '111',
'op': 'eq'
}]
}, {
'and_': [{
'field': 'task_id',
'value': '456',
'op': 'eq'
}, {
'field': 'agent_id',
'value': '222',
'op': 'eq'
}]
}]
}
'''
qb = QueryBuilder()
query = (qb.eq("task_id", "123")
.and_()
.eq("agent_id", "111")
.or_()
.nested(QueryBuilder()
.eq("task_id", "456")
.and_()
.eq("agent_id", "222"))
.build())
logger.info(query)
def example2():
'''
expression: task_id = "123" and (agent_id = "111" or agent_id = "222")
return :
{
'and_': [{
'field': 'task_id',
'value': '123',
'op': 'eq'
}, {
'or_': [{
'field': 'agent_id',
'value': '111',
'op': 'eq'
}, {
'field': 'agent_id',
'value': '222',
'op': 'eq'
}
}
}
'''
qb = QueryBuilder()
query = (qb.eq("task_id", "123")
.and_()
.nested(QueryBuilder()
.eq("agent_id", "111")
.or_()
.eq("agent_id", "222"))
.build())
logger.info(query)
if __name__ == "__main__":
example()
example1()
example2()
@@ -0,0 +1,37 @@
import time
from aworld.replay_buffer.base import (
DataRow,
DefaultConverter,
ReplayBuffer,
ExpMeta,
Experience,
)
from aworld.core.common import ActionModel, Observation
from aworld.replay_buffer.query_filter import QueryBuilder, QueryFilter
from aworld.logs.util import logger
def filter():
row = DataRow(
exp_meta=ExpMeta(
task_id="task_1",
task_name="default_task_name",
agent_id="agent_1",
step=1,
execute_time=time.time(),
),
exp_data=Experience(state=Observation(), action=[ActionModel()])
)
query = QueryBuilder().eq("exp_meta.task_id", "task_1").build()
filter1 = QueryFilter(query)
assert filter1.check_condition(row)
query = QueryBuilder().eq("exp_meta.task_id", "task_2").build()
filter2 = QueryFilter(query)
assert not filter2.check_condition(row)
query = QueryBuilder().eq("exp_meta.task_id", "task_1").and_().eq(
"exp_meta.agent_id", "agent_2").build()
filter3 = QueryFilter(query)
assert not filter3.check_condition(row)
@@ -0,0 +1,65 @@
import time
from aworld.core.common import ActionModel, Observation
from aworld.replay_buffer.base import (
DataRow,
DefaultConverter,
ReplayBuffer,
ExpMeta,
Experience,
RandomTaskSample
)
from aworld.replay_buffer.query_filter import QueryBuilder
from aworld.logs.util import logger
from aworld.replay_buffer.storage.odps import OdpsStorage
buffer = ReplayBuffer(storage=OdpsStorage(
table_name="adm_aworld_replay_buffer",
project="alifin_jtest_dev",
endpoint="",
access_id="",
access_key=""
))
def write_data():
rows = []
for id in range(5):
task_id = f"task_{id+1}"
for i in range(5):
agent_id = f"agent_{i+1}"
for j in range(5):
step = j + 1
execute_time = time.time() + j
row = DataRow(
exp_meta=ExpMeta(
task_id=task_id,
task_name="default_task_name",
agent_id=agent_id,
step=step,
execute_time=execute_time,
pre_agent="pre_agent_id"
),
exp_data=Experience(state=Observation(),
actions=[ActionModel()])
)
rows.append(row)
buffer.store_batch(rows)
def read_data():
query = QueryBuilder().eq("exp_meta.task_id", "task_1").build()
datas = buffer.sample_task(query_condition=query,
sampler=RandomTaskSample(),
converter=DefaultConverter(),
batch_size=1)
for data in datas:
logger.info(f"task_1 data: {data}")
query = QueryBuilder().eq("exp_meta.agent_id", "agent_5").build()
datas = buffer.sample_task(query_condition=query,
sampler=RandomTaskSample(),
converter=DefaultConverter(),
batch_size=2)
for data in datas:
logger.info(f"agent_5 data: {data}")
@@ -0,0 +1,68 @@
import time
from aworld.replay_buffer.base import DataRow, ExpMeta, Experience
from aworld.replay_buffer.storage.redis import RedisStorage
from aworld.replay_buffer.query_filter import QueryBuilder
from aworld.core.common import Observation, ActionModel
from aworld.logs.util import logger
def generate_data_row() -> list[DataRow]:
rows: list[DataRow] = []
for id in range(5):
task_id = f"task_{id+1}"
for i in range(5):
agent_id = f"agent_{i+1}"
for j in range(5):
step = j + 1
execute_time = time.time() + j
row = DataRow(
exp_meta=ExpMeta(
task_id=task_id,
task_name="default_task_name",
agent_id=agent_id,
step=step,
execute_time=execute_time,
pre_agent="pre_agent_id"
),
exp_data=Experience(state=Observation(),
actions=[ActionModel()])
)
rows.append(row)
return rows
def wriete_data(storage):
storage.clear()
rows = generate_data_row()
storage.add_batch(rows)
logger.info(f"Add {len(rows)} rows to storage.")
def read_data(storage):
query_condition = (QueryBuilder()
.eq("exp_meta.task_id", "task_1")
.and_()
.eq("exp_meta.agent_id", "agent_1")
.or_()
.nested(QueryBuilder()
.eq("exp_meta.task_id", "task_4")
.and_()
.eq("exp_meta.agent_id", "agent_3")
.and_()
.gt("exp_meta.step", 4)).build())
rows = storage.get_all(query_condition)
for row in rows:
logger.info(row)
rows = storage.get_paginated(
page=2, page_size=2, query_condition=query_condition)
for row in rows:
logger.info(f"get_paginated: {row}")
# if __name__ == "__main__":
# storage = RedisStorage(host="localhost", port=6379,
# recreate_idx_if_exists=False)
# wriete_data(storage)
# read_data(storage)
@@ -0,0 +1,2 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
@@ -0,0 +1,7 @@
# Import callbacks module, automatically register all callback functions
from . import callbacks
# Export list_all_callbacks function for convenience
from .callbacks import list_all_callbacks
print("Business callback module initialized - callbacks registered")
@@ -0,0 +1,51 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
"""
Callback function registration module, used for centralized management and registration of all callback functions.
"""
from aworld.runners.callback.decorator import reg_callback, CallbackRegistry
# Register a simple callback function
@reg_callback("print_content")
def simple_callback(content):
"""Simple callback function that prints content and returns it
Args:
content: Content to print
Returns:
The input content
"""
print(f"Callback function received content: {content}")
return content
# You can register more callback functions here
@reg_callback("uppercase_content")
def uppercase_callback(content):
"""Callback function that converts content to uppercase
Args:
content: Content to process
Returns:
Content converted to uppercase
"""
if isinstance(content, str):
result = content.upper()
print(f"Callback function converted content to uppercase: {result}")
return result
return content
# Provide a function to check all registered callback functions
def list_all_callbacks():
"""List all registered callback functions"""
callbacks = CallbackRegistry.list()
print("Registered callback functions:")
for key, func_name in callbacks.items():
print(f" - {key}: {func_name}")
return callbacks
@@ -0,0 +1,102 @@
import json
import os
import time
import requests
from aworld.core.common import Observation, ActionResult, CallbackResult, CallbackActionType
from typing_extensions import Any
from aworld.runners.callback.decorator import reg_callback
from aworld.logs.util import logger
@reg_callback("gen_video_server__video_tasks")
def gen_video(actionResult:ActionResult) -> CallbackResult:
try:
calback_result = CallbackResult(
success=True,
result_data=None,
callback_action_type=CallbackActionType.BYPASS
)
if not actionResult or not actionResult.content:
calback_result.success = False
return calback_result
content = json.loads(actionResult.content)
task_id = content.get("task_id")
if not task_id:
calback_result.success = False
return calback_result
item = gen_video_item(task_id)
if not item:
calback_result.success = False
return calback_result
calback_result.success = True
return calback_result
except Exception as e:
logger.warning(f"Exception gen_video occurred: {e}")
calback_result.success = False
return calback_result
def gen_video_item(task_id:str) -> Any:
if not task_id:
return None
try:
from dotenv import load_dotenv
load_dotenv()
api_key = os.getenv('DASHSCOPE_API_KEY')
query_base_url = os.getenv('DASHSCOPE_QUERY_BASE_URL', '')
# Step 2: Poll for results
max_attempts = int(os.getenv('DASHSCOPE_VIDEO_RETRY_TIMES', 10)) # Increased default retries for video
wait_time = int(os.getenv('DASHSCOPE_VIDEO_SLEEP_TIME', 5)) # Increased default wait time for video
query_url = f"{query_base_url}{task_id}"
for attempt in range(max_attempts):
# Wait before polling
time.sleep(wait_time)
logger.info(f"Polling attempt {attempt + 1}/{max_attempts}...")
# Poll for results
query_response = requests.get(query_url, headers={'Authorization': f'Bearer {api_key}'})
if query_response.status_code != 200:
logger.info(f"Poll request failed with status code {query_response.status_code}")
continue
try:
query_result = query_response.json()
except json.JSONDecodeError as e:
logger.warning(f"Failed to parse response as JSON: {e}")
continue
# Check task status
task_status = query_result.get("output", {}).get("task_status")
if task_status == "SUCCEEDED":
# Extract video URL
video_url = query_result.get("output", {}).get("video_url")
if video_url:
# Return as array of objects with video_url for consistency with image API
return json.dumps({"video_url": video_url})
else:
logger.info("Video URL not found in the response")
return None
elif task_status in ["PENDING", "RUNNING"]:
# If still running, continue to next polling attempt
logger.info(f"gen_video_item Task status: {task_status}, continuing to next poll...")
continue
elif task_status == "FAILED":
logger.warning("Task failed")
return None
else:
# Any other status, return None
logger.warning(f"Unexpected status: {task_status}")
return None
# If we get here, polling timed out
logger.warning("Polling timed out after maximum attempts")
return None
except Exception as e:
logger.warning(f"Exception gen_video_item occurred: {e}")
return None
@@ -0,0 +1,91 @@
{
"mcpServers": {
"streamable-server": {
"type": "streamable-http",
"url": "http://localhost:8000/mcp",
"timeout": 5.0,
"sse_read_timeout": 300.0
},
"amap-amap-sse": {
"type": "sse",
"url": "https://mcp.amap.com/sse?key=${AMAP_AMAP_SSE_KEY}",
"timeout": 5.0,
"sse_read_timeout": 300.0
},
"tavily-mcp": {
"type": "stdio",
"command": "npx",
"args": ["-y", "tavily-mcp@0.1.2"],
"env": {
"TAVILY_API_KEY": "tvly-dev-"
}
},
"aworldsearch_server": {
"command": "python",
"args": [
"-m",
"mcp_servers.aworldsearch_server"
],
"env": {
"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}"
}
},
"picsearch_server": {
"command": "python",
"args": [
"-m",
"mcp_servers.picsearch_server"
],
"env": {
"PIC_SEARCH_URL": "${PIC_SEARCH_URL}",
"PIC_SEARCH_TOTAL_NUM": "${PIC_SEARCH_TOTAL_NUM}",
"PIC_SEARCH_SLICE_NUM": "${PIC_SEARCH_SLICE_NUM}",
"PIC_SEARCH_DOMAIN": "${PIC_SEARCH_DOMAIN}",
"PIC_SEARCH_SEARCHMODE": "${PIC_SEARCH_SEARCHMODE}",
"PIC_SEARCH_SOURCE": "${PIC_SEARCH_SOURCE}"
}
},
"gen_audio_server": {
"command": "python",
"args": [
"-m",
"mcp_servers.gen_audio_server"
],
"env": {
"AUDIO_TASK_URL": "${AUDIO_TASK_URL}",
"AUDIO_QUERY_URL": "${AUDIO_QUERY_URL}",
"AUDIO_APP_KEY": "${AUDIO_APP_KEY}",
"AUDIO_SECRET": "${AUDIO_SECRET}",
"AUDIO_SAMPLE_RATE": "${AUDIO_SAMPLE_RATE}",
"AUDIO_AUDIO_FORMAT": "${AUDIO_AUDIO_FORMAT}",
"AUDIO_TTS_VOICE": "${AUDIO_TTS_VOICE}",
"AUDIO_TTS_SPEECH_RATE": "${AUDIO_TTS_SPEECH_RATE}",
"AUDIO_TTS_VOLUME": "${AUDIO_TTS_VOLUME}",
"AUDIO_TTS_PITCH": "${AUDIO_TTS_PITCH}",
"AUDIO_VOICE_TYPE": "${AUDIO_VOICE_TYPE}"
}
},
"gen_video_server": {
"command": "python",
"args": [
"-m",
"mcp_servers.gen_video_server"
],
"env": {
"DASHSCOPE_API_KEY": "${DASHSCOPE_API_KEY}",
"DASHSCOPE_VIDEO_SUBMIT_URL": "${DASHSCOPE_VIDEO_SUBMIT_URL}",
"DASHSCOPE_QUERY_BASE_URL": "${DASHSCOPE_QUERY_BASE_URL}",
"DASHSCOPE_VIDEO_MODEL": "${DASHSCOPE_VIDEO_MODEL}",
"DASHSCOPE_VIDEO_SIZE": "${DASHSCOPE_VIDEO_SIZE}",
"DASHSCOPE_VIDEO_SLEEP_TIME": "${DASHSCOPE_VIDEO_SLEEP_TIME}",
"DASHSCOPE_VIDEO_RETRY_TIMES": "${DASHSCOPE_VIDEO_RETRY_TIMES}"
}
}
}
}
@@ -0,0 +1,97 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import json
import os
from dotenv import load_dotenv
from aworld.agents.llm_agent import Agent
from aworld.config.conf import AgentConfig, TaskConfig
from aworld.core.task import Task
from aworld.runner import Runners
from aworld.runners.callback.decorator import reg_callback
from aworld.tools.mcp_tool import async_mcp_tool
@reg_callback("print_content")
def simple_callback(content):
"""Simple callback function, prints content and returns it
Args:
content: Content to print
Returns:
The input content
"""
print(f"callback content: {content}")
return content
async def run():
load_dotenv()
llm_provider = os.getenv("LLM_PROVIDER_WEATHER", "openai")
llm_model_name = os.getenv("LLM_MODEL_NAME_WEATHER")
llm_api_key = os.getenv("LLM_API_KEY_WEATHER")
llm_base_url = os.getenv("LLM_BASE_URL_WEATHER")
llm_temperature = os.getenv("LLM_TEMPERATURE_WEATHER", 0.0)
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,
)
#mcp_servers = ["filewrite_server", "fileread_server"]
#mcp_servers = ["amap-amap-sse","filewrite_server", "fileread_server"]
#mcp_servers = ["file_server"]
#mcp_servers = ["amap-amap-sse"]
mcp_servers = ["aworldsearch_server"]
#mcp_servers = ["gen_video_server"]
# mcp_servers = ["picsearch_server"]
#mcp_servers = ["gen_audio_server"]
#mcp_servers = ["playwright"]
#mcp_servers = ["tavily-mcp"]
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)
print("-------------------mcp_config--------------",mcp_config)
#sand_box = Sandbox(mcp_servers=mcp_servers,mcp_config=mcp_config)
# You can specify sandbox
#sand_box = Sandbox(mcp_servers=mcp_servers, mcp_config=mcp_config,env_type=SandboxEnvType.K8S)
#sand_box = Sandbox(mcp_servers=mcp_servers, mcp_config=mcp_config,env_type=SandboxEnvType.SUPERCOMPUTER)
search_sys_prompt = "You are a versatile assistant"
search = Agent(
conf=agent_config,
name="search_agent",
system_prompt=search_sys_prompt,
mcp_config=mcp_config,
mcp_servers=mcp_servers,
#sandbox=sand_box,
)
# Run agent
# Runners.sync_run(input="Use tavily-mcp to check what tourist attractions are in Hangzhou", agent=search)
task = Task(
# input="Use tavily-mcp to check what tourist attractions are in Hangzhou",
# input="Use the file_server tool to analyze this audio link: https://amap-aibox-data.oss-cn-zhangjiakou.aliyuncs.com/.mp3",
# input="Use the amap-amap-sse tool to find hotels within one kilometer of West Lake in Hangzhou",
input="Use the aworldsearch_server tool to search for the origin of the Dragon Boat Festival",
# input="Use the picsearch_server tool to search for Captain America",
# input="Make sure to use the human_confirm tool to let the user confirm this message: 'Do you want to make a payment to this customer'",
# input="Use the gen_audio_server tool to convert this sentence to audio: 'Nice to meet you'",
#input="Use the gen_video_server tool to generate a video of this description: 'A cat walking alone on a snowy day'",
# input="First call the filewrite_server tool, then call the fileread_server tool",
# input="Use the playwright tool, with Google browser, search for the latest news about the Trump administration on www.baidu.com",
# input="Use tavily-mcp",
agent=search,
conf=TaskConfig(),
event_driven=True
)
async for output in Runners.streamed_run_task(task).stream_events():
print(f"Agent Ouput: {output}")
@@ -0,0 +1,48 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
"""
Simple tool callback example, demonstrating the basic functionality of callback registration and execution.
"""
# Import business package, its __init__.py will automatically import and register callback functions
import business
from aworld.runners.callback.decorator import reg_callback, CallbackRegistry
# Import CallbackRegistry
@reg_callback("mcp_server__action")
def simple_callback(content):
"""Simple callback function, prints content and returns it
Args:
content: Content to print
Returns:
The input content
"""
print(f"Callback function received content: {content}")
return content
def main():
"""Main function, demonstrating how to get and execute callback functions"""
# List all registered callback functions
# print("\n===== Registered Callback Functions =====")
# business.list_all_callbacks()
# Get and execute print_content callback function
print("\n===== Execute print_content Callback Function =====")
callback_func = CallbackRegistry.get("mcp_server__action")
if callback_func:
print("Callback function found, executing...")
result = callback_func("Hello, Callback!!!!!")
print(f"Callback function execution result: {result}")
else:
print("print_content callback function not found")
if __name__ == "__main__":
main()
@@ -0,0 +1,12 @@
import os
def main():
from dotenv import load_dotenv
load_dotenv()
print(os.environ)
uid = os.getenv('AWORLD_SEARCH_UID')
print(uid)
if __name__ == "__main__":
main()
@@ -0,0 +1,31 @@
from aworld.core.agent.base import AgentFactory
from aworld.core.context.base import Context
from aworld.core.event.base import Message
from aworld.runners.hook.hooks import PreLLMCallHook, PostLLMCallHook
from aworld.runners.hook.hook_factory import HookFactory
from aworld.utils.common import convert_to_snake
@HookFactory.register(name="TestPreLLMHook", desc="Test pre-LLM hook")
class TestPreLLMHook(PreLLMCallHook):
def name(self):
return convert_to_snake("TestPreLLMHook")
async def exec(self, message: Message, context: Context = None) -> Message:
agent = AgentFactory.agent_instance(message.sender)
context = message.context
context.context_info.set('step', 1)
return message
@HookFactory.register(name="TestPostLLMHook", desc="Test post-LLM hook")
class TestPostLLMHook(PostLLMCallHook):
def name(self):
return convert_to_snake("TestPostLLMHook")
async def exec(self, message: Message, context: Context = None) -> Message:
agent = AgentFactory.agent_instance(message.sender)
context = message.context
assert context.context_info.get('step') == 1
return message
@@ -0,0 +1,122 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import asyncio
import os
from dataclasses import dataclass, field
from dotenv import load_dotenv
from rich.table import Table
from rich.status import Status
from rich.console import Console
from aworld.agents.llm_agent import Agent
from aworld.config.conf import AgentConfig, TaskConfig
from aworld.core.task import Task
from aworld.output import MessageOutput, WorkSpace
from aworld.output.base import StepOutput, ToolResultOutput
from aworld.output.ui.base import AworldUI
from aworld.output.utils import consume_content
from aworld.runner import Runners
@dataclass
class RichAworldUI(AworldUI):
console: Console = field(default_factory=Console)
status: Status = None
workspace: WorkSpace = None
async def message_output(self, __output__: MessageOutput):
result = []
async def __log_item(item):
result.append(item)
self.console.print(item, end="")
if __output__.reason_generator or __output__.response_generator:
if __output__.reason_generator:
await consume_content(__output__.reason_generator, __log_item)
if __output__.reason_generator:
await consume_content(__output__.response_generator, __log_item)
else:
await consume_content(__output__.reasoning, __log_item)
await consume_content(__output__.response, __log_item)
# if __output__.tool_calls:
# await consume_content(__output__.tool_calls, __log_item)
self.console.print("")
async def tool_result(self, output: ToolResultOutput):
"""
tool_result
"""
table = Table(show_header=False, header_style="bold magenta",
title=f"Call Tools#ID_{output.origin_tool_call.id}")
table.add_column("name", style="dim", width=12)
table.add_column("content")
table.add_row("function_name", output.origin_tool_call.function.name)
table.add_row("arguments", output.origin_tool_call.function.arguments)
table.add_row("result", output.data)
self.console.print(table)
async def step(self, output: StepOutput):
if output.status == "START":
self.console.print(f"[bold green]{output.name} ✈️START ...")
self.status = self.console.status(f"[bold green]{output.name} RUNNING ...")
self.status.start()
elif output.status == "FINISHED":
self.status.stop()
self.console.print(f"[bold green]{output.name} 🛬FINISHED ...")
elif output.status == "FAILED":
self.status.stop()
self.console.print(f"[bold red]{output.name} 💥FAILED ...")
else:
self.status.stop()
self.console.print(f"============={output.name} ❓❓❓UNKNOWN#{output.status} ======================")
def run():
load_dotenv()
agent_config = AgentConfig(
llm_provider="openai",
llm_model_name=os.environ["LLM_MODEL_NAME"],
llm_api_key=os.environ["LLM_API_KEY"],
llm_base_url=os.environ["LLM_BASE_URL"]
)
AMAP_API_KEY = os.environ['AMAP_API_KEY']
amap_sys_prompt = "You are a helpful agent."
amap_agent = Agent(
conf=agent_config,
name="amap_agent",
system_prompt=amap_sys_prompt,
mcp_servers=["amap-amap-sse"], # MCP server name for agent to use
history_messages=100,
mcp_config={
"mcpServers": {
"amap-amap-sse": {
"url": f"https://mcp.amap.com/sse?key={AMAP_API_KEY}",
"timeout": 5.0,
"sse_read_timeout": 300.0
}
}
}
)
user_input = (
"How long does it take to drive from Hangzhou of Zhejiang to Weihai of Shandong (generate a table with columns for starting point, destination, duration, distance), "
"which cities are passed along the way, what interesting places are there along the route, "
"and finally generate the content as markdown and save it")
async def _run(agent, input):
task = Task(
input=input,
agent=agent,
conf=TaskConfig()
)
rich_ui = RichAworldUI()
async for output in Runners.streamed_run_task(task).stream_events():
await AworldUI.parse_output(output, rich_ui)
asyncio.run(_run(amap_agent, user_input))
@@ -0,0 +1,103 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import json
from typing import Dict, Any, List, Optional
from pydantic import Field
from aworld.tools import FunctionTools
# Create another function tool server with a different name
function = FunctionTools("another-server",
description="Another function tools server example")
@function.tool(description="Get weather information for a city")
def get_weather(
city: str = Field(
description="City name to get weather for"
),
days: int = Field(
3,
description="Number of days for forecast"
)
) -> Dict[str, Any]:
"""Get weather information for a city (simulated data)"""
# Simulated weather data
weather_types = ["Sunny", "Cloudy", "Rainy", "Windy", "Snowy"]
import random
forecast = []
for i in range(days):
forecast.append({
"date": f"2023-06-{i+1:02d}",
"weather": random.choice(weather_types),
"temperature": {
"min": random.randint(15, 25),
"max": random.randint(26, 35)
},
"humidity": random.randint(30, 90)
})
return {
"city": city,
"country": "Sample Country",
"forecast": forecast
}
@function.tool(description="Convert currency from one to another")
def convert_currency(
amount: float = Field(
description="Amount to convert"
),
from_currency: str = Field(
description="Source currency code (e.g. USD)"
),
to_currency: str = Field(
description="Target currency code (e.g. EUR)"
)
) -> Dict[str, Any]:
"""Currency conversion (simulated data)"""
# Simulated exchange rate data
rates = {
"USD": 1.0,
"EUR": 0.85,
"GBP": 0.75,
"JPY": 110.0,
"CNY": 6.5
}
# Check if currencies are supported
if from_currency not in rates:
return {"error": f"Currency {from_currency} not supported"}
if to_currency not in rates:
return {"error": f"Currency {to_currency} not supported"}
# Calculate conversion
usd_amount = amount / rates[from_currency]
converted_amount = usd_amount * rates[to_currency]
return {
"from": {
"currency": from_currency,
"amount": amount
},
"to": {
"currency": to_currency,
"amount": round(converted_amount, 2)
},
"rate": round(rates[to_currency] / rates[from_currency], 4)
}
if __name__ == "__main__":
# Test tools
print("=== Testing get_weather tool ===")
weather = function.call_tool("get_weather", {"city": "Beijing"})
print("\n=== Testing convert_currency tool ===")
conversion = function.call_tool("convert_currency", {
"amount": 100,
"from_currency": "USD",
"to_currency": "EUR"
})
print(conversion)
@@ -0,0 +1,227 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import asyncio
import json
import logging
import os
import pprint
from typing import List, Dict, Any, Optional, Union
import aiohttp
from mcp.types import TextContent
from pydantic import Field
from aworld.tools import FunctionTools
# Create function tools server
function = FunctionTools("aworldsearch_server",
description="Search service for AWorld")
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 non-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:
# Only keep required fields
filtered_doc = {field: doc[field] for field in required_fields}
valid_docs.append(filtered_doc)
return valid_docs
except Exception:
return []
@function.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 main search 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():
# Use environment variable to forcibly override the input num parameter
num = int(env_total_num)
# If no query is 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 field
)
# When query count >=3 or slice_num is set, use the 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 field
)
# 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 dictionary for URL deduplication
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 field
)
# Test code
if __name__ == "__main__":
import pprint
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# List all tools
print("Tool list:")
tools = function.list_tools()
print(tools)
res = function.call_tool("search", {"query_list": ["Tencent financial report", "Baidu financial report", "Alibaba financial report"],})
print(res)
# for tool in tools:
# print(f"Tool name: {tool.name}")
# print(f"Tool description: {tool.description}")
# print(f"Parameter schema: {tool.inputSchema}")
# if tool.annotations:
# print(f"Annotation information:")
# print(f" - Title: {tool.annotations.title}")
# print()
@@ -0,0 +1,221 @@
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()
@@ -0,0 +1,88 @@
{
"mcpServers": {
"amap-amap-sse": {
"type": "sse",
"url": "https://mcp.amap.com/sse?key=${AMAP_AMAP_SSE_KEY}",
"timeout": 5.0,
"sse_read_timeout": 300.0
},
"tavily-mcp": {
"type": "stdio",
"command": "npx",
"args": ["-y", "tavily-mcp@0.1.2"],
"env": {
"TAVILY_API_KEY": "tvly-dev-"
}
},
"aworldsearch_server": {
"type": "function_tool"
},
"aworldsearch_server1": {
"command": "python",
"args": [
"-m",
"mcp_servers.aworldsearch_server"
],
"env": {
"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}"
}
},
"picsearch_server": {
"command": "python",
"args": [
"-m",
"mcp_servers.picsearch_server"
],
"env": {
"PIC_SEARCH_URL": "${PIC_SEARCH_URL}",
"PIC_SEARCH_TOTAL_NUM": "${PIC_SEARCH_TOTAL_NUM}",
"PIC_SEARCH_SLICE_NUM": "${PIC_SEARCH_SLICE_NUM}",
"PIC_SEARCH_DOMAIN": "${PIC_SEARCH_DOMAIN}",
"PIC_SEARCH_SEARCHMODE": "${PIC_SEARCH_SEARCHMODE}",
"PIC_SEARCH_SOURCE": "${PIC_SEARCH_SOURCE}"
}
},
"gen_audio_server": {
"command": "python",
"args": [
"-m",
"mcp_servers.gen_audio_server"
],
"env": {
"AUDIO_TASK_URL": "${AUDIO_TASK_URL}",
"AUDIO_QUERY_URL": "${AUDIO_QUERY_URL}",
"AUDIO_APP_KEY": "${AUDIO_APP_KEY}",
"AUDIO_SECRET": "${AUDIO_SECRET}",
"AUDIO_SAMPLE_RATE": "${AUDIO_SAMPLE_RATE}",
"AUDIO_AUDIO_FORMAT": "${AUDIO_AUDIO_FORMAT}",
"AUDIO_TTS_VOICE": "${AUDIO_TTS_VOICE}",
"AUDIO_TTS_SPEECH_RATE": "${AUDIO_TTS_SPEECH_RATE}",
"AUDIO_TTS_VOLUME": "${AUDIO_TTS_VOLUME}",
"AUDIO_TTS_PITCH": "${AUDIO_TTS_PITCH}",
"AUDIO_VOICE_TYPE": "${AUDIO_VOICE_TYPE}"
}
},
"gen_video_server": {
"command": "python",
"args": [
"-m",
"mcp_servers.gen_video_server"
],
"env": {
"DASHSCOPE_API_KEY": "${DASHSCOPE_API_KEY}",
"DASHSCOPE_VIDEO_SUBMIT_URL": "${DASHSCOPE_VIDEO_SUBMIT_URL}",
"DASHSCOPE_QUERY_BASE_URL": "${DASHSCOPE_QUERY_BASE_URL}",
"DASHSCOPE_VIDEO_MODEL": "${DASHSCOPE_VIDEO_MODEL}",
"DASHSCOPE_VIDEO_SIZE": "${DASHSCOPE_VIDEO_SIZE}",
"DASHSCOPE_VIDEO_SLEEP_TIME": "${DASHSCOPE_VIDEO_SLEEP_TIME}",
"DASHSCOPE_VIDEO_RETRY_TIMES": "${DASHSCOPE_VIDEO_RETRY_TIMES}"
}
}
}
}
@@ -0,0 +1,110 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import asyncio
import json
import os
from dotenv import load_dotenv
from aworld.agents.llm_agent import Agent
from aworld.config.conf import AgentConfig, TaskConfig
from aworld.core.task import Task
from aworld.runner import Runners
from aworld.runners.callback.decorator import reg_callback
@reg_callback("print_content")
def simple_callback(content):
"""Simple callback function, prints content and returns it
Args:
content: Content to print
Returns:
The input content
"""
print(f"callback content: {content}")
return content
async def run():
load_dotenv()
llm_provider = os.getenv("LLM_PROVIDER_WEATHER", "openai")
llm_model_name = os.getenv("LLM_MODEL_NAME_WEATHER")
llm_api_key = os.getenv("LLM_API_KEY_WEATHER")
llm_base_url = os.getenv("LLM_BASE_URL_WEATHER")
llm_temperature = os.getenv("LLM_TEMPERATURE_WEATHER", 0.0)
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,
)
#mcp_servers = ["filewrite_server", "fileread_server"]
#mcp_servers = ["amap-amap-sse","filewrite_server", "fileread_server"]
#mcp_servers = ["file_server"]
#mcp_servers = ["amap-amap-sse"]
mcp_servers = ["aworldsearch_server"]
#mcp_servers = ["gen_video_server"]
# mcp_servers = ["picsearch_server"]
#mcp_servers = ["gen_audio_server"]
#mcp_servers = ["playwright"]
#mcp_servers = ["tavily-mcp"]
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)
print("-------------------mcp_config--------------",mcp_config)
#sand_box = Sandbox(mcp_servers=mcp_servers,mcp_config=mcp_config)
# You can specify sandbox
#sand_box = Sandbox(mcp_servers=mcp_servers, mcp_config=mcp_config,env_type=SandboxEnvType.K8S)
#sand_box = Sandbox(mcp_servers=mcp_servers, mcp_config=mcp_config,env_type=SandboxEnvType.SUPERCOMPUTER)
search_sys_prompt = "You are a versatile assistant"
search = Agent(
conf=agent_config,
name="search_agent",
system_prompt=search_sys_prompt,
mcp_config=mcp_config,
mcp_servers=mcp_servers,
#sandbox=sand_box,
)
# Run agent
# Runners.sync_run(input="Use tavily-mcp to check what tourist attractions are in Hangzhou", agent=search)
task = Task(
# input="Use tavily-mcp to check what tourist attractions are in Hangzhou",
# input="Use the file_server tool to analyze this audio link: https://amap-aibox-data.oss-cn-zhangjiakou.aliyuncs.com/.mp3",
# input="Use the amap-amap-sse tool to find hotels within one kilometer of West Lake in Hangzhou",
input="Use the aworldsearch_server tool to search for the origin of the Dragon Boat Festival",
# input="Use the picsearch_server tool to search for Captain America",
# input="Make sure to use the human_confirm tool to let the user confirm this message: 'Do you want to make a payment to this customer'",
# input="Use the gen_audio_server tool to convert this sentence to audio: 'Nice to meet you'",
#input="Use the gen_video_server tool to generate a video of this description: 'A cat walking alone on a snowy day'",
#input="How's the weather in New York, Shanghai, and Beijing right now? These are three cities, I hope the large model returns three tools when it identifies tool calls",
# input="First call the filewrite_server tool, then call the fileread_server tool",
# input="Use the playwright tool, with Google browser, search for the latest news about the Trump administration on www.baidu.com",
# input="Use tavily-mcp",
agent=search,
conf=TaskConfig(),
event_driven=True
)
#result = Runners.sync_run_task(task)
#result = Runners.sync_run_task(task)
#result = await Runners.streamed_run_task(task)
# result = await Runners.run_task(task)
# print(
# "----------------------------------------------------------------------------------------------"
# )
# print(result)
# async for chunk in Runners.streamed_run_task(task).stream_events():
# print(chunk, end="", flush=True)
async for output in Runners.streamed_run_task(task).stream_events():
print(f"Agent Ouput: {output}")
@@ -0,0 +1,55 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import asyncio
import json
import os
from dotenv import load_dotenv
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
async def run():
load_dotenv()
llm_provider = os.getenv("LLM_PROVIDER_WEATHER", "openai")
llm_model_name = os.getenv("LLM_MODEL_NAME_WEATHER")
llm_api_key = os.getenv("LLM_API_KEY_WEATHER")
llm_base_url = os.getenv("LLM_BASE_URL_WEATHER")
llm_temperature = os.getenv("LLM_TEMPERATURE_WEATHER", 0.0)
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,
)
mcp_servers = ["tavily-mcp"]
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)
search_sys_prompt = "You are a versatile assistant"
search = Agent(
conf=agent_config,
name="search_agent",
system_prompt=search_sys_prompt,
mcp_config=mcp_config,
mcp_servers=mcp_servers,
)
# Run agent
task = Task(
input="Use tavily-mcp to check what tourist attractions are in Hangzhou",
agent=search,
conf=TaskConfig(),
)
result = Runners.sync_run_task(task)
print( "----------------------------------------------------------------------------------------------")
print(result)
@@ -0,0 +1,78 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import logging
def run():
from aworld.tools import get_function_tools
aworldsearch_server = get_function_tools("aworldsearch_server")
print(aworldsearch_server.list_tools())
res = aworldsearch_server.call_tool("search", {"query_list": ["Tencent financial report", "Baidu financial report", "Alibaba financial report"],})
print(res)
another_server = get_function_tools("another-server")
print(another_server.list_tools())
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Step 1: Import different modules, which will automatically register their respective FunctionTools instances
print("=== Step 1: Import modules, automatically register FunctionTools instances ===")
# Import aworldsearch_function_tools module, which registers "aworldsearch-server"
print("Imported aworldsearch_function_tools module")
# Import another_function_tools module, which registers "another-server"
print("Imported another_function_tools module")
# Step 2: Get FunctionTools instances by name
print("\n=== Step 2: Get FunctionTools instances by name ===")
from aworld.tools import get_function_tools, list_function_tools
# List all registered FunctionTools servers
print(f"All registered servers: {list_function_tools()}")
# Get server instance by specific name
aworldsearch_server = get_function_tools("aworldsearch-server")
print(f"Retrieved server: {aworldsearch_server.name}")
print(f"Server description: {aworldsearch_server.description}")
another_server = get_function_tools("another-server")
print(f"Retrieved server: {another_server.name}")
print(f"Server description: {another_server.description}")
# Step 3: Use the retrieved instances to call methods
print("\n=== Step 3: Use the retrieved instances to call methods ===")
# List all tools of aworldsearch server
print("aworldsearch-server tool list:")
for tool in aworldsearch_server.list_tools():
print(f" - {tool.name}: {tool.description}")
# List all tools of another server
print("\nanother-server tool list:")
for tool in another_server.list_tools():
print(f" - {tool.name}: {tool.description}")
# Step 4: Call tools
print("\n=== Step 4: Call tool examples ===")
# Call aworldsearch server's tool
if "demo_search" in [tool.name for tool in aworldsearch_server.list_tools()]:
print("Calling demo_search tool:")
result = aworldsearch_server.call_tool("demo_search", {"query_list": ["Test query"]})
print(result)
# Call another server's tool
if "get_weather" in [tool.name for tool in another_server.list_tools()]:
print("\nCalling get_weather tool:")
result = another_server.call_tool("get_weather", {"city": "Beijing"})
print(result)
if __name__ == "__main__":
pass # Main logic has already been executed at the module level
@@ -0,0 +1,238 @@
import unittest
import uuid
import asyncio
import random
import time
from typing import List
import pytest
from aworld.core.event.base import Constants, Message
from aworld.runners.state_manager import (
EventRuntimeStateManager,
RunNode,
RunNodeBusiType,
RunNodeStatus,
RuntimeStateManager,
)
class StateManagerTest(unittest.TestCase):
def test_runtime_state_manager(self):
state_manager = RuntimeStateManager()
session_id = "1"
node = state_manager.create_node(busi_type=RunNodeBusiType.TASK,
busi_id="1", session_id=session_id, msg_id="1")
state_manager.run_node(node.node_id)
node = state_manager.get_node(node.node_id)
assert node.status == RunNodeStatus.RUNNING
state_manager.break_node(node.node_id)
node = state_manager.get_node(node.node_id)
assert node.status == RunNodeStatus.BREAKED
state_manager.run_succeed(node.node_id)
node = state_manager.get_node(node.node_id)
assert node.status == RunNodeStatus.SUCCESS
node = state_manager.create_node(busi_type=RunNodeBusiType.TASK,
busi_id="2", session_id=session_id, msg_id="2", msg_from="1")
state_manager.run_node(node.node_id)
state_manager.run_failed(node.node_id)
node = state_manager.get_node(node.node_id)
assert node.status == RunNodeStatus.FAILED
node = state_manager.create_node(busi_type=RunNodeBusiType.TASK,
busi_id="3", session_id=session_id, msg_id="3", msg_from="1")
state_manager.run_node(node.node_id)
state_manager.run_timeout(node.node_id)
node = state_manager.get_node(node.node_id)
assert node.status == RunNodeStatus.TIMEOUT
node = state_manager.create_node(busi_type=RunNodeBusiType.TASK,
busi_id="4", session_id=session_id, msg_id="4", msg_from="3")
state_manager.run_succeed(node.node_id)
nodes = state_manager.get_nodes(session_id=session_id)
self.build_run_flow(nodes)
def build_run_flow(self, nodes: List[RunNode]):
graph = {}
start_nodes = []
for node in nodes:
if hasattr(node, 'parent_node_id') and node.parent_node_id:
if node.parent_node_id not in graph:
graph[node.parent_node_id] = []
graph[node.parent_node_id].append(node.node_id)
else:
start_nodes.append(node.node_id)
for start in start_nodes:
print("-----------------------------------")
self._print_tree(graph, start, "", True)
print("-----------------------------------")
def _print_tree(self, graph, node_id, prefix, is_last):
print(prefix + ("└── " if is_last else "├── ") + node_id)
if node_id in graph:
children = graph[node_id]
for i, child in enumerate(children):
self._print_tree(graph, child, prefix +
(" " if is_last else ""), i == len(children) - 1)
@pytest.mark.asyncio
async def test_node_group_create(self):
state_manager: EventRuntimeStateManager = EventRuntimeStateManager.instance()
await state_manager.create_group(
group_id="test_group0",
session_id="session1",
root_node_ids=["root_message_id1", "root_message_id2", "root_message_id3"],
parent_group_id="test_parant_group"
)
group = state_manager.get_group("test_group0")
assert group is not None
assert group.status == RunNodeStatus.INIT
@pytest.mark.asyncio
async def test_all_proccess(self):
state_manager: EventRuntimeStateManager = EventRuntimeStateManager.instance()
root_message_id1 = uuid.uuid4().hex
root_message_id2 = uuid.uuid4().hex
root_message_id3 = uuid.uuid4().hex
headers = {
"session_id": "session1",
"group_id": "test_group"
}
def get_headers(root_message_id):
return {
"root_message_id": root_message_id,
**headers
}
sub_node_message1 = Message(
id=root_message_id1,
category=Constants.AGENT,
session_id="session1",
topic="test_topic",
headers=get_headers(root_message_id1)
)
sub_node_message2 = Message(
id=root_message_id2,
category=Constants.AGENT,
session_id="session1",
topic="test_topic",
headers=get_headers(root_message_id2)
)
sub_node_message3 = Message(
id=root_message_id3,
category=Constants.AGENT,
session_id="session1",
topic="test_topic",
headers=get_headers(root_message_id3)
)
sub_tasks = []
async def sub_group_task(message: Message):
await asyncio.sleep(random.randint(1, 3))
state_manager.start_message_node(message)
await asyncio.sleep(random.randint(1, 3))
result_message = Message(
session_id="session1",
topic="test_topic",
headers=message.headers
)
state_manager.save_message_handle_result("sub_node_message1", message, result_message)
state_manager.end_message_node(message)
await state_manager.finish_sub_group(message.headers["group_id"], message.headers["root_message_id"],
[result_message])
sub_tasks.append(asyncio.create_task(sub_group_task(sub_node_message1)))
sub_tasks.append(asyncio.create_task(sub_group_task(sub_node_message2)))
sub_tasks.append(asyncio.create_task(sub_group_task(sub_node_message3)))
await state_manager.create_group(
group_id=headers["group_id"],
session_id=headers["session_id"],
root_node_ids=[root_message_id1, root_message_id2, root_message_id3],
parent_group_id="test_parant_group"
)
print(f"create group complete, group_id: {headers['group_id']}")
group = state_manager.get_group(headers["group_id"])
assert group is not None
await asyncio.gather(*sub_tasks)
print(f"sub group complete, group_id: {headers['group_id']}")
group = state_manager.get_group(headers["group_id"])
assert group is not None
assert group.status == RunNodeStatus.SUCCESS
group_detail = state_manager.query_group_detail(headers["group_id"])
assert group_detail is not None
for subgroup in group_detail.sub_groups:
assert subgroup.status == RunNodeStatus.SUCCESS
def test_query_by_task(self):
state_manager = RuntimeStateManager()
session_id = str(uuid.uuid4())
task_id1 = str(uuid.uuid4())
task_id2 = str(uuid.uuid4())
agent_id1 = str(uuid.uuid4())
agent_id2 = str(uuid.uuid4())
node1 = state_manager.create_node(
busi_type=RunNodeBusiType.TASK,
busi_id=task_id1,
session_id=session_id,
task_id=task_id1,
msg_id=str(uuid.uuid4())
)
time.sleep(0.01)
node2 = state_manager.create_node(
busi_type=RunNodeBusiType.AGENT,
busi_id=agent_id1,
session_id=session_id,
task_id=task_id1,
msg_id=str(uuid.uuid4())
)
time.sleep(0.01)
node3 = state_manager.create_node(
busi_type=RunNodeBusiType.TASK,
busi_id=task_id2,
session_id=session_id,
task_id=task_id2,
msg_id=str(uuid.uuid4())
)
time.sleep(0.01)
node4 = state_manager.create_node(
busi_type=RunNodeBusiType.AGENT,
busi_id=agent_id2,
session_id=session_id,
task_id=task_id1,
msg_id=str(uuid.uuid4())
)
result1 = state_manager.query_by_task(task_id=task_id1)
self.assertEqual(len(result1), 3)
self.assertGreater(result1[0].create_time, result1[1].create_time)
result2 = state_manager.query_by_task(task_id=task_id1, busi_typ=RunNodeBusiType.AGENT, busi_id=agent_id1)
self.assertEqual(len(result2), 1)
self.assertEqual(result2[0].node_id, node2.node_id)
result3 = state_manager.query_by_task(task_id=str(uuid.uuid4()))
self.assertEqual(len(result3), 0)
with self.assertRaises(Exception):
state_manager.query_by_task(task_id=task_id1, busi_typ=RunNodeBusiType.AGENT)
with self.assertRaises(Exception):
state_manager.query_by_task(task_id=task_id1, busi_id=agent_id1)
@@ -0,0 +1,2 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
@@ -0,0 +1,144 @@
from aworld.trace.server import get_trace_server
from aworld.trace.constants import RunType, SPAN_NAME_PREFIX_EVENT_AGENT
from aworld.trace.instrumentation import semconv
def _get_agent_show_name(span: dict):
agent_name_prefix = SPAN_NAME_PREFIX_EVENT_AGENT
name = span.get("name")
if name and name.startswith(agent_name_prefix):
name = name[len(agent_name_prefix):]
if name and '---' in name:
name = name.split('---', 1)[0]
return name
def _remove_span_detail(root_spans: list):
keys_to_keep = {'span_id', 'show_name', 'task_group_id', 'event_id'}
for span in root_spans:
keys_to_remove = [key for key in span.keys() if key not in keys_to_keep]
for key in keys_to_remove:
span.pop(key, None)
if 'children' in span:
_remove_span_detail(span['children'])
def _build_graph(root_spans: list):
nodes = []
edges = []
group_id_counter = 0
def __process_group_span(parent_spans, group_id, group_spans):
nonlocal group_id_counter
group_id_counter += 1
# add group node
group_node = {
'span_id': f'group_{group_id_counter}',
'group_id': group_id,
'show_name': 'Task Group'
}
nodes.append(group_node)
# add edges from parent_spans to group node
for parent_span in parent_spans:
edges.append({
'source': parent_span['span_id'],
'target': group_node['span_id']
})
# add edges from group node to children spans
last_spans = []
for child in group_spans:
edges.append({
'source': group_node['span_id'],
'target': child['span_id']
})
last_spans.extend(__process_span(child))
return last_spans
def __process_span(span):
nonlocal group_id_counter
nodes.append(span)
if 'children' in span:
groups = {}
for child in span['children']:
group_id = child.get('task_group_id', id(child))
if group_id not in groups:
groups[group_id] = []
groups[group_id].append(child)
last_spans = [span] # The leaf nodes of the current subtree
for group_id, group_spans in groups.items():
if len(group_spans) > 1:
parent_spans = last_spans
last_spans = __process_group_span(parent_spans, group_id, group_spans)
else:
child_span = group_spans[0]
# add edges from last_spans to child
for prev_node in last_spans:
edges.append({
'source': prev_node['span_id'],
'target': child_span['span_id']
})
last_spans = __process_span(child_span)
return last_spans
for span in root_spans:
__process_span(span)
return {
'nodes': nodes,
'edges': edges
}
def get_agent_flow(trace_id):
storage = get_trace_server().get_storage()
spans = storage.get_all_spans(trace_id)
spans_dict = {span.span_id: span.dict() for span in spans}
children_spans = []
filtered_spans = {}
for span_id, span in spans_dict.items():
if span.get('is_event', False) and span.get('run_type') == RunType.AGNET.value:
span['show_name'] = _get_agent_show_name(span)
span['event_id'] = span.get('attributes', {}).get('event.id')
filtered_spans[span_id] = span
sub_task_spans = []
for span in list(filtered_spans.values()):
skip_this_span = False
parent_id = span['parent_id'] if span['parent_id'] else None
while parent_id and parent_id not in filtered_spans:
parent_span = spans_dict.get(parent_id)
if parent_span and parent_span.get('run_type') == RunType.TASK.value:
if str(parent_span['attributes'].get(semconv.TASK_IS_SUB_TASK)).lower() == 'true':
sub_task_spans.append(span)
skip_this_span = True
break
else:
print(f"parent_span_name: {parent_span['name']}")
span['task_group_id'] = parent_span['attributes'].get(semconv.TASK_GROUP_ID)
parent_id = parent_span['parent_id'] if parent_span and parent_span['parent_id'] else None
if skip_this_span:
continue
if parent_id:
parent_span = filtered_spans.get(parent_id)
if not parent_span:
continue
if 'children' not in parent_span:
parent_span['children'] = []
parent_span['children'].append(span)
children_spans.append(span)
filtered_span_list = [span for span in filtered_spans.values() if span not in sub_task_spans]
root_spans = [span for span in filtered_span_list
if span not in children_spans]
data = _build_graph(root_spans)
_remove_span_detail(data["nodes"])
return data
@@ -0,0 +1,41 @@
import asyncio
import pytest
import aworld.trace as trace
from aworld.logs.util import logger
trace.configure()
async def async_handler(name):
async with trace.span("async_handler") as span:
logger.info(f"async_handler start {name}")
await asyncio.sleep(1)
logger.info(f"async_handler end {name}")
async def async_handler2(name):
span = trace.get_current_span()
logger.info(f"async_handler2 span: {span.get_trace_id()}")
logger.info(f"async_handler2 start {name}")
await asyncio.sleep(1)
logger.info(f"async_handler2 end {name}")
@pytest.mark.asyncio
async def test1():
logger.info(f"hello test1")
task = asyncio.create_task(async_handler('test1'))
# await task
logger.info(f"hello test1 end")
@pytest.mark.asyncio
async def test2():
async with trace.span("test2") as span:
logger.info(f"hello test2")
task = asyncio.create_task(async_handler2(
'test2'))
# await task
logger.info(f"hello test2 end")
@@ -0,0 +1,24 @@
import time
class TestClassA:
def classa_function_1(self):
print("classa_function_1")
def classa_function_2(self):
time.sleep(0.02)
print("classa_function_2")
def classa_function_3(self):
print("classa_function_3")
class TestClassB:
def classb_function_1(self):
time.sleep(0.02)
print("classb_function_1")
def classb_function_2(self):
a = TestClassA()
a.classa_function_1()
a.classa_function_2()
a.classa_function_3()
print("classb_function_2")
@@ -0,0 +1,54 @@
import os
import time
import threading
from aworld.trace.config import ObservabilityConfig
from aworld.trace.instrumentation.fastapi import instrument_fastapi
from aworld.trace.instrumentation.requests import instrument_requests
from aworld.logs.util import logger, trace_logger
import aworld.trace as trace
from aworld.utils.import_package import import_packages
import_packages(['fastapi', 'uvicorn']) # noqa
import fastapi # noqa
import uvicorn # noqa
os.environ["MONITOR_SERVICE_NAME"] = "otlp_example"
os.environ["ANT_OTEL_ENDPOINT"] = "https://antcollector.alipay.com/namespace/aworld/task/aworld/otlp/api/v1/metrics"
trace.configure(ObservabilityConfig(
metrics_provider="otlp",
metrics_backend="antmonitor"
))
instrument_fastapi()
instrument_requests()
app = fastapi.FastAPI()
@app.get("/api/hello")
async def hello():
return {"message": "Hello World"}
def invoke_api():
import requests
response = requests.get('http://127.0.0.1:7071/api/hello')
logger.info(f"invoke_api response={response.text}")
def main():
logger.info("main running")
with trace.span("test_fastapi") as span:
trace_logger.info("start invoke_api")
invoke_api()
# if __name__ == "__main__":
# server_thread = threading.Thread(
# target=lambda: uvicorn.run(app, host="0.0.0.0", port=7071),
# daemon=True
# )
# server_thread.start()
# time.sleep(1)
# main()
# server_thread.join()
@@ -0,0 +1,45 @@
import threading
import flask
from aworld.trace.instrumentation.flask import instrument_flask
from aworld.trace.instrumentation.requests import instrument_requests
from aworld.logs.util import logger, trace_logger
import aworld.trace as trace
import os
from aworld.trace.config import ObservabilityConfig
os.environ["MONITOR_SERVICE_NAME"] = "otlp_example"
os.environ["ANT_OTEL_ENDPOINT"] = "https://antcollector.alipay.com/namespace/aworld/task/aworld/otlp/api/v1/metrics"
trace.configure(ObservabilityConfig(
metrics_provider="otlp",
metrics_backend="antmonitor"
))
instrument_flask()
instrument_requests()
app = flask.Flask(__name__)
@app.route('/api/test')
def test():
return 'Hello, World!'
def invoke_api():
import requests
response = requests.get('http://localhost:7070/api/test')
logger.info(f"invoke_api response={response.text}")
def main():
logger.info("main running")
with trace.span("test_flask") as span:
trace_logger.info("start invoke_api")
invoke_api()
# if __name__ == "__main__":
# thread = threading.Thread(target=lambda: app.run(port=7070), daemon=True)
# thread.start()
# main()
# thread.join()
@@ -0,0 +1,25 @@
import threading
import aworld.trace as trace
import os
import time
from aworld.trace.instrumentation.threading import instrument_theading
from aworld.logs.util import logger, trace_logger
os.environ["MONITOR_SERVICE_NAME"] = "otlp_example"
trace.configure()
instrument_theading()
def child_thread_func():
logger.info("child thread running")
with trace.span("child_thread") as span:
trace_logger.info("child thread running")
time.sleep(1000)
def main():
logger.info("main running")
with trace.span("test_fastapi") as span:
trace_logger.info("start run child_thread_func")
threading.Thread(target=child_thread_func).start()
threading.Thread(target=child_thread_func).start()
@@ -0,0 +1,52 @@
import random
import time
import os
os.environ["MONITOR_SERVICE_NAME"] = "otlp_example"
# os.environ["LOGFIRE_WRITE_TOKEN"] = ""
os.environ["ANT_OTEL_ENDPOINT"] = "https://antcollector.alipay.com/namespace/aworld/task/aworld/otlp/api/v1/metrics"
os.environ["METRICS_SYSTEM_ENABLED"] = "true"
from aworld.metrics.metric import MetricType
from aworld.metrics.context_manager import MetricContext, ApiMetricTracker
from aworld.metrics.template import MetricTemplate
MetricContext.configure(provider="otlp",
backend="antmonitor"
)
my_counter = MetricTemplate(
type=MetricType.COUNTER,
name="my_counter",
description="My custom counter",
unit="1"
)
my_gauge = MetricTemplate(
type=MetricType.GAUGE,
name="my_gauge"
)
my_histogram = MetricTemplate(
type=MetricType.HISTOGRAM,
name="my_histogram",
buckets=[2,4,6,8,10]
)
@ApiMetricTracker()
def api():
time.sleep(random.uniform(0, 1))
def custom_code():
with ApiMetricTracker("test_custom_code"):
time.sleep(random.uniform(0, 1))
# if __name__ == '__main__':
# while 1:
# MetricContext.count(my_counter, 1, {"test_label": "b"})
# MetricContext.gauge_set(my_gauge, random.randint(1, 10), {"test_label": "b"})
# # MetricContext.histogram_record(my_histogram, random.randint(0, 1000))
# # api()
# # custom_code()
# time.sleep(random.random())
@@ -0,0 +1,48 @@
import random
import time
from aworld.metrics.metric import MetricType
from aworld.metrics.context_manager import MetricContext, ApiMetricTracker
from aworld.metrics.template import MetricTemplate
MetricContext.configure(
provider="prometheus",
backend="console"
)
my_counter = MetricTemplate(
type=MetricType.COUNTER,
name="my_counter",
description="My custom counter",
unit="1"
)
my_gauge = MetricTemplate(
type=MetricType.GAUGE,
name="my_gauge"
)
my_histogram = MetricTemplate(
type=MetricType.HISTOGRAM,
name="my_histogram",
buckets=[2, 4, 6, 8, 10]
)
@ApiMetricTracker()
def api():
time.sleep(random.uniform(0, 1))
def custom_code():
with ApiMetricTracker("test_custom_code"):
time.sleep(random.uniform(0, 1))
# if __name__ == '__main__':
# while 1:
# MetricContext.count(my_counter, random.randint(1, 10))
# MetricContext.gauge_set(my_gauge, random.randint(1, 10))
# MetricContext.histogram_record(my_histogram, random.randint(1, 10))
# api()
# custom_code()
# time.sleep(random.random())
@@ -0,0 +1,78 @@
import os # noqa
# os.environ["START_TRACE_SERVER"] = "false" # noqa
os.environ["MONITOR_SERVICE_NAME"] = "otlp_example" # noqa
# os.environ["OTLP_TRACES_ENDPOINT"] = "http://localhost:4318/v1/traces"
# os.environ["METRICS_SYSTEM_ENABLED"] = "true"
# os.environ["LOGFIRE_WRITE_TOKEN"] = (
# "Your logfire write token, "
# "create guide refer to "
# "https://logfire.pydantic.dev/docs/how-to-guides/create-write-tokens/"
# )
import aworld.trace as trace # noqa
from aworld.logs.util import logger, trace_logger
from aworld.trace.server import get_trace_server
from aworld.output.artifact import Artifact, ArtifactType
trace.configure(trace.ObservabilityConfig(trace_server_enabled=True))
class TestClass:
@trace.func_span(span_name="test_func_args")
def test_func(self, artifact: Artifact = None):
logger.info(f"this is a test func, artifact={artifact}")
@trace.func_span(span_name="test_func", attributes={"test_attr": "test_value"}, extract_args=["param1"], add_attr="add_attr_value")
def traced_func(param1: str = None, param2: int = None):
trace_logger.info("this is a traced func")
traced_func2(param1="func2_param1_value", param2=222)
traced_func3(param1="func3_param1_value", param2=333)
@trace.func_span(span_name="test_func_2", add_attr="add_attr_value")
def traced_func2(param1: str = None, param2: int = None):
name = 'func2'
trace_logger.info(f"this is a traced {name}")
raise Exception("this is a traced func2 exception")
@trace.func_span
def traced_func3(param1: str = None, param2: int = None):
trace_logger.info("this is a traced func3")
def main():
logger.info("this is a no trace log")
trace.auto_tracing("examples.trace.*", 0.01)
with trace.span("hello") as span:
span.set_attribute("parent_test_attr", "pppppp")
logger.info("hello aworld")
trace_logger.info("trace hello aworld")
with trace.span("child hello") as span2:
span2.set_attribute("child_test_attr", "cccccc")
logger.info("child hello aworld")
current_span = trace.get_current_span()
logger.info("trace_id=%s", current_span.get_trace_id())
try:
test_class = TestClass()
test_class.test_func(artifact=Artifact(
artifact_id="123",
artifact_type=ArtifactType.IMAGE,
content="123",
))
traced_func(param1="func1_param1_value", param2=111)
except Exception as e:
logger.error(f"exception: {e}")
# from examples.trace.autotrace_demo import TestClassB
# b = TestClassB()
# b.classb_function_1()
# b.classb_function_2()
# b.classb_function_1()
# b.classb_function_2()
if get_trace_server():
get_trace_server().join()
@@ -0,0 +1,69 @@
import os # noqa: E402
os.environ["MONITOR_SERVICE_NAME"] = "otlp_example" # noqa
os.environ["ANT_OTEL_ENDPOINT"] = "https://antcollector.alipay.com/namespace/aworld/task/aworld/otlp/api/v1/metrics" # noqa
os.environ["OTLP_TRACES_ENDPOINT"] = "https://antcollector.alipay.com/namespace/aworld/task/aworld_trace/otlp/api/v1/traces" # noqa
from aworld.trace.config import ObservabilityConfig
from aworld.logs.util import logger
from aworld.trace.baggage import BaggageContext
from aworld.trace.base import get_tracer_provider
from aworld.trace.instrumentation.requests import instrument_requests
from aworld.trace.instrumentation.flask import instrument_flask
import flask
import threading
import aworld.trace as trace
trace.configure(ObservabilityConfig(
trace_provider="otlp",
trace_backends=["other_otlp"],
trace_base_url="https://antcollector.alipay.com/namespace/aworld/task/aworld_trace/otlp/api/v1/traces",
metrics_provider="otlp",
metrics_backend="antmonitor",
metrics_base_url="https://antcollector.alipay.com/namespace/aworld/task/aworld/otlp/api/v1/metrics"
))
instrument_flask()
instrument_requests()
app = flask.Flask(__name__)
@app.route('/api/test')
def test():
sofa_trace_id = BaggageContext.get_baggage_value("attributes.sofa.traceid")
sofa_rpc_id = BaggageContext.get_baggage_value("attributes.sofa.rpcid")
sofa_pen_attrs = BaggageContext.get_baggage_value(
"attributes.sofa.penattrs")
sofa_sys_pen_attrs = BaggageContext.get_baggage_value(
"attributes.sofa.syspenattrs")
logger.info(
f"test sofa_trace_id={sofa_trace_id}, sofa_rpc_id={sofa_rpc_id}, sofa_pen_attrs={sofa_pen_attrs}, sofa_sys_pen_attrs={sofa_sys_pen_attrs}"
)
return 'Hello, World!'
def invoke_api():
import requests
session = requests.session()
session.headers.update({
"SOFA-TraceId": "12345678901234567890123456789012",
"SOFA-RpcId": "0.1.1",
"sofaPenAttrs": "key1=value1&key2=value2",
"sysPenAttrs": "key1=value1&key2=value2"
})
response = session.get('http://localhost:7070/api/test')
logger.info(f"invoke_api response={response.text}")
def main():
logger.info("main running")
invoke_api()
# if __name__ == "__main__":
# thread = threading.Thread(target=lambda: app.run(port=7070), daemon=True)
# thread.start()
# main()
# get_tracer_provider().force_flush(1000)
# thread.join()
@@ -0,0 +1,29 @@
import os
import json
from aworld.logs.util import logger, trace_logger
from typing import Sequence
import aworld.trace as trace
from aworld.trace.base import Span
from aworld.trace.span_cosumer import register_span_consumer, SpanConsumer
from aworld.logs.util import logger, trace_logger
os.environ["MONITOR_SERVICE_NAME"] = "otlp_example"
@register_span_consumer({"test_param": "MockSpanConsumer111"})
class MockSpanConsumer(SpanConsumer):
def __init__(self, test_param=None):
self._test_param = test_param
def consume(self, spans: Sequence[Span]) -> None:
for span in spans:
logger.info(
f"_test_param={self._test_param}, trace_id={span.get_trace_id()}, span_id={span.get_span_id()}, attributes={span.attributes}")
def main():
with trace.span("hello") as span:
span.set_attribute("parent_test_attr", "pppppp")
logger.info("hello aworld")
trace_logger.info("trace hello aworld")
@@ -0,0 +1,252 @@
import traceback
from aworld.agents.llm_agent import Agent
from aworld.config.conf import AgentConfig, ConfigDict
from aworld.core.common import Observation, ActionModel
from typing import Dict, Any, List, Union
from aworld.core.tool.base import ToolFactory
from aworld.models.llm import call_llm_model, acall_llm_model
from aworld.trace.config import ObservabilityConfig
from aworld.utils.common import sync_exec
from aworld.logs.util import logger
from aworld.core.agent.swarm import Swarm
from aworld.runner import Runners
from aworld.trace.server import get_trace_server
from aworld.runners.state_manager import RuntimeStateManager, RunNode
import aworld.trace as trace
trace.configure(ObservabilityConfig(trace_server_enabled=True,
metrics_provider="otlp",
metrics_backend="antmonitor",
metrics_base_url="https://antcollector.alipay.com/namespace/aworld/task/aworld/otlp/api/v1/metrics"))
class TraceAgent(Agent):
def __init__(self,
conf: Union[Dict[str, Any], ConfigDict, AgentConfig],
name: str,
**kwargs):
super().__init__(conf, name, **kwargs)
def policy(self, observation: Observation, info: Dict[str, Any] = {}, **kwargs) -> List[ActionModel]:
"""use trace tool to get trace data, and call llm to summary
Args:
observation: The state observed from tools in the environment.
info: Extended information is used to assist the agent to decide a policy.
Returns:
ActionModel sequence from agent policy
"""
self._finished = False
self.desc_transform()
tool_name = "trace"
tool = ToolFactory(tool_name, asyn=False)
tool.reset()
tool_params = {}
action = ActionModel(tool_name=tool_name,
action_name="get_trace",
agent_name=self.id(),
params=tool_params)
message = tool.step(action)
observation, _, _, _, _ = message.payload
llm_response = None
messages = self.messages_transform(content=observation.content,
sys_prompt=self.system_prompt,
agent_prompt=self.agent_prompt)
try:
llm_response = call_llm_model(
self.llm,
messages=messages,
model=self.model_name,
temperature=self.conf.llm_config.llm_temperature
)
logger.info(f"Execute response: {llm_response.message}")
except Exception as e:
logger.warn(traceback.format_exc())
raise e
finally:
if llm_response:
if llm_response.error:
logger.info(
f"{self.id()} llm result error: {llm_response.error}")
else:
logger.error(f"{self.id()} failed to get LLM response")
raise RuntimeError(
f"{self.id()} failed to get LLM response")
agent_result = sync_exec(self.model_output_parser.parse, llm_response, agent_id=self.id())
if not agent_result.is_call_tool:
self._finished = True
return agent_result.actions
async def async_policy(self, observation: Observation, info: Dict[str, Any] = {}, **kwargs) -> List[ActionModel]:
self._finished = False
self.desc_transform()
tool_name = "trace"
tool = ToolFactory(tool_name, asyn=False)
tool.reset()
tool_params = {}
action = ActionModel(tool_name=tool_name,
action_name='get_trace',
agent_name=self.id(),
params=tool_params)
message = tool.step([action])
observation, _, _, _, _ = message.payload
llm_response = None
messages = self.messages_transform(content=observation.content,
sys_prompt=self.system_prompt,
agent_prompt=self.agent_prompt)
try:
llm_response = await acall_llm_model(
self.llm,
messages=messages,
model=self.model_name,
temperature=self.conf.llm_config.llm_temperature
)
logger.info(f"Execute response: {llm_response.message}")
except Exception as e:
logger.warn(traceback.format_exc())
raise e
finally:
if llm_response:
if llm_response.error:
logger.info(
f"{self.id()} llm result error: {llm_response.error}")
else:
logger.error(f"{self.id()} failed to get LLM response")
raise RuntimeError(
f"{self.id()} failed to get LLM response")
agent_result = await self.model_output_parser.parse(llm_response, agent_id=self.id())
if not agent_result.is_call_tool:
self._finished = True
return agent_result.actions
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}
"""
trace_sys_prompt = "You are a helpful trace summary agent."
trace_prompt = """
Please act as a trace summary agent, Using the provided trace data, summarize the main tasks completed by each agent and their token usage,
whether the run_type attribute of span is an agent or a large model call:
run_type=AGNET and is_event=True represents the agent,
run_type=LLM and is_event=False represents the large model call.
run_type=TOOL and is_event=True represents the tool call.
The tool call and large model call of agent are manifested as the nearest child span of AGENT Span.
Please output in the following standard JSON format without any additional explanatory text:
[{{"agent":"xxx","summary":"xxx","token_usage":"xxx","input_tokens":"xxx","output_tokens":"xxx","use_tools":["xxx"]}}]
Here are the trace data: {task}
"""
def build_run_flow(nodes: List[RunNode]):
graph = {}
start_nodes = []
for node in nodes:
if hasattr(node, 'parent_node_id') and node.parent_node_id:
if node.parent_node_id not in graph:
graph[node.parent_node_id] = []
graph[node.parent_node_id].append(node.node_id)
else:
start_nodes.append(node.node_id)
for start in start_nodes:
print("-----------------------------------")
_print_tree(graph, start, "", True)
print("-----------------------------------")
def _print_tree(graph, node_id, prefix, is_last):
print(prefix + ("└── " if is_last else "├── ") + node_id)
if node_id in graph:
children = graph[node_id]
for i, child in enumerate(children):
_print_tree(graph, child, prefix +
(" " if is_last else ""), i == len(children) - 1)
def run():
agent_config = AgentConfig(
llm_provider="openai",
llm_model_name="DeepSeek-V3-Function-Call",
llm_temperature=0.3,
llm_base_url="http://localhost:34567",
llm_api_key="dummy-key",
)
search = Agent(
conf=agent_config,
name="search_agent",
system_prompt=search_sys_prompt,
agent_prompt=search_prompt,
tool_names=["search_api"]
)
summary = Agent(
conf=agent_config,
name="summary_agent",
system_prompt=summary_sys_prompt,
agent_prompt=summary_prompt
)
trace = TraceAgent(
conf=agent_config,
name="trace_agent",
system_prompt=trace_sys_prompt,
agent_prompt=trace_prompt
)
# default is sequence swarm mode
swarm = Swarm(search, summary, trace, max_steps=1, event_driven=True)
prefix = "search baidu:"
# can special search google, wiki, duck go, or baidu. such as:
# prefix = "search wiki: "
try:
res = Runners.sync_run(
input=prefix + """What is an agent.""",
swarm=swarm,
session_id="123"
)
print(res.answer)
except Exception as e:
logger.error(traceback.format_exc())
state_manager = RuntimeStateManager.instance()
nodes = state_manager.get_nodes("123")
logger.info(f"session 123 nodes: {nodes}")
build_run_flow(nodes)
get_trace_server().join()