Skip to content

Latest commit

 

History

History
550 lines (410 loc) · 15.7 KB

File metadata and controls

550 lines (410 loc) · 15.7 KB

ReCode 开发文档

本文档面向新手开发者,用简单清晰的语言介绍 ReCode 项目的代码结构和工作原理。


目录

  1. 项目概述
  2. 核心思想
  3. 目录结构
  4. 核心模块详解
  5. 执行流程图解
  6. 关键概念解释
  7. 如何扩展

1. 项目概述

ReCode 是一个 LLM Agent 框架,核心思想是通过 递归代码生成 来统一"规划"和"执行"。

一句话解释

把复杂任务拆成代码树,遇到"不会做的函数"就让 LLM 展开,直到全部变成能直接执行的动作。

适用场景

  • 需要多步骤推理的任务(如:机器人导航、网页操作)
  • 任务粒度不固定,需要动态调整
  • 需要生成可追溯的决策过程

2. 核心思想

2.1 传统方法 vs ReCode

传统方法 (ReAct) ReCode
思考 → 执行 → 观察 → 思考... 写代码 → 执行 → 遇到占位函数 → 展开 → 继续执行
每步都要 LLM 决策 只在需要时调用 LLM
扁平的动作序列 树形的代码结构

2.2 核心循环

任务开始
    ↓
创建根节点: solve(任务)
    ↓
执行代码 ←────────────────┐
    ↓                      │
成功?─── 是 → 下一个节点 ─┘
    │
    否
    ↓
是占位函数?─── 是 → 调用LLM展开 → 创建子节点
    │
    否
    ↓
报错,结束

3. 目录结构

ReCode/
├── run.py                    # 🚀 程序入口,命令行工具
├── base/                     # 📦 基础抽象类
│   ├── agent.py              #    Agent 接口定义
│   └── environment.py        #    Environment 接口定义
├── agents/                   # 🤖 Agent 实现
│   └── recode/
│       ├── agent.py          #    ReCode Agent 核心逻辑
│       ├── utils.py          #    辅助工具(CodeNode、代码解析)
│       └── resources/        #    提示词和示例
│           ├── prompts/      #      LLM 提示词模板
│           └── fewshots/     #      少样本示例
├── envs/                     # 🌍 环境实现
│   ├── alfworld/             #    ALFWorld 家居任务环境
│   ├── webshop/              #    WebShop 购物环境
│   └── sciworld/             #    ScienceWorld 科学实验环境
├── utils/                    # 🔧 工具模块
│   ├── llm.py                #    LLM 调用封装
│   ├── executor.py           #    Python 代码执行器
│   ├── common.py             #    通用工具函数
│   ├── logger.py             #    日志工具
│   └── errors.py             #    自定义异常
└── configs/                  # ⚙️ 配置文件
    ├── profiles.yaml         #    LLM 配置(API Key 等)
    └── prices.json           #    模型价格表

4. 核心模块详解

4.1 基础类 (base/)

base/agent.py - Agent 接口

class Agent(ABC):
    async def act(observations) -> actions    # 根据观察返回动作
    def reset(config, init_info)              # 重置 Agent 状态
    def report() -> dict                      # 返回运行报告

作用:定义所有 Agent 必须实现的方法,确保不同 Agent 可以互换使用。

base/environment.py - 环境接口

class Env(ABC):
    async def run(actions) -> observations    # 执行动作,返回观察
    def reset(config) -> init_info            # 重置环境
    def is_done() -> bool                     # 是否结束
    def is_success() -> bool                  # 是否成功
    def report() -> dict                      # 返回环境报告

作用:定义环境的标准接口,让 Agent 可以和任何环境交互。


4.2 ReCode Agent (agents/recode/)

agent.py - 核心逻辑

这是 ReCode 的大脑,负责:

  1. 初始化代码树:把任务变成根节点 solve(instruction, observation)
  2. 执行循环:执行节点代码,处理结果
  3. 展开占位函数:调用 LLM 生成子代码
  4. 管理状态:跟踪当前节点、深度等

关键方法

方法 作用
act() 主循环:执行当前节点,处理结果
_handle_stub() 处理需要展开的占位函数
_expand() 调用 LLM 生成展开代码
_execute() 执行代码片段
_init_code_tree() 初始化代码树
_build_expand_prompt() 构建 LLM 提示词

utils.py - 辅助工具

CodeNode 类:代码树的节点

@dataclass
class CodeNode:
    code: str                    # 这个节点的代码
    parent: CodeNode             # 父节点
    children: list[CodeNode]     # 子节点列表
    status: NodeStatus           # 状态(等待/完成/需展开/出错)
    depth: int                   # 树的深度
    thought: str                 # LLM 的思考过程
    observations: list           # 执行过程中的观察

辅助函数

函数 作用
split_blocks() 把代码字符串拆分成独立的语句
validate_blocks() 验证代码块是否合法
get_variables() 从代码中提取变量信息
parse_raw_observation() 解析环境的初始观察

4.3 工具模块 (utils/)

executor.py - 代码执行器

核心职责

  1. 执行 Python 代码:用 exec() 运行代码
  2. 管理变量:保存执行过程中的变量
  3. 调用环境:通过 run("动作") 与环境交互
  4. 识别占位函数:捕获 NameError 判断是否需要展开

神奇的机制

# 当执行这行代码时
obj_ID = find_and_take(obj, locations)

# Python 会报错:NameError: name 'find_and_take' is not defined

# Executor 捕获这个错误,判断它是占位函数调用
# 返回 "NeedExpansion" 信号,而不是当作真正的错误

llm.py - LLM 调用封装

AsyncLLM 类:异步调用大语言模型

llm = AsyncLLM("default")                    # 创建实例
response, cost = await llm(prompt)           # 调用

功能

  • 支持多个配置 profile(不同的 API Key、模型)
  • 自动重试失败请求
  • 计算 API 调用费用

common.py - 通用工具

函数 作用
read_json_file() 读取 JSON 文件
write_json_file() 写入 JSON 文件
read_yaml_file() 读取 YAML 文件
parse_code_block() 从 Markdown 中提取代码块
parse_xml_tag() 从文本中提取 XML 标签内容

logger.py - 日志工具

SimpleLogger 类:封装日志记录功能

logger = SimpleLogger(run_id="my_experiment")
logger.info("开始运行...")
logger.log_stats({"total_tests": 10, "successful_tests": 8})

功能

  • 同时输出到文件和控制台
  • 自动创建日志目录 logs/<run_id>/running_logs/
  • 支持多行日志格式化

mockllm.py - 模拟 LLM

用于测试和调试的模拟 LLM,用户可以手动输入响应。

mock_llm = MockLLM()
response = await mock_llm("你好")  # 在控制台手动输入响应

使用场景

  • 调试 Agent 逻辑时不想花费 API 费用
  • 手动控制 Agent 的决策过程

errors.py - 自定义异常

异常类 说明
StepLimitError 环境执行步数超过最大限制时抛出

4.4 环境模块 (envs/)

每个环境文件夹包含:

  • env.py:环境实现类
  • base_config.yaml:环境配置
  • data/:数据文件(如任务索引)

支持的环境

环境 说明 任务类型
ALFWorld 家居机器人任务 put, clean, heat, cool, examine, puttwo
WebShop 网页购物任务 搜索、浏览、购买商品
ScienceWorld 科学实验任务 物理、化学、生物实验

ALFWorld 环境详解 (envs/alfworld/)

ALFWorld 是一个基于 TextWorld 的家居交互环境。

可用动作(在 resources/prompts/alfworld/actions.txt 中定义):

  • go to <location> - 前往某位置(如 go to table 1
  • take <object> from <location> - 拿取物品
  • put <object> in/on <location> - 放置物品
  • open/close <object> - 打开/关闭容器
  • examine <object> - 检查物品或位置
  • use <object> - 使用物品(加热、冷却等)

任务类型说明

类型 说明 示例任务
put 找到物品放到指定位置 "把苹果放到桌子上"
clean 清洁物品后放置 "清洗杯子放到柜子里"
heat 加热物品后放置 "用微波炉加热土豆放到盘子上"
cool 冷却物品后放置 "用冰箱冷却番茄放到台面上"
examine 在灯光下检查物品 "在台灯下检查书"
puttwo 放置两个同类物品 "把两个苹果放到冰箱里"

4.5 配置模块 (configs/)

profiles.yaml - LLM 配置

配置不同的 LLM 访问参数,支持多个 profile。

models:
  default:
    api_key: "sk-your_api_key"
    base_url: "https://api.openai.com/v1"
    model: "gpt-4o-mini"
    temperature: 0.0
  
  gpt-4o:
    api_key: "sk-your_api_key"
    model: "gpt-4o"
    temperature: 0.7

使用方法

# 复制示例配置
cp configs/profiles_example.yaml configs/profiles.yaml

# 编辑填入你的 API Key
# 然后运行时指定 profile
python run.py --profile gpt-4o

prices.json - 模型价格表

用于计算 API 调用费用,单位是美元/百万 token。

{
    "gpt-4o": {"input": 2.5, "output": 10.0},
    "gpt-4o-mini": {"input": 0.15, "output": 0.6}
}

4.5 主程序 (run.py)

功能

  1. 解析命令行参数
  2. 加载配置文件
  3. 创建 Agent 和 Environment 实例
  4. 运行多个任务实例(支持并发)
  5. 收集结果并生成报告

使用示例

# 单个实例
python run.py -a recode -e alfworld -n 1

# 多实例并发
python run.py -a recode -e webshop -n 10 -c 5

# 使用配置文件
python run.py -C configs/example.yaml

5. 执行流程图解

5.1 整体流程

┌─────────────────────────────────────────────────────────────────┐
│                           run.py                                 │
│  1. 解析命令行参数                                                │
│  2. 创建 Agent 和 Env 实例                                       │
│  3. 调用 run_single_instance()                                   │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    run_single_instance()                         │
│  while not env.is_done():                                        │
│      actions = await agent.act(observations)                     │
│      observations = await env.run(actions)                       │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                      agent.act()                                 │
│  1. 如果当前节点需要展开 → 调用 LLM                               │
│  2. 执行当前节点代码                                              │
│  3. 根据执行结果更新状态                                          │
│  4. 移动到下一个节点                                              │
└─────────────────────────────────────────────────────────────────┘

5.2 代码树演变过程

以 "把苹果放到桌子上" 任务为例:

初始状态

solve("把苹果放到桌子上", observation)  [PENDING]

第 1 次展开后

solve(...)  [STUB → 已展开]
├── apple_id = find_and_take("apple", locations)  [PENDING]
└── put_on(apple_id, "table")  [PENDING]

第 2 次展开后

solve(...)  [COMPLETED]
├── find_and_take(...)  [STUB → 已展开]
│   ├── for loc in locations:  [PENDING]
│   │       run("go to ...")
│   │       run("take ...")
└── put_on(...)  [PENDING]

执行完成后

solve(...)  [COMPLETED]
├── find_and_take(...)  [COMPLETED]
│   └── (for 循环已执行)  [COMPLETED]
└── put_on(...)  [COMPLETED]

6. 关键概念解释

6.1 节点状态 (NodeStatus)

状态 含义 下一步
PENDING 等待执行 执行代码
COMPLETED 执行成功 移动到下一节点
STUB 占位函数,需要展开 调用 LLM
ERROR 执行出错 终止或重试
SKIP 跳过 移动到下一节点

6.2 占位函数检测机制

# executor.py 中的关键代码

try:
    exec(code, variables)  # 执行代码
except NameError as e:
    # 检查是不是函数调用
    if "函数名(" in code:
        return "NeedExpansion"  # 标记为需要展开
    else:
        return "Error"  # 真正的错误

6.3 next() 方法:找下一个节点

def next(self):
    # 1. 先看子节点
    for child in self.children:
        if child.status == PENDING:
            return child
    
    # 2. 再看兄弟节点
    for sibling in 右边的兄弟:
        if sibling.status == PENDING:
            return sibling
    
    # 3. 向上回溯
    return self.parent.next()

7. 如何扩展

7.1 添加新环境

  1. 创建 envs/your_env/env.py
from base.environment import Env

class YourEnv(Env):
    async def _run(self, action: str):
        # 实现动作执行逻辑
        return observation
    
    def reset(self, config, id=None):
        # 初始化环境
        return {"observations": [...], "env_name": "your_env", "env": self}
    
    def is_success(self):
        return self._success
    
    def report(self):
        return {"success": self._success, ...}
  1. run.py 中注册别名:
ENV_ALIASES = {
    "your_env": "envs.your_env.env.YourEnv",
}
  1. 创建提示词和示例:
    • agents/recode/resources/prompts/your_env/actions.txt
    • agents/recode/resources/fewshots/your_env/base.txt

7.2 修改 Agent 行为

可以修改这些部分:

文件 可修改内容
agents/recode/agent.py 展开逻辑、深度限制、重试策略
resources/prompts/default_new.py LLM 提示词模板
resources/fewshots/ 少样本示例

附录:常用命令

# 查看帮助
python run.py --help

# ALFWorld 测试
python run.py -a recode -e alfworld -n 5 --profile default

# WebShop 测试
python run.py -a recode -e webshop -n 10 -c 3

# 使用指定配置
python run.py -C configs/example.yaml

# 查看日志
ls logs/

文档更新日期:2025-01