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-extract1
      source: start
      target: extract1
    - data:
        sourceType: parameter-extractor
        targetType: code
      id: extract1-to-code1
      source: extract1
      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: 不審者特徴抽出（Parameter Extractor）
        type: parameter-extractor
        model:
          provider: openai
          name: gpt-5-mini
          mode: chat
          completion_params:
            temperature: 0.2
        query:
        - start
        - query
        parameters:
        - name: gender
          type: string
          description: 性別（男性/女性/不明）
          required: false
        - name: estimated_age
          type: string
          description: '推定年齢（例: 20代前半、40代後半）'
          required: false
        - name: height_cm
          type: number
          description: 推定身長（cmで数値が与えられれば数値、目測は近似可）
          required: false
        - name: clothing_top
          type: string
          description: 上衣の特徴（色・種類）
          required: false
        - name: clothing_bottom
          type: string
          description: 下衣の特徴（色・種類）
          required: false
        - name: outerwear
          type: string
          description: 上着・コート等の特徴
          required: false
        - name: hair
          type: string
          description: 髪型・色
          required: false
        - name: backpack_bag
          type: string
          description: バッグ・リュック等の有無と特徴
          required: false
        - name: vehicle_description
          type: string
          description: 車両の記述（ナンバー不明なら色・車種等）
          required: false
        - name: direction
          type: string
          description: '最終確認された移動方向（例: 北へ走る、建物内へ入る）'
          required: false
        - name: distinctive_features
          type: string
          description: 特徴的な外見（刺青、傷、持ち物、発声など）
          required: false
        - name: time_of_incident
          type: string
          description: 事件・目撃時刻（可能な限り正確に）
          required: false
        - name: location_of_incident
          type: string
          description: 目撃場所（建物名、通り名、目印など）
          required: false
        - name: confidence_level
          type: string
          description: 目撃者の確信度や映像の鮮明さの簡単な評価（高/中/低）
          required: false
        reasoning_mode: prompt
        instruction: '以下のテキスト（目撃情報、映像記録の記述、無線メモ等）から、不審者の特徴を出来るだけ構造化して抽出してください。

          ・各項目は可能な限り簡潔に標準的な表現で返すこと（例: "黒色パーカー", "青ジーンズ", "170-175" 等）。

          ・不明な項目は空のままにし、推定の場合は「推定:」を先頭につける。

          ・時間・場所は元の記述に忠実に抽出する。

          ・特徴が複数ある場合はカンマ区切りで列挙する（文字列として返す）。

          ・出力はDifyのパラメータ仕様に従い、各パラメータに対応する値のみを返してください（JSON等ではなく、内部的にパラメータにマッピングされます）。

          '
      height: 90
      id: extract1
      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    gender = str(inputs.get(\"\
          gender\", \"\") or \"\")\n    estimated_age = str(inputs.get(\"estimated_age\"\
          , \"\") or \"\")\n    height = inputs.get(\"height_cm\", \"\")\n    if height\
          \ is None or height == \"\":\n        height_str = \"\"\n    else:\n   \
          \     try:\n            height_str = str(int(float(height))) + \" cm\"\n\
          \        except:\n            height_str = str(height)\n    clothing_top\
          \ = str(inputs.get(\"clothing_top\", \"\") or \"\")\n    clothing_bottom\
          \ = str(inputs.get(\"clothing_bottom\", \"\") or \"\")\n    outerwear =\
          \ str(inputs.get(\"outerwear\", \"\") or \"\")\n    hair = str(inputs.get(\"\
          hair\", \"\") or \"\")\n    backpack_bag = str(inputs.get(\"backpack_bag\"\
          , \"\") or \"\")\n    vehicle = str(inputs.get(\"vehicle_description\",\
          \ \"\") or \"\")\n    direction = str(inputs.get(\"direction\", \"\") or\
          \ \"\")\n    distinctive = str(inputs.get(\"distinctive_features\", \"\"\
          ) or \"\")\n    time = str(inputs.get(\"time_of_incident\", \"\") or \"\"\
          )\n    location = str(inputs.get(\"location_of_incident\", \"\") or \"\"\
          )\n    confidence = str(inputs.get(\"confidence_level\", \"\") or \"\")\n\
          \    # 結合してサマリを作成\n    lines = []\n    if gender:\n        lines.append(\"\
          性別: \" + gender)\n    if estimated_age:\n        lines.append(\"推定年齢: \"\
          \ + estimated_age)\n    if height_str:\n        lines.append(\"推定身長: \"\
          \ + height_str)\n    if clothing_top:\n        lines.append(\"上衣: \" + clothing_top)\n\
          \    if clothing_bottom:\n        lines.append(\"下衣: \" + clothing_bottom)\n\
          \    if outerwear:\n        lines.append(\"上着: \" + outerwear)\n    if hair:\n\
          \        lines.append(\"髪: \" + hair)\n    if backpack_bag:\n        lines.append(\"\
          バッグ: \" + backpack_bag)\n    if vehicle:\n        lines.append(\"車両: \"\
          \ + vehicle)\n    if direction:\n        lines.append(\"移動方向: \" + direction)\n\
          \    if distinctive:\n        lines.append(\"特徴: \" + distinctive)\n   \
          \ if time:\n        lines.append(\"時刻: \" + time)\n    if location:\n  \
          \      lines.append(\"場所: \" + location)\n    if confidence:\n        lines.append(\"\
          証拠鮮明度/確信度: \" + confidence)\n    if not lines:\n        summary = \"目撃情報から抽出できた特徴はありません。追加情報を収集してください。\"\
          \n    else:\n        summary = \"目撃サマリ:\\n\" + \"\\n\".join([\"- \" + l\
          \ for l in lines])\n    # 捜索向けの簡易優先事項を追加\n    search_notes = []\n    if\
          \ direction:\n        search_notes.append(\"最終方向: \" + direction)\n    if\
          \ distinctive:\n        search_notes.append(\"優先確認: \" + distinctive)\n\
          \    if clothing_top or clothing_bottom or outerwear:\n        search_notes.append(\"\
          服装注目: \" + (clothing_top + (\" / \" + clothing_bottom if clothing_bottom\
          \ else \"\")).strip(\" / \"))\n    if vehicle:\n        search_notes.append(\"\
          車両情報がある場合は周辺の出口を確認\")\n    if not search_notes:\n        search_text = \"\
          \"\n    else:\n        search_text = \"\\n捜索ポイント:\\n\" + \"\\n\".join([\"\
          - \" + s for s in search_notes])\n    final = summary + (\"\\n\" + search_text\
          \ if search_text else \"\")\n    return dict(summary=final)\n"
        variables:
        - value_selector:
          - extract1
          - gender
          variable: gender
        - value_selector:
          - extract1
          - estimated_age
          variable: estimated_age
        - value_selector:
          - extract1
          - height_cm
          variable: height_cm
        - value_selector:
          - extract1
          - clothing_top
          variable: clothing_top
        - value_selector:
          - extract1
          - clothing_bottom
          variable: clothing_bottom
        - value_selector:
          - extract1
          - outerwear
          variable: outerwear
        - value_selector:
          - extract1
          - hair
          variable: hair
        - value_selector:
          - extract1
          - backpack_bag
          variable: backpack_bag
        - value_selector:
          - extract1
          - vehicle_description
          variable: vehicle_description
        - value_selector:
          - extract1
          - direction
          variable: direction
        - value_selector:
          - extract1
          - distinctive_features
          variable: distinctive_features
        - value_selector:
          - extract1
          - time_of_incident
          variable: time_of_incident
        - value_selector:
          - extract1
          - location_of_incident
          variable: location_of_incident
        - value_selector:
          - extract1
          - confidence_level
          variable: confidence_level
        outputs:
          summary:
            type: string
            children: null
      height: 90
      id: code1
      position:
        x: 680
        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: 'あなたは警備チーム向けの捜索サマリ最適化アシスタントです。入力された目撃サマリを基に、捜索チームが即座に運用できる形式で出力してください。出力は日本語で、以下を含めること。

            1) 重要度（Critical/High/Medium/Low）と短い理由（1文）。

            2) 捜索要点（箇条書き）：最終方向、優先確認ポイント、目撃場所からの推奨検索エリア（短文）

            3) 無線発信テンプレ（現場向け1文、指揮所向け1文）

            4) 捜索チームに対する注意点・証拠保全指示（短い箇条書き）

            5) 不足情報一覧（優先度順）


            出力は箇条書き中心で簡潔に。冗長な背景説明は不要。'
        - role: user
          text: '元の報告: {{#start.query#}}


            抽出サマリ:

            {{#code1.summary#}}'
        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
