From 654c35127ab7f3166fa9ab81ed41af8edd08dde1 Mon Sep 17 00:00:00 2001
From: Yuchang Sun <52027540+hiyuchang@users.noreply.github.com>
Date: Mon, 19 Jan 2026 12:25:13 +0800
Subject: [PATCH] Add examples for frozenlake and emailsearch (#94)
---
games/game_werewolves/game.py | 4 +-
games/game_werewolves/utils.py | 4 +-
tuner/README.md | 4 +-
tuner/README_zh.md | 4 +-
tuner/email_search/README.md | 279 ++++++++++++++
tuner/email_search/README_zh.md | 279 ++++++++++++++
tuner/email_search/_email_search_agent.py | 175 +++++++++
tuner/email_search/_utils.py | 328 ++++++++++++++++
tuner/email_search/config.yaml | 72 ++++
tuner/email_search/critic_reward_mean.png | Bin 0 -> 481654 bytes
tuner/email_search/main.py | 379 +++++++++++++++++++
tuner/email_search/prepare_data.py | 357 +++++++++++++++++
tuner/email_search/rollout_accuracy_mean.png | Bin 0 -> 452730 bytes
tuner/frozen_lake/README.md | 271 +++++++++++++
tuner/frozen_lake/README_zh.md | 250 ++++++++++++
tuner/frozen_lake/_frozenlake_agent.py | 102 +++++
tuner/frozen_lake/_frozenlake_env.py | 316 ++++++++++++++++
tuner/frozen_lake/_utils.py | 209 ++++++++++
tuner/frozen_lake/config.yaml | 53 +++
tuner/frozen_lake/critic_rewards_mean.png | Bin 0 -> 63184 bytes
tuner/frozen_lake/get_frozenlake_data.py | 131 +++++++
tuner/frozen_lake/main.py | 151 ++++++++
tuner/learn_to_ask/README.md | 2 +-
tuner/math_agent/README.md | 6 +-
tuner/math_agent/README_zh.md | 6 +-
tuner/math_agent/config.yaml | 2 +-
26 files changed, 3370 insertions(+), 14 deletions(-)
create mode 100644 tuner/email_search/README.md
create mode 100644 tuner/email_search/README_zh.md
create mode 100644 tuner/email_search/_email_search_agent.py
create mode 100644 tuner/email_search/_utils.py
create mode 100644 tuner/email_search/config.yaml
create mode 100644 tuner/email_search/critic_reward_mean.png
create mode 100644 tuner/email_search/main.py
create mode 100644 tuner/email_search/prepare_data.py
create mode 100644 tuner/email_search/rollout_accuracy_mean.png
create mode 100644 tuner/frozen_lake/README.md
create mode 100644 tuner/frozen_lake/README_zh.md
create mode 100644 tuner/frozen_lake/_frozenlake_agent.py
create mode 100644 tuner/frozen_lake/_frozenlake_env.py
create mode 100644 tuner/frozen_lake/_utils.py
create mode 100644 tuner/frozen_lake/config.yaml
create mode 100644 tuner/frozen_lake/critic_rewards_mean.png
create mode 100644 tuner/frozen_lake/get_frozenlake_data.py
create mode 100644 tuner/frozen_lake/main.py
diff --git a/games/game_werewolves/game.py b/games/game_werewolves/game.py
index e7e08e6..b2355b8 100644
--- a/games/game_werewolves/game.py
+++ b/games/game_werewolves/game.py
@@ -19,7 +19,9 @@ from structured_model import (
get_seer_model,
get_hunter_model,
)
-from prompt import EnglishPrompts as Prompts
+from prompt import (
+ EnglishPrompts as Prompts,
+) # pylint: disable=no-name-in-module
# Uncomment the following line to use Chinese prompts
# from prompt import ChinesePrompts as Prompts
diff --git a/games/game_werewolves/utils.py b/games/game_werewolves/utils.py
index d168b18..3430697 100644
--- a/games/game_werewolves/utils.py
+++ b/games/game_werewolves/utils.py
@@ -6,7 +6,9 @@ from typing import Any
import numpy as np
from agentscope.agent import AgentBase, ReActAgent
from agentscope.message import Msg
-from prompt import EnglishPrompts as Prompts
+from prompt import (
+ EnglishPrompts as Prompts,
+) # pylint: disable=no-name-in-module
MAX_GAME_ROUND = 30
MAX_DISCUSSION_ROUND = 3
diff --git a/tuner/README.md b/tuner/README.md
index 902c6c4..e89f901 100644
--- a/tuner/README.md
+++ b/tuner/README.md
@@ -22,6 +22,6 @@ AgentScope Tuner requires:
- `agentscope>=1.0.12`
- `trinity-rft>=0.4.1`
-AgentScope Tuner is built on top of [Trinity-RFT](https://github.com/modelscope/Trinity-RFT).
-Please refer to the [Trinity-RFT installation guide](https://modelscope.github.io/Trinity-RFT/en/main/tutorial/trinity_installation.html)
+AgentScope Tuner is built on top of [Trinity-RFT](https://github.com/agentscope-ai/Trinity-RFT).
+Please refer to the [Trinity-RFT installation guide](https://agentscope-ai.github.io/Trinity-RFT/en/main/tutorial/trinity_installation.html)
for detailed instructions on how to set up the environment.
diff --git a/tuner/README_zh.md b/tuner/README_zh.md
index 8247b8c..f2c1abb 100644
--- a/tuner/README_zh.md
+++ b/tuner/README_zh.md
@@ -21,6 +21,6 @@ AgentScope Tuner 需要:
- `agentscope>=1.0.12`
- `trinity-rft>=0.4.1`
-AgentScope Tuner 基于 [Trinity-RFT](https://github.com/modelscope/Trinity-RFT) 构建。
-请参考 [Trinity-RFT 安装指南](https://modelscope.github.io/Trinity-RFT/zh/main/tutorial/trinity_installation.html)
+AgentScope Tuner 基于 [Trinity-RFT](https://github.com/agentscope-ai/Trinity-RFT) 构建。
+请参考 [Trinity-RFT 安装指南](https://agentscope-ai.github.io/Trinity-RFT/zh/main/tutorial/trinity_installation.html)
获取详细的安装方法。
diff --git a/tuner/email_search/README.md b/tuner/email_search/README.md
new file mode 100644
index 0000000..d976b0d
--- /dev/null
+++ b/tuner/email_search/README.md
@@ -0,0 +1,279 @@
+# Training Email Search Agent with RL using AgentScope-Tuner
+
+This example demonstrates how to implement reinforcement fine-tuning for the Email Search task (inspired by [ART](https://openpipe.ai/blog/art-e-mail-agent)) using AgentScope-Tuner, whose RFT functionality is backed by [Trinity-RFT](https://github.com/agentscope-ai/Trinity-RFT).
+
+## Task Setting
+
+The agent's goal is to answer user queries by searching through an email inbox. The agent needs to:
+- Understand the user's question
+- Search for relevant emails using keywords
+- Read email contents to extract information
+- Provide accurate answers with proper source citations
+
+**Agent Type**: The agent (`EmailSearchAgent`) extends `ReActAgent`, which follows a reasoning-acting loop to solve tasks iteratively.
+
+**Environment**: The environment is a SQLite database containing emails from the Enron Email dataset. Each task provides:
+- `question`: The user's email search query
+- `inbox_address`: The email inbox to search
+- `query_date`: The date context for the query
+- `answer`: The expected answer (ground truth), only for reward calculation
+- `message_ids`: IDs of relevant emails containing the answer, only for reward calculation
+
+**Available Tools**:
+- `search_emails`: Find emails by keywords, inbox address, and date range. Returns a list of email summaries (message_id and snippet).
+- `read_email`: Read the full content of a specific email by message_id.
+- `generate_response`: Provide the final structured answer with sources (inherited from ReAct agent).
+
+## Dataset Preparation
+
+The dataset contains email queries based on the [Enron Email dataset](https://huggingface.co/datasets/corbt/enron-emails). Run the data preparation script to generate the email database and datasets:
+
+```bash
+python prepare_data.py
+```
+
+If you want to choose a new database path, you can modify the `DEFAULT_DB_PATH` in [`prepare_data.py`](./prepare_data.py). Also, remember to set an environment variable `DEFAULT_EMAIL_DB_PATH` to point to the database path before moving to the next step:
+
+```bash
+export DEFAULT_EMAIL_DB_PATH=/path/to/enron_emails_dataset/data/enron_emails.db
+```
+
+This will create a SQLite database and datasets:
+
+```
+/path/to/enron_emails_dataset/
+ ├── data
+ └── enron_emails.db # Email database
+ ├── train.parquet # Training samples
+ └── test.parquet # Test samples
+```
+
+Each sample looks like:
+
+```json
+{
+ "id": 0,
+ "question": "Were there any variances detected for hour 6 on 3/9/01?",
+ "answer": "Yes, variances were detected in both Generation and Energy Import/Export schedules for hour 6 on 3/9/01.",
+ "message_ids": ["<17407857.1075840601283.JavaMail.evans@thyme>"],
+ "how_realistic": 0.800000011920929,
+ "inbox_address": "pete.davis@enron.com",
+ "query_date": "2001-03-16"
+}
+```
+
+## Code Implementation
+
+This section provides a high-level overview of the code implementation. For detailed implementation, please refer to the source code.
+
+### Agent Workflow
+
+The workflow function `run_email_search_agent` implements the agent-environment interaction loop:
+
+```python
+async def run_email_search_agent(
+ task: Dict,
+ model: ChatModelBase,
+ auxiliary_models: Dict[str, ChatModelBase],
+) -> WorkflowOutput:
+ # Parse task and create agent
+ agent = EmailSearchAgent(
+ name="email_search_agent",
+ sys_prompt=system_prompt,
+ model=model,
+ max_iters=max_turns,
+ )
+
+ # Run the agent with structured output
+ response = await agent.reply(
+ msg=Msg("user", question, role="user"),
+ structured_model=AnswerModel,
+ )
+
+ return WorkflowOutput(response=response)
+```
+
+The agent follows a ReAct pattern: it reasons about the task, calls tools to search and read emails, and finally generates a structured response containing the answer and source message IDs.
+
+### Judge Function
+
+The judge function `email_search_judge` implements reward calculation using LLM-as-a-Judge:
+
+```python
+async def email_search_judge(
+ task: Dict,
+ response: Msg,
+ auxiliary_models: Dict[str, ChatModelBase],
+) -> JudgeOutput:
+ # Extract answer and sources from response
+ answer = answer_and_sources.get("answer")
+ sources = answer_and_sources.get("sources", [])
+
+ # Judge correctness using LLM-as-a-Judge
+ judge_model = auxiliary_models.get('judge') or list(auxiliary_models.values())[0]
+ judge_response = await judge_correctness(
+ answer, query, judge_model
+ )
+
+ # Calculate reward based on:
+ # - Answer correctness (accuracy: -1.0 to 1.0)
+ # - Source correctness (format: partial rewards)
+ # - Efficiency (bonus for fewer turns, correct sources)
+ result = {"accuracy": ..., "format": ...} # calculated based on judge_response
+
+ return JudgeOutput(
+ reward=sum(result.values()),
+ metrics=metrics,
+ )
+```
+
+The reward function considers:
+- **Answer correctness**: Evaluated by LLM-as-a-Judge comparing the agent's answer with the ground truth
+- **Source correctness**: Whether the agent cited the correct email message IDs
+- **Efficiency**: Bonus rewards for finding/reading the correct email and taking fewer turns
+
+See [`main.py`](./main.py) and [`email_search_agent.py`](./email_search_agent.py) for implementation details.
+
+## How to Run
+
+### Prerequisites
+
+- At least 4 NVIDIA GPUs with CUDA 12.8 or newer
+ * Note: For the 30B Judge model, you need to use a GPU with at least 4080 memory; you can also run the model on multiple GPUs by using `tensor_parallel_size > 1` to reduce the memory usage (by default, `tensor_parallel_size=2`).
+- Follow the Trinity-RFT [installation guide](https://agentscope-ai.github.io/Trinity-RFT/en/main/tutorial/trinity_installation.html) to install the latest version from source code
+- Download the model checkpoint (example):
+
+ ```bash
+ huggingface-cli download Qwen/Qwen3-4B-Instruct-2507
+ huggingface-cli download Qwen/Qwen3-30B-A3B-Instruct-2507 # judge model
+ ```
+
+### Configuration
+
+Adjust the configuration file ([`config.yaml`](./config.yaml)) based on your hardware. Key configuration sections include:
+
+- **TunerModelConfig**: Set `model_path` to your model checkpoint path
+- **AlgorithmConfig**: Configure RL algorithm parameters (e.g., `multi_step_grpo`, learning rate, policy loss function)
+- **DatasetConfig**: The dataset path is specified in `main.py` when creating the `DatasetConfig` object
+- **Auxiliary Models**: Configure judge model settings for LLM-as-a-Judge
+
+For full configuration details, see [Trinity-RFT Configuration Guide](https://agentscope-ai.github.io/Trinity-RFT/en/main/tutorial/trinity_configs.html).
+
+### Start-Up Commands
+
+1. Prepare the dataset:
+
+ ```bash
+ python prepare_data.py
+ export DEFAULT_EMAIL_DB_PATH=/path/to/enron_emails_dataset/data/enron_emails.db
+ ```
+
+2. Set up a [Ray](https://github.com/ray-project/ray) cluster:
+
+ ```bash
+ ray start --head
+ ```
+
+3. Run the training script:
+
+ ```bash
+ python main.py
+ ```
+
+## Experimental Results
+
+### Quantitative Results
+
+The training results show improvements in agent performance over training iterations. Key metrics include:
+
+- **Train reward**: The average reward on training samples increases as the agent learns better strategies
+- **Rollout accuracy**: The average accuracy on rollout samples increases as the agent learns better strategies
+
+
+
+
+
+
+### Concrete Example
+
+An example of the agent's behavior is shown below:
+
+**Query:** "What do the color codes mean in the curve assessment?"
+
+We show the last several turns of agent responses:
+
+The agent performs multiple search attempts to find relevant emails. After some unsuccessful searches, the agent tries:
+
+**Tool call:**
+```json
+{
+ "type": "tool_use",
+ "name": "search_emails",
+ "input": {
+ "inbox_address": "steven.kean@enron.com",
+ "query_date": "2001-04-03",
+ "keywords": ["curve", "assessment"]
+ }
+}
+```
+
+**Tool result:**
+```json
+{
+ "type": "tool_result",
+ "name": "search_emails",
+ "output": [
+ {
+ "message_id": "<2654330.1075846153519.JavaMail.evans@thyme>",
+ "snippet": "... curves and Senior Management as necessary.\\n\\nCurve Assessment\\n Green: \\n - curve is reasonable\\n - small P&L ..."
+ },
+ {
+ "message_id": "<12499967.1075846153495.JavaMail.evans@thyme>",
+ "snippet": "... curves and Senior Management as necessary.\\n\\nCurve Assessment\\n Green: \\n - curve is reasonable\\n - small P&L ..."
+ }
+ ]
+}
+```
+
+After finding relevant emails, the agent uses `read_email` tool to read the full content of both emails:
+
+**Tool call:**
+```json
+{
+ "type": "tool_use",
+ "name": "read_email",
+ "input": {
+ "message_id": "<2654330.1075846153519.JavaMail.evans@thyme>"
+ }
+}
+```
+
+**Tool call:**
+```json
+{
+ "type": "tool_use",
+ "name": "read_email",
+ "input": {
+ "message_id": "<12499967.1075846153495.JavaMail.evans@thyme>"
+ }
+}
+```
+
+After reading the emails, the agent generates the final structured response:
+
+**Tool call:**
+```json
+{
+ "type": "tool_use",
+ "name": "generate_response",
+ "input": {
+ "answer": "In the curve assessment, the color codes are used as follows:\n- Green: The curve is considered reasonable, and the P&L (profit and loss) is small.",
+ "sources": [
+ "<2654330.1075846153519.JavaMail.evans@thyme>",
+ "<12499967.1075846153495.JavaMail.evans@thyme>"
+ ]
+ }
+}
+```
+
+The judge evaluates the answer as correct based on the ground truth.
diff --git a/tuner/email_search/README_zh.md b/tuner/email_search/README_zh.md
new file mode 100644
index 0000000..f5e409c
--- /dev/null
+++ b/tuner/email_search/README_zh.md
@@ -0,0 +1,279 @@
+# 使用 AgentScope-Tuner 训练邮件搜索智能体
+
+本示例展示如何使用 AgentScope-Tuner 对邮件搜索任务(灵感来自 [ART](https://openpipe.ai/blog/art-e-mail-agent))进行强化微调,其 RFT 功能由 [Trinity-RFT](https://github.com/agentscope-ai/Trinity-RFT) 提供支持。
+
+## 任务设定
+
+智能体的目标是通过搜索邮件收件箱来回答用户查询。智能体需要:
+- 理解用户的问题
+- 使用关键词搜索相关邮件
+- 阅读邮件内容以提取信息
+- 提供准确的答案并附上适当的来源引用
+
+**智能体类型**:智能体(`EmailSearchAgent`)继承自 `ReActAgent`,遵循推理-行动循环来迭代解决任务。
+
+**环境**:环境是一个包含来自 Enron 邮件数据集的 SQLite 数据库。每个任务提供:
+- `question`:用户的邮件搜索查询
+- `inbox_address`:要搜索的邮件收件箱
+- `query_date`:查询的日期上下文
+- `answer`:预期答案(真实值),仅用于奖励计算
+- `message_ids`:包含答案的相关邮件 ID,仅用于奖励计算
+
+**可用工具**:
+- `search_emails`:通过关键词、收件箱地址和日期范围查找邮件。返回邮件摘要列表(message_id 和片段)。
+- `read_email`:通过 message_id 读取特定邮件的完整内容。
+- `generate_response`:提供带有来源的最终结构化答案(继承自 ReAct 智能体)。
+
+## 数据集准备
+
+数据集包含基于 [Enron 邮件数据集](https://huggingface.co/datasets/corbt/enron-emails) 的邮件查询。运行数据准备脚本以生成邮件数据库和数据集:
+
+```bash
+python prepare_data.py
+```
+
+如果你想选择新的数据库路径,可以修改 [`prepare_data.py`](./prepare_data.py) 中的 `DEFAULT_DB_PATH`。同时,请记住在进入下一步之前设置环境变量 `DEFAULT_EMAIL_DB_PATH` 指向数据库路径:
+
+```bash
+export DEFAULT_EMAIL_DB_PATH=/path/to/enron_emails_dataset/data/enron_emails.db
+```
+
+这将创建一个 SQLite 数据库和数据集:
+
+```
+/path/to/enron_emails_dataset/
+ ├── data
+ └── enron_emails.db # 邮件数据库
+ ├── train.parquet # 训练样本
+ └── test.parquet # 测试样本
+```
+
+每个样本如下所示:
+
+```json
+{
+ "id": 0,
+ "question": "Were there any variances detected for hour 6 on 3/9/01?",
+ "answer": "Yes, variances were detected in both Generation and Energy Import/Export schedules for hour 6 on 3/9/01.",
+ "message_ids": ["<17407857.1075840601283.JavaMail.evans@thyme>"],
+ "how_realistic": 0.800000011920929,
+ "inbox_address": "pete.davis@enron.com",
+ "query_date": "2001-03-16"
+}
+```
+
+## 代码实现
+
+本节提供代码实现的高级概览。详细实现请参考源代码。
+
+### 智能体工作流
+
+工作流函数 `run_email_search_agent` 实现智能体-环境交互循环:
+
+```python
+async def run_email_search_agent(
+ task: Dict,
+ model: ChatModelBase,
+ auxiliary_models: Dict[str, ChatModelBase],
+) -> WorkflowOutput:
+ # 解析任务并创建智能体
+ agent = EmailSearchAgent(
+ name="email_search_agent",
+ sys_prompt=system_prompt,
+ model=model,
+ max_iters=max_turns,
+ )
+
+ # 使用结构化输出运行智能体
+ response = await agent.reply(
+ msg=Msg("user", question, role="user"),
+ structured_model=AnswerModel,
+ )
+
+ return WorkflowOutput(response=response)
+```
+
+智能体遵循 ReAct 模式:它推理任务,调用工具搜索和阅读邮件,最后生成包含答案和来源消息 ID 的结构化响应。
+
+### 评判函数
+
+评判函数 `email_search_judge` 使用 LLM-as-a-Judge 实现奖励计算:
+
+```python
+async def email_search_judge(
+ task: Dict,
+ response: Msg,
+ auxiliary_models: Dict[str, ChatModelBase],
+) -> JudgeOutput:
+ # 从响应中提取答案和来源
+ answer = answer_and_sources.get("answer")
+ sources = answer_and_sources.get("sources", [])
+
+ # 使用 LLM-as-a-Judge 评判正确性
+ judge_model = auxiliary_models.get('judge') or list(auxiliary_models.values())[0]
+ judge_response = await judge_correctness(
+ answer, query, judge_model
+ )
+
+ # 基于以下因素计算奖励:
+ # - 答案正确性(准确度:-1.0 到 1.0)
+ # - 来源正确性(格式:部分奖励)
+ # - 效率(对更少轮次、正确来源的奖励)
+ result = {"accuracy": ..., "format": ...} # 基于 judge_response 计算
+
+ return JudgeOutput(
+ reward=sum(result.values()),
+ metrics=metrics,
+ )
+```
+
+奖励函数考虑以下因素:
+- **答案正确性**:通过 LLM-as-a-Judge 比较智能体的答案与真实值进行评估
+- **来源正确性**:智能体是否引用了正确的邮件消息 ID
+- **效率**:对找到/阅读正确邮件和更少轮次的奖励
+
+详细实现请参考 [`main.py`](./main.py) 和 [`email_search_agent.py`](./email_search_agent.py)。
+
+## 运行方法
+
+### 前置要求
+
+- 至少 4 张 NVIDIA GPU,CUDA 版本 ≥ 12.8
+ * 注意:对于 30B 评判模型,需要使用至少 4080 显存的 GPU;你也可以通过使用 `tensor_parallel_size > 1` 在多张 GPU 上运行模型以减少显存使用(默认情况下,`tensor_parallel_size=2`)。
+- 按照 Trinity-RFT [安装指南](https://agentscope-ai.github.io/Trinity-RFT/zh/main/tutorial/trinity_installation.html) 从源码安装最新版本
+- 下载模型检查点(示例):
+
+ ```bash
+ huggingface-cli download Qwen/Qwen3-4B-Instruct-2507
+ huggingface-cli download Qwen/Qwen3-30B-A3B-Instruct-2507 # 评判模型
+ ```
+
+### 配置
+
+根据你的硬件调整配置文件([`config.yaml`](./config.yaml))。关键配置部分包括:
+
+- **TunerModelConfig**:将 `model_path` 设置为你的模型检查点路径
+- **AlgorithmConfig**:配置 RL 算法参数(例如,`multi_step_grpo`、学习率、策略损失函数)
+- **DatasetConfig**:数据集路径在创建 `DatasetConfig` 对象时在 `main.py` 中指定
+- **辅助模型**:为 LLM-as-a-Judge 配置评判模型设置
+
+完整配置详情请参考 [Trinity-RFT 配置指南](https://agentscope-ai.github.io/Trinity-RFT/zh/main/tutorial/trinity_configs.html)。
+
+### 启动命令
+
+1. 准备数据集:
+
+ ```bash
+ python prepare_data.py
+ export DEFAULT_EMAIL_DB_PATH=/path/to/enron_emails_dataset/data/enron_emails.db
+ ```
+
+2. 启动 [Ray](https://github.com/ray-project/ray):
+
+ ```bash
+ ray start --head
+ ```
+
+3. 运行训练脚本:
+
+ ```bash
+ python main.py
+ ```
+
+## 实验结果
+
+### 定量结果
+
+训练结果显示智能体性能随训练迭代次数的提升。关键指标包括:
+
+- **训练奖励**:训练样本上的平均奖励随着智能体学习更好的策略而增加
+- **Rollout 准确度**:Rollout 样本上的平均准确度随着智能体学习更好的策略而增加
+
+
+
+
+
+
+### 具体示例
+
+智能体行为示例如下:
+
+**查询:** "What do the color codes mean in the curve assessment?"
+
+我们展示智能体响应的最后几轮:
+
+智能体执行多次搜索尝试以找到相关邮件。经过一些不成功的搜索后,智能体尝试:
+
+**工具调用:**
+```json
+{
+ "type": "tool_use",
+ "name": "search_emails",
+ "input": {
+ "inbox_address": "steven.kean@enron.com",
+ "query_date": "2001-04-03",
+ "keywords": ["curve", "assessment"]
+ }
+}
+```
+
+**工具结果:**
+```json
+{
+ "type": "tool_result",
+ "name": "search_emails",
+ "output": [
+ {
+ "message_id": "<2654330.1075846153519.JavaMail.evans@thyme>",
+ "snippet": "... curves and Senior Management as necessary.\\n\\nCurve Assessment\\n Green: \\n - curve is reasonable\\n - small P&L ..."
+ },
+ {
+ "message_id": "<12499967.1075846153495.JavaMail.evans@thyme>",
+ "snippet": "... curves and Senior Management as necessary.\\n\\nCurve Assessment\\n Green: \\n - curve is reasonable\\n - small P&L ..."
+ }
+ ]
+}
+```
+
+找到相关邮件后,智能体使用 `read_email` 工具读取两封邮件的完整内容:
+
+**工具调用:**
+```json
+{
+ "type": "tool_use",
+ "name": "read_email",
+ "input": {
+ "message_id": "<2654330.1075846153519.JavaMail.evans@thyme>"
+ }
+}
+```
+
+**工具调用:**
+```json
+{
+ "type": "tool_use",
+ "name": "read_email",
+ "input": {
+ "message_id": "<12499967.1075846153495.JavaMail.evans@thyme>"
+ }
+}
+```
+
+阅读邮件后,智能体生成最终的结构化响应:
+
+**工具调用:**
+```json
+{
+ "type": "tool_use",
+ "name": "generate_response",
+ "input": {
+ "answer": "In the curve assessment, the color codes are used as follows:\n- Green: The curve is considered reasonable, and the P&L (profit and loss) is small.",
+ "sources": [
+ "<2654330.1075846153519.JavaMail.evans@thyme>",
+ "<12499967.1075846153495.JavaMail.evans@thyme>"
+ ]
+ }
+}
+```
+
+评判器评估上面的答案为正确。
diff --git a/tuner/email_search/_email_search_agent.py b/tuner/email_search/_email_search_agent.py
new file mode 100644
index 0000000..d548666
--- /dev/null
+++ b/tuner/email_search/_email_search_agent.py
@@ -0,0 +1,175 @@
+# -*- coding: utf-8 -*-
+"""Adapted from Trinity-RFT"""
+import json
+import traceback
+from dataclasses import asdict
+from datetime import datetime, timedelta
+from typing import Any
+from _utils import ( # pylint: disable=E0611
+ read_email_tool,
+ search_emails_tool,
+)
+from agentscope import logger
+from agentscope.agent import ReActAgent
+from agentscope.message import TextBlock
+from agentscope.tool import Toolkit, ToolResponse
+
+
+def pre_reasoning_hook(_self: Any, _kwargs: Any) -> dict[str, Any] | None:
+ """Pre-reasoning hook to remove tool_choice from kwargs."""
+ _kwargs.pop("tool_choice", None)
+ return _kwargs
+
+
+class EmailSearchAgent(ReActAgent):
+ """
+ A customized ReAct agent with pre-defined tools for
+ email search and reading.
+ Ref: https://github.com/OpenPipe/ART
+ """
+
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
+ self.message_id_list = (
+ []
+ ) # List to store message IDs found during search
+ self.ever_read_message_ids = (
+ []
+ ) # List to store message IDs that have been read
+ toolkit = Toolkit()
+ toolkit.register_tool_function(self.search_emails)
+ toolkit.register_tool_function(self.read_email)
+ super().__init__(*args, toolkit=toolkit, **kwargs)
+
+ self.register_instance_hook(
+ "pre_reasoning",
+ "tool_choice_hook",
+ pre_reasoning_hook,
+ )
+
+ async def reset(self) -> None:
+ """Reset agent state for a new rollout/episode."""
+ self.message_id_list.clear()
+ self.ever_read_message_ids.clear()
+ await self.memory.clear()
+
+ def search_emails(
+ self,
+ inbox_address: str,
+ query_date: str,
+ keywords: list[str],
+ **_kwargs: Any,
+ ) -> ToolResponse:
+ """
+ Search the user's email inbox for emails that match the given keywords.
+
+ Args:
+ inbox_address: The user's email address.
+ query_date: The date of the query in 'YYYY-MM-DD' format.
+ keywords: Keywords to search for in the user's email inbox.
+
+ Returns:
+ ToolResponse:
+ A ToolResponse object containing a list of TextBlock objects
+ in the `content` field. On success, the text field of the
+ TextBlock contains a JSON string representing a list of email
+ summaries (e.g., message_id, snippet) matching the search
+ criteria. Each email summary is converted to a dictionary via
+ `asdict`. On failure, the text indicates an error message.
+ """
+
+ try:
+ next_day = (
+ datetime.strptime(query_date, "%Y-%m-%d") + timedelta(days=1)
+ ).strftime(
+ "%Y-%m-%d",
+ )
+ res = search_emails_tool(
+ inbox=inbox_address,
+ sent_before=next_day,
+ keywords=keywords,
+ )
+
+ self.message_id_list.extend([r.message_id for r in res])
+
+ return ToolResponse(
+ content=[
+ TextBlock(
+ type="text",
+ text=json.dumps([asdict(r) for r in res]),
+ ),
+ ],
+ )
+ except Exception as e:
+ logger.info(
+ "Error in search_emails: %s, traceback: %s",
+ e,
+ traceback.format_exc(),
+ )
+ return ToolResponse(
+ content=[
+ TextBlock(
+ type="text",
+ text=(
+ f"Error: Failed to search emails.\n"
+ f"Error message: {e}"
+ ),
+ ),
+ ],
+ )
+
+ def read_email(self, message_id: str, **_kwargs: Any) -> ToolResponse:
+ """
+ Read the content of an email from the user's email inbox.
+ Returns the email content.
+
+ Args:
+ message_id (str): The unique identifier of the email to read.
+
+ Returns:
+ ToolResponse:
+ A ToolResponse object containing the email content or an
+ error message if the email is not found.
+ """
+
+ try:
+ email_content = read_email_tool(message_id)
+
+ self.ever_read_message_ids.append(message_id)
+
+ if email_content is None:
+ return ToolResponse(
+ content=[
+ TextBlock(
+ type="text",
+ text=(
+ f"Error: Email (message_id = {message_id}) "
+ f"not found."
+ ),
+ ),
+ ],
+ )
+ return ToolResponse(
+ content=[
+ TextBlock(
+ type="text",
+ text=json.dumps(email_content.model_dump()),
+ ),
+ ],
+ )
+ except Exception as e:
+ logger.info(
+ "Error in read_email: %s, traceback: %s",
+ e,
+ traceback.format_exc(),
+ )
+ return ToolResponse(
+ content=[
+ TextBlock(
+ type="text",
+ text=(
+ f"Error: Failed to read email.\n"
+ f"Error message: {e}"
+ ),
+ ),
+ ],
+ )
diff --git a/tuner/email_search/_utils.py b/tuner/email_search/_utils.py
new file mode 100644
index 0000000..67cde62
--- /dev/null
+++ b/tuner/email_search/_utils.py
@@ -0,0 +1,328 @@
+# -*- coding: utf-8 -*-
+"""
+This file defines Dataclass and tool implementations.
+Modified from https://github.com/OpenPipe/ART/blob/art-e/
+"""
+import datetime
+import os
+import sqlite3
+from dataclasses import dataclass
+from typing import Any, List, Optional
+from pydantic import BaseModel, Field, field_validator
+from agentscope import logger
+
+DEFAULT_DB_PATH = os.environ.get("DEFAULT_EMAIL_DB_PATH")
+conn = None
+
+
+def get_conn() -> sqlite3.Connection:
+ """Get or create a database connection."""
+ global conn
+ if conn is None:
+ conn = sqlite3.connect(
+ f"file:{DEFAULT_DB_PATH}?mode=ro",
+ uri=True,
+ check_same_thread=False,
+ )
+ return conn
+
+
+class QueryModel(BaseModel):
+ """Model for email search query."""
+
+ id: int
+ question: str
+ answer: str
+ message_ids: List[str] # message_ids (strings) of referenced emails
+ how_realistic: float
+ inbox_address: str
+ query_date: str
+
+ @field_validator("query_date", mode="before")
+ @classmethod
+ def format_date(cls, v: Any) -> str:
+ """Format date to string if it's a datetime object."""
+ if isinstance(v, datetime.datetime):
+ return v.strftime("%Y-%m-%d")
+ return v
+
+
+class AnswerModel(BaseModel):
+ """Model for agent's answer with sources."""
+
+ answer: str = Field(
+ description=(
+ "It should be called with the answer and the sources. "
+ "If you cannot find the answer, you should return "
+ "'I don't know' with an empty list of sources."
+ ),
+ )
+ sources: List[str] = Field(
+ description=(
+ "a list of message ids that are relevant to the query. "
+ "Usually there will be only one. If you cannot find the "
+ "answer, you should return an empty list."
+ ),
+ )
+
+
+class Email(BaseModel):
+ """Model representing an email."""
+
+ message_id: str
+ date: str # ISO 8601 string 'YYYY-MM-DD HH:MM:SS'
+ subject: Optional[str] = None
+ from_address: Optional[str] = None
+ to_addresses: List[str] = Field(default_factory=list)
+ cc_addresses: List[str] = Field(default_factory=list)
+ bcc_addresses: List[str] = Field(default_factory=list)
+ body: Optional[str] = None
+ file_name: Optional[str] = None
+
+
+@dataclass
+class SearchResult:
+ """Result from email search."""
+
+ message_id: str
+ snippet: str
+
+
+class FinalRubric(BaseModel):
+ """Rubric for evaluating agent performance."""
+
+ answer_correct: bool = False
+ sources_correct: bool = False
+ num_turns: int = 0
+ attempted_answer: bool = False
+ ever_found_right_email: bool = False
+ ever_read_right_email: bool = False
+ cant_parse_tool_call: bool = False
+ bad_tool_call_name: bool = False
+ bad_tool_call_args: bool = False
+ ran_out_of_turns: bool = False
+ returned_i_dont_know: bool = False
+ num_sources: int = 0
+ ever_tried_to_read_invalid_email: bool = False
+ prompt_tokens: int = 0
+ completion_tokens: int = 0
+
+
+# Define tools for agent
+
+
+def search_emails_tool(
+ inbox: str,
+ keywords: List[str],
+ from_addr: Optional[str] = None,
+ to_addr: Optional[str] = None,
+ sent_after: Optional[str] = None,
+ sent_before: Optional[str] = None,
+ max_results: int = 10,
+) -> List[SearchResult]:
+ """
+ Searches the email database based on keywords, inbox,
+ sender, recipient, and date range.
+
+ Args:
+ inbox: The email address of the user performing the search.
+ Results include emails sent from or to (inc. cc/bcc)
+ this address.
+ keywords: A list of keywords that must all appear in the
+ subject or body.
+ from_addr: Optional email address to filter emails sent *from*.
+ to_addr: Optional email address to filter emails sent *to*
+ (inc. cc/bcc).
+ sent_after: Optional date string 'YYYY-MM-DD'. Filters for
+ emails sent on or after this date.
+ sent_before: Optional date string 'YYYY-MM-DD'. Filters for
+ emails sent before this date.
+ max_results: The maximum number of results to return.
+ Cannot exceed 10.
+
+ Returns:
+ A list of SearchResult objects, each containing 'message_id'
+ and 'snippet'. Returns an empty list if no results are found
+ or an error occurs.
+ """
+ # Initialize sql and params
+ sql: Optional[str] = None
+ params: List[str | int] = []
+
+ cursor = get_conn().cursor()
+
+ # --- Build Query ---
+ where_clauses: List[str] = []
+
+ # 1. Keywords (FTS)
+ if not keywords:
+ raise ValueError("No keywords provided for search.")
+
+ if max_results > 10:
+ raise ValueError("max_results must be less than or equal to 10.")
+
+ # FTS5 default is AND, so just join keywords. Escape quotes for safety.
+ fts_query = " ".join(f""" "{k.replace('"', '""')}" """ for k in keywords)
+ where_clauses.append("emails_fts MATCH ?")
+ params.append(fts_query)
+
+ # 2. Inbox filter (must be from OR to/cc/bcc the inbox user)
+ # Use the composite index idx_recipients_address_email here
+ where_clauses.append(
+ """
+ (e.from_address = ? OR EXISTS (
+ SELECT 1 FROM recipients r_inbox
+ WHERE r_inbox.recipient_address = ? AND r_inbox.email_id = e.id
+ ))
+ """,
+ )
+ params.extend([inbox, inbox])
+
+ # 3. Optional From filter
+ if from_addr:
+ where_clauses.append("e.from_address = ?")
+ params.append(from_addr)
+
+ # 4. Optional To filter (includes to, cc, bcc)
+ # Use composite index idx_recipients_address_email
+ if to_addr:
+ where_clauses.append(
+ """
+ EXISTS (
+ SELECT 1 FROM recipients r_to
+ WHERE r_to.recipient_address = ? AND r_to.email_id = e.id
+ )
+ """,
+ )
+ params.append(to_addr)
+
+ # 5. Optional Sent After filter
+ if sent_after:
+ # Assumes date format 'YYYY-MM-DD'
+ # Compare against the start of the day
+ where_clauses.append("e.date >= ?")
+ params.append(f"{sent_after} 00:00:00")
+
+ # 6. Optional Sent Before filter
+ if sent_before:
+ # Assumes date format 'YYYY-MM-DD'
+ # Compare against the start of the day (exclusive)
+ where_clauses.append("e.date < ?")
+ params.append(f"{sent_before} 00:00:00")
+
+ # --- Construct Final Query ---
+ # snippet(
, , ,
+ # , , )
+ # -1 means highlight across all columns (subject, body)
+ sql = f"""
+ SELECT
+ e.message_id,
+ snippet(emails_fts, -1, '', '', ' ... ', 15) as snippet
+ FROM
+ emails e JOIN emails_fts fts ON e.id = fts.rowid
+ WHERE
+ {" AND ".join(where_clauses)}
+ ORDER BY
+ e.date DESC -- Order by date for relevance
+ LIMIT ?;
+ """
+ params.append(max_results)
+
+ # --- Execute and Fetch ---
+ logger.debug("Executing SQL: %s", sql)
+ logger.debug("With params: %s", params)
+ cursor.execute(sql, params)
+ results = cursor.fetchall()
+
+ # Format results
+ formatted_results = [
+ SearchResult(message_id=row[0], snippet=row[1]) for row in results
+ ]
+ logger.info("Search found %d results.", len(formatted_results))
+ return formatted_results
+
+
+def read_email_tool(message_id: str) -> Optional[Email]:
+ """
+ Retrieves a single email by its message_id from the database.
+
+ Args:
+ message_id: The unique identifier of the email to retrieve.
+
+ Returns:
+ An Email object containing the details of the found email,
+ or None if the email is not found or an error occurs.
+ """
+ cursor = get_conn().cursor()
+
+ # --- Query for Email Core Details ---
+ email_sql = """
+ SELECT id, message_id, date, subject, from_address, body, file_name
+ FROM emails
+ WHERE message_id = ?;
+ """
+ cursor.execute(email_sql, (message_id,))
+ email_row = cursor.fetchone()
+
+ if not email_row:
+ logger.warning("Email with message_id '%s' not found.", message_id)
+ return None
+
+ email_pk_id, msg_id, date, subject, from_addr, body, file_name = email_row
+
+ # DEBUG
+ logger.info("[read_email_tool] input_message_id=%s", message_id)
+ logger.info(
+ "[read_email_tool] db: id=%s, message_id=%s",
+ email_pk_id,
+ msg_id,
+ )
+
+ # search for recipients by emails.id (rather than message_id)
+ recipients_sql = """
+ SELECT recipient_address, recipient_type
+ FROM recipients
+ WHERE email_id = ?;
+ """
+ cursor.execute(recipients_sql, (email_pk_id,))
+ recipient_rows = cursor.fetchall()
+
+ to_addresses: List[str] = []
+ cc_addresses: List[str] = []
+ bcc_addresses: List[str] = []
+
+ for addr, rtype in recipient_rows:
+ type_lower = rtype.lower()
+ if type_lower == "to":
+ to_addresses.append(addr)
+ elif type_lower == "cc":
+ cc_addresses.append(addr)
+ elif type_lower == "bcc":
+ bcc_addresses.append(addr)
+
+ # --- Construct Email Object ---
+ email_obj = Email(
+ message_id=msg_id, # Convert to string to match Pydantic model
+ date=date,
+ subject=subject,
+ from_address=from_addr,
+ to_addresses=to_addresses,
+ cc_addresses=cc_addresses,
+ bcc_addresses=bcc_addresses,
+ body=body,
+ file_name=file_name,
+ )
+
+ return email_obj
+
+
+__all__ = [
+ "QueryModel",
+ "AnswerModel",
+ "FinalRubric",
+ "Email",
+ "SearchResult",
+ "search_emails_tool",
+ "read_email_tool",
+ "get_conn",
+]
diff --git a/tuner/email_search/config.yaml b/tuner/email_search/config.yaml
new file mode 100644
index 0000000..76fbf11
--- /dev/null
+++ b/tuner/email_search/config.yaml
@@ -0,0 +1,72 @@
+project: "AgentScope" # Project name
+name: "Email_search" # Experiment name
+checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} # Directory to save model checkpoints
+algorithm:
+ algorithm_type: multi_step_grpo # GRPO series for multi-step scenario
+ repeat_times: 8 # Number of rollouts per prompt for advantage estimation
+ optimizer:
+ lr: 1e-6 # Learning rate
+ policy_loss_fn: "rec" # Policy loss function
+ policy_loss_fn_args: # Policy loss function arguments
+ epsilon_low: 0.2
+ epsilon_high: 0.2
+ clip_mode: "one-side"
+ weight: "none"
+ temp: 1.0
+ regularizer: "none"
+ regularizer_coef: 0.0
+ kl_loss_fn: 'k2' # KL divergence loss function
+ kl_loss_fn_args:
+ kl_coef: 0.0 # KL divergence coefficient
+ advantage_fn_args:
+ std_cal_level: 'batch' # Advantage normalization level
+model:
+ model_path: ${oc.env:TRINITY_MODEL_PATH,Qwen/Qwen3-4B-Instruct-2507} # Base model path
+ max_response_tokens: 4096 # Max tokens per response
+ max_model_len: 20480 # Max context length
+buffer:
+ total_epochs: 10 # Total training epochs
+ batch_size: 64 # Batch size per explore step
+ train_batch_size: 2560 # 64*8*5, total experiences per training step
+ trainer_input:
+ experience_buffer:
+ name: experience_buffer
+ storage_type: queue
+ replay_buffer:
+ enable: true # Enable experience replay
+ priority_fn: 'decay_limit_randomization'
+ priority_fn_args:
+ decay: 2.0
+ use_count_limit: 3
+ sigma: 2.0
+explorer:
+ eval_interval: 10
+ max_repeat_times_per_runner: 1 # Max repeat times per runner
+ max_timeout: 3600 # Max timeout for each rollout (seconds)
+ rollout_model:
+ enable_history: true # Enable conversation history
+ enable_openai_api: true # Enable OpenAI-compatible API
+ enable_auto_tool_choice: true # Enable automatic tool selection
+ tool_call_parser: hermes # Parser for tool calls
+ engine_num: 4 # Number of vLLM engines for rollout model
+ tensor_parallel_size: 1 # TP size per engine for rollout model
+ enable_prefix_caching: false # Disable prefix caching
+ auxiliary_models:
+ - name: judge
+ model_path: Qwen/Qwen3-30B-A3B-Instruct-2507 # Judge model path
+ engine_num: 1 # Number of vLLM engines for judge model
+ tensor_parallel_size: 2 # TP size per engine for judge model
+ enable_thinking: false # Disable thinking/reasoning mode
+ max_prompt_tokens: 2048 # Max tokens for prompt
+ max_response_tokens: 128 # Max tokens for response
+ max_model_len: 2500 # Max model context length
+synchronizer:
+ sync_style: dynamic_by_explorer # Sync triggered dynamically by explorer
+ sync_interval: 5 # Sync every N steps
+ sync_timeout: 3600 # Timeout for synchronization (seconds)
+trainer:
+ save_interval: 100 # Save checkpoint every N steps
+ grad_clip: 1.0 # Gradient clipping value
+ use_dynamic_bsz: true # Use dynamic batch size
+ max_token_len_per_gpu: 16384 # Max token length per GPU
+ ulysses_sequence_parallel_size: 1 # Sequence parallel size for Ulysses
diff --git a/tuner/email_search/critic_reward_mean.png b/tuner/email_search/critic_reward_mean.png
new file mode 100644
index 0000000000000000000000000000000000000000..334fe39d86a6154c66ef7e3c51930f0d4d696878
GIT binary patch
literal 481654
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≈~#VH3Df8`279!-mn1@QaTG${JThL_RRFs!vXJH
zgSVTxfc{7y-=xLKjF_}9Oi__*6#Q8$_Kw9UT0tsllJd(3kDp#
z5py5K&9v?2_ANr#$#v(oe3@zIX)CuN7i;;<@%{eCMESA@+c)*EG@bGp$Y|+MXqxvB
zI{xMLr#na3nh6$lQ?9B2#g--~-5&^v9bR5rM
zz;t420LMja@j`V$QBp=LR;5Im$e5YA0sAK#}_9`
z6liGC3lf(;MJW@L&jretR1=rLnFYm3u?la5uXZ
zkAMNqdCvQf_-IP*@8df;jeiylD8bKb=zD{^dp7U#J3%|zxj!W3iM5MXzZBLQ_q2(B
z>0pQ|!Coo8jn)}xDB*e}ig5m2taw~~Chs1%&uX~+4XykeB7#GGCuVvBxIc>eiPNM>
zEZ!`3>O$%_50ZaG_!XU>>RB0fKsSa{+u$_acpQ~#|LBw#W!*RbK`*WOJ`LU$gYM5Q
zefW`XH~cWh+EHJuxU0h%9Y!6i%$W2eacd^B6npPB0XI+71Bx-sDA8m(-$aootIy2S~cf=SW{bOy`D5&e@|f;GdOcng@`K!jwj4D{C&sWPs=P4Q?XlzmfR
z{um28Z`P7G+Ei*Dv6ynZEw?)#NL{bBTUob>N%bRzNLPcYn8+hQ(t>Zqm(#$HtF*O+
z!RhIBu7#*zgvjBsk_i;Ln6L6CAH``H7^G^KtcoQT<6h3R%)RcEeFd{wHMsfP`ElR;
z@h+JP36DF@#q(_m-l?k1%yMiRx}7FB`U}SKM`3VG`{DpSBAHZh<@RFIObay`Vi*y+
zY!u5g>i%LVw&3lhMCG|liMRDha%J^QSXVf;$d51MLmuub^Se~4Lfsy8agtu5?}5`K
zo~0j+skl_V(w&Bh3vm{UP#9=jq?+K6grsI!bK5HQ(gJK)*jjBn?VpUVmt5txQGImJl>dZqwp`%-_8uMI5B#
zR{1lf)dl{lir;Yss
zk4FZzI-$9l`cV&Y%=oi*5j=jsSqVud_3_s7-IF8{a~m3aH~*-x;FVX5R&^>8xJz4iCvfp0&PM^JMeG2+{59x6v?Y%D8
z*ug66{MuW2?;~MaE)7HNiToOmB}qy`-IMGjqHwn?oL2hMRr|f@Ho0$;lj=2~BxsOo
z<@rrn|I>xfe`(9`I=cSaRjaEp6EN=u?GspCCQbRWN@-HLCBuuw*hM?LY6Svoz!^dn
z8LiW&h&gb>!-C}uaC?uZ3^;3V%cZ7avb`QWh)@6O&W&eRJf=zSB2t(