搜索增强生成(RAG,Retrieval-Augmented Generation)
大模型固有知识不是实时更新的,rag技术成了大模型内容生成时获取外部知识的重要手段。
向量数据库安装
这里我们使用一个比较好用的向量库:milvus ,采用在docker里启动的方式运行;
详情参考文档: 【docker运行单节点milvus】
curl -sfL https://raw.githubusercontent.com/milvus-io/milvus/master/scripts/standalone_embed.sh -o standalone_embed.sh
bash standalone_embed.sh start
启动后会开启一个9091的web端口,可以对数据进行查看和管理;访问链接格式如下:
http://192.168.217.101:9091/webui/

启动完毕后,通过pip安装milvus模块,然后可以在代码里使用其提供的api操作数据库
pip install -U pymilvus
安装向量库可视化工具
这里推荐一个向量库可视化工具 : attu, https://zilliz.com.cn/attu

向量库collection初始化
我们设计一个库,包含三个字段:id ,vector,line分别表示主键,向量值和原始文本;使用以下脚本进行collection的创建:
from pymilvus import MilvusClient, DataType
from a_collection_name import collection_name_v # 指定一个集合的名称,字符串即可
class VectorDBUtil:
def init_collection(self):
"""初始化向量数据库milvus集合"""
client = MilvusClient(uri="http://192.168.217.101:19530", db_name="default")
client.use_database("default")
client.drop_collection(collection_name_v)
schema = MilvusClient.create_schema(
auto_id=False,
enable_dynamic_field=True,
)
schema.add_field(
field_name="id",
datatype=DataType.INT64,
is_primary=True,
auto_id=True,
)
schema.add_field(field_name="vector", datatype=DataType.FLOAT_VECTOR, dim=1024)
schema.add_field(field_name="line", datatype=DataType.VARCHAR, max_length=1024)
index_params = client.prepare_index_params()
index_params.add_index(field_name="id", index_type="STL_SORT")
index_params.add_index(
field_name="vector", index_type="AUTOINDEX", metric_type="COSINE"
)
client.create_collection(
collection_name=collection_name_v, schema=schema, index_params=index_params
)
res = client.get_load_state(collection_name=collection_name_v)
print(f"collection 创建结果:{res}")
if __name__ == "__main__":
vectorDBUtil = VectorDBUtil()
vectorDBUtil.init_collection()
执行完毕后效果如下,新增了一个collection: rag_demo_0505

文本提取并计算向量,数据入库
我们这里采用 pdfminer 对pdf内文本进行提取,安装 pdfminer :
pip install pdfminer.six
提取pdf文本内容代码:
from pdfminer.high_level import extract_pages, extract_text # 按照页读
from pdfminer.layout import LTTextContainer # 文本标记
class FileUtil(object):
def extract_lines_from_file(self, file_path: str, min_line_length=1):
"""使用pdfminer读取文件内容
把一个LTTextContainer内容作为一个元素
"""
lines = []
for i, page_layout in enumerate(extract_pages(file_path)):
# print(f"当前页数:{i}")
for element in page_layout:
# 如果是文本
if isinstance(element, LTTextContainer):
line = element.get_text().replace("\n", "").replace("\r", "")
lines.append(line)
print("文件读取完毕")
return lines
def extract_page_lines_from_file(self, file_path: str, min_line_length=1):
"""使用pdfminer读取文件内容
把最多300文字作为一个元素
"""
page_lines = []
for i, page_layout in enumerate(extract_pages(file_path)):
page_line = ""
for element in page_layout:
# 如果是文本
if isinstance(element, LTTextContainer):
line = element.get_text().replace("\n", "").replace("\r", "")
page_line += line
if len(page_line) > 300:
page_lines.append(page_line)
page_line = ""
page_lines.append(page_line)
print("文件读取完毕")
return page_lines
if __name__ == "__main__":
# 读取文件内容
fileUtil = FileUtil()
lines = fileUtil.extract_page_lines_from_file(
file_path=r"F:\desktop20241201\软件架构设计/软件体系结构原理、方法与实践_第2版.pdf",
min_line_length=2,
)
ls = lines
for l in ls:
print(l)
print("\n")
文件内容入库
from c_file_read_util import *
from pymilvus import MilvusClient, DataType
from a_collection_name import collection_name_v
from openai import OpenAI
import os
os.environ["OPENAI_BASE_URL"] = "https://api.siliconflow.cn/v1"
# 填写自己硅基流动的api-key
os.environ["OPENAI_API_KEY"] = "sk-xxxx"
class VectorDBUtil:
def getVextor(self, line):
"""使用硅基流动的在线embedding模型
BAAI/bge-large-zh-v1.5 免费
"""
client = OpenAI()
response = client.embeddings.create(
model="BAAI/bge-large-zh-v1.5",
input=[line],
encoding_format="float",
# 模型可能不支持该参数
dimensions=512,
)
return response.data[0].embedding
def saveVextor(sels, line, vextor):
"""将文本和对应的向量保存数据库"""
client = MilvusClient(uri="http://192.168.217.101:19530", db_name="default")
client.use_database("default")
data = [
{
"vector": vextor,
"line": line,
}
]
res = client.insert(collection_name=collection_name_v, data=data)
# print(f"数据保存结果:{res}")
if __name__ == "__main__":
# 读取文件内容
fileUtil = FileUtil()
lines = fileUtil.extract_page_lines_from_file(
file_path=r"F:\desktop20241201\软件架构设计/系统架构设计师教程第二版可搜索.pdf",
min_line_length=2,
)
# print(lines)
vectorDBUtil = VectorDBUtil()
for line in lines:
vector = vectorDBUtil.getVextor(line=line)
vectorDBUtil.saveVextor(vextor=vector, line=line)
print(f"获得向量值成功")
代码执行完毕后,可以通过attu查看入库后向量值和对应的文本数据:

向量库文本召回、向量化
这部分内容主要是根据用户的输入,进行向量计算后将向量值作为检索条件,从向量库里获得语义最近的文档原始值;
代码如下:
import os
from a_collection_name import collection_name_v
from openai import OpenAI
from pdfminer.high_level import extract_pages, extract_text # 按照页读
from pdfminer.layout import LTTextContainer # 文本标记
from pymilvus import MilvusClient, DataType
from f_llm_query import *
os.environ["OPENAI_BASE_URL"] = "https://api.siliconflow.cn/v1"
# 替换成你的api-key
os.environ["OPENAI_API_KEY"] = "sk-xxxx"
class VectorDBUtil:
def searchVextor(self, line):
"""使用硅基流动的在线embedding模型"""
client = OpenAI()
response = client.embeddings.create(
model="BAAI/bge-large-zh-v1.5",
input=[line],
encoding_format="float",
# 模型可能不支持该参数
dimensions=512,
)
embedding = response.data[0].embedding
search_params_v = {
"metric_type": "COSINE", # 分区可以在params设置
"params": {},
}
milvusclient = MilvusClient(
uri="http://192.168.217.101:19530", db_name="default"
)
res = milvusclient.search(
collection_name=collection_name_v,
data=[embedding],
limit=3,
search_params=search_params_v,
output_fields=["line"],
)
return res
if __name__ == "__main__":
vectorDBUtil = VectorDBUtil()
res = vectorDBUtil.searchVextor(line="软件架构,它影响软件开发的哪些阶段")
print("从向量数据库获取到内容:")
for r in res:
print(r[0])
print(r[1])
print(r[2])
model = MyModel()
resp = model.query("软件架构,它影响软件开发的哪些阶段", str(r[0]))
print("rag回答:")
print(resp)
以上代码是从向量库中检索最多3条语义最近的内容;
提示词管理
prompt_template = """
你是一个问答机器人。
你的任务是根据下述给定的已知信息回答用户问题。
确保你的回复完全依据下述已知信息。不要编造答案。
如果下述已知信息不足以回答用户的问题,请直接回复"我无法回答您的问题"。
已知信息:
__ANSWER__
用户问:
__QUESTION__
请用中文回答用户问题。
"""
拼接提示词,调用大模型进行总结
封装一个请求大模型的工具类,包含了提示词的格式化,代码如下:
from openai import OpenAI
import os
class MyModel:
os.environ["OPENAI_BASE_URL"] = "https://api.siliconflow.cn/v1"
# 你的api-key
os.environ["OPENAI_API_KEY"] = "sk-xxx"
def query(self, query, answer):
"""
拼接提示词,并将提示词发送给大模型进行问答
这里采用在线大模型,后续采用本地部署的模型
"""
prompt_template = """
你是一个问答机器人。
你的任务是根据下述给定的已知信息回答用户问题。
确保你的回复完全依据下述已知信息。不要编造答案。
如果下述已知信息不足以回答用户的问题,请直接回复"我无法回答您的问题"。
已知信息:
__ANSWER__
用户问:
__QUESTION__
请用中文回答用户问题。
"""
messages_str = prompt_template.replace("__QUESTION__", query).replace(
"__ANSWER__", answer
)
# 格式化输入
messages = [{"role": "user", "content": messages_str}]
model = OpenAI()
resp = model.chat.completions.create(
model="Qwen/Qwen3-235B-A22B",
messages=messages,
temperature=0,
) # 模型输出的随机性,0 表示随机性最小
return resp.choices[0].message.content
最终输出
大模型会根据提示词输出问答结果

待优化:
1、文本切分优化,由于向量参数有长度限制,代码里是按照字数长短切割的
2、向量模型和文本生成模型是调用在线模型,后需要调整成本地部署
over~~~

3208

被折叠的 条评论
为什么被折叠?



