app:
  description: Kubernetesマニフェストを解析し、リソース割当、プローブ、可用性に関する改善点と具体的なマニフェスト修正案を提案するワークフロー。マニフェストを一つのテキスト入力欄に貼り付けてください。
  icon: 🤖
  icon_background: '#FFEAD5'
  mode: workflow
  name: K8sデプロイ改善案（マニフェスト解析）
  use_icon_as_answer_icon: false
kind: app
version: 0.6.0
workflow:
  conversation_variables: []
  environment_variables: []
  features:
    file_upload:
      image:
        enabled: false
    opening_statement: ''
    suggested_questions: []
    suggested_questions_after_answer:
      enabled: false
    text_to_speech:
      enabled: false
  graph:
    edges:
    - data:
        sourceType: start
        targetType: code
      id: start-to-code1
      source: start
      target: code1
    - data:
        sourceType: code
        targetType: llm
      id: code1-to-llm1
      source: code1
      target: llm1
    - data:
        sourceType: llm
        targetType: end
      id: llm1-to-end
      source: llm1
      target: end
    nodes:
    - data:
        title: 開始
        type: start
        variables:
        - label: query
          max_length: 2048
          options: []
          required: true
          type: paragraph
          variable: query
      height: 90
      id: start
      position:
        x: 80
        y: 282
      type: custom
      width: 244
    - data:
        title: マニフェスト解析（コード）
        type: code
        code_language: python3
        code: "def _safe_str(v):\n    \"\"\"Convert any value to string safely.\"\"\
          \"\n    if v is None:\n        return \"\"\n    if isinstance(v, list):\n\
          \        return \"\\n\".join(str(i) for i in v)\n    return str(v)\n\ndef\
          \ _safe_list(v):\n    \"\"\"Ensure value is a list.\"\"\"\n    if v is None:\n\
          \        return []\n    if isinstance(v, str):\n        return [v]\n   \
          \ if isinstance(v, list):\n        return v\n    return [v]\n\ndef main(**kwargs)\
          \ -> dict:\n    inputs = kwargs\n    \"\"\"\n    簡易パーサ: 入力テキストを '---' で分割して各ドキュメントを走査し、\n\
          \    kind, metadata.name, containers情報、プローブやresources定義の有無を抽出する。\n    出力はJSON文字列（list）として返す。\n\
          \    注意: dictリテラルは使わず dict() を使って構築する。\n    \"\"\"\n    text = str(inputs.get(\"\
          query\", \"\"))\n    docs = text.split(\"\\n---\")\n    manifests = []\n\
          \    for doc in docs:\n        lines = doc.splitlines()\n        kind =\
          \ \"\"\n        name = \"\"\n        containers = []\n        liveness_present\
          \ = False\n        readiness_present = False\n        resources_present\
          \ = False\n        for i, line in enumerate(lines):\n            s = line.strip()\n\
          \            low = s.lower()\n            if low.startswith(\"kind:\"):\n\
          \                parts = s.split(\":\", 1)\n                if len(parts)\
          \ > 1:\n                    kind = parts[1].strip()\n            if low.startswith(\"\
          metadata:\"):\n                for j in range(i + 1, min(i + 10, len(lines))):\n\
          \                    sj = lines[j].strip()\n                    if sj.lower().startswith(\"\
          name:\"):\n                        parts = sj.split(\":\", 1)\n        \
          \                if len(parts) > 1:\n                            name =\
          \ parts[1].strip()\n                            break\n            if \"\
          livenessprobe\" in low or \"livenessprobe:\" in low:\n                liveness_present\
          \ = True\n            if \"readinessprobe\" in low or \"readinessprobe:\"\
          \ in low:\n                readiness_present = True\n            if low.startswith(\"\
          resources:\"):\n                resources_present = True\n            if\
          \ low.startswith(\"containers:\"):\n                # scan following lines\
          \ for container blocks\n                j = i + 1\n                while\
          \ j < len(lines):\n                    lj = lines[j].strip()\n         \
          \           if lj.startswith(\"-\"):\n                        # container\
          \ block start\n                        cont_name = \"\"\n              \
          \          cont_image = \"\"\n                        cont_liveness = False\n\
          \                        cont_readiness = False\n                      \
          \  cont_resources = False\n                        k = j + 1\n         \
          \               while k < min(j + 60, len(lines)):\n                   \
          \         lk = lines[k].strip()\n                            if lk.lower().startswith(\"\
          name:\"):\n                                parts = lk.split(\":\", 1)\n\
          \                                if len(parts) > 1:\n                  \
          \                  cont_name = parts[1].strip()\n                      \
          \      if lk.lower().startswith(\"image:\"):\n                         \
          \       parts = lk.split(\":\", 1)\n                                if len(parts)\
          \ > 1:\n                                    cont_image = parts[1].strip()\n\
          \                            if \"livenessprobe\" in lk.lower() or \"livenessprobe:\"\
          \ in lk.lower():\n                                cont_liveness = True\n\
          \                            if \"readinessprobe\" in lk.lower() or \"readinessprobe:\"\
          \ in lk.lower():\n                                cont_readiness = True\n\
          \                            if lk.lower().startswith(\"resources:\"):\n\
          \                                cont_resources = True\n               \
          \             # break heuristics: next container or end of block\n     \
          \                       if lk.startswith(\"-\") and k != j + 1:\n      \
          \                          break\n                            k += 1\n \
          \                       containers.append(dict(name=cont_name, image=cont_image,\
          \ liveness=cont_liveness, readiness=cont_readiness, resources=cont_resources))\n\
          \                        j = k\n                        continue\n     \
          \               j += 1\n        manifests.append(dict(kind=kind, name=name,\
          \ containers=containers, liveness_present=liveness_present, readiness_present=readiness_present,\
          \ resources_present=resources_present))\n    import json\n    return dict(parsed=json.dumps(manifests))\n"
        variables:
        - value_selector:
          - start
          - query
          variable: query
        outputs:
          parsed:
            type: string
            children: null
      height: 90
      id: code1
      position:
        x: 380
        y: 282
      type: custom
      width: 244
    - data:
        title: 改善案生成（LLM）
        type: llm
        model:
          provider: openai
          name: gpt-5-mini
          mode: chat
          completion_params:
            temperature: 0.6
        prompt_template:
        - role: system
          text: 'あなたはKubernetesのベストプラクティスに詳しいシニアSRE兼Kubernetesコンサルタントです。与えられたKubernetesマニフェストの解析結果をもとに、リソース定義（requests/limits）、liveness/readiness
            probe、レプリカ・可用性、PodDisruptionBudget、Pod anti-affinity、RollingUpdate戦略、StartupProbeの導入、config/secretの取り扱いなどの観点で改善点を指摘し、優先度（高/中/低）と具体的な修正例（マニフェストのスニペット）を提示してください。出力はまず要約（日本語）を短く示し、その後に構造化されたJSONを含め、最後にヒューマンリーダブルな実例と注意点を提示してください。JSONフォーマット（厳守）:

            {"summary": "短い要約", "issues": [{"id": "issue-1", "type": "resources|probe|availability|pdb|affinity|strategy|other",
            "severity": "high|medium|low", "description": "問題の説明", "recommendation":
            "推奨される修正の説明", "manifest_snippet": "yamlのスニペット（必要に応じて）"}], "recommendations":
            ["短い実践的な推奨一覧"]'
        - role: user
          text: '元のマニフェストテキスト:

            {{#start.query#}}


            解析結果(JSON文字列):

            {{#code1.parsed#}}


            上記を参照して、改善点と具体的なマニフェスト修正スニペットを日本語で出力してください。JSONフォーマットは厳守してください。'
        vision:
          enabled: false
        memory: null
        context:
          enabled: false
      height: 90
      id: llm1
      position:
        x: 680
        y: 282
      type: custom
      width: 244
    - data:
        title: 終了
        type: end
        outputs:
        - value_selector:
          - llm1
          - text
          variable: result
      height: 90
      id: end
      position:
        x: 980
        y: 282
      type: custom
      width: 244
  hash: null
