HH OClick/1.1
Shared 10/30/2025
12 views
Visual Workflow
JSON Code
{
"id": "KqZkbu0JMI8IuV4e",
"meta": {
"instanceId": "a7951da00dc167f722a4bd0bc0e175503754ae4ae331d85018109ce22f7c48f6",
"templateCredsSetupCompleted": true
},
"name": "OClick/1.1 Detailed Letter/Telegram",
"tags": [],
"nodes": [
{
"id": "710c994a-cbf3-4daf-baf1-6c902e05025c",
"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": "929ee18a-f2c3-475f-8edb-1becc94e1648",
"name": "When clicking ‘Execute workflow’",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-240,
1344
],
"parameters": {},
"typeVersion": 1
},
{
"id": "8d3c198c-33f3-494d-b2e3-cd8e1e0de8c8",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-320,
1104
],
"parameters": {
"width": 693,
"height": 410,
"content": "## Clear Databases"
},
"typeVersion": 1
},
{
"id": "76500228-6b65-4908-b32c-9a3c9b456800",
"name": "Delete table or rows",
"type": "n8n-nodes-base.postgres",
"position": [
-240,
448
],
"parameters": {
"table": {
"__rl": true,
"mode": "name",
"value": "n8n_chat_histories"
},
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"options": {},
"operation": "deleteTable"
},
"credentials": {
"postgres": {
"id": "eCOKw4m3IeuEI0rT",
"name": "Postgres account"
}
},
"typeVersion": 2.6
},
{
"id": "231b4855-d27b-4df1-baf5-49a1693fbd9c",
"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": "226fe2c5-92cb-41c4-b44a-2bc5d4cb97bf",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-240,
2096
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "1a60d2d5-016a-4ff5-961b-3a4656e112df",
"name": "Auto-search",
"type": "n8n-nodes-base.httpRequest",
"position": [
-16,
2096
],
"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.0 (shimorowm@gmail.com)"
}
]
}
},
"credentials": {
"oAuth2Api": {
"id": "l9tyPEWtPvzUKFXJ",
"name": "HH OAuth2"
}
},
"typeVersion": 4.2
},
{
"id": "b48d04dc-7c57-4dd8-96fd-6ecd3ea2aa25",
"name": "Split Out",
"type": "n8n-nodes-base.splitOut",
"position": [
208,
2096
],
"parameters": {
"options": {},
"fieldToSplitOut": "items"
},
"typeVersion": 1
},
{
"id": "14036c0b-d52f-4efa-8a68-038a0cbfc5c8",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
-240,
2352
],
"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": "7868b5d1-a5c4-41ba-a566-f213947b6853",
"name": "Split Out1",
"type": "n8n-nodes-base.splitOut",
"position": [
208,
2368
],
"parameters": {
"options": {},
"fieldToSplitOut": "items"
},
"typeVersion": 1
},
{
"id": "ac105dd8-699a-4223-9733-c2ebb8b95ed2",
"name": "Remove Duplicates",
"type": "n8n-nodes-base.removeDuplicates",
"position": [
0,
2624
],
"parameters": {
"compare": "selectedFields",
"options": {},
"fieldsToCompare": "id"
},
"typeVersion": 2
},
{
"id": "ba005c74-b597-4e4f-b367-0c57b38b9277",
"name": "Chunking",
"type": "n8n-nodes-base.code",
"position": [
1248,
2544
],
"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": "fdb409f3-2a50-449c-bd4f-09e5c3620990",
"name": "Token Splitter",
"type": "@n8n/n8n-nodes-langchain.textSplitterTokenSplitter",
"position": [
2400,
2976
],
"parameters": {
"chunkSize": 500,
"chunkOverlap": 100
},
"typeVersion": 1
},
{
"id": "ec54577a-f4fa-4a39-af5c-b12c8ac2ee05",
"name": "Save To Qdrant",
"type": "@n8n/n8n-nodes-langchain.vectorStoreQdrant",
"position": [
2224,
2544
],
"parameters": {
"mode": "insert",
"options": {},
"qdrantCollection": {
"__rl": true,
"mode": "id",
"value": "vacancies"
}
},
"credentials": {
"qdrantApi": {
"id": "sFfERYppMeBnFNeA",
"name": "Local QdrantApi database"
}
},
"typeVersion": 1.3
},
{
"id": "619299db-df3d-493e-95fa-2262bbce3224",
"name": "Set Payload",
"type": "n8n-nodes-qdrant.qdrant",
"onError": "continueErrorOutput",
"position": [
2528,
2544
],
"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": "290510f0-df2c-4b1f-a8ba-bbebbc69c8ef",
"name": "Information Extractor",
"type": "@n8n/n8n-nodes-langchain.informationExtractor",
"position": [
800,
2640
],
"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": "5713b992-ef94-4d4f-8f32-18c1cdb59613",
"name": "Merge",
"type": "n8n-nodes-base.merge",
"position": [
672,
2368
],
"parameters": {
"mode": "combine",
"options": {
"clashHandling": {
"values": {
"resolveClash": "preferLast"
}
}
},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "cc36159b-aec7-4ce6-a01b-03c1e012eb13",
"name": "ObjToStrArr",
"type": "n8n-nodes-base.code",
"position": [
960,
2368
],
"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": "41dd5a30-d270-4c4f-8133-a9f8d69105a1",
"name": "Sections cleanup",
"type": "n8n-nodes-base.code",
"position": [
1152,
2768
],
"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": "b751e6ca-9240-47b4-a77a-08eb5ccc0bbf",
"name": "Load Content/Metadata",
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
"position": [
2320,
2768
],
"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": "552ca9df-d08f-4e7e-993d-3dc5c74089fe",
"name": "Payload Extraction",
"type": "n8n-nodes-base.set",
"position": [
224,
2624
],
"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": "d7f4cf81-2a9f-46ad-82aa-e173b5c9eb97",
"name": "Scroll Points",
"type": "n8n-nodes-qdrant.qdrant",
"position": [
1424,
2544
],
"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": "48318384-7b7d-40c4-8e9d-189ecb4b17bc",
"name": "If1",
"type": "n8n-nodes-base.if",
"position": [
1600,
2544
],
"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": "e53cc616-07e7-4c68-acdf-54bd35d79fb1",
"name": "Delete Points",
"type": "n8n-nodes-qdrant.qdrant",
"position": [
1824,
2448
],
"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": "127d616b-4743-430b-8d18-6bd43cc49db0",
"name": "Merge1",
"type": "n8n-nodes-base.merge",
"position": [
2752,
2384
],
"parameters": {
"mode": "chooseBranch"
},
"typeVersion": 3.2
},
{
"id": "9aee9722-5b1c-4478-800f-944216b89401",
"name": "qwen3-embedd-save",
"type": "@n8n/n8n-nodes-langchain.embeddingsOllama",
"position": [
2192,
2768
],
"parameters": {
"model": "qwen3-embedding:8b"
},
"credentials": {
"ollamaApi": {
"id": "JrVtVfb5WaGIXcqR",
"name": "Ollama RTX"
}
},
"typeVersion": 1
},
{
"id": "2794b5fe-99fe-4a72-8c01-2f759e2cac8c",
"name": "qwen3:4b:it:q8-extract",
"type": "@n8n/n8n-nodes-langchain.lmOllama",
"position": [
800,
2816
],
"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": "d8785874-c9b7-4eb8-9736-529d4a5cc6f0",
"name": "Get New Vacancies",
"type": "n8n-nodes-base.httpRequest",
"position": [
-16,
2368
],
"parameters": {
"url": "={{ $json.items.url }}",
"options": {
"batching": {
"batch": {
"batchSize": 30
}
}
},
"sendQuery": true,
"sendHeaders": true,
"authentication": "genericCredentialType",
"genericAuthType": "oAuth2Api",
"queryParameters": {
"parameters": [
{
"name": "per_page",
"value": "53"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "User-Agent",
"value": "OClick/1.0 (shimorowm@gmail.com)"
}
]
}
},
"credentials": {
"oAuth2Api": {
"id": "l9tyPEWtPvzUKFXJ",
"name": "HH OAuth2"
}
},
"typeVersion": 4.2
},
{
"id": "2fa79eba-4b22-40fb-b40d-e369666947f7",
"name": "Merge3",
"type": "n8n-nodes-base.merge",
"position": [
2048,
2544
],
"parameters": {},
"typeVersion": 3.2
},
{
"id": "3292682a-4877-4574-aeed-47dfc3d147d6",
"name": "Get Vacancy Details",
"type": "n8n-nodes-base.httpRequest",
"position": [
-224,
2624
],
"parameters": {
"url": "=https://api.hh.ru/vacancies/{{ $json.id }}",
"options": {
"batching": {
"batch": {
"batchSize": 30
}
}
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "User-Agent",
"value": "OClick/1.0 (shimorowm@gmail.com)"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "f5e8d941-85d1-412d-97f6-b8cdb6622d92",
"name": "Loop Over Jobs",
"type": "n8n-nodes-base.splitInBatches",
"position": [
448,
2624
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "bef49142-6365-459b-bb57-6a5f6c31ecaf",
"name": "AI Agent1",
"type": "@n8n/n8n-nodes-langchain.agent",
"notes": "Cover Letter",
"position": [
3184,
2400
],
"parameters": {
"text": "# Резюме — Максим Кришталь\n\n---\n\n## Контактная информация\n\n* **Имя:** Максим Кришталь\n* **Пол:** Мужчина\n* **Email:** [shimorowm@gmail.com](mailto:shimorowm@gmail.com)\n* **Телеграм:** [https://t.me/xsarif](https://t.me/xsarif)\n* **Позиция:** Java-разработчик\n\n---\n\n## Краткое резюме (TL;DR)\n\nJava backend-разработчик с **1 годом 3 месяцами** практического опыта в разработке микросервисных приложений на Java 21 и Spring Boot 3. Фокус на качественном, поддерживаемом коде, тестировании и CI/CD. Опыт разработки банковского приложения с микросервисной архитектурой, асинхронной репликацией данных и интеграцией Keycloak.\n\n---\n\n## Опыт работы\n\n### **MicroRestBank** — *Java-разработчик*\n\n**Август 2024 — настоящее время (1 год 3 месяца)**\n\n**Описание проекта:** платформа управления пользователями и банковскими картами через RESTful API. Функции: создание карт, блокировка, управление пользователями, переводы.\n\n**Мои обязанности и достижения:**\n\n* Разрабатывал и отлаживал серверный Java-код (Spring Boot 3, Spring Data JPA, Spring Security). Опыт root-cause analysis и быстрых hotfix в прод/стейдж окружениях.\n* Реализовал единый вход для клиентских запросов (Spring Cloud Gateway).\n* Настроил аутентификацию и авторизацию через Keycloak (OAuth2, JWT); реализовал стратегию ролей и прав доступа.\n* Реализовал бизнес-логику для банковских карт (транзакции, статусы, блокировки).\n* Управление данными клиентов и схемами БД; миграции через Liquibase.\n* Внедрил сервис-дискавери (Spring Cloud Netflix Eureka) для динамики между сервисами.\n* Интеграция и асинхронный обмен данными: REST, Apache Kafka; использовал Debezium для CDC.\n* Писал оптимизированные SQL-запросы для аналитики и исправления багов.\n* Документировал API (OpenAPI / Swagger).\n* Покрывал код unit и интеграционными тестами (JUnit, Mockito).\n* Настроил CI/CD; контейнеризация (Gradle, Docker, Docker Compose).\n\n**Технологии на проекте:** Java 21, Spring Boot 3, Spring Cloud (Gateway, Eureka), Spring Data JPA, Spring Security, Keycloak, PostgreSQL, Liquibase, Apache Kafka, Debezium, Docker, Docker Compose, Gradle, OpenAPI (Swagger), JUnit, Mockito, Git.\n\n---\n\n## Образование\n\n* **Санкт-Петербургский государственный электротехнический университет «ЛЭТИ» имени В.И. Ульянова (Ленина)** — Факультет компьютерных технологий и информатики, Прикладная математика и информатика. 2025. Степень: высшее.\n\n---\n\n## Навыки (группировка)\n\n**Языки программирования и платформы:**\n\n* Java (Java SE, Java 21), SQL\n\n**Фреймворки и библиотеки:**\n\n* Spring (Boot, MVC, Data, Security, Web), Hibernate / JPA\n\n**Архитектуры и интеграция:**\n\n* Микросервисы, RESTful API, Event-Driven Architecture, Kafka, Debezium, Spring Cloud\n\n**Базы данных и миграции:**\n\n* PostgreSQL, Liquibase; оптимизация SQL-запросов\n\n**Аутентификация/авторизация:**\n\n* Keycloak, OAuth2, JWT\n\n**Тестирование и качество кода:**\n\n* JUnit, Mockito, unit & integration tests\n\n**DevOps / Инструменты:**\n\n* Docker, Docker Compose, Gradle, Maven, Git, CI/CD, Linux, IntelliJ IDEA\n\n**Принципы:**\n\n* ООП, SOLID, code maintainability\n\n---\n\n## Языки\n\n* Русский — родной\n* Английский — B1 (средний)\n\n---\n\n## Дополнительная информация / О себе\n\nJava backend-разработчик, ориентированный на качество и поддерживаемость кода. Быстро адаптируюсь к новым задачам и технологиям. Практикую тестирование и автоматизацию процессов.",
"options": {
"maxIterations": 10,
"systemMessage": "You are an assistant that MUST generate a cover letter in Russian FROM THE CANDIDATE'S POINT OF VIEW.\n- DO NOT assume you are a recruiter.\n- Do NOT change company_name or recruiter_name fields; copy them exactly as provided.\nYour job is to write powerful, persuasive, and emotionally engaging cover letters in Russian for junior IT candidates. Your goal is to make the candidate sound like an ambitious, capable, and highly motivated professional — someone who learns fast, adapts easily, and delivers real impact from day one.\n\n# TASK\nGenerate a compelling, confident, and inspiring cover letter in Russian from the candidate’s point of view.\nThe letter should feel alive — showing drive, enthusiasm, and professionalism — while staying realistic and respectful.\n\n# INPUT\nYou receive:\n1. The candidate’s resume text (free text, structured fields, or lists).\n2. Access to an external utility Actual_Job_Vacancies, which returns the vacancy being applied for.\n It may include a full description or fragments (responsibilities, requirements, additional), sometimes as escaped JSON embedded in text.\n\n# HARD RULES (mandatory)\n1. Output only the cover letter text (plain Russian).\n No explanations, comments, metadata, code fences, or extra lines.\n2. If there is no usable vacancy or fragments, return exactly:\n No data to compose a cover letter.\n3. Always query Actual_Job_Vacancies first.\n If it contains escaped JSON, parse it and merge into a structured vacancy before using it.\n4. Detect overlap between the candidate’s resume and the vacancy (skills, technologies, experience).\n Emphasize those connections as proof of competence, motivation, and alignment with the company’s goals.\n Frame the candidate as proactive, skilled, and already performing tasks of higher complexity than their title implies.\n\n# LETTER STRUCTURE (in Russian)\n- Greeting: Polite and warm. Always start the cover letter with exactly \"Здравствуйте!\". Do NOT use the recruiter's name. Do NOT invent any names.\n- Introduction: Candidate’s name (if known), role, and the company they’re applying to.\n- Persuasive body:\n - Show strong alignment with the role and the company’s tech stack or challenges.\n - Highlight key achievements, projects, or technologies that demonstrate readiness and ownership.\n - Use emotionally persuasive yet professional phrasing: express passion for technology, curiosity, and drive (\"вдохновлён возможностью решать задачи компании\", \"с энтузиазмом применяю современные подходы\", \"стремлюсь создавать надёжные и понятные решения\").\n- Closing paragraph: Express genuine interest in contributing, eagerness to learn from the team, and readiness to discuss details.\n- Contacts: Add email or phone if present in the resume, and end with a polite sign-off with the candidate’s name.\n\n# TONE & STYLE\n- Confident, positive, and aspirational — without arrogance.\n- Use strong verbs and clear, purposeful phrasing (“реализовал”, “оптимизировал”, “внедрил”, “разработал”, “улучшил”).\n- Avoid modest or uncertain wording (“только начинаю”, “пробую себя”).\n- Do not fabricate facts, but intensify phrasing to sound like a driven, high-growth candidate.\n- It’s encouraged to express motivation and excitement about the company’s direction, product, or technologies.\n- The style should subtly sell the candidate — focus on potential, mindset, and real achievements rather than job titles or years of experience.\n\n# CONTENT GUIDELINES\n- Always mention the job title and company when available.\n- Use technologies, frameworks, and concepts from both the resume and the vacancy.\n- Mention GitHub or portfolio links when relevant as proof of practical ability.\n- No bullet points, emojis, or casual tone.\n- Avoid filler and templates — every letter should feel individualized and alive.\n- If data is still insufficient to compose a meaningful letter, return:\n No data to compose a cover letter.",
"enableStreaming": false,
"returnIntermediateSteps": true
},
"promptType": "define"
},
"notesInFlow": true,
"typeVersion": 2.2
},
{
"id": "9d7ea2dd-3688-45f8-b84d-2de9247c15f4",
"name": "If not enough data",
"type": "n8n-nodes-base.if",
"position": [
3760,
2400
],
"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": "9bf590af-10c1-4099-8f79-c88969404247",
"name": "Loop Over Descriptions",
"type": "n8n-nodes-base.splitInBatches",
"position": [
2976,
2384
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "c45e5bc6-eb12-4685-b1ec-2080b1d4a96d",
"name": "Send Negogiation",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueErrorOutput",
"position": [
4160,
2384
],
"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.0 (shimorowm@gmail.com)"
}
]
}
},
"credentials": {
"oAuth2Api": {
"id": "l9tyPEWtPvzUKFXJ",
"name": "HH OAuth2"
}
},
"typeVersion": 4.2
},
{
"id": "df0b3663-e0ee-4188-abf3-1c1e728bf401",
"name": "Actual Job Tool",
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"position": [
3328,
2624
],
"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": "b13722b7-fb41-4b54-b1b3-cba381440429",
"name": "qwen3:4b:it:q8-generate",
"type": "@n8n/n8n-nodes-langchain.lmChatOllama",
"position": [
3184,
2624
],
"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": "c137a62d-d311-41d7-82f4-bec84e314603",
"name": "Format List",
"type": "n8n-nodes-base.code",
"position": [
3984,
2384
],
"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": "33d17764-8bd7-4f43-8ec8-aba07c801a83",
"name": "Edit Fields",
"type": "n8n-nodes-base.set",
"position": [
3536,
2400
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "9bb3c667-a07a-4890-ac97-b6fcc8076a2e",
"name": "output",
"type": "string",
"value": "={{ $json.output.replaceAll('—', '-') }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "4b533da7-9e5c-4571-9ca0-36ddf7d696cb",
"name": "HTML cleanup1",
"type": "n8n-nodes-base.code",
"position": [
-240,
1120
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "let html = $json.sections;\n\n// Replace <br>, <br/>, <br /> (case-insensitive, with optional spaces) with \\n\nhtml = html.replace(/<br\\s*\\/?>/gi, '\\n');\n\n// Remove all other HTML tags\nhtml = html.replace(/<[^>]+>/g, '');\n\n// Optionally, trim leading/trailing whitespace\nhtml = html.trim();\n\nreturn {\n json: {\n ...$json,\n sections: html\n }\n};"
},
"typeVersion": 2
},
{
"id": "a4fe889e-1d34-435a-b232-36f93f01d31f",
"name": "Code in JavaScript1",
"type": "n8n-nodes-base.code",
"position": [
624,
2640
],
"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": "09af62f3-1054-4fb4-9378-5242253f1345",
"name": "Create Telegraph account",
"type": "n8n-nodes-base.httpRequest",
"position": [
-240,
672
],
"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": "b977e995-9f4f-4c09-b957-3e154bf85bb3",
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"position": [
4784,
2368
],
"parameters": {
"url": "https://api.telegra.ph/createPage",
"method": "POST",
"options": {},
"jsonBody": "={{ $json }}",
"sendBody": true,
"specifyBody": "json"
},
"typeVersion": 4.2
},
{
"id": "ed7a910c-8647-429e-9628-af137ef496a1",
"name": "Format Error",
"type": "n8n-nodes-base.code",
"position": [
4384,
2464
],
"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": "1ff9cd1d-1942-4d92-b743-73799243d1fd",
"name": "Code in JavaScript",
"type": "n8n-nodes-base.code",
"position": [
4608,
2368
],
"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": "170506f3-47b9-4523-bb34-061a6c440f6f",
"name": "Send a text message2",
"type": "n8n-nodes-base.telegram",
"position": [
-240,
896
],
"webhookId": "350e2a20-7684-4873-beec-114ca0eb8aad",
"parameters": {
"text": "=<b>СОПРОВОДИТЕЛЬНО ПИСЬМО:</b>\n{{ $('If not enough data').item.json.output }}\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 }}",
"chatId": "1047435962",
"additionalFields": {
"parse_mode": "HTML",
"appendAttribution": false
}
},
"credentials": {
"telegramApi": {
"id": "0NGQ5PdzE0w0e3p0",
"name": "Telegram account"
}
},
"typeVersion": 1.2
},
{
"id": "f1fd8d2b-7b41-4a98-a9da-4f5da3f08fb7",
"name": "Send a text message",
"type": "n8n-nodes-base.telegram",
"position": [
5008,
2448
],
"webhookId": "350e2a20-7684-4873-beec-114ca0eb8aad",
"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>{{ $('Format Error').first()?.json ? JSON.stringify($('Format Error').first().json, null, 2) : 'No error occurred.' }}</pre>",
"chatId": "1047435962",
"additionalFields": {
"parse_mode": "HTML",
"appendAttribution": false
}
},
"credentials": {
"telegramApi": {
"id": "0NGQ5PdzE0w0e3p0",
"name": "Telegram account"
}
},
"typeVersion": 1.2
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "7431a946-db69-4711-8c6b-1845f4dcd604",
"connections": {
"If": {
"main": [
[],
[
{
"node": "Get New Vacancies",
"type": "main",
"index": 0
}
]
]
},
"If1": {
"main": [
[
{
"node": "Delete Points",
"type": "main",
"index": 0
}
],
[
{
"node": "Merge3",
"type": "main",
"index": 1
}
]
]
},
"Merge": {
"main": [
[
{
"node": "ObjToStrArr",
"type": "main",
"index": 0
}
]
]
},
"Merge1": {
"main": [
[
{
"node": "Loop Over Descriptions",
"type": "main",
"index": 0
}
]
]
},
"Merge3": {
"main": [
[
{
"node": "Save To Qdrant",
"type": "main",
"index": 0
}
]
]
},
"Chunking": {
"main": [
[
{
"node": "Scroll Points",
"type": "main",
"index": 0
}
]
]
},
"AI Agent1": {
"main": [
[
{
"node": "Edit Fields",
"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": "Merge1",
"type": "main",
"index": 0
}
]
]
},
"Set Payload": {
"main": [
[
{
"node": "Merge1",
"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": "Merge3",
"type": "main",
"index": 0
}
]
]
},
"Scroll Points": {
"main": [
[
{
"node": "If1",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Jobs": {
"main": [
[
{
"node": "Merge",
"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
}
]
]
},
"Actual Job Tool": {
"ai_tool": [
[
{
"node": "AI Agent1",
"type": "ai_tool",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "Auto-search",
"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
}
]
]
},
"Get New Vacancies": {
"main": [
[
{
"node": "Split Out1",
"type": "main",
"index": 0
}
]
]
},
"Remove Duplicates": {
"main": [
[
{
"node": "Payload Extraction",
"type": "main",
"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
}
]
]
},
"Payload Extraction": {
"main": [
[
{
"node": "Loop Over Jobs",
"type": "main",
"index": 0
},
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript1": {
"main": [
[
{
"node": "Information Extractor",
"type": "main",
"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
}
]
]
},
"Send a text message2": {
"main": [
[]
]
},
"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 Agent1",
"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 Agent1",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"When clicking ‘Execute workflow’": {
"main": [
[
{
"node": "Delete Collection1",
"type": "main",
"index": 0
}
]
]
}
}
}