0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Difyでポケモン図鑑を実装する(Step2:パフォーマンス測定)

0
Posted at

はじめに

前回までの続きです。前回はポケモンを6匹でしたが、今回はポケモンの数を増やしていきます。その場合にパフォーマンスにどう影響があるのか?を測定したいと思います!

30匹

30匹
import json
from typing import Any, Dict, List

# ハードコードされたポケモンデータ(30匹)
POKEMONS = [
  {
    "id": 25,
    "name_jp": "ピカチュウ",
    "name_en": "pikachu",
    "types": [
      "electric"
    ],
    "height_m": 0.4,
    "weight_kg": 6.0,
    "base_stats": {
      "hp": 35,
      "attack": 55,
      "defense": 40,
      "sp_attack": 50,
      "sp_defense": 50,
      "speed": 90
    },
    "tags": [
      "cute",
      "kanto",
      "mascot"
    ]
  },
  {
    "id": 1,
    "name_jp": "フシギダネ",
    "name_en": "bulbasaur",
    "types": [
      "grass",
      "poison"
    ],
    "height_m": 0.7,
    "weight_kg": 6.9,
    "base_stats": {
      "hp": 45,
      "attack": 49,
      "defense": 49,
      "sp_attack": 65,
      "sp_defense": 65,
      "speed": 45
    },
    "tags": [
      "starter",
      "kanto"
    ]
  },
  {
    "id": 6,
    "name_jp": "リザードン",
    "name_en": "charizard",
    "types": [
      "fire",
      "flying"
    ],
    "height_m": 1.7,
    "weight_kg": 90.5,
    "base_stats": {
      "hp": 78,
      "attack": 84,
      "defense": 78,
      "sp_attack": 109,
      "sp_defense": 85,
      "speed": 100
    },
    "tags": [
      "starter",
      "kanto",
      "popular",
      "attacker"
    ]
  },
  {
    "id": 51,
    "name_jp": "ダグトリオ",
    "name_en": "dugtrio",
    "types": [
      "ground"
    ],
    "height_m": 0.7,
    "weight_kg": 33.3,
    "base_stats": {
      "hp": 35,
      "attack": 80,
      "defense": 50,
      "sp_attack": 50,
      "sp_defense": 70,
      "speed": 120
    },
    "tags": [
      "ground",
      "fast",
      "kanto"
    ]
  },
  {
    "id": 143,
    "name_jp": "カビゴン",
    "name_en": "snorlax",
    "types": [
      "normal"
    ],
    "height_m": 2.1,
    "weight_kg": 460.0,
    "base_stats": {
      "hp": 160,
      "attack": 110,
      "defense": 65,
      "sp_attack": 65,
      "sp_defense": 110,
      "speed": 30
    },
    "tags": [
      "tank",
      "kanto",
      "heavy"
    ]
  },
  {
    "id": 94,
    "name_jp": "ゲンガー",
    "name_en": "gengar",
    "types": [
      "ghost",
      "poison"
    ],
    "height_m": 1.5,
    "weight_kg": 40.5,
    "base_stats": {
      "hp": 60,
      "attack": 65,
      "defense": 60,
      "sp_attack": 130,
      "sp_defense": 75,
      "speed": 110
    },
    "tags": [
      "kanto",
      "special_attacker",
      "fast"
    ]
  },
  {
    "id": 2,
    "name_jp": "フシギソウ",
    "name_en": "ivysaur",
    "types": [
      "grass",
      "poison"
    ],
    "height_m": 1.0,
    "weight_kg": 13.0,
    "base_stats": {
      "hp": 60,
      "attack": 62,
      "defense": 63,
      "sp_attack": 80,
      "sp_defense": 80,
      "speed": 60
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 3,
    "name_jp": "フシギバナ",
    "name_en": "venusaur",
    "types": [
      "grass",
      "poison"
    ],
    "height_m": 2.0,
    "weight_kg": 100.0,
    "base_stats": {
      "hp": 80,
      "attack": 82,
      "defense": 83,
      "sp_attack": 100,
      "sp_defense": 100,
      "speed": 80
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 4,
    "name_jp": "ヒトカゲ",
    "name_en": "charmander",
    "types": [
      "fire"
    ],
    "height_m": 0.6,
    "weight_kg": 8.5,
    "base_stats": {
      "hp": 39,
      "attack": 52,
      "defense": 43,
      "sp_attack": 60,
      "sp_defense": 50,
      "speed": 65
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 5,
    "name_jp": "リザード",
    "name_en": "charmeleon",
    "types": [
      "fire"
    ],
    "height_m": 1.1,
    "weight_kg": 19.0,
    "base_stats": {
      "hp": 58,
      "attack": 64,
      "defense": 58,
      "sp_attack": 80,
      "sp_defense": 65,
      "speed": 80
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 7,
    "name_jp": "ゼニガメ",
    "name_en": "squirtle",
    "types": [
      "water"
    ],
    "height_m": 0.5,
    "weight_kg": 9.0,
    "base_stats": {
      "hp": 44,
      "attack": 48,
      "defense": 65,
      "sp_attack": 50,
      "sp_defense": 64,
      "speed": 43
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 8,
    "name_jp": "カメール",
    "name_en": "wartortle",
    "types": [
      "water"
    ],
    "height_m": 1.0,
    "weight_kg": 22.5,
    "base_stats": {
      "hp": 59,
      "attack": 63,
      "defense": 80,
      "sp_attack": 65,
      "sp_defense": 80,
      "speed": 58
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 9,
    "name_jp": "カメックス",
    "name_en": "blastoise",
    "types": [
      "water"
    ],
    "height_m": 1.6,
    "weight_kg": 85.5,
    "base_stats": {
      "hp": 79,
      "attack": 83,
      "defense": 100,
      "sp_attack": 85,
      "sp_defense": 105,
      "speed": 78
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 10,
    "name_jp": "キャタピー",
    "name_en": "caterpie",
    "types": [
      "bug"
    ],
    "height_m": 0.3,
    "weight_kg": 2.9,
    "base_stats": {
      "hp": 45,
      "attack": 30,
      "defense": 35,
      "sp_attack": 20,
      "sp_defense": 20,
      "speed": 45
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 11,
    "name_jp": "トランセル",
    "name_en": "metapod",
    "types": [
      "bug"
    ],
    "height_m": 0.7,
    "weight_kg": 9.9,
    "base_stats": {
      "hp": 50,
      "attack": 20,
      "defense": 55,
      "sp_attack": 25,
      "sp_defense": 25,
      "speed": 30
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 12,
    "name_jp": "バタフリー",
    "name_en": "butterfree",
    "types": [
      "bug",
      "flying"
    ],
    "height_m": 1.1,
    "weight_kg": 32.0,
    "base_stats": {
      "hp": 60,
      "attack": 45,
      "defense": 50,
      "sp_attack": 90,
      "sp_defense": 80,
      "speed": 70
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 13,
    "name_jp": "ビードル",
    "name_en": "weedle",
    "types": [
      "bug",
      "poison"
    ],
    "height_m": 0.3,
    "weight_kg": 3.2,
    "base_stats": {
      "hp": 40,
      "attack": 35,
      "defense": 30,
      "sp_attack": 20,
      "sp_defense": 20,
      "speed": 50
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 14,
    "name_jp": "コクーン",
    "name_en": "kakuna",
    "types": [
      "bug",
      "poison"
    ],
    "height_m": 0.6,
    "weight_kg": 10.0,
    "base_stats": {
      "hp": 45,
      "attack": 25,
      "defense": 50,
      "sp_attack": 25,
      "sp_defense": 25,
      "speed": 35
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 15,
    "name_jp": "スピアー",
    "name_en": "beedrill",
    "types": [
      "bug",
      "poison"
    ],
    "height_m": 1.0,
    "weight_kg": 29.5,
    "base_stats": {
      "hp": 65,
      "attack": 90,
      "defense": 40,
      "sp_attack": 45,
      "sp_defense": 80,
      "speed": 75
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 16,
    "name_jp": "ポッポ",
    "name_en": "pidgey",
    "types": [
      "normal",
      "flying"
    ],
    "height_m": 0.3,
    "weight_kg": 1.8,
    "base_stats": {
      "hp": 40,
      "attack": 45,
      "defense": 40,
      "sp_attack": 35,
      "sp_defense": 35,
      "speed": 56
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 17,
    "name_jp": "ピジョン",
    "name_en": "pidgeotto",
    "types": [
      "normal",
      "flying"
    ],
    "height_m": 1.1,
    "weight_kg": 30.0,
    "base_stats": {
      "hp": 63,
      "attack": 60,
      "defense": 55,
      "sp_attack": 50,
      "sp_defense": 50,
      "speed": 71
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 18,
    "name_jp": "ピジョット",
    "name_en": "pidgeot",
    "types": [
      "normal",
      "flying"
    ],
    "height_m": 1.5,
    "weight_kg": 39.5,
    "base_stats": {
      "hp": 83,
      "attack": 80,
      "defense": 75,
      "sp_attack": 70,
      "sp_defense": 70,
      "speed": 101
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 19,
    "name_jp": "コラッタ",
    "name_en": "rattata",
    "types": [
      "normal"
    ],
    "height_m": 0.3,
    "weight_kg": 3.5,
    "base_stats": {
      "hp": 30,
      "attack": 56,
      "defense": 35,
      "sp_attack": 25,
      "sp_defense": 35,
      "speed": 72
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 20,
    "name_jp": "ラッタ",
    "name_en": "raticate",
    "types": [
      "normal"
    ],
    "height_m": 0.7,
    "weight_kg": 18.5,
    "base_stats": {
      "hp": 55,
      "attack": 81,
      "defense": 60,
      "sp_attack": 50,
      "sp_defense": 70,
      "speed": 97
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 21,
    "name_jp": "オニスズメ",
    "name_en": "spearow",
    "types": [
      "normal",
      "flying"
    ],
    "height_m": 0.3,
    "weight_kg": 2.0,
    "base_stats": {
      "hp": 40,
      "attack": 60,
      "defense": 30,
      "sp_attack": 31,
      "sp_defense": 31,
      "speed": 70
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 22,
    "name_jp": "オニドリル",
    "name_en": "fearow",
    "types": [
      "normal",
      "flying"
    ],
    "height_m": 1.2,
    "weight_kg": 38.0,
    "base_stats": {
      "hp": 65,
      "attack": 90,
      "defense": 65,
      "sp_attack": 61,
      "sp_defense": 61,
      "speed": 100
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 23,
    "name_jp": "アーボ",
    "name_en": "ekans",
    "types": [
      "poison"
    ],
    "height_m": 2.0,
    "weight_kg": 6.9,
    "base_stats": {
      "hp": 35,
      "attack": 60,
      "defense": 44,
      "sp_attack": 40,
      "sp_defense": 54,
      "speed": 55
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 24,
    "name_jp": "アーボック",
    "name_en": "arbok",
    "types": [
      "poison"
    ],
    "height_m": 3.5,
    "weight_kg": 65.0,
    "base_stats": {
      "hp": 60,
      "attack": 85,
      "defense": 69,
      "sp_attack": 65,
      "sp_defense": 79,
      "speed": 80
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 27,
    "name_jp": "ニドラン♀",
    "name_en": "nidoran_f",
    "types": [
      "poison"
    ],
    "height_m": 0.4,
    "weight_kg": 7.0,
    "base_stats": {
      "hp": 55,
      "attack": 47,
      "defense": 52,
      "sp_attack": 40,
      "sp_defense": 40,
      "speed": 41
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 28,
    "name_jp": "ニドリーナ",
    "name_en": "nidorina",
    "types": [
      "poison"
    ],
    "height_m": 0.8,
    "weight_kg": 20.0,
    "base_stats": {
      "hp": 70,
      "attack": 62,
      "defense": 67,
      "sp_attack": 55,
      "sp_defense": 55,
      "speed": 56
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 29,
    "name_jp": "ニドクイン",
    "name_en": "nidoqueen",
    "types": [
      "poison",
      "ground"
    ],
    "height_m": 1.3,
    "weight_kg": 60.0,
    "base_stats": {
      "hp": 90,
      "attack": 82,
      "defense": 87,
      "sp_attack": 75,
      "sp_defense": 85,
      "speed": 76
    },
    "tags": [
      "kanto"
    ]
  },
  {
    "id": 30,
    "name_jp": "ニドラン♂",
    "name_en": "nidoran_m",
    "types": [
      "poison"
    ],
    "height_m": 0.5,
    "weight_kg": 9.0,
    "base_stats": {
      "hp": 46,
      "attack": 57,
      "defense": 40,
      "sp_attack": 40,
      "sp_defense": 40,
      "speed": 50
    },
    "tags": [
      "kanto"
    ]
  }
]

def type_match(pokemon_types, required_types, exclude_types) -> bool:
    # required_types: いずれか1つでも含まれていればOK(OR条件)
    if required_types:
        if not any(t in pokemon_types for t in required_types):
            return False
    # exclude_types: どれか含んでいたらNG
    if exclude_types:
        if any(t in pokemon_types for t in exclude_types):
            return False
    return True

def tags_match(pokemon_tags, required_tags) -> bool:
    if not required_tags:
        return True
    return any(tag in pokemon_tags for tag in required_tags)

def _parse_filter_json(llm_raw: str) -> Dict[str, Any]:
    raw = llm_raw.strip()
    # もし ```json 〜 ``` で囲まれていたら中身だけ取り出す
    if raw.startswith("```"):
        lines = raw.splitlines()
        if lines and lines[0].startswith("```"):
            lines = lines[1:]
        if lines and lines[-1].startswith("```"):
            lines = lines[:-1]
        raw = "\n".join(lines).strip()
    return json.loads(raw)

def main(llm_raw: str) -> Dict[str, Any]:
    # ① LLMのJSONをパース
    cond = _parse_filter_json(llm_raw)

    types = cond.get("types") or []
    exclude_types = cond.get("exclude_types") or []
    min_speed = cond.get("min_speed")
    min_attack = cond.get("min_attack")
    max_height_m = cond.get("max_height_m")
    max_weight_kg = cond.get("max_weight_kg")
    required_tags = cond.get("tags") or []
    sort_by = cond.get("sort_by") or "speed"
    sort_order = cond.get("sort_order") or "desc"
    limit = cond.get("limit") or 5

    candidates = []

    for p in POKEMONS:
        if not type_match(p["types"], types, exclude_types):
            continue

        stats = p["base_stats"]

        if min_speed is not None and stats["speed"] < float(min_speed):
            continue

        if min_attack is not None and stats["attack"] < float(min_attack):
            continue

        if max_height_m is not None and p["height_m"] > float(max_height_m):
            continue

        if max_weight_kg is not None and p["weight_kg"] > float(max_weight_kg):
            continue

        if not tags_match(p["tags"], required_tags):
            continue

        candidates.append(p)

    # ソート
    def sort_key(p):
        if sort_by == "speed":
            return p["base_stats"]["speed"]
        if sort_by == "attack":
            return p["base_stats"]["attack"]
        if sort_by == "height_m":
            return p["height_m"]
        if sort_by == "weight_kg":
            return p["weight_kg"]
        return 0

    reverse = (sort_order == "desc")
    candidates.sort(key=sort_key, reverse=reverse)

    results = candidates[: int(limit)]

    # ③ ユーザー向けメッセージ生成(テキストだけ返す)
    if not results:
        message_lines = [
            "条件に合うポケモンは見つかりませんでした。",
            "・タイプや「素早い」「小さい」などの条件を少しゆるめて、もう一度聞いてみてください。",
        ]
        return {"message": "\n".join(message_lines)}

    header = "検索結果(最大 {} 匹):".format(len(results))
    lines = [header]

    for i, p in enumerate(results, start=1):
        stats = p["base_stats"]
        line = (
            f"{i}. No.{p['id']} {p['name_jp']} / {p['name_en']}\n"
            f"   タイプ: {', '.join(p['types'])}\n"
            f"   身長: {p['height_m']}m / 体重: {p['weight_kg']}kg\n"
            f"   攻撃: {stats['attack']} / 素早さ: {stats['speed']}"
        )
        lines.append(line)

    return {"message": "\n".join(lines)}

image.png

image.png

ポケモンの検索は0.7秒くらいですね。

50匹

0.74秒
image.png

image.png

100匹

1.12秒
image.png

image.png

151匹

1.14秒
image.png

→もっと遅いかと思ったんですが、そこまで遅くならないですね。
まぁ、いいですね。本当はめっちゃおそいからRAGとかに置き換えようとかの話に繋げようと思ってました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?