ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,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")
|
||||
})
|
||||
+95
@@ -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())
|
||||
|
||||
+107
@@ -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())
|
||||
+59
@@ -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())
|
||||
+75
@@ -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)
|
||||
+227
@@ -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()
|
||||
+52
@@ -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()
|
||||
Reference in New Issue
Block a user