使用离线实验上报实验结果

更新时间:
复制 MD 格式

AgentLoop SDK(agentloop-sdk)提供离线实验能力,用于批量评估 LLM Agent 应用的表现。离线实验从已有数据集逐条读取输入,调用 Agent 获取结果,并将实验数据上报至 AgentLoop 平台,支持实验完成后自动触发平台侧评估。

前提条件

  • 创建智能体空间

  • 已创建数据集并导入数据。数据集中需包含至少一条记录,记录的字段名将作为实验输入的 key。

  • 已通过评估任务创建离线实验计划,并将实验计划关联至上述数据集。实验计划的类型需为离线类型(experiment_typeoffline)。

  • (可选)为实验计划配置好评估器。也可跳过平台配置,通过 SDK 代码的 EvaluatorConfig 指定评估器。

  • 已获取阿里云 AccessKey ID 和 AccessKey Secret,用于 SDK 认证。

步骤一:安装 SDK

通过 pip 安装 agentloop-sdk:

pip install agentloop-sdk

步骤二:配置凭证

SDK 使用阿里云 AccessKey 认证身份,支持以下两种方式。

方式一:环境变量(推荐)

设置以下环境变量后,SDK 自动读取,无需通过代码传入凭证信息:

export AGENTLOOP_AK="your-access-key-id"
export AGENTLOOP_SK="your-access-key-secret"

也支持标准阿里云环境变量 ALIBABA_CLOUD_ACCESS_KEY_IDALIBABA_CLOUD_ACCESS_KEY_SECRET

方式二:代码传参

创建 AgentLoopConfig 时直接传入 AccessKey:

config = AgentLoopConfig(
    agent_space="my-agent-space",
    experiment_plan_id="my_plan_id",
    region_id="cn-hangzhou",
    access_key_id="your-ak",
    access_key_secret="your-sk",
)

步骤三:配置实验参数

使用 AgentLoopConfig 类配置实验的核心参数。

from agentloop_sdk import AgentLoopConfig

config = AgentLoopConfig(
    agent_space="my-agent-space",
    experiment_plan_id="my_plan_id",
    region_id="cn-hangzhou",
)

AgentLoopConfig 支持以下参数:

参数

类型

必填

默认值

说明

agent_space

str

-

智能体空间名称。

experiment_plan_id

str

-

实验计划 ID,需为离线类型(experiment_typeoffline)。数据集从实验计划中自动解析。

region_id

str

-

阿里云地域 ID,例如 cn-hangzhou

experiment_name

str

experimentA

实验名称,用于标识本次实验。平台上显示的实验记录名称由计划名称加时间戳自动生成。

max_rows

int

1000

自动分页加载的最大行数。

access_key_id

str

-

阿里云 AccessKey ID,未设置环境变量时使用。

access_key_secret

str

-

阿里云 AccessKey Secret,未设置环境变量时使用。

步骤四:定义 Solution 函数

Solution 函数定义了如何调用 Agent 并获取结果。SDK 提供 HTTP 调用和命令行调用两种方式:HTTP 调用适用于通过 API 接口访问的远程 Agent,命令行调用适用于本地部署的 Agent。

Solution 函数的 task.input 是一个 dict,对应数据集中的一条记录,key 为数据集的列名。

通过 HTTP 调用 Agent

使用 http_solution_with 通过 HTTP 接口调用 Agent,支持普通 JSON 响应和 SSE 流式响应。

普通 JSON 响应

from agentloop_sdk import http_solution_with

solution = http_solution_with(
    url="https://your-agent-endpoint.com/invoke",
    headers={"key": "your-api-key"},
    body_builder=lambda task: {
        "input": task.input.get("input", ""),
        "stream": False,
    },
    timeout=60.0,
)

SSE 流式响应

当 Agent 端点返回 text/event-stream 格式时,SDK 自动检测并解析 SSE,提取所有 data: 行内容拼接为最终结果:

solution = http_solution_with(
    url="https://your-agent-endpoint.com/invoke",
    headers={"key": "your-api-key"},
    body_builder=lambda task: {
        "input": task.input.get("input", ""),
        "stream": True,
    },
    timeout=120.0,
)

如果 SSE 的每个 chunk 是 JSON 格式,可以通过 chunk_extractor 提取指定字段:

solution = http_solution_with(
    url="https://your-agent-endpoint.com/invoke",
    headers={"key": "your-api-key"},
    body_builder=lambda task: {
        "input": task.input.get("input", ""),
        "stream": True,
    },
    timeout=120.0,
    chunk_extractor=lambda chunk: chunk.get("output", ""),
)

http_solution_with 支持以下参数:

参数

类型

必填

默认值

说明

url

str

-

Agent 服务的请求 URL。

method

str

POST

HTTP 请求方法。

headers

dict

{"Content-Type": "application/json"}

请求头。

body_builder

Callable[[Task], Any]

-

根据 Task 动态构造请求体的函数。

json

Any

-

静态 JSON 请求体,与 body_builder 二选一。

timeout

float

60

请求超时时间,单位为秒。

chunk_extractor

Callable[[Any], str]

-

SSE 流式响应中,从每个 JSON chunk 提取指定字段的函数。

底层 API:http_solution

如果需要完全控制请求构造逻辑,可以使用底层 API http_solution

from agentloop_sdk import http_solution, HttpRequestSpec

def build_request(task):
    return HttpRequestSpec(
        url="https://your-agent-endpoint.com/invoke",
        headers={"key": "your-api-key"},
        json={"input": task.input.get("input", "")},
    )

solution = http_solution(build_request, default_timeout=60.0)

通过命令行调用 Agent

使用 command_solution_with 通过命令行调用本地部署的 Agent:

from agentloop_sdk import command_solution_with

# 静态命令
solution = command_solution_with(["python", "agent.py", "--input", "hello"])

# 根据 Task 动态构造命令
solution = command_solution_with(
    lambda task: ["python", "agent.py", "--input", task.input.get("input", "")],
    default_timeout=120.0,
)

command_solution_with 支持以下参数:

参数

类型

必填

默认值

说明

command

list 或 Callable[[Task], list]

-

静态命令列表或根据 Task 动态构造命令的函数。

default_timeout

float

-

命令执行超时时间,单位为秒。

底层 API:command_solution

如果需要更多控制,可以使用底层 API command_solution

from agentloop_sdk import command_solution

def build_command(task):
    return ["python", "agent.py", "--input", task.input.get("input", "")]

solution = command_solution(build_command, default_timeout=120.0, cwd="/path/to/agent")

command_solution 支持以下参数:

参数

类型

必填

默认值

说明

build_command

Callable[[Task], list]

-

根据 Task 构造命令列表的函数。

default_timeout

float

-

命令执行超时时间,单位为秒。

cwd

str

-

命令执行的工作目录。

步骤五:运行实验

配置完成后,调用 SDK 提供的运行函数执行实验。SDK 支持串行和并行两种执行方式。

串行执行

逐条执行数据集中的任务:

import asyncio
from agentloop_sdk import run_experiment

asyncio.run(run_experiment(
    config=config,
    solution_fn=solution,
    result_dir="./results",
    n_repeat=1,
))

并行执行(推荐)

通过多个 worker 并发执行任务,适合数据集较大的场景:

import asyncio
from agentloop_sdk import run_experiment_parallel

asyncio.run(run_experiment_parallel(
    config=config,
    solution_fn=solution,
    result_dir="./results",
    n_repeat=1,
    n_workers=4,
))

运行函数支持以下参数:

参数

类型

必填

默认值

说明

config

AgentLoopConfig

-

实验配置。

solution_fn

Callable

-

Solution 函数。

result_dir

str

./results

本地结果保存目录。

n_repeat

int

1

每条数据的重复执行次数。

n_workers

int

串行为 1,并行为 4

并发 worker 数量。

实验运行完成后,结果自动保存至 result_dir 指定的本地目录。如果配置了评估器,实验完成后将自动触发平台侧评估。

链路追踪

http_solutionhttp_solution_with 发送 HTTP 请求时,自动向请求头注入符合 W3C Trace Context 规范的 traceparent 头,并从响应头提取 eagleeye-traceIdtraceId,用于将实验请求与平台侧链路追踪关联。无需手动处理追踪头信息。

完整示例

以下示例展示了一个完整的离线实验流程:配置实验参数、定义评估器、通过 HTTP 调用 Agent,并以 4 个 worker 并行执行。

import asyncio
from agentloop_sdk import (
    AgentLoopConfig,
    EvaluatorConfig,
    http_solution_with,
    run_experiment_parallel,
)

async def main():
    config = AgentLoopConfig(
        agent_space="default-cn-hangzhou",
        experiment_plan_id="cc_plan_id",
        region_id="cn-hangzhou",
        experiment_name="Offline-Experiment-LocalAgent",
        experiment_config={"agent_name": "LocalAgent"},
        evaluators=[
            EvaluatorConfig(
                evaluator_ref="Builtin.agent_correctness",
                result_type="score",
                variable_mapping={
                    "input": "experiment_input",
                    "output": "experiment_output",
                    "expected_output": "dataset.expected_output",
                },
            ),
        ],
    )

    solution = http_solution_with(
        url="https://your-agent-endpoint.com/invoke",
        headers={"key": "your-api-key"},
        body_builder=lambda task: {
            "input": task.input.get("input", ""),
            "stream": True,
        },
        timeout=120.0,
        chunk_extractor=lambda chunk: chunk.get("output", ""),
    )

    await run_experiment_parallel(
        config=config,
        solution_fn=solution,
        n_workers=4,
    )

if __name__ == "__main__":
    asyncio.run(main())