数据截至 (上游 commit fa8930ebe72a)
BabyAGI (functionz) — 架构与原理
30 秒导读: 这不是 2023 年那个「任务循环」版 BabyAGI(那个已归档)。这版是一个全新的实验框架:把每个 Python 函数当成数据库里一行可版本化的数据来存,运行时再用
exec即时编译出来执行。因为「函数」是数据,LLM 就能写出新函数、存回同一个库——于是这个 agent 能自己给自己长出新能力。
1. 这是什么(零基础也能懂)
-
一句话定义: BabyAGI 的新核心叫 functionz——一个「把函数存进数据库、按需取出来跑」的框架,附带依赖图、版本控制、触发器和一个可视化 dashboard。
-
解决什么问题 / 给谁用: 假设你想做一个能「自己写工具给自己用」的 AI 助手。难点是:模型生成的新函数得存在某处、能被别的函数调用、调用时它的依赖(别的函数、第三方库、API key)得自动备齐。functionz 就是这套「函数的运行时 + 仓库」。给想玩 self-building agent 概念的开发者(作者明说不是生产级)。
-
它能做什么(功能):
- 用一个装饰器
@register_function把普通 Python 函数登记进库 - 调用
babyagi.some_function()时,从数据库取出代码、即时exec出来执行 - 自动解析并装好函数的依赖(其他函数 + pip 包 + secret key)
- 每次改函数自动存为新版本,可回滚
- 触发器:某函数跑完自动连带跑另一个函数
- 一组自建 agent:把用户的一句话需求,让 LLM 拆成若干小函数、逐个生成代码、存回库
- 用一个装饰器
-
用起来什么样: 最小例子——注册两个互相依赖的函数,然后像调普通函数一样调它:
import babyagi
@babyagi.register_function()
def world():
return "world"
@babyagi.register_function(dependencies=["world"]) # 声明依赖 world
def hello_world():
x = world() # 直接调用——依赖会被自动注入
return f"Hello {x}!"
print(babyagi.hello_world()) # Hello world!
注意:hello_world 里调用的 world 并不是上面那个 Python 闭包,而是执行时从数据库取出来、临时 exec 出来的版本(见第 2 章)。
- 一句话直觉/类比: 把它想成函数版的 WordPress——函数不是写死在
.py文件里,而是像博客文章一样存在数据库里,可增删改查、有版本历史、有后台(dashboard)。代码成了数据,数据就能被(LLM)生成和改写,于是程序能修改自己。
本节不准出现底层细节。记住一句话:「函数 = 数据库里的一行可执行代码」,后面所有机制都从这一点派生。
2. 顶层全景(它大概怎么转)
怎么读这张图
左边是你写代码的入口,中间是 functionz 的三件套 + 数据库,右边是装在库里的函数包(packs)。控制流:注册时函数代码进库,执行时从 库取码 exec 出来跑。
你的代码 / dashboard functionz 核心(单例 _func_instance)
─────────────────── ──────────────────────────────────────
@register_function ──注册──► ┌──────────────┐
babyagi.foo() ──执行──► │ Functionz │ 门面,转发给下面两个
│ (framework) │
└──┬────────┬──┘
│ │
┌─────────────▼─┐ ┌──▼──────────────┐
│ FunctionRegistrar│ │ FunctionExecutor │
│ 「存」函数 │ │ 「跑」函数 │
└────────┬─────────┘ └────────┬────────┘
│ 读/写 │ 读
▼ ▼
┌───────────────────────────────────┐
│ DBRouter ─► LocalDB(SQLite) │
│ Function / FunctionVersion / │
│ Import / Log / SecretKey │
└───────────────────────────────────┘
▲
│ import 时自动 load
┌────────┴──────────────────── ────────┐
│ packs/ 函数包:default / ai / drafts │
│ (gpt_call、self_build、…都是「函数」)│
└─────────────────────────────────────┘
部件一句话职责
| 部件 | 干什么 | 在哪个文件 |
|---|---|---|
babyagi/__init__.py | 包入口:建单例、create_app、register_function、动态属性访问、import 时自动加载默认包 | babyagi/__init__.py |
Functionz | 门面类,把调用转发给 executor / registrar / db | babyagi/functionz/core/framework.py:16 |
FunctionExecutor | 执行引擎:取码、装依赖、exec、绑参、写日志、跑触发器 | babyagi/functionz/core/execution.py:11 |
FunctionRegistrar | 注册器:解析函数签名/返回值、查重、写入新版本 | babyagi/functionz/core/registration.py:12 |
DBRouter | 数据库门面,把 ORM 对象拍平成普通 dict | babyagi/functionz/db/db_router.py:14 |
LocalDB | SQLite + SQLAlchemy 的真实落库 | babyagi/functionz/db/local_db.py:12 |
models.py | 5 张表:Function / FunctionVersion / Import / Log / SecretKey | babyagi/functionz/db/models.py |
| packs | 自带函数包:default(库操作)、ai(gpt_call/embedding)、drafts(自建 agent) | babyagi/functionz/packs/ |
主线走一遍(高层,不进代码)
以 babyagi.hello_world() 为例:
babyagi模块没有hello_world这个真属性 → 触发模块级__getattr__(babyagi/__init__.py:112)。- 它去数据库问「有
hello_world吗?」有 → 返回一个会调用executor.execute('hello_world')的 lambda。 FunctionExecutor.execute从库里取出hello_world的当前活跃版本代码(字符串)。- 先递归把它的依赖
world也取出来、exec进一个局部作用域,并包一层 wrapper。 - 把
hello_world的代码exec出来,绑定参数、执行,得到"Hello world!"。 - 全程往
Log表写「开始/成功/耗时」,执行完检查有没有触发器要连带跑。
目标:看懂「函数以字符串代码存在库里,执行时才被 exec 复活」这个大盘。细节进各章。