# TCVectorDB 向量数据库实践:腾讯云托管检索与 Python 生产封装
TCVectorDB 是腾讯云的托管向量数据库。它更适合国内云上部署、企业内网访问、合规链路清晰、需要低延迟接入腾讯云生态的 RAG 和语义检索系统。
如果说 FAISS 更像本地向量检索内核,Pinecone 更像海外云上托管向量库,那么 TCVectorDB 的价值在于:把向量存储、索引、相似度检索、metadata 过滤、实例运维、私有网络接入和云账号权限放在国内云环境里统一管理。
但生产里不能只把它当成 add_texts() 的替代品。真正要设计的是数据库和集合怎么拆、Embedding 模型怎么固定、索引字段怎么声明、metadata 如何过滤、文档更新如何幂等、LangChain 封装和原生 SDK 的边界在哪里。
# TCVectorDB 的核心层级
TCVectorDB 的层级和常见云向量数据库类似。
| 概念 | 作用 | 生产理解 |
|---|---|---|
| Instance | 云数据库实例 | 网络、规格、鉴权、成本和运维边界 |
| Database | 数据库 | 按业务域、环境或系统模块隔离数据 |
| Collection | 集合 | 存储同一 schema、同一向量维度和索引策略下的数据 |
| Document | 记录 | 一条可检索数据,包含 id、vector 或 text、metadata 字段 |
| Index | 索引 | 决定向量检索方式和可过滤字段 |
这里最重要的是 Collection。它不是随便起个名字就结束了,而是决定了向量维度、索引类型、相似度计算方式、可过滤字段和后续查询能力。
生产里常见的拆法:
| 拆分维度 | 建议 |
|---|---|
| 环境 | dev、staging、prod 分开 database 或 collection |
| 租户 | 小租户可通过 metadata 过滤,大租户或强隔离租户可独立 collection |
| 知识库 | 同租户多知识库可用 metadata,也可按规模拆 collection |
| 索引版本 | 大版本重建时建议新 collection 灰度切换 |
| Embedding 模型 | 不同维度或不同模型不要混写到同一 collection |
# 安装与连接参数
TCVectorDB 官方 Python SDK 包名是 tcvectordb:
pip install -U tcvectordb
如果使用 LangChain Community 封装,还需要:
pip install -U langchain-community langchain-openai
环境变量建议统一管理:
export TC_VECTOR_DB_URL="http://your-vdb-endpoint"
export TC_VECTOR_DB_USERNAME="root"
export TC_VECTOR_DB_KEY="your-vdb-key"
export TC_VECTOR_DB_DATABASE="llmops"
export TC_VECTOR_DB_TIMEOUT="30"
export OPENAI_API_KEY="your-openai-api-key"
2
3
4
5
6
生产里不要把连接地址、账号和密钥写进代码。更稳的方式是通过密钥管理系统、容器环境变量或配置中心注入,并在服务启动时做一次配置校验。
# 原生 SDK 连接
原生 SDK 更适合做数据库和集合管理、批量写入、更新、删除、统计、运维任务。
import os
from tcvectordb import VectorDBClient
from tcvectordb.model.enum import ReadConsistency
client = VectorDBClient(
url=os.environ["TC_VECTOR_DB_URL"],
username=os.environ.get("TC_VECTOR_DB_USERNAME", "root"),
key=os.environ["TC_VECTOR_DB_KEY"],
read_consistency=ReadConsistency.EVENTUAL_CONSISTENCY,
timeout=int(os.environ.get("TC_VECTOR_DB_TIMEOUT", "30")),
)
database = client.database(os.environ["TC_VECTOR_DB_DATABASE"])
collection = database.collection("help_center_v1")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
read_consistency 要结合业务看。知识库检索通常可以接受最终一致性,但如果你在后台刚写入一批内容,马上要验证召回结果,就要理解写入后可见性的延迟边界。
# 创建 Collection 的生产关注点
Collection 创建时最容易漏掉两件事:
第一,向量字段的维度和相似度指标必须和 Embedding 模型一致。
第二,后续要参与过滤的 metadata 字段必须提前声明索引。
示例结构如下:
from tcvectordb.model.enum import FieldType, IndexType, MetricType
from tcvectordb.model.index import FilterIndex, HNSWParams, VectorIndex
client.create_collection_if_not_exists(
database_name="llmops",
collection_name="help_center_v1",
shard=2,
replicas=1,
indexes=[
FilterIndex(name="id", field_type=FieldType.String, index_type=IndexType.PRIMARY_KEY),
VectorIndex(
name="vector",
field_type=FieldType.Vector,
index_type=IndexType.HNSW,
dimension=1536,
metric_type=MetricType.COSINE,
params=HNSWParams(m=16, efconstruction=200),
),
FilterIndex(name="tenant_id", field_type=FieldType.String, index_type=IndexType.FILTER),
FilterIndex(name="knowledge_base_id", field_type=FieldType.String, index_type=IndexType.FILTER),
FilterIndex(name="document_id", field_type=FieldType.String, index_type=IndexType.FILTER),
FilterIndex(name="status", field_type=FieldType.String, index_type=IndexType.FILTER),
FilterIndex(name="page", field_type=FieldType.Uint64, index_type=IndexType.FILTER),
],
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
实际项目里,创建 collection 通常不放在 Web 服务启动流程里,而是放在初始化脚本、发布流水线、运维控制台或数据库迁移流程里。应用启动时只连接已存在的 collection。
# 内置 Embedding 与外部 Embedding
TCVectorDB 支持把文本写入后由平台侧完成向量化,也支持业务侧先生成向量再写入。
| 方式 | 优点 | 风险 |
|---|---|---|
| 内置 Embedding | 接入简单,少维护一个模型服务 | 模型选择受平台支持范围影响,跨库迁移时要重新评估 |
| 外部 Embedding | 模型、版本、缓存、成本更可控 | 需要自己处理限流、重试、维度一致性和批处理 |
生产 RAG 我更偏向外部 Embedding。原因不是内置能力不好,而是业务通常需要固定模型版本、做 Embedding 缓存、离线评估、灰度迁移和跨数据库备份。外部 Embedding 更容易把这条链路纳入自己的工程治理。
# LangChain 接入
LangChain Community 中仍然有 TencentVectorDB 封装,可以用它快速接入 Retriever。
import os
from langchain_community.vectorstores import TencentVectorDB
from langchain_community.vectorstores.tencentvectordb import ConnectionParams
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vector_store = TencentVectorDB(
embedding=embeddings,
connection_params=ConnectionParams(
url=os.environ["TC_VECTOR_DB_URL"],
username=os.environ.get("TC_VECTOR_DB_USERNAME", "root"),
key=os.environ["TC_VECTOR_DB_KEY"],
timeout=int(os.environ.get("TC_VECTOR_DB_TIMEOUT", "30")),
),
database_name=os.environ["TC_VECTOR_DB_DATABASE"],
collection_name="help_center_v1",
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
检索:
docs = vector_store.similarity_search(
"如何申请发票",
k=5,
)
2
3
4
带分数:
results = vector_store.similarity_search_with_score(
"如何申请发票",
k=5,
)
for doc, score in results:
print(score, doc.page_content, doc.metadata)
2
3
4
5
6
7
生产里建议把 LangChain 的 TencentVectorDB 当成 RAG 检索层,而不是完整运维层。复杂的 collection 创建、字段索引、批量更新、删除、审计和数据修复,优先走 tcvectordb 原生 SDK 或后台任务。
# metadata 字段必须显式设计
TCVectorDB 和 LangChain 封装里有一个容易踩的点:metadata 不是随便传就一定会被保存、过滤和返回。需要参与存储或过滤的字段,要在 collection schema 或 LangChain 初始化参数里声明清楚。
LangChain 封装里可以声明 meta_fields:
from langchain_community.vectorstores.tencentvectordb import (
META_FIELD_TYPE_STRING,
META_FIELD_TYPE_UINT64,
MetaField,
)
vector_store = TencentVectorDB(
embedding=embeddings,
connection_params=connection_params,
database_name="llmops",
collection_name="help_center_v1",
meta_fields=[
MetaField(name="text", data_type=META_FIELD_TYPE_STRING),
MetaField(name="tenant_id", data_type=META_FIELD_TYPE_STRING),
MetaField(name="knowledge_base_id", data_type=META_FIELD_TYPE_STRING),
MetaField(name="document_id", data_type=META_FIELD_TYPE_STRING),
MetaField(name="status", data_type=META_FIELD_TYPE_STRING),
MetaField(name="page", data_type=META_FIELD_TYPE_UINT64),
],
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
这里把 text 也放进 metadata,是为了让检索结果能稳定构造 Document.page_content。有些封装在外部 Embedding 模式下只写入向量,若没有保存原文,召回时就会出现“查到了向量,但拿不到正文”的尴尬情况。
# 写入文本与稳定 id
不要依赖自动生成 id。稳定 id 是生产里更新、删除、去重、审计和重放的基础。
from langchain_core.documents import Document
documents = [
Document(
page_content="企业用户可以在控制台申请发票,审批通过后进入开票流程。",
metadata={
"text": "企业用户可以在控制台申请发票,审批通过后进入开票流程。",
"tenant_id": "tenant-a",
"knowledge_base_id": "kb-help-center",
"document_id": "invoice-policy",
"status": "published",
"page": 1,
},
)
]
ids = ["invoice-policy#chunk-0001"]
vector_store.add_documents(
documents=documents,
ids=ids,
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
稳定 id 的生成规则可以这样设计:
def build_chunk_id(document_id: str, chunk_index: int, chunk_version: str) -> str:
return f"{document_id}:{chunk_version}:chunk-{chunk_index:04d}"
2
如果文档重切分了,不要复用旧 chunk id。把 chunk_version 加进去,后续可以按版本灰度、回滚和清理。
# 过滤表达式
TCVectorDB 查询过滤通常使用表达式形式,例如:
docs = vector_store.similarity_search_with_score(
"发票申请流程",
k=5,
expr='tenant_id="tenant-a" and knowledge_base_id="kb-help-center" and status="published"',
)
2
3
4
5
数值范围:
docs = vector_store.similarity_search_with_score(
"发票申请流程",
k=5,
expr='page>=1 and page<=10',
)
2
3
4
5
集合匹配:
docs = vector_store.similarity_search(
"售后政策",
k=5,
expr='status in ("published", "gray")',
)
2
3
4
5
过滤字段必须提前建索引。很多线上问题不是查询表达式写错,而是 collection 创建时没有把字段声明为可过滤字段,导致后续检索无法按权限、状态、页面、知识库收敛。
# 和 Pinecone 的区别
| 维度 | Pinecone | TCVectorDB |
|---|---|---|
| 部署生态 | 海外托管服务为主 | 腾讯云生态和国内网络更友好 |
| Python SDK | pinecone | tcvectordb |
| LangChain 集成 | 独立包 langchain-pinecone | Community 集成 TencentVectorDB |
| 隔离方式 | index、namespace、metadata | instance、database、collection、metadata |
| 过滤表达 | JSON 风格 filter | 表达式风格 expr |
| 适合场景 | 国际化 SaaS、海外云 | 国内云上 RAG、腾讯云账号和网络体系 |
这两个工具不是谁替代谁,而是部署地域、合规、团队云栈、成本模型和生态选择不同。国内业务如果已经在腾讯云上,TCVectorDB 的网络和运维整合成本通常更低。
# 和 FAISS 的区别
| 维度 | FAISS | TCVectorDB |
|---|---|---|
| 形态 | 本地向量检索库 | 托管向量数据库 |
| 运维 | 自己维护索引文件和发布 | 云服务负责实例和高可用 |
| 过滤 | 主要靠封装层和候选过滤 | 原生 metadata 过滤 |
| 更新删除 | 需要自己设计索引版本和文件发布 | 支持在线数据治理 |
| 多租户 | 需要自行拆索引或补权限层 | 可结合 database、collection、metadata 设计 |
FAISS 更适合本地开发、离线评估、小型知识库和完全自管场景。TCVectorDB 更适合线上服务、国内云环境、需要可用性和运维托管的系统。
# 生产封装建议
不要让业务代码到处拼接 TCVectorDB 参数。封装一个配置对象和工厂函数:
from dataclasses import dataclass
from langchain_community.vectorstores import TencentVectorDB
from langchain_community.vectorstores.tencentvectordb import ConnectionParams
@dataclass(frozen=True)
class TCVectorDBSettings:
url: str
username: str
key: str
database_name: str
collection_name: str
timeout: int
embedding_model: str
dimension: int
metric: str
def build_tcvectordb_vector_store(settings: TCVectorDBSettings, embeddings):
return TencentVectorDB(
embedding=embeddings,
connection_params=ConnectionParams(
url=settings.url,
username=settings.username,
key=settings.key,
timeout=settings.timeout,
),
database_name=settings.database_name,
collection_name=settings.collection_name,
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
同时保存一份 manifest:
{
"database_name": "llmops",
"collection_name": "help_center_v1",
"embedding_model": "text-embedding-3-small",
"dimension": 1536,
"metric": "cosine",
"chunk_version": "chunk-v3",
"schema_version": "schema-v2"
}
2
3
4
5
6
7
8
9
manifest 的价值是排查。线上检索质量变差时,你需要知道当前 collection 用的模型、维度、metric、chunk 策略和字段版本。
# 上线前检查清单
- Collection 的向量维度是否和 Embedding 模型一致。
- 相似度 metric 是否和模型归一化策略一致。
- metadata 字段是否提前声明并建立过滤索引。
tenant_id、knowledge_base_id、status是否能参与过滤。- 写入 id 是否稳定、可重放、可删除。
- 外部 Embedding 是否有缓存、限流、重试和批处理。
- 文档更新是否支持 delete + upsert 或 collection 版本切换。
- 是否记录 query、expr、k、score、latency 和命中文档 id。
- 是否有固定评估集验证 Recall@K、MRR 和答案质量。
- 是否有实例权限、网络访问控制和密钥轮换策略。
# 问题
TCVectorDB 最容易踩的坑不在连接,而在 schema 和治理。
第一,Collection 创建时没有声明过滤字段。后续需要按租户、知识库、状态、页面过滤时才发现查不动。
第二,外部 Embedding 模式下没有保存原文。向量能召回,但构造不出可给模型阅读的上下文。
第三,Embedding 模型版本混乱。同一个 collection 混写不同维度或不同语义空间的向量,排序会失真。
第四,id 不稳定。每次写入都生成新 id,会造成重复 chunk、删除困难、更新不可控。
第五,把 LangChain 封装当成完整数据库 SDK。检索可以走 VectorStore,批量治理和运维最好回到原生 SDK。
# 拓展
TCVectorDB 可以继续往四个方向扩展。
第一,混合检索。把向量召回、关键词召回和 reranker 组合起来,提高专业术语、编号、产品名、语义相似问题的整体命中。
第二,索引版本化。每次 chunk 策略或 Embedding 模型变化,都创建新 collection 或新版本字段,通过评估后再切流。
第三,多租户治理。小租户用 metadata 过滤,大租户或高敏数据独立 collection,强权限字段从认证态注入,不接受前端自由传入。
第四,可观测闭环。记录每次检索的 query、expr、召回文档、score、耗时和最终答案反馈,建立 RAG 质量回放能力。
# 实际生产是否使用
会使用。
TCVectorDB 适合国内云上 RAG、企业知识库、智能客服、推荐召回、语义搜索等场景。尤其是业务已经在腾讯云、希望走内网访问、需要托管运维和国内合规链路时,它比自建 FAISS 更省工程成本。
但生产里不会只写一个 LangChain 示例。真正可上线的系统还要包括文档解析、chunk 版本、Embedding 缓存、稳定 id、metadata schema、collection 发布、评估集、权限过滤、审计日志和成本监控。
# 现在是否抛弃
没有抛弃。
tcvectordb SDK 仍在维护,LangChain Community 当前也能查到 TencentVectorDB 集成。需要放弃的是早期 demo 写法:不声明 metadata、不保存原文、不固定 id、不区分检索接口和数据治理接口。
新项目应该把 TCVectorDB 当成生产向量数据库使用,而不是把它当成一段示例代码里的向量存储对象。
# 最新生产如何实现
当前更推荐这样落地:
第一,基础设施层创建实例、database 和 collection,固定 dimension、metric、索引字段和网络访问策略。
第二,文档入库链路使用外部 Embedding,统一模型版本、批量大小、缓存和重试。
第三,写入时显式传入稳定 ids、原文 text 和 metadata,保证可召回、可删除、可审计。
第四,在线检索层可以使用 LangChain TencentVectorDB 转成 retriever,方便进入 LCEL 或 RAG 链路。
第五,批量更新、删除、重建、统计和修复任务优先使用 tcvectordb 原生 SDK。
第六,权限过滤字段必须来自服务端认证态,不能由浏览器或客户端自由决定。
最小生产查询结构:
def quote_expr_value(value: str) -> str:
if not value.replace("-", "").replace("_", "").isalnum():
raise ValueError("invalid filter value")
return f'"{value}"'
def build_in_expr(field: str, values: list[str]) -> str:
if not values:
raise ValueError("empty filter values")
quoted_values = ", ".join(quote_expr_value(value) for value in values)
return f"{field} in ({quoted_values})"
def search_knowledge(vector_store: TencentVectorDB, query: str, auth_context):
tenant_id = auth_context.tenant_id
allowed_kb_ids = auth_context.allowed_knowledge_base_ids
expr = (
f"tenant_id={quote_expr_value(tenant_id)} "
f"and {build_in_expr('knowledge_base_id', allowed_kb_ids)} "
f'and status="published"'
)
return vector_store.similarity_search_with_score(
query,
k=5,
expr=expr,
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
真实项目里不要直接字符串拼接用户输入。上面的结构表达的是权限过滤必须在服务端生成。落地时应使用安全的表达式构造器、白名单字段和日志审计,避免把过滤语法暴露给用户。
# 总结
TCVectorDB 的优势是国内云上托管、低运维成本、metadata 过滤、腾讯云网络和账号体系整合。它适合把 RAG 从本地实验推进到企业级线上服务。
但它不会替你设计知识库工程。生产质量取决于 collection schema、Embedding 版本、metadata 字段、稳定 id、权限过滤、批量治理、评估集和可观测闭环。
参考: