👕

基于文档的LLM回复系统搭建

 
 

总览

v1.0:
用户提出Query:产品的优点是什么。 AI 回答Answer:
过程:
  1. 收集原始数据 rawData (txt,doc,ppt,excel…)
  1. 将原始数据切割成 若干trunk,得到知识库。(为了解决原始文档过大,不能直接放入到 prompt 上下文里,以及清洗数据)
  1. 寻找和 query 相关性较大的 trunk
    1. trunk 和 对应的向量
    2. 计算相关性(Q 的向量和trunk 的向量)
    3. 从而找到相关性较高的文本
  1. 将 Q 的string 和 trunk 的 string(不是向量)放入到 prompt
  1. 让大模型回复。
 
所以问题的关键是,如何进行相似性计算。
Q:可以直接比较token 重合度吗?
A:不行。因为很表面。要考虑到近义词,语境之类的,比如 手机,电话,两者是相似的,但是单词都不一样。所以要比较的是 Query 和 trunk 之间的真正含义。
Q:如何表达真正的意思? A:向量
 
难点 1:一个 string 计算 vector
💡
计算vector 的方法,对结果的影响是很大的。(什么样的向量 语义化
难点 2:vector1 和 vector2 计算相似度
难点 3:Q 的 vector 和 所有trunk 的 vector 比较计算相似性时,计算复杂度太高:O(n),所以需要向量数据库快速查询。
 

步骤细节

如何从文本——→trunk?

  • 根据句子来切分。
    • 每个句子切分。
    • nltk sent_tokenize
  • 根据字符数来切分
    • 每 100 个字符切割一次。
    • nltk
  • 按固定字符数,但结合 overlapping window
    • 每 100 个字符切割一次
    • eg 每 5 个字符,2 的 overlapping:(1,2,3,4,5)(4,5,6,7,8)(7,8,9,10,11)
  • 递归方法:RecursiveCharacterTextSplitter(来自 langChain)
    • 常用,首选
  • 根据语义切割
    • 难度大,要跑很多机器学习的算法。
 
eg:
常用工具
英文:
  • nltk
 
 

文本向量化(trunk—>向量)

常用的方法:
  1. openai 的 embedding model(调接口)
  1. 同理 Claude,gemini,等等
 

向量间的相似度计算

最常用的:余弦相似度(-1,1)
这是最常用的方法之一。它测量两个向量之间的夹角的余弦值。余弦相似度的值范围在-1到1之间,1表示方向相同,0表示正交,-1表示方向相反。
计算方法:
cosine_similarity = (A · B) / (||A|| * ||B||)
其中A·B是向量的点积,||A||和||B||分别是向量A和B的欧几里得范数。
 
向量点积的计算:
向量点积(Dot Product):
  • 向量点积(Dot Product):
向量点积,也称为标量积或内积,是两个向量的对应元素相乘后求和的结果。对于两个n维向量 A = (a1, a2, ..., an) 和 B = (b1, b2, ..., bn),它们的点积计算如下:
A · B = a1b1 + a2b2 + ... + anbn
def dot_product(A, B): return sum(a * b for a, b in zip(A, B)) # 使用NumPy库可以更简洁地计算: import numpy as np A = np.array([a1, a2, ..., an]) B = np.array([b1, b2, ..., bn]) dot_product = np.dot(A, B)
 
欧几里得范数(Euclidean Norm): 欧几里得范数,也称为L2范数,是向量的长度或大小的度量。它是向量各元素平方和的平方根。 对于向量 A = (a1, a2, ..., an),其欧几里得范数计算如下: ||A|| = √(a1^2 + a2^2 + ... + an^2)
import math def euclidean_norm(A): return math.sqrt(sum(a**2 for a in A)) #numpy import numpy as np A = np.array([a1, a2, ..., an]) norm = np.linalg.norm(A)
# 余弦相似度计算 import numpy as np def cosine_similarity(A, B): dot_product = np.dot(A, B) norm_A = np.linalg.norm(A) norm_B = np.linalg.norm(B) return dot_product / (norm_A * norm_B)