openJiuwen实战:用AsyncCallbackFramework为Agent增强器添加可观测性

openJiuwen实战:用AsyncCallbackFramework为Agent增强器添加可观测性

做 Agent 项目的时候经常遇到这些问题:Agent 调用之后就像个黑盒,不知道内部在干什么;想加个功能只能改核心代码,风险不小;想对接外部监控系统却找不到合适的接入点。

最近研究 openJiuwen Agent Core 框架时,发现里面有个叫 AsyncCallbackFramework 的东西------框架内置的异步回调系统。这个机制正好可以解决上面的痛点:在 Agent 生命周期的关键节点设置回调点,不用修改核心代码就能增强可观测性和扩展能力。

这篇文章记录我用这个机制开发 Agent 能力增强包装器的过程。

项目地址:https://atomgit.com/openJiuwen/agent-core?utm_source=csdn


一、为什么需要回调机制

开发 Agent 时经常会遇到几个头疼的问题:

  • 可观测性差:Agent 执行时看不到内部状态,调试困难、排查问题慢
  • 扩展性差:加功能要改核心代码,代码耦合、维护成本高
  • 集成困难:缺乏标准接口对接外部系统,生产落地门槛高

openJiuwen 的 AsyncCallbackFramework 就是来解决这些问题的。它是一个内置的异步回调系统,位于 openjiuwen.core.runner.callback 模块,提供了这些能力:

|------|----------------------------------|-------------|
| 特性 | 框架组件 | 说明 |
| 事件注册 | @framework.on(event) | 装饰器风格注册回调 |
| 事件触发 | await framework.trigger(event) | 异步触发回调执行 |
| 事件发送 | @framework.emits(event) | 函数执行后自动发送事件 |
| 过滤器 | EventFilter | 事件过滤、参数修改 |
| 链式回调 | CallbackChain | 支持回滚的回调链 |
| 性能指标 | CallbackMetrics | 自动收集性能指标 |

一开始我也想过自己实现一套回调机制,但对比后发现框架已经把这些轮子造好了:

|--------|--------------|-----------------------------|
| 对比项 | 自己写 | openJiuwen 框架 |
| LLM 调用 | 直接用 openai 库 | 通过框架的 Model 类 |
| 回调注册 | 自己实现 Handler | @framework.on(event) |
| 事件触发 | 自己实现 trigger | await framework.trigger() |
| 过滤器 | 自己写过滤逻辑 | 继承 EventFilter 类 |
| 性能指标 | 无 | CallbackMetrics 自动收集 |

简单说:框架已经把基础设施做好了,直接用就行。

二、技术架构

2.1 框架导入

先导入需要用到的模块:

复制代码
# 使用 openJiuwen 框架的统一回调机制
from openjiuwen.core.runner.callback import AsyncCallbackFramework
from openjiuwen.core.runner.callback.enums import FilterAction
from openjiuwen.core.runner.callback.filters import EventFilter
from openjiuwen.core.runner.callback.models import CallbackMetrics, FilterResult

2.2 AsyncCallbackFramework 类

这是框架的核心类:

复制代码
# 框架源码位置:openjiuwen/core/runner/callback/framework.py
class AsyncCallbackFramework:
    """Production-ready async callback framework"""

    def __init__(self, enable_metrics: bool = True, enable_logging: bool = True):
        # 核心存储
        self._callbacks: Dict[str, List[CallbackInfo]] = defaultdict(list)
        self._chains: Dict[str, CallbackChain] = {}
        self._filters: Dict[str, List[EventFilter]] = defaultdict(list)
        # ... 更多功能

2.3 使用框架装饰器注册回调

注册回调很简单,用装饰器就行:

复制代码
framework = AsyncCallbackFramework()

@framework.on("user_login", priority=10)  # 使用框架的 on 装饰器
async def handle_login(username: str):
    print(f"User logged in: {username}")

2.4 使用框架 trigger 方法触发事件

触发事件也只需要一行代码:

复制代码
# 使用框架的 trigger 方法
await framework.trigger("user_login", username="alice")

2.5 整体架构

增强包装器的整体结构:


三、环境搭建

3.1 环境要求

|------------|--------|---------------------------------|
| 组件 | 版本要求 | 说明 |
| Python | 3.11+ | 框架要求 Python 3.11 及以上 |
| openJiuwen | Latest | 核心框架(包含 AsyncCallbackFramework) |

3.2 安装步骤

复制代码
# 克隆项目
git clone https://atomgit.com/openJiuwen/agent-core.git
cd agent-core

# 创建虚拟环境
python -m venv venv
venv\Scripts\activate   # Windows

# 安装框架依赖
uv pip install -e .

# 进入增强包装器目录
cd examples/agent_enhancer

3.3 模型配置

项目使用 ModelScope 提供的 Kimi-K2.5 模型:

复制代码
API_BASE = "https://api-inference.modelscope.cn/v1"
API_KEY = "ms-de3c153b-5a19-41d1-bd3e-257a7eef7922"
MODEL_NAME = "moonshotai/Kimi-K2.5"

四、核心模块实现

4.1 创建框架实例

第一步是创建 AsyncCallbackFramework 实例:

复制代码
from openjiuwen.core.runner.callback import AsyncCallbackFramework

# 创建框架实例
framework = AsyncCallbackFramework(
    enable_metrics=True,  # 启用性能指标收集
    enable_logging=False  # 使用自己的日志系统
)

4.2 定义事件类型

定义一组事件来标记 Agent 的生命周期:

复制代码
class AgentEvents:
    """
    Agent 生命周期事件类型

    使用 openJiuwen AsyncCallbackFramework 的事件系统定义 Agent 核心事件
    """
    # 初始化事件
    AGENT_INIT = "agent_init"

    # 规划事件
    PLAN_START = "plan_start"
    PLAN_END = "plan_end"

    # 工具调用事件
    TOOL_CALL_BEFORE = "tool_call_before"
    TOOL_CALL_AFTER = "tool_call_after"
    TOOL_CALL_ERROR = "tool_call_error"

    # 消息事件
    MESSAGE_BEFORE = "message_before"
    MESSAGE_AFTER = "message_after"

    # 错误和完成事件
    ERROR_OCCURRED = "error_occurred"
    AGENT_COMPLETE = "agent_complete"

4.3 使用框架的 EventFilter

EventFilter 是事件过滤的基础类:

复制代码
# 框架源码:openjiuwen/core/runner/callback/filters.py
class EventFilter(ABC):
    @abstractmethod
    async def process(
        self,
        event: str,
        data: Dict[str, Any],
        context: Optional[Dict[str, Any]] = None
    ) -> FilterResult:
        """处理事件并返回过滤结果"""

继承它来实现内容过滤:

复制代码
from openjiuwen.core.runner.callback.filters import EventFilter
from openjiuwen.core.runner.callback.models import FilterResult
from openjiuwen.core.runner.callback.enums import FilterAction

class ContentFilter(EventFilter):
    """
    内容过滤过滤器 - 基于 openJiuwen 框架的 EventFilter

    在消息分发前过滤敏感信息
    """

    PATTERNS = {
        'phone': r'1[3-9]\d{9}',
        'email': r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}',
        'id_card': r'\d{15}|\d{17}[\dXx]',
    }

    async def process(
        self,
        event: str,
        data: Dict[str, Any],
        context: Optional[Dict[str, Any]] = None
    ) -> FilterResult:
        """过滤处理 - 实现框架 EventFilter 的 process 方法"""
        message = data.get("message", "")

        # 检测敏感信息
        detected = []
        filtered_message = message

        for name, pattern in self.PATTERNS.items():
            import re
            matches = re.findall(pattern, filtered_message)
            if matches:
                detected.extend([(name, m) for m in matches])
                filtered_message = re.sub(
                    pattern,
                    lambda m: '*' * len(m.group()),
                    filtered_message
                )

        if detected:
            # 返回 MODIFY 操作,告知框架修改数据
            return FilterResult(
                FilterAction.MODIFY,
                modified_data={"message": filtered_message},
                metadata={"detected": detected}
            )

        # 返回 ALLOW,放行事件
        return FilterResult(FilterAction.ALLOW)

4.4 可观测性模块

用框架的 @framework.on() 装饰器注册回调:

复制代码
from openjiuwen.core.runner.callback import AsyncCallbackFramework

class ObservabilityModule:
    """可观测性模块 - 使用框架的 @on 装饰器"""

    def __init__(self, framework: AsyncCallbackFramework):
        self.framework = framework
        self._register_callbacks()

    def _register_callbacks(self):
        """使用框架的 @framework.on 装饰器注册回调"""

        @self.framework.on(AgentEvents.AGENT_INIT, priority=100)
        async def _on_init(*args, **kwargs):
            """Agent 初始化回调 - 使用框架装饰器注册"""
            agent_id = kwargs.get("agent_id", "unknown")
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

            print(f"{'='*60}")
            print(f"Agent 初始化")
            print(f"  Agent ID: {agent_id}")
            print(f"  时间: {timestamp}")
            print(f"{'='*60}")

        @self.framework.on(AgentEvents.TOOL_CALL_AFTER, priority=100)
        async def _on_tool_after(*args, **kwargs):
            """工具调用后回调"""
            tool_name = kwargs.get("tool_name", "")
            duration = kwargs.get("duration_ms", 0)

            print(f"[工具] 工具返回: {tool_name}")
            print(f"  耗时: {duration:.2f}ms")

4.5 增强 Agent 类

在 Agent 执行的关键节点用 trigger 触发事件:

复制代码
# 导入事件类型
try:
    from .enhancer_core import AgentEvents
except ImportError:
    from enhancer_core import AgentEvents

class EnhancedAgent:
    """增强后的 Agent - 使用框架的 trigger 方法"""

    async def invoke(self, inputs: Dict[str, Any], session_id: str = None):
        """调用 Agent(增强版)"""
        query = inputs.get("query", "")

        # 1. 使用框架的 trigger 触发规划开始事件
        await self._framework.trigger(
            AgentEvents.PLAN_START,
            query=query,
            agent_id=self._current_agent_id
        )

        # 2. 使用框架的 trigger 触发消息前事件
        await self._framework.trigger(
            AgentEvents.MESSAGE_BEFORE,
            message=query,
            agent_id=self._current_agent_id
        )

        # 3. 调用原始 Agent
        session = Session(session_id=session_id or "default")
        result = await self._agent.invoke(inputs, session=session)

        # 4. 使用框架的 trigger 触发完成事件
        await self._framework.trigger(
            AgentEvents.AGENT_COMPLETE,
            result=result,
            agent_id=self._current_agent_id
        )

        return result

4.6 主包装器类

把所有模块组合起来:

复制代码
from openjiuwen.core.runner.callback import AsyncCallbackFramework

class AgentEnhancer:
    """
    Agent 能力增强包装器

    基于 openJiuwen 框架的 AsyncCallbackFramework 实现
    """

    def __init__(
        self,
        framework: Optional[AsyncCallbackFramework] = None,
        enable_observability: bool = True,
        enable_capability: bool = True,
        enable_integration: bool = True,
        ...
    ):
        # 创建或使用传入的框架实例
        self.framework = framework or AsyncCallbackFramework(
            enable_metrics=True,
            enable_logging=False
        )

        # 创建并注册模块
        if enable_observability:
            self.observability = ObservabilityModule(self.framework, ...)

        if enable_capability:
            self.capability = CapabilityModule(self.framework, ...)

        if enable_integration:
            self.integration = IntegrationModule(self.framework, ...)

4.7 工作流程

用户调用增强包装器后,完整的执行流程如下:

整个流程的核心是:AsyncCallbackFramework 作为事件总线,连接各个功能模块。Agent 执行的每个关键节点都会触发相应事件,各个模块通过注册回调函数来响应这些事件。

五、效果展示

5.1 演示 1:基本使用

运行 demo 看看效果:

复制代码
cd examples/agent_enhancer
python demo.py

5.2 演示 2:可观测性模块

可以看到 Agent 执行的完整追踪信息,包括工具调用、耗时统计等:

5.3 演示 3:内容过滤

当消息中包含敏感信息时,会自动过滤:

5.4 演示 4:集成能力

审计日志和通知推送:

55 演示5:完整执行报告

六、对比自己实现回调

6.1 架构对比

|------|------------------------|---------------------------------|
| 对比项 | 简单封装 | openJiuwen 框架方式 |
| 回调注册 | 自己实现 CallbackHandler | 框架的 @framework.on(event) 装饰器 |
| 事件触发 | 自己实现 trigger() 方法 | 框架的 await framework.trigger() |
| 过滤器 | 自己实现过滤逻辑 | 框架的 EventFilter 基类 |
| 性能指标 | 无自动收集 | 框架的 CallbackMetrics 自动收集 |
| 事件历史 | 需自己实现 | 框架内置事件历史(_event_history) |
| 链式回调 | 不支持 | 框架的 CallbackChain 支持 |

6.2 代码对比

简单封装方式:

复制代码
# 自己实现回调处理器
class CallbackHandler:
    def __init__(self):
        self._callbacks = defaultdict(list)

    def register(self, event_type, callback):
        self._callbacks[event_type].append(callback)

    async def trigger(self, event):
        # 自己实现触发逻辑
        ...

使用框架的方式:

复制代码
# 使用框架的 AsyncCallbackFramework
from openjiuwen.core.runner.callback import AsyncCallbackFramework

framework = AsyncCallbackFramework(enable_metrics=True)

@framework.on("event_name", priority=10)  # 框架装饰器
async def callback_handler(*args, **kwargs):
    ...

await framework.trigger("event_name", data=value)  # 框架触发器

6.3 过滤器对比

简单封装方式:

复制代码
# 自己实现过滤逻辑
def check_filter(data):
    if "敏感词" in data["message"]:
        return {"allowed": False, "modified": "..."}
    return {"allowed": True}

使用框架的方式:

复制代码
# 使用框架的 EventFilter
from openjiuwen.core.runner.callback.filters import EventFilter
from openjiuwen.core.runner.callback.models import FilterResult
from openjiuwen.core.runner.callback.enums import FilterAction

class MyFilter(EventFilter):
    async def process(self, event, data, context):
        if "敏感词" in data.get("message", ""):
            return FilterResult(FilterAction.DENY)
        return FilterResult(FilterAction.ALLOW)

# 注册到框架
framework._filters["event_name"] = [MyFilter()]

七、总结

本项目用了 openJiuwen 框架的统一回调机制来增强 Agent 的能力:

|----------------------------|-----------------------------|
| 用到的框架能力 | 说明 |
| AsyncCallbackFramework | 框架内置的回调系统 |
| 装饰器风格注册 | @framework.on(event) |
| 异步触发器 | await framework.trigger() |
| EventFilter 过滤器 | 继承实现过滤逻辑 |
| CallbackMetrics 指标 | 自动收集性能数据 |

框架带来的好处:

  • 生产级实现:框架内置的回调系统经过充分测试
  • 统一接口:所有回调使用相同的注册和触发机制
  • 可观测性:内置性能指标收集和事件历史
  • 可扩展性:支持过滤器、链式回调等高级特性
  • 异步优先:全异步设计,高性能处理

参考资源

相关推荐
llxxyy卢1 小时前
polar中等web部分题目
前端
马士兵教育1 小时前
2026年IT行业基本预测!计算机专业学生就业编程语言Java/C/C++/Python该如何选择?
java·开发语言·c++·人工智能·python·面试·职场和发展
wuhen_n1 小时前
5年前端,我为什么要all in AI Agent?
前端·vue.js·ai编程
Book思议-2 小时前
顺序表和链表核心差异与优缺点详解
java·数据结构·链表
我爱切图2 小时前
echart 移动端进行双指缩放时,当放大到最大级别后,手指没有离开屏幕,图表还会自动移动问题修复
前端
optimistic_chen2 小时前
【Vue入门】创建Vue工程环境和响应式函数
前端·javascript·vue.js·前端框架·html
南城书生2 小时前
Android Handler 机制源码分析
前端
南城书生2 小时前
Android 大图加载与 OOM 优化
前端
南城书生2 小时前
RecyclerView 源码分析
前端