<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>九层台 - Jeffrey Wang 的个人博客</title>
  <icon>https://cdn.jsdelivr.net/gh/wangerzi/blog@latest/source/static/avatar.jpg</icon>
  <subtitle>合抱之木，生于毫末；九层之台，起于累土；千里之行，始于足下。</subtitle>
  <link href="https://blog.wj2015.com/atom.xml" rel="self"/>
  
  <link href="https://blog.wj2015.com/"/>
  <updated>2026-08-12T03:28:46.856Z</updated>
  <id>https://blog.wj2015.com/</id>
  
  <author>
    <name>Jeffrey Wang</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>当我用 AI 将一天的工作压缩到一小时后，我想起了马克思</title>
    <link href="https://blog.wj2015.com/2025/07/18/%E5%BD%93%E6%88%91%E7%94%A8-AI-%E5%B0%86%E4%B8%80%E5%A4%A9%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%8B%E7%BC%A9%E5%88%B0%E4%B8%80%E5%B0%8F%E6%97%B6%E5%90%8E%EF%BC%8C%E6%88%91%E6%83%B3%E8%B5%B7%E4%BA%86%E9%A9%AC%E5%85%8B%E6%80%9D/"/>
    <id>https://blog.wj2015.com/2025/07/18/%E5%BD%93%E6%88%91%E7%94%A8-AI-%E5%B0%86%E4%B8%80%E5%A4%A9%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%8B%E7%BC%A9%E5%88%B0%E4%B8%80%E5%B0%8F%E6%97%B6%E5%90%8E%EF%BC%8C%E6%88%91%E6%83%B3%E8%B5%B7%E4%BA%86%E9%A9%AC%E5%85%8B%E6%80%9D/</id>
    <published>2025-07-18T05:42:38.000Z</published>
    <updated>2026-08-12T03:28:46.856Z</updated>
    
    <content type="html"><![CDATA[<!-- > 宫崎骏画风，深夜，背景是密密麻麻的格子间工位，一个头发稀疏的男人在视觉中心，桌面上放着苹果电脑表情狰狞的奋力工作，桌面上放着咖啡、可乐，以及名为“Cursor”、“LLM” 的书籍![cover4](../static/assets/blog-marx/cover4-20250718125707-iuaybjq.jpeg) --><!-- > 宫崎骏动画电影截图，一个经验丰富的程序员坐在他堆满书籍和模型的书桌前，窗外是黄昏时的云朵。他没有在敲代码，而是托着下巴，眼神迷茫地看着电脑屏幕，屏幕上正以极快的速度自动生成优雅的代码流。屏幕中有一个小小的发光AI精灵，正在对他微笑。液晶电脑屏幕上隐约浮现出“工作价值？”这几个字。整个画面色彩温暖，光线柔和，富有故事感。 --><!-- ![co2](../static/assets/blog-marx/co2-20250718134032-ra87zhq.jpeg) --><html><head></head><body><h2 id="“智驾式编程”从振奋到冷静的思考"><a href="#“智驾式编程”从振奋到冷静的思考" class="headerlink" title="“智驾式编程”从振奋到冷静的思考"></a>“智驾式编程”从振奋到冷静的思考</h2><p>不久前，我写了一篇关于 AI 辅助编程的文章<a href="https://blog.wj2015.com/2025/06/10/AI-First-%E7%BC%96%E7%A8%8B%EF%BC%9ACursor-%E6%B7%B1%E5%BA%A6%E4%BD%93%E9%AA%8C%E5%92%8C%E2%80%9D%E6%99%BA%E9%A9%BE%E5%BC%8F%E7%BC%96%E7%A8%8B%E2%80%9C%E5%AE%9E%E6%93%8D/">《AI First 编程：Cursor 深度体验和”智驾式编程“实操》</a>，我将其描述为“智驾式编程”。</p><p>在那篇文章中，我<strong>总结和展示了 AI 如何将我从繁琐的样板代码和重复性工作中解放出来，带来了数倍的效率提升</strong>。一个过去需要花费大半天时间反复打磨的复杂组件，在 AI 的辅助下，可能不到一小时就能构建出功能完备的雏形。就像让一名习惯于手动驾驶的老司机，体验到了 L4 级别自动驾驶的流畅和高效。</p><blockquote><p>前几天 Cursor 还禁止了国内开发者使用高级模型，用一个笑话来讲就是“突发！国内开发者一夜之间效率降低100倍！！！”，本质是现在的互联网先进生产力还不够独立自主，但我相信在大模型如烈火烹油的迭代频率下，再过几年基础模型和 Code Agent 能力的差距一定会逐渐缩小的。</p></blockquote><p>经过了一段时间的振奋期过后，我开始慢慢思考一个更为本质的问题。</p><p>作为一名有七年经验的 Web 全栈开发者，我的<strong>核心竞争力，有相当一部分是建立在经验和熟练度之上的</strong>，也就是我知道如何快速、稳定地构建各种功能，熟悉其中的“坑”与最佳实践。</p><p>而到了现在，<strong>AI 似乎正在迅速稀释这种基于重复实践所积累的熟练度</strong>。当一名初级开发者借助 AI 也能快速达到我过去多年经验积累的工作成果时，我们劳动的本质发生了什么变化？我们工作的核心价值，正在从“如何高效稳定的实现业务需求”，悄悄地转向何方呢？</p><p>这个关于价值的困惑，让我联想到了马克思《资本论》中的一些概念。它像一把尺子，帮助我们衡量在不断变化的技术浪潮中劳动的社会意义。这个核心概念就是<strong>社会平均必要劳动时间</strong> (Socially Necessary Labour Time, SNLT)。</p><h2 id="什么是“社会平均必要劳动时间”？"><a href="#什么是“社会平均必要劳动时间”？" class="headerlink" title="什么是“社会平均必要劳动时间”？"></a>什么是“社会平均必要劳动时间”？</h2><!-- > 宫崎骏画风，户外，背景是钢铁厂和忙碌的工人们，钢铁工人拿着扳手，妇女拿着麦穗，小孩子拿着纸飞机，老人坐在轮椅上，底部有一个红色的大横幅，横幅上写着“社会平均必要劳动时间” --><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-marx/avg3-20250718130732-ham3rzp.jpeg" alt="avg3" data-caption="avg3" loading="lazy"></p><p>在深入讨论之前，我们需要准确理解“社会平均必要劳动时间”的定义（后文用 SNLT 指代）。</p><p>它并非指你个人完成一项工作所花费的时间，而是指：<strong>在现有社会正常的生产条件下</strong>，在社会平均<strong>劳动熟练程度</strong>和<strong>劳动强度</strong>下，制造某种使用价值所需要的时间。</p><p>它是一个社会化的、动态的平均值。其核心影响因素包括：</p><ol><li><strong>生产力的发展水平：</strong>  这是最关键的变量。蒸汽机、电力、互联网，以及我们今天所谈论的 AI，都是生产力工具的革命性进步。它们会从根本上改变特定工作的正常生产条件。</li><li><strong>科学技术的发展与应用：</strong>  开源框架（如 React、Vue）、云计算、DevOps 工具链，都曾极大地压缩了软件开发的 SNLT。Cursor 等 AI Copilot 是这个序列中的最新成员。</li><li><strong>劳动者的平均熟练程度：</strong>  当一项新技术或工具（比如之前的 Git、Python）从少数先锋的玩物普及为行业标配时，整个社会的平均熟练度基准就随之提高了。</li></ol><p>在 AI 辅助编程普及之前，从零手写一个功能完备的用户注册组件的 SNLT 可能是 3 小时。你用 2 小时完成，说明你的劳动效率高于社会平均水平；而如果有人花费 5 小时才能做完，那么多出来的 2 小时，从社会价值的角度看，是不被计入的，会被定义为浪费掉的劳动力。</p><p>现在 AI 作为新的生产力要素登场，它就正在重置这把价值标尺的刻度。</p><h2 id="AI-时代价值的演变"><a href="#AI-时代价值的演变" class="headerlink" title="AI 时代价值的演变"></a>AI 时代价值的演变</h2><!-- > 一名年轻的工程师，站在山丘上，像一位指挥家，面前是由光芒构成的复杂系统蓝图。他伸出手，指挥着成群结队、像萤火虫一样的AI代码精灵，在远方的山谷中建造一座精巧的空中之城。天空广阔，充满希望。宫崎骏画风，吉卜力工作室，色彩明亮温暖，细节丰富，电影感。 --><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-marx/value3-20250718131737-0v205tq.jpeg" alt="value3" data-caption="value3" loading="lazy"></p><p>SNLT 的缩短，并不直接等同于我们工作价值的降低，而是<strong>标志着价值焦点的转移</strong>。</p><p>过去，开发者的大量价值体现在<strong>将抽象需求“翻译”成具体、可靠代码的熟练度上</strong>。但 AI 正在以惊人的速度接管这部分“翻译”工作。</p><p>因此我们劳动的价值，正在向这条价值链的上游，即“翻译”之前的环节集中，这些环节的工作价值先比 SNLT 依然很高，AI 短期内还难以完全接管：</p><ul><li><strong>定义问题与抽象建模的能力：</strong>  对 AI 提出一个模糊的需求，和给出一个结构清晰、边界明确、逻辑严谨的指令，产出的结果天差地别。将复杂的业务场景分解、抽象为清晰的技术模型，这种能力是价值的源泉。</li><li><strong>系统设计与架构决策的能力：</strong>  AI 可以生成一个独立的模块，但决定模块之间如何通信、数据如何在系统中安全高效地流转、如何保证整个系统的可扩展性和长期稳健性，这些架构层面的权衡与决策，是更高维度的劳动。</li><li><strong>知识的整合与创新应用的能力：</strong>  将业务逻辑、用户体验、工程实践、团队协作等多领域知识融会贯通，形成一个整体性的解决方案，并有效指挥 AI 去执行。</li></ul><p>从打工人个人来讲，这其中有危险也有机遇，危险在于目前所有的工作稳定性都在降低（结合社会环境更是如此，强如花旗、微软都在裁员），机会在于我们可以从部分繁重的劳动中解放出来，去从事更具创造性更具附加价值的工作 。</p><p>这些转变也让我们有必要重新审视自己的努力与成长方向。在未来，努力可能不再是深夜里在重复劳动中肝头发，而是在有限的时间内借助 AI 主动进行高效的深度学习和系统性思考。成长也需要从单纯地追求代码实现的熟练度，转向构建更宏观的知识体系和解决复杂、模糊问题的能力。</p><h4 id="价值与价格"><a href="#价值与价格" class="headerlink" title="价值与价格"></a><strong>价值与价格</strong></h4><!-- > 宫崎骏画风，在一个充满奇思妙想的复古工作坊里，一座古老而巨大的黄铜天平。天平的一端是一块沉重、稳固、散发着柔和白光的发光水晶（代表价值）。另一端则漂浮着许多大小不一、忽明忽暗的“￥”符号的金色光球和蝴蝶（代表价格和市场因素），它们随着气流上下舞动。一个戴着护目镜的少年正好奇地观察着这一切。 --><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-marx/val2-20250718132755-2nuamo7.jpeg" alt="val2" data-caption="val2" loading="lazy"></p><p>为了更加清晰地理解上文中提到的价值演变过程，我们有必要区分两个核心概念：<strong>价值（Value）</strong>  和 <strong>价格（Price）</strong> 。</p><p>在日常话语中，我们常常将它们混用，但在经济学视角下它们是两回事。<strong>价值是内在的、由社会平均必要劳动时间决定的基准；而价格是我们每天在市场上看到的、围绕价值波动、受到供需关系影响的货币数字。</strong></p><p>下面的表格可以帮助我们直观地对比二者的区别，尤其是在 AI 辅助编程的背景下：</p><table><thead><tr><th>对比维度</th><th><strong>价值 (Value)</strong></th><th><strong>价格 (Price)</strong></th></tr></thead><tbody><tr><td><strong>决定因素</strong></td><td>由 <strong>社会平均必要劳动时间（SNLT）</strong>  决定。是凝结在产品或服务中无差别的人类劳动。</td><td>是价值的货币表现形式。它围绕价值上下波动，受 <strong>供求关系、市场竞争、信息不对称、品牌效应</strong> 等多种因素影响。</td></tr><tr><td><strong>核心特点</strong></td><td><strong>客观、内在、相对稳定，</strong> 随生产力发展而变化。</td><td><strong>外在、表象、经常波动，</strong> 受市场情绪和具体交易条件影响。</td></tr><tr><td><strong>AI 编程的体现</strong></td><td>一个标准组件的 <strong>价值</strong>，会随着 AI 工具的普及而系统性地降低，因为完成它的 SNLT 降低了。</td><td>短期内，一个善用 AI 的开发者可以凭效率优势，为其服务标出高于其（新）价值的 <strong>价格</strong>，获得“红利”。<br>长期看，当所有人都具备同等效率时，<strong>价格</strong> 会逐渐回归到新的、更低的价值基准线。<br></td></tr></tbody></table><p>技术变革首先冲击的是内在的价值，而市场上的价格会因各种因素（如先发优势、信息差）而滞后调整。<strong>我们个人或企业在短期内能抓住的，正是价值与价格之间的短暂缺口。</strong></p><h2 id="浅谈剩余价值论"><a href="#浅谈剩余价值论" class="headerlink" title="浅谈剩余价值论"></a>浅谈剩余价值论</h2><!-- > 宫崎骏画风，一个年轻的开发者坐在木制书桌前，表情有些疲惫但专注。液晶电脑屏幕上代码闪烁。>> 关键视觉：一根科技风格的管道从电脑顶端连接出来，将发光的能量（代表“剩余价值”）源源不断地抽走，输送到画面之外。>> 显示器的一角贴着一张便利贴，上面手写着汉字：“我的价值 ≠ 我的工资”。>> 整体色调温暖、柔和，略带一丝忧郁的氛围，细节丰富，动画电影截图。 --><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-marx/shengyu1-20250718133527-go8j5gl.jpeg" alt="shengyu1" data-caption="shengyu1" loading="lazy"></p><p>理解了 SNLT，我们其实已经触及了马克思经济学说的另一个核心，也就是<strong>剩余价值论</strong>的基石。</p><p>当一个开发者率先使用 AI，用 15 分钟完成了 SNLT 仍为 2 小时的任务时，他实际上在 15 分钟内创造了相当于 2 小时的社会价值。这中间的差额，就是技术进步带来的“超额利润”或“超额剩余价值”。</p><p>而这部分新产生的巨大价值归属于谁，就引出了剩余价值论的核心。在典型的雇佣关系中，企业购买的是员工的<strong>劳动力</strong>（比如一天8小时的工作时间），而员工在此期间借助 AI 创造的<strong>全部价值则归属于企业</strong>。</p><p>员工获得的是作为其劳动力价格的工资，<strong>工资决定于劳动力再生产所花费的必要劳动时间</strong>，而工资所对应的价值通常远小于员工实际创造的总价值，超出的部分则被企业占有。</p><blockquote><p>注意：这里讨论的是内在的价值不是外在的价格</p></blockquote><p>随着技术扩散，当所有人都用 15 分钟完成工作时，SNLT 本身就会被拉低到 15 分钟，这个“超额剩余价值”便会消失，融入新的社会平均水平。但只要劳动力作为商品存在，剩余价值的生产和占有关系依然是理论所描述的常态。</p><p>这个过程引发了进一步的思考：</p><ul><li><strong>“使用价值”与“价值”：</strong>  AI 生成的代码与人手写的代码，只要功能一致，它们的<strong>使用价值</strong>（对用户的用处）就是相同的。但它们凝结的<strong>价值</strong>（由 SNLT 决定）却截然不同。</li><li><strong>“具体劳动”与“抽象劳动”：</strong>  我们每天编写代码是形式各异的“具体劳动”，但只有被社会平均水平承认的那部分，才能转化为无差别的人类“抽象劳动”，并构成价值。AI 也在让我们清晰地看到，哪些“具体劳动”正在被快速地标准化和商品化。</li></ul><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>回到最初提到的问题本质，<strong>AI 带来的效率革命，最终会沉淀为整个社会新的、更高的生产力基准线。</strong></p><p>马克思的理论，在一百多年后的今天依然为我们提供了一个冷静而深刻的分析框架。它并非在制造焦虑，而是帮助我们理解技术变革之下，价值衡量的底层逻辑。</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;!-- &gt; 宫崎骏画风，深夜，背景是密密麻麻的格子间工位，一个头发稀疏的男人在视觉中心，桌面上放着苹果电脑表情狰狞的奋力工作，桌面上放着咖啡、可乐，以及名为“Cursor”、“LLM”</summary>
        
      
    
    
    
    <category term="价值与价格" scheme="https://blog.wj2015.com/tags/%E4%BB%B7%E5%80%BC%E4%B8%8E%E4%BB%B7%E6%A0%BC/"/>
    
    
    <category term="剩余价值论" scheme="https://blog.wj2015.com/tags/%E5%89%A9%E4%BD%99%E4%BB%B7%E5%80%BC%E8%AE%BA/"/>
    
    <category term="大语言模型" scheme="https://blog.wj2015.com/tags/%E5%A4%A7%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B/"/>
    
    <category term="社会平均必要劳动时间" scheme="https://blog.wj2015.com/tags/%E7%A4%BE%E4%BC%9A%E5%B9%B3%E5%9D%87%E5%BF%85%E8%A6%81%E5%8A%B3%E5%8A%A8%E6%97%B6%E9%97%B4/"/>
    
    <category term="随笔" scheme="https://blog.wj2015.com/categories/%E9%9A%8F%E7%AC%94/"/>
    
    <category term="马克思经济学" scheme="https://blog.wj2015.com/tags/%E9%A9%AC%E5%85%8B%E6%80%9D%E7%BB%8F%E6%B5%8E%E5%AD%A6/"/>
    
    <category term="高效率工具" scheme="https://blog.wj2015.com/tags/%E9%AB%98%E6%95%88%E7%8E%87%E5%B7%A5%E5%85%B7/"/>
    
  </entry>
  
  <entry>
    <title>现代 AI 知识库基石：文本嵌入、向量数据库与 RAG 原理全解析</title>
    <link href="https://blog.wj2015.com/2025/07/17/%E7%8E%B0%E4%BB%A3-AI-%E7%9F%A5%E8%AF%86%E5%BA%93%E5%9F%BA%E7%9F%B3%EF%BC%9A%E6%96%87%E6%9C%AC%E5%B5%8C%E5%85%A5%E3%80%81%E5%90%91%E9%87%8F%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%8E-RAG-%E5%8E%9F%E7%90%86%E5%85%A8%E8%A7%A3%E6%9E%90/"/>
    <id>https://blog.wj2015.com/2025/07/17/%E7%8E%B0%E4%BB%A3-AI-%E7%9F%A5%E8%AF%86%E5%BA%93%E5%9F%BA%E7%9F%B3%EF%BC%9A%E6%96%87%E6%9C%AC%E5%B5%8C%E5%85%A5%E3%80%81%E5%90%91%E9%87%8F%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%8E-RAG-%E5%8E%9F%E7%90%86%E5%85%A8%E8%A7%A3%E6%9E%90/</id>
    <published>2025-07-17T04:51:08.000Z</published>
    <updated>2026-08-12T03:28:46.860Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>文本嵌入模型是大语言模型的基石和早期雏形，大语言模型则是文本嵌入模型结合“动态上下文理解”产生的巨大飞跃，这篇博客会从文本嵌入出发，由浅入深去探索文本嵌入、向量数据库、RAG 的本质。</p><h2 id="Text-Embedding：-让机器也能读懂文字的含义"><a href="#Text-Embedding：-让机器也能读懂文字的含义" class="headerlink" title="Text Embedding： 让机器也能读懂文字的含义"></a>Text Embedding： 让机器也能读懂文字的含义</h2><h3 id="先从一个简单问题开始"><a href="#先从一个简单问题开始" class="headerlink" title="先从一个简单问题开始"></a>先从一个简单问题开始</h3><p>想象一下，电脑能直接理解“苹果”这个词吗？</p><p>当然是不能。电脑只认识数字，比如 <code>010101</code>​。对于电脑来说，“苹果”、“香蕉”、“手机”这些词本身没有任何区别，只是一串不同的符号。</p><p><strong>那么我们如何让电脑理解 “苹果”和“香蕉”是近义词（都是水果），而和“电脑”关系较远呢？</strong></p><p>很容易想到，<strong>把词语句子转译成一串数字计算机处理处理起来就方便了，</strong> 这里提到的转译结果，就是 <strong>Text Embedding</strong>，处理转译任务用到的模型就是 <strong>Text Embedding Model</strong>。</p><blockquote><p>注1：也可以给视频、图片、音频等多媒体输入做转译，相关的模型有 ViT、<code>multimodalembedding</code>​等多模态 Embedding 模型。</p><p>注2：Embedding 的中文翻译是嵌入，直译听起来有点不明就里，不过概念理解一致即可。</p></blockquote><!-- > 生成提示词：展现一下词嵌入转译官模型的画面，宫崎骏画风，工厂忙碌的背景，顶部红色横幅写着“词嵌入模型”，中间有一个可爱的机器人，肚子上写着“Text Embedding”站在流水线的背后用手臂工作，流水线的左边是“Word”字样的箱状货品，右边是“Vector”字样的纸张叠在一起的样子 --><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-rag/vector6.jpeg" alt="vector6" data-caption="vector6" loading="lazy"></p><hr><h3 id="什么是-Text-Embedding？"><a href="#什么是-Text-Embedding？" class="headerlink" title="什么是 Text Embedding？"></a>什么是 Text Embedding？</h3><p><strong>Text Embedding</strong> 是一种技术，它能将文本（单词、句子或整个段落）转换成一个由数字组成的列表，这个列表我们称之为 <strong>向量</strong>。</p><p>这个向量可以具象的想象成一个非常多维的空间坐标，这个空间可以被称为<strong>语义空间</strong>，列表的长度就是空间的维度。</p><blockquote><p>核心思想：意义相近的文本，它们对应的“坐标”在空间中的距离也相近。</p></blockquote><p><strong>举个例子：</strong></p><p>假设我们能把词语映射到一个二维平面上（实际上的维度要高得多，有几百甚至上千维）。</p><ul><li>​<code>苹果</code>​ 的坐标可能是 <code>[0.8, 0.7]</code>​</li><li>​<code>香蕉</code>​ 的坐标可能是 <code>[0.75, 0.65]</code>​</li><li>​<code>电脑</code>​ 的坐标可能是 <code>[-0.9, -0.8]</code>​</li></ul><p>你会发现，<code>苹果</code>​ 和 <code>香蕉</code>​ 的坐标点在图上离得很近，而它们都离 <code>电脑</code>​ 很远。这样，计算机通过计算坐标之间的距离（比如欧氏距离），就能让程序理解词语之间的关系了。</p><!-- > 宫崎骏画风，带点科技感，hacker帝国的感觉，顶部中等字号的标题是“语义空间”，一个很小的人背对画面站在右下角，画面整个区域都是蓝色的空间，空间中有一些闪亮的光点，有三个特殊的光点，零散的表示着苹果、香蕉、电脑 --><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-rag/space2.jpeg" alt="space2" data-caption="space2" loading="lazy"></p><p><strong>词向量的数学性质:</strong></p><p>一个更有代表性的例子，著名的 <code>King - Man + Woman ≈ Queen</code>​</p><ul><li>​<code>向量("国王") - 向量("男人") + 向量("女人")</code>​</li><li>计算出的结果向量，会和 <code>向量("女王")</code>​ 的坐标非常非常接近</li></ul><p>这表明，Embedding 不仅编码了“是什么”，还编码了词语之间复杂的<strong>语义关系</strong>。</p><hr><h3 id="Embedding-是如何生成的？"><a href="#Embedding-是如何生成的？" class="headerlink" title="Embedding 是如何生成的？"></a>Embedding 是如何生成的？</h3><p>上文中提到的翻译过程不是人为设定的，而是让 AI 模型在海量的文本数据（比如整个维基百科、数字化的书籍、网页）上学习提炼出来，相当于 AI 模型通过阅读大量材料，自己总结出了规律。</p><p><strong>演进历程：</strong></p><ol><li><p>早期阶段 (Word2Vec, GloVe 等):</p><ul><li><strong>核心思想：</strong> 一个词的意义由它周围的词来定义（“近朱者赤，近墨者黑”）。</li><li><strong>工作方式 (以Word2Vec为例)：</strong>  模型在阅读海量文本时，会尝试做“完形填空”。比如看到句子 “一只__在树上唱歌”，模型会去预测中间的词是 “鸟”。模型不断地进行预测和修正词的向量坐标，慢慢就能学会了每个词的向量表示。</li><li><strong>缺点：</strong> 无法处理一词多义的情况。比如 “bank” 在 “river bank” (河岸) 和 “investment bank” (银行) 中，用 Word2Vec 得到的 Embedding 是一样的，这与实际情况相差甚远。</li></ul></li><li><p>现代阶段 (基于 transformer 架构的 Embedding 模型，如 <code>text-embedding-3-large</code>​、<code>qwen3-embedding</code>​等):</p><ul><li><strong>核心思想：</strong> 词语的意义依赖于其所在的上下文 (Context)。</li><li><strong>工作方式：</strong> 这类基于 Transformer 架构的 AI 模型在处理一个词时，会同时考虑到整个句子的所有其他词。AI 模型通过注意力机制来判断句子中哪些词对当前词的意义影响最大。</li><li><strong>优势：</strong> 能够较好的解决一词多义问题。对于 “river bank” 和 “investment bank”，文本向量模型会根据上下文，为 “bank” 生成两个完全不同的 Embedding 向量。</li><li><strong>劣势：</strong> 模型相比传统词向量一般要大，推理成本和训练成本高</li></ul></li></ol><p>现在我们通常使用的 Embedding 模型都是像 openai 的 <code>text-embedding-3-large</code>​ 这样能够理解上下文的强大模型。</p><p><strong>需要注意的是：</strong>  Embedding 模型和大语言模型虽然大多数都基于 Transformer 架构，但本质上存在很大区别。大语言模型的输出结果是推理的新文本，主要用于推理，而 Embedding 模型输出的是向量，主要用于捕捉语意方便后续检索。</p><table><thead><tr><th>特性</th><th>Text Embedding 模型</th><th>大语言模型 (LLM)</th></tr></thead><tbody><tr><td><strong>核心功能</strong></td><td>文本理解与量化</td><td>文本理解、推理与生成</td></tr><tr><td><strong>输入</strong></td><td>文本 (Text)</td><td>文本 (Text)</td></tr><tr><td><strong>输出</strong></td><td><strong>数字向量 (Vector)</strong></td><td><strong>新的文本 (Text)</strong></td></tr><tr><td><strong>主要目标</strong></td><td>捕捉语义，用于机器后续处理</td><td>模仿人类进行对话、创作和推理</td></tr><tr><td><strong>典型应用</strong></td><td>相似度搜索、文本分类、聚类、RAG中的检索环节</td><td>聊天机器人、内容创作、代码生成、摘要总结、RAG中的生成环节</td></tr><tr><td><strong>与对方关系</strong></td><td>LLM 的<strong>基础组件</strong>和<strong>输入预处理器</strong></td><td>Embedding 模型的<strong>超集</strong>和<strong>能力扩展</strong></td></tr></tbody></table><hr><h2 id="向量数据库：Embedding-的图书馆"><a href="#向量数据库：Embedding-的图书馆" class="headerlink" title="向量数据库：Embedding 的图书馆"></a>向量数据库：Embedding 的图书馆</h2><h3 id="有了-Embedding-，下一步能干什么"><a href="#有了-Embedding-，下一步能干什么" class="headerlink" title="有了 Embedding ，下一步能干什么"></a>有了 Embedding ，下一步能干什么</h3><p>看到这里，我们已经能把任何文本都转换成向量（坐标）了。假设有一个包含数百万篇文章的知识库，现在我们想做一个智能问答机器人。</p><p><strong>用户的提问流程大概是这样的：</strong></p><ol><li>用户提问：中国的首都是哪里？</li><li>你用 Embedding 模型将这个问题转换成一个向量 <code>V_query</code>​。</li><li><strong>下一步怎么办：</strong> 需要从你的数百万篇文章中，找到和这个问题最相关的文章。</li></ol><p><strong>朴素算法：</strong><br>遍历每一篇文章，把它也转换成向量 <code>V_doc</code>​，然后计算 <code>V_query</code>​ 和 <code>V_doc</code>​ 之间的距离。最后，找出距离最近的那几篇。</p><p><strong>问题在于：</strong> 如果有一百万篇文章，你就要做一百万次向量计算和比较。时间复杂度 O(n)，这对业务来说是不可接受的。</p><p>那这个时候向量数据库就很有用场了，因为它就是被设计用于高效迅速解决匹配查询问题的。</p><hr><h3 id="什么是向量数据库？"><a href="#什么是向量数据库？" class="headerlink" title="什么是向量数据库？"></a>什么是向量数据库？</h3><p>向量数据库 (Vector Database) 是一种专门为海量高维向量的 <strong>存储、管理和高效检索</strong> 而设计的数据库。</p><p><strong>它的核心任务就是：</strong> 当你给它一个查询向量时，它能极快地返回与这个向量最相似的 N 个向量。这个过程叫做 <strong>近似最近邻搜索 (Approximate Nearest Neighbor, ANN)</strong> 。</p><p>整理了一张向量数据库和关系型数据库的对比表</p><table><thead><tr><th>对比维度</th><th>关系型数据库 (如 MySQL)</th><th>向量数据库</th></tr></thead><tbody><tr><td><strong>核心功能</strong></td><td>精确存储和检索<strong>结构化数据</strong>，保证数据的一致性和完整性。</td><td>高效存储和检索<strong>高维向量数据</strong>，发现数据间的语义或特征相似性。</td></tr><tr><td><strong>数据模型</strong></td><td>二维表格（行和列），数据有严格的预定义模式（Schema）。</td><td>向量（高维浮点数数组）和可选的元数据（Metadata）。向量通常由 AI 模型生成。</td></tr><tr><td><strong>查询方式</strong></td><td>使用 SQL 语言进行精确条件查询。</td><td>通过输入一个<strong>查询向量</strong>，来查找与之最相似的 N 个向量（Top-K 近似最近邻搜索）。</td></tr><tr><td><strong>查询逻辑</strong></td><td><strong>精确匹配</strong>：基于布尔逻辑（是/否），如 <code>WHERE id = 123</code>​ 或 <code>age &gt; 30</code>​。</td><td><strong>相似度匹配</strong>：基于距离计算（如欧氏距离、余弦相似度），返回“有多像”，而不是“是不是”。</td></tr><tr><td><strong>索引技术</strong></td><td><strong>B-Tree 或 B+Tree 索引</strong>：非常适合精确值的快速查找和范围查询。</td><td><strong>ANN (近似最近邻) 算法</strong>：如 HNSW, IVF, LSH 等，用于在海量高维空间中快速找到近似的邻居。</td></tr><tr><td><strong>处理数据类型</strong></td><td>主要处理结构化的标量数据，如整数、字符串、日期、布尔值等。</td><td>主要处理<strong>非结构化数据</strong>（文本、图片、音频、视频等）转换后的<strong>数学表示——向量</strong>。</td></tr><tr><td><strong>查询示例</strong></td><td>​<code>SELECT * FROM users WHERE name = '张三';</code>​</td><td>​<code>find_similar_vectors(query_vector, k=10)</code>​</td></tr><tr><td><strong>适用场景</strong></td><td>- <strong>OLTP 系统</strong>：用户管理、订单系统、库存管理<br>- <strong>业务分析</strong>：财务报表、销售统计<br>- 任何需要<strong>精确、事务性</strong>操作的场景</td><td>- <strong>AI 应用</strong>：以图搜图、人脸识别<br>- <strong>语义搜索</strong>：比关键词搜索更懂你的意图<br>- <strong>推荐系统</strong>：推荐与你喜欢的商品/内容相似的东西<br>- <strong>大型语言模型 (LLM)</strong> ：为AI提供长期记忆、知识库问答</td></tr><tr><td><strong>性能关键</strong></td><td><strong>精确查找的速度</strong>：能否通过索引快速定位到具体的某一行或某几行。</td><td><strong>相似性搜索的速度和精度</strong>：能否在亿万级向量中，毫秒级地找到最相似的一批结果。</td></tr></tbody></table><!-- > 宫崎骏画风，老式图书馆的借阅台中一位年轻姑娘碰着一本书“ANN Indexing”看着镜头，桌面左侧有“向量数据库”的铭牌 --><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-rag/vdb4.jpeg" alt="vdb4" data-caption="vdb4" loading="lazy"></p><hr><h4 id="向量数据库召回技术细节"><a href="#向量数据库召回技术细节" class="headerlink" title="向量数据库召回技术细节"></a>向量数据库召回技术细节</h4><p>为什么向量数据库能那么快呢？因为它没有“暴力”地逐一比较，而是使用了一些非常高效的 <strong>ANN 索引算法</strong>。这些算法的核心思想都是 <strong>降维或分治</strong>，通过牺牲精度来换取巨大的速度提升。</p><h3 id="ANN-搜索主流算法对比"><a href="#ANN-搜索主流算法对比" class="headerlink" title="ANN 搜索主流算法对比"></a><strong>ANN 搜索主流算法对比</strong></h3><table><thead><tr><th>方法类型</th><th>代表性算法/技术</th><th>核心原理</th><th>优点</th><th>缺点</th></tr></thead><tbody><tr><td><strong>基于树的方法</strong> (Tree-based)</td><td><strong>Annoy</strong></td><td>通过随机选择的超平面，递归地将整个向量空间分割成多个子空间，构建成二叉树（或多叉树）结构。查询时，通过遍历树来定位查询向量所在的子空间，并对该子空间及其邻近子空间内的向量进行搜索，从而限定搜索范围。</td><td>1. 实现原理相对简单。<br>2. 索引构建后内存占用较少。</td><td>为达到高精度，通常需要构建多棵树（森林），导致查询时需合并多棵树的结果，增加了查询时间开销。</td></tr><tr><td><strong>基于哈希的方法</strong> (LSH)</td><td><strong>Locality-Sensitive Hashing (LSH)</strong></td><td>采用局部敏感哈希函数族对向量进行映射。这类函数的特性是：原始空间中距离相近的向量，经过哈希映射后，以高概率落入同一个哈希桶（bucket）。查询时，计算查询向量的哈希值，仅在对应的哈希桶中检索候选近邻点。</td><td>1. 具备严谨的理论保证。<br>2. 天然支持流式数据处理（新数据可独立哈希，无需重构整个索引）。</td><td>在高维数据下，为保证高召回率，需要构建大量的哈希表，导致内存消耗显著增加，实际性能常被其他方法超越。</td></tr><tr><td><strong>基于图的方法</strong> (Graph-based)</td><td><strong>HNSW</strong> (Hierarchical Navigable Small World)</td><td>结合了可导航小世界网络（NSW）和分层思想：<br>1. <strong>邻近图构建：</strong>  为所有向量构建一个邻近关系图，每个节点（向量）仅与其最近的若干邻居相连。<br>2. <strong>贪心搜索：</strong>  查询时，从一个入口点开始，在图中迭代地向着与查询目标更近的邻居节点移动，直到找到局部最优解。<br>3. <strong>分层加速：</strong>  构建一个多层图结构，上层图稀疏，下层图密集。查询从顶层稀疏图开始快速定位，再逐层下降至密集图进行精确查找，大幅提升搜索效率。</td><td>1. 查询速度极快，召回率高。<br>2. 在多个基准测试中展现出卓越的综合性能，是当前的主流高性能方案。</td><td>1. 索引构建过程计算密集，耗时较长。<br>2. 图结构本身需要较大的内存开销。</td></tr><tr><td><strong>基于量化的方法</strong> (Quantization-based)</td><td><strong>PQ</strong> (Product Quantization)</td><td>一种有损压缩技术。将高维向量分割为多个低维子向量。对每个子向量空间独立运行聚类算法（如K-Means）以生成一个码本（codebook）。每个子向量由其所属聚类中心（质心）的ID来近似表示。最终，原始向量被编码为一组紧凑的码字ID，以减少存储和计算开销。</td><td>1. 通过向量压缩，显著降低索引的内存占用。<br>2. 适用于海量（十亿级别以上）数据集。<br>3. 通常与其他索引结构（如IVF）结合使用，提升性能。</td><td>量化过程是有损的，会引入量化误差，从而在一定程度上降低查询精度。</td></tr></tbody></table><p>现代向量数据库（如 Milvus, Weaviate, Pinecone）通常会组合使用这些技术。</p><p>比如，先用 <code>IVF</code>​ (Inverted File) 进行一次粗粒度的“分区筛选”，然后在每个分区内部再使用 <code>HNSW</code>​ 或 <code>PQ</code>​ 进行精细的查找，以平衡速度、内存和精度。</p><p>安利一个很赞的开源向量数据库 <a href="https://github.com/milvus-io/milvus?tab=readme-ov-file">Milvus</a>，可以实现诸如全文检索、多模态召回、多模态相似性搜索、电影推荐等场景，本身支持分布式部署和 GPU 加速，非常强大！</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-rag/image.png" alt="image" data-caption="image" loading="lazy">​</p><hr><h3 id="RAG：检索增强生成"><a href="#RAG：检索增强生成" class="headerlink" title="RAG：检索增强生成"></a>RAG：检索增强生成</h3><p>RAG 的英文全称是 Retrieval-Augmented Generation，意为<strong>检索增强生成</strong>，它的核心技术就是上文提到的文本向量模型和向量数据库，它是一个两阶段的过程：</p><ol><li><strong>检索（Retrieval）</strong> ：根据用户提出的问题，从一个外部的、可信的知识库中，精准地找出最相关的几段信息。</li><li><strong>增强生成（Augmented Generation）</strong> ：将用户原始的问题和上一步检索到的相关信息，一起打包成一个新的、内容更丰富的提示（Prompt），然后提交给 LLM，让它基于这些给定的信息来生成最终的答案。</li></ol><!-- > 宫崎骏画风，书桌上，一个机器人身体上延展出来有六只手，机器人双手拿着笔在纸上写“Prompt”，左右各两只手拿着书“RAG” --><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-rag/rag4.jpeg" alt="rag4" data-caption="rag4" loading="lazy"></p><h4 id="RAG-的完整工作流程"><a href="#RAG-的完整工作流程" class="headerlink" title="RAG 的完整工作流程"></a>RAG 的完整工作流程</h4><h5 id="阶段一：数据准备（离线处理）"><a href="#阶段一：数据准备（离线处理）" class="headerlink" title="阶段一：数据准备（离线处理）"></a><strong>阶段一：数据准备（离线处理）</strong></h5><p>这个阶段是数据的预处理过程，在用户开始提问之前就需要完成。</p><ol><li><p><strong>数据加载</strong>：首先需要<strong>根据实际业务需求确定知识来源</strong>。可以是公司的内部文档（PDF, Word, Confluence）、网站内容、产品手册、数据库记录等。</p></li><li><p><strong>文本切分</strong>：一篇长文档对于检索来说太大了。如果直接检索整篇文档，信息的噪音会比较大。因此我们需要将文档切分成更小的、有意义的文本块。</p><ul><li>常见的切分办法有：按段落、按固定字数，或者用更细致合理的规则、AI 大模型清洗等方式切分，确保每个 chunk 都包含一个相对完整的信息点。</li></ul></li><li><p><strong>向量化（Embedding）</strong> ：这是最关键的一步。使用上文提到的<strong>文本嵌入模型（Embedding Model）</strong> ，将每一个文本块转换成一个高维的数学向量（也就是 Text Embedding）。这个向量可以被认为是该文本块在“语义空间”中的坐标。<strong>语义上相近的文本块，其对应的向量在空间中的距离也更近。</strong></p></li><li><p><strong>数据索引与存储（Indexing</strong>  <strong>&amp;</strong>  <strong>Storage）</strong> ：将生成的向量连同其原始文本块一起，存储到一个专门的数据库中。这种数据库被称为<strong>向量数据库（Vector Database）</strong> ，它经过特殊优化，能够极快地进行大规模向量的相似度搜索。</p></li></ol><p>至此，知识库就准备好了，它现在是一个被向量化、索引化的、可供快速检索的状态。</p><p>‍</p><h5 id="阶段二：问答处理（在线处理）"><a href="#阶段二：问答处理（在线处理）" class="headerlink" title="阶段二：问答处理（在线处理）"></a><strong>阶段二：问答处理（在线处理）</strong></h5><p>这个阶段的逻辑是用户真正提出问题时触发的</p><ol><li><p><strong>用户提问</strong>：系统接收到用户的提问，例如：“Apple 公司的最新产品 VisionPro 有哪些核心功能？”</p></li><li><p><strong>查询向量化</strong>：和数据准备阶段一样，系统使用<strong>同一个</strong>嵌入模型，将用户的提问也转换成一个查询向量。</p><ul><li>注意：不同 Embedding 模型的向量维度和推理结果都不一样，所以这里一定需要使用同一个 Embedding 模型进行查询向量化。</li></ul></li><li><p><strong>向量检索</strong>：系统拿着查询向量去向量数据库中进行相似度搜索。目标是找到与查询向量在语义空间中距离最近的文本块向量。通常我们会取出最相似的 Top-K 个结果（比如 K=3 或 5）。这些被检索出来的文本块就是与问题最相关的信息。</p></li><li><p><strong>构建增强提示</strong>：现在，系统拥有了两部分信息：</p><ul><li>用户的原始问题：来自用户提问</li><li>检索到的相关文本块：来自向量检索</li><li>将这两部分信息拼接成一个结构化的提示即可。这个 Prompt 通常会这样设计：</li></ul> <figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs plaintext">请根据以下提供的上下文信息来回答用户的问题。如果上下文中没有足够的信息来回答，请明确告知。<br><br>上下文信息：<br>- [文本块1]<br>- [文本块2]<br>- [文本块3]<br><br>用户问题：<br>我们公司最新的AI产品‘VisionPro’有哪些核心功能？<br></code></pre></td></tr></tbody></table></figure></li><li><p><strong>LLM 生成答案（Generation）</strong> ：将这个增强后的 Prompt 发送给大型语言模型（如 GPT-4.1）。LLM 会严格依据你提供的上下文信息，进行阅读、理解、总结，并生成一个精准的答案。比如它会回答：“根据提供的资料，‘VisionPro’的核心功能包括：空间计算能力、三维用户界面、以及与现有 Apple 生态的无缝集成。”</p></li><li><p><strong>答案返回与溯源</strong>：最终的答案会返回给用户。一个优秀的 RAG 系统还会同时返回它参考了哪些原始文本块，这样用户就可以自主点击查看来源，验证信息的准确性，极大地增强了系统的可信度。</p></li></ol><p><strong>所以RAG 框架的核心流程是：</strong> 先通过<code>文本嵌入</code>​将知识源向量化，再将这些向量存入<code>向量数据库</code>​以待检索，最终在生成任务中，通过检索出的精确上下文来引导大语言模型，从而输出更可靠的答案。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p><strong>文本嵌入</strong>的本质是将任何文本片段（词、句、文档）转换为一个高维、稠密的数字向量的过程，其核心在于让语义相似的文本在向量空间中的位置也相互靠近。</p><p><strong>向量数据库</strong>是一种专门用于高效存储、索引和查询海量高维向量的系统，其核心能力是基于向量间的距离或相似度，快速执行“近似最近邻”（ANN）检索。</p><p><strong>RAG</strong> 的本质是一种通过从外部知识库中检索相关信息、并将其作为上下文注入到大语言模型提示词（Prompt）中的技术框架，用以增强模型回答的事实准确性和时效性。</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot;</summary>
        
      
    
    
    
    <category term="RAG" scheme="https://blog.wj2015.com/tags/RAG/"/>
    
    
    <category term="向量搜索" scheme="https://blog.wj2015.com/tags/%E5%90%91%E9%87%8F%E6%90%9C%E7%B4%A2/"/>
    
    <category term="向量数据库" scheme="https://blog.wj2015.com/tags/%E5%90%91%E9%87%8F%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
    <category term="大语言模型" scheme="https://blog.wj2015.com/tags/%E5%A4%A7%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B/"/>
    
    <category term="文本嵌入" scheme="https://blog.wj2015.com/tags/%E6%96%87%E6%9C%AC%E5%B5%8C%E5%85%A5/"/>
    
    <category term="机器学习" scheme="https://blog.wj2015.com/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
    <category term="知识库" scheme="https://blog.wj2015.com/tags/%E7%9F%A5%E8%AF%86%E5%BA%93/"/>
    
  </entry>
  
  <entry>
    <title>为了给娃做照片墙，我把家用打印机研究了个底朝天</title>
    <link href="https://blog.wj2015.com/2025/07/10/%E4%B8%BA%E4%BA%86%E7%BB%99%E5%A8%83%E5%81%9A%E7%85%A7%E7%89%87%E5%A2%99%EF%BC%8C%E6%88%91%E6%8A%8A%E5%AE%B6%E7%94%A8%E6%89%93%E5%8D%B0%E6%9C%BA%E7%A0%94%E7%A9%B6%E4%BA%86%E4%B8%AA%E5%BA%95%E6%9C%9D%E5%A4%A9/"/>
    <id>https://blog.wj2015.com/2025/07/10/%E4%B8%BA%E4%BA%86%E7%BB%99%E5%A8%83%E5%81%9A%E7%85%A7%E7%89%87%E5%A2%99%EF%BC%8C%E6%88%91%E6%8A%8A%E5%AE%B6%E7%94%A8%E6%89%93%E5%8D%B0%E6%9C%BA%E7%A0%94%E7%A9%B6%E4%BA%86%E4%B8%AA%E5%BA%95%E6%9C%9D%E5%A4%A9/</id>
    <published>2025-07-10T08:43:21.000Z</published>
    <updated>2026-08-12T03:28:46.856Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本来平时对打印机是没什么需求的，这不最近想打印一批照片，做一面照片墙教小朋友认识亲属，还为了以后可以给孩子打印一些 DIY 绘本，就动了买个打印机的念头</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710104526-tlx8qwb.png" alt="image" data-caption="image" loading="lazy">​</p><p>这次需求很明确：</p><ul><li>可以<strong>彩打照片，质量不能太差</strong></li><li>可以彩打 DIY 绘本</li><li><strong>塑封膜要软</strong>一点的</li><li>未来可能打印练字帖等场景</li><li>重点：<strong>耗材成本不能比电商冲印高（不超过每张￥0.59）</strong> ，机器成本千元左右可接受</li></ul><h2 id="机器"><a href="#机器" class="headerlink" title="机器"></a>机器</h2><p>因为首先比较关注的是照片质量，所以先针对照片打印的场景让 AI 帮我总结了一下现阶段打印机分类和二手购买的注意事项，目前主流的家用照片打印机分为三大类：<strong>喷墨照片打印机</strong>、<strong>热升华照片打印机</strong> 和 <strong>ZINK无墨打印机</strong></p><table><thead><tr><th>项目</th><th><strong>喷墨照片打印机 (Inkjet Photo Printer)</strong></th><th><strong>热升华照片打印机 (Dye-Sublimation Printer)</strong></th><th><strong>ZINK 无墨打印机 (ZINK Zero Ink Printer)</strong></th></tr></thead><tbody><tr><td><strong>主要特点</strong></td><td><strong>画质上限高</strong>：色彩还原精准，分辨率高，细节表现力最强。<br><strong>尺寸/纸张灵活</strong>：可打印从A6到A4甚至更大尺寸，支持光面、绒面、艺术纸等多种介质。<br><strong>功能多样</strong>：通常集打印、复印、扫描于一体（一体机）。<br></td><td><strong>照片质感好</strong>：打印成品有覆膜，防水防指纹，色彩过渡自然，没有墨点，观感类似传统冲印照片。<br><strong>成本固定</strong>：耗材（相纸+色带）打包售卖，单张成本非常明确。<br><strong>便携小巧</strong>：多为小型便携设计，专注于打印标准尺寸照片（如6寸）。<br></td><td><strong>极致便携</strong>：体积最小，通常和充电宝差不多大，可随身携带。<br><strong>无需墨水</strong>：耗材只有ZINK相纸，使用简单。<br><strong>即时趣味</strong>：打印出来的照片通常自带背胶，可以当贴纸用，适合手账和派对。<br></td></tr><tr><td><strong>工作原理</strong></td><td>通过打印头上的微小喷嘴，将液态墨水点喷射到纸张上形成图像。墨水颜色越多（如6色、8色），色彩过渡和表现力越好。</td><td>使用一个含有青(C)、品红(M)、黄(Y)三色染料的色带。打印头通过精确加热，使色带上的固态染料直接升华为气体，渗透到专用相纸表层，最后再覆盖一层保护膜。</td><td>“ZINK”意为“Zero Ink”（零墨水）。其秘密在于专用相纸，纸张内含有无色的染料晶体。打印机通过精确的脉冲加热，激活相纸内的晶体，使其显现出相应的颜色。</td></tr><tr><td><strong>耗材与单张成本</strong></td><td><strong>耗材</strong>：墨盒/墨水、照片纸。<br><strong>成本</strong>：波动较大。<br> <strong>墨仓式</strong>：单张成本较低，约 <strong>¥0.1</strong> ~  <strong>￥0.4</strong>(6寸)。<br> <strong>墨盒式</strong>：单张成本差不多，看具体型号。<br></td><td><strong>耗材</strong>：专用的“相纸+色带”套装。<br><strong>成本</strong>：非常稳定，单张成本约 <strong>¥1 - ¥3</strong> (6寸) <strong>（看具体型号）</strong> 。<br></td><td><strong>耗材</strong>：专用的ZINK相纸。<br><strong>成本</strong>：相对较高，一张2x3英寸的小照片约 <strong>¥2 - ¥4（看具体型号）</strong> <br></td></tr><tr><td><strong>适合人群</strong></td><td>对照片画质有较高要求的摄影爱好者。<br>希望打印不同尺寸、不同材质照片的用户。<br>需要兼顾文档打印的家庭用户。<br></td><td>追求方便、快捷，希望照片能长久保存的家庭用户。<br>主要用于记录生活、制作旅行相册。<br>“懒人”首选，无需担心堵头等维护问题。<br></td><td>手账爱好者、年轻用户、派对达人。<br>追求即时打印、即时分享的乐趣。<br>对画质要求不高，便携性是第一位的用户。<br></td></tr><tr><td><strong>代表品牌/型号</strong></td><td><strong>佳能(Canon)</strong> ：TS系列（如TS8380）、G系列（G580/G680）。<br><strong>爱普生(Epson)</strong> ：L系列（如L805/L1800）。<br></td><td><strong>佳能(Canon)</strong> ：SELPHY CP系列（如CP1300, CP1500）是绝对的主流。<br><strong>小米/汉印</strong>等也有类似产品。<br></td><td><strong>小米/华为/惠普(HP)/宝丽来(Polaroid)</strong>  等品牌都有推出。</td></tr><tr><td><strong>购买二手核心注意事项</strong></td><td><strong>【致命问题】喷头堵塞！</strong> 是二手喷墨打印机最大的风险。<br><strong>务必索要“喷嘴检查”测试页的打印照片或视频</strong>，确保所有颜色的线条都完整无断线。<br>优先选择<strong>墨仓式</strong>机型，前任用户使用成本低，打印频率可能更高，堵头风险相对小。<br>询问<strong>废墨垫/维护箱</strong>的使用寿命。<br></td><td><strong>【核心检查】打印头</strong>。<br>让卖家打印一张纯色（或色彩丰富的）照片，检查照片上是否有<strong>规律性的白色细线</strong>，这通常意味着打印头发热元件损坏，无法修复。<br>检查机器外观、进纸滚轮等物理结构是否完好。<br></td><td><strong>【核心检查】电池健康度</strong>。<br>这类打印机多为内置电池，询问卖家电池续航情况，充满电大概能打多少张。<br>打印一张照片，检查加热是否均匀，有无明显的色块或条纹。<br>检查APP连接是否顺畅。<br></td></tr></tbody></table><blockquote><p>注：根据 AI 和实际调查，都不推荐用激光打印机彩打照片，这是因为激光的原理是把碳粉静电吸附到纸上，再高温加热融化固定，打印颗粒感会比较强，色彩真实性差，而且纸张兼容性等方面也不如喷墨，但维护成本低，打印成本低，家用也蛮合适的。</p></blockquote><table><thead><tr><th><strong>特性</strong></th><th><strong>激光打印机</strong></th><th><strong>墨仓式喷墨打印机</strong></th></tr></thead><tbody><tr><td><strong>照片打印质量</strong></td><td>较差，颗粒感强，色彩失真</td><td><strong>极佳</strong>，色彩鲜艳，细节丰富</td></tr><tr><td><strong>文档打印质量</strong></td><td><strong>极佳</strong>，文字锐利，防水</td><td>优秀，文字清晰</td></tr><tr><td><strong>打印速度（文档）</strong></td><td><strong>快</strong></td><td>适中</td></tr><tr><td><strong>单页打印成本</strong></td><td>较低</td><td><strong>极低</strong></td></tr><tr><td><strong>机器购买成本</strong></td><td>中等到高</td><td>中等</td></tr><tr><td><strong>照片纸兼容性</strong></td><td>差</td><td><strong>极佳</strong></td></tr><tr><td><strong>核心优势</strong></td><td>打印文档速度快、文字锐利</td><td><strong>照片质量高、综合成本低</strong></td></tr></tbody></table><p>综合考虑后，还是喷墨比较适合我，<strong>成本低、效果好，就是得每周打印一次避免堵头</strong>，后面可以每周打印测试页或者打印一张本周最佳照片留念。<br>‍<br>确认品类之后开始找产品，AI 推荐了我三个产品：爱普生墨仓系列、佳能加墨式系列、惠普连供系列</p><p>由于追求性价比再加上<strong>二手打印机的搬运和检查成本较高</strong>，就先去京东找到了符合需求的三个候选型号，墨水成本看起来差不多，官方墨水都在两百多</p><table><thead><tr><th><strong>品牌</strong></th><th><strong>型号</strong></th><th><strong>价格</strong></th><th><strong>备注</strong></th></tr></thead><tbody><tr><td>爱普生</td><td>L1285</td><td>京东自营 ￥503（咸鱼￥379）</td><td>四色染料墨水，无扫描仪（可用 APP 扫描代替），无自动双面</td></tr><tr><td>佳能</td><td>G3800</td><td>京东自营￥479.2（咸鱼￥320）</td><td>四色染料墨水，带扫描仪</td></tr><tr><td>惠普</td><td>215</td><td>京东自营￥559（咸鱼￥450）</td><td>四色染料墨水，无扫描仪</td></tr></tbody></table><p>调查了一下，因为存在专利壁垒，<strong>惠普和佳能的喷头都是热发泡技术，爱普生和兄弟是微压电技术</strong>，目的都是让墨滴从喷头喷射到纸张上进行染色，只是实现方式和效果侧重有不少差别，但我想都是较为成熟的技术，推测在低端机上各有所长，影响并不明显。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710110441-wpi9tdx.png" alt="image" data-caption="image" loading="lazy">​</p><table><thead><tr><th><strong>特性</strong></th><th><strong>热发泡技术 (Thermal Bubble Jet)</strong></th><th><strong>微压电技术 (Micro Piezo)</strong></th></tr></thead><tbody><tr><td><strong>代表品牌</strong></td><td>佳能 (Canon)、惠普 (HP)</td><td>爱普生 (Epson)</td></tr><tr><td><strong>工作原理</strong></td><td>加热墨水产生气泡，喷射墨滴</td><td>对压电晶体施加电压，使其变形挤出墨滴</td></tr><tr><td><strong>墨滴控制</strong></td><td>较好</td><td><strong>极佳</strong>，可控制墨滴大小和形状</td></tr><tr><td><strong>喷头结构</strong></td><td>通常为消耗品（与墨盒一体）</td><td><strong>永久性部件</strong>（与机身一体）</td></tr><tr><td><strong>喷头寿命</strong></td><td>较短</td><td><strong>很长</strong></td></tr><tr><td><strong>堵头处理</strong></td><td>简单，更换墨盒即可</td><td>麻烦，需清洗，严重则需专业维修</td></tr><tr><td><strong>墨水兼容性</strong></td><td>一般，墨水需耐高温</td><td><strong>极强</strong>，对墨水化学性质要求宽泛</td></tr><tr><td><strong>制造成本</strong></td><td>较低</td><td>较高</td></tr></tbody></table><p>考虑最终打印效果、稳定性、维保成本等因素，还是觉得爱普生的墨仓系列适合我。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710113830-1vq62fs.png" alt="image" data-caption="image" loading="lazy">​</p><p>‍<br>爱普生有两款让我特别心动，一款是 L1258 因为性价比高，另一款是 L8058 因为打印质量和可打印纸的范围都比较好</p><table><thead><tr><th><strong>特性</strong></th><th><strong>爱普生 L1258（￥503）</strong></th><th><strong>爱普生 L8058（￥2199）</strong></th><th><strong>备注</strong></th></tr></thead><tbody><tr><td><strong>产品定位</strong></td><td><strong>家用打印机</strong></td><td><strong>专业级照片打印机</strong></td><td><strong>这是最核心的区别</strong></td></tr><tr><td><strong>墨水系统</strong></td><td><strong>4色染料墨水 (CMYK)</strong></td><td><strong>6色染料墨水 (CMYK, LC, LM)</strong></td><td>都是染料墨，L8058 色彩过渡更平滑，照片质量更高</td></tr><tr><td><strong>核心功能</strong></td><td><strong>仅打印，最大A4</strong></td><td><strong>仅打印，最大A4</strong></td><td></td></tr><tr><td><strong>介质支持</strong></td><td>普通纸、信封、普通照片纸</td><td>专业照片纸 (更厚)、<strong>PVC卡片</strong>、<strong>光盘盘面</strong></td><td>L8058 的介质兼容性更好</td></tr><tr><td><strong>照片质量</strong></td><td>日常可用，色彩过渡有颗粒感</td><td><strong>专业水准</strong>，色彩细腻平滑</td><td>追求照片品质，必选 L8058</td></tr><tr><td><strong>文档打印</strong></td><td>优秀，速度较快</td><td>可以，但不是强项</td><td>L1258 更适合大量打印文档</td></tr><tr><td><strong>连接方式</strong></td><td>USB, Wi-Fi, Wi-Fi Direct</td><td>USB, Wi-Fi, Wi-Fi Direct</td><td>两者现代连接方式都支持</td></tr><tr><td><strong>机身与价格</strong></td><td>机身小巧，价格更亲民</td><td>机身较大，价格更高</td><td></td></tr><tr><td><strong>适用人群</strong></td><td>学生、家庭用户、小型办公室</td><td><strong>摄影爱好者</strong>、设计师、小型影楼、手账达人</td><td>需求决定选择</td></tr></tbody></table><p>‍</p><p>至于染料墨水和颜料墨水，我自己觉得家用场景中没什么特别大的区别，主要是在防水性上有差异，但这可以用过塑来解决，我看<strong>网上有换国产颜料墨水堵头的情况</strong>，大家慎重。</p><table><thead><tr><th><strong>特性</strong></th><th><strong>染料墨水 (Dye Ink)</strong></th><th><strong>颜料墨水 (Pigment Ink)</strong></th><th><strong>通俗解释</strong></th></tr></thead><tbody><tr><td><strong>成分原理</strong></td><td>分子级，完全溶解</td><td>微小固体颗粒，悬浮</td><td><strong>糖水 vs 泥水</strong></td></tr><tr><td><strong>打印原理</strong></td><td><strong>渗透</strong>到纸张纤维内部进行染色</td><td><strong>附着</strong>在纸张表面形成涂层</td><td><strong>染色 vs 上漆</strong></td></tr><tr><td><strong>色彩表现</strong></td><td><strong>色彩鲜艳、通透</strong>，色域更广</td><td>色彩沉稳、厚重，饱和度略低</td><td>糖水是透明的，光能穿透，所以颜色亮丽；泥沙是不透明的，覆盖力强，颜色更扎实。</td></tr><tr><td><strong>防水性能</strong></td><td><strong>不防水</strong>，遇水会晕开</td><td><strong>防水</strong>，墨迹基本不受影响</td><td>糖水干了再遇水，糖会再次溶解；而泥沙干了变成泥块，再浇水也不会轻易化开。</td></tr><tr><td><strong>耐光/持久性</strong></td><td><strong>较差</strong>，在光照下容易褪色</td><td><strong>极佳</strong>，能长久保存，抗紫外线能力强</td><td>染料分子结构容易被紫外线破坏而分解；颜料的固体颗粒结构稳定，不易分解。</td></tr><tr><td><strong>适用介质</strong></td><td>在<strong>光面/高光照片纸</strong>上表现绝佳</td><td>在<strong>普通纸、亚光纸、艺术纸</strong>上表现出色</td><td>染料渗透进光面纸的涂层，色彩通透；颜料附着在亚光纸粗糙表面，文字锐利，质感好。</td></tr><tr><td><strong>堵头风险</strong></td><td>风险较低，因为是溶液</td><td>风险相对较高，因为有固体颗粒</td><td>糖水不容易堵塞管道；但泥沙放久了会沉淀，可能堵塞管道。</td></tr><tr><td><strong>成本</strong></td><td>相对<strong>便宜</strong></td><td>相对<strong>昂贵</strong></td><td>生产工艺和原材料决定了价格。</td></tr></tbody></table><p>考虑到价格和需求，￥2199 的 L8058 确实效果很赞，打印速度还快，但就目前需求来说，L1258 看起来也足够了。如果后面对打照片、材料介质要求更高时，再考虑升级到能打 A3 的 L18058 也不迟，毕竟 L1258 价格如此低廉，哪怕最后<strong>真吃灰了也亏不到哪去</strong>。<br>‍</p><p>所以最终下单了爱普生的 L1285，这是爱普生目前在售最便宜的机型，送一套原装 004 墨水，性价比很高。</p><blockquote><p>注：25年7月上海国补后 ￥503，618 期间还出现过 ￥315 的神价，可惜错过了，真是拍大腿。</p></blockquote><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710113554-c0ff7p8.png" alt="image" data-caption="image" loading="lazy"><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710113124-81u63mx.png" alt="image" data-caption="image" loading="lazy">​</p><p>拆箱之后比想象中的轻，占地空间也还好，比横放的 PS5 普通版长一点点</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710130010-r99twi1.png" alt="image" data-caption="image" loading="lazy">​</p><h2 id="耗材选择"><a href="#耗材选择" class="headerlink" title="耗材选择"></a>耗材选择</h2><h3 id="墨水"><a href="#墨水" class="headerlink" title="墨水"></a>墨水</h3><p>墨水从稳定性和效果出发还是选官方的 004 系列，京东价格 ￥226</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710123235-vzg8cog.png" alt="image" data-caption="image" loading="lazy"><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710130041-ow7tr3k.png" alt="image" data-caption="image" loading="lazy">​</p><h3 id="相片纸"><a href="#相片纸" class="headerlink" title="相片纸"></a>相片纸</h3><p>相片纸的核心区别在于两大要素：<strong>基材</strong>（决定了纸张的物理特性和耐久性）和<strong>表面涂层</strong>（决定了视觉效果和触感）。</p><p><strong>核心区别：基材 (Base Material)</strong></p><ol><li><strong>RC 基材 (Resin-Coated / 树脂涂层)</strong> : 专业级相纸的基础。它是在纸芯两面都覆盖了树脂（塑料）层，使其<strong>防水、厚实、稳定</strong>，不易卷曲。这是高品质输出的保证。</li><li><strong>纸基 (Paper Base)</strong> : 消费级相纸的基础。基材就是普通纸，表面有吸墨涂层。<strong>不防水</strong>，耐久性较差，成本低。</li><li><strong>艺术纸基 (Fine Art Base)</strong> : 收藏级相纸的基础。通常由棉绒或α-纤维素制成，无酸，有纹理，为实现<strong>艺术感和长期保存</strong>而设计。</li></ol><table><thead><tr><th>类型</th><th>表面光泽</th><th>色彩与细节表现</th><th>抗指纹性</th><th>核心用途</th></tr></thead><tbody><tr><td><strong>RC 高光</strong> (RC Glossy)</td><td>镜面高光，反光强</td><td>色彩最鲜艳，细节最锐利</td><td>差</td><td>风光、商业、强视觉冲击力</td></tr><tr><td><strong>RC 绒面/绸面</strong> (RC Luster/Satin)</td><td>低调光泽，细腻纹理</td><td>色彩浓郁，肤色自然</td><td>极佳</td><td><strong>人像、婚纱、专业展览（首选）</strong></td></tr><tr><td><strong>哑光</strong> (Matte)</td><td>完全无光泽</td><td>色彩柔和沉稳，艺术感强</td><td>极佳</td><td>黑白、艺术作品、装裱展示</td></tr><tr><td><strong>普通高光</strong> (Cast-Coated Glossy)</td><td>高光，质感普通</td><td>色彩鲜艳，但层次感弱于RC</td><td>较差</td><td>日常快照、低成本打印</td></tr><tr><td><strong>艺术纸</strong> (Fine Art Paper)</td><td>无光泽，有明显纹理</td><td>色彩层次丰富，质感独特</td><td>极佳</td><td>艺术品复制、收藏级输出</td></tr></tbody></table><p>‍<br>官方 255g 6寸 RC 相纸听说效果特别好，但着实有点贵了，<strong>单张相纸成本直接打到 ￥0.58，四色打印机有点不配</strong>。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710123352-berpc2f.png" alt="image" data-caption="image" loading="lazy">​</p><p>之前网上清洗照片用过乐凯的相纸，网上再看了下相纸对比测试，感觉这家品质还不错。</p><p>于是京东找了乐凯 180g（￥0.08）/ 200g（￥0.13） 的 6寸高光相片纸，四色打印机本就不适合打印太厚的纸，买这个够用了。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710123755-jwv4r9q.png" alt="image" data-caption="image" loading="lazy"><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710123611-eyv4vvi.png" alt="image" data-caption="image" loading="lazy">​</p><blockquote><p>注：因为绒面相纸有时候影响锐度，所以冲印时个人更喜欢光面的，但发现目前状态下打印机性能才是影响效果的瓶颈，所以后面我可能两种纸都买，视题材而定，尽可能发挥出来打印机的色彩性能。</p></blockquote><p>打印设置：</p><ul><li>高质量光泽纸</li><li>自动优化开（提亮了一点点）</li></ul><p>相比手机屏幕效果有点偏白偏灰，色彩层次等细节确实差远了，不过整体质量属于可以接受范围内。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710123902-dsubaqz.png" alt="image" data-caption="image" loading="lazy">​</p><blockquote><p>注：由于打印机的原因，光面纸会残留轱辘印，斜着看比较明显，不影响整体感官，强迫症慎入，听说绒面会好很多。</p></blockquote><h3 id="塑封"><a href="#塑封" class="headerlink" title="塑封"></a>塑封</h3><p>塑封照片分为冷裱和热裱，<strong>冷裱膜一般较软可以手动操作</strong>；热裱有不同厚度规格，<strong>热裱一定要用覆膜机</strong>（按理说卷发棒也能行，就是操作起来麻烦点）</p><p>塑封机的技术含量不高，我是京东花了￥58 随便选的，原理是加热到指定温度后，滚轮把照片往后拉，塑封膜的热粘胶就会粘合到一起</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710130450-7ffdlld.png" alt="image" data-caption="image" loading="lazy">​</p><p>“丝”是中国工业生产中一个非常常用的非标准长度单位，专门用来表示<strong>厚度</strong>。</p><ul><li><strong>1 丝</strong>  <strong>=</strong>  <strong>0.01 毫米 (mm)</strong></li><li><strong>100 丝</strong>  <strong>=</strong>  <strong>1 毫米 (mm)</strong></li></ul><table><thead><tr><th>厚度 (丝)</th><th>标准厚度 (mm / μm)</th><th>硬度与手感</th><th>核心特点</th><th>适用场景</th></tr></thead><tbody><tr><td><strong>5-6 丝</strong></td><td>0.05-0.06mm / 50-60μm</td><td><strong>柔软</strong>，可轻松弯曲</td><td>最薄软，基础保护，成本低</td><td>不常翻阅的文档、存档资料、追求柔软度的场合</td></tr><tr><td><strong>7-8 丝</strong></td><td>0.07-0.08mm / 70-80μm</td><td><strong>软硬适中</strong>，有一定挺度</td><td><strong>最通用/万能选择</strong>，性价比最高</td><td><strong>照片</strong>、日常文件、奖状、海报、儿童涂鸦</td></tr><tr><td><strong>10 丝</strong></td><td>0.10mm / 100μm</td><td><strong>硬挺</strong>，不易弯折</td><td>加强保护，耐用性好</td><td><strong>菜单</strong>、价目表、教学卡片、游戏道具、常用资料</td></tr><tr><td><strong>12 丝+</strong></td><td>≥0.12mm / ≥120μm</td><td><strong>非常坚硬</strong>，类似塑料卡片</td><td>卡片级硬度，极度耐磨</td><td><strong>自制证件卡</strong>、胸牌、会员卡、设备操作牌</td></tr></tbody></table><p>在塑封膜的选择上，因为家里有宝宝，担心孩子拿着照片玩划伤自己，所以选了<strong>带倒角</strong>的 8丝（单张 ￥0.1）和 5.5丝（单张￥0.08）的塑封膜，后者明显更软。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710131055-c3c3n9l.png" alt="image" data-caption="image" loading="lazy">​</p><p>塑封膜的品牌是千帆，看网上说这家厂专注塑封膜做了挺久，蛮不错的国产品牌。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710130551-2khkq41.png" alt="image" data-caption="image" loading="lazy">​</p><p>购买时发现很多机器说明书上都说不支持 5.5 丝塑封，但我看 5.5 丝和 8 丝塑封膜的温度范围是一样的，所以都买来试了下，塑封效果正常。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710131215-e369air.png" alt="image" data-caption="image" loading="lazy"><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710131142-p1qfe9a.png" alt="image" data-caption="image" loading="lazy"></p><p>塑封后效果如下</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710131441-v56qon4.png" alt="image" data-caption="image" loading="lazy">​</p><blockquote><p>小贴士：如果发现封完有点卷边或者弯曲，可能是塑封机的温度过高了，小红书上有个办法是底部垫一张普通纸让塑封温度低一点</p></blockquote><h3 id="绘本打印"><a href="#绘本打印" class="headerlink" title="绘本打印"></a>绘本打印</h3><p>喷墨打印机也挺适合做绘本和纸盒模型的，图2是小红书上有人用 160g 的铜版纸做的，问了一下说不算很脆皮。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710134148-ta98ago.png" alt="image" data-caption="image" loading="lazy"><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710134203-ktl95c0.png" alt="image" data-caption="image" loading="lazy">​</p><p>绘本会用到的常见纸张类型和特点如下：</p><table><thead><tr><th>特性</th><th><strong>光面铜版纸 (Glossy Coated Paper)</strong></th><th><strong>双喷纸 (Double-sided Inkjet Paper)</strong></th><th><strong>双胶纸 (Uncoated / Offset Paper)</strong></th></tr></thead><tbody><tr><td><strong>核心特点</strong></td><td>表面有涂层，光滑亮丽，专为印刷</td><td>表面有特殊涂层，为喷墨打印优化</td><td>表面无涂层，质感自然，适合书写</td></tr><tr><td><strong>表面处理</strong></td><td>双面涂布高岭土等，并经过压光处理</td><td>双面有为吸收墨水设计的特殊涂层</td><td>仅施胶处理，无额外涂层</td></tr><tr><td><strong>光泽度</strong></td><td>高光泽，反光明显</td><td>通常为哑光，不反光</td><td>无光泽，表面粗糙</td></tr><tr><td><strong>手感</strong></td><td>非常光滑，甚至有点滑手</td><td>比较平滑，但不滑手，有一定质感</td><td>略带粗糙感，有纸张的天然质感</td></tr><tr><td><strong>适用打印</strong></td><td><strong>胶印/激光打印</strong>（不适合喷墨）</td><td><strong>喷墨打印</strong>（不适合激光）</td><td><strong>胶印/激光/喷墨/书写</strong>均可</td></tr><tr><td><strong>打印效果</strong></td><td>色彩鲜艳饱和，图像清晰锐利</td><td>图像清晰，色彩还原好，无洇墨现象</td><td>色彩偏沉稳、柔和，有文艺感</td></tr><tr><td><strong>主要用途</strong></td><td>宣传画册、杂志、海报、传单</td><td>高质量标书、效果图、个人作品集</td><td>书籍内页、笔记本、文件、信纸</td></tr><tr><td><strong>常见别称</strong></td><td>铜版纸、铜板纸、C2S Art Paper</td><td>彩喷纸、喷墨打印纸</td><td>胶版纸、道林纸、书写纸、影印纸</td></tr></tbody></table><p>扒了 PDD 上某商家的纸质说明，调查了类似的四色打印机可以打印 160g 以内的双面铜版纸，140g 以下的双喷纸</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250709104904-b1yyblx.png" alt="image" data-caption="image" loading="lazy"><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250709104914-z321vnf.png" alt="image" data-caption="image" loading="lazy">​</p><p>打印绘本根据小红书上博主的经验，一般用这些组合：</p><ul><li>【安全护眼】封面、内容都用 120g 双喷纸</li><li>【好看皮实】封面用 160g 铜版纸，内容用 140g 铜版纸</li><li>【综合】封面用 160g/140g 铜版纸，内容用 A4 纸或 120g 双喷纸</li></ul><p>根据网上的实测推荐买了科美达的 A4 140g 双喷纸和 A4 160g 双面高光，下单了还没到，所以暂时没法展示</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710134333-x9nmp7t.png" alt="image" data-caption="image" loading="lazy">​</p><p><strong>成本上</strong>：</p><ul><li>双面高光铜版纸 A4，140g，￥0.22 每张</li><li>双面哑光（彩喷纸）A4，140g ￥0.13 每张</li></ul><p><strong>注意事项</strong>：</p><ul><li>双喷选打印时选爱普生高质量彩喷纸，高质量或者标准鲜艳，打印完放 5~10 分钟</li><li>铜版纸用光泽纸模式打印</li><li>160g 双面铜板打印没问题，但黑色会掉色，可以覆膜解决</li><li>双喷纸色彩还原度不及铜版纸</li></ul><p>‍</p><p>下图左侧是双喷纸，右侧是高光铜版纸，色差对比起来还是蛮明显的</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710153728-tgxptao.png" alt="image" data-caption="image" loading="lazy">​</p><h2 id="成本计算"><a href="#成本计算" class="headerlink" title="成本计算"></a>成本计算</h2><p>L1258 满墨状态下，官方宣称 A4 普通纸黑白4500张，彩打 7500 张，均下来每张彩打 A4 墨水成本在 ￥0.03</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-printer/image-20250710135511-itpolk6.png" alt="image" data-caption="image" loading="lazy">​</p><p>但彩打 7500 张是基于2007年发布的国际标准 <a href="https://www.iso.org/standard/50017.html">ISO/IEC 24712</a>，所以基于下面的信息结合 AI 可以推导一下高质量 6 寸照片的单张成本：</p><ul><li>A4 彩打文档 CMYK 墨水覆盖率估算为 20%，而6寸照片几乎是 100%</li><li>6寸照片的大小是 A4 纸的 1/4</li><li>高质量比普通质量多消耗 2~4 倍的墨水（根据实际照片有所区别，可以取三倍）</li></ul><p>‍</p><p>计算：</p><ul><li>A4 纸面积：21 cm × 29.7 cm = 623.7 cm²</li><li>6 寸照片面积 (4R)：10.2 cm × 15.2 cm ≈ 155 cm²</li><li>打印一张 A4 文档的墨水消耗量 ≈ <code>(A4 面积) × (A4 覆盖率)</code>​ = 623.7 × 20% = <strong>124.7 单位墨水</strong></li><li>打印一张 6 寸普通照片的墨水消耗量 ≈ <code>(6寸面积) × (6寸覆盖率)</code>​ = 155 × 100% = <strong>155 单位墨水</strong></li><li>6 寸普通照片消耗的墨水成本在 ￥0.03 x (155/124.7) = ￥0.037</li><li>6 寸高质量照片的实际墨耗 ≈ 155 (基础墨量) × 3 (质量系数) = <strong>465 单位墨水</strong></li><li>6 寸高质量消耗的墨水约等于 <code>465 / 124.7</code>​ ≈ <strong>3.7</strong> 张标准 A4 文档。</li><li>6 寸高质量消耗的墨水成本在 ￥0.03*3.7 = ￥0.111</li><li>相纸成本，按照最低乐凯 6 寸高光 180g 算单张 ￥0.08</li><li>塑封成本，按照最低千帆 5.5丝来算，最低单张 ￥0.08</li></ul><p>‍</p><p>最低成本：180g相纸+5.5丝塑封+普通墨耗 = ￥0.197</p><p>高质量打印成本：200g相纸+8丝塑封+高质量墨耗=￥0.341</p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>不小心写了一个大长篇，打印机还是蛮有意思的，可以低成本的按需打印照片、文档、DIY 绘本，顺带了解了一些印刷行业的知识，特别是在用纸用墨艺术上比较感触，希望可以帮到大家选择符合自身需求的打印机。</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot;</summary>
        
      
    
    
    
    <category term="生活" scheme="https://blog.wj2015.com/tags/%E7%94%9F%E6%B4%BB/"/>
    
    
    <category term="随笔" scheme="https://blog.wj2015.com/categories/%E9%9A%8F%E7%AC%94/"/>
    
  </entry>
  
  <entry>
    <title>AI First 编程：Cursor 深度体验和”智驾式编程“实操</title>
    <link href="https://blog.wj2015.com/2025/06/10/AI-First-%E7%BC%96%E7%A8%8B%EF%BC%9ACursor-%E6%B7%B1%E5%BA%A6%E4%BD%93%E9%AA%8C%E5%92%8C%E2%80%9D%E6%99%BA%E9%A9%BE%E5%BC%8F%E7%BC%96%E7%A8%8B%E2%80%9C%E5%AE%9E%E6%93%8D/"/>
    <id>https://blog.wj2015.com/2025/06/10/AI-First-%E7%BC%96%E7%A8%8B%EF%BC%9ACursor-%E6%B7%B1%E5%BA%A6%E4%BD%93%E9%AA%8C%E5%92%8C%E2%80%9D%E6%99%BA%E9%A9%BE%E5%BC%8F%E7%BC%96%E7%A8%8B%E2%80%9C%E5%AE%9E%E6%93%8D/</id>
    <published>2025-06-10T02:51:41.000Z</published>
    <updated>2026-08-12T03:28:46.852Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><blockquote><p>AI First 编程是指有事儿 AI 先上，搞不定再找我；智驾式编程是类比目前新能源汽车的智能驾驶，它让我们出行更加方便但也需要一位驾驶员来确保前进方向正确和驾驶安全。</p></blockquote><h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>作为一名步入互联网行业数年的后端码农，我深知日常编码工作中的那些让人腰酸背疼的“痛点”：比如写各种测试 DEMO、纠缠逻辑细节、单元测试样板代码等重复但必要的工作；以及面对一个庞大的新项目，光是理清代码结构、找到关键信息就得耗费大量时间；学习一门新技术或框架，官方文档浩如烟海，API 细节繁杂，学习曲线陡峭。</p><p><strong>这些难题正在日复一日地消耗着码农们的精力和创造力，让我们无法把精力专注在把握实现方向上，更多的被各种实现细节、运行报错拖到晚上十二点，然后转头再去医院花大价钱做推拿按摩缓解颈椎病。</strong></p><p>而 AI 编程工具的出现，正试图从根本上改变这一切。它们不再仅仅是传统代码补全或者问答形式的简单升级，而是力求将 AI 深度整合到编码的每一个环节 —- 从需求理解到代码生成、重构、测试乃至文档编写。这就是我所理解追求的“AI First” 智驾式编码体验，AI 在其中扮演的不再仅仅是辅助角色，而是成为驱动开发效率的核心引擎之一。</p><p>在这波 AI 编程工具的浪潮中，我从 2023 年就开始尝试使用 <a href="https://www.cursor.com/">Cursor</a>。当时它主要使用 GPT-3.5 模型，功能也只有内联编辑和 Ask，从现在往回看它的模型能力和功能都很弱小，但对于当时的开发场景和产品环境来说已经足够给编码效率带来成倍的提升了。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250528142637-0cu29zc.png" alt="image" data-caption="image" loading="lazy">​</p><p>在今年年中，国产 AI 编程新秀 <strong>Trae</strong> 也进入了我的视野。Trae 的特点是更丝滑流畅的 UI、国产以及 <strong>免费</strong> 这一点吸引住了我，功能跟 Cursor 很像，主打 Agent、内联编辑、智能聊天功能，所以这次会以 Cursor 为主，顺便对比一下 Cursor（<code>v0.50.5</code>​）  和 Trae（<code>v0.5.8</code>​） 这两个 AI IDE 之间有何区别，以及差异在哪里，看看它们是如何实实在在地变革我的日常开发工作流的。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250528153208-y5qopf8.png" alt="image" data-caption="image" loading="lazy">​</p><blockquote><p>声明：Cursor 这款产品上线打磨的时间很长，Trae 在很多细节上肯定有所不如，但并不代表博主不喜欢 Trae，Trae 有自己独特的优势，比如 UI 流畅度、国产化、免费策略等等，Trae 与 Cursor 在核心功能上的差距也在不断在缩减，希望在不久的将来能看到 Trae 的弯道超车时刻！</p></blockquote><h2 id="AI-如何重塑我的编码工作流"><a href="#AI-如何重塑我的编码工作流" class="headerlink" title="AI 如何重塑我的编码工作流"></a>AI 如何重塑我的编码工作流</h2><p>经过了<strong>不长时间</strong>的磨合，我觉得 Cursor 确实在很大程度上改变了我与代码交互的方式，让 “AI First” 和 ”智驾式编程“ 不再是一个遥不可及的概念，而是融入日常工作，把我们从重复劳动中解救出来的良方。</p><p><strong>AI 编辑器的底层技术主要是 LLM 、向量数据库、MCP 或 FunctionCall，其中 LLM 主要用于意图识别、实现路径规划、函数调用等，向量数据库主要用于代码索引和召回、文档知识库的实现等，而 MCP 或 FunctionCall 是大模型与计算机交互的重要方式，这些所有关联技术的核心目的是利用好有限上下文以及扩展大模型的影响范围。</strong></p><blockquote><p>关于目前大模型技术的原理、能力和限制，可参考之前编写的一篇博客和分享稿 <a href="https://juejin.cn/post/7398046513812127784?searchId=2025060918364655E361DA3747DC890202">建立大模型的感性认知</a>。</p></blockquote><p>比如 AI Agent 在修改代码之前 LLM 需要了解待修改代码是什么样子的，可能就会用到 ListFiles 、ReadFile 等读取代码，然后向量数据库中召回相关的代码、文档，代码生成完成后，再用 WriteFile 去应用修改。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250605095547-5fesa62.png" alt="image" data-caption="image" loading="lazy">​</p><p>图片来源：<a href="https://www.bilibili.com/opus/1054156893413441539">https://www.bilibili.com/opus/1054156893413441539</a></p><blockquote><p>注：目前阶段的 AI IDE 对专业开发者而言，更像一个任劳任怨的”实习生“，他可能做错，也可能做的不好，这就首先要求开发者自己对项目、对业务、对代码、对架构有比较深入的了解，才能够对这位”实习生“的工作结果给出合适的指导意见，这样才能写出高质量、高可维护的代码。</p></blockquote><h3 id="智能聊天与代码库的深度融合"><a href="#智能聊天与代码库的深度融合" class="headerlink" title="智能聊天与代码库的深度融合"></a>智能聊天与代码库的深度融合</h3><p>Cursor 的聊天功能（官方称之为 “Chat with your code”）不止一个简单的问答机器人。它的强大之处在于能够深度理解并索引你的整个项目代码库（Codebase），从而提供高度相关的、上下文感知的回答和建议，下面是一些具体的场景。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250528154831-n3my86w.png" alt="image" data-caption="image" loading="lazy">​</p><h4 id="场景1：项目结构理解"><a href="#场景1：项目结构理解" class="headerlink" title="场景1：项目结构理解"></a>场景1：项目结构理解</h4><p>这可能是我在接触一个全新或庞大项目时最先使用的功能。过去要理解一个陌生的代码库，我可能需要花费大量时间阅读文档（如果文档足够齐全的话）、梳理代码结构、跟踪函数调用。现在有了 Cursor、Trae 等 AI IDE 的 Chat 功能，这个过程被大大加速了。</p><p>比如当我接手公司一个包含数十个模块、数万行代码的遗留项目时，我可以直接向 Cursor 提问：</p><blockquote><p>我不懂 ruby，请为我梳理整个项目的代码结构和 API 入口</p></blockquote><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250528155502-twxg9o0.png" alt="image" data-caption="image" loading="lazy">​</p><p>AI 就会帮我把整个项目的代码结构直接梳理出来，对于大部分规范项目而言，准确度还是比较高的。</p><p>下一步可以把这个项目结构输出为 markdown 文档，在下次找逻辑、找 API 端点时把项目代码结构文档作为上下文提供给 AI，得出的结果会更迅速且更准确。</p><h4 id="场景2：代码解释"><a href="#场景2：代码解释" class="headerlink" title="场景2：代码解释"></a>场景2：代码解释</h4><p>定位到需要调整或者不理解的代码时，可以选中一段代码按 Ctrl + L 向 Cursor 提问：</p><blockquote><p>我不懂 ruby，请详细解释这段代码是什么意思？</p></blockquote><p>Cursor 会扫描与这个问题相关的代码文件，自动将这些文件或关键代码片段作为上下文（@符号引用），然后给出它的理解。我发现 Cursor 在自动填充上下文这方面做得相当智能，它会优先查找与问题关键词最匹配的文件和代码段。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250528155212-e7kz5xv.png" alt="image" data-caption="image" loading="lazy">​</p><p>这对于快速建立对项目关键部分的认知非常有帮助，省去了挨个文件点进去翻看和理解的时间。</p><h3 id="无缝丝滑的内联编辑"><a href="#无缝丝滑的内联编辑" class="headerlink" title="无缝丝滑的内联编辑"></a>无缝丝滑的内联编辑</h3><p>如果说 Chat 功能侧重于宏观的理解、生成和问答，那么内联编辑（Inline AI）则专注于微观的代码修改、优化和生成，在 Cursor 中通常通过选中代码后按 <code>Cmd+K</code>​ 触发。</p><p>这是我在做老系统维护时使用频率非常高的一个功能，<strong>就像一把小手术刀，让 AI 专注在一个细分场景的逻辑做代码微调</strong>，Cursor 在这方面的体验细节相比其他家打磨得相当出色。</p><h4 id="亮点：上下文保持与编辑自由度"><a href="#亮点：上下文保持与编辑自由度" class="headerlink" title="亮点：上下文保持与编辑自由度"></a>亮点：上下文保持与编辑自由度</h4><blockquote><p>注：我自己在编写代码的时候只要不涉及多文件修改（这种场景更适合用 Agent）大多数都是以内联编辑的方式进行代码编写，目的是确保 Prompt 意图、修改代码的位置更精准以增加质量</p></blockquote><p><strong>暂存区缓存机制的优越性：</strong><br>这是 Cursor 内联编辑体验远超许多其他 AI 辅助工具（包括我体验过的早期 Trae 版本）的一个关键点。</p><p>这时候经常遇到的一个场景是：</p><ol><li>在<strong>模块 A</strong> 的某个函数中，我选中一段代码，使用内联编辑（<code>Cmd+K</code>​）让 AI 进行修改或重构。AI 给出了一些建议，我可能还在思考如何调整 Prompt 以获得更好的结果。</li><li>这时我突然意识到，为了完成模块 A 的这个修改，我需要在<strong>模块 B</strong> 中新增一个辅助函数。</li><li>我切换到<strong>模块 B</strong> 的文件，可能会用 Chat 或者再次使用内联编辑（比如选中一段注释，让 AI 生成函数体）来快速创建这个新函数。</li><li>完成模块 B 的工作后，我再切回<strong>模块 A</strong> 的文件。</li></ol><p>在 Cursor 中，当我切回模块 A 时，之前在模块 A 进行的内联编辑对话历史、AI 的建议、甚至我输入了一半的待调整 Prompt <strong>依然存在</strong>，我可以无缝接续之前的操作。这种上下文的持久性非常重要，它保证了工作流的连贯性。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250528160526-hk0x2gx.png" alt="image" data-caption="image" loading="lazy">​</p><p>而在 Trae 目前版本中，从其他文件切换回来之后，不止输入到一半的 Prompt 丢失，甚至没有被确认的代码都被自动应用了，这对高频使用内联编辑的用户来说是不太能忍受的。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250528161021-mdmwzpi.png" alt="image" data-caption="image" loading="lazy">​</p><h4 id="段内编辑的上下文感知"><a href="#段内编辑的上下文感知" class="headerlink" title="段内编辑的上下文感知"></a>段内编辑的上下文感知</h4><p>另一个值得称赞的细节是 Cursor 在进行<strong>段内编辑</strong>（不替换整个函数或类，而是修改选中代码片段中一小部分）时，对语法上下文的理解。</p><p>当我选中一段复杂的条件语句中的某个表达式，或者一个循环体内部的几行代码，要求 AI 进行修改时，Cursor 通常能很好地理解这几行代码在整个语法结构中所处的位置和角色，从而给出精准且语法正确的修改。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250528160925-4ko7ejw.png" alt="image" data-caption="image" loading="lazy">​</p><p>而 Trae 在这方面的表现，根据我的体验有时就不那么理想。它对于段内代码的修改，偶尔会显得比较“粗暴”和割裂，像是把选中的部分孤立出来进行处理，不太考虑其在原始语法结构中的嵌入关系，导致生成的代码片段虽然本身可能逻辑正确，但直接放回原文时会出现语法错误或逻辑断裂，需要开发者进行二次加工。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250528160910-lj9my0s.png" alt="image" data-caption="image" loading="lazy">​</p><h3 id="AI-Agent-—-自动化与智能辅助"><a href="#AI-Agent-—-自动化与智能辅助" class="headerlink" title="AI Agent — 自动化与智能辅助"></a>AI Agent — 自动化与智能辅助</h3><p>除去精细化的内联编辑和带有上下文感知能力的聊天，AI IDE 通常还提供一种更宏观、更具自主性的交互模式，通常称之为“Agent”模式。</p><p>在这种模式下，AI 不仅仅是响应你对特定代码片段的修改请求，而是尝试理解并执行更复杂的、可能跨越多个文件或涉及多个步骤的任务。</p><p>我体验过 Cursor、Cline、Trae 这三家的 Agent 功能，最让我眼前一亮的是 <code>Cursor Agent</code>​和 <code>claude-4-sonnet</code>​的组合，虽说在上下文过长时代码质量会显著下降，也可能出现越改越乱的情况；但相比其他组合而言，它在开发人员的意图理解及生成代码质量的提升是巨大的。</p><p>抛开模型来看，<strong>Agent 功能上各家并没有拉开本质上的差距，以及受制于 LLM 目前的基础上下文能力，目前无论多好的 AI IDE 都会出现“鬼打墙”的情况，但相信在大模型基础能力增强以及召回策略的更新迭代下，Agent 的问题会越来越少。</strong></p><p>用开源项目 <a href="https://github.com/LiberSonora/LiberSonora">LiberSonora/LiberSonora</a> 举个例子：</p><blockquote><p>请帮我实现如下功能：</p><ul><li>增加 Gemini 、 Claude、Grok 系列模型的配置</li><li>翻译功能增加意大利语、阿拉伯语、韩语、俄语的选择</li><li>优化翻译 Prompt</li></ul></blockquote><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250528163523-m28iqhc.png" alt="image" data-caption="image" loading="lazy">​</p><p>与内联编辑专注于局部修改不同，Agent 的特点在于：</p><ul><li><strong>任务导向：</strong>  你描述的是一个“任务”或“目标”，而不是具体的代码改动。例如：“为 <code>Product</code>​ 模型添加一个新的 <code>inventory_status</code>​ 字段，并在相关的服务和API中更新逻辑以支持按库存状态筛选产品。”</li><li><strong>潜在的跨文件操作：</strong>  AI Agent 可能会识别出完成该任务需要在多个文件中进行修改（例如，模型定义、数据库迁移脚本、服务层逻辑、API 控制器、单元测试等）。</li><li><strong>更高程度的自主性：</strong>  AI 会尝试自行决定修改的具体位置和方式，以及通过 Lint、命令行工具验证更改的有效性</li></ul><blockquote><p>PS：在 Typescript 场景下，动态更新的 node_modules 依赖文件有时无法很好的自动重载导致 Lint 无法识别新字段，这时候 Agent 会认为代码写错了而不会怀疑 IDE 的 Lint 出现了问题，然后给代码加一堆 as any</p><p>目前这个问题比较频繁触发的是 <a href="https://www.prisma.io/">Prisma</a> 在 Agent 更改 Schama 后自动执行 prisma generate 生成动态依赖，出现这种情况只能手动重启 Typescript Server 解决；</p></blockquote><p><strong>挑战与应对：</strong></p><ul><li><p><strong>“黑盒”与不可预测性：</strong>  AI Agent 的决策过程有时不够透明，它所做的修改可能遍布代码库，如果不对其行为进行约束或仔细审查，很容易引入难以察觉的 Bug。</p><ul><li><strong>应对：</strong>  Cursor 相比 Cline 和 Trae 做的好一点，可以自动识别 AI 做出的更改进行”Restore Checkpoint“，<strong>但我自己的习惯还是每次做大更改前我把已自测验证的功能</strong> <strong>​<code>git add .</code>​</strong> ​ <strong>或</strong> <strong>​<code>git commit</code>​</strong>​ <strong>一下，避免 Agent 把代码改乱不好恢复。</strong></li></ul></li><li><p><strong>代码质量参差不齐：</strong>  AI Agent 往往遵循“最方便更改”或“最快实现”的原则，可能会生成冗余代码，或者忽视代码的可扩展性和维护性。</p><ul><li><strong>应对：</strong> 尽可能保证代码库中现有代码质量，以及给 AI 找一个正确的规范的代码参考，AI 需要参考好代码才能写出好代码</li></ul></li><li><p><strong>理解偏差：</strong>  对于模糊或复杂的指令，AI Agent 的理解可能与你的预期有偏差，导致生成错误或不相关的代码。</p><ul><li><strong>应对：</strong>  尽量将复杂任务拆解成更小、更明确的子任务，分步引导 Agent，并且给到足够多的业务信息上下文以及代码上下文，这样稳定性会更好。</li></ul></li><li><p><strong>知识陈旧：</strong> 大模型的知识库总是有滞后性的，这是目前所有 AI 工具都面临的一个坎。新技术、新框架层出不穷，迭代速度飞快，指望大模型能及时跟上所有这些更新，有点不太现实。</p><ul><li><strong>应对：</strong> 善用文档和规则引擎功能，在每次推理前给到足够精确有效的上下文</li></ul></li></ul><h3 id="强大的自定义规则引擎-Project-Rules"><a href="#强大的自定义规则引擎-Project-Rules" class="headerlink" title="强大的自定义规则引擎 (Project Rules)"></a>强大的自定义规则引擎 (Project Rules)</h3><p>用 AI 写代码，最怕的就是它“自作聪明”或者“风格迥异”。Cursor 的 Project Rules 功能，在很大程度上缓解了这个问题，这也是我觉得它比 Trae 在项目级理解上做得更深的一个点。</p><p>简单说，Project Rules 允许你为整个项目或者项目中的特定目录、特定文件类型定义一套指导意见。这些意见会作为高优先级的上下文，影响 AI 生成代码的风格规范、代码结构、实现方案选型等。</p><blockquote><p>下图是我一个做市场的朋友，一位几乎零基础的非互联网人让 AI 编写的自定义规则；</p><p>他最近用 Cursor 开发了一个心理评测网站，打通了从原始需求沟通到项目上线的全流程，一个人承担了市场、产品、前后端研发的职能，他在这里约定了项目背景、前后端架构、开发部署规范、代码规范等。</p></blockquote><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250528165944-hyq1539.png" alt="image" data-caption="image" loading="lazy">​</p><p>我个人体验下来，Cursor 在规则灵活性和上下文填充优先级上，确实比我用过的其他工具（包括 Trae）要强一些。</p><p>在 Cursor 中，可以非常细致地为不同的文件目录配置不同的规则，并作为一个配置文件跟着 Git 提交到仓库中，保证所有人的规则版本一致</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250528165842-iwgzb3t.png" alt="image" data-caption="image" loading="lazy">​</p><p>以及支持在 Agent、Ask 模式下引用自定义规则，用于控制生成的代码质量或方便的填充统一的项目约定</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250528170315-eencnw1.png" alt="image" data-caption="image" loading="lazy">​</p><h3 id="文档：让-AI-实时学习新知识"><a href="#文档：让-AI-实时学习新知识" class="headerlink" title="文档：让 AI 实时学习新知识"></a>文档：让 AI 实时学习新知识</h3><p>大模型的知识库总是有滞后性的，这是目前所有 AI 工具都面临的一个坎。互联网的新技术、新框架、新语言以及新版本层出不穷，迭代速度飞快，指望大模型能及时跟上所有这些更新有点不太现实。<strong>一旦遇到模型训练数据里覆盖不足的技术，AI 就很容易开始胡言乱语，给出的代码可能完全不着边际。</strong></p><p>我之前工作中就遇到过这样的典型场景。当时在调研用字节新开源的一个大模型应用开发框架，叫做 <a href="https://github.com/cloudwego/eino">Eino</a>。这个框架非常新，版本迭代也特别快，几乎每天都有新变化。</p><p>这种情况下，无论是 GPT 、 Claude、以及字节自己的豆包大模型，在它们的知识库里关于 Eino 的有效信息几乎是零。直接让它们写 Eino 的代码，基本就是鸡同鸭讲。</p><p>Cursor 提供了一个非常实用的“文档” (Docs) 功能，允许你把外部的文档、网页链接，甚至是本地的 Markdown 文件、代码片段等加入到项目的知识库中，让 AI 可以实时学习和参考这些最新的信息。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250528171411-71nvi9d.png" alt="image" data-caption="image" loading="lazy">​</p><p>具体到 <a href="https://github.com/cloudwego/eino">Eino</a> 这个例子，我的做法是：</p><ol><li><strong>收集关键信息源：</strong>  我把 Eino 的官方文档链接、核心源码的 GitHub 仓库链接，以及一些社区贡献的扩展库源码链接都整理出来。</li><li><strong>添加到 Cursor 文档：</strong>  在 Cursor 的项目设置里，找到 “Add Docs” 入口，把这些链接依次加进去。Cursor 会在后台抓取这些网页内容并建立索引。</li><li><strong>在交互中引用文档：</strong>  当我需要 AI 帮我写 Eino 相关的代码，或者解答关于 Eino 的问题时，我会在 Prompt 中明确引用这些添加的文档。比如，在 Cursor 的聊天框里，我可以输入类似：“<code>@Eino官方文档 @Eino核心源码 请帮我实现一个使用 Eino 的 XXX 功能，需要注意 YYY</code>​”。通过 <code>@</code>​ 符号可以精确地告诉 AI 去参考哪些特定的知识源。</li></ol><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250528171337-v15cuc7.png" alt="image" data-caption="image" loading="lazy">​</p><p>效果立竿见影，AI 能理解 Eino 的 API 设计、特定的函数用法，甚至是一些设计理念了。虽然不能说完美无缺，但至少代码能跑起来，实现方向也是对的，大大减少了我查官方文档和翻源码考究内部实现的时间。</p><p><strong>从外部文档到内部实践的演进：</strong></p><p>随着项目开发进行，项目本身会积累大量使用 Eino 的优秀实践。这时对外部文档的依赖就可以逐渐减少，因为 AI 在索引我们项目代码时，已经能从现有的、经过验证的代码中学习到 Eino 的正确用法和最佳实践了。</p><p>这时候我可能会调整 Project Rules，让 AI 自动总结一些基于项目实践总结出来的规范和要求，以及在提问时，引导 AI 参考项目中某个写得特别好的模块作为优秀范例。</p><p><strong>一些思考</strong></p><p>文档功能配合 Codebase Indexing 和 Project Rules，它让 Cursor 不仅仅能理解你已有的代码，还能快速学习和适应项目依赖的新技术、新知识，这对于追赶技术迭代、尝试新框架的开发者来说价值非常大。它解决了 AI 水土不服的一个关键问题，让 AI 辅助编码在新技术探索阶段也能真正派上用场。</p><p>目前的文档和 indexing 并非没有局限，他们的底层实现都十分依赖向量数据召回，所以引用文档时的关键字准确性比较重要，不然在目前的索引技术上很容易找偏，带来的结果就是代码质量差。</p><h2 id="实操案例"><a href="#实操案例" class="headerlink" title="实操案例"></a>实操案例</h2><p>使用 Cursor 做了几个开源小项目满足日常需求，一个是 <a href="https://libersonora.github.io/">LiberSonora</a>，这是我在25年元旦假期期间做的，为了满足我有声书看字幕、以及字幕反向跳转的需求，核心功能使用 Python 开发开发了两天左右，主要使用 Cursor 的内联编辑结合当时成本极低的 Deepseek 模型开发，大概有 80% 以上的代码都是 AI 写的</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250609171008-r4w2yoa.png" alt="image" data-caption="image" loading="lazy">​</p><p>一个是 <a href="https://github.com/wangerzi/clash-converter/tree/main">Clash 订阅转换工具</a>，其实网上有很多转换工具，但还是想做一个轻量的，切实满足自己需求的转换工具，使用 Python 开发，完全使用 Agent（claude-4-sonnet），开发时间在两小时左右</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250609170930-ija7tkc.png" alt="image" data-caption="image" loading="lazy">​</p><p>一个是<a href="https://github.com/wangerzi/CrimsonRise/tree/main">红色年代海报生成器</a>，完全使用 Agent（claude-4.1-thinking），开发时间在三十分钟左右</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250609161012-ps9inth.png" alt="image" data-caption="image" loading="lazy"></p><h2 id="AI-近期的发展"><a href="#AI-近期的发展" class="headerlink" title="AI 近期的发展"></a>AI 近期的发展</h2><p>个人觉得最近比较值得关注趋势之一是”自我进化“、”复杂环境自适应“和”具身智能结合“。</p><h3 id="大模型的自我进化"><a href="#大模型的自我进化" class="headerlink" title="大模型的自我进化"></a>大模型的自我进化</h3><p>在大模型训练上，强化学习本身就是一种自我进化，目前已经有 SFT、DPO、PPO、GPRO 等各种强化方法用在大模型对齐等场景（<a href="https://zhuanlan.zhihu.com/p/1910019667268986241">参考资料</a>）。</p><p>最近 <a href="https://github.com/waltonfuture/MM-UPT">MM-UPT</a>、（<a href="https://mp.weixin.qq.com/s/0PPw4t2YCwu-7zrxpjglcA">公众号解读文章</a>）也给出了强化学习和自我进化的的新思路，不需要像 DPO、DPO 等算法依旧需要少量标记数据计算奖励，而是让模型自己去投票出最佳的奖励（<strong>这个策略跟模型蒸馏非常像，多次迭代可能导致模型偏见增加</strong>），整个流程如下：</p><ul><li>给定一张图片和一个问题，模型生成多个候选回答；</li><li>使用多数投票选出出现频率最高的回答，作为当前输入的「伪标签」；</li><li>使用这个「伪标签」来计算 reward，引导模型根据 GRPO 策略更新；</li></ul><p>这整个过程无需任何外部监督信号或真实答案，使得模型可以基于自身的「共识」行为进行强化学习，从而实现持续的性能提升。</p><h3 id="复杂环境自适应和知识的热更新"><a href="#复杂环境自适应和知识的热更新" class="headerlink" title="复杂环境自适应和知识的热更新"></a>复杂环境自适应和知识的热更新</h3><p>比如 <a href="https://github.com/CharlesQ9/Alita">github.com/CharlesQ9/Alita</a> 这个项目（<a href="https://mp.weixin.qq.com/s/vmp8H-3S_HH6Gvb4dH5FxA">公众号解读文章</a>），任务的难度可能超出了预定义工具或工作流的能力范围。复杂任务通常需要智能体创新性地使用新工具，或以新的方式组合和利用现有工具，而预定义的工具库和工作流会制约这种创造性和灵活性。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250605114054-2ulq68h.png" alt="image" data-caption="image" loading="lazy">​</p><p>还有一个方向是尝试解决大模型知识库始终落后这一问题的，最近一个早期发现是 <a href="https://github.com/oneal2000/PRAG"> Parametric RAG</a> 可以“<strong>喂大模型吃记忆面包</strong>”，相当于把新的最相关的知识转成“学习这段知识的前后权重差异”，在用的时候临时“吃”进模型里，并且不占用宝贵的上下文</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/ParametricRAG-20250609184610-0t0av5m.gif" alt="ParametricRAG" data-caption="ParametricRAG" loading="lazy">​</p><h3 id="具身智能结合大模型"><a href="#具身智能结合大模型" class="headerlink" title="具身智能结合大模型"></a>具身智能结合大模型</h3><p>参考资料：<a href="https://xueqiu.com/9615908983/327812508">2025年具身智能+大模型</a></p><p>具身智能是人工智能的一个重要分支，强调 Agent 在物理环境中的身体与智能相互依赖。具身智能不仅依赖于信息处理能力，还依赖于智能体的感知和行动能力，通过感知环境并采取适当的行动来解决问题。</p><p>这对 Agent 的信息采集处理、意图识别、实时任务规划和执行的要求会比较高，谷歌就开放了<a href="https://openvla.github.io/">OpenVLA</a> 这一模型，开放的版本是基于 llama2-7b 调的，小巧适合端到端部署但性能不弱（虽然现在都还是找、拿、放东西的级别），但相信国内外各大机器人厂商对这个的投入和热情不会太少。</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ai-first-program/image-20250605155241-495ajvk.png" alt="image" data-caption="image" loading="lazy">​</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>总的来说，目前阶段 AI 擅长的是做大量重复性的代码生成工作但注意力上下文较为有限，人擅长的是对需求上下文的深入理解及项目代码的整体把控；</p><p>所以现阶段 AI First 的重点在于人和 AI 的协作，各司其职发挥所长，相信在未来随着 AI 能力的提升，AI First 的理念不止在编程领域生根发芽，还会越来越多的融合到互联网领域外，像移动互联网和自来水一样成为人类生活必备的工具之一。</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;blockquote&gt;
&lt;p&gt;AI First 编程是指有事儿 AI</summary>
        
      
    
    
    
    <category term="chatgpt" scheme="https://blog.wj2015.com/tags/chatgpt/"/>
    
    <category term="大语言模型" scheme="https://blog.wj2015.com/tags/%E5%A4%A7%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B/"/>
    
    
    <category term="思考" scheme="https://blog.wj2015.com/tags/%E6%80%9D%E8%80%83/"/>
    
    <category term="机器学习" scheme="https://blog.wj2015.com/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
    <category term="编程" scheme="https://blog.wj2015.com/tags/%E7%BC%96%E7%A8%8B/"/>
    
    <category term="随笔" scheme="https://blog.wj2015.com/categories/%E9%9A%8F%E7%AC%94/"/>
    
  </entry>
  
  <entry>
    <title>如何自己动手修复一柄剑</title>
    <link href="https://blog.wj2015.com/2025/02/13/%E5%A6%82%E4%BD%95%E8%87%AA%E5%B7%B1%E5%8A%A8%E6%89%8B%E4%BF%AE%E5%A4%8D%E4%B8%80%E6%9F%84%E5%89%91/"/>
    <id>https://blog.wj2015.com/2025/02/13/%E5%A6%82%E4%BD%95%E8%87%AA%E5%B7%B1%E5%8A%A8%E6%89%8B%E4%BF%AE%E5%A4%8D%E4%B8%80%E6%9F%84%E5%89%91/</id>
    <published>2025-02-13T09:25:30.000Z</published>
    <updated>2026-08-12T03:28:46.856Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h1 id="如何自己动手修复一柄剑"><a href="#如何自己动手修复一柄剑" class="headerlink" title="如何自己动手修复一柄剑"></a>如何自己动手修复一柄剑</h1><blockquote><p>发文的时候哪吒2票房正好破百亿，太猛了，上周贡献两张电影票，敖丙踢枪那段是真帅啊，热血沸腾！！！</p></blockquote><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>为啥要俢复一柄剑呢，起因是我在快过年的时候乘着有假期想把玩练习一下剑，就把尘封至少有四年的健身用太极剑拉出来舞了舞，依稀还能看到当时练习带来的磨损。</p><p>舞的过程中发现木质剑把部分跟铁制护手没卡紧，有点滑来滑去的，于是打算回家用扳手扭一下。结果扳手力矩比较大，扭一下直接把合金的剑首搞破了，这下不得不拆开看看这把剑的内部结构了。</p><p>​​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/image-20250213160744-0xc1ufn.png" alt="image" data-caption="image" loading="lazy">​</p><h2 id="修复过程"><a href="#修复过程" class="headerlink" title="修复过程"></a>修复过程</h2><p>修复前先了解了一下剑的部件结构，损坏的部分是剑首，也叫做“剑镦”，剑把是木质的，除了有点老化之外并无损坏，剑格和剑格的挡片都是好的</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/image-20250213160032-usis1ks.png" alt="image" data-caption="image" loading="lazy">​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/image-20250213160640-s8o0q3b.png" alt="image" data-caption="image" loading="lazy">​</p><p>全拆开来之后，剑身之上的剑柄部分其实就是焊上去的一个小铁条，没有“目钉”，尾巴部分由螺母和垫片跟剑首固定，也就是俗称的“老鼠尾”，如果用作收藏或者有大力劈砍的需求最好不要买这种，但这把剑毕竟是健身锻炼用的，对重心和韧性的要求更高，总之适合自己就好，就不纠结这些了。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/image-20250213163527-ibw02vi.png" alt="image" data-caption="image" loading="lazy">​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/image-20250213161717-ucjkwgs.png" alt="image" data-caption="image" loading="lazy">​​​​</p><p>之前买的是样式随处可见的量产产品，虽然已经过去很多年，但现在依旧正常售卖，所以我猜里边的不同部件应该有不同的供应商在做，应该能找到散出的供应商</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/image-20250213162124-pnnzw64.png" alt="image" data-caption="image" loading="lazy">​</p><p>果不其然，淘宝识图一搜就找到了，加上邮费 ￥28，价格比 PDD 便宜点儿，样子和尺寸都跟原版一模一样。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/image-20250213162434-7lihm4y.png" alt="image" data-caption="image" loading="lazy">​</p><p>除了护手套件之外，剑鞘的装具也有</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/image-20250213162617-oiye66k.png" alt="image" data-caption="image" loading="lazy">​</p><p>甚至剑柄、剑鞘、剑条都能找到，全集齐自己不就能自己 DIY 一把了么😁</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/image-20250213162856-lfz89wi.png" alt="image" data-caption="image" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/image-20250213163116-levmxiv.png" alt="image" data-caption="image" loading="lazy">​</p><p>扯远了，不过由于过年无法发货，等了七八天才到。</p><p>依次装上护手、剑把、剑镦，再手拧上螺丝，锃锃，跟新的一样！</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/image-20250213163635-odyv2ko.png" alt="image" data-caption="image" loading="lazy">​</p><h2 id="怎么选剑"><a href="#怎么选剑" class="headerlink" title="怎么选剑"></a>怎么选剑</h2><p>修复过程有点短，还可以再唠唠普通爱好者怎么选一把适合自己的剑。</p><p>第一步是确认需求，如果你喜欢某套剑法，那么这套剑法一定会有与之匹配的剑的类型，比如“单手剑”、“双手剑”，“长剑”、“短剑”、“重剑”等。</p><p>第二步选择长度和重量，以太极剑为例，反手持剑剑高不过眉，低不过耳就好，商家一般也会有一个身高对照表，可以对照着来。</p><p>重量要因人因剑的类型而异，一开始就用太重的容易伤到身体，标准太极剑的重量一般在600克左右，不同的身高尺寸，剑的重量会稍有偏差一般相差在100克以内。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/image-20250213164708-rf69xbj.png" alt="image" data-caption="image" loading="lazy">​</p><p>第三步选材质和韧性，不锈钢材质基本无需保养，放个几年依旧光洁如新，碳钢、折叠钢、弹簧钢等多少都需要定期保养上油，材质也会一定程度上影响韧性和重量。</p><blockquote><p>手上的汗水对剑的伤害比较大，哪怕是不锈钢也会被汗水腐蚀，如果用手摸过或者雨水淋过最好用眼镜布加点保养油及时擦拭干净。</p></blockquote><p>同样以太极剑为例，太极剑属于单手直剑，剑身轻薄，对平衡性有一定的要求，重心在剑身距护手约一两寸为佳，韧性要求适中，弯折角度可接近90度，太极剑有“抖剑”、“刺剑”等发力动作，韧性适中的剑习练感受会更好。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/image-20250213165705-pnttdt2.png" alt="image" data-caption="image" loading="lazy">​</p><p>商家卖的太极剑里边会分平脊和起脊，脊就是指剑身背部的中脊，平脊更轻，重心更靠近手柄，起脊更重，标准太极剑是起脊的，下图为起脊的太极剑。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/image-20250213165611-qemyfro.png" alt="image" data-caption="image" loading="lazy">​</p><h3 id="便携伸缩剑"><a href="#便携伸缩剑" class="headerlink" title="便携伸缩剑"></a>便携伸缩剑</h3><p>网上针对便携需求会卖很便宜的塑料便携剑，但这种剑第一重量不够，第二没有韧性，第三重心偏离严重，但十来块钱舞着玩还是不错的。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/image-20250213170557-efkn48z.png" alt="image" data-caption="image" loading="lazy">​</p><p>因为个人也有便携需求，所以这次过年我也买了一个贵一点的不锈钢伸缩剑，花了 ￥95，重量更接近标准太极剑，但手感确实差太远了，特别剑身要收缩到剑把里，那剑把内就是空的，哪怕尾巴加上了剑穗重心也是偏护手特别远的，舞起来虎口比较累。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/image-20250213171155-j6qxx4f.png" alt="image" data-caption="image" loading="lazy">​</p><h3 id="一些建议"><a href="#一些建议" class="headerlink" title="一些建议"></a>一些建议</h3><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-sword/advice.jpg" alt="image" data-caption="image" loading="lazy">​</p><p>锻炼用的剑可以找个袋子装一下放到适当的位置，最好不要明晃晃的摆在家中随时可见的地方，特别是卧室，玄学上讲这叫“煞”容易带来冲突和不幸。从心理学上讲，时时刻刻都能看到带有“破坏力”的武器，更容易冲突和唤起暴力情绪。</p><p>以及健身习练需要适度和小心，做好热身运动，量力而为，我就有过热身没做好闪到腰，静养、推拿了好长时间都还腰酸。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>工业品生产拼凑的剑换配件很容易，两三下就搞好了，再借此机会再了解了一下选剑、剑的结构相关的信息。</p><p>‍</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h1 id=&quot;如何自己动手修复一柄剑&quot;&gt;&lt;a href=&quot;#如何自己动手修复一柄剑&quot; class=&quot;headerlink&quot;</summary>
        
      
    
    
    
    <category term="运动" scheme="https://blog.wj2015.com/tags/%E8%BF%90%E5%8A%A8/"/>
    
    
    <category term="随笔" scheme="https://blog.wj2015.com/categories/%E9%9A%8F%E7%AC%94/"/>
    
  </entry>
  
  <entry>
    <title>使用 Hexo 在十分钟内创建项目官网</title>
    <link href="https://blog.wj2015.com/2025/01/27/%E4%BD%BF%E7%94%A8-Hexo-%E5%9C%A8%E5%8D%81%E5%88%86%E9%92%9F%E5%86%85%E5%88%9B%E5%BB%BA%E9%A1%B9%E7%9B%AE%E5%AE%98%E7%BD%91/"/>
    <id>https://blog.wj2015.com/2025/01/27/%E4%BD%BF%E7%94%A8-Hexo-%E5%9C%A8%E5%8D%81%E5%88%86%E9%92%9F%E5%86%85%E5%88%9B%E5%BB%BA%E9%A1%B9%E7%9B%AE%E5%AE%98%E7%BD%91/</id>
    <published>2025-01-27T13:25:47.000Z</published>
    <updated>2026-08-12T03:28:46.856Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>蛇年春节在即，在这里祝大家蛇年大吉，财源广进！！！🧨🧨🧨</p><p>博主就乘着节前还有点业余时间继续搞一搞开源项目，背景是大概在 2025 年元旦，我使用 Cursor + DeepSeek V3 快速的写完了一个开源项目的核心功能，也就是 <a href="https://github.com/LiberSonora/LiberSonora">LiberSonora - 一个 AI 赋能的、强大的、开源有声书工具集</a>。</p><p>但这个项目的相关介绍、愿景、功能、API 接口等等信息很长，放在 README 里边非常不雅观，放 Wiki 里边又觉得不够好看，所以还是决定给这个项目搞一个官网，把 README 里边冗杂的各种信息分门别类都放到项目官网里，以及未来的更新日志都可以在官网中以博客的方式体现。</p><p>想到这里，做一个项目官网需要的核心功能就出来了：</p><ul><li>简单明了，主页不能直接是一个文章列表，主要的视觉集中处需要有充足的内容空间介绍项目情况</li><li>侧边栏菜单需要支持独立页</li><li>需要支持友情链接配置</li><li>支持点击放大图片</li><li>移动端访问友好</li><li>支持接入 Google Analysis 等分析工具</li><li>展示网站访问 PV UV</li></ul><p>项目官网预览：<a href="https://libersonora.github.io/">https://libersonora.github.io/</a><br>官网开源地址：<a href="https://github.com/LiberSonora/libersonora.github.io">https://github.com/LiberSonora/libersonora.github.io</a></p><p>完全配置完是这样的展示效果，且在提交后会自动打包内容更新网站：</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-hexo-website/image-20250127160200-ymig4x9.png" alt="image" data-caption="image" loading="lazy"></p><h2 id="技术选型"><a href="#技术选型" class="headerlink" title="技术选型"></a>技术选型</h2><p>目前并不打算为这个开源项目单独申请域名，所以在部署上就打算使用 <a href="https://pages.github.com/">Github Pages</a> 和自带的 github.io 域名。</p><p>Github Pages 本身是内置支持 Jekyll 的，<a href="https://jekyllrb.com/">Jekyll</a> 是一个静态网站生成器，可以把 Markdown 文档转换为静态网站 html，进而在 Github Pages 中部署。</p><p>这个工具本身没有问题，用的人比较多，但在 Github Pages 中内置支持的模板比较少，可以再<a href="https://pages.github.com/themes/">这个链接</a>中查看，我挨个点进去看了，都只适合做博客，不适合做项目官网。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-hexo-website/image-20250127161232-dtxzf9o.png" alt="image" data-caption="image" loading="lazy">​</p><p>如果跳出内置的主题，用<a href="https://docs.github.com/en/pages/getting-started-with-github-pages/using-custom-workflows-with-github-pages">自定义 actions 打包</a>的话，就不用限定在 Jekyll 这一个静态网站生成器上。</p><p>最后经过手工挑选+Deepseek R1 的推荐，最终选择了 Hexo + NexT 主题来完成这个功能</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-hexo-website/image-20250127161658-3doa7aj.png" alt="image" data-caption="image" loading="lazy">​</p><p>Hexo 文档：<a href="https://hexo.io/zh-cn/docs/">hexo.io/zh-cn/docs/</a></p><p>Hexo NexT 主题文档：<a href="https://theme-next.js.org/docs/">theme-next.js.org/docs/</a></p><h2 id="操作记录"><a href="#操作记录" class="headerlink" title="操作记录"></a>操作记录</h2><p>整体思路如下：</p><ul><li>创建一个 github pages 仓库</li><li>安装 hexo 、hexo-next 主题</li><li>调整 hexo 和主题配置</li></ul><h3 id="Github-Pages-配置"><a href="#Github-Pages-配置" class="headerlink" title="Github Pages 配置"></a>Github Pages 配置</h3><p>跟着这个指引 <a href="https://docs.github.com/en/pages/quickstart">Quickstart for GitHub Pages</a> 就可以初始化好一个网站仓库，接下来往这个仓库填充内容就好了。</p><p>创建一个新的仓库​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-hexo-website/image-20250127194015-pnedww5.png" alt="image" data-caption="image" loading="lazy">​</p><p>仓库名称为 [project/username].github.io</p><blockquote><p>需要注意的是，<strong>如果项目名称是驼峰的，在创建仓库的时候需要都转为小写</strong>，比如我的开源项目组织名字为 LiberSonora，在创建仓库时只能写 libersonora.github.io，访问地址同样也都只能是小写 <a href="https://libersonora.github.io/">https://libersonora.github.io</a></p></blockquote><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-hexo-website/image-20250127160907-nfd08lz.png" alt="image" data-caption="image" loading="lazy">​</p><p>设置里边打开 Pages 的支持，注意这里的构建 Source 要选 github actions，如果有自己的域名可以在输入框内填写并配置解析</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-hexo-website/image-20250127194238-ftmw33f.png" alt="image" data-caption="image" loading="lazy">​</p><p>把仓库克隆到本地，运行如下命令进行初始化</p><p>进入到仓库目录后</p><figure class="highlight bash"><table><tbody><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><code class="hljs bash">npm i -g hexo<br>npx hexo init web-app<br><span class="hljs-built_in">cd</span> web-app<br></code></pre></td></tr></tbody></table></figure><p>进入到项目目录后，跟踪为刚才新建的远程仓库</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">git init<br>git remote add origin https://github.com/LiberSonora/libersonora.github.io.git<br></code></pre></td></tr></tbody></table></figure><p>配置 github workflow</p><p>把如下 pages.yml 放到 <code>.github/workflows/</code>​ 下</p><figure class="highlight yml"><table><tbody><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><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">name:</span> <span class="hljs-string">Pages</span><br><br><span class="hljs-attr">on:</span><br>  <span class="hljs-attr">push:</span><br>    <span class="hljs-attr">branches:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span> <span class="hljs-comment"># default branch</span><br><br><span class="hljs-attr">jobs:</span><br>  <span class="hljs-attr">build:</span><br>    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span><br>    <span class="hljs-attr">steps:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span><br>        <span class="hljs-attr">with:</span><br>          <span class="hljs-attr">token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span><br>          <span class="hljs-comment"># If your repository depends on submodule, please see: https://github.com/actions/checkout</span><br>          <span class="hljs-attr">submodules:</span> <span class="hljs-string">recursive</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Use</span> <span class="hljs-string">Node.js</span> <span class="hljs-number">20</span><br>        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v4</span><br>        <span class="hljs-attr">with:</span><br>          <span class="hljs-comment"># Examples: 20, 18.19, &gt;=16.20.2, lts/Iron, lts/Hydrogen, *, latest, current, node</span><br>          <span class="hljs-comment"># Ref: https://github.com/actions/setup-node#supported-version-syntax</span><br>          <span class="hljs-attr">node-version:</span> <span class="hljs-string">"20"</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Yarn</span><br>        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span> <span class="hljs-string">--global</span> <span class="hljs-string">yarn</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Cache</span> <span class="hljs-string">Yarn</span> <span class="hljs-string">dependencies</span><br>        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/cache@v4</span><br>        <span class="hljs-attr">with:</span><br>          <span class="hljs-attr">path:</span> <span class="hljs-string">~/.yarn/cache</span><br>          <span class="hljs-attr">key:</span> <span class="hljs-string">${{</span> <span class="hljs-string">runner.OS</span> <span class="hljs-string">}}-yarn-cache</span><br>          <span class="hljs-attr">restore-keys:</span> <span class="hljs-string">|</span><br><span class="hljs-string">            ${{ runner.OS }}-yarn-cache</span><br><span class="hljs-string"></span>      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Dependencies</span><br>        <span class="hljs-attr">run:</span> <span class="hljs-string">yarn</span> <span class="hljs-string">install</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span><br>        <span class="hljs-attr">run:</span> <span class="hljs-string">yarn</span> <span class="hljs-string">build</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Upload</span> <span class="hljs-string">Pages</span> <span class="hljs-string">artifact</span><br>        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/upload-pages-artifact@v3</span><br>        <span class="hljs-attr">with:</span><br>          <span class="hljs-attr">path:</span> <span class="hljs-string">./public</span><br>  <span class="hljs-attr">deploy:</span><br>    <span class="hljs-attr">needs:</span> <span class="hljs-string">build</span><br>    <span class="hljs-attr">permissions:</span><br>      <span class="hljs-attr">pages:</span> <span class="hljs-string">write</span><br>      <span class="hljs-attr">id-token:</span> <span class="hljs-string">write</span><br>    <span class="hljs-attr">environment:</span><br>      <span class="hljs-attr">name:</span> <span class="hljs-string">github-pages</span><br>      <span class="hljs-attr">url:</span> <span class="hljs-string">${{</span> <span class="hljs-string">steps.deployment.outputs.page_url</span> <span class="hljs-string">}}</span><br>    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span><br>    <span class="hljs-attr">steps:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">GitHub</span> <span class="hljs-string">Pages</span><br>        <span class="hljs-attr">id:</span> <span class="hljs-string">deployment</span><br>        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/deploy-pages@v4</span><br></code></pre></td></tr></tbody></table></figure><p>本地运行项目</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">yarn install<br>yarn server<br></code></pre></td></tr></tbody></table></figure><p>如果一切正常，那么访问 <code>http://localhost:4000</code>​ 可以看到一个默认的 Hello World 页面</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-hexo-website/image-20250127200309-b7hiqv2.png" alt="image" data-caption="image" loading="lazy">​</p><p>推送到远程，观察 Actions，一个正常的 Action 可以参考：<a href="https://github.com/LiberSonora/libersonora.github.io/actions/runs/12974707962">https://github.com/LiberSonora/libersonora.github.io/actions/runs/12974707962</a>，然后就可以在 <code>[name].github.io</code> 上访问到刚才配置的网站了</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">git add .<br>git push origin main<br></code></pre></td></tr></tbody></table></figure><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-hexo-website/image-20250127195458-v2wptoo.png" alt="image" data-caption="image" loading="lazy">​</p><h3 id="Hexo-配置"><a href="#Hexo-配置" class="headerlink" title="Hexo 配置"></a>Hexo 配置</h3><p>在网站整体跑通的情况下，下一步就是 hexo 和 hexo-next 的配置了</p><p>首先执行如下命令安装 next 主题</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">yarn add hexo-theme-next<br></code></pre></td></tr></tbody></table></figure><p>然后复制一份配置模板到项目目录</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">cp</span> node_modules/hexo-theme-next/_config.yml _config.next.yml<br></code></pre></td></tr></tbody></table></figure><p>Hexo 中本身有一个 <code>_config.yml</code>​，只需配置一下 Site 、URL、Theme 相关的属性，成品配置参考 <a href="https://github.com/LiberSonora/libersonora.github.io/blob/main/_config.yml">_config.yml</a></p><figure class="highlight yml"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-comment"># Site</span><br><span class="hljs-attr">title:</span> <span class="hljs-string">LiberSonora</span><br><span class="hljs-attr">subtitle:</span> <span class="hljs-string">'一个 AI 赋能的、强大的、开源有声书工具集'</span><br><span class="hljs-attr">description:</span> <span class="hljs-string">'LiberSonora 寓意“自由的声音”，是一个 AI 赋能的、强大的、开源有声书工具集，包含智能字幕提取、AI标题生成、多语言翻译等功能，支持 GPU 加速、批量离线处理'</span><br><span class="hljs-attr">keywords:</span> <span class="hljs-string">'subtitle,audio-books,subtitles-generator,llm,ollama,translate,deepseek'</span><br><span class="hljs-attr">author:</span> <span class="hljs-string">LiberSonora</span><br><span class="hljs-attr">language:</span> <span class="hljs-string">zh-CN</span><br><span class="hljs-attr">timezone:</span> <span class="hljs-string">'Asia/Shanghai'</span><br><span class="hljs-comment"># URL</span><br><span class="hljs-comment">## Set your site url here. For example, if you use GitHub Page, set url as 'https://username.github.io/project'</span><br><span class="hljs-attr">url:</span> <span class="hljs-string">https://libersonora.github.io/</span><br><span class="hljs-comment"># ...</span><br><span class="hljs-attr">theme:</span> <span class="hljs-string">next</span><br><span class="hljs-comment"># ...</span><br></code></pre></td></tr></tbody></table></figure><p>然后是 next 主题的配置，可以参考他家的官方文档 <a href="https://theme-next.js.org/docs/theme-settings/">theme-settings</a> 一步步的调试，成品配置参考 <a href="https://github.com/LiberSonora/libersonora.github.io/blob/main/_config.next.yml">_config.next.yml</a></p><p>比较重要的几个配置列举如下，<a href="https://realfavicongenerator.net/">realfavicongenerator.net</a> 可以做 favicon，不过里边的 16x16  32x32 多尺寸的 favcion 得在 <a href="https://resize.com/">resize.com</a> 中二次处理下</p><figure class="highlight yml"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">scheme:</span> <span class="hljs-string">Gemini</span> <span class="hljs-comment"># Gemini 主题比较适合做项目官网</span><br><span class="hljs-comment"># favicon 得去 https://realfavicongenerator.net/ 做一下</span><br><span class="hljs-attr">favicon:</span><br>  <span class="hljs-attr">small:</span> <span class="hljs-string">/favicon/favicon-16x16.png</span><br>  <span class="hljs-attr">medium:</span> <span class="hljs-string">/favicon/favicon-32x32.png</span><br>  <span class="hljs-attr">apple_touch_icon:</span> <span class="hljs-string">/favicon/apple-touch-icon.png</span><br>  <span class="hljs-attr">safari_pinned_tab:</span> <span class="hljs-string">/favicon/favicon.svg</span><br>  <span class="hljs-attr">android_manifest:</span> <span class="hljs-string">favicon//manifest.json</span><br><br><span class="hljs-comment"># Custom Logo (Warning: Do not support scheme Mist)</span><br><span class="hljs-attr">custom_logo:</span> <span class="hljs-string">/images/logo-head.jpg</span><br><span class="hljs-comment"># Menu 是左侧的菜单，需要先建好单页再继续</span><br><span class="hljs-attr">menu:</span><br>  <span class="hljs-attr">home:</span> <span class="hljs-string">/</span> <span class="hljs-string">||</span> <span class="hljs-string">fa</span> <span class="hljs-string">fa-home</span><br>  <span class="hljs-string">快速开始:</span> <span class="hljs-string">/quick-start/</span> <span class="hljs-string">||</span> <span class="hljs-string">fa</span> <span class="hljs-string">fa-rocket</span><br>  <span class="hljs-string">转换示例:</span> <span class="hljs-string">/demo/</span> <span class="hljs-string">||</span> <span class="hljs-string">fa</span> <span class="hljs-string">fa-laptop-code</span><br>  <span class="hljs-string">语言支持和兼容性:</span> <span class="hljs-string">/support/</span> <span class="hljs-string">||</span> <span class="hljs-string">fa</span> <span class="hljs-string">fa-life-ring</span><br>  <span class="hljs-string">界面功能展示:</span> <span class="hljs-string">/pages/</span> <span class="hljs-string">||</span> <span class="hljs-string">fa</span> <span class="hljs-string">fa-file-alt</span><br>  <span class="hljs-string">API</span> <span class="hljs-string">接口:</span> <span class="hljs-string">/api/</span> <span class="hljs-string">||</span> <span class="hljs-string">fa</span> <span class="hljs-string">fa-code</span><br>  <span class="hljs-string">系统要求和性能测试:</span> <span class="hljs-string">/system-requirement/</span> <span class="hljs-string">||</span> <span class="hljs-string">fa</span> <span class="hljs-string">fa-server</span><br><span class="hljs-comment"># 友链</span><br><span class="hljs-attr">links:</span><br>  <span class="hljs-string">作者个人博客:</span> <span class="hljs-string">https://blog.wj2015.com</span><br>  <span class="hljs-string">项目</span> <span class="hljs-attr">Github:</span> <span class="hljs-string">https://github.com/LiberSonora/LiberSonora</span><br>  <span class="hljs-string">官网</span> <span class="hljs-attr">Github:</span> <span class="hljs-string">https://github.com/LiberSonora/libersonora.github.io</span><br><span class="hljs-comment"># 打赏功能</span><br><span class="hljs-attr">reward_settings:</span><br>  <span class="hljs-comment"># If true, a donate button will be displayed in every article by default.</span><br>  <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br>  <span class="hljs-attr">animation:</span> <span class="hljs-literal">true</span><br><span class="hljs-attr">reward:</span><br>  <span class="hljs-attr">wechatpay:</span> <span class="hljs-string">/images/wechatpay.png</span><br><span class="hljs-comment"># 支持点击打开图片</span><br><span class="hljs-attr">fancybox:</span> <span class="hljs-literal">true</span><br><span class="hljs-comment"># 谷歌分析的配置</span><br><span class="hljs-attr">google_analytics:</span><br>  <span class="hljs-attr">tracking_id:</span> <span class="hljs-comment"># &lt;app_id&gt;</span><br><span class="hljs-comment"># 访客统计功能</span><br><span class="hljs-attr">busuanzi_count:</span><br>  <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br></code></pre></td></tr></tbody></table></figure><blockquote><p>创建单页的命令是 <code>npx hexo new page [page-router]</code>​，创建完成后在 <code>source/[page-router]/index.md</code>​ 中修改页面即可</p></blockquote><h3 id="置顶文章"><a href="#置顶文章" class="headerlink" title="置顶文章"></a>置顶文章</h3><p>可以使用 <code>npx hexo new post "文章标题"</code>​ 创建文章，也可以直接在 <code>source/_posts</code>​ 中新建 <code>[router].md</code>​，然后手动填充如下头部信息</p><ul><li>title 是文章标题</li><li>sticky 是置顶配置，普通文章不需要，sticky 越大越靠前（这个点在文档里边没找到，翻了源代码才确认这个逻辑，所以单独拧出来提一下）</li><li>date 是选填的，默认会取文件的创建时间，不过我对这个系统维护的文件时间不太信任，不嫌麻烦还是自己硬编码一下比较好</li></ul><figure class="highlight md"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs md">---<br>title: 欢迎使用 LiberSnora<br>sticky: 1<br><span class="hljs-section">date: "2025-01-26"</span><br><span class="hljs-section">---</span><br></code></pre></td></tr></tbody></table></figure><p>而在 next 主题中，设置了 sticky 的文章会被完整的展示到首页，所以特别适合把项目情况、愿景等重要信息放到这里，就像下面这样。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-hexo-website/image-20250127160200-ymig4x9.png" alt="image" data-caption="image" loading="lazy">​</p><blockquote><p>其他发版 blog、项目进展 blog 就按照同样的方法创建文章，只不过不需要配置 sticky</p></blockquote><p>所有配置都完成后，再次提交到 github 并等待 actions 完成，整个网站就部署完了，还可以随个人喜好，加入评论系统、本地搜索、聊天等等功能，可以跟随 Next 主题文档按需配置，<a href="https://theme-next.js.org/docs/third-party-services/comments">Comment System</a>。</p><p>如果考虑国内用户访问更加友好，或者希望从 WordPress 导入文章，以及希望加上文章分类、标签等功能，以及展示的更个性一点，也可以参考我的个人博客搭建总结<a href="https://blog.wj2015.com/2022/05/08/hexo%E5%8D%9A%E5%AE%A2%E8%BF%81%E7%A7%BB/">《Hexo 博客迁移》</a></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>现在的静态网站搭建工具很完善，搭一个静态网站的时间成本很低，虽然我梳理需求、找主题、准备内容、调整模板等等事情加起来搞了一下午，但单纯上一个自己的个人站确实可以 10分钟左右搞定，Enjoy！</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h1 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot;</summary>
        
      
    
    
    
    <category term="GithubAction" scheme="https://blog.wj2015.com/tags/GithubAction/"/>
    
    
    <category term="Linux" scheme="https://blog.wj2015.com/categories/Linux/"/>
    
    <category term="hexo" scheme="https://blog.wj2015.com/tags/hexo/"/>
    
    <category term="网站搭建" scheme="https://blog.wj2015.com/tags/%E7%BD%91%E7%AB%99%E6%90%AD%E5%BB%BA/"/>
    
  </entry>
  
  <entry>
    <title>我的2024年记-续走 AI 路及新生命的诞生</title>
    <link href="https://blog.wj2015.com/2025/01/05/%E6%88%91%E7%9A%842024%E5%B9%B4%E8%AE%B0-%E7%BB%AD%E8%B5%B0-AI-%E8%B7%AF%E5%8F%8A%E6%96%B0%E7%94%9F%E5%91%BD%E7%9A%84%E8%AF%9E%E7%94%9F/"/>
    <id>https://blog.wj2015.com/2025/01/05/%E6%88%91%E7%9A%842024%E5%B9%B4%E8%AE%B0-%E7%BB%AD%E8%B5%B0-AI-%E8%B7%AF%E5%8F%8A%E6%96%B0%E7%94%9F%E5%91%BD%E7%9A%84%E8%AF%9E%E7%94%9F/</id>
    <published>2025-01-05T09:37:47.000Z</published>
    <updated>2026-08-12T03:28:46.856Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>距我上次写年终总结已经快四年了，上一次写总结<a href="https://blog.wj2015.com/2021/02/06/%E6%88%91%E7%9A%842020%E5%B9%B4%E8%AE%B0-%E5%A4%A7%E6%B5%AA%E6%BD%AE%E4%B8%8B%E7%9A%84%E5%B0%8F%E5%B0%8F%E6%89%93%E5%B7%A5%E4%BA%BA/">《我的2020年记-大浪潮下的小小打工人》</a>的时候，疫情还在肆虐，而现在疫情离我们远去已经有两年了。</p><p>跟 2020 年的有一个共性是，我的工作和生活都有了较大的变化。</p><p>比如 2020 那年，我因为疫情换了工作，而 2024 这年，又因为个人发展和大环境寒冬换了工作；</p><p>2020 那年，我跟我老婆还在见家长的阶段，而现在我俩自己已成了家长；</p><h2 id="AI-对我的影响"><a href="#AI-对我的影响" class="headerlink" title="AI 对我的影响"></a>AI 对我的影响</h2><p>这两年对互联网、对个人开发者冲击最大的产品当属大语言模型了，上家公司因为部分产品比较有 AI 基因，加之老板对 AI 相关领域比较敏锐，所以大概在 22 年中 Stable Diffusion 面世之后就对 AI 绘画大模型有所研究，做了一款 AI 头像生成类的产品，期间对<strong>模型部署、微调、运维方面又得到了更多的了解</strong>。</p><p>而 24 年五月换工作之后，工作内容同样专注于大模型应用上，这一篇博客 <a href="https://blog.wj2015.com/2024/08/01/%E5%BB%BA%E7%AB%8B%E5%A4%A7%E6%A8%A1%E5%9E%8B%E7%9A%84%E6%84%9F%E6%80%A7%E8%AE%A4%E7%9F%A5/">《建立大模型的感性认知》</a>就是用于入职后一次周会分享的，<strong>不过个人觉得目前的大模型局限有很多，只能作为辅助工具去使用，更多实现细节、架构、逻辑是否合理需要靠人类去辨别，真正解决问题的能力和技术视野是目前的 AI 无法替代的。</strong></p><h3 id="编程效率的大提升"><a href="#编程效率的大提升" class="headerlink" title="编程效率的大提升"></a>编程效率的大提升</h3><p>大概 22 年底 ChatGPT-3.5 发布后，用 GPT 辅助编程就成了日常工作流的一部分。</p><p>不过当时因为模型能力过于孱弱，生成的代码着实没有自己生成的好，只能给他少部分函数编写的任务和查询资料的任务，并且在业余时间思考了一下 AI 带来的高效率工具下，有什么可能的、有前景的方向和机会，整理了一篇博客 <a href="https://blog.wj2015.com/2023/04/05/%E5%9F%BA%E4%BA%8EChatGPT%E6%81%90%E6%80%96%E8%83%BD%E5%8A%9B%E7%9A%84%E9%AB%98%E6%95%88%E7%8E%87%E5%B7%A5%E5%85%B7%E6%B6%8C%E7%8E%B0%E6%84%8F%E5%91%B3%E7%9D%80%E4%BB%80%E4%B9%88/">《基于ChatGPT恐怖能力的高效率工具涌现意味着什么》</a>。</p><h4 id="Cursor-带来的高效率编程体验"><a href="#Cursor-带来的高效率编程体验" class="headerlink" title="Cursor 带来的高效率编程体验"></a>Cursor 带来的高效率编程体验</h4><p>再往后，Cursor 这个辅助编程产品出来了，带 codebase 和代码并支持片段引用增强了代码生成的效果，我发现可以把更多的功能交给 AI 实现了。</p><p>比如当时我为了公司新产品正在学习 NextJS + ReactJS，但当时我对 React 并不熟悉，而 AI 在新技术入门上对我帮助最大的是，我问它“实现 XX 功能有什么方法和建议” 后，拿到关键信息继续索引资料就可以实现快速上手。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/query-gpt.png" alt="image" data-caption="image" loading="lazy">​</p><p>以及在 Cursor 的帮助下，可以对一大段代码逻辑进行重写或新增，而在等待生成结果的时候就可以继续思考下一步要做什么？以及整体结构的规划是什么？</p><p>在这样的节奏和状态下，业务逻辑实现的思路是不会被代码实现细节所打断的，只需要等待生成完毕后再校对一下逻辑，或者让 GPT 对关键逻辑写几段测试用例保证输入输出正常即可，省去了很多编码的工作量。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104234333-56u6ipy.png" alt="image" data-caption="image" loading="lazy">​</p><p>所以 AI 对我整体能力的广度和速度的加持是成倍的，可以让我在不熟悉 Bash 的情况下写出很多有用高效的运维脚本、可以让我快速上手一门本来一无所知的新技术，还可以让我在编写业务逻辑的时候更加的流畅和快乐。</p><h4 id="高效解决定制需求"><a href="#高效解决定制需求" class="headerlink" title="高效解决定制需求"></a>高效解决定制需求</h4><p>最近一个对我非常有帮助的案例是：用 Cursor + Deepseek V3 在三天内开发完了一个“<strong>有声书字幕翻译、自动总结标题</strong>”的开源项目 <a href="https://github.com/LiberSonora/LiberSonora">github.com/LiberSonora/LiberSonora</a>。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104182650-jzsdjcq.png" alt="image" data-caption="image" loading="lazy">​</p><p>事情的起因是我在搬家后离地铁站点较远，所以网上下载了一些有声读物，而这些有声读物大多是没有字幕和有意义的标题的，就无法通过标题和内容反向定位想听的部分。</p><p>而开发的这个工具支持语音识别自动字幕生成、双语字幕、文本纠错、自动标题生成、批量重命名、手动编辑结果等等功能，放到网易云音乐中实机体验了一下感觉还可以。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104183235-wlk5uhz.png" alt="image" data-caption="image" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104183218-4z11h0e.png" alt="image" data-caption="image" loading="lazy">​</p><p>实现的时候，采用了开源的 AI 语音识别框架 <a href="https://github.com/modelscope/FunASR">FunASR</a> + 音频降噪服务 <a href="https://github.com/modelscope/ClearerVoice-Studio">ClearerVoice-Studio</a> + 开源大模型 Qwen2.5-7B 和运行本地大模型的服务框架 <a href="https://github.com/ollama/ollama">ollama</a> + <a href="https://streamlit.io/">streamlit.io</a> 做了一个自动识别字幕的工具，并且根据字幕内容自动总结标题，这个项目超过 95%+ 的代码、Logo、README 都是 Cursor 帮我做的，实现效果我个人觉得还挺符合预期的，大家如果也有类似的需求都可以参与或提出意见，<a href="https://github.com/LiberSonora/LiberSonora/discussions">github.com/LiberSonora/LiberSonora/discussions</a>。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104182822-idxbqer.png" alt="image" data-caption="image" loading="lazy">​</p><h2 id="工作"><a href="#工作" class="headerlink" title="工作"></a>工作</h2><p>在工作上，第一季度虽然一直有在尝试寻找新机会，但老东家的 AI 项目还是在不断推进的，推进过程本身就是一个很好的学习机会，很大程度提升了我对大模型、AI 编程相关的理解。</p><p>五月中旬入职了新公司英语流利说，主要开发语言也从 Typescript 后端转为 Golang 后端，依旧从事 AI 应用相关的工作，倒是跟未来期望深耕的方向契合。</p><h3 id="换工作"><a href="#换工作" class="headerlink" title="换工作"></a>换工作</h3><p>上家公司在离职前主要做的产品是订阅制的海外 ChatBot 应用，有好几个分站，做的较好的一个分站每天能有 2w 的独立访问，同类型产品里边看了看算不错。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20240402223149-ftj04ip.png" alt="image" data-caption="image" loading="lazy">​</p><p>老板对 AI 和新技术很敏锐，也给了我很大的帮助，整个工作氛围也不沉重。因为公司裁了几波员人比较少，离职前还简单做了下公司的总成本估计和总收入估计，如果保持当时产品的势头，大概再跑两个月就能盈亏平衡了。</p><p>不过最后考虑到个人发展、未来生活节奏、项目前景等等很多问题，恕不能细聊😁，最终还是决定溜了。</p><h3 id="求职是挺冷"><a href="#求职是挺冷" class="headerlink" title="求职是挺冷"></a>求职是挺冷</h3><p>虽然我是在 2024 年五月才正式开溜，但相关的准备早在 2023 年底就开始了。求职时我觉得 AI 会是下一个重要的应用方向，所以求职目标就是后端+AI应用的岗位，而具体的后端语言倒没有太限定，可以是 Golang，可以是 Node、Python 等等。因为当时已经可以在 AI 辅助下忽视一些技术栈的差异，更加专注于业务逻辑的思考了，而个人认为在不远的未来这个趋势会越来越显著。</p><p>博主毕业于一家二本院校，无大厂经历，从 23 年底开始求职面了快 20 家大大小小的公司，有几个感受：</p><ul><li>年底的刚需岗位给的面试机会更多，年初反而由于求职竞争激烈，招聘的公司翻来覆去就那几家，有时候简历投出去半个月才有信，直到 24 年四月才有所好转</li><li>要求经验匹配性高的企业比较多，大多是刚需岗，希望入职后无缝衔接前任工作</li><li>薪资不太好谈，到谈薪环节压薪现象严重，不少平薪和降薪的岗位</li></ul><p>最终选择了入职英语流利说这家公司，虽然入职后从家庭角度考虑，搬家到离公司一个半小时行程的青浦区去了，但整体来说还是比较适合我的，主要的编程语言从 Typescript 切到了 Golang，在 AI 的帮助下适应的还挺顺利。</p><h2 id="个人提升"><a href="#个人提升" class="headerlink" title="个人提升"></a>个人提升</h2><h3 id="社区贡献"><a href="#社区贡献" class="headerlink" title="社区贡献"></a>社区贡献</h3><p>24 年更多忙于自己的找工作、照顾怀孕的老婆，开源贡献上明显少了很多。我在 23 年还给 AuthJS、Flowise（Langchain 的可视化工作流项目）改了一些 BUG 贡献了一些代码。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104191636-cp7rzap.png" alt="image" data-caption="image" loading="lazy">​</p><p>到了 24 年，除了元旦前三天肝出来的“<strong>有声书字幕识别和自动标题</strong>”项目 <a href="https://github.com/LiberSonora/LiberSonora">github.com/LiberSonora/LiberSonora</a> 外，其他贡献就几乎没有了，commit 图比脸还干净。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104191707-z2sioxw.png" alt="image" data-caption="image" loading="lazy">​</p><p>未来也许会基于有声书的场景继续扩展，学一下 Flutter，能写自己的 APP 并上线，做一枚真正的全干工程师~</p><p>这个开源项目上线后，给我下载有声书的网站 <a href="https://www.yigushi.com/">第一故事</a> 的站长去了一封邮件，把 AI 转换过的几本有声书同步给了他，看到我的贡献被认可并出现在网站上造福更多人的时候真的非常开心。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104200334-c8mo7u0.png" alt="image" data-caption="image" loading="lazy">​</p><p><a href="https://www.yigushi.com/youshengxiaoshuo/1692.html">有声书马克思《资本论》MP3免费打包下载 276集</a>（其中之一）</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104200515-ntqtodf.png" alt="image" data-caption="image" loading="lazy">​</p><h3 id="技术领域的提升"><a href="#技术领域的提升" class="headerlink" title="技术领域的提升"></a>技术领域的提升</h3><p>2024 年主要是继续在 AI 和大模型领域继续积累经验：</p><ul><li><p>用 <a href="https://huggingface.co/docs/text-generation-inference/index">text-generation-inference</a> 给前公司做了私有模型部署相关的运维工作。</p></li><li><p>做了一个内部用的大模型测试框架，定量研究在不同的模型量化参数、推理参数下，哪个组合的效果最佳。</p></li><li><p>研究大模型原理，关注最新动态，现在正在尝试跟上社区步伐，微调出适配特定领域的大模型</p></li></ul><p>开发领域中：</p><ul><li>工作里实际应用了 ReactJS + NuxtJS，说实话不太好用，特别 NuxtJS 的中间件等功能，因为要跟 Vercel 部署深度嵌入，灵活性太低了。</li><li>后来自己做前后端大一统的项目改用了 Vue + NextJS，虽然它也不完美，依旧有非常多非常多的坑要踩，但被钳制的感觉没有那么明显。</li><li>更深入研究了 Golang 技术栈以适应新工作的需要。</li></ul><h2 id="个人生活"><a href="#个人生活" class="headerlink" title="个人生活"></a>个人生活</h2><h3 id="升级有娃一族"><a href="#升级有娃一族" class="headerlink" title="升级有娃一族"></a>升级有娃一族</h3><p>在我跟老婆结婚差不多一周年、恋爱五周年的时候，冒出来了要孩子的想法。虽然身边未婚、未育、丁克的朋友不少，家里也没怎么催，我们当然也可以多享受几年快活日子。</p><p>不过对于<strong>双沪飘不打算丁克不打算有人全职带娃的打工人家庭来说，这样的家庭情况几乎必定要靠上一辈的全力帮助才可以比较顺利的延续后代</strong>（用资本主义的话讲叫“劳动力的再生产”，但对个人来说也是一种情感上的需要），而在感情状况、经济情况允许的情况下，在自己年纪不算大，父母年纪不算大的时候生育对我们来说也是一个不错的选择了。</p><p>现在还记得 2024年 2 月24号那天，正值元宵佳节，是检查到宝宝痕迹的第一天，我老婆前一天晚上跟我一起去买了登山鞋，元宵那天特别想去龙王山赏雪，看到两道杠我老婆都快哭出来了。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/f104953727789ca44d4d5530a39ed52-20240224112741-zhbpkxq.jpg" alt="f104953727789ca44d4d5530a39ed52" data-caption="f104953727789ca44d4d5530a39ed52" loading="lazy">​</p><p>然后就是选定医院去定期产检，这边情况是非高龄产妇无基础病，所以就近找了上海长妇幼，一家老牌的妇幼保健院，第一次去愚园路那边的时候是春天，还挺漂亮的，但不知道是不是龙年的原因，检查的人确实很多。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/497ac0284b50b7d6daaaf85cd5c7296-20240316204849-24w29xk.jpg" alt="497ac0284b50b7d6daaaf85cd5c7296" data-caption="497ac0284b50b7d6daaaf85cd5c7296" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/f9df5b8d5d22a58ed89073a9cedb150-20240316204755-umtjzj3.jpg" alt="f9df5b8d5d22a58ed89073a9cedb150" data-caption="f9df5b8d5d22a58ed89073a9cedb150" loading="lazy">​</p><p>我老婆说他第一次查到心跳、通过 B 超看到宝宝的时候，边听边哭。再后来慢慢地，肚子越来越大，行动也愈发不便起来。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/MVIMG_20241020_174612_1-20241023013823-jr1kghq.jpg" alt="MVIMG_20241020_174612_1" data-caption="MVIMG_20241020_174612_1" loading="lazy">​</p><p>特别头胎开指慢，足月之后阵痛了好几天，下午两点到医院开 0.5 指，然后一直痛到第二天凌晨3点才开两指，整个过程真的很疼，只能硬抓着她的手尽量转移注意力。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/MVIMG_20241026_030010-20241026030843-olwf50y.jpg" alt="MVIMG_20241026_030010" data-caption="MVIMG_20241026_030010" loading="lazy">​</p><p>三点打无痛之后宫缩就慢慢小了，睡了一会儿恢复体力，直到凌晨五六点推进产房接生，在护士小姐的帮助下顺利的生产了，全程陪产看着老婆的努力，真的太不容易了。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/MVIMG_20241026_050849-20241026050906-j4pkzjx.jpg" alt="MVIMG_20241026_050849" data-caption="MVIMG_20241026_050849" loading="lazy">​</p><p>住院的那几天由于产后出血，我老婆整体是很虚弱的状态，想母乳但喂奶翻身都没力气。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/MVIMG_20241027_061447-20241027061911-te1z68g.jpg" alt="MVIMG_20241027_061447" data-caption="MVIMG_20241027_061447" loading="lazy">​</p><p>住院部规定只能进一位陪护，那这个人一定得是我，责无旁贷。陪护时的节奏基本是两小时醒一次，醒来之后忙活半小时到一小时，比如凌晨两点起来喂奶、换尿布、哄睡、清洗用具到凌晨三点；然后五点又起来喂奶、换尿布，迎接护士小姐的健康检查，卫生检查等等。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104230228-v02wsmy.png" alt="image" data-caption="image" loading="lazy">​</p><p>住院陪护两天下来，感觉整个人都是飘的，加上一直流汗味道也不太好，但在护士小姐的帮组下快速的熟悉了带娃的各个环节和流程，学会了喂奶，脐带消毒，以及各种抱法，竖抱、斜抱、哺乳抱、反哺乳抱、飞机抱，后面听我老婆说护士夸我是这边学的最快的爸爸哈哈哈！！！</p><p>出院前一天让家长替了我一晚上，回家快速收拾了一下自己并给老婆预定了一大束花，以宣告这个家庭全新生活的开始！</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250105162423-ohb3h6i.png" alt="image" data-caption="image" loading="lazy">​</p><h3 id="健康"><a href="#健康" class="headerlink" title="健康"></a>健康</h3><p>大家一定注意个人健康，我可能是长期不太爱喝水，今年痛风犯了两次，尿酸高的吃药都降不下去，第一次发作是换工作前大概3月底，自己骑电瓶车蹦到附近的医院的，急性期都有 543 的尿酸，很高了。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104224101-z0vk11a.png" alt="image" data-caption="image" loading="lazy">​</p><p>吃了俩月的非布司他之后一直没去复查，7月的时候又发作了一次，检查出来急性期七百多。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104225340-r9ufq9x.png" alt="image" data-caption="image" loading="lazy">​</p><p>然后医生又开了非布司他，继续哐哐吃药，到后来宝宝都出生了，想着回归工作前再去社区医院复查一下，期间饮水量也还正常，结果一查尿酸六百多了，医生还给开非布司他让继续吃。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104225735-8r1xna5.png" alt="image" data-caption="image" loading="lazy">​</p><p>总感觉要么药有问题，要么治疗方向有问题，现在换个方向看中医试试，中医给到的诊断是肝肾两虚，阴虚火旺，尝试吃一个月中药调理，才开始，看啥时候见效。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104225605-7we52it.png" alt="image" data-caption="image" loading="lazy">​</p><h3 id="玩-NAS"><a href="#玩-NAS" class="headerlink" title="玩 NAS"></a>玩 NAS</h3><p>我的 NAS 是在 2023 年 4 月左右捡垃圾捡来的，CPU 是 4600T，显卡是 P104-100 当时打算用来跑 AI 绘画，配件全靠二手捡垃圾，总价 ￥850，性价比贼高。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104232357-zpkospq.png" alt="image" data-caption="image" loading="lazy">​</p><p>但有一说一，当时花 ￥115 买一块儿 G4600T 有点大冤种不够垃圾佬，虽说核显确实不错能硬解 8K 10bit，但毕竟这个主板是可以上 E3V5 的，整一个 E3 1225V5 也就七十块钱，照样带了个能用的核显。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104232722-q5vkfj6.png" alt="image" data-caption="image" loading="lazy">​</p><p>捡的主板和机箱设计都很不合理，换个塔式散热器搞的滴血认亲了，然后我就在笔记中留了这样一条警告，警示自己以后别再折腾这台机器了，要实在性能不够用换台新的。</p><blockquote><p>Warning：闲的没事干一定不要换 CPU 散热器那套东西，因为搞的时候一定要把主板拆下来，内存拆下来，然后把底座的上半部分掀起来，把散热器的一侧挂上去，然后把上半部分扣紧在主板上，然后再扣上扣具，而且风扇一定要提前塞进去，想先扣上了再塞是不可能的。</p></blockquote><p>后面这台 “NAS” 慢慢变成了一台综合的服务器了，跑了四十多个常用服务在上面，还跑了本地大模型和 AI 绘画等重负载的东西，未来还打算让它承担本地模型训练的工作，G4600T 就有点太不够看了。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250105165723-wn9hr7j.png" alt="image" data-caption="image" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250105162756-mcp09r6.png" alt="image" data-caption="image" loading="lazy">​</p><p>所以分别在 6 月、11月的时候买了一堆配件进行升级，留了升级余量，已经有点违背垃圾佬教义了，主要是：</p><ul><li><p>6月 E3 降了一波价，花巨资 ￥180 升到了 E3-1275V5，4核8线程，性能类似 I7-7700 也足够了，内存频率只支持 2333</p></li><li><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104233743-w4dwzic.png" alt="image" data-caption="image" loading="lazy">​</p></li><li><p>11月固态的价格还行，花了 685 买了梵想 S790E 2TB 做系统盘（系统迁移又一波折腾），换完之后系统响应速度快非常多，docker 打包、加载 AI 模型这些场景贼顺畅</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104233817-b7i8hl1.png" alt="image" data-caption="image" loading="lazy">​</p></li><li><p>12月我的好兄弟送了我一块儿 2080TI，原来捡的电源有点带不动了，咸鱼淘了一下结果淘到了翻新的电源，怕炸于是去买了全新鑫谷 G850W，花了 ￥400，备着后面战未来。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104233848-r7mtqqk.png" alt="image" data-caption="image" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104233802-n6r69yw.png" alt="image" data-caption="image" loading="lazy">​</p></li></ul><p>新 CPU E3V5 的 TDP 确实高了些，但待机功耗其实并未上去；压测6分钟，温度 65度 3.7GHz 未降频，满载104w的功耗，平时所有硬件加一起 38w 左右，反而比 G4600T 更低。</p><p>12 月的时候因为折腾本地大模型应用，有时候需要用移动设备访问内网服务器上的服务，但有些能力特性只能 HTTPS 下才可以运行，所以就折腾了一下内网批量 HTTPS 的方案。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250105165804-e8rfzyl.png" alt="image" data-caption="image" loading="lazy">​</p><p>通过这套方案，所有的内网网站都有一个自己的子域，并通过同一个自动续期的泛签名证书实现 HTTPS，具体过程整理为了博客 <a href="https://blog.wj2015.com/2024/12/01/%E7%BB%99-NAS-%E5%86%85%E7%BD%91%E6%9C%8D%E5%8A%A1%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E5%85%8D%E8%B4%B9-HTTPS/">《给 NAS 内网服务自动配置免费 HTTPS》</a>。</p><h3 id="PS5-Switch"><a href="#PS5-Switch" class="headerlink" title="PS5+Switch"></a>PS5+Switch</h3><p>很开心的一点是，老婆理解并支持我通过玩游戏的方式放松娱乐，她也知道我想买游戏机已经很久了，所以在 23 年底双十一的时候送了我一台 PS5，版本和店铺是自己选的，京东数码海外旗舰店，日版 ￥2995。</p><blockquote><p>附图：你怎么知道我老婆给我买 PS5 、 Switch 还有 4K 显示器.jpg</p></blockquote><p>时间点我记得特别清楚，PS5 到货的那天是周五，我老婆有事要回老家，而我加班上线到凌晨12点多才回去。虽然回去挺累了，但我还是折腾一通，玩到凌晨一点多才放手。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104235307-pc5h8lg.png" alt="image" data-caption="image" loading="lazy">​</p><p>后来还写了一篇博客记录这个过程《<a href="https://blog.wj2015.com/2023/12/24/%E5%BF%83%E5%BF%83%E5%BF%B5%E4%BA%863%E5%B9%B4%E7%9A%84XSX%EF%BC%8C%E6%9C%80%E7%BB%88%E8%BF%98%E6%98%AF%E9%80%89%E4%BA%86PS5/">心心念了3年的XSX，最终还是选了PS5</a>》</p><p>回想一下今年玩了些啥，Switch 几乎完全吃灰，偶尔拉出来玩玩健身环，其他都是在 PS5 上玩的。</p><p><strong>黑神话悟空：</strong></p><p>一周目通关，而且是在带娃期间通关（娃哄睡了我不睡，家务干完可以黑猴启动了），买了豪华版支持国产，虽然打小黄龙、王神官有点受罪，但整体玩的很开心，美爆了</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/%E9%BB%91%E7%A5%9E%E8%A9%B1%EF%BC%9A%E6%82%9F%E7%A9%BA_20241106110416-20250105145220-tx07bc4.jpg" alt="黑神話：悟空_20241106110416" data-caption="黑神話：悟空_20241106110416" loading="lazy">​</p><blockquote><p>热梗：我来助你.jpg</p></blockquote><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/%E9%BB%91%E7%A5%9E%E8%A9%B1%EF%BC%9A%E6%82%9F%E7%A9%BA_20240916214807-20250105145306-cpyotwt.jpg" alt="黑神話：悟空_20240916214807" data-caption="黑神話：悟空_20240916214807" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/%E9%BB%91%E7%A5%9E%E8%A9%B1%EF%BC%9A%E6%82%9F%E7%A9%BA_20240917145355-20250105145255-3642yas.jpg" alt="黑神話：悟空_20240917145355" data-caption="黑神話：悟空_20240917145355" loading="lazy">​</p><p>‍</p><p><strong>地平线2 西部禁域：</strong></p><p>一周目中，画面表现确实无敌，游戏性也不错，如果能多还原一点脸模的颜值那就更棒了。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/%E5%9C%B0%E5%B9%B3%E7%B7%9A%20%E8%A5%BF%E5%9F%9F%E7%A6%81%E5%9C%B0_20241222205015-20250105151908-7yitxcs.png" alt="地平線 西域禁地_20241222205015" data-caption="地平線 西域禁地_20241222205015" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/%E5%9C%B0%E5%B9%B3%E7%B7%9A%20%E8%A5%BF%E5%9F%9F%E7%A6%81%E5%9C%B0_20241230223748-20250105151958-nixhob3.png" alt="地平線 西域禁地_20241230223748" data-caption="地平線 西域禁地_20241230223748" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/%E5%9C%B0%E5%B9%B3%E7%B7%9A%20%E8%A5%BF%E5%9F%9F%E7%A6%81%E5%9C%B0_20250103224043-20250105152007-myjdh40.png" alt="地平線 西域禁地_20250103224043" data-caption="地平線 西域禁地_20250103224043" loading="lazy">​</p><p><strong>无线暖暖：</strong></p><p>对我来说就是拍照暖暖模拟器，充钱是不会充钱的，让我拍照就行</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250105173307-lkdl79d.png" alt="image" data-caption="image" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250105173255-54m07uq.png" alt="image" data-caption="image" loading="lazy">​​​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250105152458-pl6dmer.png" alt="image" data-caption="image" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/%E7%84%A1%E9%99%90%E6%9A%96%E6%9A%96_20241221202014-20250105152709-isxd8b5.png" alt="無限暖暖_20241221202014" data-caption="無限暖暖_20241221202014" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/%E7%84%A1%E9%99%90%E6%9A%96%E6%9A%96_20241226225707-20250105152728-hncyh7e.png" alt="無限暖暖_20241226225707" data-caption="無限暖暖_20241226225707" loading="lazy">​</p><p><strong>战神4：</strong></p><p>又翻出来两周目通关，经典线性游戏，除了女武神比较有难度，其他都是解谜看电影</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/God%20of%20War_20231230145748-20250105152628-esygmkg.jpg" alt="God of War_20231230145748" data-caption="God of War_20231230145748" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/God%20of%20War_20231230142929-20250105152621-axn96o3.jpg" alt="God of War_20231230142929" data-caption="God of War_20231230142929" loading="lazy">​</p><p><strong>星球大战 绝地组织陨落：</strong></p><p>一周目通关，玩完就没啥深刻的印象了，不同的星球飞飞飞，打怪砍小兵，最后打个大 BOSS 结束</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/20240101211524-20250105152916-dvslkmn.jpg" alt="《星際大戰 絕地：組織殞落™》_20240101211524" data-caption="《星際大戰 絕地：組織殞落™》_20240101211524" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/20240120183728-20250105152924-lh0wysj.jpg" alt="《星際大戰 絕地：組織殞落™》_20240120183728" data-caption="《星際大戰 絕地：組織殞落™》_20240120183728" loading="lazy">​</p><p><strong>只狼：</strong></p><p>操作太受虐了，着实有点推不动</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/20241116110747-20250105152937-6drszxq.jpg" alt="Shadows Die Twice_20241116110747" data-caption="Shadows Die Twice_20241116110747" loading="lazy">​</p><p><strong>底特律变人：</strong></p><p>互动电影神作，非常震撼，打通了三队全活结局</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/Detroit_%20Become%20Human_20240317194544-20250105153005-5ei08z9.jpg" alt="Detroit_ Become Human_20240317194544" data-caption="Detroit_ Become Human_20240317194544" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/Detroit_%20Become%20Human_20240324002819-20250105153011-6jdcrdf.jpg" alt="Detroit_ Become Human_20240324002819" data-caption="Detroit_ Become Human_20240324002819" loading="lazy">​</p><p>没玩下去的：</p><p>FARCRY 5，突突突打邪教兴奋不起来</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/Far%20Cry%C2%AE%205_20240413094350-20250105153027-fb52u7z.jpg" alt="Far Cry® 5_20240413094350" data-caption="Far Cry® 5_20240413094350" loading="lazy">​</p><p>CONTROL，带点儿恐怖色彩，战斗不太紧凑，玩不动</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/Control%20Ultimate%20Edition_20240430212743-20250105153056-vkqoewn.jpg" alt="Control Ultimate Edition_20240430212743" data-caption="Control Ultimate Edition_20240430212743" loading="lazy">​</p><p>PREY，玩了一半，孤身一人在太空面临电车难题，挺好玩但略显惊悚</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/Prey_20240127210810-20250105153109-kh5y3s0.jpg" alt="Prey_20240127210810" data-caption="Prey_20240127210810" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/Prey_20240128191422-20250105153115-10q4vde.jpg" alt="Prey_20240128191422" data-caption="Prey_20240128191422" loading="lazy">​</p><h3 id="老车子"><a href="#老车子" class="headerlink" title="老车子"></a>老车子</h3><p>目前开的车子是 21 年左右买的亲戚的二手车，有“修理厂噩梦”之称的“老款红色雪佛兰科鲁兹”，车子平时开着还行，但几乎每年都要折腾那么一次，今年尤甚，久病成医，我都快熟悉里边的易损部件了。</p><p>时间线大概是这样的：</p><ul><li>23 年十一路上，发动机窜油咔咔冒白烟要大修，当时有点儿报废的想法，但身处异地而且过节返乡事情本来就多，处理起来太麻烦了，硬着头皮还是修了，花了5k，开着修车老板的钓鱼车继续行程</li><li>24 年初，大概四月份，稳妥起见让我老婆开车上下班，结果地下车库倒车的时候变速箱打齿直接动不了了，换了个手动挡变速箱和其他关联部件花了快3k，修完换挡还是卡，再换了个换挡机构好转了。</li><li>24年中大概 9 月，天气可热，有次出门车里空调开猛了把压缩机烧了，开着窗回到家，停好车发现车窗摇不上去了，去换了压缩机和车窗升降器，又花了小一千</li></ul><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104210802-2qjmyqv.png" alt="image" data-caption="image" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104205140-5kxtp4b.png" alt="image" data-caption="image" loading="lazy">​​​</p><p>我们倒不是完全没买新车的预算，主要未来自驾出行频率又不高，每次车子坏了都跟我老婆都从性价比出发考虑，修好兴许能再管三年，比起买新车保费+买车的费用加起来还是低的，虽然最后一点点投入进去之后发现修车的成本也不低了😭</p><p>不过俩大件大修完之后，觉得这车子又能行了，应该还能再陪我们几年，到时候根据最新需求再换~</p><h3 id="生活点滴"><a href="#生活点滴" class="headerlink" title="生活点滴"></a>生活点滴</h3><p>2月初老婆的发小结婚，我去客串了一下婚礼摄影师，网上找了教程、注意事项，婚礼当天咔咔的抓拍，64GB 的内存卡都给塞满了，最终反馈还不错，这手艺咋说都得值他三五百哈哈（隐私关系，只发侧脸）。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104201145-ybw37vc.png" alt="image" data-caption="image" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/IMG_3250-20250104200947-o38jhvr.jpg" alt="IMG_3250" data-caption="IMG_3250" loading="lazy">​</p><p>2月底回到上海后，去了松江那边辰山植物园赏樱很不错</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/20240331210420-20250104211117-odkuq36.jpg" alt="20240331210420" data-caption="20240331210420" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/IMG_3564-20250104211221-t8alt8o.jpg" alt="IMG_3564" data-caption="IMG_3564" loading="lazy">​</p><p>4月去了绍兴的石井水库</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104211718-u05cfwz.png" alt="image" data-caption="image" loading="lazy">​</p><p>4月还去看了上海博物馆看了三星堆展览，很是震撼古人的巧思</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/MVIMG_20240406_140015-20240406231313-vtgok3h.jpg" alt="MVIMG_20240406_140015" data-caption="MVIMG_20240406_140015" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/MVIMG_20240406_152827-20240406231514-4ix8vqk.jpg" alt="MVIMG_20240406_152827" data-caption="MVIMG_20240406_152827" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/MVIMG_20240406_152620-20240406231609-0len3qx.jpg" alt="MVIMG_20240406_152620" data-caption="MVIMG_20240406_152620" loading="lazy">​</p><p>5月去了海昌海洋公园，天气挺热，去看了大蓝鲸表演</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/IMG_5419-20250104211816-w5deha4.jpg" alt="IMG_5419" data-caption="IMG_5419" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/IMG_5503-20250104211846-nsr17gr.jpg" alt="IMG_5503" data-caption="IMG_5503" loading="lazy">​</p><p>5月赶上换工作的 GAP，老婆孕中期也属于比较稳定的时候，抓住机会自己去了趟厦门看海，逛了厦大、鼓浪屿。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104234821-7244b6i.png" alt="image" data-caption="image" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104234812-v8jvsne.png" alt="image" data-caption="image" loading="lazy">​</p><p>6月端午节有假期，跟朋友一起去崇明玩了一趟，东平国家森林公园挺值得逛的</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20240611233909-u3wiqot.png" alt="image" data-caption="image" loading="lazy">​​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/IMG_6057-20250104212151-r8eh1bb.jpg" alt="IMG_6057" data-caption="IMG_6057" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/IMG_6008-20250104212425-xy7xjj9.jpg" alt="IMG_6008" data-caption="IMG_6008" loading="lazy">​</p><p>就是有些设施老化之后，看着有点恐怖片</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20240611233935-44zzgdi.png" alt="image" data-caption="image" loading="lazy"></p><p>6月底，考虑到未来带娃的生活以及家长过来的居住便利性，放弃了快排到的市筹公租房（上海市筹是真难排，排了三年多才轮到），在老婆公司附近找了个大房东两室，还挺适合我们的，就是离我上班着实有点远，单程一个半小时。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104221551-t9oqhna.png" alt="image" data-caption="image" loading="lazy">​</p><p>7月回老家参加奶奶的百岁寿宴</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/MVIMG_20240714_121907-20250104212924-lbbjoq5.jpg" alt="MVIMG_20240714_121907" data-caption="MVIMG_20240714_121907" loading="lazy">​</p><p>9月已经比较显怀了，中秋那天去淀山湖赏月，人还挺多的，休闲的走了一走</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20240918111941-tl1cyui.png" alt="image" data-caption="image" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20240918112110-xmsfu88.png" alt="IMG_6855" data-caption="IMG_6855" loading="lazy">​</p><p>10月，宝宝出生了，之后全家人的目光都投向了这个小可爱，在家长的帮助和老婆的共同努力下，我也慢慢的回到工作节奏中去了。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-2024-summary/image-20250104213305-e50dsor.png" alt="image" data-caption="image" loading="lazy">​</p><p>24 年由于年初开始就有宝宝，就没有像 23 年那样到处徒步爬山、逛园林，很少有中长途的旅行，不过这也是多彩多变生活的一部分，不会总是激昂的高山流水，也有静好的清风明月。</p><h2 id="未来"><a href="#未来" class="headerlink" title="未来"></a>未来</h2><p>2025 在生活上打算继续做好父母，学习更多育儿知识，并保持身体健康给娃做好榜样；工作和职业发展上，在 AI 和大模型应用领域更加深入，有时间更多的参与开源社区建设。</p><p>祝大家 2025 新年快乐，身体健康，共勉~</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;距我上次写年终总结已经快四年了，上一次写总结&lt;a</summary>
        
      
    
    
    
    <category term="2024" scheme="https://blog.wj2015.com/tags/2024/"/>
    
    
    <category term="NAS" scheme="https://blog.wj2015.com/tags/NAS/"/>
    
    <category term="总结" scheme="https://blog.wj2015.com/tags/%E6%80%BB%E7%BB%93/"/>
    
    <category term="旅行" scheme="https://blog.wj2015.com/tags/%E6%97%85%E8%A1%8C/"/>
    
    <category term="随笔" scheme="https://blog.wj2015.com/categories/%E9%9A%8F%E7%AC%94/"/>
    
  </entry>
  
  <entry>
    <title>给 NAS 内网服务自动配置免费 HTTPS</title>
    <link href="https://blog.wj2015.com/2024/12/01/%E7%BB%99-NAS-%E5%86%85%E7%BD%91%E6%9C%8D%E5%8A%A1%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E5%85%8D%E8%B4%B9-HTTPS/"/>
    <id>https://blog.wj2015.com/2024/12/01/%E7%BB%99-NAS-%E5%86%85%E7%BD%91%E6%9C%8D%E5%8A%A1%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E5%85%8D%E8%B4%B9-HTTPS/</id>
    <published>2024-12-01T08:51:17.000Z</published>
    <updated>2026-08-12T03:28:46.860Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>对于一个喜欢折腾的人来说，NAS 上总会有各种各样的服务，有用 docker 跑的服务，有用系统服务运行的服务，他们大都挂在一个端口上，例如 <code>http://192.168.133.5:8190</code>​，时间久了自己都不记哪个端口对应哪个服务，所以一般还会做一个导航页作为统一入口，维护所有服务。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201134715-bordaxf.png" alt="image" data-caption="image" loading="lazy">​</p><p>如果能用项目名称 + 域名的方式访问网站，那看起来会优雅很多（比如 <code>https://open-webui.internal.wj2015.com</code>​），以及有些服务需要用到浏览器的最新能力，比如录音、视频等，而这些能力需要在安全的 HTTPS 下才可以被正常使用（<a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia">MDN-getUserMedia()</a>），所以本文会介绍博主的诸多本地服务如何批量且自动的加上 HTTPS 和子域名。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201135143-4kwcfi1.png" alt="image" data-caption="image" loading="lazy">​</p><blockquote><p>顺口吐槽一句，现在云厂商可以申请的免费证书都不超过3个月，单独购买证书又贵的一匹，同样可以用下面提到的证书申请方案解决。</p></blockquote><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><p>需要的资源如下：</p><ul><li>首先需要有一个域名，域名可以在腾讯云、阿里云、百度云等有域名购买入口的云服务提供商那边买，域名会解析到内网服务，所以 <strong>域名无需备案</strong>。</li><li>需要一台 NAS 或 Linux 服务器，arm 或 x86 架构都可以，重点是需要支持 docker，博主的 NAS 是捡垃圾拼出来的组装机，装了 ubuntu 22.04 系统配合 docker 提供服务，从原理上看群辉、飞牛这些支持运行 docker 容器的 NAS 专用系统也可以。</li></ul><blockquote><p>注意：做 HTTPS 这里域名一定要购买，不能只在内网 DNS 服务器做域名解析，因为申请证书时需要做 DNS 验证</p></blockquote><h2 id="整体思路"><a href="#整体思路" class="headerlink" title="整体思路"></a>整体思路</h2><p>大概分为如下几个步骤：</p><ul><li>将内网 IP 固定下来，可通过路由器自带的 MAC 绑定功能实现，比如固定为 192.168.133.5</li><li>把域名的一个子域使用 DNS 泛解析的方式解析到内网 IP 上，比如把 <code> *.internal.wj2015.com</code>​ 解析到 192.168.133.5</li><li>运行 certd 服务和 traefik 服务并做配置</li><li>给需要的服务动态添加 traefik 代理配置</li></ul><h2 id="具体操作"><a href="#具体操作" class="headerlink" title="具体操作"></a>具体操作</h2><h3 id="固定内网-IP"><a href="#固定内网-IP" class="headerlink" title="固定内网 IP"></a>固定内网 IP</h3><p>博主家的路由器是小米 AX3000T，而博主的 NAS 机器是挂在小米路由器下的，所以家里网络的 DHCP 在小米路由器中配置。</p><p>如果你的 DHCP 服务跑在做软路由上，需要在软路由的 DHCP 中找到 DHCP 静态 IP 分配的配置。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201141543-1rs9qk4.png" alt="image" data-caption="image" loading="lazy">​</p><p>添加一个这样的绑定即可。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201141817-gjmpaoi.png" alt="image" data-caption="image" loading="lazy">​</p><blockquote><p>注：如果绑定的 IP 和 NAS 现有 IP 不一样，可以重启 NAS 或重新获取 IP。</p></blockquote><p>配置完能 ping 通即可</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201143039-8p1glgo.png" alt="image" data-caption="image" loading="lazy">​</p><h3 id="添加泛解析"><a href="#添加泛解析" class="headerlink" title="添加泛解析"></a>添加泛解析</h3><p>博主域名是在腾讯云买的，所以进入 <a href="https://console.cloud.tencent.com/domain/all-domain/all">域名注册-控制台</a>，添加一条这样的泛解析</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201143328-6xegbtg.png" alt="image" data-caption="image" loading="lazy">​</p><p>本地可用 <code>nslookup</code>​ 或者 <code>ping</code>​ 命令确保解析正确，一般配完就生效，不会超过配置的 600s TTL。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201143423-fhxiesc.png" alt="image" data-caption="image" loading="lazy">​</p><h3 id="运行容器"><a href="#运行容器" class="headerlink" title="运行容器"></a>运行容器</h3><p>这里用 docker-compose 进行部署（linux 环境下可参考 <a href="https://docs.docker.com/compose/install/linux/">docs.docker.com/compose/install/linux/</a> 进行 docker-compose 的安装），docker-compose 配置中的所有属性都可以对应到群辉或飞牛系统的 docker 控制台。</p><blockquote><p>注：这个 docker-compose 包含三个应用</p><ul><li>一个是 alist，一个网盘挂载服务，这里用来测试，随后可以通过 alist.internal.wj2015.com</li><li>一个是 traefik，反向代理服务，后续会细讲</li><li>一个是 certd，HTTPS 证书申请服务，后续会细讲</li></ul></blockquote><figure class="highlight yaml"><table><tbody><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><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.8"</span><br><span class="hljs-attr">services:</span><br>  <span class="hljs-attr">alist:</span><br>    <span class="hljs-attr">image:</span> <span class="hljs-string">xhofe/alist-aria2:v3.39.1</span><br>    <span class="hljs-attr">container_name:</span> <span class="hljs-string">alist</span><br>    <span class="hljs-attr">volumes:</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">'./alist:/opt/alist/data'</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">'/mnt/data/:/mnt/data/'</span><br>    <span class="hljs-attr">ports:</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">'5244:5244'</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">'6800:6800'</span><br>    <span class="hljs-attr">environment:</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">PUID=0</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">PGID=0</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">UMASK=022</span><br>    <span class="hljs-attr">restart:</span> <span class="hljs-string">unless-stopped</span><br>  <span class="hljs-attr">traefik:</span><br>    <span class="hljs-attr">image:</span> <span class="hljs-string">traefik:v3.2</span><br>    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span><br>    <span class="hljs-attr">command:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">"--configFile=/etc/traefik/config.yml"</span><br>    <span class="hljs-attr">ports:</span><br>      <span class="hljs-comment"># The HTTP port</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">"80:80"</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">"443:443"</span><br>        <span class="hljs-comment"># The Web UI (enabled by --api.insecure=true)</span><br>        <span class="hljs-comment">#- "8081:8080"</span><br>    <span class="hljs-attr">volumes:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">./traefik/:/etc/traefik/</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">./share/:/etc/share/</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">./traefik-logs/:/var/logs/traefik/</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">/var/run/docker.sock:/var/run/docker.sock</span><br>  <span class="hljs-attr">certd:</span><br>    <span class="hljs-attr">image:</span> <span class="hljs-string">registry.cn-shenzhen.aliyuncs.com/handsfree/certd:1.21.2</span><br>    <span class="hljs-attr">container_name:</span> <span class="hljs-string">certd</span> <span class="hljs-comment"># 容器名</span><br>    <span class="hljs-attr">restart:</span> <span class="hljs-string">unless-stopped</span> <span class="hljs-comment"># 自动重启</span><br>    <span class="hljs-attr">ports:</span> <span class="hljs-comment"># 端口映射</span><br>      <span class="hljs-comment">#  ↓↓↓↓ ---------------------------------------------------------- 如果端口有冲突，可以修改第一个7001为其他不冲突的端口号</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">"7001:7001"</span><br>    <span class="hljs-attr">volumes:</span><br>      <span class="hljs-comment">#   ↓↓↓↓↓ ------------------------------------------------------- 2、 数据库以及证书存储路径,默认存在宿主机的/data/certd/目录下【可选】</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">./certd:/app/data</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">./share/certs:/var/certs</span> <span class="hljs-comment"># 本地证书存放位置</span><br>    <span class="hljs-attr">environment:</span> <span class="hljs-comment"># 环境变量</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">TZ=Asia/Shanghai</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">certd_system_resetAdminPassword=false</span><br>                                     <span class="hljs-comment">#  ↑↑↑↑↑---------------------------4、如果忘记管理员密码，可以设置为true，重启之后，管理员密码将改成123456，然后请及时修改回false【可选】</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">certd_cron_immediateTriggerOnce=false</span><br>                                     <span class="hljs-comment">#  ↑↑↑↑↑---------------------------5、如果设置为true，启动后所有配置了cron的流水线任务都将被立即触发一次【可选】</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">certd_koa_port=80</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">VITE_APP_ICP_NO=</span><br>                      <span class="hljs-comment">#  ↑↑↑↑↑ -----------------------------------------6、这里可以设置备案号【可选】</span><br>      <span class="hljs-comment"># 设置环境变量即可自定义certd配置</span><br>      <span class="hljs-comment"># 服务端配置项见： packages/ui/certd-server/src/config/config.default.ts</span><br>      <span class="hljs-comment"># 服务端配置规则： certd_ + 配置项, 点号用_代替</span><br><br>      <span class="hljs-comment"># 客户端配置项见： packages/ui/certd-client/.env</span><br>      <span class="hljs-comment"># 按实际名称配置环境变量即可，如： VITE_APP_API=http://localhost:7001</span><br></code></pre></td></tr></tbody></table></figure><p>​<code>docker-compose up -d</code>​ 运行服务。</p><h3 id="配置-CERTD-服务"><a href="#配置-CERTD-服务" class="headerlink" title="配置 CERTD 服务"></a>配置 CERTD 服务</h3><p><a href="https://github.com/certd/certd">github.com/certd/certd</a> 是一个全自动证书申请、更新、续期的开源服务，</p><blockquote><p>注：默认账号密码：admin/123456，请及时更改密码保证安全，更多安装细节可参考官方文档，<a href="https://certd.docmirror.cn/guide/install/docker/">私有化部署</a></p></blockquote><p>访问 <code>http://192.168.133.5:7001</code>​ 输入账号密码就可以看到如下页面。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201144426-896hm3q.png" alt="image" data-caption="image" loading="lazy">​</p><p>接下来做证书申请相关的配置，请根据 <a href="https://certd.docmirror.cn/guide/use/tencent/">certd 的操作文档</a>一步步进行，博主这里是腾讯云，所以最终会拿到一个 secretId 和 secretKey 。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201144856-eu7457f.png" alt="image" data-caption="image" loading="lazy">​</p><p>然后回到 certd 服务，点击左侧边栏『授权管理』按钮，进入授权管理页面，点击『添加』按钮，填写表单，我这里选择腾讯云并填写 secretId 和 secretKey，然后点击保存。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201144554-ijrwk5r.png" alt="image" data-caption="image" loading="lazy">​</p><p>‍</p><p>然后点击左侧『证书自动化流水线』，创建一个新流水线，完整形态如下，接下来一步步的看每一个节点。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201150102-z3a2ke7.png" alt="image" data-caption="image" loading="lazy">​</p><p>第一个节点在『触发源』，这里默认会有一个手动触发，可以用来调试，但我们希望证书可以自动更新，所以点击加号添加一个定时触发，定时规则可以写 <code>0 0 2 * * *</code>​，表示每天凌晨两点自动触发证书更新。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201150234-ocvxkyk.png" alt="image" data-caption="image" loading="lazy">​</p><p>第二个节点是『证书申请阶段』，点添加步骤</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201150339-mpgq2b7.png" alt="image" data-caption="image" loading="lazy">​</p><p>重点配置如下图所示，请填写自己的域名和 DNS 提供商、授权配置等。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201150430-nj9xhq9.png" alt="image" data-caption="image" loading="lazy">​</p><p>然后下一个关键点『更新证书』，注意这里的路径跟 docker-compose 中配置的 volumns 映射的右侧路径保持一致（这里是 /var/certs/）。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201150510-w7hewqv.png" alt="image" data-caption="image" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201150028-l2fk6vc.png" alt="image" data-caption="image" loading="lazy">​</p><p>【可选步骤】为及时的知道证书更新情况，可以在更新失败时通知到收件邮箱，参考<a href="https://certd.docmirror.cn/guide/use/email/">这篇文档</a></p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201152555-hfb1d00.png" alt="image" data-caption="image" loading="lazy">​</p><p>都配置完成后，点击右上角的『保存』按钮，并点击『手动运行』看整个流程是否报异常。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201152815-3crjrez.png" alt="image" data-caption="image" loading="lazy">​</p><p>运行完成后，查看 docker-compose 配置目录下的 <code>share/certs</code>​ 目录，如果可以找到这两个文件，说明证书已经申请好了。</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ <span class="hljs-built_in">ls</span> share/certs/<br>cert.key  cert.pem<br></code></pre></td></tr></tbody></table></figure><h3 id="配置-TRAEFIK-服务"><a href="#配置-TRAEFIK-服务" class="headerlink" title="配置 TRAEFIK 服务"></a>配置 TRAEFIK 服务</h3><p><a href="https://github.com/traefik/traefik/">traefik</a> 是一个开源的反向代理、负载均衡软件，除了 traefik 外 nginx 等支持反向代理的软件也可以承担同样的任务，不过 <strong>traefik 本身对容器化服务兼容性和可配置性更好</strong> ，所以在这个场景中更为推荐。</p><p>在 docker-compose 配置文件目录下，编写<code>./traefik/config.yaml</code>​ 内容如下，配置的具体含义可以参考官方文档：<a href="https://doc.traefik.io/traefik">doc.traefik.io/traefik</a></p><blockquote><p>注意：</p><ul><li>traefik HTTPS 配置文档请访问：<a href="https://doc.traefik.io/traefik/https/tls/">doc.traefik.io/traefik/https/tls/</a></li><li>通过中间件 middleware 的配置，实现了默认路由 http -&gt; https 的跳转，可按需使用</li><li>宿主机的 ./share/certs 对应 certd 服务的 /var/certs，证书申请好之后会在 ./share/certs 下生成证书文件，./share 对应 traefik 的 /etc/share，所以 traefik 需要配置的是 /etc/share/certs/cert{.key|.pem}</li><li>http.routers.ip 可以配置由 IP 直接访问时候的路由，注意这个配置不要填 tls:{}，这样就可以支持通过 http 访问而不会被自动跳转到 https</li></ul></blockquote><figure class="highlight yaml"><table><tbody><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><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">api:</span><br>  <span class="hljs-attr">insecure:</span> <span class="hljs-literal">true</span><br>  <span class="hljs-attr">dashboard:</span> <span class="hljs-literal">true</span><br><span class="hljs-attr">entryPoints:</span><br>  <span class="hljs-attr">web:</span><br>    <span class="hljs-attr">address:</span> <span class="hljs-string">":80"</span><br><span class="hljs-comment">#    http:</span><br><span class="hljs-comment">#      redirections:</span><br><span class="hljs-comment">#        entryPoint:</span><br><span class="hljs-comment">#          to: websecure</span><br>  <span class="hljs-attr">websecure:</span><br>    <span class="hljs-attr">address:</span> <span class="hljs-string">":443"</span><br><span class="hljs-attr">log:</span><br>  <span class="hljs-attr">filePath:</span> <span class="hljs-string">"/var/logs/traefik/traefik.log"</span><br><span class="hljs-attr">accessLog:</span><br>  <span class="hljs-attr">filePath:</span> <span class="hljs-string">"/var/logs/traefik/access.log"</span><br>  <span class="hljs-attr">filters:</span><br>    <span class="hljs-attr">statusCodes:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">"300-302"</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">"400-404"</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">"500-502"</span><br>    <span class="hljs-attr">retryAttempts:</span> <span class="hljs-literal">true</span><br>    <span class="hljs-attr">minDuration:</span> <span class="hljs-string">"1000ms"</span><br><span class="hljs-attr">providers:</span><br>  <span class="hljs-attr">docker:</span> <span class="hljs-literal">true</span><br>  <span class="hljs-attr">file:</span><br>    <span class="hljs-attr">directory:</span> <span class="hljs-string">"/etc/traefik"</span><br>    <span class="hljs-attr">watch:</span> <span class="hljs-literal">true</span><br><span class="hljs-attr">tls:</span><br>  <span class="hljs-attr">options:</span><br>    <span class="hljs-attr">default:</span><br>      <span class="hljs-attr">minVersion:</span> <span class="hljs-string">VersionTLS12</span><br>  <span class="hljs-attr">stores:</span><br>    <span class="hljs-attr">default:</span><br>      <span class="hljs-attr">defaultCertificate:</span><br>        <span class="hljs-attr">certFile:</span> <span class="hljs-string">"/etc/share/certs/cert.pem"</span><br>        <span class="hljs-attr">keyFile:</span> <span class="hljs-string">"/etc/share/certs/cert.key"</span><br><span class="hljs-attr">http:</span><br>  <span class="hljs-attr">middlewares:</span><br>    <span class="hljs-attr">https-redirect:</span><br>      <span class="hljs-attr">redirectScheme:</span><br>        <span class="hljs-attr">scheme:</span> <span class="hljs-string">https</span><br>        <span class="hljs-attr">permanent:</span> <span class="hljs-literal">false</span><br>  <span class="hljs-attr">routers:</span><br>    <span class="hljs-attr">ip:</span><br>      <span class="hljs-attr">rule:</span> <span class="hljs-string">"Host(`192.168.133.5`)"</span><br>      <span class="hljs-attr">service:</span> <span class="hljs-string">traefik</span><br>    <span class="hljs-attr">default:</span><br>      <span class="hljs-comment">#rule: "HostRegexp(`^.+\.internal\.wj2015\.com$`)"</span><br>      <span class="hljs-attr">rule:</span> <span class="hljs-string">"HostRegexp(`^.*.internal.wj2015.com$`)"</span><br>      <span class="hljs-attr">middlewares:</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">https-redirect</span><br>      <span class="hljs-attr">service:</span> <span class="hljs-string">flare</span><br></code></pre></td></tr></tbody></table></figure><p>如果上面的步骤都没有问题，执行 <code>docker-compose restart traefik</code>​后，访问 <code>http://你的ip</code>​ 应该可以看到如下页面</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201163431-160mc5b.png" alt="image" data-caption="image" loading="lazy">​</p><h3 id="动态配置-HTTPS-和子域名"><a href="#动态配置-HTTPS-和子域名" class="headerlink" title="动态配置 HTTPS 和子域名"></a>动态配置 HTTPS 和子域名</h3><p>下一步就是动态的给所有的服务加上 HTTPS 和子域名了，这里有多种解决方案，比较推荐的方案是 <strong>docker labels 声明 + 脚本生成动态配置</strong> 。</p><h4 id="labels-动态声明"><a href="#labels-动态声明" class="headerlink" title="labels 动态声明"></a>labels 动态声明</h4><p>docker label 配置是 traefik 支持的一项功能，可以在容器的 labels 中申明路由、负载均衡器等 traefik，进而实现动态的添加 traefik 代理配置的目的。</p><p>举个例子，这是原始的 alist 配置</p><figure class="highlight yaml"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.8"</span><br><span class="hljs-attr">services:</span><br>  <span class="hljs-attr">alist:</span><br>    <span class="hljs-attr">image:</span> <span class="hljs-string">xhofe/alist-aria2:v3.39.1</span><br>    <span class="hljs-attr">container_name:</span> <span class="hljs-string">alist</span><br>    <span class="hljs-attr">volumes:</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">'./alist:/opt/alist/data'</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">'/mnt/data/:/mnt/data/'</span><br>    <span class="hljs-attr">ports:</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">'5244:5244'</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">'6800:6800'</span><br>    <span class="hljs-attr">environment:</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">PUID=0</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">PGID=0</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">UMASK=022</span><br>    <span class="hljs-attr">restart:</span> <span class="hljs-string">unless-stopped</span><br></code></pre></td></tr></tbody></table></figure><p>增加 labels 声明后，就可以把 ports 映射移除掉了，<strong>因为 traefik 和 alist 容器在同一个网络，可以通过容器名称访问无需暴露端口</strong> 。</p><p>然后在 labels 中申明 tls = true，访问 alist.internal.wj2015.com 时路由到 alist:5244</p><figure class="highlight yaml"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.8"</span><br><span class="hljs-attr">services:</span><br>  <span class="hljs-attr">alist:</span><br>    <span class="hljs-attr">image:</span> <span class="hljs-string">xhofe/alist-aria2:v3.39.1</span><br>    <span class="hljs-attr">container_name:</span> <span class="hljs-string">alist</span><br>    <span class="hljs-attr">volumes:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">'./alist:/opt/alist/data'</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">'/mnt/data/:/mnt/data/'</span><br>    <span class="hljs-attr">environment:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">PUID=0</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">PGID=0</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">UMASK=022</span><br>    <span class="hljs-attr">restart:</span> <span class="hljs-string">unless-stopped</span><br>    <span class="hljs-attr">labels:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">"traefik.enable=true"</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">"traefik.http.routers.alist.rule=Host(`alist.internal.wj2015.com`)"</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">"traefik.http.routers.alist.tls=true"</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">"traefik.http.services.alist.loadbalancer.server.port=5244"</span> <span class="hljs-comment"># Alist在5244端口提供服务</span><br><br></code></pre></td></tr></tbody></table></figure><p>修改完毕后，访问 <code>https://alist.{域名}</code>​就能看到这个页面</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201160652-uxvzis0.png" alt="image" data-caption="image" loading="lazy">​</p><h4 id="脚本动态生成"><a href="#脚本动态生成" class="headerlink" title="脚本动态生成"></a>脚本动态生成</h4><p>脚本动态生成的目的是将“非容器运行的服务”统一管理起来，比如我通过手动部署的方式部署了 comfyui，这个服务运行在 8088 端口，那么就可以通过脚本自动生成 traefik 配置文件的方式实现网站的管理。</p><p>在 docker-compose 配置文件所在目录新建脚本 <code>traefik-dynamic.sh</code>​，公共子域名和服务列表请根据实际情况自行配置</p><figure class="highlight bash"><table><tbody><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><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-meta">#!/bin/bash</span><br><br><span class="hljs-comment"># 配置公共子域名</span><br>public_subdomain=<span class="hljs-string">"internal.wj2015.com"</span><br><br><span class="hljs-comment"># 可配置的服务列表</span><br><span class="hljs-built_in">declare</span> -A services_list=( <br>    [<span class="hljs-string">"traefik"</span>]=<span class="hljs-string">"http://traefik:8080"</span><br>    [<span class="hljs-string">"comfyui"</span>]=<span class="hljs-string">"http://172.17.0.1:8188"</span><br>)<br><br><span class="hljs-comment"># 生成结果放到 traefik/route.yml</span><br>output_file=<span class="hljs-string">"traefik/route.yml"</span><br><br><span class="hljs-comment"># 初始化路由器和服务配置</span><br>router_configs=()<br>service_configs=()<br><br><span class="hljs-comment"># 遍历服务列表，并生成配置</span><br><span class="hljs-keyword">for</span> service <span class="hljs-keyword">in</span> <span class="hljs-string">"<span class="hljs-variable">${!services_list[@]}</span>"</span>; <span class="hljs-keyword">do</span><br>    server=<span class="hljs-variable">${services_list[$service]}</span><br><br>    <span class="hljs-comment"># 构造路由器配置</span><br>    router_config=<span class="hljs-string">"    <span class="hljs-variable">$service</span>:\n      rule: \"Host(\`<span class="hljs-variable">${service}</span>.<span class="hljs-variable">${public_subdomain}</span>\`)\"\n      tls: {}\n      service: <span class="hljs-variable">$service</span>"</span><br>    router_configs+=(<span class="hljs-string">"<span class="hljs-variable">$router_config</span>"</span>)<br><br>    <span class="hljs-comment"># 构造服务配置</span><br>    service_config=<span class="hljs-string">"    <span class="hljs-variable">$service</span>:\n      loadBalancer:\n        servers:\n          - url: <span class="hljs-variable">$server</span>/"</span><br>    service_configs+=(<span class="hljs-string">"<span class="hljs-variable">$service_config</span>"</span>)<br><span class="hljs-keyword">done</span><br><br><span class="hljs-comment"># 写入配置到输出文件</span><br>{<br>    <span class="hljs-built_in">echo</span> <span class="hljs-string">"http:"</span><br>    <span class="hljs-built_in">echo</span> <span class="hljs-string">"  routers:"</span><br>    <span class="hljs-keyword">for</span> rc <span class="hljs-keyword">in</span> <span class="hljs-string">"<span class="hljs-variable">${router_configs[@]}</span>"</span>; <span class="hljs-keyword">do</span><br>        <span class="hljs-built_in">echo</span> -e <span class="hljs-string">"<span class="hljs-variable">$rc</span>"</span><br>    <span class="hljs-keyword">done</span><br>    <span class="hljs-built_in">echo</span> <span class="hljs-string">"  services:"</span><br>    <span class="hljs-keyword">for</span> sc <span class="hljs-keyword">in</span> <span class="hljs-string">"<span class="hljs-variable">${service_configs[@]}</span>"</span>; <span class="hljs-keyword">do</span><br>        <span class="hljs-built_in">echo</span> -e <span class="hljs-string">"<span class="hljs-variable">$sc</span>"</span><br>    <span class="hljs-keyword">done</span><br>} &gt; <span class="hljs-string">"<span class="hljs-variable">$output_file</span>"</span><br><br><span class="hljs-built_in">echo</span> <span class="hljs-string">"Traefik configuration has been generated in <span class="hljs-variable">$output_file</span>."</span><br></code></pre></td></tr></tbody></table></figure><p>执行 <code>bash traefik-dynamic.sh</code>​ 后，可以看到如下提示表示生成成功</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ bash traefik-dynamic.sh <br>Traefik configuration has been generated <span class="hljs-keyword">in</span> traefik/route.yml.<br></code></pre></td></tr></tbody></table></figure><p>生成的 <code>traefik/route.yml</code>​ 预期如下：</p><figure class="highlight yml"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">http:</span><br>  <span class="hljs-attr">routers:</span><br>    <span class="hljs-attr">traefik:</span><br>      <span class="hljs-attr">rule:</span> <span class="hljs-string">"Host(`traefik.internal.wj2015.com`)"</span><br>      <span class="hljs-attr">tls:</span> {}<br>      <span class="hljs-attr">service:</span> <span class="hljs-string">traefik</span><br>    <span class="hljs-attr">comfyui:</span><br>      <span class="hljs-attr">rule:</span> <span class="hljs-string">"Host(`comfyui.internal.wj2015.com`)"</span><br>      <span class="hljs-attr">tls:</span> {}<br>      <span class="hljs-attr">service:</span> <span class="hljs-string">comfyui</span><br>  <span class="hljs-attr">services:</span><br>    <span class="hljs-attr">traefik:</span><br>      <span class="hljs-attr">loadBalancer:</span><br>        <span class="hljs-attr">servers:</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-attr">url:</span> <span class="hljs-string">http://traefik:8080/</span><br>    <span class="hljs-attr">comfyui:</span><br>      <span class="hljs-attr">loadBalancer:</span><br>        <span class="hljs-attr">servers:</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-attr">url:</span> <span class="hljs-string">http://172.17.0.1:8188/</span><br></code></pre></td></tr></tbody></table></figure><p>访问 <code>https://traefik.{域名}</code>​，预期如下</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/../static/assets/blog-nas-free-https/image-20241201163448-28upiix.png" alt="image" data-caption="image" loading="lazy">​</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>至此，这台 NAS 服务器的所有服务都可以通过上述机制来自动生成 HTTPS 配置。</p><p>整个思路的关键点如下：</p><ul><li>想办法自动的获取免费证书</li><li>想办法动态的给容器和内网服务生成 https 配置</li></ul><p>‍</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;对于一个喜欢折腾的人来说，NAS 上总会有各种各样的服务，有用 docker</summary>
        
      
    
    
    
    <category term="HTTPS" scheme="https://blog.wj2015.com/tags/HTTPS/"/>
    
    
    <category term="Linux" scheme="https://blog.wj2015.com/categories/Linux/"/>
    
    <category term="NAS" scheme="https://blog.wj2015.com/tags/NAS/"/>
    
    <category term="SSL" scheme="https://blog.wj2015.com/tags/SSL/"/>
    
    <category term="安全" scheme="https://blog.wj2015.com/tags/%E5%AE%89%E5%85%A8/"/>
    
    <category term="服务" scheme="https://blog.wj2015.com/tags/%E6%9C%8D%E5%8A%A1/"/>
    
  </entry>
  
  <entry>
    <title>建立大模型的感性认知</title>
    <link href="https://blog.wj2015.com/2024/08/01/%E5%BB%BA%E7%AB%8B%E5%A4%A7%E6%A8%A1%E5%9E%8B%E7%9A%84%E6%84%9F%E6%80%A7%E8%AE%A4%E7%9F%A5/"/>
    <id>https://blog.wj2015.com/2024/08/01/%E5%BB%BA%E7%AB%8B%E5%A4%A7%E6%A8%A1%E5%9E%8B%E7%9A%84%E6%84%9F%E6%80%A7%E8%AE%A4%E7%9F%A5/</id>
    <published>2024-08-01T16:31:06.000Z</published>
    <updated>2026-08-12T03:28:46.856Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><h3 id="为什么先谈感性认知"><a href="#为什么先谈感性认知" class="headerlink" title="为什么先谈感性认知"></a>为什么先谈感性认知</h3><p>毛主席在《实践论》中详细阐述了认知的过程，我们的<strong>认知是依赖于物质生产实践的，通常是先由片面事务之间的关联建立感性认知，再通过不断实践了解其规律和本质</strong>，经过感觉达到思维，才能进一步建立起理性认知，所以本文会较少关注技术细节，而是从事务的各方面现象和联系出发，去了解和建立对大模型的认知，让读者对大模型有更好的理解和应用。</p><blockquote><p>“人的认识，主要地依赖于物质的生产活动，逐渐地了解自然的现象、自然的性质、自然的规律性、人和自然的关系；而且经过生产活动，也在各种不同程度上逐渐地认识了人和人的一定的相互关系。一切这些知识，离开生产活动是不能得到的。”</p><p>“人在实践过程中，开始只是看到过程中各个事物的现象方面，看到各个事物的片面，看到各个事物之间的外部联系。这是认识的感性阶段，就是感觉和印象的阶段。”</p><p>从感性认识到理性认识的飞跃：“社会实践的继续，使人们在实践中引起感觉和印象的东西反复了多次，于是在人们的脑子里生起了一个认识过程中的突变（即飞跃），产生了概念。概念同感觉，不但是数量上的差别，而且有了性质上的差别。循此继进，使用判断和推理的方法，就可产生出合乎论理的结论来。”</p></blockquote><p>参考：<a href="https://mp.weixin.qq.com/s/TX1jlFr3gsxnKMMTbkUdog">《实践论》</a></p><h3 id="What-大模型是什么"><a href="#What-大模型是什么" class="headerlink" title="What 大模型是什么"></a>What 大模型是什么</h3><p>大模型技术是人工智能领域长期积累和创新的结果，<strong>大模型指的是在深度学习框架下，具有大量参数的复杂神经网络模型。</strong> 这些模型通常需要大量的计算资源来训练，并且能够处理和分析大规模的数据集。大模型包括但不限于语言模型、视觉模型、多模态模型等。例如，GPT系列和BERT模型就是典型的大型语言模型，它们通过学习大量的文本数据，能够理解和生成自然语言。</p><p>在架构设计上，大模型通常采用深度学习框架，如 Transformer（Encoder-Decoder）、Autoregressive（Decoder-Only）、Autoencoder（Encoder-Only），这些架构通过自注意力机制和层级结构，有效地处理序列数据，并在并行计算方面展现出优势。</p><p>图片生成的大模型大多为 Diffusion （ Unet + Clip） 架构，也有些研究将 Diffusion 和 Transformer 结合起来，称之为 DIT 尝试增加图片生成大模型对文字的深层理解。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240726195726-6jvdjjr.png" alt="image" data-caption="image" loading="lazy">​​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240726112341-hzygya6.png" alt="image" data-caption="image" loading="lazy">​</p><p>此外，现阶段大语言模型与其他 AI 模型在运行模式上有很大的区别， 现在的大语言模型是 in-context 模式的，模型没有变化，<strong>没有被重新训练，但是能“理解”新数据。</strong></p><p>为了应对大模型训练过程中的挑战，如梯度消失、计算资源消耗等，研究人员还开发了一系列优化技术，包括参数共享、知识蒸馏、模型剪枝、模型量化等，以确保模型的训练效率和泛化能力。</p><h3 id="Why-为什么要了解大模型"><a href="#Why-为什么要了解大模型" class="headerlink" title="Why 为什么要了解大模型"></a>Why 为什么要了解大模型</h3><p>大模型之所以受到重视，主要是因为它们在多个下游任务上表现出色，如机器翻译、文本生成、对话系统等。此外，大模型能够捕捉到数据中的细微模式和深层次特征，这对于<strong>提高模型的泛化能力和性能</strong>至关重要。</p><p>之前要用很多小模型做的事情，或者小模型无法完成的任务，现在就多了一个新的，效果可能更好的选择。</p><h3 id="How-如何学习和应用"><a href="#How-如何学习和应用" class="headerlink" title="How 如何学习和应用"></a>How 如何学习和应用</h3><p>个人认为可以对其建立感性认知之后，<strong>先视为一个辅助工具，使用 Prompt 跟大模型沟通并激活它的能力帮助我们解决问题，就跟计算机代码一样</strong>，而在熟练应用后，可以再深究其原理和实现，并按照自己的想法进行创新等</p><p>从应用方向上，大模型可以提升和优化的方向非常广泛，让我印象深刻的是 23年高盛资本发布的研究报告，预测未来人工智能的发展将会取代3亿人当前的工作，比如行政、文员、法律、管理等（<a href="https://www.key4biz.it/wp-content/uploads/2023/03/Global-Economics-Analyst_-The-Potentially-Large-Effects-of-Artificial-Intelligence-on-Economic-Growth-Briggs_Kodnani.pdf">原文</a>）</p><p>而从后端开发工程师这一细分只能的岗位角度出发，大模型可以应用在各方个面提高日常工作效率：</p><ul><li>检索语法、三方库，比如我忘记了 golang 结构体的最佳实践，通过对话可以快速检索并获取知识</li><li>解释不理解的代码，比如阅读 Ruby 写的业务代码、理解非常复杂的算法等等</li><li>根据业务需求生成 SQL 数据结构，根据 SQL 数据结构和业务需求生成需要的 ORM 代码</li><li>根据业务需求生成接口文档，根据接口文档生成 protos 声明，根据 protos 声明+业务需求+ORM 函数编写接口处理逻辑</li><li>代码生成，当项目代码结构稳定后，大语言模型非常适合做重复性的工作，做代码优化、逻辑的修补、测试用例的生成等，能极大的节省开发者的时间</li></ul><h2 id="计算机是如何理解语言的"><a href="#计算机是如何理解语言的" class="headerlink" title="计算机是如何理解语言的"></a>计算机是如何理解语言的</h2><p>在计算机基础课中，大家都知道计算机通过将文本、图像、视频等多媒体信息按照格式规范编码为二进制，从而可以存储这些信息，读取的时候把二进制解码即可得到原始数据。</p><p>比如一张图像可以保存为 jpg/png/webp 等格式，解码后都是红绿蓝三原色的矩阵组合，显示在屏幕上就成为了一个个像素点。</p><p>而文字在计算机中一般以 UTF8、ASCII 编码等方式存储，但也只是存储，计算机无法理解存储的文本有何显示含义，而词向量（word vector）是一种将单词表示为固定长度的向量的方法。在自然语言处理（NLP）和机器学习领域，<strong>词向量是将文本数据转换为数值形式的重要工具</strong>。</p><p>‍</p><p>人类用字母序列来表示英文单词，比如C-A-T表示猫。语言模型使用的是一个叫做词向量的长串数字列表。例如，这是一种将猫表示为向量的方式：</p><p>[0.0074, 0.0030, -0.0105, 0.0742, 0.0765, -0.0011, 0.0265, 0.0106, 0.0191, 0.0038, -0.0468, -0.0212, 0.0091, 0.0030, -0.0563, -0.0396, -0.0998, -0.0796, …, 0.0002]</p><p>‍</p><p>语言模型采用类似的方法：<strong>每个词向量代表了“词空间（word space）”中的一个点，具有相似含义的词的位置会更接近彼此。</strong> 例如，在向量空间中与猫最接近的词包括狗、小猫和宠物。用实数向量表示单词（相对于“C-A-T”这样的字母串）的一个主要优点是，数字能够进行字母无法进行的运算。</p><p>‍</p><p>而向量表示一个词，那么就可以使用向量运算“推理”单词，例如，Google研究人员取出最大的（biggest）向量，减去大的（big）向量，再加上小的（small）向量。与结果向量最接近的词就是最小的（smallest）向量。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240726154917-yaxxh6f.png" alt="image" data-caption="image" loading="lazy">​</p><p>所以大语言模型理解语言的第一步就是 token 化，在 GPT 的网站中有一个 Tokenlizer 的工具：<a href="https://platform.openai.com/tokenizer">Tokenizer</a>，可以将一个长文本拆为 Token，而每个 Token 就是对应着一个词向量（Embedding），可以看到 Token  和字符不一定是一一对应的，一个 Token 可能代表多个单词，也可能代表一个词根或者一个符号。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240726161845-z681o2q.png" alt="image" data-caption="image" loading="lazy">​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240726161926-nmvci5b.png" alt="image" data-caption="image" loading="lazy">​</p><p>而 TokenID 是一串数字，这串数字会对应 Embedding，Embedding 才是真正的词向量，词向量中存在 N 维向量值（GPT3 是 1w2 个维度），可以抽象的理解词向量就是这个词在词空间中位置，<strong>词向量的某一个维度都是某一方面的分类属性，相似的词在此空间中距离相近，也就是向量化。</strong></p><p>以猫猫的品类举例， emb[0] 表示体型 emb[2] 表示毛色 emb[3] 表示体型，那么“狸花”、“三花”在 emb[2] 上有一定差异，其他向量值上都很相似，那么在预测“中华田园猫中，除了狸花猫还有___”时，就可以从词空间中找到与之相近的词，可能是“三花”，也可能是“橘猫”</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240726162310-eh3xqf1.png" alt="image" data-caption="image" loading="lazy">​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240726162454-sj7z316.png" alt="image" data-caption="image" loading="lazy">​</p><p>类似的思路可以用在其他形式的多媒体数据训练上，也就是多模态大模型，比如图像就可以把一张大图拆为小图，再将小图拉平为一个一维数组，也就是 patch embedding，再送入 transformer 后就可以提取这一个 patch 的特征。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240726195901-6bu5tr4.png" alt="image" data-caption="image" loading="lazy">​</p><h2 id="上下文决定词的语意"><a href="#上下文决定词的语意" class="headerlink" title="上下文决定词的语意"></a>上下文决定词的语意</h2><p>上面的词向量表示法并不兼容多义词，而词意本身就具有基本意和引申义。</p><p>就像“海”字，基本意思是大海，而引申义可以代表宽阔，甚至赋予特别的时代意义，就像这句话“刚一改革开放他就下海了,那钱可挣海了”</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240731154648-ifwm570.png" alt="image" data-caption="image" loading="lazy">​</p><p>更形象的例子也有很多，虽然都是同一个字，但在上下文的不同位置，就代表着不同的含义：</p><ul><li>一个人要是行,干一行行一行,一行行行行行。人要是不行,干一行不行一行,一行不行行行不行。</li><li>今天下雨,我骑车差一点摔倒,还好我一把把把把住了。</li></ul><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240729192728-rxpgc65.png" alt="image" data-caption="image" loading="lazy">​</p><p>从深度学习模型层面来讲，上下文就需要作为推理参数去影响生成后文的所有内容，而<strong>大模型中的 Transformer 结构就是做这个工作的，经过一层又一层的提取，让句中的每个 token 与其他 token 建立关联关系</strong>，比如下图中的例子，通过两个 Transformer 层就提取到了一系列语义信息：</p><ul><li>his 代指 John</li><li>cash 在上下文中是动词，表示提现</li><li>…</li></ul><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240729192918-oyh3wol.png" alt="image" data-caption="image" loading="lazy">​</p><p>在 Transformer 结构中，也就可以理解为 his 这个 token 与 John 存在较高的关联，这种关联可以用权重数组来表示，比如 0 - 1 的权重，his -&gt; John 的权重被配置为 0.98，这里的权重也就是“注意力”</p><p>‍</p><p>而一句话需要关注的可能不只是主语，也需要关注其他名词、动词等，所以模型开发者就提出一个概念叫做 <strong>“多头注意力”</strong> ，多个注意力权重并行的专注于不同的任务，比如一个注意力头关注代词，一个注意力头关注多义词，还有一些注意力头关注短语等等</p><p>‍</p><p>GPT-3的最大版本有96个层，每个层有96个注意力头，因此，每次预测一个新词时，GPT-3将执行9216个注意力操作，<strong>注意力机制的本质是一个权重向量（/矩阵）对原始输入进行加权，这个权值是靠输入信息自动生成的，所以叫自注意力</strong>，多头注意力在模型推理过程中的结构就是 QKV 三个矩阵，其含义可以理解为：</p><ul><li>Q 代表 Query，即查询，‌指的是当前需要处理的向量提取的特征</li><li><strong>K</strong> 代表 <strong>Key</strong>，‌即键，‌用来计算当前向量与其他向量之间的注意力权重。‌</li><li><strong>V</strong>代表<strong>Value</strong>，‌即值，‌是计算好权重后，‌将不同向量的特征V进行加权和，‌得到Q对应的当前向量所对应的最终输出。‌</li></ul><p>详细信息可参考：<a href="https://zhuanlan.zhihu.com/p/688660519">轻松理解 QKV</a></p><p>‍</p><p>这与信息熵的概念很有关联，在信息论中，熵是对不确定性的一种度量，信息量越大，不确定性就越小，熵也就越小；信息量越小，不确定性越大，熵也越大。<strong>更多的上下文，会让信息熵更小，不确定性越少</strong>。所以在模型能力足够的前提下，越准确越丰富的上下文，句中词义就约准确，多头注意力提取到的有效信息就越多，生成的回答质量就越高，这就是 Prompt 工程提升效果的核心。</p><h2 id="大模型是如何推理的"><a href="#大模型是如何推理的" class="headerlink" title="大模型是如何推理的"></a>大模型是如何推理的</h2><p>具体可以拆分为如下几个步骤：</p><ul><li>将输入的信息做分词，拿到一个个 token，并将 <strong>token 转换为位置编码（Embedding）</strong></li><li>拿到 token 之后，做 prefill：输入编码“你是谁？”，针对输入编码，产生kv cache，然后生成首个token“我”，这里的 KV Cache 就是上文提到的多头注意力中的 K V 两个矩阵</li><li>开始自回归解码，生成下一个 token，直到输出特殊的停止 token <eos> 或停止词时停止</eos></li></ul><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240731155121-jy35lp9.png" alt="image" data-caption="image" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240730102913-63n5j1l.png" alt="image" data-caption="image" loading="lazy">​</p><p>所以可以看到的大模型应用大多是流式输出的，每次只吐一个 token，token 经过后处理之后，才能转为人类可识别的文字。</p><p>这里的 next token 是存在概率的，正是因为下一个词存在多个可能，所以大语言模型才可以给到我们多样化的回答</p><p>比如“I’m Iron ” 推理 next token 有 80% 是 “man”，有 10% 是 “woman”，这个概率会根据上下文而定，比如完整的上下文是：”In the Avengers, there is a famous line: I’m Iron”，那么推理结果中下一个 token 有 99.99% 是 “man”</p><p>有几个参数可以影响大语言模型的解码策略进而控制选择结果，比较重要的有 temperature、top_p 、top_k等（参考：<a href="https://blog.csdn.net/zwqjoy/article/details/136805842">解码策略的选择</a>）</p><p><strong>控制采样的目的是让大模型的输出结果更适合任务</strong>，我们可以较为通俗的理解这几个参数，进而去找到一个合适的参数组合</p><ul><li><p>temperature，通过重新计算概率，让分布更加平滑或更加陡峭</p><ul><li>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240731190905-x5f8rc5.png" alt="image" data-caption="image" loading="lazy">​</li></ul></li><li><p>top_p，只考虑累计值不超过 top_p 的数据，进而排除低概率选择</p><ul><li>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240731191736-w2rdnj9.png" alt="image" data-caption="image" loading="lazy">​</li></ul></li><li><p>top_k，只考虑排名前 k 的数据</p><ul><li>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240731191824-aoxejlq.png" alt="image" data-caption="image" loading="lazy">​</li></ul></li></ul><p>这三个规则一起工作也就是联合采样，通常是 top_k -&gt; top_p -&gt; temperature，也就是先做硬限制，再排除低概率选择，最后重新计算概率分布后，根据概率分布拿到 next token</p><table><thead><tr><th>使用案例</th><th>温度</th><th>Top_p</th><th>描述</th></tr></thead><tbody><tr><td>代码生成</td><td>0.2</td><td>0.1</td><td>生成遵循既定模式和约定的代码。输出更具确定性和针对性。对于生成语法正确的代码很有用。</td></tr><tr><td>创意写作</td><td>0.7</td><td>0.8</td><td>生成用于讲故事的创意且多样化的文本。输出更具探索性，更少受模式限制。</td></tr><tr><td>聊天机器人</td><td>0.5</td><td>0.5</td><td>生成平衡连贯性和多样性的对话响应。输出更加自然且引人入胜。</td></tr><tr><td>代码注释生成</td><td>0.3</td><td>0.2</td><td>生成更简洁且相关的代码注释。输出更具确定性并且遵循约定。</td></tr><tr><td>数据分析脚本</td><td>0.2</td><td>0.1</td><td>生成更可能正确且高效的数据分析脚本。输出更具确定性和针对性。</td></tr><tr><td>探索性代码编写</td><td>0.6</td><td>0.7</td><td>生成探索替代解决方案和创造性方法的代码。产出较少受到既定模式的限制。</td></tr></tbody></table><p>从上面的过程我们不难联想到这种推理方式可能会带来一些问题：</p><p><strong>数学不好</strong></p><ul><li>推理 next token 的方法用来写文章非常适合，因为思考内容的时候也是一个字一个字往外蹦的</li><li>数学不一样，数理需要精确的计算，1+1=2 这种常见问题因为训练数据足够多，大模型可以“背答案”，但 17281 x 123 这种数据一定要用数学工具计算才能得到正确的答案</li><li>最近大火的 9.11 &gt; 9.8 的问题也是因为这个原因</li><li>不过人们也想到了很多办法，让大模型能准确的计算数字，比如 PAL 程序辅助语言模型</li></ul><p><strong>幻觉</strong></p><ul><li>AI模型的底层原理都是基于数学概率，其模型输出实质上是一系列数值运算，大模型也不例外，所以它有时候会一本正经地胡说八道，尤其是在大模型自身不具备某一方面的知识或不擅长的场景。</li><li>而这种幻觉问题的区分是比较困难的，因为它要求使用者自身具备相应领域的知识</li></ul><p>更详细的过程和代码请参考：<a href="https://ai.oldpan.me/t/topic/169">快速入门 LLM 推理过程</a></p><h2 id="大模型是如何训练的"><a href="#大模型是如何训练的" class="headerlink" title="大模型是如何训练的"></a>大模型是如何训练的</h2><p>大语言模型的训练被分为三步（分词 tokenlizer 、Embedding 模型需要提前准备好）：</p><ul><li>【预训练】使用大量训练数据，练一个基座模型，这个基座模型可以预测下一个词，但不擅长对话</li><li>【监督微调】使用精选的对话数据集进行训练，让基座模型更适合对话，也就是“监督微调 SFT”</li><li>【奖励建模+强化学习】通过标注 SFT 模型生成结果的评分，训练一个奖励模型，用于强化学习</li></ul><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240726173620-flfc7m6.png" alt="image" data-caption="image" loading="lazy">​</p><p>训练完成后，<strong>模型参数就固定了，不会随着时间变化而变化，GPT 响应的多样是根据输入的上下文不同而展现出来的</strong>，所以 GPT3.5 出来的时候很多人在讨论他是否有意识，这种二进制形态的数据就不可能存在意识，而且<strong>在训练完的那一刹那起，模型蕴含的知识就是固定的</strong>，哪怕下一秒发生了一件全世界都为之震惊的事情，这个大模型也无从得知；</p><p>所以这种方式训练的模型<strong>注定是有知识局限性</strong>的，但人们也想了很多办法去减少这个问题，具体在后文会提到。</p><p>完整的训练一个大模型成本非常高，但并不代表只能通过完整训练的方式获得一个新的大模型，在拿到一个训练好的大模型后，还可以通过一些手段进行微调</p><p>最常见的微调方式是 Lora，Lora 的本质是在模型层之间增加一些中间层，用新增的数据进行训练，每次训练只改变中间层的权重，这样训练的要求就可以被降到特别低，在完全训练完成后，还可以根据规则将 lora 权重加到原有模型中，进而生成新的模型。</p><p>更多微调方法可参考：<a href="https://blog.csdn.net/xiangxueerfei/article/details/139247954">大模型微调</a></p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240731155814-m8c0v8e.png" alt="image" data-caption="image" loading="lazy">​</p><p>‍</p><h2 id="提升效果的几个方法"><a href="#提升效果的几个方法" class="headerlink" title="提升效果的几个方法"></a>提升效果的几个方法</h2><p>在不动基础模型的基础上，调整的方法大多围绕 <strong>“填充有效上下文”和“扩展外部能力”</strong> 出发。</p><p>推荐看一下 openai 的提示工程师文档：<a href="https://platform.openai.com/docs/guides/prompt-engineering">Prompt Engineering</a></p><h3 id="Prompt-工程"><a href="#Prompt-工程" class="headerlink" title="Prompt 工程"></a>Prompt 工程</h3><p>Prompt 工程本质是填充有效上下文，通过规则和清晰的背景信息、目标信息，让大语言模型的多头注意力尽可能多的关联到你想要的能力上，也就是让<strong>信息熵更小，不确定性越小</strong>，好比我们希望模型帮助我们写代码，如果我们限定模型为“经验丰富的 Python 运维工程师”，那么模型注意到关键信息 “Python”、“运维” 之后，“联想”到与之关联的信息可能性就越高，如果不提这回事儿，大模型可能会猜这个场景用什么语言比较合适，模型的注意力被“分散”了，最终生成效果就出现了差别。</p><h4 id="Zero-Shot"><a href="#Zero-Shot" class="headerlink" title="Zero-Shot"></a>Zero-Shot</h4><p>Zero-shot 学习是一种能够在没有任何特定训练数据的情况下执行新任务的机器学习方法，目前的大模型逐渐展现了一些 zero-shot 的特性，比如 GPT 可以在没有特定任务训练数据的情况下，根据任务描述对文本进行分类、情感分析、文本翻译、文本摘要等。</p><h4 id="Few-Shot"><a href="#Few-Shot" class="headerlink" title="Few-Shot"></a>Few-Shot</h4><p>Few-shot学习是一种机器学习方法，在这种方法中模型在训练阶段只以少量（几次甚至一次）样本来学习识别或理解新的任务，比如在 zero-shot 效果不理想的时候，可以考虑多给大模型一些示例，大模型可以从这些示例的上下文中学习到规律并与新任务建立关联。</p><p>下面是一个 few-shot 的例子（来源自网络，仅供参考，不代表博主本人价值观）</p><figure class="highlight plaintext"><table><tbody><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><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">你是一个破译女性情感的专家，你能解读出女生话背后的「潜台词」。将解读出的内容简洁易懂、高度概括的输出，回复的内容不能与学习案例相同，要有个性。她说的话对是针对男生的。<br><br>以下是你学习的案例：<br>我还不想谈恋爱<br>潜台词：我只是不想和你谈恋爱<br><br>我不想破坏我们之间的友谊<br>潜台词：我们之间只可能是友谊<br><br>人真的很好<br>潜台词：你做备胎挺合适的<br><br>我们还是做朋友吧<br>潜台词：我们只能是朋友<br><br>你是个好人<br>潜台词：好归好，但别指望我能看上你<br><br>我从来没有想过这个问题<br>潜台词：你从来就不在我的考虑范围内<br><br>我需要时间考虑<br>潜台词：先稳住你，再想办法拒绝你吧<br><br>你挺优秀的<br>潜台词：只不过还没达到我的标准<br><br>我一直没有勇气接受你<br>潜台词：我是发自内心地抗拒你啊<br><br>你不是我喜欢的类型<br>潜台词：你很丑<br><br>你去死吧<br>潜台词：赶紧哄我，否则事大了<br><br>哎呀，讨厌<br>潜台词：赶紧再亲亲我，<br><br>你怎么会没有女朋友呢<br>潜台词：我也单身，速度追我<br><br>你饿不饿<br>潜台词：我都快饿死了<br><br>是不是快到情人节了<br>潜台词：赶紧约我<br><br>嗯<br>潜台词：知道了<br><br>哦<br>潜台词：说完了吗？<br><br>好吧<br>潜台词：还有事吗？<br><br>我去洗澡了<br>潜台词：拜拜了<br><br>哈哈<br>潜台词：求求你放过我吧，我实在是笑不出来啊<br><br>忙<br>潜台词：老娘可不想把时间浪费在你身上<br><br>有空再聊<br>潜台词：你永远不会有空，也不会有这一天<br><br>输入的内容是女生说的话，回复时每句话都需要带上 "潜台词："。回复的语气要尖酸刻薄、强唇劣嘴、脾性暴烈、河东狮吼、彪悍、PUA，无上下文关联关系，内容尽可能加上 emoji。要懂得拒绝与暗示、口是心非。这时你要注意，如果她提出一些和情感有关的问题，那多半是等着的示爱或喜欢<br></code></pre></td></tr></tbody></table></figure><h4 id="思维链和分步思考"><a href="#思维链和分步思考" class="headerlink" title="思维链和分步思考"></a>思维链和分步思考</h4><p>参考论文：<a href="https://arxiv.org/pdf/2201.11903">Chain-of-Thought Prompting Elicits Reasoningin Large Language Models</a></p><p><strong>推理，一般指根据几个已知的前提推导得出新的结论的过程，区别于理解，推理一般是一个“多步骤”的过程，推理的过程可以形成非常必要的“中间概念”，这些中间概念将辅助复杂问题的求解</strong>。<strong>通过让大模型逐步参与将一个复杂问题分解为一步一步的子问题并依次进行求解的过程可以显著提升大模型的性能。而这一系列推理的中间步骤就被称为思维链（Chain of Thought）</strong> 。这种方法对于复杂问题尤其有效，因为它可以帮助人工智能系统逐步分析和解决问题，而不是直接跳到最终答案。</p><p><strong>一个完整的包含 CoT 的 Prompt 往往由指令（Instruction），逻辑依据（Rationale），示例（Exemplars）三部分组成。</strong> 最简单的方式就是加上一句“Let’s think step by step”</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240731220444-ysytt0y.png" alt="image" data-caption="image" loading="lazy">​</p><p>更多信息和用法：<a href="https://zhuanlan.zhihu.com/p/670907685">大模型思维链 CoT</a></p><h4 id="提示词框架"><a href="#提示词框架" class="headerlink" title="提示词框架"></a>提示词框架</h4><p>日常让大模型帮助我们完成任务的时候，可以参考一些提示词框架，比如 CHAT（角色、背景、目标、任务）、CRISPE（能力、角色、洞察、声明、个性、示例）等，具体可参考：<a href="https://blog.csdn.net/Langchain/article/details/139059767">Prompt 工程全攻略</a></p><p>一些 Prompt 例子：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">你是一名外企的软件工程师。公司需要定期给同事的工作情况给予评价，评价的内容请以表扬、称赞为主，可以含蓄的给出一些改进建议。我会给你某位同事简单的介绍及其与你协作的相关信息，请用英文给出大约 200 字左右的回复，不要使用邮件的格式。<br></code></pre></td></tr></tbody></table></figure><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs plaintext">你是谁<br>你是一名摄影大师，拥有 50 年的摄影经验，是多个摄影比赛的最高级评委，对于摄影图片的分析拥有极高的造诣<br><br>你要做什么<br>用户将输入一张摄影作品，请你从如下方面分析该作品：<br><br>评价一张摄影图片通常包括以下步骤：<br><br>观察主题和内容：<br>分析照片的主题是什么。<br>检查照片是否传达了某种特定的情感或信息。<br><br>审视构图：<br>观察画面元素的布局。<br>检查使用了哪些构图技巧，如三分法、对称、前景与背景的处理等。<br><br>分析技术质量：<br>评估焦点、清晰度和深度。<br>查看曝光是否合适，高光和阴影的细节是否得到保留。<br>考察色彩处理，包括色彩的饱和度、对比度和色调。<br><br>评价光线使用：<br>分析光线对主体的影响和照片的整体氛围。<br>检查光线是如何引导观看者的视线的。<br><br>考虑创意与原创性：<br>评估照片中是否有独特的视角或创新的表达方式。<br>检查照片是否能够引起观看者的思考或情感共鸣。<br><br>总体感受：<br>总结照片给你的整体印象和感受。<br>考虑照片在艺术和技术层面的综合表现。<br>根据你的分析，给出优化建议。<br><br>注意：<br>你应该理性分析，对于图片中有不理想的部分，你应该明确指出其中的问题，不需要委婉表达，需要直接了当，让我清楚地明白我后面该怎么优化。<br></code></pre></td></tr></tbody></table></figure><h4 id="RAG-检索增强生成"><a href="#RAG-检索增强生成" class="headerlink" title="RAG 检索增强生成"></a>RAG 检索增强生成</h4><p>检索增强生成（Retrieval Augmented Generation），简称 RAG，通过自有垂域数据库检索相关信息，然后合并成为提示模板，给大模型生成漂亮的回答。</p><p>本质依旧是为大模型填充优质准确的上下文，大致流程是将用户输入的内容去数据库中按照相关性查找上下文，再将查询到的上下文注入到大模型的提示词中，进而增强效果。</p><p>如果这里的用户输入通过接口去获取搜索引擎的前20条数据后，过滤筛选出合理数量的上下文注入到提示词中，也就可以让大模型拥有获取和分析即时信息的能力，就是 <a href="https://metaso.cn/">秘塔搜索</a> 等 AI 搜索引擎的核心能力。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240731221813-mpbf4sw.png" alt="image" data-caption="image" loading="lazy">​</p><p>但这样依旧是存在问题的，因为“相似性”查找并非真正意义上的语义查找，好比用户输入是“我不要吃猪肉”，但向量数据库不理解语义，根据最高的相似性找出来“猪肉的一百种做法”“猪肉怎么做才好吃”，这些上下文的有效性是存在问题的，自然会影响输出结果。</p><p>其中还有更多细节，比如使用摘要的方式保存长期记忆，查询路由，查询转换等优化技术，可以参考更多信息：<a href="https://zhuanlan.zhihu.com/p/675509396">大模型RAG</a></p><h4 id="ReAct-推理行动结合"><a href="#ReAct-推理行动结合" class="headerlink" title="ReAct 推理行动结合"></a>ReAct 推理行动结合</h4><p>此 ReAct 不是前端的 ReactJS 开发框架，而是 推理和行动（ Reasoning and Acting）的缩写，ReAct方式的关键就是协调大语言模型和外部的信息获取，与其他功能交互，通过推理获知需要执行的行动，构造参数调用外部扩展并拿到返回后。<br><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240731225512-xso509s.png" alt="image" data-caption="image" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240731225431-c66x59j.png" alt="image" data-caption="image" loading="lazy">​</p><p>更多信息可参考论文：<a href="https://arxiv.org/pdf/2210.03629">REAC T: SYNERGIZING REASONING AND ACTING IN LANGUAGE MODEL</a></p><h4 id="PAL-程序辅助语言模型"><a href="#PAL-程序辅助语言模型" class="headerlink" title="PAL 程序辅助语言模型"></a>PAL 程序辅助语言模型</h4><p>这个说法源自论文：<a href="https://arxiv.org/pdf/2211.10435">PAL: Program-aided Language Models</a>，由于大模型的推理过程无法做真正的逻辑推理和运算，所以就绕一个弯，让模型在碰到这种需要计算的情况时，<strong>不生成答案，而是生成一段代码</strong>，让代码解释器运行这段生成的代码后，拿到运行结果再作为最终输出，有助于解决复杂的计算问题。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240731224432-j0dlsk2.png" alt="image" data-caption="image" loading="lazy">​</p><p>好比数学计算，对比 9.11 是否大于 9.8 这类的问题都可以通过这个方法处理。</p><h4 id="Agent-智能体"><a href="#Agent-智能体" class="headerlink" title="Agent 智能体"></a>Agent 智能体</h4><p>智能体是一种通用问题解决器，从软件工程的角度看来，智能体是一种基于大语言模型的，具备规划思考能力、记忆能力、使用工具函数的能力，能自主完成给定任务的计算机程序。</p><p>在基于 LLM 的智能体中，LLM 的充当着智能体的“大脑”的角色，同时还有 3 个关键部分：</p><ul><li><strong>规划（Planning）</strong>  : 智能体会把大型任务<strong>分解为子任务</strong>，并规划执行任务的流程；智能体会对任务执行的过程进行<strong>思考和反思</strong>，从而决定是继续执行任务，或判断任务完结并终止运行。</li><li><strong>记忆（Memory）</strong> : 短期记忆，是指在执行任务的过程中的上下文，会在子任务的执行过程产生和暂存，在任务完结后被清空。长期记忆是长时间保留的信息，一般是指外部知识库，通常用向量数据库来存储和检索。</li><li><strong>工具使用（Tool use）</strong>  为智能体配备工具 API，比如：计算器、搜索工具、代码执行器、数据库查询工具等。有了这些工具 API，智能体就可以是物理世界交互，解决实际的问题。</li></ul><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240731231350-ob1gmal.png" alt="image" data-caption="image" loading="lazy">​</p><p>比较让人看到美好未来的几个 Agent 项目：</p><p><a href="https://websim.ai/">websim</a>，可以通过语言设计网站，实现各种天马行空的想象，比如这就是我用几句话写的<a href="https://websim.ai/@restlesstwilight26799614/snakegame-with-ai">带 AI 的贪吃蛇游戏</a></p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240801234224-q4p9hbf.png" alt="image" data-caption="image" loading="lazy">​</p><p>还可以用来写个表白网页什么的，AI很懂你</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240801234246-65ea0gv.png" alt="image" data-caption="image" loading="lazy">​</p><p><a href="https://github.com/BAAI-Agents/Cradle">Cardle</a>，让大模型操作电脑完成复杂任务，比较出圈的是玩荒野大镖客2、星露谷等游戏，还有篇相关论文：<a href="https://arxiv.org/pdf/2403.03186">CRADLE: Empowering Foundation Agents Towards General Computer Control</a></p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240801234653-7j6s7zd.png" alt="image" data-caption="image" loading="lazy">​</p><p><a href="https://github.com/joonspk-research/generative_agents">Generative Agents</a>，人类行为的交互式模拟，就是模拟了一个虚拟小镇中，不同人设背景的角色不被人干预的自由生活，有种赛博宠物的既视感</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240801234902-6t4okku.png" alt="image" data-caption="image" loading="lazy">​</p><h2 id="实际编码工作中怎么应用-AI"><a href="#实际编码工作中怎么应用-AI" class="headerlink" title="实际编码工作中怎么应用 AI"></a>实际编码工作中怎么应用 AI</h2><p>所谓工欲善其事必先利其器，先把大模型和相关的工具准备好，才能根据实际需要嵌入到自己的工作流中</p><h3 id="稳定的模型-API"><a href="#稳定的模型-API" class="headerlink" title="稳定的模型 API"></a>稳定的模型 API</h3><p>首先第一件事情是如何拿到稳定可用的模型 API，这里有几个方向：</p><ul><li>使用国外的大模型 API，比如最知名的 openai 系列、Claude 系列、Mistrial 等，优点在于部分任务的能力会强于国内的大模型，缺点在于充值麻烦，以及需要解决国内访问网络问题</li><li>用国内的大模型 API，优势是充值方便，并不存在网络问题，缺点是部分能力较高的模型价格也不便宜</li><li>折中方案，找一家大模型聚合代理提供商，国内的提供商一般支持支付宝、微信充值，自用也足够了</li></ul><h4 id="国外的大模型"><a href="#国外的大模型" class="headerlink" title="国外的大模型"></a>国外的大模型</h4><p><a href="https://openai.com/">Openai</a>，新出的 gpt-4o-mini 价格低，但实际在编码任务中，复杂逻辑处理不好，但 gpt-4o 模型是效果和速度之间的均衡，但 4o 和 4-turbo 的成本还是比较高的</p><table><thead><tr><th>模型</th><th>描述</th><th>上下文长度</th><th>最大输出长度</th><th>输入价格</th><th>输出价格</th></tr></thead><tbody><tr><td>gpt-4-turbo</td><td>效果最好，慢，较贵</td><td>128K</td><td>4K</td><td>$10 / 百万 tokens</td><td>$30 / 百万 tokens</td></tr><tr><td>gpt-4o</td><td>效果均衡，快，稍贵</td><td>128K</td><td>4K</td><td>$5 / 百万 tokens</td><td>$15 / 百万 tokens</td></tr><tr><td>gpt-4o-mini</td><td>效果一般，快，便宜</td><td>128K</td><td>4K</td><td>$0.15 / 百万 tokens</td><td>$0.6 / 百万 tokens</td></tr></tbody></table><blockquote><p>注：Openai 注册需要用境外的手机号，使用 API 需要用境外信用卡，并且不能从大陆、香港等不允许服务的区域调用，容易被 ban</p><p>一些折中的办法：</p><ul><li>注册时要找一个尽量干净好的梯子，这个就不做推荐了</li><li>做一个低成本的代理节点，比如后文提到的 openai-scf-proxy</li><li>境外手机号可以找虚拟手机号平台，比较知名的是 sms-activate.org/cn</li><li>境外信用卡可以用虚拟卡，这个平台也不做推荐了，操作复杂费率高，大概流程是法币充虚拟币，再冲到虚拟卡平台，绑定 openai 之后付款</li><li>找代充需要注意信誉以及封号风险</li></ul></blockquote><p><a href="https://www.anthropic.com/">Claude</a>，注册和充值与 openai 类似，最近的 claude-3.5-sonnet 挺出圈的</p><table><thead><tr><th>模型</th><th>描述</th><th>上下文长度</th><th>最大输出长度</th><th>输入价格</th><th>输出价格</th></tr></thead><tbody><tr><td>claude-3.5-opus</td><td>效果好，真贵</td><td>200K</td><td>4K</td><td>$15 / 百万 tokens</td><td>$75 / 百万 tokens</td></tr><tr><td>claude-3-sonnet</td><td>效果均衡，快，稍贵</td><td>200K</td><td>4K</td><td>$3 / 百万 tokens</td><td>$15 / 百万 tokens</td></tr><tr><td>claude-3-haiku</td><td>效果一般，快，便宜</td><td>200K</td><td>4K</td><td>$0.25 / 百万 tokens</td><td>$1.25 / 百万 tokens</td></tr></tbody></table><h4 id="国内的大模型"><a href="#国内的大模型" class="headerlink" title="国内的大模型"></a>国内的大模型</h4><p>下面几个是实际用过还不错的：</p><p><a href="https://chat.deepseek.com/">DeepSeek</a>，价格非常低廉，在处理编码任务上有优势</p><table><thead><tr><th>模型</th><th>描述</th><th>上下文长度</th><th>最大输出长度</th><th>输入价格</th><th>输出价格</th></tr></thead><tbody><tr><td>deepseek-chat ^(1)^</td><td>擅长通用对话任务</td><td>128K</td><td>4K (8K ^Beta (2)^)</td><td>1 元 / 百万 tokens</td><td>2 元 / 百万 tokens</td></tr><tr><td>deepseek-coder ^(1)^</td><td>擅长处理编程和数学任务</td><td>128K</td><td>4K (8K ^Beta (2)^)</td><td>1 元 / 百万 tokens</td><td>2 元 / 百万 tokens</td></tr></tbody></table><p><a href="https://kimi.moonshot.cn/">Kimi</a>，效果还好，是真挺贵</p><table><thead><tr><th>模型</th><th>计费单位</th><th>价格</th></tr></thead><tbody><tr><td>moonshot-v1-8k</td><td>1M tokens</td><td>¥12.00</td></tr><tr><td>moonshot-v1-32k</td><td>1M tokens</td><td>¥24.00</td></tr><tr><td>moonshot-v1-128k</td><td>1M tokens</td><td>¥60.00</td></tr></tbody></table><p><a href="https://platform.stepfun.com/">阶跃星辰</a>，效果还好，价格没啥优势</p><table><thead><tr><th align="center">模型</th><th>计费单位</th><th>输入价格</th><th>输出价格</th></tr></thead><tbody><tr><td align="center">step-1-8k</td><td>1M tokens</td><td>5元</td><td>20元</td></tr><tr><td align="center">step-1-32k</td><td>1M tokens</td><td>15元</td><td>70元</td></tr><tr><td align="center">step-1-128k</td><td>1M tokens</td><td>40元</td><td>200元</td></tr><tr><td align="center">step-1-256k</td><td>1M tokens</td><td>95元</td><td>300元</td></tr><tr><td align="center">step-2-16k-nightly</td><td>1M tokens</td><td>38元</td><td>120元</td></tr></tbody></table><h4 id="模型聚合"><a href="#模型聚合" class="headerlink" title="模型聚合"></a>模型聚合</h4><p><a href="https://www.gptapi.us/">gptapi</a>，国内的模型聚合提供商，价格是真低，支持支付宝</p><table><thead><tr><th>模型</th><th>官网价格</th><th>本站价格</th><th>并发</th><th>折扣</th><th>稳定性</th></tr></thead><tbody><tr><td>gpt-4o-mini</td><td>Input：$0.00015 / 1K tokens<br>Output：$0.0006 / 1K tokens</td><td>Input：¥0.00015 / 1K tokens<br>Output：¥0.0006 / 1K tokens</td><td>支持</td><td>1.4折</td><td>优</td></tr><tr><td>gpt-4o-mini-2024-07-18</td><td>Input：$0.00015 / 1K tokens<br>Output：$0.0006 / 1K tokens</td><td>Input：¥0.0009 / 1K tokens<br>Output：¥0.0036 / 1K tokens</td><td>支持</td><td>8.4折</td><td>优</td></tr><tr><td>claude-3-5-sonnet</td><td>Input：$0.003 / 1K tokens<br>Output：$0.015 / 1K tokens</td><td>Input：¥0.0054 / 1K tokens<br>Output：¥0.027 / 1K tokens</td><td>不支持</td><td>2.5折</td><td>优</td></tr><tr><td>claude-3-5-sonnet-20240620</td><td>Input：$0.003 / 1K tokens<br>Output：$0.015 / 1K tokens</td><td>Input：¥0.01944 / 1K tokens<br>Output：¥0.0972 / 1K tokens</td><td>不支持</td><td>9折</td><td>优</td></tr><tr><td>gpt-4o-2024-05-13</td><td>Input：$0.005 / 1K tokens<br>Output：$0.015 / 1K tokens</td><td>Input：¥0.03 / 1K tokens<br>Output：¥0.09 / 1K tokens</td><td>支持</td><td>8.4折</td><td>优</td></tr><tr><td>gpt-4o</td><td>Input：$0.005 / 1K tokens<br>Output：$0.015 / 1K tokens</td><td>Input：¥0.005 / 1K tokens<br>Output：¥0.015 / 1K tokens</td><td>不支持</td><td>1.4折</td><td>优</td></tr><tr><td>gemini-1.5-pro</td><td>Input：$0.0025 / 1K tokens<br>Output：$0.0075 / 1K tokens</td><td>Input：¥0.009 / 1K tokens<br>Output：¥0.027 / 1K tokens</td><td>不支持</td><td>5折</td><td>优</td></tr></tbody></table><p><a href="https://openrouter.ai/">OpenRouter</a>，境外的一家模型聚合提供商，价格和官网差不多，但特色是支持很多开源大模型，可选丰富，需要用虚拟币或信用卡充值后方可使用，并且需要解决网络问题</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240801104154-ag3ykbx.png" alt="image" data-caption="image" loading="lazy">​</p><h3 id="大模型工具"><a href="#大模型工具" class="headerlink" title="大模型工具"></a>大模型工具</h3><h4 id="解决境外大模型网络问题"><a href="#解决境外大模型网络问题" class="headerlink" title="解决境外大模型网络问题"></a>解决境外大模型网络问题</h4><p>比较直接的方式是，购买一个科学上网，然后在所有使用的电脑都装上，并注意在使用过程中的外网 IP</p><h4 id="openai-scf-proxy-云函数代理"><a href="#openai-scf-proxy-云函数代理" class="headerlink" title="openai-scf-proxy 云函数代理"></a>openai-scf-proxy 云函数代理</h4><p><a href="https://github.com/Ice-Hazymoon/openai-scf-proxy">Github - openai-scf-proxy</a>，使用腾讯云函数一分钟搭建 OpenAI 代理，低成本封装并隔了一个 SG 区域的 IP 调用 openai，相对安全，一直没被 BAN</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240801113600-ulsvz9z.png" alt="image" data-caption="image" loading="lazy">​</p><h4 id="One-API-整合大模型"><a href="#One-API-整合大模型" class="headerlink" title="One-API 整合大模型"></a>One-API 整合大模型</h4><p><a href="https://github.com/songquanpeng/one-api">One-API</a>，是一个接口管理 &amp; 分发系统，通过 One API 端点，可以将其他服务都集成到一处，并暴露统一的 openai API</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240801104420-h7nij48.png" alt="image" data-caption="image" loading="lazy">​</p><p>最好薅一台云服务器做这个事情，并<strong>注意公网服务安全</strong>，最近云服务器都很便宜，阿里云、腾讯云之类的都有很大的折扣，比如 <a href="https://t.aliyun.com/U/erCeeo">阿里云 ￥99/年的轻量服务器</a></p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240801114000-dto63kt.png" alt="image" data-caption="image" loading="lazy">​</p><p>以及腾讯云也有类似的好价，<a href="https://curl.qcloud.com/f812J6yp">【腾讯云】2核2G云服务器新老同享 99元/年，续费同价</a></p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240801114254-1vumfus.png" alt="image" data-caption="image" loading="lazy">​</p><p>部署好之后就可以在后台维护相关渠道，并创建好令牌供调用</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240801114504-bx423in.png" alt="image" data-caption="image" loading="lazy">​</p><h4 id="Cursor-sh-好用的-AI-编辑器"><a href="#Cursor-sh-好用的-AI-编辑器" class="headerlink" title="Cursor.sh 好用的 AI 编辑器"></a>Cursor.sh 好用的 AI 编辑器</h4><p>市面上很多插件对 AI 的应用形式是自动补全，比如写好注释之后，稍等片刻自动补全代码，但这会让我有一种紧迫感，一定要在等待时间内打打字不然推理 token 就花出去了，所以个人更喜欢这种精心思考之后再让大模型推理的编辑器，可以让人慢慢的滤清逻辑思路，思考应该填充什么上下文才能更好的生成结果。</p><p>不过 AI 编辑器始终只是辅助，它会根据现在的代码来生成新的代码，代码质量取决于使用 AI 的人和堆积的历史代码，如果不对结构做合理的规划、不对业务做适当的拆分，用 AI 写的代码慢慢会变成一座危房，这篇博客也表示了类似的看法：<a href="https://github.blog/news-insights/research/survey-reveals-ais-impact-on-the-developer-experience/">Survey reveals AI’s impact on the developer experience</a>。</p><p>用 AI 编辑器之后，可以有更多的心力放在代码架构、业务逻辑等事情上，可以通过一个好习惯增加自己用 AI 的效率：<strong>发现自己手写代码的时候反思一下，为什么这里不能用 AI 去处理?</strong></p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240801235219-871883j.png" alt="image" data-caption="image" loading="lazy">​</p><p>这个注册之后额度有限，所以最好对接一下上面说的 one-api，自己买划算和方便不少。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240801235557-plw6jm7.png" alt="image" data-caption="image" loading="lazy">​</p><h4 id="搜集整理提高效率的-Bot"><a href="#搜集整理提高效率的-Bot" class="headerlink" title="搜集整理提高效率的 Bot"></a>搜集整理提高效率的 Bot</h4><p><a href="https://github.com/lobehub/lobe-chat">LobeChat</a>，与 GPTNext 这类软件类似，只是让我们有一个放 bot 的地方罢了，平时开发过程中整理一些重复的工作、麻烦的事情都可以封装为一个 BOT，下次再有重复的场景就可以指挥 AI 干活儿了。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-llm-think/image-20240802000653-mgz4d7c.png" alt="image" data-caption="image" loading="lazy">​</p><h2 id="下一步是什么"><a href="#下一步是什么" class="headerlink" title="下一步是什么"></a>下一步是什么</h2><p>感性认知并学会使用大模型工具只是第一步，AI 领域有太多东西值得去探索和学习，我能想到还可以做的事情有：</p><ul><li>自己部署一个私有的大模型 API，了解其量化、各种推理格式的异同，推荐 HuggingFace 出品的 <a href="https://huggingface.co/docs/text-generation-inference/en/index">Text Generation Inference</a>、<a href="https://github.com/ggerganov/llama.cpp">llama.cpp</a> 等</li><li>更加深度的理解大模型原理和推理训练过程，推荐从一个最小的大模型项目开始 <a href="https://github.com/karpathy/llama2.c">llama2.c</a></li><li>微调、再训练大模型，推荐，推荐 HuggingFace 的 <a href="https://huggingface.co/autotrain">AutoTrain</a></li><li>在力所能及的范围内参与开源项目并为之贡献，比如 LangChain 就有各种常见语言的实现版本可以参与</li></ul><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>通过这篇文档，基本可以对大模型有个感性的认知，AI 变化很快，世界变化更快，我们需要保持开放学习的心态，快速接纳了解新鲜事物，快速的为己所用，这样才能让我们更好的跟上时代，跟上变化。</p><p>目前的大模型局限很多，只能作为辅助工具去使用，更多实现细节、架构、逻辑是否合理需要靠人类去辨别，真正解决问题的能力和技术视野是目前的 AI 无法替代的，工具用的很爽的同时，也可以停下来思考不断增长的生产力和工具对我们有何意义（推荐之前写的一篇博客：<a href="https://blog.wj2015.com/2023/04/05/%E5%9F%BA%E4%BA%8Echatgpt%E6%81%90%E6%80%96%E8%83%BD%E5%8A%9B%E7%9A%84%E9%AB%98%E6%95%88%E7%8E%87%E5%B7%A5%E5%85%B7%E6%B6%8C%E7%8E%B0%E6%84%8F%E5%91%B3%E7%9D%80%E4%BB%80%E4%B9%88/">基于chatgpt恐怖能力的高效率工具涌现意味着什么</a>）。</p><h3 id="参考文档"><a href="#参考文档" class="headerlink" title="参考文档"></a>参考文档</h3><p>来源自搜索引擎和网络，排名不分先后</p><ul><li><a href="https://sspai.com/post/80316">搞懂语言大模型</a></li><li><a href="https://developer.volcengine.com/articles/7318306296308957238">大模型技术基础架构盘点与开源工作速览</a></li><li><a href="https://mp.weixin.qq.com/s/8U_mUn_dRDrB99OppK7oNA?from_wecom=1">探讨关于ChatGPT的五个最核心问题</a></li><li><a href="https://blog.wj2015.com/static/assets/blog-llm-think/ChatGPT%E5%8E%9F%E7%90%86-230602193139.pdf">ChatGPT 原理脑图</a></li><li><a href="https://blog.wj2015.com/2023/04/05/%E5%9F%BA%E4%BA%8Echatgpt%E6%81%90%E6%80%96%E8%83%BD%E5%8A%9B%E7%9A%84%E9%AB%98%E6%95%88%E7%8E%87%E5%B7%A5%E5%85%B7%E6%B6%8C%E7%8E%B0%E6%84%8F%E5%91%B3%E7%9D%80%E4%BB%80%E4%B9%88/">基于ChatGPT恐怖能力的高效率工具涌现意味着什么</a></li><li><a href="https://mp.weixin.qq.com/s?__biz=MzU2NjU3OTc5NA==&amp;mid=2247577211&amp;idx=2&amp;sn=cd6e90f791ca42c964c66d234a847ea9&amp;chksm=fca9a946cbde2050660ea1244a080c879a51570015082b77e6cd160d9cf3286eb8057e244d98&amp;scene=27">大模型工作原理</a></li><li><a href="https://www.bilibili.com/video/BV17t4218761">【视频】速通大模型原理</a></li><li><a href="https://blog.csdn.net/qq_39172059/article/details/136661824">Embedding 工作原理</a></li><li><a href="https://aistudio.baidu.com/blog/detail/16">多模态大模型</a></li><li><a href="https://mp.weixin.qq.com/s/Mdt55azb2ZAuxWNxTM8-mw">多头注意力</a></li><li><a href="https://blog.csdn.net/2301_79342058/article/details/134820283">QKV 和多头注意力机制</a></li><li><a href="https://zhuanlan.zhihu.com/p/631463712">从词到数：Tokenizer与Embedding串讲</a></li><li><a href="https://zhuanlan.zhihu.com/p/628511161">大模型推理原理和加速</a></li></ul><p>‍</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;h3 id=&quot;为什么先谈感性认知&quot;&gt;&lt;a href=&quot;#为什么先谈感性认知&quot;</summary>
        
      
    
    
    
    <category term="chatgpt" scheme="https://blog.wj2015.com/tags/chatgpt/"/>
    
    <category term="思考" scheme="https://blog.wj2015.com/tags/%E6%80%9D%E8%80%83/"/>
    
    
    <category term="机器学习" scheme="https://blog.wj2015.com/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
    <category term="随笔" scheme="https://blog.wj2015.com/categories/%E9%9A%8F%E7%AC%94/"/>
    
  </entry>
  
  <entry>
    <title>为什么 git rebase 合并代码总是很多冲突</title>
    <link href="https://blog.wj2015.com/2024/02/27/%E4%B8%BA%E4%BB%80%E4%B9%88%20git%20rebase%20%E5%90%88%E5%B9%B6%E4%BB%A3%E7%A0%81%E6%80%BB%E6%98%AF%E4%BC%9A%E5%87%BA%E7%8E%B0%E5%BE%88%E5%A4%9A%E5%86%B2%E7%AA%81/"/>
    <id>https://blog.wj2015.com/2024/02/27/%E4%B8%BA%E4%BB%80%E4%B9%88%20git%20rebase%20%E5%90%88%E5%B9%B6%E4%BB%A3%E7%A0%81%E6%80%BB%E6%98%AF%E4%BC%9A%E5%87%BA%E7%8E%B0%E5%BE%88%E5%A4%9A%E5%86%B2%E7%AA%81/</id>
    <published>2024-02-27T07:09:47.000Z</published>
    <updated>2026-08-12T03:28:46.856Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h1 id="为什么-git-rebase-合并代码总是很多冲突"><a href="#为什么-git-rebase-合并代码总是很多冲突" class="headerlink" title="为什么 git rebase 合并代码总是很多冲突"></a>为什么 git rebase 合并代码总是很多冲突</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>与 git 打了几年交道，合并代码是多人协同的时候最常见的操作，但合并代码有两种方式： MERGE 和 REBASE，多数人都知道 rebase 可以让分支提交变得更新性，使用 merge 合并分支总是会有很多分叉。但同时 rebase 也会让我们解决非常多的提交，不禁思考为什么会发生这样的现象呢？</p><h2 id="merge-的本质"><a href="#merge-的本质" class="headerlink" title="merge 的本质"></a>merge 的本质</h2><p><strong>git merge</strong> 是将两个分支的改动合并到一起。如果两个分支都有新的提交，Git 会创建一个新的提交，这个提交有两个父提交。这种方式保留了完整的提交历史和分支结构，但可能会导致历史记录复杂。</p><figure class="highlight bash"><table><tbody><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><code class="hljs bash">A---B---C feature<br>     /         \<br>D---E---F---G master<br></code></pre></td></tr></tbody></table></figure><p>在上图中，如果我们在 <code>master</code>​ 分支上执行 <code>git merge feature</code>​，会得到下面的结果：</p><figure class="highlight bash"><table><tbody><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><code class="hljs bash">A---B---C feature<br>     /         \<br>D---E---F---G---H master<br></code></pre></td></tr></tbody></table></figure><p>​<code>H</code>​ 是新的合并提交，它的父提交是 <code>G</code>​ 和 <code>C</code>​。</p><h2 id="rebase-的本质"><a href="#rebase-的本质" class="headerlink" title="rebase 的本质"></a>rebase 的本质</h2><p><strong>git rebase</strong> 是将一系列提交应用到另一个基础之上。如果两个分支都有新的提交，Git 会逐个取出分支上的提交，然后在目标分支上重新应用。这种方式会创建一条线性的提交历史，使得历史记录更清晰，但是会丢失一些历史信息。</p><figure class="highlight bash"><table><tbody><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><code class="hljs bash">A---B---C feature<br>     /<br>D---E---F---G master<br></code></pre></td></tr></tbody></table></figure><p>在上图中，如果我们在 <code>feature</code>​ 分支上执行 <code>git rebase master</code>​，会得到下面的结果：</p><figure class="highlight bash"><table><tbody><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><code class="hljs bash">             A<span class="hljs-string">'--B'</span>--C<span class="hljs-string">' feature</span><br><span class="hljs-string">             /</span><br><span class="hljs-string">D---E---F---G master</span><br></code></pre></td></tr></tbody></table></figure><p>​<code>A'</code>​, <code>B'</code>​, <code>C'</code>​ 是原来 <code>feature</code>​ 分支上的提交 <code>A</code>​, <code>B</code>​, <code>C</code>​ 在 <code>master</code>​ 分支上重新应用的结果。</p><p>至于为什么 <code>rebase</code>​ 时经常需要手动处理冲突，这是因为 <code>rebase</code>​ 是逐个应用提交，所以如果有多个提交修改了同一部分代码，就需要多次解决冲突。而 <code>merge</code>​ 只需要解决一次冲突，因为它是一次性合并所有改动。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本文比较浅显，只是直接了当的解释了一个常见困惑，本质上还是 commit 合并的原理不同。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>详细的 git rebase 操作指南：<a href="https://www.codercto.com/a/45325.html">https://www.codercto.com/a/45325.html</a></p><p>git merge 操作指南：<a href="https://blog.csdn.net/u010665216/article/details/129885666">https://blog.csdn.net/u010665216/article/details/129885666</a></p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h1 id=&quot;为什么-git-rebase-合并代码总是很多冲突&quot;&gt;&lt;a href=&quot;#为什么-git-rebase-合并代码总是很多冲突&quot; class=&quot;headerlink&quot; title=&quot;为什么 git rebase</summary>
        
      
    
    
    
    <category term="Linux" scheme="https://blog.wj2015.com/categories/Linux/"/>
    
    <category term="git" scheme="https://blog.wj2015.com/tags/git/"/>
    
    
    <category term="后端开发" scheme="https://blog.wj2015.com/categories/%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91/"/>
    
    <category term="多人协作" scheme="https://blog.wj2015.com/tags/%E5%A4%9A%E4%BA%BA%E5%8D%8F%E4%BD%9C/"/>
    
  </entry>
  
  <entry>
    <title>Python 并发编程方案异同</title>
    <link href="https://blog.wj2015.com/2024/02/27/Python%20%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E6%96%B9%E6%A1%88%E5%BC%82%E5%90%8C/"/>
    <id>https://blog.wj2015.com/2024/02/27/Python%20%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E6%96%B9%E6%A1%88%E5%BC%82%E5%90%8C/</id>
    <published>2024-02-27T06:04:35.000Z</published>
    <updated>2026-08-12T03:28:46.852Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h1 id="Python-并发编程方案异同"><a href="#Python-并发编程方案异同" class="headerlink" title="Python 并发编程方案异同"></a>Python 并发编程方案异同</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>博主最近用 Python 处理一些业务数据，每条数据都需要调用几个很慢的外部接口，初版实现是同步的，顺序的处理，但业务实际使用过程中，业务那边提出脚本运行缓慢，其中一条优化就是引入并发编程提高效率。</p><h2 id="方向"><a href="#方向" class="headerlink" title="方向"></a>方向</h2><p>Python 实现并发编程有如下常见方向：</p><ul><li>协程：Python 3.5 以后的版本可用 asyncio 和 async/await 特性，可以实现单个线程的多任务并发</li><li>多线程：Python 提供了 threading 模块来支持多线程</li><li>多进程：Python 的 multiprocessing 模块可以创建进程</li></ul><p>写一个模拟当时场景的 DEMO：</p><figure class="highlight python"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> time<br><span class="hljs-keyword">from</span> time <span class="hljs-keyword">import</span> sleep<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">handle_task</span>(<span class="hljs-params"><span class="hljs-built_in">id</span>, name</span>):<br>    <span class="hljs-comment"># 在这里处理你的任务</span><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"处理任务: ID=<span class="hljs-subst">{<span class="hljs-built_in">id</span>}</span>, Name=<span class="hljs-subst">{name}</span>"</span>)<br>    <span class="hljs-comment"># 模拟 IO 消耗</span><br>    sleep(<span class="hljs-number">0.5</span>)<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">main</span>():<br>    <span class="hljs-comment"># 十组数据</span><br>    data = [(<span class="hljs-number">1</span>, <span class="hljs-string">'任务1'</span>), (<span class="hljs-number">2</span>, <span class="hljs-string">'任务2'</span>), (<span class="hljs-number">3</span>, <span class="hljs-string">'任务3'</span>), (<span class="hljs-number">4</span>, <span class="hljs-string">'任务4'</span>), (<span class="hljs-number">5</span>, <span class="hljs-string">'任务5'</span>),<br>            (<span class="hljs-number">6</span>, <span class="hljs-string">'任务6'</span>), (<span class="hljs-number">7</span>, <span class="hljs-string">'任务7'</span>), (<span class="hljs-number">8</span>, <span class="hljs-string">'任务8'</span>), (<span class="hljs-number">9</span>, <span class="hljs-string">'任务9'</span>), (<span class="hljs-number">10</span>, <span class="hljs-string">'任务10'</span>)]<br><br>    <span class="hljs-comment"># 循环调用 handle_task 函数</span><br>    <span class="hljs-keyword">for</span> <span class="hljs-built_in">id</span>, name <span class="hljs-keyword">in</span> data:<br>        handle_task(<span class="hljs-built_in">id</span>, name)<br><br><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:<br>    start_time = time.time()<br>    main()<br>    end_time = time.time()<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"main 函数的执行耗时: <span class="hljs-subst">{end_time - start_time}</span> 秒"</span>)<br></code></pre></td></tr></tbody></table></figure><p>运行结果为：</p><figure class="highlight python"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs python">处理任务: ID=<span class="hljs-number">1</span>, Name=任务<span class="hljs-number">1</span><br>处理任务: ID=<span class="hljs-number">2</span>, Name=任务<span class="hljs-number">2</span><br>处理任务: ID=<span class="hljs-number">3</span>, Name=任务<span class="hljs-number">3</span><br>处理任务: ID=<span class="hljs-number">4</span>, Name=任务<span class="hljs-number">4</span><br>处理任务: ID=<span class="hljs-number">5</span>, Name=任务<span class="hljs-number">5</span><br>处理任务: ID=<span class="hljs-number">6</span>, Name=任务<span class="hljs-number">6</span><br>处理任务: ID=<span class="hljs-number">7</span>, Name=任务<span class="hljs-number">7</span><br>处理任务: ID=<span class="hljs-number">8</span>, Name=任务<span class="hljs-number">8</span><br>处理任务: ID=<span class="hljs-number">9</span>, Name=任务<span class="hljs-number">9</span><br>处理任务: ID=<span class="hljs-number">10</span>, Name=任务<span class="hljs-number">10</span><br>main 函数的执行耗时: <span class="hljs-number">5.030256271362305</span> <br></code></pre></td></tr></tbody></table></figure><h2 id="协程方案"><a href="#协程方案" class="headerlink" title="协程方案"></a>协程方案</h2><p>asyncio 可用于编写单线程并发代码，<code>asyncio</code>​​ 的事件循环是其并发模型的核心，它负责调度和执行任务，处理 I/O 等待，以及提供各种同步变量同步。</p><p>协程方案的局限性在于这是一个单线程模型，依赖事件循环做上下文的切换，所以一旦任务中存在占用 IO 较长且未经过 async/await 做协程处理，就可能拖慢整体效率。</p><figure class="highlight python"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> asyncio<br><span class="hljs-keyword">import</span> time<br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">handle_task</span>(<span class="hljs-params"><span class="hljs-built_in">id</span>, name</span>):<br>    <span class="hljs-comment"># 在这里处理你的任务</span><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"处理任务: ID=<span class="hljs-subst">{<span class="hljs-built_in">id</span>}</span>, Name=<span class="hljs-subst">{name}</span>"</span>)<br>    <span class="hljs-comment"># 模拟 IO 消耗</span><br>    time.sleep(<span class="hljs-number">0.5</span>)<br>    <span class="hljs-comment"># await asyncio.sleep(0.5)</span><br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">def</span> <span class="hljs-title function_">main</span>():<br>    <span class="hljs-comment"># 十组数据</span><br>    data = [(<span class="hljs-number">1</span>, <span class="hljs-string">'任务1'</span>), (<span class="hljs-number">2</span>, <span class="hljs-string">'任务2'</span>), (<span class="hljs-number">3</span>, <span class="hljs-string">'任务3'</span>), (<span class="hljs-number">4</span>, <span class="hljs-string">'任务4'</span>), (<span class="hljs-number">5</span>, <span class="hljs-string">'任务5'</span>),<br>            (<span class="hljs-number">6</span>, <span class="hljs-string">'任务6'</span>), (<span class="hljs-number">7</span>, <span class="hljs-string">'任务7'</span>), (<span class="hljs-number">8</span>, <span class="hljs-string">'任务8'</span>), (<span class="hljs-number">9</span>, <span class="hljs-string">'任务9'</span>), (<span class="hljs-number">10</span>, <span class="hljs-string">'任务10'</span>)]<br><br>    <span class="hljs-comment"># 创建一个任务列表</span><br>    tasks = [handle_task(<span class="hljs-built_in">id</span>, name) <span class="hljs-keyword">for</span> <span class="hljs-built_in">id</span>, name <span class="hljs-keyword">in</span> data]<br><br>    <span class="hljs-comment"># 使用 asyncio.gather 运行所有任务</span><br>    <span class="hljs-keyword">await</span> asyncio.gather(*tasks)<br><br><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:<br>    start_time = time.time()<br>    asyncio.run(main())<br>    end_time = time.time()<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"main 函数的执行耗时: <span class="hljs-subst">{end_time - start_time}</span> 秒"</span>)<br><br></code></pre></td></tr></tbody></table></figure><p>关键部分包括：</p><ul><li>​<code>async def</code>​：定义一个协程函数。协程函数是可以使用 <code>await</code>​ 语句的函数。</li><li>​<code>await asyncio.sleep(0.5)</code>​：这是一个异步的 sleep 调用。它会将控制权交还给事件循环，允许其他任务在等待期间运行。</li><li>​<code>asyncio.gather(*tasks)</code>​：这个函数接收一系列的协程对象，然后并发地运行它们。当所有的协程都完成时，它返回一个结果列表。</li><li>​<code>asyncio.run(main())</code>​：这是运行协程的主函数。它创建一个事件循环，然后运行传入的协程。当协程完成时，它关闭事件循环并返回结果。</li></ul><p>但是我们运行代码后会发现总执行时间依旧是 5s，这是因为 time.sleep 会让整个 Python 解释器（包括事件循环）都会被阻塞，无法执行其他任务</p><figure class="highlight python"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs python">处理任务: ID=<span class="hljs-number">1</span>, Name=任务<span class="hljs-number">1</span><br>处理任务: ID=<span class="hljs-number">2</span>, Name=任务<span class="hljs-number">2</span><br>处理任务: ID=<span class="hljs-number">3</span>, Name=任务<span class="hljs-number">3</span><br>处理任务: ID=<span class="hljs-number">4</span>, Name=任务<span class="hljs-number">4</span><br>处理任务: ID=<span class="hljs-number">5</span>, Name=任务<span class="hljs-number">5</span><br>处理任务: ID=<span class="hljs-number">6</span>, Name=任务<span class="hljs-number">6</span><br>处理任务: ID=<span class="hljs-number">7</span>, Name=任务<span class="hljs-number">7</span><br>处理任务: ID=<span class="hljs-number">8</span>, Name=任务<span class="hljs-number">8</span><br>处理任务: ID=<span class="hljs-number">9</span>, Name=任务<span class="hljs-number">9</span><br>处理任务: ID=<span class="hljs-number">10</span>, Name=任务<span class="hljs-number">10</span><br>main 函数的执行耗时: <span class="hljs-number">5.037123203277588</span> <br></code></pre></td></tr></tbody></table></figure><blockquote><p>这里只是模拟任务，time.sleep 更能模拟真实场景下的 IO 和计算资源占用，如果现实场景中需要在协程函数中使用 sleep，可以使用 <code>asyncio.sleep(0.5)</code>​，文件操作可以用 asyncio 包中的相关函数优化协程执行速度。</p></blockquote><p>如果把上面的 <code>time.sleep()</code>​ 改为 <code>asyncio.sleep()</code>​，执行日志如下：</p><figure class="highlight python"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs python">处理任务: ID=<span class="hljs-number">1</span>, Name=任务<span class="hljs-number">1</span><br>处理任务: ID=<span class="hljs-number">2</span>, Name=任务<span class="hljs-number">2</span><br>处理任务: ID=<span class="hljs-number">3</span>, Name=任务<span class="hljs-number">3</span><br>处理任务: ID=<span class="hljs-number">4</span>, Name=任务<span class="hljs-number">4</span><br>处理任务: ID=<span class="hljs-number">5</span>, Name=任务<span class="hljs-number">5</span><br>处理任务: ID=<span class="hljs-number">6</span>, Name=任务<span class="hljs-number">6</span><br>处理任务: ID=<span class="hljs-number">7</span>, Name=任务<span class="hljs-number">7</span><br>处理任务: ID=<span class="hljs-number">8</span>, Name=任务<span class="hljs-number">8</span><br>处理任务: ID=<span class="hljs-number">9</span>, Name=任务<span class="hljs-number">9</span><br>处理任务: ID=<span class="hljs-number">10</span>, Name=任务<span class="hljs-number">10</span><br>main 函数的执行耗时: <span class="hljs-number">0.502795934677124</span> <br></code></pre></td></tr></tbody></table></figure><h2 id="多线程方案"><a href="#多线程方案" class="headerlink" title="多线程方案"></a>多线程方案</h2><p>threading 可以让每个任务都在自己的线程中运行，这样可以实现并发执行，但由于 GIL（全局解释器锁）的存在，Python 的多线程并不能实现真正的并行计算。</p><blockquote><p>GIL，全称为全局解释器锁（Global Interpreter Lock），是 Python 中的一个机制，用于确保同一时间只有一个线程可以执行 Python 字节码。这个锁主要是因为 CPython 解释器中的内存管理并不是线程安全的，所以引入 GIL 可以避免多线程同时操作 Python 对象引起的内存管理问题。</p><p>在 GIL 的影响下，同一个进程内的多个线程只能串行而不能并行。这意味着在任何时间点都只能有一个线程处于执行状态，但密集计算领域可能导致程序效率降低。</p></blockquote><figure class="highlight python"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> time<br><span class="hljs-keyword">from</span> time <span class="hljs-keyword">import</span> sleep<br><span class="hljs-keyword">import</span> threading<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">handle_task</span>(<span class="hljs-params"><span class="hljs-built_in">id</span>, name</span>):<br>    <span class="hljs-comment"># 在这里处理你的任务</span><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"处理任务: ID=<span class="hljs-subst">{<span class="hljs-built_in">id</span>}</span>, Name=<span class="hljs-subst">{name}</span>"</span>)<br>    <span class="hljs-comment"># 模拟 IO 消耗</span><br>    sleep(<span class="hljs-number">0.5</span>)<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">main</span>():<br>    <span class="hljs-comment"># 十组数据</span><br>    data = [(<span class="hljs-number">1</span>, <span class="hljs-string">'任务1'</span>), (<span class="hljs-number">2</span>, <span class="hljs-string">'任务2'</span>), (<span class="hljs-number">3</span>, <span class="hljs-string">'任务3'</span>), (<span class="hljs-number">4</span>, <span class="hljs-string">'任务4'</span>), (<span class="hljs-number">5</span>, <span class="hljs-string">'任务5'</span>),<br>            (<span class="hljs-number">6</span>, <span class="hljs-string">'任务6'</span>), (<span class="hljs-number">7</span>, <span class="hljs-string">'任务7'</span>), (<span class="hljs-number">8</span>, <span class="hljs-string">'任务8'</span>), (<span class="hljs-number">9</span>, <span class="hljs-string">'任务9'</span>), (<span class="hljs-number">10</span>, <span class="hljs-string">'任务10'</span>)]<br><br>    <span class="hljs-comment"># 创建一个线程列表</span><br>    threads = []<br><br>    <span class="hljs-comment"># 循环创建线程并添加到线程列表</span><br>    <span class="hljs-keyword">for</span> <span class="hljs-built_in">id</span>, name <span class="hljs-keyword">in</span> data:<br>        thread = threading.Thread(target=handle_task, args=(<span class="hljs-built_in">id</span>, name))<br>        threads.append(thread)<br><br>    <span class="hljs-comment"># 启动所有线程</span><br>    <span class="hljs-keyword">for</span> thread <span class="hljs-keyword">in</span> threads:<br>        thread.start()<br><br>    <span class="hljs-comment"># 等待所有线程完成</span><br>    <span class="hljs-keyword">for</span> thread <span class="hljs-keyword">in</span> threads:<br>        thread.join()<br><br><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:<br>    start_time = time.time()<br>    main()<br>    end_time = time.time()<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"main 函数的执行耗时: <span class="hljs-subst">{end_time - start_time}</span> 秒"</span>)<br><br></code></pre></td></tr></tbody></table></figure><p>执行结果如下：</p><figure class="highlight python"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs python">处理任务: ID=<span class="hljs-number">1</span>, Name=任务<span class="hljs-number">1</span><br>处理任务: ID=<span class="hljs-number">2</span>, Name=任务<span class="hljs-number">2</span><br>处理任务: ID=<span class="hljs-number">3</span>, Name=任务<span class="hljs-number">3</span><br>处理任务: ID=<span class="hljs-number">4</span>, Name=任务<span class="hljs-number">4</span><br>处理任务: ID=<span class="hljs-number">5</span>, Name=任务<span class="hljs-number">5</span><br>处理任务: ID=<span class="hljs-number">6</span>, Name=任务<span class="hljs-number">6</span><br>处理任务: ID=<span class="hljs-number">7</span>, Name=任务<span class="hljs-number">7</span><br>处理任务: ID=<span class="hljs-number">8</span>, Name=任务<span class="hljs-number">8</span><br>处理任务: ID=<span class="hljs-number">9</span>, Name=任务<span class="hljs-number">9</span><br>处理任务: ID=<span class="hljs-number">10</span>, Name=任务<span class="hljs-number">10</span><br>main 函数的执行耗时: <span class="hljs-number">0.5059280395507812</span> 秒<br></code></pre></td></tr></tbody></table></figure><h2 id="多进程方案"><a href="#多进程方案" class="headerlink" title="多进程方案"></a>多进程方案</h2><p>由于 GIL（全局解释器锁）的存在，Python 的多线程并不能有效利用多核优势，但多进程可以。</p><p>相比多线程，多进程的优缺点也很明显</p><p>优点：</p><ul><li><strong>并行处理</strong>：多进程可以利用多核 CPU 的优势，实现真正的并行计算。</li><li><strong>独立性</strong>：每个进程有自己的内存空间，互不影响。如果一个进程崩溃，不会影响其他进程。</li></ul><p>缺点：</p><ul><li><strong>开销大</strong>：创建进程的开销比创建线程大，尤其是在大量并发的情况下。</li><li><strong>通信复杂</strong>：进程间的通信比线程间的通信复杂，需要使用特殊的 IPC（Inter-Process Communication）机制。</li></ul><blockquote><p>with Pool() as p 是创建进程池的代码，如果 with Pool(4) as p 代表进程池中最多存在 4 个进程</p><p>如果是 CPU 密集型任务，进程池的数量建议设为 CPU 核数，以及考虑内存限制、资源竞争等影响综合而定</p></blockquote><figure class="highlight python"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> time<br><span class="hljs-keyword">from</span> time <span class="hljs-keyword">import</span> sleep<br><span class="hljs-keyword">from</span> multiprocessing <span class="hljs-keyword">import</span> Pool<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">handle_task</span>(<span class="hljs-params">data</span>):<br>    <span class="hljs-built_in">id</span>, name = data<br>    <span class="hljs-comment"># 在这里处理你的任务</span><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"处理任务: ID=<span class="hljs-subst">{<span class="hljs-built_in">id</span>}</span>, Name=<span class="hljs-subst">{name}</span>"</span>)<br>    <span class="hljs-comment"># 模拟 IO 消耗</span><br>    sleep(<span class="hljs-number">0.5</span>)<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">main</span>():<br>    <span class="hljs-comment"># 十组数据</span><br>    data = [(<span class="hljs-number">1</span>, <span class="hljs-string">'任务1'</span>), (<span class="hljs-number">2</span>, <span class="hljs-string">'任务2'</span>), (<span class="hljs-number">3</span>, <span class="hljs-string">'任务3'</span>), (<span class="hljs-number">4</span>, <span class="hljs-string">'任务4'</span>), (<span class="hljs-number">5</span>, <span class="hljs-string">'任务5'</span>),<br>            (<span class="hljs-number">6</span>, <span class="hljs-string">'任务6'</span>), (<span class="hljs-number">7</span>, <span class="hljs-string">'任务7'</span>), (<span class="hljs-number">8</span>, <span class="hljs-string">'任务8'</span>), (<span class="hljs-number">9</span>, <span class="hljs-string">'任务9'</span>), (<span class="hljs-number">10</span>, <span class="hljs-string">'任务10'</span>)]<br><br>    <span class="hljs-comment"># 创建一个进程池</span><br>    <span class="hljs-keyword">with</span> Pool() <span class="hljs-keyword">as</span> p:<br>        p.<span class="hljs-built_in">map</span>(handle_task, data)<br><br><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:<br>    start_time = time.time()<br>    main()<br>    end_time = time.time()<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"main 函数的执行耗时: <span class="hljs-subst">{end_time - start_time}</span> 秒"</span>)<br><br></code></pre></td></tr></tbody></table></figure><p>执行结果如下，可以看出由于创建进程成本比线程高，所以整体执行时间相比速度较慢</p><figure class="highlight python"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs python">处理任务: ID=<span class="hljs-number">1</span>, Name=任务<span class="hljs-number">1</span><br>处理任务: ID=<span class="hljs-number">2</span>, Name=任务<span class="hljs-number">2</span><br>处理任务: ID=<span class="hljs-number">3</span>, Name=任务<span class="hljs-number">3</span><br>处理任务: ID=<span class="hljs-number">4</span>, Name=任务<span class="hljs-number">4</span><br>处理任务: ID=<span class="hljs-number">5</span>, Name=任务<span class="hljs-number">5</span><br>处理任务: ID=<span class="hljs-number">6</span>, Name=任务<span class="hljs-number">6</span><br>处理任务: ID=<span class="hljs-number">7</span>, Name=任务<span class="hljs-number">7</span><br>处理任务: ID=<span class="hljs-number">8</span>, Name=任务<span class="hljs-number">8</span><br>处理任务: ID=<span class="hljs-number">9</span>, Name=任务<span class="hljs-number">9</span><br>处理任务: ID=<span class="hljs-number">10</span>, Name=任务<span class="hljs-number">10</span><br>main 函数的执行耗时: <span class="hljs-number">1.1109559535980225</span> <br></code></pre></td></tr></tbody></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本文介绍了 Python 中实现并发编程的三种主要方法：协程、多线程和多进程。在实际应用中，需要根据业务需求和系统环境选择最合适的并发编程模式。希望这篇文章能帮助你理解 Python 的并发编程，在实际工作中提高代码的执行效率。</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h1 id=&quot;Python-并发编程方案异同&quot;&gt;&lt;a href=&quot;#Python-并发编程方案异同&quot; class=&quot;headerlink&quot; title=&quot;Python 并发编程方案异同&quot;&gt;&lt;/a&gt;Python</summary>
        
      
    
    
    
    <category term="Linux" scheme="https://blog.wj2015.com/categories/Linux/"/>
    
    <category term="Python" scheme="https://blog.wj2015.com/tags/Python/"/>
    
    
    <category term="后端开发" scheme="https://blog.wj2015.com/categories/%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91/"/>
    
    <category term="并发" scheme="https://blog.wj2015.com/tags/%E5%B9%B6%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>如何在一秒内安全清理九千万大表数据</title>
    <link href="https://blog.wj2015.com/2024/02/21/%E5%A6%82%E4%BD%95%E5%9C%A8%E4%B8%80%E7%A7%92%E5%86%85%E5%AE%89%E5%85%A8%E6%B8%85%E7%90%86%E4%B9%9D%E5%8D%83%E4%B8%87%E5%A4%A7%E8%A1%A8%E6%95%B0%E6%8D%AE/"/>
    <id>https://blog.wj2015.com/2024/02/21/%E5%A6%82%E4%BD%95%E5%9C%A8%E4%B8%80%E7%A7%92%E5%86%85%E5%AE%89%E5%85%A8%E6%B8%85%E7%90%86%E4%B9%9D%E5%8D%83%E4%B8%87%E5%A4%A7%E8%A1%A8%E6%95%B0%E6%8D%AE/</id>
    <published>2024-02-21T07:06:35.000Z</published>
    <updated>2026-08-12T03:28:46.856Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h1 id="如何在一秒内安全清理九千万大表数据"><a href="#如何在一秒内安全清理九千万大表数据" class="headerlink" title="如何在一秒内安全清理九千万大表数据"></a>如何在一秒内安全清理九千万大表数据</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>博主在工作中碰到这样一个问题，具体场景是公司的数据库需要做升级，而在做升级前需要 <strong>对现有数据表进行数据清理</strong> 以降低维护成本并减少迁移时间</p><p>​​在清理的过程中发现一张记录广告回调记录的大表，有 <strong>9kw + 的数据</strong>，以及 <strong>两张 3kw+</strong> 数据的大表需要清理，但同时也需要保留其中大概 3w 条数据，避免正在进行中的业务出现统计问题​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/img_v3_0289_66f21fb8-e6df-4157-b548-f410f5d6e44h-20240221115251-m4sxwcm.jpg" alt="img_v3_0289_66f21fb8-e6df-4157-b548-f410f5d6e44h" data-caption="img_v3_0289_66f21fb8-e6df-4157-b548-f410f5d6e44h" loading="lazy">​</p><h2 id="如何找到大表"><a href="#如何找到大表" class="headerlink" title="如何找到大表"></a>如何找到大表</h2><p>顺便提一下数据清理的时候常用的几个 SQL，通过这些 SQL 可以快速的定位到占用空间最大的数据库及其下占用最大的数据表，进而针对性的对数据进行清理</p><h3 id="磁盘总占用"><a href="#磁盘总占用" class="headerlink" title="磁盘总占用"></a>磁盘总占用</h3><p>下面这个 SQL 可以快速的统计整个 SQL 占用了多少磁盘空间，原理是将所有库的数据空间和索引空间占用的存储加在一起</p><figure class="highlight sql"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">SUM</span>(data_length <span class="hljs-operator">+</span> index_length) <span class="hljs-operator">/</span> <span class="hljs-number">1024</span> <span class="hljs-operator">/</span> <span class="hljs-number">1024</span> <span class="hljs-operator">/</span> <span class="hljs-number">1024</span> <span class="hljs-keyword">AS</span> `Total Size (GB)` <span class="hljs-keyword">FROM</span> information_schema.TABLES;<br></code></pre></td></tr></tbody></table></figure><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/img_v3_0286_91712565-b53f-4542-a3c4-4318f733d95h-20240221115739-7nnhiq8.jpg" alt="img_v3_0286_91712565-b53f-4542-a3c4-4318f733d95h" data-caption="img_v3_0286_91712565-b53f-4542-a3c4-4318f733d95h" loading="lazy">​</p><h3 id="查数据库的存储占用"><a href="#查数据库的存储占用" class="headerlink" title="查数据库的存储占用"></a>查数据库的存储占用</h3><p>通过下列可以清楚的看到具体哪个库的存储占用最大</p><figure class="highlight sql"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> table_schema <span class="hljs-keyword">AS</span> `Database`, <span class="hljs-built_in">SUM</span>(data_length <span class="hljs-operator">+</span> index_length) <span class="hljs-operator">/</span> <span class="hljs-number">1024</span> <span class="hljs-operator">/</span> <span class="hljs-number">1024</span> <span class="hljs-operator">/</span> <span class="hljs-number">1024</span> <span class="hljs-keyword">AS</span> `Size (GB)` <span class="hljs-keyword">FROM</span> information_schema.TABLES <span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> table_schema;<br></code></pre></td></tr></tbody></table></figure><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/image-20240221120052-ezr1pl2.png" alt="image" data-caption="image" loading="lazy">​</p><h3 id="查具体数据表的存储占用"><a href="#查具体数据表的存储占用" class="headerlink" title="查具体数据表的存储占用"></a>查具体数据表的存储占用</h3><p>最终会排查到某个表占用了多少存储空间，接下来就可以对不需要的数据进行定向的清理</p><figure class="highlight sql"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> table_name <span class="hljs-keyword">AS</span> `<span class="hljs-keyword">Table</span>`, round(((data_length <span class="hljs-operator">+</span> index_length) <span class="hljs-operator">/</span> <span class="hljs-number">1024</span> <span class="hljs-operator">/</span> <span class="hljs-number">1024</span> <span class="hljs-operator">/</span> <span class="hljs-number">1024</span>), <span class="hljs-number">2</span>) `Size (GB)` <span class="hljs-keyword">FROM</span> information_schema.TABLES <span class="hljs-keyword">WHERE</span> table_schema <span class="hljs-operator">=</span> <span class="hljs-string">'inssrv'</span> <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> `Size (GB)` <span class="hljs-keyword">DESC</span>;<br></code></pre></td></tr></tbody></table></figure><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/image-20240221120154-av9lmsg.png" alt="image" data-caption="image" loading="lazy">​</p><h2 id="整理思路"><a href="#整理思路" class="headerlink" title="整理思路"></a>整理思路</h2><p>删除数据无非有这么几条路：</p><ul><li>【最直接】一条 SQL 删除：DELETE FROM xxx WHERE id &lt; abcde;</li><li>【最慢】写一个脚本，脚本中循环调用 DELETE * FROM xxx WHERE id &lt; abcde LIMIT 50000;</li><li>【可能中断】重建数据表并导入需要的老数据</li></ul><p>考虑几个问题：</p><ul><li>业务逻辑中会在什么场景下用到这个表，改动过程中会对这些业务造成多大的影响</li><li>对插入/查询性能的影响</li><li>主从同步性能问题</li><li>执行时间是否可接受</li><li>如果有从库，从库的同步模式、性能可能影响阻塞时间</li></ul><p>这几个方案都有尝试，以及出于对生产数据需要抱有敬畏之心，千万注意：</p><ul><li>不要直接动生产数据库，需要在测试环境或生产数据库的镜像上做尽可能贴合生产环境的验证</li><li>如果需要改动脚本参数，一定要在测试数据库上经过充分验证再上生产，不要过于自信（教训之一）</li></ul><h3 id="直接删除的方案"><a href="#直接删除的方案" class="headerlink" title="直接删除的方案"></a>直接删除的方案</h3><figure class="highlight sql"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> xxx <span class="hljs-keyword">WHERE</span> id <span class="hljs-operator">&lt;</span> <span class="hljs-number">90000000</span>;<br></code></pre></td></tr></tbody></table></figure><p>‍</p><p>本质上执行的操作如下：</p><ul><li>开启事务</li><li>扫聚簇索引，从小到大找 id &lt; 指定值的数据，批量打删除标记</li><li>期间新数据的读写可正常进行，Innodb 数据库下，<strong>id &lt; 指定值的数据会由于 GAP 锁、行锁的原因无法更新</strong>（但即将被清理的历史数据也不会更新）</li><li>提交事务，相关标记空间为可重用</li><li>执行 OPTIMIZE TABLE ，清理聚簇索引和非聚簇索引的无用数据并逐步刷到磁盘中</li><li>从库同步进行</li></ul><p>毫无疑问这条 SQL 会一直卡在 MySQL 后台，公司目前使用 2c2g 的 AWS db.t3.small 规格的机器，根据数据量做了水平预估，9kw 数据大概需要 8.3h 可以处理完毕，<strong>有相当大的 IO 开销</strong>，而且没有后悔药可吃，KILL 掉之后会导致 DELETE 标记数据恢复缓慢，查询和更新性能受到影响</p><h3 id="脚本批量删除"><a href="#脚本批量删除" class="headerlink" title="脚本批量删除"></a>脚本批量删除</h3><p>好处是不用改表结构，也不用担心读写中断的问题，这个脚本在 db.t3.small 300w 左右的数据下工作正常，脚本中的 SLEEP 、LIMIT 值需要根据主从库机器性能而定，需要在同性能、同负载下的测试环境中得到一个较佳的数字</p><figure class="highlight python"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> pymysql<br><span class="hljs-keyword">import</span> time<br><br>master_db_config = <span class="hljs-built_in">dict</span>(<br>    host=<span class="hljs-string">"rds.amazonaws.com"</span>, port=<span class="hljs-number">3306</span>, user=<span class="hljs-string">"user"</span>,<br>    passwd=<span class="hljs-string">"pwd"</span>, cursorclass=pymysql.cursors.DictCursor)<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_mysql_connection</span>(<span class="hljs-params">mysql_config</span>) -&gt; pymysql.connect:<br>    <span class="hljs-keyword">try</span>:<br>        conn = pymysql.connect(**mysql_config, autocommit=<span class="hljs-literal">True</span>)<br>    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:<br>        <span class="hljs-built_in">print</span>(<span class="hljs-built_in">str</span>(e))<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f"db conn err: <span class="hljs-subst">{mysql_config}</span>"</span>)<br>    <span class="hljs-keyword">else</span>:<br>        <span class="hljs-keyword">return</span> conn<br><br>con = get_mysql_connection(master_db_config)<br>con.select_db(<span class="hljs-string">'db_name'</span>)<br>cursor = con.cursor()<br><br>sql = <span class="hljs-string">"""DELETE FROM `table_records` WHERE `id` &lt; 28374149 limit 50000; """</span><br><br>cursor.execute(sql)<br><br>num_rows_deleted = cursor.rowcount<br><span class="hljs-keyword">while</span> num_rows_deleted &gt; <span class="hljs-number">0</span>:<br>    cursor.execute(sql)<br>    num_rows_deleted = cursor.rowcount<br>    <span class="hljs-keyword">if</span> num_rows_deleted &gt; <span class="hljs-number">0</span>:<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">f"成功删除了 <span class="hljs-subst">{num_rows_deleted}</span> 行。"</span>)<br>    <span class="hljs-keyword">else</span>:<br>        <span class="hljs-built_in">print</span>(<span class="hljs-string">"没有符合条件的行被删除。"</span>)<br>    time.sleep(<span class="hljs-number">4</span>)<br></code></pre></td></tr></tbody></table></figure><blockquote><p>踩过的坑：</p><ul><li>测试最佳参数时，一定要注意从库与主库存在一定性能差异，否则差距逐渐拉大可能导致从库同步失败</li><li>删除的越多，由于磁盘IO性能的约束会越来越慢，会出现读写阻塞的情况，也跟性能相关，所以采用此方案需要多在同级环境下验证，看下速度能否接受</li></ul></blockquote><p>在从库上，用 root 登录后，执行下面的语句可以拿到 <code>Seconds_Behind_Master</code>​ 属性，这个值的含义是从库与主库数据差异，单位是秒数，值越大说明从库数据离主库越久，久到主库把同步数据需要的 binlog 删除的话，从库就无法正常从主库同步，只能重建了，这种情况的外在表现为 <code>Seconds_Behind_Master</code>​ 越来越大，并且错误日志中显示同步失败，binlog 不存在，从库不在同步主库的信息</p><figure class="highlight sql"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">show</span> slave status;<br></code></pre></td></tr></tbody></table></figure><h3 id="【推荐】临时表重建"><a href="#【推荐】临时表重建" class="headerlink" title="【推荐】临时表重建"></a>【推荐】临时表重建</h3><p>大表的数据如果一点点删掉的话，会产生插入/查询性能问题、从节点同步性能问题，所以下面这个脚本的思路是：</p><ul><li>重命名老表为临时表（底层是表空间文件改名，很快）</li><li>临时表不会有写入（auto_increment 不会增加），所以拿到建表语句后直接创建新表<ul><li>如果有主键自增，这里的 auto_increment 会继承之前的数据，不会存在冲突</li><li>如果主键是 uuid 也不用管 auto_increment 的问题，该怎么插入就怎么插入</li><li>改名和建新表时间特别短，在毫秒级，而且改名会等表意向锁解开才会执行，不会导致现有 session 出现问题，但改名后和建表成功之间的短短时间内，查询和插入可能失败</li></ul></li><li>拿一些还有用的老数据写入到新表中，期间无法插入新数据，需要等老数据插入完毕 （innodb 间隙锁）</li><li>最后验证无误后，手动删除临时表即可，大概几秒钟（底层是表空间文件删除，也很快）</li></ul><p>实测 9kw 级别的大表也能在极短时间内初始化完毕且不影响新增的数据，整体处理时间大概两秒内，从库的同步压力也很小</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/image-20240221145227-x2mba9n.png" alt="image" data-caption="image" loading="lazy">​</p><figure class="highlight python"><table><tbody><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><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> pymysql<br><span class="hljs-keyword">import</span> re<br><span class="hljs-keyword">import</span> datetime<br><span class="hljs-keyword">import</span> time<br><span class="hljs-keyword">import</span> sys<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">log</span>(<span class="hljs-params">*args, **kwargs</span>):<br>    current_time = time.time()<br>    formatted_time = datetime.datetime.fromtimestamp(current_time).strftime(<span class="hljs-string">'%Y-%m-%d %H:%M:%S'</span>)<br>    millis = <span class="hljs-built_in">int</span>((current_time - <span class="hljs-built_in">int</span>(current_time)) * <span class="hljs-number">1000</span>)<br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"当前时间（毫秒级）: <span class="hljs-subst">{formatted_time}</span>.<span class="hljs-subst">{<span class="hljs-built_in">str</span>(millis).zfill(<span class="hljs-number">3</span>)}</span>"</span>)<br>    <span class="hljs-built_in">print</span>(*args, **kwargs)<br><br><br><span class="hljs-comment"># 定义数据库连接参数</span><br>host = <span class="hljs-string">"rds.amazonaws.com"</span><br>username = <span class="hljs-string">"user"</span><br>password = <span class="hljs-string">"password"</span><br>database = <span class="hljs-string">"db_name"</span><br>port = <span class="hljs-number">3306</span><br><span class="hljs-comment"># 创建数据库连接</span><br>db = pymysql.connect(host=host, user=username, password=password, db=database, port=port)<br><span class="hljs-comment"># 创建一个游标对象</span><br>cursor = db.cursor()<br><br><span class="hljs-comment"># 定义变量</span><br>table_name = <span class="hljs-string">'table_records'</span><br>tmp_table_name = <span class="hljs-string">'tmp_old'</span><br>num_records = <span class="hljs-number">100000</span><br><br><span class="hljs-comment"># 检查 tmp_table_name 是否存在</span><br>cursor.execute(<span class="hljs-string">f"SHOW TABLES LIKE '<span class="hljs-subst">{tmp_table_name}</span>';"</span>)<br>result = cursor.fetchone()<br><span class="hljs-keyword">if</span> result:<br>    log(<span class="hljs-string">f"<span class="hljs-subst">{tmp_table_name}</span> 已存在，请检查"</span>)<br>    sys.exit()<br><br>log(<span class="hljs-string">"开始重命名"</span>)<br><span class="hljs-comment"># 第一步：重命名 table_name 为 tmp_table_name</span><br>cursor.execute(<span class="hljs-string">f"RENAME TABLE <span class="hljs-subst">{table_name}</span> TO <span class="hljs-subst">{tmp_table_name}</span>;"</span>)<br>log(<span class="hljs-string">"重命名结束"</span>)<br><br><span class="hljs-comment"># 第二步：建立一个新表名为 table_name，表结构与 tmp_table_name 一致，并且自增主键 id 为 tmp_table_name 的最大自增id</span><br><span class="hljs-comment"># 执行 SHOW CREATE TABLE 获取旧表的创建语句</span><br>cursor.execute(<span class="hljs-string">f"SHOW CREATE TABLE <span class="hljs-subst">{tmp_table_name}</span>;"</span>)<br>result = cursor.fetchone()<br>create_table_statement = result[<span class="hljs-number">1</span>]<br><br><span class="hljs-comment"># 使用正则表达式将旧表名替换为新表名</span><br>create_table_statement = re.sub(<span class="hljs-string">f'CREATE TABLE `<span class="hljs-subst">{tmp_table_name}</span>`'</span>, <span class="hljs-string">f'CREATE TABLE `<span class="hljs-subst">{table_name}</span>`'</span>, create_table_statement)<br><br><span class="hljs-comment"># 执行创建新表的 SQL 语句</span><br>cursor.execute(create_table_statement)<br>log(<span class="hljs-string">"新表创建完毕"</span>)<br><br><span class="hljs-comment"># 第三步：把 tmp_table_name 表中最新的 num_records 条数据插入到新建的 table_name 中</span><br>cursor.execute(<span class="hljs-string">f"INSERT INTO <span class="hljs-subst">{table_name}</span> SELECT * FROM <span class="hljs-subst">{tmp_table_name}</span> ORDER BY id DESC LIMIT <span class="hljs-subst">{num_records}</span>;"</span>)<br><br>log(<span class="hljs-string">"数据库初始化完毕"</span>)<br><br><span class="hljs-comment"># 提交到数据库执行</span><br>db.commit()<br><br><span class="hljs-comment"># 关闭数据库连接</span><br>db.close()<br></code></pre></td></tr></tbody></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>MySQL 大表结构或数据操作都相对麻烦，容易把数据库搞死或者把表锁死，所以数据结构和历史数据的问题，有条件提前暴露，提前规划是最好的。</p><p>不得不改底层结构的时候，我们也要敬畏生产，较大的数据库更改一定要在测试环境过一遍，避免直接出生产环境问题。</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h1 id=&quot;如何在一秒内安全清理九千万大表数据&quot;&gt;&lt;a href=&quot;#如何在一秒内安全清理九千万大表数据&quot; class=&quot;headerlink&quot;</summary>
        
      
    
    
    
    <category term="Linux" scheme="https://blog.wj2015.com/categories/Linux/"/>
    
    <category term="Mysql" scheme="https://blog.wj2015.com/categories/Linux/Mysql/"/>
    
    
    <category term="Mysql" scheme="https://blog.wj2015.com/tags/Mysql/"/>
    
  </entry>
  
  <entry>
    <title>心心念了3年的XSX，最终还是选了PS5</title>
    <link href="https://blog.wj2015.com/2023/12/24/%E5%BF%83%E5%BF%83%E5%BF%B5%E4%BA%863%E5%B9%B4%E7%9A%84XSX%EF%BC%8C%E6%9C%80%E7%BB%88%E8%BF%98%E6%98%AF%E9%80%89%E4%BA%86PS5/"/>
    <id>https://blog.wj2015.com/2023/12/24/%E5%BF%83%E5%BF%83%E5%BF%B5%E4%BA%863%E5%B9%B4%E7%9A%84XSX%EF%BC%8C%E6%9C%80%E7%BB%88%E8%BF%98%E6%98%AF%E9%80%89%E4%BA%86PS5/</id>
    <published>2023-12-24T12:42:27.000Z</published>
    <updated>2026-08-12T03:28:46.856Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>PS5 买来已经有一个多月了，它暂时治好了我的电子 ED，让我一个半月通关了五部单机佳作并且乐在其中，这是此前一直没做到过的事情</p><p>这篇文主要想分享一下当时为什么抛弃了心心念很多年的 XSX 投向了 PS5 的阵营<a href="">，</a>面对索尼高昂的游戏价格，我要说上一句：<strong>“索尼罪大恶极”</strong>，但从一个多月的体验来看，现在是丝毫不后悔</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/74247533-591e-4475-a396-8e485fb11172-20231224094955-wx56n0o.jpg" alt="74247533-591e-4475-a396-8e485fb11172" data-caption="74247533-591e-4475-a396-8e485fb11172" loading="lazy">​</p><p><strong>【叠BUFF】</strong> 我的选择并不适合所有人，只是从我当前的需求出发做出的决定，有条件肯定 PC + PS5 + XSX + Switch 拉满了</p><blockquote><p>附图：“你怎么知道我老婆给我买了 PS5.jpg“</p></blockquote><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/img_v3_026e_148cef0a-4f6e-4311-80c2-e6eafdffda7h-20231224095850-da6sn32.jpg" alt="img_v3_026e_148cef0a-4f6e-4311-80c2-e6eafdffda7h" data-caption="img_v3_026e_148cef0a-4f6e-4311-80c2-e6eafdffda7h" loading="lazy">​</p><h2 id="为什么要买游戏机"><a href="#为什么要买游戏机" class="headerlink" title="为什么要买游戏机"></a>为什么要买游戏机</h2><p>家中硬件情况是这样的：</p><ul><li>SWITCH 日版长续</li><li>Intel(R) Core(TM) i5-7300HQ 16.0 GB 1050Ti 游戏本</li><li>NAS 主机、家庭服务器（G4600T 32.0GB P104 台式）</li><li>DELL S2721QS 4K@60Hz 显示器</li></ul><p>虽然有 Switch 作为轻度娱乐的手段，但日用 PC 和服务器都无法承载我大型 3A 游戏的需求，打工人平时空闲时间也有限，坐下就可以立即沉浸开玩的体验对我来说比较重要。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/img_v3_026e_5a7de2b7-f8a8-45cd-9395-ce61d5795a4h-20231224150753-fo1lx6f.jpg" alt="img_v3_026e_5a7de2b7-f8a8-45cd-9395-ce61d5795a4h" data-caption="img_v3_026e_5a7de2b7-f8a8-45cd-9395-ce61d5795a4h" loading="lazy">​​</p><p>所以当时 XSX 一出来还是非常心动的，可以说完美打到我对游戏硬件的需求上，然而出于价格、预算、精力上的考虑一直没有上车，直到开售三年后的双十一，XSX 和 PS5 的价格降到我可以接受的程度，而且 Switch 借给朋友玩塞尔达了，买机器的想法就愈发强烈了。</p><h2 id="PS5如何顶替-XSX-成为第一顺位的"><a href="#PS5如何顶替-XSX-成为第一顺位的" class="headerlink" title="PS5如何顶替 XSX 成为第一顺位的"></a>PS5如何顶替 XSX 成为第一顺位的</h2><p>虽然已成为一名光荣的无产阶级劳动者，手里握着一笔不算太低的预算，但我的习惯是买东西的时候会不断对比挑选，直到摸透里边的门道，选出一个最适合自己的产品</p><p>第一步从高到低明确需求：</p><ul><li><strong>游戏阵容值得玩，设备沉浸感要好</strong>，支持 4K@60Hz HDR</li><li>打工人的游戏时间有限，快速唤醒，坐下就玩</li><li>扩展性越高越好</li><li>游戏价格越低越好</li><li>噪音越低越好</li></ul><p>当时从成本考虑，也调研过上一代机型，也就是 Xbox One X 和 Xbox One S、PS4 和 PS4 Pro，但阵容和帧率始终无法接受，所以都排除掉了</p><table><thead><tr><th>机型</th><th>价格(2023-12)</th><th>备注</th></tr></thead><tbody><tr><td>Xbox One S</td><td>500</td><td>性能孱弱</td></tr><tr><td>Xbox One X</td><td>880</td><td>部分游戏降分辨率也只能 30帧</td></tr><tr><td>PS4/ PS4 Slim</td><td>680</td><td>性能孱弱，游戏阵容缺失</td></tr><tr><td>PS4 Pro</td><td>1000</td><td>部分游戏只能30帧，游戏阵容缺失</td></tr></tbody></table><p>在我的需求中，游戏阵容、设备沉浸感、画质是首位的，所以只会在 XSX 和 PS5 中挑选</p><table><thead><tr><th>机型</th><th>价格(2023-11)</th><th>备注</th></tr></thead><tbody><tr><td>Xbox Series S</td><td>1800</td><td>无法支持 4k</td></tr><tr><td>Xbox Series X</td><td>3000</td><td>决赛圈</td></tr><tr><td>PS5</td><td>3000</td><td>决赛圈</td></tr></tbody></table><h3 id="游戏阵容"><a href="#游戏阵容" class="headerlink" title="游戏阵容"></a>游戏阵容</h3><p>于是先对比了一下我比较的游戏阵容，并且我没有高配 PC，所以不管是否上了 PC 对我来说都是主机独占，单机大作一圈看下来梳理出来了一个主机可玩的阵容表</p><blockquote><p>游戏属于萝卜青菜各有所爱了，从这一步开始就注定我的决定不一定适合所有人</p><p>个人偏向 ARPG 的单机游戏，GTA5、2077、泰坦陨落2就很合我的胃口，有很多人喜欢的 PS 下的美末、生化危机，出于对恐怖游戏的抗拒，就不是我的菜</p></blockquote><p>不难看出，<strong>索尼有很多我想体验的 “主机独占”</strong>，阵营上 PS5 胜，但 XSX 也不是完全没了机会，因为列表中还有很多游戏并非单个平台独占</p><table><thead><tr><th>平台</th><th>游戏</th></tr></thead><tbody><tr><td>PS5</td><td>蜘蛛侠系列</td></tr><tr><td></td><td>战神系列</td></tr><tr><td></td><td>瑞奇叮当<br></td></tr><tr><td></td><td>对马岛之魂<br></td></tr><tr><td></td><td>尼尔机械纪元</td></tr><tr><td></td><td>地平线系列</td></tr><tr><td>XSX</td><td>模拟飞行</td></tr><tr><td></td><td>极限竞速地平线系列</td></tr><tr><td>都有的</td><td>GTA5</td></tr><tr><td></td><td>2077</td></tr><tr><td></td><td>巫师3</td></tr><tr><td></td><td>双人成行</td></tr><tr><td></td><td>刺客信条系列</td></tr><tr><td></td><td>杀手系列</td></tr><tr><td></td><td>毛线小精灵2</td></tr><tr><td></td><td>只狼</td></tr><tr><td></td><td>荒野大镖客系列</td></tr><tr><td></td><td>老头环</td></tr><tr><td></td><td>究极风暴系列</td></tr><tr><td></td><td>上古卷轴</td></tr><tr><td></td><td>无人星空</td></tr><tr><td></td><td>古墓丽影系列</td></tr><tr><td></td><td>伊迪芬奇的秘密<br></td></tr><tr><td></td><td>极限国度</td></tr><tr><td></td><td>尘埃系列</td></tr><tr><td></td><td>泰坦陨落</td></tr><tr><td></td><td>死亡搁浅</td></tr></tbody></table><h3 id="沉浸感"><a href="#沉浸感" class="headerlink" title="沉浸感"></a>沉浸感</h3><p>我个人是很习惯手柄的，喜欢细腻的震感以及线性扳机的感觉，前几年一直用着北通 x360 布局的手柄玩单机很是享受</p><p>​​​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/img_v3_026e_9332b558-524f-4aec-b0b6-a043623f799h-20231224104106-2s08fgr.jpg" alt="img_v3_026e_9332b558-524f-4aec-b0b6-a043623f799h" data-caption="img_v3_026e_9332b558-524f-4aec-b0b6-a043623f799h" loading="lazy">​​​</p><p>而在这一世代主机中，对我来说 Xbox 手柄和 PS5 手柄主要区别在：</p><ul><li>PS5 有自适应扳机、麦克风、扬声器、触摸板，交互更多样一点</li><li>摇杆布局不一样</li><li>【仅个人】XBOX A/B 按键与 Switch 是反的，需要适应</li></ul><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/image-20231224105044-rbif12d.png" alt="image" data-caption="image" loading="lazy">​</p><p>自适应扳机增加了更多维度的交互，我相信<strong>在震感反馈、线性扳机这类基本功上上不会拉开太大的差距</strong>，所以在“沉浸感”维度来说对比 Xbox 手柄已经是压倒性的优势</p><p>然后还有问题是，支持自适应扳机的游戏在我游戏列表中有多少，简单做了下筛选，已经值得为这个功能买单了：</p><ul><li>蜘蛛侠系列</li><li>战神系列</li><li>对马岛之魂</li><li>赛博朋克 2077</li><li>GTA5</li><li>巫师三</li><li>小机器人</li><li>等等</li></ul><h3 id="扩展性、购入价格、游戏价格等其他方面"><a href="#扩展性、购入价格、游戏价格等其他方面" class="headerlink" title="扩展性、购入价格、游戏价格等其他方面"></a>扩展性、购入价格、游戏价格等其他方面</h3><p>当时喜欢 XSX 有一部分原因就是扩展性，以及 XSX 独有的多个游戏切换也挺适合一直开机不关的我，还可以用 EDGE 浏览器以及微软商店中的各类 APP，反观索尼就那么几个视频 APP，软件平台不够开放，非游戏功能外的其他交互在我看来都不很好，对于一个折腾党来说会少一个电视盒子（但也不缺这个电视盒子）</p><p>购入价格对当时的我来说都差不多，2023 双十一的时候两台机器的外版在京东上的价格都在 3000 上下</p><p>游戏价格上，XGP 确实太香了，索尼完败，不过骨折作品买数字版，大作买游戏光盘置换亏个邮费还可以接受，比如双十一买的赛博朋克2077和蜘蛛侠迈尔斯，算上7元邮费，<strong>每款游戏实际花费 ￥26</strong>，大作可能波动大一点，但属于可接受的范围。</p><p>购入价：<br>​​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/img_v3_026e_0a862596-6c04-4a86-af7e-dd13eca2540h-20231224111037-af643zu.jpg" alt="img_v3_026e_0a862596-6c04-4a86-af7e-dd13eca2540h" data-caption="img_v3_026e_0a862596-6c04-4a86-af7e-dd13eca2540h" loading="lazy">​​<br>回收价：<br><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/img_v3_026e_c77f93f0-3af5-4f13-809b-e0ada41c9c8h-20231224111046-2o33q9f.jpg" alt="img_v3_026e_c77f93f0-3af5-4f13-809b-e0ada41c9c8h" data-caption="img_v3_026e_c77f93f0-3af5-4f13-809b-e0ada41c9c8h" loading="lazy">​​</p><h2 id="游戏体验分享"><a href="#游戏体验分享" class="headerlink" title="游戏体验分享"></a>游戏体验分享</h2><h3 id="机器"><a href="#机器" class="headerlink" title="机器"></a>机器</h3><p>双十一当时 PS5 Slim 并没有开卖，所以买的老款 1200A，硬盘只有 800GB，用起来捉襟见肘；当时已经可以判断未来很长一段时间固态硬盘的价格上涨是大趋势，所以乘着双十一没结束，买了个 S790 PRO 2TB 给 PS5 用，现在看来还是挺明智的。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/img_v3_026e_01ebd40e-9a1b-405a-b59c-be13853e288h-20231224112645-hgjmzvp.jpg" alt="img_v3_026e_01ebd40e-9a1b-405a-b59c-be13853e288h" data-caption="img_v3_026e_01ebd40e-9a1b-405a-b59c-be13853e288h" loading="lazy"><br>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/img_v3_026e_e3f7d4d0-8ffc-46a5-9914-f8f9a267082h-20231224112920-se96kcf.jpg" alt="img_v3_026e_e3f7d4d0-8ffc-46a5-9914-f8f9a267082h" data-caption="img_v3_026e_e3f7d4d0-8ffc-46a5-9914-f8f9a267082h" loading="lazy">​​</p><p>还记得 PS5 送达的那天加班到凌晨十二点多才到家，拆开机器第一件事当然是，小机器人和 <strong>“原神启动.jpg”</strong> 啦【狗头】</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/img_v3_026e_b07c848b-f738-4676-a940-3c25dbc8bddh-20231224113026-6soatt9.jpg" alt="img_v3_026e_b07c848b-f738-4676-a940-3c25dbc8bddh" data-caption="img_v3_026e_b07c848b-f738-4676-a940-3c25dbc8bddh" loading="lazy">​</p><p>​​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/img_v3_026e_3b99ae46-1f78-4634-8e82-cb56c4718cdh-20231224113044-5u9qo78.jpg" alt="img_v3_026e_3b99ae46-1f78-4634-8e82-cb56c4718cdh" data-caption="img_v3_026e_3b99ae46-1f78-4634-8e82-cb56c4718cdh" loading="lazy">​​</p><h3 id="游戏"><a href="#游戏" class="headerlink" title="游戏"></a>游戏</h3><p>再分享几个骨折游戏以及这一个多月通关过的游戏吧，不喜欢清问号，更喜欢推剧情和灵活探索</p><blockquote><p>都是游戏实机画面截图或内置拍照模式</p></blockquote><h4 id="赛博朋克2077"><a href="#赛博朋克2077" class="headerlink" title="赛博朋克2077"></a>赛博朋克2077</h4><p>游玩时间：40h 左右</p><p>入手价格：150 实体盘</p><p>个人打分：7</p><p>个人简评：沉浸剧情不错，世界还不够开放，探索欲不高</p><p>第一人称有点晕，但是习惯之后沉浸感莫名的好，虽然已经是修复过很多次 BUG 的版本，但在游玩过程中还是经常有无法召唤车辆、闪退之类的影响体验的 BUG，最后太阳结局达成</p><p>对我个人而言，2077是一个神作，他的开放世界对比 GTA5，探索欲望并不会那么强，主观感受是：</p><ul><li>架空的背景环境熟悉而陌生，没有现实中的深切体会不会自发的迸发出很多探索欲望</li><li>主线和重要支线都比较线性，在剧情引导中方能窥见世界全貌，离开了任务引导玩家很难找到一个合适的探索方向<br>​​<br>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/2077_20231111190724-20231224141840-ay6977n.jpg" alt="_20231111190724" data-caption="_20231111190724" loading="lazy"><br>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/2077_20231111222656-20231224141852-kn9cvpj.jpg" alt="20231111222656" data-caption="20231111222656" loading="lazy"><br>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/2077_20231118210116-20231224141819-h8lisqk.jpg" alt="_20231118210116" data-caption="_20231118210116" loading="lazy"><br>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/2077_20231112215123-20231224141830-20yxlt5.jpg" alt="_20231112215123" data-caption="_20231112215123" loading="lazy">​​<br>​​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/2077_20231121003843-20231224141902-8v2k66l.jpg" alt="_20231121003843" data-caption="_20231121003843" loading="lazy">​​</li></ul><h4 id="蜘蛛侠迈尔斯"><a href="#蜘蛛侠迈尔斯" class="headerlink" title="蜘蛛侠迈尔斯"></a>蜘蛛侠迈尔斯</h4><p>游玩时间：20h 左右</p><p>入手价格：175 实体盘</p><p>个人打分：8</p><p>个人简评：让我知道什么是第一方大作，前所未有的体验</p><p>没有体验过的打击感，蛛丝荡来荡去真的棒，风景也是真美，剧情合理合格</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Morales_20231105204740-20231224142507-ut43ffv.jpg" alt="Morales_20231105204740" data-caption="Morales_20231105204740" loading="lazy"><br>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Morales_20231105205530-20231224142514-vo8cb9c.jpg" alt="Morales_20231105205530" data-caption="Morales_20231105205530" loading="lazy"><br>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Morales_20231104162123-20231224142531-v93apov.jpg" alt="Morales_20231104162123" data-caption="Morales_20231104162123" loading="lazy"><br>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Morales_20231105022501-20231224142537-3d9g0l7.jpg" alt="Morales_20231105022501" data-caption="Morales_20231105022501" loading="lazy"><br>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Morales_20231105223213-20231224142544-5older3.jpg" alt="Morales_20231105223213" data-caption="Morales_20231105223213" loading="lazy">​​​​​</p><h4 id="泰坦陨落2"><a href="#泰坦陨落2" class="headerlink" title="泰坦陨落2"></a>泰坦陨落2</h4><p>游玩时间：15h 左右</p><p>入手价格：35 数字终极版</p><p>个人打分：8</p><p>个人简评：关卡设计很赞，全程基本不重复，我的 BT 啊 /(ㄒoㄒ)/~~</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Titanfall2_20231217142215-20231224141139-k4o0dzn.jpg" alt="Titanfall2_20231217142215" data-caption="Titanfall2_20231217142215" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Titanfall2_20231218230001_1-20231224141206-yymj4id.jpg" alt="Titanfall2_20231218230001_1" data-caption="Titanfall2_20231218230001_1" loading="lazy">​</p><h4 id="宇宙机器人无线控制器使用指南"><a href="#宇宙机器人无线控制器使用指南" class="headerlink" title="宇宙机器人无线控制器使用指南"></a>宇宙机器人无线控制器使用指南</h4><p>游玩时间：3h左右</p><p>个人打分：8</p><p>个人简评：自适应扳机带来的体验感真好</p><p>绝对的佳作，我目前体验过的游戏中，自适应扳机应用的最好的游戏，2h通关，对于其他大作来说时间确实很短，但对很久没有沉浸玩游戏的我来说，不多也不少体验刚刚好</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/%E5%AE%87%E5%AE%99%E6%9C%BA%E5%99%A8%E4%BA%BA%E6%97%A0%E7%BA%BF%E6%8E%A7%E5%88%B6%E5%99%A8%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97_20231104095608-20231224142934-xqcq950.jpg" alt="宇宙机器人无线控制器使用指南_20231104095608" data-caption="宇宙机器人无线控制器使用指南_20231104095608" loading="lazy"></p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/%E5%AE%87%E5%AE%99%E6%9C%BA%E5%99%A8%E4%BA%BA%E6%97%A0%E7%BA%BF%E6%8E%A7%E5%88%B6%E5%99%A8%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97_20231104012839-20231224142950-tn506dj.jpg" alt="宇宙机器人无线控制器使用指南_20231104012839" data-caption="宇宙机器人无线控制器使用指南_20231104012839" loading="lazy">​</p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/%E5%AE%87%E5%AE%99%E6%9C%BA%E5%99%A8%E4%BA%BA%E6%97%A0%E7%BA%BF%E6%8E%A7%E5%88%B6%E5%99%A8%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97_20231104104720-20231224143007-68nwq16.jpg" alt="宇宙机器人无线控制器使用指南_20231104104720" data-caption="宇宙机器人无线控制器使用指南_20231104104720" loading="lazy"></p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/%E5%AE%87%E5%AE%99%E6%9C%BA%E5%99%A8%E4%BA%BA%E6%97%A0%E7%BA%BF%E6%8E%A7%E5%88%B6%E5%99%A8%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97_20231104013921-20231224143025-prc9fwb.jpg" alt="宇宙机器人无线控制器使用指南_20231104013921" data-caption="宇宙机器人无线控制器使用指南_20231104013921" loading="lazy">​​​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/%E5%AE%87%E5%AE%99%E6%9C%BA%E5%99%A8%E4%BA%BA%E6%97%A0%E7%BA%BF%E6%8E%A7%E5%88%B6%E5%99%A8%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97_20231104143403-20231224143034-8xuove2.jpg" alt="宇宙机器人无线控制器使用指南_20231104143403" data-caption="宇宙机器人无线控制器使用指南_20231104143403" loading="lazy">​</p><h4 id="原神"><a href="#原神" class="headerlink" title="原神"></a>原神</h4><p>游玩时间：1h内</p><p>个人打分：6</p><p>个人简评：搓玻璃久了，手柄下的交互是真不习惯</p><p>之前都是在 IPad 上玩的原神，后来家里工作事情忙起来再加上重复的每日任务、经常长草，后来就弃坑了，有了 PS5 自然要体验下 4K@60Hz HDR 的原神画面表现如何</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/%E5%8E%9F%E7%A5%9E_20231104083528-20231224150237-000xprv.jpg" alt="原神_20231104083528" data-caption="原神_20231104083528" loading="lazy"></p><p>​​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/20231104091817_png_-20231224150251-wjrt3ds.png" alt="20231104091817_png_" data-caption="20231104091817_png_" loading="lazy"></p><p>​​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/20231104091907_png_-20231224150259-4i8fqtj.png" alt="20231104091907_png_" data-caption="20231104091907_png_" loading="lazy">​</p><h4 id="伊迪芬奇的秘密"><a href="#伊迪芬奇的秘密" class="headerlink" title="伊迪芬奇的秘密"></a>伊迪芬奇的秘密</h4><p>游玩时间：2h 左右</p><p>入手价格：32 数字版</p><p>个人打分：7</p><p>个人简评：只有30帧很晕，故事讲的很不错，前后呼应来了个反转挺感慨的</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Finch_20231115210903-20231224140631-c6i1269.jpg" alt="Finch_20231115210903" data-caption="Finch_20231115210903" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Finch_20231116212728-20231224140639-rlm0yzg.jpg" alt="Finch_20231116212728" data-caption="Finch_20231116212728" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Finch_20231116220851-20231224140645-ewy61yt.jpg" alt="Finch_20231116220851" data-caption="Finch_20231116220851" loading="lazy">​</p><h4 id="刺客信条奥德赛"><a href="#刺客信条奥德赛" class="headerlink" title="刺客信条奥德赛"></a>刺客信条奥德赛</h4><p>游玩时间：30h 左右（主线未通关）</p><p>入手价格：154 数字终极版</p><p>个人打分：7</p><p>个人简评：地图特别大，风景震撼，还没玩到亚特兰蒂斯 DLC，就当旅行拍照模拟器，实在是太肝了，等级的设计也不适合剧情党和探索党</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Odyssey_20231123223313-20231224120714-9d6tu02.jpg" alt="Odyssey_20231123223313" data-caption="Odyssey_20231123223313" loading="lazy"></p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Odyssey_20231121224904-20231224140329-704nrzv.jpg" alt="Odyssey_20231121224904" data-caption="Odyssey_20231121224904" loading="lazy">​​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/img_v3_026e_7b90d4b0-5a41-457a-b246-c304ea5c682h-20231224115752-u33xkq7.jpg" alt="img_v3_026e_7b90d4b0-5a41-457a-b246-c304ea5c682h" data-caption="img_v3_026e_7b90d4b0-5a41-457a-b246-c304ea5c682h" loading="lazy">​</p><p>​​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Odyssey__47-20231224140246-2x3vyk7.jpeg" alt="Odyssey__47" data-caption="Odyssey__47" loading="lazy">​​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Odyssey__5-20231224135426-pgwapzu.jpeg" alt="Odyssey__5" data-caption="Odyssey__5" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Odyssey__67-20231224140256-ljl32qf.jpeg" alt="Odyssey__67" data-caption="Odyssey__67" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Odyssey__123-20231224140347-74cs213.jpeg" alt="Odyssey__123" data-caption="Odyssey__123" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Odyssey_20231222224848-20231224121344-tolyj89.jpg" alt="Odyssey_20231222224848" data-caption="Odyssey_20231222224848" loading="lazy">​</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-ps5/Odyssey_20231125002708-20231224140356-mve2415.jpg" alt="Odyssey_20231125002708" data-caption="Odyssey_20231125002708" loading="lazy">​</p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>作为一名已婚无产阶级劳动者，如何高质量的利用好不算充裕的业余娱乐时间更为重要，所以哪怕 XSX 心心念再久，也是那位不适合自己的TA，最终还是会选择适合自己生活的 PS5，以后条件合适了再包养下一世代的 XBOX【狗头】。</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;PS5 买来已经有一个多月了，它暂时治好了我的电子</summary>
        
      
    
    
    
    <category term="游戏" scheme="https://blog.wj2015.com/tags/%E6%B8%B8%E6%88%8F/"/>
    
    
    <category term="生活" scheme="https://blog.wj2015.com/tags/%E7%94%9F%E6%B4%BB/"/>
    
    <category term="随笔" scheme="https://blog.wj2015.com/categories/%E9%9A%8F%E7%AC%94/"/>
    
  </entry>
  
  <entry>
    <title>MYSQL 的底层数据结构</title>
    <link href="https://blog.wj2015.com/2023/10/22/MYSQL%20%E7%9A%84%E5%BA%95%E5%B1%82%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    <id>https://blog.wj2015.com/2023/10/22/MYSQL%20%E7%9A%84%E5%BA%95%E5%B1%82%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/</id>
    <published>2023-10-22T08:14:35.000Z</published>
    <updated>2026-08-12T03:28:46.852Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h1 id="MYSQL-的底层数据结构"><a href="#MYSQL-的底层数据结构" class="headerlink" title="MYSQL 的底层数据结构"></a>MYSQL 的底层数据结构</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本篇从《MYSQL 是怎样运行的：从根上理解 MYSQL》摘录而来，系列博客会有三个篇章，分别是：</p><ul><li>底层数据结构，整理 MYSQL 是怎么记录数据的，包括InnoDB数据结构，b+树的数据结构</li><li>高效查询，包括单表查询，连接和索引等</li><li>事务和锁，包括事务、相关日志文件、锁结构和功能等</li></ul><p>这篇博客是三个篇章中的第一篇，因为这本书我买了小册、买了实体书，已经是第三遍阅读了，我会整理出我个人认为最核心的东西，目的是理解其设计思路，让 MySQL 理解和应用变得更为醇熟；这个系列并不会细化到每个 bit 的具体细节，如果需要更深入的了解细节建议阅读小册或者看其他深入解析的书籍。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-mysql-data/img_v2_3c29bd41-af86-40c8-a487-843cc4c7fddh-20231022151142-puvufsu.jpg" alt="img_v2_3c29bd41-af86-40c8-a487-843cc4c7fddh" data-caption="img_v2_3c29bd41-af86-40c8-a487-843cc4c7fddh" loading="lazy">​</p><h2 id="INNODB-和-MyISAM"><a href="#INNODB-和-MyISAM" class="headerlink" title="INNODB 和 MyISAM"></a>INNODB 和 MyISAM</h2><p>innodb 和 myism 是 MySQL 的存储引擎，除此之外还会有 Memory、blackhole、CSV、NDB、Merge、Federated、Example 等，但并不常用，都可以有其他更合适的方案替代。</p><p>innodb和myisam都是落地在文件上的，在mysql5.7以后，全局变量中的 datadir 就表示这个数据库实例的数据目录，数据库中的每个表会对应数据目录下的一个子目录。</p><p>innnodb 默认使用独立表空间，会在数据库子目录下为每个表创建一个fmr结构定义文件和.idb 页文件</p><p>由于上面的特性，文件名约束会对数据库名和表名产生制约，系统最大文件大小会对单表最大数据产生制约</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-mysql-data/image-20231022155550-5k0l6km.png" alt="image" data-caption="image" loading="lazy">​</p><h3 id="MyISAM"><a href="#MyISAM" class="headerlink" title="MyISAM"></a>MyISAM</h3><p>MyISAM 直接在数据库目录下为每个表创建 fmr 结构定义文件，myd 数据文件和myi 数据索引文件</p><p>MyISAM 由于不支持事物，读取速度会比 innodb 更好，所以经常被用于读多写少的场景</p><h4 id="行格式"><a href="#行格式" class="headerlink" title="行格式"></a>行格式</h4><p>FIXD 静态格式，静态格式是MyISAM表的默认存储格式。当表不包含变量长度列（VARCHAR, BLOB, 或TEXT）时，使用这个格式。每一行用固定字节数存储。</p><p>DYNAMIC 动态格式，如果一个MyISAM表包含任何可变长度列（VARCHAR, BLOB或TEXTDynamic），或者如果一个表被用ROW_FORMAT=DYNAMIC选项来创建，动态存储格式被使用。</p><p>COMPRESSED 压缩格式，一种带空间压缩的只读格式。</p><h3 id="INNODB-如何进行存储"><a href="#INNODB-如何进行存储" class="headerlink" title="INNODB 如何进行存储"></a>INNODB 如何进行存储</h3><h4 id="行格式-1"><a href="#行格式-1" class="headerlink" title="行格式"></a>行格式</h4><p>MySQL 日常操作以行为单位进行存储和处理，存放行的格式被称为行格式</p><p>innodb 有四种行格式，compact 是目前最新的默认行格式，reduntant 是 MySQL 5.6之前的格式，dynamic 在数据溢出时表现与compact有细微差别，compressed 是在 compact 格式的基础上加上了数据压缩，更为节省空间</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-mysql-data/image-20231022151349-hhfcsvi.png" alt="image" data-caption="image" loading="lazy">​</p><p>所谓变长字段长度列表，简言之 VARCHAR、TEXT 等长度不固定的列需要记录一下他们的真实长度，否则无法正常的分割真实数据中的二进制数据</p><p>NULL 值列表也同理，对于部分可以位 NULL 的列，都在 NULL 值表中以二进制的形式来记录，方便记录同时也增加访问效率</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-mysql-data/image-20231022151427-cdomd4f.png" alt="image" data-caption="image" loading="lazy">​</p><p>‍</p><p>MYSQL 是支持数据持久化的数据库，而由内存到硬盘之间的 IO 读写速度差距特别大，因此 MYSQL 会设计一系列缓存及读写策略以保证效率最优化。</p><h4 id="内存与磁盘的交互"><a href="#内存与磁盘的交互" class="headerlink" title="内存与磁盘的交互"></a>内存与磁盘的交互</h4><p>页是磁盘和内存交互的基本单位，默认页大小是16kb，单次磁盘读取的最小规模是一个页，单次从内存刷入到硬盘的最小规模也是一个页。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-mysql-data/image-20231022152408-lw69gqr.png" alt="image" data-caption="image" loading="lazy"></p><p>INNODB 设计了不同用途的页，其中存储数据的页叫做数据页，除了实际数据之外，还会存储更多的双向链表、文件信息等管理需要的字段，多个页通过 page header 行成双向链表。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-mysql-data/image-20231022152531-jqyzc9r.png" alt="image" data-caption="image" loading="lazy">​</p><p>在管理页内数据的时候，会自动按照排序进行分组，，在插入或查询时会按照二分法的思想，快速找到对应的“槽”，再找到槽内插入、查询的数据位置</p><p>比较重要的是最小纪录，最大纪录以及用户记录这几个部分，相邻用户记录使用 next record 做单向链表，再把一个页中的数据按照大小排序后用槽分组，便于二分查找插入数据且通过算法保证每个分组中的数据不会太多进而提高效率</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-mysql-data/image-20231022152639-tt5wpmc.png" alt="image" data-caption="image" loading="lazy">​</p><h4 id="索引"><a href="#索引" class="headerlink" title="索引"></a>索引</h4><p>MySQL 的索引结构也是一种数据，也会以页进行存储，MYSQL 最常见的索引结构是 B+ 树。</p><p>B+树和 B 树的核心思想依旧是二分法，这两种树会在索引节点顺序存储数据页范围，这样就可以在顺序范围下通过二分法找到最符合要求的下一级节点的地址，循环往复即可找到最终需要的数据。</p><p>B+ 树比 B 树更适合 MySQL 索引结构的原因是，B 树会把完整数据存储在所有节点上，而 B+ 树只会把完整数据存储在叶子节点上，非叶子节点会存储索引需要的信息，而 InnoDB是按照页去存储索引数据的，在插入和查询的时候都会通过非叶子节点找到下一级节点，一个页的大小是固定的，这时候会更希望一次读取能获取到更多的索引信息而非被不需要的完整数据占用了大量空间。</p><p>‍</p><p>B+树的索引结构和用法决定了使用过程中的一些限制</p><p>特性：</p><ul><li><p>由于索引按照列顺序存储，如果在聚簇索引的场景下插入顺序非自增的话容易导致页分裂，降低写入效率</p></li><li><p>在非聚簇索引查询的场景下，就出现了查询的最左原则，只能顺序的单向的使用索引。</p></li><li><p>索引除查询外还可用于数据的排序、分组</p></li><li><p>为提高索引空间时间效率，列数据应该尽可能多样且列结构应该尽量小</p></li></ul><p>代价：</p><ul><li>创建索引会有空间损失 回表和维护额外数据结构有时间损失</li></ul><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-mysql-data/image-20231022153108-w3co404.png" alt="image" data-caption="image" loading="lazy">​</p><p>innodb中，每个页通过双向链表连接，再把页编号和做比较的键提取出来放到上一层的索引页中，进而可以通过二分法快速的获取数据。</p><p>‍</p><p>MySQL 在存储表数据时，本质上是创建了一个索引，存储完整数据的索引被称为聚簇索引，而我们自建列的索引被称为非聚簇索引，非聚簇索引只会存储 索引列、主键等必要信息，体积比完整索引小很多，但带来的问题是如果需要获取非索引列的数据就需要根据主键去聚簇索引中找，这个过程就叫做回表，回表会带来额外的性能损失。反之，如果需要查询的列都在非聚簇索引中，那么就会省略这个回表操作，进而提升性能。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-mysql-data/image-20231022160918-fkn8z8d.png" alt="image" data-caption="image" loading="lazy">​</p><h4 id="表空间、段和区"><a href="#表空间、段和区" class="headerlink" title="表空间、段和区"></a>表空间、段和区</h4><p>表空间，表空间是为单个数据表服务的，对应一个或多个表文件，比如前面提到的 表名.idb</p><p>每个索引（聚簇或非聚簇都一样）包含两个段，叶子节点段和非叶子节点段，每个段都包含一些区，区下面就是页，每个区有256个页面。</p><p>段内第一个区所存储的数据与其他区不一样，因为会有额外的段锁需要的信息，意思也就是段信息最终写文件也是落地到页上的。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-mysql-data/image-20231022155028-q9j66v5.png" alt="image" data-caption="image" loading="lazy">​</p><p>引入段目的是加快索引速度，方便管理页面，以及减少随机io。</p><p>为优化数据利用，段在为写入数据找区的时候会先申请在碎片区中写入，写满32个碎片区会申请独立区。</p><blockquote><p>独立的区会按照利用情况放到三个链表中，free，not_full和full。</p><p>区有四种状态，free 空闲区，free_frag 有空闲碎片区，full_frag 无空闲碎片区，faeg 属于某个段的区。</p><p>如果把表空间比作是一个集团军，段就相当于师，区就相当于团。一般的团都是隶属于某个师的，就像是处于FSEG的区全都隶属于某个段，而处于FREE、FREE_FRAG以及FULL_FRAG这三种状态的区却直接隶属于表空间，就像独立团直接听命于军部一样。</p></blockquote><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>理解底层数据结构的设计有利于掌握 MySQL 的设计初衷和存在一些规则约束的根本原因，比如命名规则、索引最左匹配原则、避免回表的优化等等。</p><p>语言整理可能稍显混乱，最后贴上文章中整理的底层结构图</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/blog-mysql-data/image-20231022155257-ex7gsav.png" alt="image" data-caption="image" loading="lazy">​</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h1 id=&quot;MYSQL-的底层数据结构&quot;&gt;&lt;a href=&quot;#MYSQL-的底层数据结构&quot; class=&quot;headerlink&quot; title=&quot;MYSQL 的底层数据结构&quot;&gt;&lt;/a&gt;MYSQL 的底层数据结构&lt;/h1&gt;&lt;h2</summary>
        
      
    
    
    
    <category term="Linux" scheme="https://blog.wj2015.com/categories/Linux/"/>
    
    <category term="Mysql" scheme="https://blog.wj2015.com/categories/Linux/Mysql/"/>
    
    
    <category term="Mysql" scheme="https://blog.wj2015.com/tags/Mysql/"/>
    
  </entry>
  
  <entry>
    <title>致郁的欲及再谈电子阳痿</title>
    <link href="https://blog.wj2015.com/2023/08/15/%E8%87%B4%E9%83%81%E7%9A%84%E6%AC%B2%E5%8F%8A%E5%86%8D%E8%B0%88%E7%94%B5%E5%AD%90%E9%98%B3%E7%97%BF/"/>
    <id>https://blog.wj2015.com/2023/08/15/%E8%87%B4%E9%83%81%E7%9A%84%E6%AC%B2%E5%8F%8A%E5%86%8D%E8%B0%88%E7%94%B5%E5%AD%90%E9%98%B3%E7%97%BF/</id>
    <published>2023-08-15T16:17:25.000Z</published>
    <updated>2026-08-12T03:28:46.860Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>为什么会想到这个看起来比较沉重的话题，原因之一是博主最近会明显的感觉到一丝莫名的忧愁，那种缺失某些东西的惆怅，还有一点欲而不得的压力，并且会在上下班各一个半小时的通勤时间里发酵，不断加重着社畜机械生活带来的疲惫。</p><p>那天我站在地铁门口，突然就想探究反思一下，欲求不满会导致忧郁，那么我究竟对什么有欲？为什么会有该欲？能否得到妥善的解决和舒缓？</p><h2 id="对欲的分析"><a href="#对欲的分析" class="headerlink" title="对欲的分析"></a>对欲的分析</h2><p>这里并没有把马斯洛需求层析理论搬出来，目前大部分人已经在社会生活中满足了生理需要、安全需要以及也有不少人在婚姻和社交中满足了归属和爱的需要，在这样的日常生活下剩下的那些需求描述就有些太宽泛和抽象了。</p><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/image-20230815233223-dd3iv60.png" alt="image" data-caption="image" loading="lazy">​</p><p>于是我把近期个人生活中迸发出的让人忧郁的欲望分为下面三类：</p><ul><li>追求快乐的欲，比如男生喜欢寄望于游戏带来的快乐以及有充足时间去猎取快乐的自由</li><li>得到认可的欲，从外在环境获得进认同，实现自我价值的欲</li><li>要而不得的欲，缺失选择自由时，会对无法得到的物寄予欲</li></ul><p>​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/image-20230815233613-ortj9ui.png" alt="image" data-caption="image" loading="lazy">​</p><h3 id="追求快乐的欲"><a href="#追求快乐的欲" class="headerlink" title="追求快乐的欲"></a>追求快乐的欲</h3><p>追求更多快乐的欲可以认为是一种本能的追求，以及对日常世界的躲避，正因为日常生活中缺少这样的快乐，才会去其他地方追逐快乐。比如广义上的游戏：运动、旅行等一切愉悦自己的东西。以及生活中缺少某些自由，才会到游戏中追求自由。</p><h3 id="得到认可的欲"><a href="#得到认可的欲" class="headerlink" title="得到认可的欲"></a>得到认可的欲</h3><p>由于表现自我、刷新他人认知带来的自我满足阈值会随时间增长越来越高。当个人能力和外界对个人的预期达到一个短期平衡的时候，会更难获得这些稀缺的嘉奖，这样会带来更多的欲，内心求之而不得的循环往复只会加强这个过程。</p><h3 id="要而不得的欲"><a href="#要而不得的欲" class="headerlink" title="要而不得的欲"></a>要而不得的欲</h3><p>客观现实与理想期望有冲突的时候就会触动这样的欲，通常是上面两种欲望的延伸。比如我在追求打游戏的快乐时会想置办性能更强大的机器以及购买更多的游戏，但我的现实资源不允许我自由的选择想要的东西，<strong>这种要而不得，会产生欲，理性和欲望交织就会忧郁苦闷，而且越循环越深陷</strong>。</p><p>有一类行为叫逛淘宝，真的只逛不买，一来是希望淘到更多的东西，还喜欢在挑选商品的过程中想想它出现在自己生活中的感觉，但现实中的钱包又不允许，花时间花精力花脑力后什么也没有获得。而这时候可能花呗会跳出来，让人们可以通过透支未来实现这样的欲望，不过这就是另一个话题了。</p><p>以及标题中提到的电子阳痿更像是对快乐的要而不得，<strong>因为现实中缺少自由游戏的时光和更多的快乐，才会这么热衷于搜集“快乐的外在实体”，但同时又没有现实条件（时间/精力/其他更容易快乐的手段）去通过这些外在实体去收获想要的快乐。</strong></p><h2 id="如何解决和舒缓"><a href="#如何解决和舒缓" class="headerlink" title="如何解决和舒缓"></a>如何解决和舒缓</h2><p>在博主的前文 <a href="https://blog.wj2015.com/2023/06/08/%E8%B0%88%E8%B0%88%E5%A8%B1%E4%B9%90%E7%84%A6%E8%99%91%E5%92%8C%E7%94%B5%E5%AD%90%E9%98%B3%E7%97%BF/">《谈谈娱乐焦虑和电子阳痿》</a>中提到了延迟满足和专注的方式与自己的本能脑和解，从欲的角度出发，短期的延迟娱乐是延缓和满足欲的一种方式，但对于众多要而不得的欲来说，还需要力所能及+有度渐进。</p><p>以电子阳痿为例，<strong>购买更多游戏的前提，应该是我需要靠他来获得快乐，而不只是为了搜集这份快乐的外在实体</strong>。不然短期可能会因为搜集和拥有实体而得到快乐，长期来看拥有的只是对游戏内容和自由游戏时间的求而不得的欲。当一件游戏商品不能带给我它最重要的提供快乐的价值，我在现在拥有了它与我没有它又有什么样的本质区别呢？</p><p>​​<img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/image-20230815234540-akj9bw3.png" alt="image" data-caption="image" loading="lazy">​​</p><p>追求快乐的欲，这与跟纯粹的热爱搜集有所区别，搜集的目标是获得外在实体，而大多数买游戏不玩的行为是在追求游玩游戏背后的快乐。但也并不是一刀切完全阻止自己去了解和翻阅各种各样感兴趣的内容，因为这本身也是一种快乐，只是不要把自己一直置于求而不得的漩涡中。</p><p>得到认可的欲，对应的是人的被赞美被认可的基础需求，也许<strong>我们需要更多的理智去思考，增加更多的自我认同的基石，而不只是寄望于外界不稳定的稀缺的正反馈</strong>。</p><p>要而不得的欲，<strong>本质上是理想和现实的冲突</strong>，虽然有很多东西无法立即拿到，但可以在力所能及的范围内满足自己，就如我没有可以玩4k@60hz游戏的设备，但我可以降低分辨率，可以游玩前作，可以玩其他能带给我快乐的东西。也可以做延迟满足，给自己积累现实条件的时间（金钱，状态等）未来再购入做延迟满足。<strong>如果一直纠结于没有办法获得的快乐载体而非尝试去获取快乐本身，就会陷入要而不得的欲望漩涡中</strong>。</p><p>整理了几个关键词：</p><ul><li>面对追求快乐的欲，应思考快乐本身的价值在何处，理智行动</li><li>得到认可的欲，应增加更多自我认同的基石，客观看待世界</li><li>要而不得的欲，应力所能及，辅以延迟满足，不要落于欲望的外在，更应落于本质</li></ul><h2 id="尾声"><a href="#尾声" class="headerlink" title="尾声"></a>尾声</h2><p>博主尝试过梳理了自己最近萌生的各种欲并一一思考，没有这些致郁的欲的干扰后，会更容易专注于应该做的事情上，心里不会一直有郁郁的压力，也不会不时望着自己想要的东西流口水影响节奏。</p><p>不过每个人的情况和问题是不一样的，重要的是在自己烦郁的时候也要去思考自己情绪的根源，希望能给你一些启发。</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot;</summary>
        
      
    
    
    
    <category term="欲望" scheme="https://blog.wj2015.com/tags/%E6%AC%B2%E6%9C%9B/"/>
    
    
    <category term="焦虑" scheme="https://blog.wj2015.com/tags/%E7%84%A6%E8%99%91/"/>
    
    <category term="生活" scheme="https://blog.wj2015.com/tags/%E7%94%9F%E6%B4%BB/"/>
    
    <category term="随笔" scheme="https://blog.wj2015.com/categories/%E9%9A%8F%E7%AC%94/"/>
    
  </entry>
  
  <entry>
    <title>一图说清Linux中的虚拟内存、驻留内存、共享内存</title>
    <link href="https://blog.wj2015.com/2023/06/24/%E4%B8%80%E5%9B%BE%E8%AF%B4%E6%B8%85Linux%E4%B8%AD%E7%9A%84%E8%99%9A%E6%8B%9F%E5%86%85%E5%AD%98%E3%80%81%E9%A9%BB%E7%95%99%E5%86%85%E5%AD%98%E3%80%81%E5%85%B1%E4%BA%AB%E5%86%85%E5%AD%98/"/>
    <id>https://blog.wj2015.com/2023/06/24/%E4%B8%80%E5%9B%BE%E8%AF%B4%E6%B8%85Linux%E4%B8%AD%E7%9A%84%E8%99%9A%E6%8B%9F%E5%86%85%E5%AD%98%E3%80%81%E9%A9%BB%E7%95%99%E5%86%85%E5%AD%98%E3%80%81%E5%85%B1%E4%BA%AB%E5%86%85%E5%AD%98/</id>
    <published>2023-06-24T03:33:07.000Z</published>
    <updated>2026-08-12T03:28:46.856Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>日常工作生活中，总是会遇到查看Linux内某进程占用内存的场景，但打开 <code>top</code> 或 <code>htop</code> 就会看到内存栏其实被分为了三个指标：VIRT、RES、SHR，分别代表 虚拟内存（Virtual Memory）、驻留内存（Resident Memory）、共享内存（Share Memory）</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">进程号 USER      PR  NI    VIRT    RES    SHR    %CPU  %MEM     TIME+ COMMAND<br> 837833 root      20   0  722628  22684  13948 S   4.3   0.1   6:22.89 aliyunpan<br></code></pre></td></tr></tbody></table></figure><p>本篇博客会尝试通过一张参考图讲清这三者之间的关系和用途</p><h2 id="几种内存概念"><a href="#几种内存概念" class="headerlink" title="几种内存概念"></a>几种内存概念</h2><p><img src="/static/assets/e997f051f55c0f6e2cf75c6b8c95c5534917421a.jpg"></p><p>随后会根据上图两个进程的内存占用图做具体的分析</p><h3 id="虚拟内存"><a href="#虚拟内存" class="headerlink" title="虚拟内存"></a>虚拟内存</h3><p>虚拟内存是一个逻辑意义上的内存空间，程序运行过程中需要被访问到的部分会映射到实际的物理内存空间中。</p><p><strong>虚拟内存较大只能表示程序运行时的可访问空间大</strong>，不代表真实内存的空间占用。</p><p>在上图中：</p><ul><li><p>A进程的 A1+A2+A3+A4+灰色部分=A进程虚拟内存大小</p></li><li><p>B进程的 B1+B2+B3+灰色部分=B进程虚拟内存大小</p></li></ul><p>每个进程都有一个<strong>内存页映射表</strong>，用于存放虚拟内存=&gt;物理内存的映射关系</p><h3 id="共享内存"><a href="#共享内存" class="headerlink" title="共享内存"></a>共享内存</h3><p>不同进程依赖的同一部分内存，比如多个程序依赖同一个动态库（.so），<strong>这些动态库的内存在系统中只保留一份，在进程需要使用时动态的映射到进程的虚拟内存空间中</strong>（使用上面提到的内存页映射表）。</p><p>在上图中：</p><ul><li><p>A 进程的 A4 = A 进程共享内存</p></li><li><p>B 进程的 B3 = B 进程共享内存</p></li></ul><h3 id="驻留内存"><a href="#驻留内存" class="headerlink" title="驻留内存"></a>驻留内存</h3><p>顾名思义，程序实际在物理内存中驻留使用的内存，一般看进程的内存占用就看这个指标，值得注意的是，这里是包括共享内存的。</p><p>在上图中：</p><ul><li><p>A 进程的 A1+A2+A3+A4 = A进程的驻留内存</p></li><li><p>B 进程的 B1+B2+B3 = B进程的驻留内存</p></li></ul><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>虚拟内存是逻辑内存空间，通过页映射表与物理内存空间对应；共享内存是不同进程间的相同依赖；驻留内存是进程实际使用的部分内存。</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;日常工作生活中，总是会遇到查看Linux内某进程占用内存的场景，但打开</summary>
        
      
    
    
    
    <category term="Linux" scheme="https://blog.wj2015.com/categories/Linux/"/>
    
    
    <category term="Linux" scheme="https://blog.wj2015.com/tags/Linux/"/>
    
    <category term="内存" scheme="https://blog.wj2015.com/tags/%E5%86%85%E5%AD%98/"/>
    
  </entry>
  
  <entry>
    <title>谈谈娱乐焦虑和电子阳痿</title>
    <link href="https://blog.wj2015.com/2023/06/08/%E8%B0%88%E8%B0%88%E5%A8%B1%E4%B9%90%E7%84%A6%E8%99%91%E5%92%8C%E7%94%B5%E5%AD%90%E9%98%B3%E7%97%BF/"/>
    <id>https://blog.wj2015.com/2023/06/08/%E8%B0%88%E8%B0%88%E5%A8%B1%E4%B9%90%E7%84%A6%E8%99%91%E5%92%8C%E7%94%B5%E5%AD%90%E9%98%B3%E7%97%BF/</id>
    <published>2023-06-08T12:42:27.000Z</published>
    <updated>2026-08-12T03:28:46.860Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>开始本篇文章之前，想跟大伙儿交流下病情，不知道你们有没有遇到过这样的场景：</p><ul><li><p>每天下班路上心潮澎拜，想着回去可以做一下 XXX 提升一下个人技术力，但又惯性的点个外卖/下碗面条，拿着手机边吃饭边刷视频，然后眼睁睁看着时间跑到了11点，愈发觉得休息时间这么短，明天又要早起上班，在着一丝焦虑的心情中失眠，接着拿起了手机希望快乐的时间能延长一点</p></li><li><p>克服了一切困难，坐到了电脑面前开机，打开浏览器，突然想了解下国（鸡）家（毛）大（蒜）事（皮），然后就输入了 zhihu.com，刷他一个小时，直到头晕脑热心情疲倦，一看时间凌晨11点，心里想着今天怎么又啥也没干，带着丝丝焦虑入眠</p></li><li><p>做正事儿的时候走了个神，想到同龄人已经年入 xx 万，买房买车抚育下一代了，自己却好像还在一个地方转圈，想着想着心理心里逐渐焦虑，最后事情也没做好，焦虑却更加严重了</p></li></ul><p>再以及电子阳痿：</p><ul><li>心潮澎湃买了个 switch，整天盯着数字商店刷哪个游戏打折，看着评价和截图一阵哇塞买买买，512G的存储装满后发现真正打开玩完的游戏寥寥无几</li></ul><p>上面的示例都来自博主自己以及与博主倾诉的朋友们，不知道有多少小伙伴对此有切身体会呢。</p><h2 id="关于焦虑"><a href="#关于焦虑" class="headerlink" title="关于焦虑"></a>关于焦虑</h2><p>相比电子阳痿，博主更想先了解下焦虑是怎么一回事，那么要想了解一个事物产生的原因，首先得了解它是什么，比如百度百科：</p><blockquote><p>焦虑（Anxiety）是指个人对即将来临的、可能会造成的危险或威胁所产生的紧张、不安、忧虑、烦恼等不愉快的复杂情绪状态。</p></blockquote><h4 id="成长焦虑"><a href="#成长焦虑" class="headerlink" title="成长焦虑"></a>成长焦虑</h4><p>分析一下前言中提到的第一个例子，在回家路上想着提升个人技能，但到家后又在惯性下投入到重复而又无效娱乐活动中。</p><p>这里的技能成长焦虑对应的危险就是“世界变化这么快，不抓紧时间学习就会被淘汰”，所以理智告诉我“回去把 xx 学了吧，以后在 xx 场景可能用上”，但实际的表现却是吃饭刷手机睡觉，<strong>这样的行动只会让“被淘汰”这个危险可能性变得更大了，离目标越来越远，那么焦虑就在其中逐渐加深</strong>。</p><p>成长的焦虑还会一些辅助原因，比如学习本身就是费脑力的，带着困难的，相比面对困难，荡漾在愉悦的信息流中不是更舒适么？</p><h4 id="物质焦虑"><a href="#物质焦虑" class="headerlink" title="物质焦虑"></a>物质焦虑</h4><p>再思考一下前言中提到的第三个例子，相比同龄人，自己不足的地方有很多，焦虑的质问自己为什么自己达不到那样的高度。</p><p>这里的物质焦虑对应的危险就是“反观自身，<strong>发现与之前同级的人差异已经逐渐拉开，有被歧视、脱离群体的危险</strong>”</p><p>这时候我们看到的是结果，甚至不知道他们是如何一步步拉开差距的，更别说自己要如何做，全都是模糊的一团，一直沉浸在这样的情绪反应中是解决不了问题的，我们需要找到焦虑的本质并应对它。</p><h3 id="焦虑的本质"><a href="#焦虑的本质" class="headerlink" title="焦虑的本质"></a>焦虑的本质</h3><p>其本质在定义里边说了，我个人觉得很准确，就是<strong>对威胁、危险的个人情绪反应</strong>；</p><p>但焦虑持久存在还有一些深层次的原因，比如：</p><ul><li><p><strong>急于求成，想同时做很多事</strong>，结果给自己的目标越来越大和困难越来越多，分心乏术又带着痛苦的继续往上爬</p></li><li><p><strong>趋易避难，想不怎么努力就看到好的效果；</strong> 实施过程中总是在自己可以轻易做到的事情上打转，花了时间却又很难看到提升的效果，每天完成打卡任务似的应付自己。</p></li><li><p><strong>重形式，轻实践；</strong> 看了无数的书籍和教程，但实际上手的时间屈指可数，过了半个月，发现自己之前会的东西全忘了，又不想再重新学一遍，于是这一个大难关就一直埋在心里，时不时出来刺激自己一下</p></li></ul><h2 id="如何应对？"><a href="#如何应对？" class="headerlink" title="如何应对？"></a>如何应对？</h2><p>那么理解这些原因之后，我们一点点的刨析就可以想办法应对它</p><h4 id="保持耐心"><a href="#保持耐心" class="headerlink" title="保持耐心"></a>保持耐心</h4><p><strong>有一个实用技巧叫做“延迟满足”</strong>，理论是这样讲的，让人沉浸在愉悦的信息流中的是人类自进化以来保留的 <strong>“潜意识”追逐低能耗的天性</strong>，动脑是高能耗的运动，而刷抖音并不需要动脑，只需要躺着享受就好了。</p><p>而我们的“理智脑”是可以克服这种本能的冲动，让自己做最理性最正确的事情的；不过应该都知道，克服本能是很难受的，因为“潜意识”会不停的反抗说“我们应该做低能耗且享受的事情”；</p><blockquote><p>“找一个自己能坚持做下去的方式，比单纯按照标准化的时间和方式做更重要”</p><p>适当降低难度和标准，会持续得更久，感到不适只靠专注硬撑容易被潜意识抗拒。</p></blockquote><p>减少这种反抗就可以使用“延迟满足”的技巧，在理智脑思考的霎那，我们把当下刷抖音、刷知乎的<strong>快乐留到完成某个小目标后</strong>，完成这个小目标在自己专注的前提下大概需要多少时间自己心里都有数，然后等待片刻，<strong>让心里衡量一下“带着焦虑去追求快乐”和“等一段时间目标达成就可以无忧无虑的快乐”两者的得失，追求确定性和单一性的“潜意识”一般都不会拒绝，并且也不会在处理任务期间打扰到“理智”</strong>，专注度和效率也会得到提升。</p><p>长此以往形成习惯，在娱乐前做一些沉浸式的阶段性的任务，在任务完成之后专注的享受一段娱乐，“理智”和“潜意识”互利共赢。</p><h4 id="保持专注"><a href="#保持专注" class="headerlink" title="保持专注"></a>保持专注</h4><p>不知道多少人会在走路的时候刷抖音，吃饭的时候看动漫，博主之前一直也不觉其害，甚至觉得筷子跟手机简直绝配，一心二用赚爆了。</p><p>但一些习惯会逐渐演化为：工作的时候走神，准备看书的时候被手机信息流吸引，<del>陪老婆逛街的时候看美女（划掉）</del></p><p>危害都懂在此不表，这种状态下，我们在躯壳内装了一个混乱的灵魂，这个灵魂总是“做A想B”：刷牙的时候走神，走路的时候走神，洗澡的时候走神……无时无刻不在走神。</p><p><strong>分心的本质是一种趋易避难的逃避天性</strong>，原因无非两个：</p><ul><li><p>一是觉得当下太无聊，所以追求更有意思的事情；</p></li><li><p>二是觉得当下太痛苦，于是追求更舒适的事情。因为身体受困于现实，只好让思想天马行空。</p></li></ul><p>保持专注也并非学霸专属，正如李大钊同志教育孩子所述：</p><blockquote><p>“做什么事情都不能三心二意，要学就学个踏实，要玩就玩个痛快。” –李大钊</p></blockquote><p>可以从小事做起，吃饭的时候享受美食，工作的时候专注工作，学习的时候屏蔽无关干扰，玩游戏的时候时刻留神战场动态，养成专注的习惯。</p><h4 id="少即是多，多既是少"><a href="#少即是多，多既是少" class="headerlink" title="少即是多，多既是少"></a>少即是多，多既是少</h4><p><img src="/static/assets/140d4b5a05242a8d2c20a283e83627e847a1bac5.jpg"></p><p>看书也好、看视频也罢，都是获取信息的一个方面，看完这一段话，是左耳朵进右耳朵出呢，还是停下来反思一下自己的日常，尝试定制一些可行措施呢？ <strong>学习层投入再多努力，也需要思考实践和改变，</strong> 大家都看完了这一段文字，但最终的收获差别可能是很大的。</p><p>这里有一个技巧叫做，“熔断不读书法”，意思就是在读书时一旦看到有启发的内容，就触发熔断点，立刻停止阅读。停止阅读之后做什么呢？围绕这个触发点对自己提问。</p><ul><li><p>为什么刚才这个点让我有启发？</p></li><li><p>我能够把这个启发点用在3个不同的事情上吗？</p></li><li><p>这个启发点有没有其他类似的知识？</p></li></ul><h4 id="目标明确，拒绝模糊"><a href="#目标明确，拒绝模糊" class="headerlink" title="目标明确，拒绝模糊"></a>目标明确，拒绝模糊</h4><p>缺乏自我观察意识的人只能无意识地顺着感觉和喜好行事，无论在生理上还是在精神上，都会不自觉地追求眼前的舒适和简单，觉察不到自己当前的思维和行为有什么不妥，直到碰壁。</p><blockquote><p>如果人生是大海，那我们每个人都是一条小船，自我认知能力强的人会时刻掌握方向舵，主动控制生命之船的航向，而认知能力弱的人总是喜欢待在甲板上当个忙碌的水手，至于船嘛，漂到哪里算哪里……</p></blockquote><p>我们会更喜欢给自己一个大目标、大梦想，比如数学考到140、长大住大house、成为一名歌手万人迷，<strong>但这种目标都是大而模糊的，能具体实现的目标应该是清晰且细化的</strong>，所以合理的拆分为小而明确的目标并逐步实现，才会离自己的大追求越来越近。</p><p>这个过程就类似深度学习中的梯度下降，我们不能指望一次性就找到一个最合理的权重，但每一次改变都会离我们的方向更进一步。</p><h3 id="电子阳痿"><a href="#电子阳痿" class="headerlink" title="电子阳痿"></a>电子阳痿</h3><p>思考完焦虑，反过头来看“电子阳痿”，我相信有很多人为了生活真的不会有时间玩游戏，久而久之反而对曾今最渴望的娱乐手段提不起兴趣，甚至感到陌生。</p><p>想想小时候能玩上那么一会儿超级玛丽，4399，街机都能兴奋好久，现在各种娱乐设备触手可及但又提不起兴致，心里确实会感到那么一丝无奈。</p><p>不怕丢人，博主就是那个买了 switch，游戏塞满了内存卡，却没玩几个的深度患者；但对于那些需要持续占领心智思考，体验时间很长的游戏提不起精神，<strong>其中一个主要原因还是专注</strong></p><p>比如我知道解决这个谜题推进剧情、花很长时间沉静在故事里会让我很开心，但是追求确定和节能的“潜意识”还是会让我选择刷刷刷就能重复获得快乐和爽感的游戏，或者转向刷一刷就能获得多巴胺奖励的短视频。</p><p>在娱乐多元化的时代，选择什么样的娱乐方式萝卜青菜各有所爱，对博主个人来说，做到 “要学就学个踏实，要玩就玩个痛快。”，<strong>不要在学习工作的时候想着娱乐，在娱乐的时候焦虑未来，这就足够了</strong>！</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>某位冬泳博主曾说：“消除恐惧的最好办法就是面对恐惧，加油，奥利给！”</p><p><img src="/static/assets/a2beba1842ef16a92feed54860ff21e568c9ebcd.jpeg"></p><p>认知是一回事，行动和改变是另一回事，从小事做起，保持耐心，保持专注，然后逐渐变得更好！</p><p>就像博主读完书后产出的这一片文章就是一种行动输出，在上文提到的方案切实在生活中尝试并且持续下去了，就会逐步改善生活状态和心态。</p><p>博客中部分文字、图片素材、观点引入自 <strong>《认知觉醒：开启自我改变的原动力》</strong>，是非常棒的一本书，推荐阅读。</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot;</summary>
        
      
    
    
    
    <category term="焦虑" scheme="https://blog.wj2015.com/tags/%E7%84%A6%E8%99%91/"/>
    
    
    <category term="生活" scheme="https://blog.wj2015.com/tags/%E7%94%9F%E6%B4%BB/"/>
    
    <category term="随笔" scheme="https://blog.wj2015.com/categories/%E9%9A%8F%E7%AC%94/"/>
    
  </entry>
  
  <entry>
    <title>ChatGPT 调研小结</title>
    <link href="https://blog.wj2015.com/2023/06/02/ChatGPT%E8%B0%83%E7%A0%94%E5%B0%8F%E7%BB%93/"/>
    <id>https://blog.wj2015.com/2023/06/02/ChatGPT%E8%B0%83%E7%A0%94%E5%B0%8F%E7%BB%93/</id>
    <published>2023-06-02T13:52:07.000Z</published>
    <updated>2026-08-12T03:28:46.852Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>博主其实并不是专门从事 LLM 方向的程序员，但 23年初 ChatGPT 横空出世的时候着实震惊了我一把，博主也算第一时间注册了账号进行体验；</p><p>事实证明这很明智，这两个月发现新账号越来越难注册了，界面也越来越不稳定，于是乎我又化身为一个资(bei)源(po)的付费用户，直到今天。</p><p>与 GPT 交流的时间长了，然后就一直很好奇一些问题：</p><ul><li><p>ChatGPT 是怎么演变而来的？它的内部结构是什么样子？</p></li><li><p>ChatGPT 有智慧么？</p></li><li><p>为什么它有时候会胡言乱语？ </p></li><li><p>为什么我跟它说话，它会一个字一个字往外蹦？而不是一个词一个词的回答我？</p></li></ul><p>本文会从怎么使用聊起，探索它现在有什么有趣的玩法，以及它是怎么形成、怎么工作的；不过碍于篇幅，这篇文章并不会过多的啰嗦，只是一个使用小结，大家可以轻松愉快的看完它。</p><h2 id="怎么跟它对话"><a href="#怎么跟它对话" class="headerlink" title="怎么跟它对话"></a>怎么跟它对话</h2><p>想要玩 ChatGPT，自然就得先访问到他们的服务嘛，那这里就分两条路了：</p><ul><li><p>注册 ChatGPT，直接在 web 界面上使用</p></li><li><p>使用国内转出来的“盗版” ChatGPT</p></li></ul><p>第一条路呢，网上写的注册教程铺天盖地，随便找了一个，仅供参考：<a href="https://github.com/xiaoming2028/FreePAC/wiki/ChatGPT%E6%B3%A8%E5%86%8C%E6%95%99%E7%A8%8B%EF%BC%88%E5%AE%8C%E6%95%B4%E6%8C%87%E5%8D%97%EF%BC%89">ChatGPT注册教程（完整指南） · xiaoming2028/FreePAC Wiki · GitHub</a></p><p>重点就是：</p><ul><li><p>解决网络问题</p></li><li><p>搞一个谷歌账号</p></li><li><p>搞一个SIM接码平台</p></li><li><p>官网注册</p></li></ul><p><img src="/static/assets/2023-06-02-22-21-11-image.png"></p><p>那个 sms activate 里边的号码很多都被玩坏了，尝试新注册过很多次都碰到风控了，但我同事就能一次成功，所以碰到了就多试几次，功夫不负有心人，再者小黄鱼各种平台都有卖账号的，想尝试的都能想到办法。</p><p>第二条路，国内外可用的资源就比较多了，基本上就是注册账号就能免费玩上那么一阵子，需要持久使用的话多个站点切换就行，重点是免费</p><p>搜集到一些可用的网站，可直接体验：</p><ul><li><p><a href="https://ministers.ai/home">https://ministers.ai/home</a></p></li><li><p><a href="https://chat.forefront.ai/">https://chat.forefront.ai/</a></p></li><li><p><a href="https://chatgptonline.ai/chat/">Chat &amp;#8211; ChatGPT Online</a></p></li><li><p><a href="https://gpt4login.com/use-chatgpt-online-free/">Use ChatGPT Online Free - GPT-4 Login</a></p></li></ul><h2 id="一些好玩的东西"><a href="#一些好玩的东西" class="headerlink" title="一些好玩的东西"></a>一些好玩的东西</h2><h3 id="角色扮演"><a href="#角色扮演" class="headerlink" title="角色扮演"></a>角色扮演</h3><p><a href="https://github.com/SillyTavern/SillyTavern">GitHub - SillyTavern/SillyTavern: LLM Frontend for Power Users.</a></p><p><a href="https://book.character.ai/character-book/welcome-to-character-book">Welcome to Character Book! - Character.AI</a></p><p><img src="/static/assets/2023-06-02-22-28-38-image.png"></p><p><img src="/static/assets/2023-06-02-22-31-09-image.png"></p><p><img src="/static/assets/2023-06-02-22-31-30-image.png"></p><p>通过 SillyTavern 这个工具，可以比较方便的定制自己喜欢的角色、通过 StableDiffusion 扩展描绘场景和角色，甚至让 ChatGPT 扮演文字探险游戏的 GM 来一场刺激的地牢探险</p><p><img src="/static/assets/2023-06-02-22-32-40-img_v2_97a8c31f-3e69-42bd-8552-45a808fd520h.jpg"></p><h3 id="写小说"><a href="#写小说" class="headerlink" title="写小说"></a>写小说</h3><p><a href="https://mp.weixin.qq.com/s/26S-y9ZhTxi3KHWL6v62Ew">仅用自然语言，让ChatGPT输出连贯的长篇小说！苏黎世联邦理工大学提出RecurrentGPT</a></p><p>本质是使用 prompt 引导 GPT 思考情节，并对每次新增的内容不断的做总结（压缩），思考后续场景时把前面次总结 + 总体的大纲带上，模拟人类长期记忆过程。</p><p>开源项目地址：<a href="https://github.com/aiwaves-cn/RecurrentGPT">https://github.com/aiwaves-cn/RecurrentGPT</a><br>作者开放体验地址：<a href="https://www.aiwaves.org/interactivefiction">交互式小说生成</a></p><p><img src="/static/assets/2023-06-03-11-12-34-a081c51f5c5ffaece100657672c016e.jpg"></p><h3 id="自建-GPT-服务"><a href="#自建-GPT-服务" class="headerlink" title="自建 GPT 服务"></a>自建 GPT 服务</h3><p><a href="https://github.com/Yidadaa/ChatGPT-Next-Web">GitHub - Yidadaa/ChatGPT-Next-Web: One-Click to deploy well-designed ChatGPT web UI on Vercel. 一键拥有你自己的 ChatGPT 网页服务。</a></p><p><a href="https://github.com/GaiZhenbiao/ChuanhuChatGPT">GitHub - GaiZhenbiao/ChuanhuChatGPT: GUI for ChatGPT API and many LLMs</a></p><p>这类服务最大的意义是，可以让身边感兴趣但折腾能力不足的人无缝上手，避免去注册账号、搞定网络、找网站；而且最大的一点：稳定。</p><blockquote><p>不过这类服务如果自己部署的话，在注册账号免费的 API 额度过期后，需要绑定海外信用卡才可以继续使用下去。这个成本会比较高，操作也比较麻烦，有需要自行检索即可。</p></blockquote><p>我尝试部署过很多个这样的服务，最终留下了 ChatGPT Next，面具和预设真的很方便，分享给别人后，聊天数据只会持久化到浏览器，隐私困扰少。</p><p><img src="/static/assets/2023-06-02-22-42-18-image.png"></p><h3 id="LangChain"><a href="#LangChain" class="headerlink" title="LangChain"></a>LangChain</h3><p><a href="https://github.com/binary-husky/gpt_academic">GitHub - binary-husky/gpt_academic: 为ChatGPT/GLM提供图形交互界面，特别优化论文阅读润色体验，模块化设计支持自定义快捷按钮&amp;函数插件，支持代码块表格显示，Tex公式双显示，支持Python和C++等项目剖析&amp;自译解功能，PDF/LaTex论文翻译&amp;总结功能，支持并行问询多种LLM模型，支持清华chatglm等本地模型。兼容复旦MOSS, llama, rwkv, 盘古, newbing, claude等</a></p><p><a href="https://github.com/mayooear/gpt4-pdf-chatbot-langchain">GitHub - mayooear/gpt4-pdf-chatbot-langchain: GPT4 &amp; LangChain Chatbot for large PDF docs</a></p><p><a href="https://github.com/jerryjliu/llama_index">GitHub - jerryjliu/llama_index: LlamaIndex (GPT Index) is a data framework for your LLM applications</a></p><p>基于 LangChain 可以让 GPT 做更多的事情，比如与一篇 PDF 文档聊天、读论文等等</p><blockquote><p>PS:  这张图是 gpt_academic 项目截图，底层并非使用 langchain，只有一个理解 pdf 功能与 langchain 的 combine 总结功能类似。</p></blockquote><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="https://static.flomoapp.com/file/2023-05-28/73871/2b8019867a4c451bb4e60500383a57b9.png?OSSAccessKeyId=LTAI4G9PcaGksWVKCPrE1TVL&amp;Expires=1685889191&amp;Signature=SfYEb7j9z69Bf3RdJmccpAWYtUI=" alt="thumbnailwebp" data-caption="thumbnailwebp" loading="lazy"></p><p><a href="https://github.com/FlowiseAI/Flowise">GitHub - FlowiseAI/Flowise: Drag &amp; drop UI to build your customized LLM flow using LangchainJS</a></p><p><a href="https://github.com/hwchase17/langchain/">GitHub - hwchase17/langchain: ⚡ Building applications with LLMs through composability ⚡</a></p><p>更技术一点，就是可以使用工作流的方式把 LangChain 组件组合起来，为自己服务</p><p><img src="https://github.com/FlowiseAI/Flowise/raw/main/images/flowise.gif?raw=true"></p><h2 id="一些底层原理"><a href="#一些底层原理" class="headerlink" title="一些底层原理"></a>一些底层原理</h2><p>作为技术工作者，自然也需要对其原理做一下简单的了解，以明确其能力边界和特性，文字版教程推荐：<a href="https://juejin.cn/book/7225592349563289600?suid=1679709499036206&amp;source=android">人人都能看懂的 ChatGPT 原理课 - JioNLP - 掘金小册</a></p><p><img src="/static/assets/2023-06-02-22-44-41-img_v2_4970535a-5101-4393-b020-ac914870caah.jpg"></p><p>视频版教程推荐两个 B站的教程：</p><p><a href="https://www.bilibili.com/video/BV1yV4y1k7Tc/?spm_id_from=333.880.my_history.page.click">训练原理科普</a></p><p><a href="https://www.bilibili.com/video/BV1em4y187CU/?spm_id_from=333.880.my_history.page.click&amp;vd_source=d3df27ba79e25e88d0242b745a857c29">ChatGPT会产生意识么？</a></p><p>看完相关教程后，博主还整理了一个思维导图分享，供大家参考：</p><p><img src="/static/assets/f5a5a4f733061a73f9062f1de907a546c1cd5fe9.Jpeg"></p><p>还有更清楚的 PDF 版本：<a href="/static/assets/ChatGPT%E5%8E%9F%E7%90%86-230602193139.pdf">ChatGPT原理-230602193139.pdf</a></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>ChatGPT 所代表的大语言模型潜力还远远没有被挖掘出来，上面提到的都只是冰山一角，博主自我感觉连门槛都没有踏入。</p><p>博主相信随着 LLM 潜力的进一步挖掘，基于 ChatGPT 也一定会涌现出来无数高效率工具，它会如20年前的互联网一样，一步步成为我们工作生活中密不可分的一部分（参见博主写的另一篇文章：<a href="https://blog.wj2015.com/2023/04/05/%E5%9F%BA%E4%BA%8Echatgpt%E6%81%90%E6%80%96%E8%83%BD%E5%8A%9B%E7%9A%84%E9%AB%98%E6%95%88%E7%8E%87%E5%B7%A5%E5%85%B7%E6%B6%8C%E7%8E%B0%E6%84%8F%E5%91%B3%E7%9D%80%E4%BB%80%E4%B9%88/">基于ChatGPT恐怖能力的高效率工具涌现意味着什么 - 九层台</a>）。</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;博主其实并不是专门从事 LLM 方向的程序员，但 23年初 ChatGPT</summary>
        
      
    
    
    
    <category term="chatgpt" scheme="https://blog.wj2015.com/tags/chatgpt/"/>
    
    
    <category term="机器学习" scheme="https://blog.wj2015.com/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>人月神话阅读纲要</title>
    <link href="https://blog.wj2015.com/2023/06/01/%E4%BA%BA%E6%9C%88%E7%A5%9E%E8%AF%9D%E9%98%85%E8%AF%BB%E7%BA%B2%E8%A6%81/"/>
    <id>https://blog.wj2015.com/2023/06/01/%E4%BA%BA%E6%9C%88%E7%A5%9E%E8%AF%9D%E9%98%85%E8%AF%BB%E7%BA%B2%E8%A6%81/</id>
    <published>2023-06-01T13:19:09.000Z</published>
    <updated>2026-08-12T03:28:46.856Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>《人月神话》这本书是我从大学时候开始就听说过的一本神作，记得几年前参加一个技术大会，台上的讲师问了参会者一个问题：<strong>“已知孕妇十月怀胎可以剩下一个小孩，那么给你一百个孕妇多久能生下来一个小孩”</strong></p><p>这个问题其实就是经典的“人月问题”，我们都知道一百个孕妇不可能在一个星期内生下来一个孩子，体现在实际项目中就表现为<strong>人和时间是不可互换的</strong>；人越多，沟通成本越高，反而导致完成时间增加，<strong>向进度落后的项目追加人手，只会让项目进度更加落后</strong>。</p><p><img src="/static/assets/2023-06-01-22-05-39-img_v2_3e47f572-2c87-4177-9e74-b8919382755h.jpg"></p><p>《人月神话》从人出发，分多个章节讨论了很多项目管理、实际开发中的问题以及作者的个人经验，包括但不限于：</p><ul><li><p>项目管理</p></li><li><p>不同团队构成和特点</p></li><li><p>文档体系构建</p></li><li><p>产品进度管理</p></li><li><p>组织架构和计划</p></li><li><p>产品定价、成本和功能的平衡</p></li><li><p>测试环节的问题</p></li><li><p>版本依赖和控制</p></li><li><p>构件设计和集成思路</p></li><li><p>里程碑和分工协作</p></li></ul><h2 id="纲要"><a href="#纲要" class="headerlink" title="纲要"></a>纲要</h2><p>分享一些博主认为获益匪浅的金句吧，你也许能在其中看到自己项目的影子：</p><blockquote><p>缺乏合理的进度管理，是造成项目滞后的主要原因，向进度落后的项⽬中增加⼈⼿，只会使进度更加落后</p><p>《第二章-人月神话》</p><p>组织是成功的关键，交流组织技能和能⼒提升⼀样重要</p><p>《第七章-为什么巴比伦塔会失败》</p><p>精湛的技艺出自创造，技艺的改进往往是战略的突破，战略的突破源于数据的重新表达，<strong>数据的表现是编程的根本</strong></p><p>《第九章-削足适履》</p><p><strong>不变只是愿望，变化才是永恒，唯一不变的就是变化本身</strong>，软件产品易于掌握的特性和不可⻅性，导致开发者⾯临永恒的需求变更</p><p>《第十一章-未雨绸缪》</p><p><strong>软件开发是减熵</strong>的过程，本⾝处于亚稳态。<strong>软件维护是增熵</strong>过程，熟练的维护者也只能放缓系统退化到⾮稳态的过程。</p><p>《第十一章-未雨绸缪》</p><p>产品定义是关键，细致的功能定义，规范的说明能有效减少bug</p><p>《第十三章-整体设计》</p><p><strong>软件项⽬看似简单明了，却可能变成落后进度，超出预算，⼤量缺陷的⼈狼怪物</strong>，其根本困难在于<strong>软件特性中固有的困难</strong>以及<strong>软件⼯程本⾝的特性</strong>就代表着⽆法像硬件⼀样，翻倍的提升⽣产率</p><p>《第十六章-没有银弹》</p></blockquote><p>博主<strong>把每一章节的关键信息点都提炼了出来做成了思维导图</strong>，可以根据导图做整体上的理解，并跟着感兴趣的地方去网上继续检索资料学习，有条件也可以买一本闲时阅读，不贵但读完很有帮助。</p><p>JD直达：<a href="https://u.jd.com/asgRl19">京东-人月神话</a></p><p>PDF 版本的纲要会更清晰也更值得收藏：<a href="/static/assets/%E4%BA%BA%E6%9C%88%E7%A5%9E%E8%AF%9D-230601213124.pdf">人月神话阅读纲要</a></p><p><img onerror="imgOnError(this);" data-fancybox="gallery" src="/static/assets/060121323704.jpg" alt="思维导图" data-caption="思维导图" loading="lazy"></p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot;</summary>
        
      
    
    
    
    <category term="产品" scheme="https://blog.wj2015.com/categories/%E4%BA%A7%E5%93%81/"/>
    
    
    <category term="产品" scheme="https://blog.wj2015.com/tags/%E4%BA%A7%E5%93%81/"/>
    
    <category term="人月神话" scheme="https://blog.wj2015.com/tags/%E4%BA%BA%E6%9C%88%E7%A5%9E%E8%AF%9D/"/>
    
    <category term="阅读" scheme="https://blog.wj2015.com/tags/%E9%98%85%E8%AF%BB/"/>
    
  </entry>
  
  <entry>
    <title>Ubuntu 22.04 安装使用绿联千兆网口 AX88179</title>
    <link href="https://blog.wj2015.com/2023/05/15/Ubuntu22.04%E5%AE%89%E8%A3%85%E4%BD%BF%E7%94%A8%E7%BB%BF%E8%81%94%E5%8D%83%E5%85%86%E7%BD%91%E5%8F%A3/"/>
    <id>https://blog.wj2015.com/2023/05/15/Ubuntu22.04%E5%AE%89%E8%A3%85%E4%BD%BF%E7%94%A8%E7%BB%BF%E8%81%94%E5%8D%83%E5%85%86%E7%BD%91%E5%8F%A3/</id>
    <published>2023-05-15T22:47:54.000Z</published>
    <updated>2026-08-12T03:28:46.852Z</updated>
    
    <content type="html"><![CDATA[<html><head></head><body><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>此篇博客的目标是为 Ubuntu 22.04 的服务器扩展一个千兆网口，JD 上踩过其他品牌的坑，最终还是选择了绿联（PS:没给广告费），虽然官网比较简陋提供的驱动也都还有点问题，但好歹是 Linux 下一番折腾还是能用的。</p><p><img src="/static/assets/2023-05-16-06-59-00-img_v2_8c473334-bc0f-4e0c-b20a-ee753896e0bh.jpg"></p><h2 id="自带驱动问题"><a href="#自带驱动问题" class="headerlink" title="自带驱动问题"></a>自带驱动问题</h2><p>我使用的是 Ubuntu 22.04 的操作系统，内核版本 5.19，里边自带一个 ax88179_179a  的内核模块，使用 <code>lsmod</code> 和 <code>modinfo</code> 命令可查看相关信息</p><figure class="highlight bash"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs bash">$ <span class="hljs-built_in">uname</span> -a<br>Linux wang-B250ET2 5.19.0-41-generic <span class="hljs-comment">#42~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue Apr 18 17:40:00 UTC 2 x86_64 x86_64 x86_64 GNU/Linux</span><br>$ lsmod | grep <span class="hljs-string">"ax88179"</span><br>ax88179_178a           36864  0<br>usbnet                 53248  1 ax88179_178a<br>mii                    20480  2 usbnet,ax88179_178a<br>$ modinfo ax88179_178a<br>filename:       /lib/modules/5.19.0-41-generic/kernel/drivers/net/usb/ax88179_178a.ko<br>license:        GPL<br>description:    ASIX AX88179_178A USB 2.0/3.0 Ethernet Devices<br>author:         David Hollis<br>srcversion:     BDE6AA432409AEB043AF2AA<br><span class="hljs-built_in">alias</span>:          usb:v0711p0179d*dc*dsc*dp*ic*isc*ip*<span class="hljs-keyword">in</span>*<br><span class="hljs-built_in">alias</span>:          usb:v2001p4A00d*dc*dsc*dp*ic*isc*ip*<span class="hljs-keyword">in</span>*<br><span class="hljs-built_in">alias</span>:          usb:v04E8pA100d*dc*dsc*dp*ic*isc*ip*<span class="hljs-keyword">in</span>*<br><span class="hljs-built_in">alias</span>:          usb:v0930p0A13d*dc*dsc*dp*ic*isc*ip*<span class="hljs-keyword">in</span>*<br><span class="hljs-built_in">alias</span>:          usb:v17EFp304Bd*dc*dsc*dp*ic*isc*ip*<span class="hljs-keyword">in</span>*<br><span class="hljs-built_in">alias</span>:          usb:v0DF6p0072d*dc*dsc*dp*ic*isc*ip*<span class="hljs-keyword">in</span>*<br><span class="hljs-built_in">alias</span>:          usb:v0B95p178Ad*dc*dsc*dp*ic*isc*ip*<span class="hljs-keyword">in</span>*<br><span class="hljs-built_in">alias</span>:          usb:v0B95p1790d*dc*dsc*dp*ic*isc*ip*<span class="hljs-keyword">in</span>*<br>depends:        mii,usbnet<br>retpoline:      Y<br>name:           ax88179_178a<br>vermagic:       5.19.0-41-generic SMP preempt mod_unload modversions <br>parm:           msg_enable:usbnet msg_enable (int)<br>parm:           bsize:RX Bulk IN Queue Size (int)<br>parm:           ifg:RX Bulk IN Inter Frame Gap (int)<br>parm:           bEEE:EEE advertisement configuration (int)<br>parm:           bGETH:Green ethernet configuration (int)<br>parm:           tx_dma_sg:Whether to use the dma_sg feature <span class="hljs-keyword">for</span> tx <span class="hljs-keyword">if</span> supported, <span class="hljs-string">"no"</span> by default (bool)<br></code></pre></td></tr></tbody></table></figure><p>插入之后直接就可以识别出来设备，执行 <code>lsusb</code> 可以看到一个 <code>AX88179 Gigabit Ethern</code> 的设备，但是 <code>ip addr</code> 是看不到新增的网口</p><figure class="highlight bash"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs bash">$ lsusb <br>Bus 002 Device 003: ID 1058:2622 Western Digital Technologies, Inc. Elements SE 2622<br>Bus 002 Device 004: ID 0b95:1790 ASIX Electronics Corp. AX88179 Gigabit Ethernet<br>Bus 002 Device 002: ID 05e3:0626 Genesys Logic, Inc. USB3.1 Hub<br>Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub<br>Bus 001 Device 003: ID 046d:c52b Logitech, Inc. Unifying Receiver<br>Bus 001 Device 002: ID 062a:4101 MosArt Semiconductor Corp. Wireless Keyboard/Mouse<br>Bus 001 Device 004: ID 05e3:0610 Genesys Logic, Inc. Hub<br>Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub<br></code></pre></td></tr></tbody></table></figure><p>可能是设备启动时出了问题，那么下一步就是看 <code>dmesg</code>，从输出中很容易就找到了下面这个错误</p><blockquote><p>ax88179_178a 2-5.4:1.0 (unnamed net_device) (uninitialized): Failed to read reg index 0x0040: -32</p></blockquote><h2 id="安装新驱动"><a href="#安装新驱动" class="headerlink" title="安装新驱动"></a>安装新驱动</h2><p>既然内核自带的驱动不好使，这个时候我就考虑重新安装驱动了</p><p>我首先尝试的是绿联官网的千兆网卡驱动：<a href="https://www.lulian.cn/download/4-cn.html">绿联Type-C千兆网卡驱动下载</a></p><p>下载压缩包并解压其中的 Linux 驱动 .tar.bz2 文件</p><figure class="highlight bash"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs bash">$ tar -jxvf AX88179_178A_LINUX_DRIVER_v1.20.0_SOURCE.tar.bz2 <br>AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.h<br>AX88179_178A_Linux_Driver_v1.20.0_source/Makefile<br>AX88179_178A_Linux_Driver_v1.20.0_source/readme<br>AX88179_178A_Linux_Driver_v1.20.0_source/<br>AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c<br></code></pre></td></tr></tbody></table></figure><p>然后安装编译环境</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">sudo apt-get install make gcc<br></code></pre></td></tr></tbody></table></figure><p>执行编译，如果你内核版本跟我一样(5.13+)，大概会碰到这样的错误：<code>error: ‘usbnet_get_stats64’ undeclared here (not in a function); did you mean ‘usbnet_cdc_status’?</code></p><figure class="highlight bash"><table><tbody><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><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ make<br>make -C /lib/modules/5.19.0-41-generic/build M=/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source modules<br>make[1]: 进入目录“/usr/src/linux-headers-5.19.0-41-generic”<br>warning: the compiler differs from the one used to build the kernel<br>  The kernel was built by: x86_64-linux-gnu-gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0<br>  You are using:           gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0<br>  CC [M]  /home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.o<br>In file included from ./include/linux/string.h:253,<br>                 from ./include/linux/bitmap.h:11,<br>                 from ./include/linux/cpumask.h:12,<br>                 from ./arch/x86/include/asm/cpumask.h:5,<br>                 from ./arch/x86/include/asm/msr.h:11,<br>                 from ./arch/x86/include/asm/processor.h:22,<br>                 from ./arch/x86/include/asm/timex.h:5,<br>                 from ./include/linux/timex.h:67,<br>                 from ./include/linux/time32.h:13,<br>                 from ./include/linux/time.h:60,<br>                 from ./include/linux/stat.h:19,<br>                 from ./include/linux/module.h:13,<br>                 from /home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:30:<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c: In <span class="hljs-keyword">function</span> ‘ax88179_set_mac_addr’:<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:1000:19: warning: passing argument 1 of ‘__builtin_memcpy’ discards ‘const’ qualifier from pointer target <span class="hljs-built_in">type</span> [-Wdiscarded-qualifiers]<br> 1000 |         memcpy(net-&gt;dev_addr, addr-&gt;sa_data, ETH_ALEN);<br>      |                ~~~^~~~~~~~~~<br>./include/linux/fortify-string.h:379:27: note: <span class="hljs-keyword">in</span> definition of macro ‘__fortify_memcpy_chk’<br>  379 |         __underlying_<span class="hljs-comment">##op(p, q, __fortify_size);                        \</span><br>      |                           ^<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:1000:9: note: <span class="hljs-keyword">in</span> expansion of macro ‘memcpy’<br> 1000 |         memcpy(net-&gt;dev_addr, addr-&gt;sa_data, ETH_ALEN);<br>      |         ^~~~~~<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:1000:19: note: expected ‘void *’ but argument is of <span class="hljs-built_in">type</span> ‘const unsigned char *’<br> 1000 |         memcpy(net-&gt;dev_addr, addr-&gt;sa_data, ETH_ALEN);<br>      |                ~~~^~~~~~~~~~<br>./include/linux/fortify-string.h:379:27: note: <span class="hljs-keyword">in</span> definition of macro ‘__fortify_memcpy_chk’<br>  379 |         __underlying_<span class="hljs-comment">##op(p, q, __fortify_size);                        \</span><br>      |                           ^<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:1000:9: note: <span class="hljs-keyword">in</span> expansion of macro ‘memcpy’<br> 1000 |         memcpy(net-&gt;dev_addr, addr-&gt;sa_data, ETH_ALEN);<br>      |         ^~~~~~<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:1004:47: warning: passing argument 6 of ‘ax88179_write_cmd’ discards ‘const’ qualifier from pointer target <span class="hljs-built_in">type</span> [-Wdiscarded-qualifiers]<br> 1004 |                                  ETH_ALEN, net-&gt;dev_addr);<br>      |                                            ~~~^~~~~~~~~~<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:216:46: note: expected ‘void *’ but argument is of <span class="hljs-built_in">type</span> ‘const unsigned char *’<br>  216 |                              u16 size, void *data)<br>      |                                        ~~~~~~^~~~<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c: At top level:<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:1030:35: error: ‘usbnet_get_stats64’ undeclared here (not <span class="hljs-keyword">in</span> a <span class="hljs-keyword">function</span>); did you mean ‘usbnet_cdc_status’?<br> 1030 |         .ndo_get_stats64        = usbnet_get_stats64,<br>      |                                   ^~~~~~~~~~~~~~~~~~<br>      |                                   usbnet_cdc_status<br>In file included from ./include/linux/string.h:253,<br>                 from ./include/linux/bitmap.h:11,<br>                 from ./include/linux/cpumask.h:12,<br>                 from ./arch/x86/include/asm/cpumask.h:5,<br>                 from ./arch/x86/include/asm/msr.h:11,<br>                 from ./arch/x86/include/asm/processor.h:22,<br>                 from ./arch/x86/include/asm/timex.h:5,<br>                 from ./include/linux/timex.h:67,<br>                 from ./include/linux/time32.h:13,<br>                 from ./include/linux/time.h:60,<br>                 from ./include/linux/stat.h:19,<br>                 from ./include/linux/module.h:13,<br>                 from /home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:30:<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c: In <span class="hljs-keyword">function</span> ‘access_eeprom_mac’:<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:1436:32: warning: passing argument 1 of ‘__builtin_memcpy’ discards ‘const’ qualifier from pointer target <span class="hljs-built_in">type</span> [-Wdiscarded-qualifiers]<br> 1436 |                 memcpy(dev-&gt;net-&gt;dev_addr, buf, ETH_ALEN);<br>      |                        ~~~~~~~~^~~~~~~~~~<br>./include/linux/fortify-string.h:379:27: note: <span class="hljs-keyword">in</span> definition of macro ‘__fortify_memcpy_chk’<br>  379 |         __underlying_<span class="hljs-comment">##op(p, q, __fortify_size);                        \</span><br>      |                           ^<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:1436:17: note: <span class="hljs-keyword">in</span> expansion of macro ‘memcpy’<br> 1436 |                 memcpy(dev-&gt;net-&gt;dev_addr, buf, ETH_ALEN);<br>      |                 ^~~~~~<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:1436:32: note: expected ‘void *’ but argument is of <span class="hljs-built_in">type</span> ‘const unsigned char *’<br> 1436 |                 memcpy(dev-&gt;net-&gt;dev_addr, buf, ETH_ALEN);<br>      |                        ~~~~~~~~^~~~~~~~~~<br>./include/linux/fortify-string.h:379:27: note: <span class="hljs-keyword">in</span> definition of macro ‘__fortify_memcpy_chk’<br>  379 |         __underlying_<span class="hljs-comment">##op(p, q, __fortify_size);                        \</span><br>      |                           ^<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:1436:17: note: <span class="hljs-keyword">in</span> expansion of macro ‘memcpy’<br> 1436 |                 memcpy(dev-&gt;net-&gt;dev_addr, buf, ETH_ALEN);<br>      |                 ^~~~~~<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c: In <span class="hljs-keyword">function</span> ‘ax88179_get_mac’:<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:1498:54: warning: passing argument 2 of ‘access_eeprom_mac’ discards ‘const’ qualifier from pointer target <span class="hljs-built_in">type</span> [-Wdiscarded-qualifiers]<br> 1498 |                 ret = access_eeprom_mac(dev, dev-&gt;net-&gt;dev_addr, 0x0, 1);<br>      |                                              ~~~~~~~~^~~~~~~~~~<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:1399:54: note: expected ‘u8 *’ {aka ‘unsigned char *’} but argument is of <span class="hljs-built_in">type</span> ‘const unsigned char *’<br> 1399 | static int access_eeprom_mac(struct usbnet *dev, u8 *buf, u8 offset, int wflag)<br>      |                                                  ~~~~^~~<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:1535:45: warning: passing argument 6 of ‘ax88179_write_cmd’ discards ‘const’ qualifier from pointer target <span class="hljs-built_in">type</span> [-Wdiscarded-qualifiers]<br> 1535 |                           ETH_ALEN, dev-&gt;net-&gt;dev_addr);<br>      |                                     ~~~~~~~~^~~~~~~~~~<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:216:46: note: expected ‘void *’ but argument is of <span class="hljs-built_in">type</span> ‘const unsigned char *’<br>  216 |                              u16 size, void *data)<br>      |                                        ~~~~~~^~~~<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c: In <span class="hljs-keyword">function</span> ‘ax88179_reset’:<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:2104:45: warning: passing argument 6 of ‘ax88179_write_cmd’ discards ‘const’ qualifier from pointer target <span class="hljs-built_in">type</span> [-Wdiscarded-qualifiers]<br> 2104 |                           ETH_ALEN, dev-&gt;net-&gt;dev_addr);<br>      |                                     ~~~~~~~~^~~~~~~~~~<br>/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.c:216:46: note: expected ‘void *’ but argument is of <span class="hljs-built_in">type</span> ‘const unsigned char *’<br>  216 |                              u16 size, void *data)<br>      |                                        ~~~~~~^~~~<br>make[2]: *** [scripts/Makefile.build:257：/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/ax88179_178a.o] 错误 1<br>make[1]: *** [Makefile:1850：/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source] 错误 2<br>make[1]: 离开目录“/usr/src/linux-headers-5.19.0-41-generic”<br>make: *** [Makefile:30：default] 错误 2<br></code></pre></td></tr></tbody></table></figure><p>于是乎，我拿着错误在网上搜了搜，尝试了很多解决方案均无法正常使用网卡，所以我求助了绿联的专业客服，但他们给我的回复是：“仅支持 5.13 以下内核，建议降低内核版本试试，更新的内核驱动修复日期不详”</p><p><img src="/static/assets/2023-05-16-07-17-23-img_v2_22e39f24-bbc3-41f8-b007-d48d1a28f89h.jpg"></p><p>不过内核版本直接影响系统稳定，肯定是不能随便降的，但是作为一个折腾服务器的老网络人，还是没有放弃，继续寻找解决方案，最终让我发现了这么一个 ISSUE: <a href="https://github.com/nothingstopsme/AX88179_178A_Linux_Driver/issues/1">Compile errors with kernel 5.16.10 // Getting your imrovements upstream into the Linux kernel · Issue #1 · nothingstopsme/AX88179_178A_Linux_Driver · GitHub</a></p><p>作者说已经解决了这个 BUG，于是乎我就下载了这个开源的驱动仓库并尝试编译，成功！</p><figure class="highlight bash"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs bash">$ git <span class="hljs-built_in">clone</span> https://github.com/nothingstopsme/AX88179_178A_Linux_Driver<br>$ <span class="hljs-built_in">cd</span> AX88179_178A_Linux_Driver/source/<br>$ make<br>make -C /lib/modules/5.19.0-41-generic/build M=/home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/AX88179_178A_Linux_Driver/source modules<br>make[1]: 进入目录“/usr/src/linux-headers-5.19.0-41-generic”<br>warning: the compiler differs from the one used to build the kernel<br>  The kernel was built by: x86_64-linux-gnu-gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0<br>  You are using:           gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0<br>  CC [M]  /home/wang/AX88179_178A_Linux_Driver_v1.20.0_source/AX88179_178A_Linux_Driver/source/ax88179_178a.o<br><span class="hljs-comment"># 做 make install 之前，可以</span><br>$ make install<br></code></pre></td></tr></tbody></table></figure><p>编译完成后会出现编译产物 <code>ax88179_178a.ko</code>，使用 <code>make install</code> 安装此模块，如果担心玩坏了，可以先备份一下 <code>/lib/modules/5.19.0-41-generic/kernel/drivers/net/usb/ax88179_178a.ko</code></p><figure class="highlight bash"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs bash">$ <span class="hljs-built_in">ls</span> -lah<br>总计 1.4M<br>drwxrwxr-x 2 wang wang 4.0K  5月 16 07:20 .<br>drwxrwxr-x 4 wang wang 4.0K  5月 16 07:20 ..<br>-rw-rw-r-- 1 wang wang  68K  5月 16 07:20 ax88179_178a.c<br>-rw-rw-r-- 1 wang wang  12K  5月 16 07:20 ax88179_178a.h<br>-rw-rw-r-- 1 wang wang 574K  5月 16 07:20 ax88179_178a.ko<br>-rw-rw-r-- 1 wang wang  497  5月 16 07:20 .ax88179_178a.ko.cmd<br>-rw-rw-r-- 1 wang wang  100  5月 16 07:20 ax88179_178a.mod<br>-rw-rw-r-- 1 wang wang 3.7K  5月 16 07:20 ax88179_178a.mod.c<br>-rw-rw-r-- 1 wang wang  364  5月 16 07:20 .ax88179_178a.mod.cmd<br>-rw-rw-r-- 1 wang wang  54K  5月 16 07:20 ax88179_178a.mod.o<br>-rw-rw-r-- 1 wang wang  32K  5月 16 07:20 .ax88179_178a.mod.o.cmd<br>-rw-rw-r-- 1 wang wang 522K  5月 16 07:20 ax88179_178a.o<br>-rw-rw-r-- 1 wang wang  59K  5月 16 07:20 .ax88179_178a.o.cmd<br>-rw-rw-r-- 1 wang wang 1.2K  5月 16 07:20 Makefile<br>-rw-rw-r-- 1 wang wang  101  5月 16 07:20 modules.order<br>-rw-rw-r-- 1 wang wang  343  5月 16 07:20 .modules.order.cmd<br>-rw-rw-r-- 1 wang wang    0  5月 16 07:20 Module.symvers<br>-rw-rw-r-- 1 wang wang  382  5月 16 07:20 .Module.symvers.cmd<br>-rw-rw-r-- 1 wang wang 3.5K  5月 16 07:20 readme<br><span class="hljs-comment"># 执行此操作前可以先备份一下</span><br>$ make install<br><span class="hljs-comment"># 安装完后重启</span><br>$ sudo reboot<br></code></pre></td></tr></tbody></table></figure><p>重启前 <code>dmesg</code> 检查下，没有再次出现之前的报错就算问题解决了，重启后就能看到新识别出来的网卡</p><figure class="highlight bash"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs bash">$ ip addr<br>1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000<br>    <span class="hljs-built_in">link</span>/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00<br>    inet 127.0.0.1/8 scope host lo<br>       valid_lft forever preferred_lft forever<br>    inet6 ::1/128 scope host <br>       valid_lft forever preferred_lft forever<br>2: enp2s0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc fq_codel state UP group default qlen 1000<br>    <span class="hljs-built_in">link</span>/ether b8:97:5a:ff:45:cc brd ff:ff:ff:ff:ff:ff<br>3: enx000ec68ee90d: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc fq_codel state UP group default qlen 1000<br>    <span class="hljs-built_in">link</span>/ether 00:0e:c6:8e:e9:0d brd ff:ff:ff:ff:ff:ff<br>    inet 192.168.1.16/24 brd 192.168.1.255 scope global dynamic noprefixroute enx000ec68ee90d<br>       valid_lft 56815sec preferred_lft 56815sec<br>    inet6 240e:388:9f06:100:f4d:af8b:e099:f2d3/64 scope global temporary dynamic <br>       valid_lft 259180sec preferred_lft 56494sec<br>    inet6 240e:388:9f06:100:3497:293c:4393:a48d/64 scope global dynamic mngtmpaddr noprefixroute <br>       valid_lft 259180sec preferred_lft 172780sec<br>    inet6 fe80::8b25:81fe:35fb:b655/64 scope <span class="hljs-built_in">link</span> noprefixroute <br>       valid_lft forever preferred_lft forever<br></code></pre></td></tr></tbody></table></figure><p>翻阅了一下代码，发现是内核 API 变动导致的，作者使用 #IF 做了内核版本的判断，就解决了这个 BUG</p><figure class="highlight c"><table><tbody><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></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-meta">#<span class="hljs-keyword">if</span> LINUX_VERSION_CODE &gt; KERNEL_VERSION(2, 6, 29)</span><br><span class="hljs-type">static</span> <span class="hljs-type">const</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">net_device_ops</span> <span class="hljs-title">ax88179_netdev_ops</span> =</span> {<br>    .ndo_open        = usbnet_open,<br>    .ndo_stop        = usbnet_stop,<br>    .ndo_start_xmit        = usbnet_start_xmit,<br>    .ndo_tx_timeout        = usbnet_tx_timeout,<br>    .ndo_change_mtu        = ax88179_change_mtu,<br>    .ndo_do_ioctl        = ax88179_ioctl,<br>    .ndo_set_mac_address    = ax88179_set_mac_addr,<br>    .ndo_validate_addr    = eth_validate_addr,<br><span class="hljs-meta">#<span class="hljs-keyword">if</span> LINUX_VERSION_CODE &lt;= KERNEL_VERSION(3, 2, 0)</span><br>    .ndo_set_multicast_list    = ax88179_set_multicast,<br><span class="hljs-meta">#<span class="hljs-keyword">else</span></span><br>    .ndo_set_rx_mode    = ax88179_set_multicast,<br><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">if</span> LINUX_VERSION_CODE &gt;= KERNEL_VERSION(2, 6, 39)</span><br>    .ndo_set_features    = ax88179_set_features,<br><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">if</span> LINUX_VERSION_CODE &gt;= KERNEL_VERSION(5, 11, 0)</span><br>    .ndo_get_stats64    = dev_get_tstats64,<br><span class="hljs-meta">#<span class="hljs-keyword">elif</span> LINUX_VERSION_CODE &gt;= KERNEL_VERSION(4, 12, 0)</span><br>    .ndo_get_stats64    = usbnet_get_stats64,<br><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span><br>};<br><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span><br></code></pre></td></tr></tbody></table></figure><p>随后笔者为绿联客服反馈了相关解决方案，不过在笔者写这篇文章的时候，绿联官网上千兆网卡驱动的最后更新时间还是 “2022/07/29”，如果你也有类似的需求，请注意不要下错了。</p><p><img src="/static/assets/2023-05-16-07-17-33-img_v2_f66d4ec8-5f33-402f-9451-6400aa70a08h.jpg"></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>所谓官方驱动不一定是真”官方“，碰到问题还是需要多在网上搜索尝试，希望能帮到你。</p></body></html>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;此篇博客的目标是为 Ubuntu 22.04 的服务器扩展一个千兆网口，JD</summary>
        
      
    
    
    
    <category term="Linux" scheme="https://blog.wj2015.com/categories/Linux/"/>
    
    
    <category term="NAS" scheme="https://blog.wj2015.com/tags/NAS/"/>
    
  </entry>
  
</feed>
