关于搜索
搜索是一个非常常见、但也很「吃工程细节」的能力:例如浏览器要从你的问题里抽出意图,再去匹配更可能有用的一页结果。
下面先简单对齐一下经典搜索流程里的要素,再对照后面「任务型搜索 / Search Agent」里我们在做什么。
搜索流程
- 用户输入查询:在搜索框里输入查询词或自然语言问题。
- 查询解析:解析输入,尽量还原意图与上下文。
- 索引匹配:在索引里找和查询相关的文档或数据。
- 排序与过滤:按相关性、权重、时效、质量等做排序与过滤。
- 结果展示:把排序后的结果交给用户。
一个相对成熟的 Search Agent 流水线,在现代产品里常被画成:
User Query
↓
Query Understanding
↓
Planner / Router
↓
Multi-source Retrieval
↓
Filtering
↓
Reranking
↓
Evidence Synthesis
↓
Reflection / Verification
↓
Final Answer和传统「输入几个关键词 → 网页列表」相比,我们这类项目更接近:带着任务去找证据(例如围绕某个预测市场问题,找可跟进阅读的链接),因此后面的「召回 + 精排」比重会更大。
我的实践
(AI 的恩情还不完👋😭👋)
上面那张图里,我在工程里先把「搜索」拆成三块,方便分工和迭代:
- query:把用户意图转成可执行的检索词(以及必要的上下文)。
- search:用检索词去各信息源拉候选(HTTP、解析、去重——本质是代码)。
- score & rank:对候选打分、排序,筛掉明显不可用或过期内容,再交给上游展示或 Agent 继续读。
这三块里,query / score 很适合用 LLM 做「结构化输出」;search 则更适合先定输入输出契约(例如「给什么 query 列表、返回什么形状的候选」),再写具体抓取逻辑——否则后面换源、加缓存会很难维护。
再往下,我们希望把一些能力收成 tool,交给上层的 search agent 按需调用,而不是把所有分支写死在一条超长流水线里.从而实现真正的「Agentic」
query 模块
原来的mvp中,只是利用代码简单的将问题拆解,非常粗糙(中文也很难处理),因此我这里使用了 deepseek-v4-flash 这个模型来作为 query 阶段的主力。
需要牢记的主旨是:「LLM 的输出不可盲信」,因此至少要有:
- 结构化输出:例如固定 JSON 形态(我们要求只返回可
JSON.parse的一段文本)。 - Schema 校验:Planner 侧我用 Zod 做了严格校验(多余字段、类型不对、置信度越界等一律走降级),避免「模型偶发胡写键名」把下游打挂。
进阶上还会遇到:
-
效率 / 多样性:怎么让检索词列表少重复、少同义堆叠(semantic duplication)。
如果 prompt 不加约束,很容易出现类似:用户问题:AI 会不会取代前端?
模型给的检索词:- AI replace frontend
- frontend AI future
- frontend developer future
这本质是 prompt 设计问题:可以限制时间跨度、要求「语义分叉」、限制条数、禁止近义复述等。
-
成本:可以做一层复杂度判断,复杂度不高时走更短、更便宜的计划(甚至纯规则)。
我们预设场景多是「围绕一整句市场问题去搜证据」,不是用户直接敲btc price那种短关键词,因此 planner 的形态也会偏「问题改写 + 变体」而不是传统 SEO 词表。 -
(可选)按平台口味定制 query:例如 Reddit 偏讨论、GitHub 偏仓库与实现、arXiv 偏论文——若未来在结构化计划里带上「渠道提示」,生成词会更贴源;这需要和 search 的契约一起设计。
search 模块
早期 MVP 里,曾用比较硬的方式做配额:例如把检索词带来的结果按大致比例分给 Reddit 和 Google News。现在更清楚的一点是:「搜哪里、搜多少」是策略,真正发 HTTP、解析 RSS/JSON 的是代码;Agent 适合参与策略,不适合替代确定性的 IO 与错误语义。
对照典型 Search Agent:Planner/Router 决定渠道与预算 → Multi-source Retrieval 执行 → Filtering / Reranking 再收敛。我一开始只分「query / search / score」确实略粗,但和这张图是可以对齐的。
和当前实现诚实对齐的一句:我们主线上的 Planner 契约,目前是 primary_query + variants +(可选)confidence,还没有把「去哪个平台」塞进同一份 JSON;多源组合仍以代码侧为主。把「意图理解 + 渠道建议」合并进 query 输出、省掉单独 Router,是我们正在演进的方向,写进博客是为了提醒自己:契约先长出来,再让模型填内容,而不是反过来。
精排阶段再通过 score 用的 system prompt 约束模型:输入里带上市场文本、候选标题/摘要、时间、来源类型等,让打分和规则兜底能对齐。
score & rank 模块
这里的核心挑战是:评分机制是否可解释,以及 LLM 参与时如何保证「不会比没有模型更差」。
在 MVP 里,这一块可以概括成两件事:先算分,再排序。
1. 先算分:多维度拆解
直接问模型「这篇好不好」太粗,也难回归测试。更稳的是拆成几条人类也能对齐的维度,例如:
- 相关性:和当前问题贴不贴题。
- 时效性:信息是不是过旧(对预测类问题往往更敏感)。
- 信息质量 / 可信度:能不能当证据用、有没有可核对的事实。
尤其不要把「相关」和「质量」混成同一个维度,否则「排版像新闻、其实跑题」的内容很容易骗到高分。
2. 再排序:加权与调参
各维度分出来后,用权重合成总分。权重没有标准答案,取决于产品:要更「钉题」就抬高相关性;要更「追进展」就抬高时效性。每调一次权重,整体排序的「性格」都会变,建议把默认权重写进配置或设计文档,方便复盘和协作。
防御性编程:LLM 一样不能裸奔
和 query 同思路:
- 结构化输出:例如固定数值字段 + 一两句
rationale,并限制在可解析 JSON 内。 - 校验:Query 侧我已经用 Zod 锁死 Planner;打分侧至少要有「解析失败 / 字段缺失」的处理,并与规则分对齐;若团队统一技术栈,用 Zod 再包一层打分结果也很自然。
- 兜底(Fallback):模型超时、空内容、JSON 坏了,应退回规则侧算出来的分数,接口仍返回有序列表,而不是空白或 500。我们还会对「明显过期 / 弱相关」做规则标记,再在返回前过滤——这类逻辑要和 prompt 里的口径对齐,否则会出现「模型给高分、规则仍判无效」的拉扯,需要靠测试集慢慢磨。
一致性:怎么让系统尽量稳?
同类任务尽量固定同一套 prompt 版本 + 权重版本;规则分支保持确定性;用 固定案例集(几条典型问题 + 好/坏链接)在每次改 prompt 或权重后跑一遍,看排序整体有没有「漂移」。
再往后
候选一多,「每条候选打一次分」在成本和延迟上都会顶不住,更适合改成 batch 内的相对排序(模型更擅长比大小,而不是给绝对分),并配合 并发上限 / 限流。这些都属于「主路径跑通以后」的优化;地基仍是:维度拆开、结构化、可降级、可测。