ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

多轮智能体信用分配新思路:CREST验证器约束机制解析

多轮智能体信用分配新思路:CREST验证器约束机制解析 在大多数多轮大语言模型智能体项目里最让人头疼的问题之一不是模型不会调用工具而是模型明明跑完了整条轨迹最终答案也正确但你无法确定究竟是哪一轮推理、哪一次工具调用真正决定了这个结果。CREST 论文提出的方向是把验证器verifier作为外部约束引入多轮智能体的信用分配过程让最终奖励不再被简单摊平到每一步而是先经过验证器对中间状态的判断再决定哪些动作应该被加强、哪些动作应该被减弱。换句话说它想解决的不是“模型能不能完成任务”而是“任务成功后功劳应该记在谁头上”。这篇文章会围绕多轮智能体训练中最容易被低估的信用分配问题展开先说明什么是多轮智能体、为什么最终奖励不够用再拆解 CREST 中“验证器约束信用分配”的设计思路然后给出一套便于复现的最小实现框架最后讨论验证数据、训练稳定性、常见报错和工程化建议。如果你正在做工具调用智能体、Agent 强化学习微调、过程监督相关的研究或工程下面这些内容会比较实用。1. 先理解多轮智能体为什么需要信用分配1.1 多轮智能体不是“回答一道题”而是“执行一条任务链”普通大模型评测通常只关心一轮输入和一轮输出模型给出最终答案任务结束。多轮智能体则完全不同。它面对一个用户目标后需要自己规划步骤生成思考内容决定调用搜索、计算器、数据库、外部 API 或代码执行器等工具然后读取工具返回的观察结果再决定下一步动作。这个循环可能要重复多次直到模型认为信息足够才输出最终答案。这类结构在工程里通常被称为 Agent 轨迹trajectory。一次完整的轨迹可以由下面的数据对象表示from dataclasses import dataclass from typing import Optional dataclass class Turn: step_id: int # 第几轮 thought: str # 模型内部推理 action: str # 动作类型例如 search / python / db action_input: str # 动作参数 observation: str # 工具返回的观察 final_answer: Optional[str] # 如果本轮结束记录最终答案 dataclass class Trajectory: query: str # 用户请求 turns: list[Turn] # 多轮动作列表 final_reward: float # 整体奖励通常只有 0 或 1为什么需要这个结构因为后续所有信用分配计算都要以“每一轮转换”为基本单位。如果训练数据里只保留最终答案而丢弃中间动作那么即使采用再复杂的强化学习算法也没有办法把成功或失败归因到具体步骤上。1.2 只看最终结果无法回答“哪一步真正导致失败”假设一个模型要完成下面这条任务链用户提问某公司某年的营收是多少。模型第一步调用搜索但检索关键词写错了。工具返回了一个错误实体的结果。模型根据错误结果继续推理中途发现数字明显不合理。模型重新调整关键词第二次检索成功。模型输出正确答案。从最终结果看这条轨迹成功了所以奖励为正。但仔细看内部结构第一次检索动作本身是错误的。如果没有过程信号强化学习会把最终成功的正奖励均匀地传播给每一步导致“错误的关键词”也可能被增强。更危险的是如果模型在错误检索之后很快通过某种巧合修正了方向那么错误动作反而会被当成有效动作来学习。反过来也存在另一个问题模型前几轮推理和工具调用完全正确但最后一轮回答时格式不满足要求最终奖励为负数。此时如果对整条轨迹做反向传播前面正确步骤的概率也会被整体压低。这在强化学习里叫稀疏延迟奖励问题。最终奖励只告诉模型“结果好坏”不告诉模型“过程哪一步好坏”。多轮智能体链条越长中间动作数量越多这个问题越严重。1.3 信用分配要同时处理时间和动作两个维度多轮智能体的信用分配并不只是在时间维度上推迟奖励这么简单。它还要处理两个更复杂的问题。第一个是时间维度。奖励发生在轨迹末尾模型在步骤 t 采取的决策可能要等到几步之后才能看到影响。错误的动作可能来自很远的前缀也可能来自最近一次观察的误判不能简单地把最后一步当成罪魁祸首。第二个是动作维度。模型每一步可能同时包含思考、工具选择和参数拼接。同样是一次工具调用选择“调用哪个工具”和“传入什么参数”会影响后续结果而“如何解读工具返回的 observation”又会影响下一步决策。如果只对整个动作给同一个 credit就很难定位是检索策略出了问题还是工具参数出了问题。所以在多轮智能体训练里真正需要的是一个更细粒度的过程信号而不是只在大结局后做一次全局审判。CREST 解决的核心问题正在这里。2. CREST 的方法骨架验证器如何成为信用分配约束2.1 验证器不是普通奖励模型在主流 LLM 对齐方案里奖励模型reward model通常对整条序列或一个最终状态打分例如用户上一轮提问、模型回答了一长串内容奖励模型输出一个总分。这种打分天然是粗粒度的。验证器verifier更接近“过程监督器”。它不直接回答“这条轨迹最终好不好”而是回答“当前这个中间状态或这一小步转换是否合理”。例如在某个工具调用后的 observation 上验证器可以判断这一步得到的信息是否与问题相关、是否包含明显矛盾、是否偏离了目标。在 CREST 的思路里验证器的作用不是额外给一个过程奖励就结束而是为信用分配提供边界。它告诉训练系统这一步从局部看是可靠的还是不可靠的如果不可靠那么即使在最终成功轨迹里这一步骤也不能获得过高的正向 credit。2.2 “约束信用”到底约束了什么这里需要先把“最终奖励如何传播到每一步”这个问题形式化。一种简单做法是认为每条动作都拿到相同的回报。另一种是使用蒙特卡洛回报即把从当前步到轨迹结束之间的累计奖励当作当前步的回报。还有一种是用 Critic 网络估计状态价值从而计算优势函数。但上述方法都缺少一个外部监督来回答“当前这一步在局部是否真的正确”。CREST 用验证器把这个缺失信息补了进来。用便于理解的示意公式可以写成credit_t lambda_t * final_reward lambda_t verify_score_t / sum_i(verify_score_i)这里verify_score_t不是某一个模型凭空生成的而是结合了验证器对当前步骤的局部判断。如果验证器认为第 t 步的工具调用和观察结果存在明显错误那么verify_score_t会被压低。即使 final_reward 为正该步骤得到的credit_t也很小不会导致错误动作概率被错误提升。如果最终奖励为负同理验证器分数高的步骤不应该承担过大的负面责任因为它们局部是正确的问题可能出在后面几步。因此验证器不是简单地在最终奖励上再加一层筛选而是在“整条轨迹的最终奖惩”和“每一步该承担多少责任”之间建立一道闸门。方法名里的“约束”二字强调的是验证器不会直接替策略模型做动作也不会直接把局部分数当作最终反馈而是把局部正确性作为信用分配的上界或下界限制最终奖励的不合理扩散。2.3 与几种常规信用分配方法做对比为了理解 CREST 的定位可以把主流信用分配方式放进一张表里对比。方法监督信号来源是否使用中间状态典型问题整条轨迹 REINFORCE最终奖励否对每个动作给同样 credit稀疏且噪声大Monte Carlo 回报从当前步到轨迹结束的累计奖励否方差高无法区分错误动作出现的具体位置GAE Critic最终奖励 学到的价值函数弱Critic 自身误差会污染优势估计结果奖励模型对整条回答打分否无法给中间工具调用步骤细粒度反馈过程奖励模型对每一步打分是每步分数本身可能不准缺少与最终回报的融合CREST 思路最终奖励 验证器约束是验证器的局部判断决定了最终奖励如何分配给每一步这张表不是要说明前几种方法没有价值而是要说明它们各自解决的粒度不同。CREST 最值得关注的地方在于它没有抛弃最终奖励而是把最终奖励当作总预算把验证器当作预算分配规则。局部正确的步骤可以拿到更多贡献局部可疑的步骤必须降权。这里给出的公式是便于理解方法思想的抽象写法实际论文中的实现可能包含更复杂的归一化、时间折扣和约束条件。工程复现时要结合自己的轨迹结构来设计不能把示意公式原样当作最终的损失函数。3. 用最小实现说明 credit 计算过程3.1 第一步准备带中间状态的数据集无论使用什么算法多轮智能体的训练数据都不能只保存最终答案。至少要保存每一步的思考、动作、动作输入、工具返回的 observation、最终奖励以及可选的验证器分数。一条带验证器标注的轨迹可以设计成如下 JSON 结构{ query: 查询某公司2023年营收并对比前一年变化, final_reward: 1, turns: [ { step_id: 1, thought: 需要先找到公司的财报数据, action: search, action_input: 某公司 2023 年营收, observation: 返回了该公司的新闻页面, verifier_score: 0.9, verifier_label: correct }, { step_id: 2, thought: 从新闻页提取数据再查找前一年数据, action: search, action_input: 某公司 2022 年营收 财报, observation: 返回了包含同比增速的报告, verifier_score: 0.4, verifier_label: uncertain }, { step_id: 3, thought: 增速与前一页数据矛盾需要再核实, action: python, action_input: 计算2023与2022的同比, observation: 计算出差异超过5%, verifier_score: 0.8, verifier_label: correct } ] }这段 JSON 的核心信息是每一步不仅有动作内容还带了一个verifier_score和verifier_label。verifier_label可以是correct、uncertain、incorrect等离散标签verifier_score可以是由验证器模型或规则得到的连续分数。后续信用分配主要依赖这些内容。在实际项目里这一步最容易犯的错是只给每条轨迹准备一个最终答案文本而没有把 action、observation 映射成可计算的字段。到训练阶段才发现需要重放历史数据成本会很高。3.2 第二步让验证器对每一轮转换打分验证器可以有多种实现形态。如果希望工程落地简单可以用一个大模型作为在线判断器给每一步生成一个评分如果希望训练和推理成本可控可以用一个较小的序列分类模型输入当前 query、历史步骤和当前 observation输出分数。一个简化版的验证器打分函数可以写成def verify_step( query: str, past_turns: list[dict], current_turn: dict, verifier_model, ) - tuple[str, float]: 返回 (label, score)。 label 建议使用 correct / uncertain / incorrect。 score 是连续值范围建议保持在 0 到 1。 prompt build_verifier_prompt(query, past_turns, current_turn) result verifier_model.predict(prompt) label result[label] score result[score] if label incorrect: score score * 0 # 局部错误时强制降低 credit elif label uncertain: score score * 0.5 # 不确定时打一个折中 return label, float(score)这里的关键是验证器不应该只给“与最终答案是否一致”评分而应该独立判断当前步骤是否合理。比如工具返回数据与待求问题无关即使后续模型靠其他步骤成功了这一步也应该被标记为incorrect。incorrect时把 score 压到很低是实现“约束”最直接的一种方式。最终奖励为正时它不能被扩散到这一步最终奖励为负时这一步也不该承担主要责任。3.3 第三步把最终奖励拆成每个动作的 credit在拿到每一步的验证器分数后就可以计算 credit 序列。为了降低随机噪声可以加入时间折扣因子让靠近最终结果的步骤拥有更高的时间敏感度但局部错误仍然由验证器负责降权。def compute_credit( total_reward: float, verify_scores: list[float], gamma: float 0.95, ) - list[float]: 根据验证器分数和折扣因子将最终奖励分配到每一步。 返回的 credit 序列满足credit 的总和约等于 total_reward。 n len(verify_scores) raw_weights [] for i in range(n): score verify_scores[i] # 越靠近最终答案的步骤时间折扣越小 time_weight gamma ** (n - 1 - i) raw_weights.append(score * time_weight) total_weight sum(raw_weights) 1e-6 credits [total_reward * (w / total_weight) for w in raw_weights] return credits如果某一步验证器分数为 0它的权重也会变成 0最终奖励会自动分配到其他步骤上去。这比平均分配更合理。假设某条轨迹有 3 步final_reward 为 1验证器分数分别是 0.9、0.4、0.8折扣因子为 0.95。那么第 2 步因为验证器分数低获得的 credit 会明显小于第 1 步和第 3 步。如果第 1 步是incorrect被压到 0最终的正向 credit 就会主要落在第 3 步上。这一步最重要的目的是观察 credit 序列是否与人工判断一致。可以先抽几十条轨迹把计算结果打印成对照表不要直接进入训练。3.4 第四步用 credit 更新策略模型拿到每一步的 credit 后可以采用类似策略梯度的方式更新模型只是这里不使用整条轨迹的单一优势而是使用验证器约束后的逐步优势。一个最小损失函数可以参考下面这段伪代码import torch def policy_gradient_loss( policy_logprobs: torch.Tensor, # shape: [num_steps] credits: list[float], # shape: [num_steps] ) - torch.Tensor: advantages torch.tensor(credits, dtypetorch.float32) # 可选减去均值降低方差并保持稳定性 advantages advantages - advantages.mean() loss -(advantages * policy_logprobs).mean() return loss这个损失函数的含义是如果某一步的 credit 为正就提高这一步动作的 log probability如果 credit 为负就降低它。需要注意的是直接对每一步都做梯度更新会增加样本效率风险因为每一步之间并不独立。工程上更稳妥的做法是引入 KL 惩罚防止策略在单次更新中偏离参考策略太远。常见写法是在原损失上加上beta * kl_divergence其中beta可以按训练进度动态调节。在多轮任务中轨迹长度差异较大建议对不同长度轨迹分开记录 loss。如果发现长轨迹普遍训练不稳定则需要在归一化时按轨迹长度调整或者使用截断后的固定窗口来限制依赖范围。3.5 可调参数与影响上面伪代码中涉及几个参数实际项目里要理解它们各自的作用。参数含义推荐起点调大影响调小影响verify_score验证器分数0 到 1更强调过程正确性更容易被最终奖励带偏gamma时间折扣0.95强调早期步骤也很重要只关心靠近结尾的动作KL betaKL 惩罚系数0.01策略更新更保守策略可能快速漂移advantage_mean优势去均值是方差更低credit 正负比例不稳定verifier_label离散标签correct/uncertain/incorrect控制约束强度过少则无法约束这里没有标准答案需要根据轨迹平均长度、验证器准确率和任务复杂度做实验。最忌讳的是所有参数都直接照搬某个 benchmark 项目那通常只在特定数据分布下有效。4. 训练过程怎么设计以及怎么证明 credit 分配有效4.1 推荐的训练管线在实际实验里验证器约束信用分配不太适合一上来就端到端跑完整 RL。建议按下面这个顺序推进。第一步先做监督微调基线。用一批人工标注或模型生成的高质量多轮轨迹训练一个基础策略确保模型本身已经具备基本任务能力。这样后续强化学习阶段主要做偏好优化和错误修正而不是从零学工具调用。第二步训练验证器。验证器可以独立于策略模型训练输入格式是“用户问题 历史步骤 当前步骤的 observation”输出是correct / uncertain / incorrect和连续分数。这里要有独立的验证集不能只使用训练轨迹做评测否则无法判断验证器是否过拟合。第三步用当前策略采样一批新轨迹。对每条轨迹记录最终奖励和所有中间状态然后调用验证器打分生成 credit 序列。第四步用第 3.4 节的损失函数更新策略然后回到第三步继续采样。为了让实验可复现每次迭代都要保存固定版本。对多轮智能体来说数据分布会随策略更新而变化如果不同时记录策略版本、验证器版本、采样温度、credit 计算参数后面很难定位是模型问题还是数据处理问题。4.2 离线的过程正确性评估训练过程中不能只看最终成功率还要看过程层面的变化。如果最终成功率没变但成功轨迹里的中间错误步骤明显减少这也是一种有效提升。建议记录一组过程指标每条轨迹中verifier_label incorrect的比例。在最终成功轨迹中仍被验证器标记为incorrect的步骤数量。在最终失败轨迹中被验证器标记为correct的步骤数量。每个步骤的验证器分数与最终奖励之间的相关性。其中最后一个指标很有意思。如果验证器分数与最终奖励几乎不相关说明验证器给的是与结果无关的局部噪声用它约束信用分配会很危险。对于少量关键轨迹还需要人工复核验证器分数是否正确。CREST 这类方法有一个隐含假设验证器比最终奖励更能反映中间步骤质量。如果验证器本身不准确那么加再多约束也只是把噪声换成另一个方向的噪声。4.3 在线评估要看哪些维度在线评估要与离线计算对齐。每次策略更新后在固定评测集上测试新的策略可能需要多次采样取 passk而不是只取一次 greedy 结果。多轮智能体本身具有随机性单次采样容易受偶然因素影响。在线评估至少要覆盖三个维度任务最终成功率以及达到成功所需的平均步数。工具调用错误率例如调用了不该调用的工具或参数不完整。在成功轨迹中是否存在可观测的“绕路修复”现象。即模型先犯了错后来又在后面的步骤里被迫修正这种轨迹要单独统计。如果模型成功率没有提升但“绕路修复”明显减少说明 credit 分配让模型开始避免早期错误如果二者都没有变化应该优先检查验证器分数和 credit 归一化逻辑。4.4 一套可复用的实验记录清单要判断新方法是否有效最有效的方法是提前列一份实验记录清单每次改动只动一个变量。记录项示例说明实验编号exp-014唯一标识策略模型版本policy_v3模型 checkpoint验证器版本verifier_v2如果换过验证器结果不可直接比较采样温度0.7影响轨迹分布采样轨迹数2000决定梯度噪声credit 计算参数gamma0.95, score_weight1.0核心参数KL beta0.01策略更新步长约束评估集eval_hard_50固定测试集最终成功率62%最终结果指标过程错误比例8.4%过程质量指标备注第二次 retry 成功后奖励仍然过大记录影响判断的现象这份清单不只是给论文实验用工程团队做模型迭代时同样需要。很多 Agent 训练项目结果忽好忽坏最后查出来的原因经常不是算法写错而是验证器换了一个版本或者 credit 参数被人改动后没有同步记录。5. 常见问题与排查路径5.1 训练曲线平稳但最终效果不提升现象是 loss 看起来在正常下降greedy 评测成功率也稳定但多轮任务成功率没有明显升高。优先检查 credit 序列是否产生了有效差异。直接把一条成功轨迹和一条失败轨迹分别打印出来看每步 credit 是不是“全为正”或“全为负”。如果几乎所有成功轨迹中每一步 credit 都接近同一个正数那么验证器约束实际上没有起作用模型收到的仍然是一个平均信号。常见原因包括验证器分数区分度太低、归一化权重把差异抹平、或者大部分轨迹里步骤标签都被标记为correct。此时应当先看验证器在轨迹上的分数分布。如果分布过于集中在 0.8 到 1.0建议先增强困难负例或降低correct的判定阈值。5.2 最终成功率上升但中间错误动作也被增强这种问题比“不提升”更难发现。表现为最终成功率提高但模型产生了更多早期错误然后依靠后面步骤强行纠错。如果只看最终结果会误以为训练是成功的。需要将成功轨迹按“是否出现过 verifier_label incorrect 的步骤”分组分别统计这些错误步骤所在位置的 credit。如果错误步骤在成功轨迹中仍然拿到较高正 credit说明约束没有通过验证器生效。可能原因有三个验证器没有真正把错误步骤判为错误在计算raw_weights时错误步骤 score 没有被压到足够低时间折扣因子过大导致位置靠前的高错误步骤获得了过高的权重。5.3 验证器准确率不低但训练中逐渐失真验证器在静态数据集上可能很准确但策略更新后模型生成的动作分布会发生变化出现验证器没有见过的中间状态。此时验证器会产生系统性偏差甚至把本来正确的新动作误判为incorrect。常见表现是策略逐步趋向保守不敢调用工具或反复输出同一句验证器认为安全的中间话术。处理方式不是立刻换一个更大的验证器而是让验证器在训练过程中定期用新采样的负例做增量校准。同时要监控每个训练 batch 中incorrect标签占比。如果这个占比在几个迭代周期内突然升到异常值说明策略漂移已经超出验证器覆盖范围建议回滚到上一个 checkpoint。5.4 数据集里没有中间步骤标签怎么做 starter真实业务数据很难一开始就具备完美的过程标签。可以采用由粗到精的弱监督流程。第一步用规则判断明显错误。例如工具返回状态码异常、检索结果为空、代码执行报错都可以直接标记为incorrect。第二步用大模型作为旁路验证器对每条轨迹逐步骤打分。为了减小过拟合可以把用户请求和整条历史拼接后让模型给出结构化输出。第三步人工抽检一批结果计算验证器与人工的一致率。一致率达到 85% 以上再开始做训练否则先修正验证 prompt 或补充标签样本。弱监督只适合启动阶段。随着模型能力提升最终还要逐步引入人工标注高质量负例否则验证器会停留在“能识别明显错误”的粗粒度水平无法对 subtle 的错误步骤施加约束。5.5 一个可以直接照着查的顺序当训练结果异常时按下表顺序排查能省不少时间。检查顺序检查对象判断方法处理建议1输入轨迹是否有 action、observation、step_id 等字段字段缺失时无法做细粒度归因2验证器分数打印所有轨迹的 score 分布分布单一则提高数据多样性或更换验证器3credit 序列同一轨迹是否出现明显正负区分如果全部同号检查归一化逻辑4梯度方向对比错误步骤和正确步骤的 logprob 变化错误步骤 logprob 不应持续上升5训练稳定性KL 散度和 reward variancebeta 太小或 advantage 没去均值6评测方式单次采样还是多次采样多轮任务建议用 passk 评估这六步就是把上面所有问题统一成一条排查链路。遇到任何异常先不要急着改网络结构先确认数据是否支持“过程级归因”。6. 工程实践建议与后续方向6.1 研究阶段建议从小规模“可解释环境”开始如果你刚开始理解 CREST 这类方法不建议直接在一个复杂的真实 Agent 系统上做实验。真实工具返回的 observation 太杂问题也无法穷举很难判断 credit 分配到底是否正确。更稳妥的做法是先用一个 step 数量可控、成功条件明确的多轮环境做实验。例如设计 5 到 8 步的检索与计算任务让环境能明确告诉模型当前步骤的工具返回是否符合目标。此时验证器可以先保持简单甚至用手工规则替代。等规则版验证器能稳定提升过程质量再替换成学习的验证器并对比两者在训练样本效率上的差异。这个方法的好处是你能准确区分方法的收益来源是“过程监督信息本身有用”还是“特定验证器实现有效”。如果刚开始就把验证器、信用分配、策略更新、真实工具全部耦合在一起出了问题很难定位。6.2 生产环境要补齐哪些工程保障生产环境与实验环境相差很大。实验环境可以手工重启任务生产环境则必须考虑数据版本、监控、回滚和异常保护。第一验证器和策略必须分开部署并固定版本。不要在生产训练任务中偷偷更新验证器模型否则历史 rollout 数据与新验证器分数之间会出现不可控偏差。第二所有 rollout 数据要完整落盘。即使当前只计划训练一个很小的策略也应该把 query、每步 thought、action、observation、工具返回 metadata、final_reward 都保存下来。以后改动验证器时可以用同一批历史轨迹重放对比。第三设置安全回滚策略。如果观察到训练后的策略在某类任务上最终成功率下降要能快速切回上一版策略而不是等待下一次训练迭代把问题修好。第四对最终奖励本身要谨慎。在多轮 Agent 中很多最终奖励不是环境天然给出的而是由结果评测模型产生的。如果这个结果评测模型本身有偏好那整个信用分配都会被污染。生产环境至少要保留人工抽检最终奖励的机制。6.3 下一步可以扩展的方向CREST 所代表的思路并不局限于“用验证器做奖惩分配”。它可以往几个方向继续扩展。一个方向是让验证器具备“前瞻性”。当前验证器往往只基于已有轨迹判断当前步是否正确但真正的中间状态质量还要看它对后续动作的帮助。例如某一步工具调用返回的全是无关信息但碰巧包含一个能引发正确搜索的线索局部验证器可能给出低分实际却是有价值的。引入前瞻性验证器时需要把未来是否成功的信息也作为弱约束进入训练。另一个方向是把 credit 分配扩展到工具调用参数级别。现在很多工作只对 action 级别做奖励归因但同一个 action 里 query 拼接、top-k 选择、温度参数都值得细粒度建模。如果 trajectory 数据结构足够精细这部分也能纳入验证器约束。还有一个方向是与偏好优化的结合。传统 RL 使用最终奖励DPO 等偏好优化方法使用成对轨迹偏好。如果把验证器约束后的过程质量融入偏好对构建可能比单纯使用最终答案胜负更稳定。多轮智能体训练真正有价值的地方不是把模型输出调得越来越像某个人工答案而是让模型在复杂工具链中学会自己判断哪一步是关键的、哪一步是危险的。验证器约束信用分配提供了一条新的路径在最终结果之外给训练过程加一个状态级解释器。对做 Agent 训练和评测的团队来说真正值得投入的也不是堆更多算力跑更多轨迹而是先把“每一步为什么对、为什么错”这个信号做强、做细、做到可回滚。
返回列表