Vibe Coding 实战:从零构建现代专业级 AI 搜索引擎

写在前面

大家好,我是不会喷火的小火龙。这是一份面向个人开发者的实战教程。我们将从零开始,使用现代技术栈(Next.js 15 + FastAPI + DeepSeek-Flash),完整构建一款专为技术研报与深度工程设计的专业级 AI 搜索引擎——XHL AI Search。

本教程重点介绍掌握以下两套核心技能:

  1. 让 AI 写出顶级前端的 Vibe Coding 最佳实践:掌握 Google Stitch 视觉锚定、/impeccable 工艺硬约束与 AGENTS.md 宪法级规约的联动,让 AI 告别粗制滥造的蓝紫渐变与塑料质感;
  2. 六大工业级核心技术模块全栈实战:
    • 全双工 SSE 流式推演与全链路毫秒级可观测体系(Observability);
    • OpenUI 混合生成式 UI 与紧凑行式 DSL(比标准 JSON 节省 67% Token);
    • 多源检索路由、Scope 四阶物理与语义防穿透过滤、多目标权威时效重排;
    • QuickRouter TypeSafe Jev 毫秒级非生成式决策分类;
    • 双层 TTL 内存隔离缓存与 SQLite 异步持久化会话快照引擎;
    • 原文段落级证据抽屉与高保真双向高亮联动(Grounding Snapshot)。

如图 1 所示,这是我们最终交付的系统首屏命令台。整体采用极简冷调的 Apple Cupertino Light 风格,彻底摒弃传统 AI 搜索的炫彩渐变与浮夸动画,呈现严谨、专注且克制的学术工程质感。

image.png


第一章:需求剖析与“反 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 等待,而是:

  1. 瑞士网格极简学术排版:高信噪比排印,冷瓷白底色搭配 1px 细边框,专注阅读本身;
  2. 多步推理全透明可观测:清楚知道系统重写了哪些检索词、并发抓取了哪些信源,以及每一个推演阶段精确消耗了多少毫秒;
  3. 段落级可追溯证据链(Grounding Snapshot):正文每一个断言都有对应的角标,点击即可联动还原原文高亮切片与 BibTeX;
  4. 全键盘极客工效学:支持 ⌘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 流式长连接持续推送标准事件,前端状态机据此精确驱动界面渲染。核心业务流程图如下:

image.png

2.2 确定性状态机契约(三态流转)

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

image.png

状态只允许沿固定路径单向流转:

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

如图 3 所示,当用户提交检索词后,界面立即进入平滑推进的 Retrieving 推演态:

image.png

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

image.png


第三章:让 AI 写出顶级前端的 Vibe Coding 最佳实践(Stitch + Impeccable)

让 AI 写出工业级代码的核心不在于更长的 Prompt,而在于构建一堵“让 AI 无法走样”的工程围墙。我们在本项目中践行了 Stitch 与 Impeccable 双剑合璧的实战体系。

3.1 第一道锁:Google Stitch 构建视觉世界与高保真原型

传统 AI 编程的根本问题在于:AI 在写代码前根本不知道最终产品长什么样。

我们在写第一行代码前,优先使用 Google Stitch(Stitch MCP)进行云端高保真设计探索:

  1. 在 Stitch 中通过结构化提示词分别生成 Screen 1(首屏命令台)、Screen 2(Heron 推理流水线)与 Screen 3(研报证据工作台)的现代 Cupertino Light 变体;
  2. 将确立的原型导出为自包含的静态 HTML 与高保真设计工件,持久化至项目的 .stitch/designs/ 目录;
  3. 将这些静态资产作为后续 AI 编码不可违背的“视觉真理”(Visual Truth)。当 AI 编写组件遇到布局疑问时,直接比对 Stitch 设计工件,从根本上锁死视觉边界。

3.2 第二道锁:/impeccable 从产品定义到设计系统 Token 锁死

有了视觉原型后,需要一套工程化的设计规约将审美下沉为代码约束。我们通过 /impeccable 工具链构建了项目宪章:

  1. 运行 /impeccable init 生成 PRODUCT.md:确定产品模式为 Operate & Read(任务操作与高浓度阅读),明确服务于资深开发者,严禁添加任何消费级 SaaS 的套路控件;
  2. 运行 /impeccable document 生成 DESIGN.md:提取 Apple Cupertino Light 极简语义变量,严格禁止硬编码十六进制颜色与魔法数字;
  3. 落实 Craft Floor 工艺底线:在代码审查中自动拦截内联样式(style={{}})、杂乱圆角与未经定义的渐变。

表 2:DESIGN.md 核心语义变量规范(Tokens)

Token 变量物理色值语义角色与使用准则
background#FBFBFD全局超浅冷灰背景,提供冷调纯净质感
surface#FFFFFF主研报面板、工作台与核心卡片底色
surface-secondary#F5F5F7输入框底色、次级胶囊与代码块内衬
border-light#E5E5EA视网膜 1px 分割线与默认超细线框
primary#0071E3Apple Blue 核心动作按钮与焦点微动效
evidence#F59E0B证据切片高亮(琥珀黄),文献精准段落还原
emerald#10B981推理完成标记与共识度徽章

如图所示,在 Superbar 复合控制台中,用户点击左侧 + 图标即可呼出权威信源过滤选单。整个弹窗的底色、圆角、分割线与激活蓝均严格对齐表 2 的 Design Tokens,没有任何野生的临时样式:

image.png

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 编程失败的原因在于试图“一次性生成整套系统”。我们在本项目中坚持小步快跑的四步闭环:

  1. 原型锚定:Stitch 生成界面并确认视觉基准;
  2. 规约固化:Impeccable 生成 DESIGN.md 与 AGENTS.md;
  3. 单切片生成:单次对话仅聚焦单一组件或单一端点(例如仅实现 Superbar.tsx,保持接口独立);
  4. 自测验收:在浏览器中启动服务并进行全键盘走查,确认无误后再进入下一模块。

第四章:核心技术模块深度落地与源码实战

4.1 模块一:全双工 SSE 流式推演与全链路可观测体系 (Observability Pipeline)

传统 AI 搜索的痛点在于“黑盒等待”:用户提交一个问题,界面只转一个无聊的加载圈,没人知道后台正在干什么。

我们在本项目中构建了全链路毫秒级精度的可观测流水线。

4.1.1 后端阶段耗时细粒度埋点与 SSE 事件流

后端利用 time.perf_counter(),将每次检索精确拆解为四个离散阶段,并对外持续广播五类标准化事件:

  1. event: trace:推送当前多步执行进度(rewriting、searching、reasoning、generating);
  2. event: sources:实时推送信源卡片列表;
  3. event: token:LLM 逐字推导内容增量;
  4. event: related:动态给出延伸追问建议;
  5. 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)

前端对后端推送的阶段遥测进行了多维度的立体映射:

  1. HeronStepper 垂直时间轴:如图 3 所示,左侧步骤轴清晰标出每一步的运行状态与耗时计数器(如 234ms),点击可展开内部独白(Monologue)与重写检索词;
  2. ReasoningAccordion 研报顶部抽屉:如图 4 所示,长文研报上方常驻耗时微标(⚡ 3/3 阶段完成 · 耗时 0.6s | 6 篇证据文献);
  3. Twin-View 视角 2:检索推演全景观测台(Panoramic Observability Deck):研报完成后,用户可在顶部随时切至“推演全景观测台”。如图 5 所示,系统 1:1 完整保留与复原检索推演全景,左 7 栏呈现 100% 进度、执行耗时与各步骤独白,右 5 栏呈现 10 路权威信源的分类雷达,点击任一信源卡片可无缝切回研报证据抽屉。

image.png


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"}`} > &lt; / &gt; 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 源码 即可瞬间查看底层代码:

fig6_openui_benchmark.png


4.3 模块三:多源检索、Scope 四阶物理过滤与权威时效重排机制

4.3.1 多引擎适配与平滑降级

后端在 backend/app/pipeline/providers/ 中建立了统一的 SearchProvider 抽象基类,并分别实装了:

  • DeepSeekSearchProvider:直连 DeepSeek 搜索端点,适合针对长上下文的技术文献精细解析;
  • TavilySearchProvider:深度网络聚合搜索,支持按 advanced 深度并发抓取。

前端 Superbar 右下角提供引擎切换下拉菜单。当外部搜索网络遭遇抖动时,系统自动执行毫秒级平滑降级,确保流程始终稳定推进。

4.3.2 Scope 范围四阶物理过滤与防穿透体系

许多 AI 项目的过滤机制仅在界面上隐藏某些条目,导致模型依然根据无关信源生成回答。我们建立了四阶防穿透流水线:

image.png

如图 2(信源过滤选单)与过滤流转图所示,系统在四个关键节点实施严密拦截:

  1. 阶梯 1(意图重写级注入):未勾选预印本时,重写 Prompt 明确禁止生成 arXiv、paper、survey 等论文检索词;
  2. 阶梯 2(服务 API 级排除):在 Tavily API 请求体中动态传入 exclude_domains: ["arxiv.org", "openreview.net"];
  3. 阶梯 3(物理裁切):在多源结果合并时,调用 is_source_allowed_by_scopes() 基于域名黑白名单直接丢弃违规条目;
  4. 阶梯 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 缓存引擎:

  1. 流水线全量缓存:缓存完整的研报正文与结构化信源列表(默认 TTL 600s)。命中后跳过搜索与大模型生成,通过模拟平滑 SSE 流毫秒级回放, Token 消耗为 0;
  2. 提供商子检索词缓存:针对重写后的子检索词进行独立缓存,极大提升跨查询复用率;
  3. 隔离 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 额度。

image.png

如代码清单 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. 正文可交互角标:所有的 [1]、[2] 角标均被解析为交互式 CitationPill;
  2. 证据抽屉精准还原:用户点击或悬浮在角标上时,右侧固定 40% 宽度的 EvidenceDrawer 立即自动平滑滚动至对应信源卡片;
  3. 原文切片高亮:如图 7 所示,信源卡片直接展示抓取到的原文段落,并以琥珀黄(#F59E0B / #FEF3C7 底衬)精准高亮匹配句子,同时提供 BibTeX 格式一键复制与 PDF 直达链接。

image.png

总结

回顾整个项目的构建过程,我们不仅交付了一款高保真的专业级 AI 搜索引擎,更验证了一套现代 AI 时代的软件工程方法论。 在 Vibe Coding 时代,AI 并没有消灭工程师的价值,而是放大了系统架构设计、接口契约与工程品味的重要性。

  • 如果你把 AI 当作“全包打工人”,只给一两句模糊的提示词,那么得到的注定是廉价的塑料界面与混乱的代码;
  • 但如果你充当严密的系统架构师与设计总监——用 Google Stitch 建立视觉真理,用 /impeccable 锁定设计系统与工艺底线,用 AGENTS.md 锁死状态机契约与类型接口——AI 就会成为你手中极其强悍的研发外骨骼。

项目开源地址:https://github.com/sz-xiaohuolong/xhl-ai-search

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
不会喷火的小火龙
下载 APP