app:
  description: 上传提炼知识点
  icon: 🤖
  icon_background: '#E4FBCC'
  mode: workflow
  name: 恩喜-01-知识点分析-考陪练
  use_icon_as_answer_icon: false
dependencies:
- current_identifier: null
  type: marketplace
  value:
    marketplace_plugin_unique_identifier: langgenius/openrouter:0.0.22@99ef4cf4e08292c28806abaf24f295ed66e04e4b9e74385b487fd0767c7f56df
    version: null
kind: app
version: 0.5.0
workflow:
  conversation_variables: []
  environment_variables: []
  features:
    file_upload:
      allowed_file_extensions:
      - .JPG
      - .JPEG
      - .PNG
      - .GIF
      - .WEBP
      - .SVG
      allowed_file_types:
      - image
      allowed_file_upload_methods:
      - local_file
      - remote_url
      enabled: false
      fileUploadConfig:
        audio_file_size_limit: 50
        batch_count_limit: 5
        file_size_limit: 15
        image_file_batch_limit: 10
        image_file_size_limit: 10
        single_chunk_attachment_limit: 10
        video_file_size_limit: 100
        workflow_file_upload_limit: 10
      image:
        enabled: false
        number_limits: 3
        transfer_methods:
        - local_file
        - remote_url
      number_limits: 3
    opening_statement: ''
    retriever_resource:
      enabled: true
    sensitive_word_avoidance:
      enabled: false
    speech_to_text:
      enabled: false
    suggested_questions: []
    suggested_questions_after_answer:
      enabled: false
    text_to_speech:
      enabled: false
      language: ''
      voice: ''
  graph:
    edges:
    - data:
        isInIteration: false
        isInLoop: false
        sourceType: document-extractor
        targetType: llm
      id: 1757513748987-source-1757513757216-target
      selected: false
      source: '1757513748987'
      sourceHandle: source
      target: '1757513757216'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        isInLoop: false
        sourceType: llm
        targetType: code
      id: 1757513757216-source-1757516212204-target
      selected: false
      source: '1757513757216'
      sourceHandle: source
      target: '1757516212204'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        isInLoop: false
        sourceType: llm
        targetType: end
      id: 1757513757216-fail-branch-1757572091560-target
      selected: false
      source: '1757513757216'
      sourceHandle: fail-branch
      target: '1757572091560'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        isInLoop: false
        sourceType: code
        targetType: end
      id: 1757516212204-fail-branch-1757576655478-target
      selected: false
      source: '1757516212204'
      sourceHandle: fail-branch
      target: '1757576655478'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        isInLoop: false
        sourceType: code
        targetType: iteration
      id: 1757516212204-source-1757687332404-target
      selected: false
      source: '1757516212204'
      sourceHandle: source
      target: '1757687332404'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInLoop: false
        sourceType: iteration
        targetType: end
      id: 1757687332404-source-1757522230050-target
      selected: false
      source: '1757687332404'
      sourceHandle: source
      target: '1757522230050'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: true
        isInLoop: false
        iteration_id: '1757687332404'
        sourceType: iteration-start
        targetType: code
      id: 1757687332404start-source-1758575376121-target
      selected: false
      source: 1757687332404start
      sourceHandle: source
      target: '1758575376121'
      targetHandle: target
      type: custom
      zIndex: 1002
    - data:
        isInIteration: false
        isInLoop: false
        sourceType: start
        targetType: tool
      id: 1757513649648-source-1766636080995-target
      source: '1757513649648'
      sourceHandle: source
      target: '1766636080995'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInLoop: false
        sourceType: tool
        targetType: document-extractor
      id: 1766636080995-source-1757513748987-target
      source: '1766636080995'
      sourceHandle: source
      target: '1757513748987'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInLoop: false
        sourceType: tool
        targetType: end
      id: 1766636080995-fail-branch-1764240729694-target
      source: '1766636080995'
      sourceHandle: fail-branch
      target: '1764240729694'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: true
        isInLoop: false
        iteration_id: '1757687332404'
        sourceType: code
        targetType: tool
      id: 1758575376121-source-1766636254081-target
      source: '1758575376121'
      sourceHandle: source
      target: '1766636254081'
      targetHandle: target
      type: custom
      zIndex: 1002
    nodes:
    - data:
        desc: ''
        selected: false
        title: 开始
        type: start
        variables:
        - allowed_file_extensions: []
          allowed_file_types:
          - document
          - audio
          - video
          - image
          allowed_file_upload_methods:
          - local_file
          - remote_url
          label: file
          max_length: 1
          options: []
          required: true
          type: file
          variable: file
        - label: course_name
          max_length: 255
          options: []
          required: true
          type: text-input
          variable: course_name
        - default: '1'
          label: course_id
          max_length: 48
          options: []
          required: true
          type: number
          variable: course_id
        - default: '16'
          label: material_id
          max_length: 48
          options: []
          required: true
          type: number
          variable: material_id
      height: 187
      id: '1757513649648'
      position:
        x: 30
        y: 283
      positionAbsolute:
        x: 30
        y: 283
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 242
    - data:
        desc: ''
        is_array_file: true
        selected: false
        title: 文档提取器
        type: document-extractor
        variable_selector:
        - '1757513649648'
        - file
      height: 104
      id: '1757513748987'
      position:
        x: 934
        y: 281
      positionAbsolute:
        x: 934
        y: 281
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 242
    - data:
        context:
          enabled: false
          variable_selector: []
        desc: ''
        error_strategy: fail-branch
        model:
          completion_params:
            temperature: 0.1
          mode: chat
          name: google/gemini-3-pro-preview
          provider: langgenius/openrouter/openrouter
        prompt_template:
        - id: f0a75809-0e24-491f-bd19-964d8b2eae4c
          role: system
          text: "# 角色\n你是一个文件拆解高手，擅长将用户提交的内容进行精准拆分，拆分后的内容做个简单的优化处理使其更具可读性，但要尽量使用原文的原词原句。\n\
            \n## 技能\n### 技能 1: 内容拆分\n1. 当用户提交内容后，拆分为多段。\n2. 对拆分后的内容做简单优化，使其更具可读性，比如去掉奇怪符号（如换行符、乱码），若语句不通顺，或格式原因导致错位，则重新表达。用户可能会提交录音转文字的内容，因此可能是有错字的，注意修复这些小瑕疵。\n\
            3. 优化过程中，尽量使用原文的原词原句，特别是话术类，必须保持原有的句式、保持原词原句，而不是重构。\n4. 注意是拆分而不是重写，不需要润色，尽量不做任何处理。\n\
            5. 输出到 content。\n\n### 技能 2: 为每一个选段概括一个标题\n1. 为每个拆分出来的选段概括一个标题，并输出到 title。\n\
            \n### 技能 3: 为每一个选段说明与主题的关联\n1. 详细说明这一段与全文核心主题的关联，并输出到 topic_relation。\n\
            \n### 技能 4: 为每一个选段打上一个类型标签\n1. 用户提交的内容很有可能是一个课程、一篇讲义、一个产品的说明书，通常是用户希望他公司的员工或高管学习的知识。\n\
            2. 用户通常是医疗美容机构或轻医美、生活美容连锁品牌。\n3. 你要为每个选段打上一个知识类型的标签，最好是这几个类型中的一个：\"理论知识\"\
            , \"诊断设计\", \"操作步骤\", \"沟通话术\", \"案例分析\", \"注意事项\", \"技巧方法\", \"客诉处理\"\
            。当然你也可以为这个选段匹配一个更适合的。\n\n## 输出要求（严格按要求输出）\n请直接输出一个纯净的 JSON 数组（Array），不要包含\
            \ Markdown 标记（如 ```json），也不要包含任何解释性文字。格式如下：\n\n[\n  {\n    \"title\":\
            \ \"知识点标题\",\n    \"content\": \"知识点内容\",\n    \"topic_relation\": \"\
            知识点与主题的关系\",\n    \"type\": \"知识点类型\"\n  },\n  {\n    \"title\": \"第二个知识点标题\"\
            ,\n    \"content\": \"第二个知识点内容...\",\n    \"topic_relation\": \"...\"\
            ,\n    \"type\": \"...\"\n  }\n]\n\n## 限制\n- 仅围绕用户提交的内容进行拆分和关联标注，不涉及其他无关内容。\n\
            - 拆分后的内容必须最大程度保持与原文一致。\n- 关联说明需清晰合理。\n- 不论如何，不要拆分超过 20 段！"
        - id: bc1168ad-45de-475e-9365-8791306c8bb3
          role: user
          text: '课程主题：{{#1757513649648.course_name#}}

            ## 用户提交的内容：

            {{#1757513748987.text#}}


            ## 注意

            - 以json的格式输出

            - 不论如何，不要拆分超过20 段！'
        selected: false
        structured_output:
          schema:
            additionalProperties: false
            properties:
              content:
                description: 知识点内容
                type: string
              title:
                description: 知识点标题
                type: string
            required:
            - title
            - content
            type: object
        structured_output_enabled: false
        title: 知识点提取
        type: llm
        variables: []
        vision:
          enabled: false
      height: 124
      id: '1757513757216'
      position:
        x: 1236
        y: 283
      positionAbsolute:
        x: 1236
        y: 283
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 242
    - data:
        code: "import json\nimport re\ndef main(arg1: str) -> dict:\n    # --- 内部辅助函数：清洗文本以适配\
          \ SQL ---\n    def clean_text_for_sql(text):\n        if not isinstance(text,\
          \ str):\n            return text\n        \n        # 1. 【关键修改】将物理换行符替换为\
          \ SQL 转义换行符 (\\\\n)\n        # 这样 SQL 语句本身是一行，但数据库会将其解释为换行\n        text\
          \ = text.replace('\\n', '\\\\n').replace('\\r', '')\n        \n        #\
          \ 2. 将单引号替换为两个单引号（SQL 标准转义），防止截断\n        text = text.replace(\"'\", \"\
          ''\")\n        \n        return text\n    try:\n        if not arg1:\n \
          \           return {\"data\": []}\n        \n        # --- 1. 提取 JSON 字符串\
          \ (保持不变) ---\n        json_str = arg1\n        match = re.search(r'```json\\\
          s*(.*?)\\s*```', arg1, re.DOTALL)\n        if match:\n            json_str\
          \ = match.group(1)\n        else:\n            start = arg1.find('[')\n\
          \            end = arg1.rfind(']')\n            if start != -1 and end !=\
          \ -1:\n                json_str = arg1[start:end+1]\n        # --- 2. 解析\
          \ JSON ---\n        data_list = json.loads(json_str)\n        \n       \
          \ # --- 3. 校验与清洗数据 ---\n        if not isinstance(data_list, list):\n  \
          \          if isinstance(data_list, dict) and \"items\" in data_list:\n\
          \                data_list = data_list[\"items\"]\n            else:\n \
          \               return {\"data\": []}\n        if len(data_list) >= 30:\n\
          \            data_list = data_list[:29]\n            \n        # 遍历列表进行清洗\n\
          \        cleaned_list = []\n        for item in data_list:\n           \
          \ if isinstance(item, dict):\n                cleaned_item = {\n       \
          \             \"title\": clean_text_for_sql(item.get(\"title\", \"\")),\n\
          \                    \"content\": clean_text_for_sql(item.get(\"content\"\
          , \"\")),\n                    \"topic_relation\": clean_text_for_sql(item.get(\"\
          topic_relation\", \"\")),\n                    \"type\": clean_text_for_sql(item.get(\"\
          type\", \"\"))\n                }\n                cleaned_list.append(cleaned_item)\n\
          \        \n        return {\"data\": cleaned_list}\n    \n    except json.JSONDecodeError\
          \ as e:\n        print(f\"JSON解析错误: {str(e)}\")\n        return {\"data\"\
          : []}\n    except Exception as e:\n        print(f\"处理过程中发生错误: {str(e)}\"\
          )\n        return {\"data\": []}"
        code_language: python3
        desc: ''
        error_strategy: fail-branch
        outputs:
          data:
            children: null
            type: array[object]
        retry_config:
          max_retries: 3
          retry_enabled: true
          retry_interval: 1000
        selected: false
        title: 转换格式
        type: code
        variables:
        - value_selector:
          - '1757513757216'
          - text
          value_type: string
          variable: arg1
      height: 117
      id: '1757516212204'
      position:
        x: 1538
        y: 283
      positionAbsolute:
        x: 1538
        y: 283
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 242
    - data:
        desc: ''
        outputs:
        - value_selector:
          - '1757687332404'
          - output
          value_type: array[string]
          variable: output
        selected: false
        title: 结束
        type: end
      height: 88
      id: '1757522230050'
      position:
        x: 2708
        y: 572
      positionAbsolute:
        x: 2708
        y: 572
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 242
    - data:
        desc: ''
        outputs:
        - value_selector:
          - '1757513757216'
          - error_message
          value_type: string
          variable: reasoning_content
        selected: false
        title: 结束 2
        type: end
      height: 88
      id: '1757572091560'
      position:
        x: 1361.0212171476019
        y: 472.6567992168116
      positionAbsolute:
        x: 1361.0212171476019
        y: 472.6567992168116
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 242
    - data:
        desc: ''
        outputs:
        - value_selector:
          - '1757516212204'
          - error_message
          value_type: string
          variable: result
        selected: false
        title: 结束 3
        type: end
      height: 88
      id: '1757576655478'
      position:
        x: 2123
        y: 283
      positionAbsolute:
        x: 2123
        y: 283
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 242
    - data:
        desc: ''
        error_handle_mode: continue-on-error
        height: 410
        is_parallel: true
        iterator_input_type: array[object]
        iterator_selector:
        - '1757516212204'
        - data
        output_selector:
        - '1758575376121'
        - title
        output_type: array[string]
        parallel_nums: 10
        selected: false
        start_node_id: 1757687332404start
        title: 迭代
        type: iteration
        width: 1310.983478660764
      height: 410
      id: '1757687332404'
      position:
        x: 1939.6507421681436
        y: 396.2369270862009
      positionAbsolute:
        x: 1939.6507421681436
        y: 396.2369270862009
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 1311
      zIndex: 1
    - data:
        desc: ''
        isInIteration: true
        selected: false
        title: ''
        type: iteration-start
      draggable: false
      height: 48
      id: 1757687332404start
      parentId: '1757687332404'
      position:
        x: 60
        y: 62
      positionAbsolute:
        x: 1999.6507421681436
        y: 458.2369270862009
      selectable: false
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom-iteration-start
      width: 44
      zIndex: 1002
    - data:
        code: "def main(arg1: dict) -> dict:\n    # 上一个节点已经完成了所有清洗工作（包括换行符转义 \\n 和单引号转义\
          \ ''）\n    # 这里只需要直接透传数据即可，不要再做任何处理\n    return {\n        \"title\": arg1.get(\"\
          title\"),\n        \"content\": arg1.get(\"content\"),\n        \"topic_relation\"\
          : arg1.get(\"topic_relation\"),\n        \"type\": arg1.get(\"type\")\n\
          \    }"
        code_language: python3
        desc: ''
        isInIteration: true
        isInLoop: false
        iteration_id: '1757687332404'
        outputs:
          content:
            children: null
            type: string
          title:
            children: null
            type: string
          topic_relation:
            children: null
            type: string
          type:
            children: null
            type: string
        selected: false
        title: 内容提取
        type: code
        variables:
        - value_selector:
          - '1757687332404'
          - item
          value_type: object
          variable: arg1
      height: 52
      id: '1758575376121'
      parentId: '1757687332404'
      position:
        x: 204
        y: 60
      positionAbsolute:
        x: 2143.6507421681436
        y: 456.2369270862009
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 242
      zIndex: 1002
    - data:
        outputs:
        - value_selector:
          - '1766636080995'
          - text
          value_type: string
          variable: error_message
        selected: true
        title: 结束 5
        type: end
      height: 88
      id: '1764240729694'
      position:
        x: 934
        y: 411
      positionAbsolute:
        x: 934
        y: 411
      selected: true
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 242
    - data:
        error_strategy: fail-branch
        is_team_authorization: true
        paramSchemas:
        - auto_generate: null
          default: null
          form: llm
          human_description:
            en_US: ''
            ja_JP: ''
            pt_BR: ''
            zh_Hans: ''
          label:
            en_US: SQL 语句
            ja_JP: SQL 语句
            pt_BR: SQL 语句
            zh_Hans: SQL 语句
          llm_description: ''
          max: null
          min: null
          name: sql
          options: []
          placeholder:
            en_US: ''
            ja_JP: ''
            pt_BR: ''
            zh_Hans: ''
          precision: null
          required: true
          scope: null
          template: null
          type: string
        params:
          sql: ''
        plugin_id: null
        plugin_unique_identifier: null
        provider_icon:
          background: '#FFEAD5'
          content: 🤖
        provider_id: 2e7e915c-606c-4230-b4bd-ff95efb72f39
        provider_name: 恩喜-00-SQL 执行器-考陪练专用
        provider_type: workflow
        retry_config:
          max_retries: 3
          retry_enabled: true
          retry_interval: 1000
        selected: false
        title: 恩喜-00-SQL 执行器-考陪练专用
        tool_configurations: {}
        tool_description: 考陪练系统专用的 sql 执行器
        tool_label: 恩喜-00-SQL 执行器-考陪练专用
        tool_name: SQL_executor_enxi
        tool_node_version: '2'
        tool_parameters:
          sql:
            type: mixed
            value: DELETE FROM knowledge_points WHERE material_id = {{#1757513649648.material_id#}};
        type: tool
      height: 117
      id: '1766636080995'
      position:
        x: 369.1162304946969
        y: 472.6567992168116
      positionAbsolute:
        x: 369.1162304946969
        y: 472.6567992168116
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 242
    - data:
        isInIteration: true
        isInLoop: false
        is_team_authorization: true
        iteration_id: '1757687332404'
        paramSchemas:
        - auto_generate: null
          default: null
          form: llm
          human_description:
            en_US: ''
            ja_JP: ''
            pt_BR: ''
            zh_Hans: ''
          label:
            en_US: SQL 语句
            ja_JP: SQL 语句
            pt_BR: SQL 语句
            zh_Hans: SQL 语句
          llm_description: ''
          max: null
          min: null
          name: sql
          options: []
          placeholder:
            en_US: ''
            ja_JP: ''
            pt_BR: ''
            zh_Hans: ''
          precision: null
          required: true
          scope: null
          template: null
          type: string
        params:
          sql: ''
        plugin_id: null
        plugin_unique_identifier: null
        provider_icon:
          background: '#FFEAD5'
          content: 🤖
        provider_id: 2e7e915c-606c-4230-b4bd-ff95efb72f39
        provider_name: 恩喜-00-SQL 执行器-考陪练专用
        provider_type: workflow
        selected: false
        title: 恩喜-00-SQL 执行器-考陪练专用
        tool_configurations: {}
        tool_description: 考陪练系统专用的 sql 执行器
        tool_label: 恩喜-00-SQL 执行器-考陪练专用
        tool_name: SQL_executor_enxi
        tool_node_version: '2'
        tool_parameters:
          sql:
            type: mixed
            value: INSERT INTO knowledge_points (course_id, material_id, name, description,
              type, source, topic_relation) VALUES ({{#1757513649648.course_id#}},
              {{#1757513649648.material_id#}}, '{{#1758575376121.title#}}', '{{#1758575376121.content#}}',
              '{{#1758575376121.type#}}', 1, '{{#1758575376121.topic_relation#}}');
        type: tool
      height: 52
      id: '1766636254081'
      parentId: '1757687332404'
      position:
        x: 552.0682037973988
        y: 83.70934394330777
      positionAbsolute:
        x: 2491.7189459655424
        y: 479.9462710295087
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 242
      zIndex: 1002
    viewport:
      x: -154.69868384174993
      y: 184.48211749520988
      zoom: 0.5480921885368025
  rag_pipeline_variables: []
