app:
  description: IR向け：KPI時系列データを解析して短い解説文と要因分析コメントを生成するワークフロー
  icon: 📈
  icon_background: '#FFEAD5'
  mode: workflow
  name: IR 業績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: 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\
          \    text = str(inputs.get(\"text\", \"\"))\n    import re, statistics,\
          \ math\n    # 行ごとに分割して有効行を抽出\n    lines = [l.strip() for l in text.splitlines()\
          \ if l.strip()]\n    parsed = []\n    for line in lines:\n        # カンマ/タブ/セミコロン区切りのCSV風\
          \ \"date,value\"\n        parts = re.split(r'[,\\t;]+', line)\n        if\
          \ len(parts) >= 2:\n            date = parts[0].strip()\n            val\
          \ = parts[1].strip().replace(',', '')\n            try:\n              \
          \  v = float(val)\n            except:\n                m = re.search(r'(-?[\\\
          d\\.]+)', val)\n                if m:\n                    v = float(m.group(1))\n\
          \                else:\n                    continue\n            parsed.append((date,\
          \ v))\n            continue\n        # \"date: value\" や \"date value\"\
          \ パターン\n        m = re.match(r'(.+?)[\\:\\s]+(-?[\\d,\\.]+)', line)\n  \
          \      if m:\n            date = m.group(1).strip()\n            val = m.group(2).replace(',',\
          \ '')\n            try:\n                v = float(val)\n            except:\n\
          \                continue\n            parsed.append((date, v))\n    if\
          \ not parsed:\n        return dict(analysis=\"エラー: 時系列データが解析できませんでした。入力形式は「YYYY-MM,\
          \ 1234」または「日付: 値」の行を想定しています。\")\n    # 日付文字列でソート（簡易）\n    try:\n       \
          \ parsed_sorted = sorted(parsed, key=lambda x: x[0])\n    except:\n    \
          \    parsed_sorted = parsed\n    values = [v for (_, v) in parsed_sorted]\n\
          \    n = len(values)\n    first = values[0]\n    last = values[-1]\n   \
          \ # 変化率（%）\n    change_pct = None\n    try:\n        if first != 0:\n  \
          \          change_pct = (last - first) / abs(first) * 100.0\n    except:\n\
          \        change_pct = None\n    avg = statistics.mean(values) if n > 0 else\
          \ 0.0\n    stdev = statistics.pstdev(values) if n > 0 else 0.0\n    vol_pct\
          \ = (stdev / avg * 100.0) if avg != 0 else 0.0\n    # 簡易トレンド判定\n    trend\
          \ = \"stable\"\n    if change_pct is not None:\n        if change_pct >\
          \ 5.0:\n            trend = \"increasing\"\n        elif change_pct < -5.0:\n\
          \            trend = \"decreasing\"\n        else:\n            trend =\
          \ \"stable\"\n    # 要因候補（ルールベースの簡易判定）\n    factors = []\n    if vol_pct\
          \ > 15.0:\n        factors.append(\"変動が大きく、四半期要因や一時的イベントの影響が疑われます\")\n \
          \   else:\n        factors.append(\"変動は比較的安定しています\")\n    if trend == \"\
          increasing\":\n        factors.append(\"中長期の成長トレンドが見られます\")\n    elif trend\
          \ == \"decreasing\":\n        factors.append(\"継続的な下振れ要因がある可能性があります\")\n\
          \    # 出力テキストを構成\n    lines_out = []\n    lines_out.append(\"data_points:\
          \ \" + str(n))\n    lines_out.append(\"first_value: \" + str(first))\n \
          \   lines_out.append(\"last_value: \" + str(last))\n    if change_pct is\
          \ not None:\n        lines_out.append(\"change_pct: \" + \"{:.2f}\".format(change_pct))\n\
          \    else:\n        lines_out.append(\"change_pct: N/A\")\n    lines_out.append(\"\
          mean: \" + \"{:.2f}\".format(avg))\n    lines_out.append(\"vol_pct: \" +\
          \ \"{:.2f}\".format(vol_pct))\n    lines_out.append(\"trend: \" + trend)\n\
          \    lines_out.append(\"factors: \" + \"; \".join(factors))\n    out = \"\
          \\n\".join(lines_out)\n    return dict(analysis=out)\n"
        variables:
        - value_selector:
          - start
          - query
          variable: text
        outputs:
          analysis:
            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: あなたはIR向けの短い解説文と要因分析を作成するアシスタントです。出力は日本語で、以下のフォーマットに従ってください。1) 短い解説文（2〜3文程度）でトレンドの要点を述べる。2)
            箇条書きで主要な要因候補を3つ以内で列挙する。推定の根拠として、解析済みの要約を参照しつつ、過度に断定しない表現を使ってください。
        - role: user
          text: '元データ（生入力）:

            {{#start.query#}}


            前処理・統計要約:

            {{#code1.analysis#}}


            上記を参照して、IR向けに「短い解説文」と「想定される要因（箇条書き、各行1文）」を生成してください。出力はプレーンテキストで、解説 → 空行
            → 箇条書き の順にしてください。'
        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
