app:
  description: 商社向けの為替影響試算ワークフロー。ユーザー入力から為替シナリオを抽出し、各シナリオごとの仕入コスト・粗利への影響を数値試算し、分かりやすいレポートと機械判読可能なJSONを出力します。
  icon: 💱
  icon_background: '#FFEAD5'
  mode: workflow
  name: 為替影響試算
  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: llm
      id: start-to-llm1
      source: start
      target: llm1
    - data:
        sourceType: llm
        targetType: code
      id: llm1-to-code1
      source: llm1
      target: code1
    - data:
        sourceType: code
        targetType: llm
      id: code1-to-llm2
      source: code1
      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: llm
        model:
          provider: openai
          name: gpt-5-mini
          mode: chat
          completion_params:
            temperature: 0.2
        prompt_template:
        - role: system
          text: 'あなたは財務・為替に詳しいアナリストです。ユーザーの自由記述から、以下のJSON形式だけを出力してください。JSONは必ずパース可能でなければなりません。フィールド:

            - base_currency: 文字列（例: "JPY"）

            - current_rate: 現行為替レート（数値。例: 110.0）

            - baseline_revenue: ベースの売上高（数値。任意。なければ0）

            - baseline_cogs: ベースの仕入原価（数値。任意。なければ0）

            - baseline_gross_profit: ベースの粗利（金額）。無ければ売上-原価で計算される。

            - fx_sensitivity: 仕入原価のうち為替影響を受ける割合（0-1の数値。例:0.8 = 80%）

            - scenarios: 配列。各要素は {"name": "シナリオ名", "rate": 数値} の形式。

            出力例:

            {"base_currency":"JPY","current_rate":110.0,"baseline_revenue":1000000,"baseline_cogs":700000,"fx_sensitivity":0.8,"scenarios":[{"name":"円安+10%","rate":121.0},{"name":"円高-10%","rate":99.0}]}

            出力は上記JSONのみ、追加の説明や余分なテキストは付けないでください。'
        - role: user
          text: '入力: {{#start.query#}}

            上記入力から前述のJSONを抽出してください。'
        vision:
          enabled: false
        memory: null
        context:
          enabled: false
      height: 90
      id: llm1
      position:
        x: 380
        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    import json\n    text = inputs.get(\"\
          text\", \"\")\n    try:\n        data = json.loads(text)\n    except Exception:\n\
          \        return dict(output=dict(error=\"failed_to_parse_json\", raw=text))\n\
          \    def to_float(v, default=0.0):\n        try:\n            return float(v)\n\
          \        except Exception:\n            return default\n    current_rate\
          \ = to_float(data.get(\"current_rate\", 0.0))\n    baseline_revenue = to_float(data.get(\"\
          baseline_revenue\", 0.0))\n    baseline_cogs = to_float(data.get(\"baseline_cogs\"\
          , 0.0))\n    # baseline_gross_profit may be provided; if not, compute from\
          \ revenue - cogs\n    provided_gross = data.get(\"baseline_gross_profit\"\
          , None)\n    if provided_gross is None:\n        baseline_gross = baseline_revenue\
          \ - baseline_cogs\n    else:\n        baseline_gross = to_float(provided_gross,\
          \ baseline_revenue - baseline_cogs)\n    fx_sensitivity = to_float(data.get(\"\
          fx_sensitivity\", 1.0))\n    scenarios = data.get(\"scenarios\", [])\n \
          \   results = []\n    for s in scenarios:\n        # s is expected to be\
          \ a mapping with keys 'name' and 'rate'\n        name = s.get(\"name\",\
          \ \"scenario\")\n        rate = to_float(s.get(\"rate\", current_rate))\n\
          \        delta = 0.0\n        if current_rate:\n            delta = (rate\
          \ - current_rate) / current_rate\n        # assume fx_sensitivity is proportion\
          \ of COGS exposed to FX movement\n        new_cogs = baseline_cogs * (1\
          \ + delta * fx_sensitivity)\n        new_gross = baseline_revenue - new_cogs\n\
          \        baseline_margin_pct = None\n        new_margin_pct = None\n   \
          \     if baseline_revenue:\n            try:\n                baseline_margin_pct\
          \ = round((baseline_gross / baseline_revenue) * 100, 4)\n            except\
          \ Exception:\n                baseline_margin_pct = None\n            try:\n\
          \                new_margin_pct = round((new_gross / baseline_revenue) *\
          \ 100, 4)\n            except Exception:\n                new_margin_pct\
          \ = None\n        impact_gross_absolute = round(new_gross - baseline_gross,\
          \ 2)\n        impact_margin_pct = None\n        if baseline_margin_pct is\
          \ not None and new_margin_pct is not None:\n            impact_margin_pct\
          \ = round(new_margin_pct - baseline_margin_pct, 4)\n        results.append(dict(name=name,\
          \ rate=rate, new_cogs=round(new_cogs, 2), new_gross=round(new_gross, 2),\
          \ baseline_gross=round(baseline_gross, 2), baseline_margin_pct=baseline_margin_pct,\
          \ new_margin_pct=new_margin_pct, impact_gross_absolute=impact_gross_absolute,\
          \ impact_margin_pct=impact_margin_pct))\n    return dict(output=dict(results=results,\
          \ assumptions=dict(current_rate=current_rate, baseline_revenue=baseline_revenue,\
          \ baseline_cogs=baseline_cogs, baseline_gross=baseline_gross, fx_sensitivity=fx_sensitivity)))\n"
        variables:
        - value_selector:
          - llm1
          - text
          variable: text
        outputs:
          output:
            type: object
            children: null
      height: 90
      id: code1
      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: 'あなたは財務分析に長けたアナリストです。以下のルールに従って日本語で最終レポートを作成してください。

            1) 最初に『要約（短く）』を1段落で示す。

            2) 次に各シナリオごとの主要数値（仕入原価、新粗利、粗利差額、粗利率変化）をMarkdown表で示す。

            3) それぞれのシナリオについて短い解説（2-3行）を付ける。

            4) 最後に『機械判読可能なJSON』をコードブロックで貼り、コードブロック内には計算結果のJSON（下流処理用）をそのまま出力すること。

            5) 前提・仮定がある場合は『前提』として列挙すること。

            出力は読みやすく、JSON部分は厳密にパース可能であること。'
        - role: user
          text: '計算結果: {{#code1.output#}}


            元の入力: {{#start.query#}}


            上記を使ってレポートを作成してください。'
        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
      id: end
      height: 90
      position:
        x: 1280
        y: 282
      type: custom
      width: 244
  hash: null
