大家好,我是你们的技术伙伴。👋
在2026年的今天,大模型(LLM)已经不再是简单的聊天玩具,而是企业数字化转型的核心引擎。但你是否遇到过这些问题:
- 检索太慢? 数据量一大,相似度搜索就要等半天。
- Token太贵? 每次都把海量文档塞给大模型,成本高得吓人。
- 日志混乱? 系统出错了,却找不到具体的报错信息。
今天,我将结合Python 、Milvus 、Redis 和Ollama ,带你构建一套高性能RAG问答系统的底层基石。我们将通过代码实战,解决上述所有痛点。
核心内容概览:
- Milvus向量数据库:掌握集合管理、索引优化与混合检索。
- Redis缓存:实现查询结果的秒级返回,防御缓存穿透。
- Ollama本地模型:低成本、高隐私的模型调用。
- Logging日志系统:规范化日志记录,让Bug无处遁形。
🧠 第一部分:Milvus向量数据库------AI的"记忆中枢"
Milvus是专为AI设计的向量数据库,它能帮我们解决海量非结构化数据的相似度检索问题。
1. 数据库与集合管理
首先,我们需要连接Milvus并创建一个数据库。附件中的代码展示了如何优雅地处理数据库的"存在即使用,不存在即创建"的逻辑。
ini
from pymilvus import MilvusClient
def operate_db():
# 连接本地Milvus服务
client = MilvusClient(uri="http://localhost:19530")
# 检查并创建数据库
databases = client.list_databases()
if "milvus_demo" not in databases:
client.create_database(db_name="milvus_demo")
print('创建数据库成功!')
else:
client.using_database(db_name="milvus_demo")
print('使用数据库成功!')
return client
client = operate_db()
2. 深入Schema设计与索引
在创建集合(表)时,Schema的设计至关重要。我们需要定义主键、向量字段以及动态标量字段。
代码实战:创建支持动态字段的集合
ini
def operate_collection():
# 1. 创建Schema,开启动态字段(允许插入未定义的额外字段)
schema = client.create_schema(auto_id=False, enable_dynamic_field=True)
# 2. 添加核心字段
schema.add_field(field_name='id', datatype=DataType.INT64, is_primary=True)
schema.add_field(field_name='vector', datatype=DataType.FLOAT_VECTOR, dim=5)
schema.add_field(field_name='scalar', datatype=DataType.VARCHAR, max_length=256)
# 3. 创建集合
client.create_collection(collection_name='demo_v1', schema=schema)
print('创建集合成功!')
3. 混合检索(Hybrid Search)------ 进阶必杀技
单纯的向量检索可能不够精准,我们需要结合标量过滤(如时间范围、类别)进行混合检索。
代码实战:多向量字段混合检索
ini
from pymilvus import AnnSearchRequest, WeightedRanker
def complex_query():
# 场景: 同时根据"电影特征向量"和"电影海报向量"进行检索
# 1. 配置第一个向量字段的搜索参数
request_1 = AnnSearchRequest(
data=[[0.889, 0.370, ...]], # 查询向量
anns_field="filmVector",
param={"metric_type": "L2"},
limit=2
)
# 2. 配置第二个向量字段的搜索参数
request_2 = AnnSearchRequest(
data=[[0.025, 0.006, ...]],
anns_field="posterVector",
param={"metric_type": "COSINE"},
limit=2
)
# 3. 组合请求并设置权重 (电影特征70%, 海报特征30%)
reqs = [request_1, request_2]
ranker = WeightedRanker(0.7, 0.3)
# 4. 执行混合检索
outputs = client.hybrid_search(
collection_name='demo_v3',
reqs=reqs,
ranker=ranker,
limit=2
)
return outputs
⚡ 第二部分:Redis缓存------防御"高并发"的利剑
在RAG系统中,很多问题(如"介绍一下公司")是高频重复的。如果不加缓存,每次都要走一遍检索+大模型生成的流程,既慢又贵。
1. Redis连接与基础操作
附件中封装了一个非常实用的RedisClient类,它处理了连接异常和JSON序列化。
代码实战:封装Redis操作类
python
import redis
import json
from base import Config, logger
class RedisClient:
def __init__(self):
try:
# 连接Redis
self.client = redis.StrictRedis(
host=Config.REDIS_HOST,
password=Config.REDIS_PASSWORD,
decode_responses=True
)
logger.info('Redis加载成功!')
except redis.RedisError as e:
logger.error(f'Redis加载失败: {e}!')
raise
def set_data(self, key, value):
"""存储数据,自动序列化为JSON"""
try:
self.client.set(key, json.dumps(value, ensure_ascii=False))
logger.info(f'存储数据到Redis: {key}')
except redis.RedisError as e:
logger.error(f'Redis存储失败: {e}')
def get_data(self, key):
"""获取数据,自动反序列化"""
try:
data = self.client.get(key)
return json.loads(data) if data else None
except redis.RedisError as e:
logger.error(f'Redis获取失败: {e}')
return None
2. 缓存击穿与穿透防御
在实际应用中,我们需要为特定格式的查询(如answer:{query})设置缓存。
代码实战:带缓存的问答逻辑
python
def get_answer(self, query):
# 1. 先查缓存
cache_key = f'answer:{query}'
cached_answer = self.get_data(cache_key)
if cached_answer:
logger.info(f'命中缓存: {query}')
return cached_answer
# 2. 缓存未命中,执行昂贵的RAG检索...
# (此处省略检索逻辑)
# result = rag_search(query)
# 3. 将结果写入缓存,设置过期时间(例如300秒)
# self.set_data(cache_key, result)
# return result
🤖 第三部分:Ollama与本地模型------低成本的"大脑"
相比于调用昂贵的云端API,Ollama让我们可以在本地运行大模型,既保护隐私又降低成本。
代码实战:Ollama的Python API调用
ini
import ollama
# 聊天式调用
response = ollama.chat(
model='qwen2:1.5b',
messages=[{'role': 'user', 'content': '为啥天空是蓝色的?'}]
)
print(response.message.content)
# 生成式调用
response = ollama.generate(model='qwen2:1.5b', prompt='写一首诗')
print(response)
📝 第四部分:Logging日志系统------开发者的"眼睛"
一个成熟的系统必须有完善的日志。附件中的日志配置非常规范,支持同时输出到控制台和文件。
代码实战:配置双输出日志
ini
import logging
import os
def setup_logger(name, log_file='app.log'):
# 1. 确保日志目录存在
os.makedirs(os.path.dirname(log_file), exist_ok=True)
# 2. 创建记录器
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
# 3. 创建处理器 (控制台 + 文件)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
file_handler = logging.FileHandler(log_file, encoding="utf-8")
file_handler.setLevel(logging.DEBUG)
# 4. 定义格式
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s")
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# 5. 添加处理器
if not logger.hasHandlers():
logger.addHandler(console_handler)
logger.addHandler(file_handler)
return logger
# 使用
logger = setup_logger("AI_RAG")
logger.info("系统启动成功")
🏁 结语
通过上述四个部分的实战,我们构建了一个企业级RAG系统的雏形。我们不仅掌握了Milvus的混合检索,还利用Redis实现了缓存加速,并规范了日志系统。
给读者的建议:
- 索引调优 :Milvus的索引参数(如
nlist)直接影响检索速度和精度,需要根据数据量调整。 - 缓存策略:对于高频低变的问答,Redis是提升QPS的神器。
- 日志分级:在生产环境中,合理使用DEBUG、INFO、ERROR级别日志,能帮你快速定位问题。
如果你觉得这篇文章对你有帮助,希望点赞、收藏、关注!你的支持是我持续输出硬核内容的最大动力!