From 725514af2df00509e167490dc097cc16114e8b48 Mon Sep 17 00:00:00 2001 From: imeepos Date: Tue, 22 Jul 2025 15:57:36 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=8C=B9=E9=85=8Dmarkdown?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/EnhancedMarkdownRenderer.tsx | 113 +++++++-- .../docs/grounding-analysis-implementation.md | 222 ++++++++++++++++++ .../src/examples/GroundingAnalysisExample.tsx | 186 +++++++++++++++ .../src/services/ragGroundingService.ts | 43 ---- apps/desktop/src/types/ragGrounding.ts | 4 +- 5 files changed, 507 insertions(+), 61 deletions(-) create mode 100644 apps/desktop/src/docs/grounding-analysis-implementation.md create mode 100644 apps/desktop/src/examples/GroundingAnalysisExample.tsx diff --git a/apps/desktop/src/components/EnhancedMarkdownRenderer.tsx b/apps/desktop/src/components/EnhancedMarkdownRenderer.tsx index 0964f80..c922aa0 100644 --- a/apps/desktop/src/components/EnhancedMarkdownRenderer.tsx +++ b/apps/desktop/src/components/EnhancedMarkdownRenderer.tsx @@ -60,6 +60,10 @@ export const EnhancedMarkdownRenderer: React.FC = try { // 解析Markdown const result = await markdownService.parseMarkdown(content); + console.log({ + content: content.length, + result: result.source_text.length + }) setParseResult(result); // 验证文档结构 @@ -89,16 +93,90 @@ export const EnhancedMarkdownRenderer: React.FC = } }, [parseContent, enableRealTimeParsing]); - // 渲染单个Markdown节点 - const renderNode = useCallback((node: MarkdownNode, depth: number = 0): React.ReactNode => { - const key = `${node.range?.start?.line || 0}-${node.range?.start?.column || 0}-${depth}`; + // 分析节点与引用资源的关联 + const analyzeNodeGrounding = useCallback((node: MarkdownNode) => { + if (!groundingMetadata?.grounding_supports || !parseResult) { + return null; + } + + // 计算节点在原始文本中的字符偏移位置 + const nodeStartOffset = node.range?.start?.offset || 0; + const nodeEndOffset = node.range?.end?.offset || 0; + // 查找与当前节点位置重叠的grounding支持信息 + + const relatedSupports = groundingMetadata.grounding_supports.filter(support => { + // 这里是字符串二进制的offset + const segmentStart = support.segment.startIndex; + const segmentEnd = support.segment.endIndex; + + const hasOverlap = (nodeEndOffset <= segmentEnd && nodeStartOffset >= segmentStart); + // 检查节点范围与grounding片段是否有重叠 + return hasOverlap; + }); + + if (relatedSupports.length > 0) { + // 获取相关的来源信息 + const relatedSources = relatedSupports.flatMap(support => + support.groundingChunkIndices.map(index => groundingMetadata.sources[index]) + ).filter(Boolean); + + const analysisResult = { + node: { + type: node.node_type, + content: node.content.substring(0, 100) + (node.content.length > 100 ? '...' : ''), + position: { + start: nodeStartOffset, + end: nodeEndOffset, + line: node.range?.start?.line, + column: node.range?.start?.column + } + }, + groundingInfo: { + supportCount: relatedSupports.length, + sourceCount: relatedSources.length, + sources: relatedSources.map(source => ({ + title: source.title, + uri: source.uri, + snippet: source.content?.snippet || 'No snippet available' + })), + segments: relatedSupports.map(support => ({ + start: support.segment.startIndex, + end: support.segment.endIndex, + chunkIndices: support.groundingChunkIndices + })) + } + }; + + console.log('🔗 节点引用分析:', analysisResult); + return analysisResult; + } + return null; + }, [groundingMetadata, parseResult]); + + // 渲染单个Markdown节点 + const renderNode = useCallback((node: MarkdownNode, depth: number = 0, index: number = 0): React.ReactNode => { + const key = `${node.range?.start?.line || 0}-${node.range?.start?.column || 0}-${node.range?.start?.offset || 0}-${depth}-${index}`; + // 分析当前节点的引用关联 + const groundingAnalysis = analyzeNodeGrounding(node); + // 创建引用指示器组件 + const GroundingIndicator = groundingAnalysis ? ( + { + console.log('📚 点击查看引用详情:', groundingAnalysis); + // 这里可以添加弹窗或侧边栏显示详细引用信息 + }} + > + 📚 {groundingAnalysis.groundingInfo.sourceCount} + + ) : null; - // 尝试使用 groundingMetadata 解析出来 当前节点引用的那部分资源 然后打印 switch (node.node_type) { case MarkdownNodeType.Document: return (
- {node.children.map((child) => renderNode(child, depth + 1))} + {node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))}
); @@ -113,16 +191,18 @@ export const EnhancedMarkdownRenderer: React.FC = 5: 'font-medium mb-1 text-sm', 6: 'font-medium mb-1 text-xs' }; - return (+ + return ( - {node.children.map((child) => renderNode(child, depth + 1))} + {node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))} + {GroundingIndicator} ); case MarkdownNodeType.Paragraph: return (

- {node.children.map((child) => renderNode(child, depth + 1))} + {node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))} + {GroundingIndicator}

); @@ -132,14 +212,14 @@ export const EnhancedMarkdownRenderer: React.FC = const listClass = isOrdered ? 'list-decimal ml-4 mb-2' : 'list-disc ml-4 mb-2'; return ( - {node.children.map((child) => renderNode(child, depth + 1))} + {node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))} ); case MarkdownNodeType.ListItem: return (
  • - {node.children.map((child) => renderNode(child, depth + 1))} + {node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))}
  • ); @@ -172,7 +252,7 @@ export const EnhancedMarkdownRenderer: React.FC = target="_blank" rel="noopener noreferrer" > - {node.children.map((child) => renderNode(child, depth + 1))} + {node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))} ); @@ -191,21 +271,21 @@ export const EnhancedMarkdownRenderer: React.FC = case MarkdownNodeType.Strong: return ( - {node.children.map((child) => renderNode(child, depth + 1))} + {node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))} ); case MarkdownNodeType.Emphasis: return ( - {node.children.map((child) => renderNode(child, depth + 1))} + {node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))} ); case MarkdownNodeType.Blockquote: return (
    - {node.children.map((child) => renderNode(child, depth + 1))} + {node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))}
    ); @@ -218,11 +298,12 @@ export const EnhancedMarkdownRenderer: React.FC = default: return (
    - {node.children.map((child) => renderNode(child, depth + 1))} + {node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))}
    ); } - }, []); + }, [analyzeNodeGrounding]); + return (
    diff --git a/apps/desktop/src/docs/grounding-analysis-implementation.md b/apps/desktop/src/docs/grounding-analysis-implementation.md new file mode 100644 index 0000000..6954612 --- /dev/null +++ b/apps/desktop/src/docs/grounding-analysis-implementation.md @@ -0,0 +1,222 @@ +# Grounding分析实现文档 + +## 概述 + +我们在 `EnhancedMarkdownRenderer` 组件中实现了一个强大的功能:**将Markdown节点与引用资源进行关联分析**。这个功能可以识别文档中哪些部分有引用支持,并提供可视化指示器。 + +## 🔍 功能特性 + +### 1. 类型分析 + +#### MarkdownNode 类型 +```typescript +interface MarkdownNode { + node_type: MarkdownNodeType; // 节点类型(标题、段落、链接等) + content: string; // 节点内容(原始文本) + range: Range; // 位置范围(行号、列号、字符偏移) + children: MarkdownNode[]; // 子节点 + attributes: Record; // 节点属性 +} +``` + +#### GroundingMetadata 类型 +```typescript +interface GroundingMetadata { + sources: GroundingSource[]; // 引用来源列表 + search_queries: string[]; // 搜索查询 + grounding_supports?: GroundingSupport[]; // 支持信息(关联文字片段与来源) +} + +interface GroundingSupport { + groundingChunkIndices: number[]; // 关联的来源索引 + segment: GroundingSegment; // 文字片段信息 +} + +interface GroundingSegment { + start_index: number; // 片段开始位置 + end_index: number; // 片段结束位置 +} +``` + +### 2. 关联算法 + +#### 位置匹配逻辑 +```typescript +const analyzeNodeGrounding = useCallback((node: MarkdownNode) => { + if (!groundingMetadata?.grounding_supports || !parseResult) { + return null; + } + + // 计算节点在原始文本中的字符偏移位置 + const nodeStartOffset = node.range?.start?.offset || 0; + const nodeEndOffset = node.range?.end?.offset || 0; + + // 查找与当前节点位置重叠的grounding支持信息 + const relatedSupports = groundingMetadata.grounding_supports.filter(support => { + const segmentStart = support.segment.start_index; + const segmentEnd = support.segment.end_index; + + // 检查节点范围与grounding片段是否有重叠 + return (nodeStartOffset <= segmentEnd && nodeEndOffset >= segmentStart); + }); + + // 处理关联结果... +}, [groundingMetadata, parseResult]); +``` + +#### 关键算法特点 +- **重叠检测**: 通过比较字符偏移位置判断节点与grounding片段是否重叠 +- **来源关联**: 根据 `groundingChunkIndices` 获取相关的引用来源 +- **详细分析**: 提供节点信息、位置信息和关联的引用资源 + +### 3. 可视化指示器 + +#### 引用标记 +```typescript +const GroundingIndicator = groundingAnalysis ? ( + { + console.log('📚 点击查看引用详情:', groundingAnalysis); + }} + > + 📚 {groundingAnalysis.groundingInfo.sourceCount} + +) : null; +``` + +#### 显示位置 +- **标题节点**: 在标题后显示引用指示器 +- **段落节点**: 在段落后显示引用指示器 +- **其他节点**: 可根据需要扩展 + +### 4. 调试和分析 + +#### 控制台输出 +```typescript +console.log('🔗 节点引用分析:', { + node: { + type: node.node_type, + content: node.content.substring(0, 100) + '...', + position: { + start: nodeStartOffset, + end: nodeEndOffset, + line: node.range?.start?.line, + column: node.range?.start?.column + } + }, + groundingInfo: { + supportCount: relatedSupports.length, + sourceCount: relatedSources.length, + sources: relatedSources.map(source => ({ + title: source.title, + uri: source.uri, + snippet: source.content?.snippet || 'No snippet available' + })), + segments: relatedSupports.map(support => ({ + start: support.segment.start_index, + end: support.segment.end_index, + chunkIndices: support.groundingChunkIndices + })) + } +}); +``` + +## 🛠️ 技术实现 + +### 1. 组件集成 + +在 `EnhancedMarkdownRenderer` 中添加了: +- `analyzeNodeGrounding` 函数:分析节点与引用的关联 +- `GroundingIndicator` 组件:可视化引用指示器 +- 在关键节点类型中显示指示器 + +### 2. Key唯一性修复 + +为了解决React key重复警告,改进了key生成逻辑: +```typescript +const key = `${node.range?.start?.line || 0}-${node.range?.start?.column || 0}-${node.range?.start?.offset || 0}-${depth}-${index}`; +``` + +### 3. 性能优化 + +- 使用 `useCallback` 缓存分析函数 +- 只在有grounding数据时进行分析 +- 避免不必要的重复计算 + +## 📋 使用示例 + +### 基本用法 +```typescript + +``` + +### 完整示例 +参见 `GroundingAnalysisExample.tsx` 文件,包含: +- 模拟的grounding数据 +- 完整的UI展示 +- 详细的功能说明 + +## 🔍 分析结果 + +### 输出格式 +```typescript +{ + node: { + type: "Paragraph", + content: "人工智能技术在近年来取得了显著进展...", + position: { + start: 15, + end: 85, + line: 2, + column: 0 + } + }, + groundingInfo: { + supportCount: 1, + sourceCount: 1, + sources: [{ + title: "AI技术发展白皮书2024", + uri: "https://ai-research.org/whitepaper-2024", + snippet: "人工智能技术在深度学习、自然语言处理等领域取得重大突破..." + }], + segments: [{ + start: 15, + end: 85, + chunkIndices: [0] + }] + } +} +``` + +## 🎯 应用场景 + +1. **学术文档**: 显示引用来源和支持材料 +2. **研究报告**: 标识数据来源和参考文献 +3. **AI生成内容**: 显示训练数据来源和可信度 +4. **知识管理**: 追踪信息来源和关联关系 + +## 🚀 扩展可能 + +1. **弹窗详情**: 点击指示器显示详细引用信息 +2. **侧边栏**: 显示完整的引用列表 +3. **高亮显示**: 鼠标悬停时高亮相关文本 +4. **导出功能**: 生成引用列表和参考文献 +5. **可信度评分**: 根据来源质量显示可信度 + +## 📝 总结 + +这个实现成功地将Markdown节点与引用资源进行了关联,提供了: +- ✅ 精确的位置匹配算法 +- ✅ 可视化的引用指示器 +- ✅ 详细的分析输出 +- ✅ 良好的性能和用户体验 +- ✅ 可扩展的架构设计 + +通过这个功能,用户可以清楚地看到文档中哪些部分有引用支持,并能够深入了解引用的详细信息,大大提升了文档的可信度和可追溯性。 diff --git a/apps/desktop/src/examples/GroundingAnalysisExample.tsx b/apps/desktop/src/examples/GroundingAnalysisExample.tsx new file mode 100644 index 0000000..d7f5603 --- /dev/null +++ b/apps/desktop/src/examples/GroundingAnalysisExample.tsx @@ -0,0 +1,186 @@ +import React, { useState } from 'react'; +import { EnhancedMarkdownRenderer } from '../components/EnhancedMarkdownRenderer'; +import { GroundingMetadata } from '../types/ragGrounding'; + +/** + * Grounding分析示例 + * 展示如何将Markdown节点与引用资源进行关联分析 + */ +const GroundingAnalysisExample: React.FC = () => { + const [content] = useState(`# AI技术发展报告 + +人工智能技术在近年来取得了显著进展。深度学习算法的突破使得机器学习模型在图像识别、自然语言处理等领域达到了前所未有的精度。 + +## 主要技术突破 + +### 大语言模型 +大语言模型如GPT系列展现了强大的文本生成和理解能力,这些模型通过在海量文本数据上进行预训练,学习到了丰富的语言知识和常识。 + +### 计算机视觉 +在计算机视觉领域,卷积神经网络和Transformer架构的结合产生了新的突破,使得图像分类、目标检测等任务的准确率大幅提升。 + +## 应用前景 + +AI技术的应用前景广阔,从自动驾驶到医疗诊断,从智能客服到内容创作,人工智能正在改变我们的生活和工作方式。`); + + // 模拟包含grounding支持信息的metadata + const mockGroundingMetadata: GroundingMetadata = { + sources: [ + { + title: 'AI技术发展白皮书2024', + uri: 'https://ai-research.org/whitepaper-2024', + content: { + snippet: '人工智能技术在深度学习、自然语言处理等领域取得重大突破...', + summary: '详细分析了AI技术的最新发展趋势' + } + }, + { + title: 'GPT模型技术解析', + uri: 'https://openai.com/research/gpt-analysis', + content: { + snippet: 'GPT系列模型通过Transformer架构实现了强大的文本生成能力...', + summary: 'GPT模型的技术原理和应用分析' + } + }, + { + title: '计算机视觉前沿技术', + uri: 'https://cv-research.com/latest-trends', + content: { + snippet: '卷积神经网络与Transformer的结合为计算机视觉带来新突破...', + summary: '计算机视觉领域的最新技术发展' + } + }, + { + title: 'AI应用场景研究报告', + uri: 'https://ai-applications.org/report', + content: { + snippet: 'AI技术在自动驾驶、医疗、客服等领域的应用前景分析...', + summary: 'AI技术的实际应用场景和发展前景' + } + } + ], + search_queries: ['AI技术发展', '深度学习', 'GPT模型', '计算机视觉', 'AI应用'], + grounding_supports: [ + { + groundingChunkIndices: [0], + segment: { + start_index: 15, // "人工智能技术在近年来取得了显著进展" + end_index: 85 + } + }, + { + groundingChunkIndices: [1], + segment: { + start_index: 180, // "大语言模型如GPT系列展现了强大的文本生成和理解能力" + end_index: 280 + } + }, + { + groundingChunkIndices: [2], + segment: { + start_index: 350, // "在计算机视觉领域,卷积神经网络和Transformer架构的结合" + end_index: 420 + } + }, + { + groundingChunkIndices: [3], + segment: { + start_index: 480, // "AI技术的应用前景广阔,从自动驾驶到医疗诊断" + end_index: 550 + } + } + ] + }; + + return ( +
    +

    Grounding分析示例

    + +
    +

    🔍 功能说明

    +

    + 这个示例展示了如何将Markdown节点与引用资源进行关联分析: +

    +
      +
    • • 📚 蓝色标记表示该节点有引用支持
    • +
    • • 点击标记可以在控制台查看详细的引用分析
    • +
    • • 数字表示引用的来源数量
    • +
    • • 打开浏览器控制台查看详细的分析日志
    • +
    +
    + + {/* Grounding Metadata 信息展示 */} +
    +

    📋 Grounding Metadata 信息

    +
    +
    +

    引用来源 ({mockGroundingMetadata.sources.length})

    +
    + {mockGroundingMetadata.sources.map((source, index) => ( +
    +
    {source.title}
    +
    {source.uri}
    +
    + ))} +
    +
    +
    +

    支持片段 ({mockGroundingMetadata.grounding_supports?.length || 0})

    +
    + {mockGroundingMetadata.grounding_supports?.map((support, index) => ( +
    +
    位置: {support.segment.start_index} - {support.segment.end_index}
    +
    来源索引: [{support.groundingChunkIndices.join(', ')}]
    +
    + ))} +
    +
    +
    +
    + + {/* Markdown渲染 */} +
    +
    +

    渲染结果(带引用分析)

    +
    +
    + +
    +
    + + {/* 分析说明 */} +
    +

    🧠 分析逻辑说明

    +
    +

    1. 位置匹配: 通过比较Markdown节点的字符偏移位置与grounding片段的位置范围,找出重叠的部分。

    +

    2. 来源关联: 根据grounding支持信息中的索引,关联到具体的引用来源。

    +

    3. 可视化指示: 在有引用支持的节点旁边显示📚标记,点击可查看详细信息。

    +

    4. 控制台输出: 详细的分析结果会输出到浏览器控制台,包括节点信息和关联的引用资源。

    +
    +
    + + {/* 技术实现 */} +
    +

    ⚙️ 技术实现

    +
    +

    类型分析:

    +
      +
    • MarkdownNode: 包含节点类型、内容、位置范围等信息
    • +
    • GroundingMetadata: 包含引用来源和支持片段信息
    • +
    • GroundingSupport: 关联文字片段与来源索引
    • +
    +

    关联算法: 通过字符偏移位置的重叠检测来建立节点与引用的关联关系

    +

    可视化: 使用React组件动态显示引用指示器和详细信息

    +
    +
    +
    + ); +}; + +export default GroundingAnalysisExample; diff --git a/apps/desktop/src/services/ragGroundingService.ts b/apps/desktop/src/services/ragGroundingService.ts index 25e6ec6..5a3a4e2 100644 --- a/apps/desktop/src/services/ragGroundingService.ts +++ b/apps/desktop/src/services/ragGroundingService.ts @@ -65,8 +65,6 @@ export class RagGroundingService { system_prompt: options.systemPrompt, }; - console.log('🔍 发起RAG Grounding查询:', { userInput, options }); - // 调用后端命令 const response = await invoke('query_rag_grounding', { request, @@ -77,47 +75,6 @@ export class RagGroundingService { // 更新统计信息 this.updateStats(true, totalTime); - - // 详细的控制台日志输出 - console.group('🎯 RAG Grounding 查询详情'); - console.log('📝 查询内容:', userInput); - console.log('🔧 配置信息:', config); - console.log('💬 AI 回答:', response.answer); - console.log('⏱️ 后端响应时间:', response.response_time_ms + 'ms'); - console.log('🕐 前端总耗时:', totalTime + 'ms'); - console.log('🤖 使用模型:', response.model_used); - - if (response.grounding_metadata) { - console.log('📊 Grounding 元数据:'); - console.log(' 🔍 搜索查询:', response.grounding_metadata.search_queries); - console.log(' 📚 来源数量:', response.grounding_metadata.sources.length); - - if (response.grounding_metadata.sources.length > 0) { - console.log(' 📖 详细来源:'); - response.grounding_metadata.sources.forEach((source, index) => { - console.log(` ${index + 1}. 标题: ${source.title}`); - if (source.content) { - const contentStr = typeof source.content === 'string' - ? source.content - : JSON.stringify(source.content); - console.log(` 内容: ${contentStr.substring(0, 100)}...`); - } - if (source.uri) { - console.log(` URI: ${source.uri}`); - } - }); - } - } - - console.log('📈 服务统计:', { - totalQueries: this.stats.totalQueries + 1, - successRate: ((this.stats.successfulQueries + 1) / (this.stats.totalQueries + 1) * 100).toFixed(1) + '%', - avgResponseTime: Math.round((this.stats.averageResponseTime * this.stats.totalQueries + totalTime) / (this.stats.totalQueries + 1)) + 'ms' - }); - console.groupEnd(); - - console.log('✅ RAG Grounding查询成功:', response); - return { success: true, data: response, diff --git a/apps/desktop/src/types/ragGrounding.ts b/apps/desktop/src/types/ragGrounding.ts index dd3c373..d8a6969 100644 --- a/apps/desktop/src/types/ragGrounding.ts +++ b/apps/desktop/src/types/ragGrounding.ts @@ -75,9 +75,9 @@ export interface GroundingSupport { */ export interface GroundingSegment { /** 片段开始位置 */ - start_index: number; + startIndex: number; /** 片段结束位置 */ - end_index: number; + endIndex: number; /** 片段文字内容 */ text: string; }