app:
  description: 物流在庫データ（品目、現在庫数、入庫予定、出庫実績）を分析し、在庫サマリー・欠品リスク・過剰在庫・発注推奨リストを生成するワークフロー
  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: 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: "import csv\nimport io\nimport json\n\ndef _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\
          \    raw = inputs.get(\"text\", \"\")\n    # 想定入力:\n    # - CSV形式（ヘッダー行あり）\n\
          \    #   例: 品目コード,品目名,現在庫数,入庫予定,出庫実績(日次平均),リードタイム日数,安全在庫\n    # - または簡易なテキスト表。パースできない場合はそのままLLMに渡す。\n\
          \    parsed_rows = []\n    error = None\n\n    try:\n        # CSVとしてトライ\n\
          \        f = io.StringIO(raw)\n        sniffer = csv.Sniffer()\n       \
          \ sample = raw[:1000]\n        dialect = sniffer.sniff(sample)\n       \
          \ reader = csv.DictReader(f, dialect=dialect)\n        for row in reader:\n\
          \            clean_row = {}\n            for k, v in row.items():\n    \
          \            if k is None:\n                    continue\n             \
          \   key = str(k).strip()\n                val = \"\" if v is None else str(v).strip()\n\
          \                clean_row[key] = val\n            if len(clean_row) > 0:\n\
          \                parsed_rows.append(clean_row)\n    except Exception as\
          \ e:\n        error = \"csv_parse_failed: \" + str(e)\n\n    result_obj\
          \ = dict(\n        structured_rows=parsed_rows,\n        is_structured=len(parsed_rows)\
          \ > 0,\n        raw_text=raw,\n        parse_error=error\n    )\n\n    result_json\
          \ = json.dumps(result_obj, ensure_ascii=False)\n    return dict(output=result_json)\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.2
        prompt_template:
        - role: system
          text: "あなたは物流・在庫管理のエキスパートとして、在庫分析と発注提案を行うアシスタントです。\n入力として在庫データ（品目、現在庫数、入庫予定、出庫実績など）が与えられます。\n\
            \n目的:\n(1) 在庫状況サマリー\n(2) 欠品リスクのある品目\n(3) 過剰在庫の品目\n(4) 発注推奨リスト（品目 + 推奨発注数量\
            \ + 推奨発注日）\n\n【入力データの形式について】\ncode1ノードから、以下のJSON文字列が渡されます:\n{\n  \"structured_rows\"\
            : [ { ... 各品目の行 ... } ],\n  \"is_structured\": true/false,\n  \"raw_text\"\
            : \"元の入力テキスト\",\n  \"parse_error\": \"エラー内容 または null\"\n}\n\n- is_structured\
            \ が true の場合:\n  - structured_rows は CSVとして解釈された行のリストです。\n  - 各行には「品目コード」「品目名」「現在庫数」「入庫予定」「出庫実績」「リードタイム日数」「安全在庫」などの列が含まれている可能性があります（正確な列名は入力依存）。\n\
            \  - 数量・日数など数値として使える列は、あなたの判断で数値として解釈してかまいません。\n\n- is_structured が false\
            \ の場合:\n  - structured_rows は空リストです。\n  - raw_text を人間として読める形で解釈し、テキストベースで分析してください。\n\
            \n【分析・ロジックの指針（厳密な数式というより実務的な目安でよい）】\n- 出庫実績:\n  - 「出庫実績(日次平均)」「日平均出庫」「平均出庫」などと推定できる列があれば、それを日次需要とみなす。\n\
            \  - 無い場合は、テキストから需要感を読み取り、定性的にコメントする。\n\n- 欠品リスク判定の例:\n  - 現在庫 + 近い将来の入庫予定\
            \ より、リードタイム中（および少し先）に必要な数量が多い場合を「欠品リスクあり」とする。\n  - 安全在庫が列として存在する場合:\n\
            \    - 「現在庫 - 安全在庫」がゼロもしくはマイナスになりそうな品目を欠品リスクとみなす。\n  - 数値情報が足りない場合は、「情報不足だが、現状の在庫水準からみて相対的に低在庫と思われる」といった形で記述する。\n\
            \n- 過剰在庫判定の例:\n  - 現在庫が、日次出庫実績の 30～60日分を大きく超えている場合を「過剰在庫」とみなす。\n  - 安全在庫や標準在庫があれば、それを大きく上回る在庫を過剰とみなす。\n\
            \  - 数値が不足する場合は、「他品目と比べて相対的に高水準の在庫」として定性的にコメントする。\n\n- 発注推奨リスト作成の例:\n\
            \  - 対象: 欠品リスクの高い品目、または在庫が需要に対して明らかに不足している品目。\n  - 推奨発注数量:\n    - 需要（出庫実績の日数分）＋安全在庫\
            \ を満たすような水準を目安に数量を提案する。\n    - データが不足する場合は、「〇〇～〇〇程度」などレンジで提案してもよい。\n \
            \ - 推奨発注日:\n    - リードタイム日数が分かる場合: 欠品が発生しそうなタイミングから逆算して発注日を提案。\n    - 分からない場合:\
            \ 「できるだけ早く」「〇日以内」などの形で具体的に提案。\n  - 必要に応じて、なぜその数量・タイミングを提案したか、理由も簡潔に書く。\n\
            \n【出力フォーマット（日本語で、構造化して書く）】\n以下の構成で、見出し付きで出力してください:\n\n1. 在庫状況サマリー\n  \
            \ - 全体の傾向（在庫水準、偏り、欠品/過剰の傾向など）を2～5行程度でまとめる。\n   - データが構造化されている場合は、「品目数」「平均在庫日数の傾向」など定量的なコメントも含める。\n\
            \n2. 欠品リスクのある品目\n   - 箇条書きで、以下のような情報を含めて列挙:\n     - 品目コード/品目名\n     -\
            \ 現在庫・入庫予定（分かる範囲で）\n     - 想定日次出庫もしくは需要の傾向\n     - 欠品リスクの理由（安全在庫割れ、需要急増など）\n\
            \   - 特に該当がない場合は「現時点で明確な欠品リスクは見当たりません」と明記。\n\n3. 過剰在庫の品目\n   - 箇条書きで、以下のような情報を含めて列挙:\n\
            \     - 品目コード/品目名\n     - 現在庫・日次出庫の目安\n     - 想定在庫日数（分かる場合）\n     - 過剰と判断した理由\n\
            \   - 特に該当がない場合は「現時点で明確な過剰在庫は見当たりません」と明記。\n\n4. 発注推奨リスト（品目＋数量＋推奨発注日）\n\
            \   - 表形式に近い箇条書きで、各品目について:\n     - 品目コード/品目名\n     - 推奨発注数量\n     - 推奨発注日\n\
            \     - 簡単な根拠（例: 「リードタイムX日・日次出庫Y個・安全在庫Z個を考慮」）\n   - 発注不要と判断される場合は、その旨を明記し、代わりに「モニタリングを推奨」などコメントする。\n\
            \n【記載上の注意】\n- 数値が不足している場合でも、できるだけ実務で使えるコメントを出すこと。\n- 不確実な前提には「想定」「推定」などの言葉を付けて、憶測であることを明示する。\n\
            - 出力は日本語で、現場担当者がそのまま検討資料に使えるレベルの具体性を意識してください。\n\n\n---\n\n【最終出力フォーマット\
            \ (この指示が最優先)】\n最終出力は **読みやすい日本語の Markdown** で返してください。JSON\n配列・オブジェクトをそのまま出力してはいけません。\n\
            - 見出し (## / ###) でセクションを区切る\n- リスト (- / 1.) や表 (| ... |) で構造化\n- 各項目の意図や根拠が分かるよう短い説明文を添える\n\
            - 営業デモで顧客に直接見せられる体裁にする\n\n(構造化されたデータ項目を内部で扱うのは構いませんが、出力時に Markdown\nに変換してください。)"
        - role: user
          text: '以下は在庫データの前処理結果です（JSON文字列）:

            {{#code1.output#}}


            このデータに基づき、

            (1)在庫状況サマリー

            (2)欠品リスクのある品目

            (3)過剰在庫の品目

            (4)発注推奨リスト（品目+数量+推奨発注日）

            を上記フォーマットに沿って作成してください。

            '
        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
