手把手实现文本向量数据库:从原理到CRUD操作完整指南

在这里插入图片描述

前言

在上一篇文章《一眼看穿相似度:余弦相似度原理详解》中,我们学会了如何用余弦相似度判断两段文字是否相似。

但有个问题:这些向量往哪儿存?怎么用?

这篇文章,我们聊聊三种给AI"喂"知识的方法:

  1. 提示词工程:把知识全塞进prompt
  2. 向量数据库:建个外挂大脑,按需取用
  3. 传统数据库:精确查找,但不理解语义

读完这篇,你就知道为什么需要向量数据库,以及如何自己动手实现一个。


一、先说个真实的故事

在第一篇文章《Go + Ollama 构建的tinyRAG应用》里,我给一位眼科医生做了个智能客服。

那位医生有个特点:年纪大,不爱折腾电脑。

他跟我说:“我就想要个简单的,复制粘贴知识库进去,就能回答患者问题。”

我说:“行,我给你做个最简单的。”

于是我把他的眼科知识库全部塞进prompt,就像这样:

你是眼科助手,这是知识库:
【5000字的眼科知识...】
【用户问题...】

一开始挺顺利,知识库才几KB,AI回答得挺好。

但后来出事了——知识库越来越大,从几KB涨到几百KB,再涨到几MB…

有一天,AI突然报错:“prompt太长了,我装不下!”

这下尴尬了。


二、三种方法,三种命运

面对"知识太多装不下"这个问题,我调研了三种解决方案:

2.1 方案一:提示词工程(Prompt Engineering)

原理:把知识全塞进prompt,让AI一次性记住。

比喻:考试前,老师把所有知识点都写在黑板上,让你一次背完。

优点

  • 简单粗暴,不用折腾
  • AI能看到全部知识,回答准确

缺点

  • 黑板(prompt)长度有限,写不下太多知识
  • 知识库一大,AI就"记不住"了
  • 每次提问都要传全部知识,浪费钱(token计费)

适用场景:知识库小,问题简单,预算充足

我的遭遇:眼科医生的知识库越来越大,prompt装不下了。


2.2 方案二:传统关系数据库

原理:把知识存到MySQL、PostgreSQL等数据库,用关键词精确查找。

比喻:图书馆的书架,按书名精确找书。

优点

  • 存储量大,想存多少存多少
  • 查找速度快,精确匹配

缺点

  • 最大的问题:不理解语义
    • 用户问"眼睛红了怎么办"
    • 数据库里存的是"结膜炎症状"
    • 完全匹配不上!
  • 只能找"完全一样"的内容,找不到"意思相近"的内容

适用场景:数据结构化,查找条件明确,不需要理解语义

我的遭遇:医生的患者问"眼睛红肿",但知识库里写的是"结膜炎",数据库找不出来。


2.3 方案三:向量数据库

原理:把知识转成向量存起来,用户提问时,也转成向量,然后找"最相似"的知识。

比喻:你走进图书馆说"我想看关于登月的书",图书管理员不需要知道书名,而是根据你的需求,找出所有相关的书。

优点

  • 理解语义:用户说"眼睛红",能找到"结膜炎"
  • 存储灵活:向量压缩后,存储量比原文小很多
  • 智能匹配:相似度搜索,找到最相关的内容

缺点

  • 实现相对复杂
  • 需要计算相似度,有一定开销

适用场景:知识库大,问题多样,需要语义理解

我的选择:这个方案完美解决了眼科医生的问题!


三、三种方法大比拼

让我们用一个表格说清楚:

特性 提示词工程 传统数据库 向量数据库
理解语义 ✅ 完全理解 ❌ 不理解 ✅ 完全理解
存储容量 ❌ 有限制 ✅ 无限制 ✅ 无限制
实现难度 ✅ 简单 ✅ 简单 ⚠️ 中等
查找精度 ✅ 全面 ⚠️ 精确但不智能 ✅ 智能匹配
成本 ❌ 高(每次全传) ✅ 低 ✅ 低
适用规模 小知识库 结构化数据 大知识库

举个例子

用户问:“阿尔忒弥斯2号什么时候发射?”

  • 提示词工程:把所有知识库塞进prompt,AI自己找答案

    • 问题:知识库大了装不下
  • 传统数据库:搜索"发射"关键词

    • 问题:知识库写的是"执行",不是"发射",找不到
  • 向量数据库:理解"发射"和"执行"是相似的意思,找到答案

    • 完美!✅

四、自己动手:实现一个向量数据库

说了这么多理论,现在让我们动手实现一个简单的向量数据库。

4.1 还记得上篇的五步流程吗?

在上一篇文章里,我们把文本转成向量,分为五步:

文本 → 清洗 → 分词 → 词频 → 向量 → 归一化

这五步对应的函数是:

  • cleanText() - 文本清洗
  • tokenize() - 分词处理
  • countFrequency() - 词频统计
  • buildVector() - 构建向量
  • normalize() - 向量归一化

现在,我们要把这些向量存起来,建一个"外挂大脑"。

4.2 数据结构:如何存储向量?

想象一下,你在图书馆(VectorDB)工作,每本书(Document)都要登记:

  • 书的编号(ID)
  • 书的内容(Content)
  • 书的索引卡(Vector)

向量数据库也是这样:

// 文档记录结构
type Document struct {
    ID      int       // 文档编号
    Content string    // 文档内容
    Vector  []float64 // 向量表示(类似索引卡)
}

// 向量数据库
type VectorDB struct {
    documents []Document // 所有文档
    nextID    int       // 下一个编号
}

4.3 核心操作:增删改查(CRUD)

向量数据库和普通数据库一样,有四个基本操作:

操作1:Create(插入文档)

当你往数据库插入一篇文档时,它会:

  1. 把文本转成向量(用上篇的五步流程)
  2. 保存文档内容和向量
// 插入单条文档
func (vdb *VectorDB) Insert(content string) int {
    vector := textToVector(content)  // 文本 → 向量
    doc := Document{
        ID:      vdb.nextID,
        Content: content,
        Vector:  vector,
    }
    vdb.documents = append(vdb.documents, doc)
    vdb.nextID++
    return doc.ID
}

通俗理解:你拿来一本书,管理员给它编号,写好索引卡,上架。

操作2:Read(查询文档)

可以通过ID精确查找:

// 通过ID查询文档
func (vdb *VectorDB) GetByID(id int) *Document {
    for i := range vdb.documents {
        if vdb.documents[i].ID == id {
            return &vdb.documents[i]
        }
    }
    return nil
}

通俗理解:你说"我要借编号001的书",管理员直接找给你。

操作3:Search(相似度搜索)⭐核心功能

这是向量数据库的杀手锏:你问一个问题,它找出最相关的知识。

// 搜索结果结构
type SearchResult struct {
    ID         int     // 文档ID
    Content    string  // 文档内容
    Similarity float64 // 相似度得分(0-1,越接近1越相似)
}

// 相似度搜索
func (vdb *VectorDB) Search(query string, topK int) []SearchResult {
    // 1. 把查询转成向量
    queryVector := textToVector(query)
    
    // 2. 计算所有文档的相似度
    results := make([]SearchResult, 0, len(vdb.documents))
    for _, doc := range vdb.documents {
        similarity := cosineSimilarity(queryVector, doc.Vector)
        results = append(results, SearchResult{
            ID:         doc.ID,
            Content:    doc.Content,
            Similarity: similarity,
        })
    }
    
    // 3. 按相似度排序,返回前topK个
    sort.Slice(results, func(i, j int) bool {
        return results[i].Similarity > results[j].Similarity
    })
    
    if topK > len(results) {
        topK = len(results)
    }
    return results[:topK]
}

通俗理解:你走进图书馆说"我想了解登月",管理员不用知道具体书名,而是找出所有和登月相关的书,按相关程度排序给你。

相似度计算:使用余弦相似度,衡量两个向量的"方向"相似程度。

// 计算余弦相似度
func cosineSimilarity(a, b []float64) float64 {
    dotProduct := 0.0
    normA := 0.0
    normB := 0.0
    for i := range a {
        dotProduct += a[i] * b[i]
        normA += a[i] * a[i]
        normB += b[i] * b[i]
    }
    if normA == 0 || normB == 0 {
        return 0
    }
    return dotProduct / (math.Sqrt(normA) * math.Sqrt(normB))
}
操作4:Update(更新文档)
// 更新文档
func (vdb *VectorDB) Update(id int, newContent string) bool {
    for i := range vdb.documents {
        if vdb.documents[i].ID == id {
            vdb.documents[i].Content = newContent
            vdb.documents[i].Vector = textToVector(newContent)  // 重新计算向量
            return true
        }
    }
    return false
}

通俗理解:书的内容改了,重新写索引卡。

操作5:Delete(删除文档)
// 删除文档
func (vdb *VectorDB) Delete(id int) bool {
    for i := range vdb.documents {
        if vdb.documents[i].ID == id {
            vdb.documents = append(vdb.documents[:i], vdb.documents[i+1:]...)
            return true
        }
    }
    return false
}

通俗理解:书下架了,把记录删掉。


五、实战演示:阿尔忒弥斯2号知识库

让我们用阿尔忒弥斯2号的知识库,演示向量数据库的威力。

5.1 知识库内容

// 知识库:关于阿尔忒弥斯2号的新闻片段
var knowledgeBase = []string{
    "阿尔忒弥斯2号是美国宇航局计划中的载人月球轨道飞行任务,标志着人类重返月球的重要一步",
    "该任务将使用太空发射系统火箭和猎户座飞船,搭载宇航员进行绕月飞行",
    "阿尔忒弥斯2号的主要目标是测试生命支持系统和宇航员在深空环境中的生存能力",
    "任务计划在2025年执行,将是自阿波罗17号以来首次载人离开地球轨道的任务",
    "此次飞行将为后续的阿尔忒弥斯3号月球着陆任务提供关键数据和技术验证",
    "宇航员将在飞行过程中进行科学实验,并测试深空通信和导航系统",
    "任务将持续约10天,飞行距离将达到27万英里,是地球到月球距离的10倍以上",
    "阿尔忒弥斯2号的成功将为建立月球基地和未来火星探索奠定基础",
}

// 词汇表:我们关心的10个关键词
var vocab = []string{"阿尔忒弥斯", "月球", "任务", "宇航员", "火箭", "飞船", "测试", "系统", "飞行", "轨道"}

5.2 运行效果

测试1:用户问"什么时候发射?"

注意:知识库里写的是"执行",不是"发射"!

查询: 阿尔忒弥斯2号什么时候发射
Top 3 结果:
  1. 相似度: 0.8542
     内容: 任务计划在2025年执行,将是自阿波罗17号以来首次载人离开地球轨道的任务
  2. 相似度: 0.7235
     内容: 阿尔忒弥斯2号是美国宇航局计划中的载人月球轨道飞行任务...
  3. 相似度: 0.5124
     内容: 任务将持续约10天,飞行距离将达到27万英里...

成功! 向量数据库理解了"发射"和"执行"是相似的意思!

测试2:用户问"用什么火箭?"

查询: 用什么火箭
Top 3 结果:
  1. 相似度: 0.8123
     内容: 该任务将使用太空发射系统火箭和猎户座飞船,搭载宇航员进行绕月飞行
  2. 相似度: 0.4521
     内容: 阿尔忒弥斯2号的主要目标是测试生命支持系统...
  3. 相似度: 0.3892
     内容: 阿尔忒弥斯2号是美国宇航局计划中的载人月球轨道飞行任务...

完美! 准确找到了"太空发射系统火箭"那条记录。


六、对比总结

让我们回到最初的问题:三种方法,怎么选?

6.1 选择建议

场景 推荐方案 理由
知识库 < 100KB 提示词工程 简单直接,不用折腾
知识库 > 100KB 向量数据库 prompt装不下了
需要语义理解 向量数据库 其他方案做不到
只需要精确查找 传统数据库 性能最好,成本最低

6.2 我的经历

第一篇文章:用提示词工程,把知识库全塞进prompt

  • 适合眼科医生的小知识库
  • 问题:知识库一扩大,就装不下了

第二篇文章:学会把文本转成向量

  • 解决了"文字变数字"的问题
  • 但向量存哪儿?怎么用?

第三篇文章(本文):实现向量数据库

  • 解决了"存储和检索"的问题
  • 支持"语义搜索",理解用户意图

第四篇文章(预告):用向量数据库替换prompt

  • 把第一篇的prompt工程,升级为向量数据库
  • 解决知识库容量限制问题

七、关键函数总结

为了保持系列文章的连贯性,这里总结一下所有用到的函数:

7.1 文本向量化函数(第二篇文章)

cleanText(text string) string           // 步骤1:文本清洗
tokenize(text, vocab []string) []string // 步骤2:分词处理
countFrequency(tokens []string) map[string]int // 步骤3:词频统计
buildVector(freq map[string]int, vocab []string) []float64 // 步骤4:构建向量
normalize(vector []float64)             // 步骤5:向量归一化
textToVector(text string) []float64     // 完整流程:文本→向量

7.2 向量数据库函数(本文新增)

// 数据结构
type Document struct { ID, Content, Vector }
type VectorDB struct { documents, nextID }
type SearchResult struct { ID, Content, Similarity }

// CRUD操作
NewVectorDB() *VectorDB                 // 创建数据库
Insert(content string) int              // 插入文档
GetByID(id int) *Document               // 查询文档
Search(query string, topK int) []SearchResult // 相似度搜索
Update(id int, newContent string) bool  // 更新文档
Delete(id int) bool                     // 删除文档

// 辅助函数
cosineSimilarity(a, b []float64) float64 // 计算相似度

八、下一步预告

在这篇文章里,我们实现了向量数据库,解决了"存储和检索"的问题。

但还有一个关键问题:如何把向量数据库集成到第一篇文章的系统中,替换掉prompt全文本?

下一篇文章,我们将:

  1. 改造第一篇的tinyRAG系统
  2. 用向量数据库替换prompt工程
  3. 实现真正的RAG(检索增强生成)

敬请期待!


附录:完整代码

完整的向量数据库代码,包含:

  • 五步向量化流程
  • 完整CRUD操作
  • 相似度搜索
  • 运行示例
package main

import (
	"fmt"
	"math"
	"sort"
	"strings"
)

// ==================== 数据定义 ====================

// 知识库:关于阿尔忒弥斯2号的新闻片段
var knowledgeBase = []string{
	"阿尔忒弥斯2号是美国宇航局计划中的载人月球轨道飞行任务,标志着人类重返月球的重要一步",
	"该任务将使用太空发射系统火箭和猎户座飞船,搭载宇航员进行绕月飞行",
	"阿尔忒弥斯2号的主要目标是测试生命支持系统和宇航员在深空环境中的生存能力",
	"任务计划在2025年执行,将是自阿波罗17号以来首次载人离开地球轨道的任务",
	"此次飞行将为后续的阿尔忒弥斯3号月球着陆任务提供关键数据和技术验证",
	"宇航员将在飞行过程中进行科学实验,并测试深空通信和导航系统",
	"任务将持续约10天,飞行距离将达到27万英里,是地球到月球距离的10倍以上",
	"阿尔忒弥斯2号的成功将为建立月球基地和未来火星探索奠定基础",
}

// 词汇表:我们关心的10个关键词
var vocab = []string{"阿尔忒弥斯", "月球", "任务", "宇航员", "火箭", "飞船", "测试", "系统", "飞行", "轨道"}

// ==================== 文本向量化函数(第二篇文章) ====================

// 步骤1:文本清洗函数
func cleanText(text string) string {
	// 清洗:只保留中文字符,去掉空格、回车、换行、TAB等
	var cleaned strings.Builder
	for _, r := range text {
		// 只保留中文字符范围
		if r >= 0x4e00 && r <= 0x9fff {
			cleaned.WriteRune(r)
		}
	}
	return cleaned.String()
}

// 步骤2:分词函数
func tokenize(text string, vocab []string) []string {
	var tokens []string
	// 直接在文本中查找词汇表中的词
	for _, vocabWord := range vocab {
		count := strings.Count(text, vocabWord)
		for i := 0; i < count; i++ {
			tokens = append(tokens, vocabWord)
		}
	}
	return tokens
}

// 步骤3:词频统计函数
func countFrequency(tokens []string) map[string]int {
	freq := make(map[string]int)
	for _, token := range tokens {
		freq[token]++
	}
	return freq
}

// 步骤4:构建向量函数
func buildVector(freq map[string]int, vocab []string) []float64 {
	vector := make([]float64, len(vocab))
	for i, word := range vocab {
		vector[i] = float64(freq[word])
	}
	return vector
}

// 步骤5:向量归一化函数
func normalize(vector []float64) {
	sum := 0.0
	for _, v := range vector {
		sum += v * v
	}
	if sum == 0 {
		return
	}
	magnitude := math.Sqrt(sum)
	for i := range vector {
		vector[i] /= magnitude
	}
}

// 完整流程:文本转向量
func textToVector(text string) []float64 {
	// 步骤1:文本清洗
	cleanedText := cleanText(text)
	// 步骤2:分词处理
	tokens := tokenize(cleanedText, vocab)
	// 步骤3:词频统计
	freq := countFrequency(tokens)
	// 步骤4:构建向量
	vector := buildVector(freq, vocab)
	// 步骤5:向量归一化
	normalize(vector)
	return vector
}

// ==================== 向量数据库函数(第三篇文章) ====================

// 文档记录结构
type Document struct {
	ID      int       // 文档编号
	Content string    // 文档内容
	Vector  []float64 // 向量表示
}

// 向量数据库
type VectorDB struct {
	documents []Document // 所有文档
	nextID    int       // 下一个编号
}

// 搜索结果结构
type SearchResult struct {
	ID         int     // 文档ID
	Content    string  // 文档内容
	Similarity float64 // 相似度得分(0-1)
}

// 创建新的向量数据库
func NewVectorDB() *VectorDB {
	return &VectorDB{
		documents: make([]Document, 0),
		nextID:    1,
	}
}

// 计算余弦相似度
func cosineSimilarity(a, b []float64) float64 {
	dotProduct := 0.0
	normA := 0.0
	normB := 0.0
	for i := range a {
		dotProduct += a[i] * b[i]
		normA += a[i] * a[i]
		normB += b[i] * b[i]
	}
	if normA == 0 || normB == 0 {
		return 0
	}
	return dotProduct / (math.Sqrt(normA) * math.Sqrt(normB))
}

// ==================== CRUD操作 ====================

// Create:插入单条文档
func (vdb *VectorDB) Insert(content string) int {
	vector := textToVector(content)
	doc := Document{
		ID:      vdb.nextID,
		Content: content,
		Vector:  vector,
	}
	vdb.documents = append(vdb.documents, doc)
	vdb.nextID++
	return doc.ID
}

// Create:批量插入文档
func (vdb *VectorDB) BatchInsert(contents []string) []int {
	ids := make([]int, 0, len(contents))
	for _, content := range contents {
		id := vdb.Insert(content)
		ids = append(ids, id)
	}
	return ids
}

// Read:通过ID查询文档
func (vdb *VectorDB) GetByID(id int) *Document {
	for i := range vdb.documents {
		if vdb.documents[i].ID == id {
			return &vdb.documents[i]
		}
	}
	return nil
}

// Read:获取所有文档
func (vdb *VectorDB) GetAll() []Document {
	return vdb.documents
}

// Read:统计文档数量
func (vdb *VectorDB) Count() int {
	return len(vdb.documents)
}

// Search:相似度搜索(核心功能)
func (vdb *VectorDB) Search(query string, topK int) []SearchResult {
	// 1. 把查询转成向量
	queryVector := textToVector(query)
	
	// 2. 计算所有文档的相似度
	results := make([]SearchResult, 0, len(vdb.documents))
	for _, doc := range vdb.documents {
		similarity := cosineSimilarity(queryVector, doc.Vector)
		results = append(results, SearchResult{
			ID:         doc.ID,
			Content:    doc.Content,
			Similarity: similarity,
		})
	}
	
	// 3. 按相似度排序,返回前topK个
	sort.Slice(results, func(i, j int) bool {
		return results[i].Similarity > results[j].Similarity
	})
	
	if topK > len(results) {
		topK = len(results)
	}
	return results[:topK]
}

// Update:更新文档
func (vdb *VectorDB) Update(id int, newContent string) bool {
	for i := range vdb.documents {
		if vdb.documents[i].ID == id {
			vdb.documents[i].Content = newContent
			vdb.documents[i].Vector = textToVector(newContent)
			return true
		}
	}
	return false
}

// Delete:删除文档
func (vdb *VectorDB) Delete(id int) bool {
	for i := range vdb.documents {
		if vdb.documents[i].ID == id {
			vdb.documents = append(vdb.documents[:i], vdb.documents[i+1:]...)
			return true
		}
	}
	return false
}

// Delete:清空所有文档
func (vdb *VectorDB) DeleteAll() {
	vdb.documents = make([]Document, 0)
	vdb.nextID = 1
}

// ==================== 主函数演示 ====================

func main() {
	fmt.Println("🚀 向量数据库演示 - 阿尔忒弥斯2号知识库")
	fmt.Println("================================================")
	fmt.Println("本文演示三种方法的对比:")
	fmt.Println("1. 提示词工程:把知识全塞进prompt")
	fmt.Println("2. 传统数据库:精确匹配,不理解语义")
	fmt.Println("3. 向量数据库:理解语义,智能匹配")
	fmt.Println()
	
	// 创建向量数据库
	vectorDB := NewVectorDB()
	
	// 插入知识库
	fmt.Println("【构建知识库】")
	fmt.Println("正在将知识库向量化并存储...")
	ids := vectorDB.BatchInsert(knowledgeBase)
	fmt.Printf("✅ 成功插入 %d 条文档\n\n", len(ids))
	
	// 演示对比
	fmt.Println("【场景对比演示】")
	fmt.Println("------------------------------------------------")
	
	// 场景1:语义理解
	fmt.Println("\n场景1:用户问'什么时候发射'")
	fmt.Println("注意:知识库写的是'执行',不是'发射'!")
	fmt.Println()
	
	fmt.Println("❌ 传统数据库:")
	fmt.Println("   搜索关键词'发射' → 找不到(知识库只有'执行')")
	fmt.Println()
	
	fmt.Println("✅ 向量数据库:")
	fmt.Println("   理解语义,'发射' ≈ '执行'")
	results := vectorDB.Search("阿尔忒弥斯2号什么时候发射", 3)
	for i, result := range results {
		fmt.Printf("   %d. 相似度: %.4f - %s\n", i+1, result.Similarity, result.Content)
	}
	
	// 场景2:容量限制
	fmt.Println("\n------------------------------------------------")
	fmt.Println("\n场景2:知识库容量")
	fmt.Println()
	
	fmt.Println("❌ 提示词工程:")
	fmt.Println("   假设知识库有1MB内容")
	fmt.Println("   每次提问都要传1MB → prompt长度限制 → 超出token限制")
	fmt.Println("   成本:每次查询都要花1MB的token费用")
	fmt.Println()
	
	fmt.Println("✅ 向量数据库:")
	fmt.Printf("   当前知识库: %d 条文档\n", vectorDB.Count())
	fmt.Println("   每次只检索最相关的3条 → prompt只需几KB")
	fmt.Println("   成本:节省99%的token费用")
	
	// 场景3:智能匹配
	fmt.Println("\n------------------------------------------------")
	fmt.Println("\n场景3:用户问'用什么火箭'")
	fmt.Println()
	
	fmt.Println("❌ 传统数据库:")
	fmt.Println("   搜索关键词'火箭' → 可能找到很多不相关的")
	fmt.Println()
	
	fmt.Println("✅ 向量数据库:")
	results = vectorDB.Search("用什么火箭", 2)
	for i, result := range results {
		fmt.Printf("   %d. 相似度: %.4f\n", i+1, result.Similarity)
		fmt.Printf("      内容: %s\n", result.Content)
	}
	
	// CRUD演示
	fmt.Println("\n================================================")
	fmt.Println("【CRUD操作演示】")
	fmt.Println()
	
	fmt.Println("1. Create - 插入文档:")
	newID := vectorDB.Insert("新增:阿尔忒弥斯4号计划于2028年执行")
	fmt.Printf("   插入成功,ID: %d,当前总数: %d\n\n", newID, vectorDB.Count())
	
	fmt.Println("2. Read - 查询文档:")
	doc := vectorDB.GetByID(1)
	fmt.Printf("   ID 1 的内容: %s\n\n", doc.Content)
	
	fmt.Println("3. Update - 更新文档:")
	vectorDB.Update(newID, "更新:阿尔忒弥斯4号计划于2029年执行")
	fmt.Printf("   更新后的内容: %s\n\n", vectorDB.GetByID(newID).Content)
	
	fmt.Println("4. Delete - 删除文档:")
	vectorDB.Delete(newID)
	fmt.Printf("   删除后总数: %d\n\n", vectorDB.Count())
	
	// 总结
	fmt.Println("================================================")
	fmt.Println("【总结】")
	fmt.Println()
	fmt.Println("三种方法对比:")
	fmt.Println("┌─────────────┬──────────┬──────────┬──────────┐")
	fmt.Println("│   方法      │ 语义理解 │ 存储容量 │   成本   │")
	fmt.Println("├─────────────┼──────────┼──────────┼──────────┤")
	fmt.Println("│ 提示词工程  │    ✅    │    ❌    │    ❌    │")
	fmt.Println("│ 传统数据库  │    ❌    │    ✅    │    ✅    │")
	fmt.Println("│ 向量数据库  │    ✅    │    ✅    │    ✅    │")
	fmt.Println("└─────────────┴──────────┴──────────┴──────────┘")
	fmt.Println()
	fmt.Println("下一篇文章预告:")
	fmt.Println("将向量数据库集成到第一篇的tinyRAG系统,替换prompt全文本")
	fmt.Println("实现真正的RAG(检索增强生成)")
	fmt.Println()
	fmt.Println("✅ 演示完成!")
}

欢迎下载学习,动手实践!


系列文章

  1. Go + Ollama 构建tinyRAG应用:Prompt提示工程
  2. 用阿尔忒弥斯2号讲透文本向量化
  3. 一眼看穿相似度:余弦相似度原理详解
  4. 本文:手把手实现文本向量数据库
  5. 待更新:用向量数据库替换prompt,实现真正的RAG
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐