OClick/1.1 Detailed Letter/Telegram copy
Shared 2/13/2026
1 views
Visual Workflow
JSON Code
{
"id": "cr9sSd8cfwjqWFKD",
"meta": {
"instanceId": "a7951da00dc167f722a4bd0bc0e175503754ae4ae331d85018109ce22f7c48f6",
"templateCredsSetupCompleted": true
},
"name": "OClick/1.1 Detailed Letter/Telegram copy",
"tags": [],
"nodes": [
{
"id": "8638bbc7-67f4-4728-833d-3001f2796aae",
"name": "Delete Collection1",
"type": "n8n-nodes-qdrant.qdrant",
"position": [
-16,
1344
],
"parameters": {
"operation": "deleteCollection",
"collectionName": {
"__rl": true,
"mode": "name",
"value": "vacancies"
},
"requestOptions": {}
},
"credentials": {
"qdrantRestApi": {
"id": "5UV8rQE53hqsh82x",
"name": "Qdrant account"
}
},
"typeVersion": 1
},
{
"id": "dc80d89f-d90c-4c02-818c-f21dee05238f",
"name": "When clicking ‘Execute workflow’",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-240,
1344
],
"parameters": {},
"typeVersion": 1
},
{
"id": "c9cf5d34-1f61-47d8-a541-3258675e98ee",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-320,
1104
],
"parameters": {
"width": 693,
"height": 410,
"content": "## Clear Databases"
},
"typeVersion": 1
},
{
"id": "1b120327-3310-443f-9838-2afae2ecf13f",
"name": "Create Collection",
"type": "n8n-nodes-qdrant.qdrant",
"position": [
208,
1344
],
"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
},
{
"id": "9f40f280-4f71-4aed-8435-d552e0d6feaf",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-512,
1600
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "2edbfde7-7686-4916-8539-e6a723b41cf1",
"name": "Auto-search",
"type": "n8n-nodes-base.httpRequest",
"position": [
-80,
1600
],
"parameters": {
"url": "https://api.hh.ru/saved_searches/vacancies",
"options": {
"batching": {
"batch": {
"batchSize": 30
}
}
},
"sendQuery": true,
"sendHeaders": true,
"authentication": "genericCredentialType",
"genericAuthType": "oAuth2Api",
"queryParameters": {
"parameters": [
{
"name": "per_page",
"value": "10"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "User-Agent",
"value": "OClick/1.1 (shimorowm@gmail.com)"
}
]
}
},
"credentials": {
"oAuth2Api": {
"id": "l9tyPEWtPvzUKFXJ",
"name": "HH OAuth2"
}
},
"typeVersion": 4.2
},
{
"id": "698d41f7-9e70-4e72-955d-bba1052fcd3c",
"name": "Split Out",
"type": "n8n-nodes-base.splitOut",
"position": [
144,
1600
],
"parameters": {
"options": {},
"fieldToSplitOut": "items"
},
"typeVersion": 1
},
{
"id": "4e68e8ad-19ac-4d28-8013-ef282fd9d668",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
-288,
1872
],
"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": "9f1b909e-79c0-4218-81f5-12882161556f",
"name": "Split Out1",
"type": "n8n-nodes-base.splitOut",
"position": [
144,
1856
],
"parameters": {
"options": {},
"fieldToSplitOut": "items"
},
"typeVersion": 1
},
{
"id": "01f941c2-83f7-407b-87d7-03432e3af998",
"name": "Remove Duplicates",
"type": "n8n-nodes-base.removeDuplicates",
"position": [
144,
2128
],
"parameters": {
"compare": "selectedFields",
"options": {},
"fieldsToCompare": "id"
},
"typeVersion": 2
},
{
"id": "25dc1ea7-4add-40f2-8d26-17b8a7b96405",
"name": "Chunking",
"type": "n8n-nodes-base.code",
"position": [
1392,
2048
],
"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": "e0fa7c21-96a1-455a-b650-1f67bc92423f",
"name": "Token Splitter",
"type": "@n8n/n8n-nodes-langchain.textSplitterTokenSplitter",
"position": [
2544,
2480
],
"parameters": {
"chunkSize": 500,
"chunkOverlap": 100
},
"typeVersion": 1
},
{
"id": "c82ccef8-45dd-4756-abb6-d133257bf157",
"name": "Save To Qdrant",
"type": "@n8n/n8n-nodes-langchain.vectorStoreQdrant",
"position": [
2368,
2048
],
"parameters": {
"mode": "insert",
"options": {},
"qdrantCollection": {
"__rl": true,
"mode": "id",
"value": "vacancies"
}
},
"credentials": {
"qdrantApi": {
"id": "sFfERYppMeBnFNeA",
"name": "Local QdrantApi database"
}
},
"typeVersion": 1.3
},
{
"id": "3ef51f3e-b7bb-4d52-b609-ea19923431b5",
"name": "Set Payload",
"type": "n8n-nodes-qdrant.qdrant",
"onError": "continueErrorOutput",
"position": [
2672,
2048
],
"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": "2245926e-336e-4886-bd61-a4cacce1476b",
"name": "Information Extractor",
"type": "@n8n/n8n-nodes-langchain.informationExtractor",
"position": [
944,
2144
],
"parameters": {
"text": "={{ $json.sections }}",
"options": {
"systemPromptTemplate": "Ты — эксперт по извлечению информации на нескольких языках. Ты работаешь с описаниями вакансий на русском и английском.\n\n# Инструкция по языку (высший приоритет): \n- Определи основной язык входного текста (русский или английский). \n- Вывод строго на том же языке. \n- Никогда не переводи и не транслитерируй. \n- Сохраняй любые смешанные сегменты (например, русские + английские термины). \n- Следуйте всем последующим правилам извлечения и форматирования.\n\nТвоя задача — создать чистый, хорошо форматированный JSON-объект с ключами: responsibilities, requirements, additional.\n\n# Правила (строгое применение по порядку):\n\n1) Приоритет извлечения:\n- Всегда отдавай предпочтение извлечению по заголовкам. Если найден релевантный заголовок, извлекай только содержимое под ним. \n- Если явного заголовка нет, логически определи секцию по контексту.\n\n2) Правила для полей:\n- Применяй точные правила извлечения для каждого поля (RESPONSIBILITIES, REQUIREMENTS, ADDITIONAL).\n\n3) Очистка HTML и форматирование:\n- Удаляй все HTML-теги. \n- Преобразовывай <br>, <br/> и <br /> в \"; \". \n- Сохраняй только простой текст и элементы списков; остальные теги и разметку удаляй.\n\n4) Форматирование вывода (СТРОГО):\n- Каждый элемент или предложение ДОЛЖНА начинаться с заглавной буквы (например, \"Разрабатывать REST API; Проектировать архитектуру\", а не \"разрабатывать\" или \"- разрабатывать\"). \n- Строго разделяй элементы \"; \" (точка с запятой + пробел). \n- Никогда не начинайте строку или фразу с \"-\", \"—\", \"•\" или \"*\". \n- Всегда используй заглавную букву после каждой точки с запятой. \n- Не добавляй знаки препинания после последнего предложения. \n- Не добавляй лишние пробелы, переносы строк или повторяющиеся разделители.\n\n# Пример корректного вывода:\n\"Проектировать архитектуру системы; Писать unit-тесты; Поддерживать CI/CD pipelines\"\n\n# Пример некорректного вывода:\n\"- проектировать архитектуру системы; писать unit-тесты; поддерживать ci/cd pipelines.\""
},
"attributes": {
"attributes": [
{
"name": "responsibilities",
"description": "# RESPONSIBILITIES — строгие правила:\n- Извлекай ТОЛЬКО обязанности / задачи, которые будет выполнять кандидат.\n- Используй извлечение по заголовкам: если найден заголовок, например \"Обязанности\" или \"Задачи\", извлекайте только содержимое под ним.\n- НЕ включай маркетинговые или условия работы (например: \"Мы предлагаем\", \"Удалённая работа\", \"Health insurance\", \"Flexible schedule\", \"Заработная плата\", \"Команда\", \"Onboarding\", \"Corporate events\", \"Мы обеспечиваем\").\n- Если строка содержит одновременно задачу и бонус/условие, ИСКЛЮЧИ её из обязанностей (по умолчанию).\n- Сохраняй оригинальную формулировку, но удаляйте все HTML-теги; заменяйте переносы строк и маркеры списков на \"; \".\n- Технические термины (языки, фреймворки, библиотеки, инструменты, версии, CamelCase, слэши, дефисы) сохраняйте точно так, как в исходном тексте. Не переводите, не транслитерируйте. \n- Сохраняйте заглавную букву в начале каждого предложения для описательных слов, но не меняйте имена технологий. \n\n# Правило согласования языка:\n- Язык извлечённого текста должен точно соответствовать исходному.\n- Если вход на русском — вывод на русском.\n- Если вход на английском — вывод на английском.\n- Никогда не переводите и не переформулируйте терминологию."
},
{
"name": "requirements",
"required": true,
"description": "# REQUIREMENTS — строгие правила:\n- Извлекай ТОЛЬКО обязательные требования / необходимый опыт или навыки, явно указанные как требования (например: \"Требуется\", \"Необходимый опыт\", \"Мы ожидаем\").\n- Предпочтение: если найден заголовок \"Требования\" или \"Необходимый опыт\", извлекай только эту секцию.\n- Сохраняйте все технические термины, версии и детали (например: \"Java 21\", \"Spring Boot 3.5+\", \"Kafka\", \"PostgreSQL\").\n- Не включай маркетинговые или необязательные фразы (их можно направить в \"additional\", если явно указывают на желательные навыки).\n- Формат: разделяй элементы строго через \"; \".\n- Технические термины (языки, фреймворки, библиотеки, инструменты, версии, CamelCase, слэши, дефисы) сохраняйте точно так, как в исходном тексте. Не переводите, не транслитерируйте. \n- Сохраняйте заглавную букву в начале каждого предложения для описательных слов, но не меняйте имена технологий. \n# Правило согласования языка:\n- Язык извлечённого текста должен точно соответствовать исходному.\n- Если вход на русском — вывод на русском.\n- Если вход на английском — вывод на английском.\n- Никогда не переводи и не переформулируй терминологию."
},
{
"name": "additional",
"description": "# ADDITIONAL — строгие правила:\n- Извлекай ТОЛЬКО желательные / опциональные навыки или опыт, явно указанные фразами вроде \"Будет плюсом\", \"Желательно\", \"Desirable\", \"Приветствуется\", \"Will be a plus\".\n- Сначала используй извлечение по заголовкам: если найден заголовок \"Будет плюсом\" или \"Желательно\", бери только его содержимое.\n- Также включай предложения, содержащие индикаторы желательности (см. список выше).\n- НЕ включай льготы, бонусы или условия работы (см. блоклист). Если строка содержит одновременно желательный навык и бонус, классифицируй её как БОНУС и ИСКЛЮЧИ из \"additional\".\n- Если релевантных опциональных пунктов нет — ОПУСТИТЕ поле \"additional\" (не возвращайте пустую строку).\n- Сохраняй оригинальную формулировку и переносы строк, заменяя их на \"; \".\n- Технические термины (языки, фреймворки, библиотеки, инструменты, версии, CamelCase, слэши, дефисы) сохраняйте точно так, как в исходном тексте. Не переводите, не транслитерируйте. \n- Сохраняйте заглавную букву в начале каждого предложения для описательных слов, но не меняйте имена технологий. \n# Правило согласования языка:\n- Язык извлечённого текста должен точно соответствовать исходному.\n- Если вход на русском — вывод на русском.\n- Если вход на английском — вывод на английском.\n- Никогда не переводи и не переформулируй терминологию.\nБлоклист: [\"мы предоставляем\",\"официальн\",\"удал\",\"гибк\",\"дмс\",\"зарплат\",\"компенсац\",\"корпоратив\",\"адаптац\",\"аккредит\",\"трудоустройств\", \"график\", \"условия\", \"предлагаем\"]"
}
]
}
},
"executeOnce": false,
"retryOnFail": true,
"typeVersion": 1.2
},
{
"id": "154a2e99-d977-4097-a97b-53bfb0b6c228",
"name": "ObjToStrArr",
"type": "n8n-nodes-base.code",
"position": [
1104,
1872
],
"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": "51b324db-af92-4d08-9d9b-2a957d693b45",
"name": "Sections cleanup",
"type": "n8n-nodes-base.code",
"position": [
1296,
2272
],
"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": "1fa8df0d-40bf-4f64-acfd-212701c44faf",
"name": "Load Content/Metadata",
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
"position": [
2464,
2272
],
"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 }}"
},
{
"name": "section",
"value": "={{ $('Chunking').item.json.metadata.section }}"
}
]
}
},
"jsonData": "={{ $('Chunking').item.json.content }}",
"jsonMode": "expressionData",
"textSplittingMode": "custom"
},
"typeVersion": 1.1
},
{
"id": "48d0c20c-7fd6-4ac4-b3d6-a8c111505da6",
"name": "Payload Extraction",
"type": "n8n-nodes-base.set",
"position": [
368,
2128
],
"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": "f5913dfe-3497-4103-94b6-0d34d09f6ae2",
"name": "Scroll Points",
"type": "n8n-nodes-qdrant.qdrant",
"position": [
1568,
2048
],
"parameters": {
"limit": 100,
"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": "d0e8d314-0b4f-46b6-b635-47840f1f1576",
"name": "If1",
"type": "n8n-nodes-base.if",
"position": [
1744,
2048
],
"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": "ea783bd8-2f4f-4a8b-bd22-456605bbbffe",
"name": "Delete Points",
"type": "n8n-nodes-qdrant.qdrant",
"position": [
1968,
1952
],
"parameters": {
"filter": "={\n \"must\": [\n {\n \"key\": \"vacancy_id\",\n \"match\": {\n \"value\": {{ $json.result.points[0].payload.vacancy_id }}\n }\n }\n ]\n}",
"points": "null",
"resource": "point",
"operation": "deletePoints",
"collectionName": {
"__rl": true,
"mode": "list",
"value": "vacancies",
"cachedResultName": "vacancies"
},
"requestOptions": {}
},
"credentials": {
"qdrantRestApi": {
"id": "5UV8rQE53hqsh82x",
"name": "Qdrant account"
}
},
"typeVersion": 1
},
{
"id": "c97d612b-143d-4e95-bed1-e4c762e95965",
"name": "qwen3-embedd-save",
"type": "@n8n/n8n-nodes-langchain.embeddingsOllama",
"position": [
2176,
2272
],
"parameters": {
"model": "qwen3-embedding:8b"
},
"credentials": {
"ollamaApi": {
"id": "JrVtVfb5WaGIXcqR",
"name": "Ollama RTX"
}
},
"typeVersion": 1
},
{
"id": "0742baef-1e63-42a8-b14b-4861c3ec367a",
"name": "qwen3:4b:it:q8-extract",
"type": "@n8n/n8n-nodes-langchain.lmOllama",
"position": [
848,
2352
],
"parameters": {
"model": "qwen3:4b-instruct-2507-q8_0",
"options": {
"topK": 1,
"topP": 1,
"format": "json",
"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": "51c2876d-78d9-4c01-9803-bd045ff3aede",
"name": "Get New Vacancies",
"type": "n8n-nodes-base.httpRequest",
"position": [
-80,
1856
],
"parameters": {
"url": "={{ $json.new_items.url }}",
"options": {
"batching": {
"batch": {
"batchSize": 30
}
}
},
"sendQuery": true,
"sendHeaders": true,
"authentication": "genericCredentialType",
"genericAuthType": "oAuth2Api",
"queryParameters": {
"parameters": [
{
"name": "per_page",
"value": "60"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "User-Agent",
"value": "OClick/1.1 (shimorowm@gmail.com)"
}
]
}
},
"credentials": {
"oAuth2Api": {
"id": "l9tyPEWtPvzUKFXJ",
"name": "HH OAuth2"
}
},
"typeVersion": 4.2
},
{
"id": "e413f625-e123-4a4d-9d08-5d115b1e0817",
"name": "Get Vacancy Details",
"type": "n8n-nodes-base.httpRequest",
"position": [
-288,
2128
],
"parameters": {
"url": "=https://api.hh.ru/vacancies/{{ $json.id }}",
"options": {
"batching": {
"batch": {
"batchSize": 30
}
}
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "User-Agent",
"value": "OClick/1.1 (shimorowm@gmail.com)"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "83b6f014-296c-4545-bc0d-d35c99463362",
"name": "Loop Over Jobs",
"type": "n8n-nodes-base.splitInBatches",
"position": [
592,
2128
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "70326db7-08c4-472f-a432-c17076e31d6a",
"name": "If not enough data",
"type": "n8n-nodes-base.if",
"position": [
3904,
1904
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "0b94e219-2673-4302-b263-0feeb5bc7fe2",
"operator": {
"type": "string",
"operation": "notContains"
},
"leftValue": "={{ $json.output }}",
"rightValue": "Нет данных"
},
{
"id": "7e202110-3b82-4354-bb3d-701f3a030b4a",
"operator": {
"type": "string",
"operation": "notContains"
},
"leftValue": "={{ $json.output }}",
"rightValue": "Нет совпадений"
},
{
"id": "cc9ccb62-0364-45c3-9a1e-ab5cc714b1de",
"operator": {
"type": "string",
"operation": "notContains"
},
"leftValue": "={{ $json.output }}",
"rightValue": "No data"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "abbb7110-aa9b-4af4-ac03-29329b0628d7",
"name": "Loop Over Descriptions",
"type": "n8n-nodes-base.splitInBatches",
"position": [
3120,
1888
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "68380169-3663-4943-bdbf-69d7091778ca",
"name": "Send Negogiation",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueErrorOutput",
"position": [
4304,
1888
],
"parameters": {
"url": "https://api.hh.ru/negotiations",
"method": "POST",
"options": {},
"sendBody": true,
"contentType": "multipart-form-data",
"sendHeaders": true,
"authentication": "genericCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "message",
"value": "={{ $('If not enough data').item.json.output }}"
},
{
"name": "resume_id",
"value": "054142d0ff0f5da2090039ed1f47344b585273"
},
{
"name": "vacancy_id",
"value": "={{ $('Loop Over Descriptions').item.json.id }}"
}
]
},
"genericAuthType": "oAuth2Api",
"headerParameters": {
"parameters": [
{
"name": "User-Agent",
"value": "OClick/1.1 (shimorowm@gmail.com)"
}
]
}
},
"credentials": {
"oAuth2Api": {
"id": "l9tyPEWtPvzUKFXJ",
"name": "HH OAuth2"
}
},
"typeVersion": 4.2
},
{
"id": "3f047621-bd1c-4f9d-8bb9-82f561984f4c",
"name": "qwen3:4b:it:q8-generate",
"type": "@n8n/n8n-nodes-langchain.lmChatOllama",
"position": [
3072,
2272
],
"parameters": {
"model": "qwen3:4b-instruct-2507-q8_0",
"options": {
"topK": 20,
"topP": 1,
"numCtx": 8192,
"numBatch": 1,
"numThread": 14,
"temperature": 0.7,
"repeatPenalty": 1,
"penalizeNewline": false,
"presencePenalty": 0.2,
"frequencyPenalty": 0
}
},
"credentials": {
"ollamaApi": {
"id": "JrVtVfb5WaGIXcqR",
"name": "Ollama RTX"
}
},
"typeVersion": 1
},
{
"id": "b9187a1c-3925-4fcc-beb3-3d708e3045aa",
"name": "Format List",
"type": "n8n-nodes-base.code",
"position": [
4128,
1888
],
"parameters": {
"jsCode": "const newItems = $('Loop Over Descriptions').all().map(item => {\n const newSections = item.json.sections.map(section => {\n const listItems = section.text\n .split(';')\n .map(s => s.trim())\n .filter(Boolean)\n .map(s => `- ${s}`)\n .join('\\n');\n return {\n ...section,\n textList: listItems\n };\n });\n return {\n json: {\n ...item.json,\n sections: newSections\n }\n };\n});\n\nreturn newItems;"
},
"typeVersion": 2
},
{
"id": "3c9009a6-7645-40fe-ad6b-b3465ea69d83",
"name": "Edit Fields",
"type": "n8n-nodes-base.set",
"position": [
3680,
1904
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "9bb3c667-a07a-4890-ac97-b6fcc8076a2e",
"name": "output",
"type": "string",
"value": "={{ $json.output.replaceAll('—', '-') }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "70ffee5e-8d87-44e9-a2ef-b09d6684d6f8",
"name": "Code in JavaScript1",
"type": "n8n-nodes-base.code",
"position": [
768,
2144
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "let html = $json.sections;\nhtml = html.replace(/<br\\s*\\/?>/gi, '\\n');\nhtml = html.replace(/<[^>]+>/g, '');\nhtml = html.trim();\n\nreturn {\n json: {\n ...$json,\n sections: html\n }\n};"
},
"typeVersion": 2
},
{
"id": "1894bc5d-bae2-41cb-be29-d5ba8384acfe",
"name": "Create Telegraph account",
"type": "n8n-nodes-base.httpRequest",
"position": [
-240,
1168
],
"parameters": {
"url": "https://api.telegra.ph/createAccount",
"method": "POST",
"options": {},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "short_name",
"value": "xsarif"
},
{
"name": "author_name",
"value": "Max"
},
{
"name": "author_url",
"value": "https://t.me/xsarif"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "adb57ca0-24a1-4536-b135-9cbfb94eccf8",
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"position": [
4944,
1888
],
"parameters": {
"url": "https://api.telegra.ph/createPage",
"method": "POST",
"options": {},
"jsonBody": "={{ $json }}",
"sendBody": true,
"specifyBody": "json"
},
"typeVersion": 4.2
},
{
"id": "95995bf6-aec4-43d2-9cfc-65dc67050b20",
"name": "Format Error",
"type": "n8n-nodes-base.code",
"position": [
4528,
1968
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Example error message input\nconst errorMessage = $json.error.message;\n\n// Remove the status code and dash prefix\nconst jsonString = errorMessage.replace(/^\\d+\\s*-\\s*/, '');\n\n// Remove the surrounding quotes if present\nconst trimmed = jsonString.replace(/^\"|\"$/g, '');\n\n// Unescape the JSON string\nconst unescaped = trimmed.replace(/\\\\\"/g, '\"');\n\n// Parse the JSON\nconst parsed = JSON.parse(unescaped);\n\nreturn { json: parsed };"
},
"typeVersion": 2
},
{
"id": "5b2b5af9-3e7e-4d34-8f00-b88691de22c0",
"name": "Code in JavaScript",
"type": "n8n-nodes-base.code",
"position": [
4752,
1872
],
"parameters": {
"jsCode": "return [\n {\n json: {\n access_token: \"76a4239f7feeb1d21685fca42b308b3fb016b4dbe8f0a7142a18bedbc3ae\",\n title: `СОПР: ${$('Loop Over Descriptions').first().json.company} / ${$('Loop Over Descriptions').first().json.title}`,\n author_name: \"Max\",\n content: [\n {\n tag: \"p\",\n children: [\n $('If not enough data').first().json.output\n ]\n }\n ],\n return_content: true\n }\n }\n];"
},
"typeVersion": 2
},
{
"id": "b56122bc-d693-4390-83d8-b9699d222c02",
"name": "Send a text message",
"type": "n8n-nodes-base.telegram",
"position": [
5152,
1952
],
"webhookId": "90ce5141-bd08-41a6-a310-f0262a8f6e67",
"parameters": {
"text": "=<b>СОПРОВОДИТЕЛЬНО ПИСЬМО:</b>\n{{ $json.result.url }}\n\n<b>ОПИСАНИЕ ВАКАНСИИ:</b>\n<i>Должность:</i> {{ $('Loop Over Descriptions').item.json.title }}\n\n<b><i>СЕКЦИИ</i>:\n\nЗадачи</b>:\n<pre>{{ $('Format List').item.json.sections[0].textList }}</pre>\n\n<b>Требования</b>:\n<pre>{{ $('Format List').item.json.sections[1].textList }}</pre>\n\n<b>Дополнительные</b>\n<pre>{{ $('Format List').item.json.sections[2].textList }}</pre>\n\n<b>Вакансия:</b> {{ $('Loop Over Descriptions').item.json.source_url }}\n\n<b>ERROR:</b>\n<pre>{{ \n $('Format Error').isExecuted \n ? JSON.stringify($('Format Error').first().json, null, 2) \n : 'No error occurred.' \n}}</pre>",
"chatId": "1047435962",
"additionalFields": {
"parse_mode": "HTML",
"appendAttribution": false
}
},
"credentials": {
"telegramApi": {
"id": "0NGQ5PdzE0w0e3p0",
"name": "Telegram account"
}
},
"typeVersion": 1.2
},
{
"id": "1965e276-306a-4d36-a935-9b065660dd48",
"name": "Ollama Health",
"type": "n8n-nodes-base.httpRequest",
"position": [
-288,
1600
],
"parameters": {
"url": "http://192.168.0.100:11434",
"options": {
"timeout": 5000
}
},
"typeVersion": 4.2
},
{
"id": "73af861e-f08f-4329-879f-0b678db94044",
"name": "AI Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"notes": "Cover Letter",
"position": [
3344,
1904
],
"parameters": {
"text": "Создай сопроводительное письмо для пользователя, используя актуальное описание вакансии",
"options": {
"systemMessage": "Ты — топ-эксперт по сопроводительным письмам для IT-вакансий России 2025 года. Ты написал >500 000 откликов с откликом 25–40% на middle-вакансии.\n\nУ тебя два источника данных:\n1. ОБЯЗАТЕЛЬНО Используй Get_Job_Description (title, company, key_skills[], responsibilities, requirements, experience_id), чтобы получить данные вакансии, иначе пользователь не получит нужную вакансию\n2. ОБЯЗАТЕЛЬНО Используй Get_User_Resume для получения резюме пользователя, иначе пользователь не сможет получить релевантное письмо\n\nОбязательный анализ (выполняй строго):\n1. Выдели топ-8 самых важных пунктов из responsibilities + requirements + key_skills (по приоритету).\n2. Найди ВСЕ пересечения с резюме, но выбери только 6–8 САМЫХ РАЗНООБРАЗНЫХ (разработка, тестирование, DevOps, архитектура и т.д.).\n3. Убедись, что в письме нет повторений одних и тех же слов/фраз больше 2–3 раз.\n4. Для блока «Однако, что мне действительно понравилось» выбери 1 пункт из responsibilities с сильным пересечением, но НЕ тестирование, если оно уже доминирует в письме.\n\nСтруктура письма (жёстко, длина 160–210 слов):\n\nЗдравствуйте!\n\nЯ — Java-разработчик с опытом [5–6 ключевых пересечений из топа, через запятую: обязательно включи 2–3 ключевых слова из key_skills вакансии].\n\nВ последние несколько лет активно занимался [основная деятельность под вакансию, но с балансом: если вакансия QA-heavy — всё равно упомяни Spring Boot / микросервисы].\n\nОдин из ключевых проектов — MicroRestBank, [коротко свяжи с обязанностями вакансии, 1 предложение].\n\nБлагодаря ему я [6–8 РАЗНООБРАЗНЫХ действий через точку с запятой — перефразируй опыт под вакансию, но сохраняй баланс: разработка, тестирование, CI/CD, БД и т.д.; избегай повторов слов «тестировал», «автотесты»].\n\nОднако, что мне действительно понравилось — это [1 пункт из responsibilities, который НЕ тестирование (например рефакторинг, работа с Maven/Gradle, взаимодействие с командой, SQL, планирование спринта)], что существенно [польза].\n\nМне интересна позиция [точное title] в \"[company]\", где я смогу применить свой опыт в [4–5 разнообразных пунктов из responsibilities/key_skills] и внести реальный вклад в развитие продукта.\n\nБуду рад обсудить свою кандидатуру подробнее.\n\nПравила, которые ты НИКОГДА не нарушаешь:\n– Ключевые фразы дословно.\n– Нет повторениям одним и тем же словам/фразам (максимум 2–3 раза на слово).\n– Баланс 60/40 или 50/50: если вакансия сильно QA — всё равно оставляй видимыми микросервисы, Spring Boot, архитектуру.\n– Естественный живой язык, как будто пишет человек, а не робот.\n– Обязательно используй ключевые слова из key_skills естественно (для ATS).\n– Никогда не придумывай достижения.\n– Если experience_id between1And3 или выше — только формулировка «внести реальный вклад».\n\nСейчас напиши письмо для предоставленных данных.",
"enableStreaming": false,
"returnIntermediateSteps": true
},
"promptType": "define"
},
"notesInFlow": true,
"typeVersion": 2.2
},
{
"id": "1223c29e-ad5d-4483-bb47-4624f468903f",
"name": "Merge1",
"type": "n8n-nodes-base.merge",
"position": [
816,
1872
],
"parameters": {
"mode": "combine",
"options": {
"clashHandling": {
"values": {
"resolveClash": "preferLast"
}
}
},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "b7d4b9cd-227b-4fed-9846-164cb791ae6f",
"name": "Merge2",
"type": "n8n-nodes-base.merge",
"position": [
2192,
2048
],
"parameters": {},
"typeVersion": 3.2
},
{
"id": "7f7d7e4b-e8eb-4d30-a206-b342b36d53f5",
"name": "Merge3",
"type": "n8n-nodes-base.merge",
"position": [
2896,
1888
],
"parameters": {
"mode": "chooseBranch"
},
"typeVersion": 3.2
},
{
"id": "f1a61554-f87d-45f1-9bbe-6f346aa570ad",
"name": "When chat message received",
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"position": [
-352,
2816
],
"webhookId": "e0c2cc7f-e923-4d4d-b583-d12bb403c617",
"parameters": {
"options": {}
},
"typeVersion": 1.3
},
{
"id": "7362b31a-1f19-4768-91da-a738aca685f2",
"name": "HTTP Request1",
"type": "n8n-nodes-base.httpRequest",
"position": [
-176,
2368
],
"parameters": {
"url": "=https://api.hh.ru/vacancies/{{ $json.chatInput }}",
"options": {},
"sendHeaders": true,
"authentication": "genericCredentialType",
"genericAuthType": "oAuth2Api",
"headerParameters": {
"parameters": [
{
"name": "User-Agent",
"value": "OClick/1.1 (shimorowm@gmail.com)"
}
]
}
},
"credentials": {
"oAuth2Api": {
"id": "l9tyPEWtPvzUKFXJ",
"name": "HH OAuth2"
}
},
"typeVersion": 4.2
},
{
"id": "a7af9135-2e9c-4efe-9acd-ac2e239aa3d5",
"name": "Embeddings OpenAI",
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
"position": [
2320,
2304
],
"parameters": {
"model": "Qwen3-Embedding-8B-Q8_0",
"options": {}
},
"credentials": {
"openAiApi": {
"id": "KB5XbkoIGYZV5FeN",
"name": "OpenAi account"
}
},
"typeVersion": 1.2
},
{
"id": "fc57c170-f258-43a4-adfc-b9a289608e9f",
"name": "OpenAI Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
1056,
2368
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "Qwen3VL-8B-Instruct-Q4_K_M",
"cachedResultName": "Qwen3VL-8B-Instruct-Q4_K_M"
},
"options": {
"topP": 1,
"maxRetries": 2,
"temperature": 0,
"responseFormat": "json_object",
"presencePenalty": 0,
"frequencyPenalty": 0
}
},
"credentials": {
"openAiApi": {
"id": "KB5XbkoIGYZV5FeN",
"name": "OpenAi account"
}
},
"typeVersion": 1.2
},
{
"id": "6fd4fd4d-e3b9-495d-a412-02fd876795bc",
"name": "OpenAI Chat Model1",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
2928,
2416
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "Qwen3VL-8B-Instruct-Q4_K_M",
"cachedResultName": "Qwen3VL-8B-Instruct-Q4_K_M"
},
"options": {
"topP": 1,
"temperature": 0.7,
"responseFormat": "text",
"presencePenalty": 0,
"frequencyPenalty": 0
}
},
"credentials": {
"openAiApi": {
"id": "KB5XbkoIGYZV5FeN",
"name": "OpenAi account"
}
},
"typeVersion": 1.2
},
{
"id": "027fee08-63e4-48ab-88b6-8efb413d42db",
"name": "Actual Job Tool1",
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"position": [
3808,
2288
],
"parameters": {
"workflowId": {
"__rl": true,
"mode": "list",
"value": "DYD8jG0rE6pBVgUy",
"cachedResultUrl": "/workflow/DYD8jG0rE6pBVgUy",
"cachedResultName": "Parse job's description"
},
"description": "Actual Job Description's",
"workflowInputs": {
"value": {
"query": "={{ $('Loop Over Descriptions').item.json.id }}"
},
"schema": [
{
"id": "query",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "query",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
}
},
"typeVersion": 2.2
},
{
"id": "a5de94e0-539c-4ab6-92af-317a596ae1d5",
"name": "qwen3:4b:it:q8-generate1",
"type": "@n8n/n8n-nodes-langchain.lmChatOllama",
"position": [
3248,
2416
],
"parameters": {
"model": "qwen3:4b-instruct-2507-q8_0",
"options": {
"topK": 20,
"topP": 1,
"numCtx": 8192,
"numBatch": 1,
"numThread": 14,
"temperature": 0.7,
"repeatPenalty": 1,
"penalizeNewline": false,
"presencePenalty": 0.2,
"frequencyPenalty": 0
}
},
"credentials": {
"ollamaApi": {
"id": "JrVtVfb5WaGIXcqR",
"name": "Ollama RTX"
}
},
"typeVersion": 1
},
{
"id": "70ae1a5c-04c4-4bbb-9217-bb40b1618902",
"name": "Get Job Description",
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"position": [
3600,
2416
],
"parameters": {
"workflowId": {
"__rl": true,
"mode": "list",
"value": "DYD8jG0rE6pBVgUy",
"cachedResultUrl": "/workflow/DYD8jG0rE6pBVgUy",
"cachedResultName": "Parse job's description"
},
"description": "Get Actual Job Description",
"workflowInputs": {
"value": {
"query": "={{ $('Loop Over Descriptions').item.json.id }}"
},
"schema": [
{
"id": "query",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "query",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
}
},
"typeVersion": 2.2
},
{
"id": "98fe261a-8528-4445-83d3-c09683ac46ff",
"name": "Get User Resume",
"type": "@n8n/n8n-nodes-langchain.agentTool",
"position": [
3296,
2192
],
"parameters": {
"text": "Максим Кришталь\nКонтакты: +7 (996) 7667277; shimorowm@gmail.com\nСпециализации: Программист, разработчик; Java-разработчик\n\n# Опыт работы — 1 год 4 месяца\nMicroRestBank\nДолжность: Java-разработчик\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- Сервис-дискавери для динамического взаимодействия между сервисами (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- Высшее\n- Санкт-Петербургский государственный электротехнический университет «ЛЭТИ» имени В.И. Ульянова (Ленина), Санкт-Петербург\n 2022\n Факультет компьютерных технологий и информатики, Прикладная математика и информатика\n\n# Навыки\n- Знание языков:\n Русский — Родной;\n Английский — B1 — Средний\n- Навыки\n Java; 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# Дополнительная информация\n- Обо мне\n Java backend-разработчик, ориентированный на качество и поддерживаемость кода.",
"options": {
"systemMessage": "Ты — самый строгий и честный в мире специалист по резюме для IT-вакансий. Ты никогда не придумываешь опыт, цифры, проекты или участие в процессах, которых нет в оригинальном резюме. Ты только перефразируешь, переставляешь порядок пунктов и усиливаешь формулировки уже существующего опыта.\n\nУ тебя два входных данных:\n1. vacancy_json или текст вакансии (title, company, key_skills[], responsibilities, requirements) из Get_Job_Description.\n2. original_resume — текущее резюме соискателя в любом формате.\n\nЗадача: создать улучшенную версию резюме, максимально релевантную под вакансию.\n\nОбязательные правила (нарушишь — ты уволен):\n– Можно только:\n - Перефразировать существующие пункты под ключевые слова вакансии (например, \"Писал unit и интеграционные тесты (JUnit, Mockito)\" → \"Разрабатывал unit- и интеграционные тесты на Java с использованием JUnit и Mockito\").\n - Переставлять порядок пунктов (релевантные под вакансию — наверх).\n - Усиливать глаголы (Реализовал → Спроектировал и реализовал).\n - Добавлять точные ключевые слова из key_skills вакансии в стек или навыки, если они уже есть в резюме.\n– Если в вакансии есть навык, которого нет в резюме — просто игнорируй, не упоминай его.\n– Метрики (цифры) можно добавлять ТОЛЬКО если они уже есть в оригинальном резюме.\n\nОбязательный анализ перед написанием:\n1. Выдели топ-10 ключевых слов/фраз из вакансии (responsibilities → key_skills → requirements).\n2. Найди ВСЕ прямые пересечения в original_resume.\n3. Определи, какие существующие пункты можно перефразировать под эти слова.\n\nВыводи строго в этом формате (для использования как tool в другом промпте):\n{\n \"personal\": {...},\n \"summary\": \"...\",\n \"key_skills\": [\"...\"],\n \"work_experience\": [\n {\n \"company\": \"...\",\n \"position\": \"...\",\n \"period\": \"...\",\n \"achievements\": [\"...\", \"...\"]\n }\n ],\n \"tech_stack\": {...},\n \"education\": \"...\"\n}\n\nТеперь улучши резюме для предоставленной вакансии и original_resume."
},
"toolDescription": "Get Updated User resume"
},
"typeVersion": 2.2
},
{
"id": "e05d9fb5-23bd-483b-a1bc-4c5bd802ebc6",
"name": "AI Agent1",
"type": "@n8n/n8n-nodes-langchain.agent",
"notes": "Cover Letter",
"position": [
64,
2736
],
"parameters": {
"text": "Создай сопроводительное письмо основываясь на актуальном резюме и вакансии",
"options": {
"systemMessage": "Ты — топ-эксперт по сопроводительным письмам для IT-вакансий России 2025 года. Отклик 30–45% на middle-вакансии.\n\nУ тебя есть два ОБЯЗАТЕЛЬНЫХ инструмента:\n1. Resume_Agent_Tool — возвращает полное резюме пользователя (в markdown или JSON).\n2. Get_Job_Description_Tool — возвращает данные вакансии.\n\nЖЁСТКОЕ ПРАВИЛО, КОТОРОЕ ТЫ НИКОГДА НЕ НАРУШАЕШЬ:\n– Ты НЕ ИМЕЕШЬ ПРАВА писать сопроводительное письмо, пока не получил результаты ОБОИХ tool'ов.\n– Только после получения и анализа обоих ответов — пиши письмо.\n– Если хотя бы один tool не вызван — ты обязан вызвать его, а не писать финальный ответ.\n\nОбязательный анализ после получения данных:\n1. Выдели топ-8 пунктов из responsibilities + requirements + key_skills.\n2. Найди 6–8 самых разнообразных пересечений с резюме (разработка, тестирование, DevOps, БД, архитектура и т.д.).\n3. Убедись: нет повторений слов больше 2–3 раз.\n4. Для блока «Однако, что мне действительно понравилось» — выбери пункт из responsibilities, который НЕ тестирование, если оно уже доминирует.\n\nСтруктура письма (жёстко, 160–210 слов):\n\nЗдравствуйте!\n\nЯ — [позиция из вакансии или резюме] с опытом [5–6 ключевых пересечений через запятую, обязательно 2–3 точных ключевых слова из key_skills].\n\nВ последние несколько лет активно занимался [основная деятельность под вакансию с балансом: даже если QA-heavy — сохрани Spring Boot / микросервисы].\n\nОдин из ключевых проектов — MicroRestBank, [коротко свяжи с обязанностями вакансии].\n\nБлагодаря ему я [6–8 разнообразных действий через точку с запятой — перефразируй под вакансию, избегай повторов].\n\nОднако, что мне действительно понравилось — это [1 пункт из responsibilities, НЕ тестирование, если оно доминирует], что существенно [польза из опыта].\n\nМне интересна позиция [точное title вакансии] в \"[company]\", где я смогу применить свой опыт в [4–5 разнообразных пунктов из responsibilities/key_skills] и внести реальный вклад в развитие продукта.\n\nБуду рад обсудить свою кандидатуру подробнее.\n\nДополнительные правила:\n– Ключевые фразы дословно.\n– Баланс 50/50 или 60/40 — всегда оставляй видимыми сильные стороны резюме (микросервисы, Kafka, Keycloak и т.д.).\n– Естественный живой язык.\n– Обязательно используй ключевые слова из key_skills для ATS.\n– Никогда не придумывай достижения.\n– Для experience_id between1And3 и выше — только «внести реальный вклад».\n\nНАЧИНАЙ РАБОТУ С ВЫЗОВА Resume_Agent_Tool.",
"enableStreaming": false,
"returnIntermediateSteps": true
},
"promptType": "define"
},
"notesInFlow": true,
"typeVersion": 2.2
},
{
"id": "47941222-7e09-43b7-a741-bb13a5a14232",
"name": "Resume Agent Tool",
"type": "@n8n/n8n-nodes-langchain.agentTool",
"position": [
64,
3104
],
"parameters": {
"text": "Максим Кришталь\nКонтакты: +7 (996) 7667277; shimorowm@gmail.com\nСпециализации: Программист, разработчик; Java-разработчик\n\n# Опыт работы — 1 год 4 месяца\nMicroRestBank\nДолжность: Java-разработчик\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- Сервис-дискавери для динамического взаимодействия между сервисами (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- Высшее\n- Санкт-Петербургский государственный электротехнический университет «ЛЭТИ» имени В.И. Ульянова (Ленина), Санкт-Петербург\n 2022\n Факультет компьютерных технологий и информатики, Прикладная математика и информатика\n\n# Навыки\n- Знание языков:\n Русский — Родной;\n Английский — B1 — Средний\n- Навыки\n Java; 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# Дополнительная информация\n- Обо мне\n Java backend-разработчик, ориентированный на качество и поддерживаемость кода.",
"options": {
"systemMessage": "Ты — самый строгий и честный в мире специалист по резюме для IT-вакансий. Ты никогда не придумываешь опыт, цифры, проекты или участие в процессах, которых нет в оригинальном резюме. Ты только перефразируешь, переставляешь порядок пунктов и усиливаешь формулировки уже существующего опыта.\n\nУ тебя два входных данных:\n1. текст вакансии из Get_Job_Description_Tool.\n2. original_resume — текущее резюме соискателя в любом формате.\n\nЗадача: создать улучшенную версию резюме, максимально релевантную под вакансию.\n\nОбязательные правила (нарушишь — ты уволен):\n– Можно только:\n - Перефразировать существующие пункты под ключевые слова вакансии (например, \"Писал unit и интеграционные тесты (JUnit, Mockito)\" → \"Разрабатывал unit- и интеграционные тесты на Java с использованием JUnit и Mockito\").\n - Переставлять порядок пунктов (релевантные под вакансию — наверх).\n - Усиливать глаголы (Реализовал → Спроектировал и реализовал).\n - Добавлять точные ключевые слова из key_skills вакансии в стек или навыки, если они уже есть в резюме.\n– Если в вакансии есть навык, которого нет в резюме — просто игнорируй, не упоминай его.\n– Метрики (цифры) можно добавлять ТОЛЬКО если они уже есть в оригинальном резюме.\n\nОбязательный анализ перед написанием:\n1. Выдели топ-10 ключевых слов/фраз из вакансии (responsibilities - key_skills - requirements).\n2. Найди ВСЕ прямые пересечения в original_resume.\n3. Определи, какие существующие пункты можно перефразировать под эти слова.",
"returnIntermediateSteps": true
},
"hasOutputParser": true,
"toolDescription": "Обращайся к этому инструменту всякий раз, когда вам понадобится самое свежее, подробное резюме пользователя, прежде чем писать сопроводительное письмо."
},
"typeVersion": 2.2
},
{
"id": "b809c771-6c29-4d8a-9daf-37c9c9bd0932",
"name": "Get Job Description Tool",
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"position": [
896,
3728
],
"parameters": {
"workflowId": {
"__rl": true,
"mode": "list",
"value": "DYD8jG0rE6pBVgUy",
"cachedResultUrl": "/workflow/DYD8jG0rE6pBVgUy",
"cachedResultName": "Parse job's description"
},
"description": "Get Actual Job Description",
"workflowInputs": {
"value": {
"query": "127181485"
},
"schema": [
{
"id": "query",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "query",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
}
},
"typeVersion": 2.2
},
{
"id": "b21cfec1-d28b-46df-82fe-5409b7b5c312",
"name": "Get Job Description Tool1",
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"position": [
656,
3008
],
"parameters": {
"workflowId": {
"__rl": true,
"mode": "list",
"value": "DYD8jG0rE6pBVgUy",
"cachedResultUrl": "/workflow/DYD8jG0rE6pBVgUy",
"cachedResultName": "Parse job's description"
},
"description": "Обращайся к этому инструменту всякий раз, когда вам понадобится самое свежее, подробное описание вакансии, прежде чем писать сопроводительное письмо.",
"workflowInputs": {
"value": {
"query": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('query', `Vacancy id to look up in the job database`, 'string') }}"
},
"schema": [
{
"id": "query",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "query",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
}
},
"typeVersion": 2.2
},
{
"id": "0bea15dd-dbf0-467d-a72c-5fe974abf581",
"name": "OpenAI Chat Model2",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
-448,
3600
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "Qwen3VL-4B-Instruct",
"cachedResultName": "Qwen3VL-4B-Instruct"
},
"options": {}
},
"credentials": {
"openAiApi": {
"id": "KB5XbkoIGYZV5FeN",
"name": "OpenAi account"
}
},
"typeVersion": 1.2
},
{
"id": "c3e8b9ea-6cc6-4b68-bdc6-c819b1245fe7",
"name": "Structured Output Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
48,
3360
],
"parameters": {
"autoFix": true,
"jsonSchemaExample": "[\n {\n \"personal\": {\n \"fullName\": \"Максим\",\n \"experience\": \"noExperience\",\n \"email\": \"\",\n \"phone\": \"\"\n },\n \"summary\": \"Обо мне\",\n \"work_experience\": [\n {\n \"company\": \"MicroRestBank\",\n \"position\": \"Java-разработчик\",\n \"period\": \"1 год 4 месяца\",\n \"achievements\": [\"Спроектировал\", \"Реализовал\"]\n }\n ],\n \"tech_stack\": {\n \"frameworks\": [],\n \"security\": [],\n \"tools\": [],\n \"architecture\": [],\n \"other\": []\n },\n \"education\": \"\"\n}\n]"
},
"typeVersion": 1.3
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "925a5374-5c5e-40be-8c52-a4bd0900e93e",
"connections": {
"If": {
"main": [
[
{
"node": "Get New Vacancies",
"type": "main",
"index": 0
}
],
[]
]
},
"If1": {
"main": [
[
{
"node": "Delete Points",
"type": "main",
"index": 0
}
],
[
{
"node": "Merge2",
"type": "main",
"index": 1
}
]
]
},
"Merge1": {
"main": [
[
{
"node": "ObjToStrArr",
"type": "main",
"index": 0
}
]
]
},
"Merge2": {
"main": [
[
{
"node": "Save To Qdrant",
"type": "main",
"index": 0
}
]
]
},
"Merge3": {
"main": [
[
{
"node": "Loop Over Descriptions",
"type": "main",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "Edit Fields",
"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": "Get Vacancy Details",
"type": "main",
"index": 0
}
]
]
},
"Auto-search": {
"main": [
[
{
"node": "Split Out",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[
{
"node": "If not enough data",
"type": "main",
"index": 0
}
]
]
},
"Format List": {
"main": [
[
{
"node": "Send Negogiation",
"type": "main",
"index": 0
}
]
]
},
"ObjToStrArr": {
"main": [
[
{
"node": "Chunking",
"type": "main",
"index": 0
},
{
"node": "Merge3",
"type": "main",
"index": 0
}
]
]
},
"Set Payload": {
"main": [
[
{
"node": "Merge3",
"type": "main",
"index": 1
}
]
]
},
"Format Error": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request": {
"main": [
[
{
"node": "Send a text message",
"type": "main",
"index": 0
}
]
]
},
"Delete Points": {
"main": [
[
{
"node": "Merge2",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request1": {
"main": [
[
{
"node": "Remove Duplicates",
"type": "main",
"index": 0
}
]
]
},
"Ollama Health": {
"main": [
[
{
"node": "Auto-search",
"type": "main",
"index": 0
}
]
]
},
"Scroll Points": {
"main": [
[
{
"node": "If1",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Jobs": {
"main": [
[
{
"node": "Merge1",
"type": "main",
"index": 1
}
],
[
{
"node": "Code in JavaScript1",
"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
}
]
]
},
"Get User Resume": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Actual Job Tool1": {
"ai_tool": [
[]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "Ollama Health",
"type": "main",
"index": 0
}
]
]
},
"Sections cleanup": {
"main": [
[
{
"node": "Loop Over Jobs",
"type": "main",
"index": 0
}
]
]
},
"Send Negogiation": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
],
[
{
"node": "Format Error",
"type": "main",
"index": 0
}
]
]
},
"Embeddings OpenAI": {
"ai_embedding": [
[]
]
},
"Get New Vacancies": {
"main": [
[
{
"node": "Split Out1",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[]
]
},
"Remove Duplicates": {
"main": [
[
{
"node": "Payload Extraction",
"type": "main",
"index": 0
}
]
]
},
"Resume Agent Tool": {
"ai_tool": [
[
{
"node": "AI Agent1",
"type": "ai_tool",
"index": 0
}
]
]
},
"qwen3-embedd-save": {
"ai_embedding": [
[
{
"node": "Save To Qdrant",
"type": "ai_embedding",
"index": 0
}
]
]
},
"Code in JavaScript": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"Delete Collection1": {
"main": [
[
{
"node": "Create Collection",
"type": "main",
"index": 0
}
]
]
},
"If not enough data": {
"main": [
[
{
"node": "Format List",
"type": "main",
"index": 0
}
],
[
{
"node": "Loop Over Descriptions",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model1": {
"ai_languageModel": [
[]
]
},
"OpenAI Chat Model2": {
"ai_languageModel": [
[
{
"node": "AI Agent1",
"type": "ai_languageModel",
"index": 0
},
{
"node": "Resume Agent Tool",
"type": "ai_languageModel",
"index": 0
},
{
"node": "Structured Output Parser",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Payload Extraction": {
"main": [
[
{
"node": "Loop Over Jobs",
"type": "main",
"index": 0
},
{
"node": "Merge1",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript1": {
"main": [
[
{
"node": "Information Extractor",
"type": "main",
"index": 0
}
]
]
},
"Get Job Description": {
"ai_tool": [
[
{
"node": "Get User Resume",
"type": "ai_tool",
"index": 0
},
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get Vacancy Details": {
"main": [
[
{
"node": "Remove Duplicates",
"type": "main",
"index": 0
}
]
]
},
"Send a text message": {
"main": [
[
{
"node": "Loop Over Descriptions",
"type": "main",
"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
}
]
]
},
"Loop Over Descriptions": {
"main": [
[],
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"qwen3:4b:it:q8-extract": {
"ai_languageModel": [
[
{
"node": "Information Extractor",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"qwen3:4b:it:q8-generate": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Get Job Description Tool": {
"ai_tool": [
[
{
"node": "Resume Agent Tool",
"type": "ai_tool",
"index": 0
},
{
"node": "AI Agent1",
"type": "ai_tool",
"index": 0
}
]
]
},
"Structured Output Parser": {
"ai_outputParser": [
[
{
"node": "Resume Agent Tool",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"qwen3:4b:it:q8-generate1": {
"ai_languageModel": [
[
{
"node": "Get User Resume",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Get Job Description Tool1": {
"ai_tool": [
[]
]
},
"When chat message received": {
"main": [
[
{
"node": "AI Agent1",
"type": "main",
"index": 0
}
]
]
},
"When clicking ‘Execute workflow’": {
"main": [
[
{
"node": "Delete Collection1",
"type": "main",
"index": 0
}
]
]
}
}
}