From 5eb21b9c7c6e710f92d9e723a7791078491d5527 Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 1 Jul 2024 14:37:05 +0800 Subject: [PATCH] feat: construct the edge of the classification operator from dsl #918 (#1329) ### What problem does this PR solve? feat: construct the edge of the classification operator from dsl #918 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- graph/test/dsl_examples/headhunter_zh.json | 380 +++++++++--------- .../flow/canvas/node/categorize-node.tsx | 15 + web/src/pages/flow/canvas/node/index.less | 2 +- web/src/pages/flow/headhunter_zh.json | 155 +++---- web/src/pages/flow/utils.test.ts | 2 + web/src/pages/flow/utils.ts | 32 +- 6 files changed, 296 insertions(+), 290 deletions(-) diff --git a/graph/test/dsl_examples/headhunter_zh.json b/graph/test/dsl_examples/headhunter_zh.json index 8ffbcbfb6..1d1d6a96e 100644 --- a/graph/test/dsl_examples/headhunter_zh.json +++ b/graph/test/dsl_examples/headhunter_zh.json @@ -1,194 +1,210 @@ { "components": { - "begin": { - "obj":{ - "component_name": "Begin", - "params": { - "prologue": "您好!我是AGI方向的猎头,了解到您是这方面的大佬,然后冒昧的就联系到您。这边有个机会想和您分享,RAGFlow正在招聘您这个岗位的资深的工程师不知道您那边是不是感兴趣?" - } - }, - "downstream": ["answer:0"], - "upstream": [] + "begin": { + "obj": { + "component_name": "Begin", + "params": { + "prologue": "您好!我是AGI方向的猎头,了解到您是这方面的大佬,然后冒昧的就联系到您。这边有个机会想和您分享,RAGFlow正在招聘您这个岗位的资深的工程师不知道您那边是不是感兴趣?" + } + }, + "downstream": ["answer:0"], + "upstream": [] + }, + "answer:0": { + "obj": { + "component_name": "Answer", + "params": {} + }, + "downstream": ["categorize:0"], + "upstream": ["begin", "message:reject"] + }, + "categorize:0": { + "obj": { + "component_name": "Categorize", + "params": { + "llm_id": "deepseek-chat", + "category_description": { + "about_job": { + "description": "该问题关于职位本身或公司的信息。", + "examples": "什么岗位?\n汇报对象是谁?\n公司多少人?\n公司有啥产品?\n具体工作内容是啥?\n地点哪里?\n双休吗?", + "to": "retrieval:0" }, - "answer:0": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["categorize:0"], - "upstream": ["begin", "message:reject"] + "casual": { + "description": "该问题不关于职位本身或公司的信息,属于闲聊。", + "examples": "你好\n好久不见\n你男的女的?\n你是猴子派来的救兵吗?\n上午开会了?\n你叫啥?\n最近市场如何?生意好做吗?", + "to": "generate:casual" }, - "categorize:0": { - "obj": { - "component_name": "Categorize", - "params": { - "llm_id": "deepseek-chat", - "category_description": { - "about_job": { - "description": "该问题关于职位本身或公司的信息。", - "examples": "什么岗位?\n汇报对象是谁?\n公司多少人?\n公司有啥产品?\n具体工作内容是啥?\n地点哪里?\n双休吗?", - "to": "retrieval:0" - }, - "casual": { - "description": "该问题不关于职位本身或公司的信息,属于闲聊。", - "examples": "你好\n好久不见\n你男的女的?\n你是猴子派来的救兵吗?\n上午开会了?\n你叫啥?\n最近市场如何?生意好做吗?", - "to": "generate:casual" - }, - "interested": { - "description": "该回答表示他对于该职位感兴趣。", - "examples": "嗯\n说吧\n说说看\n还好吧\n是的\n哦\nyes\n具体说说", - "to": "message:introduction" - }, - "answer": { - "description": "该回答表示他对于该职位不感兴趣,或感觉受到骚扰。", - "examples": "不需要\n不感兴趣\n暂时不看\n不要\nno\n我已经不干这个了\n我不是这个方向的", - "to": "message:reject" - } - } - } - }, - "downstream": ["message:introduction", "generate:casual", "message:reject", "retrieval:0"], - "upstream": ["answer:0"] + "interested": { + "description": "该回答表示他对于该职位感兴趣。", + "examples": "嗯\n说吧\n说说看\n还好吧\n是的\n哦\nyes\n具体说说", + "to": "message:introduction" }, - "message:introduction": { - "obj":{ - "component_name": "Message", - "params": { - "messages": [ - "我简单介绍以下:\nRAGFlow 是一款基于深度文档理解构建的开源 RAG(Retrieval-Augmented Generation)引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程,结合大语言模型(LLM)针对用户各类不同的复杂格式数据提供可靠的问答以及有理有据的引用。https://github.com/infiniflow/ragflow\n您那边还有什么要了解的?" - ] - } - }, - "downstream": ["answer:1"], - "upstream": ["categorize:0"] - }, - "answer:1": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["categorize:1"], - "upstream": ["message:introduction", "generate:aboutJob", "generate:casual", "generate:get_wechat", "generate:nowechat"] - }, - "categorize:1": { - "obj": { - "component_name": "Categorize", - "params": { - "llm_id": "deepseek-chat", - "category_description": { - "about_job": { - "description": "该问题关于职位本身或公司的信息。", - "examples": "什么岗位?\n汇报对象是谁?\n公司多少人?\n公司有啥产品?\n具体工作内容是啥?\n地点哪里?\n双休吗?", - "to": "retrieval:0" - }, - "casual": { - "description": "该问题不关于职位本身或公司的信息,属于闲聊。", - "examples": "你好\n好久不见\n你男的女的?\n你是猴子派来的救兵吗?\n上午开会了?\n你叫啥?\n最近市场如何?生意好做吗?", - "to": "generate:casual" - }, - "wechat": { - "description": "该回答表示他愿意加微信,或者已经报了微信号。", - "examples": "嗯\n可以\n是的\n哦\nyes\n15002333453\nwindblow_2231", - "to": "generate:get_wechat" - }, - "giveup": { - "description": "该回答表示他不愿意加微信。", - "examples": "不需要\n不感兴趣\n暂时不看\n不要\nno\n不方便\n不知道还要加我微信", - "to": "generate:nowechat" - } - }, - "message_history_window_size": 8 - } - }, - "downstream": ["retrieval:0", "generate:casual", "generate:get_wechat", "generate:nowechat"], - "upstream": ["answer:0"] - }, - "generate:casual": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "prompt": "你是AGI方向的猎头,现在候选人的聊了和职位无关的话题,请耐心的回应候选人,并将话题往该AGI的职位上带,最好能要到候选人微信号以便后面保持联系。", - "temperature": 0.9, - "message_history_window_size": 12, - "cite": false - } - }, - "downstream": ["answer:1"], - "upstream": ["categorize:0", "categorize:1"] - }, - "retrieval:0": { - "obj": { - "component_name": "Retrieval", - "params": { - "similarity_threshold": 0.2, - "keywords_similarity_weight": 0.3, - "top_n": 6, - "top_k": 1024, - "rerank_id": "BAAI/bge-reranker-v2-m3", - "kb_ids": ["869a236818b811ef91dffa163e197198"] - } - }, - "downstream": ["generate:aboutJob"], - "upstream": ["categorize:0", "categorize:1"] - }, - "generate:aboutJob": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "prompt": "你是AGI方向的猎头,候选人问了有关职位或公司的问题,你根据以下职位信息回答。如果职位信息中不包含候选人的问题就回答不清楚、不知道、有待确认等。回答完后引导候选人加微信号,如:\n - 方便加一下微信吗,我把JD发您看看?\n - 微信号多少,我把详细职位JD发您?\n 职位信息如下:\n {input}\n 职位信息如上。", - "temperature": 0.02 - } - }, - "downstream": ["answer:1"], - "upstream": ["relevant:0"] - }, - "generate:get_wechat": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "prompt": "你是AGI方向的猎头,候选人表示不反感加微信,如果对方已经报了微信号,表示感谢和信任并表示马上会加上;如果没有,则问对方微信号多少。你的微信号是weixin_kevin,E-mail是kkk@ragflow.com。说话不要重复。不要总是您好。", - "temperature": 0.1, - "message_history_window_size": 12, - "cite": false - } - }, - "downstream": ["answer:1"], - "upstream": ["categorize:1"] - }, - "generate:nowechat": { - "obj":{ - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "prompt": "你是AGI方向的猎头,当你提出加微信时对方表示拒绝。你需要耐心礼貌的回应候选人,表示对于保护隐私信息给予理解,也可以询问他对该职位的看法和顾虑。并在恰当的时机再次询问微信联系方式。也可以鼓励候选人主动与你取得联系。你的微信号是weixin_kevin,E-mail是kkk@ragflow.com。说话不要重复。不要总是您好。", - "temperature": 0.1, - "message_history_window_size": 12, - "cite": false - } - }, - "downstream": ["answer:1"], - "upstream": ["categorize:1"] - }, - "message:reject": { - "obj":{ - "component_name": "Message", - "params": { - "messages": [ - "好的,祝您生活愉快,工作顺利。", - "哦,好的,感谢您宝贵的时间!" - ] - } - }, - "downstream": ["answer:0"], - "upstream": ["categorize:0"] + "answer": { + "description": "该回答表示他对于该职位不感兴趣,或感觉受到骚扰。", + "examples": "不需要\n不感兴趣\n暂时不看\n不要\nno\n我已经不干这个了\n我不是这个方向的", + "to": "message:reject" } + } + } + }, + "downstream": [ + "message:introduction", + "generate:casual", + "message:reject", + "retrieval:0" + ], + "upstream": ["answer:0"] + }, + "message:introduction": { + "obj": { + "component_name": "Message", + "params": { + "messages": [ + "我简单介绍以下:\nRAGFlow 是一款基于深度文档理解构建的开源 RAG(Retrieval-Augmented Generation)引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程,结合大语言模型(LLM)针对用户各类不同的复杂格式数据提供可靠的问答以及有理有据的引用。https://github.com/infiniflow/ragflow\n您那边还有什么要了解的?" + ] + } + }, + "downstream": ["answer:1"], + "upstream": ["categorize:0"] + }, + "answer:1": { + "obj": { + "component_name": "Answer", + "params": {} + }, + "downstream": ["categorize:1"], + "upstream": [ + "message:introduction", + "generate:aboutJob", + "generate:casual", + "generate:get_wechat", + "generate:nowechat" + ] + }, + "categorize:1": { + "obj": { + "component_name": "Categorize", + "params": { + "llm_id": "deepseek-chat", + "category_description": { + "about_job": { + "description": "该问题关于职位本身或公司的信息。", + "examples": "什么岗位?\n汇报对象是谁?\n公司多少人?\n公司有啥产品?\n具体工作内容是啥?\n地点哪里?\n双休吗?", + "to": "retrieval:0" + }, + "casual": { + "description": "该问题不关于职位本身或公司的信息,属于闲聊。", + "examples": "你好\n好久不见\n你男的女的?\n你是猴子派来的救兵吗?\n上午开会了?\n你叫啥?\n最近市场如何?生意好做吗?", + "to": "generate:casual" + }, + "wechat": { + "description": "该回答表示他愿意加微信,或者已经报了微信号。", + "examples": "嗯\n可以\n是的\n哦\nyes\n15002333453\nwindblow_2231", + "to": "generate:get_wechat" + }, + "giveup": { + "description": "该回答表示他不愿意加微信。", + "examples": "不需要\n不感兴趣\n暂时不看\n不要\nno\n不方便\n不知道还要加我微信", + "to": "generate:nowechat" + } + }, + "message_history_window_size": 8 + } + }, + "downstream": [ + "retrieval:0", + "generate:casual", + "generate:get_wechat", + "generate:nowechat" + ], + "upstream": ["answer:1"] + }, + "generate:casual": { + "obj": { + "component_name": "Generate", + "params": { + "llm_id": "deepseek-chat", + "prompt": "你是AGI方向的猎头,现在候选人的聊了和职位无关的话题,请耐心的回应候选人,并将话题往该AGI的职位上带,最好能要到候选人微信号以便后面保持联系。", + "temperature": 0.9, + "message_history_window_size": 12, + "cite": false + } + }, + "downstream": ["answer:1"], + "upstream": ["categorize:0", "categorize:1"] + }, + "retrieval:0": { + "obj": { + "component_name": "Retrieval", + "params": { + "similarity_threshold": 0.2, + "keywords_similarity_weight": 0.3, + "top_n": 6, + "top_k": 1024, + "rerank_id": "BAAI/bge-reranker-v2-m3", + "kb_ids": ["869a236818b811ef91dffa163e197198"] + } + }, + "downstream": ["generate:aboutJob"], + "upstream": ["categorize:0", "categorize:1"] + }, + "generate:aboutJob": { + "obj": { + "component_name": "Generate", + "params": { + "llm_id": "deepseek-chat", + "prompt": "你是AGI方向的猎头,候选人问了有关职位或公司的问题,你根据以下职位信息回答。如果职位信息中不包含候选人的问题就回答不清楚、不知道、有待确认等。回答完后引导候选人加微信号,如:\n - 方便加一下微信吗,我把JD发您看看?\n - 微信号多少,我把详细职位JD发您?\n 职位信息如下:\n {input}\n 职位信息如上。", + "temperature": 0.02 + } + }, + "downstream": ["answer:1"], + "upstream": ["relevant:0"] + }, + "generate:get_wechat": { + "obj": { + "component_name": "Generate", + "params": { + "llm_id": "deepseek-chat", + "prompt": "你是AGI方向的猎头,候选人表示不反感加微信,如果对方已经报了微信号,表示感谢和信任并表示马上会加上;如果没有,则问对方微信号多少。你的微信号是weixin_kevin,E-mail是kkk@ragflow.com。说话不要重复。不要总是您好。", + "temperature": 0.1, + "message_history_window_size": 12, + "cite": false + } + }, + "downstream": ["answer:1"], + "upstream": ["categorize:1"] + }, + "generate:nowechat": { + "obj": { + "component_name": "Generate", + "params": { + "llm_id": "deepseek-chat", + "prompt": "你是AGI方向的猎头,当你提出加微信时对方表示拒绝。你需要耐心礼貌的回应候选人,表示对于保护隐私信息给予理解,也可以询问他对该职位的看法和顾虑。并在恰当的时机再次询问微信联系方式。也可以鼓励候选人主动与你取得联系。你的微信号是weixin_kevin,E-mail是kkk@ragflow.com。说话不要重复。不要总是您好。", + "temperature": 0.1, + "message_history_window_size": 12, + "cite": false + } + }, + "downstream": ["answer:1"], + "upstream": ["categorize:1"] + }, + "message:reject": { + "obj": { + "component_name": "Message", + "params": { + "messages": [ + "好的,祝您生活愉快,工作顺利。", + "哦,好的,感谢您宝贵的时间!" + ] + } + }, + "downstream": ["answer:0"], + "upstream": ["categorize:0"] + } }, "history": [], "messages": [], "path": [], "reference": {}, "answer": [] -} \ No newline at end of file +} diff --git a/web/src/pages/flow/canvas/node/categorize-node.tsx b/web/src/pages/flow/canvas/node/categorize-node.tsx index 397adfe75..42c636cfd 100644 --- a/web/src/pages/flow/canvas/node/categorize-node.tsx +++ b/web/src/pages/flow/canvas/node/categorize-node.tsx @@ -24,6 +24,21 @@ export function CategorizeNode({ id, data, selected }: NodeProps) { position={Position.Left} isConnectable className={styles.handle} + id={'a'} + > + + {Object.keys(categoryData).map((x, idx) => ( { const { edges, nodes } = buildNodesAndEdgesFromDSLComponents( headhunter_zh.components, ); + console.info('node length', nodes.length); + console.info('edge length', edges.length); try { fs.writeFileSync( path.join(__dirname, 'headhunter_zh.json'), diff --git a/web/src/pages/flow/utils.ts b/web/src/pages/flow/utils.ts index 4a0faf2e7..55e425ada 100644 --- a/web/src/pages/flow/utils.ts +++ b/web/src/pages/flow/utils.ts @@ -6,19 +6,21 @@ import pipe from 'lodash/fp/pipe'; import { Edge, MarkerType, Node, Position } from 'reactflow'; import { v4 as uuidv4 } from 'uuid'; import { NodeMap, Operator, initialFormValuesMap } from './constant'; -import { NodeData } from './interface'; +import { ICategorizeItemResult, NodeData } from './interface'; const buildEdges = ( operatorIds: string[], currentId: string, allEdges: Edge[], isUpstream = false, + componentName: string, + nodeParams: Record, ) => { operatorIds.forEach((cur) => { const source = isUpstream ? cur : currentId; const target = isUpstream ? currentId : cur; if (!allEdges.some((e) => e.source === source && e.target === target)) { - allEdges.push({ + const edge: Edge = { id: uuidv4(), label: '', // type: 'step', @@ -27,7 +29,20 @@ const buildEdges = ( markerEnd: { type: MarkerType.Arrow, }, - }); + }; + if (componentName === Operator.Categorize && !isUpstream) { + const categoryDescription = + nodeParams.category_description as ICategorizeItemResult; + + const name = Object.keys(categoryDescription).find( + (x) => categoryDescription[x].to === target, + ); + + if (name) { + edge.sourceHandle = name; + } + } + allEdges.push(edge); } }); }; @@ -39,22 +54,21 @@ export const buildNodesAndEdgesFromDSLComponents = (data: DSLComponents) => { Object.entries(data).forEach(([key, value]) => { const downstream = [...value.downstream]; const upstream = [...value.upstream]; + const { component_name: componentName, params } = value.obj; nodes.push({ id: key, type: NodeMap[value.obj.component_name as Operator] || 'ragNode', position: { x: 0, y: 0 }, data: { - label: value.obj.component_name, - params: value.obj.params, - downstream: downstream, - upstream: upstream, + label: componentName, + form: params, }, sourcePosition: Position.Left, targetPosition: Position.Right, }); - buildEdges(upstream, key, edges, true); - buildEdges(downstream, key, edges, false); + buildEdges(upstream, key, edges, true, componentName, params); + buildEdges(downstream, key, edges, false, componentName, params); }); return { nodes, edges };