上周刷 GitHub Trending,GenericAgent 冲到周榜前五,一周涨了 3500 star。点进去看,README 第一句话就挺狠:
整个仓库从 git init 到每一次 commit,全是 Agent 自己完成的。作者没开过一次终端。
说实话,看到这种话我一般先打个问号。但翻了源码之后,确实有几个设计让我觉得值得拆一拆。
预加载技能 vs 自己长出来的技能
现在市面上的 Agent 框架,大多数走"预加载"路线——框架自带几十上百个工具,用户按需调用。LangChain 这么干,AutoGPT 也这么干。
问题在哪?
工具越多,system prompt 越长,token 消耗越高。一个带 50 个工具描述的 prompt,光工具定义就能吃掉 8000-12000 token。用户说"帮我查个天气",模型也得先读完 50 个工具定义再决定调哪个。
GenericAgent 反过来——核心只有 9 个原子工具。没有"查天气""发邮件""读微信"这些预置能力。它第一次遇到"帮我读微信消息"这个任务时,会自己装依赖、逆向数据库、写读取脚本,然后把整个执行路径固化成一个 skill。下次再问同样的事,直接一行调用。
第一次:安装依赖 → 逆向DB → 写读取脚本 → 保存skill(约120 token/步 × 15步 = 1800 token)
第二次:调用已有skill(约200 token)
这就是它号称"6倍token节省"的来源——不是第一次省,是后面每一次都省。用得越久,积累的 skill 越多,平均 token 消耗持续下降。
100 行 Agent Loop 到底干了什么
我把 agent_loop.py 翻了一遍,核心循环确实就 100 行出头。去掉日志和格式化代码,关键逻辑可以压缩成这样:
def agent_runner_loop(client, system_prompt, user_input, handler, tools_schema, max_turns=40):
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_input}
]
turn = 0
while turn < max_turns:
turn += 1
# 每10轮重置工具描述,防止上下文膨胀
if turn % 10 == 0:
client.last_tools = ''
response = client.chat(messages=messages, tools=tools_schema)
if not response.tool_calls:
break # 模型没调用工具,任务结束
tool_results = []
for tc in response.tool_calls:
tool_name = tc.function.name
args = json.loads(tc.function.arguments)
# 通过 handler 分发到具体工具
outcome = handler.dispatch(tool_name, args, response)
if outcome.should_exit:
return # 工具主动退出
if not outcome.next_prompt:
break # 当前任务完成
tool_results.append({
'tool_use_id': tc.id,
'content': json.dumps(outcome.data, ensure_ascii=False)
})
# 只传新消息,历史由 Session 管理
messages = [{"role": "user", "content": next_prompt, "tool_results": tool_results}]
几个值得注意的设计:
每 10 轮重置工具描述。 这个操作很务实。长对话里,工具定义会被反复携带,上下文越来越大。每 10 轮清一次 last_tools,强制模型重新加载工具列表,既控制了 token 消耗,又避免了上下文窗口溢出。
历史消息不在循环里累积。 messages 每轮只放当前轮的用户消息和工具结果,历史对话由外层的 Session 对象管理。这样做的好处是 Agent Loop 本身不关心对话有多长——它只看当前轮需要什么。
handler.dispatch 是个生成器。 工具执行结果通过 yield 流式输出,支持长时间运行的工具(比如跑测试、装依赖)在执行过程中就给前端反馈,而不是等全部跑完再返回。
五层记忆:从规则到经验
GenericAgent 的记忆系统分 5 层,每层解决不同问题:
| 层级 | 名称 | 存什么 | 类比 |
|---|---|---|---|
| L0 | Meta Rules | Agent 的行为规则和系统约束 | 你的本能反应 |
| L1 | Insight Index | 极简记忆索引,用于快速路由 | 你的目录和书签 |
| L2 | Global Facts | 长期运行积累的稳定知识 | 你的常识库 |
| L3 | Task Skills | 可复用的任务执行流程 | 你的肌肉记忆 |
| L4 | Session Archive | 已完成任务的精炼记录 | 你的日记 |
这 5 层不是平等的。L3 是核心——每次完成一个新任务,Agent 会把执行路径结晶成一个 skill 存到 L3。L1 是索引层,记录"什么任务该调哪个 skill",保证下次遇到类似任务时能快速路由,不用重新探索。
用代码说的话,skill 结晶的过程大概是这样的:
# 伪代码:skill 结晶流程
class SkillCrystallizer:
def crystallize(self, task_description, execution_trace):
"""从执行轨迹中提取可复用的 skill"""
# 1. 过滤掉失败的步骤和重试
clean_steps = [s for s in execution_trace if s.status == 'success']
# 2. 提取关键操作序列
key_ops = self.extract_key_operations(clean_steps)
# 3. 参数化——把具体值替换成变量
parameterized = self.parameterize(key_ops)
# 比如把 "pip install mootdx" 变成 "pip install {package}"
# 4. 生成 skill 定义
skill = {
"name": self.generate_name(task_description),
"trigger": task_description,
"steps": parameterized,
"dependencies": self.extract_deps(clean_steps),
}
# 5. 写入 L3 记忆层
self.memory.write_l3(skill)
# 6. 更新 L1 索引
self.memory.update_l1_index(skill["name"], skill["trigger"])
return skill
这套设计的好处是 skill 跟用户绑定。同一个 GenericAgent 实例,给不同人用一段时间后,长出来的技能树完全不同。炒股的人有一棵量化筛选的技能树,做自媒体的人有一棵内容发布的技能树。
9 个原子工具够用吗
GenericAgent 只提供 9 个工具:
code_run 执行任意代码
file_read 读文件
file_write 写文件
file_patch 修改文件
web_scan 感知网页内容
web_execute_js 控制浏览器行为
ask_user 人机确认
update_working_checkpoint 持久化上下文
start_long_term_update 写入长期记忆
看起来很少,但仔细想想——code_run 一个工具就能覆盖"装包、写脚本、调 API、控制硬件"这些事。它不是在工具层做扩展,而是在运行时通过 code_run 动态创建新能力,再通过 skill 系统把临时能力固化成永久能力。
这个思路跟 Unix 哲学有点像。Unix 给你 pipe、fork、exec 几个原语,上层的一切都是组合出来的。GenericAgent 给你 code_run、file_*、web_* 几个原语,上层的 skill 都是跑出来的。
说实话,我能想到的局限是——第一次执行复杂任务时,它需要反复试错。比如"帮我读微信消息"这个任务,它得先找到微信数据库在哪、用什么格式存的、怎么解密,中间可能失败好几次。这个过程的 token 消耗不低,用户体验也不太好。但一旦成功一次,后面就是一行调用的事了。
跟其他框架比,它赢在哪
我对比了 GenericAgent 和另一个本周很火的项目 hermes-agent(NousResearch 做的,10万+ star):
| GenericAgent | hermes-agent | |
|---|---|---|
| 代码量 | ~3000 行 | 53万行 |
| 部署 | pip install + API Key | 多服务编排 |
| 浏览器 | 真实浏览器,保留登录态 | 沙箱/无头浏览器 |
| 自进化 | 自主 skill 生长 | 插件生态 |
| 记忆 | 5层分级记忆 | 学习循环+用户建模 |
GenericAgent 赢在极简和自主性。3000 行代码意味着你能在一个下午读完全部源码,理解每个设计决策。而且它的 skill 是完全自主生长的——不需要人写插件、不需要社区贡献,用就行了。
hermes-agent 赢在工程完整度。多平台网关、子 agent 编排、RL 训练支持,这些都是 GenericAgent 目前没有的。
选哪个取决于你想要什么。想理解 Agent 架构原理、想要一个轻量级的个人 Agent,GenericAgent 更合适。想要一个开箱即用的生产级工具,hermes-agent 更成熟。
我的看法
GenericAgent 让我觉得最有意思的不是代码本身,是它背后的理念——Agent 的价值不在于它出厂时带了多少工具,而在于它能不能在使用过程中自己变强。
这跟人学东西其实很像。没人天生会做饭,但你做了几次之后就记住了步骤,下次不用再看菜谱。GenericAgent 的 skill 系统就是这个逻辑——执行过一次的任务,把关键步骤记下来,下次直接用。
当然,目前这类"自进化 Agent"还有不少问题。skill 质量参差不齐(有的执行路径本身不是最优解),skill 之间缺乏组合能力(不能把两个 skill 拼成一个新 skill),记忆层的搜索效率在 skill 数量大了之后也是个问题。
但方向我觉得是对的。未来的 Agent 框架,大概率会从"预装大量工具"走向"少量原语 + 自主进化"。GenericAgent 算是这条路上一个不错的起点。
想试的话,装起来很简单:
git clone https://github.com/lsdefine/GenericAgent.git
cd GenericAgent
pip install streamlit pywebview
cp mykey_template.py mykey.py
# 编辑 mykey.py 填入你的 API Key
python launch.pyw