Vibe Coding 实战:从零构建现代专业级 AI 搜索引擎
写在前面
大家好,我是不会喷火的小火龙。这是一份面向个人开发者的实战教程。我们将从零开始,使用现代技术栈(Next.js 15 + FastAPI + DeepSeek-Flash),完整构建一款专为技术研报与深度工程设计的专业级 AI 搜索引擎——XHL AI Search。
本教程重点介绍掌握以下两套核心技能:
- 让 AI 写出顶级前端的 Vibe Coding 最佳实践:掌握 Google Stitch 视觉锚定、/impeccable 工艺硬约束与 AGENTS.md 宪法级规约的联动,让 AI 告别粗制滥造的蓝紫渐变与塑料质感;
- 六大工业级核心技术模块全栈实战:
- 全双工 SSE 流式推演与全链路毫秒级可观测体系(Observability);
- OpenUI 混合生成式 UI 与紧凑行式 DSL(比标准 JSON 节省 67% Token);
- 多源检索路由、Scope 四阶物理与语义防穿透过滤、多目标权威时效重排;
- QuickRouter TypeSafe Jev 毫秒级非生成式决策分类;
- 双层 TTL 内存隔离缓存与 SQLite 异步持久化会话快照引擎;
- 原文段落级证据抽屉与高保真双向高亮联动(Grounding Snapshot)。
如图 1 所示,这是我们最终交付的系统首屏命令台。整体采用极简冷调的 Apple Cupertino Light 风格,彻底摒弃传统 AI 搜索的炫彩渐变与浮夸动画,呈现严谨、专注且克制的学术工程质感。

第一章:需求剖析与“反 AI 廉价感”的前端重塑
1.1 传统 AI 生成前端的三大问题
在日常使用 Cursor、Claude 或 Windsurf 进行 Vibe Coding 时,如果你只给出一句“帮我写一个 AI 搜索引擎”,AI 交付的前端代码通常会暴露出三个致命缺陷:
- 问题一:视觉上的花哨廉价感。受海量公开网页与低质模板训练影响,AI 往往对“紫蓝渐变、巨大毛玻璃、无序发光描边与 Emoji 滥用”有着病态的偏好。生成的界面像是一个堆砌特效的 Demo 玩具,完全无法用于严肃的学术阅读与技术推演;
- 问题二:虚假 SaaS 控件堆砌。AI 会盲目套用商业化模板,臆造出大量未实装的假控件——比如无法点击的多模型下拉切换、没有后端支撑的用户登录头像、虚假的订阅升级按钮。界面上每三个按钮就有一个“不通电”;
- 问题三:状态失控与面条代码。缺乏架构约束的 AI 会在一个 2000 行的庞大组件里混杂数据拉取、状态控制与 DOM 渲染。用户一旦按
Esc键,界面就会陷入搜索中与结果页并存的逻辑死锁,改动一行代码引发连锁崩塌。
1.2 现代专业级 AI 搜索的真实需求
我们的目标受众是系统架构师、算法研究员与深度工程师。这类用户检索的是诸如“NVIDIA Hopper TMA 异步拷贝指令优化”或“FlashAttention-3 FP8 内核吞吐基准”等硬核课题。他们需要的不是黑盒 Spinner 等待,而是:
- 瑞士网格极简学术排版:高信噪比排印,冷瓷白底色搭配 1px 细边框,专注阅读本身;
- 多步推理全透明可观测:清楚知道系统重写了哪些检索词、并发抓取了哪些信源,以及每一个推演阶段精确消耗了多少毫秒;
- 段落级可追溯证据链(Grounding Snapshot):正文每一个断言都有对应的角标,点击即可联动还原原文高亮切片与 BibTeX;
- 全键盘极客工效学:支持
⌘K全局唤醒聚焦与⌘B侧边栏折叠,零鼠标完成高效检索。
如表 1 所示,我们在核心维度上对项目定位进行了严格的工程对齐:
表 1:普通 AI 玩具项目 vs 本项目工业级定位对比
| 维度 | 普通 AI 玩具项目 | 本项目工业级定位 |
|---|---|---|
| 视觉基调 | 紫蓝炫光、粗暴毛玻璃、营销口号 | Apple Cupertino Light 极简冷调、瑞士学术排版 |
| 功能真实性 | 臆造假模型切换、假个人中心 | 100% 真实契约:本地 DeepSeek、SQLite、真实检索 |
| 状态流转 | 随意散漫、状态混乱死锁 | 严格单向三态状态机(initial $\rightarrow$ retrieving $\rightarrow$ result) |
| 推演过程 | 单一 Spinner + “正在生成...” | HeronStepper 垂直时间轴 + 检索推演全景观测台 |
| 信源证据 | 死链接列表、无法定位段落 | 证据抽屉(EvidenceDrawer)+ 琥珀黄段落切片还原 |
| 硬件展示 | 纯文本 Markdown 表格 | OpenUI 混合生成式 UI(交互卡片与 DSL 源码切换) |
第二章:系统整体架构与三态流转状态机
2.1 整体架构设计(前端驱动契约)
系统采用前后端解耦的现代全栈架构:
- 前端工作台:Next.js 15(App Router,React 19)+ Tailwind CSS v3 + Lucide Icons + Framer Motion;
- 后端服务层:Python FastAPI + LangChain + Uvicorn + SQLite 本地持久化;
- 模型与检索连接:DeepSeek 官方 API(采用最新
deepseek-flash架构,支持 1M 上下文)+ Tavily Search API 多源聚合 + QuickRouter Jev-1.14.0 决策模型。
检索流水线严格遵循“前端驱动契约”(Frontend-Driven Contract)。后端通过全双工 SSE 流式长连接持续推送标准事件,前端状态机据此精确驱动界面渲染。核心业务流程图如下:

2.2 确定性状态机契约(三态流转)
为彻底消除多条件并存导致的界面紊乱,我们在前端确立了唯一的三态状态机(SearchLifecycleState)。

状态只允许沿固定路径单向流转:
- 屏幕 1:首屏命令台(
initial):视口宽度居中 760px,如图 1 所示,包含 Asterisk 极简图腾、Superbar复合搜索框与精选探针胶囊。无任何冗余信息; - 屏幕 2:Heron 多步透明化推理态(
retrieving):双栏流水线展开至 1040px,如图 3 所示,左 7 栏渲染HeronStepper垂直时间轴,右 5 栏通过LiveSourceRadar呈现并发抓取雷达与骨架过渡; - 屏幕 3:综合研报与双向证据工作台(
result):如图 4 所示,研报正文与EvidenceDrawer形成 60/40 双栏切片工作台,同时支持一键切入“推演全景观测台”。
如图 3 所示,当用户提交检索词后,界面立即进入平滑推进的 Retrieving 推演态:

如图 4 所示,一旦推演完成,界面平滑过渡至 Result 态综合研报工作台。这是系统最核心的工作台视口:顶部常驻 Academic Control Strip 极简学术控制条与 ReasoningAccordion 折叠抽屉(标明阶段完成数与总耗时),正文呈现高浓度技术排版、Key Takeaways 核心要点卡片与公式,右侧 40% 则是高保真文献证据抽屉:

第三章:让 AI 写出顶级前端的 Vibe Coding 最佳实践(Stitch + Impeccable)
让 AI 写出工业级代码的核心不在于更长的 Prompt,而在于构建一堵“让 AI 无法走样”的工程围墙。我们在本项目中践行了 Stitch 与 Impeccable 双剑合璧的实战体系。
3.1 第一道锁:Google Stitch 构建视觉世界与高保真原型
传统 AI 编程的根本问题在于:AI 在写代码前根本不知道最终产品长什么样。
我们在写第一行代码前,优先使用 Google Stitch(Stitch MCP)进行云端高保真设计探索:
- 在 Stitch 中通过结构化提示词分别生成 Screen 1(首屏命令台)、Screen 2(Heron 推理流水线)与 Screen 3(研报证据工作台)的现代 Cupertino Light 变体;
- 将确立的原型导出为自包含的静态 HTML 与高保真设计工件,持久化至项目的
.stitch/designs/目录; - 将这些静态资产作为后续 AI 编码不可违背的“视觉真理”(Visual Truth)。当 AI 编写组件遇到布局疑问时,直接比对 Stitch 设计工件,从根本上锁死视觉边界。
3.2 第二道锁:/impeccable 从产品定义到设计系统 Token 锁死
有了视觉原型后,需要一套工程化的设计规约将审美下沉为代码约束。我们通过 /impeccable 工具链构建了项目宪章:
- 运行
/impeccable init生成PRODUCT.md:确定产品模式为Operate & Read(任务操作与高浓度阅读),明确服务于资深开发者,严禁添加任何消费级 SaaS 的套路控件; - 运行
/impeccable document生成DESIGN.md:提取 Apple Cupertino Light 极简语义变量,严格禁止硬编码十六进制颜色与魔法数字; - 落实 Craft Floor 工艺底线:在代码审查中自动拦截内联样式(
style={{}})、杂乱圆角与未经定义的渐变。
表 2:DESIGN.md 核心语义变量规范(Tokens)
| Token 变量 | 物理色值 | 语义角色与使用准则 |
|---|---|---|
background | #FBFBFD | 全局超浅冷灰背景,提供冷调纯净质感 |
surface | #FFFFFF | 主研报面板、工作台与核心卡片底色 |
surface-secondary | #F5F5F7 | 输入框底色、次级胶囊与代码块内衬 |
border-light | #E5E5EA | 视网膜 1px 分割线与默认超细线框 |
primary | #0071E3 | Apple Blue 核心动作按钮与焦点微动效 |
evidence | #F59E0B | 证据切片高亮(琥珀黄),文献精准段落还原 |
emerald | #10B981 | 推理完成标记与共识度徽章 |
如图所示,在 Superbar 复合控制台中,用户点击左侧 + 图标即可呼出权威信源过滤选单。整个弹窗的底色、圆角、分割线与激活蓝均严格对齐表 2 的 Design Tokens,没有任何野生的临时样式:

3.3 第三道锁:宪法级规约 AGENTS.md 与类型不变性
我们在根目录建立了不可违背的 AGENTS.md 规范文件,并强制要求 AI 在每一个会话中优先加载。
其中最具生产力的两条工程铁律是:
- 本地真实性铁律:严禁臆造不存在的功能。界面上的每一个按钮必须直连真实后端端点;
- 只读契约(
readonly不变性):所有 React 组件属性必须使用readonly修饰,类型必须精确导出。
如代码清单 1 所示,这是我们在 src/components/MarkdownReport.tsx 中定义的标准接口契约。严格的只读属性消除了副作用,保证了流式渲染过程中的单向数据流动:
▼typescript复制代码// 代码清单 1: src/components/MarkdownReport.tsx (组件只读契约规范) import type { ResearchReport, SourceCard } from "@/types"; export interface MarkdownReportProps { readonly report: ResearchReport; readonly isStreaming?: boolean; readonly streamedText?: string; readonly activeSourceId?: string | null; readonly onSelectSource?: (id: string) => void; readonly onHoverBadge?: (badgeNumber: number | null) => void; readonly sources?: readonly SourceCard[]; }
3.4 人在环路:渐进式切片开发工作流
很多开发者使用 AI 编程失败的原因在于试图“一次性生成整套系统”。我们在本项目中坚持小步快跑的四步闭环:
- 原型锚定:Stitch 生成界面并确认视觉基准;
- 规约固化:Impeccable 生成
DESIGN.md与AGENTS.md; - 单切片生成:单次对话仅聚焦单一组件或单一端点(例如仅实现
Superbar.tsx,保持接口独立); - 自测验收:在浏览器中启动服务并进行全键盘走查,确认无误后再进入下一模块。
第四章:核心技术模块深度落地与源码实战
4.1 模块一:全双工 SSE 流式推演与全链路可观测体系 (Observability Pipeline)
传统 AI 搜索的痛点在于“黑盒等待”:用户提交一个问题,界面只转一个无聊的加载圈,没人知道后台正在干什么。
我们在本项目中构建了全链路毫秒级精度的可观测流水线。
4.1.1 后端阶段耗时细粒度埋点与 SSE 事件流
后端利用 time.perf_counter(),将每次检索精确拆解为四个离散阶段,并对外持续广播五类标准化事件:
event: trace:推送当前多步执行进度(rewriting、searching、reasoning、generating);event: sources:实时推送信源卡片列表;event: token:LLM 逐字推导内容增量;event: related:动态给出延伸追问建议;event: done:包含总耗时latencyMs、阶段分段用时切片与tokenStats。
如代码清单 2 所示,我们在 backend/app/main.py 的检索主循环中,精准采集各个阶段耗时,并嵌入了客户端断开连接感知逻辑(request.is_disconnected()),用户按 Esc 即可毫秒级中止下游 LLM 与网络任务:
▼python复制代码# 代码清单 2: backend/app/main.py (各阶段耗时追踪与客户端中断感知) @app.post("/api/search") async def search_endpoint(request: Request, search_req: SearchRequest): async def sse_event_generator() -> AsyncGenerator[str, None]: t_start = time.perf_counter() # 1. 意图消歧与重写 yield format_sse("trace", {"step": "rewriting", "message": "正在解析查询意图..."}) t0 = time.perf_counter() queries = await rewriter.rewrite(search_req.query, search_req.focusMode) rewriting_ms = int((time.perf_counter() - t0) * 1000) # 客户端中断校验 if await request.is_disconnected(): logger.info("客户端连接中止,释放检索任务") return # 2. 多源网络检索 yield format_sse("trace", {"step": "searching", "message": f"正在并发检索 {len(queries)} 组关键词..."}) t1 = time.perf_counter() raw_results = await search_with_fallback(queries, engine=search_req.engine) searching_ms = int((time.perf_counter() - t1) * 1000) # 3. Jev 分类与多目标重排 yield format_sse("trace", {"step": "reasoning", "message": "正在进行权威度评估与高熵分类..."}) t2 = time.perf_counter() filtered_results = filter_by_scopes(raw_results, search_req.scopeFilters) classified_sources = await classifier.batch_classify(filtered_results) ranked_sources = ranker.rank(classified_sources, search_req.focusMode) reasoning_ms = int((time.perf_counter() - t2) * 1000) # 广播信源列表 yield format_sse("sources", [s.model_dump() for s in ranked_sources]) # 4. LLM 研报流式生成 full_text = [] async for chunk in generator.generate_stream(search_req.query, ranked_sources): if await request.is_disconnected(): return full_text.append(chunk) yield format_sse("token", {"delta": chunk}) total_latency_ms = int((time.perf_counter() - t_start) * 1000) yield format_sse("done", { "latencyMs": total_latency_ms, "stageTimings": { "rewritingMs": rewriting_ms, "searchingMs": searching_ms, "reasoningMs": reasoning_ms, }, "consensusScore": "100% 交叉验证一致" }) return StreamingResponse(sse_event_generator(), media_type="text/event-stream")
4.1.2 双重视角与推演全景观测台(Twin-View Observability)
前端对后端推送的阶段遥测进行了多维度的立体映射:
HeronStepper垂直时间轴:如图 3 所示,左侧步骤轴清晰标出每一步的运行状态与耗时计数器(如234ms),点击可展开内部独白(Monologue)与重写检索词;ReasoningAccordion研报顶部抽屉:如图 4 所示,长文研报上方常驻耗时微标(⚡ 3/3 阶段完成 · 耗时 0.6s | 6 篇证据文献);- Twin-View 视角 2:检索推演全景观测台(Panoramic Observability Deck):研报完成后,用户可在顶部随时切至“推演全景观测台”。如图 5 所示,系统 1:1 完整保留与复原检索推演全景,左 7 栏呈现 100% 进度、执行耗时与各步骤独白,右 5 栏呈现 10 路权威信源的分类雷达,点击任一信源卡片可无缝切回研报证据抽屉。

4.2 模块二:OpenUI 混合生成式 UI 与紧凑 DSL 实战
在传统的技术研报中,大模型生成的 Markdown 评测表格不仅排版脆弱,而且缺乏交互性。而如果让模型直接输出复杂 JSON 渲染 React 组件,又会引发严重的 Token 膨胀与高延迟。
我们引入了 Hybrid Generative UI 架构:90% 正文保持高信息密度的 Markdown,10% 高熵硬件评测与时序图则交由 OpenUI Lang DSL 动态驱动。
4.2.1 紧凑行式 DSL 语法优势
OpenUI 采用行式声明 DSL 语法,相比标准 JSON,Token 消耗降低约 67%,且原生支持流式逐行解析:
▼text复制代码HardwareBenchmarkCard(title="FlashAttention-3 vs FlashAttention-2 吞吐对比 [1]") column(key="kernel", label="内核架构", sortable=false) column(key="precision", label="精度", sortable=false) column(key="tflops", label="FP16/FP8 吞吐 (TFLOPS)", sortable=true, highlight=true) column(key="gain", label="相比 FA2 提速", sortable=false) row(kernel="FlashAttention-2", precision="FP16", tflops="350", gain="基准 1.0x") row(kernel="FlashAttention-3", precision="FP16", tflops="740", gain="+111% (2.1x) [1]") row(kernel="FlashAttention-3 (FP8)", precision="FP8", tflops="1200", gain="+242% (3.4x) [2]")
4.2.2 双重视角(Twin-Mode)与容错渲染器
每一个 OpenUI 组件均由统一的 OpenUIBlock 容器承载,顶部常驻“✨ 交互视图”与“< / > DSL 源码”切换按钮,专为资深架构师提供微码级别的审查体验。
如代码清单 3 所示,我们结合了 @openuidev/react-lang 的 Renderer,并封装了全局错误边界与引用上下文:
▼tsx复制代码// 代码清单 3: src/components/openui/OpenUIBlock.tsx (OpenUI 动态渲染与双重视角切换) "use client"; import React, { useState } from "react"; import { Renderer } from "@openuidev/react-lang"; import { openUILibrary } from "./library"; import { OpenUIContextProvider } from "./OpenUIContext"; import type { SourceCard } from "@/types"; export interface OpenUIBlockProps { readonly code: string; readonly sources?: readonly SourceCard[]; readonly onSelectSource?: (id: string) => void; } export const OpenUIBlock: React.FC<OpenUIBlockProps> = ({ code, sources, onSelectSource }) => { const [viewMode, setViewMode] = useState<"interactive" | "dsl">("interactive"); return ( <div className="rounded-apple border border-border-light bg-surface overflow-hidden my-4"> {/* 顶部 Twin-Mode 控制条 */} <div className="flex items-center justify-between px-3 py-1.5 border-b border-border-light bg-surface-secondary text-xs"> <div className="flex gap-2"> <button onClick={() => setViewMode("interactive")} className={`px-2 py-0.5 rounded font-medium ${viewMode === "interactive" ? "bg-surface text-primary shadow-sm" : "text-text-tertiary"}`} > ✨ 交互视图 </button> <button onClick={() => setViewMode("dsl")} className={`px-2 py-0.5 rounded font-medium ${viewMode === "dsl" ? "bg-surface text-primary shadow-sm" : "text-text-tertiary"}`} > < / > DSL 源码 </button> </div> </div> {viewMode === "interactive" ? ( <OpenUIContextProvider sources={sources} onSelectSource={onSelectSource}> <div className="p-3"> <Renderer code={code} library={openUILibrary} /> </div> </OpenUIContextProvider> ) : ( <pre className="p-4 bg-[#1D1D1F] text-[#F5F5F7] text-xs font-mono overflow-x-auto leading-relaxed"> <code>{code}</code> </pre> )} </div> ); };
如图 6 所示,这是系统中渲染出的真实 OpenUI 交互卡片。上方瑞士网格硬件基准对比表支持点击列头动态升降序排序,重点行高亮显示,并内嵌可点击的 [1] 引用标签;下方紧随微码执行流水线时钟卡片(阶段涵盖 TMA 异步加载、Tensor Core 计算与 SMEM 写入),点击即可展开时延详情。用户点击右上角 < / > DSL 源码 即可瞬间查看底层代码:

4.3 模块三:多源检索、Scope 四阶物理过滤与权威时效重排机制
4.3.1 多引擎适配与平滑降级
后端在 backend/app/pipeline/providers/ 中建立了统一的 SearchProvider 抽象基类,并分别实装了:
DeepSeekSearchProvider:直连 DeepSeek 搜索端点,适合针对长上下文的技术文献精细解析;TavilySearchProvider:深度网络聚合搜索,支持按advanced深度并发抓取。
前端 Superbar 右下角提供引擎切换下拉菜单。当外部搜索网络遭遇抖动时,系统自动执行毫秒级平滑降级,确保流程始终稳定推进。
4.3.2 Scope 范围四阶物理过滤与防穿透体系
许多 AI 项目的过滤机制仅在界面上隐藏某些条目,导致模型依然根据无关信源生成回答。我们建立了四阶防穿透流水线:

如图 2(信源过滤选单)与过滤流转图所示,系统在四个关键节点实施严密拦截:
- 阶梯 1(意图重写级注入):未勾选预印本时,重写 Prompt 明确禁止生成
arXiv、paper、survey等论文检索词; - 阶梯 2(服务 API 级排除):在 Tavily API 请求体中动态传入
exclude_domains: ["arxiv.org", "openreview.net"]; - 阶梯 3(物理裁切):在多源结果合并时,调用
is_source_allowed_by_scopes()基于域名黑白名单直接丢弃违规条目; - 阶梯 4(语义防穿透):强制纠正分类器可能对非预印本产生的误判,并清空
bibtex引用,确保证据链彻底合规。
4.3.3 多目标重排打分引擎 (SearchRanker)
对通过过滤的信源,SearchRanker 依据权威度、时效性与模态自适应动态计算综合得分:
$$\text{final_score} = w_{rel} \cdot \text{relevance} + w_{auth} \cdot \text{authority} + w_{fresh} \cdot \text{freshness}$$
如代码清单 4 所示,我们在 backend/app/pipeline/ranker.py 中实现了学术与代码模态下的动态加权公式:
▼python复制代码# 代码清单 4: backend/app/pipeline/ranker.py (多目标自适应打分公式) class SearchRanker: @staticmethod def rank(results: List[RawSearchResult], focus_mode: str = "web") -> List[RawSearchResult]: # 模态自适应权重配置 weights = { "academic": {"rel": 0.30, "auth": 0.50, "fresh": 0.20}, "code": {"rel": 0.40, "auth": 0.40, "fresh": 0.20}, "web": {"rel": 0.50, "auth": 0.35, "fresh": 0.15}, } w = weights.get(focus_mode, weights["web"]) for item in results: auth_score = SearchRanker.compute_authority_score(item) fresh_score = SearchRanker.compute_freshness_score(item) rel_score = item.score or 0.8 item.final_score = ( w["rel"] * rel_score + w["auth"] * auth_score + w["fresh"] * fresh_score ) return sorted(results, key=lambda x: x.final_score, reverse=True)
4.4 模块四:QuickRouter Jev 毫秒级决策分类器
传统方案使用通用 LLM 对网页进行结构化分类,不仅需要花费 2~4 秒的串行推理耗时,而且输出格式极易损坏。
我们接入了专有的 System One 决策模型 jev-1.14.0:
- 非生成式确定性决策:模型绕过逐字生成的冗长自回归计算,以确定性概率(
Choice与Noul原语)在 50ms 级别直接输出概率分布; - 细粒度技术分类:精确归类为
学术预印本 (ArXiv)、底层硬件与协议规范 (Spec / RFC)、开源代码实现 (GitHub)、框架官方开发文档与深度架构评测实践; - 双重弹性保障:URL 级内存哈希缓存(同链接 0ms 命中)搭配本地启发式域名规则兜底,确保系统 100% 不会因外部网络异常而中断。
在图 3、图 5 与图 7 的信源卡片与雷达中,你可以清晰看到 Jev 分类赋予的权威彩色徽章(如绿色的 学术预印本 (ArXiv) 与蓝色的 开源代码实现 (GitHub))。
如代码清单 5 所示,这是 backend/app/pipeline/classifier.py 中的关键调用与回退逻辑:
▼python复制代码# 代码清单 5: backend/app/pipeline/classifier.py (Jev 决策模型与本地规则回退) class JevClassifier: async def classify(self, url: str, title: str, snippet: str) -> Tuple[str, float]: if url in self._cache: return self._cache[url] try: async with httpx.AsyncClient(timeout=2.5) as client: res = await client.post( self._endpoint, headers=self._headers, json={ "model": "jev-1.14.0", "prompt": f"Title: {title}\nSnippet: {snippet}\nURL: {url}", "choices": ["academic", "code", "spec", "framework_docs", "technical_blog"], } ) choice = res.json().get("choice", "technical_blog") confidence = float(res.json().get("probability", 0.95)) except Exception: # 外部请求超时或异常,平滑降级至本地启发式正则引擎 choice, confidence = self._fallback_infer(urlparse(url).netloc, url) label = CATEGORY_MAPPING[choice]["default"] self._cache[url] = (label, confidence) return label, confidence
4.5 模块五:双层 TTL 内存缓存引擎与 SQLite 本地持久化存储机制
4.5.1 双层 TTL 内存缓存架构 (SearchCache)
为了兼顾响应极速与开发成本,我们在后端实现了 coroutine-safe 的双层 TTL 缓存引擎:
- 流水线全量缓存:缓存完整的研报正文与结构化信源列表(默认 TTL 600s)。命中后跳过搜索与大模型生成,通过模拟平滑 SSE 流毫秒级回放, Token 消耗为 0;
- 提供商子检索词缓存:针对重写后的子检索词进行独立缓存,极大提升跨查询复用率;
- 隔离 Key 归一化:Key 由
engine、focus_mode、scope_filters、is_deep与query联合计算而成,杜绝常规搜索与深度研究发生缓存踩踏。
4.5.2 SQLite 本地异步持久化引擎 (backend/app/db/database.py)
系统坚持 Local-First 理念,无需部署外部 MySQL 或 Redis,直接基于轻量级标准库 sqlite3 实现会话快照:
- 异步线程隔离:全面采用
asyncio.to_thread封装数据库读写,彻底消除 SQLite 同步 I/O 对 FastAPI 异步事件循环的阻塞; - 1:1 会话快照还原:完整记录每次研报的
query、sources(含高保真原文切片)、report_text、latency_ms、source_count与char_count; - 侧边栏联动:如图 8 所示,前端
Sidebar.tsx提供 260px 展开态与 68px 紧凑态,清晰列出持久化到本地 SQLite 的历史检索课题。用户点击任一历史记录,系统直接从本地数据库提取完整快照,以 0ms 瞬间恢复至 Result 态工作台,无需重新消耗外部 API 额度。

如代码清单 6 所示,这是 backend/app/db/database.py 中异步持久化会话快照的核心代码:
▼python复制代码# 代码清单 6: backend/app/db/database.py (SQLite 异步线程持久化引擎) async def save_history( query: str, focus_mode: str, sources: List[Dict[str, Any]], report_text: str, latency_ms: int = 0, **kwargs ) -> str: def _sync_insert() -> str: conn = _get_connection() history_id = f"hist_{int(time.time())}_{uuid.uuid4().hex[:8]}" now = time.time() with conn: conn.execute(""" INSERT INTO search_history ( id, query, focus_mode, scope_filters, is_deep_research, consensus_score, takeaways, sources, report_text, follow_ups, latency_ms, source_count, char_count, created_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( history_id, query, focus_mode, json.dumps(kwargs.get("scope_filters", [])), 1 if kwargs.get("is_deep_research", True) else 0, kwargs.get("consensus_score", "100% 交叉验证一致"), json.dumps(kwargs.get("takeaways", [])), json.dumps(sources), report_text, json.dumps(kwargs.get("follow_ups", [])), latency_ms, len(sources), len(report_text), now, now )) return history_id return await asyncio.to_thread(_sync_insert)
4.6 模块六:段落级证据抽屉与双向联动(Grounding Snapshot)
许多搜索引擎的行内引用只是简单跳转到外部链接,用户依然需要在一篇几万字的论文里翻找依据。
我们构建了高保真的 Grounding Snapshot 机制:
- 正文可交互角标:所有的
[1]、[2]角标均被解析为交互式CitationPill; - 证据抽屉精准还原:用户点击或悬浮在角标上时,右侧固定 40% 宽度的
EvidenceDrawer立即自动平滑滚动至对应信源卡片; - 原文切片高亮:如图 7 所示,信源卡片直接展示抓取到的原文段落,并以琥珀黄(
#F59E0B/#FEF3C7底衬)精准高亮匹配句子,同时提供 BibTeX 格式一键复制与 PDF 直达链接。

总结
回顾整个项目的构建过程,我们不仅交付了一款高保真的专业级 AI 搜索引擎,更验证了一套现代 AI 时代的软件工程方法论。 在 Vibe Coding 时代,AI 并没有消灭工程师的价值,而是放大了系统架构设计、接口契约与工程品味的重要性。
- 如果你把 AI 当作“全包打工人”,只给一两句模糊的提示词,那么得到的注定是廉价的塑料界面与混乱的代码;
- 但如果你充当严密的系统架构师与设计总监——用 Google Stitch 建立视觉真理,用 /impeccable 锁定设计系统与工艺底线,用 AGENTS.md 锁死状态机契约与类型接口——AI 就会成为你手中极其强悍的研发外骨骼。
