app:
  description: 部門別月次KPI（当月実績・予算・前月・前年同月）を入力して、差異分析コメント・要因推定・次月見通しを自動生成するワークフロー
  icon: 📊
  icon_background: '#FFEAD5'
  mode: workflow
  name: 経営企画 KPIコメント自動生成
  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: llm
      id: llm1-to-llm2
      source: llm1
      target: llm2
    - data:
        sourceType: llm
        targetType: end
      id: llm2-to-end
      source: llm2
      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    text = str(inputs.get(\"text\", \"\"\
          ))\n    import re, json\n    # 正規表現で「部門: 当月 予算 前月 前年」の形式を抽出\n    pattern\
          \ = re.compile(\n        r'(?P<dept>[^:\\n]+?)[:：]?\\s*(?:当月|実績)[:：]?\\\
          s*(?P<actual>[\\d,\\.]+)\\s*(?:予算)[:：]?\\s*(?P<budget>[\\d,\\.]+)\\s*(?:前月)[:：]?\\\
          s*(?P<prev>[\\d,\\.]+)\\s*(?:前年|前年同月)[:：]?\\s*(?P<yearago>[\\d,\\.]+)',\n\
          \        re.M\n    )\n    entries = []\n    def to_num(s):\n        try:\n\
          \            return float(s.replace(',', ''))\n        except:\n       \
          \     return None\n    for m in pattern.finditer(text):\n        dept =\
          \ m.group(\"dept\").strip()\n        actual = to_num(m.group(\"actual\"\
          ))\n        budget = to_num(m.group(\"budget\"))\n        prev = to_num(m.group(\"\
          prev\"))\n        yearago = to_num(m.group(\"yearago\"))\n        e = dict()\n\
          \        e[\"department\"] = dept\n        e[\"actual\"] = actual\n    \
          \    e[\"budget\"] = budget\n        e[\"prev_month\"] = prev\n        e[\"\
          year_ago\"] = yearago\n        entries.append(e)\n    # フォールバック: 単純行分割で先頭5項目を使う\n\
          \    if not entries:\n        lines = [l.strip() for l in text.splitlines()\
          \ if l.strip()]\n        for l in lines:\n            parts = re.split(r'\\\
          s+', l)\n            if len(parts) >= 5:\n                dept = parts[0].rstrip(':：')\n\
          \                try:\n                    actual = float(parts[1].replace(',',\
          \ ''))\n                    budget = float(parts[2].replace(',', ''))\n\
          \                    prev = float(parts[3].replace(',', ''))\n         \
          \           yearago = float(parts[4].replace(',', ''))\n               \
          \ except:\n                    continue\n                e = dict()\n  \
          \              e[\"department\"] = dept\n                e[\"actual\"] =\
          \ actual\n                e[\"budget\"] = budget\n                e[\"prev_month\"\
          ] = prev\n                e[\"year_ago\"] = yearago\n                entries.append(e)\n\
          \    return dict(output=json.dumps(entries, ensure_ascii=False))\n"
        variables:
        - value_selector:
          - start
          - query
          variable: text
        outputs:
          output:
            type: string
            children: null
      height: 90
      id: code1
      position:
        x: 380
        y: 282
      type: custom
      width: 244
    - data:
        title: 分析（差異・要因・見通し）
        type: llm
        model:
          provider: openai
          name: gpt-5-mini
          mode: chat
          completion_params:
            temperature: 0.6
        prompt_template:
        - role: system
          text: 'あなたは経営企画の専門家です。与えられた部門別KPIデータをもとに、経営会議や社長報告に使える下書きコメントを作成してください。出力には必ず以下のセクションを含めること:
            1) 要約（1-2文） 2) 部門別の差異と示唆（箇条書き、各部門ごとに示唆を明記） 3) 主な要因の推定（箇条書き、理由を簡潔に） 4)
            次月見通し（1-2文） 5) 推奨アクション（箇条書）。数値の羅列ではなく、解釈と示唆に注力して、日本語でビジネス向けのトーンで書いてください。


            ---


            【最終出力フォーマット (この指示が最優先)】

            最終出力は **読みやすい日本語の Markdown** で返してください。JSON

            配列・オブジェクトをそのまま出力してはいけません。

            - 見出し (## / ###) でセクションを区切る

            - リスト (- / 1.) や表 (| ... |) で構造化

            - 各項目の意図や根拠が分かるよう短い説明文を添える

            - 営業デモで顧客に直接見せられる体裁にする


            (構造化されたデータ項目を内部で扱うのは構いませんが、出力時に Markdown

            に変換してください。)'
        - role: user
          text: 'ユーザー入力:

            {{#start.query#}}


            抽出データ:

            {{#code1.output#}}


            注意: 不確かな点や抜けがあれば合理的な仮定を明示し、その前提で示唆を述べてください。出力は読みやすく、会議資料にそのまま貼れるレベルを目指してください。'
        vision:
          enabled: false
        memory: null
        context:
          enabled: false
      height: 90
      id: llm1
      position:
        x: 680
        y: 282
      type: custom
      width: 244
    - data:
        title: 仕上げ（会議用整形）
        type: llm
        model:
          provider: openai
          name: gpt-5-mini
          mode: chat
          completion_params:
            temperature: 0.6
        prompt_template:
        - role: system
          text: 'あなたは編集者です。下書きを経営会議や社長報告に適した簡潔で洞察的な最終文面に整えてください。長すぎず、重要な示唆が一目で分かるように段落と箇条書きで整理してください。箇条書きは最大5項目を目安に。


            ---


            【最終出力フォーマット (この指示が最優先)】

            最終出力は **読みやすい日本語の Markdown** で返してください。JSON

            配列・オブジェクトをそのまま出力してはいけません。

            - 見出し (## / ###) でセクションを区切る

            - リスト (- / 1.) や表 (| ... |) で構造化

            - 各項目の意図や根拠が分かるよう短い説明文を添える

            - 営業デモで顧客に直接見せられる体裁にする


            (構造化されたデータ項目を内部で扱うのは構いませんが、出力時に Markdown

            に変換してください。)'
        - role: user
          text: '下書き:

            {{#llm1.text#}}


            指示: 文章の冗長な部分を削り、見出し・要約・重要な示唆を強調して最終版を出力してください。出力は日本語で、会議資料の「コメント欄」に貼れるレベルでお願いします。'
        vision:
          enabled: false
        memory: null
        context:
          enabled: false
      height: 90
      id: llm2
      position:
        x: 980
        y: 282
      type: custom
      width: 244
    - data:
        title: 終了
        type: end
        outputs:
        - value_selector:
          - llm2
          - text
          variable: result
      height: 90
      id: end
      position:
        x: 1280
        y: 282
      type: custom
      width: 244
  hash: null
