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: knowledge-retrieval
      id: code1-to-kr_auto
      source: code1
      target: kr_auto
    - data:
        sourceType: llm
        targetType: end
      id: llm1-to-end
      source: llm1
      target: end
    - id: kr_auto-to-llm1
      source: kr_auto
      target: llm1
      data:
        sourceType: knowledge-retrieval
        targetType: llm
    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: "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 re\n    q = str(inputs.get(\"\
          query\", \"\")).strip()\n    q_low = q.lower()\n    # ヘルパー: 任意のキーワードに続く数値を抽出\n\
          \    def extract_by_keywords(keywords):\n        for k in keywords:\n  \
          \          m = re.search(k + r'[：: ]*([0-9,]+(?:\\.[0-9]+)?)', q_low)\n\
          \            if m:\n                try:\n                    return float(m.group(1).replace(\"\
          ,\", \"\"))\n                except:\n                    pass\n       \
          \ return None\n    # 抽出試行\n    remaining_months = extract_by_keywords([r'残存期間',\
          \ r'残存', r'期間', r'残り期間'])\n    monthly_rent = extract_by_keywords([r'賃料',\
          \ r'月額', r'毎月', r'家賃', r'月の賃料'])\n    residual_value = extract_by_keywords([r'残価',\
          \ r'残存価値', r'残価額'])\n    outstanding_balance = extract_by_keywords([r'残債',\
          \ r'未払残高', r'未払', r'残り債務', r'残債額'])\n    # 違約金率（%）の抽出（例: 違約金 2%）\n    early_fee_percent\
          \ = None\n    m = re.search(r'違約金[：: ]*([0-9,]+(?:\\.[0-9]+)?)\\s*%', q_low)\n\
          \    if m:\n        try:\n            early_fee_percent = float(m.group(1).replace(\"\
          ,\", \"\"))\n        except:\n            early_fee_percent = None\n   \
          \ # 保険的既定値\n    if remaining_months is None:\n        # 月数が明示されない場合は推定として12か月を仮置き\n\
          \        remaining_months = 12.0\n    if monthly_rent is None:\n       \
          \ # 賃料不明時の仮定(例): 100,000円\n        monthly_rent = 100000.0\n    if residual_value\
          \ is None:\n        residual_value = 0.0\n    # 残債が与えられていない場合は、単純に残期間*賃料\
          \ + 残価 を残債と仮定\n    if outstanding_balance is None:\n        outstanding_balance\
          \ = remaining_months * monthly_rent + residual_value\n    # 違約金の算出: %指定があればそれを用いる。ない場合は「月額賃料1か月分」をデフォルト違約金とする\n\
          \    if early_fee_percent is not None:\n        termination_fee = outstanding_balance\
          \ * (early_fee_percent / 100.0)\n    else:\n        termination_fee = monthly_rent\n\
          \    total_cost = outstanding_balance + termination_fee\n    # 円表示（小数なし）\n\
          \    out_lines = []\n    out_lines.append(\"抽出結果・仮定:\")\n    out_lines.append(\"\
          \  入力文: \" + q)\n    out_lines.append(\"  残存期間(推定): \" + str(int(round(remaining_months)))\
          \ + \"ヶ月\")\n    out_lines.append(\"  月額賃料(推定): \" + str(int(round(monthly_rent)))\
          \ + \"円\")\n    out_lines.append(\"  残価(推定): \" + str(int(round(residual_value)))\
          \ + \"円\")\n    out_lines.append(\"  残債(推定): \" + str(int(round(outstanding_balance)))\
          \ + \"円\")\n    if early_fee_percent is not None:\n        out_lines.append(\"\
          \  違約金率(入力): \" + str(early_fee_percent) + \"%\")\n    else:\n        out_lines.append(\"\
          \  違約金: 月額賃料1か月分を仮定\")\n    out_lines.append(\"\")\n    out_lines.append(\"\
          試算(概算):\")\n    out_lines.append(\"  推定残債: \" + str(int(round(outstanding_balance)))\
          \ + \"円\")\n    out_lines.append(\"  推定違約金: \" + str(int(round(termination_fee)))\
          \ + \"円\")\n    out_lines.append(\"  概算合計費用: \" + str(int(round(total_cost)))\
          \ + \"円\")\n    out_lines.append(\"\")\n    out_lines.append(\"注意: 上記は簡易試算です。詳細な契約条項・利息計算・未経過費用・税等は反映していません。\"\
          )\n    result_text = \"\\n\".join(out_lines)\n    return dict(output=result_text)\n"
        variables:
        - value_selector:
          - start
          - query
          variable: query
        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: 'あなたは日本のリース審査担当者向けのアシスタントです。ユーザーの入力と前処理で抽出した数値をもとに、早期解約時の概算費用（残債・違約金・合計）をわかりやすく日本語で出力してください。出力は次のフォーマットに従ってください。


            出力フォーマット（日本語、厳密な表記）:

            1) 推定残債: 半角数字円（例: 120000円）

            2) 推定違約金: 半角数字円（例: 100000円）

            3) 概算合計費用: 半角数字円（例: 220000円）

            4) 前提・仮定: 箇条書きで主要な仮定を列挙

            5) 計算内訳（任意）: 簡潔に示す


            注意: 数値は円で半角、説明は簡潔に。必要なら追加でユーザーに確認すべき項目を1-3点程度提案してください。


            重要: 回答の各手順・セクションにナレッジ検索結果を使用した場合は [参考: 文書名] タグを付けること。回答の末尾に「参考資料:」セクションを付けて参照した全文書名を一覧すること。省略不可。'
        - role: user
          text: "ユーザー入力:\n{{#start.query#}}\n\n前処理による抽出・仮定:\n{{#code1.output#}}\n\n\
            上記を参照して、出力フォーマットに従い日本語で結果を返してください。\n\n【参考資料の明示ルール】\n回答の各セクション・箇条書きで、ナレッジ検索結果の情報を使った箇所には\
            \ [参考: 文書名] タグを付けてください。\n回答の末尾に「参考資料:」セクションを設け、参照した全文書名を一覧してください。\n出力例:\n\
            \  1. VPNクライアントを再起動してください [参考: VPN接続トラブルシューティング.txt]\n  ...\n  参考資料:\n\
            \  - VPN接続トラブルシューティング.txt\n  - パスワードリセット手順.txt"
        vision:
          enabled: false
        memory: null
        context:
          enabled: true
          variable_selector:
          - kr_auto
          - result
      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
    - id: kr_auto
      type: custom
      width: 244
      height: 90
      position:
        x: 380
        y: 282
      data:
        title: ナレッジ検索
        type: knowledge-retrieval
        dataset_ids:
        - __KR_0__
        retrieval_mode: single
        single_retrieval_config:
          model:
            provider: openai
            name: gpt-5-mini
            mode: chat
            completion_params:
              temperature: 0.7
        query_variable_selector:
        - start
        - query
  hash: null
