ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s

This commit is contained in:
2026-08-20 13:12:50 +00:00
commit b119135836
10275 changed files with 3284984 additions and 0 deletions
@@ -0,0 +1,2 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
@@ -0,0 +1,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))