Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Pythonを活用したLLMによる構造的データ生成の手法と実践

BrainPad
September 30, 2024

 Pythonを活用したLLMによる構造的データ生成の手法と実践

PyCon2024JP での発表資料です。
https://2024.pycon.jp/ja/talk/AYJ3GS

BrainPad

September 30, 2024
Tweet

More Decks by BrainPad

Other Decks in Technology

Transcript

  1. 2 ©BrainPad Inc. 自己紹介 しばない かずひろ 柴内一宏 株式会社ブレインパッド XaaS ユニット

    フロンティア開発G グループリーダー 主な業務:生成AI関連サービスの研究・開発 / プロダクトアーキテクト / データ基盤管理 PyConJP 2021 「Pythonのバリデーション定義から フロントエンドTypeScriptのコード生成」 https://speakerdeck.com/brainpadpr/pythonfalsebaridesiyonding-yi-kara-hurontoendotypescriptfalsekodosheng-cheng https://speakerdeck.com/brainpadpr/pythonfalsebaridesiyondi ng-yi-kara-hurontoendotypescriptfalsekodosheng-cheng
  2. 3 ©BrainPad Inc. LLMとアプリケーションへの組み込み 背景 近年、ChatGPTやGPT-4, Gemini, Claudeに代表される大規模言語モデル(LLM)の発展 は目覚ましく、その能力は日々進化している。 また、これらのLLMを活用したアプリケーション開発も急速に進んでいる。

    構造データについて LLMをアプリに組み込むにあたり、出力形式の一貫性の制御が課題であったが、ベンダー側の 様々なソリューションによりJSON出力などフォーマットの制御ができるようになった。 ソリューション例 • JSON Mode • Function Calling • Structured Output
  3. 4 ©BrainPad Inc. LLMとアプリケーションへの組み込み 課題 ただし、JSONによる出力などフォーマットの制御はできるようになったが、内部の細かい制御や 精度については課題が残っている。 問題点 例 複雑なデータ構造のスキーマが

    複雑になる • データの複雑な入れ子 • 複数箇所の同型構造:JSON Modeでリファレンスに対応していない サービスが多いので、同じデータ構造がスキーマの各所に置かれる • 再帰的データ構造:木構造など 繰り返しを含む出力の冗長さ によるレイテンシの増加 入力のCSVに対して加工したデータを出力するなどのタスクで、そのまま入れ ると入出力のトークンも大きくなる 数値や日付などのデータ処理 の問題 • 数値データの扱いが正確ではない (例: 9.11 と 9.8 の比較) • 100個のデータを生成してください→実際には100個より少ないデータが 出力されることがある • 今日から180日後の日付をdateフィールドに追加 → 精度が低い
  4. 5 ©BrainPad Inc. 今回紹介する手法 今回のテーマ Pythonのコードを中間生成させ、 そのコードを実行することで構造化されたデータを生成させる。 発表のポイント: 1. ターゲット言語をPythonにすることにより得られるメリットを具体的に解説します。

    2. 具体的なプロンプトの与え方を示します。(実際のユースケースもあります。) 3. セキュアなコード実行のためのアーキテクチャを示します。 4. 直接出力、他言語との比較評価を行います。
  5. 8 ©BrainPad Inc. 中間コードを用いて解決する課題 コード生成→実行の方法をとることで、出力の精度向上やスケーラビリティのメリットがある。 (※ Groundingを一部コード処理系にオフロードするテクニックは既存研究がある。 PAL: Program-aided Language

    Models Gao et al., 2023, arXiv:2211.10435 等) クエリ: 1243871924*12348712437912 直接出力: 153774257842381740272 コード生成経由: print(1243871924*12348712437912) # 15360216699068329982688 prefectures = ["北海道", "青森県", …] genders = ["男性", "女性"] age_groups = ["10代", "20代", …] data = [ {"都道府県": pref, "性別": gender, "年齢層": age} for pref in prefectures for gender in genders for age in age_groups ] print(json.dumps(data)) 反復的なデータ 計算の正確さ クエリ: 都道府県×男女×年齢層のデータ
  6. 9 ©BrainPad Inc. Why Python? 出力されるPythonをターゲット言語にする理由は以下の通り。 • LLM自体の特性 • 元データの学習量が多いことが期待できる(直近数年のGitHubの中ではトレンド1位

    ) • 言語自体の特性 • リストやネスト構造を含む複雑なデータ構造を型付きのdataclassとして表現可能。 • f-string による柔軟な文字列フォーマット。 • calendarパッケージ等、便利な標準ライブラリとの連携。 • シリアライゼーション/デシリアライゼーションの実装: Pydanticなどのバリデーションライブラリの 存在 • ランタイムのメリット • (比較的)軽量なインタプリタでコード実行が可能 • WebAssemblyでセキュアな実行が可能 後述 https://madnight.github.io/githut/#/pull_requests/2024/1
  7. 10 ©BrainPad Inc. LLMで能力を発揮する、dataclass の表現力 @dataclass class TimeSlot: start: datetime

    end: datetime def duration(self) -> timedelta: return self.end - self.start @dataclass class Speaker: name: str affiliation: str | None = None @dataclass class Event: title: str time: TimeSlot speaker: Speaker @dataclass class Talk(Event): ... @dataclass class Break(Event): ... @dataclass class Track: name: str events: list[Talk | Break] dataclass を使うことで、比較的少ないトークンで複雑なデータ構造を表現できる。 (下の例: 148 token、同等の JSON Schema では 563 token) ロジックの記述 オプショナルの記載 ユニオン型 クラスの継承
  8. 11 ©BrainPad Inc. LLMで能力を発揮する、batteries includedの思想 Python の豊富な標準ライブラリにより、多様性のあるコード出力がサードパーティ 不要で利用可能。 • 日付・時間:calendar,

    zoneinfo, datetime • 文字列:string, unicodedata, re • 汎用アルゴリズム:itertools, collections, built-ins (set, map, ...) • 数学・確率統計:math, fractions, statistics, random • ネットワーク・バイナリ:structs, base64, urllib, ipaddress • その他:uuid, hashlib → データ生成時のサポートとして有用なライブラリ
  9. 12 ©BrainPad Inc. LLMで能力を発揮する、batteries includedの思想 例:import calendar を使うときと使わないときに、生成されるコードの変化。 クエリ 「2024年の各月の月曜日を全てリストで取得してください。」

    def generate() -> list[MonthInfo]: result = [] for month in range(1, 13): mondays = [] for day in range(1, 32): try: d = date(2024, month, day) if d.weekday() == 0: mondays.append(day) except ValueError: pass result.append(MonthInfo(month=month, mondays=mondays)) return result def generate() -> list[MonthInfo]: return [MonthInfo(month=month, mondays=[day for day in calendar.monthcalendar(2024, month)[0] if day != 0]) for month in range(1, 13)] importなしの 生成結果 importありの 生成結果
  10. 13 ©BrainPad Inc. 具体例: PyConJP タイムテーブル生成 PyCon のタイムテーブルを生成する(Gemini 1.5 Proを利用)

    PyCon JP 2024 のタイムテーブルを作る。 会場は #pyconjp_1 - #pyconjp_4 までの4ラ イン。 それぞれの枠は30分。転換は20分。 全体で10:20からスタートして17:00まで。 ただし、11:40-12:40, 14:50-15:30は休憩時間 from datetime import datetime, timedelta from dataclasses import dataclass @dataclass class Schedule: start_date: datetime end_date: datetime @dataclass class Track: name: str presentations: list[Schedule] 今日は 2024/9/28 ユーザー入力 コンテキスト データクラス定義
  11. 14 ©BrainPad Inc. def generate_timetable() -> list[Track]: start_time = datetime(2024,

    9, 28, 10, 20) end_time = datetime(2024, 9, 28, 17, 00) break_times = [(datetime(2024, 9, 28, 11, 40), datetime(2024, 9, 28, 12, 40)), (datetime(2024, 9, 28, 14, 50), datetime(2024, 9, 28, 15, 30))] slot_length = timedelta(minutes=30) transition_length = timedelta(minutes=20) lines = [Track(f"#pyconjp_{i}", []) for i in range(1, 5)] current_time = start_time while current_time + slot_length <= end_time: if any(start <= current_time < end for start, end in break_times): current_time = max(end for start, end in break_times if start <= current_time < end) else: for line in lines: line.presentations.append(Schedule(current_time, current_time + slot_length)) current_time += slot_length + transition_length return lines 具体例: PyConJP タイムテーブル生成 生成されたコード(Gemini 1.5 Proを利用) 中間コード
  12. 15 ©BrainPad Inc. 具体例(実行結果) #pyconjp_1 2024-09-28 10:20:00 - 2024-09-28 10:50:00

    2024-09-28 11:10:00 - 2024-09-28 11:40:00 2024-09-28 12:40:00 - 2024-09-28 13:10:00 2024-09-28 13:30:00 - 2024-09-28 14:00:00 2024-09-28 14:20:00 - 2024-09-28 14:50:00 2024-09-28 15:30:00 - 2024-09-28 16:00:00 2024-09-28 16:20:00 - 2024-09-28 16:50:00 #pyconjp_2 2024-09-28 10:20:00 - 2024-09-28 10:50:00 2024-09-28 11:10:00 - 2024-09-28 11:40:00 2024-09-28 12:40:00 - 2024-09-28 13:10:00 2024-09-28 13:30:00 - 2024-09-28 14:00:00 2024-09-28 14:20:00 - 2024-09-28 14:50:00 2024-09-28 15:30:00 - 2024-09-28 16:00:00 2024-09-28 16:20:00 - 2024-09-28 16:50:00 #pyconjp_3 2024-09-28 10:20:00 - 2024-09-28 10:50:00 2024-09-28 11:10:00 - 2024-09-28 11:40:00 2024-09-28 12:40:00 - 2024-09-28 13:10:00 2024-09-28 13:30:00 - 2024-09-28 14:00:00 2024-09-28 14:20:00 - 2024-09-28 14:50:00 2024-09-28 15:30:00 - 2024-09-28 16:00:00 2024-09-28 16:20:00 - 2024-09-28 16:50:00 #pyconjp_4 2024-09-28 10:20:00 - 2024-09-28 10:50:00 2024-09-28 11:10:00 - 2024-09-28 11:40:00 2024-09-28 12:40:00 - 2024-09-28 13:10:00 2024-09-28 13:30:00 - 2024-09-28 14:00:00 2024-09-28 14:20:00 - 2024-09-28 14:50:00 2024-09-28 15:30:00 - 2024-09-28 16:00:00 2024-09-28 16:20:00 - 2024-09-28 16:50:00
  13. 19 ©BrainPad Inc. 基本となるプロンプトの与え方 # 目的 あなたの主な仕事は、xxxxxxxxxxxx を作成するための ユーザーの指示を解釈することです。そして、その結果を 実行可能なPython

    3.12コードとして出力します。 # コンテキスト ここに生成するデータの仕様や詳細を記載 ... # タスク generate_xxxxx 関数の定義を出力してください。 ・注意事項1 (後述) ・注意事項2 .... ・注意事項3 .... # Preceding Code ```python ここに生成するデータの定義を記載 ``` # 出力 ```python def generate_xxxxx() -> list[XXX]: ... ``` # ユーザの指示 {{ USER_INPUT }} ※ 実際のケースでは英語でプロンプト記述しています。 ここにデータ定義や 利用するライブラリを記載
  14. 20 ©BrainPad Inc. Jinja2によるテンプレート # 目的 あなたの目的は .... # コンテキスト

    {% include 'context.txt' %} # タスク generate_xxx の関数定義を出力 してください。… # Preceding Code {% include 'preceding.py' %} # 入力 {{ user_input }} {% include 'preceding.py' %} {{ generated_code }} print( json.dumps( generate_xxx()) ) このアプリケーションは …… という目的で … を する …… @dataclass class Foo: ... input.txt 入力プロンプト context.txt コンテキスト run_code.py 実行コード preceding.py クラス定義やインポートなど ユ ー ザ ー 入 力 LLM Python
  15. 21 ©BrainPad Inc. 基本となるプロンプトの細かいポイント 以下のようなコード生成の品質を制御する、いろいろな工夫を各所に入れています。 ※ 実効性について定量的な効果検証はしていないことはご了承ください。 プロンプト 期待できる効果 forループやリスト内包表記を使用して行数

    を減らしてください。 繰り返しのコードは”いい感じ”にまとめてくれる。 「Preceding Code」の内容を出力でリーク しないでください。 たまにPreceding Codeの内容を鸚鵡返しすることで無 駄な出力が出ることがあることので、その防止。 コードが危険である、潜在的に有害である、 またはユーザーの要求を満たせない場合は、 単にreturn []と出力して、さらに情報を停 止してください。 悪用対策:無限ループや危険なコードを簡易的に止め る。これだけでは不十分なので、後述のサンドボックスは 必須。 欠落や省略のない完全な実行可能コード を記述してください。 たまに関数の中身を … として実行できないコードを 出力することがあるので、その防止。
  16. 23 ©BrainPad Inc. セキュアなランタイムの構築 悪意ある入力により悪意のあるコードが作成できる(Jailbreak) • ファイルアクセス(例:/etc/passwd にアクセスして内容を格納) • 外部URLへのアクセス(例:example.comに100回アクセスする)

    • リソース枯渇 • fork爆弾 • 無限ループ • IO・ネットワークから隔離されて、 • 計算リソースも監視・制御できる、 • 標準ライブラリが使える、 • 速度も実用的な、 Python実行環境が欲しい
  17. 24 ©BrainPad Inc. セキュアなランタイムの構築 - Pythonで"安全"なコードを実現する方法いろいろ 手法 説明 備考 pysandbox

    ソースコードに対するメタプログラミングで、外部か らのインポートやIOを制限する 根本的にデザインが壊れている(脆弱性が ある)と公式で利用にアナウンスがされてい る。 PyPy sandbox PyPyの機能を利用してサンドボックスを実現する。プロトタイプ段階のまま、メンテナンスされて いない Dockerなどのコンテナ機構での 実装 コンテナ上でcpythonを実行して結果を得ること ができる。 「穴」をすべて塞ぐのに慎重でなければなら ない。 オーバーヘッドがある seccompによる実装 Linuxのシステムコール制限機能「seccomp」を 使って外部IOを制御する 自己責任でフルスクラッチ実装をする必要 がある QEMUなどのCPUエミュレータで 実行 Pythonランタイム自体をCPUレベルでシミュレート することで、きめ細かい制御を行う 環境やランタイムのセットアップが大変。制 御コードも大変 コード実行API SaaSやクラウドベンダーが提供しているAPIを呼ぶ。 LLMの周辺サービスとして提供しているものもあり。 サービスコストが発生。柔軟性の低さやオー バーヘッドもある WebAssemblyによる実行 PythonランタイムのWebAssemblyビルドを動 かす Secure by Design。エコシステムが充実し ており、制御も簡単 → 今回はこれを採用
  18. 25 ©BrainPad Inc. WebAssemblyについて • WebAssembly • ウェブブラウザ上で動作する低レベルのバイナリ命令形式で、ネイティブに近い速度で実行できる。 • 主にウェブアプリケーションのパフォーマンス向上に使用されますが、ブラウザ外での使用も増えてきている。

    • Pythonでの対応状況 • 公式での対応 • Tier 2 での対応 (PEP11) → aarch64-unknown-linux-gnuと同じ程度 • ビルド情報がドキュメントに載っている • https://github.com/python/cpython/blob/main/Tools/wasm/README.md • オーバーヘッドは、手元の計測ではネイティブの1.5-3倍程度 • コミュニティでバイナリも配布されている(25MB程度) • https://github.com/webassemblylabs/webassembly-language-runtimes • 制限 • ネットワーク関連は使えない • スレッド・プロセス関連も使えない • ファイルシステムやOSモジュールは部分的対応
  19. 26 ©BrainPad Inc. 今回採用したWebAssemblyランタイム 今回はwasmtimeを選択しました https://github.com/bytecodealliance/wasmtime • Bytecode Alliance(WebAssembly標準化を推進してる団体)が作っているwasm 実行系

    選定理由: • 実行速度が速い(Rust) • バイナリサイズが軽い • セキュリティに力を入れている(公式の説明を参照) • ランタイム制御が可能 • 実行時間(命令サイクルなど)に基づいた制限 • メモリの上限制限 • Python SDKも用意されている https://github.com/bytecodealliance/wasmtime-py 他の選択肢:wasmedge, wasmer, (py2wasm), (pyodide)
  20. 27 ©BrainPad Inc. wasmtimeを用いた処理の図 Python runtime (wasm) Pythonコード # LLMが出力したコード

    def generate(): ... # 標準出力 print(json.dumps(generate())) wasmtime LLM stdin stdout {} JSON
  21. 28 ©BrainPad Inc. WebAssemblyサンプルコード wasmtime-py 実行サンプルコード(※セキュリティ機構などは省略) import wasmtime as ws

    cfg = ws.Config() linker = ws.Linker(ws.Engine(cfg)) linker.define_wasi() module = ws.Module.from_file(linker.engine, './python.wasm') config = ws.WasiConfig() config.inherit_stdin() # stdin を継承 config.inherit_stdout() # stdout を継承 config.inherit_stderr() # stderr を継承 store = ws.Store(linker.engine) store.set_limits(memory_size=20 * 1024 * 1024) # メモリ上限20MB store.set_wasi(config) instance = linker.instantiate(store, module) entrypoint = instance.exports(store)["_start"] entrypoint(store) 入力: print("hello") 出力: "hello" 入力: print(datetime.now()) 出力: "2024-09-19 06:27:27" 入力: print(open("/etc/password").read()) 出力: No such file or directory: '/etc/password' 入力: urllib.request.urlopen("http://examp le.com") 出力: AttributeError: module '_socket' has no attribute 'getaddrinfo' 参考: Run Python code in a WebAssembly sandbox https://til.simonwillison.net/webassembly/python-in-a-wasm-sandbox
  22. 30 ©BrainPad Inc. シリアライゼーションとデシリアライゼーションについて WebAssemblyとホスト(アプリ)のやりとりは、文字列またはバイナリの形式で行うことになる。 通常ケースではJSONへのシリアライズで十分であると考えられる。 • 安全な処理が可能 • 外部ライブラリ不要

    [他の選択肢] • pickle – デシリアライズ時に任意コード実行ができ、セキュリティ的に問題があるので非推奨。 • protobuf ー ホストの言語が違う場合に便利だが、wasm内部でライブラリを使うように調整が必要 しかし、dataclass→JSON は比較的簡単だが、 JSON → dataclass はちょっと面倒。 (ネストしたフィールドを詰め替えするコードを書く必要アリ)
  23. 31 ©BrainPad Inc. Pydantic Dataclass 課題: JSON → dataclass はちょっと面倒

    アイデア: ライブラリ Pydantic では dataclass と互換性のあるバリデー ション/シリアライザを提供している。 これによりdataclassによる型定義を共有したまま、 • WASMサンドボックス内ではPython標準のdataclassを使 い、JSON にダンプ。 • アプリ側では pydantic.dataclass を使って デシリアライズ / バリデーション を行う。 ということが実現できる。 (もちろん、元の型定義自体を pydantic.BaseModel にしてもよいが、 その場合は wasm に Pydantic を入れる必要がある) from pydantic import RootModel from pydantic.dataclasses import dataclass @dataclass class Auth: auth_type: Literal['basic', 'none'] password: str @dataclass class User: id: int name: str auth: Auth RootModel[User].model_validate_json(""" {"id":42,"name":"aa", "auth":{ "auth_type":"basic","password":"po" }}""")
  24. 32 ©BrainPad Inc. バリデーションによる精度向上 TIPS for LLM 1. エラー内容を観察し、それに合わせる。 •

    よく出力仕様を間違える(例:別の型になる、複数形になる)場合、その「間違えた方」を正にして 元のクラス定義を修正する。 • 出力される名前やデータ設計の方がLLMにとって「自然」であるという認識を取って、柔軟に合わせて いく。 2. デシリアライズをゆるやかに行う • 似たような名前・データ型でも受けいれられるよう、受け入れ側を柔軟に修正する。 3. 再生成フローを組み込む • シードや温度を変えて、バリデーションが通るまで再試行する。 • アイデア:バリデーションライブラリのエラーメッセージを再生成時に付与する • 富豪的解決:並列で複数候補を生成して良いものを採用。 LLMによる方式では出力されるデータに構造データな誤りが生じる可能性がある。特にパラメータ数が少ない モデルだとその頻度は上がる。 (例:フィールド名が違う、出力される型が違う、データ構造が違う、…) その場合、以下の考え方で修正するのがよい。
  25. 35 ©BrainPad Inc. ユースケース:マーケティングSaaSでのルール生成 • 設定できる条件項目が多岐に渡り、構造が複雑で学習曲線が急 • 条件同士の掛け合わせやテンプレート化など、高度な機能はないので、ルールをコピーして似た ようなものを作っている。 といった課題があった。

    大量のルール管理(修正)が大変。 • キャンペーンごとにルールを作成する • セグメント組み合わせの数だけルールを 作成しようとするので、ルールの数が増え 複雑になる どのようなルールを作ればいいか分からない。 • どのようなルールの条件があるかを知らな い。 • 一般にどのようにセグメントを切ればいい かわからない。 • ルールを切りたいが、条件の設定方法が わからない。 ヘビーユーザー 新規顧客
  26. 36 ©BrainPad Inc. ユースケース:マーケティングSaaSでのルール生成 条件の種類: • 属性: ユーザー自身に関する情報(例:ユーザーID、 ログイン状態、カートの中身) •

    スコア: ユーザーに紐づいたポイント(例:合計ポイン ト、セッションポイント) • 時間: ルールが有効な日時(例:曜日、時間帯、 祝日) • アクセス: アクセス情報(例:IPアドレス、ページURL、 滞在時間、参照元) • 状態: ユーザーの状態(例:ページビュー数、訪問回 数、最終訪問日) 比較演算子の種類 文字列: • 等しい、等しくない、含む、含まない • で始まる、で始まらない、で終わる、で終わらない • 正規表現に一致、一致しない 数値: • 等しい、等しくない • 以上、より大きい • 以下、より小さい • 範囲内 その他: • 存在する、存在しない • 年月日、曜日、時間帯を指定 複雑なルール条件 → これを dataclass として定義する。
  27. 37 ©BrainPad Inc. ユースケース:マーケティングSaaSでのルール生成 繰り返しが含まれるルールや組み合わせのルールをプログラム的に生成 →レコメンド設定にかかる時間を大幅に削減して作業負担を軽減可能に! 関心スコア _HIGH_PC 訴求コンテンツ _HIGH_PC

    グループ 「顧客属性」 内の累積スコア 「関心スコア」 が 10ポイント 以上 ユーザーエージェントが「^.*(?:iPhone|iPod|Android.*Mobile).*$」 の正規表現に一致しない 関心スコア _HIGH_SP 訴求コンテンツ _HIGH_SP グループ 「顧客属性」 内の累積スコア 「関心スコア」 が 10ポイント 以上 ユーザーエージェントが「^.*(?:iPhone|iPod|Android.*Mobile).*$」 の正規表現に一致する 関心スコア _MID_PC 訴求コンテンツ _MID_PC グループ 「顧客属性」 内の累積スコア 「関心スコア」 が 5 から 9ポイント に一致する ユーザーエージェントが「^.*(?:iPhone|iPod|Android.*Mobile).*$」 の正規表現に一致しない def generate_rules() -> List[Rule]: rules = [] for score_low, score_high, content_suffix in [(10, None, "HIGH"), (5, 9, "MID"), (0, 4, "LOW")]: for device in ["PC", "SP"]: score_condition = Score(key="関心スコア", point=NumericCondition("GREATER_EQUAL", score_low) if score_high is None else NumericCondition("RANGE", from_value=score_low, to_value=score_high)) device_condition = Access(user_agent=StringCondition("NOT_MATCH", "^.*(?:iPhone|iPod|Android.*Mobile).*$") if device == "PC" else StringCondition("MATCHES", "^.*(?:iPhone|iPod|Android.*Mobile).*$")) rules.append( Rule(rule_name=f"関心スコア_{content_suffix}_{device}", content_name=f"訴求コンテンツ_{content_suffix}_{device}", score=[score_condition], access=device_condition)) return rules 中間コード 最終出力 顧客の「関心スコア」が • 10〜 • 5〜9 • 0〜4 のそれぞれについて「訴求コンテン ツ_HIGH/MID/LOW_デバイス (PC/SP)」を表示する。 ユーザー入力
  28. 39 ©BrainPad Inc. 評価 先ほどの「Rtoaster」のユースケースをモデルに、ベンチマークを作成し、評価を行う。 評価ターゲット: • JSON 出力 (JSON

    Schema入力) • Python による出力 • JavaScript による出力 評価指標: • 結果出力の長さ • データ構造の妥当性(実行可能性も含む) • データ出力の妥当性 • クエリの解釈に曖昧な点がある場合は人力 による判定を行う。 言語モデル:Gemini Pro 1.5 ベンチマークのクエリ例: 1. 基本的なクエリ • 例)2024年7月1日から2024年8月31日までの期間 中、URLが "/products/" で始まる商品ページに30秒 以上滞在したユーザーに「夏季限定クーポン」を表示す る。 2. 時間計算を含むクエリ • 例)直近30日間のうち、週末(土曜日と日曜 日)にのみ訪問し、かつ訪問回数が5回以上のユー ザーに対して、「週末愛好者特別オファー」というコンテ ンツを表示する。 3. 繰り返しを含むクエリ • 例)ユーザーの年齢層(10代、20代、30代、40代、 50代以上)と性別(男性、女性)の組み合わせ ごとに、それぞれ異なる「ターゲット広告」コンテンツを 表示する。
  29. 40 ©BrainPad Inc. 評価結果と考察 0 20 40 60 80 100

    JSON JavaScript Python 中身の妥当性 構造の妥当性 (100点満点) 以下について100点満点で評価。 • データ構造の妥当性:出力されるオブジェクトが正しくデシリアライズ可能か • データの中身の妥当性:中身がクエリの要求と合っているか
  30. 41 ©BrainPad Inc. 評価結果と考察 0 500 1000 1500 2000 2500

    3000 3500 JSON JavaScript Python 平均文字数(繰り返しを含むケース) • 繰り返しを含むケースでの平均文字数
  31. 42 ©BrainPad Inc. 評価結果と考察 総括 • JSON Schemaを与えて直接出力するよりも、プログラム経由で出力したほうが妥当な構造が出力 される。 •

    (最近はスキーマをモデルに直接与えることができる機能もあるが、今回は未検証) • 繰り返しを含むケースでは、プログラム的に書いたほうが出力効率が良い。 今後の課題 • Pythonの強みが出るケースの発見 • 今回、時間計算でPythonが有意になると想定されたが、あまり差異が無かった。 • 標準パッケージをもっと活用した例を見つけたい。 • 各言語の(LLM出力による)強み弱みを体系化 → 最適な言語(+直接JSON)選択が自動に 出来るようになる? • プロンプトのチューニング • 構造的データを生成するベンチマークの拡充 ※注意:今回はライトな評価実験であり、厳密な妥当性はありません。精度はモデルやプロンプト構成により大 きく左右されます。
  32. 43 ©BrainPad Inc. まとめ 今回のテーマ「Pythonのコードを中間生成させ、そのコードを実行することで構造化されたデー タを生成させる。」 Python採用のメリット: • LLM自体の特性: 元データの学習量が多いことが期待で

    きる • 言語自体の特性 • 複雑なデータ構造を dataclass として表現可能。 • f-stringによるコンパクトで柔軟な文字列フォーマット。 • 標準ライブラリの充実 • ランタイムのメリット • (比較的)軽量なインタプリタでコード実行が可能 • WebAssemblyでセキュアな実行が可能 中間コードを利用して解決する課題: • 反復的なデータを直接出力させるよりも、出力 トークンの効率が向上し、レスポンス時間短縮が 可能。 • 数値や時間の計算について、より厳密な結果 が期待できる。 • 典型的なデータの変換処理(文字列置換な ど)のタスクを正確に、スケーラブルに実行できる。 発表のポイント: 1. ターゲット言語をPythonにすることにより享受できるメリットを具体的に解説した。 2. 具体的なプロンプトの与え方を示した。 3. セキュアなコード実行のためのアーキテクチャを示した。 4. 直接出力、他言語との比較評価を行った。 フォローアップ記事 執筆予定!