app:
  description: 原料のCOAや微生物試験結果から受入可否と対応案を自動で要約・判定する補助ツール
  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: 情報抽出（COA / 微生物結果）
        type: parameter-extractor
        model:
          provider: openai
          name: gpt-5-mini
          mode: chat
        query:
        - start
        - query
        parameters:
        - name: sample_id
          type: string
          description: 試料IDやロット番号
          required: false
        - name: product_name
          type: string
          description: 製品名または原料名
          required: true
        - name: ph
          type: number
          description: pH
          required: false
        - name: moisture
          type: number
          description: 水分（%）
          required: false
        - name: aerobic_count
          type: number
          description: 好気性細菌数（CFU/g）
          required: false
        - name: coliforms
          type: number
          description: 大腸菌群（CFU/g）
          required: false
        - name: e_coli
          type: number
          description: 大腸菌（E.coli）（CFU/g）
          required: false
        - name: yeast_mold
          type: number
          description: 酵母・カビ（CFU/g）
          required: false
        - name: staph_aureus
          type: number
          description: 黄色ブドウ球菌（CFU/g）
          required: false
        - name: salmonella_present
          type: string
          description: サルモネラ検出（検出/未検出/不明）
          required: false
        reasoning_mode: prompt
        instruction: 以下のテキストから試料情報と試験結果（pH, 水分, 好気性細菌数, 大腸菌群, E.coli, 酵母・カビ, 黄色ブドウ球菌,
          サルモネラの検出有無など）を抽出してください。数値は可能な限り数値化し、項目が無ければ空にしてください。出力は機械的に各パラメータ名と値を対応させて返してください。
      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  \
          \  ph_min = 4.0\n    ph_max = 7.0\n    moisture_max = 14.0\n    aerobic_limit\
          \ = 1e5\n    coliform_limit = 100.0\n    e_coli_limit = 10.0\n    yeast_mold_limit\
          \ = 1e3\n    staph_limit = 100.0\n    # 安全に数値変換\n    def to_float_safe(key):\n\
          \        v = inputs.get(key)\n        try:\n            if v is None or\
          \ str(v).strip() == \"\":\n                return None\n            return\
          \ float(v)\n        except Exception:\n            return None\n    ph =\
          \ to_float_safe(\"ph\")\n    moisture = to_float_safe(\"moisture\")\n  \
          \  aerobic = to_float_safe(\"aerobic_count\")\n    coliforms = to_float_safe(\"\
          coliforms\")\n    e_coli = to_float_safe(\"e_coli\")\n    yeast_mold = to_float_safe(\"\
          yeast_mold\")\n    staph = to_float_safe(\"staph_aureus\")\n    salmonella\
          \ = inputs.get(\"salmonella_present\", \"\")\n    issues = []\n    critical\
          \ = False\n    # 致命的な指標（サルモネラ検出は致命）\n    if salmonella is not None and str(salmonella).strip()\
          \ != \"\":\n        s = str(salmonella).lower()\n        if \"検出\" in s\
          \ or \"detected\" in s or \"positive\" in s or \"陽性\" in s:\n          \
          \  issues.append(\"サルモネラ検出\")\n            critical = True\n    # pH\n \
          \   if ph is not None:\n        if ph < ph_min or ph > ph_max:\n       \
          \     issues.append(\"pHが許容範囲外 (検出値: \" + str(ph) + \")\")\n    # 水分\n \
          \   if moisture is not None:\n        if moisture > moisture_max:\n    \
          \        issues.append(\"水分が高い (検出値: \" + str(moisture) + \" %)\")\n   \
          \ # 好気性細菌\n    if aerobic is not None:\n        try:\n            if aerobic\
          \ > aerobic_limit:\n                issues.append(\"好気性細菌数が高い (CFU/g: \"\
          \ + str(aerobic) + \")\")\n        except Exception:\n            pass\n\
          \    # 大腸菌群\n    if coliforms is not None:\n        if coliforms > coliform_limit:\n\
          \            issues.append(\"大腸菌群が基準超過 (CFU/g: \" + str(coliforms) + \"\
          )\")\n    # E.coli\n    if e_coli is not None:\n        if e_coli > e_coli_limit:\n\
          \            issues.append(\"E.coliが基準超過 (CFU/g: \" + str(e_coli) + \")\"\
          )\n    # 酵母・カビ\n    if yeast_mold is not None:\n        if yeast_mold >\
          \ yeast_mold_limit:\n            issues.append(\"酵母・カビが基準超過 (CFU/g: \" +\
          \ str(yeast_mold) + \")\")\n    # 黄色ブドウ球菌\n    if staph is not None:\n \
          \       if staph > staph_limit:\n            issues.append(\"黄色ブドウ球菌が基準超過\
          \ (CFU/g: \" + str(staph) + \")\")\n    # 総合判定ロジック（単純ルール）\n    if critical:\n\
          \        decision = \"Reject\"\n        recommendation = [\n           \
          \ \"ロットを拒否してください（サルモネラ検出）\",\n            \"製造元へ報告し、原因調査および再試験を実施してください\"\
          \n        ]\n    elif len(issues) == 0:\n        decision = \"Accept\"\n\
          \        recommendation = [\n            \"受入可能\",\n            \"通常のロット処理を行ってください\"\
          \n        ]\n    else:\n        decision = \"Conditional Accept\"\n    \
          \    recs = [\n            \"問題となった項目の再試験を推奨\",\n            \"製造元へ確認。必要に応じてトレーサビリティを確保\"\
          ,\n            \"保管や使用条件の見直しを検討\"\n        ]\n        recommendation = recs\n\
          \    # 出力サマリ作成\n    product = inputs.get(\"product_name\", \"\")\n    sample\
          \ = inputs.get(\"sample_id\", \"\")\n    details = []\n    details.append(\"\
          製品: \" + str(product))\n    if sample:\n        details.append(\"試料ID: \"\
          \ + str(sample))\n    # 抽出された値を列挙\n    fields = [\"ph\", \"moisture\", \"\
          aerobic_count\", \"coliforms\", \"e_coli\", \"yeast_mold\", \"staph_aureus\"\
          , \"salmonella_present\"]\n    for f in fields:\n        v = inputs.get(f)\n\
          \        if v is not None and str(v).strip() != \"\":\n            details.append(f\
          \ + \": \" + str(v))\n    detail_text = \"; \".join(details)\n    issues_text\
          \ = \"、\".join(issues) if issues else \"なし\"\n    out = \"判定: \" + decision\
          \ + \"\\n\" + \"要因: \" + issues_text + \"\\n\" + \"詳細: \" + detail_text\
          \ + \"\\n\" + \"推奨: \" + \"; \".join(recommendation)\n    return dict(output=out)\n"
        variables:
        - value_selector:
          - extract1
          - sample_id
          variable: sample_id
        - value_selector:
          - extract1
          - product_name
          variable: product_name
        - value_selector:
          - extract1
          - ph
          variable: ph
        - value_selector:
          - extract1
          - moisture
          variable: moisture
        - value_selector:
          - extract1
          - aerobic_count
          variable: aerobic_count
        - value_selector:
          - extract1
          - coliforms
          variable: coliforms
        - value_selector:
          - extract1
          - e_coli
          variable: e_coli
        - value_selector:
          - extract1
          - yeast_mold
          variable: yeast_mold
        - value_selector:
          - extract1
          - staph_aureus
          variable: staph_aureus
        - value_selector:
          - extract1
          - salmonella_present
          variable: salmonella_present
        outputs:
          output:
            type: string
            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.3
        prompt_template:
        - role: system
          text: あなたは食品製造現場向けの品質管理専門家です。以下の入力をもとに、受入可否（Accept / Conditional Accept
            / Reject）を明確に示し、その理由と現場向けの対応案（短いステップ）を日本語で箇条書きで出力してください。出力は「判定:」「理由:」「推奨対応:」の3つの見出しを含め、推奨対応は3つ以内で簡潔にしてください。
        - 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
