環境
- llama.cpp b7971
困っていること
しかし、Zodと組み合わせた場合、describeで書いた説明文がLLMに渡っていないようだ。
以下が検証コードで、Zodスキーマのdescribeに特定の文字列を指定し、OpenAI APIとllama.cppの両方で同じスキーマを使わせることで、describeの内容が反映されているかを確認している。
import { openai } from '@ai-sdk/openai'; import { createOpenAI } from '@ai-sdk/openai'; import { Output, generateText } from 'ai'; import { z } from 'zod'; const localOpenAI = createOpenAI({ baseURL: 'http://192.168.16.21:8000/v1' }); const run = async () => { const testSchema = z.object({ secretCode: z.string().describe("必ず 'PINEAPPLE' という文字列を入れてください") }); const { output } = await generateText({ // model: localOpenAI.chat('main'), model: openai('gpt-4.1'), output: Output.object({ schema: testSchema }), prompt: 'こんにちは' }); console.log(output); }; run();
OpenAIのAPI(openai('gpt-4.1'))ではレスポンスが「PINEAPPLE」となり、describeが正しく渡っていることが確認できた。
一方、llama.cpp(localOpenAI.chat('main'))を使うと「12345」など、describeの内容が反映されていない出力になった。
解決策
Zod v4で toJSONSchema が実装された。これを使うことで、describeの内容を含む正しいJSON Schemaを生成できる。
${JSON.stringify(z.toJSONSchema(testSchema))}
上記のように記述すると、以下のようなJSON Schemaが生成される。
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "secretCode": { "type": "string", "description": "必ず 'PINEAPPLE' という文字列を入れてください" } }, "required": [ "secretCode" ], "additionalProperties": false }
このJSON Schemaをプロンプトに含めることで、describeの内容がLLMに正しく伝わるようになる。
なんでllama.cppはdescribeが抜け落ちるの?
describeの内容が無視される理由は、llama.cppのがJSONスキーマを「文法制約(Grammar)」としてのみ使用し、「プロンプト(指示)」としてモデルに伝えていないためだ。
llama.cppは渡されたJSONスキーマをGBNF(Grammar-Based Normalization Form)という文法定義に変換する。しかし、describeやdescriptionに書かれた意味的な指示は文法定義には含まれない。