ファンクションコールで取得した値の取り扱い
以前、STROM Platform で外部 API から情報を取得する例を Qiita 上でご説明しました。
その際、取得したデータ(上記の例だと JSON データ)の中身がそれぞれどのような意味合いを持つのか?を定義していないのに、LLM が、それなりに、いいように扱ってくれていることに少しもやもやしていました。
いい感じに扱ってくれてるのはいいんですが、複雑な構造になった場合、本当に正確に理解してくれるんかな?って思いますよね?
なんとなくLLMにいいようにやってもらう
とりあえずの実験として、仮想的な API がある番組の 1)MC 2)メインコメンテーター 3)ゲストコメンテーター の名前を返す環境で、ユーザーからの質問に回答するというフローを作ります。
仮想的な API は、AWS lambda で下記のような実装です。
import json
def lambda_handler(event, context):
ret_json = {
"master_of_ceremony": "神崎 恒一",
"main_commentator": "白石 麻耶",
"guest_commentatorperson_in_charge_c": "ゴルゴンゾーラ 明子"
}
return {
'statusCode': 200,
'body': json.dumps(ret_json, ensure_ascii=False)
}
理解して動いていますね。
取得した JSON のタグをLLMの方で判断して、いいように取り扱ってくれているようです。
では、データが
{
"person_in_charge_a": "神崎 恒一",
"person_in_charge_b": "白石 麻耶",
"person_in_charge_c": "ゴルゴンゾーラ 明子"
}
さすがに、わからないようですね。昔の LLM だと勝手に判断していわゆるハルシネーションを起こしていたかもしれません。
データスキーマの追加
ここで説明する手法は、STORM Platform の機能というよりは、一般的な LLM/AI エージェントの手法です
というようなシステムプロンプトになっていましたが、ここに
出演者情報のスキーマ :
{
"type": "object",
"properties": {
"person_in_charge_a": {
"type": "string",
"description": "MC の氏名"
},
"person_in_charge_b": {
"type": "string",
"description": "メインコメンテーターの氏名"
},
"person_in_charge_c": {
"type": "string",
"description": "ゲストコメンテーターの氏名"
}
},
"required": [
"person_in_charge_a",
"person_in_charge_b",
"person_in_charge_c"
],
"additionalProperties": false
}
こちらをシステムプロンプトに追加します。
期待した動作になりました。
まとめ
API 経由で情報を取得しそのデータを LLM で活用する場合に、特に LLM にデータ構造を指定しなくてもある程度期待した動作になることが多いですが、今回のように、情報を明確に記載し、LLMに対する不明確な情報を与えることを避ける方が、安定して動作すると考えられます。
ワークフロー
{
"schema_version": 1,
"nodes": [
{
"id": 1,
"type": "storm/request",
"properties": {},
"metadata": {
"position": {"x": 12, "y": 41},
"size": {"width": 300, "height": 51},
"virtualId": "Dg7KhjnMfz"
}
},
{
"id": 2,
"type": "storm/llm",
"properties": {
"llm": {
"model": "gpt-5-mini-2025-08-07",
"chat_engine": "openai",
"stop": [],
"tools": [
{
"type": "function",
"function": {
"name": "get_commentators",
"description": "出演者情報を取得する。",
"parameters": {
"type": "object",
"properties": {},
"additionalProperties": false
}
}
}
]
},
"system_template": "あなたは、ユーザーの質問を理解して、呼び出す関数を決定するエージェントです。",
"user_template": "question : {{query}}",
"retrieval_chunks_template": "[[#{{no}}]] filename: {{filename}}, pagename: {{pagename}}\nContext: {{context}}",
"num_latest_chats": 5
},
"metadata": {
"position": {"x": 352, "y": 41},
"size": {"width": 300, "height": 51},
"formDraft": {
"propertiesHash": "11azu82hs6j",
"fields": {
"model": "gpt-5-mini-2025-08-07",
"functions": [
{
"name": "get_commentators",
"parameters": "{\n \"type\": \"object\",\n \"properties\": {},\n \"additionalProperties\": false \n}",
"description": "出演者情報を取得する。"
}
],
"modelSettings": {"chat_engine": "openai"},
"user_template": "question : {{query}}",
"modelParameters": {
"stop": [],
"extra_params": "",
"max_tokens_fallback": 2048
},
"system_template": "あなたは、ユーザーの質問を理解して、呼び出す関数を決定するエージェントです。",
"num_latest_chats": 5,
"retrieval_chunks_template": "[[#{{no}}]] filename: {{filename}}, pagename: {{pagename}}\nContext: {{context}}"
}
},
"virtualId": "3Tu0hvBqQ7"
}
},
{
"id": 3,
"type": "storm/response",
"properties": {"result_list": ["{{custom_variables}}"]},
"metadata": {
"position": {"x": 1792, "y": 40.99999999999999},
"size": {"width": 300, "height": 51},
"virtualId": "1iIhOVuFio"
}
},
{
"id": 4,
"type": "storm/ifelse",
"properties": {
"if": [
{
"note": "",
"logic": "AND",
"condition": [
{
"operator": "EQUAL",
"value": "get_commentators",
"comparison_target": "{{result.name:2}}"
}
],
"idx": 0,
"links": [4]
}
],
"else": {"idx": 1, "links": [5], "note": ""}
},
"metadata": {
"position": {"x": 692, "y": 12},
"size": {"width": 300, "height": 109},
"extra": {"use_json": false},
"virtualId": "IAwOaZoEvS"
}
},
{
"id": 5,
"type": "storm/llm",
"properties": {
"llm": {
"model": "claude-sonnet-4-20250514",
"chat_engine": "claude",
"max_tokens": 20000,
"tools": []
},
"system_template": "出演者情報 : {{body:6}}\n出演者情報のスキーマ : \n{\n \"type\": \"object\",\n \"properties\": {\n \"person_in_charge_a\": {\n \"type\": \"string\",\n \"description\": \"MC の氏名\"\n },\n \"person_in_charge_b\": {\n \"type\": \"string\",\n \"description\": \"メインコメンテーターの氏名\"\n },\n \"person_in_charge_c\": {\n \"type\": \"string\",\n \"description\": \"ゲストコメンテーターの氏名\"\n }\n },\n \"required\": [\n \"person_in_charge_a\",\n \"person_in_charge_b\",\n \"person_in_charge_c\"\n ],\n \"additionalProperties\": false\n}\n\n出演者情報の情報をもとにユーザーの質問に回答してください。\n",
"user_template": "question : {{query}}",
"retrieval_chunks_template": "[[#{{no}}]] filename: {{filename}}, pagename: {{pagename}}\nContext: {{context}}",
"num_latest_chats": 5
},
"metadata": {
"position": {"x": 1452, "y": 40.99999999999999},
"size": {"width": 300, "height": 51},
"formDraft": {
"propertiesHash": "1dtv5ifi2kj",
"fields": {
"model": "claude-sonnet-4-20250514",
"functions": [],
"modelSettings": {"chat_engine": "claude"},
"user_template": "question : {{query}}",
"modelParameters": {
"max_tokens": 20000,
"extra_params": "",
"max_tokens_fallback": 20000
},
"system_template": "出演者情報 : {{body:6}}\n出演者情報のスキーマ : \n{\n \"type\": \"object\",\n \"properties\": {\n \"person_in_charge_a\": {\n \"type\": \"string\",\n \"description\": \"MC の氏名\"\n },\n \"person_in_charge_b\": {\n \"type\": \"string\",\n \"description\": \"メインコメンテーターの氏名\"\n },\n \"person_in_charge_c\": {\n \"type\": \"string\",\n \"description\": \"ゲストコメンテーターの氏名\"\n }\n },\n \"required\": [\n \"person_in_charge_a\",\n \"person_in_charge_b\",\n \"person_in_charge_c\"\n ],\n \"additionalProperties\": false\n}\n\n出演者情報の情報をもとにユーザーの質問に回答してください。\n",
"num_latest_chats": 5,
"customModelSettings": "{\"chat_engine\": \"claude\"}",
"retrieval_chunks_template": "[[#{{no}}]] filename: {{filename}}, pagename: {{pagename}}\nContext: {{context}}"
}
},
"virtualId": "S4dGos3q9z"
}
},
{
"id": 6,
"type": "storm/apicall",
"properties": {
"method": "GET",
"url": "https://hogehoge",
"timeout": {
"response_timeout": 5000,
"read_timeout": 5000,
"write_timeout": 5000
}
},
"metadata": {
"position": {"x": 1072, "y": 59.24999999999999},
"size": {"width": 300, "height": 51},
"virtualId": "mbdFkWTAVY"
}
}
],
"links": [[1, 1, 2], [3, 2, 4], [4, 4, 6], [5, 4, 5], [6, 6, 5], [7, 5, 3]],
"custom_variables": {}
}





