types.ts ファイルを作成し、Output SDK(外部データ出力用のツール群)ワークフロー向けに Zod スキーマ(データ形式の定義・検証ツール)を設定します。 次のような場合に使用: 入出力スキーマを定義する、型定義を作成する、スキーマ関連のエラーを修正する場合。
Create types.ts files with Zod schemas for Output SDK workflows. Use when defining input/output schemas, creating type definitions, or fixing schema-related errors.
このスキルは、Output SDK ワークフロー向けの types.ts ファイル作成方法をまとめています。このファイルに含めるのは、入出力のデータ検証を行う Zod スキーマ(スキーマとは、データの形や内容を定義するルールのこと)と、対応する TypeScript の型定義です。
z は 必ず @outputai/core からインポートしてください。zod から直接インポートしてはいけません:
// 正しい
import { z } from '@outputai/core';
// 間違い - 実行時エラーが発生
import { z } from 'zod';
関連スキル: インポートの問題をトラブルシューティングする場合は output-error-zod-import を参照してください。
import { z } from '@outputai/core';
// 1. ワークフロー入力スキーマ
export const WorkflowInputSchema = z.object( {
// 入力フィールドを定義
} );
// 2. ワークフロー出力型
export type WorkflowInput = z.infer<typeof WorkflowInputSchema>;
export type WorkflowOutput = /* 出力型 */;
// 3. ステップスキーマ(各ステップごと)
export const StepNameInputSchema = z.object( {
// ステップ入力フィールド
} );
export const StepNameOutputSchema = z.object( {
// ステップ出力フィールド
} );
// 4. 型のエクスポート
export type StepNameInput = z.infer<typeof StepNameInputSchema>;
export type StepNameOutput = z.infer<typeof StepNameOutputSchema>;
aiSdk.Output.object() に渡すスキーマは、LLM プロバイダー(AI サービス提供者)にツール定義として送信されます。Anthropic は Zod が生成する JSON スキーマの制約のいくつかを拒否します。これを間違えると、実行時エラーが発生します。
z.number() の .min()、.max() は minimum/maximum を生成し、Anthropic により拒否されます。z.array() の .min()、.max()、.length() は minItems/maxItems を生成し、Anthropic は minItems が 0 または 1 の場合のみサポートします。.length(3) や .min(2) などの値は拒否されます。.describe() を使用する.describe() は LLM 出力の品質を導く主要な仕組みです。LLM プロバイダーはスキーマのフィールド名と説明を使用して、各フィールドに何を含めるべきかを判断します。意図を明確に伝える具体的な説明を書いてください。
重要: .describe() は、サポートされていない制約とプロンプトベースの形式指示の両方に置き換わります。スキーマをプロンプトで説明してはいけません。スキーマは自動的にプロバイダーに送信され、重複するとパフォーマンスが低下し、ズレのリスクが生じます。詳細は output-dev-prompt-file を参照してください。
// LLM 出力スキーマ(aiSdk.Output.object() 経由でプロバイダーに送信)-- .describe() のみ
const llmOutputSchema = z.object( {
score: z.number().describe( 'スコア 0-100' ),
confidence: z.number().describe( '信頼度 0-1' ),
predictions: z.array( predictionSchema ).describe( '予測結果ちょうど 3 つ' )
} );
// ワークフロー・ステップ検証用スキーマ(Zod のみ、LLM に送信されない)-- .min()/.max()/.length() 可
const workflowOutputSchema = z.object( {
score: z.number().min( 0 ).max( 100 ).describe( 'スコア 0-100' ),
confidence: z.number().min( 0 ).max( 1 ).describe( '信頼度 0-1' ),
predictions: z.array( predictionSchema ).length( 3 ).describe( '予測結果ちょうど 3 つ' )
} );
| 文脈 | .min()/.max()/.length() |
.describe() |
|---|---|---|
aiSdk.Output.object() に渡すスキーマ |
不可(数値または配列) | 推奨 |
ステップの inputSchema / outputSchema |
可 | 任意 |
ワークフローの inputSchema / outputSchema |
可 | 任意 |
評価関数の outputSchema |
可 | 任意 |
aiSdk.Output.object() で使用するすべてのスキーマを types.ts で定義し、ステップ関数内でインポートしてください。インラインで定義してはいけません。重複が発生し、上記の制約に従っているか検証しづらくなります。
import { z } from '@outputai/core';
// 文字列
const stringField = z.string();
const optionalString = z.string().optional();
const stringWithDefault = z.string().default( 'デフォルト値' );
const describedString = z.string().describe( 'フィールドの説明' );
// 数値
const numberField = z.number();
const integerField = z.number().int();
const rangedNumber = z.number().min( 1 ).max( 100 ); // 実行時のみ — aiSdk.Output.object() スキーマでは非推奨
// 真偽値
const booleanField = z.boolean();
const defaultBoolean = z.boolean().default( false );
// 列挙型(決まった複数の値から選ぶ型)
const enumField = z.enum( [ 'option1', 'option2', 'option3' ] );
const enumWithDefault = z.enum( [ 'small', 'medium', 'large' ] ).default( 'medium' );
import { z } from '@outputai/core';
// 配列
const stringArray = z.array( z.string() );
const objectArray = z.array( z.object( { id: z.string(), name: z.string() } ) );
// オブジェクト
const nestedObject = z.object( {
user: z.object( {
id: z.string(),
email: z.string().email()
} ),
settings: z.object( {
notifications: z.boolean()
} )
} );
// 合併型(複数の型のどれか)
const flexibleInput = z.union( [
z.string(),
z.array( z.string() )
] );
// キーと値のペア
const keyValueMap = z.record( z.string(), z.number() );
import { z } from '@outputai/core';
// 文字列の検証
const emailField = z.string().email();
const urlField = z.string().url();
const uuidField = z.string().uuid();
const minLengthString = z.string().min( 1 );
const maxLengthString = z.string().max( 1000 );
// 数値の検証
const positiveNumber = z.number().positive();
const nonNegativeNumber = z.number().nonnegative();
const percentageNumber = z.number().min( 0 ).max( 100 );
// 配列の検証(実行時のみ — aiSdk.Output.object() スキーマでは非推奨)
const nonEmptyArray = z.array( z.string() ).min( 1 );
const limitedArray = z.array( z.string() ).max( 10 );
const fixedLengthArray = z.array( z.string() ).length( 3 );
実際のワークフロー(image_infographic_nano)に基づいています:
import { z } from '@outputai/core';
// ============================================
// ワークフロースキーマ
// ============================================
export const WorkflowInputSchema = z.object( {
content: z.string().describe( '画像アイデア生成の元となるテキスト' ),
mode: z.enum( [ 'infographic' ] ).default( 'infographic' ).describe( '生成する画像のタイプ' ),
colorPalette: z.string().optional().describe( '画像の色合いに関する希望' ),
artDirection: z.string().optional().describe( 'アート方向やスタイルの希望' ),
numberOfIdeas: z.number().min( 1 ).max( 10 ).default( 1 ).describe( '生成する画像コンセプト数' ),
referenceImageUrls: z.union( [
z.string(),
z.array( z.string() )
] ).optional().describe( 'スタイル参考用の画像 URL(最大 14 個)' ),
aspectRatio: z.enum( [ '1:1', '16:9', '9:16', '4:3', '3:4' ] ).default( '1:1' ).describe( '生成画像のアスペクト比' ),
resolution: z.enum( [ '1K', '2K', '4K' ] ).default( '1K' ).describe( '生成画像の解像度' ),
numberOfGenerations: z.number().min( 1 ).max( 10 ).default( 1 ).describe( 'コンセプトあたりの生成画像数' ),
storageNamespace: z.string().optional().describe( '画像保存先の S3 フォルダパス' )
} );
export type WorkflowInput = z.infer<typeof WorkflowInputSchema>;
export type WorkflowOutput = string[];
// ============================================
// ステップスキーマ
// ============================================
export const ValidateReferenceImagesInputSchema = z.object( {
referenceImageUrls: z.array( z.string() ).optional()
} );
export const GenerateImageIdeasInputSchema = z.object( {
content: z.string(),
numberOfIdeas: z.number(),
colorPalette: z.string().optional(),
artDirection: z.string().optional()
} );
export const GenerateImagesInputSchema = z.object( {
input: z.object( {
referenceImageUrls: z.union( [ z.string(), z.array( z.string() ) ] ).optional(),
aspectRatio: z.enum( [ '1:1', '16:9', '9:16', '4:3', '3:4' ] ),
resolution: z.enum( [ '1K', '2K', '4K' ] ),
numberOfGenerations: z.number(),
storageNamespace: z.string().optional()
} ),
prompt: z.string()
} );
// LLM レスポンス検証用スキーマ
export const ImageIdeasSchema = z.object( {
ideas: z.array( z.string() ).describe( 'Gemini 用の詳細な画像プロンプト配列' )
} );
// ============================================
// 型のエクスポート
// ============================================
export type ValidateReferenceImagesInput = z.infer<typeof ValidateReferenceImagesInputSchema>;
export type GenerateImageIdeasInput = z.infer<typeof GenerateImageIdeasInputSchema>;
export type GenerateImagesInput = z.infer<typeof GenerateImagesInputSchema>;
export type ImageIdeas = z.infer<typeof ImageIdeasSchema>;
// 良い例 - ドキュメントとエラーメッセージに役立つ
z.string().describe( 'お知らせ用のユーザーメールアドレス' )
// 避けるべき例 - コンテキストがない
z.string()
// 良い例 - オプショナルフィールドなしでワークフローが動作
numberOfIdeas: z.number().min( 1 ).max( 10 ).default( 1 )
// 避けるべき例 - ユーザーがすべてのフィールドを指定する必要がある
numberOfIdeas: z.number().min( 1 ).max( 10 )
// ワークフロー入力スキーマ(ユーザーが指定するもの)
export const WorkflowInputSchema = z.object( { ... } );
// ステップスキーマ(内部でのデータ形式)
export const StepNameInputSchema = z.object( { ... } );
// 実行時の検証用にスキーマをエクスポート
export const UserSchema = z.object( { ... } );
// TypeScript の型チェック用に型をエクスポート
export type User = z.infer<typeof UserSchema>;
z が @outputai/core からインポートされている.describe() があるThis skill documents how to create types.ts files for Output SDK workflows. These files contain Zod schemas for input/output validation and their corresponding TypeScript types.
ALWAYS import z from @outputai/core, NEVER from zod directly:
// CORRECT
import { z } from '@outputai/core';
// WRONG - will cause runtime errors
import { z } from 'zod';
Related Skill: output-error-zod-import for troubleshooting import issues
import { z } from '@outputai/core';
// 1. Workflow Input Schema
export const WorkflowInputSchema = z.object( {
// Define input fields
} );
// 2. Workflow Output Type
export type WorkflowInput = z.infer<typeof WorkflowInputSchema>;
export type WorkflowOutput = /* output type */;
// 3. Step Schemas (for each step)
export const StepNameInputSchema = z.object( {
// Step input fields
} );
export const StepNameOutputSchema = z.object( {
// Step output fields
} );
// 4. Type Exports
export type StepNameInput = z.infer<typeof StepNameInputSchema>;
export type StepNameOutput = z.infer<typeof StepNameOutputSchema>;
Schemas passed to aiSdk.Output.object() are sent to LLM providers as tool definitions. Anthropic rejects several JSON Schema constraints that Zod methods produce. Getting this wrong causes runtime errors.
.min(), .max() on z.number() produce minimum/maximum -- rejected by Anthropic..min(), .max(), .length() on z.array() produce minItems/maxItems -- Anthropic only supports minItems of 0 or 1. Values like .length( 3 ) or .min( 2 ) will be rejected..describe() Instead.describe() is the primary mechanism for guiding LLM output quality. LLM providers use field names and descriptions from the schema to understand what each field should contain. Write clear, specific descriptions that communicate your intent.
Important: .describe() replaces both unsupported constraints AND prompt-based format instructions. Do not also describe the schema in the prompt -- the schema is sent to the provider automatically, and duplicating it reduces performance and creates drift risk. See output-dev-prompt-file for details.
// LLM output schema (sent to provider via aiSdk.Output.object()) -- .describe() ONLY
const llmOutputSchema = z.object( {
score: z.number().describe( 'Quality score 0-100' ),
confidence: z.number().describe( 'Confidence 0-1' ),
predictions: z.array( predictionSchema ).describe( 'Exactly 3 predictions' )
} );
// Workflow/step validation schema (Zod-only, NOT sent to LLM) -- .min()/.max()/.length() OK
const workflowOutputSchema = z.object( {
score: z.number().min( 0 ).max( 100 ).describe( 'Quality score 0-100' ),
confidence: z.number().min( 0 ).max( 1 ).describe( 'Confidence 0-1' ),
predictions: z.array( predictionSchema ).length( 3 ).describe( 'Exactly 3 predictions' )
} );
| Context | .min()/.max()/.length() |
.describe() |
|---|---|---|
Schema passed to aiSdk.Output.object() |
No (numbers or arrays) | Yes |
inputSchema / outputSchema on steps |
OK | Optional |
inputSchema / outputSchema on workflows |
OK | Optional |
outputSchema on evaluators |
OK | Optional |
Define all schemas used in aiSdk.Output.object() in types.ts and import them in step functions. Never define them inline -- this causes duplication and makes it harder to verify they follow the constraints above.
import { z } from '@outputai/core';
// Strings
const stringField = z.string();
const optionalString = z.string().optional();
const stringWithDefault = z.string().default( 'default value' );
const describedString = z.string().describe( 'Field description' );
// Numbers
const numberField = z.number();
const integerField = z.number().int();
const rangedNumber = z.number().min( 1 ).max( 100 ); // runtime only — NOT safe for aiSdk.Output.object() schemas
// Booleans
const booleanField = z.boolean();
const defaultBoolean = z.boolean().default( false );
// Enums
const enumField = z.enum( [ 'option1', 'option2', 'option3' ] );
const enumWithDefault = z.enum( [ 'small', 'medium', 'large' ] ).default( 'medium' );
import { z } from '@outputai/core';
// Arrays
const stringArray = z.array( z.string() );
const objectArray = z.array( z.object( { id: z.string(), name: z.string() } ) );
// Objects
const nestedObject = z.object( {
user: z.object( {
id: z.string(),
email: z.string().email()
} ),
settings: z.object( {
notifications: z.boolean()
} )
} );
// Union Types
const flexibleInput = z.union( [
z.string(),
z.array( z.string() )
] );
// Records
const keyValueMap = z.record( z.string(), z.number() );
import { z } from '@outputai/core';
// String Validations
const emailField = z.string().email();
const urlField = z.string().url();
const uuidField = z.string().uuid();
const minLengthString = z.string().min( 1 );
const maxLengthString = z.string().max( 1000 );
// Number Validations
const positiveNumber = z.number().positive();
const nonNegativeNumber = z.number().nonnegative();
const percentageNumber = z.number().min( 0 ).max( 100 );
// Array Validations (runtime only — NOT safe for aiSdk.Output.object() schemas)
const nonEmptyArray = z.array( z.string() ).min( 1 );
const limitedArray = z.array( z.string() ).max( 10 );
const fixedLengthArray = z.array( z.string() ).length( 3 );
Based on a real workflow (image_infographic_nano):
import { z } from '@outputai/core';
// ============================================
// Workflow Schemas
// ============================================
export const WorkflowInputSchema = z.object( {
content: z.string().describe( 'Text content to generate image ideas from' ),
mode: z.enum( [ 'infographic' ] ).default( 'infographic' ).describe( 'Type of image to generate' ),
colorPalette: z.string().optional().describe( 'Color palette preference for the images' ),
artDirection: z.string().optional().describe( 'Art direction or style preference' ),
numberOfIdeas: z.number().min( 1 ).max( 10 ).default( 1 ).describe( 'Number of image concepts to generate' ),
referenceImageUrls: z.union( [
z.string(),
z.array( z.string() )
] ).optional().describe( 'Reference image URLs for style guidance (max 14)' ),
aspectRatio: z.enum( [ '1:1', '16:9', '9:16', '4:3', '3:4' ] ).default( '1:1' ).describe( 'Aspect ratio for generated images' ),
resolution: z.enum( [ '1K', '2K', '4K' ] ).default( '1K' ).describe( 'Resolution for generated images' ),
numberOfGenerations: z.number().min( 1 ).max( 10 ).default( 1 ).describe( 'Number of images to generate per concept' ),
storageNamespace: z.string().optional().describe( 'S3 folder path for storing images' )
} );
export type WorkflowInput = z.infer<typeof WorkflowInputSchema>;
export type WorkflowOutput = string[];
// ============================================
// Step Schemas
// ============================================
export const ValidateReferenceImagesInputSchema = z.object( {
referenceImageUrls: z.array( z.string() ).optional()
} );
export const GenerateImageIdeasInputSchema = z.object( {
content: z.string(),
numberOfIdeas: z.number(),
colorPalette: z.string().optional(),
artDirection: z.string().optional()
} );
export const GenerateImagesInputSchema = z.object( {
input: z.object( {
referenceImageUrls: z.union( [ z.string(), z.array( z.string() ) ] ).optional(),
aspectRatio: z.enum( [ '1:1', '16:9', '9:16', '4:3', '3:4' ] ),
resolution: z.enum( [ '1K', '2K', '4K' ] ),
numberOfGenerations: z.number(),
storageNamespace: z.string().optional()
} ),
prompt: z.string()
} );
// Schema for LLM response validation
export const ImageIdeasSchema = z.object( {
ideas: z.array( z.string() ).describe( 'Array of detailed image prompts for Gemini' )
} );
// ============================================
// Type Exports
// ============================================
export type ValidateReferenceImagesInput = z.infer<typeof ValidateReferenceImagesInputSchema>;
export type GenerateImageIdeasInput = z.infer<typeof GenerateImageIdeasInputSchema>;
export type GenerateImagesInput = z.infer<typeof GenerateImagesInputSchema>;
export type ImageIdeas = z.infer<typeof ImageIdeasSchema>;
// Good - helps with documentation and error messages
z.string().describe( 'User email address for notifications' )
// Avoid - no context for errors
z.string()
// Good - workflow works without optional fields
numberOfIdeas: z.number().min( 1 ).max( 10 ).default( 1 )
// Avoid - forces users to provide every field
numberOfIdeas: z.number().min( 1 ).max( 10 )
// Workflow input schema (what the user provides)
export const WorkflowInputSchema = z.object( { ... } );
// Step schemas (internal data shapes)
export const StepNameInputSchema = z.object( { ... } );
// Export schema for runtime validation
export const UserSchema = z.object( { ... } );
// Export type for TypeScript type checking
export type User = z.infer<typeof UserSchema>;
z is imported from @outputai/core.describe() for important fields.optional() or .default().min()/.max() for runtime schemas, .describe() for aiSdk.Output.object() schemas)output-dev-workflow-function - Using schemas in workflow definitionsoutput-dev-step-function - Using schemas in step definitionsoutput-dev-evaluator-function - Using schemas in evaluator definitionsoutput-dev-folder-structure - Where types.ts belongs in the projectoutput-error-zod-import - Troubleshooting schema import issuesoutput-dev-code-style - Code style conventions原文・著作権は Anthropic および各プラグイン作者に帰属します。日本語訳は Claude API による自動翻訳です。