在智能体开发领域,我们经常会遇到这样的困境:框架功能越强大,代码越复杂;抽象层次越多,调试越困难。而 bu-agent-sdk 就像是给过度工程化的 AI 开发吹来一股清风。
这个开源项目名为browser-use/agent-sdk,是一个基于Python构建的轻量级 AI 智能体开发框架。它推崇极简主义的设计理念,认为复杂的抽象层反而会限制大语言模型的能力,因此核心逻辑仅由一个基础的循环调用组成。
该框架支持Anthropic、OpenAI和Google等主流模型,并提供了显式完成工具、上下文压缩以及依赖注入等实用功能。通过这种方式,开发者可以赋予 AI 更多自由度,利用简单的代码快速搭建如Claude Code类似的沙盒编程助手。
我用这个极简框架构建了一个实用的代码评审智能体,并将它接入 GitHub Actions,实现真正的自动化代码审查。
bu-agent-sdk 设计理念
bu-agent-sdk 的核心理念:
AI 系统的真正价值在于模型本身,而不是开发者编写的复杂控制逻辑。
换句话说,应该:
- 专注于给模型提供完整的工具集(行动空间)
- 让模型自主决策如何使用这些工具
- 不要用层层抽象"指导"模型该怎么做
根据 bu-agent-sdk 的设计理念,构建智能体只需要四个核心要素:
- 完整的行动空间:足够的工具和能力
- 一个循环:持续调用工具直到任务完成
- 明确的退出机制:清晰知道任务何时结束
- 上下文管理:有效处理对话历史,避免信息过载
这就是全部!没有复杂的状态机,没有多层抽象,只有简单直接的循环和工具调用。
5 分钟体验 bu-agent-sdk
我们通过一个简单例子感受一下 bu-agent-sdk 的魅力。
安装
# 使用 uv 包管理器安装
uv add bu-bu-agent-sdk
# 或使用 pip
pip install bu-bu-agent-sdk
第一个智能体:计算器
import asyncio
from bu_agent_sdk import Agent, tool, TaskComplete
from bu_agent_sdk.llm import ChatAnthropic
# 定义工具:加法
@tool("Add two numbers")
async def add(a: int, b: int) -> int:
"""将两个数字相加"""
return a + b
# 定义工具:任务完成信号
@tool("Signal task completion")
async def done(message: str) -> str:
"""当任务完成时调用此工具"""
raise TaskComplete(message)
# 创建智能体
agent = Agent(
llm=ChatAnthropic(model="claude-sonnet-4-20250514"),
tools=[add, done],
)
# 执行查询
async def main():
result = await agent.query("计算 25 + 17 等于多少?")
print(result)
asyncio.run(main())
运行过程解析:
- 智能体收到问题:"计算 25 + 17 等于多少?"
- 它识别出需要调用
add工具 - 执行
add(25, 17),得到结果42 - 智能体判断任务完成,调用
done("答案是 42") TaskComplete异常被抛出,循环结束- 返回最终结果
为什么需要done工具?
你可能会问:为什么不能像其他框架那样,"没有工具调用时自动停止"?
bu-bu-agent-sdk 的答案是:那样太不可靠了!
想象这个场景:
- 智能体需要先读取文件,再处理数据,最后写入结果
- 如果在"处理数据"步骤暂时没找到合适工具,就会过早停止
- 任务实际上还没完成!
通过强制要求一个明确的done工具,我们确保智能体主动声明任务完成,而不是被动地因为"不知道下一步做什么"而停止。
构建代码评审智能体
现在进入正题,让我们构建一个真正实用的代码评审智能体!
项目架构设计
我们的代码评审智能体需要具备以下能力:
代码评审智能体
├── 工具集(Tools)
│ ├── read_file: 读取代码文件
│ ├── list_changed_files: 获取变更文件列表
│ ├── get_file_diff: 获取文件的 Git diff
│ ├── post_review_comment: 发布评审意见
│ └── done: 完成评审
├── 上下文管理
│ └── GitHubContext: 管理 GitHub API 交互
└── 评审逻辑
└── 由 LLM 自主决策评审流程
核心代码实现
步骤 1:定义 GitHub 上下文
from dataclasses import dataclass
from github import Github
from typing import List, Optional
@dataclass
class GitHubContext:
"""GitHub 操作的上下文管理器"""
repo_name: str
pr_number: int
github_token: str
def __post_init__(self):
self.github = Github(self.github_token)
self.repo = self.github.get_repo(self.repo_name)
self.pr = self.repo.get_pull(self.pr_number)
步骤 2:定义工具集
from bu_agent_sdk import tool, TaskComplete, Depends
from typing import Annotated
def get_github_context() -> GitHubContext:
"""依赖注入:获取 GitHub 上下文"""
# 这是一个占位符,实际的上下文会在运行时注入
pass
@tool("List all files changed in this pull request")
async def list_changed_files(
ctx: Annotated[GitHubContext, Depends(get_github_context)]
) -> str:
"""获取 PR 中所有变更的文件列表"""
files = ctx.pr.get_files()
file_list = [f.filename for f in files]
return f"Changed files ({len(file_list)}):\n" + "\n".join(f"- {f}" for f in file_list)
@tool("Get the git diff for a specific file")
async def get_file_diff(
filename: str,
ctx: Annotated[GitHubContext, Depends(get_github_context)]
) -> str:
"""获取指定文件的 Git diff"""
files = ctx.pr.get_files()
for file in files:
if file.filename == filename:
return f"Diff for {filename}:\n```diff\n{file.patch}\n```"
return f"File {filename} not found in this PR"
@tool("Read the full content of a file")
async def read_file(
filename: str,
ctx: Annotated[GitHubContext, Depends(get_github_context)]
) -> str:
"""读取文件的完整内容"""
try:
content = ctx.repo.get_contents(filename, ref=ctx.pr.head.sha)
return f"Content of {filename}:\n```\n{content.decoded_content.decode('utf-8')}\n```"
except Exception as e:
return f"Error reading {filename}: {str(e)}"
@tool("Post a review comment on the pull request")
async def post_review_comment(
body: str,
ctx: Annotated[GitHubContext, Depends(get_github_context)]
) -> str:
"""在 PR 上发布评审评论"""
ctx.pr.create_issue_comment(body)
return "Review comment posted successfully"
@tool("Signal that code review is complete")
async def done(message: str) -> str:
"""标记代码评审完成"""
raise TaskComplete(message)
步骤 3:创建智能体
from bu_agent_sdk import Agent
from bu_agent_sdk.llm import ChatAnthropic
# 定义系统提示词
SYSTEM_PROMPT = """你是一个专业的代码评审助手。你的任务是:
1. 首先查看 PR 中有哪些文件被修改
2. 对每个重要文件,获取其 diff 并仔细审查
3. 关注以下方面:
- 代码质量和可读性
- 潜在的 bug 或逻辑错误
- 性能问题
- 安全隐患
- 最佳实践的遵守情况
4. 如果发现问题,使用 post_review_comment 工具发布详细的评审意见
5. 完成所有文件的评审后,调用 done 工具
请保持专业、建设性的态度,提供具体可行的改进建议。
"""
def create_code_review_agent(
github_token: str,
repo_name: str,
pr_number: int
) -> Agent:
"""创建代码评审智能体"""
# 创建 GitHub 上下文
ctx = GitHubContext(
repo_name=repo_name,
pr_number=pr_number,
github_token=github_token
)
# 创建智能体
agent = Agent(
llm=ChatAnthropic(model="claude-sonnet-4-20250514"),
tools=[
list_changed_files,
get_file_diff,
read_file,
post_review_comment,
done
],
system_prompt=SYSTEM_PROMPT,
dependency_overrides={get_github_context: lambda: ctx},
require_done_tool=True # 强制要求调用 done 工具
)
return agent
步骤 4:执行评审(带实时反馈)
async def run_code_review(agent: Agent, pr_description: str):
"""执行代码评审,并打印实时进度"""
print("🤖 Code Review Agent Started")
print("=" * 50)
task = f"Please review this pull request:\n{pr_description}"
async for event in agent.query_stream(task):
# 工具调用事件
if hasattr(event, 'tool') and hasattr(event, 'args'):
tool_name = event.tool
print(f"🔧 Calling tool: {tool_name}")
if event.args:
print(f" Args: {event.args}")
# 工具结果事件
elif hasattr(event, 'tool') and hasattr(event, 'result'):
tool_name = event.tool
result_preview = str(event.result)[:100]
print(f"✅ {tool_name} completed")
print(f" Result preview: {result_preview}...")
# 最终响应事件
elif hasattr(event, 'content') and event.content:
print(f"\n📝 Final Summary:\n{event.content}")
print("=" * 50)
print("✨ Code Review Completed!")
完整的主程序
import os
import asyncio
async def main():
# 从环境变量获取配置
github_token = os.environ.get("GITHUB_TOKEN")
repo_name = os.environ.get("GITHUB_REPOSITORY") # 格式: owner/repo
pr_number = int(os.environ.get("PR_NUMBER"))
pr_description = os.environ.get("PR_DESCRIPTION", "")
# 创建智能体
agent = create_code_review_agent(
github_token=github_token,
repo_name=repo_name,
pr_number=pr_number
)
# 执行评审
await run_code_review(agent, pr_description)
if __name__ == "__main__":
asyncio.run(main())
集成 GitHub Actions
现在让我们把智能体接入 GitHub Actions,实现自动化!
创建 GitHub Action 配置文件
在你的项目根目录创建.github/workflows/code-review.yml:
name: AI Code Review
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
code-review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # 获取完整历史以便做 diff
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install bu-bu-agent-sdk PyGithub anthropic
- name: Run AI Code Review
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_DESCRIPTION: ${{ github.event.pull_request.body }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
python code_review_agent.py
配置密钥
在你的 GitHub 仓库设置中:
- 进入Settings→Secrets and variables→Actions
- 添加新密钥:
ANTHROPIC_API_KEY: 你的 Anthropic API 密钥
GITHUB_TOKEN是 GitHub 自动提供的,无需手动配置。
工作流程说明
当有新的 Pull Request 或更新时:
- 触发 GitHub Action
- 检出代码仓库
- 安装 Python 和依赖
- 运行 code_review_agent.py
- 智能体自动:
- 列出变更文件
- 逐个审查 diff
- 发现问题时发布评论
- 完成后调用
done工具
- 评审意见自动出现在 PR 中
bu-agent-sdk 高级特性
Done 工具模式:确保任务自主与精确完成
秉承“明确的退出机制”这一核心原则,bu-agent-sdk 摒弃了传统框架中模糊的隐式停止条件。通过强制使用一个明确的done工具来解决这个问题。
@tool("Signal completion")
async def done(message: str) -> str:
raise TaskComplete(message)
agent = Agent(
llm=llm,
tools=[..., done],
require_done_tool=True, # 开启自主模式
)
通过设置require_done_tool = True,智能体被强制要求必须显式调用done工具才能结束任务。这赋予了智能体更强的自主性和任务完成的精确性,完美契合了“让模型自己决定何时完成”的设计理念。
上下文管理策略
一个强大的智能体需要高效的“上下文管理”。当“完整的行动空间”意味着工具可能返回海量信息时(例如,整个网页的 DOM 树、高清截图),agent-sdk 提供了两种优雅且符合极简理念的策略来防止上下文窗口崩溃。
瞬态消息管理
对于那些只需要在短期内有用的信息,可以将其标记为“临时的”瞬态信息。bu-agent-sdk 只会保留最近 N 次的此类工具输出,自动丢弃更早的信息。
@tool("Read large log file", ephemeral=True)
async def read_log(filename: str) -> str:
"""读取日志文件(内容会被自动清理)"""
with open(filename) as f:
return f.read()
agent = Agent(
llm=llm,
tools=[read_log],
ephemeral_limit=3 # 只保留最近 3 次临时工具的输出
)
智能上下文压缩
当对话历史接近模型的上下文长度限制时,bu-agent-sdk 可以自动触发一次压缩操作,将早期的对话内容进行摘要,从而为新的信息腾出空间,确保长对话的持续进行。
agent = Agent(
llm=llm,
tools=tools,
context_compaction_threshold=0.8, # 达到 80% 上下文长度时触发压缩
)
多 LLM 提供商支持
bu-agent-sdk 为主流的 LLM 提供商(Anthropic, OpenAI, Google)提供了统一且简洁的接口。切换模型就像更换一个类实例一样简单,无需修改任何其他代码。
from bu_agent_sdk.llm import ChatAnthropic, ChatOpenAI, ChatGoogle
# 使用 Claude
agent = Agent(llm=ChatAnthropic(model="claude-sonnet-4-20250514"), tools=tools)
# 切换到 GPT-4
agent = Agent(llm=ChatOpenAI(model="gpt-4o"), tools=tools)
# 或者使用 Gemini
agent = Agent(llm=ChatGoogle(model="gemini-2.0-flash"), tools=tools)
依赖注入的强大之处
借鉴了 FastAPI 的成功经验,bu-agent-sdk 引入了依赖注入系统。这不仅仅是一种便利,更是一种构建可扩展智能体的战略模式。它允许你管理状态(如用户会话或数据库连接)并引入复杂依赖,而无需污染核心工具逻辑,使得工具高度可重用和可测试。
让我们看一个实际场景:
from typing import Annotated
from bu_agent_sdk import Depends
# 定义依赖
class DatabaseConnection:
def __init__(self, db_url: str):
self.db_url = db_url
async def query(self, sql: str):
# 执行数据库查询
pass
def get_db() -> DatabaseConnection:
"""依赖提供者"""
pass
# 在工具中使用依赖
@tool("Query user data")
async def get_user(
user_id: int,
db: Annotated[DatabaseConnection, Depends(get_db)]
) -> str:
"""查询用户数据"""
result = await db.query(f"SELECT * FROM users WHERE id = {user_id}")
return str(result)
# 创建智能体时注入实际的数据库连接
db_conn = DatabaseConnection("postgresql://localhost/mydb")
agent = Agent(
llm=llm,
tools=[get_user],
dependency_overrides={get_db: lambda: db_conn}
)
优势:
- 工具函数保持纯净,易于测试
- 依赖可以在运行时灵活替换
- 避免全局状态,更加可维护
流式事件
为了提供实时的用户体验,流式处理至关重要。bu-agent-sdk 通过query_stream方法,让开发者可以轻松监听智能体执行过程中的每一个事件。这不仅能极大地改善用户交互,更是调试和观察智能体行为的关键工具,让开发者能够实时洞察其每一步的“思考”过程。
from bu_agent_sdk.agent import ToolCallEvent, ToolResultEvent, FinalResponseEvent
async for event in agent.query_stream("do something"):
match event:
case ToolCallEvent(tool=name, args=args):
print(f"Calling {name}")
case ToolResultEvent(tool=name, result=result):
print(f"{name} -> {result[:50]}")
case FinalResponseEvent(content=text):
print(f"Done: {text}")
这使得构建具有实时反馈的交互式应用(如聊天机器人、代码助手)变得异常简单。
构建得越少,工作得越好
让我们回到开头的核心理念。bu-agent-sdk 通过这个代码评审项目向我们展示了:
- 简单的循环 + 完整的工具集 = 强大的智能体
- 让模型自主决策,而不是用代码控制每一步
- 依赖注入让架构清晰、可测试
- 明确的退出机制确保任务可靠完成
这种极简主义不是功能的削减,而是对本质的回归。当我们停止试图"指导"模型,而是专注于"赋能"模型时,AI 智能体的真正潜力才能被释放。
#智能体 #Agent #开发框架 #Python #代码评审 #GitHubActions #LLM #氛围编程 #Claude #Agent框架