文章建议Laravel项目先从成熟SQL搜索起步,仅在语言理解需求真实出现时才引入向量数据库,并详细对比了三种方案在精确词匹配、可解释性、维护成本上的差异。
SQL 在更多场景仍然胜出,超乎承认
对于许多 Laravel 产品,搜索仍然被精确匹配、字段权重、过滤器、新近度和业务规则所主导。这是经典的关系型领地。
当用户搜索发票号、客户邮箱、SKU、职位头衔、方案名称或功能开关时,向量搜索带来的提升非常有限。事实上,它往往会让这些场景变得更糟,因为语义相似性并不等于精确性。
一个正常的 SQL 后端方案,在结构化得当的情况下可以走得很远:
在 Laravel 中,这个基线版本通常比向量技术栈更快交付,也更容易调试。
$query = trim($request->string('q'));
$results = Post::query()
->select('*')
->selectRaw(
"(
CASE WHEN slug = ? THEN 100 ELSE 0 END +
CASE WHEN title LIKE ? THEN 40 ELSE 0 END +
CASE WHEN MATCH(title, excerpt, body) AGAINST (? IN NATURAL LANGUAGE MODE)
THEN MATCH(title, excerpt, body) AGAINST (? IN NATURAL LANGUAGE MODE)
ELSE 0
END +
CASE WHEN published_at >= NOW() - INTERVAL 90 DAY THEN 5 ELSE 0 END
) AS relevance",
[$query, "$query%", $query, $query]
)
->where('status', 'published')
->having('relevance', '>', 0)
->orderByDesc('relevance')
->limit(20)
->get();
这并不花哨,但它是可运维的。你能够解释为什么一条记录排名靠前。你可以调整权重。你可以添加硬约束。你可以记录糟糕的查询并快速改进。
SQL 搜索的局限性
局限性出现在查询和文档使用了不同语言的时候。
用户搜索"取消订阅",但你的帮助文章写的是"终止计费周期"。招聘人员搜索"后端负责人",但简历写的是"首席平台工程师"。客服输入"2FA 后登录循环",而事件摘要用的是完全不同的措辞。
这就是普通关键词检索开始泄漏相关性的地方。问题不再是索引或过滤器,而是语义不匹配。
Embedding 解决的是不同的问题
Embedding 在你需要基于意义的检索、而不仅仅是文本匹配时才有价值。它们在内容是长格式、语言丰富、且很可能被各种不同措辞查询时效果最好。
好的候选场景包括:
不适合的场景包括:
这个区别很重要,因为向量搜索伴随着真实的成本。你不只是存储 embedding 就完事了。你现在需要负责:分块策略、模型选择、重 embedding 管道、向量索引存储、延迟预算,以及当搜索开始返回"有点相关"的垃圾时的故障分析。
隐藏成本是相关性调试
SQL 搜索失败得很响亮。向量搜索往往失败得很安静。
使用 SQL,你通常能知道为什么一条记录没有匹配上。使用 embedding,失败模式更加模糊:
这使得 embedding 很强大,但并非天然对开发者友好。
在 Laravel 应用中,一个典型的向量流程可能是这样的:
$embedding = app(EmbeddingClient::class)->embed($request->string('q'));
$results = DB::table('knowledge_chunks')
->select('id', 'document_id', 'chunk_text')
->selectRaw('1 - (embedding <=> ?) as similarity', [$embedding])
->where('workspace_id', $workspaceId)
->orderByRaw('embedding <=> ?', [$embedding])
->limit(10)
->get();
语法取决于你的存储选择,但核心是一样的:这套系统的好坏取决于分块、embedding 模型及其周围的检索约束。
如果你跳过这个设计工作,向量搜索就会变成一个演示起来很酷但生产表现令人失望的昂贵模糊查找。
混合检索通常是更成熟的答案
如果你的 Laravel 应用既有结构化精确需求,也有语言密集型的发现需求,混合检索是最佳默认终态。
混合不是说"把 SQL 和向量扔进一个袋子里希望它们有用"。它意味着每种检索方法做它擅长的事,你的排序层则有意识地组合它们。
模式很简单:
这给你提供了平衡精确率和召回率的最佳机会。
一个实用的 Laravel 形态
实际上,你可能维护两个索引:
然后在应用代码中合并结果集。
$keywordResults = app(KeywordSearch::class)->search($query, limit: 20);
$vectorResults = app(VectorSearch::class)->search($query, limit: 20);
$merged = collect([$keywordResults, $vectorResults])
->flatten(1)
->groupBy('id')
->map(function ($group) {
$item = $group->first();
$keywordScore = $group->max('keyword_score') ?? 0;
$vectorScore = $group->max('vector_score') ?? 0;
$freshnessBoost = $item->published_at?->gt(now()->subDays(30)) ? 0.05 : 0;
$exactTitleBoost = $item->exact_title_match ? 0.25 : 0;
$item->final_score =
($keywordScore * 0.55) +
($vectorScore * 0.35) +
$freshnessBoost +
$exactTitleBoost;
return $item;
})
->sortByDesc('final_score')
->take(10)
->values();
这在数学上并不优雅,但没关系。生产环境中的搜索相关性很少是优雅的。重要的是排序逻辑保持清晰可调整。
混合检索真正值得的地方
当以下条件全部满足时,混合检索值得这个开销:
带 AI 聊天 grounding 的帮助中心就是一个强例。用户可能用模糊的措辞搜索,但精确的产品名称、功能开关、方案层级和错误代码仍然很重要。纯 SQL 会错过语义匹配。纯向量搜索可能埋没正确答案。混合给你一个更好的操作空间。
索引成本比大多数团队预期的更能改变决策
检索质量只是故事的一半。另一半是保持索引正确性的成本。
SQL 搜索的维护负担最低,因为真相来源和可搜索表示通常离得很近。更新很直接。重建索引很熟悉。运维所有权与拥有应用的团队保持一致。
Embedding 改变了这个等式。
每一个有意义的内容变更都可能需要重新 embedding。如果你存储分块文档,你还需要确定性的分块策略,这样更新才不会不可预测地破坏你的排名行为。如果你以后切换 embedding 模型,你可能需要全量回填。如果你支持多租户搜索,索引增长就会成为一条真正的预算线,而不是实现细节。
一个有用的决策规则
在引入向量之前问三个问题:
如果第三个问题的答案是否定的,不要假装 embedding 是即插即用的升级。它是一个基础设施选择。
对于 Laravel 团队来说,这通常意味着在语义检索上线之前构建一个队列支撑的索引管道。
class SyncKnowledgeChunkEmbeddings implements ShouldQueue
{
public function __construct(public int $documentId) {}
public function handle(EmbeddingClient $embeddings): void
{
$document = KnowledgeDocument::findOrFail($this->documentId);
$chunks = app(DocumentChunker::class)->split($document->body_markdown);
KnowledgeChunk::where('document_id', $document->id)->delete();
foreach ($chunks as $index => $chunkText) {
KnowledgeChunk::create([
'document_id' => $document->id,
'chunk_index' => $index,
'chunk_text' => $chunkText,
'embedding' => $embeddings->embed($chunkText),
]);
}
}
}
这是人们在架构图中跳过的部分。检索演示很容易。索引生命周期才是团队要么建立真实系统、要么积累搜索债务的地方。
相关性调优:你的团队能真正运维哪套系统?
这就是简单 SQL 持续让人惊讶的地方。这不是因为 embedding 很弱。而是关键词排序对于产品团队来说更容易在一周接一周地改进。
当利益相关者抱怨错误的结果排在前面时,你需要一个调优循环:
这个循环在 SQL 中紧凑得多,在混合系统中中等难度。在纯向量系统中最难,除非你投入评估工具。
SQL 更适合高控制搜索
如果你的产品需要确定性排序规则,SQL 或全文应该保持为主干。想想 CRM、仪表盘、管理面板、市场平台、内部工具,以及任何有密集过滤器的场景。
在这些系统中,用户不是在问宽泛的概念性问题。他们是在约束条件下试图找到正确的记录。精确性胜过聪明。
Embedding 在知识检索中胜出
如果你的应用在为助手响应、内部文档查找或支持知识发现提供动力,语义检索会挣得它的成本。但即使在那里,如果语料库不是真正叙事的、且缺乏结构化信号,我仍然会避免纯向量搜索。
大多数生产系统需要元数据过滤器、来源权重、新近度偏好,有时还需要文档类型加权。这已经把你推向混合思维。
如果我在真实 Laravel 代码库中构建会怎么发货
如果我现在构建这个,我不会从最先进的技术栈开始。我会分阶段构建系统。
第一阶段:强大的 SQL 基线
这能让你快速获得一个可靠的基线。
第二阶段:只在召回失败的地方添加向量
不要 embedding 所有内容。选择那些措辞不匹配明显伤害到结果的内容类型。通常这意味着文档、笔记、转录或工单。保持关系型搜索用于结构化实体。
这种分离在架构上更清晰,在运维上更便宜。
第三阶段:合并为混合检索
一旦你从真实搜索日志中有了证据,合并两条路径并调优重排序层。这是你添加产品特定智能的地方:
最后一点很重要。如果你想使用重排序器或 LLM 裁判,用在小候选集上。不要在你整个语料库上浪费昂贵的推理。
对于 Laravel 团队来说,这种分阶段方法是"我们发货了有用的搜索"和"我们采用了搜索基础设施"之间的区别。
明确的建议
如果你的 Laravel 应用主要搜索记录、产品、用户、工单或操作数据,从 SQL 和全文搜索开始。它更便宜、更容易调优,而且通常更正确。
如果你的应用搜索长格式知识,而用户用不一致的语言描述想法,添加 embedding。但要把它们当作语义召回层,而不是魔法相关性粉尘。
如果两个世界都很重要,发货混合检索。对于大多数认真的 AI 赋能应用,这是务实的生产答案。
错误不是选择了"错误的"算法。错误是用业务规则工具解决语义问题,或者用语义工具解决精确问题。
需要控制时用 SQL。需要意义时用 embedding。用户两者都需要时用混合。这是经得起生产环境检验的版本。