把 AI 搜索成本打下来:从检索、缓存到本地部署的完整实操指南
AI搜索 如何降低成本|附完整命令
在企业落地 AI 搜索、知识库问答、RAG 检索增强生成系统时,很多团队一开始最关注的是“效果”:能不能搜得准、答得好、引用是否可靠。但真正上线后,大家很快会遇到另一个更现实的问题:成本太高。
AI 搜索的成本并不只来自大模型调用。一次完整的 AI 搜索请求,可能涉及文档解析、文本切分、向量化、向量数据库检索、关键词检索、重排序、上下文拼接、大模型生成、日志存储、监控告警等多个环节。任何一个环节设计不当,都会导致成本迅速上升。
本文将系统拆解 AI 搜索的成本来源,并给出一套可落地的降本方案。文末附完整命令,帮助你快速搭建一套本地低成本 AI 搜索原型。
一、AI搜索的成本主要花在哪里?
一个典型的 AI 搜索系统通常包括以下流程:
用户问题
↓
问题改写 / 意图识别
↓
向量检索 / 关键词检索
↓
结果重排序
↓
上下文拼接
↓
大模型生成答案
↓
返回答案与引用来源
其中主要成本来自以下几个方面。
二、成本来源一:Embedding 向量化成本
Embedding 是 AI 搜索的基础。无论是文档入库,还是用户提问时进行相似度检索,通常都需要将文本转换成向量。
1. 文档入库时的向量化成本
如果企业有大量 PDF、Word、网页、知识库文档,每次入库都需要调用 embedding 模型。例如:
- 10 万段文本;
- 每段 500 tokens;
- 总计 5000 万 tokens;
- 如果使用云端 embedding API,就会产生持续费用。
2. 查询时的向量化成本
每个用户问题都要进行一次 query embedding。单次成本不高,但高并发下会明显增加。
降本方法
方法一:使用本地 embedding 模型
例如使用:
bge-small-zh-v1.5bge-base-zh-v1.5bge-m3multilingual-e5-smallgte-small
这些模型可以部署在本地或私有云中,用 CPU 也可以运行,成本主要是机器成本。
方法二:文档只向量化一次
文档入库后,如果内容没有变化,不要重复向量化。可以为文档生成 hash:
文档内容 → hash → 判断是否变化 → 未变化则跳过向量化
方法三:合理控制切分粒度
切分太细,会产生更多 chunk,向量化成本更高;切分太粗,检索效果下降。
一般建议:
中文知识库:每段 300~800 字
英文知识库:每段 300~700 tokens
chunk overlap:50~100 字
三、成本来源二:向量数据库存储与检索成本
向量数据库用于存储 embedding,并支持相似度搜索。常见方案包括:
- Milvus
- Qdrant
- Weaviate
- Elasticsearch Vector
- PostgreSQL + pgvector
- Redis Vector
- FAISS
如果数据规模很大,向量维度很高,存储成本会很快增加。
降本方法
方法一:选择合适的向量维度
很多团队一开始就使用 1536、3072 维的 embedding,但并不是所有业务都需要这么高的维度。
例如:
| 场景 | 推荐维度 |
|---|---|
| 小型企业知识库 | 384~768 |
| 中文客服问答 | 512~1024 |
| 多语言搜索 | 768~1024 |
| 高精度法律 / 医疗场景 | 1024 以上 |
维度越高,存储成本和计算成本越高。
方法二:冷热数据分层
不是所有文档都需要常驻高性能向量库。
可以分为:
热数据:最近常用文档,放在向量库
温数据:偶尔访问,放在低成本存储
冷数据:归档数据,只在需要时重新索引或异步检索
方法三:定期清理无效向量
很多系统上线后会不断上传新文档,但不会删除旧向量。结果是数据库越来越大,成本越来越高。
应定期清理:
- 已删除文档对应的向量;
- 重复文档;
- 过期版本;
- 低质量 chunk;
- 无引用来源的数据。
四、成本来源三:大模型生成成本
AI 搜索中最贵的部分,通常不是检索,而是大模型生成答案。
一次 RAG 请求可能会把多个检索结果拼接进 prompt。上下文越长,消耗 tokens 越多,成本越高。
例如:
系统提示词:500 tokens
用户问题:50 tokens
检索上下文:6000 tokens
模型回答:800 tokens
总计:7350 tokens
如果每次请求都这样处理,在高并发场景下费用会非常可观。
降本方法
方法一:减少上下文长度
不要把 Top 20 的结果全部塞给大模型。更合理的方式是:
先召回 Top 20
再重排序 Top 5
最后只传 Top 3 给大模型
这样可以显著减少 prompt tokens。
方法二:使用小模型处理简单问题
不是所有问题都需要最强模型。
可以建立模型路由:
| 问题类型 | 推荐模型 |
|---|---|
| 简单 FAQ | 小模型 / 规则匹配 |
| 普通知识问答 | 中等模型 |
| 复杂推理问题 | 高级模型 |
| 敏感或高价值问题 | 高级模型 + 审核 |
例如:
如果命中 FAQ,直接返回答案;
如果问题简单,使用本地小模型;
如果需要复杂推理,再调用云端大模型。
方法三:答案缓存
大量用户会重复问类似问题,例如:
- “怎么重置密码?”
- “发票怎么开?”
- “怎么申请退款?”
- “接口限流是多少?”
可以对问题归一化后缓存答案。
缓存策略:
问题 → 标准化 → embedding → 相似问题匹配 → 命中缓存则直接返回
这样可以避免重复调用大模型。
五、成本来源四:重排序模型成本
为了提高准确率,很多 AI 搜索系统会使用 reranker 模型,例如:
- bge-reranker-base
- bge-reranker-large
- Cohere Rerank
- Jina Reranker
- 云厂商 rerank API
重排序效果很好,但如果每次对大量候选结果进行 rerank,成本也会增加。
降本方法
1. 控制候选数量
推荐:
向量召回 Top 20~50
关键词召回 Top 20~50
合并去重后最多 rerank 20 条
最终取 Top 3~5
不要对 Top 100、Top 200 全量重排序。
2. 只在必要时 rerank
以下场景可以跳过 rerank:
- 关键词精准命中标题;
- FAQ 完全匹配;
- 向量相似度明显高于其他结果;
- 用户只是查询固定字段。
3. 使用本地 reranker
本地部署 bge-reranker-base,在访问量稳定时,通常比持续调用 API 更低成本。
六、推荐的低成本 AI 搜索架构
一个比较适合中小团队的降本架构如下:
┌──────────────┐
│ 用户问题 │
└──────┬───────┘
↓
┌──────────────┐
│ 问题标准化 │
└──────┬───────┘
↓
┌─────────────────────────────────┐
│ 缓存 / FAQ / 规则匹配 │
└──────────────┬──────────────────┘
│ 未命中
↓
┌─────────────────────────────────┐
│ Hybrid Search:向量 + 关键词 │
└──────────────┬──────────────────┘
↓
┌─────────────────────────────────┐
│ 轻量 rerank Top N │
└──────────────┬──────────────────┘
↓
┌─────────────────────────────────┐
│ 拼接少量上下文,调用模型生成 │
└──────────────┬──────────────────┘
↓
┌─────────────────────────────────┐
│ 返回答案 + 引用来源 │
└─────────────────────────────────┘
这个架构的核心思想是:
能不用大模型就不用大模型,能用小模型就不用大模型,能少传上下文就少传上下文,能缓存就缓存。
七、具体降本策略清单
下面是一份可以直接用于项目评审的降本清单。
1. 文档处理阶段
- 删除重复文档;
- 对文档内容计算 hash;
- 未变化文档不重新入库;
- 过滤页眉、页脚、目录、版权声明等低价值内容;
- 控制 chunk 大小;
- 避免过大的 overlap;
- 图片 OCR 按需执行,不要默认全量 OCR。
2. 向量化阶段
- 优先使用本地 embedding;
- 批量处理 embedding;
- 降低向量维度;
- 对失败任务重试,但避免无限重试;
- 文档版本变更时只处理变化部分。
3. 检索阶段
- 使用 hybrid search;
- 先粗召回,再精排;
- 控制 Top K;
- 设置最低相似度阈值;
- 无关结果不要传给大模型;
- 高频问题走缓存。
4. 生成阶段
- 控制 prompt 模板长度;
- 控制上下文数量;
- 控制最大输出 tokens;
- 简单问题使用小模型;
- 复杂问题再调用强模型;
- 流式输出提升体验,但不要放任无限生成。
5. 监控阶段
必须记录以下指标:
每次请求的输入 tokens
每次请求的输出 tokens
检索耗时
rerank 耗时
模型生成耗时
缓存命中率
平均单次请求成本
每日总成本
用户满意度
无答案率
没有监控,就无法持续降本。
八、本地低成本 AI 搜索方案
下面给出一套本地可运行的示例方案:
- 向量数据库:Qdrant
- 本地大模型:Ollama
- 本地 embedding:Ollama embedding 模型
- 后端:Python
- 检索框架:LangChain
- 文档格式:Markdown / TXT / PDF
这套方案适合:
- 内部知识库;
- 小型客服系统;
- 产品文档搜索;
- 研发文档问答;
- 本地验证 RAG 流程。
九、完整命令:搭建本地 AI 搜索环境
以下命令以 macOS / Linux 为例。
1. 创建项目目录
mkdir low-cost-ai-search
cd low-cost-ai-search
2. 创建 Python 虚拟环境
python3 -m venv .venv
source .venv/bin/activate
如果是 Windows PowerShell:
python -m venv .venv
.\.venv\Scripts\Activate.ps1
3. 安装依赖
pip install -U pip
pip install langchain langchain-community langchain-qdrant qdrant-client ollama pypdf fastapi uvicorn python-dotenv
4. 启动 Qdrant 向量数据库
如果你已经安装 Docker,可以执行:
docker run -d \
--name qdrant \
-p 6333:6333 \
-p 6334:6334 \
-v "$(pwd)/qdrant_storage:/qdrant/storage" \
qdrant/qdrant
检查 Qdrant 是否启动:
curl http://localhost:6333
如果返回类似下面内容,说明启动成功:
{"title":"qdrant - vector search engine","version":"x.x.x"}
5. 安装 Ollama
macOS 可以使用:
brew install ollama
Linux 可以使用:
curl -fsSL https://ollama.com/install.sh | sh
启动 Ollama:
ollama serve
如果提示端口已占用,说明 Ollama 可能已经在后台运行。
6. 下载本地模型
下载一个生成模型:
ollama pull qwen2.5:7b
下载一个 embedding 模型:
ollama pull nomic-embed-text
如果机器配置较低,可以使用更小的模型:
ollama pull qwen2.5:3b
查看已安装模型:
ollama list
十、创建示例文档
创建文档目录:
mkdir docs
创建一个示例知识库文件:
cat > docs/company_policy.md <<'EOF'
# 公司报销制度
员工因公产生的交通费、住宿费、餐饮费可以申请报销。
## 报销时间
员工应在费用发生后的30天内提交报销申请。
## 报销材料
报销时需要提供合法有效的发票、费用说明、审批记录。
## 审批流程
员工提交报销申请后,由直属主管审批。金额超过5000元的,需要部门负责人二次审批。
## 注意事项
个人消费、非工作相关支出、无票据支出原则上不予报销。
EOF
再创建一个产品文档:
cat > docs/product_api.md <<'EOF'
# 产品 API 使用说明
本产品提供 REST API 服务,支持用户管理、订单查询和数据同步。
## 鉴权方式
所有 API 请求都需要在 Header 中携带 Authorization 字段。
格式如下:
Authorization: Bearer
## 限流规则
默认情况下,每个账号每分钟最多请求600次。
如果超过限制,接口会返回 HTTP 429 状态码。
## 错误码
401 表示认证失败。
403 表示没有权限。
429 表示请求过于频繁。
500 表示服务器内部错误。
EOF
十一、编写入库脚本
创建 ingest.py:
cat > ingest.py <<'EOF'
import os
from langchain_community.document_loaders import DirectoryLoader, TextLoader, PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.embeddings import OllamaEmbeddings
from langchain_qdrant import QdrantVectorStore
from qdrant_client import QdrantClient
DOCS_DIR = "docs"
COLLECTION_NAME = "knowledge_base"
QDRANT_URL = "http://localhost:6333"
def load_documents():
docs = []
for root, _, files in os.walk(DOCS_DIR):
for file in files:
path = os.path.join(root, file)
if file.endswith(".md") or file.endswith(".txt"):
loader = TextLoader(path, encoding="utf-8")
docs.extend(loader.load())
elif file.endswith(".pdf"):
loader = PyPDFLoader(path)
docs.extend(loader.load())
return docs
def main():
documents = load_documents()
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=80,
separators=["\n\n", "\n", "。", "!", "?", ";", ",", " ", ""]
)
chunks = splitter.split_documents(documents)
embeddings = OllamaEmbeddings(
model="nomic-embed-text",
base_url="http://localhost:11434"
)
client = QdrantClient(url=QDRANT_URL)
QdrantVectorStore.from_documents(
documents=chunks,
embedding=embeddings,
url=QDRANT_URL,
collection_name=COLLECTION_NAME,
force_recreate=True
)
print(f"入库完成,共写入 {len(chunks)} 个文本块。")
if __name__ == "__main__":
main()
EOF
执行入库:
python ingest.py
十二、编写查询脚本
创建 query.py:
cat > query.py <<'EOF'
from langchain_community.embeddings import OllamaEmbeddings
from langchain_qdrant import QdrantVectorStore
from langchain_community.llms import Ollama
COLLECTION_NAME = "knowledge_base"
QDRANT_URL = "http://localhost:6333"
PROMPT_TEMPLATE = """
你是一个严谨的企业知识库助手。
请只根据下面提供的上下文回答问题。
如果上下文中没有答案,请回答:根据现有资料无法确定。
上下文:
{context}
问题:
{question}
请用中文回答,并尽量简洁。
"""
def main():
question = input("请输入问题:")
embeddings = OllamaEmbeddings(
model="nomic-embed-text",
base_url="http://localhost:11434"
)
vectorstore = QdrantVectorStore.from_existing_collection(
embedding=embeddings,
url=QDRANT_URL,
collection_name=COLLECTION_NAME
)
docs = vectorstore.similarity_search(
question,
k=3
)
context = "\n\n".join([
f"来源:{doc.metadata.get('source', 'unknown')}\n内容:{doc.page_content}"
for doc in docs
])
llm = Ollama(
model="qwen2.5:7b",
base_url="http://localhost:11434",
temperature=0.2
)
prompt = PROMPT_TEMPLATE.format(
context=context,
question=question
)
answer = llm.invoke(prompt)
print("\n=== 检索到的上下文 ===")
print(context)
print("\n=== AI回答 ===")
print(answer)
if __name__ == "__main__":
main()
EOF
执行查询:
python query.py
测试问题:
报销需要在多少天内提交?
或者:
API 每分钟最多请求多少次?
十三、继续降低成本的优化命令
1. 限制 Docker 容器资源
如果你不希望 Qdrant 占用过多资源,可以限制内存和 CPU:
docker rm -f qdrant
docker run -d \
--name qdrant \
--memory=1g \
--cpus=1 \
-p 6333:6333 \
-p 6334:6334 \
-v "$(pwd)/qdrant_storage:/qdrant/storage" \
qdrant/qdrant
2. 使用更小的大模型
如果 7B 模型太慢,可以换成 3B:
ollama pull qwen2.5:3b
然后修改 query.py:
sed -i.bak 's/qwen2.5:7b/qwen2.5:3b/g' query.py
重新运行:
python query.py
3. 减少检索数量
如果当前使用 k=3,可以改为 k=2:
sed -i.bak 's/k=3/k=2/g' query.py
这样每次传给大模型的上下文更少,生成成本更低。
4. 减小 chunk 大小
如果文档内容较短,可以把 chunk 从 500 改为 400:
sed -i.bak 's/chunk_size=500/chunk_size=400/g' ingest.py
重新入库:
python ingest.py
5. 减少 chunk overlap
将 overlap 从 80 改为 40:
sed -i.bak 's/chunk_overlap=80/chunk_overlap=40/g' ingest.py
重新入库:
python ingest.py
十四、生产环境建议
本地方案适合验证,但生产环境还需要进一步增强。
1. 增加缓存层
可以使用 Redis 缓存高频问题答案:
用户问题 → 标准化 → hash → 查询 Redis → 命中直接返回
2. 增加权限控制
企业知识库通常需要按用户、部门、角色控制可见范围。检索时必须带上权限过滤条件。
3. 增加日志与成本统计
每次请求建议记录:
{
"question": "用户问题",
"retrieval_top_k": 3,
"model": "qwen2.5:7b",
"input_tokens": 1200,
"output_tokens": 300,
"cache_hit": false,
"latency_ms": 1800
}
4. 增加无答案机制
不要让模型强行回答。Prompt 中必须明确:
如果资料中没有相关信息,请回答无法确定。
这不仅能减少幻觉,也能降低反复追问导致的额外成本。
十五、AI搜索降本的核心公式
AI 搜索成本可以简单理解为:
总成本 =
文档处理成本
+ 向量化成本
+ 向量库存储成本
+ 检索计算成本
+ rerank 成本
+ 大模型输入 tokens 成本
+ 大模型输出 tokens 成本
+ 服务器与运维成本
因此,降本不是单点优化,而是系统性优化。
最有效的策略通常是:
减少无效文档
减少重复向量化
减少无关召回
减少传入上下文
减少大模型调用
减少高级模型调用
提高缓存命中率
结语
AI 搜索想要真正降低成本,关键不是盲目换更便宜的模型,而是重新设计整个链路。
如果只关注模型单价,很容易忽略检索数量、上下文长度、重复问题、无效文档、向量维度和缓存命中率这些更关键的因素。对于大多数企业来说,最实用的降本路径是:
- 文档入库前先清洗和去重;
- 使用本地 embedding 降低长期成本;
- 检索时控制 Top K;
- 对结果做轻量重排序;
- 只把最相关内容传给大模型;
- 高频问题使用缓存;
- 简单问题使用小模型;
- 复杂问题才调用更强模型;
- 持续记录 tokens、耗时和命中率;
- 根据真实数据不断优化。
一句话总结:
AI 搜索降本的本质,是把大模型从“万能入口”变成“最后一步的高价值生成器”。