3K 行代码干翻 Claude Code?从 Agent 架构设计者视角深度拆解 GenericAgent 源码
我是 iuyup,一个专注于 agnet 开发的大三学生。最近在 V2EX 看到一篇帖子,有人说高强度使用半年 Claude Code 后卸载了它,转投一个叫 GenericAgent 的项目。帖子 200+ 回复,热度很高。作为一个整天跟 Agent 架构打交道的人,我决定把源码拉下来彻底读一遍,然后亲手跑了一圈。这篇文章是我读完 3K 行核心代码后的技术拆解。
一、先说结论
GenericAgent(以下简称 GA)是复旦团队开源的通用 Agent 框架,GitHub 10K+ stars,有 arXiv 技术报告支撑。它的核心代码只有约 3000 行,却能实现文件操作、浏览器控制、终端执行、手机 ADB 操控等全系统级能力。
读完源码后我最大的感受是:GA 的厉害不在于它做了什么,而在于它没做什么。
没有 LangGraph,没有 DAG,没有 class 继承链,没有向量数据库,没有 embedding。就是一个 while 循环不断调 LLM,靠 prompt engineering 和极简的工具集驱动一切。而它的论文数据显示,这种极简设计在长程任务上能用 1/3 的 token 预算达到同等甚至更好的效果。
下面从架构层面拆解它为什么能做到这一点。
二、代码全景:3K 行里有什么
先看文件分布:
| 文件 | 行数 | 职责 |
|---|---|---|
ga.py |
585 | 核心大脑 --- 工具实现 + 记忆管理 + Working Memory |
agent_loop.py |
125 | Agent Loop --- 执行主循环 |
agentmain.py |
270 | 入口 + 会话管理 + reflect 模式 |
llmcore.py |
1026 | LLM API 统一调用层 + 上下文压缩 |
TMWebDriver.py |
283 | 浏览器 CDP 控制 |
simphtml.py |
873 | HTML 解析压缩引擎 |
真正需要精读的是 ga.py + agent_loop.py,加起来 710 行,这就是整个系统的灵魂。
三、Key Point 1:9 个原子工具,为什么「少」反而更强
GA 的全部工具集:
css
code_run → 执行 Python / Bash
file_read → 读文件
file_write → 写文件
file_patch → 精确替换文件片段
web_scan → 获取简化 HTML + 标签页列表
web_execute_js → 注入 JS 控制浏览器
update_working_checkpoint → 更新工作记忆
ask_user → 中断问用户
start_long_term_update → 触发长期记忆结算
就这 9 个。没有 web_search------搜索是通过 web_execute_js 在浏览器里操作 Google 完成的。没有 screenshot------截图是通过 code_run 调 Python 脚本做的。没有 send_telegram------发消息是通过 code_run 调 Telegram Bot API 完成的。
这是一个元工具 vs 专用工具的架构选择。Claude Code 给每类操作设计专用工具,GA 只提供几个"万能执行器"。
这样做的好处很直接:
1. Tool schema 占用的 context 大幅减少。 每轮对话都要把工具描述带上,9 个工具的 schema 和 30 个工具的 schema,token 消耗差距巨大。这就是论文里说的「上下文信息密度最大化」在工具层面的体现。
2. LLM 的工具选择决策更简单。 在 9 个工具里选 vs 在 30 个工具里选,决策空间降了一个量级,出错率自然更低。
3. 不存在"没有对应工具所以做不了"的问题。 code_run 理论上能做任何事------HTTP 请求、图像处理、数据分析,只要 Python 能做的它都能做。
当然代价也存在:它把工具选择的复杂度转移到了代码生成的复杂度上。 Claude Code 调 web_search 只需要传一个 query 参数,GA 要让 LLM 生成一段完整的 JS 去操作搜索引擎。这对模型的代码生成能力要求更高,用弱模型时效果会打折扣。
本质上 GA 是在赌:强模型 + 少 token > 弱模型 + 多 token。 在当前 Sonnet/DeepSeek-V3 这个级别的模型能力下,这个赌注是成立的。
四、Key Point 2:四层记忆架构------task-centric 记忆设计
这是我认为 GA 最值得研究的部分。先看架构:
makefile
L0: META-SOP(记忆管理规则本身 ------ memory_management_sop.md)
L1: global_mem_insight.txt(≤30 行的极简索引)
L2: global_mem.txt(环境事实库:路径、配置、凭证)
L3: memory/*.md, *.py(任务级 SOP 和工具脚本)
L4: L4_raw_sessions/(历史会话原始记录)
L1 索引层:30 行的全局导航
这是整个记忆系统最精妙的设计。L1 只有 30 行,硬约束,常驻在 system prompt 里。它的内容是这样的:
css
L3: memory_cleanup_sop(记忆整理) | skill_search | ui_detect.py | ...
浏览器特殊操作: tmwebdriver_sop(文件上传/图搜/PDF blob/...)
键鼠: ljqCtrl_sop(禁pyautogui/先activate)
[RULES]
1. 搜索先行: 搜文件名严禁不用es, 搜索一定优先使用web工具的google
2. 交叉验证: 禁信摘要, 数值进详情页核实
...
这意味着 Agent 每一轮都能看到自己的全部能力索引,但不会因为记忆太多而把 context 撑爆。需要细节时再 file_read 去拿 L2/L3。
这就是「分层按需加载」------和操作系统的内存分页是同一个思想。
记忆写入的高门槛
GA 的记忆管理有一条最高优先级公理:
No Execution, No Memory.(无行动,不记忆)
只有工具调用成功验证的信息才能写入记忆。猜测、推理、未执行的计划一律禁止。这从源头避免了记忆污染的问题。
对比我自己做的 Agent Memory System,我用的是 Mem0 风格的提取管道------从对话中自动提取信息写入记忆。这种方式容易引入 LLM 的幻觉。GA 的「只记工具验证成功的信息」是更 robust 的策略,虽然记忆覆盖率低一些,但准确率极高。
Task-centric vs User-centric
传统的 Agent 记忆系统(包括我做的那个)是 user-centric 的------记住用户偏好、历史对话、个人信息。GA 的记忆是 task-centric 的------只关心「这个任务怎么做更高效」。
L3 里的 SOP 文件存的都是类似「用这个方法会踩坑,正确做法是 XXX」这种高度提炼的信息,而不是用户画像。这两种方式各有适用场景,但对于一个「效率工具」定位的 Agent,task-centric 显然是更合理的选择。
五、Key Point 3:Working Memory 注入------长程任务的命脉
这是 Agent Loop 层面最核心的机制。看 _get_anchor_prompt() 的实现:
python
def _get_anchor_prompt(self, skip=False):
h = self.history_info; W = 30
# 早期历史压缩成一行摘要
earlier = self._fold_earlier(h[:-W])
# 最近 30 条完整保留
h_str = "\n".join(h[-W:])
prompt = f"[WORKING MEMORY]\n{earlier}\n{h_str}"
# Agent 自己设置的关键信息
if self.working.get('key_info'):
prompt += f"\n<key_info>{self.working.get('key_info')}</key_info>"
return prompt
每一轮工具调用结束后,都会把这段 Working Memory 注入到下一轮的 user message 中。 它包含压缩过的早期历史、最近 30 条完整记录、以及 Agent 通过 update_working_checkpoint 主动设置的关键信息。
这就是为什么 GA 能在长程任务中保持连贯------它不是靠 LLM 自己记住上下文,而是每轮强制注入一个结构化的「当前状态摘要」。
配合 turn_end_callback 里的多重防护机制:
python
if turn % 7 == 0:
next_prompt += "[DANGER] 禁止无效重试。若无有效进展,必须切换策略。"
if turn % 10 == 0:
next_prompt += get_global_memory() # 每 10 轮重新注入全局记忆
每 7 轮强制提醒切换策略、每 10 轮重新注入全局记忆、每 10 轮重置工具描述防止 context 膨胀。这些都是工程层面的细节,但对长程任务的稳定性至关重要。
六、Key Point 4:上下文压缩------两层裁剪
LLM 历史压缩
compress_history_tags 每 5 轮触发一次,把早期消息中的 <thinking>、<tool_use>、<tool_result> 标签内容截断到 800 字符:
python
def compress_history_tags(messages, keep_recent=10, max_len=800):
# 保留最近 10 条不压缩
for i, msg in enumerate(messages):
if i >= len(messages) - keep_recent: break
# 截断早期消息中的 thinking/tool_result
msg['content'] = _trunc(msg['content'])
简单粗暴但逻辑成立------早期的 thinking 过程对当前决策几乎没有价值,但如果不压缩会持续消耗 token。
HTML 压缩
simphtml.py 是 873 行的 HTML 简化引擎。先在浏览器端用 JS 做 DOM 裁剪(移除隐藏元素、浮动元素、广告栏、script/style 标签),然后 Python 端再做 token 级截断。最终送给 LLM 的是高度精简的页面主体内容。
这就是为什么 GA 浏览器操作只用 1/5 预算的核心原因------其他 Agent 把整个 HTML 塞给 LLM,GA 只给简化后的关键内容。
七、Key Point 5:Skill Tree 自进化
GA 的「Skill」本质就是 L3 层的 SOP 文件和 Python 脚本。当 Agent 完成一个复杂任务后,通过 start_long_term_update 触发记忆结算,由 LLM 自己决定是否要把经验写成 SOP 或工具脚本。
没有向量数据库,没有 embedding,就是文件系统 + LLM 自主读写。用几周后,你的 memory/ 目录会积累起一棵独一无二的技能树。
skill_search 模块还支持连接复旦的 API 服务搜索社区共享的 Skill,但核心的个人 Skill Tree 就是本地文件积累。
八、实测体验:3 个任务看 GA 的决策链路
任务 1:创建斐波那契脚本(5 Turn)
创建 hello.py → 运行验证 → 发现执行失败 → 改用 PowerShell → 验证成功
干净利落,展示了基本的工具调用 + 失败升级。

任务 2:查 GitHub Star 数(8 Turn)
这个更有意思:
web_scan(尝试浏览器)→ 失败
→ 改用 Google 搜索 → 浏览器还是不行
→ 读 tmwebdriver_sop(查 L3 记忆找方案)→ SOP 也解决不了
→ 最终 fallback 到 code_run 用 PowerShell 调 GitHub API → 成功
8 个 turn 里完整展示了失败升级策略。而且它最终选了 code_run 调 API 而不是继续死磕浏览器------同一个"元工具"既能写文件也能当 HTTP 客户端。
任务 3:发 Telegram 消息(7 Turn)
给了它一句「帮我发一条消息到 Telegram」,它:
- 立刻
ask_user问我要 Bot Token - 我只给了 Token,没给 Chat ID
- 它自己用
code_run调 Telegram Bot API 的getUpdates获取到了 Chat ID - 然后
sendMessage发出去
成功了。整个「Telegram 集成」不需要预置------它用 9 个原子工具现场组合出来的。Claude Code 要做同样的事需要装 MCP Server 或者手动写脚本。

九、GA 的局限性
公平起见,也说说不足:
- 前端极其简陋。 开发者自己说了,毛坯房风格,不改。当然我们可以根据自己的需求自己写一个前端。
- 安全性粗放。 给 Agent 系统级控制权,一旦 LLM 幻觉,后果不只是代码写错------可能是文件删除或系统误操作。Claude Code 至少有沙箱和权限控制的概念。
- 依赖强模型。 元工具设计把工具选择的复杂度转移到了代码生成上,弱模型用 GA 效果会大打折扣。
- 生态成熟度不及 CC。 学术团队项目 vs Anthropic 商业产品,长期维护的确定性不同。
十、对 Agent 开发者的启示
读完 GA 源码后,有几个认知升级:
1. 工具数量不是越多越好。 每多一个工具就多占 context、多一层决策复杂度。9 个原子工具覆盖全系统操作,这个设计让我反思自己项目里的工具集是否过度膨胀。
2. 记忆系统需要一个常驻的极简索引层。 GA 的 L1 只有 30 行,但让 Agent 每轮都知道「我有哪些能力」。这比把所有记忆塞进 system prompt 或者依赖检索召回都更高效。
3. 「No Execution, No Memory」是比 Mem0 更 robust 的策略。 从对话中自动提取信息容易引入幻觉,只记工具验证成功的信息更安全。
4. Working Memory 注入是长程任务连贯性的关键。 不能指望 LLM 自己记住 50 轮前的上下文,需要每轮强制注入结构化的状态摘要。
5. 极简不是偷懒,是设计选择。 3K 行代码,没有框架依赖,任何人都能读完。这在 Agent 框架普遍臃肿的现状下是巨大的差异化------也是它能拿到 10K stars 的重要原因。
项目地址: github.com/lsdefine/Ge...
技术报告: arxiv.org/abs/2604.17...
如果你也在做 Agent 相关的开发,推荐花两个小时把 ga.py 和 agent_loop.py 读一遍。710 行代码,收获会比读很多论文都大。