Bub框架:基于Tape与插件化的AI智能体开发实战
1. 项目概述一个为“共生”而生的AI智能体框架如果你和我一样在过去的几年里尝试过各种AI智能体框架从LangChain到AutoGen再到LlamaIndex你可能会发现一个共同点它们大多是为“单用户、单任务、理想环境”而设计的。它们在一个干净的沙箱里运行得很好但一旦你把它们丢进一个真实、混乱、多人在线的协作环境——比如一个活跃的Slack频道、一个Telegram群组或者一个内部开发者的Discord服务器——它们就开始“水土不服”了。上下文管理混乱、会话状态难以隔离、不同渠道的适配代码重复……这些问题会迅速暴露出来。这就是Bub诞生的背景。它不是一个实验室里的玩具而是一个在真实的“群聊战场”中成长起来的框架。它的核心设计哲学是“共生”——让AI智能体能够像一个真正的队友一样与人类和其他智能体在同一个“嘈杂”的对话环境中协同工作。Bub的Slogan “Bub it. Build it.” 非常贴切它鼓励你直接用它来构建因为它本身就是一个为构建而生、高度可塑的“通用形态”。简单来说Bub是一个基于Python的、插件化的AI智能体框架。它的核心极其精简约200行代码所有功能包括你认为的“内置”功能都只是默认插件可以被你完全替换或覆盖。它不假设任何特定的工作流而是通过一套精心设计的“钩子”系统让你能够精确控制智能体处理每一次交互的完整生命周期。无论是通过命令行的一次性指令还是在Telegram群聊里的持续对话都走同一套处理管道确保了行为的一致性。2. 核心设计哲学与架构拆解2.1 从“会话积累”到“按需装配”基于Tape的上下文管理大多数智能体框架管理上下文的方式是“会话历史积累”。用户和模型的对话被不断追加到一个列表中随着轮次增加这个列表会越来越长。为了解决token限制问题常见的做法是进行“摘要压缩”但这不可避免地会导致信息损失智能体可能会忘记一些关键的细节。Bub彻底摒弃了这种方式转而采用来自 Tape 的理念。你可以把Tape想象成一个只追加、不可变的事实记录带。每一次交互、每一个工具调用结果、每一个系统事件都被作为一个独立的“事实”记录在这条带上。智能体需要上下文时不是去翻阅一个冗长的聊天记录而是根据当前任务的需要从这条“磁带”中按需选取、组装相关的“事实片段”。为什么这种设计更优避免信息稀释传统的摘要会丢失细节而Tape保留了所有原始事实。智能体在需要时可以回溯到最精确的信息。支持复杂场景在多人、多智能体的群聊中对话是交错并发的。基于会话历史的线性模型很难处理这种交错性。Tape的“事实”模型天然支持从多个并行会话线中提取相关信息。状态清晰通过“锚点”标记会话的不同阶段例如一个复杂任务开始、一个子任务完成智能体可以更清晰地理解当前所处的“上下文段落”而不是面对一锅粥的历史记录。2.2 彻底的插件化一切皆钩子这是Bub架构中最激进也最迷人的部分。整个智能体处理单次消息称为一个“轮次”的生命周期被抽象为一个清晰的管道而管道的每一个阶段都是一个“钩子”。解析会话 → 加载状态 → 构建提示词 → 运行模型 ↓ 派发输出 ← 渲染输出 ← 保存状态这个管道不是硬编码在框架核心里的。框架核心只定义了这些钩子接口在hookspecs.py中和调用它们的顺序在framework.py中。具体每个钩子做什么完全由插件来实现。所谓的“内置功能”只不过是几个默认注册的插件而已。这种设计带来的颠覆性优势无特权代码框架核心不包含任何业务逻辑。如果你想改变提示词构建方式你不需要去修改框架源码只需要写一个插件实现build_prompt钩子并注册它。你的插件会覆盖默认的插件。极致的可定制性你可以替换模型调用逻辑run_model、改变输出格式render_outbound、甚至重写整个状态管理机制load_state/save_state。Bub提供了一个坚实的骨架而血肉完全由你定义。清晰的关注点分离每个插件只关心一个特定的钩子代码更内聚更容易测试和维护。2.3 技能即文档降低创作与使用门槛在许多框架中给智能体添加新能力技能需要编写Python类使用装饰器注册并确保导入路径正确。这对开发者很友好但对领域专家或想快速共享能力的人来说门槛较高。Bub提出了“技能即文档”的概念。一个技能就是一个Markdown文件例如SUMMARIZE.md文件顶部用YAML格式的Frontmatter声明技能的名称、描述、参数等信息下面则是给AI模型看的自然语言描述和使用示例。这种做法的好处易于创建和分享任何人只要会写Markdown就能创建一个技能。你可以把技能文件放在Git仓库里Bub能自动发现它们。对AI更友好技能描述是用自然语言写的这本身就是给大模型的最佳说明书比结构化的代码注释更易于理解。版本化管理技能文件可以和项目代码一起进行版本控制变更历史清晰可见。当Bub启动时它会扫描工作区以及配置的技能路径自动发现所有SKILL.md文件解析它们的Frontmatter并将这些技能作为工具提供给AI模型调用。3. 从零开始环境搭建与基础使用3.1 安装与初始化Bub推荐使用uv作为Python包管理器和运行器这能保证依赖环境的隔离和一致性。当然用传统的pip也完全没问题。使用 uv (推荐):# 安装 uv (如果尚未安装) curl -LsSf https://astral.sh/uv/install.sh | sh # 克隆仓库并进入目录 git clone https://github.com/bubbuild/bub.git cd bub # 同步依赖uv会自动创建虚拟环境 uv sync # 验证安装 uv run bub --help使用 pip:# 直接从PyPI安装 pip install bub # 验证安装 bub --help安装完成后你需要配置AI模型。Bub默认使用OpenRouter上的qwen/qwen3-coder-next模型这是一个在代码任务上表现优秀的模型。你需要设置API密钥。# 方法一设置环境变量适用于OpenAI、OpenRouter等 export OPENAI_API_KEYyour-api-key-here # 如果使用OpenAI模型 # 或者 export OPENROUTER_API_KEYyour-openrouter-key-here # 如果使用默认模型 # 方法二使用Bub的登录命令目前主要支持OpenAI Codex的OAuth流程 uv run bub login openai # 这会打开浏览器引导你完成OAuth授权之后密钥会安全地存储在本地。注意BUB_API_KEY环境变量是通用后备选项。Bub的模型客户端会按照OPENAI_API_KEY-OPENROUTER_API_KEY-BUB_API_KEY的顺序查找密钥。建议根据你实际使用的模型提供商来设置对应的环境变量。3.2 三种核心运行模式体验Bub提供了三种交互模式对应不同的使用场景。1. 交互式聊天模式这类似于一个增强版的Python REPL智能体在此模式下可以持续对话并调用技能。uv run bub chat启动后你会看到一个提示符。你可以直接输入问题例如“列出当前目录下的文件”。如果要执行内部命令如管理技能、操作文件系统需要在命令前加一个逗号,例如,help查看所有内部命令。2. 单次任务模式适合自动化脚本或快速执行一次性命令。uv run bub run 请总结当前Git仓库的最近三次提交Bub会处理这条消息调用必要的技能如读取Git日志生成结果并输出到终端然后退出。3. 网关监听模式这是让Bub“活”在聊天软件中的关键。启动后Bub会作为一个服务监听特定渠道如Telegram的消息。uv run bub gateway要使用此模式你需要先配置通道适配器。以Telegram为例需要设置TELEGRAM_BOT_TOKEN和TELEGRAM_CHAT_ID环境变量。当配置好后在对应的Telegram群组中你的机器人它就会用Bub的核心管道来处理消息。3.3 初探技能系统Bub的强大之处在于其技能。让我们先看看内置技能和如何发现它们。在bub chat交互模式下输入内部命令查看技能,skill list这会列出所有已发现的技能包括内置的和在工作区AGENTS.md或skills/目录下找到的。尝试调用一个简单的内置技能比如读取文件,fs.read pathREADME.md你会看到Bub执行了文件读取操作并返回了内容。注意这个命令是由Bub的内部命令系统处理的而不是AI模型。AI模型调用技能则是另一种方式。要让AI使用技能你只需要在聊天中提出需求。例如在bub chat中 请帮我读取README.md文件并总结其核心内容。AI模型会自己决定调用fs.read技能获取文件内容然后进行分析和总结。你不需要显式地告诉它用什么技能。4. 深度定制编写你的第一个插件理解了Bub的插件化架构后最好的学习方式就是动手写一个插件。我们将创建一个简单的“回声”插件它不会调用AI模型而是直接将用户输入原样返回并加上一个前缀。4.1 创建插件项目结构首先我们创建一个独立的目录来管理我们的插件这有利于复用和分发。mkdir bub-echo-plugin cd bub-echo-plugin创建一个pyproject.toml文件来定义我们的插件包[project] name bub-echo-plugin version 0.1.0 description A simple echo plugin for Bub authors [{name Your Name, email youexample.com}] readme README.md requires-python 3.9 dependencies [ bub0.1.0, ] [project.entry-points.bub] echo bub_echo_plugin.plugin:EchoPlugin [build-system] requires [hatchling] build-backend hatchling.build关键部分是[project.entry-points.bub]。这行告诉Bub在“bub”这个入口点组下有一个名为“echo”的插件其实现位于bub_echo_plugin.plugin模块的EchoPlugin类中。4.2 实现插件逻辑接下来创建插件的主要代码文件。首先创建包目录和__init__.pymkdir -p bub_echo_plugin touch bub_echo_plugin/__init__.py然后创建插件实现文件bub_echo_plugin/plugin.py 一个简单的Bub回声插件。 这个插件拦截了提示词构建和模型运行两个钩子实现了直接回声的功能。 from typing import Any, Dict from bub import hookimpl class EchoPlugin: 回声插件实现类。 hookimpl def build_prompt(self, message: Dict[str, Any], session_id: str, state: Dict[str, Any]) - str: 构建提示词的钩子实现。 这里我们完全忽略原有的提示词构建逻辑直接返回一个加工后的用户消息。 参数: message: 原始入站消息字典通常包含 content 等字段。 session_id: 当前会话的唯一标识符。 state: 当前轮次的状态字典可以在钩子间传递信息。 返回: 一个字符串将作为“提示词”传递给 run_model 钩子。 user_content message.get(content, ) # 我们给内容加上一个回声标记 echoed_prompt f[ECHO PLUGIN ACTIVATED] User said: {user_content} # 我们可以修改state供后续钩子使用虽然这个简单例子用不到 state[processed_by_echo] True return echoed_prompt hookimpl async def run_model(self, prompt: str, session_id: str, state: Dict[str, Any]) - str: 运行模型的钩子实现。 这里我们完全绕过任何AI模型调用直接返回接收到的prompt。 参数: prompt: 由 build_prompt 钩子产生的提示词字符串。 session_id: 当前会话的唯一标识符。 state: 当前轮次的状态字典。 返回: 一个字符串将作为模型生成的“响应”传递给后续钩子。 # 简单地将prompt作为响应返回实现“回声” # 在实际插件中这里可能会调用OpenAI API、本地模型等。 model_response fEcho Response {prompt} return model_response # 注意我们没有实现 render_outbound 钩子所以会使用Bub默认的或其它插件的渲染逻辑。 # 默认渲染器可能会直接输出我们的 model_response 字符串。代码解读导入与装饰器我们从bub导入hookimpl装饰器。任何插件方法都需要用这个装饰器标记Bub才能识别它。build_prompt方法这个钩子负责将原始用户消息转换为给模型的提示词。我们这里做了最简单的处理提取用户内容加上一个前缀。我们还修改了state字典展示了如何在钩子间传递信息。run_model方法这个钩子通常负责调用大语言模型。我们这里“劫持”了这个过程直接返回一个加工后的提示词字符串模拟了模型响应。注意这个方法是async的因为真实的模型调用是异步操作。选择性实现一个插件不需要实现所有钩子。我们只实现了两个这意味着对于其他钩子如load_state,save_state,render_outboundBub会使用其他插件包括内置插件的实现。4.3 安装并测试插件在插件项目目录下使用uv或pip以可编辑模式安装# 使用 uv uv pip install -e . # 或使用 pip pip install -e .现在进入一个包含Bub项目的工作区或者任何目录运行bub chat。由于我们的插件通过entry-points注册Bub会自动加载它。在bub chat中输入 Hello, Bub!你期望看到的可能是一个AI生成的回复但实际输出会是Echo Response [ECHO PLUGIN ACTIVATED] User said: Hello, Bub!这说明我们的插件已经成功覆盖了默认的提示词构建和模型运行逻辑Bub的内置AI模型插件被我们“静默”了。如何确认插件被加载使用Bub的命令查看所有钩子与插件的绑定关系uv run bub hooks在输出列表中你应该能看到build_prompt和run_model钩子下列出了我们的EchoPlugin实现。4.4 插件优先级与覆盖规则Bub的插件系统遵循“后来者居上”的原则。插件的加载顺序决定了它们的优先级。后加载的插件中实现的钩子会覆盖先加载的插件中实现的同一个钩子。我们的pyproject.toml中定义的入口点通常是在Bub启动时较晚被加载的取决于Python的包加载机制。如果你想控制更精确的覆盖或者有多个插件需要协调你可能需要研究更高级的插件管理方式或者考虑将你的定制逻辑打包到Bub项目本身的插件目录中。实操心得插件开发的调试技巧开发插件时一个常见的困惑是“我的插件真的被加载了吗” 除了bub hooks命令一个更直接的方法是在插件代码的__init__或某个钩子方法开始时加入一个打印语句。但注意在生产中要移除这些调试输出。更好的做法是利用Bub的日志系统。你可以在插件中获取Bub的loggerimport logging; logger logging.getLogger(__name__)然后使用logger.debug(“Plugin loaded”)来输出信息。通过设置环境变量BUB_LOG_LEVELDEBUG来查看详细日志。5. 技能开发实战创建“天气查询”技能插件用于定制框架行为而技能则是赋予AI模型新的能力。现在我们来创建一个实用的“天气查询”技能。5.1 技能文件结构与规范在Bub的工作区通常是你的项目根目录或者skills/子目录下创建一个名为GET_WEATHER.md的文件。技能文件遵循严格的格式Frontmatter文件开头用---分隔的YAML区域用于声明技能的元数据。描述与示例YAML之后的内容是Markdown格式用于向AI模型描述这个技能怎么用。以下是GET_WEATHER.md的完整内容--- name: get_weather description: 获取指定城市的当前天气信息。 parameters: - name: city type: string description: 城市名称例如“北京”、“San Francisco”。支持中文和英文城市名。 required: true - name: unit type: string description: 温度单位。c 表示摄氏度f 表示华氏度。默认为 c。 required: false default: c enum: [c, f] returns: type: string description: 格式化的天气信息字符串包含温度、天气状况、湿度等。 --- # get_weather 技能 此技能允许智能体查询实时天气信息。 ## 功能说明 调用此技能将连接到外部天气API获取指定城市的当前天气数据并以人类可读的格式返回。 ## 参数详解 * city (字符串必需): 要查询天气的城市名称。请尽量提供完整的城市名如“上海市”、“New York”。对于有歧义的名字可能无法获得准确结果。 * unit (字符串可选): 温度单位。默认为 c摄氏度。如果用户要求使用华氏度请将此参数设置为 f。 ## 调用示例 **用户请求**: “今天北京天气怎么样” **智能体应调用**: get_weather(city北京) **用户请求**: “Whats the temperature in London in Fahrenheit?” **智能体应调用**: get_weather(cityLondon, unitf) ## 返回格式 技能将返回一个格式化的字符串例如 “北京市晴当前温度 22°C体感温度 24°C湿度 65%西北风 2级。” 如果城市不存在或API出错将返回错误信息例如“无法获取‘某某市’的天气信息请检查城市名称是否正确。” ## 注意事项 1. 此技能需要网络连接。 2. 调用频率可能受外部API限制。 3. 返回的天气信息可能有轻微延迟通常不超过15分钟。5.2 实现技能的后端逻辑技能文件只是“说明书”我们还需要实现真正的逻辑。这通常通过一个插件来完成该插件实现get_tools钩子返回一个与技能名对应的函数。我们在之前插件项目的基础上创建一个新的插件类。编辑bub_echo_plugin/plugin.py添加以下内容import aiohttp import os from typing import List, Any, Dict from bub import hookimpl # ... 之前已有的 EchoPlugin 类 ... class WeatherSkillPlugin: 为Bub提供天气查询技能的插件。 hookimpl def get_tools(self) - List[Dict[str, Any]]: 返回此插件提供的工具列表。 每个工具是一个字典必须包含 name 和 function。 name 必须与技能文件中的 name 一致。 return [ { name: get_weather, function: self._get_weather_impl, # 指向实际的实现函数 description: 获取指定城市的当前天气信息。, # 可与技能描述一致 } ] async def _get_weather_impl(self, city: str, unit: str c) - str: 天气查询技能的实际实现函数。 参数名和类型必须与技能文件中定义的 parameters 完全匹配。 参数: city: 城市名 unit: 温度单位c 或 f 返回: 格式化的天气字符串或错误信息。 # 在实际项目中你应该使用一个可靠的天气API并妥善保管API Key # 例如OpenWeatherMap, WeatherAPI, 和风天气等 # 这里我们使用一个模拟的API作为示例 api_key os.getenv(WEATHER_API_KEY) if not api_key: return 错误未配置天气API密钥。请设置 WEATHER_API_KEY 环境变量。 # 构建请求URL (这里以和风天气的免费API为例需要注册获取key) # 实际URL和参数请查阅对应API文档 url fhttps://devapi.qweather.com/v7/weather/now params { location: city, key: api_key, lang: zh, # 根据需求调整语言 unit: unit, # 注意不同API的参数名可能不同这里是示例 } try: async with aiohttp.ClientSession() as session: async with session.get(url, paramsparams, timeout10) as resp: if resp.status 200: data await resp.json() # 解析API返回的JSON数据这里需要根据实际API响应结构调整 # 示例解析 temp data.get(now, {}).get(temp, N/A) text data.get(now, {}).get(text, 未知) humidity data.get(now, {}).get(humidity, N/A) wind_scale data.get(now, {}).get(windScale, N/A) unit_symbol °C if unit c else °F return f{city}{text}当前温度 {temp}{unit_symbol}湿度 {humidity}%风力 {wind_scale}级。 else: return f错误天气API请求失败状态码 {resp.status}。 except aiohttp.ClientError as e: return f错误网络请求失败 - {str(e)} except Exception as e: return f错误处理天气数据时发生意外 - {str(e)} # 重要我们需要更新入口点让Bub加载这个新的插件类 # 修改 pyproject.toml 中的 entry-points或者创建一个新的入口点。 # 为了简单我们可以修改 EchoPlugin让它也提供工具或者创建一个综合插件类。 # 这里我们创建一个新的综合插件类来包含所有功能。 class MyCustomPlugin(EchoPlugin, WeatherSkillPlugin): 集成了回声和天气功能的综合插件。 pass # 然后在 pyproject.toml 中将入口点指向 MyCustomPlugin # [project.entry-points.bub] # myplugin bub_echo_plugin.plugin:MyCustomPlugin关键点解析get_tools钩子这个钩子返回一个工具字典列表。name字段必须与技能Markdown文件Frontmatter中的name严格一致。Bub通过这个名称将技能描述和工具实现关联起来。实现函数_get_weather_impl是实际执行逻辑的函数。它的参数 (city,unit) 必须与技能文件中定义的parameters在名称和类型上兼容。Bub会利用Pydantic等工具进行参数验证和绑定。异步支持由于涉及网络请求实现函数是异步的 (async def)。Bub的框架能正确处理异步工具。错误处理在工具实现中必须做好错误处理并以清晰的字符串形式返回错误信息。AI模型会接收到这个字符串并可能决定如何向用户报告。5.3 注册与测试新技能首先更新pyproject.toml将入口点指向我们新的综合插件类[project.entry-points.bub] myplugin bub_echo_plugin.plugin:MyCustomPlugin # 改为 MyCustomPlugin然后重新安装插件因为入口点发生了变化pip install -e . --force-reinstall # 或 uv pip install -e . --force-reinstall接下来将写好的GET_WEATHER.md技能文件放到Bub的工作目录下例如项目根目录。同时你需要获取一个真实的天气API Key并设置环境变量export WEATHER_API_KEYyour_actual_weather_api_key_here现在启动bub chatuv run bub chat首先确认技能已被发现,skill list你应该能在列表中看到get_weather。然后让AI使用这个技能 查询一下上海现在的天气。如果一切配置正确Bub的AI模型会理解你的指令自动调用get_weather工具插件会执行网络请求并将结果返回给AI模型最终AI会生成一个包含天气信息的自然语言回复给你。注意事项技能与工具的匹配最常见的错误是技能文件的name和插件get_tools返回的name不匹配或者参数定义不一致。务必仔细检查。使用bub hooks命令可以查看已注册的工具但调试技能调用逻辑更有效的方法是开启Bub的调试日志BUB_LOG_LEVELDEBUG uv run bub chat观察AI模型决定调用工具时的日志输出。6. 高级配置与生产环境部署6.1 环境变量详解Bub的配置主要通过环境变量完成这使得它在各种部署环境中都非常灵活。变量名默认值描述与使用技巧BUB_MODELopenrouter:qwen/qwen3-coder-next模型标识符。格式为provider:model-name。例如openai:gpt-4o、anthropic:claude-3-5-sonnet、openrouter:meta-llama/llama-3-70b-instruct。OpenRouter是一个聚合平台可以访问多种模型。BUB_API_KEY—通用API密钥。如果设置了特定provider的key如OPENAI_API_KEY则优先使用。否则Bub会尝试使用此变量。BUB_API_BASE—自定义API端点。用于连接自托管的模型服务如LocalAI, vLLM, Ollama。例如BUB_API_BASEhttp://localhost:11434/v1用于连接本地的Ollama服务。BUB_API_FORMATcompletionAPI通信格式。可选completion(旧版OpenAI格式)、responses(新版OpenAI格式) 或messages(通用Chat格式)。大多数现代API使用messages。BUB_CLIENT_ARGS—底层客户端参数。一个JSON字符串用于传递额外的参数给底层的模型客户端库。例如BUB_CLIENT_ARGS{timeout: 60, max_retries: 3}。BUB_MAX_STEPS50最大工具调用步数。防止AI陷入无限循环。如果一个任务需要链式调用很多工具可以适当调高。BUB_MAX_TOKENS1024单次模型调用的最大生成token数。根据模型能力和任务复杂度调整。复杂任务可能需要2048或4096。BUB_MODEL_TIMEOUT_SECONDS—模型调用超时时间。网络不稳定或模型响应慢时有用。例如设置为30。BUB_LOG_LEVELINFO日志级别。调试时设置为DEBUG可以看到详细的钩子调用、工具调用和网络请求信息。BUB_WORKSPACE当前目录工作区根目录。Bub在此目录下寻找AGENTS.md和skills/文件夹。配置示例使用本地Ollama运行Llama 3模型export BUB_MODELollama:llama3.1 export BUB_API_BASEhttp://localhost:11434/v1 # Ollama通常不需要API Key但如果设置了验证可以配置 # export BUB_API_KEYollama-api-key export BUB_API_FORMATmessages uv run bub chat6.2 部署为长期运行的服务要让Bub作为一个真正的“群聊队友”运行你需要将其部署为一个长期运行的服务。使用 Systemd (Linux)这是最经典的方式。创建一个服务文件/etc/systemd/system/bub-gateway.service[Unit] DescriptionBub AI Agent Gateway Afternetwork.target [Service] Typesimple Useryour_username WorkingDirectory/path/to/your/bub/workspace EnvironmentPATH/home/your_username/.local/bin:/usr/local/bin:/usr/bin:/bin EnvironmentTELEGRAM_BOT_TOKENYOUR_BOT_TOKEN EnvironmentTELEGRAM_CHAT_IDYOUR_CHAT_ID EnvironmentOPENAI_API_KEYYOUR_OPENAI_KEY # 其他环境变量... ExecStart/home/your_username/.local/bin/uv run bub gateway Restartalways RestartSec10 StandardOutputjournal StandardErrorjournal [Install] WantedBymulti-user.target然后启用并启动服务sudo systemctl daemon-reload sudo systemctl enable bub-gateway.service sudo systemctl start bub-gateway.service # 查看日志 sudo journalctl -u bub-gateway.service -f使用 DockerDocker提供了更好的隔离性和可移植性。首先创建一个DockerfileFROM python:3.11-slim WORKDIR /app # 安装 uv 用于高效的依赖管理 RUN pip install --no-cache-dir uv # 复制项目文件 COPY . . # 使用 uv 同步依赖 RUN uv sync --frozen # 设置环境变量敏感信息建议通过运行时传入 ENV BUB_WORKSPACE/app ENV PYTHONPATH/app/src # 运行网关 CMD [uv, run, bub, gateway]构建并运行docker build -t bub-gateway . docker run -d \ --name bub-agent \ -v $(pwd)/skills:/app/skills \ # 挂载技能目录 -v $(pwd)/.bub_data:/app/.bub_data \ # 挂载数据目录如果需要持久化状态 -e TELEGRAM_BOT_TOKEN${TELEGRAM_BOT_TOKEN} \ -e OPENAI_API_KEY${OPENAI_API_KEY} \ -e BUB_MODELopenai:gpt-4 \ bub-gateway6.3 性能优化与监控连接池如果技能插件频繁进行网络请求如天气插件考虑在插件初始化时创建aiohttp.ClientSession并复用而不是为每个请求新建。状态缓存对于load_state钩子如果从数据库或远程存储加载状态较慢可以考虑添加内存缓存但要注意会话隔离和缓存失效。异步优化确保所有I/O操作网络、磁盘、数据库都使用异步库如aiohttp,aiofiles,asyncpg避免阻塞事件循环。日志聚合在生产环境中将Bub的日志通过Pythonlogging输出接入到像ELK、Loki或Sentry这样的日志聚合系统中便于监控和排查问题。健康检查可以为Bub网关添加一个简单的HTTP健康检查端点通过一个自定义插件实现hookimpl挂接到某个生命周期事件或者启动一个简单的副线程HTTP服务器方便Kubernetes或负载均衡器检查服务状态。7. 故障排除与常见问题在实际使用和开发Bub插件、技能的过程中你可能会遇到一些典型问题。以下是一个快速排查指南。问题现象可能原因解决方案运行bub chat或bub run立即报错ModuleNotFoundError或ImportError。1. Bub未正确安装。2. 虚拟环境未激活或路径不对。3. 自定义插件依赖未安装。1. 使用 pip list自定义插件已安装但功能未生效如bub hooks中未列出。1. 插件入口点未在pyproject.toml中正确声明。2. 插件包未安装或安装失败。3. 插件类未使用hookimpl装饰器。1. 检查pyproject.toml中[project.entry-points.bub]部分格式是否正确。2. 重新安装插件并检查是否有错误信息pip install -e . --force-reinstall。3. 确保插件类中的方法都正确使用了from bub import hookimpl和hookimpl。技能文件已创建但,skill list未显示或AI无法调用。1. 技能文件不在Bub的工作区目录下。2. 技能文件命名不正确应为SKILL_NAME.md。3. 技能文件的Frontmatter YAML格式错误。4. 插件中get_tools返回的name与技能文件中的name不匹配。1. 将技能文件放在项目根目录或skills/子目录下。检查BUB_WORKSPACE环境变量。2. 确保文件名是大写下划线风格如GET_WEATHER.md。3. 使用YAML校验器检查---之间的内容确保缩进、冒号后空格正确。4. 仔细核对插件代码和技能文件中的name字段必须完全一致包括大小写。AI模型不调用技能而是尝试用文字回答。1. 模型能力不足无法理解工具调用格式。2. 系统提示词中未充分描述可用技能。3. 用户请求的描述不够清晰模型无法匹配到技能。1. 尝试更换更强大的模型如GPT-4。2. 检查工作区是否有AGENTS.md文件其中应清晰描述智能体的职责和可用技能。Bub会自动将其附加到系统提示词。3. 在技能文件的Markdown描述部分提供更丰富、更贴近自然语言的示例。技能调用失败返回权限错误或网络错误。1. 技能插件实现中访问了需要权限的资源如文件、网络。2. API密钥未设置或错误。3. 网络防火墙或代理阻止了请求。1. 确保Bub进程有足够的权限读取/写入所需文件。2. 使用echo $API_KEY_VAR确认环境变量已正确设置并导出。3. 在技能插件代码中添加更详细的错误日志或设置BUB_LOG_LEVELDEBUG查看网络请求详情。bub gateway启动后收不到Telegram消息。1.TELEGRAM_BOT_TOKEN或TELEGRAM_CHAT_ID环境变量未设置或错误。2. 机器人未添加到群组或未在群组中授予发送消息权限。3. 服务器网络无法访问Telegram API。1. 通过 env处理速度慢响应延迟高。1. 模型API调用慢如GPT-4。2. 技能插件中有同步阻塞操作如未使用异步库进行网络请求。3. 状态加载/保存钩子如连接慢速数据库。1. 考虑使用更快的模型如GPT-3.5-Turbo或调整BUB_MODEL_TIMEOUT_SECONDS。2. 将插件中的所有I/O操作改为异步使用async/await和aiohttp等库。3. 优化load_state/save_state插件添加缓存或使用更快的存储后端。调试心法日志是你的朋友当遇到任何诡异的问题时第一反应应该是打开调试日志BUB_LOG_LEVELDEBUG uv run bub chat 21 | less仔细查看日志输出你会看到插件加载顺序。每个钩子被哪个插件实现调用。模型接收到的原始提示词和返回的响应。工具调用的参数和返回值。网络请求的详细信息。 这些信息是定位问题最直接的依据。Bub的设计哲学是简洁和模块化这意味着大部分问题都出现在模块的边界上插件注册、技能定义、环境配置。按照上述表格系统性排查大部分问题都能迎刃而解。