はじめに
企業内の機密データをインターネットに出さず、生成AIソリューションを構築するツールの1つとして、Ollamaがあります。
このツールは、インターネット接続なしでオンプレミス上のサーバやローカルマシン上で多様なオープンソースの生成AIを実行することができます。
今回、Ollamaにおいて2024年12月6日にサポートされたStructured Outputを試してみました。
Structured Outputとは
Structured Outputは、生成AIの出力を予め定義された形式で構造化するための機能です。
この機能には以下のような特徴があります:
主な特徴
- 出力フォーマットの制御
- スキーマに従った一貫性のある出力を生成
- 複数回の生成でも同じ構造を維持
- システム統合の容易さ
- 構造化されたデータを直接プログラムで扱える
- 後続の処理フローへの連携が容易
- バッチ処理との親和性が高い
このStructured Outputにより、会話などの非構造化データを構造化データとして変換でき、
業務システムやワークフローに確実に組み込むことが期待できます。
検証の動機
現在、オンプレミス上で音声会話データから重要な情報を抽出・分類するシステムを構築しており、会話内容の自動分析結果に応じて適切なワークフローを選択できる仕組みの実装を進めています。
この音声会話データを生成AIで処理するプロセスにおいて、以下の課題がありました:
- プロンプトで指示したJSONスキーマに従わない
- 非構造化データ抽出、分類
これらの課題に対し、Structured Outputが有効な解決策になるかを確認します。
期待する結果
Structured Outputを利用することで、以下のような構造化データを得られることを期待し確認を進めます:
入力内容
「商品返品に関する品質クレームが発生しました。緊急対応が必要ですので、品質管理部門に通知し、24時間以内に対応をお願いします。」
出力結果
{
"keywords": ["商品返品", "品質クレーム", "緊急対応"],
"category": "クレーム対応",
"priority": "高",
"next_action": "品質管理部門への通知",
"required_response_time": "24時間以内"
}
ためしてみる
まずは、Ollama公式のドキュメントに従い、試してみます。
- 最新バージョンであることを確認する
ollama -v
ollama version is 0.5.3
- ローカル上で、curlコマンドを実行する
curl -s -X POST http://localhost:11434/api/chat -H "Content-Type: application/json" -d '{ "model": "llama3.1",
"messages": [{"role": "user", "content": "Tell me about Canada."}],
"stream": false,
"format": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"capital": {
"type": "string"
},
"languages": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"name",
"capital",
"languages"
]
}
}' | jq .message
- requiredで指定したプロパティが出力されたことを確認できました
{
"role": "assistant",
"content": "{ \"name\": \"Canada\", \"capital\": \"Ottawa\", \"languages\": [\"English\", \"French\", \"First Nations languages\", \"Indigenous sign languages\", \"others including Punjabi, Spanish and many others as well.\" ]\n}\n \t\t\t \t\t\t"
}
実環境を想定したプロンプト
ここまでStructured Output動作が確認できたので、次は実際の利用を想定した
コールセンターの会話のやりとり(ダミーデータ)をプロンプトに入力してみます。
curl -s -X POST http://localhost:11434/api/chat -H "Content-Type: application/json" -d '{ "model": "llama3.2:3b",
"messages": [{"role": "user", "content": "ユーザーID: U12345、名前: 山田です。料金プランについて教えてください。現在のプランは何ですか?スマホのデータプランです。データ容量はどのくらい必要ですか?10GBあれば 十分です。10GBのプランは月額4,000円です。変更しますか?はい、お願いします。手続きが完了しました。ありがとうございます。お困りのことがあればいつでもお電話ください。"}],
"stream": false,
"temperature": 0.0,
"format": {
"type": "object",
"properties": {
"userId": {
"type": "string"
},
"userName": {
"type": "string"
},
"phoneNumber": {
"type": "string"
},
"serviceName": {
"type": "string"
},
"content": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"userId",
"userName",
"phoneNumber",
"serviceName",
"content"
]
}
}' | jq .message
出力結果
以下が出力結果になります。
会話内容から、指定したJSONスキーマに従い、出力されていることがわかります。
また、指定したプロパティで、ユーザID(userId)、名前(userName)、サービス(serviceName)に関する内容が会話内容からうまく抽出できています。一方、内容(content)については、もう一工夫必要に見えます。複数回実行したところ、一意に決まるようなプロパティの内容については、大きな揺れはありませんでした。しかし内容のような複数個出力が想定されるものは、回答数の変化や揺れが見られました。
{
"role": "assistant",
"content": "{\"userId\": \"U12345\", \"userName\": \"山田さん\", \"phoneNumber\": \"03-1234-5678\", \"serviceName\": \"スマホデータプラン\", \"content\": [\"データ容量: 10GB\", \"月額料金: 4000円\", \"変更しました\"]}"
}
まとめ
今回、Ollama Structured Outputがサポートされたため、
音声会話データをプロンプトに入力しその動作を確認しました。
一意に決まるものについては、有用であることが確認できました。
複数回答があるものについては、もう一工夫が必要なことを確認できました。