编程导航交流话题讨论

交流

1.3k 参与
分享

快来分享你的内容吧~

点击登录,快来和大家讨论吧~
表情
图片
话题
打卡
综合
交流
文章
问答

AI 时代少有人类阅读,我开始建自己的独立站,但真的是 49 年入国军吗

前几天,我在一个平台发了条动态,预告自己在做的一个写作发布工具。底下有人说没啥用,不如做个抢优惠券的;也有人说别自嗨。 我没有生气,也没往心里去,但它让我想了很久。平台上的读者来自四面八方,关心的东西差别很大,同一件事在不同的人眼里,价值完全不同,这很正常。我真正在意的是另一件事:我想写的东西,是不是只能放进推荐流里,等一个不确定的结果? 这让我重新审视了一个老问题:在 AI 时代,个人博客还有什么位置,能起什么作用。 先说难听的:我知道现在开独立博客很傻。 平台有推荐,AI 有摘要,人连搜索结果都懒得点。新域名没有权重,没有外链,没有推荐位,写一篇长文发出去,阅读量个位数是常态。这不是“内容不够好”的问题,是分发结构就这样。所以问题不是“博客还有没有价值”,而是:**在一个人人都不直接访问网站的时代,我为什么还要建一个自己的站?** ![site-home-light](https://pic.code-nav.cn/post_picture/1697067765011161089/RX7kAOJA7fmysTup.webp) *yishuai.wang 的首页。* 这看起来确实像 49 年入国军。 ## 这个比喻,成立一半 成立的一半是:**如果把独立站当流量生意做,那它就是逆风局。** 靠 SEO 等自然流量、靠广告联盟变现、靠批量更新维持权重——这套玩法在 AI 把内容供给打到无限之后,基本没有胜算。AI 一秒能生成的通稿,你写十篇也没人看,搜索引擎也未必收录。这个意义上,49 年入国军不是玩笑。 不成立的一半是:**49 年入国军是押注一个正在崩塌的旧秩序;我不是押注“独立博客会重新成为流量中心”。** 我不认为独立站会回到 2010 年那种“写博客—被搜索引擎收录—拿流量—接广告”的黄金年代。那个年代确实结束了。 我押的是另一件事:**AI 内容越泛滥,一手经历、真实项目、可验证署名,可能越稀缺。** 现在下结论还太早。AI 可以帮我查资料、改代码、润色句子,但它没有做过我做的项目,没有踩过我踩的坑,没有在四个月里对着分页排版一个像素一个像素地改。它写不出“我当时为什么决定不用现成方案”这种判断,因为它没有“当时”。但这套东西到底有没有人认,我不知道,只能先做再看。 ## 不是没人读,可能是阅读结构变了 “AI 时代少有人类阅读”这句话对一半。更准确地说:**人直接访问一个陌生独立站的概率变低了,但人通过 AI 搜索、AI 摘要、GitHub、开发者社区、RSS 找到你的路径,可能变多了。** 读者未必消失,入口可能在变。以前入口是搜索引擎结果页,现在入口可能是: - 有人问 AI“中文分页编辑器怎么做”,AI 引用了你的文章; - 有人在 GitHub 看到 QuireKit,顺藤摸瓜找到你的站; - 有人从 V2EX、即刻、X、掘金看到一段踩坑记录,点进来看完整版; - 有人用 RSS 订阅了你的站,每篇都读,但从不留言。 这些都不体现在“首页访问量”里。但这也只是我的观察,不一定对。还有一个坑必须说清楚:**被 AI 读到 ≠ 被 AI 引用 ≠ 被 AI 导流。** 放一个 `llms.txt` 就以为 AI 会替你传播,是自我安慰。llms.txt 不是标准,很多 AI 不读,读了也不一定引用;被训练进模型更不等于有人会来你的站。所以我没把 llms.txt 当战略,只当导览。我试图做的是:**让站更像一个可被引用的一手源,并且尽量确保引用里有我的名字、链接和工具。** 做不做得成,另说。 ## 还有一些很个人的原因 yishuai.wang 就是我的名字。我很喜欢这个域名,只是一直没好好用起来。 这几年在各个平台发文,我也有个感受:有些平台越来越平台化,推荐、热榜、流量规则排在前面,技术本身反而退到了后面。V2EX 是我觉得一直保持初心的地方,也是给我帮助最多的社区。如果有一天有机会,我也想在国内做一个像它这样的社区。现在还很远,先从把自己的站写好开始。 所以我重新把站点搭了起来,也终于把这个域名用上了。给自己留一块净土:不用围着推荐规则写,想写什么就写什么。 ## 我正在尝试的事 都是很具体、也还没验证的事。 **第一,写我想写的,但让站里有一部分内容有“为什么是我”。** 我会写工程记录,也会写随笔、观点、工具分享。这些内容别人也能写,我不装作每篇都有独家价值。但站里得有一部分是别人替代不了的:QuireKit 的工程记录、分页排版的踩坑、为什么不用现成方案、四个月做到 Word 常用功能 85% 兼容是什么过程。这些不是每篇都有,但它们决定了这个站“为什么是我”。其余内容自由写,写得开心就写,只是我不拿它们当护城河。 **第二,让独立站做首发站。** 以后文章先发在 yishuai.wang,再同步到各个平台,平台上也是完整内容,不做删减。之后有修改和补充,先更新站内版本。能设 canonical 的平台就指向站内原文,不能设的就在文末写一句“本文首发并持续更新于 yishuai.wang”。 **第三,给 AI 和人准备可引用的结构。** 长文里补结论卡、事实表、FAQ、版本记录;给 QuireKit 单独做工具页,写清楚一句话定义、功能、边界、许可证、对比、路线图。AI 最爱抽取的通常不是长叙事,是结论、事实、版本、对比、代码和边界。工具页可能比博客文章更容易被 AI 推荐。这只是假设,我按这个假设在做。 ![site-article](https://pic.code-nav.cn/post_picture/1697067765011161089/ljzfu2HZb768xuA4.webp) *文章页:开头是智能总结,右侧是目录,每篇都能直接拿到 Markdown 原文。* **第四,保留 RSS。** 独立站没有平台推荐,RSS 是我能想到的少数真正属于我的连接:不经过算法,更新了就能收到。中文、英文和全部内容各有一个订阅源。有没有人用,还要再看。 **第五,不和 AI 对抗,把 AI 当一层分发。** 我不指望拦着 AI 读我的站,也拦不住。我能做的是让 AI 读到的内容是可验证的、有署名的、有链接的。AI 的输出有人看,那我希望那个输出里,有我的名字和我的工具。 ## 这个站,是用我自己做的工具搭的 我清楚 QuireKit 是什么、能做什么,所以愿意自己先吃这份 dogfood:这个站就是用它搭的。 QuireKit 是我在做的开源文档编辑器生态。主打的是 Classic 分页文档编辑器:在浏览器里真正分页,断行、分页、光标、选区都自己算,像 Word、WPS 一样一页一页地编辑;另一条线是 MSFE,专为 Markdown 定制的编辑器框架,Markdown 原文就是文档。 yishuai.wang 从页面到写作,用的都是它: - 页面用 QuireKit 的组件库和 InkWash 水墨主题; - 写文章的后台和评论框都是 MD Editor(基于 MSFE),文档、实时预览、源码三种视图随时切换; - 每篇文章在地址后面加 `.md` 就能拿到原文,配上中英文 RSS、canonical、`llms.txt`,就是上面说的“可引用的结构”; - 文章开头的智能总结在发布后生成一次,读者阅读时不会调用模型; - 站点也支持 Micropub,以后可以从别的写作工具直接发文到站上。 ![site-comment](https://pic.code-nav.cn/post_picture/1697067765011161089/dxz7Ph32N3azywns.webp) *评论框就是 MD Editor,页脚是 RSS 和 llms.txt。* 站点源码也开源了(MIT):[https://github.com/kingshuaishuai/yishuai.wang](<https://github.com/kingshuaishuai/yishuai.wang>) 。自己的东西先自己用,哪里不顺手,最先知道的是我。 ## 所以,是 49 年入国军吗 **我认为不是。但这只是我的个人观点,不构成对任何人的建议。** 国军押的是一套正在崩塌的秩序;我押的是另一件事:**当 AI 把内容供给打到无限,人的经历、判断、署名和可验证的一手记录,可能会变贵。** 独立站可能没人看,但它至少是我留给自己的一个源头。域名、文章和版本记录归我,不被平台算法拿捏,也不被 AI 摘要一笔带过。 还有一层原因。好的中文技术博客本来就不多,而大模型能答成什么样,很大程度上取决于它能读到什么。如果资料大多是搬运和水文,模型再强,也会被这样的资料生态限制住。一个人的站很渺小,像一颗星;但好的内容生态,本来就是很多颗星组成的。我想做其中一颗。 没人看是常态,不是判决。我先写一批只有我能写的东西,再写更多我想写的东西,然后看它值不值得活下去。 AI 可以读它、引用它、忽略它。但源头在这里。 至于这是不是 49 年入国军——我现在不知道。我只是觉得,与其等一个确定的答案,不如先把自己的那一份留下来。 --- *本文首发并持续更新于 [yishuai.wang](https://yishuai.wang)。*

给你的编辑器加上思维导图、流程图、白板:一套开源组件,多种文档通用

做 QuireKit 的时候,我顺手把文档里最常用的 5 种图:**思维导图、流程图、白板、文本绘图、多维表格**,做成了一套独立的开源组件,MIT 许可。 QuireKit 自己的三种文档:分页文档、块文档、Markdown,用的就是这一套。它们也可以单独拿出来用:Tiptap、ProseMirror、CodeMirror、自己写的编辑器,甚至一个 textarea,都能接。 ![思维导图编辑器](https://pic.code-nav.cn/post_picture/1697067765011161089/GDNASHVWcBaMW4XA.webp) *双击文档里的导图,打开的就是这个编辑器。* 它和你见过的导图、流程图插件,有四点不一样: - **像图片一样用。** 在任何文档里,它们都渲染成一张普通图片:单击可以像图片一样调大小、改对齐,背后存的却是能继续编辑的数据。三种文档里体验完全一致。 - **一份数据,哪种文档都能放。** 在富文本里是一个块,在 Markdown 里是一段代码,在 Word 式的分页文档里是一个图片对象。 - **AI 辅助写作时能直接画。** 用 AI 辅助写文章,它会在合适的地方插入流程图、导图,插进来的是能双击编辑的真图,不是截图。 - **导出了也不会“死”。** 导出成 Word,在 Word 里是图;导回来,还能双击接着改。 ## 五个编辑器,各有什么亮点 **思维导图(Quire Mind)** - 20 个模板,分基础风格、工作规划、学习生活、分析决策四类:每周计划、项目推进、会议记录、SWOT 分析、读书摘记……打开就能用 - 脑图、逻辑图、组织结构图、时间轴,一键切换结构 - 主题上能加链接、备注、图片和标记 - 直接导入导出 XMind,也能从文字大纲生成 - 没保存就关掉,修改留在本机草稿里,下次打开还能恢复 ![思维导图模板库](https://pic.code-nav.cn/post_picture/1697067765011161089/UfWkFI7Qio2HuEUB.webp) *思维导图的模板库。* 文档:https://docs.quirekit.com/plugins/reference/mind-map/ **流程图** - 三种画风:标准、轻手绘、铅笔排线。手绘风格放进技术文档、方案汇报里,一下子就不一样了 - 31 个模板,分 ER 图、UML 类图、时序图、泳道图、组织结构、鱼骨分析、架构关系、业务流程、数据处理等十多类 - 自动布局、对齐分布、旋转翻转,连线自动绕行,支持分页打印 - 演示模式:按步骤播放节点和连线的流动动画,可以暂停、单步、调速 - 能画 ER 图(字段、类型、主外键、关系基数)和 UML 类图(关联、依赖、泛化、实现、聚合、组合) - 整张图能存成模板,选中的部分能存成组件,下次直接复用 ![流程图的手绘风格](https://pic.code-nav.cn/post_picture/1697067765011161089/MOG7KFIlY8E3S4u7.webp) *流程图的手绘风格:自然笔触,铅笔排线填充。* ![流程图模板库](https://pic.code-nav.cn/post_picture/1697067765011161089/ZQzQVwXJWXzpKk4I.webp) *流程图的模板库。* 文档:https://docs.quirekit.com/plugins/reference/flowchart/ **白板** - 自由书写、图形、文字和笔迹 - 直接导入导出 Excalidraw 文件,也能导出 PNG 文档:https://docs.quirekit.com/plugins/reference/whiteboard/ **文本绘图** - 写 Mermaid、PlantUML、Graphviz 的源码,旁边实时出图 - 可以切换语言,也能从模板开始 在线体验:https://text-diagram.quirekit.com **多维表格** - 文本、数字、单选、多选、勾选、日期、日期时间等字段 - 表格和看板两种视图随时切换,拖动卡片就改状态 - 筛选、排序、分组、数值汇总,记录详情里编辑长文本 ![多维表格的看板视图](https://pic.code-nav.cn/post_picture/1697067765011161089/13AzaFnia7bWlfSQ.webp) *多维表格的看板视图。* 文档:https://docs.quirekit.com/plugins/reference/data-view/ ## 为什么要做 现在的文档,只有文字已经不够:要图文并茂,图要好看,还要能被 AI 读写。导图、流程图、白板、多维表格,成了文档的标配。 用 AI 辅助写文章已经很常见了,可写出来的大多是一整屏文字。要让 AI 辅助写出图文并茂的文章,图就不能是截图:截图在 AI 眼里是一团像素,它既画不出来,也改不了。图的数据得以结构化的形式留在文档里,AI 才能画、才能改。 我做 QuireKit 时,难点还要再多一层:分页文档、块文档、Markdown 三种文档,要共用同一张导图。 市面上常见的做法有三类,各有适用的场景,但都满足不了这个需求: - **嵌入成品。** 比如 draw.io 的嵌入模式:用 iframe 加载完整的编辑器,通过消息传递 XML。功能很全,但界面和数据格式都是它自己的,导出 Word、写进 Markdown 时怎么显示,得自己处理。 - **直接用画布库。** X6、simple-mind-map、Excalidraw 的画布和交互都很成熟,可它们只负责“画”。数据存在哪、怎么保存和取消、导出时显示什么,每个接入方都要自己再写一遍。 - **编辑器自带的扩展。** 开箱即用,但和那一个编辑器绑死了:数据存在那个编辑器的格式里,换个编辑器就得重做。 所以我的思路是:**画布用现成的库,“放进文档”这一层自己做,而且做成通用的。** 任何文档,接一次就能用。 ## 怎么设计的 核心只有一句话:**编辑器只管编辑,存在哪由文档决定。** 编辑器不知道自己在哪种文档里。它拿到一份数据,让用户编辑,再把结果交回去。每种文档只需要写一个很薄的接入层,负责取出数据、写回结果。两层之间,只靠两个约定连接。 ### 约定一:一个通用的嵌入对象 一张导图,不管放在哪种文档里,都是同一个 JSON 对象: ```json { "type": "quirekit-mind-map-embed", "version": 1, "id": "mind-1", "title": "项目推进", "document": { "...": "导图本身的数据" }, "preview": { "dataUrl": "data:image/png;base64,...", "width": 732, "height": 380 } } ``` 它由三部分组成:导图的数据、一个稳定的 ID、一张预览图。 数据在 `document` 里,AI 读的、改的就是它。预览图留给不认识这种对象的地方:在 GitHub 上看 Markdown,或者导出 Word、PDF 时,不用把编辑器跑起来,直接显示这张图。预览图丢了,也能从数据重新生成。 ### 约定二:一个编辑请求 接入层要打开编辑器,只需要提供三项: ```ts interface MindMapEditRequest { document: MindMapEmbed // 当前数据 commit(document: MindMapEmbed): 'saved' | 'conflict' | 'readonly' | 'missing' // 写回 release(): void // 结束,归还焦点 } ``` `commit` 有四种返回值。你在编辑导图的这段时间里,文档可能已经被别人改了、变成了只读,或者这张图已经被删掉了;编辑器会按返回值提示你,不会悄悄覆盖别人的内容。没保存就关掉也没关系,修改会留在本机的草稿里,下次打开还能恢复。 ### 内核可以随时换 画布内核用的是成熟的开源库:流程图基于 AntV X6,思维导图基于 simple-mind-map,白板基于 Excalidraw,文本绘图直接用 Mermaid、PlantUML、Graphviz 的官方库。感谢这些项目。 内核通过独立的适配包接入。哪天上游停止维护,或者出现了更合适的选择,换掉内核即可,文档里的数据不受影响。多维表格是自己写的。所有编辑器共用同一套组件库,界面风格一致。 ## AI 辅助写出图文并茂的文章 结构化数据带来的最大好处,是 AI 可以直接“画图”。 每个图形组件都会把自己的命令描述出来,交给 AI 调用:插入流程图、插入思维导图、插入文本绘图,以及更新它们。在 MD Editor 里用 AI 辅助写一篇文章,它会先想清楚哪里需要图、该用哪种图:步骤和分支用流程图,层级和提纲用思维导图,时序交互用时序图,占比和趋势用饼图或折线图,对比用表格。 AI 插进来的是真正的流程图和导图,跟你手动画的一样,双击就能接着改。画错了也不怕:比如 Mermaid 语法写错,这一步会被拒绝并返回错误,AI 按错误修好源码再提交。每次插入或修改都是一步撤销;修改已有的图时会核对版本,不会覆盖别人刚改过的内容。 (图形组件和命令描述都是 MIT;MD Editor 里的 AI 助手是商业组件,开发和评估免费。) ## 不同场景怎么接 ### 富文本编辑器(ProseMirror、Tiptap 一类) 嵌入对象存成一个块节点的属性,双击这个块就打开编辑器。QuireKit 的块编辑器就是这样接的: ![块编辑器里的流程图和思维导图](https://pic.code-nav.cn/post_picture/1697067765011161089/QTtVFetYB7tAtXOE.webp) *块编辑器里的流程图和思维导图,可以拖动、复制。* ### Markdown 把嵌入对象的 JSON 写进一段代码围栏。编辑器里显示成图,源码里就是一段代码: ![Markdown 里的思维导图](https://pic.code-nav.cn/post_picture/1697067765011161089/2EWzcIgxXTi87vBM.webp) ![源码视图:思维导图就是一段代码围栏](https://pic.code-nav.cn/post_picture/1697067765011161089/Iq893ZqNFc6MHzoO.webp) *切到源码视图,导图就是一段 `quirekit-mind-map` 代码围栏。* 放进 Git,每次修改都看得见;用 GitHub、VS Code 打开也不会坏。QuireKit 的 Markdown 编辑器(基于 CodeMirror)就是这样接的。 ### Word 式的分页文档 导图以图片对象的形式排在页面上,随正文一起分页、一起打印,背后存着导图的数据: ![分页文档里的思维导图](https://pic.code-nav.cn/post_picture/1697067765011161089/zxOxBSKCtoMIdTkl.webp) 导出 Word 时,预览图和原始数据一起写进文件。导回来以后,导图和流程图还能接着编辑。这部分涉及 Word 格式,属于 QuireKit 的商业组件,开发和评估免费。 ### 没有编辑器,或者想自己管存储 直接调用:读出数据,打开编辑器,保存时写回。 ```bash npm install @quirekit/mind-map@alpha @quirekit/mind-editor@alpha ``` ```ts import { createMindMapEmbed, parseMindMapEmbed, serializeMindMapEmbed } from '@quirekit/mind-map' import { openMindMapDialog } from '@quirekit/mind-editor' import '@quirekit/mind-editor/styles.css' const saved = loadFromYourDoc() // 你自己的读取逻辑,返回字符串或 null const document = saved ? parseMindMapEmbed(saved) : createMindMapEmbed('mind-1') openMindMapDialog({ ownerDocument: window.document, request: { document, commit(next) { saveToYourDoc(serializeMindMapEmbed(next)) // 你自己的写入逻辑 return 'saved' }, release() {}, }, }) ``` 想嵌在页面里而不是弹窗,用 Vue 组件 `QuireMindEditor`(从 `@quirekit/mind-editor/vue` 导入)。流程图、白板的接法相同,换成对应的包即可。 ### 预览图太大 预览图默认以 base64 写进文档,不需要任何服务就能用。代价是体积:一张导图几十到几百 KB,Markdown 源码里会出现一大段编码。 有图床的话,提供一个上传函数: ```ts openMindMapDialog({ // ... uploadPreview: async (png) => uploadToYourCdn(png), // 返回图片地址 }) ``` 预览图会像普通图片一样传上去,文档里只保存地址。 ## 开源范围 - **MIT**:5 个编辑器、块编辑器、Markdown 编辑器,以及它们的接入层,闭源商用也可以。 - **商业组件**(开发和评估免费):分页文档中涉及 Word 格式的接入,以及可往返的 Word 导出。 ## 最后说说 QuireKit 这些图形组件,只是 QuireKit 的一小部分。QuireKit 是一套开源的文档编辑器生态,用来在你自己的产品里搭在线文档、知识库和 Markdown 写作工具。它底下是两套 Tiptap 风格的编辑器框架,一套基于 ProseMirror,一套基于 CodeMirror,上面是三个现成的编辑器: - **Classic 分页文档编辑器**:QuireKit 主打的产品。在浏览器里真正分页,断行、分页、光标、选区全部自己算,画在 Canvas 或 SVG 上,像 Word、WPS 一样一页一页地编辑。可以导入导出 DOCX,也能导出 PDF 和 OFD(这几项是商业组件)。 - **MSFE,为 Markdown 定制的编辑器框架**:Markdown 原文就是文档,文档视图、实时预览、源码视图改的都是同一份 Markdown,没碰过的行一个字节都不变。上面那个 Markdown 编辑器 MD Editor,就是用它搭出来的。 - **块编辑器**:Notion 式的块文档,适合笔记和知识库。 三个编辑器和这些图形组件,用的都是同一套 UI 组件库。 目前还是 alpha,接口还会调整,欢迎试用和反馈: - 官网(首屏就是 Classic,可以直接编辑):https://quirekit.com - MSFE:https://msfe.quirekit.com - 块编辑器:https://quirekit.com/demo/block-editor/ - GitHub:https://github.com/quirekit/quirekit 觉得有用,欢迎点个 Star。也想听听:你的编辑器最想接入哪种图?

https://yucongming.com 鱼聪明是更换入口了么,请问ai助手从哪里进入呢😅

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](https://pic.code-nav.cn/post_picture/1612112775822180354/4KYqIDcjc1ZHxCRD.webp) --- ## 第一章:需求剖析与“反 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](https://pic.code-nav.cn/post_picture/1612112775822180354/g2UJfsAduviHK5a9.webp) ### 2.2 确定性状态机契约(三态流转) 为彻底消除多条件并存导致的界面紊乱,我们在前端确立了唯一的三态状态机(`SearchLifecycleState`)。 ![image.png](https://pic.code-nav.cn/post_picture/1612112775822180354/i1AWrwJLWKIQgy5r.webp) 状态只允许沿固定路径单向流转: 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](https://pic.code-nav.cn/post_picture/1612112775822180354/HWyYphZzKD4gEtwV.webp) 如图 4 所示,一旦推演完成,界面平滑过渡至 Result 态综合研报工作台。这是系统最核心的工作台视口:顶部常驻 Academic Control Strip 极简学术控制条与 ReasoningAccordion 折叠抽屉(标明阶段完成数与总耗时),正文呈现高浓度技术排版、Key Takeaways 核心要点卡片与公式,右侧 40% 则是高保真文献证据抽屉: ![image.png](https://pic.code-nav.cn/post_picture/1612112775822180354/hD0D9cWW4Ysi4oAl.webp) --- ## 第三章:让 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` | `#0071E3` | Apple Blue 核心动作按钮与焦点微动效 | | `evidence` | `#F59E0B` | 证据切片高亮(琥珀黄),文献精准段落还原 | | `emerald` | `#10B981` | 推理完成标记与共识度徽章 | 如图所示,在 Superbar 复合控制台中,用户点击左侧 `+` 图标即可呼出权威信源过滤选单。整个弹窗的底色、圆角、分割线与激活蓝均严格对齐表 2 的 Design Tokens,没有任何野生的临时样式: ![image.png](https://pic.code-nav.cn/post_picture/1612112775822180354/3h3OpDTbizLPfEQi.webp) ### 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](https://pic.code-nav.cn/post_picture/1612112775822180354/DbAvMj53Apoijv6o.webp) --- ### 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](https://pic.code-nav.cn/post_picture/1612112775822180354/OsPTHgyo28L1fqPs.webp) --- ### 4.3 模块三:多源检索、Scope 四阶物理过滤与权威时效重排机制 #### 4.3.1 多引擎适配与平滑降级 后端在 `backend/app/pipeline/providers/` 中建立了统一的 `SearchProvider` 抽象基类,并分别实装了: - `DeepSeekSearchProvider`:直连 DeepSeek 搜索端点,适合针对长上下文的技术文献精细解析; - `TavilySearchProvider`:深度网络聚合搜索,支持按 `advanced` 深度并发抓取。 前端 `Superbar` 右下角提供引擎切换下拉菜单。当外部搜索网络遭遇抖动时,系统自动执行毫秒级平滑降级,确保流程始终稳定推进。 #### 4.3.2 Scope 范围四阶物理过滤与防穿透体系 许多 AI 项目的过滤机制仅在界面上隐藏某些条目,导致模型依然根据无关信源生成回答。我们建立了四阶防穿透流水线: ![image.png](https://pic.code-nav.cn/post_picture/1612112775822180354/ikd8j3FF6afI41gT.webp) 如图 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](https://pic.code-nav.cn/post_picture/1612112775822180354/gwyxAhK7FLxXoQlG.webp) 如代码清单 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](https://pic.code-nav.cn/post_picture/1612112775822180354/K8NcsfJZva8PIdRs.webp) ## 总结 回顾整个项目的构建过程,我们不仅交付了一款高保真的专业级 AI 搜索引擎,更验证了一套现代 AI 时代的软件工程方法论。 在 Vibe Coding 时代,AI 并没有消灭工程师的价值,而是放大了系统架构设计、接口契约与工程品味的重要性。 - 如果你把 AI 当作“全包打工人”,只给一两句模糊的提示词,那么得到的注定是廉价的塑料界面与混乱的代码; - 但如果你充当严密的系统架构师与设计总监——用 Google Stitch 建立视觉真理,用 /impeccable 锁定设计系统与工艺底线,用 AGENTS.md 锁死状态机契约与类型接口——AI 就会成为你手中极其强悍的研发外骨骼。 项目开源地址:https://github.com/sz-xiaohuolong/xhl-ai-search

天天 AI Coding的你,如果出去面试,咋体现自己的优势?

​ 一直很好奇一件事情,如今已经是个人人都AI Coding的时代,那么外面的面试现在变成了什么样呢,还会问那些老掉牙的面试题吗?让我来脑补一个面试流程,比如有一天小明去面试一个Android工程师的岗位 面试官:说一下View的绘制流程 小明:你low不low,AI时代谁还关心这个? 面试官:那好,说一下你做的项目中有啥技术亮点没 小明:都是AI写的,我不知道有啥亮点 面试官:那解决过什么印象深刻的问题吗 小明:....没印象了,记得有个问题消耗了我特别多的token,嘿嘿~ 面试官:看来AI用的挺多啊,那你看这里有一段 AI 生成的代码,你看一眼,告诉我哪里有问题,为什么 小明:......好像也没啥问题,我一般都是让AI帮我找代码中的错误 面试官:你平时用AI干活,那你怎么保证AI写的代码是能上线的?出问题你怎么定位? 小明:让AI检索一遍代码啊,没问题了就能上 面试官:行了,你回去吧 在这个面试中,小明脑子里积累的"AI 效率"几乎全部失效, 他没法再让AI帮他解决问题,因为面试不能用AI,面试官要考的就是小明,其实像小明这样的人我相信一定还有不少,这里就引申出一个问题:天天AI Coding的你,如果真的出去面试,你的竞争力到底是什么? ## AI时代的面试,到底在考什么 这里很多人的第一反应是"考 AI 技能",是吗?虽然现在工作当中已经基本离不开AI,但在面试环节,AI能力恰恰是最不会考的东西,为啥呢? 人人都会用AI,面试官没法用"你会不会用 AI"来区分候选人,问了也白问 "会用 AI"在面试里无法验证,总不能在面试现场让你当场调个prompt吧,那测不出任何东西。 所以AI时代的面试,考的从来不是"你会不会用 AI",而是下面这几样,AI替代不了、简历写不出、只能现场测的东西 ## 判断力:AI 给的东西,你敢不敢说"不对" AI 会给你一个"看起来合理"的答案,但它不一定对,面试官考你的,是你能不能识别出它的不对、为什么不对、正确的应该是什么。这需要你真的懂,而不是"会用" 所有权:你对自己写的代码,负不负责 AI 生成的代码,最终是你提交的。面试官会问:"这段代码出 bug 了,你身为负责人怎么定位?怎么排查?",这个问题里面,他想知道你愿不愿意、能不能为AI的产出兜底 审查能力:你能不能把AI的"差点意思"优化成"能上线" 2025年Stack Overflow 调查里,66% 的开发者被"AI 答案几乎对,但差那么一点"这样的问题折磨,面试官考你的,恰恰是你能不能把"差那么一点"解决成"能上线",这需要的不是AI,是深度 ![1.png](https://pic.code-nav.cn/post_picture/2000454716282159105/HFqabilN1Fsg4nEQ.webp) 工程判断:方案的取舍、性能的权衡、架构的边界 AI 能给你一个"能跑"的方案,但面试官要的是"为什么这个方案比那个好",数据量、性能、可维护性、边界情况,这些是AI给不了、也替代不了的东西 ## 做个小总结 如今AI时代的面试,真正要考的是"AI帮你做完事情之后,剩下的你还能做什么",这里的“剩下的”,才是你真正的竞争力 顺便说个,技术大厂[机会](https://jsj.top/f/mx4wQA),会任一语言Java / C++ / Python / Go / JS 均有HC,月薪可观稳定性还可以,感兴趣共事~ ## 竞争力,从来不是"会用 AI" 那真正的竞争力究竟是什么?把上面说的四点概括成三样,就是AI时代面试里真正值钱、且 AI 替代不了的竞争力 ## 深度:你"懂"到什么程度 这是最核心的,AI能帮你写 Kotlin,但你懂不懂 Kotlin 的协程调度、Compose 的重组机制、底层源码的实现,这是AI给不了你的,深度决定你能判断AI输出对不对,也决定面试官信不信你 判断力:你扛不扛得住"AI不可靠"这件事 AI会错、会编、会做那种"几乎对但差点意思"的事情,你判断它错了、并纠正到能上线的能力,就是你的竞争力,而46%开发者的不信任和66%的挫败感,恰恰说明这种能力是真稀缺的,因为大多数人并没有 ![2.png](https://pic.code-nav.cn/post_picture/2000454716282159105/fXubeNXBXnL2JzTW.webp) 工程素养:你是一个"负责人",不是"发生器" 你负责过的上线的项目、你踩过的坑、你的架构决策、你的性能优化方案,这些"你真正负责过、经历过"的东西,是 AI 没法替你生成的,它们是面试官能信服的硬性标准 ## 面试怎么把你的竞争力"证明"出来 这是最实操的一环,两个求职者,简历上都可以写"熟练使用 AI Coding",但面试表现天差地别,区别在于 A 把"会用 AI"当标签,简历写着,面试答不上来 B 把"会用 AI"当过程,用 AI 干活,但证明自己有判断力 B 是怎么做到的?他在面试里展示行为证据,而不是堆标签。具体是这几招: ## 主动展示"怎么用 AI,而不是用了 AI" 面试官问"你平时用 AI 吗",别回答"用"。要回答: 他考的是"你怎么用",不是"你用了没有“,你的判断过程,就是证据 主动展示"怎么Review AI代码" 这是最直接的加分项。面试官提到 AI 代码时,主动说: 你展示了"驾驭AI不可靠"的能力,正是上面那66%的人缺的 主动展示"怎么兜底" 面试官问"出bug怎么定位",别只说"用 AI查",要展示你的排查方法论 你证明了"所有权":你负责,不是 AI 负责 主动展示"深度"压过"AI" 面试里遇到算法题、系统设计、源码追问这种没法用 AI代替的环节,别慌,这恰恰是你的机会,因为这是你证明"我懂"的时候,你平时用 AI 省下的时间,如果灌进了这些深度,现在的你就能答上来;如果没灌进去,这里就会露馅 ## 又是个总结 面试官要的不是"你会用 AI"这个标签,而是你能为自己的AI产出负责这个行为证据,标签人人会写,行为只有真的懂才拿得出来 ## 最后 所以这篇文章全文都在说一件事,就是如今的你,你的竞争力究竟是什么? 如果你用AI省下的时间,培养了判断力、深度、工程素养,那么你的竞争力就很强 如果你用AI省下的时间,只换来了"更快地贴代码",你的竞争力很弱,因为你不懂的东西仍旧不懂,别人一面就露馅了 AI时代是公平的也是残酷的,你去面试不像是你去做需求,面试官不会让你去问AI,到了那个时候,恰恰就是检验你平时有没有用AI来锻炼自己,天天AI Coding没毛病,不是问题,问题是你有没有借助AI让你比别人更厉害,天天花出去的token换回来的是不是仅仅只是那几行代码 ——转载自:Coffeeee

把星座「握」在手里:3D 交互与运营大屏的落地手记

> 十月下旬,M3 的下半场做完了两件事:让 3D 地球从「能看」变成「能操作」,让数据从「在电脑上看」变成「能挂到墙上」。 > > 这篇不列任务清单,也不铺指标表,只回答三个问题:为什么这么做、用起来是什么手感、哪些地方我们故意留了白。文中所有界面图来自本地演示数据。 ![M3下半场-3D轨道与星下点.png](https://pic.code-nav.cn/post_picture/1944355748262088705/ehOzWiliPdzIId9r.webp) --- ## 一、换个比喻:从「每两秒换一张照片」到「摄像机一直开着」 早期的 3D 页面有个说不出口的毛病:画面在抖。不是显卡的问题,而是写法的问题——每次拿到新数据就把整个场景重建一遍,地球重新挂载、材质重新加载、相机重新对焦。站在用户视角,就是「地球每隔两秒哆嗦一下」。 这次改造换了个比喻:**不再每两秒换一张照片,而是把摄像机一直开着,只换演员。** 落到代码上是三件事: - **一条 requestAnimationFrame 循环到底**。整个引擎只有一个连续渲染循环,相机的阻尼、标签的屏幕坐标、测距读数的位置都在这条循环里更新。数据来了只是「换一帧演员的位置」,场景不重建,自然就不抖。 - **把场景拆成一组独立图层**。地球、星空、卫星、星间链路、最短路由、历史轨迹、轨道线、星下点轨迹、书签标记,各自是一个独立模块,各自有 `dispose()`;面板上露出来的是其中七个开关。开关和透明度作用在图层上,而不是重建整个场景——这也是为什么图层面板上的滑条能「即时生效」而不会卡。 - **把像素比按住**。`devicePixelRatio` 最高取 2。4K 屏 + 大星座时,这条限制省下来的 GPU 比任何算法优化都直接。 顺手还清掉了几个容易漏的口子:容器尺寸用 `ResizeObserver` 跟,组件卸载时把渲染器、控制器、贴图、几何体、事件监听全部回收。**3D 页面的内存泄漏几乎都出在「忘了 dispose」**,这件事后面讲大屏时还会再遇到一次。 --- ## 二、时间轴:真正难的不是画面,是「这一刻卫星在哪」 3D 页面这轮最有意思的功能是那条时间轴:滑块从「24 小时前」拖到「24 小时后」,地球上的卫星跟着走到那个时刻的位置。 看起来是个滑块,实际上藏着一个架构级的岔路口。 ### 一个不能直接复用的接口 系统里本来就有一个「取当前帧」的接口 `/simulation/snapshot`。它返回所有卫星的经纬高、星间链路、最短路由——正好是 3D 渲染需要的数据。**但它有一个副作用:每调用一次,服务端的仿真时钟就往前推一步。** 这在实时模式下是特性,在拖动时间轴时就变成了事故: - 用户只是想「回看六小时前」,结果把整个系统的仿真时钟拖到了六小时前; - 实时刷新(每 2 秒一次)和拖动刷新同时在推时钟,两边互相打架; - 更麻烦的是,别的页面、别的用户看到的「现在」,已经不是真正的现在了。 所以这次新开了一个**只读**接口:`/simulation/snapshot/seek?offsetSeconds=…`。 ### 只读的意思,是「什么都不改」 这个接口的契约写得很死: - 只把卫星传播到「基准历元 + 偏移」,返回经纬高; - **不推进** `simTime`,**不改**共享状态,**不做**故障注入; - 偏移钳制在 ±86400 秒(±24 小时),越界按边界算; - 因为它只回答「卫星在哪」,所以星间链路和路由直接返回空数组,不浪费带宽。 一句话:**拖动只「看」,实时才「走」。** ![M3下半场-时间轴链路.png](https://pic.code-nav.cn/post_picture/1944355748262088705/NS994IlgWJSNdINs.webp) ### 前端只做两件事:少发请求、别抢主线程 拖动滑块会连续触发几十次事件。如果每次都发请求,后端会被自己人打穿。所以这里用的是**尾部节流**:150 毫秒内的连续输入合并成一次请求,最后一次一定落定——算下来最多每秒七次,手感是连贯的,压力是可控的。 还有一个更容易被忽略的细节:**拖动模式下,轮询不再调用会推进时钟的那个接口**,只刷新 KPI 面板。否则你一边拖,系统一边把时钟往前推,画面就会「往回跳」。 至于「谁来算位置」这个问题,我们选了后端算,而不是在前端塞 satellite.js + Web Worker。原因不是前端算不动,而是**口径一致**:后端的只读 seek、实时的当前帧、2D 轨道视图里的那条曲线,走的是同一个 SGP4 传播器、同一个坐标转换。三处显示如果各算各的,迟早会出现「3D 说在头顶、2D 说在地平线下」的尴尬。 ### 轨道线:一圈 121 个点,只在需要时重建 打开「轨道线」图层,你会看到每颗卫星一条闭合的蓝色曲线;打开「星下点轨迹」,地球表面多出一条黄色曲线。 这两条线不是前端逐帧算出来的,而是后端顺着真实 TLE 采样一圈返回的:默认 120 段、也就是 **121 个点**,周期直接从 TLE 第二行的平均运动反推(解析失败退回默认值),一次最多取 20 颗星。 前端拿到这些点之后只做一件事:把经纬高换成场景坐标,重建一次折线几何体。**低频操作,不做增量脏检查,也不参与逐帧计算**——这是它能和 60 帧动画和平共处的原因。 --- ## 三、点一下、量一下、做个记号 如果说时间轴解决的是「看什么时候」,那么工具解决的是「怎么看」。 ### 点卫星,弹出一张「可信到数量级」的卡片 点击卫星会弹出详情卡:NORAD 编号、分类、是否故障、经纬高,以及两个稍显「越界」的字段——**速度**和**周期**。 这两个值不是查库来的,也不是额外调接口要的,而是用**圆轨道近似**(vis-viva)当场推的:半径 = 地球半径 + 高度,速度 ≈ 7.6 km/s,周期 ≈ 96 分钟(550 km 高度)。 为什么这么做?因为详情卡是「一眼确认」的场景:你要判断这颗星是不是在合理的高度、合理的速度上。为了一次性展示去加一个接口、加一张表,代价和收益完全不成比例。**在演示和巡检场景里,「数量级对」比「小数点后六位对」更有价值**——这处取舍在评审里专门说明过,避免后来人误以为是精确解算。 拾取用的是射线检测(Raycaster),没有引入任何引擎自带的选择器。好处是跟渲染解耦:想在哪个图层上加拾取,给那一层加个 `pick()` 就行。 ### 测距:先找卫星,找不到就落到地面 测距工具的逻辑只有两句,但顺序很重要: 1. 先判断点到的位置有没有卫星——有就取卫星的真实位置; 2. 没有就取射线与地球表面的交点。 读数会跟着相机走:拖动视角时,距离数字会贴着两点连线的中点移动。这种「跟着相机每帧重投影」的做法,比在固定位置弹一个提示框更接近真实测绘软件的手感。 ### 书签:存本地,比存数据库更合适 标记工具允许你在地球上点一个位置,起个名字(北京测控站、三亚站……),重启浏览器还在。 存储用的是 `localStorage`,按用户名分键:`leo-sim-markers:<username>`。为什么不上库表? - 它是**个人视角的偏好**,不是业务数据,别人不需要看到; - 它需要**离线可用**——演示环境没网、没后端也要能放标记; - 它不该为一个小功能引入一次数据库迁移、一套接口、一批测试。 风险也接受了:换一台机器、换一个浏览器就没了;存储配额不足或隐私模式时,静默退化成「只在本次会话有效」,不做任何阻断。 ![M3下半场-标记工具.png](https://pic.code-nav.cn/post_picture/1944355748262088705/I4txJvjTzPdoPlnS.webp) ### 图层面板的默认值,是设计出来的 七行开关看着简单,默认值其实都有理由: - **卫星、标签、星间链路、最短路由**:默认开。这是「一眼看懂星座在干什么」的最小集合。 - **历史轨迹、轨道线、星下点轨迹**:默认关,三者的理由各不相同。轨迹是「跑一段时间才积累出来」的,打开后从当前帧开始记,切换编目或重置仿真会清空;轨道线要额外拉一次采样接口、重建 20 颗星的几何体;星下点同理。它们都属于「想深挖时再打开」的功能。 ![M3下半场-时间轴与图层面板.png](https://pic.code-nav.cn/post_picture/1944355748262088705/BHNFqmi5Ev27yNHj.webp) --- ## 四、大屏:一面墙的阅读习惯,和桌面上不一样 3D 页面的服务对象是坐在椅子上、握着鼠标的人;大屏的服务对象是**站在三米外、只看八秒的人**。这两个前提几乎决定了所有设计选择。 ### 它不是一个「更大的仪表盘」 大屏是一个独立路由:不套主布局、没有侧栏、没有顶部菜单。投到墙上时,屏幕上只应该有三样东西——**结论、态势、异常**。导航是给操作者用的,不是给观众用的。 配色也故意「叛变」了一次:**大屏固定深空色,不跟随全局的亮/暗主题**。理由很实际——投墙时环境光不可控,跟着主题切换反而会让某一档配色变得不可读。这处不一致被明确记为偏差,而不是偷偷埋在样式里。 ### 八张卡片,回答八个问题 顶部一行八张大字卡,每张只回答一个问题:有多少颗星、多少个星座、多少个地面站、多少条链路在跑、在线率多少、覆盖率多少、多少人在接入、有多少条告警。 栅格是 12 列、每卡跨 3 列,所以 2560 宽是「两行四张」;1600 以下收字号、收紧主体比例,1280 以下切成「每行两张、主体单列」。数字用等宽数字字形(`tabular-nums`)——不是强迫症,是为了让数字变化时不要左右跳。 ### 全球覆盖区:为什么最后没用 3D 最初的设计稿里,覆盖区想直接嵌一个 3D 地球。最后选了 ECharts 的 2D 世界地图,原因是三条: 1. **投墙可读性**:三米外看一个转动的球,你分不清哪条线连着哪颗星;平面地图上,一条弧线从欧洲连到北美,一眼就懂。 2. **预算**:3D 引擎是首页预算里最贵的一块,塞进大屏等于让每个进大屏的人先下载一遍 Three.js。 3. **可控**:地图是静态资源 + 几个 series,出问题好定位;3D 出问题只能看截图猜。 世界底图放在 `public/maps/world.geo.json`(217 个要素,约 1 MB),由 nginx 直供、可缓存、可离线;不进 JS 包,只在进入大屏时 `fetch` 一次再注册。**万一地图加载失败,会自动降级成经纬度直角坐标上的散点加连线**——不好看,但不会白屏。 地图上画三样东西:地面站(按状态着色,故障红、降级黄、正常青)、卫星(菱形,大小随高度变化,近似表达「覆盖圈」大小)、星间链路(线的透明度随链路质量变化)。同时渲染的卫星上限 160 颗、连线 240 条,这是用真实数据压过之后定的天花板。 这里要诚实一句:**卫星大小表达的覆盖圈是「近似发光」,不是精确的星下点足迹(footprint)**。真实的覆盖圈要考虑最小仰角、地形遮挡、天线方向图,那是一个独立课题。 ### 右侧两条线:一条看链路,一条看告警 - **实时链路状态**取托管链路的列表,按状态判通断(ACTIVE / OPERATIONAL / ESTABLISHED 算通),用信号强度折算成一根质量条。没有托管链路时给空态,不硬编数据。 - **告警跑马灯**复用通知中心那个 WebSocket 单例。有个细节值得说:大屏是独立路由,主布局已经卸载,所以它可以安全「接管」这条连接,离开时再断开——不会和通知中心抢连接。超过五条时,前端把列表复制一份、用 CSS 位移做无缝上滚,鼠标悬停暂停。 推送是「尽力而为」:连不上就只有轮询,页面上会亮一个红点写「推送已断开」。**我们宁可让它说实话,也不做一个假装在线的绿点。** ### 挂两小时不崩,靠的是四个「收口」 大屏和普通页面的最大区别是**没人关它**。所以刷新和清理必须写死: - 一个 5 秒的定时器驱动全量刷新,六路请求并发、各自独立兜底,某一路挂了就保留旧值,不让整屏闪白; - `document.hidden` 时暂停轮询,切回来立刻刷一次再重启——投墙的机器可能整夜开着,但没人看的时候不该空转; - 离开页面时依次:清定时器 → 退订 WebSocket 回调 → `dispose()` 图表实例 → 移除 resize 监听; - 大屏代码走路由级动态引入,天然是一个独立 chunk(约 9.5 KB),不占首页预算。 这四条不是「优化」,是**挂墙的前提**。少一条,第二天早上回来就是一屏白。 ![M3下半场-运营大屏-2560.png](https://pic.code-nav.cn/post_picture/1944355748262088705/zMNb6B1Xkc0lSaqf.webp) --- ## 五、怎么证明它不是「演示专用」 演示能跑通,和「明天还能跑通」,中间隔着一整套验证。这次的做法是:**能在 DOM 和网络层断言的,全部自动化;只能靠眼睛看的,老实写进人工联调清单。** 自动化的部分: - 后端只读 seek 与轨道采样写了专门的单测,四个用例全绿:只读性(两次请求结果一致)、位置合法性(经纬度落在合理区间、高度接近标称值)、越界偏移被钳到 ±24 小时、合成模式下轨道采样返回空; - 前端 3D 交互四个用例:图层开关联动、工具按钮激活态、时间轴的「拖动触发 seek / 切回实时不再触发 seek」、开启轨道图层才去拉采样接口; - 大屏三个用例:八张卡片与口径、仪表盘入口跳转、**离开大屏后不再产生轮询请求**。 最后这条值得单独说。它验证的不是「显示对不对」,而是「走了以后有没有留下东西在跑」。这类用例平时没人愿意写,但它恰好对应最真实的事故形态:页面关了,定时器还在跑,内存一点点涨。 人工联调的部分也说清楚:WebGL 画布里的点击拾取、标记落点、轨道线渲染,在无头浏览器里截图断言意义不大(像素级比对脆弱、又验证不了「点得准」),所以这部分靠本地起后端、手动核对,并在文档里标明「需 WebGL 手动验证」。 体积上,3D 页面自己的 chunk 约 42 KB,大屏约 9.5 KB,都没有把重依赖塞回首页。**性能预算是一道闸门,不是一句口号**——上一轮刚把首页 JS 压下来,这一轮不能再放回去。 --- ## 六、留白清单:我们没做的,和故意不做的 一份诚实的交付,应该能列出「边界」在哪里。这次明确留在门外的是: 1. **精确覆盖圈**。地图上的卫星大小是近似发光的视觉效果,不是测地线足迹。 2. **遥测专用推送**。大屏的实时指标目前靠轮询(KPI + 快照),没有为遥测单开一条 WebSocket topic。投墙场景下 5 秒粒度够用,但要做到「秒级曲线」就得补这条链路。 3. **大屏跟随主题**。固定深色是刻意的,代价是「白天在明亮的会议室看会偏暗」。 4. **时间轴里的链路**。只读 seek 只回答「卫星在哪」,不回星间链路和路由——因为拖动时观众关心的是轨迹,不是拓扑。 5. **合成星座的轨道线**。没有真实 TLE 就采不出真实轨道,所以合成模式下这个图层是空的,不做「假轨道」。 6. **多屏拼接与自动轮播**。先把「一面墙稳定跑两小时」做扎实,再谈「三面墙拼一张图」。 这些不是「TODO 列表」,而是**主动划定的边界**:每一条都知道上限在哪、为什么现在不做、以后要做需要补什么。 --- ## 七、三句话总结 - **画面不抖**,靠的是一条渲染循环 + 数据换帧,而不是更贵的显卡。 - **时间能拖**,靠的是一个「只读」的接口和对时钟的敬畏——能不写状态就不写状态。 - **墙能挂住**,靠的是定时器、订阅、图表、监听四个收口,而不是一张好看的截图。 3D 和大屏是这一轮里最「好看」的两个交付,但它们真正的价值,恰恰在于那些看不见的部分:一个不推进时钟的接口、一次 150 毫秒的节流、一个卸载时就消失的定时器。 下一段路,M4 会把注意力放回「规模」——K8s、碰撞预警、任务规划。到那时,今天这些「看不见的部分」会变成地基。

终于,我上线了一个多Agent、多用户实时协作的云端无限画布项目

大家好,我是 woopsion! 花了大量的时间和精力做了个多智能体创作平台,Pecan Studio。已经上线了 https://ai.pecan.wang/ pecan这个东西具备大多数无限画布的能力,但是更加突出的是多智能体和画板的深度结合、协作能力。我认为它更是偏向生产化、云端智能体saas服务 沙盒计算资源 云虚拟工作空间 的一种实际落地的解决方案。 不废话,简单解释一下**pecan studio**: - **无限画布的基础能力**:参考图、分镜、视频、音频、各种md文档 pdf 都可以在上面直接渲染,并且支持HTML文档、3D建模相关结点的渲染;选中对象、框选区域、各种关系线、画板上的标记文字等就是给 Agent直观的上下文。 - **Pecan 主 Agent + 四个领域 Agent**:Pecan 主 Agent 带队,橘子负责故事,小a负责的是视觉,蓝莓负责视频,悦悦负责的是声音模块(这些名字都是我随便起的!没有特殊含义!);画布上能看到实时的操作痕迹,除此之外各个子agent可以委派自己的内存级别即用即丢的子智能体(但是目前系统约束不建议使用,之后会优化系统架构) - **用户、系统 Agent Team、MCP客户端等协作于同一个项目(一个项目有多个画板)**:画布每次变化你都能实时感知,也能看到是谁在操作画布上的对象——操作用户、或其附属的 Agent Team、或第MCP客户端都会实时出现在画布上。 - **外部 Agent 也能指挥它**:在 Claude Code 或 Codex 里可以直接指挥 Pecan 在画布上接着做;同一个 Pecan、同一份上下文,网页上能看到这是一条 MCP 任务。 - **长任务异步回调,等待时不占算力**:图片、视频、音乐、声音生成,Blender 渲染,委派子 Agent等这些长任务都异步调度到后台;结果回调后 Agent 接着干,等待期间它可随时释放自己的计算资源,由统一巡检模块在回调后把它拉起来,资源弹性分配的方式把服务器算力、存储等资源省下来。(这个涉及到很多具体的架构设计,如果有兴趣的可以展开其中的细节仔细的聊) - **人机协作有边界:关键动作你拍板(HITL),11 类操作可配边界、可审计**:删除、付费生成、开沙盒默认要确认,查询类自动放行,也能设“总是允许 / 总是询问”;小到一次沙盒 Bash,大到发起生成任务,人机确认、操作边界和审计记录都在。 - **随时对接、随时指挥各个 Agent**:上下文压缩做了大幅优化;无限画布里的项目数据、云端工作区的现有成果,加上模型能力,让 Agent 始终带着足够丰富的上下文——你指哪,它打哪,并且这些智能体都是有独立人格的小伙伴 不仅仅是作为一个子智能体协作的身份! - **Skill 随装随用,内置专业、精品的视频创作流程,还能“画”出来**:自己的 Skill 能装能卸;系统还自带专业、精品的 AI 视频创作流程 Skill。工作流被可视化后接进画布,Agent 既用 Skill 的专业性,也借画布的可视化、灵活性、实时性和多 Agent 协作。 可以先看一下pecan studio 的环境 :rofl: ``` 产品原生工作室界面;图中角色消息和工作状态为演示数据。 谁在忙、谁在等人拍板、谁已经交付,一眼就能看到;点一下角色就能直接对话。它比一排聊天窗口更像一个真的工作间。 真正难的地方,不是让几个 Agent 互相说话,而是让它们能长时间干活、失败还能续上。底层是在 deepagents / LangGraph 上改的,按 SaaS 的形态把 Agent 架构重做了一版,分布式异步调度也重新设计过:Session Supervisor → Session Host → 分布式 Agent 运行时状态 → Task Run / LangGraph 状态;谁在跑、任务做到哪、失败后由谁拉起,每一层都得说清楚。沙盒也从“Agent 一启动就开”拆成了项目级资源,用完自己回收。 做之前我以为最难的是让模型听话,做完发现最难的是架构取舍:计算和存储怎么省,服务之间的数据流怎么少绕路,到底能扛住多少并发而不炸机,以及这套东西是不是 真的能可靠闭环、真正落地。用户感觉到的“靠谱”,基本都来自这些不性感的部分。 ``` ![Pecan Studio 的像素工作室](https://pic.code-nav.cn/post_picture/1902633242032910338/N71570LDbIBi1PEy.webp) ``` 用户在具体交互的过程中、不同的状态 不同的阶段 调用不同的工具会有不同的实时动画的渲染,我更偏向做一个有意思的东西,这里也是有趣的一点吧 ``` ![Pecan 主 Agent、橘子、小a、蓝莓、悦悦的头像](https://pic.code-nav.cn/post_picture/1902633242032910338/LMaYuRrsEhPQIOjU.webp) ![实时协作](https://ai.pecan.wang/assets/blog/pecan-realtime-identity.webp ) 做它的起因挺简单:我不想再做一个更大的聊天框。我自己用聊天框做东西,最烦的是来回描述“刚才那张图”“左边那个视频” 亦或者大量的文字堆砌 以及各种繁琐的文档、流程等! 聊天是线性的,创作不是。画布上选中一个对象、拉一条线,一片区域这些本身就是给 Agent 的上下文,不用再写一段话解释 另一个我比较坚持的点是:不让 Agent 全自动,也不让人每一步都盯着,我觉得这个度很难平衡。但是对于这个影视创作流程来说,人机交互的频率肯定是更高的,需要用户自身的审美判断、因此这里做了很多的人机交互,人类审批的系统限制,你可以控制11种类型的操作权限 ![会话权限演示](https://pic.code-nav.cn/post_picture/1902633242032910338/1YCVKjNHTiDnzfg9.webp) 案例:工作室与人工确认面板 ![会话权限演示](https://pic.code-nav.cn/post_picture/1902633242032910338/rXJTyZg1P0ZqNIXy.webp) 15 秒真实协作:pecan Agent 在实时操作画板: ![15 秒真实协作:pecan Agent 在实时动节点,人在旁边看着](https://pic.code-nav.cn/post_picture/1902633242032910338/U1M9zxdmpPSXH6PK.webp) 感觉还是有非常大的可玩性吧,这里再简单放点实际案例: 项目影视风格可视化: ![影视风格可视化](https://pic.code-nav.cn/post_picture/1902633242032910338/RQk3DEAssRkwA8La.webp) 具体剧本分镜可视化: ![剧本与分镜](https://pic.code-nav.cn/post_picture/1902633242032910338/rofaUF63MNTFy9WX.webp) 还有具体的多人协作的3d建模 以及其他细节可以看一下介绍页面 https://ai.pecan.wang/ 不过这个非盈利项目 非商业化项目 可以直接找我要积分 (如果还有的话)的兑换码去体验一下其中的乐趣 更欢迎影视制作方面的朋友过来体验 由于我个人资金有限 服务器是个小弱鸡 4c8g 的弱鸡 以及本人确实太穷了 给不了太多体验的积分 八月份刚离职 目前还在找工作的状态中~ 热烈欢迎交流! **Pecan Studio 是我个人的作品,非商业项目** 它更像一个探索性、学习性的产品,用来验证多智能体、无限画布和实时协作这些创新想法;后面有开源计划,具体看情况。

🖥️ 程序员的一天,6张图全中算我输(1024程序员节快乐)

​ 明天就是1024程序员节了  “跟拍”了我同事普通的一天,拍完他沉默了,我也沉默了……来看看你能中几张👇 1️⃣ 8:30|被吵醒 ![1.JPG](https://pic.code-nav.cn/post_picture/2000454716282159105/aOylyuRqTt3QcRSi.webp) 2️⃣ 9:00|人在上班路了灵魂还在床上 ![2.JPG](https://pic.code-nav.cn/post_picture/2000454716282159105/cVOb2OsTBzKsxLIx.webp) 3️⃣ 10:30|开会嘛,懂得都懂,就是最近自己心有些痛 ![4.JPG](https://pic.code-nav.cn/post_picture/2000454716282159105/0bgwmnasinrA1WYi.webp) 4️⃣ 14:00|「需求又双叒变了」每天一拜!额弥陀福 ![5.JPG](https://pic.code-nav.cn/post_picture/2000454716282159105/bYELGXIzEXSKXgi0.webp) 5️⃣ 16:00|如果打工想赚钱,必须摸点鱼,否则全是劳动报酬 ![3.JPG](https://pic.code-nav.cn/post_picture/2000454716282159105/AKGDgfr6hUnHK3GS.webp) 如果想赚钱养小美,必须有一份比较稳定且薪酬还不错的[工作](https://jsj.top/f/mx4wQA),会Java / C++ / Python / Go / JS 任一语言均可~技术大厂,一二线城市均有坑位~ 6️⃣ 18:00|小美!我今天一定不加班,准时赴约! ![6.JPG](https://pic.code-nav.cn/post_picture/2000454716282159105/jM8xiHFRf4ahbHfT.webp) —— 程序员的你,中了几个? 祝所有程序员:需求一次过、代码零bug、上线不回滚、股票全是红、约会必准时! 🎉

1111

我爱编程导航

做了一个月了,聊聊「闲鱼虚拟」

### 1. 引子 & 收益 相信大家多多少少都有过做「副业」的想法,但副业很多是良莠不齐的,社媒上也有很多的副业打假视频,这期想要和大家来聊一聊 **「闲鱼虚拟」** 这个副业 这个副业在互联网上也是各种乱吹了,夸大的成分是极高的 为什么我这么说呢?因为我信了,我也真的去做了,距离我第一次出单,到现在差不多正好一个月的时间,我想有资格来聊一聊了 在社媒上是这样来宣传的 基本上就是轻松躺赚、日月几千甚至上万的... 看的我是不吐不快啊,直接把真实的闲鱼虚拟的收入放在开头吧,这是我这一个月自己实际做下来和一些行业内幕了解到的 **这里我们讨论的是一个人一个店铺的情况,一个人有多家店铺的情况不在这里的讨论范围内** **一个闲鱼虚拟的店铺如果做的还不错的话,一个月大概会在 700 - 1k,上限也就是这些了,至于下限的话,我只能说没有下限,闲鱼虚拟的稳定性是比较差的** ![image.png](https://pic.code-nav.cn/post_picture/1835174163661012994/9N16bB1IXZDx4S70.webp) ![image.png](https://pic.code-nav.cn/post_picture/1835174163661012994/xzdoNp3JZym8YKJ8.webp) 下面是我做闲鱼虚拟这一个月的收益情况,可以完全透明的展示给大家,这就是我 9.23 第一次出单到今天的数据 看起来还不错是吧?有差不多 1.9k 呢,但这不是利润,利润的话,是需要减去退款金额和这些虚拟资料的成本的,以及这里面的收入还有一些我卖一些闲置的实体商品的收入,这个部分占比大概是 150 左右,**最终的收入大概是在七八百左右的样子** 其中这个退款比较多的原因是,我打算把我的电动滑板给出掉,确实已经有人买了,但发快递的时候,电动滑板属于管制商品,需要一些手续和执照啥的才能正常发,就没办法给人家退款了 ![image.png](https://pic.code-nav.cn/post_picture/1835174163661012994/nsgUqGDpjmseI97y.webp) ### 2. 我为什么会做闲鱼虚拟 这里我也说一下我为什么会做闲鱼虚拟吧,非常直接的原因——没钱了... 进一步为什么没钱了,是因为我去秦皇岛海边玩的时候,手机掉水里了,检查之后,非常遗憾的废掉了...检查费还花了 100 这个手机我才买了两个月啊(是一个二手的 iPhone 15,新的买不起...) 但没办法,我需要用 iPhone 来做测试,因为我开发的 iOS 应用已经到收尾的阶段了,所以我又买了一个,花了大概 3400,相当于萍雨实习一个月的工资了🥹,iPhone 真的好贵啊 ![image.png](https://pic.code-nav.cn/post_picture/1835174163661012994/LsEMtcFqbDRnPwE8.webp) ### 3. 什么是闲鱼虚拟 & 如何赚钱 闲鱼虚拟拆开来看,「闲鱼」自然是在闲鱼这个平台,「虚拟」指的是虚拟资料 平台是可以换的,比如拼多多和淘宝,但现在拼多多虚拟已经是红海了... **虚拟资料的话,比如 PPT 模板、视频剪辑的素材、提示词、skill 等等这些都算** 那它靠什么赚钱呢? 做的时候,一般是先找到一个已经有销量的商品,在闲鱼这个平台就是去看想要数和浏览量 然后把这个商品放到自己的店铺上 **降低一些价格** 去卖,如果有人来买的话,就用自己的小号去 **抄** 的这个商家那里去买,拿到资料之后再发给在我们这里买的用户 你可能会想,进价比卖价还高,这不是亏钱吗? **没错,第一单基本上肯定是亏的** 但你想想,同一个东西,一家销量很高,一家是零销量,价格还一样,你会买哪家?大部分人都会选销量高的那家,因为看着更靠谱。所以你刚开始只能把价格压低一点,先让人愿意买你的 而虚拟资料最大的好处是,买一次就能一直卖。第一单亏一点,从第二单开始,卖一单基本就赚一单 刚才大家应该也关注到一个词了——抄 **是的,这个市场是这个样子的,抄的成本是最低的,且效果也是很不错的**,我也尝试过去出自己的虚拟商品,但事实就是压根没有人去买,nothing 接受这点需要一些时间 ### 4. 怎么做 整个流程分五步:养号,选品,上架,发货,售后 #### 1. 养号 新号先别急着上虚拟商品。你可以去拼多多复制一些日用品,发到闲鱼上,实体的东西也完全没问题。再把闲鱼的日常任务做一做,比如每天擦亮,闲鱼币的活动。 这样养个三天左右,就可以开始上架虚拟商品了。 #### 2. 选品 选品是整个流程里最关键的一步,有三个点 **第一,单价别太低。** 大家应该刷到过那种几分钱、几毛钱的,那种是纯靠走量的。我更推荐单价稍微高一点,比如几块钱到十几块钱。能找到几十块甚至上百块的当然最好。不过我自己还没卖过这么贵的,最贵的也就二十块钱左右。 **第二,能复制。** 像卡密这种东西就不行,一个人用了,别人就没法用了。 **第三,没有版权问题。** 这一点最重要。一旦碰上版权纠纷,你就算是摊上事了,要赔的钱比你做闲鱼虚拟能赚的多得多。 所以我不建议你随便在网上找份资料就开始做。我自己每次上架之前,都会先问一下带我的人,这个品能不能上,有没有问题,确认没问题了我才上。 那怎么判断带你的人靠不靠谱呢?说实话,这个我也判断不了,我算是运气比较好。这个市场真的是良莠不齐,割韭菜的很多。所以这期视频我不做任何推荐,只把这件事给大家讲清楚。 OK,这里我提到带我的人,那么这里带我的人,哪里来的呢? **很简单——买课,买服务,我是在手机落水之前买的课,价格是 1k** 如果真的要闲鱼虚拟的话,单纯靠自己去摸索,我觉得风险还是比较大的,尤其是选品这块 **如果你希望通过闲鱼虚拟去赚钱,正常情况大概需要两个月才能把你的学费给赚回来,当然这期间你会学到不少和虚拟行业的知识和相关的能力** #### 3. 上架 上架其实没什么好讲的。照着你选的那个商品抄就行,价格比他低一点,其他的都不用改。 我自己也试过去优化一下标题、描述、配图,结果优化完连买的人都没了。很现实,照抄反而效果最好。 #### 4. 发货 买家下单以后,你把文件发给他就行。也可以设置成自动发货,这个我就不展开了,我是不推荐一个闲鱼散户去做自动发货的,因为投入产出比确实是比较低的 #### 5. 售后 虚拟商品基本上没什么售后,有人来问问题,你回答一下就好。要是你也不知道怎么答,就去找你买资料的那个商家,把买家的问题原样问一遍。记得换个小号去问。 ### 5. 适合哪些群体 **前面说过了,闲鱼虚拟的上限不高,但下限低啊** 首先就需要有一个买课的成本,也可以不买,但自己摸索的成本是比较高的,成功概率也会低不少,当然这是基于买的课是靠谱的前提下,如果连卖课的人都不靠谱,那就是赔了夫人折了兵了,血亏 接着时间和精力肯定是要投入的,尤其是初期自己什么都不懂的时候,一天差不多两个小时吧,至少一周的时间 后续就算真的算是起号成功了,也会需要日常的为维护,日常的任务不能断,一些用户的询问肯定也是要回复的,当然这些基本可以在碎片时间做 所以,适合哪些群体呢? **我是觉得适合大学生,或者宝妈这类时间比较多、又没有固定收入的人 **所以如果你是上班族,我真的不建议你做。上限这么低,不如把时间花在别的地方** OK,那么这就是这期的全部内容了。大家如果有什么疑惑,也可以发在评论区,我会尽力给大家解答的。 我是萍雨,那我们下期再见~

下载 APP