HH n8n OClick/1.0

    Shared 10/21/2025

    1 views

    Visual Workflow

    JSON Code

    {
      "id": "bZExXXjMZbhWbtiy",
      "meta": {
        "instanceId": "a7951da00dc167f722a4bd0bc0e175503754ae4ae331d85018109ce22f7c48f6",
        "templateCredsSetupCompleted": true
      },
      "name": "OClick/1.0",
      "tags": [],
      "nodes": [
        {
          "id": "70ce1f80-03b5-434c-99ed-cd8e31f35c94",
          "name": "Schedule Trigger",
          "type": "n8n-nodes-base.scheduleTrigger",
          "position": [
            -1056,
            1072
          ],
          "parameters": {
            "rule": {
              "interval": [
                {
                  "field": "hours"
                }
              ]
            }
          },
          "typeVersion": 1.2
        },
        {
          "id": "fbc4f82c-1b40-46d6-ad2b-7ad27f0c11dd",
          "name": "Auto-search",
          "type": "n8n-nodes-base.httpRequest",
          "position": [
            -864,
            1072
          ],
          "parameters": {
            "url": "https://api.hh.ru/saved_searches/vacancies",
            "options": {},
            "sendQuery": true,
            "sendHeaders": true,
            "authentication": "genericCredentialType",
            "genericAuthType": "oAuth2Api",
            "queryParameters": {
              "parameters": [
                {
                  "name": "page",
                  "value": "0"
                },
                {
                  "name": "per_page",
                  "value": "5"
                }
              ]
            },
            "headerParameters": {
              "parameters": [
                {
                  "name": "User-Agent",
                  "value": "OClick/1.0 (shimorowm@gmail.com)"
                }
              ]
            }
          },
          "credentials": {
            "oAuth2Api": {
              "id": "l9tyPEWtPvzUKFXJ",
              "name": "HH OAuth2"
            }
          },
          "typeVersion": 4.2
        },
        {
          "id": "4d2e08b7-ab89-4815-a3f9-fedb91035d3f",
          "name": "Split Out",
          "type": "n8n-nodes-base.splitOut",
          "position": [
            -704,
            1072
          ],
          "parameters": {
            "options": {},
            "fieldToSplitOut": "items"
          },
          "typeVersion": 1
        },
        {
          "id": "cb9603b9-6cd6-421a-b855-2431c9c1c33b",
          "name": "If",
          "type": "n8n-nodes-base.if",
          "position": [
            -528,
            1072
          ],
          "parameters": {
            "options": {},
            "conditions": {
              "options": {
                "version": 2,
                "leftValue": "",
                "caseSensitive": true,
                "typeValidation": "strict"
              },
              "combinator": "and",
              "conditions": [
                {
                  "id": "96d0a2cc-b2ed-4a93-941b-2278aa255c79",
                  "operator": {
                    "type": "number",
                    "operation": "gt"
                  },
                  "leftValue": "={{ $json.new_items.count }}",
                  "rightValue": 0
                }
              ]
            }
          },
          "typeVersion": 2.2
        },
        {
          "id": "86febd0c-cbaf-4720-865f-3163ccecd70b",
          "name": "Split Out1",
          "type": "n8n-nodes-base.splitOut",
          "position": [
            -896,
            1312
          ],
          "parameters": {
            "options": {},
            "fieldToSplitOut": "items"
          },
          "typeVersion": 1
        },
        {
          "id": "3ca0be82-3e29-4956-9b17-54d89548b283",
          "name": "Remove Duplicates",
          "type": "n8n-nodes-base.removeDuplicates",
          "position": [
            -752,
            1312
          ],
          "parameters": {
            "compare": "selectedFields",
            "options": {},
            "fieldsToCompare": "id"
          },
          "typeVersion": 2
        },
        {
          "id": "abb1d406-feaa-47fb-9cd7-273a1556d4af",
          "name": "Vacancy Details",
          "type": "n8n-nodes-base.httpRequest",
          "position": [
            -1056,
            1312
          ],
          "parameters": {
            "url": "={{ $json.items.url }}",
            "options": {},
            "sendHeaders": true,
            "authentication": "genericCredentialType",
            "genericAuthType": "oAuth2Api",
            "headerParameters": {
              "parameters": [
                {
                  "name": "User-Agent",
                  "value": "OClick/1.0 (shimorowm@gmail.com)"
                }
              ]
            }
          },
          "credentials": {
            "oAuth2Api": {
              "id": "l9tyPEWtPvzUKFXJ",
              "name": "HH OAuth2"
            }
          },
          "typeVersion": 4.2
        },
        {
          "id": "90d5f3e3-504c-418d-b7dd-82ca5acb5965",
          "name": "Chunking",
          "type": "n8n-nodes-base.code",
          "position": [
            352,
            944
          ],
          "parameters": {
            "jsCode": "const items = $input.all();\nconst out = [];\n\nfor (const item of items) {\n  const doc = item.json.data ?? item.json;\n  if (!doc) continue;\n\n  // гарантируем, что sections — массив (в твоём случае они всегда есть)\n  const sections = Array.isArray(doc.sections) ? doc.sections : [];\n\n  let secIndex = 0;\n  for (const sec of sections) {\n    // безопасные дефолты\n    const sectionName = sec && sec.section ? String(sec.section) : `section_${secIndex}`;\n    const sectionText = sec && sec.text ? String(sec.text).trim() : '';\n\n    // Unicode-safe slug (короткий, для id)\n    const rawSlug = sectionName.toLowerCase().replace(/\\s+/g, '_');\n    const sectionSlug = rawSlug.replace(/[^\\p{L}\\p{N}_-]/gu, '').slice(0, 50) || `sec${secIndex}`;\n\n    // уникальный id чанка\n    const chunkId = `${doc.id ?? 'doc'}_${secIndex}_${sectionSlug}`;\n\n    // content: заголовок секции + текст\n    //const content = `### ${sectionName}\\n\\n${sectionText}`;\n    const content = `${sectionText}`;\n\n    // сохраняем в metadata только поля, которые соответствуют исходной схеме документа\n    const metadata = {\n      // id чанка (уникален)\n      id: chunkId,\n      // оригинальный документный id\n      document_id: doc.id ?? null,\n      // основной набор полей из оригинального документа (копируем как есть)\n      title: doc.title ?? null,\n      salary_range: doc.salary_range ?? null,\n      location: doc.location ?? null,\n      // experience — копируем целиком (объект {id, name} или null)\n      experience: doc.experience ?? null,\n      // ключевые навыки — сохраняем оригинал (массив объектов или пустой массив)\n      key_skills: doc.key_skills ?? [],\n      source_url: doc.source_url ?? null,\n      employment_type: doc.employment_type ?? null,\n      work_format: doc.work_format ?? [],\n      company: doc.company ?? null,\n      // информация о секции для удобства поиска/фильтрации\n      section: sectionName\n    };\n\n    out.push({ json: { content, metadata } });\n    secIndex++;\n  }\n\n  // если sections пустой — сейчас мы не создаём чанков (поведение по умолчанию)\n}\n\nreturn out;\n"
          },
          "executeOnce": false,
          "typeVersion": 2
        },
        {
          "id": "96107c13-1e42-4765-8db3-8a317a2099c8",
          "name": "qwen3-embedding",
          "type": "@n8n/n8n-nodes-langchain.embeddingsOllama",
          "position": [
            928,
            1040
          ],
          "parameters": {
            "model": "qwen3-embedding:8b"
          },
          "credentials": {
            "ollamaApi": {
              "id": "JrVtVfb5WaGIXcqR",
              "name": "Ollama RTX"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "cf086d55-3263-414f-8127-5755fbca4bb8",
          "name": "qwen3:4b:it:q8",
          "type": "@n8n/n8n-nodes-langchain.lmOllama",
          "position": [
            32,
            1568
          ],
          "parameters": {
            "model": "qwen3:4b-instruct-2507-q8_0",
            "options": {
              "topK": 1,
              "topP": 1,
              "numCtx": 8192,
              "lowVram": false,
              "mainGpu": 0,
              "useMMap": true,
              "numBatch": 128,
              "useMLock": true,
              "numThread": 14,
              "vocabOnly": false,
              "temperature": 0,
              "repeatPenalty": 1,
              "penalizeNewline": false,
              "presencePenalty": 0,
              "frequencyPenalty": 0
            }
          },
          "credentials": {
            "ollamaApi": {
              "id": "JrVtVfb5WaGIXcqR",
              "name": "Ollama RTX"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "b5ddbd7a-7ad2-4dae-80a1-c5858a82873e",
          "name": "Token Splitter",
          "type": "@n8n/n8n-nodes-langchain.textSplitterTokenSplitter",
          "position": [
            1216,
            1136
          ],
          "parameters": {
            "chunkSize": 500,
            "chunkOverlap": 100
          },
          "typeVersion": 1
        },
        {
          "id": "a35d520f-55ce-4bf8-b103-cf9275600c8f",
          "name": "Save To Qdrant",
          "type": "@n8n/n8n-nodes-langchain.vectorStoreQdrant",
          "position": [
            1120,
            752
          ],
          "parameters": {
            "mode": "insert",
            "options": {},
            "qdrantCollection": {
              "__rl": true,
              "mode": "id",
              "value": "vacancies"
            }
          },
          "credentials": {
            "qdrantApi": {
              "id": "sFfERYppMeBnFNeA",
              "name": "Local QdrantApi database"
            }
          },
          "typeVersion": 1.3
        },
        {
          "id": "b3350ecd-22f7-4d3d-98e7-8746480c288d",
          "name": "Set Payload",
          "type": "n8n-nodes-qdrant.qdrant",
          "onError": "continueErrorOutput",
          "position": [
            1408,
            752
          ],
          "parameters": {
            "filter": "={\n    \"must\": [\n      {\n        \"key\": \"metadata.chunk_id\",\n        \"match\": {\n          \"value\": \"{{ $json.metadata.chunk_id }}\"\n        }\n      }\n    ]\n}",
            "points": "=",
            "payload": "={\n  \"vacancy_id\": {{ $('Chunking').item.json.metadata.document_id }},\n  \"location\": {{ $('Chunking').item.json.metadata.location ? $('Chunking').item.json.metadata.location.toJsonString() : \"null\" }},\n  \"source_url\": \"{{ $('Chunking').item.json.metadata.source_url }}\",\n  \"employment_type_id\": {{ $('Chunking').item.json.metadata.employment_type.id.toJsonString() }},\n  \"work_format\": {{ JSON.stringify($('Chunking').item.json.metadata.work_format) }}\n}",
            "resource": "payload",
            "operation": "setPayload",
            "collectionName": {
              "__rl": true,
              "mode": "name",
              "value": "vacancies"
            },
            "requestOptions": {
              "timeout": 10000,
              "allowUnauthorizedCerts": true
            }
          },
          "credentials": {
            "qdrantRestApi": {
              "id": "5UV8rQE53hqsh82x",
              "name": "Qdrant account"
            }
          },
          "retryOnFail": false,
          "typeVersion": 1
        },
        {
          "id": "1f37befa-0a0e-483d-84dc-92f97a4cbfec",
          "name": "Information Extractor",
          "type": "@n8n/n8n-nodes-langchain.informationExtractor",
          "position": [
            32,
            1360
          ],
          "parameters": {
            "text": "={{ $json.sections }}",
            "options": {},
            "attributes": {
              "attributes": [
                {
                  "name": "responsibilities",
                  "description": "обязанности / что нужно делать;"
                },
                {
                  "name": "requirements",
                  "required": true,
                  "description": "обязательные требования / необходимый опыт;"
                },
                {
                  "name": "additional",
                  "description": "only optional / desirable skills or experience (explicitly indicated as \"Would be a plus\", \"Welcome if\", \"Desirable\", \"preferred\", \"will be a plus\").\\n IMPORTANT RULES (must follow exactly):\\n1) Use heading-based extraction first: if input contains \"Welcome if\", \"Would be a plus\", \"Desirable\" or similar, extract the text under that heading into \"advantages\".\\n2) If there are lines/sentences containing the optional indicators (see list below), include them in \"advantages\".\\n3) **Do NOT** include in \"advantages\" any marketing/benefits/conditions text such as \"What we offer\", \"Dream job\", \"official employment\", \"paid vacation\", \"remotely\", \"schedule\", \"team\", \"adaptation\" — these are company perks, not desirable experience, and must be excluded from advantages.\\n4) If a sentence matches both optional-indicator and benefit keywords, prefer treating it as BENEFIT and therefore do NOT place it into \"advantages\" unless it clearly expresses desirable SKILL/EXPERIENCE.\\n5) If no relevant optional/desirable items are found, OMIT the \"advantages\" section entirely (do not include it with empty text).\\n6) Preserve original wording and line breaks. Do NOT summarize or paraphrase."
                }
              ]
            }
          },
          "executeOnce": false,
          "retryOnFail": true,
          "typeVersion": 1.2
        },
        {
          "id": "e4afd87f-bf00-4e54-8913-765027f94a31",
          "name": "Loop Over Items",
          "type": "n8n-nodes-base.splitInBatches",
          "position": [
            -192,
            1344
          ],
          "parameters": {
            "options": {}
          },
          "typeVersion": 3
        },
        {
          "id": "c19605f4-5f2e-4748-b4c2-f248627699b3",
          "name": "Merge",
          "type": "n8n-nodes-base.merge",
          "position": [
            -16,
            1184
          ],
          "parameters": {
            "mode": "combine",
            "options": {
              "clashHandling": {
                "values": {
                  "resolveClash": "preferLast"
                }
              }
            },
            "combineBy": "combineByPosition"
          },
          "typeVersion": 3.2
        },
        {
          "id": "af2cc2b7-aaad-42b3-af84-81e2aeeb8a1a",
          "name": "ObjToStrArr",
          "type": "n8n-nodes-base.code",
          "position": [
            160,
            1184
          ],
          "parameters": {
            "jsCode": "const items = $input.all();\n\nreturn items.map(item => {\n  // Преобразуем key_skills в массив строк\n  if (Array.isArray(item.json.key_skills)) {\n    item.json.key_skills = item.json.key_skills.map(skill => skill.name);\n  }\n\n  // Преобразуем work_format в массив строк по id\n  if (Array.isArray(item.json.work_format)) {\n    item.json.work_format = item.json.work_format.map(format => format.id);\n  }\n\n  return item;\n});"
          },
          "typeVersion": 2
        },
        {
          "id": "c77a967d-05bf-4c4f-a88f-5e0bf4e23d11",
          "name": "Sections cleanup",
          "type": "n8n-nodes-base.code",
          "position": [
            368,
            1360
          ],
          "parameters": {
            "jsCode": "const input = $input.first().json.output;\nconst sections = Object.keys(input)\n  .map(key => ({\n    section: key,\n    text: input[key].replace(/\\n/g, ' ')\n  }))\n  .filter(section => section.text.trim() !== '');\nreturn [{ json: { sections } }];"
          },
          "typeVersion": 2
        },
        {
          "id": "09bf7483-d866-45cf-8ff5-a51a2ecede2a",
          "name": "Load Content/Metadata",
          "type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
          "position": [
            1104,
            960
          ],
          "parameters": {
            "options": {
              "metadata": {
                "metadataValues": [
                  {
                    "name": "chunk_id",
                    "value": "={{ $('Chunking').item.json.metadata.id }}"
                  },
                  {
                    "name": "title",
                    "value": "={{ $('Chunking').item.json.metadata.title }}"
                  },
                  {
                    "name": "experience_id",
                    "value": "={{ $('Chunking').item.json.metadata.experience.id }}"
                  },
                  {
                    "name": "key_skills",
                    "value": "={{ $('Chunking').item.json.metadata.key_skills }}"
                  },
                  {
                    "name": "company",
                    "value": "={{ $('Chunking').item.json.metadata.company }}"
                  }
                ]
              }
            },
            "jsonData": "={{ $('Chunking').item.json.content }}",
            "jsonMode": "expressionData",
            "textSplittingMode": "custom"
          },
          "typeVersion": 1.1
        },
        {
          "id": "43490c76-a93b-40d2-92d2-a34e912e3c05",
          "name": "Payload Extraction",
          "type": "n8n-nodes-base.set",
          "position": [
            -416,
            1312
          ],
          "parameters": {
            "options": {},
            "assignments": {
              "assignments": [
                {
                  "id": "cbca6f2d-469e-4be9-a42f-02e4403eb2f8",
                  "name": "id",
                  "type": "string",
                  "value": "={{ $json.id }}"
                },
                {
                  "id": "8dc8b87c-bfb7-4655-abd7-c54e5d30a4d4",
                  "name": "title",
                  "type": "string",
                  "value": "={{ $json.name }}"
                },
                {
                  "id": "91281710-76ee-4531-bf5c-ca6e4c5707be",
                  "name": "salary_range",
                  "type": "object",
                  "value": "={{ $json.salary_range }}"
                },
                {
                  "id": "f1953bd7-2ba2-4fb7-b9a8-cf226bf58b25",
                  "name": "location",
                  "type": "string",
                  "value": "={{ $json.address.city }}"
                },
                {
                  "id": "c197a189-80ba-4834-8c3a-40e33dfa0759",
                  "name": "experience",
                  "type": "object",
                  "value": "={{ $json.experience }}"
                },
                {
                  "id": "d2a8f427-565e-49e2-a606-3c5a6cac1836",
                  "name": "sections",
                  "type": "string",
                  "value": "={{ $json.description }}"
                },
                {
                  "id": "ba314b21-e78c-425e-b975-10bb2432ef3d",
                  "name": "key_skills",
                  "type": "array",
                  "value": "={{ $json.key_skills }}"
                },
                {
                  "id": "6b91dc6d-84cf-4ec8-b9f5-decc788efd3f",
                  "name": "source_url",
                  "type": "string",
                  "value": "={{ $json.alternate_url }}"
                },
                {
                  "id": "0a9e300a-f2ea-4ada-be3d-ef971441e80f",
                  "name": "employment_type",
                  "type": "object",
                  "value": "={{ $json.employment_form }}"
                },
                {
                  "id": "8bbaf1fc-9da7-4fc7-84ca-850a8e1905ea",
                  "name": "work_format",
                  "type": "array",
                  "value": "={{ $json.work_format }}"
                },
                {
                  "id": "5b72d543-b957-4dfb-aaa1-f55b1a685248",
                  "name": "company",
                  "type": "string",
                  "value": "={{ $json.employer.name }}"
                }
              ]
            }
          },
          "typeVersion": 3.4
        },
        {
          "id": "1dd77057-c683-4524-8545-2cc7d7749083",
          "name": "Get Vacancy Details",
          "type": "n8n-nodes-base.httpRequest",
          "position": [
            -576,
            1312
          ],
          "parameters": {
            "url": "=https://api.hh.ru/vacancies/{{ $json.id }}",
            "options": {},
            "sendHeaders": true,
            "headerParameters": {
              "parameters": [
                {
                  "name": "User-Agent",
                  "value": "OClick/1.0 (shimorowm@gmail.com)"
                }
              ]
            }
          },
          "typeVersion": 4.2
        },
        {
          "id": "ef6ed304-8f42-4e75-99f5-ca756b748748",
          "name": "Delete Collection1",
          "type": "n8n-nodes-qdrant.qdrant",
          "position": [
            -752,
            848
          ],
          "parameters": {
            "operation": "deleteCollection",
            "collectionName": {
              "__rl": true,
              "mode": "name",
              "value": "vacancies"
            },
            "requestOptions": {}
          },
          "credentials": {
            "qdrantRestApi": {
              "id": "5UV8rQE53hqsh82x",
              "name": "Qdrant account"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "566543c1-9c0d-4983-a771-b1b135c61269",
          "name": "When clicking ‘Execute workflow’",
          "type": "n8n-nodes-base.manualTrigger",
          "position": [
            -976,
            752
          ],
          "parameters": {},
          "typeVersion": 1
        },
        {
          "id": "67c03245-4d13-4324-9bbe-2b930c625c87",
          "name": "Sticky Note",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            -1056,
            592
          ],
          "parameters": {
            "width": 693,
            "height": 410,
            "content": "## Clear Databases"
          },
          "typeVersion": 1
        },
        {
          "id": "9c0be8ed-4fb6-4976-91b4-6854ed3e99f6",
          "name": "Scroll Points",
          "type": "n8n-nodes-qdrant.qdrant",
          "position": [
            528,
            944
          ],
          "parameters": {
            "filter": "={\n  \"must\": [\n      {\n        \"key\": \"vacancy_id\",\n        \"match\": {\n          \"value\": {{ $json.metadata.document_id }}\n        }\n      }\n    ]\n}",
            "resource": "point",
            "operation": "scrollPoints",
            "collectionName": {
              "__rl": true,
              "mode": "list",
              "value": "vacancies",
              "cachedResultName": "vacancies"
            },
            "requestOptions": {}
          },
          "credentials": {
            "qdrantRestApi": {
              "id": "5UV8rQE53hqsh82x",
              "name": "Qdrant account"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "c6e560c0-8868-41a0-9b50-483df14ca2ca",
          "name": "If1",
          "type": "n8n-nodes-base.if",
          "position": [
            720,
            944
          ],
          "parameters": {
            "options": {},
            "conditions": {
              "options": {
                "version": 2,
                "leftValue": "",
                "caseSensitive": true,
                "typeValidation": "strict"
              },
              "combinator": "and",
              "conditions": [
                {
                  "id": "62eac888-357f-4c41-9e12-163819307fd6",
                  "operator": {
                    "type": "array",
                    "operation": "notEmpty",
                    "singleValue": true
                  },
                  "leftValue": "={{ $json.result.points }}",
                  "rightValue": ""
                }
              ]
            }
          },
          "typeVersion": 2.2
        },
        {
          "id": "e5c58b3c-295c-4162-ae1d-b404035b864b",
          "name": "Delete Points",
          "type": "n8n-nodes-qdrant.qdrant",
          "position": [
            880,
            752
          ],
          "parameters": {
            "filter": "={\n  \"must\": [\n      {\n        \"key\": \"vacancy_id\",\n        \"match\": {\n          \"value\": {{ $json.result.points[0].payload.vacancy_id }}\n        }\n      }\n    ]\n}",
            "resource": "point",
            "operation": "deletePoints",
            "collectionName": {
              "__rl": true,
              "mode": "list",
              "value": "vacancies",
              "cachedResultName": "vacancies"
            },
            "requestOptions": {}
          },
          "credentials": {
            "qdrantRestApi": {
              "id": "5UV8rQE53hqsh82x",
              "name": "Qdrant account"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "68b762f7-3ade-4b6d-8f0a-2dc9a4534ecc",
          "name": "AI Agent",
          "type": "@n8n/n8n-nodes-langchain.agent",
          "position": [
            1808,
            1280
          ],
          "parameters": {
            "text": "Создай очень короткое сопроводительное письмо используя актуальную вакансию из базы данных, объясняя работодателю, почему именно я подхожу на эту вакансию. Основывайся при этом на моём резюме, отвечай на русском.\nResume:\n```txt\nМаксим Кришталь\nМужчина\nshimorowm@gmail.com\nПроживает: Санкт-Петербург\nГражданство: Россия, есть разрешение на работу: Россия\nJava-разработчик\nСпециализации:\nПрограммист, разработчик\nОпыт работы — 1 год 3 месяца\nMicroRestBank\ngithub.com/22crystyle/MicroRestBank\nАвгуст 2024 — настоящее время 1 год 3 месяца\nJava-разработчик\nРазработка комплексного банковского приложения на основе микросервисной архитектуры.\nОписание: Проект MicroRestBank представляет собой платформу для управления пользователями и банковскими картами через RESTful API. Система включает в себя функции создания карт, их блокировки, управления пользователями и осуществления денежных переводов.\nАрхитектура и Реализация: Спроектировал и разработал набор независимых микросервисов на Java 21 и Spring Boot 3, включая:\n- Разрабатывал и отлаживал серверный Java-код (Spring Boot 3, Spring Data JPA, Spring Security) - опыт root-cause analysis и hotfix’ов в продакшн/стейдж окружениях.\n- Единая точка входа для всех клиентских запросов, реализованная на Spring Cloud Gateway.\n- Создал и поддерживал механизмы аутентификации и авторизации с Keycloak (OAuth2, JWT), в т.ч. стратегию ролей и прав доступа.\n- Реализация всей бизнес-логики для банковских карт, включая транзакции и управление статусами.\n- Управление данными клиентов.\n- Сервис-дискавери для динамического взаимодействия между сервисами (Spring Cloud Netflix Eureka).\n- Проектировал и реализовал интеграционные механизмы между микросервисами (REST, Kafka), обеспечив надёжную асинхронную репликацию данных.\n- Реализовал асинхронное взаимодействие между сервисами для обновления данных с помощью Apache Kafka и Debezium (Change Data Capture).\n- Управлял схемами БД и миграциями (PostgreSQL, Liquibase); писал оптимизированные SQL-запросы для аналитики и исправления ошибок.\n- Документировал API с использованием OpenAPI (Swagger).\n- Писал unit и интеграционные тесты (JUnit, Mockito) и автоматизировал сценарии сборки/деплоя.\n- Настроил CI/CD и контейнеризацию (Gradle, Docker, Docker Compose) для локальной и командной разработки.\nСтек:\n- Бэкенд: Java 21, Spring Boot 3, Spring Framework, Spring Data JPA, Spring Security\n- Микросервисы: Microservices Architecture, Spring Cloud (Gateway, Netflix Eureka), RESTful API, Event-Driven Architecture\n- Базы данных и брокеры сообщений: PostgreSQL, Liquibase, Apache Kafka, Debezium (Change Data Capture)\n- Аутентификация и Авторизация: Keycloak, JWT, OAuth2\n- DevOps и Инструменты: Docker, Docker Compose, Gradle, Git, CI/CD, JetBrains IntelliJ IDEA\n- API и Документация: OpenAPI (Swagger)\nОбразование\nВысшее\nСанкт-Петербургский государственный электротехнический университет «ЛЭТИ» имени В.И. Ульянова (Ленина), Санкт-Петербург\n2025\nФакультет компьютерных технологий и информатики, Прикладная математика и информатика\nНавыки\nЗнание языков\nРусский — Родной\nАнглийский — B1 — Средний\nНавыки\nJava; Spring Framework; Hibernate; Git; Docker; Liquibase; Apache Kafka; Gradle; Apache Maven; PostgreSQL; Linux; REST API; Java SE; JPA; Английский язык; Spring; Spring Boot; Spring Cloud; Spring Data; Spring MVC; Spring Security; Spring Web; Keycloak; Hibernate ORM; SQL; SOLID; ООП; JUnit; Debezium; Mockito\nДополнительная информация\nОбо мне\nJava backend-разработчик, ориентированный на качество и поддерживаемость кода. Владею современным стеком:\n- Бэкенд: Java 21, Spring Boot 3, Spring Framework, Spring Data JPA, Spring Security\n- Микросервисы: Microservices Architecture, Spring Cloud (Gateway, Netflix Eureka), RESTful API, Event-Driven Architecture\n- Базы данных и брокеры сообщений: PostgreSQL, Liquibase, Apache Kafka, Debezium (Change Data Capture)\n- Аутентификация и Авторизация: Keycloak, JWT, OAuth2\n- DevOps и Инструменты: Docker, Docker Compose, Gradle, Git, CI/CD, JetBrains IntelliJ IDEA\n- API и Документация: OpenAPI (Swagger)\nПрименяю принципы ООП и SOLID, практикую тестирование и автоматизацию процессов. Быстро адаптируюсь к новым задачам и технологиям.\n```",
            "options": {},
            "promptType": "define"
          },
          "typeVersion": 2.2
        },
        {
          "id": "a5f7af9f-e285-4147-b116-a52abe4bf3f0",
          "name": "Embeddings Ollama1",
          "type": "@n8n/n8n-nodes-langchain.embeddingsOllama",
          "position": [
            1840,
            1600
          ],
          "parameters": {
            "model": "qwen3-embedding:8b"
          },
          "credentials": {
            "ollamaApi": {
              "id": "JrVtVfb5WaGIXcqR",
              "name": "Ollama RTX"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "7c87de4d-fa86-4e48-a3fc-ea830bb26a0b",
          "name": "Ollama Chat Model1",
          "type": "@n8n/n8n-nodes-langchain.lmChatOllama",
          "position": [
            1728,
            1472
          ],
          "parameters": {
            "model": "qwen3:4b-instruct-2507-q8_0",
            "options": {
              "numCtx": 128000
            }
          },
          "credentials": {
            "ollamaApi": {
              "id": "JrVtVfb5WaGIXcqR",
              "name": "Ollama RTX"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "a82edf70-84dd-4aaf-9c41-877128f3bf18",
          "name": "Actual Job Vacancies",
          "type": "@n8n/n8n-nodes-langchain.vectorStoreQdrant",
          "position": [
            1840,
            1472
          ],
          "parameters": {
            "mode": "retrieve-as-tool",
            "topK": 3,
            "options": {
              "searchFilterJson": "={\n  \"should\": [\n    {\n      \"key\": \"vacancy_id\",\n      \"match\": {\n        \"value\": {{ $('Merge1').item.json.id }}\n      }\n    }\n  ]\n}"
            },
            "toolDescription": "Database of Actual Job Vacancies",
            "qdrantCollection": {
              "__rl": true,
              "mode": "list",
              "value": "vacancies",
              "cachedResultName": "vacancies"
            }
          },
          "credentials": {
            "qdrantApi": {
              "id": "sFfERYppMeBnFNeA",
              "name": "Local QdrantApi database"
            }
          },
          "typeVersion": 1.3
        },
        {
          "id": "ed665982-6c49-494f-b1b6-26a059ed476d",
          "name": "Merge1",
          "type": "n8n-nodes-base.merge",
          "position": [
            1408,
            1232
          ],
          "parameters": {
            "mode": "chooseBranch",
            "useDataOfInput": 2
          },
          "typeVersion": 3.2
        },
        {
          "id": "9cfc07b6-3b1a-468e-979f-d9b5fbaadd88",
          "name": "Loop Over Items1",
          "type": "n8n-nodes-base.splitInBatches",
          "position": [
            1584,
            1264
          ],
          "parameters": {
            "options": {}
          },
          "typeVersion": 3
        },
        {
          "id": "c008230f-1f41-4944-9ce5-646041d7624e",
          "name": "Delete table or rows",
          "type": "n8n-nodes-base.postgres",
          "position": [
            -752,
            656
          ],
          "parameters": {
            "table": {
              "__rl": true,
              "mode": "list",
              "value": "n8n_chat_histories",
              "cachedResultName": "n8n_chat_histories"
            },
            "schema": {
              "__rl": true,
              "mode": "list",
              "value": "public"
            },
            "options": {},
            "operation": "deleteTable"
          },
          "credentials": {
            "postgres": {
              "id": "eCOKw4m3IeuEI0rT",
              "name": "Postgres account"
            }
          },
          "typeVersion": 2.6
        },
        {
          "id": "c1f0b4fc-32a6-482a-909e-c239615dd369",
          "name": "Create Collection",
          "type": "n8n-nodes-qdrant.qdrant",
          "position": [
            -528,
            848
          ],
          "parameters": {
            "vectors": "{\n  \"size\": 4096,\n  \"distance\": \"Cosine\",\n  \"on_disk\": true,\n  \"datatype\": \"float32\"\n}",
            "operation": "createCollection",
            "walConfig": "{\n  \"wal_capacity_mb\": 32,\n  \"wal_segments_ahead\": 0,\n  \"wal_retain_closed\": 1\n}",
            "hnswConfig": "{\n  \"m\": 16,\n  \"ef_construct\": 100,\n  \"full_scan_threshold\": 10000,\n  \"max_indexing_threads\": 0,\n  \"on_disk\": false\n}",
            "shardNumber": 1,
            "onDiskPayload": true,
            "collectionName": "vacancies",
            "requestOptions": {},
            "optimizersConfig": "{\n  \"deleted_threshold\": 0.2,\n  \"vacuum_min_vector_number\": 1000,\n  \"default_segment_number\": 0,\n  \"max_segment_size\": null,\n  \"memmap_threshold\": null,\n  \"indexing_threshold\": 10000,\n  \"flush_interval_sec\": 5,\n  \"max_optimization_threads\": null\n}",
            "strictModeConfig": "{\n  \"enabled\": false\n}",
            "replicationFactor": 1,
            "writeConsistencyFactor": 1
          },
          "credentials": {
            "qdrantRestApi": {
              "id": "5UV8rQE53hqsh82x",
              "name": "Qdrant account"
            }
          },
          "executeOnce": false,
          "typeVersion": 1
        }
      ],
      "active": false,
      "pinData": {},
      "settings": {
        "executionOrder": "v1"
      },
      "versionId": "e895b086-6f7a-4769-89e1-f36d3cd85548",
      "connections": {
        "If": {
          "main": [
            [],
            [
              {
                "node": "Vacancy Details",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "If1": {
          "main": [
            [
              {
                "node": "Delete Points",
                "type": "main",
                "index": 0
              }
            ],
            [
              {
                "node": "Save To Qdrant",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Merge": {
          "main": [
            [
              {
                "node": "ObjToStrArr",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Merge1": {
          "main": [
            [
              {
                "node": "Loop Over Items1",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "AI Agent": {
          "main": [
            [
              {
                "node": "Loop Over Items1",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Chunking": {
          "main": [
            [
              {
                "node": "Scroll Points",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Split Out": {
          "main": [
            [
              {
                "node": "If",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Split Out1": {
          "main": [
            [
              {
                "node": "Remove Duplicates",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Auto-search": {
          "main": [
            [
              {
                "node": "Split Out",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "ObjToStrArr": {
          "main": [
            [
              {
                "node": "Chunking",
                "type": "main",
                "index": 0
              },
              {
                "node": "Merge1",
                "type": "main",
                "index": 1
              }
            ]
          ]
        },
        "Set Payload": {
          "main": [
            [
              {
                "node": "Merge1",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Delete Points": {
          "main": [
            [
              {
                "node": "Save To Qdrant",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Scroll Points": {
          "main": [
            [
              {
                "node": "If1",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Save To Qdrant": {
          "main": [
            [
              {
                "node": "Set Payload",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Token Splitter": {
          "ai_textSplitter": [
            [
              {
                "node": "Load Content/Metadata",
                "type": "ai_textSplitter",
                "index": 0
              }
            ]
          ]
        },
        "qwen3:4b:it:q8": {
          "ai_languageModel": [
            [
              {
                "node": "Information Extractor",
                "type": "ai_languageModel",
                "index": 0
              }
            ]
          ]
        },
        "Loop Over Items": {
          "main": [
            [
              {
                "node": "Merge",
                "type": "main",
                "index": 1
              }
            ],
            [
              {
                "node": "Information Extractor",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Vacancy Details": {
          "main": [
            [
              {
                "node": "Split Out1",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "qwen3-embedding": {
          "ai_embedding": [
            [
              {
                "node": "Save To Qdrant",
                "type": "ai_embedding",
                "index": 0
              }
            ]
          ]
        },
        "Loop Over Items1": {
          "main": [
            [],
            [
              {
                "node": "AI Agent",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Schedule Trigger": {
          "main": [
            [
              {
                "node": "Auto-search",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Sections cleanup": {
          "main": [
            [
              {
                "node": "Loop Over Items",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Remove Duplicates": {
          "main": [
            [
              {
                "node": "Get Vacancy Details",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Delete Collection1": {
          "main": [
            [
              {
                "node": "Create Collection",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Embeddings Ollama1": {
          "ai_embedding": [
            [
              {
                "node": "Actual Job Vacancies",
                "type": "ai_embedding",
                "index": 0
              }
            ]
          ]
        },
        "Ollama Chat Model1": {
          "ai_languageModel": [
            [
              {
                "node": "AI Agent",
                "type": "ai_languageModel",
                "index": 0
              }
            ]
          ]
        },
        "Payload Extraction": {
          "main": [
            [
              {
                "node": "Loop Over Items",
                "type": "main",
                "index": 0
              },
              {
                "node": "Merge",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Get Vacancy Details": {
          "main": [
            [
              {
                "node": "Payload Extraction",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Actual Job Vacancies": {
          "ai_tool": [
            [
              {
                "node": "AI Agent",
                "type": "ai_tool",
                "index": 0
              }
            ]
          ]
        },
        "Information Extractor": {
          "main": [
            [
              {
                "node": "Sections cleanup",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Load Content/Metadata": {
          "ai_document": [
            [
              {
                "node": "Save To Qdrant",
                "type": "ai_document",
                "index": 0
              }
            ]
          ]
        },
        "When clicking ‘Execute workflow’": {
          "main": [
            [
              {
                "node": "Delete Collection1",
                "type": "main",
                "index": 0
              },
              {
                "node": "Delete table or rows",
                "type": "main",
                "index": 0
              }
            ]
          ]
        }
      }
    }