可易

将智能体追踪转换为评估数据集

可易团队阅读约 9 分钟

说明如何在 Microsoft Foundry 中把生产追踪做成带版本的评估数据集:用智能采样控制规模,脱敏私密内容,并对照该数据集运行评估。

目录

本文介绍基于追踪(trace)的数据集生成。关于全部数据集准备选项和标准字段名,请参阅《Microsoft Foundry 中的评估数据集》(Evaluation datasets in Microsoft Foundry)和《评估数据集模式》(Evaluation dataset schema)。

生产追踪是你的智能体(agent)面对真实用户时行为最有代表性的来源。本文说明如何使用 Microsoft Foundry 中的数据生成,把智能体已经发出的追踪变成一份经过整理、带版本的数据集,供你据此评估。设置 max_samples 以使用智能采样选出一个有代表性的子集,省略它则处理匹配的追踪而不采样,或者提供特定的追踪 ID。

把追踪转换成数据集,闭合了智能体改进闭环:你通过追踪捕获的生产行为,成为你用来度量并改进质量的测试集。

基于追踪的生成与合成生成是互补的:生产追踪反映真实用户行为,而合成生成覆盖发布前场景和边界情况。如果你的智能体还没有生产追踪,或者你想把覆盖范围扩展到生产流量尚未触及的部分,请参阅《生成合成评估数据集》(Generate a synthetic evaluation dataset)。

智能采样

当你设置 max_samples 时,服务并不只是从选定窗口中随机抽样。它使用智能采样自动选出一个有代表性的子集。你不配置各个过滤阶段;服务为你处理选择。智能采样执行以下任务:

  • 滤除无价值的追踪,例如单字符消息以及其他低意图流量,这些不会增加评估信号。
  • 使用 MinHash 选出多样且有代表性的样本,使结果覆盖智能体的场景范围,而不是过度集中于频繁出现、几乎相同的提示词。

这一过程之所以重要,是因为评估成本高,而大多数原始追踪几乎不增加信号。一套有代表性的集合,比评估全部内容能以更低成本产生更好的信号。智能采样使追踪选择在生产规模上变得可行,因此你无需编写自定义过滤或去重代码,就能得到可用于评估的数据集。

智能采样在 Foundry 的三种体验中使用同一套追踪选择算法:

  • 从追踪创建数据集——本文介绍这一项。
  • 创建基于追踪的评估——用所选时间范围内的代表性样本,对照已有追踪进行评估。
  • 从生产追踪生成评分细则评估器(rubric evaluator)——同一采样算法选出用作输入的追踪。

将 max_samples 设为 1 到 1,000,以限制生成的数据集规模。省略 max_samples 即关闭采样。要选择特定追踪,在追踪来源的 trace_ids 中提供一份非空的、不含空白追踪 ID 的列表。

私密内容脱敏与采样是分开的。默认情况下,服务会从追踪中脱敏私密内容。仅当你的隐私、保留和数据集访问要求允许生成的数据集保留这些内容时,才把 redact_private_content 设为 false。

先决条件

  • Python SDK 2.8.0 或更高版本:pip install "azure-ai-projects>=2.8.0"(仅 SDK 路径)
  • JavaScript SDK 2.8.0 或更高版本:npm install "@azure/ai-projects@^2.8.0" @azure/identity
  • 一个 Microsoft Foundry 项目终结点 URL,格式为 https://<your-resource>.services.ai.azure.com/api/projects/<your-project>
  • 对项目拥有 Foundry User 角色或更高角色。
  • 为已部署且会发出追踪的智能体设置追踪。Foundry 智能体自动发出追踪,经过 OpenTelemetry 插桩的第三方智能体也受支持。设置步骤请参阅《为你的智能体设置追踪》(Set up tracing for your agent)。
  • 项目的托管标识必须对所连接的 Application Insights 资源拥有 Reader 角色,服务才能查询追踪数据。如果存储你的追踪的表受到保护,还要分配 Privileged Monitoring Data Reader 角色。
  • 有关全部基于追踪的评估角色要求,请参阅《为评估工作流设置权限》(Set up permissions for evaluation workflows)。
  • 一个受支持的区域。列表请参阅《数据生成的受支持区域》(Supported regions for data generation)。

从追踪生成评估数据集(门户)

你可以在门户中直接从追踪创建数据集,无需编写代码。这是把近期生产流量变成评估数据集的最快方法。

  1. 在门户中,打开 Data Generation 选项卡。选择 Create dataset > From traces。
  2. 在 Create from traces 对话框中,配置数据集:
  • Agent:选择你要使用其追踪的已部署智能体。
  • Dataset:选择现有数据集或创建数据集。
  • Create dataset for:设为 Evaluation。
  • Date range:选择拉取追踪的窗口,例如最近一天或最近七天。
  • Sampling:启用采样,以选择匹配追踪的代表性子集。
  • Maximum samples:启用采样后,设置 1 到 1,000 行的上限。
  1. 选择 Create 提交作业。数据集生成作为后台作业运行。你可以在 Data Generation 选项卡上跟踪其状态。
  2. 作业完成后,转到 Data 选项卡并选择该数据集,预览生成的行,包括每一行的 description、query 和 response。你可以在那里下载或删除数据集。
  3. 使用该数据集。已完成的评估数据生成作业会直接链接到启动一次评估运行。

手动把追踪加入数据集(门户)

要整理特定的智能体交互,从追踪表中选择追踪,并把它们加入新的或现有的数据集。

  1. 在 Foundry 门户中,打开你的项目和智能体,然后选择 Traces/Trace view。
  2. 在追踪表中,使用可用筛选器缩小追踪列表,并选择你要添加的追踪。
  3. 选择 Add to dataset。
  4. 选择是创建新数据集,还是把追踪加入现有数据集:对于新数据集,输入所需的数据集详情。对于现有数据集,选择你要更新的数据集。
  5. 审阅并选择 Create,以添加追踪。
  6. 数据集就绪后,会出现数据集创建通知。选择通知中的数据集链接,打开数据集并查看已添加的行。

从追踪生成评估数据集(SDK)

用真实流量驱动你已部署的智能体,然后用这些对话构建评估数据集。定义时间窗口或显式追踪 ID,指向你的智能体,配置采样和私密内容脱敏,选择如何写入输出数据集,并提交作业。

首先,使用你的项目终结点和 DefaultAzureCredential 创建一个 AIProjectClient。你可以在 project_client.datasets 下找到全部数据生成操作。

Python

from azure.identity import DefaultAzureCredential
from azure.ai.projects import AIProjectClient

credential = DefaultAzureCredential()
project_client = AIProjectClient(
    endpoint="https://<your-resource>.services.ai.azure.com/api/projects/<your-project>",
    credential=credential,
)

JavaScript/TypeScript

npm install "@azure/ai-projects@^2.8.0" @azure/identity
import { DefaultAzureCredential } from "@azure/identity";
import { AIProjectClient } from "@azure/ai-projects";

const projectEndpoint =
  "https://<your-resource>.services.ai.azure.com/api/projects/<your-project>";
const projectClient = new AIProjectClient(
  projectEndpoint,
  new DefaultAzureCredential(),
);

使用 @azure/ai-projects 2.8.0 或更高版本。通过 projectClient.datasets 访问数据生成操作。

以下示例在 Python 和 JavaScript/TypeScript 中提交同一个时间窗口追踪来源。

参考:AIProjectClient 类

注意

Application Insights 摄取 span 需要 30–90 秒。如果你在捕获流量后过快提交作业,作业会针对一个空窗口运行,并且不会产生样本。

Python

import time
from datetime import datetime, timedelta, timezone

from azure.ai.projects.models import (
    DataGenerationJobOutputWriteMode,
    DatasetDataGenerationJobOutput,
    EvaluationDataGenerationJobInputs,
    EvaluationDataGenerationJobOutputConfiguration,
    TracesDataGenerationJobConfiguration,
    TracesDataGenerationJobSource,
)

AGENT_NAME = "retail-agent"
poll_interval_seconds = 10

# 1. Record the window around your traffic.
end_time = datetime.now(tz=timezone.utc)
start_time = end_time - timedelta(days=7)

# 2. Define an evaluation job. The class sets scenario="evaluation".
job = EvaluationDataGenerationJobInputs(
    name="retail-agent-eval-set",
    sources=[
        TracesDataGenerationJobSource(
            description=(
                "Application Insights conversation traces for the Foundry "
                "agent."
            ),
            agent_name=AGENT_NAME,
            start_time=start_time,
            end_time=end_time,
            # agent_version="3",  # Pin to a specific version.
            # trace_ids=["trace-id-1", "trace-id-2"],  # Select exact traces.
        ),
    ],
    generation_configuration=TracesDataGenerationJobConfiguration(
        # Omit max_samples to turn off intelligent sampling.
        max_samples=100,
        # Private content is redacted by default.
        redact_private_content=True,
    ),
    output_configuration=EvaluationDataGenerationJobOutputConfiguration(
        name="retail-agent-eval-set",
        description="Representative production traces for agent evaluation.",
        tags={"source": "production-traces"},
        write_mode=DataGenerationJobOutputWriteMode.OVERWRITE,
    ),
)

# 3. Submit and wait for completion.
poller = project_client.datasets.begin_create_generation_job(job=job)
while not poller.done():
    print(f"\tstatus=`{poller.status()}`")
    time.sleep(poll_interval_seconds)
result = poller.result()

# 4. Resolve the generated dataset.
output_name = ""
output_version = ""
for output in (result.outputs if result is not None else None) or []:
    if isinstance(output, DatasetDataGenerationJobOutput):
        output_name = output.name or ""
        output_version = output.version or ""
        break

if not output_name or not output_version:
    raise RuntimeError("The data generation job didn't return a dataset output.")

dataset = project_client.datasets.get(name=output_name, version=output_version)
print(f"Generated dataset: {dataset.name} v{dataset.version} (id: {dataset.id})")
if result is not None and result.generated_samples is not None:
    print(f"Generated samples: {result.generated_samples}")

JavaScript/TypeScript

const agentName = "retail-agent";
const jobName = "retail-agent-eval-set";

// 1. Record the window around your traffic.
const endTime = new Date();
endTime.setMilliseconds(0);
const startTime = new Date(endTime.getTime() - 7 * 24 * 60 * 60 * 1000);

// 2. Define and submit the evaluation job.
const generationPoller = projectClient.datasets.createGenerationJob({
  name: jobName,
  scenario: "evaluation",
  sources: [
    {
      type: "traces",
      description:
        "Application Insights conversation traces for the Foundry agent.",
      agent_name: agentName,
      start_time: startTime,
      end_time: endTime,
      // agent_version: "3", // Pin to a specific version.
      // trace_ids: ["trace-id-1", "trace-id-2"], // Select exact traces.
    },
  ],
  generation_configuration: {
    type: "traces",
    // Omit max_samples to turn off intelligent sampling.
    max_samples: 100,
    // Private content is redacted by default.
    redact_private_content: true,
  },
  output_configuration: {
    name: jobName,
    description: "Representative production traces for agent evaluation.",
    tags: { source: "production-traces" },
    write_mode: "overwrite",
  },
});

await generationPoller.submitted();
const result = await generationPoller.pollUntilDone();

// 3. Resolve the generated dataset.
const datasetOutput = result.outputs?.find((output) => output.type === "dataset");
if (
  !datasetOutput ||
  !("name" in datasetOutput) ||
  !("version" in datasetOutput) ||
  !datasetOutput.name ||
  !datasetOutput.version
) {
  throw new Error("The data generation job didn't return a dataset output.");
}

const dataset = await projectClient.datasets.get(
  datasetOutput.name,
  datasetOutput.version,
);
console.log(
  `Generated dataset: ${dataset.name} v${dataset.version} (id: ${dataset.id})`,
);
if (result.generated_samples !== undefined) {
  console.log(`Generated samples: ${result.generated_samples}`);
}

作业会在你的项目中注册一份带版本的数据集。当你设置 max_samples 时,行数受该值限制,但如果窗口在智能采样之后没有足够多的、彼此不同的高质量追踪,行数可能会更少。

默认写入模式是 Python 的 DataGenerationJobOutputWriteMode.OVERWRITE,或 JavaScript/TypeScript 的 "overwrite"。它只用新生成的行创建下一个数据集版本。要把新行与最新的现有数据集版本合并,并对追踪行去重,把 write_mode 设为 Python 的 DataGenerationJobOutputWriteMode.MERGE,或 JavaScript/TypeScript 的 "merge"。两种模式都不会就地修改已有的数据集版本。

无论你是从门户还是从 SDK 创建数据集,都可以在 Data 选项卡上预览,在评估前检查生成的行。你也可以在那里下载或删除它。

对照生成的数据集运行评估

数据集存在之后,对照它评估你的智能体。生成的数据集使用标准的 query-response 模式,因此可以直接用于评估 API。把数据集的名称和版本(或其 id)传给你的评估运行。

完整评估流程,包括选择评估器和审阅结果,请参阅《评估智能体目标》(Evaluate an agent target)。一个从追踪生成评估数据集的完整可运行示例,请参阅 GitHub 上的 sample_dataset_generation_job_traces_for_evaluation.py。

管理数据生成作业

使用 project_client.datasets API 列出、检查、取消和删除数据生成作业。

Python

# List recent evaluation jobs.
for job in project_client.datasets.list_generation_jobs(
    limit=20,
    order="desc",
):
    if job.scenario == "evaluation":
        print(f"{job.id}  {job.status:<12}  {job.name}")

# Get a job.
job = project_client.datasets.get_generation_job(job_id="job_...")

# Cancel a running job.
project_client.datasets.cancel_generation_job(job_id="job_...")

# Delete a job record.
project_client.datasets.delete_generation_job(job_id="job_...")

JavaScript/TypeScript

// List recent evaluation jobs.
for await (const job of projectClient.datasets.listGenerationJobs({
  limit: 20,
})) {
  if (job.scenario === "evaluation") {
    console.log(`${job.id}  ${job.status}  ${job.scenario}  ${job.name}`);
  }
}

// Get a job.
const job = await projectClient.datasets.getGenerationJob("job_...");

// Cancel a running job.
await projectClient.datasets.cancelGenerationJob("job_...");

// Delete a job record.
await projectClient.datasets.deleteGenerationJob("job_...");

参考:datasets.listGenerationJobs

限制

  • 连接到你的 Foundry 项目的 Application Insights 资源必须允许公共网络访问,服务才能查询 Application Insights 数据。如果 Application Insights 位于 Azure Monitor Private Link Scope 之后,请确保已启用公共网络查询访问。
  • 如果你的 Foundry 项目连接到你自己的存储账户,该存储账户必须启用公共网络访问,数据集才能成功创建。

最佳实践

  • 为追踪作业固定 agent_version。不固定时,作业会混入每个活跃版本的 span,其中可能包含过时行为,并削弱你的评估信号。
  • 在采样作业之后检查 generated_samples。当你设置 max_samples 时,它是上限,不是保证。智能采样会移除重复项和低质量追踪,因此你得到的行数可能少于上限。
  • 使用有代表性的时间窗口。七天窗口通常能捕获足够的多样性。围绕已知事件的窄窗口,适合用来构建有针对性的回归集。

PRIMARY_SOURCE: https://learn.microsoft.com/en-us/azure/foundry/observability/how-to/traces-to-dataset

  • #生产追踪
  • #评估数据集
  • #智能采样
  • #Foundry