|
1 | | -# ReAct 例子 |
| 1 | +# ReAct Agent 训练 |
2 | 2 |
|
3 | | -本示例用于演示如何通过我们兼容 OpenAI 接口的 `ModelWrapper` 类,将 Trinity-RFT 训练工作流适配到你自己的智能体项目中。 |
| 3 | +本节将会展示如何借助 Trinity-RFT 训练一个基于智能体框架实现的 ReAct Agent。这里我们以 [AgentScope](https://github.com/agentscope-ai/agentscope) 框架为例,并使用其内置的 ReAct 智能体来解决 GSM8K 数学问题。开发者可以参考此示例,将 Trinity-RFT 的训练工作流适配到自己的智能体项目中。 |
4 | 4 |
|
5 | | -这里我们以 [AgentScope](https://github.com/modelscope/agentscope) 框架为例,但你完全可以使用其他任何框架,因为 Trinity 提供了极大的灵活性。该示例利用一个采用 ReAct 风格推理并支持原生工具调用的智能体(Agent),在 GSM8K 数学数据集上对模型进行微调。 |
6 | 5 |
|
7 | 6 | ## 关键特性 |
8 | 7 |
|
9 | | -此示例突出了 Trinity-RFT 框架的几项高级特性: |
| 8 | +在介绍案例之前,我们先来看看 Trinity-RFT 在训练智能体应用方面的几个重要特性。 |
10 | 9 |
|
11 | | -### 与外部智能体框架的无缝集成 |
12 | | -Trinity-RFT 被设计为高度模块化,因此你可以轻松地将来自外部框架(如 AgentScope)的复杂、现成的智能体逻辑直接嵌入到 Trinity 的 `Workflow` 中。 |
| 10 | +### 兼容各种智能体框架 |
13 | 11 |
|
14 | | -- **无需重写智能体**:你不必在 Trinity 内重新实现智能体的复杂逻辑(例如 ReAct 循环、内存管理或工具调用)。 |
15 | | -- **关注高层编排**:正如我们在 `AgentScopeReactV2MathWorkflow` 中所展示的那样,Trinity 工作流只需初始化并调用外部智能体的 `reply` 方法。Trinity 对底层复杂性负责,使你能专注于高层任务编排和奖励设计。 |
| 12 | +当前智能体开发框架众多,对模型的封装和调用方式也各不相同。为了最大限度地兼容各种框架,Trinity-RFT 对 `openai.OpenAI` 以及 `openai.AsyncOpenAI` 接口进行了封装,只要你的智能体框架支持使用 openai 接口调用模型,就可以通过 Trinity-RFT 提供的 `OpenAI` 或是 `AsyncOpenAI` 实例对智能体进行训练。当然,你也可以不使用任何智能体框架,直接借助 Trinity-RFT 提供的 openai 接口实现自己的智能体。 |
16 | 13 |
|
17 | | -### 通用多步训练 |
18 | | -现代智能体任务通常涉及多步推理、工具使用和观察。Trinity-RFT 原生支持跨这些多步交互的训练。 |
19 | 14 |
|
20 | | -- **逐步步经验生成**:Trinity 不仅从最终结果进行学习,还能将智能体推理轨迹中的每一步视为独立的学习经验(experience)。 |
21 | | -- **奖励分配**:解决任务的奖励(reward)会传播至成功轨迹内的所有 experience,使模型能够学习整个推理链,而不仅仅是最终响应。这由配置中的 `advantage_fn` 控制。 |
| 15 | +### 无需修改智能体代码 |
22 | 16 |
|
23 | | -### 原生工具调用支持 |
24 | | -Trinity-RFT 的推理引擎和训练流水线专为支持原生 OpenAI `tool_calls` 格式而构建。 |
| 17 | +智能体的训练需要收集智能体运行中产生的对话历史以及其他相关信息(例如 `token_id`,`logprobs`),这往往需要对智能体应用代码进行一定的修改。Trinity-RFT 通过封装 `openai.OpenAI` 或 `openai.AsyncOpenAI` 实例的方式,在模型调用时自动收集训练所需的各种信息,从而避免了对智能体自身代码的修改。 |
25 | 18 |
|
26 | | -- **学习使用工具**:该框架允许模型学习*何时*调用工具、*调用哪个*工具以及*使用什么*参数,全部采用标准 `tool_calls` 格式。 |
27 | | -- **易操作性**:这种原生支持确保了与任何消费 OpenAI API 格式的服务或环境无缝集成,例如 `MCP_server`(多智能体协作平台)或其他工具使用评估器。 |
28 | 19 |
|
29 | | -## 工作原理 |
| 20 | +### 支持多轮次交互 |
30 | 21 |
|
31 | | -下面我们逐步介绍如何执行此流程。 |
| 22 | +智能体任务通常涉及多步推理、工具使用和观察。为了支持训练智能体应用,Trinity-RFT 原生支持包含多轮交互的训练任务,且不限制交互轮次(只需确保每次模型调用的序列长度不超过模型所支持的上限),这意味着你可以根据任务的复杂度,设计动态长度的交互过程。Trinity-RFT 通过动态同步机制,能够在收集到足够的训练样本后立即启动训练任务,从而提升训练效率。 |
32 | 23 |
|
33 | | -### 工作流 (`workflow.py`) |
34 | 24 |
|
35 | | -核心逻辑封装在 `AgentScopeReactV2MathWorkflow` 类中。 |
| 25 | +## 实现流程 |
36 | 26 |
|
37 | | -1. **初始化 (`__init__`)** |
38 | | - - 首先初始化 AgentScope 环境和所需的 Agent(`ReActAgentV2`)。 |
39 | | - - 最关键的集成步骤是将 Trinity 的模型客户端注入到 Agent 中: |
40 | | - ```python |
41 | | - self.openai_client = model.get_openai_client() |
42 | | - # self.openai_client = get_openai_async_client() # or async client depend on whether you are using async openai client |
43 | | - # ... |
44 | | - self.agent.model.client = self.openai_client |
45 | | - ``` |
46 | | - 这确保了 Agent 发出的所有 API 请求都通过 Trinity 的 `ModelWrapper` 进行路由,后者会记录完整的对话历史。 |
| 27 | +我们将逐步介绍如何使用 Trinity-RFT 训练一个基于 AgentScope 实现的 ReAct 智能体。 |
47 | 28 |
|
48 | | -2. **执行 (`run`)** |
49 | | - - `run` 方法非常简洁,它只是将任务描述传递给 Agent。 |
50 | | - ```python |
51 | | - content = self.agent.reply(msg).content # your agent logic |
52 | | - ``` |
53 | | - - 在 Agent 完成其多步推理并产生最终答案后,Trinity 从模型历史中提取所有中间轮次: |
54 | | - ```python |
55 | | - experiences = self.model.extract_experience_from_history(clear_history=True) |
56 | | - ``` |
57 | | - - 基于最终答案计算奖励,并将其应用于从该轨迹生成的所有 `Experience` 对象。然后这些 experiences 被发送到 Buffer 中用于训练。 |
58 | 29 |
|
59 | | -### 配置说明 |
| 30 | +### 1. 更换智能体的 OpenAI 客户端 |
60 | 31 |
|
61 | | -配置文件用于微调整个系统的行为。以下是本示例的关键参数: |
| 32 | +{class}`AgentScopeReActAgent <trinity.common.workflows.agentscope.react.react_agent.AgentScopeReActAgent>` 封装了 AgentScope 的 ReAct 智能体,并在初始化时注入 Trinity-RFT 提供的 `openai.AsyncOpenAI` 实例,而后续的执行过程均由 AgentScope 智能体自行处理,无需任何修改。 |
62 | 33 |
|
63 | | -#### 原生工具调用设置 |
64 | 34 |
|
65 | | -`explorer.rollout_model` 部分的这些设置用于配置基于 vLLM 的引擎,以生成和解析兼容 OpenAI 的工具调用。 |
66 | | -我们使用 `Qwen3` 模型并通过 vLLM 托管模型。不同模型的配置可参考 [vLLM Toolcalls](https://docs.vllm.ai/en/stable/features/tool_calling.html#qwen-models) |
| 35 | +```python |
| 36 | +# A simplified version of trinity.common.workflows.agentscope.react.react_agent.AgentScopeReActAgent |
| 37 | +class AgentScopeReActAgent: |
| 38 | + def __init__( |
| 39 | + self, |
| 40 | + openai_client: openai.AsyncOpenAI, # provided by Trinity-RFT |
| 41 | + # some other params |
| 42 | + ): |
| 43 | + """Initialize the AgentScope ReAct agent with specified tools and model. |
| 44 | +
|
| 45 | + Args: |
| 46 | + openai_client (openai.AsyncOpenAI): An instance of AsyncOpenAI client. |
| 47 | + """ |
| 48 | + self.agent_model = OpenAIChatModel( |
| 49 | + api_key="EMPTY", |
| 50 | + model_name=model_name, |
| 51 | + generate_kwargs=generate_kwargs, |
| 52 | + stream=False, |
| 53 | + ) |
| 54 | + # patch the OpenAIChatModel to use the openai_client provided by Trinity-RFT |
| 55 | + self.agent_model.client = openai_client |
| 56 | + self.agent = ReActAgent( |
| 57 | + name="react_agent", |
| 58 | + model=self.agent_model, |
| 59 | + ) |
| 60 | + |
| 61 | + async def reply(self, query): |
| 62 | + """Generate a response based on the query.""" |
| 63 | + # no need to modify your agent logic |
| 64 | + return await self.agent.reply( |
| 65 | + Msg("user", query, role="user") |
| 66 | + ) |
| 67 | +``` |
| 68 | + |
| 69 | +```{note} |
| 70 | +这里用一个新类封装 AgentScope 的 ReAct 智能体主要是为了清晰地展示更换 OpenAI 客户端的过程。 |
| 71 | +在实践中,你可以直接修改现有智能体的 OpenAI 客户端,而无需创建一个新的类。 |
| 72 | +``` |
| 73 | + |
| 74 | + |
| 75 | +### 2. 实现训练工作流 |
| 76 | + |
| 77 | +{class}`AgentScopeReActWorkflow <trinity.common.workflows.agentscope.react.react_workflow.AgentScopeReActWorkflow>` 展示了智能体的训练流程,其核心 `run_async` 方法包含三个步骤: |
| 78 | + |
| 79 | + 1. 调用智能体完成指定任务并获取任务结果。 |
| 80 | + 2. 对任务结果进行评估,计算奖励。 |
| 81 | + 3. 收集任务执行中产生的可训练数据并集合奖励生成训练样本(`Experience`)。 |
| 82 | + |
| 83 | +```python |
| 84 | +# A simplified version of trinity.common.workflows.agentscope.react.react_workflow.AgentScopeReActWorkflow |
| 85 | +class AgentScopeReActWorkflow(Workflow): |
| 86 | + def __init__( |
| 87 | + self, |
| 88 | + *, |
| 89 | + task: Task, |
| 90 | + model: ModelWrapper, |
| 91 | + auxiliary_models: Optional[List[openai.OpenAI]] = None, |
| 92 | + ): |
| 93 | + # initialize the agent |
| 94 | + self.agent = AgentScopeReActAgent( |
| 95 | + openai_client=model.get_openai_async_client(), |
| 96 | + # some other params |
| 97 | + ) |
| 98 | + # get query from the task |
| 99 | + self.query = task.raw_task.get(task.format_args.prompt_key) # type: ignore [index] |
| 100 | + |
| 101 | + async def run_async(self): |
| 102 | + """Run the workflow asynchronously.""" |
| 103 | + # Step 1: call the ReAct agent to solve the task |
| 104 | + response = await self.agent.reply(self.query) |
| 105 | + # Step 2: calculate the reward based on the response |
| 106 | + reward = await self.calculate_reward(response) |
| 107 | + # Step 3: construct experiences from the interaction history and return them |
| 108 | + return self.construct_experiences(reward) |
| 109 | + |
| 110 | + async def calculate_reward(self, response) -> float: |
| 111 | + """Calculate the reward based on the response.""" |
| 112 | + # your reward logic |
| 113 | + |
| 114 | + def construct_experiences(self, reward: float) -> List[Experience]: |
| 115 | + """Construct experiences from the agent's interaction history. |
| 116 | +
|
| 117 | + Returns: |
| 118 | + List: A list of Experience objects. |
| 119 | + """ |
| 120 | + # Extract all interaction history generated by this task |
| 121 | + exps = self.model.extract_experience_from_history() |
| 122 | + # update the reward for each experience |
| 123 | + for exp in exps: |
| 124 | + exp.reward = reward |
| 125 | + return exps |
| 126 | + |
| 127 | +``` |
| 128 | + |
| 129 | +### 3.训练配置 |
| 130 | + |
| 131 | +Trinity-RFT 借助配置文件来控制整个训练流程,下面是本示例的关键配置参数说明。 |
| 132 | + |
| 133 | +#### 推理模型配置 |
| 134 | + |
| 135 | +`explorer.rollout_model` 部分负责配置智能体应用所使用的模型,其中的关键参数如下: |
67 | 136 |
|
68 | 137 |
|
69 | 138 | ```yaml |
70 | 139 | explorer: |
71 | 140 | rollout_model: |
72 | 141 | # ... |
73 | | - enable_auto_tool_choice: true # 允许模型生成 `tool_calls` |
| 142 | + enable_openai_client: true # 启用 OpenAI Client |
| 143 | + enable_history: true # 启用调用历史自动记录 |
| 144 | + enable_auto_tool_choice: true # 允许模型生成 `tool_calls` |
74 | 145 | tool_call_parser: hermes # 指定格式化解析工具调用输出的解析器 |
75 | 146 | reasoning_parser: deepseek_r1 # 有助于解析模型的思维过程 |
76 | | - enable_thinking: true # 允许模型生成中间“思考”内容 |
| 147 | + enable_thinking: true # 是否启用模型深度思考能力(主要针对 Qwen3 系列模型) |
77 | 148 | ``` |
78 | 149 |
|
79 | | -#### 多步训练策略 |
| 150 | +#### 多步训练算法 |
80 | 151 |
|
81 | | -`algorithm` 部分的此设置定义了如何处理多步 rollout 产生的 experience。 |
| 152 | +`algorithm` 部分负责配置智能体应用所使用的训练算法,其中的关键参数如下: |
82 | 153 |
|
83 | 154 | ```yaml |
84 | 155 | algorithm: |
85 | 156 | algorithm_type: grpo |
86 | | - advantage_fn: step_wise_grpo # 多步训练的关键 |
| 157 | + advantage_fn: step_wise_grpo # 多步训练的关键,该策略告诉 Trinity 为智能体执行路径中的每一步创建独立的训练样本。`grpo` 算法随后使用这些样本来更新模型。 |
87 | 158 | ``` |
88 | | -- `step_wise_grpo`:该策略告诉 Trinity 为智能体执行路径中的每一步创建独立的训练样本。`grpo` 算法随后使用这些样本来更新模型。 |
89 | 159 |
|
90 | | -#### 异步同步提升效率 |
| 160 | +#### 动态同步配置 |
91 | 161 |
|
92 | | -由于多步 rollout 会产生数量不固定的 experience,等待固定数量的 *rollout* 是低效的。我们采用动态同步策略。 |
| 162 | +由于智能体应用在完成不同任务时,交互轮次往往不固定,导致生成的训练样本数量也不固定;为此需要开启 Trinity-RFT 的动态同步功能,以便在收集到足够的训练样本后立即启动训练任务,从而提升训练效率。相关配置如下: |
93 | 163 |
|
94 | 164 | ```yaml |
95 | 165 | synchronizer: |
96 | | - sync_style: dynamic_by_explorer # 当积累足够 experience 时即开始训练 |
97 | | - sync_interval: 2 |
| 166 | + sync_style: dynamic_by_explorer # 当产生足够训练数据时,trainer 立即启动训练任务,而不是将生成的数据补齐到一个固定规模,能够有效提升训练效率 |
| 167 | + sync_interval: 2 # 每执行两个批次的任务后检查是否需要同步更新模型参数 |
98 | 168 | ``` |
99 | | -- `sync_style: dynamic_by_explorer`:当缓冲区收集到足够的 *experience*(即单个对话轮次)时,trainer 即启动一次训练任务,而不是等待固定数量的完整智能体轨迹。这显著提高了 GPU 利用率和训练吞吐量。 |
100 | | - |
101 | | -## 如何运行示例 |
102 | | - |
103 | | -1. **前置条件**:确保已安装 Trinity 及本示例所需依赖(如 `AgentScope`)。请参考 [Agentscope Github link](https://github.com/agentscope-ai/agentscope/tree/v0) |
104 | | - |
105 | | -> **注意**:本示例需要以下来源之一的 AgentScope: |
106 | | -> - Commit: `ad13ed5dacecb79d20abf626769f8c7d7a7d2afb` |
107 | | -> - 分支: [`v0`](https://github.com/agentscope-ai/agentscope/tree/v0) |
108 | | - |
109 | | -2. 下载你想使用的模型,并填写 `examples/agentscope_tool_react/agentscopev0_tool_react_gsm8k.yaml` 或 `examples/agentscope_tool_react/agentscopev0_tool_react_dapo.yaml` 中的配置文件 |
110 | | - |
111 | | -3. **启动训练任务**:从仓库根目录运行以下命令。 |
112 | 169 |
|
113 | | - ```bash |
114 | | - trinity run --config examples/agentscope_tool_react/agentscopev0_tool_react_gsm8k.yaml |
115 | | - ``` |
116 | 170 |
|
117 | | - 或 |
| 171 | +## 运行示例 |
118 | 172 |
|
119 | | - ```bash |
120 | | - trinity run --config examples/agentscope_tool_react/agentscopev0_tool_react_dapo.yaml |
121 | | - ``` |
| 173 | +1. 安装依赖库:按照 [安装指南](/tutorial/installation.md) 成功安装 Trinity-RFT,并且安装了 AgentScope 的 v1.0 及以上版本。 |
122 | 174 |
|
| 175 | +```bash |
| 176 | +pip install agentscope>=1.0.4 |
| 177 | +``` |
123 | 178 |
|
124 | | -GSM8K 数据集的示例非常简单,在 8 块 H20 GPU 上几分钟内即可收敛。 |
125 | | - |
126 | | - |
| 179 | +2. 下载模型和数据集: |
127 | 180 |
|
128 | | -DAPO 数据集的示例耗时稍长,但也能够收敛。 |
| 181 | +```bash |
| 182 | +huggingface-cli download Qwen/Qwen3-8B |
| 183 | +huggingface-cli download openai/gsm8k --repo-type dataset |
| 184 | +``` |
129 | 185 |
|
130 | | - |
| 186 | +3. 启动训练任务: |
131 | 187 |
|
132 | | -我们还可以看到,模型总体上开始更多地使用工具调用来解决问题。 |
| 188 | + ```bash |
| 189 | + # Navigate to the Trinity-RFT root directory |
| 190 | + cd /path/to/Trinity-RFT |
133 | 191 |
|
134 | | - |
| 192 | + # Run the training for GSM8k dataset: |
| 193 | + trinity run --config examples/agentscope_react/gsm8k.yaml |
| 194 | + ``` |
135 | 195 |
|
136 | | -我们也可以把使用 v1 版本的 AgentScope 仓库,然后对 Qwen3-4b-instrcut-2507 进行训练: |
137 | 196 |
|
138 | | - |
| 197 | +## 结果展示 |
139 | 198 |
|
140 | 199 |
|
141 | | -## 总结 |
| 200 | +reward 变化曲线: |
142 | 201 |
|
143 | | -这个示例虽然简单,但展示了 Trinity 在训练使用工具的复杂多步智能体方面的强大功能和灵活性。通过无缝集成外部智能体逻辑,并提供对多步训练和工具调用的原生支持,Trinity-RFT 使你能够高效地在复杂且真实的任务上微调模型。 |
| 202 | + |
0 commit comments