跳到主要内容

数据截至 (上游 commit 460c729002dc)

BeeAI Framework — 架构与原理

30 秒导读: BeeAI Framework 是 IBM 捐给 Linux Foundation(LF AI & Data)的 agent 框架,Python 和 TypeScript 双实现。它最独特的东西叫 RequirementAgent:你不写 if/else 控制流,而是声明约束——「第 1 步必须先思考」「搜索只能在查完天气之后」「天气工具最多用两次、不许连着用」——框架每一轮把这些约束折叠成「这一轮只准用哪几个工具、必须调哪个」,再连同强制工具调用参数一起发给模型。模型再弱,也很难跑偏。


1. 这是什么(零基础也能懂)

一句话定义: BeeAI Framework 是一个构建 AI agent 与多 agent 系统的通用框架,把「调模型 / 调工具 / 管记忆 / 编排流程 / 对外托管」这几件事做成一整套可组合的模块。

它解决谁的什么问题

假设你要写一个「旅行规划 agent」:先思考一下,再查目的地天气,再搜当地活动,最后给出计划。

你手写这个循环,会遇到三件很烦的事:

麻烦具体表现
模型不听话你在提示词里写「先思考再查天气」,GPT-4 会听,某个 7B 本地模型第一步就直接瞎答
各家 API 不一样OpenAI 支持 tool_choice="required",Ollama 根本没有这个参数,同一份代码换个模型就崩
出错看不见中途某个工具返回空、模型反复调同一个工具死循环,你只看到最后一坨输出

BeeAI 对这三件事各给了一个答案,也就是本套文档要讲的三条主线:

  1. 需求(Requirement)机制 —— 把「先思考再查天气」变成框架层面的硬约束,而不是提示词里的祈祷。
  2. 统一 Backend 层 —— 探测模型不支持 tool_choice 时,自动降级成「用 JSON 结构化输出伪造一次工具调用」。
  3. Emitter + 中间件 —— 每一次模型调用、每一次工具调用都是可订阅的事件,一行 .middleware(GlobalTrajectoryMiddleware()) 就能把整棵执行树打印出来。

它能做什么(功能清单)

  • 多种 agent:RequirementAgent(旗舰,声明式约束)、ReActAgent(经典 Thought/Action/Observation 文本协议)、ToolCallingAgentLiteAgent(零系统提示,用来裸测模型能力)、RAGAgent
  • 统一 LLM 接入:OpenAI / Anthropic / Ollama / watsonx / Bedrock / Gemini / Vertex AI / Groq / Mistral / xAI / DeepSeek / Qwen / MiniMax,以及跑在本机的 Transformers(Hugging Face 本地推理)。绝大多数经由 LiteLLM 转接,transformers / langchain / agentstack 三个是例外(见第 3 章)。
  • 内置工具:搜索(DuckDuckGo / Wikipedia)、天气(OpenMeteo)、代码执行(沙箱 / 本地 Python / Shell)、文件系统(读 / 编辑 / glob / grep)、OpenAPI、MCP 工具、以及给多 agent 用的 HandoffTool
  • 记忆:无上限 / 滑动窗口 / token 预算 / 自动摘要 / 只读。
  • 编排:Workflow 状态机 + AgentWorkflow(把多个 agent 串成流水线)。
  • 托管:一行把 agent 暴露成 A2A、MCP、ACP、OpenAI 兼容接口的服务。

用起来什么样

下面是仓库自带的真实示例(python/examples/agents/requirement/quickstart_requirement.py:15-37),完整地体现了这个框架的味道:

agent = RequirementAgent(
llm=ChatModel.from_name("ollama:granite4:micro"),
tools=[ThinkTool(), OpenMeteoTool(), DuckDuckGoSearchTool()],
instructions="Plan activities for a given destination based on current weather and events.",
requirements=[
ConditionalRequirement(ThinkTool, force_at_step=1), # 第 1 步必须思考
ConditionalRequirement(DuckDuckGoSearchTool,
only_after=[OpenMeteoTool], # 查完天气才能搜
min_invocations=1, max_invocations=2),
ConditionalRequirement(OpenMeteoTool,
consecutive_allowed=False, # 不许连着调两次
min_invocations=1, max_invocations=2),
],
)
response = await agent.run("What to do in Boston?").middleware(GlobalTrajectoryMiddleware())

注意三个细节:

  • 模型是 ollama:granite4:micro —— 一个本地小模型。这不是随便挑的,官方例子刻意用弱模型来证明约束机制有效。
  • requirements 是声明,不是回调 —— 你说「什么条件下允许 / 强制哪个工具」,不写循环。
  • .middleware(...) 挂在 run() 返回值上 —— run() 返回的不是协程而是一个可以先挂钩子再 await 的 Run 对象(见第 4 章)。

一句话直觉

把 RequirementAgent 想成 给模型发的「本轮工具通行证」:每一轮开始前,框架根据历史记录重新签发一张通行证,上面写清楚「这轮你只能用这几个工具、其中这个必须用、这几个连看都别看」。模型的自由度被压缩到通行证之内,于是弱模型也走不丢。


2. 顶层全景(它大概怎么转)

2.1 主要部件与数据流

怎么读这张图:从上往下是一次 agent.run() 的推进方向,右边的回边表示「工具结果写回记忆,进入下一轮」。

你的代码
│ agent.run("What to do in Boston?")

┌──────────────────────────────┐
│ RequirementAgent(门面/配置) │ 持久记忆·模板·工具表·需求表
└──────────────┬───────────────┘
│ 每次 run 新建一个

┌──────────────────────────────┐
│ Runner(逐轮推进的循环) │◄──────────┐
└──────────────┬───────────────┘ │
① 问「这轮允许什么」 │ ④ 工具结果写回
▼ │ 本轮记忆
┌──────────────────────────────┐ │
│ Reasoner(需求→规则→通行证) │ │
└──────────────┬───────────────┘ │
② 允许的工具 + tool_choice │
▼ │
┌──────────────────────────────┐ │
│ ChatModel(统一 LLM 层) │ │
└──────────────┬───────────────┘ │
③ 工具调用 │
▼ │
┌──────────────────────────────┐ │
│ Tool.run(校验·缓存·重试) │───────────┘
└──────────────┬───────────────┘
│ 当模型调到 final_answer 这个特殊工具

最终答案

2.2 部件一句话职责

部件干什么在哪个文件(相对克隆根)
RequirementAgent门面:持有配置和跨轮持久记忆,每次 run 造一个 Runnerpython/beeai_framework/agents/requirement/agent.py
RequirementAgentRunner单次运行的循环体:迭代、发请求、跑工具、收尾python/beeai_framework/agents/requirement/_runner.py
RequirementsReasoner把需求跑成规则,再折叠成「本轮允许的工具 + tool_choice」python/beeai_framework/agents/requirement/utils/_llm.py
Requirement / Rule约束的抽象与最小单位(5 个布尔开关)python/beeai_framework/agents/requirement/requirements/requirement.py
FinalAnswerTool一个「假工具」:模型调用它 = 宣布结束,并写入结构化答案python/beeai_framework/agents/requirement/utils/_tool.py
ChatModel统一 LLM 抽象:工具调用、结构化输出、流式、缓存、重试、能力降级python/beeai_framework/backend/chat.py
Tool工具基类:pydantic 入参校验 → 缓存 → 重试 → 事件python/beeai_framework/tools/tool.py
RunContext / Run每次执行的上下文树 + 可挂钩子的惰性 awaitablepython/beeai_framework/context.py
Emitter分层事件总线,所有可观测性与中间件的地基python/beeai_framework/emitter/emitter.py
Workflow / AgentWorkflow显式状态机 / 多 agent 流水线python/beeai_framework/workflows/
Server把 agent 注册成 A2A / MCP / ACP / OpenAI 兼容服务python/beeai_framework/serve/server.py

2.3 主线走一遍(高层,不进代码)

  1. 入口:agent.run("...")@runnable_entry 装饰,先开一个 RunContext(拿到 run_id、abort 信号、子 emitter),返回一个 Run 对象。
  2. 建临时世界:Runner 新建一份只属于这次运行的记忆(state.memory),把 agent 的持久记忆和这次的新消息倒进去。
  3. 签通行证:Reasoner 遍历所有需求,每个需求看着当前 state(已经走过哪些步、哪个工具用了几次)吐出若干 Rule;这些规则按工具折叠出「允许集 / 隐藏集 / 强制哪个 / 能不能结束」。
  4. 问模型:系统提示按通行证现场渲染(不允许的工具也会列出来并附上「为什么不给用」),连同 tools=允许集tool_choice=强制工具或 required 发给 ChatModel
  5. 跑工具:模型回的工具调用被并发执行,结果作为 ToolMessage 写回本轮记忆;每一步记进 state.steps,供下一轮的需求判断。
  6. 收尾:当模型调用 final_answer 这个特殊工具,FinalAnswerTool._run 把答案写进 state.answer,while 循环条件失效,退出。
  7. 落账:把本轮记忆搬回 agent 的持久记忆(默认保留全部中间步骤)。

3. 阅读地图

建议顺序如下。想快速抓住这个项目的「精华」,读 01 和 02 就够;想读懂它为什么能在弱模型上工作,必须读 03。

章节讲什么什么时候读
01-requirement-agent.md一次 run() 的完整生命周期:双层记忆、迭代循环、文本答案兜底、死循环检测先读,建立主线
02-requirements-and-rules.md需求怎么写、规则怎么合并成通行证、优先级到底管什么本项目最值得学的一章
03-backend-chatmodel.md各家模型 tool_choice 能力差异怎么抹平;用 JSON schema 伪造工具调用想跨模型稳定时读
04-runcontext-emitter-middleware.mdRun / RunContext / Emitter 三件套;中间件如何短路和改写任意执行想做可观测、审批、流式时读
05-tools-memory-workflows-serve.md工具流水线、四种记忆、Workflow、Handoff、对外托管当作模块速查
06-deep-dive-and-boundaries.md巧妙之处清单、真实边界与已知缺陷、横向对比、总代码地图最后读,或用来做技术选型

4. 项目坐标(事实)

仓库i-am-bee/beeai-framework,Apache-2.0,LF AI & Data 项目
布局monorepo:python/(主力)、typescript/docs/(新文档站)、docs-old/
Python 包版本beeai-framework 0.1.82(python/pyproject.toml)
TypeScript 包版本0.1.30(typescript/package.json)
Python 版本要求>=3.11,<3.14
Python 代码量python/beeai_framework/ 约 2.7 万行

两个实现不同步。 Python 侧版本号远超 TS 侧,RequirementAgent 在两边都有目录(python/beeai_framework/agents/requirement/typescript/src/agents/requirement/),但本套文档只解剖 Python 实现——所有引用都指向 python/ 下的真实文件。


5. 代码地图(总入口)

主题文件路径关键符号
旗舰 agent 门面python/beeai_framework/agents/requirement/agent.pyRequirementAgent
单次运行循环python/beeai_framework/agents/requirement/_runner.pyRequirementAgentRunner.run
约束折叠算法python/beeai_framework/agents/requirement/utils/_llm.pyRequirementsReasoner.create_request
约束最小单位python/beeai_framework/agents/requirement/requirements/requirement.pyRuleRequirementrequirement
最常用的内置约束python/beeai_framework/agents/requirement/requirements/conditional.pyConditionalRequirement
统一 LLM 层python/beeai_framework/backend/chat.pyChatModel_force_tool_call_via_response_format
伪造工具调用的 schemapython/beeai_framework/backend/utils.pygenerate_tool_union_schema
provider 能力与别名表python/beeai_framework/backend/constants.pyProviderDefBackendProviders
执行上下文python/beeai_framework/context.pyRunRunContext.enter
事件总线python/beeai_framework/emitter/emitter.pyEmitterEmitter.pipe
工具基类python/beeai_framework/tools/tool.pyTool.runtool
状态机python/beeai_framework/workflows/workflow.pyWorkflow
对外托管python/beeai_framework/serve/server.pyServer.register_factory