fix: 匹配markdown

This commit is contained in:
imeepos
2025-07-22 15:57:36 +08:00
parent f92d9a7c39
commit 725514af2d
5 changed files with 507 additions and 61 deletions

View File

@@ -60,6 +60,10 @@ export const EnhancedMarkdownRenderer: React.FC<EnhancedMarkdownRendererProps> =
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<EnhancedMarkdownRendererProps> =
}
}, [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 ? (
<span
className="inline-flex items-center ml-1 px-1 py-0.5 text-xs bg-blue-100 text-blue-700 rounded cursor-help"
title={`引用了 ${groundingAnalysis.groundingInfo.sourceCount} 个来源`}
onClick={() => {
console.log('📚 点击查看引用详情:', groundingAnalysis);
// 这里可以添加弹窗或侧边栏显示详细引用信息
}}
>
📚 {groundingAnalysis.groundingInfo.sourceCount}
</span>
) : null;
// 尝试使用 groundingMetadata 解析出来 当前节点引用的那部分资源 然后打印
switch (node.node_type) {
case MarkdownNodeType.Document:
return (
<div key={key} className="markdown-document">
{node.children.map((child) => renderNode(child, depth + 1))}
{node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))}
</div>
);
@@ -113,16 +191,18 @@ export const EnhancedMarkdownRenderer: React.FC<EnhancedMarkdownRendererProps> =
5: 'font-medium mb-1 text-sm',
6: 'font-medium mb-1 text-xs'
};
return (+
return (
<HeadingTag key={key} className={headingClasses[level as keyof typeof headingClasses] || headingClasses[3]}>
{node.children.map((child) => renderNode(child, depth + 1))}
{node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))}
{GroundingIndicator}
</HeadingTag>
);
case MarkdownNodeType.Paragraph:
return (
<p key={key} className="mb-2">
{node.children.map((child) => renderNode(child, depth + 1))}
{node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))}
{GroundingIndicator}
</p>
);
@@ -132,14 +212,14 @@ export const EnhancedMarkdownRenderer: React.FC<EnhancedMarkdownRendererProps> =
const listClass = isOrdered ? 'list-decimal ml-4 mb-2' : 'list-disc ml-4 mb-2';
return (
<ListTag key={key} className={listClass}>
{node.children.map((child) => renderNode(child, depth + 1))}
{node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))}
</ListTag>
);
case MarkdownNodeType.ListItem:
return (
<li key={key} className="mb-1">
{node.children.map((child) => renderNode(child, depth + 1))}
{node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))}
</li>
);
@@ -172,7 +252,7 @@ export const EnhancedMarkdownRenderer: React.FC<EnhancedMarkdownRendererProps> =
target="_blank"
rel="noopener noreferrer"
>
{node.children.map((child) => renderNode(child, depth + 1))}
{node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))}
</a>
);
@@ -191,21 +271,21 @@ export const EnhancedMarkdownRenderer: React.FC<EnhancedMarkdownRendererProps> =
case MarkdownNodeType.Strong:
return (
<strong key={key}>
{node.children.map((child) => renderNode(child, depth + 1))}
{node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))}
</strong>
);
case MarkdownNodeType.Emphasis:
return (
<em key={key}>
{node.children.map((child) => renderNode(child, depth + 1))}
{node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))}
</em>
);
case MarkdownNodeType.Blockquote:
return (
<blockquote key={key} className="border-l-4 border-gray-300 pl-4 italic text-gray-600 mb-2">
{node.children.map((child) => renderNode(child, depth + 1))}
{node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))}
</blockquote>
);
@@ -218,11 +298,12 @@ export const EnhancedMarkdownRenderer: React.FC<EnhancedMarkdownRendererProps> =
default:
return (
<div key={key} className="unknown-node">
{node.children.map((child) => renderNode(child, depth + 1))}
{node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))}
</div>
);
}
}, []);
}, [analyzeNodeGrounding]);
return (
<div className={`enhanced-markdown-renderer ${className}`}>

View File

@@ -0,0 +1,222 @@
# Grounding分析实现文档
## 概述
我们在 `EnhancedMarkdownRenderer` 组件中实现了一个强大的功能:**将Markdown节点与引用资源进行关联分析**。这个功能可以识别文档中哪些部分有引用支持,并提供可视化指示器。
## 🔍 功能特性
### 1. 类型分析
#### MarkdownNode 类型
```typescript
interface MarkdownNode {
node_type: MarkdownNodeType; // 节点类型(标题、段落、链接等)
content: string; // 节点内容(原始文本)
range: Range; // 位置范围(行号、列号、字符偏移)
children: MarkdownNode[]; // 子节点
attributes: Record<string, string>; // 节点属性
}
```
#### 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 ? (
<span
className="inline-flex items-center ml-1 px-1 py-0.5 text-xs bg-blue-100 text-blue-700 rounded cursor-help"
title={`引用了 ${groundingAnalysis.groundingInfo.sourceCount} 个来源`}
onClick={() => {
console.log('📚 点击查看引用详情:', groundingAnalysis);
}}
>
📚 {groundingAnalysis.groundingInfo.sourceCount}
</span>
) : 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
<EnhancedMarkdownRenderer
content={markdownContent}
enableMarkdown={true}
enableReferences={true}
groundingMetadata={groundingData}
/>
```
### 完整示例
参见 `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节点与引用资源进行了关联提供了
- ✅ 精确的位置匹配算法
- ✅ 可视化的引用指示器
- ✅ 详细的分析输出
- ✅ 良好的性能和用户体验
- ✅ 可扩展的架构设计
通过这个功能,用户可以清楚地看到文档中哪些部分有引用支持,并能够深入了解引用的详细信息,大大提升了文档的可信度和可追溯性。

View File

@@ -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 (
<div className="grounding-analysis-example p-6 max-w-4xl mx-auto">
<h1 className="text-3xl font-bold mb-6">Grounding分析示例</h1>
<div className="mb-6 p-4 bg-blue-50 border border-blue-200 rounded-lg">
<h2 className="text-lg font-semibold mb-2">🔍 </h2>
<p className="text-sm text-blue-700 mb-2">
Markdown节点与引用资源进行关联分析
</p>
<ul className="text-sm text-blue-700 space-y-1">
<li> 📚 </li>
<li> </li>
<li> </li>
<li> </li>
</ul>
</div>
{/* Grounding Metadata 信息展示 */}
<div className="mb-6 p-4 bg-gray-50 rounded-lg">
<h3 className="text-lg font-semibold mb-3">📋 Grounding Metadata </h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<h4 className="font-medium mb-2"> ({mockGroundingMetadata.sources.length})</h4>
<div className="space-y-2">
{mockGroundingMetadata.sources.map((source, index) => (
<div key={index} className="text-sm p-2 bg-white rounded border">
<div className="font-medium">{source.title}</div>
<div className="text-gray-600 text-xs">{source.uri}</div>
</div>
))}
</div>
</div>
<div>
<h4 className="font-medium mb-2"> ({mockGroundingMetadata.grounding_supports?.length || 0})</h4>
<div className="space-y-2">
{mockGroundingMetadata.grounding_supports?.map((support, index) => (
<div key={index} className="text-sm p-2 bg-white rounded border">
<div>: {support.segment.start_index} - {support.segment.end_index}</div>
<div className="text-gray-600">: [{support.groundingChunkIndices.join(', ')}]</div>
</div>
))}
</div>
</div>
</div>
</div>
{/* Markdown渲染 */}
<div className="border border-gray-300 rounded-lg overflow-hidden">
<div className="bg-gray-100 px-4 py-2 border-b">
<h3 className="font-semibold"></h3>
</div>
<div className="p-4">
<EnhancedMarkdownRenderer
content={content}
enableMarkdown={true}
enableReferences={true}
groundingMetadata={mockGroundingMetadata}
showStatistics={true}
/>
</div>
</div>
{/* 分析说明 */}
<div className="mt-6 p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
<h3 className="text-lg font-semibold mb-2">🧠 </h3>
<div className="text-sm text-yellow-800 space-y-2">
<p><strong>1. :</strong> Markdown节点的字符偏移位置与grounding片段的位置范围</p>
<p><strong>2. :</strong> grounding支持信息中的索引</p>
<p><strong>3. :</strong> 📚</p>
<p><strong>4. :</strong> </p>
</div>
</div>
{/* 技术实现 */}
<div className="mt-6 p-4 bg-green-50 border border-green-200 rounded-lg">
<h3 className="text-lg font-semibold mb-2"> </h3>
<div className="text-sm text-green-800 space-y-2">
<p><strong>:</strong></p>
<ul className="ml-4 space-y-1">
<li> <code>MarkdownNode</code>: </li>
<li> <code>GroundingMetadata</code>: </li>
<li> <code>GroundingSupport</code>: </li>
</ul>
<p><strong>:</strong> </p>
<p><strong>:</strong> 使React组件动态显示引用指示器和详细信息</p>
</div>
</div>
</div>
);
};
export default GroundingAnalysisExample;

View File

@@ -65,8 +65,6 @@ export class RagGroundingService {
system_prompt: options.systemPrompt,
};
console.log('🔍 发起RAG Grounding查询:', { userInput, options });
// 调用后端命令
const response = await invoke<RagGroundingResponse>('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,

View File

@@ -75,9 +75,9 @@ export interface GroundingSupport {
*/
export interface GroundingSegment {
/** 片段开始位置 */
start_index: number;
startIndex: number;
/** 片段结束位置 */
end_index: number;
endIndex: number;
/** 片段文字内容 */
text: string;
}