convert_v8_to_graph.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /**
  2. * 将 v6.1.2.8 的 run_context.json 转换成图结构
  3. */
  4. function convertV8ToGraph(runContext) {
  5. const nodes = {};
  6. const edges = [];
  7. const iterations = {};
  8. const o = runContext.o || '原始问题';
  9. const rounds = runContext.rounds || [];
  10. // 添加原始问题根节点
  11. const rootId = 'root_o';
  12. nodes[rootId] = {
  13. type: 'root',
  14. query: o,
  15. level: 0,
  16. relevance_score: 1.0,
  17. strategy: '原始问题',
  18. iteration: 0,
  19. is_terminated: false,
  20. no_suggestion_rounds: 0,
  21. evaluation_reason: '用户输入的原始问题',
  22. is_selected: true
  23. };
  24. iterations[0] = [rootId];
  25. // 处理每一轮
  26. rounds.forEach((round, roundIndex) => {
  27. if (round.type === 'initialization') {
  28. // 初始化阶段:处理 seg_list
  29. const roundNum = 0;
  30. if (!iterations[roundNum]) iterations[roundNum] = [];
  31. round.seg_list.forEach((seg, segIndex) => {
  32. const segId = `seg_${seg.text}_${roundNum}`;
  33. nodes[segId] = {
  34. type: 'seg',
  35. query: seg.text,
  36. level: 1,
  37. relevance_score: seg.score || 0,
  38. strategy: '初始分词',
  39. iteration: roundNum,
  40. is_terminated: false,
  41. evaluation_reason: '分词专家分词结果',
  42. is_selected: true,
  43. parent_query: o
  44. };
  45. // 添加边:root -> seg
  46. edges.push({
  47. from: rootId,
  48. to: segId,
  49. edge_type: 'root_to_seg',
  50. strategy: '初始分词'
  51. });
  52. iterations[roundNum].push(segId);
  53. });
  54. // 将 seg 作为第一轮的 q
  55. round.q_list_1.forEach((q, qIndex) => {
  56. const qId = `q_${q.text}_r1`;
  57. const segId = `seg_${q.text}_0`;
  58. nodes[qId] = {
  59. type: 'q',
  60. query: q.text,
  61. level: 1,
  62. relevance_score: q.score || 0,
  63. strategy: '来自分词',
  64. iteration: 1,
  65. is_terminated: false,
  66. evaluation_reason: '初始Query',
  67. is_selected: true,
  68. parent_query: q.text,
  69. from_source: 'seg'
  70. };
  71. // 添加边:seg -> q
  72. if (nodes[segId]) {
  73. edges.push({
  74. from: segId,
  75. to: qId,
  76. edge_type: 'seg_to_q',
  77. strategy: '作为Query'
  78. });
  79. }
  80. if (!iterations[1]) iterations[1] = [];
  81. iterations[1].push(qId);
  82. });
  83. } else {
  84. // 普通轮次
  85. const roundNum = round.round_num;
  86. const nextRoundNum = roundNum + 1;
  87. if (!iterations[nextRoundNum]) iterations[nextRoundNum] = [];
  88. // 添加本轮的操作步骤节点
  89. const stepNodes = [];
  90. // 获取第一个输入Query作为所有操作节点的连接点(代表本轮输入)
  91. // 注意:需要从已创建的节点中查找,因为节点ID可能带有index后缀
  92. let firstInputQId = null;
  93. if (round.input_q_list && round.input_q_list.length > 0) {
  94. const firstInputQ = round.input_q_list[0];
  95. // 尝试查找匹配的节点(考虑可能有index后缀)
  96. firstInputQId = Object.keys(nodes).find(id => {
  97. const node = nodes[id];
  98. return node.type === 'q' &&
  99. node.query === firstInputQ.text &&
  100. node.iteration === roundNum;
  101. });
  102. }
  103. // 步骤1:请求sug操作节点
  104. if (round.sug_count > 0) {
  105. const requestSugId = `operation_request_sug_r${roundNum}`;
  106. nodes[requestSugId] = {
  107. type: 'operation',
  108. query: `步骤1: 请求建议词 (${round.sug_count}个)`,
  109. level: roundNum,
  110. relevance_score: 0,
  111. strategy: '请求建议词',
  112. iteration: roundNum,
  113. operation_type: 'request_sug',
  114. detail: `为${round.input_q_list?.length || 0}个Query请求建议词,获得${round.sug_count}个建议`,
  115. is_selected: true
  116. };
  117. stepNodes.push(requestSugId);
  118. // 平级连接:从第一个输入Query连接
  119. if (firstInputQId && nodes[firstInputQId]) {
  120. edges.push({
  121. from: firstInputQId,
  122. to: requestSugId,
  123. edge_type: 'q_to_operation',
  124. strategy: '请求建议词'
  125. });
  126. }
  127. }
  128. // 步骤2:评估sug操作节点
  129. if (round.sug_count > 0) {
  130. const evaluateSugId = `operation_evaluate_sug_r${roundNum}`;
  131. nodes[evaluateSugId] = {
  132. type: 'operation',
  133. query: `步骤2: 评估建议词 (高分:${round.high_score_sug_count})`,
  134. level: roundNum,
  135. relevance_score: 0,
  136. strategy: '评估建议词',
  137. iteration: roundNum,
  138. operation_type: 'evaluate_sug',
  139. detail: `评估${round.sug_count}个建议词,${round.high_score_sug_count}个达到阈值`,
  140. is_selected: true
  141. };
  142. stepNodes.push(evaluateSugId);
  143. // 平级连接:从第一个输入Query连接
  144. if (firstInputQId && nodes[firstInputQId]) {
  145. edges.push({
  146. from: firstInputQId,
  147. to: evaluateSugId,
  148. edge_type: 'q_to_operation',
  149. strategy: '评估建议词'
  150. });
  151. }
  152. }
  153. // 步骤3:执行搜索操作节点
  154. if (round.search_count > 0) {
  155. const searchOpId = `operation_search_r${roundNum}`;
  156. nodes[searchOpId] = {
  157. type: 'operation',
  158. query: `步骤3: 执行搜索 (${round.search_count}次)`,
  159. level: roundNum,
  160. relevance_score: 0,
  161. strategy: '执行搜索',
  162. iteration: roundNum,
  163. operation_type: 'search',
  164. search_count: round.search_count,
  165. total_posts: round.total_posts,
  166. detail: `搜索${round.search_count}个高分建议词,找到${round.total_posts}个帖子`,
  167. is_selected: true
  168. };
  169. stepNodes.push(searchOpId);
  170. // 平级连接:从第一个输入Query连接
  171. if (firstInputQId && nodes[firstInputQId]) {
  172. edges.push({
  173. from: firstInputQId,
  174. to: searchOpId,
  175. edge_type: 'q_to_operation',
  176. strategy: '执行搜索'
  177. });
  178. }
  179. }
  180. // 步骤4:加词操作节点
  181. const addWordCount = round.output_q_list?.filter(q => q.from === 'add').length || 0;
  182. if (addWordCount > 0) {
  183. const addWordId = `operation_add_word_r${roundNum}`;
  184. nodes[addWordId] = {
  185. type: 'operation',
  186. query: `步骤4: 智能加词 (${addWordCount}个)`,
  187. level: roundNum,
  188. relevance_score: 0,
  189. strategy: '智能加词',
  190. iteration: roundNum,
  191. operation_type: 'add_word',
  192. detail: `为Seed选择词并组合,生成${addWordCount}个新Query`,
  193. is_selected: true
  194. };
  195. stepNodes.push(addWordId);
  196. // 平级连接:从第一个输入Query连接
  197. if (firstInputQId && nodes[firstInputQId]) {
  198. edges.push({
  199. from: firstInputQId,
  200. to: addWordId,
  201. edge_type: 'q_to_operation',
  202. strategy: '智能加词'
  203. });
  204. }
  205. }
  206. // 步骤5:筛选高分sug操作节点
  207. const sugCount = round.output_q_list?.filter(q => q.from === 'sug').length || 0;
  208. if (sugCount > 0) {
  209. const filterSugId = `operation_filter_sug_r${roundNum}`;
  210. nodes[filterSugId] = {
  211. type: 'operation',
  212. query: `步骤5: 筛选高分sug (${sugCount}个)`,
  213. level: roundNum,
  214. relevance_score: 0,
  215. strategy: '筛选高分sug',
  216. iteration: roundNum,
  217. operation_type: 'filter_sug',
  218. detail: `筛选出${sugCount}个分数高于来源Query的建议词`,
  219. is_selected: true
  220. };
  221. stepNodes.push(filterSugId);
  222. // 平级连接:从第一个输入Query连接
  223. if (firstInputQId && nodes[firstInputQId]) {
  224. edges.push({
  225. from: firstInputQId,
  226. to: filterSugId,
  227. edge_type: 'q_to_operation',
  228. strategy: '筛选高分sug'
  229. });
  230. }
  231. }
  232. // 将操作节点添加到当前轮次
  233. stepNodes.forEach(nodeId => {
  234. if (!iterations[roundNum]) iterations[roundNum] = [];
  235. iterations[roundNum].push(nodeId);
  236. });
  237. // 处理输出的 q_list
  238. if (round.output_q_list) {
  239. round.output_q_list.forEach((q, qIndex) => {
  240. const qId = `q_${q.text}_r${nextRoundNum}_${qIndex}`;
  241. nodes[qId] = {
  242. type: 'q',
  243. query: q.text,
  244. level: nextRoundNum,
  245. relevance_score: q.score || 0,
  246. strategy: q.from === 'add' ? '加词生成' : q.from === 'sug' ? '建议词' : '未知',
  247. iteration: nextRoundNum,
  248. is_terminated: false,
  249. evaluation_reason: `来源: ${q.from}`,
  250. is_selected: true,
  251. from_source: q.from
  252. };
  253. // 连接到对应的操作节点
  254. if (q.from === 'add') {
  255. // 加词生成的Query连接到加词操作节点
  256. const addWordId = `operation_add_word_r${roundNum}`;
  257. if (nodes[addWordId]) {
  258. edges.push({
  259. from: addWordId,
  260. to: qId,
  261. edge_type: 'operation_to_q',
  262. strategy: '加词生成'
  263. });
  264. }
  265. } else if (q.from === 'sug') {
  266. // sug生成的Query连接到筛选sug操作节点
  267. const filterSugId = `operation_filter_sug_r${roundNum}`;
  268. if (nodes[filterSugId]) {
  269. edges.push({
  270. from: filterSugId,
  271. to: qId,
  272. edge_type: 'operation_to_q',
  273. strategy: '建议词生成'
  274. });
  275. }
  276. }
  277. iterations[nextRoundNum].push(qId);
  278. });
  279. }
  280. }
  281. });
  282. return {
  283. nodes,
  284. edges,
  285. iterations
  286. };
  287. }
  288. module.exports = { convertV8ToGraph };