结构化输出

本主题介绍如何使用 JSON 模式从代理工作流中返回经过模式验证的 JSON。代理可以使用完成任务所需的任何工具,在验证成功后,结果将包含符合您所定义模式的非结构化数据。

为您所需的结构定义一个 JSON 模式 (https://json-schema.org/understanding-json-schema/about),SDK 将根据该模式对模型的最终输出进行验证。

为何需要结构化输出?

默认情况下,代理返回自由格式的文本,这适用于对话场景,但在需要以编程方式使用输出时则不适用。结构化输出提供类型化的数据,您可以直接将其传递给应用程序逻辑、数据库或 UI 组件。

以分析代码库的代理为例。如果没有结构化输出,您将获得需要自行解析的自由格式文本。使用结构化输出后,您可以定义所需的数据形态,并获得可直接使用的类型化数据。

不使用结构化输出

使用结构化输出

This codebase uses Python and
TypeScript. It has 42 files
and the main entry point is...
{ "languages": ["Python", "TypeScript"],
  "file_count": 42,
  "entry_point": "src/main.ts" }

快速入门

将 JSON 模式传递给 outputFormat (TypeScript) 或 output_format (Python) 选项。当验证成功时,结果消息将包含一个 structured_output 字段,其中的数据与您的模式匹配。如果代理在重试后仍无法满足模式要求,SDK 将返回一个错误结果。

import { query } from "cortex-code-agent-sdk";

const schema = {
  type: "object",
  properties: {
    company_name: { type: "string" },
    founded_year: { type: "number" },
    headquarters: { type: "string" },
  },
  required: ["company_name"],
};

for await (const message of query({
  prompt: "Research Snowflake and provide key company information",
  options: {
    cwd: process.cwd(),
    outputFormat: { type: "json_schema", schema },
  },
})) {
  if (message.type === "result" && message.structured_output) {
    console.log(message.structured_output);
    // { company_name: "Snowflake", founded_year: 2012, headquarters: "Bozeman, MT" }
  }
}

使用 Zod 和 Pydantic 实现类型安全的模式

无需手动编写 JSON 模式,而是使用 Zod (https://zod.dev/) (TypeScript) 或 Pydantic (https://docs.pydantic.dev/latest/) (Python) 来定义您的模式。这些库会为您生成 JSON 模式,并允许您将响应解析为具有自动补全和类型检查功能的完全类型化对象。

import { z } from "zod";
import { query } from "cortex-code-agent-sdk";

const FeaturePlan = z.object({
  feature_name: z.string(),
  summary: z.string(),
  steps: z.array(
    z.object({
      step_number: z.number(),
      description: z.string(),
      estimated_complexity: z.enum(["low", "medium", "high"]),
    })
  ),
  risks: z.array(z.string()),
});

type FeaturePlan = z.infer<typeof FeaturePlan>;

const schema = z.toJSONSchema(FeaturePlan);

for await (const message of query({
  prompt: "Plan how to add dark mode support to a React app.",
  options: {
    cwd: process.cwd(),
    outputFormat: { type: "json_schema", schema },
  },
})) {
  if (message.type === "result" && message.structured_output) {
    const parsed = FeaturePlan.safeParse(message.structured_output);
    if (parsed.success) {
      const plan: FeaturePlan = parsed.data;
      console.log(`Feature: ${plan.feature_name}`);
      plan.steps.forEach((step) => {
        console.log(`${step.step_number}. [${step.estimated_complexity}] ${step.description}`);
      });
    }
  }
}

示例:TODO 跟踪代理

本示例展示了结合多步工具使用的结构化输出。该代理使用内置工具(Grep、Bash)在代码库中查找 TODO 注释,然后将结果以结构化数据的形式返回。像 author 这样的可选字段用于处理可能无法获取 git blame 信息的情况。

import { query } from "cortex-code-agent-sdk";

const todoSchema = {
  type: "object",
  properties: {
    todos: {
      type: "array",
      items: {
        type: "object",
        properties: {
          text: { type: "string" },
          file: { type: "string" },
          line: { type: "number" },
          author: { type: "string" },
          date: { type: "string" },
        },
        required: ["text", "file", "line"],
      },
    },
    total_count: { type: "number" },
  },
  required: ["todos", "total_count"],
};

for await (const message of query({
  prompt: "Find all TODO comments in this codebase and identify who added them",
  options: {
    cwd: process.cwd(),
    outputFormat: { type: "json_schema", schema: todoSchema },
  },
})) {
  if (message.type === "result" && message.structured_output) {
    const data = message.structured_output;
    console.log(`Found ${data.total_count} TODOs`);
    data.todos.forEach((todo) => {
      console.log(`${todo.file}:${todo.line} - ${todo.text}`);
      if (todo.author) {
        console.log(`  Added by ${todo.author} on ${todo.date}`);
      }
    });
  }
}

示例:SQL 查询结果

Cortex Code 具有内置的 Snowflake SQL 工具。您可以将这些工具与结构化输出结合使用,以获得类型化的查询结果:

import { query } from "cortex-code-agent-sdk";

const schema = {
  type: "object",
  properties: {
    top_customers: {
      type: "array",
      items: {
        type: "object",
        properties: {
          name: { type: "string" },
          total_revenue: { type: "number" },
          order_count: { type: "number" },
        },
        required: ["name", "total_revenue", "order_count"],
      },
    },
    query_used: { type: "string" },
  },
  required: ["top_customers", "query_used"],
};

for await (const message of query({
  prompt: "Find the top 5 customers by revenue from the ORDERS table",
  options: {
    cwd: process.cwd(),
    connection: "my-connection",
    outputFormat: { type: "json_schema", schema },
  },
})) {
  if (message.type === "result" && message.structured_output) {
    const { top_customers, query_used } = message.structured_output;
    console.log(`Query: ${query_used}`);
    top_customers.forEach((c) => {
      console.log(`${c.name}: $${c.total_revenue} (${c.order_count} orders)`);
    });
  }
}

输出格式配置

outputFormat (TypeScript) 或 output_format (Python) 选项接受一个包含以下字段的对象:

字段

描述

type

"json_schema"

必填。仅支持 json_schema

schema

JSON 模式对象

定义输出结构。使用 Zod 的 z.toJSONSchema() 或 Pydantic 的 .model_json_schema() 生成。

支持标准的 JSON 模式功能:所有基本类型(objectarraystringnumberbooleannull)、enumconstrequired、嵌套对象以及 $ref 定义。

错误处理

当代理无法生成与您的模式匹配的有效 JSON 时,结构化输出的生成可能会失败。此时,结果消息将包含一个 subtype,用于指示出错原因:

子类型

含义

success

已成功生成并验证输出

error_max_structured_output_retries

代理在多次尝试后仍无法生成有效输出

for await (const msg of query({
  prompt: "Extract contact info from the document",
  options: {
    cwd: process.cwd(),
    outputFormat: { type: "json_schema", schema: contactSchema },
  },
})) {
  if (msg.type === "result") {
    if (msg.subtype === "success" && msg.structured_output) {
      console.log(msg.structured_output);
    } else if (msg.subtype === "error_max_structured_output_retries") {
      console.error("Could not produce valid output");
    }
  }
}

小技巧

避免错误的技巧:

  • 保持模式聚焦。 深层嵌套且包含多个必填字段的模式更难满足。从简单开始,根据需要逐步增加复杂度。

  • 使模式与任务匹配。 如果任务可能不包含模式所需的全部信息,请将这些字段设为可选。

  • 使用清晰的提示。 模糊的提示会让代理更难知道应该生成什么样的输出。