$30 off During Our Annual Pro Sale. View Details »

【登壇資料】LLMのSDKに渡すためのJSON Schemaを Goのstructから良い感じ...

Avatar for エブリー エブリー
December 14, 2025
37

【登壇資料】LLMのSDKに渡すためのJSON Schemaを Goのstructから良い感じに生成したい

20251216 golang.tokyo #42

Avatar for エブリー

エブリー

December 14, 2025
Tweet

Transcript

  1. 2 Copyright © 2015 every, Inc. All rights reserved. 自己紹介

    名前:赤川 正朋 (X: まさとも @mathatomo57) 所属:株式会社エブリー 開発1部(2025.4~) 主な技術領域: Go、 AWS Go歴:1年ちょっと
  2. 4 Copyright © 2015 every, Inc. All rights reserved. サービス紹介

    「だれでもおいしく簡単に 作れるレシピ」を毎日配信 するレシピ動画メディア 「子育てを通じて、人が、社 会が、ともに手をとりあう世 界を実現する」ファミリー向 け動画メディア 「熱狂を、仕掛ける。世の 中を、揺さぶる。」SNS・動 画のプロフェッショナル チーム デリッシュキッチンのア セットを活かした「簡単に 楽しく続けられる」をめざ す食事管理アプリ デリシュキッチンを初め日本最大級のメディアを運営。生活に寄り添ったサービスを提供しています。
  3. 6 Copyright © 2015 every, Inc. All rights reserved. Structured

    Output • Structured Output: LLMのAPIの出力を指定した json形式で出力させる • GeminiやOpenAIのSDKでは、JSON Schemaを使って指定できる https://ai.google.dev/gemini-api/docs/structured-output?hl=ja&example=recipe
  4. 7 Copyright © 2015 every, Inc. All rights reserved. Structured

    Output Goではレスポンスの jsonをunmarshallするために、同じ形の Goの構造体が必要
  5. 8 Copyright © 2015 every, Inc. All rights reserved. Structured

    Output (ほぼ)同じ形のデータ型を 2つも定義したくない! Goの構造体から JSON Schemaを生成したい! 今日話すこと • 上記を実現するために調べた・考えたこと • 実際に作ってみる • 既存パッケージを活用する方法
  6. 10 Copyright © 2015 every, Inc. All rights reserved. JSON

    Schema • JSONの構造や型、制約を定義するための言語 • 各プロパティのことをキーワードという 例:オブジェクトと配列の基本的な書き方 ↓
  7. 11 Copyright © 2015 every, Inc. All rights reserved. JSON

    Schema • キーワードにはいろいろな種類がある • Structured Outputの目的はLLMの出力を制限することなので、うまく活用していきたい
  8. 12 Copyright © 2015 every, Inc. All rights reserved. JSON

    Schema バージョンによって、書き方が変わっているものも   Draft4                 Draft6以降  
  9. 13 Copyright © 2015 every, Inc. All rights reserved. JSON

    Schema 注意しなければいけないこと • 各SDKが、特定バージョンの JSON Schemaに完全に準拠しているとは限らない …😭 • 例えばGeminiでは、minLength / maxLength はサポートされていない https://github.com/googleapis/go-genai/blob/2a3297de/types.go
  10. 14 Copyright © 2015 every, Inc. All rights reserved. JSON

    Schema • OpenAIも同様に、 minLength / maxLength はサポートされていなかったりする https://platform.openai.com/docs/guides/structured-outputs#supported-schemas
  11. 15 Copyright © 2015 every, Inc. All rights reserved. JSON

    Schema OpenAIがサポートしているオプションはあとこれだけ、意外と少ない
  12. 17 Copyright © 2015 every, Inc. All rights reserved. reflectでJSON

    Schemaを作る • reflect: Goの標準パッケージの一つ • 実行時に型や値の情報を取得することができる • 以下のあたりを使えば、今回やりたいことができる reflect.TypeOf(v) → reflect.Type ├── Kind() → reflect.Kind (Ptr, Struct, Slice, Map, String...) ├── Elem() → 要素の reflect.Type ├── Key() → マップキーの reflect.Type ├── NumField() → フィールド数 (構造体のみ ) └── Field(i) → reflect.StructField ├── Name → フィールド名 ├── Type → フィールドの型 ├── Tag → reflect.StructTag │ └── Get("key") → タグ値 └── IsExported() → エクスポート判定
  13. 18 Copyright © 2015 every, Inc. All rights reserved. 方針

    • 以下のキーワードに対応できるようにする ◦ type ◦ properties ◦ items ◦ OpenAIでサポートされているオプションたち • オプションの指定は struct tag `validate` で行う ◦ struct tagが長くなりすぎるのは好ましくないが、OpenAIに渡す用途に限定すればそこまで 肥大化しないため • バージョンは Draft6以降に対応 ◦ 調べた限りではOpenAIがサポートしているキーワードに大きな変更がないため ◦ $schemaはつけない(OpenAIに必要ないため)
  14. 19 Copyright © 2015 every, Inc. All rights reserved. reflectでJSON

    Schemaを作る ざっくりつくってみたので、一部実装を紹介 https://github.com/masatomo57/jsonschema-go
  15. 20 Copyright © 2015 every, Inc. All rights reserved. reflectでJSON

    Schemaを作る 以下のような関数を作った
  16. 21 Copyright © 2015 every, Inc. All rights reserved. reflectでJSON

    Schemaを作る 構造体のフィールドごとに、 map[string]anyに再帰的に変換していく(一部省略)
  17. 22 Copyright © 2015 every, Inc. All rights reserved. reflectでJSON

    Schemaを作る 基本型に対して、 “type”: “hogehoge” を対応させる
  18. 23 Copyright © 2015 every, Inc. All rights reserved. reflectでJSON

    Schemaを作る structの変換の中身(一部省略) jsonタグとvalidateタグを取り出して、 schemaに追加していく
  19. 25 Copyright © 2015 every, Inc. All rights reserved. 既存パッケージを使う

    • invopop/jsonschema • OpenAI SDKのExampleや、Genkitの内部実装で採用されている • 基本はstruct tagでオプション指定を行うが、以下のようにオプションで渡せるものも https://github.com/openai/openai-go/blob/main/examples/structured-outputs/main.go#L25-L35
  20. 26 Copyright © 2015 every, Inc. All rights reserved. 既存パッケージを使う

    • google/jsonschema-go • modelcontextprotocol/go-sdkで採用されている • Structタグではなく、 Schema構造体のプロパティでキーワードを指定する(まだ v0なので変 わるかも) • 他にも、SchemaのValidation関数も使えるのが Good https://github.com/openai/openai-go/blob/main/examples/structured-outputs/main.go#L25-L35
  21. 27 Copyright © 2015 every, Inc. All rights reserved. 最後に

    使うときは以下に注意 • reflectは実行時に型や値の情報を取得するため、コンパイラの最適化が効かない • オブジェクトも多量に使うので、 GC負荷が高い • 呼び出して初めて panicするかもしれない → init()とかで起動時に一度だけ呼び出して持っておくのが良さそう
  22. 28 Copyright © 2015 every, Inc. All rights reserved. まとめ

    • LLMがサポートしている JSON Schemaのキーワード、バージョンは限定的 • バージョンごとのキーワードの使い方に気をつける • reflectで型の情報を struct tagも含め扱える • が、負荷に気をつける
  23. 29 Copyright © 2015 every, Inc. All rights reserved. every

    - Recruit Info 🔍 エブリー 採用 🔍 エブリー テックブログ 🔍 エブリー オウンドメディア メディア / ブログ 開発部 公式 @every_engineerで技術的な発信を行っています。 エンジニアリングに関する発信やイベント情報などをお 知らせしますので、フォローお願いします! 『every.thing』では、エブリーではたらく人、サービス・ 事業、開催イベント、働き方まで、社内の出来事をぜん ぶお伝えしています。 『テックブログ』では、エブリーの開発の裏側や技術的 な挑戦、エンジニアの知見を発信しています。 採用HP エブリーでは働く仲間を大募集中です! 「中の人と話してみたい」「まずはざっくばらんにエブ リーのことを知りたい」「選考に進むか悩んでいる」など など、 まずは面談にて気軽にお話しましょう!!