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: parameter-extractor
      id: start-to-pe1
      source: start
      target: pe1
    - data:
        sourceType: parameter-extractor
        targetType: code
      id: pe1-to-code1
      source: pe1
      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: parameter-extractor
        model:
          provider: openai
          name: gpt-5-mini
          mode: chat
        query:
        - start
        - query
        parameters:
        - name: reasons
          type: string
          description: 退職理由のタグ/要約（複数レコードならカンマ区切りで）
          required: true
        - name: organization_issues
          type: string
          description: 組織的な問題点（カンマ区切り）
          required: false
        - name: retainability_scores
          type: string
          description: 引き止め可能性スコア（0-1の数値、複数ならカンマ区切り）
          required: false
        - name: suggestions
          type: string
          description: 被面談者からの改善提案・自由記述（複数ならカンマ区切り）
          required: false
        reasoning_mode: prompt
        instruction: 入力された面談記録・アンケートから、退職理由（タグ化可能な短文をカンマ区切り）、組織課題（カンマ区切り）、引き止め可能性スコア（0-1、カンマ区切り）、被面談者の提案を抽出してください。複数レコードがある場合は各レコード分をカンマ区切りで列挙してください。
      height: 90
      id: pe1
      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    # 入力を取得（各項目はカンマ区切りの文字列を想定）\n    reasons_raw\
          \ = inputs.get(\"reasons\", \"\")\n    org_raw = inputs.get(\"organization_issues\"\
          , \"\")\n    scores_raw = inputs.get(\"retainability_scores\", \"\")\n \
          \   suggestions_raw = inputs.get(\"suggestions\", \"\")\n\n    # パースしてリスト化\n\
          \    reasons_list = [r.strip() for r in reasons_raw.split(\",\") if r.strip()]\n\
          \    org_list = [o.strip() for o in org_raw.split(\",\") if o.strip()]\n\
          \    scores_list = list()\n    for s in scores_raw.split(\",\"):\n     \
          \   s_val = s.strip()\n        if not s_val:\n            continue\n   \
          \     try:\n            scores_list.append(float(s_val))\n        except:\n\
          \            try:\n                # パーセンテージ表記対応\n                scores_list.append(float(s_val.replace(\"\
          %\", \"\")) / 100.0)\n            except:\n                # 解析不能な値は無視\n\
          \                pass\n\n    # 件数集計\n    reason_counts = dict()\n    for\
          \ r in reasons_list:\n        reason_counts[r] = reason_counts.get(r, 0)\
          \ + 1\n    org_counts = dict()\n    for o in org_list:\n        org_counts[o]\
          \ = org_counts.get(o, 0) + 1\n\n    # 上位理由を整形\n    top_reasons_list = sorted(list(reason_counts.items()),\
          \ key=lambda x: x[1], reverse=True)\n    top_reasons = list()\n    for item\
          \ in top_reasons_list:\n        name = item[0]\n        cnt = item[1]\n\
          \        top_reasons.append(str(name) + \" (\" + str(cnt) + \")\")\n\n \
          \   average_retainability = None\n    if len(scores_list) > 0:\n       \
          \ average_retainability = sum(scores_list) / float(len(scores_list))\n\n\
          \    result = dict(\n        record_count = len(reasons_list),\n       \
          \ reason_counts = reason_counts,\n        org_issue_counts = org_counts,\n\
          \        top_reasons = top_reasons,\n        average_retainability = average_retainability,\n\
          \        suggestions = suggestions_raw\n    )\n    return dict(output=result)\n"
        variables:
        - value_selector:
          - pe1
          - reasons
          variable: reasons
        - value_selector:
          - pe1
          - organization_issues
          variable: organization_issues
        - value_selector:
          - pe1
          - retainability_scores
          variable: retainability_scores
        - value_selector:
          - pe1
          - suggestions
          variable: suggestions
        outputs:
          output:
            type: object
            children: null
      height: 90
      id: code1
      position:
        x: 680
        y: 282
      type: custom
      width: 244
    - data:
        title: AI分析・提言生成
        type: llm
        model:
          provider: openai
          name: gpt-5-mini
          mode: chat
          completion_params:
            temperature: 0.6
        prompt_template:
        - role: system
          text: 'あなたは上級人事アナリストです。与えられた面談記録と抽出済みの数値・集計データをもとに、以下を出力してください：1) 退職理由の構造化された要約（主要カテゴリと割合）、2)
            組織的課題の抽出とその影響度、3) 各退職者の引き止め可能性評価の総括（平均値・分布の解釈）、4) 優先度付きの改善提言（短期/中期/長期）、5)
            組織の構造的課題を可視化するためのダッシュボードに出すべき指標一覧（メトリクス名と説明）。出力は箇条書きと、最後にJSON形式の要約オブジェクト（keys:
            summary, recommendations, metrics）を含めてください。


            ---


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

            最終出力は **読みやすい日本語の 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: 980
        y: 282
      type: custom
      width: 244
    - data:
        title: 終了
        type: end
        outputs:
        - value_selector:
          - llm1
          - text
          variable: result
      height: 90
      id: end
      position:
        x: 1280
        y: 282
      type: custom
      width: 244
  hash: null
