2026-05-28 22:00:00
<h2 id="背景:一个繁琐到让人崩溃的任务"><a href="#背景:一个繁琐到让人崩溃的任务" class="headerlink" title="背景:一个繁琐到让人崩溃的任务"></a>背景:一个繁琐到让人崩溃的任务</h2><p>最近想给自己的 Hexo 博客换个主题。用了好几年的 Next 主题,虽然经典,但想换个更现代的风格。</p><p>看起来很简单对吧?但实际操作过的朋友都知道,Hexo 主题迁移是一个极其繁琐的过程:</p><ol><li><strong>配置文件长达 1000+ 行</strong>,需要逐项对比新旧主题的配置格式</li><li><strong>功能映射复杂</strong>:Next 的 <code>leancloud_visitors</code> 对应 Butterfly 的 <code>busuanzi</code>,Next 的 <code>reading_progress</code> 对应 Butterfly 的 <code>preloader</code></li><li><strong>两个站点要同步</strong>:中英文站都要改,而且配置略有不同</li><li><strong>Font Awesome 版本兼容</strong>:Butterfly 默认配的 FA 7.1.0 根本不存在于 cdnjs</li><li><strong>YAML 格式敏感</strong>:缩进、换行符都可能导致配置失效</li></ol><p>如果手动操作,估计要花一整天,而且很容易出错。</p><h2 id="解决方案:让-AI-Agent-来干"><a href="#解决方案:让-AI-Agent-来干" class="headerlink" title="解决方案:让 AI Agent 来干"></a>解决方案:让 AI Agent 来干</h2><p>这次我尝试了一个不同的方式:<strong>让 AI Agent(Hermes)全程接管这个任务</strong>。</p><h3 id="AI-Agent-的工作流程"><a href="#AI-Agent-的工作流程" class="headerlink" title="AI Agent 的工作流程"></a>AI Agent 的工作流程</h3><p>整个过程,AI Agent 自主完成了以下工作:</p><ol><li><strong>环境分析</strong>:检查当前 Hexo 版本(7.0.0)、Node.js 版本(v24.14.0)、两个站点的配置差异</li><li><strong>版本调研</strong>:查询 GitHub API,获取最新 Hexo 版本(8.1.2)和 breaking changes</li><li><strong>依赖升级</strong>:批量更新 hexo 及所有插件到最新版本</li><li><strong>主题对比</strong>:分析 Next 和 Butterfly 的功能覆盖度,生成对比表格</li><li><strong>配置迁移</strong>:将 Next 的 1000+ 行配置逐项映射到 Butterfly 格式</li><li><strong>问题排查</strong>:<ul><li>发现 FA 7.1.0 在 cdnjs 上 404,降级到 6.7.2</li><li>发现知乎图标需要 <code>fab</code> 前缀</li><li>发现英文站菜单路径错误</li><li>发现 GA、百度统计、站长验证等关键配置丢失</li></ul></li><li><strong>测试验证</strong>:构建测试、本地预览服务器、检查 HTML 输出</li><li><strong>代码提交</strong>:自动生成 commit message 并 push</li></ol><h3 id="关键技术点"><a href="#关键技术点" class="headerlink" title="关键技术点"></a>关键技术点</h3><h4 id="1-Font-Awesome-版本兼容性问题"><a href="#1-Font-Awesome-版本兼容性问题" class="headerlink" title="1. Font Awesome 版本兼容性问题"></a>1. Font Awesome 版本兼容性问题</h4><p>这是最隐蔽的一个 bug。Butterfly 主题的 <code>plugins.yml</code> 配置了 FA 7.1.0:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">fontawesome:</span></span><br><span class="line"> <span class="attr">name:</span> <span class="string">'@fortawesome/fontawesome-free'</span></span><br><span class="line"> <span class="attr">version:</span> <span class="number">7.1</span><span class="number">.0</span></span><br></pre></td></tr></table></figure><p>但 cdnjs 上根本没有这个版本!导致所有品牌图标(GitHub、StackOverflow、知乎)都不加载。</p><p>AI Agent 通过以下步骤定位问题:</p><ul><li>检查生成的 HTML,发现加载的是 FA 7.1.0</li><li>请求 cdnjs API,确认 7.1.0 返回 404</li><li>测试 FA 6.7.2,确认包含所有需要的图标</li><li>在配置中覆盖 CDN 地址</li></ul><h4 id="2-YAML-配置迁移的陷阱"><a href="#2-YAML-配置迁移的陷阱" class="headerlink" title="2. YAML 配置迁移的陷阱"></a>2. YAML 配置迁移的陷阱</h4><p>Hexo 的配置文件是 YAML 格式,对缩进和换行符非常敏感。AI Agent 在迁移过程中遇到了几个问题:</p><ul><li><strong>CRLF vs LF</strong>:Butterfly 默认配置是 CRLF 换行,用 sed 替换时匹配失败</li><li><strong>重复键</strong>:patch 工具导致 <code>scroll_percent</code> 出现两次</li><li><strong>值丢失</strong>:YAML 缩进不对导致配置值没有正确写入</li></ul><p>最终用 Python 直接操作文件内容才解决了这些问题。</p><h4 id="3-多站点同步"><a href="#3-多站点同步" class="headerlink" title="3. 多站点同步"></a>3. 多站点同步</h4><p>中英文站的配置大部分相同,但有几处差异:</p><ul><li>英文站的 <code>language</code> 需要改为 <code>en</code></li><li>英文站的菜单需要指向中文站</li><li>英文站的 Valine placeholder 需要用英文</li></ul><p>AI Agent 自动识别这些差异并分别处理。</p><h2 id="效果对比"><a href="#效果对比" class="headerlink" title="效果对比"></a>效果对比</h2><table><thead><tr><th>指标</th><th>手动操作</th><th>AI Agent</th></tr></thead><tbody><tr><td>耗时</td><td>4-8 小时</td><td>30 分钟</td></tr><tr><td>出错率</td><td>高(容易遗漏配置)</td><td>低(系统化检查)</td></tr><tr><td>问题排查</td><td>需要逐个 Google</td><td>自动定位根因</td></tr><tr><td>代码提交</td><td>手动写 commit message</td><td>自动生成</td></tr></tbody></table><h2 id="AI-Agent-的优势"><a href="#AI-Agent-的优势" class="headerlink" title="AI Agent 的优势"></a>AI Agent 的优势</h2><p>这次实践让我深刻体会到 <strong>AI Agent 在这类繁琐、重复性工作中</strong> 的巨大优势:</p><h3 id="1-系统化思维"><a href="#1-系统化思维" class="headerlink" title="1. 系统化思维"></a>1. 系统化思维</h3><p>AI Agent 不会像人一样”想到哪改到哪”。它会:</p><ul><li>先分析当前状态</li><li>制定完整计划</li><li>逐步执行并验证</li><li>发现问题及时修复</li></ul><h3 id="2-跨领域知识"><a href="#2-跨领域知识" class="headerlink" title="2. 跨领域知识"></a>2. 跨领域知识</h3><p>主题迁移涉及多个技术领域:</p><ul><li>Hexo 配置</li><li>Font Awesome 版本管理</li><li>YAML 格式处理</li><li>Git 工作流</li><li>前端资源加载</li></ul><p>AI Agent 能够在这些领域之间自由切换,而人类开发者通常只精通其中一两个。</p><h3 id="3-持久注意力"><a href="#3-持久注意力" class="headerlink" title="3. 持久注意力"></a>3. 持久注意力</h3><p>人类在处理 1000+ 行配置文件时,注意力会逐渐下降,容易遗漏。AI Agent 不会疲劳,每个配置项都会被检查到。</p><h3 id="4-自动化验证"><a href="#4-自动化验证" class="headerlink" title="4. 自动化验证"></a>4. 自动化验证</h3><p>AI Agent 不仅会修改配置,还会:</p><ul><li>运行构建测试</li><li>启动本地服务器验证</li><li>检查 HTML 输出</li><li>确认关键配置生效</li></ul><h2 id="适用场景"><a href="#适用场景" class="headerlink" title="适用场景"></a>适用场景</h2><p>基于这次经验,我认为 <strong>AI Agent 特别适合以下类型的工作</strong>:</p><ol><li><strong>配置迁移</strong>:不同系统之间的配置格式转换</li><li><strong>依赖升级</strong>:批量更新多个包并处理兼容性问题</li><li><strong>代码重构</strong>:大规模的代码格式调整</li><li><strong>文档整理</strong>:从多个来源整合信息</li><li><strong>环境搭建</strong>:新项目的初始化配置</li></ol><h2 id="局限性"><a href="#局限性" class="headerlink" title="局限性"></a>局限性</h2><p>当然,AI Agent 也有局限:</p><ol><li><strong>创意性工作</strong>:UI 设计、文案撰写等仍需人类主导</li><li><strong>业务决策</strong>:是否升级、选择哪个主题等决策需要人类判断</li><li><strong>复杂调试</strong>:某些运行时问题需要人工介入</li></ol><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>这次用 AI Agent 完成 Hexo 主题迁移的实践,让我看到了 <strong>LLM(大语言模型)在软件工程领域的巨大潜力</strong>。</p><p>AI Agent 不是来取代开发者的,而是来增强我们的能力。它把我们从繁琐、重复的工作中解放出来,让我们能够专注于更有价值的事情。</p><p>如果你也有类似的”体力活”,不妨试试让 AI Agent 来帮忙。你会发现,<strong>AI 辅助开发</strong> 不是未来,而是现在。</p><h2 id="相关技术栈"><a href="#相关技术栈" class="headerlink" title="相关技术栈"></a>相关技术栈</h2><ul><li><strong>AI Agent</strong>:Hermes Agent</li><li><strong>LLM</strong>:DeepSeek V4 Pro / MiMo v2.5</li><li><strong>静态站点生成器</strong>:Hexo 8.1.2</li><li><strong>主题</strong>:Butterfly 5.5.4</li><li><strong>图标库</strong>:Font Awesome 6.7.2</li><li><strong>评论系统</strong>:Valine (LeanCloud)</li><li><strong>统计分析</strong>:Google Analytics / 百度统计</li></ul><hr><p><em>本文由 AI Agent 辅助撰写,记录了一次真实的主题迁移实践。</em></p><p>原文地址:<a href="https://lichuanyang.top/posts/48979/">https://lichuanyang.top/posts/48979/</a></p>
2026-05-11 19:30:00
<p>前几天写了篇新文章,照常往 Vercel 部署,结果 pipeline 直接报错了。一开始以为是构建问题,折腾了一会儿才发现不对——是 Vercel 账号出了状况。用 163 邮箱注册的账号怎么都登不上去了。后来才搞清楚,Vercel 把整个 163.com 邮箱根域给封禁了。</p><span id="more"></span><h2 id="先说说-Vercel-是什么"><a href="#先说说-Vercel-是什么" class="headerlink" title="先说说 Vercel 是什么"></a>先说说 Vercel 是什么</h2><p>如果你是搞独立博客的,大概率听过或者正在用 Vercel。简单说,它是一个前端部署平台,支持自动从 GitHub 仓库拉代码、构建、部署,给你的网站分配一个域名,还自带 CDN 加速。很多人的博客就是 GitHub Pages + Vercel 的组合——代码放在 GitHub,用 Vercel 来部署和托管。</p><p>我自己的博客也是这么搞的。GitHub Pages 本来也能直接部署静态页面,但 Vercel 在自定义域名、HTTPS、CDN 这些方面用起来更方便,所以就选了它。</p><h2 id="163-邮箱被封禁的来龙去脉"><a href="#163-邮箱被封禁的来龙去脉" class="headerlink" title="163 邮箱被封禁的来龙去脉"></a>163 邮箱被封禁的来龙去脉</h2><p>大概在 2026 年 5 月 4 号左右,大量使用 163 邮箱注册 Vercel 的用户发现,自己的账号突然无法登录了。我在网上搜了一下,发现不只是我一个人遇到这个问题。NodeSeek、V2EX 这些论坛上都有人在讨论。</p><p>根据网上的信息,Vercel 在 2026 年 4 月中旬遭遇了一次比较严重的安全事件。黑客团伙 ShinyHunters 通过一个第三方 AI 工具入侵了 Vercel 的内部系统,窃取了员工数据、企业后台权限和一些部署凭证,还索要了 200 万美元的赎金。</p><p>这次封禁 163 邮箱,大概率是安全事件后的应对措施之一。可能是因为 163 邮箱在此次事件中被大量用于恶意注册或攻击行为,也可能只是 Vercel 对某些邮箱域名做了更严格的风控。具体原因 Vercel 没有公开说明,但结果就是:整个 163.com 邮箱域名被一刀切了。</p><p>影响范围不小。很多国内开发者和独立博客作者都是用 163 邮箱注册的 Vercel,这次封禁直接导致这些人的博客全部无法访问。</p><p>不过如果你现在 Vercel 账号还处于登录状态(比如工作电脑的浏览器还保持着登录),其实是可以抢救一下的:进账号设置,添加一个新邮箱(Outlook、Gmail 都行),然后把 163 邮箱去掉。这样账号就能继续正常使用,不需要重新部署。网上有人分享了这个方法,能省不少事。但如果你跟我一样,发现的时候已经完全登不上去了,那就只能走下面的重建流程了。</p><h2 id="回忆我的博客部署架构"><a href="#回忆我的博客部署架构" class="headerlink" title="回忆我的博客部署架构"></a>回忆我的博客部署架构</h2><p>账号被封了,博客打不开了,第一件事就是得搞清楚自己的博客到底是怎么部署的。说实话,这个博客配置的时间已经比较久了,当时配完之后就没怎么动过,具体流程已经记不太清了。</p><p>花了不少功夫才回忆起来,大概的链路是这样的:</p><ol><li>博客源码放在 GitHub 仓库里</li><li>Vercel 连接 GitHub 仓库,负责构建和部署</li><li>域名解析这块,分了两层:阿里云的 DNS 指向 Cloudflare,Cloudflare 再指向 Vercel</li></ol><p>为什么要搞这么复杂?主要是为了用 Cloudflare 的 CDN 和安全防护能力。阿里云做域名注册商,Cloudflare 做中间层的 DNS 管理和 CDN,Vercel 做最终的静态页面托管。当年配的时候也是参考了网上的一些教程,虽然链路长了点,但稳定运行了很久,也就没再动过。</p><h2 id="恢复过程:新账号-重新部署"><a href="#恢复过程:新账号-重新部署" class="headerlink" title="恢复过程:新账号 + 重新部署"></a>恢复过程:新账号 + 重新部署</h2><p>搞清楚部署架构之后,恢复操作其实并不复杂。</p><p><strong>第一步:注册新的 Vercel 账号</strong></p><p>这次学聪明了,不用 163 邮箱了,用 Outlook 重新注册了一个 Vercel 账号。其实 Gmail 也行,但考虑到这次是 Vercel 封禁了特定邮箱域名,用国外主流的邮箱服务会更稳妥一些。</p><p><strong>第二步:重新部署 GitHub 项目</strong></p><p>登录新账号后,连接 GitHub,把博客仓库导入 Vercel。Vercel 自动识别了项目类型,构建和部署都是自动完成的,跟之前的操作基本一样。</p><p><strong>第三步:重新配置域名</strong></p><p>这一步是关键。因为之前域名解析是阿里云 → Cloudflare → Vercel 的链路,重新部署后需要把域名绑定到新的 Vercel 项目上。</p><p>让我惊喜的是,现在 Vercel 在添加自定义域名的时候,已经支持自动修改 Cloudflare 的 DNS 配置了。以前我记得需要手动去 Cloudflare 后台添加 CNAME 记录,现在 Vercel 直接通过 API 帮你搞定,点几下就完事了。</p><p>整个恢复过程,从注册新账号到博客重新可以访问,大概花了不到半小时。</p><h2 id="几点经验"><a href="#几点经验" class="headerlink" title="几点经验"></a>几点经验</h2><ol><li><p><strong>不要用国内邮箱注册海外服务</strong>。163、QQ 邮箱这类国内邮箱,在海外服务的风控体系里天然就是高风险对象。这次是 Vercel,下次可能是别的服务。建议主力账号用 Gmail 或 Outlook。</p></li><li><p><strong>记录好自己的部署架构</strong>。这次最浪费时间的环节就是回忆部署链路。当时配置完觉得都记住了,结果过了这么久早就忘得差不多了。建议把部署架构和关键配置写在笔记里,哪怕只是几句话。</p></li><li><p><strong>Vercel 现在对 Cloudflare 的集成做得不错</strong>。以前手动配 DNS 的时候容易出错,现在自动化程度高了很多,重新部署的成本降低了不少。</p></li><li><p><strong>有条件的话,做好备份和多平台准备</strong>。这次运气好,恢复起来比较快。但如果 GitHub 仓库也出了问题,或者 Vercel 彻底封了项目而不只是账号,恢复就会麻烦很多。重要的博客内容,建议在本地或者别的平台也有一份。</p></li></ol><h2 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h2><p>这次 Vercel 封禁 163 邮箱的事,算是给所有用国内邮箱注册海外服务的人提了个醒。平台侧的安全措施我们控制不了,但自己的账号安全和部署架构,还是可以提前做好准备的。</p><p>如果你也遇到了 Vercel 163 邮箱被封、Vercel 账号无法登录的问题,希望这篇文章能帮到你。恢复的核心思路就是:换一个邮箱重新注册,把项目重新部署一遍,域名重新绑定一下。操作不复杂,但前提是你得记得自己的部署链路。</p><p>原文地址: <a href="https://lichuanyang.top/posts/39648/">https://lichuanyang.top/posts/39648/</a></p><hr>
2026-05-11 15:38:16
<p>最近在整理团队的安全开发规范时,遇到了一个老问题:安全规范文档越积越多,但真正用的时候却找不到。每次新人入职,都要翻遍各种文档才能拼凑出完整的安全规范;每次出安全故障,事后总结的经验也散落在各处,下次遇到类似问题还得重新排查。</p><p>我试过用Confluence、Notion、甚至Git仓库的README来管理,但效果都不理想。直到看到了Karpathy提的llm-wiki思路,才觉得这可能是个突破口。</p><span id="more"></span><h2 id="Karpathy的llm-wiki是什么?"><a href="#Karpathy的llm-wiki是什么?" class="headerlink" title="Karpathy的llm-wiki是什么?"></a>Karpathy的llm-wiki是什么?</h2><p>Karpathy在<a href="https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f">Gist</a>里提了个想法:让LLM维护一个Wiki中间层。原话是”the wiki is a persistent, compounding artifact”——这个Wiki层就像个编译器,把原始文档编译成结构化数据,这样LLM每次回答问题时就不用从头在原始文档里”找”信息,直接查Wiki就行。</p><p>我觉得这个思路特别妙。传统文档管理是”存”和”找”,而llm-wiki是”编译”和”查询”。原始文档可能很乱、有重复、甚至有矛盾,但Wiki层是干净、结构化、有交叉引用的。</p><p>举个例子:我们团队有十几份安全开发规范文档,从输入验证到渗透测试都有,还有近十份安全故障复盘报告。这些文档格式不一,有的在Word里,有的在Markdown里,有的甚至只是Slack聊天记录。如果让LLM直接处理这些原始材料,效率会很低。但如果先”编译”成Wiki,效果就完全不一样了。</p><h2 id="我的实现过程"><a href="#我的实现过程" class="headerlink" title="我的实现过程"></a>我的实现过程</h2><h3 id="第一步:生成CLAUDE-md"><a href="#第一步:生成CLAUDE-md" class="headerlink" title="第一步:生成CLAUDE.md"></a>第一步:生成CLAUDE.md</h3><p>我首先把Karpathy的Gist链接丢给Claude,让它基于这个思路生成一份CLAUDE.md文件。这个文件相当于给LLM的”操作手册”,告诉它如何维护Wiki。</p><p>生成的CLAUDE.md包含了几个关键部分:</p><ul><li>三层架构说明(raw → wiki → output)</li><li>Wiki页面规范(frontmatter格式、交叉引用规则)</li><li>摄入工作流(如何处理新素材)</li><li>查询工作流(如何回答用户问题)</li><li>Lint检查(如何保证Wiki健康)</li></ul><p>实际用下来,我发现模型生成的结果基本可用,没有做太多微调。只是另外让AI生成了output层,用于存放最终的规范文档。这样三层架构就完整了:raw放原始素材,wiki放结构化Wiki,output放最终输出。</p><h3 id="第二步:收集和整理原始素材"><a href="#第二步:收集和整理原始素材" class="headerlink" title="第二步:收集和整理原始素材"></a>第二步:收集和整理原始素材</h3><p>这是最耗时的一步。我把散落在各处的安全开发规范文档和故障案例都收集起来,统一放到raw/sources/目录下。为了保持原貌,我没有修改这些文件的内容,也没有统一格式——PDF、Word、PPT、Markdown等各种格式直接放到raw层就行,模型都可以处理。</p><p>具体来说,我收集了:</p><ul><li>十几份安全开发规范文档:涵盖输入验证、SQL注入防护、XSS防护、CSRF防护、敏感数据保护、日志安全等</li><li>近十份安全故障复盘报告:包括SQL注入漏洞、越权访问、敏感信息泄露、会话劫持等真实案例</li><li>几份安全架构方案文档:涉及零信任架构、安全编码指南、渗透测试流程等</li></ul><p>这些素材质量参差不齐。有的写得很规范,有清晰的标题和列表;有的就是随手记的笔记,甚至还有截图。但没关系,llm-wiki的妙处就在于它能处理这种”脏数据”。</p><h3 id="第三步:让LLM生成Wiki层"><a href="#第三步:让LLM生成Wiki层" class="headerlink" title="第三步:让LLM生成Wiki层"></a>第三步:让LLM生成Wiki层</h3><p>有了CLAUDE.md和原始素材,就可以让LLM开始工作了。我这里用的是Claude,主要是因为它的上下文窗口足够大,能一次性处理较多内容。</p><p>具体操作是这样的:</p><ol><li>把CLAUDE.md作为系统提示词</li><li>告诉LLM:”请按照CLAUDE.md中的规范,处理raw/sources/目录下的所有素材,生成wiki层”</li><li>LLM会依次读取每个原始文件,提取关键信息,生成对应的Wiki页面</li></ol><p>这个过程不是一次就能完成的。LLM生成的初版Wiki会有一些问题:</p><ul><li>交叉引用不够充分</li><li>有些概念没有被提取成独立页面</li><li>页面之间的逻辑关系不够清晰</li></ul><p>所以需要多轮迭代。我会检查生成的Wiki,指出问题,让LLM修正。比如:<br>“这个故障案例里提到了’连接池配置不当’,但为什么没有对应的concept页面?请创建一个[[connection-pool-best-practices]]页面,并在故障案例里引用它。”</p><p>经过5-6轮迭代,Wiki层才逐渐成型。</p><h3 id="第四步:生成最终规范文档"><a href="#第四步:生成最终规范文档" class="headerlink" title="第四步:生成最终规范文档"></a>第四步:生成最终规范文档</h3><p>Wiki层准备好后,就可以生成最终的规范文档了。我让LLM基于Wiki层,生成一份面向开发者的、可直接阅读的规范文档。</p><p>这里有个设计决策:最终文档是放在output/目录下,而不是直接修改原始素材。这样保持了三层架构的清晰性:</p><ul><li>raw/:原始素材,不可修改</li><li>wiki/:结构化Wiki,LLM维护</li><li>output/:最终输出,人类阅读</li></ul><p>生成的规范文档有几个特点:</p><ol><li>每个规范条目都有”为什么”——不只是说”要这样做”,还解释”为什么要这样做”</li><li>相关的故障案例会作为”反面教材”链接在旁边</li><li>有交叉引用,比如”数据库连接池配置”会链接到”连接池最佳实践”这个concept页面</li></ol><h2 id="实际效果"><a href="#实际效果" class="headerlink" title="实际效果"></a>实际效果</h2><h3 id="阅读体验"><a href="#阅读体验" class="headerlink" title="阅读体验"></a>阅读体验</h3><p>最终的规范文档可以直接用Obsidian打开阅读。因为是Markdown格式,支持:</p><ul><li>代码块高亮</li><li>内部链接跳转(点击就能跳转到相关概念或故障案例)</li><li>标签筛选(比如筛选所有#安全相关的规范)</li></ul><p>更重要的是,每条安全规范都关联着真实的故障案例。比如在”SQL注入防护”这条规范旁边,就链接着[[sql-injection-vulnerability-incident]]这个故障案例,详细描述了漏洞是怎么产生的、造成了什么影响、怎么修复的。这样开发者不仅能知道”要怎么做”,还能看到”不这么做会出什么问题”。</p><p>比起以前散落在各处的文档,阅读体验好了不止一个量级。</p><h3 id="LLM问答"><a href="#LLM问答" class="headerlink" title="LLM问答"></a>LLM问答</h3><p>更强大的是LLM问答能力。因为有了Wiki层,LLM回答问题时特别准。比如我问:<br>“数据库批量更新时要注意什么?”</p><p>LLM会:</p><ol><li>先在Wiki里找到[[batch-operation-security]]这个concept页面</li><li>结合[[sql-injection-batch-incident]]这个故障案例</li><li>给出具体的建议:必须使用参数化查询、禁止字符串拼接SQL、批量操作要有事务控制、超大批次要分页处理等</li><li>还会解释为什么:因为批量操作一旦出问题,影响范围是单条操作的N倍</li></ol><p>再比如问:”用户上传文件怎么处理才安全?”</p><p>LLM会:</p><ol><li>找到[[file-upload-security]]这个concept页面</li><li>结合[[malicious-file-upload-incident]]这个故障案例</li><li>给出具体建议:文件类型白名单校验、文件内容检测、重命名存储、限制文件大小、隔离存储等</li></ol><p>这比让LLM在十几份原始文档里”找”答案要靠谱得多。</p><h3 id="持续更新"><a href="#持续更新" class="headerlink" title="持续更新"></a>持续更新</h3><p>最让我满意的是更新机制。当有新的规范文档或故障报告时,我只需要:</p><ol><li>把新文件放到raw/sources/目录下</li><li>告诉LLM:”有新素材需要摄入”</li><li>LLM会自动更新Wiki层和规范文档</li></ol><p>比如上周我们出了个新的安全故障:因为用户输入未过滤导致XSS漏洞。我把故障报告放到raw目录后,LLM自动:</p><ol><li>创建了[[xss-user-input-incident]]这个故障案例页面</li><li>更新了[[input-validation-security]]这个concept页面,补充了XSS相关的注意事项</li><li>在规范文档的”输入验证规范”章节增加了相关条目</li></ol><h2 id="遇到的问题和解决方案"><a href="#遇到的问题和解决方案" class="headerlink" title="遇到的问题和解决方案"></a>遇到的问题和解决方案</h2><h3 id="问题1:LLM生成的Wiki质量不稳定"><a href="#问题1:LLM生成的Wiki质量不稳定" class="headerlink" title="问题1:LLM生成的Wiki质量不稳定"></a>问题1:LLM生成的Wiki质量不稳定</h3><p>刚开始时,LLM生成的Wiki质量波动很大。有时能很好地提取关键信息,有时会漏掉重要内容。</p><p><strong>解决方案</strong>:我细化了CLAUDE.md里的Wiki页面规范。比如要求每个页面必须有:</p><ul><li>Summary行:一句话描述核心内容</li><li>Tags行:用#标签标记主题</li><li>明确的页面类型(concept/entity/source/comparison)</li></ul><hr><p>对于安全故障案例,还要求包含:漏洞类型、攻击向量、影响范围、修复方案、预防措施。这样LLM就有更清晰的指导,生成质量稳定了很多。</p><h3 id="问题2:交叉引用经常断裂"><a href="#问题2:交叉引用经常断裂" class="headerlink" title="问题2:交叉引用经常断裂"></a>问题2:交叉引用经常断裂</h3><p>Wiki的核心价值在于交叉引用,但LLM经常创建单向链接,或者链接到不存在的页面。</p><p><strong>解决方案</strong>:在CLAUDE.md里强化了交叉引用规则:</p><ol><li>必须双向链接:如果A页面引用了[[B]],那么B页面也必须引用[[A]]</li><li>frontmatter里的related字段必须和正文里的[[wiki-link]]一致</li><li>只能引用已存在的页面,需要先创建再引用</li></ol><p>我还加了Lint检查,定期检查断裂链接和孤儿页面。</p><h3 id="问题3:处理非结构化素材效率低"><a href="#问题3:处理非结构化素材效率低" class="headerlink" title="问题3:处理非结构化素材效率低"></a>问题3:处理非结构化素材效率低</h3><p>有些原始素材质量很差,比如截图、手写笔记、甚至是Slack聊天记录的导出。LLM处理这些素材时效率很低。</p><p><strong>解决方案</strong>:对于这类素材,我会先手动整理成结构化的Markdown,再交给LLM处理。虽然增加了前期工作量,但保证了最终质量。</p><h2 id="一些经验教训"><a href="#一些经验教训" class="headerlink" title="一些经验教训"></a>一些经验教训</h2><ol><li><p><strong>不要追求一步到位</strong>。llm-wiki是个迭代过程。第一版Wiki肯定不完美,但可以通过持续迭代改进。</p></li><li><p><strong>CLAUDE.md要不断优化</strong>。随着使用,你会发现CLAUDE.md里缺失的规则。比如我后来加了”安全故障案例必须包含:漏洞类型、攻击向量、影响范围、修复方案、预防措施”这样的结构化要求。</p></li><li><p><strong>保持三层架构的纯净</strong>。raw/就是raw/,不要修改;wiki/由LLM维护;output/面向人类。混在一起会越来越乱。</p></li><li><p><strong>定期做健康检查</strong>。我会每周跑一次Lint,检查断裂链接、孤儿页面、单向链接等问题。发现问题及时修复。</p></li><li><p><strong>接受不完美</strong>。LLM生成的Wiki不会100%准确,会有遗漏和错误。但相比完全靠人工维护,已经好太多了。</p></li></ol><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>用llm-wiki管理安全开发规范,本质上是把”文档管理”变成了”知识工程”。原始文档是”原材料”,Wiki是”半成品”,规范文档是”成品”。LLM在这个过程中充当了”编译器”和”维护者”的角色。</p><p>这个方法最大的价值是<strong>可持续性</strong>。传统的文档管理,时间一长就会荒废,因为维护成本太高。而llm-wiki把维护成本转移给了LLM,人类只需要:</p><ol><li>提供原始素材(这个避免不了)</li><li>检查和修正LLM的工作(比从头写轻松多了)</li><li>使用最终产出(享受成果)</li></ol><p>如果你也在为安全开发规范的管理头疼,不妨试试这个思路。不用完全照搬我的实现,关键是理解”Wiki中间层”这个核心理念。至于具体用什么工具、怎么组织目录,都可以根据实际情况调整。</p><p>毕竟,工具是为人服务的,不是吗?</p><p>原文地址: <a href="https://lichuanyang.top/posts/88001/---">https://lichuanyang.top/posts/88001/---</a></p>
2024-03-06 19:36:22
<p>后端工程师开发前端的痛点,通常来说莫过于太过繁琐,经常要为了一些很小的事查半天。Vaadin很好的解决了这个痛点,为后端工程师提供易上手、方便使用的前端代码编写解决方案,今天我们就来了解一下。</p><span id="more"></span><p>大家好,今天跟大家介绍一个对后端工程师特别有价值的工具——Vaadin。</p><p>说起来,上手前端基本的html, css开发,确实并不难,但是如果只会这些基本的东西,开发起来会很繁琐。如果想要使用前端生态中的各种轮子,虽说便利度提升了,但学习成本也会同步上升。所以,如果不是职业的全栈工程师,只是作为一个后端,想临时写点前端代码,比如自己想做点小项目,通常来说都会有个很痛苦的过程。</p><p>Vaadin很好的解决了这个痛点。通过vaadin包装好的常用前端组件,我们几乎可以零学习成本的编写出功能完备、不太难看的页面。对于后端背景的程序员来说,无疑会大幅度降低自己做些小项目的成本。</p><p>Vaadin提供的功能,就是可以直接用java代码来写页面。Vaadin提供了多种输入框、表单等等封装好的前端样式,而且与springboot做了深度的融合,使用起来非常方便。</p><p>Vaadin的实际原理并不复杂,主要是基于服务端渲染,即在后端生成最终的html代码,交给浏览器。服务端渲染,这个并不罕见,与客户端渲染的优势和劣势,我们在这里不多讲。当然,对于vaadin来说,使用服务端渲染,似乎也没什么好说的,毕竟是写的后端代码,直接在后端做渲染,是个再正常不过的实现路径。Vaadin的引擎对前后端之间的交互做了封装,所以对使用者来说,前后端之间的交互是无感的,在页面层,我们也可以正常的调用后端service.</p><p>下面是我写的一段代码示例,可以更直观的感受Vaadin的作用:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">@Route(value = "path", layout = MainView.class)</span><br><span class="line">@PageTitle("路径规划")</span><br><span class="line">public class PathView extends VerticalLayout {</span><br><span class="line"></span><br><span class="line"> @Autowired</span><br><span class="line"> private PathService pathService;</span><br><span class="line"></span><br><span class="line"> public PathView() {</span><br><span class="line"> TextField start = new TextField();</span><br><span class="line"> TextField end = new TextField();</span><br><span class="line"> HorizontalLayout path = new HorizontalLayout();</span><br><span class="line"> path.add(start, end);</span><br><span class="line"> Button pathCalculate = new Button("calculate path");</span><br><span class="line"></span><br><span class="line"> VerticalLayout result = new VerticalLayout();</span><br><span class="line"> TextField transferNum = new TextField();</span><br><span class="line"> TextField distance = new TextField();</span><br><span class="line"> Text stations = new Text("");</span><br><span class="line"> result.add(</span><br><span class="line"> new H3("换乘数: "),</span><br><span class="line"> transferNum,</span><br><span class="line"> new H3("总距离 :"),</span><br><span class="line"> distance,</span><br><span class="line"> new H3("途径站点详情:"),</span><br><span class="line"> stations</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> pathCalculate.addClickListener(click -> {</span><br><span class="line"> PathInfoVO pathResult = pathService.getPath(start.getValue(), end.getValue());</span><br><span class="line"> System.out.println(pathResult);</span><br><span class="line"> stations.setText(StringUtils.join(pathResult.getDetail(), ","));</span><br><span class="line"> transferNum.setValue(String.valueOf(pathResult.getTransferNum()));</span><br><span class="line"> distance.setValue(String.valueOf(pathResult.getDistance()));</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> add(</span><br><span class="line"> new Text("hello world"),</span><br><span class="line"> path,</span><br><span class="line"> pathCalculate,</span><br><span class="line"> result</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这段代码中,我们完全使用java代码对页面中的各个组件进行了编排,包括button的click函数,也是使用java开发者习惯的方式定义,并且能够直接调用其他后端service, 可以说是几乎零学习成本了。</p><p>详细代码和运行效果,可以到 <a href="https://github.com/lcy362/mobo-subway">项目地址</a>中查看。</p><p>如果你对Vaadin感兴趣,或者有任何问题或想法,欢迎在评论区交流。一起探索如何更好地利用Vaadin,提升我们的开发效率吧!</p><p>原文地址: <a href="https://lichuanyang.top/posts/43947/">https://lichuanyang.top/posts/43947/</a></p><hr>
2024-01-22 18:12:31
<p>对于hexo的多语言方案,官方并没有提供很完备的支持,但是网络上有很多人尝试过不同的解决方案。今天,我们对这些方案简单做个总结,看看我们需要的多语言方案是什么样子的。</p><span id="more"></span><p>其实,hexo的多语言方案,说白了就是两个思路,一个是在文章维度切换语言,借助hexo原生的能力和hexo-generator-i18n等插件,用一套框架维护两个语言的内容;另一个则是独立维护不同语言的样式、内容,在站点维度切换语言,只是在域名层面合并到同一个域名。</p><p>具体要选择什么方案,取决于大家想要一个什么样的多语言网站。一开始我想象中的多语言网站的是第一种,即统一的首页,在首页和每篇文章上都支持切换语言,对每篇文章,可以通过切换语言,直达对应的其他语言译文上。</p><p>这个思路,借助于hexo-generator-i18n插件,可以实现,但要做比较多的定制开发。所以,我又重新思量了一下思路,我要的多语言网站应该是什么样。重新考虑之后,逐渐觉得第二种思路才是更合理的。我们要做一个多语言网站的目的是什么?对我来说,其实就是要获取一些英文流量。作为一个未备案网站,已经很久无法获取百度的搜索流量了,google在中国占的份额又太低,所以单凭中文内容,很难从搜索中获取较高流量。所以我希望通过提供一些英文内容,获取英文流量。这个目标下,完全不需要对所有的文章都维护多语言版本。何况,不同文章的语言受众也的确有很大区别,所以,语言的切换可以直接在站点层面进行。</p><p>具体操作方式上,一种是直接维护多个站点,两个站点内容、样式完全独立,只是部署在同一个域名下。比如中文站点根路径是lichuanyang.top,英文站点根路径是lichuanyang.top/en . 两个站点上各做一个跳转链接,向对方跳转。</p><p>我采用的是另一种方式,相比上述方式,成本小一些,不需要实际维护两个站点。原理大致如下:</p><p>在本地,同样维护两个站点。但是英文站点不会去做实际的部署,只是用作生成静态网页的工具。英文版的内容生成之后,直接复制到中文网站的对应目录下,只对中文网站做部署即可。</p><p>具体操作步骤如下:</p><ul><li><p>将博客目录整体复制一份,作为英文博客目录. 例如,我的博客根目录叫blog.source, 复制出一个blog.source.en. 这步完成后,如果博客源码也是以git维护的,可以直接在原目录的外层直接新建一个git项目,在外层做管理就行了。 示例如下:</p> <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">blog(git根目录)/blog.source</span><br><span class="line"> /blog.source.en</span><br></pre></td></tr></table></figure></li><li><p>将en目录下的文章全部删除;站点描述等文本调整为英文内容;英文语言设置为en;英文站点根 (_config.yml中的root配置)设置成/en</p></li><li><p>在两个站点下增加跳转链接。我是借助菜单功能,直接在菜单里加一个其他语言项。 next主题中配置如下:</p> <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">中文站 : English: /en || fa fa-language</span><br><span class="line">英文站: 中文: https://lichuanyang.top || fa fa-language</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li><p>之后配置基本就完成了,可以在en目录下写英文文章,流程与之前写中文文章时完全一致</p></li><li><p>最后就是生成和发布环节了,这一步要注意,每次生成时,我们需要先生成中文站点,再生成英文站点并将英文内容复制到中文目录下,避免生成中文内容时将英文内容覆盖掉。具体操作示例如下:</p></li></ul> <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo clean && hexo g &&cd ../blog.source.en && hexo clean && hexo g && cd ../blog.source &&cp -r ../blog.source.en/public/. public/en/ && hexo s</span><br></pre></td></tr></table></figure><p> 命令最后一段,使用hexo s就是本地启动,使用hexo d就是发布出去,和正常使用时一样。</p><p> 这样,我们就有了一个好用的多语言站点了。之后,写中文内容就在中文目录下进行,写英文内容就在英文目录下进行, 最后执行一下上边的命令就可以了。</p><h2 id="原文地址-https-lichuanyang-top-posts-40400"><a href="#原文地址-https-lichuanyang-top-posts-40400" class="headerlink" title="原文地址: https://lichuanyang.top/posts/40400/"></a>原文地址: <a href="https://lichuanyang.top/posts/40400/">https://lichuanyang.top/posts/40400/</a></h2>
2023-12-22 17:24:18
<p>一个优化知乎显示的油猴工具</p><span id="more"></span><p>之前在 <a href="https://meta.appinn.net/t/topic/47711/18">小众软件</a> 上看到一个知乎评论区时间展示混乱的吐槽,有大佬立刻就给了一个油猴脚本。</p><p>用了一段时间很舒服,然后前几天知乎做了个更新,导致展示开始混乱。研究了一下,做了更新,想着就发布到GreasyFork上吧,后边知乎再有调整,可以直接从线上更新。</p><p>脚本地址: <a href="https://greasyfork.org/zh-CN/scripts/482871-%E7%9F%A5%E4%B9%8E%E5%A2%9E%E5%BC%BA-%E8%AF%84%E8%AE%BA%E6%97%B6%E9%97%B4%E7%B2%BE%E7%A1%AE%E5%88%B0%E7%A7%92">安装地址</a></p><p>大家感兴趣的话,欢迎留言,后边考虑做个油猴脚本的开发教程。</p><p>原文地址: <a href="https://lichuanyang.top/posts/44912/">https://lichuanyang.top/posts/44912/</a></p><hr>