数据截至 (上游 commit 101f0313b0dd)
自主 Agent 与 Ralph 自主开发循环
30 秒导读: 前面几章的 Agent 每次「你问一句、它答一句」。本章讲 Upsonic 最有辨识度的那类 agent:它自己会动手——在一个受限的工作区(workspace)里读写文件、执行 shell 命令;更进一步,
RalphLoop让它自己迭代——你只给一句「做一个 FastAPI 待办应用」,它就分解需求、列 TODO、一轮实现一个任务,直到测试全部通过。核心两件套:能动手的AutonomousAgent/DeepAgent,和能自迭代的RalphLoop。
本章聚焦「沙箱化 + 自迭代」这一层。一次运行怎么跑完 24 步管线见 02-execution-pipeline.md;通用工具系统(自定义工具 / MCP / HITL) 见 04-tools-system.md;这里只讲把工具箱预装进 agent、并把「运行」包成一个能容错的循环。
1. 这是什么(零基础也能懂)
一句话定义: 自主 Agent 是预装了「手脚」的 agent——出厂即带文件工具和 shell 工具,并把所有操作关进一个工作区目录里;Ralph 循环则是把这样的 agent 反复运行、每轮换一个全新实例、跑完一个任务的自动开发引擎。
解决什么问题 / 给谁用:
- 想让 AI 帮你改一个真实项目的代码(读文件、改文件、跑测试)——但你不想让它误删你主目录的东西。
- 想给 AI 一个目标(而不是一步步指令),让它整晚自己把项目写出来,早上回来看结果。
它能做什么:
| 能力 | 由谁提供 |
|---|---|
| 读/写/编辑/搜索/移动/删除文件 | AutonomousFilesystemToolKit |
| 跑 shell 命令、跑 Python 片段、查命令是否存在 | AutonomousShellToolKit |
| 所有操作只限工作区,危险命令拦截 | 两个 toolkit 的路径校验 + 命令黑名单 |
| 会话记忆(默认开) | 出厂注入 InMemoryStorage + Memory |
| 可插拔虚拟文件系统(内存 / 持久化 / 混合) | DeepAgent + backends |
| 给一个 goal 自动写完项目 | RalphLoop |
用起来什么样: 一个最小真实示例(来自 RalphLoop 类文档串,ralph/loop.py:36-45):
from upsonic import RalphLoop
loop = RalphLoop(
goal="Build a FastAPI TODO app", # 只给目标
model="openai/gpt-4o",
test_command="pytest", # 用它当「验收门」
)
result = loop.run() # 自己跑,直到 TODO 清空
print(result.summary())
一句话直觉/类比: 把它想成**「土拨鼠之日」(Groundhog Day)里的程序员**:每天早上失忆醒来(全新 agent、干净上下文),但桌上留着三份纸——需求书、待办清单、给自己的便签(learnings)。他每天只做一件待办、做完打勾、给自己写张便签,然后睡去;明天的「他」接着干。日复一日,项目就长出来了。Ralph 论文管这叫「eventually consistent(最终一致)」的开发。
本节不出现底层代码。记住两个词:沙箱(sandbox) 和 自迭代(loop)。
2. 顶层全景(它大概怎么转)
Upsonic 在通用 Agent(见 01-agent-and-task.md)之上,派生出三个「会动手」的类,再用一个循环把它们编排起来:
Agent (通用基类,第 1、2 章)
│ 继承
┌──────────────┼───────────────────┐
▼ ▼ ▼
AutonomousAgent DeepAgent (Ralph 内部临时 Agent)
├ 文件工具 ├ 可插拔虚拟FS 每轮 new 一个、用完 del
├ shell 工具 ├ 规划工具 write_todos
├ 工作区沙箱 └ 子 agent 委派 task
└ 默认内存+记忆
RalphLoop ──把「运行一个 agent」包成能容错的循环──
│
▼
需求阶段 → TODO 阶段 → 增量阶段(死循环,一轮一个任务)
│
每轮:全新 Agent + 背压门(build/test)
怎么读这张图: 上半是三种能动手的 agent(都继承通用 Agent);下半是把 agent 反复跑的 RalphLoop。两半的连接点:Ralph 的增量阶段每轮临时 new 一个普通 Agent(不是 AutonomousAgent),给它装上 Ralph 自己的一套工具。
部件一句话职责:
| 部件 | 干什么 | 在哪个文件 |
|---|---|---|
AutonomousAgent | 预装文件+shell 工具、工作区沙箱、默认记忆 | agent/autonomous_agent/autonomous_agent.py:30 |
AutonomousFilesystemToolKit | 读写编辑搜索文件,含读前置校验 | agent/autonomous_agent/filesystem_toolkit.py:19 |
AutonomousShellToolKit | 跑命令/Python,含超时和黑名单 | agent/autonomous_agent/shell_toolkit.py:22 |
DeepAgent | 规划 + 可插拔虚拟文件系统 + 子 agent 委派 | agent/deepagent/deepagent.py:29 |
| backends | 虚拟 FS 的三种后端:内存/持久化/混合路由 | agent/deepagent/backends/ |
RalphLoop | 三阶段编排 + 死循环 + 信号处理 | ralph/loop.py:24 |
BackpressureGate | 跑 build/test/lint,当「验收门」 | ralph/backpressure/gate.py:85 |
StateManager | 读写工作区里的状态文件(specs/fix_plan/AGENT.md) | ralph/state/manager.py:16 |
SubagentSpawnerToolKit | 主 agent 派生一次性 subagent 干重活 | ralph/tools/subagent_spawner.py:25 |
主线走一遍(高层):
输入一个 goal → RalphLoop.run() 先跑需求阶段(把目标写成 specs/*.md)→ TODO 阶段(把 specs 拆成 fix_plan.md 勾选表)→ 进入增量阶段死循环:每轮读状态、new 一个全新 agent、让它挑一条未勾选任务做完、跑背压门、打勾、记 learnings → 当 fix_plan.md 里没有未勾选项时,循环判定 completed 退出。
3. 核心原理之一:AutonomousAgent —— 把工具箱和沙箱预装进 agent
它要解决的小问题: 通用 Agent 你得自己配工具、自己配记忆、自己防止它乱碰文件系统。写一个「编码助手」时这些是每次都要重来的样板。AutonomousAgent 把这套打包成开箱即用。
思路/直觉: 它就是一个带默认值的 Agent 子类——__init__ 里把「默认存储 + 默认记忆 + 文件工具 + shell 工具 + 一段讲清工具用法的系统提示」都准备好,再调 super().__init__()。
三个默认值的装配(源码走读):
- 工作区兜底——没给
workspace就用当前目录,并确保它存在:
# agent/autonomous_agent/autonomous_agent.py:216
if workspace is not None:
self.autonomous_workspace = Path(workspace).resolve()
else:
self.autonomous_workspace = Path.cwd().resolve()
-
默认存储/记忆——没传 storage/memory/db 时,自动 new 一个
InMemoryStorage,并据此建Memory(autonomous_agent.py:224-259)。所以full_session_memory默认为True,对话历史默认就有。 -
默认工具——按开关把两个 toolkit 塞进
default_tools,再和用户自带工具拼一起:
# agent/autonomous_agent/autonomous_agent.py:271
if enable_filesystem:
self.filesystem_toolkit = AutonomousFilesystemToolKit(workspace=self.autonomous_workspace)
default_tools.append(self.filesystem_toolkit)
if enable_shell:
self.shell_toolkit = AutonomousShellToolKit(workspace=self.autonomous_workspace, ...)
default_tools.append(self.shell_toolkit)
all_tools = default_tools + (tools or [])
关键细节:动态系统提示。 _build_autonomous_system_prompt(autonomous_agent.py:358)根据开了哪些工具拼出提示词:列出每个工具、给使用守则(「edit 前必须先 read」)、最后附一段安全声明——
# agent/autonomous_agent/autonomous_agent.py:470
security_notes = ["\n## Security Restrictions"]
if enable_filesystem:
security_notes.append("- All file operations are sandboxed to the workspace directory.")
security_notes.append("- Path traversal (../) outside the workspace is blocked.")
if enable_shell:
security_notes.append("- Dangerous shell commands are blocked for security.")
注意:这段提示只是告诉模型有沙箱;真正的强制在下面 §4 的 toolkit 代码里。用户若自带 system_prompt,则原样包进 <AutonomousAgent>...</AutonomousAgent>(autonomous_agent.py:379),不再拼默认那套。
附带的心跳(heartbeat)能力: 传 heartbeat=True 后,接口层可周期性给 agent 发 heartbeat_message,由 aexecute_heartbeat(autonomous_agent.py:508)以静默方式(临时把 print 关掉)跑一次 do_async 并取回文本。这是「让 agent 定时自己动一下」的钩子。