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,37 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import abc
from aworld.core.context.base import Context
from aworld.core.event.base import Message
from aworld.runners.hook.hook_factory import HookFactory
from aworld.runners.hook.hooks import PostLLMCallHook, PreLLMCallHook
from aworld.utils.common import convert_to_snake
@HookFactory.register(name="PreLLMCallContextProcessHook",
desc="PreLLMCallContextProcessHook")
class PreLLMCallContextProcessHook(PreLLMCallHook):
"""Process in the hook point of the pre_llm_call."""
__metaclass__ = abc.ABCMeta
def name(self):
return convert_to_snake("PreLLMCallContextProcessHook")
async def exec(self, message: Message, context: Context = None) -> Message:
# and do something
pass
@HookFactory.register(name="PostLLMCallContextProcessHook",
desc="PostLLMCallContextProcessHook")
class PostLLMCallContextProcessHook(PostLLMCallHook):
"""Process in the hook point of the post_llm_call."""
__metaclass__ = abc.ABCMeta
def name(self):
return convert_to_snake("PostLLMCallContextProcessHook")
async def exec(self, message: Message, context: Context = None) -> Message:
# get context
pass
@@ -0,0 +1,44 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import sys
from typing import Dict, List
from aworld.core.factory import Factory
from aworld.logs.util import logger
from aworld.runners.hook.hooks import Hook, StartHook, HookPoint
class HookManager(Factory):
def __init__(self, type_name: str = None):
super(HookManager, self).__init__(type_name)
def __call__(self, name: str, **kwargs):
if name is None:
raise ValueError("hook name is None")
try:
if name in self._cls:
act = self._cls[name](**kwargs)
else:
raise RuntimeError("The hook was not registered.\nPlease confirm the package has been imported.")
except Exception:
err = sys.exc_info()
logger.warning(f"Failed to create hook with name {name}:\n{err[1]}")
act = None
return act
def hooks(self, name: str = None) -> Dict[str, List[Hook]]:
vals = list(filter(lambda s: not s.startswith('__'), dir(HookPoint)))
results = {val.lower(): [] for val in vals}
for k, v in self._cls.items():
hook = v()
if name and hook.point() != name:
continue
results.get(hook.point(), []).append(hook)
return results
HookFactory = HookManager("hook_type")
@@ -0,0 +1,85 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import abc
from aworld.core.context.base import Context
from aworld.core.event.base import Message
from aworld.models.model_response import ModelResponse
class HookPoint:
START = "start"
FINISHED = "finished"
ERROR = "error"
PRE_LLM_CALL = "pre_llm_call"
POST_LLM_CALL = "post_llm_call"
OUTPUT_PROCESS = "output_process"
class Hook:
"""Runner hook."""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def point(self):
"""Hook point."""
@abc.abstractmethod
async def exec(self, message: Message, context: Context = None) -> Message:
"""Execute hook function."""
class StartHook(Hook):
"""Process in the hook point of the start."""
__metaclass__ = abc.ABCMeta
def point(self):
return HookPoint.START
class FinishedHook(Hook):
"""Process in the hook point of the finished."""
__metaclass__ = abc.ABCMeta
def point(self):
return HookPoint.FINISHED
class ErrorHook(Hook):
"""Process in the hook point of the error."""
__metaclass__ = abc.ABCMeta
def point(self):
return HookPoint.ERROR
class PreLLMCallHook(Hook):
"""Process in the hook point of the pre_llm_call."""
__metaclass__ = abc.ABCMeta
def point(self):
return HookPoint.PRE_LLM_CALL
class PostLLMCallHook(Hook):
"""Process in the hook point of the post_llm_call."""
__metaclass__ = abc.ABCMeta
def point(self):
return HookPoint.POST_LLM_CALL
class OutputProcessHook(Hook):
"""Output process hook for processing output data for display."""
__metaclass__ = abc.ABCMeta
def point(self):
return HookPoint.OUTPUT_PROCESS
def process_output_content(self, content: str) -> str:
"""process output content
Args:
content: original content
Returns:
processed content
"""
return content
@@ -0,0 +1,111 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import re
import copy
from typing import Dict, Any, AsyncGenerator
from aworld.core.context.base import Context
from aworld.core.event.base import Message
from aworld.logs.util import logger
from aworld.models.model_response import ModelResponse
from aworld.output.base import Output, MessageOutput
from aworld.runners.hook.hook_factory import HookFactory
from aworld.runners.hook.hooks import OutputProcessHook
from aworld.utils.common import convert_to_snake
@HookFactory.register(name="ModelResponseProcessHook",
desc="Process ModelResponse type messages before sending to frontend display")
class ModelResponseProcessHook(OutputProcessHook):
"""Process ModelResponse type messages before sending to frontend display"""
def name(self):
return convert_to_snake("ModelResponseProcessHook")
async def exec(self, message: Message, context: Context = None) -> Message:
"""Process ModelResponse type messages
Args:
message: Message object
context: Context object
Returns:
Processed message object
"""
# Get payload
if not message or not message.payload:
return message
payload = message.payload
# Process different types of payload
if isinstance(payload, ModelResponse):
# Directly process ModelResponse type
processed_payload = self.process_model_response(payload)
message.payload = processed_payload
# Record processing results
self._log_processing_result(payload, processed_payload, context)
elif isinstance(payload, MessageOutput) and hasattr(payload, 'source'):
# Process ModelResponse in MessageOutput
source = payload.source
if isinstance(source, ModelResponse):
processed_source = self.process_model_response(source)
payload.source = processed_source
# Record processing results
self._log_processing_result(source, processed_source, context)
return message
def process_model_response(self, model_response: ModelResponse) -> ModelResponse:
"""Process ModelResponse
Args:
model_response: ModelResponse object
Returns:
Processed ModelResponse object
"""
if not model_response:
return model_response
# Create a new ModelResponse object to avoid modifying the original
processed_response = copy.deepcopy(model_response)
content = self.process_output_content(processed_response.content)
processed_response.content = content
return processed_response
def _log_processing_result(self, original: ModelResponse, processed: ModelResponse, context: Context = None):
"""Record processing results
Args:
original: Original ModelResponse
processed: Processed ModelResponse
context: Context object
"""
# Record content length before and after processing for analysis
original_length = len(original.content) if original and original.content else 0
processed_length = len(processed.content) if processed and processed.content else 0
# Save processing results to context for later retrieval
if context:
if not hasattr(context, 'hook_results'):
context.hook_results = {}
if not hasattr(context.hook_results, 'output_process'):
context.hook_results.output_process = {}
# Save processing results
context.hook_results.output_process = {
'hook_name': self.name(),
'original_length': original_length,
'processed_length': processed_length,
'removed_content': original_length - processed_length,
'processed_at': context.get_current_timestamp() if hasattr(context, 'get_current_timestamp') else None,
'processing_details': {
'removed_html_tags': True,
'removed_think_tags': True
}
}
logger.info(f"ModelResponse processing result: Original length {original_length}, Processed length {processed_length}, Removed content {original_length - processed_length}")
@@ -0,0 +1,41 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
HOOK_TEMPLATE = """
import traceback
from aworld.core.context.base import Context
from aworld.core.event.base import Message, Constants, TopicType
from aworld.runners.hook.hooks import *
from aworld.runners.hook.hook_factory import HookFactory
from aworld.logs.util import logger
from aworld.utils.common import convert_to_snake
@HookFactory.register(name="{name}",
desc="{desc}")
class {name}({point}Hook):
def name(self):
return convert_to_snake("{name}")
async def exec(self, message: Message) -> Message:
{func_import}import {func}
try:
res = {func}(message)
if not res:
raise ValueError(f"{func} no result return.")
return Message(payload=res,
session_id=message.context.session_id,
sender="{name}",
category=Constants.TASK,
topic="{topic}")
except Exception as e:
logger.error(traceback.format_exc())
return Message(payload=str(e),
session_id=message.context.session_id,
sender="{name}",
category=Constants.TASK,
topic=TopicType.ERROR)
"""
@@ -0,0 +1,55 @@
# coding: utf-8
# Copyright (c) 2025 inclusionAI.
import importlib
import inspect
import os
from typing import Callable, Any
from aworld.runners.hook.template import HOOK_TEMPLATE
from aworld.utils.common import snake_to_camel
def hook(hook_point: str, name: str = None):
"""Hook decorator.
NOTE: Hooks can be annotated, but they need to comply with the protocol agreement.
The input parameter of the hook function is `Message` type, and the @hook needs to specify `hook_point`.
Examples:
>>> @hook(hook_point=HookPoint.ERROR)
>>> def error_process(message: Message) -> Message | None:
>>> print("process error")
The function `error_process` will be executed when an error message appears in the task,
you can choose return nothing or return a message.
Args:
hook_point: Hook point that wants to process the message.
name: Hook name.
"""
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
# converts python function into a hoop with associated hoop point
func_import = func.__module__
if func_import == '__main__':
path = inspect.getsourcefile(func)
package = path.replace(os.getcwd(), '').replace('.py', '')
if package[0] == '/':
package = package[1:]
func_import = f"from {package} "
else:
func_import = f"from {func_import} "
real_name = name if name else func.__name__
con = HOOK_TEMPLATE.format(func_import=func_import,
func=func.__name__,
point=snake_to_camel(hook_point),
name=real_name,
topic=hook_point,
desc='')
with open(f"{real_name}.py", 'w+') as write:
write.writelines(con)
importlib.import_module(real_name)
return func
return decorator