rag_trace_panel.html 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. <!DOCTYPE html>
  2. <html lang="zh">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>施工方案RAG链路透视镜</title>
  7. <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
  8. <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
  9. <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
  10. <script src="https://cdn.tailwindcss.com"></script>
  11. <style>
  12. .score-bar { transition: width 0.5s ease; }
  13. .lane-connector {
  14. position: absolute;
  15. left: 50%;
  16. border-left: 2px dashed #cbd5e1;
  17. z-index: 0;
  18. }
  19. </style>
  20. </head>
  21. <body class="bg-slate-50 text-slate-800">
  22. <div id="root"></div>
  23. <script type="text/babel">
  24. // 模拟你提供的日志数据
  25. const logData = {
  26. "stage": "rag_enhanced_check",
  27. "steps": {
  28. "1_query_extract": {
  29. "output": {
  30. "query_pairs": [
  31. {
  32. "entity": "T梁",
  33. "background": "安装作业,涉及外观检查、焊接连接...",
  34. "parameter": "梁端面平齐,梁缝符合要求..."
  35. },
  36. {
  37. "entity": "安全带",
  38. "background": "高处作业人员使用...",
  39. "parameter": "挂钩或绳子应挂在结实牢固的构件上..."
  40. }
  41. ]
  42. }
  43. },
  44. "2_entity_enhance_retrieval": {
  45. "output": {
  46. "results": [
  47. [ // T梁的结果
  48. {
  49. "metadata": { "title": "17.2.7 构件的存放" },
  50. "text_content": "构件应按其安装的先后顺序编号存放...",
  51. "hybrid_similarity": 0.78,
  52. "rerank_score": 0.96,
  53. "bfp_rerank_score": 0.09, // 低分致死原因
  54. "source_entity": "T梁"
  55. },
  56. {
  57. "metadata": { "title": "17.2.6 构件的场内移运" },
  58. "bfp_rerank_score": 0.02,
  59. "source_entity": "T梁"
  60. }
  61. ],
  62. [ // 安全带的结果
  63. {
  64. "metadata": { "title": "坠落防护 安全带" },
  65. "text_content": "坠落防护 安全带...",
  66. "hybrid_similarity": 0.65,
  67. "rerank_score": 0.92,
  68. "bfp_rerank_score": 0.97,
  69. "source_entity": "安全带"
  70. }
  71. ]
  72. ]
  73. }
  74. },
  75. "4_extract_query_pairs_results": {
  76. "output": {
  77. // 这里模拟那个 Bug:Entity是T梁,内容却是安全带
  78. "entity_results": [
  79. {
  80. "entity": "T梁",
  81. "query_index": 0,
  82. "metadata": { "title": "坠落防护 安全带" },
  83. "final_score": 0.97,
  84. "is_bug": true // 标记用于前端高亮
  85. }
  86. ]
  87. }
  88. }
  89. }
  90. };
  91. const ScoreBadge = ({ label, value, threshold = 0.5 }) => {
  92. const isLow = value < threshold;
  93. const colorClass = isLow ? "bg-red-100 text-red-700 border-red-200" : "bg-green-100 text-green-700 border-green-200";
  94. return (
  95. <div className={`flex flex-col border rounded px-2 py-1 text-xs ${colorClass} mb-1`}>
  96. <span className="opacity-70">{label}</span>
  97. <span className="font-bold text-sm">{value ? value.toFixed(2) : 'N/A'}</span>
  98. </div>
  99. );
  100. };
  101. const DocCard = ({ doc, isFinal = false, isBug = false }) => {
  102. return (
  103. <div className={`relative p-3 rounded-lg border-2 mb-2 bg-white shadow-sm ${isBug ? 'border-red-500 ring-2 ring-red-200' : 'border-slate-200'}`}>
  104. {isBug && (
  105. <div className="absolute -top-3 -right-3 bg-red-600 text-white text-xs font-bold px-2 py-1 rounded shadow animate-pulse">
  106. BUG DETECTED: 索引错位
  107. </div>
  108. )}
  109. <div className="font-semibold text-slate-700 text-sm truncate" title={doc.metadata?.title}>
  110. {doc.metadata?.title || doc.file_name || "Unknown Doc"}
  111. </div>
  112. {!isFinal && (
  113. <div className="grid grid-cols-3 gap-1 mt-2">
  114. <ScoreBadge label="混合检索" value={doc.hybrid_similarity} />
  115. <ScoreBadge label="粗排分数" value={doc.rerank_score} />
  116. <ScoreBadge label="背景重排" value={doc.bfp_rerank_score} threshold={0.5} />
  117. </div>
  118. )}
  119. {doc.bfp_rerank_score < 0.5 && !isFinal && (
  120. <div className="mt-1 text-xs text-red-500 font-bold text-center bg-red-50 py-1 rounded">
  121. {'[X] 因背景不匹配被过滤 (Score < 0.5)'}
  122. </div>
  123. )}
  124. <div className="mt-2 text-xs text-slate-500 line-clamp-2">
  125. {doc.text_content}
  126. </div>
  127. </div>
  128. );
  129. };
  130. const SwimLane = ({ queryPair, retrievalResults, finalResult, index }) => {
  131. const hasResults = retrievalResults && retrievalResults.length > 0;
  132. // 检查是否存在 Bug:Final result 的 entity 和 query 的 entity 不一致
  133. const isMismatchBug = finalResult && finalResult.entity !== queryPair.entity;
  134. const isContentMismatch = finalResult && finalResult.metadata?.title.includes("安全带") && queryPair.entity.includes("T梁");
  135. const bugDetected = isMismatchBug || isContentMismatch;
  136. return (
  137. <div className="flex flex-row gap-4 mb-8 min-w-[1000px]">
  138. {/* 1. 提取阶段 */}
  139. <div className="w-1/4 flex-shrink-0">
  140. <div className="bg-blue-50 border-l-4 border-blue-500 p-4 rounded shadow-sm h-full relative">
  141. <div className="absolute -left-3 -top-3 w-8 h-8 bg-blue-600 text-white rounded-full flex items-center justify-center font-bold">
  142. {index + 1}
  143. </div>
  144. <h3 className="font-bold text-lg text-blue-900">{queryPair.entity}</h3>
  145. <div className="mt-2 text-xs text-slate-600">
  146. <span className="font-semibold bg-blue-100 px-1 rounded">背景</span> {queryPair.background}
  147. </div>
  148. <div className="mt-2 text-xs text-slate-600">
  149. <span className="font-semibold bg-purple-100 px-1 rounded">参数</span> {queryPair.parameter}
  150. </div>
  151. </div>
  152. </div>
  153. {/* 箭头 */}
  154. <div className="flex flex-col justify-center items-center w-8 text-slate-300">
  155. </div>
  156. {/* 2. 检索 & 排序阶段 */}
  157. <div className="w-1/3 flex-shrink-0 flex flex-col gap-2">
  158. {hasResults ? (
  159. retrievalResults.map((doc, idx) => (
  160. <DocCard key={idx} doc={doc} />
  161. ))
  162. ) : (
  163. <div className="text-center p-4 border-2 border-dashed border-gray-300 rounded text-gray-400">
  164. 未召回到相关文档
  165. </div>
  166. )}
  167. </div>
  168. {/* 箭头 */}
  169. <div className="flex flex-col justify-center items-center w-8 text-slate-300">
  170. </div>
  171. {/* 3. 最终结果阶段 (展示 Bug) */}
  172. <div className="w-1/4 flex-shrink-0">
  173. {finalResult ? (
  174. <div className={`h-full ${bugDetected ? 'bg-red-50' : 'bg-green-50'} p-1 rounded`}>
  175. <DocCard doc={finalResult} isFinal={true} isBug={bugDetected} />
  176. {bugDetected && (
  177. <div className="text-xs text-red-600 mt-2 font-mono p-2 bg-red-100 rounded">
  178. 错误分析: <br/>
  179. Query: <strong>{queryPair.entity}</strong><br/>
  180. Result: <strong>{finalResult.metadata?.title}</strong><br/>
  181. <span className="font-bold">索引匹配错误!</span>
  182. </div>
  183. )}
  184. </div>
  185. ) : (
  186. <div className="h-full flex items-center justify-center border-2 border-dashed border-slate-200 rounded bg-slate-50 text-slate-400 text-sm">
  187. 此链路被过滤
  188. </div>
  189. )}
  190. </div>
  191. </div>
  192. );
  193. };
  194. const Dashboard = () => {
  195. const queryPairs = logData.steps["1_query_extract"].output.query_pairs;
  196. const retrievalOutputs = logData.steps["2_entity_enhance_retrieval"].output.results;
  197. const finalResults = logData.steps["4_extract_query_pairs_results"].output.entity_results;
  198. return (
  199. <div className="p-8 min-h-screen">
  200. <header className="mb-8 border-b pb-4">
  201. <h1 className="text-3xl font-bold text-slate-800">RAG 链路透视看板</h1>
  202. <p className="text-slate-500 mt-2">
  203. 针对 "T梁安装" 案例的执行轨迹回放与问题诊断
  204. </p>
  205. </header>
  206. <div className="flex flex-col">
  207. {/* 表头 */}
  208. <div className="flex flex-row gap-4 mb-4 text-sm font-bold text-slate-400 uppercase tracking-wider min-w-[1000px]">
  209. <div className="w-1/4">Step 1: 语义提取 (Query Construction)</div>
  210. <div className="w-8"></div>
  211. <div className="w-1/3">Step 2 & 3: 召回与重排序 (Recall & Rerank)</div>
  212. <div className="w-8"></div>
  213. <div className="w-1/4">Step 4: 最终映射 (Final Mapping)</div>
  214. </div>
  215. {/* 泳道 */}
  216. {queryPairs.map((pair, index) => {
  217. // 简单的按索引获取,模拟代码中的行为
  218. const results = retrievalOutputs[index] || [];
  219. // 模拟代码中的 Bug:这里强制让 index 0 的 T梁 显示 结果列表里的第 0 个 (其实是安全带,因为T梁被过滤了)
  220. // 在真实逻辑中,你需要根据 query_index 来 find
  221. const finalRes = finalResults.find(r => r.query_index === index) || (index === 0 && finalResults[0]);
  222. return (
  223. <SwimLane
  224. key={index}
  225. index={index}
  226. queryPair={pair}
  227. retrievalResults={results}
  228. finalResult={finalRes}
  229. />
  230. );
  231. })}
  232. </div>
  233. <div className="mt-12 bg-slate-800 text-slate-200 p-6 rounded-lg">
  234. <h3 className="text-xl font-bold mb-4 text-white">[工具] 修复建议 (基于日志分析)</h3>
  235. <ul className="list-disc pl-5 space-y-2">
  236. <li>
  237. <strong className="text-red-400">修复索引错位 Bug (Critical):</strong>
  238. 在 `extract_query_pairs_results` 函数中,不要假设 `enhanced_results` 的索引与 `query_pairs` 一一对应。
  239. 因为中间有过滤步骤,列表长度变了。应该使用 Map 或字典来通过 `query_index` 锚定结果。
  240. </li>
  241. <li>
  242. <strong className="text-yellow-400">解决 T梁 召回相关性低:</strong>
  243. 观察到 `bfp_rerank_score` 只有 0.09。这说明虽然召回了"T梁",但内容是关于"存放"的,而你需要"安装"。
  244. 建议在 Step 2 检索时,强制将 `background` 中的关键词(如"安装"、"焊接"、"支撑")加入到 Keyword Search 中,而不仅仅是搜索 Entity。
  245. </li>
  246. </ul>
  247. </div>
  248. </div>
  249. );
  250. };
  251. ReactDOM.render(<Dashboard />, document.getElementById('root'));
  252. </script>
  253. </body>
  254. </html>