news 2026/7/21 13:18:58

Qdrant向量数据库实战:从零部署到RAG生产落地

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qdrant向量数据库实战:从零部署到RAG生产落地

1. 项目概述:为什么是 Qdrant,而不是别的向量数据库?

你点开这篇内容,大概率不是在搜索引擎里漫无目的地闲逛,而是正被一个具体问题卡住:手头有个 RAG 应用要上线,文档切片后生成了上万条文本向量,本地用 SQLite 存 embedding 表查相似度,一搜就卡死;试过 Elasticsearch 加 dense_vector 字段,结果召回率忽高忽低,调试半天发现是默认的 L2 距离和你的 embedding 模型根本不匹配;甚至有同事提议直接上 Milvus,结果 Docker Compose 起不来,日志里全是“segment not loaded”——你盯着终端发呆,心里默念:“我只是想让一段用户提问,快速找到最相关的三段知识库原文,怎么这么难?”

这就是我写这本书、也是你读这篇实操指南的起点。Qdrant 不是凭空冒出来的“新宠”,它是在真实工程场景里被反复验证过的务实选择。它不像某些数据库把“支持 HNSW”“支持 IVF-PQ”当宣传语堆在首页,而是把“默认开启压缩索引”“写入时自动触发量化重平衡”“payload 过滤与向量搜索原生融合,不走两阶段 pipeline”这些真正影响线上延迟和内存占用的细节,藏在文档第 7 节的配置项里。我选它,不是因为它名字好听,而是因为在我用 OpenAI 的text-embedding-3-large(3072 维)跑满 500 万条法律文书向量的压测中,它在 64GB 内存的云服务器上,P95 响应时间稳定在 87ms,而同等配置下,另一个主流方案在 200 万条后就开始频繁 GC,P95 拉到 320ms 以上。

更关键的是它的设计哲学:Qdrant 把“向量”和“业务数据”看作不可分割的一体,而不是先存向量、再用 ID 去关联另一张表。它的point概念天然包含vector + payload + id,这意味着你搜索时加一句filter: {"status": "published", "category": "finance"},数据库底层会直接在索引扫描阶段就过滤掉不匹配的点,而不是像传统方案那样先召回 1000 个 ID,再回表查 status 字段,最后筛出 3 个。这个差异,在你处理带权限、带状态、带时效性的业务数据时,就是“能上线”和“上线即告警”的区别。所以,这篇内容不讲虚的架构图,只带你亲手拧紧每一颗螺丝:从注册账号那一刻起,到在 Jupyter 里敲出第一行client.search()并看到返回结果,中间所有可能绊倒你的坑,我都替你踩过了。

2. 核心细节解析与实操要点:环境、依赖与安全边界的建立

2.1 云服务 vs 本地部署:为什么这次我们坚决不上 Docker?

很多教程一上来就甩出docker run -p 6333:6333 -v $(pwd)/qdrant_storage:/qdrant/storage qdrant/qdrant这行命令,然后说“搞定”。但现实是,当你在公司内网开发,IT 部门明确禁止任何未经白名单的 Docker 镜像拉取;或者你用的是 M1 Mac,Docker Desktop 启动后风扇狂转,qdrant进程 CPU 占用长期 180%;又或者你只是想在咖啡馆用笔记本快速验证一个想法,却要先花 20 分钟配好 Docker 环境——这些都不是“障碍”,而是真实的成本。Qdrant Cloud 的免费层(500MB 存储 + 100M 向量/月)恰恰卡在这个临界点:它足够让你完成从数据建模、embedding 生成、到相似性搜索的全链路验证,又不会因为资源超限突然中断你的调试流程。

提示:免费层的“100M 向量/月”是指向量条数,不是维度。比如你存 10 万条 3072 维的向量,只消耗 0.1M 配额。我实测过,用text-embedding-3-small(1536 维)处理 50 万条新闻摘要,一个月才用掉 12M 配额,完全够用。

但这里有个极易被忽略的细节:集群地域选择。Qdrant Cloud 控制台创建集群时,默认给你分配的是us-east-1(美国东部)。如果你的 Python 服务部署在国内华东区,那么每次client.search()请求都要跨太平洋绕一圈,实测平均增加 180ms 网络延迟。解决方案很简单:在创建集群页面,把 Region 下拉框手动切换成ap-northeast-1(东京)或ap-southeast-1(新加坡)。别小看这一步,它直接决定了你后续所有搜索请求的基线延迟。我见过太多团队,花了两周优化 embedding 模型和索引参数,最后发现 P99 延迟瓶颈其实在 DNS 解析和跨境路由上。

2.2 依赖版本锁死:为什么必须严格限定qdrant-client==1.9.0

你可能会想:“不就是个 SDK 吗?pip install qdrant-client 最新版不就行了?” 错。Qdrant 的 API 在 v1.8.x 到 v1.9.x 之间做了一次静默升级:create_collection方法的vectors_config参数,从接受一个VectorParams对象,变成了必须传入一个Dict[str, VectorParams]。这意味着,如果你用qdrant-client==1.10.0,而代码里还写着client.create_collection(..., vectors_config=collection_config),运行时会直接抛TypeError: create_collection() got an unexpected keyword argument 'vectors_config'。更糟的是,这个错误不会在 import 时出现,而是在你调用create_collection的那一刻才爆发,调试成本极高。

我锁死1.9.0的另一个原因是它的batch写入行为。在1.8.x版本中,upsert一批 100 条向量,SDK 会默认拆成 10 个 HTTP 请求(每批 10 条);而1.9.0改为了单请求批量提交,配合 Qdrant Cloud 后端的流式解析,吞吐量提升 3.2 倍。这个优化对你的数据导入速度影响巨大。举个例子:导入 10 万条向量,1.8.x要 4 分 12 秒,1.9.0只要 1 分 18 秒。这不是理论值,是我用同一台机器、同一份数据、同一份脚本实测的结果。所以,pip install命令里的每一个==x.y.z都不是教条,而是我在生产环境里用真金白银换来的经验。

2.3 环境变量与.env文件:为什么export命令在 Jupyter 里根本不起作用?

这是新手最容易栽跟头的地方。你兴冲冲地在终端里敲下export QDRANT_API_KEY=xxxexport QDRANT_URL=https://xxx.qdrant.cloud,然后打开 Jupyter Notebook,运行import os; print(os.getenv('QDRANT_API_KEY')),结果输出是None。为什么?因为export设置的环境变量只对当前 shell 进程及其子进程有效。当你在终端里启动jupyter notebook,Jupyter 是那个 shell 的子进程,它能继承变量;但当你在 Jupyter 的 Web 界面里新建一个 notebook,再运行 Python 代码,这段代码运行在 Jupyter 内核(一个独立的 Python 进程)里,它并不知道你之前在 shell 里export过什么。

解决方案就是.env文件。但很多人以为只要echo "KEY=VALUE" > .env就完事了。错。python-dotenv库默认只加载当前工作目录下的.env,且它不会递归向上查找。这意味着,如果你的 notebook 文件放在/home/user/rag_project/notebooks/step1_setup.ipynb,而.env文件放在/home/user/rag_project/.env,那么load_dotenv('./.env')会失败,因为./.env指的是/home/user/rag_project/notebooks/.env。正确做法是:在 notebook 的第一行,用绝对路径加载:

from pathlib import Path from dotenv import load_dotenv # 获取 notebook 所在目录的父目录(即项目根目录) root_dir = Path().resolve().parent load_dotenv(root_dir / ".env")

这样,无论 notebook 放在notebooks/还是experiments/子目录下,都能正确加载根目录的.env。这个小技巧,能帮你省下至少半小时的“为什么我的 API KEY 总是 None”的无效调试。

3. 实操过程与核心环节实现:从零构建第一个可搜索的集合

3.1 创建集群与获取凭证:控制台操作中的三个致命陷阱

登录 Qdrant Cloud 控制台后,创建集群看似简单,但有三个地方极易出错,我用加粗标出:

  1. 集群名称不能含下划线以外的特殊字符:你可能会想给集群起名my-rag-app-v1,结果点击“Create Cluster”后,页面毫无反应,控制台 Network 标签页显示 400 错误,提示Invalid cluster name: must match regex ^[a-z0-9][a-z0-9_-]{1,61}[a-z0-9]$。这是因为 Qdrant 的集群名规则严格遵循 DNS 子域名规范:只能小写字母、数字、短横线(-)和下划线(_),且不能以短横线开头或结尾。所以Practical_Retrieval_Augmented_Generation是合法的(全是字母和下划线),但my-rag-app-v1中的短横线就违规了。解决方案:全部用下划线连接,如my_rag_app_v1

  2. “Get API Key”按钮是有时效性的:这个红色按钮在你首次创建集群后才会出现,且只出现一次。如果你没点,或者点了但没复制全,它就会永远消失。后续你无法在 UI 上再次生成新的 API Key,只能去 “Settings” -> “API Keys” 页面手动创建。但新创建的 Key 默认是read权限,而create_collection需要admin权限。所以,第一次看到那个红按钮,务必立刻复制,粘贴到你的.env文件里,并用#注释说明这是adminKey。我建议你复制两份,一份存.env,一份存密码管理器,标题注明“Qdrant Cloud Admin Key - my_rag_app_v1”。

  3. Cluster URL 的格式陷阱:你在控制台复制的 URL 看起来是https://abcdefg-12345.qdrant.cloud,这没错。但当你把它写进.env文件时,千万别加末尾的斜杠/。如果写成QDRANT_URL=https://abcdefg-12345.qdrant.cloud/qdrant-client会在内部拼接时变成https://.../collections,导致最终请求地址是https://...//collections(双斜杠),Qdrant 后端会返回 404。这个错误极其隐蔽,因为client.get_collections()可能返回空列表,而不是报错,你会误以为集合没创建成功,其实是因为请求根本没发对地方。

3.2 初始化客户端与验证连接:如何确认你真的连上了?

QdrantClient的初始化代码只有两行,但背后有深意:

from qdrant_client import QdrantClient import os client = QdrantClient( url=os.getenv('QDRANT_URL'), api_key=os.getenv('QDRANT_API_KEY') )

这里的关键是url参数。它必须是完整的 HTTPS 地址,不能是裸域名。比如qdrant-cloud.com是错的,必须是https://your-cluster-id.qdrant.cloudqdrant-client库不会自动补全https://,也不会帮你加端口(Qdrant Cloud 默认是 443,不用显式写)。

初始化后,不要急着create_collection,先做两件事验证连接:

  1. 检查健康状态

    try: client.health() print("✅ Qdrant Cloud 连接正常") except Exception as e: print(f"❌ 连接失败: {e}") # 这里可以打印更详细的错误,比如网络超时还是认证失败
  2. 列出所有集合(即使为空)

    collections = client.get_collections() print(f"当前集群中有 {len(collections.collections)} 个集合") # 输出应该是 0,证明你能成功调用 API,且没有权限问题

如果health()报错ConnectionError,八成是网络问题或 URL 写错了;如果get_collections()报错Unexpected response status code: 401,那就是 API Key 复制错了或权限不足。这两个简单的验证步骤,能帮你把 80% 的连接类问题,在写第一行业务代码前就定位清楚。

3.3 设计并创建第一个集合:维度、距离与命名的实战决策

创建集合 (create_collection) 是整个流程的“心脏”,它的参数设计直接决定了你后续搜索的准确性和效率。我们来逐个拆解models.VectorParams的关键字段:

  • size=1536:这是向量的维度。我这里用1536,而不是原文提到的3072,原因很实际:text-embedding-3-large确实是 3072 维,但它在 1536 维版本(text-embedding-3-small)上的性能损失微乎其微(在标准 MTEB 评测集上,平均下降仅 0.8%),但内存占用和索引构建时间直接减半。对于学习和验证阶段,优先保证流畅性,而非理论上的最高精度。等你确定模型和数据流后,再平滑升级到large版本。

  • distance=models.Distance.COSINE:这是距离度量方式。为什么选余弦相似度?因为你的 embedding 模型(无论是 OpenAI 还是sentence-transformers)在训练时,目标函数就是最大化同类样本的余弦相似度。用欧氏距离(L2)去搜索,相当于用一把尺子去量两个方向,结果必然失真。你可以做个实验:用同一个 embedding 模型对“苹果”和“香蕉”生成向量,计算它们的余弦相似度(约 0.82)和欧氏距离(约 1.2),再对“苹果”和“汽车”计算,余弦相似度会降到 0.15,而欧氏距离可能还是 1.3。余弦值的变化范围(0~1)比欧氏距离(0~∞)更能反映语义相关性。

  • on_disk=True(隐藏参数):这个参数在官方文档里藏得比较深,但它对云环境至关重要。默认情况下,Qdrant 会把向量索引加载到内存(RAM)中。在免费层,你的集群只有 1GB 内存,如果向量数据量稍大(比如超过 50 万条),内存很快就会爆。加上on_disk=True,Qdrant 会将索引文件存储在磁盘上,并采用内存映射(mmap)技术按需加载,内存占用能降低 60% 以上。虽然单次搜索会慢几毫秒,但换来的是稳定性——你的服务不会因为内存 OOM 而被云平台强制重启。

创建集合的完整代码如下,包含了上述所有最佳实践:

from qdrant_client.http import models # 定义向量参数:1536维,余弦距离,索引存磁盘 vector_config = models.VectorParams( size=1536, distance=models.Distance.COSINE, on_disk=True # 关键!防止内存溢出 ) # 创建集合 client.create_collection( collection_name="p_rag_series_1", vectors_config=vector_config, # 可选:为 payload 字段预定义索引,加速过滤 # payload_schema={"category": models.PayloadSchemaType.KEYWORD} ) # 验证创建成功 collections = client.get_collections() assert len(collections.collections) == 1 print(f"✅ 集合 '{collections.collections[0].name}' 创建成功")

3.4 理解PointPayload:为真实业务数据建模的第一步

Point是 Qdrant 的核心数据单元,它由三部分组成:id(唯一标识)、vector(浮点数组)、payload(任意 JSON)。很多初学者只关注vector,把payload当成可有可无的备注。这是巨大的误区。payload是你把向量“翻译”回业务语言的桥梁。

假设你要构建一个客服知识库搜索。一条原始数据是:

{ "article_id": "KB-2024-001", "title": "如何重置您的账户密码?", "content": "请访问登录页面,点击‘忘记密码’链接,输入您的注册邮箱...", "category": "account_management", "status": "published", "updated_at": "2024-04-28T10:15:00Z" }

你不会把整段content直接喂给 embedding 模型。正确的做法是:用sentence-transformers/all-MiniLM-L6-v2title + content进行分块(chunking),比如切成 256 字符的片段,每个片段生成一个向量。那么,一个Pointpayload应该长这样:

{ "article_id": "KB-2024-001", "chunk_id": 0, "title": "如何重置您的账户密码?", "content_chunk": "请访问登录页面,点击‘忘记密码’链接...", "category": "account_management", "status": "published" }

注意两点:

  1. article_idchunk_id组合起来,能唯一确定这个向量来自哪篇文章的哪个片段;
  2. categorystatus是未来搜索时的过滤条件。比如,客服机器人只会搜索status: "published"category: "account_management"的向量,避免把草稿或已下线的知识返回给用户。

payload的设计,本质上是你在定义一套业务元数据 Schema。它不参与向量计算,但决定了你搜索结果的业务可用性。我建议你在创建集合前,先用纸笔画出你的payload结构,问自己:“用户搜索时,哪些条件是必须过滤的?哪些字段是必须返回给前端展示的?” 答案就是你的payload字段列表。

4. 常见问题与排查技巧实录:那些文档里不会写的“血泪教训”

4.1 问题速查表:高频报错与精准定位

错误现象可能原因排查命令/步骤解决方案
ConnectionRefusedError: [Errno 111] Connection refused1.QDRANT_URL写成了http://而非https://
2. 集群尚未完全启动(创建后需 1-2 分钟)
curl -I https://your-cluster-id.qdrant.cloud确保 URL 以https://开头;等待 2 分钟后重试
Unexpected response status code: 4011.QDRANT_API_KEY复制不全(末尾有空格)
2. Key 已被删除或权限降级
echo "'${QDRANT_API_KEY}'" | hexdump -C(检查空格)重新从控制台复制 Key,用trim()函数清理空格
ValidationError: 1 validation error for VectorParams size\n field requiredvectors_config参数未传入,或传入了Noneprint(type(collection_config))确保collection_configVectorParams实例,不是None
ResponseError: Not found: Collection p_rag_series_11. 集合名拼写错误(大小写敏感)
2. 在错误的集群上操作
client.get_collections()检查get_collections()返回的集合名,确保完全一致
SearchResult返回空列表,但count显示有数据1.searchlimit设为 0
2.filter条件过于严格,无匹配项
client.count(collection_name="p_rag_series_1")先用count()确认数据存在,再逐步放宽filter

4.2 独家避坑技巧:来自生产环境的“老司机”经验

技巧一:用count()代替get_collections()做数据存在性校验
很多教程教你在插入数据后,用client.get_collections()看集合是否存在。但这只能证明集合存在,不能证明里面有数据。更可靠的做法是:在upsert数据后,立即执行client.count(collection_name="your_collection")。如果返回的count是 0,说明数据根本没写进去,问题一定出在upsert步骤(比如points参数格式错误)。这个技巧帮我快速定位过一次payload中嵌套了datetime对象(JSON 不支持),导致整个 batch 写入静默失败的问题。

技巧二:upsert前,先用validate_batch()做本地校验
qdrant-client提供了一个隐藏的工具函数qdrant_client.models.validate_batch。它能在数据发送到服务器前,就检查points列表里每个PointStructvector维度是否一致、id类型是否正确、payload是否为合法 JSON。虽然它不检查业务逻辑,但能拦截 90% 的格式类错误。用法很简单:

from qdrant_client.models import PointStruct, validate_batch points = [ PointStruct(id=1, vector=[0.1, 0.2, 0.3], payload={"text": "hello"}), PointStruct(id=2, vector=[0.4, 0.5], payload={"text": "world"}) # ❌ 维度不一致! ] try: validate_batch(points) # 这里会抛出 ValidationError except Exception as e: print(f"数据校验失败: {e}")

技巧三:为payload字段建立索引,让过滤飞起来
默认情况下,Qdrant 对payload字段是“按需解析”的,即搜索时临时从 JSON 中提取字段值。如果你的payload很大(比如存了整段 HTML),或者你频繁按某个字段(如category)过滤,性能会急剧下降。解决方案是:在创建集合时,为关键字段显式声明索引:

client.create_collection( collection_name="p_rag_series_1", vectors_config=vector_config, # 为 category 字段创建 keyword 索引,加速等值过滤 payload_schema={ "category": models.PayloadSchemaType.KEYWORD, "status": models.PayloadSchemaType.KEYWORD } )

这个操作只需在创建集合时做一次,之后所有对该字段的filter查询,都会走索引,速度提升 5-10 倍。我在线上环境用它把一个category == "billing"的查询,从 120ms 优化到了 18ms。

技巧四:delete_collection不是“删除”,而是“标记为待回收”
你以为client.delete_collection("p_rag_series_1")是瞬间清空?错。Qdrant 的删除是异步的,它只是把集合标记为deleted,真正的物理删除会在后台任务中进行。这意味着,你刚删完,立刻client.get_collections(),可能还能看到它(状态为deleted)。更关键的是,删除操作不可逆,且不释放配额。免费层的 500MB 存储空间,是按“已分配”计算的,不是按“当前使用”。所以,与其频繁删库重建,不如用client.delete()配合filter清空数据,或者直接创建新集合。这是我踩过最深的坑:连续删了 5 个测试集合,结果发现配额还剩 10MB,根本没法建新集合,最后只能联系支持团队手动清理。

4.3 实战复盘:一次典型的“从崩溃到稳定”的调试全过程

上周,我帮一个创业团队调试他们的 RAG 服务。现象是:服务启动后,前 10 次search()请求都正常,第 11 次开始,所有请求都卡住,10 秒后超时,日志里只有ReadTimeout。他们已经检查了网络、API Key、集合名,一切看起来都没问题。

我的排查路径是:

  1. 先看基础连接client.health()返回正常,排除网络和认证问题。
  2. 再看资源水位client.get_collections()成功,但client.get_collection("main")返回的statusyellow(警告),不是green(健康)。点开控制台,发现磁盘使用率 98%。
  3. 定位罪魁祸首:他们用upsert导入了 200 万条向量,但没设on_disk=True,所有索引都塞进了 1GB 内存,Qdrant 为了保命,把大量数据刷到磁盘临时文件,导致 I/O 爆满。
  4. 终极解决:不是扩容,而是重建。我让他们:
    • 创建一个新集合main_v2,参数里明确加上on_disk=True
    • scrollAPI 分批导出旧集合的数据;
    • batch方式导入到新集合;
    • 更新服务代码,指向新集合名。

整个过程花了 35 分钟,服务恢复。这个案例印证了一个真理:向量数据库的稳定性,80% 取决于初始配置,而不是后期优化。你花 5 分钟在create_collection时多写一个on_disk=True,能省下后面 5 小时的深夜救火。

5. 进阶概念精要:命名向量与多租户的务实理解

5.1 命名向量(Named Vectors):不是炫技,而是解决混合模态的刚需

“一个集合存多种向量”听起来像高级功能,但它的诞生源于一个朴素需求:用户搜索时,不关心你是用文本还是图片匹配的,他只想要最相关的结果。想象一个电商搜索场景:用户输入“红色连衣裙”,系统需要同时考虑:

  • 文本描述的语义(商品标题、详情页文字);
  • 图片的视觉特征(主图、细节图的颜色、纹理、款式)。

如果强行把文本向量和图片向量都塞进同一个vector字段,维度怎么统一?用text-embedding-3-small(1536 维)和clip-ViT-B-32(512 维)硬拼?那 cosine 相似度就失去了数学意义。命名向量给出了优雅解法:

# 创建集合时,定义两个命名向量 client.create_collection( collection_name="ecommerce_products", vectors_config={ "text": models.VectorParams(size=1536, distance=models.Distance.COSINE), "image": models.VectorParams(size=512, distance=models.Distance.COSINE) } ) # 插入数据时,指定向量名 client.upsert( collection_name="ecommerce_products", points=[ models.PointStruct( id=1, vector={ "text": [0.1, 0.2, ..., 0.1536], # 1536维文本向量 "image": [0.01, 0.02, ..., 0.512] # 512维图片向量 }, payload={"product_id": "P-001", "type": "dress"} ) ] )

搜索时,你可以选择只用文本向量:

client.search( collection_name="ecommerce_products", query_vector=("text", [0.15, 0.25, ...]), # 指定用"text"向量搜索 limit=5 )

也可以用图片向量:

client.search( collection_name="ecommerce_products", query_vector=("image", [0.015, 0.025, ...]), # 指定用"image"向量搜索 limit=5 )

甚至可以做混合搜索(Qdrant 1.9+ 支持):

client.search( collection_name="ecommerce_products", query_vector={ "text": [0.15, 0.25, ...], "image": [0.015, 0.025, ...] }, # 指定每个向量的权重 search_params=models.SearchParams(hybrid_fusion=models.HybridFusion.RELATIVE_SCORE_FUSION) )

这不再是“能不能”的问题,而是“如何设计才能让业务更灵活”的问题。命名向量让你的集合具备了模态无关性,为未来接入音频、视频、3D 模型等新数据源,铺平了道路。

5.2 多租户(Multitenancy):隔离与成本的永恒权衡

多租户的本质,是数据隔离策略的选择题。Qdrant 提供两种模式,没有绝对优劣,只有场景适配:

  • 单集合 + Payload 过滤(推荐给大多数 SaaS)
    所有租户(客户)的数据都存在一个集合里,靠payload中的tenant_id字段区分。优点是资源利用率高、运维简单;缺点是必须在每一次searchupsertdelete时,都显式带上filter: {"tenant_id": "acme_corp"}。漏写一个 filter,就是严重的数据泄露事故。所以,必须把 filter 封装进你的 DAO 层,而不是散落在业务代码里。我通常会写一个TenantAwareQdrantClient类,所有方法都自动注入tenant_id

  • 多集合(推荐给强合规要求场景)
    每个租户一个独立集合,如acme_corp_databeta_inc_data。优点是物理隔离,审计清晰;缺点是集合数量多了,Qdrant 的元数据管理开销会上升,且免费层的 500MB 配额是按集合分配的,10 个租户就要预留 5GB,远超免费额度。所以,除非你的客户合同里白纸黑字写了“数据必须物理隔离”,否则单集合是更务实的选择。

注意:Qdrant 的“多租户”目前不支持集合级别的 RBAC(基于角色的访问控制)。也就是说,你给了一个 API Key,它就有权操作该集群下的所有集合。因此,永远不要把生产集群的 admin Key 交给任何第三方或前端应用。正确的做法是:后端服务用自己的 admin Key 操作,前端只通过你的 API 获取数据,Key 永远不出服务器。

我个人在实际使用中发现,Qdrant 的设计理念非常“工程师友好”:它不追求功能大而全,而是把每个核心能力(向量搜索、payload 过滤、命名向量、多租户)都做到极致稳定。你不需要记住 20 个配置项,只要吃透create_collection的那几个关键参数,再配上searchupsert的基本用法,就能构建出一个健壮的 RAG 底座。剩下的,都是业务逻辑的延伸。这个认知,让我在面对任何新的向量数据库时,都能快速抓住它的“设计灵魂”,而不是被眼花缭乱的文档淹没。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/21 13:16:49

深度探索Umi-OCR:构建你的离线文字识别工作流完整指南

深度探索Umi-OCR:构建你的离线文字识别工作流完整指南 【免费下载链接】Umi-OCR OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片,PDF文档识别,排除水印/页眉页脚,扫描/生成二维码。内置多国语言…

作者头像 李华
网站建设 2026/7/21 13:15:24

Letgo与主流Golang框架对比:为什么它是轻量级项目的首选

Letgo与主流Golang框架对比:为什么它是轻量级项目的首选 【免费下载链接】letgo go web 框架 golang web框架 轻量级高并发 go web framework 项目地址: https://gitcode.com/gh_mirrors/le/letgo 在Go语言的Web开发领域,选择一个合适的框架往往是…

作者头像 李华
网站建设 2026/7/21 13:13:48

Node.js 快速入门:一小时掌握环境搭建、HTTP 服务器与 npm 核心用法

这次我们来看一个 Node.js 的快速入门指南。Node.js 是一个开源的、跨平台的 JavaScript 运行时环境,它让 JavaScript 从浏览器中解放出来,能够直接运行在服务器端。这意味着你可以用你熟悉的 JavaScript 来开发后端服务、命令行工具、桌面应用甚至物联网…

作者头像 李华
网站建设 2026/7/21 13:12:45

如何轻松保存网络视频:VideoDownloadHelper的完整使用指南

如何轻松保存网络视频:VideoDownloadHelper的完整使用指南 【免费下载链接】VideoDownloadHelper Chrome Extension to Help Download Video for Some Video Sites. 项目地址: https://gitcode.com/gh_mirrors/vi/VideoDownloadHelper 你是否经常遇到想要保存…

作者头像 李华