2025年05月必学:Dart开发者的RAG应用实战

云信安装大师
90
AI 质量分
10 5 月, 2025
4 分钟阅读
0 阅读

2025年05月必学:Dart开发者的RAG应用实战

引言

在2025年的今天,RAG(检索增强生成)技术已经成为AI应用开发的标准范式。作为Dart开发者,如何在Flutter生态中构建RAG应用?本文将带你从零开始,使用Dart语言实现一个完整的RAG应用,包含知识库检索和文本生成两大核心功能。

准备工作

环境要求

  • Dart SDK 3.5+ (2025年稳定版)
  • Flutter 4.0+
  • PostgreSQL 16+ (作为向量数据库)
  • Python 3.10+ (用于模型服务)

所需依赖

代码片段
dependencies:
  dart_openai: ^3.0.0  # OpenAI Dart SDK
  pg: ^4.0.0          # PostgreSQL客户端
  vector_math: ^2.1.0 # 向量计算

RAG系统架构

我们的RAG应用将分为三个核心模块:

  1. 知识库处理:将文档转换为向量并存储
  2. 检索模块:根据查询查找相关文档片段
  3. 生成模块:基于检索结果生成回答

第一步:设置向量数据库

PostgreSQL安装与配置

代码片段
# Ubuntu安装示例
sudo apt-get install postgresql-16 postgresql-contrib-16
sudo -u postgres psql -c "CREATE DATABASE rag_demo;"
sudo -u postgres psql -c "CREATE EXTENSION vector;" rag_demo

Dart连接配置

代码片段
import 'package:pg/postgres.dart';

final conn = PostgreSQLConnection(
  'localhost',
  5432,
  'rag_demo',
  username: 'postgres',
  password: 'yourpassword',
);

await conn.open();

第二步:构建知识库

文档嵌入处理

代码片段
import 'package:dart_openai/dart_openai.dart';

Future<List<double>> getEmbedding(String text) async {
  final embedding = await OpenAI.instance.embeddings.create(
    model: 'text-embedding-3-large',
    input: text,
  );

  return embedding.data.first.embedding;
}

存储到向量数据库

代码片段
Future<void> storeDocument(String docId, String content) async {
  final embedding = await getEmbedding(content);

  await conn.execute(
    '''
    INSERT INTO documents (id, content, embedding)
    VALUES (@id, @content, @embedding)
    ''',
    substitutionValues: {
      'id': docId,
      'content': content,
      'embedding': embedding,
    },
  );
}

第三步:实现检索功能

相似度搜索实现

代码片段
Future<List<Map<String, dynamic>>> searchSimilarDocuments(String query, {int limit = 3}) async {
  final queryEmbedding = await getEmbedding(query);

  final results = await conn.query(
    '''
    SELECT id, content 
    FROM documents 
    ORDER BY embedding <=> @embedding 
    LIMIT @limit
    ''',
    substitutionValues: {
      'embedding': queryEmbedding,
      'limit': limit,
    },
  );

  return results.map((row) => {
    'id': row[0],
    'content': row[1],
  }).toList();
}

RAG整合实现

RAG问答完整流程

代码片段
Future<String> askQuestion(String question) async {
 // Step1: Retrieve relevant documents 
 final docs = await searchSimilarDocuments(question);

 // Step2: Build context for LLM 
 final context = docs.map((doc) => doc['content']).join('\n\n');

 // Step3: Generate answer with context 
 final chatCompletion = await OpenAI.instance.chat.create(
   model: 'gpt-5-turbo',
   messages: [
     OpenAIChatCompletionChoiceMessageModel(
       role: OpenAIChatMessageRole.system,
       content: '''
       你是一个专业助手,请基于以下上下文回答问题:
       $context

       如果上下文不包含答案,请回答"我不知道"。
       '''
     ),
     OpenAIChatCompletionChoiceMessageModel(
       role: OpenAIChatMessageRole.user,
       content: question,
     ),
   ],
 );

 return chatCompletion.choices.first.message.content;
}

Flutter前端集成示例

代码片段
class ChatScreen extends StatefulWidget {
 @override
 _ChatScreenState createState() => _ChatScreenState();
}

class _ChatScreenState extends State<ChatScreen> {
 final TextEditingController _controller = TextEditingController();
 List<String> messages = [];
 bool isLoading = false;

 Future<void> _sendMessage() async {
   setState(() { isLoading = true; });

   final question = _controller.text;
   _controller.clear();

   setState(() { messages.add('User: $question'); });

   try {
     final answer = await askQuestion(question);
     setState(() { messages.add('AI: $answer'); });
   } catch (e) {
     setState(() { messages.add('Error: ${e.toString()}'); });
   }

   setState(() { isLoading = false; });
 }

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     body: Column(
       children: [
         Expanded(child: ListView.builder(
           itemCount: messages.length,
           itemBuilder: (ctx, i) => Text(messages[i]),
         )),
         if(isLoading) CircularProgressIndicator(),
         Padding(
           padding: const EdgeInsets.all(8.0),
           child: Row(children:[
             Expanded(child: TextField(controller:_controller)),
             IconButton(
               icon: Icon(Icons.send),
               onPressed:_sendMessage,
             )
           ]),
         )
       ],
     ),
   );
 }
}

RAG优化技巧

  1. 分块策略

    代码片段
    // Markdown文档分块示例
    List<String> chunkMarkdown(String content) {
      // Split by headings and keep chunks between them
      return content.split(RegExp(r'^#{1,6}\s.+$', multiLine: true));
    }
    
  2. 混合搜索

    代码片段
    Future<List<Map>> hybridSearch(String query) async {
      // Combine vector search with keyword search for better recall 
      final vectorResults = await searchSimilarDocuments(query);
    
      // Add BM25 keyword search results from Postgres full-text search 
      final keywordResults = await conn.query('''
        SELECT id, content FROM documents 
        WHERE to_tsvector(content) @@ plainto_tsquery(@query)
        LIMIT @limit''', 
        substitutionValues:{'query':query,'limit':2}
      );
    
      return [...vectorResults, ...keywordResults];
    }
    
  3. 缓存机制

    代码片段
    class EmbeddingCache {
      static final _cache = <String, List<double>>{};
    
      static Future<List<double>> getOrCompute(String text) async { 
        if(_cache.containsKey(text)) return _cache[text]!;
    
        final embedding = await getEmbedding(text);
        _cache[text] = embedding;
    
        return embedding;
      }
    }
    

FAQ常见问题解答

Q1: Dart处理大文本时内存不足怎么办?
A: Stream API分段处理:

代码片段
Stream<List<double>> streamEmbeddings(List<String> chunks) async* {
 for(final chunk in chunks) { 
   yield await getEmbedding(chunk); 
 }
}  

Q2: PostgreSQL向量搜索性能如何优化?
A:
1. Create an index on the vector column:

代码片段
CREATE INDEX ON documents USING ivfflat (embedding vector_l2_ops)
WITH (lists=100); -- Adjust lists based on dataset size  
  1. Partition large tables by document type/time

Q3: Flutter中如何实时显示生成过程?
A: Use streaming API:

代码片段
final stream = OpenAI.instance.chat.createStream(...);  

await for(final chunk in stream) {  
 setState((){ answer += chunk.content; });  
}  

RAG未来展望(2025版)

  1. 多模态RAG:支持图像/视频检索增强
  2. 实时知识更新:动态更新向量数据库
  3. 边缘计算:在移动设备本地运行小型RAG模型

Conclusion关键总结

  1. Dart生态已完全支持现代RAG应用开发
  2. PostgreSQL是性价比极高的向量数据库选择
  3. RAG的核心在于平衡检索质量与生成效果

完整项目代码已发布在GitHub:Dart-RAG-Demo


希望这篇2025年的前瞻性教程能帮助Dart开发者掌握RAG技术!如果有任何问题,欢迎在评论区讨论。

原创 高质量