强化学习的可观测配置

更新时间:
复制为 MD 格式

本文以 agentic-rl-example 项目为例,介绍如何为强化学习训练配置可观测能力:添加 OpenTelemetry 依赖、接入 Tracing、在控制台查看轨迹与指标。

概述

强化学习训练基于 OpenTelemetry 实现了深度链路追踪(Tracing)。您只需在函数代码中添加少量装饰器和包装调用,训练过程中的每一次 LLM 调用、每一次工具调用、每一个评分细节都会自动记录,并导出到 ARMS(应用实时监控服务),在百炼控制台可视化展示。

可观测性能帮助您回答:

  • 模型在训练中实际做了什么?—— 查看轨迹详情,回放完整对话过程

  • 得分为什么高或低?—— 查看 Reward 分析的每个评分维度

  • Agent 如何调用工具?调用是否正常?—— 查看工具调用分析和 Tracing Span 详情

  • 自定义指标如何变化?—— 在指标 Tab 中追踪 reward_metrics 和 rollout_metrics 的曲线

数据流:您的函数代码(@observe_processor / trace_client / trace_tool)→ OpenTelemetry SDK → ARMS → 百炼控制台(轨迹 Tab、指标 Tab)

本文范围:agentic-rl-example 项目(CalcX 计算器场景)为主线,演示从零配置到控制台查看效果的完整流程。训练提交和超参数配置的完整说明请参见强化学习训练配置

接入 Tracing

以下以 agentic-rl-example 的 CalcXRolloutProcessor 为例,3 步完成 Tracing 接入。

Step 1:添加依赖

在项目根目录的 requirements.txt 中添加以下 OpenTelemetry 相关依赖:

opentelemetry-api==1.41.1
opentelemetry-sdk==1.41.1
opentelemetry-exporter-otlp-proto-http==1.41.1
opentelemetry-processor-baggage==0.62b1
loongsuite-util-genai==0.4.0
说明

dashscopefastapiuvicornpyyaml 已预装在运行环境中,无需列入 requirements.txt。

Step 2:添加观测代码

代码改动集中在 5 个装饰器/函数。它们会自动嵌套,形成完整的调用链:

[ENTRY: ROLLOUT] @observe_processor          ← Rollout 处理器入口
├── [LLM] trace_client / @observe_llm        ← LLM 调用
│   └── (OpenAI / LangChain / DashScope API)
├── [TOOL] trace_tool / @observe_tool        ← 工具调用
│   ├── tool: calculator (MCP)
│   └── tool: response_scorer (自定义)
└── [custom] rollout_metrics                 ← Rollout 自定义指标

[ENTRY: REWARD] @observe_processor           ← Reward 处理器入口
├── [LLM] trace_client / @observe_llm        ← LLM 调用(评分用)
├── [TOOL] trace_tool / @observe_tool        ← 工具调用
└── [custom] reward_metrics                  ← Reward 自定义指标

@observe_processor — 追踪处理器入口

加在 process() 方法上,创建顶层 ENTRY Span。SDK 根据父类自动识别类型:继承 AbstractRolloutProcessor → ROLLOUT 类型,继承 AbstractRewardProcessor → REWARD 类型。自动记录每次调用的输入、输出、耗时、成功/失败状态。

Rollout 侧(functions/rollout/rollout.py):

from dashscope.finetune.reinforcement.component.observability import (
    observe_processor
)

class CalcXRolloutProcessor(AbstractRolloutProcessor):
    @observe_processor  # Span 类型 = ROLLOUT
    async def process(self, input: RolloutInput) -> RolloutOutput:
        await self._async_setup()
        return await self._async_process(input)

Reward 侧(functions/reward/reward.py):

class DemoRewardProcessor(AbstractRewardProcessor):
    @observe_processor  # Span 类型 = REWARD
    async def process(self, input: RewardInput) -> RewardOutput:
        score = await evaluate(content, input.ground_truth)
        return RewardOutput(
            reward=Reward(
                reward_score=score,
                reward_metrics={"test1": 0.5, "test2": 0.3}
            ),
            status=TaskStatus.SUCCESS,
        )

trace_client() — 追踪 LLM 客户端

在初始化方法中调用,包装 LLM 客户端实例。之后该客户端的所有 LLM 请求都会自动产生 LLM Span,记录模型名、请求内容、Token 用量、延迟等。

支持的客户端类型(鸭子类型自动检测):

  • OpenAI 客户端(AsyncOpenAI / OpenAI

  • OpenAI completions 资源(.chat.completions

  • LangChain ChatOpenAI 等类(通过 .client / .async_client

  • DashScope Generation 类(传入类本身,非实例)

示例(functions/rollout/rollout.py):

from langchain_openai import ChatOpenAI
from dashscope.finetune.reinforcement.component.observability import (
    trace_client
)

class CalcXRolloutProcessor(AbstractRolloutProcessor):
    def _build_llm(self, input: RolloutInput) -> ChatOpenAI:
        llm = ChatOpenAI(
            model=input.model_resource.model_name,
            openai_api_key=api_key,
            openai_api_base=input.model_resource.base_url,
            ...
        )
        trace_client(llm)  # 包装后自动追踪所有 LLM 调用
        return llm

trace_tool() — 追踪工具调用

在获取工具实例后调用,包装工具对象。之后每次工具调用都会产生 TOOL Span,记录工具名、参数、返回值、延迟。

支持的输入格式:

  • 单个 LangChain BaseTool

  • 列表 / 元组 / 字典(自动遍历)

  • LangGraph ToolNode(自动展开 .tools_by_name

  • MCP 工具(自动检测,provider 设为 "mcp")

示例(functions/rollout/rollout.py):

from dashscope.finetune.reinforcement.component.observability import (
    trace_tool
)

class CalcXRolloutProcessor(AbstractRolloutProcessor):
    async def _init_resources_async(self):
        client = MultiServerMCPClient({
            "calculator": {"url": "http://localhost:10086/sse"}
        })
        tools = await client.get_tools()
        trace_tool(tools)  # 必须在 get_tools() 之后调用
警告

MCP 特别注意:MCP Server 和 Client 运行在不同进程中。Server 端的 @observe_tool 对 Client 端无效。必须在 Client 端调用 get_tools() 之后,对返回的工具列表执行 trace_tool(tools)

@observe_llm — 自定义 LLM 函数

trace_client() 无法自动检测您的 LLM 客户端时,用此装饰器手动标记 LLM 调用函数。

签名要求:函数必须包含 * 后的关键字参数 modelmessages

from dashscope.finetune.reinforcement.component.observability import (
    observe_llm
)

@observe_llm  # 标记为 LLM Span
async def call_custom_llm(*, model: str, messages: list, **kwargs):
    # 自定义 LLM 调用逻辑
    ...

@observe_tool — 自定义工具函数

trace_tool() 无法自动检测您的工具时(如普通 Python 函数充当工具),用此装饰器手动标记。可通过 name 参数自定义 Span 名称。

from dashscope.finetune.reinforcement.component.observability import (
    observe_tool
)

@observe_tool(name="response_scorer")  # 标记为 TOOL Span
def score_response(*, messages: list) -> float:
    # 自定义评分逻辑
    ...

Step 3:提交任务

提交任务时,Runtime 的 env 字段留空即可——链路追踪默认开启

from dashscope.finetune.agentic_rl import AgenticRL
from dashscope.finetune.reinforcement import (
    RolloutFunctionComponent, RewardFunctionComponent,
    FunctionComponentModel, FunctionComponentRuntime
)

client = AgenticRL()

rollout_runtime = FunctionComponentRuntime(
    cpu=2, memory_size=4096, disk_size=512,
    concurrency=30, capacity=30,
    min_capacity=30, max_capacity=60,
    env={}  # 留空 = 默认开启 Tracing
)

reward_runtime = FunctionComponentRuntime(
    cpu=2, memory_size=4096, disk_size=512,
    concurrency=30, capacity=30,
    min_capacity=30, max_capacity=60,
    env={}
)

result = await client.run(
    model="qwen3.5-9b",
    functions=[
        RolloutFunctionComponent(
            name="rollout-1",
            fcmodel=FunctionComponentModel(
                classpath="functions.rollout.rollout.CalcXRolloutProcessor"),
            runtime=rollout_runtime,
        ),
        RewardFunctionComponent(
            name="reward-1",
            weight=1.0,
            fcmodel=FunctionComponentModel(
                classpath="functions.reward.reward.DemoRewardProcessor"),
            runtime=reward_runtime,
        ),
    ],
    ...
)
说明

如需关闭 Tracing(节省成本),在 env 中设置 {"ENABLE_TRAJECTORY": "false"} 即可。关闭后 Actor/Critic/Perf 等系统指标不受影响,仅链路追踪数据停止采集。

自定义指标

在代码中通过以下入口定义的 key-value 指标,会自动出现在控制台指标 Tab 的 trace/ 分组和Reward 分析页面:

入口

代码位置

控制台路径

reward_metrics

Reward(reward_metrics={"acc": 0.8})

trace/reward_metrics/{reward-name}/acc/{avg,sum}

rollout_metrics

AgentOutput(rollout_metrics={"latency": 1.2})

trace/rollout_metrics/latency/{avg,sum}

子维度评分

@sub_reward_func("toxicity") 内返回的 reward_metrics

合并到对应 Reward 函数的 reward_metrics

多 Reward 函数场景:通过 RewardFunctionComponent(name="reward-1") 为每个 Reward 函数设置唯一名称,在指标路径中区分(trace/reward_metrics/reward-1/...)。还可以通过 reward_metric_weight 细分子指标在综合评分中的权重。

控制台查看效果

训练开始后,在百炼控制台进入模型调优页面,点击任务名称进入详情,即可看到观测数据。以下说明"您在代码中做了什么 → 在控制台看到什么"的对应关系。

轨迹详情 — 对应 @observe_processor

轨迹 Tab 的轨迹详情子页面,可以看到每次 Rollout 的完整交互过程:

  • 轨迹列表:展示所有采样轨迹,支持按 Sample ID / 轨迹 ID / Epoch / Step 筛选

  • 对话过程:完整的多轮交互(user → assistant → tool_call → tool_result → assistant),直观看到模型的推理链

  • Reward 分数:每个 Step 显示对应的 Reward 分数和状态(SUCCESS/FAILED)

工具调用分析 — 对应 trace_tool / @observe_tool

轨迹 Tab 的工具调用分析子页面,可以查看:

  • 工具调用记录:工具名称、调用参数、返回结果和耗时

  • Tracing 子 Tab:每条轨迹的 Span 树,可展开查看每次工具调用和 LLM 请求的完整详情

典型用途:排查 Agent 的工具调用失败——哪个工具报错?参数传递是否正确?耗时是否过长?

Reward 分析 — 对应 reward_metrics

轨迹 Tab 的Reward 分析子页面,从三个维度评估训练效果:

  • Step 维度:选择训练 Step,查看该 Step 下所有样本的 Reward 聚合(平均分、成功率、趋势图),判断整体训练趋势

  • Sample 维度:选择 Sample ID,查看同一样本在不同轨迹中的 Reward 对比,发现问题样本

  • Trajectory 维度:查看单条轨迹的每个评分维度原始分,用于归因分析

指标 Tab — 对应 rollout_metrics / reward_metrics

指标 Tab 的 trace/ 分组,可以看到您在代码中定义的所有自定义指标的聚合曲线(avg / sum)。

除了 trace/ 分组(40 个指标,含用户自定义),以下系统分组也与 Agent 行为分析密切相关:

  • actor/(8 个):策略网络指标 — loss、entropy、KL 散度、grad_norm

  • critic/(12 个):价值网络指标 — rewards、advantages、score 的 max/mean/min

  • trajectory/(16 个):轨迹统计 — 回复长度、截断率、对话轮次

  • perf/(5 个):性能指标 — MFU、throughput、time_per_step、total_num_tokens

完整的 13 组 / 123 个训练指标说明请参见强化学习训练配置中的指标参考章节。

时间线 Tab

时间线 Tab 查看训练各阶段(Rollout 生成、Reward 计算、模型更新、Checkpoint 保存)的起止时间和持续时长,帮助判断是否存在性能瓶颈。

常见问题

Trace 数据在哪里查看?

在百炼控制台完成 ARMS 授权后:

  • 轨迹 Tab → 轨迹详情 / Reward 分析 / 工具调用分析

  • 指标 Tab → trace/ 分组

Trace 看不到怎么办?

按以下顺序排查:

  1. 确认 Runtime env没有设置 ENABLE_TRAJECTORY=false(默认开启,无需显式设置)

  2. 确认已在百炼控制台授权 ARMS 服务

  3. 检查 requirements.txt 是否包含 OpenTelemetry 相关依赖

  4. 检查 process() 方法是否添加了 @observe_processor 装饰器

开启 Tracing 对性能有影响吗?

Tracing 会增加少量存储和延迟开销。建议在开发调试阶段开启,正式大规模训练时如只需看训练指标(Actor/Critic/Perf 等)而无需轨迹详情,可以关闭:在 Runtime env 中设置 {"ENABLE_TRAJECTORY": "false"}。关闭后系统指标不受影响。

如何在指标中区分不同的 Reward 函数?

通过 RewardFunctionComponent(name="reward-1") 为每个 Reward 函数设置唯一名称。该名称会出现在指标路径中(trace/reward_metrics/reward-1/...),控制台会自动按名称分组展示。

如何关闭 Tracing 以节省成本?

在 Runtime 的 env 中设置 {"ENABLE_TRAJECTORY": "false"}。可以通过 FunctionComponentRuntime(env={"ENABLE_TRAJECTORY": "false"}) 或 YAML 中设置 env: {ENABLE_TRAJECTORY: false}