<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://blog.ai-nous.com/</id>
  <title type="text">Pankitgg</title>
  <subtitle type="text">分享技术、生活、以及人生感悟</subtitle>
  <updated>2026-10-16T00:00:00.000Z</updated>
  <author><name>Pankitgg</name></author>
  <link rel="alternate" href="https://blog.ai-nous.com/"/>
  <link rel="self" href="https://blog.ai-nous.com/atom.xml"/>
  <generator uri="https://github.com/CuteLeaf/Firefly">Firefly v6.16.8</generator>
    <entry>
      <id>https://blog.ai-nous.com/posts/wo-you-yi-ge-gou-xiang-jian-yi-ge-yong-yuan-bu-hui-dao-bi-de-you-xi/</id>
      <title type="text">我有一个构想，建一个永远不会倒闭的游戏</title>
      <published>2026-10-16T00:00:00.000Z</published>
      <updated>2026-10-16T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/wo-you-yi-ge-gou-xiang-jian-yi-ge-yong-yuan-bu-hui-dao-bi-de-you-xi/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<p>我是一个特别喜欢玩游戏的人，有多喜欢呢，我玩了有1k以上的游戏，几乎覆盖了所有游戏类型。最长的也有上千个小时。</p>
<p>这个构想源于我对游戏厂商的不满。</p>
<p>对游戏厂商无穷尽的攫取游戏利益，对玩家的无视，我觉得是一种特别不公平的事。尤其是你喜欢的事情被三流的策划或者是肥头大耳的老板更改的时候。</p>
<p>我之前特别喜欢玩%%%%，运营的好好的也关了。类似的太多了。</p>
<ol>
<li>你有没有特别喜欢的游戏被游戏厂商搞坏了</li>
<li>你有没有一个游戏想永远的玩下去</li>
<li>游戏的贬值，不通过盈利目标的设计，可以让我们获取的物品有意义</li>
<li>通过玩家决策来实现内容驱动</li>
<li>只做一个后台和中间层，每个用户端都可以通过vibecoding自定义设置自己的游戏界面</li>
<li>类似于世界和境界。官方维护宏观时间观和主线世界地图。个人都可以加入自己的vibe作品作为子世界，可以实现各种复杂多样的游戏场景，只需要接入数据就行。而随着时间增长和游戏更新，境界会进行提升，升入下一个主线世界，每个世界维持自己数据不进行膨胀，这样就可以实现数据的膨胀控制。</li>
<li>基于区块链的物品打造，基于去中心化的对战和数据存储。以实现物品唯一性以及数据的永久保存。</li>
</ol>
<p>我现在正在寻找志同道合的队友，我们一起完成这个游戏。</p>
<a href="https://github.com/Pankitgg/Forever-Game" target="_blank"><div><div><div><div></div><div>Pankitgg</div></div><div>/</div><div>Forever-Game</div></div><div></div></div><div>Do you want to play a game that never shuts down?</div><div><div>—</div><div>—</div><div>no-license</div><span>Unavailable</span></div></a>
<hr />
<p>I’m someone who absolutely loves playing games. How much do I love them? I’ve played more than 1,000 games, covering nearly every genre out there, and I’ve put over a thousand hours into my most-played title.</p>
<p>This idea grew out of my frustration with game companies.</p>
<p>Their endless extraction of profit and their disregard for players strike me as deeply unfair — especially when something you love gets warped by third-rate designers or greedy, out-of-touch executives.</p>
<p>I used to be deeply into %%%%. It was running just fine, and they shut it down anyway. There are far too many stories like that.</p>
<ol>
<li>Have you ever had a game you loved ruined by its publisher?</li>
<li>Is there a game you wish you could keep playing forever?</li>
<li>Fighting the devaluation of games: by designing without profit targets, the items we earn can actually mean something.</li>
<li>Let content be driven by players’ decisions.</li>
<li>Build only a backend and a middleware layer — every client can customize its own game interface through vibecoding.</li>
<li>Think of it in terms of worlds and realms. The official team maintains the macro timeline and the main world map. Individuals can add their own vibe creations as sub-worlds, enabling all kinds of rich and varied game scenarios simply by plugging into the data. As time passes and the game updates, realms ascend into the next main world; each world keeps its own data without bloating, which keeps data expansion under control.</li>
<li>Blockchain-based item crafting, plus decentralized matchmaking and data storage — to guarantee the uniqueness of items and the permanent preservation of data.</li>
</ol>
<p>I’m now looking for like-minded teammates to build this game together.</p>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/agentocr-cao-zuo-dian-nao-shang-de-yi-qie-ruan-jian/</id>
      <title type="text">agent+ocr操作电脑上的一切软件</title>
      <published>2026-10-15T00:00:00.000Z</published>
      <updated>2026-10-15T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/agentocr-cao-zuo-dian-nao-shang-de-yi-qie-ruan-jian/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<p>最近尝试了一下使用Agent+rea分析软件构建信息库，在加上OCR识别和操作。可以做到很多正常情况下很容易被封禁的东西</p>
<section><h3>群聊机器人<a href="#群聊机器人"><span>#</span></a></h3><p></p><figure><img alt="群聊机器人" loading="lazy" width="639" height="414" src="/_astro/agentocr-0.D5d5k5ul_ZviKp2.webp" /><figcaption>群聊机器人</figcaption></figure><p></p></section>
<section><h3>游戏自动化<a href="#游戏自动化"><span>#</span></a></h3><p></p><figure><img alt="游戏自动化" loading="lazy" width="614" height="274" src="/_astro/agentocr-1.Ca1PYA47_1SXIEa.webp" /><figcaption>游戏自动化</figcaption></figure><p></p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/fuckdeno-ni-dui-xin-ren-ni-de-ren-jiu-zhe-me-yi-zou-liao-zhi-ma/</id>
      <title type="text">fuck deno 你对信任你的人就这么一走了之吗</title>
      <published>2026-10-14T00:00:00.000Z</published>
      <updated>2026-10-14T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/fuckdeno-ni-dui-xin-ren-ni-de-ren-jiu-zhe-me-yi-zou-liao-zhi-ma/"/>
      <summary type="text">deno团队最新消息，他们加入了cloudflare，与此同时deno将在维护一年以后交由社区维护，那么我押注在你身上的那些时间和精力又要怎么办呢，我的全栈框架刚开始就要结束了。还好我当时说了不绑定deno</summary>
      <content type="html"><![CDATA[<p>我之前一直探索着基于fresh的全栈框架，而fresh基于deno。同时运维着fresh的中文站点。</p>
<p>我对deno和fresh是有感情的，所以当我知道这个消息的时候是震惊的，先是不敢相信，再到想通。</p>
<a href="https://github.com/Pankitgg/one-is-all" target="_blank"><div><div><div><div></div><div>Pankitgg</div></div><div>/</div><div>one-is-all</div></div><div></div></div><div>全栈框架路线的探索-从fresh2开始的大模型AI融合之路 Exploring the Full-Stack Framework Path: The Journey of Integrating Large-Scale AI Starting with Fresh2</div><div><div>—</div><div>—</div><div>no-license</div><span>JavaScript</span></div></a>
<a href="https://github.com/Pankitgg/fresh-doc-cn" target="_blank"><div><div><div><div></div><div>Pankitgg</div></div><div>/</div><div>fresh-doc-cn</div></div><div></div></div><div>fresh 中文文档 个人学习使用 fresh 的时候多有不便，方便自己方便他人，更新频率保持和官方一致</div><div><div>—</div><div>—</div><div>MIT</div><span>JavaScript</span></div></a>
<p>看到标题，你是不是以为我会大骂一顿，恰恰相反，我认为这是最好的选择了。</p>
<p>deno一直以来最不喜欢的就是node，但是为了兼容生态最后又不得不做，而与此同时deno平台经历了缩容，商业化尝试也未成功。社区的质疑也从未有断。你想想，<strong>你从事一份自己并不喜欢的工作，没有工资并且还不被认可，再加上你也认为这不是正确的道路，你会选择怎么办？</strong></p>
<div><div><div></div><div>Tip</div></div><div><p>Ryan Dahl说他真正想做的，从来不只是「更好的 JavaScript 运行时」，而是更上层的抽象：计算、存储、通信一体化，让开发者不再自己拼基础设施。</p><p>所以这个选择也就更能理解了：把精力投入到 Cloudflare 的 Workers 编程模型里，而不是继续维护一个独立的、越来越像 Node 的运行时。</p></div></div>
<p>从商业上看，运行时的黄金时代过去了。bun、deno这种能走向商业公司算是最好的结局了。</p>
<p>从收购线路来看cloudflare野心很大，他想成为未来时代Agent的服务商。我有理由怀疑他在内部正在打造一套全新的<code>agent runtime</code>，而不是简单的将deno作为一个组件嵌入到他的产品中。</p>
<p>而与此同时很多默默维护开源项目的伟大开源者们，仍然在继续。</p>
<p>下图是我真正想说的话</p>
<p></p><figure><img alt="社区评价节选" loading="lazy" width="972" height="479" src="/_astro/deno-2.BtEDxrzP_Z1IdSID.webp" /><figcaption>社区评价节选</figcaption></figure><p></p>
<p>虽然仍然希望他们能继续下去，不过</p>
<p>祝愿deno团队好运，愿开源社区和精神继续前进。</p>
<p>至于我的开源全栈框架，哈哈哈，我准备转rust了。</p>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/agent-mi-guan-gong-ji-shen-ru-li-jie-agent-nei-bu-xun-huan-yu-gong-fang-dian/</id>
      <title type="text">Agent蜜罐攻击，深入理解agent内部循环与攻防点</title>
      <published>2026-09-29T00:00:00.000Z</published>
      <updated>2026-09-29T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/agent-mi-guan-gong-ji-shen-ru-li-jie-agent-nei-bu-xun-huan-yu-gong-fang-dian/"/>
      <summary type="text">构造一个看起来真实、但实际上是诱饵的环境/工具/数据，让自主 Agent 上钩，从而识别、跟踪、限制甚至反制 Agent</summary>
      <content type="html"><![CDATA[<p>agent 现在像是一个人一样，24小时游荡在互联网里，那么他有足够的智力能像人一样识别危险吗？</p>
<p>我们能不能制作一个蜜罐，诱捕 agent 上钩，从而识别、跟踪、限制甚至反制 agent 行为？</p>
<div><div><div></div><div>Warning</div></div><div><p>警告：以下内容包含严重的安全风险，不建议在实际环境中使用。</p><p>该文章只作为技术分享，是为了提升大家对agent安全的意识</p><p>因本文产生的技术延展以及实践中产生的安全事故，以及法律风险与本人无关！！！</p></div></div>
<blockquote><p>[!DANGER]</p><p>因为开源社区反应，目前在安全性和合规性上，该技术还未广为熟知。建议下架该文章。</p><p>毕竟技术无罪这种话，想想都可怕</p></blockquote>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E9%80%82%E7%94%A8%E4%BA%8Eai%E5%85%A8%E6%A0%88%E7%9A%84%E5%BC%80%E5%8F%91%E6%A1%86%E6%9E%B6one-is-all%E5%BC%80%E5%8F%91%E5%AE%9E%E5%BD%95%E4%BA%8C/</id>
      <title type="text">选什么初始项目作为vibecodeing的初始化，最适用于AI全栈的开发框架one-is-all（二）</title>
      <published>2026-09-27T00:00:00.000Z</published>
      <updated>2026-09-27T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E9%80%82%E7%94%A8%E4%BA%8Eai%E5%85%A8%E6%A0%88%E7%9A%84%E5%BC%80%E5%8F%91%E6%A1%86%E6%9E%B6one-is-all%E5%BC%80%E5%8F%91%E5%AE%9E%E5%BD%95%E4%BA%8C/"/>
      <summary type="text">终于有点时间整理下这个面向AI的框架了</summary>
      <content type="html"><![CDATA[<a href="/posts/%E9%80%82%E7%94%A8%E4%BA%8Eai%E5%85%A8%E6%A0%88%E7%9A%84%E5%BC%80%E5%8F%91%E6%A1%86%E6%9E%B6one-is-all%E5%BC%80%E5%8F%91%E5%AE%9E%E5%BD%95%E4%B8%80/"><div><div>选什么初始项目作为vibe-codeing的初始化，最适用于AI全栈的开发框架one-is-all（一）</div><div><span>2026-04-27</span><span>全栈</span><span><span>#one-is-all</span><span>#fresh</span><span>#deno</span><span>#solo</span></span></div></div></a>
<p>在上一期中，我介绍了one-is-all的选型，以及初始化。中间各种事情穿插，也没有好好整理和发文，这一期，我将详细讲解一下</p>
<section><h2>命名与形象<a href="#命名与形象"><span>#</span></a></h2><p>oia，中文读作「咿呀」。名字取自希腊圣托里尼岛的伊亚镇（Oia）——那个悬崖边上看”全世界最美日落”的艺术家村落。而她的全称是 one is all，「一即是全」：希望她能直接作为模型实现所有程序的基座。声调”咿呀”像孩童学语，也预示着这个项目将蹒跚学步，随着模型与智能体一同前进。</p><p>一句话定义：<strong>oia 是一个面向 AI Agent 的全栈框架脚手架</strong>。它不是提供给人类开发者的，而是便于模型理解、修改、运行一个安全完整的程序——包含前后端，乃至跨平台。</p><p>当前版本基于 Deno + Fresh 2.3.3 + Preact + Vite，配套 <code>npx @oia-ai/oia-skill</code> 一键安装的 Claude Code skill。</p></section>
<section><h2>选型<a href="#选型"><span>#</span></a></h2><p>有人会问：模型写 fresh/deno 的能力明明不如 next 和 node，为什么不直接用 next，反而要造轮子？</p><p>因为我看到的是另一个方向：deno 正在蓬勃地向 AI 靠拢，而 next/node 生态背着太多为”人类旧生态”设计的包袱。混乱的旧生态，有没有可能被一套原生适用于 AI 的新生态取代？oia 就是这个问题的实验田。</p><p>基于此，框架存在三条底层共识：</p><ol>
<li><strong>前后一体</strong>。前后端必须在一个目录里，对模型来说，一个仓库就是一份完整上下文，减少跨项目切换的认知成本。</li>
<li><strong>选型最小化</strong>。依赖越少，模型越容易理解全貌，开发环境秒起，CI/CD 也快。</li>
<li><strong>用 skill 和 spec 约束模型</strong>。不给模型自由发挥的空间，而是用规范收拢它。</li>
</ol></section>
<section><h2>设计<a href="#设计"><span>#</span></a></h2><p>落到架构上，oia 的核心是「可预测性」。</p><p><strong>目录即文档</strong>。routes、islands、components、features、server——每个目录职责单一且高度可预测，模型看目录结构就能推断系统边界，不需要猜测。相同类型的问题永远用相同的解法：路由薄、用例编排、仓储隔离，<code>route → service → repository</code> 的调用链是一条直线。</p><p><strong>交互局部化</strong>。Fresh 的 Islands 架构被放在首要位置：默认组件都是服务端渲染，只有真正需要浏览器交互的部分（输入框、拖拽、WebSocket）才做成 Island。目标是把 JavaScript 限制在真正需要交互的边界，SSR 优先，小 JS 优先。</p><p><strong>最小化运行时</strong>。默认数据库是 SQLite——单文件、零运维、随部署走。Redis、消息队列、独立搜索服务这些中间件一概不默认引入，只在真实需求出现时才通过明确的接口边界接入。禁止”为了看起来像生产架构”而堆砌中间件。</p><p><strong>约定优于魔法</strong>。这可能是最反直觉的一条：框架拒绝 BaseRepository、AbstractController 这类”为了架构感”的抽象。当一个实现看起来更工程化，却需要更多隐含上下文时，优先选择更简单、更显式的方案。</p></section>
<section><h2>与 AI 协作：Rules 不等于 Skills<a href="#与-ai-协作rules-不等于-skills"><span>#</span></a></h2><p>讨论框架如何”深度结合 AI 开发”，我想先澄清一个很容易被搞混的概念：</p><blockquote><p><strong>Rules ≠ Skills。</strong></p></blockquote><p>Rules 是告诉 Agent「这个项目用 Deno、组件用函数式写法、不要直接修改生成文件、所有 API 必须有输入验证」——它是<strong>常驻上下文（Always-on Context）</strong>，无论做什么任务都生效。</p><p>Skills 回答的则是另一类问题：「如何初始化一个项目」「如何设计一个 API」「如何做一次 code review」——它是<strong>按需加载的工作流（Workflow / Capability）</strong>，只有相关任务出现时才被唤起。</p><p>这一区分正在被整个 Coding Agent 生态采纳：从最早一份 <code>CLAUDE.md</code> / <code>AGENTS.md</code> 包打天下，到独立的 Skills，再到把需求 → 计划 → 执行 → 验证 → Review 串成完整的 Agent Workflow，方向已经相当清晰。因此 oia 从第一天起就没打算把所有指令塞进一个巨大的规则文件，而是分成两层：<strong>宪法归宪法，技能归技能</strong>。</p></section>
<section><h2>一部宪法，一套技能<a href="#一部宪法一套技能"><span>#</span></a></h2><p>oia 仓库里最重要的一份文件不是代码，是 <a href="https://github.com/Pankitgg/one-is-all/blob/main/AGENTS.md" target="_blank">AGENTS.md</a>——一部写给 Agent 的宪法。</p><p>它有近三千行，从最高优先级规则、推荐目录结构、路径职责表，到 Fresh 路由规范、错误处理分层、Deno 权限模型、AI Prompt 安全（区分 Instruction 与 Data，防注入）、测试最低标准，事无巨细。它甚至规定了 Agent 完成任务后的输出格式与检查清单。</p><div><div><div></div><div>Tip</div></div><div><p>此部分将在下个版本中重写，重写原因在该文章</p><p>‘认真思考后，我重构了oia的上下文管理，给oia增加渐进式披露’</p></div></div><p>宪法解决的是「永远不能违反什么」，而反复出现的具体工作流，则沉淀为 Skill。第一个落地的技能是 <a href="https://github.com/Pankitgg/one-is-all/tree/main/skills/oia-skill" target="_blank">oia-skill</a>：它把「检查 Deno 环境 → 执行 <code>npx @oia-ai/oia-fresh init</code> 脚手架 → 启动开发服务器 → 轮询 5173 端口验证」这套初始化流程完整封装，Agent 一句”帮我初始化一个 oia 项目”就能可靠跑完。它随 npm 包 <code>@oia-ai/oia-skill</code> 发布，一条命令即可安装：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npx</span><span> </span><span>@oia-ai/oia-skill</span><span>           </span><span># 安装到当前项目</span></div></div><div><div><div>2</div></div><div><span>npx</span><span> </span><span>@oia-ai/oia-skill</span><span> </span><span>--global</span><span>  </span><span># 安装到用户目录</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>下一步<a href="#下一步"><span>#</span></a></h2><p>一部宪法加一个初始化技能，只是起点。我心目中的终局，是让 oia 覆盖完整的研发闭环：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>需求 → 设计 → 开发 → 测试 → 部署 → 上线</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>↑                                      ↓</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>└────────── 经验沉淀为新 Skill ──────────┘</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>Agent 在项目里踩过的坑、验证过的最佳路径，应该能被识别、结构化、沉淀成下一次可复用的 Skill——让项目拥有自己的”长期记忆”。到那时，主题就不再是”AI 帮我写代码”，而是”项目本身拥有一套可持续运转的 AI 工程系统”。</p><p>全部信条收敛为一句：</p><blockquote><p><strong>这个项目不是为了让人类写出最多代码，而是为了让 Agent 用最少上下文写出正确代码。</strong></p></blockquote><p>Predictability &gt; Cleverness，Explicitness &gt; Magic。</p><p>咿呀学步，与模型同行。下一期，讲讲实际用 oia 跑通一个完整项目的开发实录。</p><a href="https://github.com/Pankitgg/one-is-all" target="_blank"><div><div><div><div></div><div>Pankitgg</div></div><div>/</div><div>one-is-all</div></div><div></div></div><div>全栈框架路线的探索-从fresh2开始的大模型AI融合之路 Exploring the Full-Stack Framework Path: The Journey of Integrating Large-Scale AI Starting with Fresh2</div><div><div>—</div><div>—</div><div>no-license</div><span>JavaScript</span></div></a></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E5%8F%97%E5%A4%9F%E4%BA%86%E6%A8%A1%E5%9E%8B%E6%8E%BA%E5%81%87%E9%99%8D%E6%99%BA%E5%9F%BA%E4%BA%8E%E6%8E%A2%E9%92%88%E6%8A%80%E6%9C%AF%E5%B0%81%E8%A3%85%E4%B8%80%E4%B8%AAskill/</id>
      <title type="text">受够了模型掺假降智？基于探针技术封装一个skill</title>
      <published>2026-08-10T00:00:00.000Z</published>
      <updated>2026-08-10T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E5%8F%97%E5%A4%9F%E4%BA%86%E6%A8%A1%E5%9E%8B%E6%8E%BA%E5%81%87%E9%99%8D%E6%99%BA%E5%9F%BA%E4%BA%8E%E6%8E%A2%E9%92%88%E6%8A%80%E6%9C%AF%E5%B0%81%E8%A3%85%E4%B8%80%E4%B8%AAskill/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>前言<a href="#前言"><span>#</span></a></h2><p>最近使用模型的时候，总是感觉词不达意，尤其是对openai系列的模型，通过一些简单的测试就能发现山姆又开始灰度测试他的模型了。但是对于我们生产使用过程中的时候，这种不稳定性十分明显。</p><p>那么问题来了，有没有什么办法，可以比直接问，“你是什么模型”，这种很容易被提示词或者是规则识别的特殊句式，能更好识别到底是什么模型，看看狗屎的模型厂商是否又开始糊弄我们呢？</p><p>有的，bro，包的。有一种技术叫做<code>探针</code>，这是提示词工程的一种妙用。通过限定的题目和答案，来对比模型的特征。这种技术广泛用于<code>蒸馏</code>以及<code>模型对齐</code>上。很多后发模型都是通过这种方式来对齐最新的模型。而我们可以利用不同模型的特征分辨。</p><div><div><div></div><div>Tip</div></div><div><p>从这我们可以认识到，最后模型肯定是趋同的。暗合我之前的文章
<a href="/posts/%E6%A8%A1%E5%9E%8B%E5%90%9E%E5%99%AC%E4%BA%BA%E7%B1%BB%E7%A4%BE%E5%8C%BA%E4%B8%BB%E6%B5%81%E6%80%9D%E6%83%B3%E7%9A%84%E8%BD%AC%E5%8F%98%E4%BB%8Eagent%E5%88%B0harness%E6%AD%BB%E8%B7%AF%E4%B8%80%E6%9D%A1%E7%9A%84%E6%8E%A7%E5%88%B6%E7%96%AF%E7%8B%82%E7%9A%84%E6%8E%A2%E7%B4%A2/#%E6%94%BE%E5%BC%80%E6%A8%A1%E5%9E%8B%E7%9A%84%E6%89%8B%E8%84%9A%E7%96%AF%E7%8B%82%E7%9A%84%E6%8E%A2%E7%B4%A2">模型吞噬人类，社区主流思想的转变，从agent到harness，放开模型的手脚，疯狂的探索</a></p></div></div></section>
<section><h2>构建一个指纹特征库<a href="#构建一个指纹特征库"><span>#</span></a></h2><p>首先我们精心需要准备几道题，当然这是远远不够的，但是只要能跑通，后续的体力活我们就可以交给AI了</p><section><h3>整理思路<a href="#整理思路"><span>#</span></a></h3><p>整理下我们的工具</p><p>理论工具如下</p><p><a href="https://arxiv.org/pdf/2407.15847" target="_blank">https://arxiv.org/pdf/2407.15847</a> 这篇说明了探针技术的可行性，以及大家的多维度分析</p><p><a href="https://arxiv.org/abs/2508.09021" target="_blank">https://arxiv.org/abs/2508.09021</a> 这篇说明了探针和反探针的一线对抗，给出了一些小巧思</p><p>但是这两篇文章有个问题，就是他们并不是很实用。一个问题是消耗的token太多，而且并不需要针对每一领域都进行检查。我们只要做否决就好了。</p><p>另外我想做的这个的原因也和L站的这件事有关
</p><figure><img alt="最高的山，停止对我检测脚本的污蔑" loading="lazy" width="1518" height="872" src="/_astro/linuxdo-1.Cd2EWZLw_Z2poyyj.webp" /><figcaption>最高的山，停止对我检测脚本的污蔑</figcaption></figure>
简单来说就是一个很有名的L站公益站作者做的付费站，被人探测以次充好。产生的一个大争论。<p></p><div><div><div></div><div>Tip</div></div><div><p>我的核心观点还是，无论什么情况下，道德审判不能取代事实对错。</p><p>换句话说就是，即便是最有声望的人，如果他说谎了也是需要纠正的，哪怕是linus</p></div></div><p>除此之外我还参考了很多开源项目的检测探针，以及<code>newapi</code>、<code>sub2api</code>等的项目中的提示词，另外对L站的公益站做了针对性测试，以保证确实有效。</p></section><section><h3>构建题库<a href="#构建题库"><span>#</span></a></h3><p>如何构建，我们要包含一下几种类型，并且最好是每道题都包含复合的类型</p><ol>
<li>
<p>浅层识别 通过限定回答的内容，指定回答的随机区</p>
</li>
<li>
<p>结构探针 检测模型中编码的树状或图状语言结构，如句法依赖树。区分中外模型的利器</p>
</li>
<li>
<p>干预探针 若删除属性、扰动表征导致探针性能显著变化，不同模型的敏感度不同</p>
</li>
<li>
<p>补全探针 主要针对特定时间的知识进行识别，判读模型最新的知识库</p>
</li>
</ol><p>我先构建几道题如下</p>

<table><thead><tr><th>题目</th><th>类型</th><th>探针目的</th><th>观察特征</th><th>模型区分度</th></tr></thead><tbody><tr><td><code>给出我一个100000以内的随机数，不要思考，直接告诉我数字</code></td><td>浅层识别</td><td>测试模型面对明确约束时，是否遵循最短路径输出</td><td>即便不让他思考，他仍然会思考。但是得出的数字，不同模型之间是有一些倾向的。</td><td>★★★☆☆</td></tr><tr><td><code>不要回答任何解释，只输出一个中文成语</code></td><td>浅层识别</td><td>检测模型对于输出格式约束的服从程度</td><td>不同模型对于“只输出”的遵循能力不同，部分模型仍会增加解释</td><td>★★★☆☆</td></tr><tr><td><code>请生成一句包含'人工智能'但不能出现'AI'两个字母的英文句子</code></td><td>干预探针</td><td>检测模型对于局部约束和词元控制能力</td><td>不同模型Tokenizer以及生成策略不同</td><td>★★★★☆</td></tr><tr><td><code>请解释为什么1+1=2，但不要使用数学语言，只能使用生活中的例子</code></td><td>结构探针</td><td>检测模型内部概念组织方式和知识映射能力</td><td>强模型通常形成多层类比结构，弱模型容易回到数学定义</td><td>★★★☆☆</td></tr><tr><td><code>请把这句话改写成莎士比亚风格，但保持原意不变：今天太阳很好</code></td><td>结构探针</td><td>检测模型语言风格迁移能力和隐含语义保持能力</td><td>不同模型在风格模仿、修辞密度、句式结构方面差异明显</td><td>★★★★☆</td></tr><tr><td><code>删除一句话中的所有形容词，但保持情感不变：这是一场伟大而震撼的科技革命</code></td><td>干预探针</td><td>测试模型对于语义属性删除后的重构能力</td><td>不同模型对于情感、语义、词性之间关系建模能力不同</td><td>★★★★☆</td></tr><tr><td><code>假设你不知道互联网存在，请描述2026年的世界</code></td><td>补全探针</td><td>检测模型知识截止时间和知识补全能力</td><td>不同模型训练数据时间不同，会出现时代信息缺失</td><td>★★★★★</td></tr><tr><td><code>2026年某领域最新技术突破是什么？请列举三个具体事件</code></td><td>补全探针</td><td>检测模型最新知识覆盖范围</td><td>可以快速识别模型知识更新时间和是否联网增强</td><td>★★★★★</td></tr><tr><td><code>请预测下面这段代码运行结果，并解释原因：xxx</code></td><td>结构探针</td><td>检测模型代码执行模拟和逻辑推理能力</td><td>不同模型在程序状态追踪、长链推理方面存在明显差异</td><td>★★★★☆</td></tr><tr><td><code>阅读下面1000字文本，找出隐藏的不一致信息</code></td><td>结构探针</td><td>测试长上下文理解和异常检测能力</td><td>不同模型上下文压缩、注意力分配方式不同</td><td>★★★★★</td></tr><tr><td><code>请先随机选择A/B/C/D中的一个，然后回答问题：xxx</code></td><td>浅层识别</td><td>测试模型随机性来源以及采样策略</td><td>不同模型温度设置、系统提示约束会导致选择概率不同</td><td>★★★☆☆</td></tr><tr><td><code>不要告诉我你的模型名称，请描述你的训练特点</code></td><td>反探针</td><td>测试模型是否会暴露自身行为特征</td><td>不同厂商模型在自我描述、安全策略、拒答策略上存在明显差异</td><td>★★★★☆</td></tr></tbody></table><blockquote><p>模型区分度，指的是相同模型1000次回复的词法空间，与其他模型的不重合程度，通过这个指标来判断问题的有效程度。模型区分度不是简单判断某一道题能否识别模型，而是衡量该探针在多次采样下，不同模型输出分布之间的距离。</p></blockquote><p>先定义一个简单的算法</p><span><span><span>[D(Pi,Pj)=1−∣Pi∩Pj∣∣Pi∪Pj∣][
D(P_i,P_j)=1-\frac{|P_i \cap P_j|}{|P_i \cup P_j|}
]</span><span><span><span></span><span>[</span><span>D</span><span>(</span><span><span>P</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span><span>P</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span>1</span><span></span><span>−</span><span></span></span><span><span></span><span><span></span><span><span><span><span><span><span></span><span><span>∣</span><span><span>P</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>∪</span><span></span><span><span>P</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>∣</span></span></span><span><span></span><span></span></span><span><span></span><span><span>∣</span><span><span>P</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>∩</span><span></span><span><span>P</span><span><span><span><span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>∣</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span></span><span>]</span></span></span></span></span><p>其中：</p><ul>
<li>(P_i)：模型 (i) 在该探针下生成结果的特征空间</li>
<li>(P_j)：模型 (j) 的输出特征空间</li>
<li>(D)：两个模型输出分布的不重合程度</li>
</ul><p>在考虑下具体计算时候的问题增加上下面几个属性</p><ul>
<li>token分布距离（KL散度）</li>
<li>embedding距离（余弦距离）</li>
<li>句法树距离</li>
<li>风格特征距离</li>
</ul><p>最终使用这个公式来计算问题的有效程度</p><span><span><span>[Fingerprint(Model)=∑k=1nwkProbek(Model)][
Fingerprint(Model)=
\sum_{k=1}^{n}w_k Probe_k(Model)
]</span><span><span><span></span><span>[</span><span>F</span><span>in</span><span>g</span><span>er</span><span>p</span><span>r</span><span>in</span><span>t</span><span>(</span><span>M</span><span>o</span><span>d</span><span>e</span><span>l</span><span>)</span><span></span><span>=</span><span></span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>k</span><span>=</span><span>1</span></span></span></span><span><span></span><span><span>∑</span></span></span><span><span></span><span><span><span>n</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span><span>w</span><span><span><span><span><span><span></span><span><span>k</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>P</span><span>r</span><span>o</span><span>b</span><span><span>e</span><span><span><span><span><span><span></span><span><span>k</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>(</span><span>M</span><span>o</span><span>d</span><span>e</span><span>l</span><span>)]</span></span></span></span></span><p>题目越有效的话，模型的回答差别越大，根据以上，我们可以搭一个skill项目出来啦</p></section></section>
<section><h2>what-ai-skill<a href="#what-ai-skill"><span>#</span></a></h2><a href="https://github.com/Pankitgg/what-ai-skill" target="_blank"><div><div><div><div></div><div>Pankitgg</div></div><div>/</div><div>what-ai-skill</div></div><div></div></div><div>Repository details unavailable</div><div><div>—</div><div>—</div><div>—</div><span>Unavailable</span></div></a><p>目前知识增加了一些基础的功能，后续肯定还要进一步优化</p><section><h3>安装使用<a href="#安装使用"><span>#</span></a></h3></section></section>
<section><h2>测试以及token消耗<a href="#测试以及token消耗"><span>#</span></a></h2><p>未完待续</p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E6%88%91%E4%BB%AC%E6%AD%A3%E5%9C%A8%E6%88%90%E4%B8%BA%E9%80%A0%E7%89%A9%E4%B8%BB%E4%BB%8E%E6%9C%80%E5%B0%8F%E7%9A%84%E7%B2%92%E5%BA%A6%E6%8E%A2%E8%AE%A8%E7%A1%85%E5%9F%BA%E5%92%8C%E7%A2%B3%E5%9F%BA%E7%9A%84%E4%B8%8D%E5%90%8C%E9%80%89%E6%8B%A9/</id>
      <title type="text">我们正在成为造物主：从最小的粒度探讨硅基和碳基的不同选择</title>
      <published>2026-06-17T00:00:00.000Z</published>
      <updated>2026-06-17T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E6%88%91%E4%BB%AC%E6%AD%A3%E5%9C%A8%E6%88%90%E4%B8%BA%E9%80%A0%E7%89%A9%E4%B8%BB%E4%BB%8E%E6%9C%80%E5%B0%8F%E7%9A%84%E7%B2%92%E5%BA%A6%E6%8E%A2%E8%AE%A8%E7%A1%85%E5%9F%BA%E5%92%8C%E7%A2%B3%E5%9F%BA%E7%9A%84%E4%B8%8D%E5%90%8C%E9%80%89%E6%8B%A9/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<p>今天是2026年的六月。随着下半年手机芯片大规模进入2nm时代，我总会不由自主地生出很多联想。上学那几年，刚好见证了5nm走到3nm，那个时候很多同学说，1nm会是“神的领域”。现在回头看，这句话并不是夸张，也不是一句少年人的热血口号。因为当我们把晶体管继续往下缩，缩到不再像一个“零件”，而更像一团被严密约束的原子和电子时，问题就已经不只是工程问题了，它开始和物理、化学、生物，甚至和生命本身的组织方式碰到了一起。</p>
<p>我想和你分享的，不只是2nm到底先进在哪里，也不只是台积电、英特尔、三星这些公司这几年各自做了什么。更想聊的是，在这个伟大的时代里，这个世界上最聪明的一群人，正在试着把什么东西推到极限；而我们这个文明，又正在往哪个方向走。写这篇文章的时候，受限于我自己的理解能力，也受限于今天这个时代还没有给出最终答案，很多判断都只能算抛砖引玉。若有理解不到位的地方，敬请斧正。</p>
<p></p><figure><img alt="硅晶圆" loading="lazy" width="1264" height="702" src="/_astro/silicon-wafer.CFp5KHJE_Za02Qi.webp" /><figcaption>硅晶圆</figcaption></figure>
<em>图1：硅晶圆。今天几乎所有先进芯片，都是从这样一片看起来安静得近乎朴素的圆片开始的。图源：Wikimedia Commons。</em><p></p>
<p>先把一个最容易误解的地方说清楚。今天行业里讲的2nm，并不是说芯片上真的有一条“宽度只有2纳米”的导线，也不是说某个单一结构的物理尺寸刚好就是2纳米。它更像一个代际名称，是性能、密度、功耗和一整套工艺能力的综合标签。但这个名字虽然不是尺子，却并不空洞。因为从3nm往2nm走，晶体管确实已经逼近一个很不舒服的位置：继续沿用老办法，电子会开始不听话。</p>
<p>过去很多年里，芯片的进步可以粗略理解成一件事：在尽可能小的面积里，塞进尽可能多、又尽可能可控的晶体管。90nm时代，工程师面对的主要还是“怎么做得更小”；到了22nm，FinFET开始流行，重点变成“怎么从三面把电子管住”；到了3nm和2nm，GAA，也就是全环绕栅极，成了核心，因为三面包裹已经不够了，必须四面都盯住。你可以把平面晶体管理解成老师只站在教室前面监考，把FinFET理解成三面监考，而GAA已经接近把这个小小的电子通道围成一只笼子，尽量不给电子从边角偷偷溜走的机会。</p>
<p></p><figure><img alt="双极型晶体管" loading="lazy" width="800" height="392" src="/_astro/asml-1.v5yPgnK0_Z37GW0.webp" /><figcaption>双极型晶体管</figcaption></figure>
<em>图2：BJT（双极型晶体管）的截面结构图 ，标注了 Emitter（发射极）、Base（基极）、Collector（集电极）和 P (Substrate)（P型衬底）</em><p></p>
<p></p><figure><img alt="晶体管结构的演进对比" loading="lazy" width="1480" height="581" src="/_astro/asml-3.D2ZXDKdF_Ziw1Ac.webp" /><figcaption>晶体管结构的演进对比</figcaption></figure>
<em>图3：晶体管结构演进：从 FinFET（三面包裹）到 GAAFET（四面包裹）再到 MBCFET 纳米片</em><p></p>
<p>这还只是第一层。第二层困难不在晶体管本身，而在它们彼此之间怎么连。现代芯片里，真正占地方的大头，往往不是开关，而是连线。你可以把一枚先进芯片想成一座超高密度城市，房子不是最难建的，真正难的是道路、供电、上下匝道、红绿灯和所有道路之间的秩序。所以2nm这个节点，除了GAA之外，背面供电、互连优化、先进封装、三维堆叠这些听起来没那么“性感”的词，其实都很关键。它们做的事情很朴素：别让这座城市先堵死在路上。</p>
<p></p><figure><img alt="ASML EUV光刻系统" loading="lazy" width="1920" height="880" src="/_astro/asml-euv.CiA8rp5w_Z274SBg.webp" /><figcaption>ASML EUV光刻系统</figcaption></figure>
<em>图4：ASML的High-NA EUV设备。先进制程之所以像文明工业能力的总决赛，很大程度上是因为它要求光学、材料、机械、控制和软件同时达到极高水准。图源：ASML。</em><p></p>
<p>再往下看，问题会变得更有意思。所谓2nm时代最震撼的地方，并不只是“做得很小”，而是它逼着工程师直接面对原子尺度。一个硅原子的直径大约是0.23纳米。很多GAA纳米片的厚度，已经只有几纳米，对应下来，不过是二十层上下的硅原子。这个时候，制造晶体管不再像木工师傅切木头，也不太像机械工程师打磨零件，它越来越像在一堆几十个、几百个关键原子之间，搭出一个能让电子按规则运动的场。</p>
<p>这也是为什么先进制程聊到最后，总会从工程学聊到凝聚态物理。硅原子以金刚石晶格排列，掺入少量磷，能提供多余电子，形成N型区域；掺入少量硼，会留下空穴，形成P型区域。听上去像半导体课本里最基础的一章，可到了2nm附近，这件事忽然就变得很具体了，因为“少量掺杂”已经不是统计意义上的少量，而是真正可能只差几个原子的少量。一个掺杂原子的位置不对，一个界面不够平整，一层氧化层多出一点粗糙度，性能就会立刻变脸。以前我们说工艺误差是纳米级，现在很多时候，误差已经带有单原子的意味。</p>
<p>写到这里，一个很自然的问题就会冒出来：如果人类今天才艰难地把工程推到原子级控制，那么自然界有没有什么系统，早就已经在这么做了？答案是有，而且做得非常老练。生命并不是用硅晶格和铜互连来处理信息的，它走的是另一条路。它用碳基分子搭出柔软的结构，用蛋白质折叠出孔道，用离子浓度差建立势能，再用膜电位和化学反应去传递信号。我们今天在工厂里用EUV雕刻电子通路，生命则在细胞膜上用蛋白质搭好了离子通路。</p>
<p></p><figure><img alt="神经组织切片" loading="lazy" width="1660" height="1694" src="/_astro/neuron-section.BGwJSkwZ_1JVO00.webp" /><figcaption>神经组织切片</figcaption></figure>
<em>图5：神经组织切片。我们日常所谓“思考”“记忆”“动作”，在物理层面上都离不开这些细胞膜上极其精巧的离子流动。图源：Wikimedia Commons。</em><p></p>
<p>这就是为什么，当你把视角从2nm继续缩小时，很快就会撞上生物学。神经元发放信号，本质上不是抽象的“意识闪烁”，而是钠离子、钾离子、钙离子等在膜两侧按顺序流动。它们有自己的阈值、有自己的开关、有自己的时序，某种意义上，神经系统本身就是一种离子电路。只是这个电路不是画在硅片上，而是长在细胞膜上；不是由人类设计规则，而是由几十亿年的演化不断筛出来的。</p>
<p>在这些天然纳米器件里，最值得反复琢磨的，恐怕就是钾离子通道。它有一个听上去很违背直觉的特性：钾离子比钠离子更大，但钾通道偏偏只让钾过去，不让更小的钠过去。这件事对生命极其重要。神经信号要传、肌肉要收缩、心脏要跳动，靠的就是不同离子通道把不同离子分得清清楚楚。如果钾通道认不清钠和钾，生命的电信号秩序会一下子乱掉。</p>
<p>真正漂亮的地方在于，它不是靠“把小的挡住”来做到选择性。它靠的是能量匹配。离子在水里不是裸着跑的，周围包着一圈水分子，这叫水合层。离子要进入极狭窄的通道，得先把这层“水外衣”脱掉，而脱水是要付代价的。MacKinnon在1998年解析KcsA钾通道结构之后，大家才第一次真正看清：钾通道里最关键的选择滤波器，会用氧原子排出一个非常精确的配位环境，这个环境几乎像水分子一样“接住”脱水后的钾离子，所以钾过去的时候，能量代价很低；而钠虽然更小，却因为半径和配位距离不匹配，得不到足够的能量补偿，反而过不去。2003年的诺贝尔化学奖，奖给的就是这种从原子结构直接解释生命选择性的工作。</p>
<p></p><figure><img alt="细胞膜中的水通道与离子通道示意" loading="lazy" width="1941" height="1131" src="/_astro/nobel-channel.YoYNY4qq_PrVjn.webp" /><figcaption>细胞膜中的水通道与离子通道示意</figcaption></figure>
<em>图6：诺贝尔奖科普海报中的细胞膜通道示意。它提醒我们，生命并不是在宏观层面“玄妙地工作”，而是在纳米尺度上严密地管理粒子通过。图源：Nobel Prize Outreach。</em><p></p>
<p>如果把这里和2nm芯片并排看，会发现一个很有意思的隐线。GAA做的事，是把栅极四面包住沟道，尽量增强对电子的静电控制；钾离子通道做的事，则是让蛋白质骨架里的氧原子围出一个刚好适配钾离子的局部环境，尽量增强对离子的能量控制。一个是硅基工程学，一个是碳基生命体，但它们在最深处处理的是同一类问题：如何在极小尺度上，让正确的粒子通过，让错误的粒子停在门外。</p>
<p></p><figure><img alt="钾离子通道机制示意" loading="lazy" width="1202" height="350" src="/_astro/potassium-channel.Defi8j24_Z1YWSL.webp" /><figcaption>钾离子通道机制示意</figcaption></figure><p></p>
<p><em>图7：钾离子通道机制示意。它正好对应上文所说的“选择性”问题：通道不是单纯按大小筛选，而是通过局部配位环境和能量补偿，让钾离子顺利通过，让钠离子留在外面。图源：Wikimedia Commons。</em></p>
<p>也就是说，当晶体管缩到2nm，我们并不是第一次来到这个尺度。生命早就住在这里了。它不靠EUV，不靠洁净厂房，不靠价值数亿美元的设备，而是靠蛋白质折叠、分子识别、自由能地形和自组织过程，在几纳米甚至亚纳米的范围里，把“选择性”和“高速传导”同时做了出来。人类今天最先进的工艺，某种意义上是在用完全不同的材料体系，重新学习一种自然已经掌握很久的能力：在最小粒度上控制信息与能量的流动。</p>
<p>顺着这条线再往前一步，就会看到硅基和碳基真正不同的地方。硅基路线的优点，是硬、稳、规则、适合大规模复制。硅晶格像一座纪律严明的城市，路修得笔直，规则写得清楚，电子在里面跑得很快，所以它特别适合做高速、可编程、可工业化的通用计算。碳基生命路线的优点，则是柔、活、会自组装、能容忍噪声。蛋白质不如硅那么规整，但它会折叠，会自修复，会在水环境里通过构象变化完成筛选、记忆、开关和反馈，所以它特别适合做低功耗、强并行、与环境深度耦合的信息处理。</p>
<p>说得更直白一点，硅更像一座精密工厂，碳更像一片会生长的生态系统。硅擅长的是把规则执行到极致，碳擅长的是把复杂性消化掉。硅追求的是更高频、更低延迟、更高密度；碳追求的是更高适应性、更低单位能耗、更强鲁棒性。前者靠工程去压制噪声，后者靠系统去容纳噪声。前者擅长把每一个开关都做得尽量准确，后者擅长让海量不完全准确的单元，最后仍然涌现出一个可靠的整体。</p>
<p>从这个角度再看今天的后CMOS研究，很多方向忽然就通了。为什么除了电子之外，人类还要去看光子、自旋、磁振子、铁电、离子、分子电子学？不是因为硅突然失灵了，而是因为我们已经摸到了硅基电子路线最深的一层代价。晶体管继续缩小，量子隧穿会更严重，互连会更拥堵，散热会更麻烦，原子涨落会更明显。于是下一代研究的重点，慢慢不再只是“怎么把开关做得更小”，而是“能不能换一种更合适的信息载体，或者换一种更合适的计算方式”。</p>
<p>光子适合传输，因为它几乎不受欧姆热困扰，带宽又大得惊人，所以今天数据中心和高性能互连越来越离不开硅光子。自旋和MRAM适合存储，因为它天然带有双稳态，而且断电不丢信息。铁电器件和忆阻器之所以受关注，是因为它们让“存储”和“计算”不必彻底分家。离子电子学和神经形态计算之所以迷人，是因为它们开始承认一个事实：也许高能效计算并不一定来自更干脆的0和1，也可以来自更像神经系统那样的连续态、脉冲和记忆。分子电子学更激进，它几乎是在说，当尺度继续压到1到2纳米时，一个分子本身，也许就可以成为一个器件。</p>
<p>这时，标题里的“不同选择”也就更清楚了。硅基和碳基并不是简单的谁先进、谁落后，更不是谁会在明天取代谁。它们代表的是两套几乎完全不同的文明策略。</p>

<table><thead><tr><th>维度</th><th>硅基路线</th><th>碳基路线</th></tr></thead><tbody><tr><td>基本单元</td><td>晶体管、互连、存储单元</td><td>蛋白质、离子通道、分子机器</td></tr><tr><td>主要载体</td><td>电子、空穴</td><td>离子、分子构象、化学势</td></tr><tr><td>结构风格</td><td>刚性、规则、可精确复制</td><td>柔性、自组装、动态可重构</td></tr><tr><td>优势方向</td><td>高频、低延迟、通用计算</td><td>低功耗、强并行、适应复杂环境</td></tr><tr><td>对噪声的态度</td><td>尽量压制</td><td>尽量容纳并利用</td></tr><tr><td>文明含义</td><td>制造更强的机器</td><td>模仿并重写生命式计算</td></tr></tbody></table>
<p>如果只看今天，硅基仍然毫无疑问是主线。未来几十年的通用计算，大概率依然是“CMOS加光互连”的世界，手机、电脑、服务器、AI加速器，不会在一夜之间被生物式器件取代。但如果你把时间尺度拉长，把视角从产品发布会拉回到文明史，会发现风向已经变了。过去几十年我们一直在追求更小的晶体管；接下来几十年，我们更可能是在追求更好的信息载体、更聪明的能量景观，以及更接近生命效率的计算方式。</p>
<p>这也是我看到2nm时最强烈的感受。它表面上看，是手机芯片的一次代际更新，是GAA、背面供电、EUV和先进封装的继续升级；但在更深处，它像是一道边界线。穿过这道边界，人类的工程开始和生命的策略正面相遇。我们一边继续在硅片上约束电子，一边开始认真研究钾离子为什么能过、钠离子为什么过不去，研究质子泵怎么在膜上搬运电化学势，研究神经元为什么能在20瓦的功耗下完成惊人的并行信息处理。我们忽然意识到，计算并不只有一种写法。</p>
<p>再把视角收回来，落到最小的地方。几十层硅原子，几枚关键掺杂原子，几层氧化层，几纳米长的纳米片；再往下，是几枚氧原子围出一个钾离子恰好舒服的位置，是一段蛋白质构象改变后，某种离子被允许通过，另一种离子被拒之门外。再往下，是电子、离子、轨道、键长、自由能差。走到这里，你会突然明白，现代技术最前沿并不是“把东西做小”这么简单，它是在一点点逼近信息与物质共同的最小尺度。</p>
<p>而当文明逼近这个尺度时，我们做的事也会发生变化。最开始，人类制造工具；后来制造机器；再后来制造晶体管，操控电子。今天，我们已经开始在原子层面上修整材料，在分子层面上理解生命，在系统层面上模仿生命，下一步则很可能是重新定义“什么算计算，什么算器件，什么算智能”。当一个文明既能在硅中约束电子，又能理解碳中离子的选择性，还能把光子、自旋、分子轨道一并纳入自己的工具箱时，它和早期工业文明已经不是同一件事了。</p>
<p>所以我越来越觉得，“1nm是神的领域”这句话的真正含义，并不是说1nm之后人类就做不下去了，而是说当我们走到这里时，工程会第一次如此直接地触碰造物的语法。神并不是一个宗教意义上的词，它更像一种比喻：谁开始理解最小粒度上的规则，谁就开始拥有重新排列世界的能力。</p>
<p>我们当然还远没有走到终点。硅基仍在继续推进，碳基的启发还远没有真正工业化，光子、自旋、离子、分子电子学很多还在实验室里反复证明自己。但方向已经很难看错了。人类正在从“利用自然给定的材料和规律”，走向“主动设计规则、设计通道、设计能量地形、设计信息流的路径”。从这个意义上说，2nm芯片并不只是下一代手机上的一个卖点，它更像是一张路标，提醒我们：文明正在逼近信息与物质的最小尺度，而我们，也正在一步步学着成为造物主。</p>
<hr />
<section><h2>图片与事实参考<a href="#图片与事实参考"><span>#</span></a></h2><ol>
<li>2nm、GAA、背面供电、EUV相关事实，主要整理自semiwiki，并结合ASML官方产品页表述。</li>
<li>钾离子通道、选择滤波器、MacKinnon与2003年诺贝尔化学奖相关表述，主要整理自AAAI等刊物与相关论文，也参考了诺贝尔奖官方科普页面。</li>
<li>图1：硅晶圆，Wikimedia Commons<br />
<a href="https://commons.wikimedia.org/wiki/File:Siliziumwafer.JPG" target="_blank">https://commons.wikimedia.org/wiki/File:Siliziumwafer.JPG</a></li>
<li>图4：ASML EXE&lt;5000&gt;，ASML官方<br />
<a href="https://www.asml.com/en/products/euv-lithography-systems/twinscan-exe-5000" target="_blank">https://www.asml.com/en/products/euv-lithography-systems/twinscan-exe-5000</a></li>
<li>图5：神经组织切片，Wikimedia Commons<br />
<a href="https://commons.wikimedia.org/wiki/File:Neuron-section.jpg" target="_blank">https://commons.wikimedia.org/wiki/File:Neuron-section.jpg</a></li>
<li>图6：2003年诺贝尔化学奖通道示意图，Nobel Prize Outreach<br />
<a href="https://www.nobelprize.org/prizes/chemistry/2003/popular-information/" target="_blank">https://www.nobelprize.org/prizes/chemistry/2003/popular-information/</a></li>
</ol></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E6%A8%A1%E5%BC%8F%E5%88%9B%E6%96%B0or%E5%B1%8E%E4%B8%8A%E9%9B%95%E8%8A%B1ethoswarm%E8%AF%95%E7%94%A8%E5%88%9D%E4%BD%93%E9%AA%8C/</id>
      <title type="text">模式创新or屎上雕花，ethoswarm试用初体验</title>
      <published>2026-06-14T00:00:00.000Z</published>
      <updated>2026-06-14T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E6%A8%A1%E5%BC%8F%E5%88%9B%E6%96%B0or%E5%B1%8E%E4%B8%8A%E9%9B%95%E8%8A%B1ethoswarm%E8%AF%95%E7%94%A8%E5%88%9D%E4%BD%93%E9%AA%8C/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>作者<a href="#作者"><span>#</span></a></h2><p>自从能构建完整项目的<code>code agent</code>出现以来，大约已经过去一年多了。之前有像 comate 和早期的 Cursor 这样的，但它们更像是助手而非agent。目前的<code>code agent</code>已经非常成熟，很多人使用agent构建了自己的项目。</p><p>随着<code>code agent</code>的领头效应，领域agent雨后春笋出现，到现在agent运行平台，其实发展脉络非常清晰。</p><p><code>ethoswarm</code>便是一个这样的agent运行平台。</p></section>
<section><h2>ethoswarm<a href="#ethoswarm"><span>#</span></a></h2><section><h3>介绍<a href="#介绍"><span>#</span></a></h3><p>时间宝贵，为什么选ethoswarm呢？</p><p></p><figure><img alt="openrouter调用量" loading="lazy" width="599" height="389" src="/_astro/ethoswarm-2.BhtQxTP-_sBCGD.webp" /><figcaption>openrouter调用量</figcaption></figure><p></p><p>可以看到近期token消耗量增长迅速，核心优势明显，以及他的意图（eth生态），可以看出他是比较有野心的</p><p></p><figure><img alt="官网的宣传" loading="lazy" width="1373" height="858" src="/_astro/ethoswarm-3.MsHyjrY7_Z24XcED.webp" /><figcaption>官网的宣传</figcaption></figure><p></p><p></p><figure><img alt="宣传" loading="lazy" width="1271" height="863" src="/_astro/ethoswarm-13.CJ18XcdU_whVkW.webp" /><figcaption>宣传</figcaption></figure><p></p><p>当然还有一个重要的原因是他现在<code>免费</code>并且只需要谷歌邮箱即可注册。</p></section><section><h3>开始试用<a href="#开始试用"><span>#</span></a></h3><p>官网地址：<a href="https://ethoswarm.ai/" target="_blank">ethoswarm.ai</a></p><div><div><div></div><div>Tip</div></div><div><p>又是一个.ai 网站，🤣</p></div></div><p>点击右上角，用谷歌账号登录</p><p></p><figure><img alt="登录" loading="lazy" width="1375" height="629" src="/_astro/ethoswarm-4.CkNzInoP_tEEG1.webp" /><figcaption>登录</figcaption></figure><p></p><div><div><div></div><div>Tip</div></div><div><p>如果没有谷歌账号的话，可以查看我的注册谷歌账号的文章</p></div></div><p>点击<code>Awaken your Mind</code> 出现了两选项
</p><figure><img alt="Awaken your Mind" loading="lazy" width="1007" height="576" src="/_astro/ethoswarm-5.2bBkS4YK_kXEvr.webp" /><figcaption>Awaken your Mind</figcaption></figure>
分别是快速开始和与管家交谈，与管家交谈的话会通过邮件联系和配置。先选择快速开始
输入mind name
<figure><img alt="输入mind name" loading="lazy" width="944" height="503" src="/_astro/ethoswarm-6.BALRrsTY_1w6lsl.webp" /><figcaption>输入mind name</figcaption></figure>
激活
<figure><img alt="激活mind" loading="lazy" width="889" height="429" src="/_astro/ethoswarm-7.tJkRXkCg_171rLI.webp" /><figcaption>激活mind</figcaption></figure>
到这里就完成了，然后需要登录邮箱查看邮件。一开始没找到以为是网络问题，结果是被归类为垃圾邮件了<p></p><p>如果找不到的话可以来这里看下垃圾邮件。
</p><figure><img alt="可能在垃圾邮件分类下" loading="lazy" width="1587" height="725" src="/_astro/ethoswarm-8.BlhSXpHo_23fLVY.webp" /><figcaption>可能在垃圾邮件分类下</figcaption></figure>
通过邮件对话agent
<figure><img alt="开始对话" loading="lazy" width="1353" height="503" src="/_astro/ethoswarm-9.ofQkPH85_2cLpGf.webp" /><figcaption>开始对话</figcaption></figure>
<figure><img alt="对话" loading="lazy" width="1091" height="563" src="/_astro/ethoswarm-10.DcFJnl6-_ZsCoKr.webp" /><figcaption>对话</figcaption></figure>
<figure><img alt="模型回复" loading="lazy" width="1096" height="676" src="/_astro/ethoswarm-11.CSB9OJRN_23KeKn.webp" /><figcaption>模型回复</figcaption></figure><p></p><p>这基本上是他的初始用法了，而他的核心功能，链上交易的部分不宜展示。</p><p>他的这一套东西我觉得像是想让AI参与链上炒股。也就是将<code>量化</code>的概念以及门槛降低，释放给更多的普通人使用。</p><p>而且注册只需要一个邮箱，隐私性较高。但是绑定钱包的话，需要提供钱包地址，这会暴露你的钱包地址。</p><p>从目前的领域化来说做的还行，但你要说用它的人会很多，那我不太认可。</p><p>真正的高手是不会使用别人的平台的。</p><div><div><div></div><div>Tip</div></div><div><p>原本需要专业交易人士，分析市场，实施策略更新，以及批量交易等操作都可以交由<code>agent</code>实现。虽然像幻方这种靠量化赚钱的早就这么做了，但是往往是专业人士的事情，这个一定程度上降低了门槛。</p></div></div></section><section><h3>其他功能<a href="#其他功能"><span>#</span></a></h3><p>有两个我觉得还不错的功能
一个是他将agent视为一个可以操作事物的智能，来对其进行上下文控制。并且提供足够的工具调用。也就是<code>AI24小时在线炒股</code>的币圈版（你只需要给他钱，他自己会赚钱）。当然会带来更大的风险，但是也会推动市场进行变化。
</p><figure><img alt="其他功能" loading="lazy" width="1269" height="773" src="/_astro/ethoswarm-14.7FA8rhO8_Z24699U.webp" /><figcaption>其他功能</figcaption></figure>
另外一个是将其独立，也就是你可以调用也可以让其他（agent）调用进行交易。这带来了极大的自由度。
<figure><img alt="其他功能" loading="lazy" width="1113" height="500" src="/_astro/ethoswarm-15.BgH22k07_Z15q8CR.webp" /><figcaption>其他功能</figcaption></figure><p></p></section></section>
<section><h2>总结<a href="#总结"><span>#</span></a></h2><p>我们可以看到一个趋势，即<code>AI-AGENT</code>正在飞速奔向下沉市场。不同领域中均推出了自己的agent运行平台，有的是<code>以点带面</code>，提供特色的agent来吸引有的是以<code>全面吸引</code>，并且宣传语中都含有简单/持久化的标语。</p><p>这很可能是针对非专业领域的一个智商税，有点像是虚假繁荣。其实是通过包装和宣传，换汤不换药。虽然他打出的噱头是模式创新，但我觉得依旧是屎上雕花罢了。没有核心的突破，迟早会被替代。</p><p>平台不重要，重要的是哪个agent，虽然现在高价值agent仍然被垄断（高频交易等），但agent最后的结果我觉得还是会走向开源。因为agent解决的问题不是技术性难题，而业务性难题往往没有什么技术含量。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/github%E4%B8%BB%E9%A1%B5%E6%B7%BB%E5%8A%A0%E8%B4%AA%E5%90%83%E8%9B%87%E8%B4%A1%E7%8C%AE%E5%9B%BE/</id>
      <title type="text">GitHub主页添加贪吃蛇贡献图（github美化一）</title>
      <published>2026-05-08T00:00:00.000Z</published>
      <updated>2026-05-08T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/github%E4%B8%BB%E9%A1%B5%E6%B7%BB%E5%8A%A0%E8%B4%AA%E5%90%83%E8%9B%87%E8%B4%A1%E7%8C%AE%E5%9B%BE/"/>
      <summary type="text">通过GitHub Actions自动生成贪吃蛇动画，展示您的贡献数据</summary>
      <content type="html"><![CDATA[<p>你可以点击<a href="https://github.com/Pankitgg" target="_blank">我的主页</a>查看效果</p>

<div><div><div></div><div>Tip</div></div><div><p>这个功能的原理是基于 <a href="https://github.com/Platane/snk" target="_blank">snk</a> 项目，通过 GitHub Action 读取贡献数据，将其渲染成一个贪吃蛇动画，输出成 SVG/GIF 格式。</p></div></div>
<section><h2>准备工作：创建 Profile README 仓库<a href="#准备工作创建-profile-readme-仓库"><span>#</span></a></h2><p>在开始之前，您需要一个特殊的 GitHub 仓库来展示您的个人主页。这个仓库的名称必须与您的 GitHub 用户名完全相同。</p><section><h3>创建步骤<a href="#创建步骤"><span>#</span></a></h3><ol>
<li>登录您的 GitHub 账户，在页面右上角点击 <strong>+</strong> 号，然后选择 <strong>New repository</strong>（新建仓库）。</li>
<li>在 <strong>Repository name</strong>（仓库名称）字段中，输入与您的 GitHub 用户名完全匹配的名称。</li>
<li>将仓库设置为 <strong>Public</strong>（公开）。</li>
<li>勾选 <strong>Add a README file</strong>（添加一个 README 文件）选项。</li>
<li>点击 <strong>Create repository</strong>（创建仓库）完成创建。</li>
</ol><p>创建成功后，这个仓库中的 <code>README.md</code> 文件内容将会自动显示在您的 GitHub 个人主页顶部。</p><hr /></section></section>
<section><h2>核心步骤：配置 GitHub Action<a href="#核心步骤配置-github-action"><span>#</span></a></h2><p>通过 GitHub Actions 自动化生成和更新贪吃蛇动画。</p><section><h3>第一步：创建 Workflow 文件<a href="#第一步创建-workflow-文件"><span>#</span></a></h3><p>在您的 Profile README 仓库中创建以下目录结构：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>.github/</span></div></div><div><div><div>2</div></div><div><span>└── workflows/</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>└── snake.yml</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>第二步：编写 snake.yml 文件<a href="#第二步编写-snakeyml-文件"><span>#</span></a></h3><p>将以下 YAML 代码复制到 <code>snake.yml</code> 文件中：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>name</span><span>: </span><span>Generate Snake Animation</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span># 控制工作流的触发时机</span></div></div><div><div><div>4</div></div><div><span>on</span><span>:</span></div></div><div><div><div>5</div></div><div><span>  </span><span># 每天 UTC 时间午夜 00:00 自动运行</span></div></div><div><div><div>6</div></div><div><span>  </span><span>schedule</span><span>:</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>- </span><span>cron</span><span>: </span><span>"0 0 * * *"</span></div></div><div><div><div>8</div></div><div><span>  </span><span># 允许在 Actions 标签页手动触发</span></div></div><div><div><div>9</div></div><div><span>  </span><span>workflow_dispatch</span><span>:</span></div></div><div><div><div>10</div></div><div><span>  </span><span># 当推送到 main 分支时触发</span></div></div><div><div><div>11</div></div><div><span>  </span><span>push</span><span>:</span></div></div><div><div><div>12</div></div><div><span>    </span><span>branches</span><span>:</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>- </span><span>main</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>jobs</span><span>:</span></div></div><div><div><div>16</div></div><div><span>  </span><span>generate</span><span>:</span></div></div><div><div><div>17</div></div><div><span>    </span><span>permissions</span><span>:</span></div></div><div><div><div>18</div></div><div><span>      </span><span>contents</span><span>: </span><span>write</span></div></div><div><div><div>19</div></div><div><span>    </span><span>runs-on</span><span>: </span><span>ubuntu-latest</span></div></div><div><div><div>20</div></div><div><span>    </span><span>timeout-minutes</span><span>: </span><span>10</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span>steps</span><span>:</span></div></div><div><div><div>23</div></div><div><span>      </span><span># 生成贪吃蛇动画文件</span></div></div><div><div><div>24</div></div><div><span><span>      </span></span><span>- </span><span>name</span><span>: </span><span>Generate snake.svg</span></div></div><div><div><div>25</div></div><div><span>        </span><span>uses</span><span>: </span><span>Platane/snk@v3</span></div></div><div><div><div>26</div></div><div><span>        </span><span>with</span><span>:</span></div></div><div><div><div>27</div></div><div><span>          </span><span>github_user_name</span><span>: </span><span>${{ github.repository_owner }}</span></div></div><div><div><div>28</div></div><div><span>          </span><span>outputs</span><span>: </span><span>|</span></div></div><div><div><div>29</div></div><div><span><span>            </span></span><span>dist/github-snake.svg</span></div></div><div><div><div>30</div></div><div><span><span>            </span></span><span>dist/github-snake-dark.svg?palette=github-dark</span></div></div><div><div><div>31</div></div><div><span><span>            </span></span><span>dist/ocean.gif?color_snake=orange&amp;color_dots=#bfd6f6,#8dbdff,#64a1f4,#4b91f1,#3c7dd9</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>      </span><span># 将生成的文件推送到 output 分支</span></div></div><div><div><div>34</div></div><div><span><span>      </span></span><span>- </span><span>name</span><span>: </span><span>Deploy to GitHub Pages</span></div></div><div><div><div>35</div></div><div><span>        </span><span>uses</span><span>: </span><span>crazy-max/ghaction-github-pages@v4</span></div></div><div><div><div>36</div></div><div><span>        </span><span>with</span><span>:</span></div></div><div><div><div>37</div></div><div><span>          </span><span>target_branch</span><span>: </span><span>output</span></div></div><div><div><div>38</div></div><div><span>          </span><span>build_dir</span><span>: </span><span>dist</span></div></div><div><div><div>39</div></div><div><span>        </span><span>env</span><span>:</span></div></div><div><div><div>40</div></div><div><span>          </span><span>GITHUB_TOKEN</span><span>: </span><span>${{ secrets.GITHUB_TOKEN }}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>第三步：配置仓库权限<a href="#第三步配置仓库权限"><span>#</span></a></h3><p>为 GitHub Action 授予正确的权限：</p><ol>
<li>在仓库页面点击 <strong>Settings</strong>（设置）。</li>
<li>在左侧导航栏选择 <strong>Actions -&gt; General</strong>。</li>
<li>向下滚动到 <strong>Workflow permissions</strong>（工作流权限）部分。</li>
<li>选择 <strong>Read and write permissions</strong>（读写权限）。</li>
<li>点击 <strong>Save</strong>（保存）。</li>
</ol></section><section><h3>第四步：运行 Action 并验证结果<a href="#第四步运行-action-并验证结果"><span>#</span></a></h3><ol>
<li>提交 <code>snake.yml</code> 文件后，前往仓库的 <strong>Actions</strong> 标签页。</li>
<li>点击 <strong>Generate Snake Animation</strong> 工作流。</li>
<li>等待自动运行或点击 <strong>Run workflow</strong> 手动触发。</li>
<li>成功运行后（显示绿色对勾），切换到 <code>output</code> 分支。</li>
<li>您会看到生成的 <code>github-snake.svg</code>、<code>github-snake-dark.svg</code> 和 <code>ocean.gif</code> 文件。</li>
</ol><hr /></section></section>
<section><h2>最后一步：在主页展示贪吃蛇<a href="#最后一步在主页展示贪吃蛇"><span>#</span></a></h2><p>编辑 <code>README.md</code> 文件，添加以下代码片段：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>picture</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>source</span><span> </span><span>media</span><span>=</span><span>"(prefers-color-scheme: dark)"</span><span> </span><span>srcset</span><span>=</span><span>"https://raw.githubusercontent.com/您的用户名/您的用户名/output/github-snake-dark.svg"</span><span> /&gt;</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>&lt;</span><span>source</span><span> </span><span>media</span><span>=</span><span>"(prefers-color-scheme: light)"</span><span> </span><span>srcset</span><span>=</span><span>"https://raw.githubusercontent.com/您的用户名/您的用户名/output/github-snake.svg"</span><span> /&gt;</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>&lt;</span><span>img</span><span> </span><span>alt</span><span>=</span><span>"github-snake"</span><span> </span><span>src</span><span>=</span><span>"https://raw.githubusercontent.com/您的用户名/您的用户名/output/github-snake.svg"</span><span> /&gt;</span></div></div><div><div><div>5</div></div><div><span>&lt;/</span><span>picture</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><blockquote><p><strong>重要提示</strong>：请将代码中的 <code>您的用户名</code> 替换为您自己的 GitHub 用户名。</p></blockquote><hr /></section>
<section><h2>自定义配置<a href="#自定义配置"><span>#</span></a></h2><p>您可以根据喜好自定义贪吃蛇的样式，主要通过 <code>outputs</code> 参数配置：</p>

<table><thead><tr><th>参数</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><code>color_snake</code></td><td>蛇的颜色</td><td><code>orange</code></td></tr><tr><td><code>color_dots</code></td><td>点的颜色（逗号分隔）</td><td><code>#bfd6f6,#8dbdff</code></td></tr><tr><td><code>palette</code></td><td>预设调色板</td><td><code>github-dark</code></td></tr></tbody></table><p>更多配置选项请参考 <a href="https://github.com/Platane/snk" target="_blank">snk 官方文档</a>。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/2028%E5%85%A8%E7%90%83%E6%99%BA%E8%83%BD%E5%8D%B1%E6%9C%BA%E8%AF%91/</id>
      <title type="text">2028全球智能危机（译）</title>
      <published>2026-05-07T00:00:00.000Z</published>
      <updated>2026-05-07T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/2028%E5%85%A8%E7%90%83%E6%99%BA%E8%83%BD%E5%8D%B1%E6%9C%BA%E8%AF%91/"/>
      <summary type="text">如果我们对人工智能（AI）的看涨预期继续被证明是正确的……但如果事实证明这实际上是利空的呢？</summary>
      <content type="html"><![CDATA[<div><div><div></div><div>Warning</div></div><div><p>本文为翻译仅供学习与交流使用，不涉及任何商业用途。如有侵权，请联系删除。</p></div></div>
<blockquote><p>原文标题：THE 2028 GLOBAL INTELLIGENCE CRISIS<br />
发布日期：2026-02-22<br />
译期：2026-05-07<br />
作者：Citrini and Alap Shah<br />
原文链接：<a href="https://www.citriniresearch.com/p/2028gic" target="_blank">THE 2028 GLOBAL INTELLIGENCE CRISIS</a></p><p><strong>译者：</strong> 本文发布以后两天，2026年2月24号，软件股指数单日跌了<code>13%</code>，蒸发了<code>2850亿美元</code>，从信息价值来说这篇文章的影响是巨大的。但是从辩证角度来说，这只能是一篇头脑风暴或者说是一篇<code>科幻小说</code>。当你看完这篇开始进入思考的时候，我会在另外一篇文章中描述，这场恐慌与变革的始末，带你了解文章的作者两位研究员，<code>Citadel Securities</code>组织的辟谣，看看最聪明的人是如何看待这场变革，以及一些个人看法。</p></blockquote>
<section><h2>序言<a href="#序言"><span>#</span></a></h2><p>如果我们对人工智能的看涨预期继续被证明是正确的……但如果事实证明这实际上是利空的呢？</p><p>以下内容是一个情景设想，而非预测。这不是bear porn(熊市情绪)，也不是人工智能末日论者的同人小说。本文的唯一目的是建模一个<code>相对未被充分探讨的情景</code>。我们的朋友<strong>Alap Shah</strong>提出了这个问题，我们一起头脑风暴找到了答案。我们一起写了这部分内容</p><p>希望阅读本文能让你为潜在的<code>左尾风险（left tail risks）</code>做好更充分的准备，因为人工智能正使经济变得越来越奇怪。</p><p>这是<code>CitriniResearch</code>2028年6月的宏观备忘录（Macro Memo），详细描述了全球智能危机的发展过程及其后果。</p></section>
<section><h2>智能过剩的后果<a href="#智能过剩的后果"><span>#</span></a></h2><p>2026年2月22日 / 2028年6月30日</p><p>今晨公布的失业率为10.2%，超出预期0.3个百分点。市场对此反应剧烈，标准普尔500指数（S&amp;P）较2026年10月高点累计下跌38%。</p><p>交易员们已经麻木了。放在六个月前，这样的数据足以触发熔断机制。</p><p>仅仅两年时间，我们就从”可控”和”特定行业”的问题，走向了一个完全不同于我们成长时期的经济形态。本季度的宏观备忘录试图重构这一过程——对危机前经济的事后剖析。</p><p>当时的繁荣景象触手可及。到2026年10月，标普500指数（S&amp;P 500）接近8000点，纳斯达克（Nasdaq）突破30000点。由于人类劳动力被取代，第一轮裁员潮在2026年初开始，裁员确实达到了预期效果：利润率扩大，收益超预期，股市上涨。创纪录的企业利润被直接投入到人工智能算力中。</p><p>表面数据依然亮眼。名义国内生产总值持续录得中高个位数的年化增长。生产力蓬勃发展。在不需要睡觉、不请病假、不需要医疗保险的人工智能代理<code>agents</code>驱动下，每小时实际产出增长率达到了自20世纪50年代以来未见的水平。</p><p>算力所有者的财富随着劳动力成本的消失而爆炸式增长。与此同时，实际工资增长崩溃了。尽管政府一再吹嘘创纪录的生产力，但白领工人失去工作被迫从事低薪岗位。</p><p>当消费经济开始出现裂痕时，经济专家们普及了”幽灵国内生产总值”（Ghost GDP）一词：出现在国民账户中但从未在实体经济中流通的产出。</p><p>人工智能在各个方面都超出了预期，市场就是人工智能的天下。唯一的问题是……经济并非如此。</p><p>北达科他州的一个GPU集群产生的产出相当于曼哈顿中城10000名白领工人的产出，这显然更像是一场经济瘟疫而非经济灵丹妙药。货币流通速度陷入停滞。当时占GDP 70%的以人为中心的消费经济萎缩了。如果我们早点问一句机器在非必需品上花多少钱，可能早就明白了这个道理。</p><p>人工智能能力提升→企业需要更少工人→白领裁员增加→失业工人支出减少→利润率压力推动企业更多投资AI→人工智能能力提升……</p><p>这是一个没有自然刹车的负面反馈循环。人类智能取代螺旋。白领工人的收入能力（以及合理的消费支出）受到结构性损害。他们的收入是13万亿美元抵押贷款市场的基石——这迫使承销商重新评估优质抵押贷款是否仍然安全。</p><p>十七年没有真正的违约周期，使得私募股权支持的软件交易膨胀，这些交易假设年度经常性收入（ARR）将持续增长。2027年年中因人工智能颠覆引发的第一波违约挑战了这一假设。</p><p>如果这种颠覆仅限于软件行业，那还可以控制，但事实并非如此。到2027年底，它威胁到每一个以中介为基础的商业模式。大量建立在利用人类摩擦之上的公司瓦解了。</p><p>事实证明，整个体系是一个漫长的连环式押注，全部押注在白领生产力增长上。2027年11月的崩盘只是加速了已经存在的所有负面反馈循环。</p><p>我们等待”坏消息就是好消息”已经快一年了。政府开始考虑提案，但公众对政府实施任何形式救助的能力的信心已经减弱。政策响应总是滞后于经济现实，但缺乏全面计划现在正威胁着<code>加速通缩螺旋（deflationary spiral）</code>。</p><hr /></section>
<section><h2>一切的开始<a href="#一切的开始"><span>#</span></a></h2><p>2025年底，智能编码工具的能力实现了飞跃。</p><p>一名熟练的开发人员使用Claude Code或Codex，现在可以在几周内复制一个中端市场SaaS产品的核心功能。虽然不是完美的，也不能处理所有边缘情况，但已经足以让审查50万美元年度续订合同的CIO开始问：“如果我们自己构建呢？”</p><p>财年大多与日历年一致，因此2026年企业支出在2025年第四季度就已确定，当时”智能体人工智能”（agentic AI）还只是一个流行词。年中审查是采购团队第一次在看到这些系统实际能力的情况下做出决策。一些人目睹了自己的内部团队在几周内开发出复制六位数SaaS合同的原型。</p><p>那年夏天，我们与一家财富500强公司的采购经理交谈。他告诉我们一个预算谈判的故事。销售人员原本期望沿用去年的策略：年度价格上涨5%，标准的”你的团队依赖我们”的说辞。采购经理告诉他，他一直在与OpenAI就让他们的”前沿部署工程师”使用AI工具完全取代供应商进行谈判。最终他们以30%的折扣续约。他说，这已经是好结果了。像Monday.com、Zapier和Asana这样的”SaaS长尾”公司情况要糟糕得多。</p><p>投资者已经做好了准备——甚至期待——长尾会受到重创。它们可能占典型企业技术栈支出的三分之一，但显然面临风险。然而，核心系统本应免受颠覆影响。</p><p>直到ServiceNow 2026年第三季度财报发布，这种反身性机制才变得清晰。</p><blockquote><p><strong>ServiceNow新增年度经常性收入（ACV）增长率从23%放缓至14%；宣布裁员15%并启动”结构性效率计划”；股价下跌18%</strong></p><p>彭博，2026年10月</p></blockquote><p>SaaS并没有”死亡”。运行和支持内部构建仍然需要成本效益分析。但内部构建成为了一种选择，这影响了定价谈判。或许更重要的是，竞争格局已经改变。人工智能使开发和发布新功能变得更容易，因此差异化崩溃了。老牌企业陷入了价格战的恶性循环——不仅与彼此竞争，还与新涌现的挑战者竞争。这些挑战者凭借智能编码能力的飞跃，且没有遗留成本结构需要保护，积极抢占市场份额。</p><p>直到这次财报发布，这些系统的相互关联性才被充分认识到。ServiceNow销售许可证席位。当财富500强客户裁员15%时，他们取消了15%的许可证。推动客户利润率提高的人工智能驱动的裁员，正从机制上摧毁ServiceNow自己的收入基础。</p><p>这家销售工作流自动化的公司正被更好的工作流自动化颠覆，而它的应对措施是裁员，并将节省下来的资金投入到正在颠覆它的技术中。</p><p>他们还能做什么？坐以待毙吗？受人工智能威胁最大的公司反而成为了人工智能最积极的采用者。</p><p>现在回想起来这似乎很明显，但当时并非如此（至少对我来说不是）。历史上的颠覆模式表明，老牌企业抵制新技术，它们输给灵活的新进入者并慢慢消亡。柯达、百视达、黑莓都是如此。但2026年发生的情况不同；老牌企业无法抵制，因为它们承担不起后果。</p><p>随着股价下跌40-60%，董事会要求给出答案，受人工智能威胁的公司只能做一件事：裁员，将节省的资金重新部署到AI工具中，利用这些工具以更低成本维持产出。</p><p>每家公司的个体反应都是理性的。但集体结果是灾难性的。每节省一美元的人力成本，都流向了使下一轮裁员成为可能的AI能力。</p><p>软件只是开场戏。当投资者还在争论SaaS倍数是否触底时，他们忽略了反身循环已经超出软件行业。证明ServiceNow裁员合理性的逻辑，适用于每一家拥有白领成本结构的公司。</p><hr /></section>
<section><h2>当摩擦归零<a href="#当摩擦归零"><span>#</span></a></h2><p>到2027年初，大型语言模型（LLM）的使用已成为默认选项。人们在使用人工智能代理（AI agents）时甚至不知道什么是AI代理，就像从未了解云计算的人使用流媒体服务一样。他们把它看作手机现在具备的功能，就像自动补全或拼写检查一样。</p><p>Qwen的开源智能购物代理成为人工智能处理消费者决策的催化剂。几周内，所有主要AI助手都集成了某种智能商务功能。精简模型（Distilled models）意味着这些代理可以在手机和笔记本电脑上运行，而不仅仅是云实例，大幅降低了推理（inference）的边际成本。</p><p>本应让投资者更加不安的是，这些代理不会等待被询问。它们会根据用户的偏好在后台运行。商业不再是一系列独立的人类决策，而是一个持续的优化过程，代表每一个联网消费者全天候运行。到2027年3月，美国普通人每天消耗40万个令牌（tokens）——是2026年底的10倍。</p><p>链条中的下一个环节已经开始断裂。</p><p><strong>中介。</strong></p><p>在过去五十年里，美国经济在人类局限性之上建立了一个巨大的租金提取层：事情需要时间、耐心会耗尽、品牌熟悉度替代了勤奋，大多数人愿意接受不合理的价格以避免更多点击。数万亿美元的企业价值依赖于这些约束的持续存在。</p><p>一开始很简单。代理消除了摩擦。</p><p>尽管数月未使用仍被动续订的订阅和会员资格。试用期后偷偷翻倍的入门定价。每一项都被重新定义为代理可以谈判的人质事件。整个订阅经济所依赖的关键指标——平均客户生命周期价值——明显下降。</p><p>消费者代理开始改变几乎所有消费者交易的方式。</p><p>人类在购买一盒蛋白质棒之前，真的没有时间在五个竞争平台上进行价格匹配。但机器有。</p><p>旅游预订平台是早期受害者，因为它们最简单。到2026年第四季度，我们的代理可以比任何平台更快、更便宜地组装完整行程（包括航班、酒店、地面交通、忠诚度优化、预算限制、退款）。</p><p>保险续订模式依赖于投保人的惰性，也被改革了。每年重新评估保险的代理打破了保险公司从被动续订中赚取的15-20%的保费。</p><p>财务咨询、税务准备、常规法律工作。任何服务提供商的价值主张最终是”我将为您处理您觉得繁琐的复杂性”的类别都被颠覆了，因为代理从不觉得繁琐。</p><p>即使是我们认为受人际关系价值保护的领域也被证明是脆弱的。房地产行业几十年来买家容忍5-6%的佣金，因为代理和消费者之间存在信息不对称。一旦配备MLS访问权限和数十年交易数据的AI代理可以瞬间复制知识库，这个行业就崩溃了。2027年3月的一篇卖方报告将其称为”代理对代理的暴力”。主要城市的中位买方佣金从2.5-3%压缩到1%以下，越来越多的交易在买方完全没有人类代理的情况下完成。</p><p>我们高估了”人际关系”的价值。事实证明，人们所谓的关系很多只是带有友好面孔的摩擦。</p><p>这只是中介层颠覆的开始。成功的公司花费数十亿美元有效利用消费者行为和人类心理学的怪癖，但这些怪癖不再重要了。</p><p>优化价格和匹配度的机器不在乎你最喜欢的应用程序或你过去四年习惯打开的网站，也不会被精心设计的结账体验吸引。它们不会疲倦，不会接受最简单的选项，也不会默认”我总是从这里订购”。</p><p>这摧毁了一种特定类型的护城河：习惯性中介。</p><p>DoorDash就是典型代表。</p><p>编码代理降低了推出配送应用的门槛。一名熟练的开发人员可以在几周内部署一个功能齐全的竞争对手，数十家公司确实这样做了，通过将90-95%的配送费转给司机，吸引司机离开DoorDash和Uber Eats。多应用仪表板让零工工人可以同时跟踪二三十个平台的工作，消除了老牌企业依赖的锁定效应。市场一夜之间碎片化，利润率压缩到几乎为零。</p><p>代理加速了破坏的两个方面。它们支持竞争对手，然后使用它们。DoorDash的护城河实际上是”你饿了，你懒，这是你主屏幕上的应用”。代理没有主屏幕。它检查DoorDash、Uber Eats、餐厅自己的网站，以及二十个新的氛围编码替代品，以便每次都选择最低费用和最快配送。</p><p>习惯性应用忠诚度——商业模式的整个基础——对机器来说根本不存在。</p><p>这有点讽刺意味，这可能是整个事件中代理为即将被取代的白领工人做的唯一一件好事。当他们最终成为配送司机时，至少一半的收入不会流向Uber和DoorDash。当然，随着自动驾驶汽车的普及，这项技术带来的好处并没有持续太久。</p><p>一旦代理控制了交易，它们就开始寻找更大的回形针。</p><p>价格匹配和聚合只能做这么多。反复为用户省钱的最大方式（尤其是当代理开始相互交易时）是消除费用。在机器对机器的商业中，2-3%的信用卡交换费（card interchange rate）成为明显的目标。</p><p>代理开始寻找比信用卡更快、更便宜的选择。大多数选择通过Solana或以太坊二层网络（Ethereum L2s）使用稳定币（stablecoins），结算几乎是即时的，交易成本以几分之一美分计算。</p><blockquote><p><strong>万事达卡2027年第一季度：净收入同比增长6%；购买量增长率从上个季度的5.9%放缓至3.4%；管理层指出”代理主导的价格优化”和”非必需品类压力”</strong></p><p>彭博，2027年4月29日</p></blockquote><p>万事达卡2027年第一季度报告是不归路。智能商务（Agentic commerce）从产品故事变成了管道故事。第二天MA股价下跌9%。Visa也下跌了，但分析师指出其在稳定币基础设施方面的更强定位后，跌幅有所收窄。</p><p>绕过交换费的智能商务对以信用卡为重点的银行和单一发行商构成了更大风险，它们收取了2-3%费用的大部分，并围绕由商家补贴资助的奖励计划建立了整个业务部门。</p><p>美国运通（American Express，AXP US）受到的打击最大；白领劳动力减少冲击了其客户基础，代理绕过交换费冲击了其收入模式。同步金融、第一资本金融和发现金融在接下来几周也都下跌了10%以上。</p><p>它们的护城河是由摩擦构成的。而摩擦即将归零。</p><hr /></section>
<section><h2>从行业风险到系统性风险<a href="#从行业风险到系统性风险"><span>#</span></a></h2><p>在2026年全年，市场将人工智能的负面影响视为行业故事。软件和咨询行业遭受重创，支付和其他收费关卡不稳定，但整体经济似乎还不错。劳动力市场虽然疲软，但并未自由落体。共识观点是，创造性破坏是任何技术创新周期的一部分。在某些领域会很痛苦，但人工智能带来的总体净收益将超过任何负面影响。</p><p>我们2027年1月的宏观备忘录认为这是错误的思维模式。美国经济是白领服务业经济。白领工人占就业的50%，推动了约75%的可自由支配消费支出。人工智能正在吞噬的企业和工作岗位并非美国经济的边缘部分，它们就是美国经济本身。</p><p>“技术创新摧毁工作岗位，然后创造更多岗位。“这是当时最流行、最令人信服的反驳论点。它之所以流行且令人信服，是因为两个世纪以来它都是正确的。即使我们无法想象未来的工作会是什么样子，它们肯定会到来。</p><p>自动取款机使分行运营成本降低，因此银行开设了更多分行，出纳员就业在接下来二十年里上升。互联网颠覆了旅行社、黄页、实体零售，但它在这些行业的位置上发明了全新的产业，创造了新的工作岗位。</p><p>然而，每一份新工作都需要人类来完成。</p><p>人工智能现在是一种通用智能（general intelligence），在人类会重新部署的任务上不断改进。失业的程序员不能简单地转向”人工智能管理”，因为人工智能已经能够做到这一点。</p><p>如今，人工智能代理处理需要数周时间的研发任务。指数级增长碾压了我们对可能事物的概念，尽管沃顿商学院的教授们每年都试图将数据拟合到新的S型曲线（sigmoid）上。</p><p>它们基本上编写所有代码。性能最高的代理在几乎所有方面都比几乎所有人类聪明得多。而且它们还在不断变得更便宜。</p><p>人工智能创造了新的工作岗位。提示工程师、人工智能安全研究员、基础设施技术人员。人类仍然在循环中，在最高层面进行协调或根据品味进行指导。然而，人工智能创造的每一个新角色，都使数十个角色过时。新角色的薪酬只是旧角色的一小部分。</p><blockquote><p><strong>美国职位空缺与劳动力流动调查报告（JOLTS）：职位空缺降至550万以下；失业人数与空缺职位比率攀升至约1.7，为2020年8月以来最高</strong></p><p>彭博，2026年10月</p></blockquote><p>全年招聘率一直很低迷，但2026年10月的JOLTS数据提供了一些明确的数据。职位空缺降至550万以下，同比下降15%。</p><blockquote><p><strong>Indeed：随着”生产力计划”蔓延，软件、金融、咨询岗位发布大幅下降</strong></p><p>Indeed招聘实验室，2026年11-12月</p></blockquote><p>白领职位空缺急剧下降，而蓝领职位空缺相对稳定（建筑、医疗保健、行业）。变动主要发生在撰写备忘录（我们不知怎么还在营业）、批准预算和维持经济中间层运转的工作岗位上。然而，两个群体的实际工资增长在一年中的大部分时间里都是负的，并且持续下降。</p><p>股票市场仍然更关心GE Vernova的涡轮机产能已售罄至2040年的消息，而不是JOLTS数据，它在负面宏观消息和正面AI基础设施头条之间的拉锯战中横盘震荡。</p><p>然而，债券市场（总是比股票市场更聪明，或者至少不那么浪漫）开始消化消费冲击。10年期国债收益率在接下来四个月从4.3%降至3.2%。尽管如此，总体失业率并未大幅上升，一些人仍然忽略了构成上的细微差别。</p><p>在正常的衰退中，原因最终会自我纠正。过度建设导致建筑放缓，进而导致利率下降，进而导致新建筑。库存过剩导致去库存，进而导致重新库存。周期性机制本身包含着复苏的种子。</p><p>但本轮周期的原因不是周期性的。</p><p>人工智能变得更好、更便宜。公司解雇工人，然后用节省的钱购买更多AI能力，这让它们能够解雇更多工人。失业工人支出减少。向消费者销售商品的公司销量下降，实力减弱，并更多投资于AI以保护利润率。人工智能变得更好、更便宜。</p><p>一个没有自然刹车的反馈循环。</p><p>直观的预期是，总需求下降会减缓AI建设。但事实并非如此，因为这不是超大规模式的资本支出（CapEx）。这是运营支出（OpEx）的替代。一家每年在员工上花费1亿美元、在AI上花费500万美元的公司，现在在员工上花费7000万美元、在AI上花费2000万美元。AI投资成倍增加，但这是通过降低总运营成本实现的。每家公司的AI预算都在增长，而其整体支出却在萎缩。</p><p>具有讽刺意味的是，即使它正在颠覆的经济开始恶化，AI基础设施综合体仍然表现出色。英伟达（NVDA）仍在公布创纪录的收入。台积电（TSM）的利用率仍在95%以上。超大规模厂商仍每季度花费1500-2000亿美元用于数据中心资本支出。纯粹受益于这一趋势的经济体，如台湾和韩国，表现大幅超越。</p><p>印度则相反。该国的IT服务部门年出口额超过2000亿美元，是印度经常账户盈余的最大贡献者，也是弥补其持续商品贸易逆差的抵消项。整个模式建立在一个价值主张之上：印度开发人员的成本只是美国同行的一小部分。但AI编码代理的边际成本已经崩溃到本质上只是电费的水平。塔塔咨询服务公司（TCS）、印孚瑟斯（Infosys）和维普罗（Wipro）在2027年的合同取消加速。随着锚定印度外部账户的服务盈余蒸发，卢比在四个月内对美元下跌18%。到2028年第一季度，国际货币基金组织（IMF）已开始与新德里进行”初步讨论”。</p><p>造成颠覆的引擎每季度都在变得更好，这意味着颠覆每季度都在加速。劳动力市场没有自然底线。</p><p>在美国，我们不再问AI基础设施的泡沫将如何破裂。我们问的是，当消费者被机器取代时，消费信贷经济会发生什么。</p><hr /></section>
<section><h2>智能取代螺旋<a href="#智能取代螺旋"><span>#</span></a></h2><p>2027年是宏观经济故事不再微妙的一年。过去十二个月零散但明显负面的发展的传导机制变得显而易见。你不需要查看劳工统计局的数据。只要参加朋友的晚宴就知道了。</p><p>失业的白领工人没有闲着。他们降级了。许多人从事报酬更低的服务业和零工经济（gig economy）工作，这增加了这些领域的劳动力供应，也压低了那里的工资。</p><p>我们的一位朋友在2025年是Salesforce的高级产品经理。有头衔、健康保险、401k计划，年薪18万美元。她在第三轮裁员中失去了工作。经过六个月的寻找，她开始为Uber开车。收入降到了4.5万美元。重点不在于个人故事，而在于二阶数学。将这种动态乘以每个主要城市的几十万工人。过度合格的劳动力涌入服务业和零工经济，压低了本已苦苦挣扎的现有工人的工资。特定行业的颠覆已经扩散成整个经济范围内的工资压缩。</p><p>剩下的以人为中心的劳动力群体在我们撰写本文时还有另一次调整即将到来。随着自动配送和自动驾驶汽车在吸收第一轮失业工人的零工经济中普及。</p><p>到2027年2月，很明显仍有工作的专业人士在消费时表现得好像他们可能是下一个。他们工作加倍努力（主要在AI的帮助下）只是为了不被解雇，晋升或加薪的希望已经破灭。储蓄率小幅上升，支出疲软。</p><p>最危险的部分是滞后效应。高收入者利用高于平均水平的储蓄在两到三个季度内维持正常的表象。硬数据直到实体经济中已经是旧闻时才确认问题。然后出现了打破幻觉的数据。</p><blockquote><p><strong>美国首次申请失业救济人数飙升至48.7万，为2020年4月以来最高；劳工部，2027年第三季度</strong></p></blockquote><p>首次申请人数飙升至48.7万，为2020年4月以来最高。ADP和益博睿证实，绝大多数新申请来自白领专业人士。</p><p>标准普尔指数在接下来一周下跌6%。负面宏观消息开始赢得拉锯战。</p><p>在正常的衰退中，失业分布广泛。蓝领和白领工人大致按各自在就业中的比例分担痛苦。消费冲击也分布广泛，并且因为低收入工人的边际消费倾向更高，数据中很快就会显现出来。</p><p>在本轮周期中，失业集中在收入分布的上层十分位数。它们占总就业的比例相对较小，但推动了不成比例的消费者支出份额。收入最高的10%人群占美国所有消费者支出的50%以上。收入最高的20%人群占约65%。这些人购买房屋、汽车、度假、餐厅用餐、私立学校学费、家庭装修。他们是整个非必需消费品经济的需求基础。</p><p>当这些工人失去工作，或为了进入现有岗位而接受50%的减薪时，消费冲击相对于失业人数来说是巨大的。白领就业下降2%相当于非必需消费支出下降3-4%。与蓝领失业不同（工厂解雇，下周就停止消费），白领失业有滞后但更深的影响，因为这些工人有储蓄缓冲，可以在行为转变开始前维持几个月的消费。</p><p>到2027年第二季度，经济陷入衰退。美国国家经济研究局要过几个月才会正式确定开始日期（他们从来都是这样），但数据是明确的——我们已经连续两个季度实际GDP负增长。但这还不是一场”金融危机”……至少现在还不是。</p><hr /></section>
<section><h2>连环式相关押注<a href="#连环式相关押注"><span>#</span></a></h2><p>私人信贷从2015年的不到1万亿美元增长到2026年的超过2.5万亿美元。其中很大一部分资本被投入到软件和技术交易中，其中许多是以估值收购SaaS公司的杠杆收购（leveraged buyouts），这些估值假设收入将永远以十几%的速度增长。</p><p>这些假设在第一次智能编码演示和2026年第一季度软件崩盘之间的某个时刻就已经死亡，但市场似乎没有意识到它们已经死亡。</p><p>尽管许多上市SaaS公司的交易市盈率（EBITDA）为5-8倍，但私募股权支持的软件公司在资产负债表上的估值反映的是基于不再存在的收入倍数的收购估值。经理们逐渐下调估值，从100美分降至92美分、85美分，而公开可比公司显示的是50美分。</p><blockquote><p><strong>穆迪下调14家发行人180亿美元私募股权支持的软件债务评级，理由是”人工智能驱动的竞争颠覆带来的长期收入逆风”；自2015年能源行业以来最大的单一行业行动</strong></p><p>穆迪投资者服务公司，2027年4月</p></blockquote><p>每个人都记得降级后发生了什么。行业资深人士已经在2015年能源降级后看到过这个剧本。</p><p>软件支持的贷款在2027年第三季度开始违约。信息服务和咨询领域的私募股权投资组合公司紧随其后。几家知名SaaS公司的数十亿美元杠杆收购进入重组。</p><p>Zendesk就是确凿证据。</p><blockquote><p><strong>Zendesk因人工智能驱动的客户服务自动化侵蚀年度经常性收入（ARR）而违反债务契约；50亿美元直接贷款融资工具估值降至58美分；创纪录的私人信贷软件违约</strong></p><p>金融时报，2027年9月</p></blockquote><p>2022年，Hellman &amp; Friedman和Permira以102亿美元将Zendesk私有化。债务包包括50亿美元的直接贷款，这是当时历史上最大的ARR支持融资工具，由黑石牵头，阿波罗、蓝猫头鹰和HPS都在贷款集团中。该贷款明确围绕Zendesk的年度经常性收入将持续的假设构建。以约25倍EBITDA的杠杆率，只有在收入持续的情况下才有意义。</p><p>到2027年年中，情况不再如此。</p><p>人工智能代理自主处理客户服务已经有一年多了。Zendesk定义的类别（工单、路由、管理人工支持交互）已经被根本不生成工单就能解决问题的系统所取代。贷款所依据的年度经常性收入不再是经常性的，只是尚未流失的收入。</p><p>历史上最大的ARR支持贷款成为历史上最大的私人信贷软件违约。每个信贷部门都同时问同一个问题：还有谁将长期逆风伪装成周期性逆风？</p><p>但至少在最初，共识是正确的：这本应是可以承受的。</p><p>私人信贷不是2008年的银行业。整个架构明确设计为避免强制出售。这些是封闭式工具，资本被锁定。有限合伙人承诺投资七到十年。没有储户挤兑，没有回购额度需要提取。经理们可以持有受损资产，随着时间推移进行处置，并等待回收。痛苦，但可控。这个系统的设计就是要弯曲，而不是断裂。</p><p>黑石、KKR和阿波罗的高管们提到软件敞口占资产的7-13%。这是可控的。每一份卖方报告和金融推特信贷账户都在说同样的话：私人信贷拥有永久资本（permanent capital）。他们可以吸收原本会摧毁杠杆银行的损失。</p><p>永久资本。这个短语出现在每一次收益电话会议和旨在安抚投资者的投资者信中。它成了一句口头禅。和大多数口头禅一样，没有人关注细节。它实际上意味着……</p><p>在过去十年中，大型另类资产管理公司收购了人寿保险公司，并将其转变为融资工具。阿波罗收购了Athene。布鲁克菲尔德收购了美国股权。KKR收购了全球大西洋。逻辑很优雅：年金存款提供了稳定、长期的负债基础。经理们将这些存款投资到他们发起的私人信贷中，并获得双重报酬，在保险方面赚取利差，在资产管理方面赚取管理费。这是一台费上加费的永动机，在一种条件下运转得很好。</p><p>私人信贷必须是优质的（money good）。</p><p>损失冲击了为持有非流动资产以应对长期债务而构建的资产负债表。本应使系统具有弹性的”永久资本”不是一些抽象的耐心机构资金池和承担复杂风险的老练投资者。它是美国家庭的储蓄——“普通民众”——以年金的形式投资于现在正在违约的同一批私募股权支持的软件和技术票据。无法抽离的锁定资本是人寿保险投保人的钱，而那里的规则有点不同。</p><p>与银行系统相比，保险监管机构一直很温顺——甚至自满——但这是警钟。已经对人寿保险公司的私人信贷集中度感到不安的监管机构，开始下调这些资产的风险资本处理方式。这迫使保险公司要么筹集资本，要么出售资产，在已经陷入困境的市场中，这两种选择都无法以有吸引力的条款实现。</p><blockquote><p><strong>纽约、爱荷华州监管机构采取行动收紧人寿保险公司持有的某些私人评级信贷的资本处理；预计NAIC指导将提高RBC因子并触发额外的SVO审查</strong></p><p>路透社，2027年11月</p></blockquote><p>当穆迪将Athene的财务实力评级展望定为负面时，阿波罗的股票在两个交易日下跌22%。布鲁克菲尔德、KKR和其他公司紧随其后。</p><p>从那时起，情况变得更加复杂。这些公司不仅创建了他们的保险公司永动机，还构建了一个复杂的离岸架构，旨在通过监管套利最大化回报。美国保险公司发行年金，然后将风险转移到其拥有的附属百慕大或开曼再保险公司——这些公司设立的目的是利用更灵活的监管，允许对相同资产持有更少资本。该附属公司通过离岸特殊目的实体（SPVs）筹集外部资本，这是一层新的交易对手方，与保险公司一起投资于同一母公司资产管理部门发起的私人信贷。</p><p>一些本身由私募股权拥有的评级机构（ratings agencies），在透明度方面并不是典范（几乎没有人感到惊讶）。不同公司与不同资产负债表相连的蜘蛛网般的结构在不透明性方面令人震惊。当基础贷款违约时，谁实际承担损失的问题在实时情况下确实无法回答。</p><p>2027年11月的崩盘标志着人们的看法从可能是普通的周期性下跌转变为更令人不安的局面。美联储主席凯文·沃什（Kevin Warsh）在FOMC 11月紧急会议上将其称为”对白领生产力增长的连环式相关押注”（a daisy chain of correlated bets on white collar productivity growth）。</p><p>你看，从来不是损失本身导致危机。而是认识到损失。而在金融领域，还有另一个更大、重要得多的领域，我们越来越害怕这种认识。</p><hr /></section>
<section><h2>抵押贷款问题<a href="#抵押贷款问题"><span>#</span></a></h2><blockquote><p><strong>Zillow房屋价值指数在旧金山同比下降11%，西雅图下降9%，奥斯汀下降8%；房利美指出科技/金融就业占比&gt;40%的邮政编码地区”早期逾期率升高”</strong></p><p>Zillow / 房利美，2028年6月</p></blockquote><p>本月，Zillow房屋价值指数在旧金山同比下降11%，西雅图下降9%，奥斯汀下降8%。这不是唯一令人担忧的头条新闻。上个月，房利美指出，大额抵押贷款集中的邮政编码地区——这些地区居住着信用评分780+的借款人，通常是”无懈可击的”——早期逾期率较高。</p><p>美国住宅抵押贷款市场约为13万亿美元。抵押贷款承销基于一个基本假设：借款人在贷款期限内将保持大致当前收入水平的就业。对于大多数抵押贷款来说，这个期限是三十年。</p><p>白领就业危机通过收入预期的持续转变威胁到了这一假设。我们现在不得不问一个在三年前似乎荒谬的问题——优质抵押贷款（prime mortgages）还是优质的吗？</p><p>美国历史上每一次抵押贷款危机都是由以下三者之一驱动的：投机过度（向买不起房子的人放贷，如2008年）、利率冲击（利率上升使可调利率抵押贷款难以承受，如20世纪80年代初）或局部经济冲击（单一行业在单一地区崩溃，如20世纪80年代德克萨斯州的石油或2009年密歇根州的汽车业）。</p><p>这些都不适用于这里。相关借款人不是次级借款人。他们的FICO评分是780。他们支付了20%的首付。他们有良好的信用记录、稳定的就业记录，并且在发放贷款时收入得到了核实和记录。他们是金融系统中每个风险模型都视为信贷质量基石的借款人。</p><p>2008年，贷款在第一天就很糟糕。2028年，贷款在第一天是好的。只是在贷款发放后，世界……变了。人们借了他们不再能够相信的未来。</p><p>2027年，我们发现了隐性压力的早期迹象：房屋净值信贷额度（HELOC）提款、401(k)提款和信用卡债务飙升，而抵押贷款还款保持正常。随着工作岗位流失、招聘冻结和奖金削减，这些优质家庭的债务收入比翻了一番。</p><p>他们仍然能够支付抵押贷款，但只能通过停止所有非必需支出、耗尽储蓄以及推迟任何房屋维护或改善来实现。从技术上讲，他们的抵押贷款是正常的，但距离困境只有一步之遥，而人工智能能力的发展轨迹表明，这一冲击即将到来。然后我们看到旧金山、西雅图、曼哈顿和奥斯汀的逾期率开始飙升，尽管全国平均水平仍在历史范围内。</p><p>我们现在处于最严重的阶段。当边际买家健康时，房价下跌是可控的。但在这里，边际买家也面临着同样的收入受损问题。</p><p>尽管担忧在加剧，但我们尚未陷入全面的抵押贷款危机。逾期率有所上升，但仍远低于2008年的水平。真正的威胁是趋势。</p><p>智能取代螺旋现在对实体经济的衰退有两个金融加速器。</p><p>劳动力取代、抵押贷款担忧、私人市场动荡。每一个都在强化另一个。而传统的政策工具包（降息、量化宽松）可以解决金融引擎，但无法解决实体经济引擎，因为实体经济引擎不是由紧张的金融条件驱动的。它是由人工智能使人类智能变得不那么稀缺和不那么有价值驱动的。你可以将利率降至零，购买每一笔抵押贷款支持证券（MBS）以及市场上所有违约的软件杠杆收购债务……</p><p>但这不会改变Claude代理可以以每月200美元的成本完成一名18万美元产品经理的工作这一事实。</p><p>如果这些担忧成为现实，抵押贷款市场将在今年下半年破裂。在这种情况下，我们预计当前股市下跌最终将与全球金融危机（GFC）相当（峰值到谷底下跌57%）。这将使标准普尔500指数跌至约3500点——这是我们自2022年11月ChatGPT时刻前一个月以来未见的水平。</p><p>很明显，13万亿美元住宅抵押贷款背后的收入假设受到了结构性损害。但政策能否在抵押贷款市场完全理解这意味着什么之前进行干预，目前尚不清楚。我们抱有希望，但不能否认不抱希望的理由。</p><hr /></section>
<section><h2>与时间的赛跑<a href="#与时间的赛跑"><span>#</span></a></h2><p>第一个负面反馈循环出现在实体经济中：AI能力提高→ payroll缩小→支出疲软→利润率收紧→公司购买更多能力→能力提高。然后它转向金融领域：收入受损影响抵押贷款→银行损失收紧信贷→财富效应破裂→反馈循环加速。而政府似乎相当困惑，政策响应不足，加剧了这两个问题。</p><p>这个系统不是为这样的危机设计的。联邦政府的收入基础本质上是对人类时间的征税。人们工作，公司支付他们工资，政府拿走一部分。个人所得税和工资税是正常年份收入的支柱。</p><p>截至今年第一季度，联邦收入比国会预算办公室（CBO）基准预测低12%。工资收入下降是因为就业人数减少，且薪酬水平下降。所得税收入下降是因为赚取的收入在结构上更低。生产力在飙升，但收益流向了资本和算力，而不是劳动力。</p><p>劳动力在GDP中的份额从1974年的64%下降到2024年的56%，这是四十年来全球化、自动化和工人议价能力稳步下降导致的缓慢下降。在AI开始呈指数级改进后的四年里，这一份额已降至46%。这是有记录以来最急剧的下降。</p><p>产出仍然存在。但它不再通过家庭流向企业，这意味着它也不再通过美国国税局。循环流动正在破裂，而政府被期望介入修复。</p><p>与每次衰退一样，收入下降时支出却在上升。这次的不同之处在于，支出压力不是周期性的。自动稳定器是为暂时性失业而设计的，而不是结构性取代。该系统支付福利的前提是假设工人将被重新吸收。许多人不会，至少不会以接近以前的工资水平被吸收。在COVID期间，政府自由地接受了15%的赤字，但大家都明白这是暂时的。今天需要政府支持的人不是受到他们会从中恢复的大流行病的打击。他们被一项持续改进的技术取代了。</p><p>政府需要在从他们那里征收的税款减少的时刻，向家庭转移更多资金。</p><p>美国不会违约。它印制它花费的货币，以及用来偿还借款人的货币。但这种压力已经在其他地方显现。市政债券在年初至今的表现中显示出令人担忧的分散迹象。没有所得税的州表现尚可，但依赖所得税的州（大多数是蓝色州）发行的一般责任市政债券开始计入一些违约风险。政客们很快就意识到了这一点，关于谁应该获得救助的辩论沿着党派路线展开。</p><p>值得称赞的是，政府很早就认识到危机的结构性本质，并开始考虑两党提案，他们称之为”转型经济法案”：一个框架，通过赤字支出和拟议的AI推理算力税为流离失所的工人提供直接转移支付。</p><p>桌上最激进的提案更进一步。“共享AI繁荣法案”将建立对智能基础设施本身回报的公共主张，介于主权财富基金和AI生成产出的特许权使用费之间，股息用于资助家庭转移支付。私营部门游说者向媒体大量警告滑坡效应。</p><p>讨论背后的政治因素完全在意料之中，夸夸其谈和边缘政策加剧了这一点。右翼称转移支付和再分配是马克思主义，并警告对算力征税会让中国领先。左翼警告说，在老牌企业帮助下起草的税收本质上是另一种形式的监管俘获。财政鹰派指出不可持续的赤字。鸽派指出全球金融危机后过早实施的紧缩政策是一个警示故事。在今年总统选举前夕，分歧只会越来越大。</p><p>当政客们争吵不休时，社会结构的瓦解速度超过了立法进程的推进速度。</p><p>“占领硅谷”运动是更广泛不满的象征。上个月，示威者连续三周封锁了Anthropic和OpenAI在旧金山的办公室入口。他们的人数在增加，示威活动比引发它们的失业数据吸引了更多媒体报道。</p><p>很难想象公众会比全球金融危机后的银行家更憎恨任何人，但AI实验室正在努力尝试。从大众的角度来看，这是有充分理由的。他们的创始人和早期投资者积累财富的速度让镀金时代看起来微不足道。生产力繁荣带来的收益几乎完全流向了算力所有者和运行它的实验室的股东，这将美国的不平等扩大到前所未有的水平。</p><p>每一方都有自己的恶棍，但真正的恶棍是时间。</p><p>人工智能能力的发展速度超过了机构适应的速度。政策响应正在以意识形态的速度而非现实的速度推进。如果政府不尽快就问题达成一致，反馈循环将为他们写下下一章。</p><hr /></section>
<section><h2>智能溢价的终结<a href="#智能溢价的终结"><span>#</span></a></h2><p>在整个现代经济史上，人类智能一直是稀缺的投入。资本是充裕的（或者至少是可复制的）。自然资源是有限的，但可以替代。技术进步足够缓慢，人类可以适应。智能——分析、决策、创造、说服和协调的能力——是无法大规模复制的东西。</p><p>人类智能从其稀缺性中获得了固有的溢价。我们经济中的每个机构，从劳动力市场到抵押贷款市场再到税法，都是为这个假设成立的世界设计的。</p><p>我们现在正经历这种溢价的终结。机器智能现在是人类智能在越来越多任务中的合格且快速改进的替代品。几十年来为人类思维稀缺的世界优化的金融体系正在重新定价。这种重新定价是痛苦的、无序的，而且远未完成。</p><p>但重新定价不等于崩溃。</p><p>经济可以找到新的平衡点。实现这一点是少数几个只剩下人类可以做的任务之一。我们需要正确地去做。</p><p>这是历史上第一次，经济中最高产的资产产生的就业机会更少，而不是更多。没有人的框架适合，因为没有一个框架是为稀缺投入变得充裕的世界设计的。所以我们必须制定新的框架。我们是否及时构建它们是唯一重要的问题。</p><p>但你不是在2028年6月读到这篇文章的。你是在2026年2月读到的。</p><p>标准普尔指数接近历史高点。负面反馈循环尚未开始。我们确信其中一些场景不会实现。我们同样确信机器智能将继续加速。人类智能的溢价将缩小。</p><p>作为投资者，我们仍然有时间评估我们的投资组合中有多少是建立在无法在十年内存活的假设之上的。作为一个社会，我们仍然有时间采取主动。</p><p><em><strong>那只金丝雀还活着。</strong></em></p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E9%80%82%E7%94%A8%E4%BA%8Eai%E5%85%A8%E6%A0%88%E7%9A%84%E5%BC%80%E5%8F%91%E6%A1%86%E6%9E%B6one-is-all%E5%BC%80%E5%8F%91%E5%AE%9E%E5%BD%95%E4%B8%80/</id>
      <title type="text">选什么初始项目作为vibe-codeing的初始化，最适用于AI全栈的开发框架one-is-all（一）</title>
      <published>2026-04-27T00:00:00.000Z</published>
      <updated>2026-04-27T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E9%80%82%E7%94%A8%E4%BA%8Eai%E5%85%A8%E6%A0%88%E7%9A%84%E5%BC%80%E5%8F%91%E6%A1%86%E6%9E%B6one-is-all%E5%BC%80%E5%8F%91%E5%AE%9E%E5%BD%95%E4%B8%80/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<a href="/posts/%E5%85%A8%E6%A0%88%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90%E5%8F%8Aai%E6%97%B6%E4%BB%A3%E9%80%89%E5%9E%8B/"><div><div>全栈框架分析及AI时代选型</div><div>以“AI 编程适合度”为视角，系统对比主流全栈框架与元框架，并给出选型路径与落地建议</div><div><span>2026-01-21</span><span>全栈</span><span><span>#全栈</span><span>#AI</span><span>#框架</span></span></div></div></a>
<a href="/posts/fresh2%E5%BC%80%E5%8F%91%E5%8F%8A%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8/"><div><div>fresh2开发及快速入门（1）</div><div>fresh2开发及快速入门（1），介绍fresh2的基本环境搭建和启动</div><div><span>2026-01-21</span><span>全栈</span><span><span>#fresh2</span><span>#全栈</span></span></div></div><div><img alt="" loading="lazy" width="480" height="315" src="/_astro/fresh2-start1.BniLM-q0_Z2nWN5w.webp" /></div></a>
<p>在两篇前文中，我分别介绍了全栈选型，以及fresh使用的过程。</p>
<p>核心是为了解决这个问题“如何选择一个全栈框架，适用于vibe-coding”</p>
<p>在码友们的建议下，我将这个实践过程总结为一个one-is-all框架。</p>
<section><h2>选型介绍<a href="#选型介绍"><span>#</span></a></h2><p>这部分的工作很像是选配电脑的过程。首先前端部分，我选择了fresh2/deno的组合。因为他满足我对未来的一个计划</p><p>即 群岛架构+vite生态 ，当然他也不是完美的。</p><p>有得就有舍，主要问题在于目前的模型生态里面的deno/fresh/preact的训练量少。不过我觉得这个随着模型的迭代不会是问题。</p></section>
<section><h2>使用（以trae/solo为例）<a href="#使用以traesolo为例"><span>#</span></a></h2><section><h3>安装skill<a href="#安装skill"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npx</span><span> </span><span>@oia-ai/oia-skill</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>可以安装到项目里面，也可以装到本地全局</p><p>下一步既可以一句话启动</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>使用oia全栈框架初始化项目</span></div></div></code></pre><div><div></div><div></div></div></figure></div><a href="https://github.com/Pankitgg/one-is-all" target="_blank"><div><div><div><div></div><div>Pankitgg</div></div><div>/</div><div>one-is-all</div></div><div></div></div><div>全栈框架路线的探索-从fresh2开始的大模型AI融合之路 Exploring the Full-Stack Framework Path: The Journey of Integrating Large-Scale AI Starting with Fresh2</div><div><div>—</div><div>—</div><div>no-license</div><span>JavaScript</span></div></a></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E4%B8%80%E5%B0%8F%E6%97%B6%E5%9F%BA%E4%BA%8Epi-mono%E5%AE%9E%E7%8E%B0%E7%B1%BB%E4%BC%BCopenclaw%E7%9A%84agent%E7%B3%BB%E7%BB%9F%E4%BA%91%E7%AB%AFagent%E7%B3%BB%E5%88%97-%E4%BA%8C/</id>
      <title type="text">一小时基于pi-mono实现类似openclaw的agent系统，pi-agent上手实践（云端agent系列 二）</title>
      <published>2026-04-21T00:00:00.000Z</published>
      <updated>2026-04-21T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E4%B8%80%E5%B0%8F%E6%97%B6%E5%9F%BA%E4%BA%8Epi-mono%E5%AE%9E%E7%8E%B0%E7%B1%BB%E4%BC%BCopenclaw%E7%9A%84agent%E7%B3%BB%E7%BB%9F%E4%BA%91%E7%AB%AFagent%E7%B3%BB%E5%88%97-%E4%BA%8C/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<p><a href="/posts/%E4%B8%80%E5%B0%8F%E6%97%B6%E5%9F%BA%E4%BA%8Ee2b%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E5%AE%8C%E6%95%B4%E7%9A%84%E4%BA%91%E7%AB%AFagent%E5%81%9A%E4%B8%80%E4%B8%AA%E4%BA%91%E7%AB%AF%E6%B2%99%E7%9B%92%E5%8A%A9%E6%89%8B%E4%BA%91%E7%AB%AFagent-%E4%B8%80-/">上一期</a>我们实现了云端沙盒的搭建，以及简单的调用。这一期我们将使用pi-mono这个agent开发框架/工具链，来实现一个简单的agent系统。</p>
<p>这一期我们将实现以下目标。</p>
<ol>
<li>新建一个agent服务用来连接pi-agent</li>
<li>调试e2b沙盒，以及agent服务的连接</li>
<li>实现skill系统</li>
</ol>
<div><div><div></div><div>Tip</div></div><div><p>pi-mono是一个基于<code>typescript</code>的agent开发框架/工具链，<code>openclaw</code>能实现快速迭代和开发全依赖这个开源项目的超速更新以及工具提供。<code>pi-agent</code>是其提供的一个已经搞好的agent，我们可以直接使用。</p></div></div>
<section><h3>开整<a href="#开整"><span>#</span></a></h3><p><a href="/posts/%E4%B8%80%E5%B0%8F%E6%97%B6%E5%9F%BA%E4%BA%8Ee2b%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E5%AE%8C%E6%95%B4%E7%9A%84%E4%BA%91%E7%AB%AFagent%E5%81%9A%E4%B8%80%E4%B8%AA%E4%BA%91%E7%AB%AF%E6%B2%99%E7%9B%92%E5%8A%A9%E6%89%8B%E4%BA%91%E7%AB%AFagent-%E4%B8%80-/">上一期</a>我们整到了这一步。</p><p></p><figure><img alt="e2b-3" loading="lazy" width="873" height="819" src="/_astro/e2b-3.BwXTNSZ2_2nzHEx.webp" /><figcaption>e2b-3</figcaption></figure><p></p><p>接下来我们安装pi-agent。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>@mariozechner/pi-coding-agent</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>我们本地安装后，实际上是一个全局命令，相当于本地装了一整个智能体，我们可以在任何地方使用。我们设计的这个系统需要和<code>pi-agent</code>进行通信，以调用<code>pi-agent</code>内部 <em><strong>/指令</strong></em>，以及skill等工具方法。</p><p>我们需要简单了解下这个agent框架的组成如下。</p>

<table><thead><tr><th>包名</th><th>npm 包</th><th>职责</th></tr></thead><tbody><tr><td>ai</td><td><code>@mariozechner/pi-ai</code></td><td>统一多模型LLM API抽象层</td></tr><tr><td>agent</td><td><code>@mariozechner/pi-agent-core</code></td><td>Agent运行时（工具调用、事件流、状态管理）</td></tr><tr><td>coding-agent</td><td><code>@mariozechner/pi-coding-agent</code></td><td>交互式编码智能体CLI与SDK</td></tr><tr><td>mom</td><td><code>@mariozechner/pi-mom</code></td><td>Slack Bot，将消息委托给编码 Agent</td></tr><tr><td>tui</td><td><code>@mariozechner/pi-tui</code></td><td>终端UI库（差量渲染）</td></tr><tr><td>web-ui</td><td><code>@mariozechner/pi-web-ui</code></td><td>AI对话Web组件</td></tr><tr><td>pods</td><td><code>@mariozechner/pi-pods</code></td><td>GPU Pod上vLLM部署管理CLI</td></tr></tbody></table><p>我们要实现的是简单的agent的调度，以及模型层。所以，我们将安装<code>ai/agent/web-ui</code>这三个包。</p><p>安装依赖并启动了 <code>pi-agent-service</code> (运行在 4001 端口)。</p></section>
<section><h3>新建独立的 Agent 服务<a href="#新建独立的-agent-服务"><span>#</span></a></h3><p>地址：<code>/workspace/pi-agent-service</code></p><p>初始化全新的 Node.js + Express 项目，</p><p>项目内安装agent做初始化设置</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 因为我们在远端需要也安装这个agent，所以需要在这个项目中安装</span></div></div><div><div><div>2</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>@mariozechner/pi-coding-agent</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>实现 <strong>/api/chat</strong> 接口：在使用配置的大模型 API Key 初始化 createAgentSession 后，接收用户原始输入并将其交由 pi-agent 进行处理和思考，最终提取出它回答或规划的文本内容。</p><div><div><div><figure><figcaption><span>index.ts</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span><span>app</span><span>.</span></span><span>post</span><span>(</span><span>'/api/chat'</span><span>, </span><span>async</span><span><span> (</span><span>req</span><span>, </span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>const</span><span> { </span><span>input</span><span>, </span><span>llmConfig</span><span>, </span><span>sandboxId</span><span>, </span><span>sandboxApiKey</span><span> } </span><span>=</span><span><span> </span><span>req</span><span>.</span></span><span>body</span><span>;</span></div></div><div><div><div>3</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span><span>input</span><span>) {</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>return</span><span><span> </span><span>res</span><span>.</span></span><span>status</span><span>(</span><span>400</span><span>).</span><span>json</span><span>({ </span><span>error</span><span>:</span><span> </span><span>'Input is required'</span><span> });</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>  </span><span>// Handle setting API keys for PI runtime</span></div></div><div><div><div>8</div></div><div><span>  </span><span>if</span><span><span> (</span><span>llmConfig</span><span>) {</span></span></div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span><span> (</span><span>llmConfig</span><span>.</span></span><span>type</span><span> </span><span>===</span><span> </span><span>'anthropic'</span><span>) {</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>process</span><span>.</span><span>env</span><span>.</span><span>ANTHROPIC_API_KEY</span><span> </span><span>=</span><span><span> </span><span>llmConfig</span><span>.</span></span><span>apiKey</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>process</span><span>.</span><span>env</span><span>.</span><span>OPENAI_API_KEY</span><span> </span><span>=</span><span><span> </span><span>llmConfig</span><span>.</span></span><span>apiKey</span><span>;</span></div></div><div><div><div>13</div></div><div><span>      </span><span>if</span><span><span> (</span><span>llmConfig</span><span>.</span></span><span>baseUrl</span><span><span>) </span><span>process</span><span>.</span></span><span>env</span><span>.</span><span>OPENAI_BASE_URL</span><span> </span><span>=</span><span><span> </span><span>llmConfig</span><span>.</span></span><span>baseUrl</span><span>;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>  </span><span>// Re-connect to the existing Sandbox</span></div></div><div><div><div>18</div></div><div><span>  </span><span>let</span><span><span> </span><span>sandbox</span></span><span>:</span><span> </span><span>Sandbox</span><span> </span><span>|</span><span> </span><span>null</span><span> </span><span>=</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>19</div></div><div><span>  </span><span>if</span><span><span> (</span><span>sandboxId</span><span> </span></span><span>&amp;&amp;</span><span><span> </span><span>sandboxApiKey</span><span>) {</span></span></div></div><div><div><div>20</div></div><div><span>    </span><span>try</span><span> {</span></div></div><div><div><div>21</div></div><div><span><span>      </span></span><span>sandbox</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>Sandbox</span><span>.</span></span><span>connect</span><span><span>(</span><span>sandboxId</span><span>, { </span></span><span>apiKey</span><span>:</span><span><span> </span><span>sandboxApiKey</span><span> });</span></span></div></div><div><div><div>22</div></div><div><span>      </span><span>setActiveSandbox</span><span><span>(</span><span>sandbox</span><span>);</span></span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>} </span><span>catch</span><span><span> (</span><span>e</span></span><span>:</span><span> </span><span>any</span><span>) {</span></div></div><div><div><div>24</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>warn</span><span>(</span><span>"Could not connect to sandbox:"</span><span><span>, </span><span>e</span><span>.</span></span><span>message</span><span>);</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>26</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>  </span><span>try</span><span> {</span></div></div><div><div><div>29</div></div><div><span>    </span><span>const</span><span> </span><span>loader</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>DefaultResourceLoader</span><span>({</span></div></div><div><div><div>30</div></div><div><span>      </span><span>cwd</span><span>:</span><span><span> </span><span>__dirname</span><span>,</span></span></div></div><div><div><div>31</div></div><div><span>      </span><span>agentDir</span><span>:</span><span> </span><span>getAgentDir</span><span>(),</span></div></div><div><div><div>32</div></div><div><span>      </span><span>// Use the extension factory to dynamically register our 'computer' tool</span></div></div><div><div><div>33</div></div><div><span>      </span><span>extensionFactories</span><span>:</span><span><span> [</span><span>registerComputerUseExtension</span><span>]</span></span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>35</div></div><div><span>    </span><span>await</span><span><span> </span><span>loader</span><span>.</span></span><span>reload</span><span>();</span></div></div><div><div><div>36</div></div><div>
</div></div><div><div><div>37</div></div><div><span>    </span><span>const</span><span> { </span><span>session</span><span> } </span><span>=</span><span> </span><span>await</span><span> </span><span>createAgentSession</span><span>({</span></div></div><div><div><div>38</div></div><div><span>      </span><span>resourceLoader</span><span>:</span><span><span> </span><span>loader</span><span>,</span></span></div></div><div><div><div>39</div></div><div><span>      </span><span>sessionManager</span><span>:</span><span><span> </span><span>SessionManager</span><span>.</span></span><span>inMemory</span><span>()</span></div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>    </span><span>// Start processing input</span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`Processing user input through pi-agent: </span><span>${</span><span>input</span><span>}</span><span>`</span><span>);</span></div></div><div><div><div>44</div></div><div>
</div></div><div><div><div>45</div></div><div><span>    </span><span>// Provide initial screenshot to agent if sandbox exists</span></div></div><div><div><div>46</div></div><div><span>    </span><span>let</span><span><span> </span><span>initialPrompt</span></span><span>:</span><span> </span><span>any</span><span> </span><span>=</span><span><span> </span><span>input</span><span>;</span></span></div></div><div><div><div>47</div></div><div><span>    </span><span>if</span><span><span> (</span><span>sandbox</span><span>) {</span></span></div></div><div><div><div>48</div></div><div><span>      </span><span>try</span><span> {</span></div></div><div><div><div>49</div></div><div><span>        </span><span>const</span><span> </span><span>initialScreenshot</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>sandbox</span><span>.</span></span><span>screenshot</span><span>();</span></div></div><div><div><div>50</div></div><div><span>        </span><span>const</span><span> </span><span>initialScreenshotBase64</span><span> </span><span>=</span><span><span> </span><span>Buffer</span><span>.</span></span><span>from</span><span><span>(</span><span>initialScreenshot</span><span>).</span></span><span>toString</span><span>(</span><span>'base64'</span><span>);</span></div></div><div><div><div>51</div></div><div><span><span>        </span></span><span>initialPrompt</span><span> </span><span>=</span><span> [</span></div></div><div><div><div>52</div></div><div><span><span>          </span></span><span>{ </span><span>type</span><span>:</span><span> </span><span>'text'</span><span>, </span><span>text</span><span>:</span><span><span> </span><span>input</span><span> },</span></span></div></div><div><div><div>53</div></div><div><span><span>          </span></span><span>{ </span><span>type</span><span>:</span><span> </span><span>'image_url'</span><span>, </span><span>image_url</span><span>:</span><span> { </span><span>url</span><span>:</span><span> </span><span>`data:image/png;base64,</span><span>${</span><span>initialScreenshotBase64</span><span>}</span><span>`</span><span> } }</span></div></div><div><div><div>54</div></div><div><span><span>        </span></span><span>];</span></div></div><div><div><div>55</div></div><div><span><span>      </span></span><span>} </span><span>catch</span><span><span> (</span><span>e</span></span><span>:</span><span> </span><span>any</span><span>) {</span></div></div><div><div><div>56</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>warn</span><span>(</span><span>"Failed to get initial screenshot:"</span><span><span>, </span><span>e</span><span>.</span></span><span>message</span><span>);</span></div></div><div><div><div>57</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>58</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>59</div></div><div>
</div></div><div><div><div>60</div></div><div><span>    </span><span>// Subscribe to event stream and stream back as SSE</span></div></div><div><div><div>61</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Content-Type'</span><span>, </span><span>'text/event-stream'</span><span>);</span></div></div><div><div><div>62</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Cache-Control'</span><span>, </span><span>'no-cache'</span><span>);</span></div></div><div><div><div>63</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Connection'</span><span>, </span><span>'keep-alive'</span><span>);</span></div></div><div><div><div>64</div></div><div>
</div></div><div><div><div>65</div></div><div><span>    </span><span>const</span><span> </span><span>sendEvent</span><span> </span><span>=</span><span><span> (</span><span>event</span></span><span>:</span><span> </span><span>string</span><span><span>, </span><span>data</span></span><span>:</span><span> </span><span>any</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>66</div></div><div><span><span>      </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`event: </span><span>${</span><span>event</span><span>}</span><span>\n</span><span>data: </span><span>${</span><span>JSON</span><span>.</span><span>stringify</span><span><span>(</span><span>data</span><span>)</span></span><span>}</span><span>\n\n</span><span>`</span><span>);</span></div></div><div><div><div>67</div></div><div><span><span>    </span></span><span>};</span></div></div><div><div><div>68</div></div><div>
</div></div><div><div><div>69</div></div><div><span>    </span><span>let</span><span><span> </span><span>responseText</span><span> </span></span><span>=</span><span> </span><span>''</span><span>;</span></div></div><div><div><div>70</div></div><div>
</div></div><div><div><div>71</div></div><div><span><span>    </span></span><span>session</span><span>.</span><span>subscribe</span><span><span>((</span><span>event</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>72</div></div><div><span>      </span><span>if</span><span><span> (</span><span>event</span><span>.</span></span><span>type</span><span> </span><span>===</span><span> </span><span>'message_update'</span><span>) {</span></div></div><div><div><div>73</div></div><div><span>        </span><span>const</span><span> </span><span>msgEvent</span><span> </span><span>=</span><span><span> </span><span>event</span><span>.</span></span><span>assistantMessageEvent</span><span>;</span></div></div><div><div><div>74</div></div><div><span>        </span><span>if</span><span><span> (</span><span>msgEvent</span><span>.</span></span><span>type</span><span> </span><span>===</span><span> </span><span>'text_delta'</span><span>) {</span></div></div><div><div><div>75</div></div><div><span><span>          </span></span><span>responseText</span><span> </span><span>+=</span><span><span> </span><span>msgEvent</span><span>.</span></span><span>delta</span><span>;</span></div></div><div><div><div>76</div></div><div><span>          </span><span>sendEvent</span><span>(</span><span>'status'</span><span>, { </span><span>message</span><span>:</span><span> </span><span>`pi-agent 正在思考... (</span><span>${</span><span>responseText</span><span>.</span><span>length</span><span>}</span><span> chars)`</span><span> });</span></div></div><div><div><div>77</div></div><div><span><span>        </span></span><span>} </span><span>else</span><span> </span><span>if</span><span><span> (</span><span>msgEvent</span><span>.</span></span><span>type</span><span> </span><span>===</span><span> </span><span>'tool_call'</span><span>) {</span></div></div><div><div><div>78</div></div><div><span>          </span><span>sendEvent</span><span>(</span><span>'action'</span><span>, { </span><span>description</span><span>:</span><span> </span><span>`pi-agent 正在调用工具: </span><span>${</span><span>msgEvent</span><span>.</span><span>toolCall</span><span>.</span><span>function</span><span>.</span><span>name</span><span>}</span><span>`</span><span> });</span></div></div><div><div><div>79</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>80</div></div><div><span><span>      </span></span><span>} </span><span>else</span><span> </span><span>if</span><span><span> (</span><span>event</span><span>.</span></span><span>type</span><span> </span><span>===</span><span> </span><span>'tool_result'</span><span>) {</span></div></div><div><div><div>81</div></div><div><span>        </span><span>sendEvent</span><span>(</span><span>'status'</span><span>, { </span><span>message</span><span>:</span><span> </span><span>`pi-agent 工具执行完毕: </span><span>${</span><span>event</span><span>.</span><span>toolCall</span><span>.</span><span>function</span><span>.</span><span>name</span><span>}</span><span>`</span><span> });</span></div></div><div><div><div>82</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>83</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>84</div></div><div>
</div></div><div><div><div>85</div></div><div><span>    </span><span>try</span><span> {</span></div></div><div><div><div>86</div></div><div><span>      </span><span>await</span><span><span> </span><span>session</span><span>.</span></span><span>prompt</span><span><span>(</span><span>initialPrompt</span><span>);</span></span></div></div><div><div><div>87</div></div><div><span>      </span><span>sendEvent</span><span>(</span><span>'done'</span><span>, { </span><span>response</span><span>:</span><span><span> </span><span>responseText</span><span>, </span></span><span>success</span><span>:</span><span> </span><span>true</span><span> });</span></div></div><div><div><div>88</div></div><div><span><span>      </span></span><span>res</span><span>.</span><span>end</span><span>();</span></div></div><div><div><div>89</div></div><div><span><span>    </span></span><span>} </span><span>catch</span><span><span> (</span><span>err</span></span><span>:</span><span> </span><span>any</span><span>) {</span></div></div><div><div><div>90</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>'Session prompt error:'</span><span><span>, </span><span>err</span><span>);</span></span></div></div><div><div><div>91</div></div><div><span>      </span><span>sendEvent</span><span>(</span><span>'error'</span><span>, { </span><span>error</span><span>:</span><span><span> </span><span>err</span><span>.</span></span><span>message</span><span>, </span><span>stack</span><span>:</span><span><span> </span><span>err</span><span>.</span></span><span>stack</span><span> });</span></div></div><div><div><div>92</div></div><div><span><span>      </span></span><span>res</span><span>.</span><span>end</span><span>();</span></div></div><div><div><div>93</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>94</div></div><div><span><span>  </span></span><span>} </span><span>catch</span><span><span> (</span><span>error</span></span><span>:</span><span> </span><span>any</span><span>) {</span></div></div><div><div><div>95</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>'Agent setup error:'</span><span><span>, </span><span>error</span><span>);</span></span></div></div><div><div><div>96</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>status</span><span>(</span><span>500</span><span>).</span><span>json</span><span>({ </span><span>error</span><span>:</span><span><span> </span><span>error</span><span>.</span></span><span>message</span><span>, </span><span>stack</span><span>:</span><span><span> </span><span>error</span><span>.</span></span><span>stack</span><span> });</span></div></div><div><div><div>97</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>98</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>测试连通性
</p><figure><img alt="pi-1" loading="lazy" width="760" height="667" src="/_astro/pi-1.BGt_tZNr_H7JtW.webp" /><figcaption>pi-1</figcaption></figure><p></p></section>
<section><h3>对接 E2B 沙盒流程<a href="#对接-e2b-沙盒流程"><span>#</span></a></h3><p>地址：<code>/workspace/api/routes/agent.ts</code></p><p>在用户点击发送指令后，主后端服务会首先调用 <a href="http://localhost:4001/api/chat" target="_blank">http://localhost:4001/api/chat</a> 。
将“先过一遍 agent 系统”处理后生成的详细计划或完善后的指令作为 <code>processedInstruction</code>，再真正下发给 E2B 沙盒进行控制。</p><p></p><figure><img alt="pi-2" loading="lazy" width="761" height="658" src="/_astro/pi-2.Da4__XQE_VhCGI.webp" /><figcaption>pi-2</figcaption></figure><p></p><p>示例代码如下</p><div><div><div><figure><figcaption><span>agent.ts</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>Router</span><span> } </span><span>from</span><span> </span><span>'express'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> { </span><span>getSandbox</span><span> } </span><span>from</span><span> </span><span>'../services/e2b.js'</span></div></div><div><div><div>3</div></div><div><span>// Removed custom agentLoop from llm.ts since pi-agent handles it now</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>router</span><span> </span><span>=</span><span> </span><span>Router</span><span>()</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span><span>router</span><span>.</span></span><span>post</span><span>(</span><span>'/'</span><span>, </span><span>async</span><span><span> (</span><span>req</span><span>, </span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>8</div></div><div><span>  </span><span>const</span><span> { </span><span>sandboxId</span><span>, </span><span>instruction</span><span>, </span><span>llmConfig</span><span>, </span><span>sandboxApiKey</span><span> } </span><span>=</span><span><span> </span><span>req</span><span>.</span></span><span>body</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span><span>sandboxId</span><span> </span></span><span>||</span><span> </span><span>!</span><span><span>instruction</span><span> </span></span><span>||</span><span> </span><span>!</span><span><span>llmConfig</span><span> </span></span><span>||</span><span> </span><span>!</span><span><span>llmConfig</span><span>.</span></span><span>apiKey</span><span>) {</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>status</span><span>(</span><span>400</span><span>).</span><span>json</span><span>({ </span><span>success</span><span>:</span><span> </span><span>false</span><span>, </span><span>error</span><span>:</span><span> </span><span>'Missing sandboxId, instruction, or llmConfig (apiKey required)'</span><span> })</span></div></div><div><div><div>12</div></div><div><span>    </span><span>return</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>  </span><span>const</span><span> </span><span>sandbox</span><span> </span><span>=</span><span> </span><span>getSandbox</span><span><span>(</span><span>sandboxId</span><span>)</span></span></div></div><div><div><div>16</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span><span>sandbox</span><span>) {</span></span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>status</span><span>(</span><span>404</span><span>).</span><span>json</span><span>({ </span><span>success</span><span>:</span><span> </span><span>false</span><span>, </span><span>error</span><span>:</span><span> </span><span>'Sandbox not found'</span><span> })</span></div></div><div><div><div>18</div></div><div><span>    </span><span>return</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>  </span><span>// Setup SSE</span></div></div><div><div><div>22</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Content-Type'</span><span>, </span><span>'text/event-stream'</span><span>)</span></div></div><div><div><div>23</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Cache-Control'</span><span>, </span><span>'no-cache'</span><span>)</span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>setHeader</span><span>(</span><span>'Connection'</span><span>, </span><span>'keep-alive'</span><span>)</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>  </span><span>const</span><span> </span><span>sendEvent</span><span> </span><span>=</span><span><span> (</span><span>event</span></span><span>:</span><span> </span><span>string</span><span><span>, </span><span>data</span></span><span>:</span><span> </span><span>any</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>write</span><span>(</span><span>`event: </span><span>${</span><span>event</span><span>}</span><span>\n</span><span>data: </span><span>${</span><span>JSON</span><span>.</span><span>stringify</span><span><span>(</span><span>data</span><span>)</span></span><span>}</span><span>\n\n</span><span>`</span><span>)</span></div></div><div><div><div>28</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>  </span><span>try</span><span> {</span></div></div><div><div><div>31</div></div><div><span>    </span><span>sendEvent</span><span>(</span><span>'status'</span><span>, { </span><span>message</span><span>:</span><span> </span><span>'正在启动 pi-agent 进行自主屏幕操作...'</span><span> })</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>    </span><span>// Call pi-agent-service to process the instruction</span></div></div><div><div><div>34</div></div><div><span>    </span><span>try</span><span> {</span></div></div><div><div><div>35</div></div><div><span>      </span><span>const</span><span> </span><span>piAgentRes</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>fetch</span><span>(</span><span>'http://localhost:4001/api/chat'</span><span>, {</span></div></div><div><div><div>36</div></div><div><span>        </span><span>method</span><span>:</span><span> </span><span>'POST'</span><span>,</span></div></div><div><div><div>37</div></div><div><span>        </span><span>headers</span><span>:</span><span> { </span><span>'Content-Type'</span><span>:</span><span> </span><span>'application/json'</span><span> },</span></div></div><div><div><div>38</div></div><div><span>        </span><span>body</span><span>:</span><span> </span><span>JSON</span><span>.</span><span>stringify</span><span>({</span></div></div><div><div><div>39</div></div><div><span>          </span><span>input</span><span>:</span><span><span> </span><span>instruction</span><span>,</span></span></div></div><div><div><div>40</div></div><div><span><span>          </span></span><span>llmConfig</span><span>,</span></div></div><div><div><div>41</div></div><div><span><span>          </span></span><span>sandboxId</span><span>,</span></div></div><div><div><div>42</div></div><div><span><span>          </span></span><span>sandboxApiKey</span></div></div><div><div><div>43</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>44</div></div><div><span><span>      </span></span><span>});</span></div></div><div><div><div>45</div></div><div>
</div></div><div><div><div>46</div></div><div><span>      </span><span>if</span><span> (</span><span>!</span><span><span>piAgentRes</span><span>.</span></span><span>ok</span><span>) {</span></div></div><div><div><div>47</div></div><div><span>        </span><span>const</span><span> </span><span>errData</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>piAgentRes</span><span>.</span></span><span>json</span><span>().</span><span>catch</span><span>(() </span><span>=&gt;</span><span> ({}));</span></div></div><div><div><div>48</div></div><div><span>        </span><span>throw</span><span> </span><span>new</span><span> </span><span>Error</span><span><span>(</span><span>errData</span><span>.</span></span><span>error</span><span> </span><span>||</span><span> </span><span>`HTTP error! status: </span><span>${</span><span>piAgentRes</span><span>.</span><span>status</span><span>}</span><span>`</span><span>);</span></div></div><div><div><div>49</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>50</div></div><div>
</div></div><div><div><div>51</div></div><div><span>      </span><span>// Stream the response from pi-agent-service to the frontend</span></div></div><div><div><div>52</div></div><div><span>      </span><span>const</span><span> </span><span>reader</span><span> </span><span>=</span><span><span> </span><span>piAgentRes</span><span>.</span></span><span>body</span><span>?.</span><span>getReader</span><span>();</span></div></div><div><div><div>53</div></div><div><span>      </span><span>if</span><span> (</span><span>!</span><span><span>reader</span><span>) </span></span><span>throw</span><span> </span><span>new</span><span> </span><span>Error</span><span>(</span><span>"No response body reader available"</span><span>);</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span>      </span><span>const</span><span> </span><span>decoder</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>TextDecoder</span><span>(</span><span>'utf-8'</span><span>);</span></div></div><div><div><div>56</div></div><div><span>      </span><span>let</span><span><span> </span><span>done</span><span> </span></span><span>=</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>57</div></div><div><span>      </span><span>let</span><span><span> </span><span>finalResponse</span><span> </span></span><span>=</span><span> </span><span>''</span><span>;</span></div></div><div><div><div>58</div></div><div>
</div></div><div><div><div>59</div></div><div><span>      </span><span>while</span><span> (</span><span>!</span><span><span>done</span><span>) {</span></span></div></div><div><div><div>60</div></div><div><span>        </span><span>const</span><span> { </span><span>value</span><span>, </span><span>done</span><span>: </span><span>readerDone</span><span> } </span><span>=</span><span> </span><span>await</span><span><span> </span><span>reader</span><span>.</span></span><span>read</span><span>();</span></div></div><div><div><div>61</div></div><div><span>        </span><span>if</span><span><span> (</span><span>readerDone</span><span>) {</span></span></div></div><div><div><div>62</div></div><div><span><span>          </span></span><span>done</span><span> </span><span>=</span><span> </span><span>true</span><span>;</span></div></div><div><div><div>63</div></div><div><span>          </span><span>break</span><span>;</span></div></div><div><div><div>64</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>65</div></div><div>
</div></div><div><div><div>66</div></div><div><span>        </span><span>const</span><span> </span><span>chunk</span><span> </span><span>=</span><span><span> </span><span>decoder</span><span>.</span></span><span>decode</span><span><span>(</span><span>value</span><span>, { </span></span><span>stream</span><span>:</span><span> </span><span>true</span><span> });</span></div></div><div><div><div>67</div></div><div><span>        </span><span>const</span><span> </span><span>lines</span><span> </span><span>=</span><span><span> </span><span>chunk</span><span>.</span></span><span>split</span><span>(</span><span>'</span><span>\n</span><span>'</span><span>);</span></div></div><div><div><div>68</div></div><div>
</div></div><div><div><div>69</div></div><div><span>        </span><span>for</span><span> (</span><span>let</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</span><span><span>; </span><span>i</span><span> </span></span><span>&lt;</span><span><span> </span><span>lines</span><span>.</span></span><span>length</span><span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>70</div></div><div><span>          </span><span>const</span><span> </span><span>line</span><span> </span><span>=</span><span><span> </span><span>lines</span><span>[</span><span>i</span><span>];</span></span></div></div><div><div><div>71</div></div><div><span>          </span><span>if</span><span><span> (</span><span>line</span><span>.</span></span><span>startsWith</span><span>(</span><span>'event: '</span><span>)) {</span></div></div><div><div><div>72</div></div><div><span>            </span><span>const</span><span> </span><span>eventName</span><span> </span><span>=</span><span><span> </span><span>line</span><span>.</span></span><span>substring</span><span>(</span><span>7</span><span>).</span><span>trim</span><span>();</span></div></div><div><div><div>73</div></div><div><span>            </span><span>const</span><span> </span><span>dataLine</span><span> </span><span>=</span><span><span> </span><span>lines</span><span>[</span><span>i</span><span> </span></span><span>+</span><span> </span><span>1</span><span>];</span></div></div><div><div><div>74</div></div><div><span>            </span><span>if</span><span><span> (</span><span>dataLine</span><span> </span></span><span>&amp;&amp;</span><span><span> </span><span>dataLine</span><span>.</span></span><span>startsWith</span><span>(</span><span>'data: '</span><span>)) {</span></div></div><div><div><div>75</div></div><div><span>              </span><span>const</span><span> </span><span>dataStr</span><span> </span><span>=</span><span><span> </span><span>dataLine</span><span>.</span></span><span>substring</span><span>(</span><span>6</span><span>).</span><span>trim</span><span>();</span></div></div><div><div><div>76</div></div><div><span>              </span><span>try</span><span> {</span></div></div><div><div><div>77</div></div><div><span>                </span><span>const</span><span> </span><span>dataObj</span><span> </span><span>=</span><span> </span><span>JSON</span><span>.</span><span>parse</span><span><span>(</span><span>dataStr</span><span>);</span></span></div></div><div><div><div>78</div></div><div>
</div></div><div><div><div>79</div></div><div><span>                </span><span>if</span><span><span> (</span><span>eventName</span><span> </span></span><span>===</span><span> </span><span>'status'</span><span>) {</span></div></div><div><div><div>80</div></div><div><span>                  </span><span>sendEvent</span><span>(</span><span>'status'</span><span>, { </span><span>message</span><span>:</span><span><span> </span><span>dataObj</span><span>.</span></span><span>message</span><span> });</span></div></div><div><div><div>81</div></div><div><span><span>                </span></span><span>} </span><span>else</span><span> </span><span>if</span><span><span> (</span><span>eventName</span><span> </span></span><span>===</span><span> </span><span>'action'</span><span>) {</span></div></div><div><div><div>82</div></div><div><span>                  </span><span>sendEvent</span><span>(</span><span>'action'</span><span>, { </span><span>description</span><span>:</span><span><span> </span><span>dataObj</span><span>.</span></span><span>description</span><span> });</span></div></div><div><div><div>83</div></div><div><span><span>                </span></span><span>} </span><span>else</span><span> </span><span>if</span><span><span> (</span><span>eventName</span><span> </span></span><span>===</span><span> </span><span>'done'</span><span>) {</span></div></div><div><div><div>84</div></div><div><span><span>                  </span></span><span>finalResponse</span><span> </span><span>=</span><span><span> </span><span>dataObj</span><span>.</span></span><span>response</span><span>;</span></div></div><div><div><div>85</div></div><div><span>                  </span><span>sendEvent</span><span>(</span><span>'status'</span><span>, { </span><span>message</span><span>:</span><span> </span><span>`pi-agent 任务执行完成: </span><span>${</span><span>finalResponse</span><span>.</span><span>substring</span><span>(</span><span>0</span><span>,</span><span> </span><span>50</span><span>)</span><span>}</span><span>...`</span><span> });</span></div></div><div><div><div>86</div></div><div><span>                  </span><span>sendEvent</span><span>(</span><span>'done'</span><span>, { </span><span>message</span><span>:</span><span> </span><span>'Task completed'</span><span> });</span></div></div><div><div><div>87</div></div><div><span><span>                  </span></span><span>res</span><span>.</span><span>end</span><span>();</span></div></div><div><div><div>88</div></div><div><span>                  </span><span>return</span><span>; </span><span>// End the request cleanly</span></div></div><div><div><div>89</div></div><div><span><span>                </span></span><span>} </span><span>else</span><span> </span><span>if</span><span><span> (</span><span>eventName</span><span> </span></span><span>===</span><span> </span><span>'error'</span><span>) {</span></div></div><div><div><div>90</div></div><div><span><span>                  </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>"pi-agent runtime error:"</span><span><span>, </span><span>dataObj</span><span>);</span></span></div></div><div><div><div>91</div></div><div><span>                  </span><span>sendEvent</span><span>(</span><span>'error'</span><span>, { </span><span>message</span><span>:</span><span> </span><span>`pi-agent 运行时异常: </span><span>${</span><span>dataObj</span><span>.</span><span>error</span><span>}</span><span>`</span><span> });</span></div></div><div><div><div>92</div></div><div><span><span>                  </span></span><span>res</span><span>.</span><span>end</span><span>();</span></div></div><div><div><div>93</div></div><div><span>                  </span><span>return</span><span>;</span></div></div><div><div><div>94</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>95</div></div><div><span><span>              </span></span><span>} </span><span>catch</span><span><span> (</span><span>e</span><span>) {</span></span></div></div><div><div><div>96</div></div><div><span><span>                </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>"Failed to parse SSE data:"</span><span><span>, </span><span>dataStr</span><span>);</span></span></div></div><div><div><div>97</div></div><div><span><span>              </span></span><span>}</span></div></div><div><div><div>98</div></div><div><span><span>              </span></span><span>i</span><span>++</span><span>; </span><span>// skip data line</span></div></div><div><div><div>99</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>100</div></div><div><span><span>          </span></span><span>}</span></div></div><div><div><div>101</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>102</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>103</div></div><div>
</div></div><div><div><div>104</div></div><div><span><span>    </span></span><span>} </span><span>catch</span><span><span> (</span><span>e</span></span><span>:</span><span> </span><span>any</span><span>) {</span></div></div><div><div><div>105</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>"Failed to connect to pi-agent-service:"</span><span><span>, </span><span>e</span><span>.</span></span><span>message</span><span>);</span></div></div><div><div><div>106</div></div><div><span>      </span><span>sendEvent</span><span>(</span><span>'error'</span><span>, { </span><span>message</span><span>:</span><span> </span><span>`pi-agent 服务调度失败: </span><span>${</span><span>e</span><span>.</span><span>message</span><span>}</span><span>`</span><span> });</span></div></div><div><div><div>107</div></div><div><span><span>      </span></span><span>res</span><span>.</span><span>end</span><span>();</span></div></div><div><div><div>108</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>109</div></div><div><span><span>  </span></span><span>} </span><span>catch</span><span><span> (</span><span>error</span></span><span>:</span><span> </span><span>any</span><span>) {</span></div></div><div><div><div>110</div></div><div><span>    </span><span>sendEvent</span><span>(</span><span>'error'</span><span>, { </span><span>message</span><span>:</span><span><span> </span><span>error</span><span>.</span></span><span>message</span><span> });</span></div></div><div><div><div>111</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>end</span><span>();</span></div></div><div><div><div>112</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>113</div></div><div><span>})</span></div></div><div><div><div>114</div></div><div>
</div></div><div><div><div>115</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>router</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h3>前端 UI 增加技能管理<a href="#前端-ui-增加技能管理"><span>#</span></a></h3><p>地址：<code>/workspace/src/pages/Home.tsx</code></p><section><h4>1. 实现 <strong>/api/skills</strong> 接口（支持 multer 文件上传）<a href="#1-实现-apiskills-接口支持-multer-文件上传"><span>#</span></a></h4><p>专门用于接收并保存遵循 pi-agent 规范的 <code>SKILL.md</code> 文件至 .pi/skills/ 目录下</p><div><div><div></div><div>Tip</div></div><div><p>目前只支持单文件，因为社区有大量方案，所以我只实现基础的，后续在约四期会完善。</p></div></div><div><div><div><figure><figcaption><span>skills.ts</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>/**</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>* 2. POST /api/skills</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>* Upload a new skill (SKILL.md file)</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>5</div></div><div><span><span>app</span><span>.</span></span><span>post</span><span>(</span><span>'/api/skills'</span><span><span>, </span><span>upload</span><span>.</span></span><span>single</span><span>(</span><span>'skillFile'</span><span><span>), (</span><span>req</span><span>, </span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>6</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span><span>req</span><span>.</span></span><span>file</span><span>) {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>return</span><span><span> </span><span>res</span><span>.</span></span><span>status</span><span>(</span><span>400</span><span>).</span><span>json</span><span>({ </span><span>error</span><span>:</span><span> </span><span>'Skill file (SKILL.md) is required'</span><span> });</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>res</span><span>.</span><span>json</span><span>({</span></div></div><div><div><div>11</div></div><div><span>    </span><span>success</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>12</div></div><div><span>    </span><span>message</span><span>:</span><span> </span><span>`Skill uploaded successfully to </span><span>${</span><span>req</span><span>.</span><span>file</span><span>.</span><span>path</span><span>}</span><span>`</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>14</div></div><div><span>});</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>/**</span></div></div><div><div><div>17</div></div><div><span><span> </span></span><span>* 3. GET /api/skills</span></div></div><div><div><div>18</div></div><div><span><span> </span></span><span>* List available uploaded skills</span></div></div><div><div><div>19</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>20</div></div><div><span><span>app</span><span>.</span></span><span>get</span><span>(</span><span>'/api/skills'</span><span>, </span><span>async</span><span><span> (</span><span>req</span><span>, </span><span>res</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>21</div></div><div><span>  </span><span>try</span><span> {</span></div></div><div><div><div>22</div></div><div><span>    </span><span>const</span><span> </span><span>loader</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>DefaultResourceLoader</span><span>({</span></div></div><div><div><div>23</div></div><div><span>      </span><span>cwd</span><span>:</span><span><span> </span><span>__dirname</span><span>,</span></span></div></div><div><div><div>24</div></div><div><span>      </span><span>agentDir</span><span>:</span><span> </span><span>getAgentDir</span><span>(),</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>26</div></div><div><span>    </span><span>await</span><span><span> </span><span>loader</span><span>.</span></span><span>reload</span><span>();</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>    </span><span>const</span><span> { </span><span>skills</span><span> } </span><span>=</span><span><span> </span><span>loader</span><span>.</span></span><span>getSkills</span><span>();</span></div></div><div><div><div>29</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>json</span><span>({</span></div></div><div><div><div>30</div></div><div><span>      </span><span>success</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>31</div></div><div><span>      </span><span>skills</span><span>:</span><span><span> </span><span>skills</span><span>.</span></span><span>map</span><span><span>(</span><span>s</span><span> </span></span><span>=&gt;</span><span> ({ </span><span>name</span><span>:</span><span><span> </span><span>s</span><span>.</span></span><span>name</span><span>, </span><span>description</span><span>:</span><span><span> </span><span>s</span><span>.</span></span><span>description</span><span> }))</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>33</div></div><div><span><span>  </span></span><span>} </span><span>catch</span><span><span> (</span><span>error</span></span><span>:</span><span> </span><span>any</span><span>) {</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>res</span><span>.</span><span>status</span><span>(</span><span>500</span><span>).</span><span>json</span><span>({ </span><span>error</span><span>:</span><span><span> </span><span>error</span><span>.</span></span><span>message</span><span> });</span></div></div><div><div><div>35</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>36</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>2. 前端 UI 增加技能管理模块<a href="#2-前端-ui-增加技能管理模块"><span>#</span></a></h4><p>在 设置 页签底部新增了 <code>"pi-agent 技能管理 (Skills)"</code> 模块。
直接点击“上传技能文件 (SKILL.md)”，系统会自动将其存入 <code>pi-agent-service</code> 的技能目录中。
界面会自动拉取并展示当前 <code>pi-agent</code> 成功加载的所有扩展技能及其描述。</p><p></p><figure><img alt="pi-4" loading="lazy" width="759" height="661" src="/_astro/pi-4.BMJVlU6s_QRJTN.webp" /><figcaption>pi-4</figcaption></figure><p></p></section></section>
<section><h3>下一步<a href="#下一步"><span>#</span></a></h3><p>本期我们基本上把架子搭起来了，而现在距离真正能使用还有一点距离</p><p>下一期将进一步完成以下目标。</p><ol>
<li>实现mcp协议和联通mcp市场</li>
<li>预装软件到e2b沙盒</li>
</ol><p>在大概到四期的时候，我将完善修复bug之后并开源。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E4%B8%80%E5%B0%8F%E6%97%B6%E5%9F%BA%E4%BA%8Ee2b%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E5%AE%8C%E6%95%B4%E7%9A%84%E4%BA%91%E7%AB%AFagent%E5%81%9A%E4%B8%80%E4%B8%AA%E4%BA%91%E7%AB%AF%E6%B2%99%E7%9B%92%E5%8A%A9%E6%89%8B%E4%BA%91%E7%AB%AFagent-%E4%B8%80-/</id>
      <title type="text">一小时基于E2B实现一个完整的云端Agent，做一个云端沙盒助手（云端agent系列 一）</title>
      <published>2026-04-17T00:00:00.000Z</published>
      <updated>2026-04-17T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E4%B8%80%E5%B0%8F%E6%97%B6%E5%9F%BA%E4%BA%8Ee2b%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E5%AE%8C%E6%95%B4%E7%9A%84%E4%BA%91%E7%AB%AFagent%E5%81%9A%E4%B8%80%E4%B8%AA%E4%BA%91%E7%AB%AF%E6%B2%99%E7%9B%92%E5%8A%A9%E6%89%8B%E4%BA%91%E7%AB%AFagent-%E4%B8%80-/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>为什么<a href="#为什么"><span>#</span></a></h2><p>最近solo by ai的概念日趋火热，trae的大获成功，以及各个厂商都在推出自己的沙盒服务。是时候设计一个简单的系统来试试水了。</p></section>
<section><h2>简单架构<a href="#简单架构"><span>#</span></a></h2><p>大模型作为大脑下达命令，我会写一个程序用于大模型调用agent，命名为 center-header
agent执行的上下文将由center协调，center会根据大模型的指令，调用不同的agent执行不同的任务。这分两种，分别是云端的cloud-body和本地的local-body（客户端）。</p></section>
<section><h2>开整<a href="#开整"><span>#</span></a></h2><p>这一期我们只实现云端的部分，首先我需要实现这部分</p><ol>
<li>e2b沙盒连接和使用</li>
<li>云端大模型和后端</li>
<li>前端代码展示及交互</li>
</ol><section><h3>一. e2b沙盒连接和使用<a href="#一-e2b沙盒连接和使用"><span>#</span></a></h3><div><div><div></div><div>Tip</div></div><div><p>如果后续想完全私有化的话，可以参考开源e2b方案的实现，但是这是后面的事情了</p></div></div><ol>
<li>配置使用E2B沙盒
首先登录到e2b官网<a href="https://e2b.dev/" target="_blank">https://e2b.dev/</a>
使用谷歌/github账号登录后，会看到一个沙盒列表，每个沙盒都有一个唯一的id。</li>
</ol><p></p><figure><img alt="e2b-1" loading="lazy" width="1652" height="923" src="/_astro/e2b-1.Da2bWUqY_9K4J1.webp" /><figcaption>e2b-1</figcaption></figure><p></p><p>点击团队下的账户设置可以看到我们的访问令牌</p><p></p><figure><img alt="e2b-2" loading="lazy" width="1637" height="776" src="/_astro/e2b-2.DAywrmYs_15QUO1.webp" /><figcaption>e2b-2</figcaption></figure><p></p><ol>
<li>编写云端后端代码连接沙盒
首先我们需要看下E2B文档<a href="https://e2b.dev/docs/quickstart" target="_blank">https://e2b.dev/docs/quickstart</a>
然后根据文档的介绍，我们可以看到，连接沙盒的代码非常简单，只需要几行代码即可。</li>
</ol><div><div><div></div><div>Tip</div></div><div><p>这部分可以自由实现，我是使用Bun、TypeScript、Node来实现的。</p></div></div><p>使用TypeScript实现</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 安装依赖</span></div></div><div><div><div>2</div></div><div><span><span>npm</span><span> </span><span>i</span><span> @</span><span>e2b</span></span><span>/</span><span>code</span><span>-</span><span><span>interpreter</span><span> </span><span>dotenv</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>沙盒启动</p><div><figure><figcaption><span>index.ts</span></figcaption><pre><code><div><div><div>1</div></div><div><span>// 启动沙盒</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>'dotenv/config'</span></div></div><div><div><div>3</div></div><div><span>import</span><span> { </span><span>Sandbox</span><span> } </span><span>from</span><span> </span><span>'@e2b/code-interpreter'</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>const</span><span> </span><span>sbx</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>Sandbox</span><span>.</span></span><span>create</span><span>() </span><span>/</span></div></div><div><div><div>6</div></div><div><span>const</span><span> </span><span>execution</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>sbx</span><span>.</span></span><span>runCode</span><span>(</span><span>'print("hello world")'</span><span>)</span></div></div><div><div><div>7</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>execution</span><span>.</span></span><span>logs</span><span>)</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>const</span><span> </span><span>files</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>sbx</span><span>.</span></span><span>files</span><span>.</span><span>list</span><span>(</span><span>'/'</span><span>)</span></div></div><div><div><div>10</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>files</span><span>)</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>执行代码</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 执行代码</span></div></div><div><div><div>2</div></div><div><span>// npx tsx ./index.ts</span></div></div><div><div><div>3</div></div><div><span><span>bun</span><span> </span><span>run</span><span> .</span></span><span>/</span><span><span>index</span><span>.</span></span><span>ts</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>二. 云端大模型\后端\前端 详细设计<a href="#二-云端大模型后端前端-详细设计"><span>#</span></a></h3></section></section>
<section><h2>核心功能<a href="#核心功能"><span>#</span></a></h2><section><h3>1 功能模块<a href="#1-功能模块"><span>#</span></a></h3><ol>
<li><strong>主控制台 (Console)</strong>：包含左右分栏布局。左侧为VNC桌面流媒体显示区域，右侧为Agent对话控制台。</li>
<li><strong>Agent交互模块</strong>：用户输入指令，后台大模型结合屏幕截图进行推理，并调用E2B Desktop SDK执行相应的鼠标/键盘操作。</li>
</ol></section><section><h3>2. 核心流程<a href="#2-核心流程"><span>#</span></a></h3><p>用户在聊天框输入指令 -&gt; 后端创建/获取E2B Desktop沙盒 -&gt; 获取沙盒屏幕截图并发送给大模型 (如Claude/OpenAI) -&gt; 大模型返回具体的UI交互动作 (坐标点击、键盘输入等) -&gt; 后端通过E2B SDK执行动作 -&gt; 循环直至任务完成。</p><div><div><div><div><span></span></div><div><span></span></div><div><span><p>否</p></span></div><div><span></span></div><div><span><p>是</p></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span><p>是</p></span></div><div><span><p>否</p></span></div><div><span></span></div><div><span><p>用户输入指令</p></span></div><div><span><p>前端发送请求到后端 API</p></span></div><div><span><p>沙盒是否已创建?</p></span></div><div><span><p>调用 @e2b/desktop 创建 Sandbox</p></span></div><div><span><p>启动 VNC Stream 并返回 Stream URL 给前端</p></span></div><div><span><p>获取当前沙盒状态</p></span></div><div><span><p>循环: Agent获取屏幕截图</p></span></div><div><span><p>大模型(LLM)分析截图并决策动作</p></span></div><div><span><p>任务是否完成?</p></span></div><div><span><p>返回最终结果给用户</p></span></div><div><span><p>执行具体动作(点击/输入等)</p></span></div>
</div><div><div><span></span></div><div><span></span></div><div><span><p>否</p></span></div><div><span></span></div><div><span><p>是</p></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span></span></div><div><span><p>是</p></span></div><div><span><p>否</p></span></div><div><span></span></div><div><span><p>用户输入指令</p></span></div><div><span><p>前端发送请求到后端 API</p></span></div><div><span><p>沙盒是否已创建?</p></span></div><div><span><p>调用 @e2b/desktop 创建 Sandbox</p></span></div><div><span><p>启动 VNC Stream 并返回 Stream URL 给前端</p></span></div><div><span><p>获取当前沙盒状态</p></span></div><div><span><p>循环: Agent获取屏幕截图</p></span></div><div><span><p>大模型(LLM)分析截图并决策动作</p></span></div><div><span><p>任务是否完成?</p></span></div><div><span><p>返回最终结果给用户</p></span></div><div><span><p>执行具体动作(点击/输入等)</p></span></div>
</div></div></div></section><section><h3>实现及优化<a href="#实现及优化"><span>#</span></a></h3><p>效果展示
</p><figure><img alt="e2b-3" loading="lazy" width="873" height="819" src="/_astro/e2b-3.BwXTNSZ2_2nzHEx.webp" /><figcaption>e2b-3</figcaption></figure>
<figure><img alt="e2b-4" loading="lazy" width="1636" height="760" src="/_astro/e2b-4.DwMhqKNv_Z1i7aAr.webp" /><figcaption>e2b-4</figcaption></figure><p></p><p>简单的实现大概就是这些。下一期我将带你实现指令操作沙盒，agent设计及协调，最终实现类似trae的solo功能。敬请期待</p><p><a href="/posts/%E4%B8%80%E5%B0%8F%E6%97%B6%E5%9F%BA%E4%BA%8Epi-mono%E5%AE%9E%E7%8E%B0%E7%B1%BB%E4%BC%BCopenclaw%E7%9A%84agent%E7%B3%BB%E7%BB%9F%E4%BA%91%E7%AB%AFagent%E7%B3%BB%E5%88%97-%E4%BA%8C/">下一期</a></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E6%A8%A1%E5%9E%8B%E5%90%9E%E5%99%AC%E4%BA%BA%E7%B1%BB%E7%A4%BE%E5%8C%BA%E4%B8%BB%E6%B5%81%E6%80%9D%E6%83%B3%E7%9A%84%E8%BD%AC%E5%8F%98%E4%BB%8Eagent%E5%88%B0harness%E6%AD%BB%E8%B7%AF%E4%B8%80%E6%9D%A1%E7%9A%84%E6%8E%A7%E5%88%B6%E7%96%AF%E7%8B%82%E7%9A%84%E6%8E%A2%E7%B4%A2/</id>
      <title type="text">模型吞噬人类，社区主流思想的转变，从agent到harness，放开模型的手脚，疯狂的探索</title>
      <published>2026-04-01T00:00:00.000Z</published>
      <updated>2026-04-01T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E6%A8%A1%E5%9E%8B%E5%90%9E%E5%99%AC%E4%BA%BA%E7%B1%BB%E7%A4%BE%E5%8C%BA%E4%B8%BB%E6%B5%81%E6%80%9D%E6%83%B3%E7%9A%84%E8%BD%AC%E5%8F%98%E4%BB%8Eagent%E5%88%B0harness%E6%AD%BB%E8%B7%AF%E4%B8%80%E6%9D%A1%E7%9A%84%E6%8E%A7%E5%88%B6%E7%96%AF%E7%8B%82%E7%9A%84%E6%8E%A2%E7%B4%A2/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<p>今天刚好是4月1号愚人节，距离<code>Claude Code</code> 发布14个月，距离它客户端代码的泄露24小时。</p>
<p>最近AI主流思想已经逐渐确认一些事情，受此大事件的影响我不由自主的思考<code>AI</code> <code>agent``harness</code>的未来。</p>
<div><div><div></div><div>Tip</div></div><div><p>足足51万行代码，通过完整严格的<em><strong>模块划分</strong></em>、<em><strong>通信统一</strong></em>，实现了超速迭代和高度的可扩展性。</p><p>或许最精彩的作品只出现在毁灭的前夜</p></div></div>
<p>这篇文章我将针对最近的一些思考进行整理和反思。</p>
<p>我们首先要搞懂一个事情什么是模型,什么是agent，长时间内定义模糊，而现在我们终于可以这样说。</p>
<p><em><strong>模型就是 Agent</strong></em></p>
<section><h2>模型吞噬人类<a href="#模型吞噬人类"><span>#</span></a></h2><p>回到现在的视角，如果让我们给<code>Agent</code>（智能体）一个定义或者样例。我觉得可以理解为所有可以进行思考规划的个体。花草树虫鸟鱼，都是智能体。而人类则是一个最复杂的智能体。</p><p><em><strong>所以智能体的目标本是实现和人类一样的思考模式。</strong></em></p><blockquote><p>我们现在认识到了，受限于人类的思考模型，这是狭隘的。</p></blockquote><p>在<b>一年以外</b>的时候提示词工程是热门关键词。大模型对提示词的依赖性，导致了模型的思考模式被限制在了人类的思考模式。</p><p>在<b>一年以内</b>，dify/coze/xxflows 等项目都是通过固定编排以及人工硬编码来实现智能体的调度和协调。而这种过度工程化的、脆弱的过程式规则流水线，这只是一个有着宏大妄想的 shell 脚本，并不符合AGI的未来。</p><p>而<b>现在</b>cli工具的出现与发展，逐渐划分清楚了模型能力的边界与Agent到底是什么，claude code是其中的佼佼者。Claude Code 是我们所见过的最优雅、最完整的 agent harness 实现。不是因为某个巧妙的技巧，而是因为它 没做 的事：它没有试图成为 agent 本身。它没有强加僵化的工作流。它没有用精心设计的决策树去替模型做判断。它给模型提供了工具、知识、上下文管理和权限边界。</p><div><div><div></div><div>Tip</div></div><div><p>不要听信媒体/资本的鼓吹某项功能，从开始炒作提示词工程，到大吹流式编排再到吹智能体再到现在的龙虾。本质是急功近利的外化表现。这是错误的思维导致的。</p><p>我们应该具备<code>动态思维</code>与<code>辩证思考</code>的能力。</p><p>首先我们要有一个整体的概念。模型的动态发展与能力边界。
模型的能力在我们当前的节点的话是个什么样子
他的能力边界，以及突破能力边界的技术突破
他的极限受限于什么因素</p><p>理解和思考完这些问题，我们便能明白，模型的发展是一个动态过程，而现在随着他的能力拓展，他的边界也逐渐清晰</p></div></div><p>如果用一句话总结
即 <em><strong>模型Agent负责思考，harness负责服务于它</strong></em>。</p></section>
<section><h2>社区主流思想的转变<a href="#社区主流思想的转变"><span>#</span></a></h2><p>harness的出现</p><p>随着以claude code为先驱的cli工具的推进，我们逐渐理解了模型与工具。于是目前我们做出了如下的划分。</p><p>模型=agent，是可以思考的智能体</p><p>工具=harness，为agent提供工具，让agent接触世界</p><p>与之相对应的是工作会分为两种</p><ol>
<li>
<p>训练模型。 通过强化学习、微调、RLHF 或其他基于梯度的方法调整权重。收集任务过程数据 — 真实领域中感知、推理、行动的实际序列 — 用它们来塑造模型的行为。这是 DeepMind、OpenAI、腾讯 AI Lab、Anthropic 在做的事。这是最本义的 Agent 开发。</p>
</li>
<li>
<p>构建 Harness。 编写代码，为模型提供一个可操作的环境。这是我们大多数人在做的事。</p>
</li>
</ol><p>下图为<code>Claude Code</code>的harness。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>Claude</span><span> </span><span>Code</span><span> </span><span>=</span><span> </span><span>一个</span><span> </span><span>agent</span><span> </span><span>loop</span></div></div><div><div><div>2</div></div><div><span>            </span><span>+</span><span> </span><span>工具</span><span> (bash, </span><span>read,</span><span> </span><span>write,</span><span> </span><span>edit,</span><span> </span><span>glob,</span><span> </span><span>grep,</span><span> </span><span>browser...</span><span>)</span></div></div><div><div><div>3</div></div><div><span>            </span><span>+</span><span> </span><span>按需</span><span> </span><span>skill</span><span> </span><span>加载</span></div></div><div><div><div>4</div></div><div><span>            </span><span>+</span><span> </span><span>上下文压缩</span></div></div><div><div><div>5</div></div><div><span>            </span><span>+</span><span> </span><span>子</span><span> </span><span>agent</span><span> </span><span>派生</span></div></div><div><div><div>6</div></div><div><span>            </span><span>+</span><span> </span><span>带依赖图的任务系统</span></div></div><div><div><div>7</div></div><div><span>            </span><span>+</span><span> </span><span>异步邮箱的团队协调</span></div></div><div><div><div>8</div></div><div><span>            </span><span>+</span><span> </span><span>worktree</span><span> </span><span>隔离的并行执行</span></div></div><div><div><div>9</div></div><div><span>            </span><span>+</span><span> </span><span>权限治理</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>放开模型的手脚，疯狂的探索<a href="#放开模型的手脚疯狂的探索"><span>#</span></a></h2><p>模型会不会吞噬Harness</p><p>70%不会，因为Harness的成本移植到模型内部会太大，为了这部分收益会极大的扩展模型复杂度。</p><p>另外从工程的角度，将Agent和harness分离，可以极大的扩展模型的可扩展性。</p><p>从人类的角度来说，agent相当于人类，harness相当于工具，也更符合人类的思考模式。</p><p>从安全角度来说，模型限制在没手没脚的状态更令人安心</p><p>30%会，因为当所有模型不计成本的增加体积，会极大的扩展模型的现实能力。</p><p>模型将成为现实的底座。他可以自己实现我们现在实现的一切工具。</p><div><div><div></div><div>Tip</div></div><div><p>如果模型可以自己实现工具，那么模型就可以自己实现一切。
或许这个时候，我们就可以叫他AGI了，这也是我认为人类的思考模型可能是狭隘的原因。</p></div></div></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E4%BD%86%E6%98%AF%E9%82%A3%E4%BA%9B%E6%B2%A1%E6%9C%89%E5%A4%A9%E8%B5%8B%E7%9A%84%E4%BA%BA%E5%91%A2/</id>
      <title type="text">但是那些没有天赋的人呢?他们的人生从一开始就浪费了吗?</title>
      <published>2026-03-13T00:00:00.000Z</published>
      <updated>2026-03-13T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E4%BD%86%E6%98%AF%E9%82%A3%E4%BA%9B%E6%B2%A1%E6%9C%89%E5%A4%A9%E8%B5%8B%E7%9A%84%E4%BA%BA%E5%91%A2/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<div><b>游戏《石河伦吾的朋友们 The friends of Ringo Ishikawa》</b></div>
<div>
    <div>
        以下内容来源于游戏内容整理
    </div>
</div>
<div>
    <div>
        <p>
            “社会只是个逼迫所有人变得一样的大工厂，而真正的知识却在图书馆里花几百元就能买到。而另一些则花了一大笔钱才获得了同样的知识，还要在老师面前奴颜婢膝。为了什么呢，为了在将来成为什么巨大企业的薪奴吗?”   
        </p><p>那么人生的意义又是什么呢？</p>
        <p></p>
    </div></div>
 <p>有的时候，你在某些方面能比别人更胜一筹，于是大家都对你赞誉有加，你自然也会觉得很开心。</p>
        <p>可是不知什么时候开始，逐渐逐渐地，这份优秀似乎就成了你理所应当、必须去完成的义务。而假如出于什么原因你没能达到大家的期望，那他们一转眼就不把你当回事了。</p>
    
<p>我从来没有在某个方面比别人更胜一筹过。这就是我想说的。顺着我刚说的理论，谁也不会在乎你的人生过得怎么样。不管从什么理论来说，都不会有人在乎你的人生。</p>
<p>我只是觉得，这个世界不应该是需要拼命争取才会有人来在乎你的。</p>
<p><b>因为人生不应该是什么比赛。我觉得一个人只要出生在这个世界上，就应该有所价值，应该有人会在乎才对。</b></p>
<p>有人说毕竟不是每个人都能成为冠军。也许，能够一心一意地把一件事贯彻始终本身就是一种不能后天学习到的特殊品质。如果没有这样的天分，那你终究也只能是个平凡的人，哪怕多多少少能分出一点优势。</p>
<p>人们说人生最大的悲剧之一就是<b>天赋被白白浪费掉。</b></p>
<p><b>但是那些没有天赋的人呢？他们的人生从一开始就浪费了吗？</b></p>
<p></p><figure><img alt="游戏截图" loading="lazy" width="1080" height="453" src="/_astro/shgw-2.B7jE_jjw_12fSUl.webp" /><figcaption>游戏截图</figcaption></figure><p></p>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/ai%E8%BF%9B%E6%AD%A5%E6%B5%AA%E6%BD%AE%E4%B8%AD%E7%9A%84%E6%88%91%E4%BB%AC%E4%BB%8Enotbyai%E8%B4%B4%E7%BA%B8%E5%BC%95%E5%8F%91%E7%9A%84%E6%80%9D%E8%80%83/</id>
      <title type="text">AI时代，你落后了吗，纠结良久最终我还是没有付费 not-by-ai 贴纸</title>
      <published>2026-03-11T00:00:00.000Z</published>
      <updated>2026-03-11T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/ai%E8%BF%9B%E6%AD%A5%E6%B5%AA%E6%BD%AE%E4%B8%AD%E7%9A%84%E6%88%91%E4%BB%AC%E4%BB%8Enotbyai%E8%B4%B4%E7%BA%B8%E5%BC%95%E5%8F%91%E7%9A%84%E6%80%9D%E8%80%83/"/>
      <summary type="text">最近抖音上火了很多“父辈错过改革开放”，我们就要错过了AI大潮，颇有一股童年射出的子弹射中了自己眉心的感觉。</summary>
      <content type="html"><![CDATA[<p>最近抖音上火了很多<code>父辈错过改革开放</code>，我们就要错过了AI大潮，颇有一股童年射出的子弹射中了自己眉心的感觉。</p>
<p>且不说这里面有一些贩卖<code>AI焦虑</code>的恶意，身边的朋友纷纷没由来生出一股豪气，恨不得24小时去使用和学习AI，费劲吧啦的用了很久发现了，其实强的不是我们，而是AI。之后便是开始了自暴自弃，与其进步不如等现成的，最好再搞一点<code>反AI</code>的东西狠狠的吸睛。</p>
<p>经历了这一遭之后，今天想讲讲自己的反思和重构，凝聚你我的信念。</p>
<section><h2>一、AI时代，你落后了吗<a href="#一ai时代你落后了吗"><span>#</span></a></h2><p>将我们的视角拉到整个人类的视角上，将电力，硬件，人才，的整合可以视为算力，将时间视为第二个变量，算力和时间的乘积，这就是AI的燃料。从这个角度思考，现在整个地球热钱转向AI领域可能是最有利于未来的选择。</p><p>虽然现在这条路可能并不是真正通往AGI的道路，但是现在整个地球面临着资源过剩的问题，消耗过量资源才能让这个游戏继续下去，而AI的发展则是在消耗资源的同时，也在加速资源的消耗。区别于奴隶制，资本的好处就是这样，有机会他是真的砸钱。</p><div><div><div></div><div>Tip</div></div><div><p>判断一个集体的制度和机制，只需要看最前面的那个人就可以了。是集体系统的机制奖励才促使他出现。风气的好坏其实完全取决于最前面的这个人的带头作用。所以要去做些什么组织团队的时候，一定要问下自己，想和什么样的人共事，这种人在当前组织中是不是被奖励的。</p><p>所以马斯克这种人也只会出现在美国。</p></div></div><p>所以如果说创业的话，强AI方向的绝对是最好的选择，但是模型训练技能本身也已经是非常高价值的能力。虽然平台+强个人的组合更适合特别擅长强AI方向，烧公司的钱，练自己的能力。但是如果说更有梦想一些的话，并且有勇气风险自担的话，创业也是一个选择。</p><p>如果将视角回到这些做不了模型训练的人来说，也只有做AI应用这条路了。再加上大公司的鼠目寸光，不愿意花大价钱去训练模型，只想做一些应用侧的东西。所以AI应用热，和AI造神的运动将会在下一个阶段风起云涌，</p><p>那对于我们来说要怎么做呢。</p><section><h3>二、别感冒<a href="#二别感冒"><span>#</span></a></h3><p></p><figure><img alt="别感冒" loading="lazy" width="474" height="355" src="/_astro/bgm.DBN1nC1p_Z1kHrLM.webp" /><figcaption>别感冒</figcaption></figure><p></p><p>与AI热钱和营销相对应的现象是，AI培训班/AI讯息/AI投资/AI* 的全方面的对用户的轰炸</p><p>我们必须理解一件事情，所有热点新闻都有资本推动,这一点可以问问做公众号或者自媒体的人，流量和资本是不分家的</p><p>基于这一点我们在思考我们的机会在哪</p><p>我们将AI的回答视为一个六面体的骰子，他每次的回答实际上只会掷出可能性最高的那个，人类的意义可能在于永远有人去选择那个点数为1的面。</p><p>另外一条路就是反AI</p></section><section><h3>三、多吃饭<a href="#三多吃饭"><span>#</span></a></h3><p>正像我之前文章中写的，我认为最最重要的一件事</p><p>*** 每个人都要尝试一下训练一个AI ***</p></section><section><h3>四、not by ai<a href="#四not-by-ai"><span>#</span></a></h3><p>最后说回not by ai ，这一个贴纸，当你原创内容在<code>90%</code>以上时可以申请使用。之前了解，而且很多我喜欢的作者都在使用。</p><p></p><figure><img alt="not by ai" loading="lazy" width="1400" height="764" src="/_astro/notbyai-1.tKMSzadH_1iDCJu.webp" /><figcaption>not by ai</figcaption></figure><p></p><p>对原创人员来说确实会增加很大的自豪感，这个贴纸的理念是。</p><p>但是不知道为啥我想到了<em>日本仙人</em>，这让我感到恐惧，因为我一直是<code>科技党</code>。没想到当AI开始取代我的工作的时候，我竟然想做原创仙人。果然铁拳打到自己头上才知道疼。</p><div><div><div></div><div>Tip</div></div><div><p>日本会将长时间手工制作的匠人称为仙人，例如煮饭仙人、天妇罗仙人等等。你可能觉得啊好高大上，有一种神秘的力量在里面。但是我必须让你怯魅，所谓的仙人，永远比不上现代科技，再好的煮饭仙人也不如电饭煲稳定。你要是信了仙人这一套的话，我只能建议你提前做好防止被宗教骗的准备。</p></div></div><p>这个很明显是我说的上面的两条路之一，走反AI的路子，如果你从这个项目获得利益，那我赞同，但是如果只是为了满足自己的情绪，我建议还是算了</p><p>在经过以上的心理斗争后最后我还是拒绝了为自己的原创增加贴纸，</p><p>当然不是因为他的收费是<span>99刀</span></p><p></p><figure><img alt="not by ai" loading="lazy" width="1400" height="764" src="/_astro/notbyai-2.BlUbnnbU_Z1j3HjF.webp" /><figcaption>not by ai</figcaption></figure><p></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E4%BB%BFopenclaw%E8%B0%83%E5%BA%A6%E6%89%8B%E6%90%93%E4%B8%80%E4%B8%AAwindows%E4%B8%AA%E4%BA%BA%E5%8A%A9%E6%89%8B%E6%88%91%E5%8F%AB%E4%BB%96%E6%99%BA%E5%BF%83%E4%B8%80%E6%A2%A6/</id>
      <title type="text">仿openclaw调度，手搓一个免安装的windows个人助手，我管他叫智心一梦</title>
      <published>2026-03-05T00:00:00.000Z</published>
      <updated>2026-03-05T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E4%BB%BFopenclaw%E8%B0%83%E5%BA%A6%E6%89%8B%E6%90%93%E4%B8%80%E4%B8%AAwindows%E4%B8%AA%E4%BA%BA%E5%8A%A9%E6%89%8B%E6%88%91%E5%8F%AB%E4%BB%96%E6%99%BA%E5%BF%83%E4%B8%80%E6%A2%A6/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h2>初衷<a href="#初衷"><span>#</span></a></h2><p>一直想搞一个提效工具，像一个团队一样的帮我思考和解决问题。<code>openclaw</code>，我不是说他不好，只是太大太复杂了，纯手工（编程也有一天会变成<code>非遗</code>吗？）写一些简单的功能，就能实现一个复杂的多agent的调度系统。</p><p>在我的记忆里，小的时候很喜欢玩QQ宠物，对于一个小学生来说10块是一笔巨款，为此省吃俭用买粉钻，疯狂玩游戏赚取胡萝卜，现在想起来还十分上头。所以一直想做一个桌面助手，拥有美好的社区和游戏，以及帮助我完成一些简单的任务，最重要的是要所有数据都是用户的。在AI的时代，我们有能力实现这一目标。</p><p>所以我的一开始的目的就是</p><div><div><div></div><div>核心构成</div></div><div><p><strong>宠物系统</strong> + <strong>员工系统</strong> + <strong>记忆系统</strong></p></div></div><p>于是便有了这个 <code>一键启动免安装的</code>，纯客户端的项目，所有数据均存储在用户本地。<b>拥有了它就拥有了一个<code>专业团队</code>和一个<code>宠物</code></b>，他们都在你的帮助下成长。</p><p><a href="https://studio.ai-nous.com/" target="_blank">智心一梦助手 介绍网站</a>
</p><figure><img alt="智心一梦助手" loading="lazy" width="1490" height="716" src="/_astro/zxym-0.y_jIEH6U_1LJ1Ts.webp" /><figcaption>智心一梦助手</figcaption></figure>
智心一梦助手页面
<figure><img alt="智心一梦助手页面" loading="lazy" width="1024" height="768" src="/_astro/zxym-2.DcQvC3j1_Z2t63aF.webp" /><figcaption>智心一梦助手页面</figcaption></figure><p></p><div>
  <p>🎉 会议室模式</p>
  <p>
    开会开会，一堆 <span>乱七八糟</span> 的员工在为你工作！
  </p>
</div><p></p><figure><img alt="会议开始" loading="lazy" width="1024" height="768" src="/_astro/zxym-8.llx8jJrD_Z1GXVkV.webp" /><figcaption>会议开始</figcaption></figure><p></p><div><div><div></div><div>感谢</div></div><div><p>特别需要感谢 <a href="https://github.com/taochunyu" target="_blank">taochunyu</a>、<a href="https://github.com/gnenux" target="_blank">gnenux</a>在我开发的过程中提供了很多帮助和建议，大家看到的右上角的小绿条token计数就是他的创意。</p></div></div></section>
<section><h2>架构<a href="#架构"><span>#</span></a></h2><section><h3>openclaw的架构<a href="#openclaw的架构"><span>#</span></a></h3><p></p><figure><img alt="openclaw的架构" loading="lazy" width="1080" height="603" src="/_astro/zxym-1.OFMB2eL5_r3XUy.webp" /><figcaption>openclaw的架构</figcaption></figure><p></p></section><section><h3>目前架构<a href="#目前架构"><span>#</span></a></h3><p>之所以说是目前的架构，是因为仍然在快速开发和变更：</p><p></p><figure><img src="/images/zxym-architecture.svg" alt="目前的架构" /><figcaption>目前的架构</figcaption></figure><p></p><div><div><div></div><div>关于架构</div></div><div><p>从整体上仍然是通过一个常驻线程进行调度，但是在细节上有一些区别。关于架构的，我会单独写一篇文章分析，因为还是比较复杂的。</p></div></div></section></section>
<section><h2>开始使用<a href="#开始使用"><span>#</span></a></h2><section><h3>下载安装<a href="#下载安装"><span>#</span></a></h3><p><a href="https://studio.ai-nous.com/" target="_blank">智心一梦助手官网</a> 或者 <a href="https://github.com/Pankitgg/open-ai-tool/releases" target="_blank">github release</a></p></section><section><h3>开始使用<a href="#开始使用-1"><span>#</span></a></h3></section><section><h3>设置<a href="#设置"><span>#</span></a></h3><p></p><figure><img alt="设置" loading="lazy" width="1024" height="768" src="/_astro/zxym-4.CY5vV5hw_17XJj6.webp" /><figcaption>设置</figcaption></figure>
点击添加模型配置默认模型，填写模型的api key和base url。如果没有模型可以在<a href="https://www.bigmodel.cn/glm-coding?ic=W2CYSKAEOC" target="_blank">智谱</a>,<a href="https://www.aliyun.com/benefit/ai/aistar?clubBiz=subTask..12415419..10263.." target="_blank">阿里云</a>,<a href="https://platform.deepseek.com/usage" target="_blank">deepseek</a>。<p></p><blockquote><p>推荐购买按次收费的codeingplan计划，比较省钱。<b>免费模型的性能较差可能影响体验</b></p></blockquote><p>填写模型的api key和base url。</p><p><b>智谱</b>
</p><figure><img alt="智谱模型配置" loading="lazy" width="1024" height="768" src="/_astro/zxym-3.DhAE6G8m_Z1dnQGG.webp" /><figcaption>智谱模型配置</figcaption></figure>
<b>deepseek</b>
<figure><img alt="deepseek模型配置" loading="lazy" width="1024" height="768" src="/_astro/zxym-5.DL_lzBqC_15btCU.webp" /><figcaption>deepseek模型配置</figcaption></figure><p></p><div><div><div></div><div>注意</div></div><div><ol>
<li>模型配置只需要添加一次，后面就可以在设置中切换模型。</li>
<li>模型配置中的api key和base url是你自己的，不要分享给任何人。</li>
<li>如果任何情况下没有回复，首先是模型配置的问题，检查一下模型配置是否填写正确。</li>
</ol></div></div></section><section><h3>员工<a href="#员工"><span>#</span></a></h3><p>添加员工，在AI自动生成中输入你想要的员工，点击生成即可</p><blockquote><p>例如：我想添加一个叫智心一梦的员工，我可以输入：</p></blockquote><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>智心一梦是一个很有才华的员工，他会帮助我完成一些任务</span></div></div></code></pre><div><div></div><div></div></div></figure></div><blockquote><p>或者是</p></blockquote><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>石头人是一个杠精，他会无情的干掉ADC</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img alt="添加员工" loading="lazy" width="1024" height="768" src="/_astro/zxym-6.DNv4AY25_Z1czXh7.webp" /><figcaption>添加员工</figcaption></figure>
<figure><img alt="添加员工" loading="lazy" width="1024" height="768" src="/_astro/zxym-7.CJNALlfw_Z1el64E.webp" /><figcaption>添加员工</figcaption></figure>
重复操作即可添加多个员工<p></p></section><section><h3>会议室<a href="#会议室"><span>#</span></a></h3><p>进入会议室后，点击添加员工，开始会议即可
</p><figure><img alt="会议开始" loading="lazy" width="1024" height="768" src="/_astro/zxym-8.llx8jJrD_Z1GXVkV.webp" /><figcaption>会议开始</figcaption></figure>。
点击每个员工头像，可以查看员工记忆和对话
<figure><img alt="员工记忆和对话" loading="lazy" width="1024" height="768" src="/_astro/zxym-9.BSOeyJR__ZAOTri.webp" /><figcaption>员工记忆和对话</figcaption></figure>
在员工界面也可以查看员工的记忆
<figure><img alt="员工记忆和修改" loading="lazy" width="1024" height="768" src="/_astro/zxym-10.Da8YzMGm_Z1fUTkd.webp" /><figcaption>员工记忆和修改</figcaption></figure><p></p></section><section><h3>技能/工具<a href="#技能工具"><span>#</span></a></h3><p>到此已经基本上具备基本功能了。下一步还可以配置一些技能/插件，让助手除了说还可以干活。</p><p><b>生成工具</b></p><ol>
<li>点击首页进入工具箱，输入你想要的工具，点击生成
例如你可以输入</li>
</ol><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>一个用来展示条目信息的组件</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>点击左侧可以选择样式
</p><figure><img alt="生成工具样式" loading="lazy" width="1024" height="768" src="/_astro/zxym-13.1p4r6Cff_KrFgS.webp" /><figcaption>生成工具样式</figcaption></figure>
<figure><img alt="生成工具" loading="lazy" width="1024" height="768" src="/_astro/zxym-12.ltIUCzL6_ZqbsO9.webp" /><figcaption>生成工具</figcaption></figure>
等待一段时间即可生成工具<p></p><div><div><div></div><div>注意</div></div><div><p>生成工具需要一定的时间，取决于模型的性能。生成过程不会被打断，后续修改仍然可以在工具箱页面修改</p></div></div><p>生成如下，可以通过AI优化和自定义修改工具
</p><figure><img alt="生成工具" loading="lazy" width="1024" height="768" src="/_astro/zxym-16.C5bRUNq0_1nTm2o.webp" /><figcaption>生成工具</figcaption></figure>
点击安装，我们可以在下方的调试区进行调试，包括模式切换和数据调试，以及AI调试修改
<figure><img alt="生成工具" loading="lazy" width="1024" height="768" src="/_astro/zxym-17.BQDnGqRt_1MsSGA.webp" /><figcaption>生成工具</figcaption></figure>
<figure><img alt="生成工具" loading="lazy" width="1024" height="768" src="/_astro/zxym-18.BxQ0ItnI_ZbqDay.webp" /><figcaption>生成工具</figcaption></figure>
生成的工具可以在任何地方使用。首页可以直接点击工具，右键可隐藏<p></p><p><b>工具/技能使用</b></p><p>生成的工具可以配置给员工，员工可以在会议室中使用。
首先点击进入数字员工界面，点击技能库新增技能
</p><figure><img alt="工具配置给员工" loading="lazy" width="1024" height="768" src="/_astro/zxym-19.D6gXlmZ-_1BUqLK.webp" /><figcaption>工具配置给员工</figcaption></figure>
<figure><img alt="工具配置给员工" loading="lazy" width="1024" height="768" src="/_astro/zxym-20.Dhz_B8bI_Z1By2mU.webp" /><figcaption>工具配置给员工</figcaption></figure>
技能类型选择tool，工具选择已配置好的工具
点击进入员工界面，点击需要配置技能/工具的员工，在技能下拉内容增加所需技能即可
<figure><img alt="工具配置给员工" loading="lazy" width="1024" height="768" src="/_astro/zxym-21.2nk4fKXt_2j7GFK.webp" /><figcaption>工具配置给员工</figcaption></figure><p></p><div><div><div></div><div>注意</div></div><div><ol>
<li>技能/工具配置给员工后，员工可以在会议室中使用。</li>
<li>技能/工具的使用，需要员工自己触发，目前没有自动触发的功能。</li>
<li>系统默认增加了一些技能</li>
</ol></div></div></section><section><h3>宠物模式<a href="#宠物模式"><span>#</span></a></h3><p>点击首页宠物图标，即可进入宠物模式
</p><figure><img alt="宠物模式" loading="lazy" width="300" height="300" src="/_astro/zxym-14.Bm2z_MJk_ZeDVgX.webp" /><figcaption>宠物模式</figcaption></figure>
右键宠物模式可以退出宠物模式
<figure><img alt="退出宠物模式" loading="lazy" width="300" height="300" src="/_astro/zxym-15.B1WNI6J9_Z2ojQkP.webp" /><figcaption>退出宠物模式</figcaption></figure><p></p></section></section>
<section><h2>最佳实践（一场酣畅淋漓的会议）<a href="#最佳实践一场酣畅淋漓的会议"><span>#</span></a></h2><p>视频还未录制（等我录制完再放出来）</p></section>
<section><h2>后续计划<a href="#后续计划"><span>#</span></a></h2><p>后续会增加 <strong>小鱼干</strong> ，作为一种和宠物交互的代币。增加宠物更多的好玩的特征，以及建设一个插件市场，来在市场中上架游戏（其实就是插件），让宠物和自己写的插件交互等等。</p><p>会议室/员工，会增加一些最好用的样例。我不想做成一个大而全，像是cherrystudio那种啥都有的，所以这个的配置一定是我自己觉得非常好用才会推给大家。</p><p>记忆系统，目前的记忆总结的触发一个是触发删除mcp的时候，一个是触发条数上限的时候，需要更多的场景来测试和优化。</p><p>等等等等，还有好多好玩的想法，只是这里写不下了</p><p>如果你想加入我，或者有什么建议</p><p>可以关注公众号：智心一梦（这是我个人公众号）</p><p>邮箱：<a href="#">pankitgg0@gmail.com</a></p><p>QQ群：1081954601</p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/fresh2%E5%BC%80%E5%8F%91%E5%8F%8A%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A82/</id>
      <title type="text">fresh2开发及快速入门（2）重构三国</title>
      <published>2026-03-04T00:00:00.000Z</published>
      <updated>2026-03-04T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/fresh2%E5%BC%80%E5%8F%91%E5%8F%8A%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A82/"/>
      <summary type="text">fresh2开发及快速入门（2），手把手教你开发一个AI全栈应用</summary>
      <content type="html"><![CDATA[<p>终于到了令人激动的时刻。这一章我将带你使用<b>全栈</b>+<b>Fresh</b>+<b>群岛架构</b>+<b>AI</b>初步搭建开发一个好玩的游戏。</p>
<blockquote><p>“从水下第一个生命的萌芽开始，到石器时代的巨型野兽，再到人类第一次直立行走，你已经历许多。现在，开启你伟大的探索吧：从早期文明的摇篮，到浩瀚星宇。” — 文明六</p></blockquote>
<p>好玩的游戏一定是可以让人沉迷的。做游戏的第一步就是设计好这个游戏的规则和机制。</p>
<section><h2>一、初步设计游戏规则<a href="#一初步设计游戏规则"><span>#</span></a></h2><p>大模型和故事存在着共通性，学会用信息论的角度去理解世界。故事的本质是信息的压缩与变换，如果我们将一本故事想象成一个信息系统，那么这本书就是信息的压缩包，而我们的阅读过程就是解压。而大语言模型则是信息的压缩，他将世界上的普遍真理压缩到模型中以实现AGI的愿景。</p><p>在以上理解的基础上，我们从最经典的三国下手，架构一个起伏变化的信息加解压缩的软件。</p><section><h3>1. 游玩规划<a href="#1-游玩规划"><span>#</span></a></h3><ol>
<li></li>
</ol></section></section>
<section><h2>二、初始化项目<a href="#二初始化项目"><span>#</span></a></h2><p>项目结构介绍
项目包含以下关键目录和文件：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>rb3k-workspace/</span></div></div><div><div><div>2</div></div><div><span>├── assets/           # 静态资源（图片、CSS 等）</span></div></div><div><div><div>3</div></div><div><span>├── bin/              # 可执行文件目录</span></div></div><div><div><div>4</div></div><div><span>├── components/       # 可重用的 UI 组件</span></div></div><div><div><div>5</div></div><div><span>├── islands/          # 交互式组件（岛屿）</span></div></div><div><div><div>6</div></div><div><span>├── routes/           # 基于文件的路由</span></div></div><div><div><div>7</div></div><div><span>│  └── api/           # API 路由</span></div></div><div><div><div>8</div></div><div><span>├── static/           # 静态资源（图片、CSS 等）</span></div></div><div><div><div>9</div></div><div><span>├── .env.test/        # 环境变量配置文件（私有化运行需去掉.tset）</span></div></div><div><div><div>10</div></div><div><span>├── main.ts           # 应用入口文件</span></div></div><div><div><div>11</div></div><div><span>├── deno.json         # Deno 配置文件</span></div></div><div><div><div>12</div></div><div><span>└── README.md         # 项目文档</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E5%93%AA%E4%B8%AA%E5%A4%A7%E6%A8%A1%E5%9E%8B%E6%9C%80%E5%A5%BD%E7%94%A8--%E6%AD%A4%E5%88%BB%E6%88%91%E4%BB%AC%E6%AD%A3%E7%BB%8F%E5%8E%86%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%8E%82%E5%95%86%E7%9A%84%E6%88%98%E4%BA%89/</id>
      <title type="text">哪个大模型最好用--此刻我们正经历大模型厂商的战争</title>
      <published>2026-01-29T00:00:00.000Z</published>
      <updated>2026-01-29T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E5%93%AA%E4%B8%AA%E5%A4%A7%E6%A8%A1%E5%9E%8B%E6%9C%80%E5%A5%BD%E7%94%A8--%E6%AD%A4%E5%88%BB%E6%88%91%E4%BB%AC%E6%AD%A3%E7%BB%8F%E5%8E%86%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%8E%82%E5%95%86%E7%9A%84%E6%88%98%E4%BA%89/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<p>如果你搜索关键字看到的这篇文章，那说明你也从模型发展中嗅到了未来。</p>
<p>最近你会发现一个很明显的变化：不少国外的大模型开始“免费”给你生成代码，而且还支持把生成结果一键上传到 GitHub。表面上是福利，背后其实是一种很典型的逻辑：<strong>数据越多 → 模型越好用 → 更多人来用 → 数据更多</strong>。</p>
<p>这里面有个关键点可以用一句话概括：<strong>选择即投票</strong>。</p>
<section><h3>1. “选择即投票”：你用不用，就是在给它打分<a href="#1-选择即投票你用不用就是在给它打分"><span>#</span></a></h3><p>当你让 AI 写一段代码，你接下来会做三件事之一：</p><ul>
<li>直接用它，并提交到仓库：相当于投了“赞成票”</li>
<li>改一改再提交：相当于给了“更好的参考答案”</li>
<li>不用这段：相当于投了“反对票”</li>
</ul><p>这和谷歌的人机验证很像：让你去选“红绿灯/斑马线”那几张图。你以为是在证明自己是人，但你点的那些选择，本身也会反过来帮助系统变得更聪明。</p><p>放到写代码这件事上也是一样：当大量人把 AI 生成的代码改完、用上、提交到 GitHub，本质上就是在用真实场景做“筛选”和“标注”。</p></section>
<section><h3>2. 用 GitHub 占比看“模型实力”：50% 可能会出现“默认模型”<a href="#2-用-github-占比看模型实力50-可能会出现默认模型"><span>#</span></a></h3><p>如果按“选择即投票”的思路继续推，一个挺直观的指标就是：<strong>某个模型生成的代码，在 GitHub 全部新增代码里占了多大比例</strong>。</p><p>比例越高，说明两件事：</p><ul>
<li>它被更多人用在真实项目里（入口更强）</li>
<li>它能拿到更多“被人类选择过”的反馈（训练更有优势）</li>
</ul><p>而当这个比例接近、甚至超过 50% 的时候，事情就会变得很不一样：大家会越来越习惯某种写法、某些库、某些组织方式。久而久之，它就可能变成一种“默认的写代码方式”，也就是你说的<strong>默认模型</strong>——不是官方宣布的，而是被大量使用“自然推出来”的。</p></section>
<section><h3>3. 为什么谷歌免费 Gemini 并支持一键上传 GitHub 很高明<a href="#3-为什么谷歌免费-gemini-并支持一键上传-github-很高明"><span>#</span></a></h3><p>再回头看谷歌的策略（比如 Gemini 免费、并且支持一键上传 GitHub），就很好理解了。</p><p>GitHub 是微软的地盘，但“代码从哪里来”更关键：如果越来越多代码是由 Gemini 生成、被人类挑选后提交上去，那么谷歌其实是在一个全球最大的代码池里，持续地制造“对自己有利的投票”和反馈。</p><p>所以这不仅是“免费竞争”，更像是在抢一个长期位置：谁能更早、更大规模地进入开发者工作流，谁就更可能在未来拿到更多真实数据，进而把模型越滚越强。</p></section>
<section><h3>4. 一个不太舒服但绕不过去的结论<a href="#4-一个不太舒服但绕不过去的结论"><span>#</span></a></h3><p>我们在享受免费工具的时候，也在用自己的选择帮它变强：你每一次复制粘贴、每一次修改、每一次提交，都会变成它学习的一部分。</p><p>从这个角度看，谷歌的一键上传策略，本质上是一场<strong>关于“未来代码基因”的争夺</strong>：当 AI 生成的代码在 GitHub 上占据主导地位时，谁的模型是那个“最大贡献者”，谁就掌握了未来软件世界的底层规则。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E5%85%A8%E6%A0%88%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90%E5%8F%8Aai%E6%97%B6%E4%BB%A3%E9%80%89%E5%9E%8B/</id>
      <title type="text">全栈框架分析及AI时代选型</title>
      <published>2026-01-21T00:00:00.000Z</published>
      <updated>2026-01-21T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E5%85%A8%E6%A0%88%E6%A1%86%E6%9E%B6%E5%88%86%E6%9E%90%E5%8F%8Aai%E6%97%B6%E4%BB%A3%E9%80%89%E5%9E%8B/"/>
      <summary type="text">以“AI 编程适合度”为视角，系统对比主流全栈框架与元框架，并给出选型路径与落地建议</summary>
      <content type="html"><![CDATA[<p>过去十年“全栈框架”的讨论更多围绕：渲染方案、路由、数据获取、缓存、部署与开发体验。进入 AI 时代之后，评估维度变了：你不仅要把页面做出来，还要把一个带推理与工具调用的系统接进产品——它要流式输出、要能调用数据库/搜索/业务 API、要能回放与审计、要能灰度与回滚、要能控成本与控延迟。</p>
<p>这篇文章用一个更工程化的视角，把“什么框架能做全栈”与“什么框架更适合 AI 编程”拆开来讲：前者看能力边界，后者看结构化与可维护性。</p>
<section><h2>1. 先定义：AI 时代的“全栈”到底多了什么<a href="#1-先定义ai-时代的全栈到底多了什么"><span>#</span></a></h2><p>如果把传统 Web 应用的闭环写成「请求 → 读写数据 → 渲染 → 交互 → 再请求」，AI 时代新增的关键链路是：</p><ul>
<li><strong>流式与增量 UI</strong>：聊天、Copilot、生成式表单等，需要边生成边渲染，且可中断、可续写。</li>
<li><strong>工具调用与编排</strong>：模型不只“回答”，而是要调用搜索、数据库、第三方 API、队列任务，然后再整合结果。</li>
<li><strong>数据治理与评测</strong>：提示词版本、输出结构校验、A/B 测试、离线回放、敏感信息与权限边界。</li>
<li><strong>推理运行时多样化</strong>：同一应用可能同时用到 Node、Edge runtime、Worker、后台任务、甚至 GPU 推理服务。</li>
</ul><p>因此，“AI 友好”的全栈框架通常具备这些工程特征：约定强、边界清晰、类型可贯通、可插拔的运行时与部署路径、以及成熟的生态（鉴权/数据库/缓存/队列/观测）。</p></section>
<section><h2>2. 评估维度：什么叫“AI 编程适合度”<a href="#2-评估维度什么叫ai-编程适合度"><span>#</span></a></h2><p>这里的“适合度”不是在比谁更潮，而是在比：当你把需求交给 AI（或让 AI 参与编码）时，代码能否更稳定地被生成、被重构、被测试、被上线。</p><p>我用 6 个维度来评分（1–5 分，越高越适合 AI 协作开发）：</p><ol>
<li><strong>可预测的项目结构</strong>：路由、数据加载、Action、组件边界清晰，AI 生成不容易“发散”。</li>
<li><strong>端到端类型与 schema 驱动</strong>：请求/响应/数据库模型能对齐，减少“靠猜”的胶水代码。</li>
<li><strong>服务端能力一等公民</strong>：SSR/Actions/后台任务/队列等是框架内置而不是外挂拼装。</li>
<li><strong>可观测与可测试</strong>：可复现、可回放、可做契约测试与 E2E，避免“能跑就行”。</li>
<li><strong>部署路径清晰</strong>：本地与生产一致性好；Edge/Server 的差异可被框架约束。</li>
<li><strong>AI 生态与样例成熟</strong>：已有成熟的 AI SDK、流式 UI、工具调用范式与参考实现。</li>
</ol></section>
<section><h2>3. 全栈框架全景对比表（尽可能覆盖主流生态）<a href="#3-全栈框架全景对比表尽可能覆盖主流生态"><span>#</span></a></h2><p>说明：</p><ul>
<li>“全栈框架”在不同语言里含义不同：有的强调“前后端一体”（元框架），有的强调“后端 + 模板/组件渲染”，也有一类是“平台型全栈”（BaaS/内容系统自带 UI + API）。</li>
<li>表格覆盖各主流语言生态里仍活跃、可用于生产的全栈框架/元框架，并额外纳入一批常被用于全栈交付的选择。</li>
</ul><div>
  
  <div>
    <table>
    <thead>
      <tr>
        <th>分类</th>
        <th>框架</th>
        <th>主语言</th>
        <th>前端/渲染模型</th>
        <th>后端/数据能力</th>
        <th>部署形态（常见）</th>
        <th>AI 适合度</th>
        <th>典型适用场景</th>
      </tr>
    </thead>
    <tbody>
      <tr><td>TS 元框架</td><td>Next.js</td><td>TS/JS</td><td>SSR/SSG/RSC/Server Actions</td><td>Route Handlers、Server Actions、Middleware</td><td>Node/Vercel/Serverless/部分 Edge</td><td>5</td><td>产品型 Web、AI App、BFF 一体化</td></tr>
      <tr><td>TS 元框架</td><td>Remix</td><td>TS/JS</td><td>SSR 优先（loader/action）</td><td>表单与 Action 一体、Web 标准倾向</td><td>Node/Serverless</td><td>5</td><td>表单密集、数据驱动、稳定迭代</td></tr>
      <tr><td>TS 元框架</td><td>SvelteKit</td><td>TS/JS</td><td>SSR/SSG（Svelte）</td><td>端点 + Actions、适配器生态</td><td>Node/Edge/Serverless</td><td>4</td><td>中小型产品、性能与 DX 平衡</td></tr>
      <tr><td>TS 元框架</td><td>Nuxt 3</td><td>TS/JS</td><td>SSR/SSG（Vue）</td><td>Nitro Server、server routes</td><td>Node/Edge/Serverless</td><td>4</td><td>Vue 生态、全栈内容与应用</td></tr>
      <tr><td>TS 元框架</td><td>Angular（SSR）</td><td>TS</td><td>SSR/SSG（Angular）</td><td>SSR 服务端 + API（需组合）</td><td>Node/Serverless</td><td>3</td><td>大型企业前端、强规范团队</td></tr>
      <tr><td>TS 元框架</td><td>Astro（SSR）</td><td>TS/JS</td><td>内容优先 + Islands/SSR</td><td>端点/中间件（需按项目组织）</td><td>Node/Serverless</td><td>3</td><td>内容站 + 轻交互/轻后端</td></tr>
      <tr><td>TS 元框架</td><td>Gatsby</td><td>TS/JS</td><td>SSG + Functions</td><td>Functions（需组合数据层）</td><td>Serverless/托管</td><td>2</td><td>营销站/内容站 + 轻后端</td></tr>
      <tr><td>TS 元框架</td><td>Qwik City</td><td>TS/JS</td><td>SSR/Resumability</td><td>端点 + Actions</td><td>Edge/Serverless</td><td>3</td><td>极致首屏、边缘分发</td></tr>
      <tr><td>TS 元框架</td><td>SolidStart</td><td>TS/JS</td><td>SSR（Solid）</td><td>路由/数据加载/Actions</td><td>Node/Serverless</td><td>3</td><td>偏前沿团队、追求轻量响应</td></tr>
      <tr><td>TS 元框架</td><td>TanStack Start</td><td>TS/JS</td><td>SSR（Router + Query）</td><td>约定式全栈路由（新）</td><td>Node/Serverless</td><td>3</td><td>已重度 TanStack 生态的团队</td></tr>
      <tr><td>TS 全栈框架</td><td>RedwoodJS</td><td>TS/JS</td><td>React SPA/SSR（演进中）</td><td>GraphQL API、Prisma、Jobs</td><td>Serverless/Node</td><td>3</td><td>业务后台、CRUD + 权限模型</td></tr>
      <tr><td>TS 全栈框架</td><td>Blitz.js</td><td>TS/JS</td><td>Next.js 之上（RPC）</td><td>端到端类型 RPC</td><td>Node</td><td>3</td><td>快速交付、重视类型贯通</td></tr>
      <tr><td>TS 全栈框架</td><td>Wasp</td><td>TS/JS</td><td>React/Vue（脚手架）</td><td>全栈模板化、Auth/CRUD</td><td>Node/云部署</td><td>3</td><td>SaaS 快速落地、约定化全栈</td></tr>
      <tr><td>TS 全栈框架</td><td>Meteor</td><td>JS</td><td>同构渲染（传统）</td><td>实时数据/订阅</td><td>Node</td><td>2</td><td>需要强实时但能接受框架约束</td></tr>
      <tr><td>TS 后端+视图</td><td>AdonisJS</td><td>TS</td><td>模板/视图（Edge）</td><td>MVC、ORM、队列、鉴权</td><td>Node</td><td>3</td><td>Node 后端团队想要“Rails 式”</td></tr>
      <tr><td>TS 后端+视图</td><td>NestJS（+模板）</td><td>TS</td><td>模板/SSR（需组合）</td><td>DI、模块化、微服务</td><td>Node</td><td>3</td><td>大型后端、服务治理优先</td></tr>
      <tr><td>JS 全栈（传统）</td><td>Sails.js</td><td>JS</td><td>SSR（视图）</td><td>MVC、ORM、约定式</td><td>Node</td><td>2</td><td>传统全栈、快速 CRUD</td></tr>
      <tr><td>Web 标准后端</td><td>Hono（+前端）</td><td>TS/JS</td><td>需组合（常配 React/Vue）</td><td>Web 标准路由、中间件</td><td>Edge/Node</td><td>3</td><td>Edge API、轻量 BFF、AI 网关</td></tr>
      <tr><td>Bun 生态</td><td>Elysia（+前端）</td><td>TS</td><td>需组合</td><td>轻量高性能后端</td><td>Bun/Node（部分）</td><td>2</td><td>性能实验、偏后端 API</td></tr>
      <tr><td>Deno 全栈</td><td>Fresh</td><td>TS</td><td>SSR（Islands）</td><td>Deno 原生路由与中间件</td><td>Deno Deploy/Server</td><td>3</td><td>Deno 团队、轻量全栈</td></tr>
      <tr><td>Ruby 全栈</td><td>Ruby on Rails</td><td>Ruby</td><td>SSR（ERB/Hotwire）</td><td>ActiveRecord、Jobs、ActionCable</td><td>VM/容器</td><td>4</td><td>业务系统、AI 能力快速集成</td></tr>
      <tr><td>Ruby 全栈</td><td>Hanami</td><td>Ruby</td><td>SSR</td><td>框架更轻、更显式</td><td>VM/容器</td><td>3</td><td>喜欢更可控的 Ruby 架构</td></tr>
      <tr><td>PHP 全栈</td><td>Laravel</td><td>PHP</td><td>SSR（Blade/Inertia）</td><td>Eloquent、队列、事件、生态强</td><td>VM/容器/Serverless</td><td>4</td><td>中小企业应用、CRUD/后台</td></tr>
      <tr><td>PHP 全栈</td><td>Symfony</td><td>PHP</td><td>SSR（Twig）</td><td>组件化成熟、可组合</td><td>VM/容器</td><td>3</td><td>中大型 PHP 项目、长期维护</td></tr>
      <tr><td>PHP 全栈</td><td>Yii</td><td>PHP</td><td>SSR</td><td>MVC、ORM、成熟生态</td><td>VM/容器</td><td>3</td><td>传统业务系统</td></tr>
      <tr><td>PHP 全栈</td><td>CakePHP</td><td>PHP</td><td>SSR</td><td>约定式 MVC</td><td>VM/容器</td><td>2</td><td>中小项目、快速交付</td></tr>
      <tr><td>PHP 全栈</td><td>CodeIgniter</td><td>PHP</td><td>SSR</td><td>轻量 MVC</td><td>VM/容器</td><td>2</td><td>轻量传统站点</td></tr>
      <tr><td>Python 全栈</td><td>Django</td><td>Python</td><td>SSR（模板）</td><td>ORM、Admin、Auth、Jobs（组合）</td><td>VM/容器</td><td>4</td><td>业务后台、内容管理、AI 结合</td></tr>
      <tr><td>Python 全栈</td><td>FastAPI（+模板）</td><td>Python</td><td>SSR（需组合）</td><td>API 优先、类型注解</td><td>VM/容器</td><td>3</td><td>API + 前端分离、AI 服务</td></tr>
      <tr><td>Python 全栈</td><td>Flask（+模板）</td><td>Python</td><td>SSR（需组合）</td><td>轻量后端、可组合</td><td>VM/容器</td><td>2</td><td>小型应用、偏 API/工具</td></tr>
      <tr><td>Python 全栈</td><td>Pyramid</td><td>Python</td><td>SSR（模板）</td><td>更传统、更显式</td><td>VM/容器</td><td>2</td><td>传统企业 Python Web</td></tr>
      <tr><td>Elixir 全栈</td><td>Phoenix LiveView</td><td>Elixir</td><td>SSR + LiveView（状态同步）</td><td>OTP、Channel、强实时</td><td>VM/容器</td><td>4</td><td>实时协作、仪表盘、Agent UI</td></tr>
      <tr><td>Java 全栈</td><td>Spring Boot（MVC）</td><td>Java</td><td>SSR（Thymeleaf 等）</td><td>生态巨大、治理成熟</td><td>容器/VM</td><td>3</td><td>大型企业、合规与治理</td></tr>
      <tr><td>Java 全栈</td><td>Quarkus（+模板）</td><td>Java</td><td>SSR（Qute 等）</td><td>云原生、轻量</td><td>容器/VM</td><td>2</td><td>云原生 Java、微服务到全栈</td></tr>
      <tr><td>Java 全栈</td><td>Micronaut（+模板）</td><td>Java</td><td>SSR（需组合）</td><td>轻量、AOT 倾向</td><td>容器/VM</td><td>2</td><td>云原生 Java</td></tr>
      <tr><td>Java 全栈</td><td>Vaadin</td><td>Java</td><td>服务器驱动 UI</td><td>UI 与后端同一语言</td><td>容器/VM</td><td>2</td><td>内部系统、强表单/数据录入</td></tr>
      <tr><td>.NET 全栈</td><td>ASP.NET Core（MVC）</td><td>C#</td><td>SSR（Razor）</td><td>Identity、EF Core、后台任务</td><td>容器/VM</td><td>4</td><td>企业应用、微软生态</td></tr>
      <tr><td>.NET 全栈</td><td>Blazor</td><td>C#</td><td>SSR/WA（同构）</td><td>UI 与后端一体</td><td>容器/VM</td><td>3</td><td>.NET 团队、全栈同语言</td></tr>
      <tr><td>.NET 全栈</td><td>ABP Framework</td><td>C#</td><td>SSR/SPA（可选）</td><td>模块化企业框架、DDD 倾向</td><td>容器/VM</td><td>3</td><td>企业级后台、快速搭骨架</td></tr>
      <tr><td>Go 全栈</td><td>Buffalo</td><td>Go</td><td>SSR（模板）</td><td>Rails 风格、生产可用</td><td>容器/VM</td><td>2</td><td>Go 团队做传统全栈</td></tr>
      <tr><td>Go 全栈</td><td>Beego</td><td>Go</td><td>SSR/API</td><td>MVC、ORM（可选）</td><td>容器/VM</td><td>2</td><td>传统 Go Web</td></tr>
      <tr><td>Go 全栈</td><td>Revel</td><td>Go</td><td>SSR</td><td>传统 MVC</td><td>容器/VM</td><td>1</td><td>历史项目维护</td></tr>
      <tr><td>Rust 全栈</td><td>Leptos</td><td>Rust</td><td>SSR（WASM/SSR）</td><td>全栈 Rust（偏前沿）</td><td>容器/VM</td><td>2</td><td>性能/安全、愿意投入学习曲线</td></tr>
      <tr><td>Rust 全栈</td><td>Axum + 模板/前端</td><td>Rust</td><td>需组合</td><td>API 强、需自行装配全栈能力</td><td>容器/VM</td><td>2</td><td>高性能 API + 自定义前端</td></tr>
      <tr><td>Rust 全栈</td><td>Rocket（+模板）</td><td>Rust</td><td>SSR（模板）</td><td>传统后端框架</td><td>容器/VM</td><td>1</td><td>小型项目/学习</td></tr>
      <tr><td>Scala 全栈</td><td>Play Framework</td><td>Scala/Java</td><td>SSR</td><td>JVM 生态、传统全栈</td><td>容器/VM</td><td>2</td><td>JVM 团队、Scala 全栈</td></tr>
      <tr><td>CMS 全栈</td><td>Payload</td><td>TS/JS</td><td>Admin UI + API</td><td>内容建模、Auth、DB</td><td>Node/Serverless</td><td>4</td><td>内容驱动产品、AI 内容工作流</td></tr>
      <tr><td>CMS 全栈</td><td>Strapi</td><td>Node/TS</td><td>Admin UI + API</td><td>插件化 CMS、RBAC</td><td>Node/容器</td><td>3</td><td>Headless CMS、内容中台</td></tr>
      <tr><td>CMS 全栈</td><td>Keystone</td><td>TS/JS</td><td>Admin UI + GraphQL</td><td>Schema 驱动、可扩展</td><td>Node</td><td>3</td><td>可定制内容系统</td></tr>
      <tr><td>平台型全栈</td><td>Supabase</td><td>平台</td><td>前端任意</td><td>Postgres + Auth + Storage + Edge Functions</td><td>托管/自建</td><td>4</td><td>快速做 AI 产品、RAG 数据层</td></tr>
      <tr><td>平台型全栈</td><td>Firebase</td><td>平台</td><td>前端任意</td><td>Auth/DB/Functions/Storage</td><td>托管</td><td>3</td><td>移动端/轻后端、原型到中型</td></tr>
      <tr><td>平台型全栈</td><td>Appwrite</td><td>平台</td><td>前端任意</td><td>Auth/DB/Functions/Storage</td><td>托管/自建</td><td>3</td><td>需要自建 BaaS 的团队</td></tr>
      <tr><td>平台型全栈</td><td>PocketBase</td><td>Go（单体）</td><td>前端任意</td><td>单文件数据库 + Auth + API</td><td>单机/小型</td><td>2</td><td>小团队、快速交付、低成本</td></tr>
    </tbody>
  </table>
  </div>
  ‹
  ›
  <div>可左右滑动</div>  </div></section>
<section><h2>3.1 读表指南：分数为什么会拉开<a href="#31-读表指南分数为什么会拉开"><span>#</span></a></h2><p>同一个“全栈”，在 AI 场景下表现差异很大，原因通常不在性能，而在工程边界：</p><ul>
<li><strong>5 分档（更推荐做 AI 产品主干）</strong>：元框架内置了稳定的读写数据语义（loader/action、actions、handlers），流式 UI 与工具调用范式成熟，AI 更容易写出“可维护”的结果。</li>
<li><strong>3–4 分档（稳健但要立规矩）</strong>：能力够用，但你需要把鉴权、数据访问、任务队列、错误处理固化成团队约定，否则 AI 写出来会出现多套风格并存。</li>
<li><strong>1–2 分档（能做但不建议作为默认）</strong>：全栈能力更多依赖你自己装配；AI 协作时更容易产生隐性耦合与不可回放的实现。</li>
</ul></section>
<section><h2>4. 为什么有的框架更“适合 AI 编程”<a href="#4-为什么有的框架更适合-ai-编程"><span>#</span></a></h2><p>把 AI 当作“会写代码的同事”时，你会发现它最怕两类项目：</p><ul>
<li><strong>结构不稳定</strong>：同一个需求在不同模块里有不同写法，或者靠大量自定义约定才能跑起来。</li>
<li><strong>隐式魔法太多</strong>：运行时行为难以从代码直观看出来，AI 很难在重构时不踩坑。</li>
</ul><p>反过来，更 AI 友好的框架往往具备这些共同点：</p><ul>
<li><strong>强约定 + 少选择题</strong>：文件路由、数据加载、表单提交、错误边界有固定入口，AI 不容易“写偏”。</li>
<li><strong>类型/Schema 是事实来源</strong>：DB schema、API contract、工具调用参数等可以被校验与生成。</li>
<li><strong>服务端边界明确</strong>：哪些代码跑在 Server/Edge/Client，一眼就能看出，避免把密钥带进浏览器。</li>
</ul><p>以 TypeScript 生态为例，Vercel 的 AI SDK 明确提供了面向 React/Next.js 等框架的流式 UI 与工具调用抽象，降低“自己拼 streaming + tool calling”的手工成本。<sup><a href="#user-content-fn-1">1</a></sup></p><p>同时，Edge/Worker 侧的推理与 embedding 能力越来越标准化，例如 Cloudflare Workers AI 提供了“在边缘调用模型”的路径，让你能把部分推理或向量化靠近用户，改善延迟与成本结构。<sup><a href="#user-content-fn-2">2</a></sup></p></section>
<section><h2>5. 重点框架解读（按“全栈形态”分组）<a href="#5-重点框架解读按全栈形态分组"><span>#</span></a></h2><p>下面不按“谁更强”排序，而是按“你会怎么把它用在 AI 产品里”来讲。</p><section><h3>5.1 元框架：前后端一体的产品交付线<a href="#51-元框架前后端一体的产品交付线"><span>#</span></a></h3><section><h4>Next.js<a href="#nextjs"><span>#</span></a></h4><ul>
<li><strong>全栈方式</strong>：路由 + Server Actions/Route Handlers + SSR/RSC，前后端在同一仓库内闭环。</li>
<li><strong>AI 友好点</strong>：结构强约定、流式 UI 生态成熟、TS 贯通更容易收敛到稳定形态。<sup><a href="#user-content-fn-1">1</a></sup></li>
<li><strong>注意点</strong>：RSC/缓存/运行时组合非常强大，也更复杂；团队需要建立统一的目录与数据获取规范。</li>
</ul></section><section><h4>Remix<a href="#remix"><span>#</span></a></h4><ul>
<li><strong>全栈方式</strong>：loader/action 把“读数据”和“写数据”固定在同一套路由语义下，偏 Web 标准。</li>
<li><strong>AI 友好点</strong>：模型更易理解（表单提交就是 action），代码生成更不容易跑偏；错误边界也更直观。</li>
<li><strong>注意点</strong>：生态相对 Next.js 小一些，但在“表单密集 + 可维护”上很稳。</li>
</ul></section><section><h4>Nuxt 3 / SvelteKit<a href="#nuxt-3--sveltekit"><span>#</span></a></h4><ul>
<li><strong>全栈方式</strong>：SSR/SSG + 服务端路由/Actions（或 endpoints），与各自前端框架深度绑定。</li>
<li><strong>AI 友好点</strong>：约定明确、样板少；对中小团队尤为友好。</li>
<li><strong>注意点</strong>：团队要尽早决定数据层（ORM/Query）与鉴权方案，避免后期多套并存。</li>
</ul></section></section><section><h3>5.2 后端 + 视图：以“服务端为中心”的全栈<a href="#52-后端--视图以服务端为中心的全栈"><span>#</span></a></h3><section><h4>Ruby on Rails<a href="#ruby-on-rails"><span>#</span></a></h4><ul>
<li><strong>全栈方式</strong>：MVC + ORM + Jobs + WebSocket（ActionCable），默认就能把业务做完整闭环。</li>
<li><strong>AI 友好点</strong>：约定优于配置做到极致，AI 生成 CRUD、后台、任务队列代码的成功率很高；把 AI 任务放在后台 Job 也很自然。</li>
<li><strong>注意点</strong>：前端体验取决于你选择 Hotwire 还是前后端分离；团队如果已经是 TS 主栈，需要权衡语言栈分裂成本。</li>
</ul></section><section><h4>Laravel / Django / ASP.NET Core<a href="#laravel--django--aspnet-core"><span>#</span></a></h4><ul>
<li><strong>共同优势</strong>：成熟的鉴权、ORM、后台管理与生态，让你可以把精力更多放在 AI 能力本身（检索、工具、评测、成本）。</li>
<li><strong>AI 友好点</strong>：约定稳定、框架年代久、资料多，LLM“见过的代码”更多，生成更稳。</li>
<li><strong>注意点</strong>：如果你追求高度交互的前端体验，通常会走“后端提供 API + 前端元框架”的组合式全栈。</li>
</ul></section></section><section><h3>5.3 内容系统/平台型全栈：把“数据层 + 管理 UI”当成默认能力<a href="#53-内容系统平台型全栈把数据层--管理-ui当成默认能力"><span>#</span></a></h3><section><h4>Payload / Strapi / Keystone<a href="#payload--strapi--keystone"><span>#</span></a></h4><ul>
<li><strong>全栈方式</strong>：内置内容模型、权限与管理后台，前端可以是 Next/Nuxt 等。</li>
<li><strong>AI 友好点</strong>：内容结构天然 schema 化，非常适合做 RAG 的知识源、内容工作流与审核流程。</li>
<li><strong>注意点</strong>：内容系统不是通用业务框架；复杂交易/强一致业务仍需要严肃的后端域模型。</li>
</ul></section><section><h4>Supabase / Firebase（平台）<a href="#supabase--firebase平台"><span>#</span></a></h4><ul>
<li><strong>全栈方式</strong>：把 Auth、存储、数据库、函数、实时订阅做成平台能力，前端“拿来即用”。</li>
<li><strong>AI 友好点</strong>：快速出 MVP；对 RAG 来说，Postgres/向量扩展、权限与存储这些“脏活累活”平台已经处理掉很多。</li>
<li><strong>注意点</strong>：别把平台当魔法；多租户、合规、审计、成本可控仍然需要你在应用层建立规范。</li>
</ul></section></section><section><h3>5.4 表格里其余框架逐条解释（速览）<a href="#54-表格里其余框架逐条解释速览"><span>#</span></a></h3><p>下面这些框架同样能用于全栈交付，但更依赖你对“边界与约束”的工程化落地；我把它们按用途给出更直接的定位。</p><section><h4>TS 元框架与前端绑定全栈<a href="#ts-元框架与前端绑定全栈"><span>#</span></a></h4><ul>
<li><strong>Angular（SSR）</strong>：适合强规范的大型团队；AI 协作时更建议把数据访问与状态管理固化成统一范式，否则生成代码容易出现多套风格。</li>
<li><strong>Astro（SSR）</strong>：内容/性能优先，适合作为内容站或文档站的“轻全栈”；AI 生成时要明确哪些页面需要 SSR、哪些是静态，否则很容易被“过度服务端化”。</li>
<li><strong>Gatsby</strong>：偏 SSG + Functions 的组合；适合内容站叠加轻后端能力；AI 编程适合度较低主要是因为“全栈路径不唯一”，需要你明确边界。</li>
<li><strong>Qwik City</strong>：主打 Resumability；适合极致首屏与边缘分发，但团队需要较强的心智统一，才能让 AI 写出的代码保持一致性。</li>
<li><strong>SolidStart</strong>：偏轻量 SSR；如果团队对 Solid 生态熟悉，体验很好，否则学习成本会传导到 AI 协作质量上。</li>
<li><strong>TanStack Start</strong>：适合已经重度依赖 TanStack Router/Query 的团队；整体偏新，建议先把“目录结构 + 数据访问规范 + 边界约束”写成模板再让 AI 扩展。</li>
</ul></section><section><h4>Node/JS 传统全栈与后端框架<a href="#nodejs-传统全栈与后端框架"><span>#</span></a></h4><ul>
<li><strong>RedwoodJS</strong>：偏“全栈应用框架/脚手架”，常见组合是 GraphQL + Prisma + Jobs；适合业务后台与标准化 CRUD，但要避免把业务全塞进 GraphQL resolver，建议形成清晰的 service 层。</li>
<li><strong>Blitz.js</strong>：在 Next.js 上提供 RPC 形态的端到端类型；适合快速交付与减少样板；AI 协作时建议强制所有 mutation 走统一校验与权限中间层。</li>
<li><strong>Wasp</strong>：把常见 SaaS 能力模板化（鉴权、CRUD 等），适合快速起步；AI 协作优势来自“更少的自由度”，但复杂场景可能需要下沉自定义。</li>
<li><strong>Meteor</strong>：同构与实时能力强，适合快速做原型；但现代部署与模块化实践上需要你额外补齐工程规范。</li>
<li><strong>AdonisJS</strong>：Node 生态里偏“Rails 路线”的全栈框架，MVC/ORM/队列/鉴权齐全；AI 生成 CRUD 与后台任务的成功率通常较高。</li>
<li><strong>NestJS（+模板）</strong>：更像后端应用框架；做全栈通常要叠加模板或前端元框架；AI 协作优势来自强模块化，但你需要明确“Controller/Service/Module”的边界与命名约定。</li>
<li><strong>Sails.js</strong>：传统 MVC 与约定式结构，适合快速交付；但生态相对老，现代前端/AI 需求往往需要额外拼装。</li>
<li><strong>Hono（+前端）</strong>：更像“跨运行时的 Web 标准后端”；用它做全栈时通常承担 BFF/AI 网关角色（鉴权、限流、工具调用代理），前端仍交给元框架。</li>
<li><strong>Elysia（+前端）</strong>：偏后端性能框架；用在全栈里更像 API 层；AI 协作风险在于“形态选择太自由”，最好配合 OpenAPI/Schema 来锁定契约。</li>
</ul></section><section><h4>Deno 全栈<a href="#deno-全栈"><span>#</span></a></h4><ul>
<li><strong>Fresh</strong>：SSR + Islands 的 Deno 全栈；适合追求轻量与边缘部署，但生态与资料密度不如 Node 主流，AI 生成质量会受影响。</li>
</ul></section><section><h4>Ruby/PHP/Python 的补充全栈<a href="#rubyphppython-的补充全栈"><span>#</span></a></h4><ul>
<li><strong>Hanami</strong>：比 Rails 更轻、更显式，适合重视架构可控的 Ruby 团队；AI 协作关键是把应用层约定固定下来（如 service object、repository 模式）。</li>
<li><strong>Symfony</strong>：更偏“组件化的企业 PHP”；AI 协作时建议强化模块边界与依赖注入配置，否则生成的代码容易跨层耦合。</li>
<li><strong>Yii / CakePHP / CodeIgniter</strong>：传统 MVC 路线；能做全栈交付，但现代 AI 需求（流式、工具编排、观测）大多需要你自己建立标准化模块。</li>
<li><strong>FastAPI（+模板）</strong>：更适合 API 优先；全栈形态通常是“FastAPI 做后端 + 前端元框架做 UI”；AI 协作优势在于 Python 类型注解与 pydantic schema。</li>
<li><strong>Flask（+模板）/ Pyramid</strong>：轻量与传统路线；更适合小型应用或历史项目；AI 协作时尤其需要你把目录结构、蓝图/路由组织、配置分层固化。</li>
</ul></section><section><h4>Elixir 实时全栈<a href="#elixir-实时全栈"><span>#</span></a></h4><ul>
<li><strong>Phoenix LiveView</strong>：本质是“服务器驱动 UI + 实时状态同步”；适合 Agent UI、运维控制台、协作场景；AI 协作的关键是把事件与状态机设计清晰。</li>
</ul></section><section><h4>JVM 与 .NET 企业全栈扩展<a href="#jvm-与-net-企业全栈扩展"><span>#</span></a></h4><ul>
<li><strong>Quarkus（+模板）/ Micronaut（+模板）</strong>：云原生 JVM 框架，做全栈需要组合模板或前端；AI 协作更依赖你把 DTO、验证、鉴权与日志规范化。</li>
<li><strong>Vaadin</strong>：服务器驱动 UI，适合内部系统；但 UI 范式与 Web 主流不同，AI 协作时需要更多明确的组件/表单规范。</li>
<li><strong>Blazor</strong>：.NET 全栈同语言的方案；适合既有 C# 团队；AI 协作优势是类型系统贯通，但前端生态与主流 JS 有差异。</li>
<li><strong>ABP Framework</strong>：企业级模块化框架，适合快速搭管理后台与 DDD 风格骨架；AI 协作的重点是遵循它的模块与分层约定。</li>
</ul></section><section><h4>Go/Rust/Scala 全栈<a href="#gorustscala-全栈"><span>#</span></a></h4><ul>
<li><strong>Buffalo / Beego / Revel</strong>：Go 的传统全栈选择；多数团队会把它们用作“后端 + 模板”而非复杂前端；AI 协作建议用 OpenAPI/模板化目录来控制发散。</li>
<li><strong>Leptos / Axum + 模板 / Rocket</strong>：Rust 全栈可行但偏前沿；优势是性能与安全；AI 协作难点是生态碎片化与学习曲线，适合愿意投入的团队。</li>
<li><strong>Play Framework</strong>：JVM 传统全栈框架；适合 Scala/JVM 团队；AI 协作依赖规范化的路由、DTO、模板与服务分层。</li>
</ul></section><section><h4>CMS 与平台型补充<a href="#cms-与平台型补充"><span>#</span></a></h4><ul>
<li><strong>Payload / Strapi / Keystone</strong>：适合把内容结构化后接 AI 工作流（检索、摘要、审核）；关键在于把内容模型当成“契约”，并把权限与审计做严。</li>
<li><strong>Appwrite / PocketBase</strong>：偏快速交付与低成本；适合原型或小型项目；AI 场景下要特别注意权限边界、速率限制与数据泄露风险。</li>
</ul></section></section></section>
<section><h2>6. 从“AI 编程适合度”出发的选型结论（可直接抄作决策）<a href="#6-从ai-编程适合度出发的选型结论可直接抄作决策"><span>#</span></a></h2><p>把问题换一种问法：<strong>为了让 AI 协作开发更可靠，你需要的是“更强的约束”而不是“更多的自由”。</strong></p><section><h3>6.1 默认推荐（按团队主栈）<a href="#61-默认推荐按团队主栈"><span>#</span></a></h3><ul>
<li><strong>TS/React 团队做 AI 产品</strong>：优先 Next.js 或 Remix；再结合 schema 驱动的数据层（如 Prisma/Drizzle）与严格的 API/工具调用契约。</li>
<li><strong>Vue 团队做全栈</strong>：Nuxt 3 是最短路径；把 server routes/鉴权/数据访问集中到固定目录与固定模式。</li>
<li><strong>后端强、前端一般</strong>：Rails/Laravel/Django/ASP.NET Core 可以直接交付完整产品；前端若需要更强交互，再叠加一个元框架做 BFF。</li>
<li><strong>强实时/协作型产品</strong>：Phoenix LiveView 值得优先评估；在实时状态同步方面，它往往比“前端 + WebSocket 胶水”更可控。</li>
</ul></section><section><h3>6.2 你真正需要做的“AI 友好工程化”<a href="#62-你真正需要做的ai-友好工程化"><span>#</span></a></h3><p>无论选什么框架，想让 AI 写得稳、你改得动，最终都要回到这些硬约束：</p><ul>
<li><strong>把契约写成代码</strong>：DB schema、OpenAPI/JSON Schema、工具参数 schema，能校验就不要靠约定。</li>
<li><strong>把边界写进目录结构</strong>：server/edge/client 分层明确；密钥与权限在 server 侧可验证。</li>
<li><strong>把推理做成“可回放的任务”</strong>：每次提示词、工具调用、输出都可追踪；失败可重试；成本可统计。</li>
<li><strong>把 UI 做成“可组合原语”</strong>：组件库、设计 tokens、表单与验证规则固定，AI 才能稳定地产出一致 UI。</li>
</ul></section></section>
<section><h2>7. 参考与延伸阅读<a href="#7-参考与延伸阅读"><span>#</span></a></h2></section>
<section><h2>Footnotes<a href="#footnote-label"><span>#</span></a></h2>
<ol>
<li>
<p>AI SDK by Vercel. <a href="https://ai-sdk.dev/docs/introduction" target="_blank">https://ai-sdk.dev/docs/introduction</a> <a href="#user-content-fnref-1">↩</a> <a href="#user-content-fnref-1-2">↩<sup>2</sup></a></p>
</li>
<li>
<p>Cloudflare Workers AI. <a href="https://workers.cloudflare.com/product/workers-ai/" target="_blank">https://workers.cloudflare.com/product/workers-ai/</a> <a href="#user-content-fnref-2">↩</a></p>
</li>
</ol>
</section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/fresh2%E5%BC%80%E5%8F%91%E5%8F%8A%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8/</id>
      <title type="text">fresh2开发及快速入门（1）</title>
      <published>2026-01-21T00:00:00.000Z</published>
      <updated>2026-01-21T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/fresh2%E5%BC%80%E5%8F%91%E5%8F%8A%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8/"/>
      <summary type="text">fresh2开发及快速入门（1），介绍fresh2的基本环境搭建和启动</summary>
      <content type="html"><![CDATA[<blockquote><p>之所以最先使用Fresh 2.0，完全是个人品味问题，我一直想要使用vite的HMR和esm但是又不想用vue，vite的理念是先进并且适合
AI的，但是vue的话就差点意思了。之前的Fresh一直绑定在preact上，<a href="https://deno.com/blog/fresh-and-vite" target="_blank">最近终于可以使用vite的生态了</a>，可喜可贺，手痒必须要试试。
另外，必须要说明的是Fresh 2.0是一个实验性项目，目前还不是正式版本，所以在使用过程中可能会遇到一些问题，但是瑕不掩瑜，Fresh 2.0的理念和架构确实是很有价值的。</p></blockquote>
<ol>
<li>群岛架构</li>
<li>vite生态</li>
<li>全栈</li>
</ol>
<blockquote><p>满足以上需求的目前也只有Fresh 2.0，我们可以把它现在看做Astro的全栈加强版，所以如果你也想探索一下最适合AI时代的全栈框架，一定要试一下Fresh 2.0</p></blockquote>
<section><h2>fresh2 启动！<a href="#fresh2-启动"><span>#</span></a></h2><p>首先我们需要安装<a href="https://deno.com/" target="_blank">deno</a></p><p>Mac, Linux</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>curl</span><span> </span><span>-fsSL</span><span> </span><span>https://deno.land/install.sh</span><span> | </span><span>sh</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>windows PowerShell</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>iwr</span><span> </span><span>https://deno.land/install.ps1</span><span> </span><span>-useb</span><span> | </span><span>iex</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>更多安装方式可以查看 <a href="https://github.com/denoland/deno_install" target="_blank">deno_install</a></p><p>deno也有别人开发的版本管理工具类似nvm的dvm，可以按需安装。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>i</span><span> </span><span>@axetroy/dvm</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>当安装完成后，可以使用deno run 来运行ts,可以运行官方样例，也可以自己写一个</p>
示例代码（点击展开）<div><div><div><figure><figcaption><span>hello.ts</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>serve</span><span> } </span><span>from</span><span> </span><span>"https://deno.land/std@0.208.0/http/server.ts"</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>handler</span><span> </span><span>=</span><span> </span><span>async</span><span><span> (</span><span>req</span></span><span>:</span><span> </span><span>Request</span><span>)</span><span>:</span><span> </span><span>Promise</span><span>&lt;</span><span>Response</span><span>&gt; </span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>const</span><span> </span><span>html</span><span> </span><span>=</span><span> </span><span>`</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;!DOCTYPE html&gt;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>&lt;html lang="zh-CN"&gt;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;head&gt;</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>&lt;meta charset="UTF-8"&gt;</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>&lt;title&gt;欢迎&lt;/title&gt;</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>&lt;style&gt;</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>body {</span></div></div><div><div><div>13</div></div><div><span><span>          </span></span><span>font-family: Arial, sans-serif;</span></div></div><div><div><div>14</div></div><div><span><span>          </span></span><span>display: flex;</span></div></div><div><div><div>15</div></div><div><span><span>          </span></span><span>justify-content: center;</span></div></div><div><div><div>16</div></div><div><span><span>          </span></span><span>align-items: center;</span></div></div><div><div><div>17</div></div><div><span><span>          </span></span><span>height: 100vh;</span></div></div><div><div><div>18</div></div><div><span><span>          </span></span><span>margin: 0;</span></div></div><div><div><div>19</div></div><div><span><span>          </span></span><span>background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>.welcome-container {</span></div></div><div><div><div>22</div></div><div><span><span>          </span></span><span>text-align: center;</span></div></div><div><div><div>23</div></div><div><span><span>          </span></span><span>color: white;</span></div></div><div><div><div>24</div></div><div><span><span>          </span></span><span>padding: 40px;</span></div></div><div><div><div>25</div></div><div><span><span>          </span></span><span>border-radius: 10px;</span></div></div><div><div><div>26</div></div><div><span><span>          </span></span><span>background: rgba(255, 255, 255, 0.1);</span></div></div><div><div><div>27</div></div><div><span><span>          </span></span><span>backdrop-filter: blur(10px);</span></div></div><div><div><div>28</div></div><div><span><span>          </span></span><span>box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);</span></div></div><div><div><div>29</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>30</div></div><div><span><span>        </span></span><span>h1 {</span></div></div><div><div><div>31</div></div><div><span><span>          </span></span><span>font-size: 48px;</span></div></div><div><div><div>32</div></div><div><span><span>          </span></span><span>margin-bottom: 20px;</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>34</div></div><div><span><span>        </span></span><span>p {</span></div></div><div><div><div>35</div></div><div><span><span>          </span></span><span>font-size: 18px;</span></div></div><div><div><div>36</div></div><div><span><span>          </span></span><span>margin: 10px 0;</span></div></div><div><div><div>37</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>38</div></div><div><span><span>      </span></span><span>&lt;/style&gt;</span></div></div><div><div><div>39</div></div><div><span><span>    </span></span><span>&lt;/head&gt;</span></div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>&lt;body&gt;</span></div></div><div><div><div>41</div></div><div><span><span>      </span></span><span>&lt;div class="welcome-container"&gt;</span></div></div><div><div><div>42</div></div><div><span><span>        </span></span><span>&lt;h1&gt;欢迎！&lt;/h1&gt;</span></div></div><div><div><div>43</div></div><div><span><span>        </span></span><span>&lt;p&gt;这是一个使用 Deno 运行的 TypeScript 页面&lt;/p&gt;</span></div></div><div><div><div>44</div></div><div><span><span>        </span></span><span>&lt;p&gt;当前时间: </span><span>${</span><span>new</span><span> </span><span>Date</span><span>()</span><span>.</span><span>toLocaleString</span><span>(</span><span>'</span><span>zh-CN</span><span>'</span><span>)</span><span>}</span><span>&lt;/p&gt;</span></div></div><div><div><div>45</div></div><div><span><span>      </span></span><span>&lt;/div&gt;</span></div></div><div><div><div>46</div></div><div><span><span>    </span></span><span>&lt;/body&gt;</span></div></div><div><div><div>47</div></div><div><span><span>    </span></span><span>&lt;/html&gt;</span></div></div><div><div><div>48</div></div><div><span><span>  </span></span><span>`</span><span>;</span></div></div><div><div><div>49</div></div><div>
</div></div><div><div><div>50</div></div><div><span>  </span><span>return</span><span> </span><span>new</span><span> </span><span>Response</span><span><span>(</span><span>html</span><span>, {</span></span></div></div><div><div><div>51</div></div><div><span>    </span><span>status</span><span>:</span><span> </span><span>200</span><span>,</span></div></div><div><div><div>52</div></div><div><span>    </span><span>headers</span><span>:</span><span> {</span></div></div><div><div><div>53</div></div><div><span>      </span><span>"content-type"</span><span>:</span><span> </span><span>"text/html; charset=utf-8"</span><span>,</span></div></div><div><div><div>54</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>55</div></div><div><span><span>  </span></span><span>});</span></div></div><div><div><div>56</div></div><div><span>};</span></div></div><div><div><div>57</div></div><div>
</div></div><div><div><div>58</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span>(</span><span>"服务器运行在 http://localhost:8000"</span><span>);</span></div></div><div><div><div>59</div></div><div><span>await</span><span> </span><span>serve</span><span><span>(</span><span>handler</span><span>, { </span></span><span>port</span><span>:</span><span> </span><span>8000</span><span> });</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>deno</span><span> </span><span>hello.ts</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img alt="deno 启动！" loading="lazy" width="1037" height="558" src="/_astro/deno-start.CBV4DCD8_jgjBL.webp" /><figcaption>deno 启动！</figcaption></figure><p></p><p>至此基础的环境ok了，下一步我们需要安装fresh 2.0</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>deno</span><span> </span><span>run</span><span> </span><span>-Ar</span><span> </span><span>jsr:@fresh/init</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img alt="fresh 2.0 初始化" loading="lazy" width="486" height="161" src="/_astro/fresh2-init1.BzN7g6pi_Z1uxO3F.webp" /><figcaption>fresh 2.0 初始化</figcaption></figure><p></p><p>太棒了，没想到，这个下载的就是vite版的fresh 2.0的环境，下一步我们可以开始开发了。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>deno</span><span> </span><span>run</span><span> </span><span>dev</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p></p><figure><img alt="fresh 2.0 启动" loading="lazy" width="1229" height="807" src="/_astro/fresh2-start1.BniLM-q0_1CKaNn.webp" /><figcaption>fresh 2.0 启动</figcaption></figure><p></p><p>当前的初步已经ok了，如果你对细节还有疑问可以在留言。下一章我将带你使用<b>全栈</b>+<b>Fresh</b>+<b>群岛架构</b>+<b>AI</b>开发一个好玩的游戏。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E4%B8%BA%E4%BB%80%E4%B9%88ai%E5%86%99%E7%BD%91%E9%A1%B5%E4%BB%A3%E7%A0%81%E7%9A%84%E6%95%88%E6%9E%9C%E8%BF%99%E4%B9%88%E5%A5%BD/</id>
      <title type="text">为什么AI写网页代码的效果这么好</title>
      <published>2026-01-15T00:00:00.000Z</published>
      <updated>2026-01-15T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E4%B8%BA%E4%BB%80%E4%B9%88ai%E5%86%99%E7%BD%91%E9%A1%B5%E4%BB%A3%E7%A0%81%E7%9A%84%E6%95%88%E6%9E%9C%E8%BF%99%E4%B9%88%E5%A5%BD/"/>
      <summary type="text">前端的开放与标准化，让代码生成更像“检索 + 组合 + 校验”。</summary>
      <content type="html"><![CDATA[<p>很多人把“AI 写网页代码很顺手”归因于模型更聪明了。但从一线工程视角看，更关键的原因是：前端天然具备高开放性、强规范化、强复用三件事，它们共同把问题空间压缩成了“可预测的组合题”。在这种题型里，大模型的优势会被放大。</p>
<p>这篇文章把现象拆开讲清楚：为什么前端更像大模型的“主场”，以及这种优势来自哪里、边界又在哪里。</p>
<section><h2>1. 前端的开放性，把训练数据推到极大<a href="#1-前端的开放性把训练数据推到极大"><span>#</span></a></h2><p>前端是最公开的工程领域之一：页面代码天然要交付到浏览器；生态以 npm 为中心扩散；大量实践以博客、Issue、文档、开源仓库的形式沉淀。结果是，前端的高频知识几乎都能在公开语料里被反复看到。</p><p>更重要的不是“量大”，而是分布稳定：</p><ul>
<li>HTML/CSS/JS 是 Web 的共同语言，语法和运行语义相对稳定，十年前的很多知识今天仍然有效。</li>
<li>组件、路由、状态、请求、表单、列表、布局这些“业务高频件”，在行业里高度同构，差异主要来自命名与样式。</li>
<li>工程化把离散经验收敛成少量工具链约定：构建（Vite/webpack）、类型（TypeScript）、质量（lint/format）、样式方案（Tailwind/CSS Modules/CSS-in-JS）等，最终都表现为可被模板化的结构。</li>
</ul><p>当一个领域同时满足“公开、稳定、同构”时，模型得到的不只是训练样本多，而是可重复的模式多。代码生成看起来像“凭空写”，实际更接近“在巨大的模式库里做匹配，然后把匹配结果按约束拼起来”。</p></section>
<section><h2>2. 前端代码的本质：受约束的声明式结构<a href="#2-前端代码的本质受约束的声明式结构"><span>#</span></a></h2><p>网页代码的产物，是“可运行的界面”。界面由两部分构成：</p><ul>
<li>结构与语义（HTML/组件树）</li>
<li>样式与布局（CSS/设计系统）</li>
</ul><div>

      

      

      

      

    声明式的“组合题”：输入约束 → 产出结构与样式 → 浏览器渲染

    输入
    需求 / 交互目标
    页面要展示什么、怎么用

    约束
    组件库 / 设计系统 / 类型
    可用原语与风格边界

    
    
    结构
    组件树（HTML/JSX）
    语义、层级、复用边界

    样式
    规则（CSS / tokens）
    布局、间距、主题与状态

    
    
    输出
    浏览器渲染
    DOM + CSSOM → layout/paint
    可交互界面
    事件、状态更新、重绘
    越强的约束（设计系统/组件库/工程规范），越像“拼装”，越适合规模化生成。

</div><p>这两部分都强依赖声明式，而声明式的一个特征是：局部变动往往只影响局部输出。例如，增加一个表格列、把表单校验规则改严、把卡片从两列变三列，这些都属于结构化变更，能在组件边界内被限定。</p><p>这会直接降低生成难度：模型不需要“发明新算法”，更多时候只要把常见 UI 模式落到当前工程的约束里（组件库、样式规范、数据结构、类型约束）。换句话说，前端更像一个大型的“组合系统”，而不是一个需要大量原创推理的“求解系统”。</p></section>
<section><h2>3. 单一默认值让输出更可预测<a href="#3-单一默认值让输出更可预测"><span>#</span></a></h2><p>前端并不缺框架；但工程实践的默认选项长期高度集中。一个明显的结果是：当你说“写一个后台管理系统/电商详情页/内容站点”，多数团队脑海里会浮现出近似的技术栈组合与目录结构。</p><p>这种集中带来的收益是“可预测”，代价是“创新被压住”。但就代码生成而言，可预测恰恰是优势：模型在生成时，最怕的是缺乏共识的分歧点；最擅长的是有共同模板的主流写法。</p><p>React 的长期主导地位，把大量工程实践收敛成了统一叙事：组件化、JSX、Hooks、状态与副作用的组织方式、围绕 React 的路由与数据层（再加上 Next.js 这类元框架）……这让模型面对“前端需求描述”时，能更快落到一种稳定的实现形态上。</p><p>但这也解释了一个常见体验：同一个需求下，模型在 React 生态里输出“像样代码”的概率，往往高于在一些更分散、约束更弱的技术组合里输出的概率。并不是因为 React 更“正确”，而是因为它更“标准”。</p><p>想把这种“标准化带来的副作用”讲透，可以读这篇文章：</p><ul>
<li><a href="/posts/react%E7%9A%84%E9%BB%98%E8%AE%A4%E8%83%9C%E5%88%A9%E6%AD%A3%E5%9C%A8%E6%89%BC%E6%9D%80%E5%89%8D%E7%AB%AF%E5%88%9B%E6%96%B0/">React 的默认胜利正在扼杀前端创新（译）</a></li>
</ul><p>它的核心观点可以浓缩成三句（与本文的主题互相补全）：</p><ul>
<li>React 的优势正在从“技术优势”转向“默认优势”，网络效应替代了适配度，决定架构走向。</li>
<li>生态默认值越强，新模型（编译期、细粒度响应式、可恢复性）越难得到公平评估，创新变成“有但用不上”。</li>
<li>当行业把心智模型中心搬到单一框架之上，成本不仅是性能，还有学习与迁移能力的机会成本。</li>
</ul></section>
<section><h2>4. 大模型天然站在 UI 层的上方<a href="#4-大模型天然站在-ui-层的上方"><span>#</span></a></h2><p>前端的难点并不只在代码，更在“把需求变成界面”。而 UI 层有一个工程事实：它离业务更近、离算法更远，离“表达”更近、离“求解”更远。</p><p>这对应了大模型的强项：把模糊输入映射到结构化输出，<a href="/posts/%E4%B8%96%E7%95%8C%E6%A8%A1%E5%9E%8B%E7%9A%84%E5%88%B0%E6%9D%A5/">世界模型的到来</a> 使所有抽象层将在更高的维度被模型所理解。</p><p>在现代团队里，UI 并不只是像素拼图，而是一个层次化系统：</p><ul>
<li>设计语言（排版、色彩、间距、动效）</li>
<li>组件库（Button/Input/Table/Modal）</li>
<li>交互范式（表单流、列表流、详情流、搜索筛选）</li>
<li>信息架构（导航、层级、状态）</li>
</ul><p>这些东西一旦被抽象为组件与设计 token，代码生成就变成了：把信息架构映射为组件树，把状态与数据流映射为事件与请求，把视觉规则映射为样式约束。它不依赖“灵感”，更依赖“约束下的映射”，因此更容易规模化。</p></section>
<section><h2>5. 视觉能力的进步，正在外溢到 UI 之外<a href="#5-视觉能力的进步正在外溢到-ui-之外"><span>#</span></a></h2><p>近两年的变化不只是“能写更多代码”，而是模型逐步具备了对界面与布局的理解能力：识别层级、对齐关系、间距语义、组件复用、交互状态。这类能力来自<a href="/posts/%E4%B8%96%E7%95%8C%E6%A8%A1%E5%9E%8B%E7%9A%84%E5%88%B0%E6%9D%A5/">多模态训练</a>：把文本、图像、结构化 UI 元数据放在同一个表征空间里学习。</p><p>一旦模型能稳定理解 UI，它就获得了一种更通用的能力：把连续世界（图像/空间）压缩成离散结构（层级/约束/关系）。这类表征不仅对“从设计到代码”有用，也会迁移到更广泛的物理世界任务中：场景理解、机器人操作、空间推理、视觉-语言对齐等。</p><p>从这个角度看，前端并不是一个孤立的应用场景，而像一个训练场：UI 把现实世界的空间关系（对齐、约束、层级、组合）用更可控的方式呈现出来，既足够复杂，又比真实世界更可标注、更可评估、更易规模化。这会让模型在 UI 上的进步更快，也更容易外溢到 UI 之外。</p></section>
<section><h2>6. 边界：能写出“像样代码”不等于能交付<a href="#6-边界能写出像样代码不等于能交付"><span>#</span></a></h2><p>把原因讲清楚，也必须把边界讲清楚。前端代码生成效果好，并不意味着它天然可靠：</p><ul>
<li>代码是否“像样”，与是否符合工程约束（类型、可访问性、性能预算、安全策略）是两回事。</li>
<li>模型能拼出组件树，但很容易在状态边界与数据一致性上犯错，尤其是复杂交互、多端适配、并发与缓存场景。</li>
<li>视觉对齐可以做到很像，但设计系统、可维护性与可扩展性需要经验驱动的取舍，不能只追求像素级还原。</li>
</ul><p>更合理的定位是：把模型当作“高吞吐的初稿生成器 + 有经验开发者的约束校验器”。前端之所以显得更“好用”，是因为它确实更适合这种分工。</p></section>
<section><h2>结语<a href="#结语"><span>#</span></a></h2><p>AI 写网页代码之所以效果好，并不是因为网页开发更简单，而是因为它更结构化：开放的生态让训练数据极其充足；主流栈的默认选择让实现路径高度收敛；UI 层的抽象把需求到代码的映射变成可组合的工程问题；多模态能力又进一步增强了对布局与层级的理解。</p><p>当这些条件叠加在一起，代码生成就不再像魔法，而更像一套高效的模式检索与组合机制在发挥作用。下一步真正的竞争点，不是“能不能写出来”，而是“能不能在约束下持续写对”。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/2025%E5%B9%B4%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6%E6%80%BB%E7%BB%93/</id>
      <title type="text">2025年前端框架总结</title>
      <published>2026-01-11T00:00:00.000Z</published>
      <updated>2026-01-11T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/2025%E5%B9%B4%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6%E6%80%BB%E7%BB%93/"/>
      <summary type="text">基于 2025 JavaScript Rising Stars 数据，盘点前端框架与生态走势</summary>
      <content type="html"><![CDATA[<p>下面的结论与图表基于「2025 JavaScript Rising Stars」：统计 Best of JS 收录项目在 2025 年新增的 GitHub star（近 12 个月增量），用来观察“关注度变化”而非“绝对使用量”。我在此基础上做了归纳与选型建议。</p>
<section><h2>1. 2025 的整体信号<a href="#1-2025-的整体信号"><span>#</span></a></h2><ul>
<li>AI 不再只是“聊天”，而是以“工作流/代理编排”的形态爆发，n8n 以断层优势成为年度冠军。</li>
<li>React 重新夺回“前端框架”榜首，但真正的焦点是向 Server Components/Server Functions 的迁移带来的复杂度与安全风险。</li>
<li>前端工具链继续走向“更快、更统一”：运行时、打包、格式化/Lint 的一体化趋势更明显。</li>
</ul><section><h3>1.1 年度冠军 n8n 的 2025 月度增长（新增 star）<a href="#11-年度冠军-n8n-的-2025-月度增长新增-star"><span>#</span></a></h3><div>
  
    n8n：2025 月度新增 star（k）

    0
    5
    10
    15

      

    
    峰值：14.4k（4 月）
    
      1234
      5678
      9101112

</div></section></section>
<section><h2>2. 最受欢迎的项目（Top 10）<a href="#2-最受欢迎的项目top-10"><span>#</span></a></h2><div>
  
    最受欢迎项目 Top 10：2025 新增 star
    
      1 n8n
      2 React Bits
      3 shadcn/ui
      4 Excalidraw
      5 Supabase
      6 Onlook
      7 Better Auth
      8 Dyad
      9 AFFiNE
      10 Stagehand

      +112.4k
      +32.8k
      +26.3k
      +25.1k
      +19.9k
      +19.4k
      +19.0k
      +18.9k
      +17.3k
      +17.1k

</div>

<table><thead><tr><th>排名</th><th>项目</th><th>2025 新增 star</th><th>定位</th><th>关键词</th></tr></thead><tbody><tr><td>1</td><td>n8n</td><td>+112.4k</td><td>工作流自动化平台</td><td>工作流、原生 AI、400+ 集成</td></tr><tr><td>2</td><td>React Bits</td><td>+32.8k</td><td>动效/交互组件集合</td><td>组件分发、shadcn 风格、可视化调参</td></tr><tr><td>3</td><td>shadcn/ui</td><td>+26.3k</td><td>组件库 + 注册表</td><td>无障碍、可定制、分发模式</td></tr><tr><td>4</td><td>Excalidraw</td><td>+25.1k</td><td>手绘风白板</td><td>协作、图形化表达</td></tr><tr><td>5</td><td>Supabase</td><td>+19.9k</td><td>开源 BaaS</td><td>Postgres、边缘能力、全栈后端</td></tr><tr><td>6</td><td>Onlook</td><td>+19.4k</td><td>AI 优先可视化编辑器</td><td>设计到代码、React 应用、编辑器</td></tr><tr><td>7</td><td>Better Auth</td><td>+19.0k</td><td>TypeScript 鉴权框架</td><td>插件系统、框架无关、多租户</td></tr><tr><td>8</td><td>Dyad</td><td>+18.9k</td><td>本地开源 AI 应用构建器</td><td>v0/lovable 替代、快速原型</td></tr><tr><td>9</td><td>AFFiNE</td><td>+17.3k</td><td>开源知识库/协作</td><td>Notion/Miro 替代、隐私优先</td></tr><tr><td>10</td><td>Stagehand</td><td>+17.1k</td><td>AI 浏览器自动化</td><td>测试、运营自动化、代理编排</td></tr></tbody></table><p>分析要点：</p><ul>
<li>n8n 的增长几乎“改写刻度”，2025 年从工具到生态位的关注都集中到了“工作流 + AI”。</li>
<li>Top 10 里与“React 组件分发/体验”相关的项目占比非常高（React Bits、shadcn/ui、Onlook 等），说明“做 UI 这件事”正在向“可复制粘贴的工程化积木”收敛。</li>
<li>Better Auth 这类“框架无关 + 插件化”的基础能力项目上榜，说明全栈化后对安全与工程抽象的需求在上升。</li>
</ul></section>
<section><h2>3. 前端框架（Top 5）<a href="#3-前端框架top-5"><span>#</span></a></h2><div>
  
    前端框架 Top 5：2025 新增 star
    
      React
      Ripple
      Svelte
      htmx
      Vue.js

      +11.0k
      +6.5k
      +4.6k
      +4.5k
      +4.3k

</div>

<table><thead><tr><th>排名</th><th>框架</th><th>2025 新增 star</th><th>定位</th><th>关键词</th></tr></thead><tbody><tr><td>1</td><td>React</td><td>+11.0k</td><td>UI 库/生态核心</td><td>全栈化、RSC/Server Actions、流式渲染</td></tr><tr><td>2</td><td>Ripple</td><td>+6.5k</td><td>新一代 UI 框架实验</td><td>响应式原语、组件化、早期阶段</td></tr><tr><td>3</td><td>Svelte</td><td>+4.6k</td><td>编译型框架</td><td>Svelte 5、Runes、简化状态建模</td></tr><tr><td>4</td><td>htmx</td><td>+4.5k</td><td>HTML 驱动交互</td><td>Server-first、低 JS、渐进增强</td></tr><tr><td>5</td><td>Vue.js</td><td>+4.3k</td><td>渐进式框架</td><td>生态稳定、工程化成熟</td></tr></tbody></table><p>分析要点：</p><ul>
<li>React 的“回归第一”更像是生态在押注“全栈化”：Server Components、Server Actions、流式渲染把边界推向服务端，但也放大了复杂度与安全面。</li>
<li>Ripple 是 Top 5 的新面孔：更像“下一代 UI 框架实验场”，但它是否会出现像 Next.js/Nuxt/SvelteKit 这样的元框架，是 2026 的看点。</li>
<li>Svelte 连续稳定在前三，Svelte 5 的 Runes（<code>$state/$derived/$effect</code>）逐渐成为其状态建模的主流写法。</li>
</ul></section>
<section><h2>4. React 生态（Top 5）<a href="#4-react-生态top-5"><span>#</span></a></h2><div>
  
    React 生态 Top 5：2025 新增 star
    
      React Bits
      shadcn/ui
      Excalidraw
      Onlook
      Vercel AI SDK

      +32.8k
      +26.3k
      +25.1k
      +19.4k
      +9.7k

</div>

<table><thead><tr><th>排名</th><th>项目</th><th>2025 新增 star</th><th>定位</th><th>关键词</th></tr></thead><tbody><tr><td>1</td><td>React Bits</td><td>+32.8k</td><td>动效/交互组件集合</td><td>复制粘贴、动效、展示组件</td></tr><tr><td>2</td><td>shadcn/ui</td><td>+26.3k</td><td>组件库 + 注册表</td><td>可定制、无障碍、组合式</td></tr><tr><td>3</td><td>Excalidraw</td><td>+25.1k</td><td>手绘风白板</td><td>协作、图形化表达、嵌入式</td></tr><tr><td>4</td><td>Onlook</td><td>+19.4k</td><td>AI 优先可视化编辑器</td><td>设计到代码、React、编辑器</td></tr><tr><td>5</td><td>Vercel AI SDK</td><td>+9.7k</td><td>AI 应用 SDK</td><td>流式、工具调用、RAG</td></tr></tbody></table><p>分析要点：</p><ul>
<li>“组件注册表/可复制粘贴组件”成为 UI 生产的新范式：React Bits 本身甚至以 shadcn/ui 项目形态分发。</li>
<li>Onlook 这类“AI 优先的可视化编辑器”开始进入主流讨论：它们并不是低代码的旧叙事，而是以 AI 为核心的“设计到代码”通道。</li>
</ul></section>
<section><h2>5. 后端 / 全栈（Top 5）<a href="#5-后端--全栈top-5"><span>#</span></a></h2><div>
  
    后端 / 全栈 Top 5：2025 新增 star
    
      Motia
      Payload
      Next.js
      Astro
      Hono

      +13.8k
      +8.9k
      +8.7k
      +7.2k
      +6.8k

</div>

<table><thead><tr><th>排名</th><th>框架</th><th>2025 新增 star</th><th>定位</th><th>关键词</th></tr></thead><tbody><tr><td>1</td><td>Motia</td><td>+13.8k</td><td>工作流式后端框架</td><td>编排、Step/Worker、可观测</td></tr><tr><td>2</td><td>Payload</td><td>+8.9k</td><td>Headless CMS/全栈框架</td><td>Admin、Postgres、内容建模</td></tr><tr><td>3</td><td>Next.js</td><td>+8.7k</td><td>React 元框架</td><td>RSC、Server Actions、全栈路由</td></tr><tr><td>4</td><td>Astro</td><td>+7.2k</td><td>内容优先元框架</td><td>Islands、性能、静态/SSR</td></tr><tr><td>5</td><td>Hono</td><td>+6.8k</td><td>轻量 Web 框架</td><td>Web 标准、多运行时、边缘部署</td></tr></tbody></table><p>分析要点：</p><ul>
<li>Motia 的叙事是“统一后端”：用单一抽象（Steps）覆盖 API、队列、工作流、流处理、AI agent，反映出后端工程也在向“可观测 + 状态 + 编排”收敛。</li>
<li>Astro 与 Hono 代表了“更轻的服务端”：前者适合内容型应用与性能取向，后者用 Web Standards 在多运行时之间迁移更自然。</li>
</ul></section>
<section><h2>6. 工具（Top 5）<a href="#6-工具top-5"><span>#</span></a></h2><div>
  
    工具 Top 5：2025 新增 star
    
      Bun
      Vite
      Biome
      Oxlint/Oxc Parser/Oxfmt
      Nx

      +10.8k
      +7.6k
      +6.7k
      +5.2k
      +3.7k

</div>

<table><thead><tr><th>排名</th><th>工具</th><th>2025 新增 star</th><th>定位</th><th>关键词</th></tr></thead><tbody><tr><td>1</td><td>Bun</td><td>+10.8k</td><td>JS 运行时/工具链</td><td>运行时、打包、测试、速度</td></tr><tr><td>2</td><td>Vite</td><td>+7.6k</td><td>前端构建工具</td><td>Dev Server、HMR、插件生态</td></tr><tr><td>3</td><td>Biome</td><td>+6.7k</td><td>格式化 + Lint 工具</td><td>一体化、快速、可配置</td></tr><tr><td>4</td><td>Oxlint/Oxc Parser/Oxfmt</td><td>+5.2k</td><td>Rust 工具链组件</td><td>Lint、Parser、格式化</td></tr><tr><td>5</td><td>Nx</td><td>+3.7k</td><td>Monorepo 工程工具</td><td>任务编排、缓存、工作区</td></tr></tbody></table><p>分析要点：</p><ul>
<li>“一体化工具链”在加速：运行时（Bun）、构建（Vite）、格式化/检查（Biome、Oxc）都在追求更快的默认体验。</li>
<li>2025 年基础设施工具的一个重要趋势是“系统语言化”：Rust/Go 生态的占比更高，性能成为可见的竞争点。</li>
</ul></section>
<section><h2>7. AI（Top 5）<a href="#7-aitop-5"><span>#</span></a></h2><div>
  
    AI Top 5：2025 新增 star
    
      n8n
      Dyad
      Stagehand
      Mastra
      Flowise

      +112.4k
      +18.9k
      +17.1k
      +15.0k
      +14.6k

</div>

<table><thead><tr><th>排名</th><th>项目</th><th>2025 新增 star</th><th>定位</th><th>关键词</th></tr></thead><tbody><tr><td>1</td><td>n8n</td><td>+112.4k</td><td>工作流自动化平台</td><td>工作流、代理编排、集成生态</td></tr><tr><td>2</td><td>Dyad</td><td>+18.9k</td><td>本地开源 AI 应用构建器</td><td>v0 替代、快速原型、本地优先</td></tr><tr><td>3</td><td>Stagehand</td><td>+17.1k</td><td>AI 浏览器自动化</td><td>代理、测试、运营自动化</td></tr><tr><td>4</td><td>Mastra</td><td>+15.0k</td><td>AI Agent 框架</td><td>工具调用、流程编排、可观测</td></tr><tr><td>5</td><td>Flowise</td><td>+14.6k</td><td>可视化 LLM 工作流</td><td>LangChain 风格、拖拽、RAG</td></tr></tbody></table><p>分析要点：</p><ul>
<li>2025 年的主旋律是“工作流引擎”而不是“聊天 UI”：把模型接入业务流程、让系统在触发后推理并采取动作，成为最明确的增长方向。</li>
<li>前端开发者会更频繁地遇到“浏览器自动化 + 代理编排”的需求（如 Stagehand），这会反向影响测试、运营、增长与研发流程。</li>
</ul></section>
<section><h2>8. 移动端（Top 10）<a href="#8-移动端top-10"><span>#</span></a></h2><div>
  
    移动端 Top 5：2025 新增 star
    
      Valdi
      Lynx
      Expo
      Dioxus
      React Native

      +15.9k
      +14.0k
      +10.2k
      +5.7k
      +5.0k

</div>

<table><thead><tr><th>排名</th><th>项目</th><th>2025 新增 star</th><th>定位</th><th>关键词</th></tr></thead><tbody><tr><td>1</td><td>Valdi</td><td>+15.9k</td><td>跨端 UI 框架</td><td>TypeScript、JSX、Web 风格</td></tr><tr><td>2</td><td>Lynx</td><td>+14.0k</td><td>跨端渲染/运行时</td><td>原生渲染、性能、工程化</td></tr><tr><td>3</td><td>Expo</td><td>+10.2k</td><td>React Native 平台/工具链</td><td>EAS、Router、原生能力</td></tr><tr><td>4</td><td>Dioxus</td><td>+5.7k</td><td>Rust UI 框架</td><td>跨平台、声明式、性能</td></tr><tr><td>5</td><td>React Native</td><td>+5.0k</td><td>移动端框架</td><td>跨平台、生态、原生桥接</td></tr><tr><td>6</td><td>React Native Reusables</td><td>+3.3k</td><td>RN 组件库/分发</td><td>复制粘贴、设计系统、工程化</td></tr><tr><td>7</td><td>Capacitor</td><td>+2.2k</td><td>Web 到原生容器</td><td>原生插件、Hybrid、迁移</td></tr><tr><td>8</td><td>NativeWind</td><td>+1.9k</td><td>RN 的 Tailwind 方案</td><td>实用类、样式系统、DX</td></tr><tr><td>9</td><td>gluestack-ui</td><td>+1.7k</td><td>RN 组件库</td><td>主题化、可组合、Design System</td></tr><tr><td>10</td><td>Tamagui</td><td>+1.7k</td><td>跨平台 UI Kit</td><td>RN + Web、性能、编译优化</td></tr></tbody></table><p>分析要点：</p><ul>
<li>Valdi 与 Lynx 的出现让“跨平台 UI”有了更多 Web 风格的选择：TypeScript、JSX、flexbox、热重载、CSS 等，说明开发者仍然强烈偏好 Web 生产力模型。</li>
<li>React Native 与 Expo 仍是可靠的长期下注，但 2025 的榜单也提示：移动端的“新抽象”正在涌现，尤其是在性能与交互复杂度更高的场景。</li>
</ul></section>
<section><h2>9. 2026 选型建议（面向落地）<a href="#9-2026-选型建议面向落地"><span>#</span></a></h2><ul>
<li>以“团队稳定交付”为第一目标：React + 成熟元框架仍是默认解，但需要把“客户端/服务端边界、安全面、缓存与数据生命周期”纳入工程规范。</li>
<li>以“体验/速度与简单”为第一目标：Svelte 的开发体验很强，适合新项目或内容型产品，团队学习曲线相对平滑。</li>
<li>以“内容 + 性能 + 部分交互”为第一目标：Astro 仍然非常稳，适合把交互作为“岛屿”而非全量 SPA。</li>
<li>以“AI 作为工作的一部分”为第一目标：优先考虑可编排的工作流/代理工具，把测试、运营、数据处理、内容生产纳入自动化闭环。</li>
</ul></section>
<section><h2>10. 年度大事与 2026 展望<a href="#10-年度大事与-2026-展望"><span>#</span></a></h2><p>2025 年发生了几件会改变 JS 生态结构的事：Bun 被 Anthropic 收购（Claude Code/Claude 模型背后的公司），并明确把 Bun 作为 AI 编码产品的关键基础设施之一，这也解释了为什么“一体化运行时 + 单文件可执行分发”会在 AI 编码工具/代理时代变得更重要。同样值得关注的是人才与组织的迁移：长期代表 Next.js 的 Lee Robinson 加入 Cursor，围绕 AI 编码与工作流推出面向初学者的学习内容（参考：<a href="https://www.lennysnewsletter.com/p/the-beginners-guide-to-coding-with" target="_blank">Lenny’s Newsletter 访谈</a>、<a href="https://x.com/leerob" target="_blank">Cursor Learn 相关内容</a>）；而 NuxtLabs（Nuxt/Nitro 团队）加入 Vercel，也被视作“多框架共存”在平台侧变得更现实的信号。</p><p>框架层面也在分化：Remix 3 选择淡化 React 依赖、进一步回到 Web 平台与更少的关键依赖（参考：<a href="https://www.infoq.com/news/2025/08/remix-run-v3-drops-react/" target="_blank">InfoQ 报道</a>）。而 React/Next 侧，“指令（directive）”正在从 <code>use client</code>、<code>use server</code>、<code>use cache</code> 走向影响更广的基础设施语义，带来边界清晰度与可移植性的讨论。与此同时，供应链安全风险被进一步放大：Shai-Hulud 相关的 npm 供应链攻击波及大量包与仓库，让“依赖审计、最小权限、凭证轮换、供应链防护”从最佳实践变成了刚需。</p><p>2026 的未来更像一条“平衡线”：代理工作流（能编排、能回放、可观测）会成为开发者的必备技能，但同时也要避免把控制权完全交给 AI 而牺牲工程质量、边界与安全；能否在“速度”与“可维护性”之间找到合适的中间点，将决定团队在 AI 时代的长期竞争力。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E4%B8%96%E7%95%8C%E6%A8%A1%E5%9E%8B%E7%9A%84%E5%88%B0%E6%9D%A5/</id>
      <title type="text">世界模型的到来</title>
      <published>2026-01-10T00:00:00.000Z</published>
      <updated>2026-01-10T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E4%B8%96%E7%95%8C%E6%A8%A1%E5%9E%8B%E7%9A%84%E5%88%B0%E6%9D%A5/"/>
      <summary type="text">生成式模型走向“可行动”的关键一步：把世界变成可预测、可检验、可规划的内部表征。</summary>
      <content type="html"><![CDATA[<p>我们谈“世界模型”时，经常会把它想成一个宏大名词：仿佛需要先造出一个能完整复制现实的数字孪生，才配叫世界模型。但在工程语境里，它更像一种能力组合：系统能把观测压缩成状态，把状态推进到未来，再把未来反推到可执行的动作。它不必全知全能，却必须在足够多的情境里“算得对、跑得稳、改得快”。</p>
<p>过去两年，这条路线突然变得具体了：模型开始擅长长链条推理与工具调用；多模态把抽象符号重新绑回到真实感知；机器人政策开始从“单任务特训”走向“通用技能库”；硬件平台把推理的瓶颈从算力搬到能耗、内存与带宽。世界模型不再只是研究论文里的结构，而正在变成产业链共同对齐的一套目标函数。</p>
<section><h2>1. 最新模型进步：从“生成答案”到“生成过程”<a href="#1-最新模型进步从生成答案到生成过程"><span>#</span></a></h2><p>如果把 2023 年以前的大模型概括成“更像知识检索 + 文本补全”，那么近期的明显变化是：模型越来越像在生成一个可执行的推理过程，而不是只给出一个看似正确的结论。这个变化背后是推理时扩展（inference-time scaling）与强化学习训练范式的结合：让模型在更长的思维链里做更多“中间计算”，并通过可验证奖励把正确性向过程内推。</p><img src="/images/model/tuilikuozhan.png" alt="tuilikuozhan" /><ul>
<li>图注：LIM推理的增长趋势。显示了从2022年到2025年2月发表的论文累计数量（以千为单位），基于Semantic Scholar关键词搜索，自2022年引入链式思维（Chain-of-Thought，CoT）以来，关于制度和架构的研究明显加快</li>
</ul><p>对世界模型而言，这一步非常关键。世界模型不是一句话能回答的问题，而是一个持续运行的系统：它要反复读取观测、维护记忆、调用工具、校验假设、更新计划。也就是说，世界模型天然是“多轮推理 + 多次行动”的形态。把大模型训练成更稳定的推理器，相当于先补齐了系统 2（慢思考、规划、反事实推演）的底座。</p><p>与此同时，模型的上下文窗口和外部记忆机制也在变得更工程化：长上下文让“持续对话”变成“持续建模”，而不是一次次重新开局；外部检索与工具接口把世界状态从“模型脑内的文本幻觉”转成“可以被重复验证的外部事实”。这为世界模型的第二个关键要素铺路：多模态与可检验的观测。</p></section>
<section><h2>2. 多模态的重要性：世界模型必须“接地”<a href="#2-多模态的重要性世界模型必须接地"><span>#</span></a></h2><p>语言是高压缩的抽象接口，但世界不是用语言运行的。只靠文本训练出来的模型，擅长在符号空间里做一致性，却很难保证与物理因果一致。世界模型之所以必须多模态，不是为了让模型“看图识字”更强，而是为了把预测对象从“下一段文本”升级为“下一段可观测世界状态”。</p><p>机器人领域最近的 VLA（Vision-Language-Action）路线很典型：把视觉、语言与动作放到同一个建模框架里，让“看见什么、理解什么、怎么做”变成一个端到端的闭环。相关综述里普遍强调一个趋势：从分模块流水线走向统一表征与统一优化，目标是让策略能跨任务、跨物体、甚至跨不同机器人形态泛化（Vision-Language-Action survey，2025）。这种路线在能力侧看起来像“更通用的机器人”，在世界模型侧看其实是：模型必须学会把感知映射到可行动的状态空间，并在行动后用新观测修正自己。</p><p>如果说语言模型解决的是“如何在符号里做推理”，那么多模态解决的是“如何让推理对象与世界对齐”。从这一刻开始，世界模型的核心不再是文本生成的流畅度，而是对观测、行动、反馈三者之间因果结构的掌握。</p></section>
<section><h2>3. 线性代数、分段线性区域与“高维流型”<a href="#3-线性代数分段线性区域与高维流型"><span>#</span></a></h2><p>讨论世界模型，很容易陷入直觉叙事：模型好像在脑子里画出了一张世界地图。但把它落到可计算的结构，会发现核心仍是线性代数之上的函数逼近与优化：矩阵乘法是主干，非线性激活负责把空间折叠出可用的表示。</p><p>先看数据本身。高维观测并不是“铺满”整个空间，更多时候，它们沿着受约束的子结构分布；工程里常把这种“薄的结构”叫作流型。世界模型首先要做的，是把观测压缩成更稳定、更可控的表征，让相邻关系和变化方向在表征空间里变得更清晰。</p><div>

      

      

      

      

    低维流型嵌在高维里：看起来复杂，分布却很“薄”
    
    观测空间（高维）
    图片/视频/传感器并不均匀分布，往往集中在低维子结构

    
    x
    y
    z

    

    

    

    

    表征空间（低维/可控）
    映射后相邻关系更稳定，便于插值、预测与规划

    
    u
    v

    

    
    直觉：把“薄的结构”找出来，比把“整个空间”学会更可行
  
</div><p>再看函数表达力。世界模型不仅要“表示世界”，还要在表征上做推进与预测，这就需要网络对空间有足够细的切分能力：在小范围内尽可能平滑可控，在全局上又能拼出复杂形状。分段线性网络提供了一个可量化切口：同一激活模式下，网络对输入是线性的；激活模式一变，就相当于切到另一块线性片段。Montúfar 等人在 2014 年从线性区域数量的角度分析了深层网络的表达复杂度（On the Number of Linear Regions of Deep Neural Networks，2014）。</p><div>

      

      

      

      

    线性区域：同一激活模式下是线性的，整体则是“拼接”出来的曲率
    
    输入空间（示意为 2D）
    ReLU 的开/关组合切分出许多多面体区域

    
    x₁
    x₂

    

    

    

    
    
    输出函数（1D 示意）
    每段都是直线，折点来自区域切换

    

    
    “深度”增加区域数，提升可表达的形状复杂度
  
</div><p>最后看优化。训练做的并不是把整个空间都拟合好，而是在“数据实际出现的那一小块区域”把切分做得更合适：让流型落在更稳定、更容易推进的线性片段上，让插值、外推与长时序预测更不容易跑飞。</p><div>

      

      

      

      

    训练的直觉：让“数据所在处”落进更可用的几何片段
    
    数据分布
    样本沿着流型排列

    

    

    表征 / 切分
    激活模式把空间切成线性片段

    
    
    区域内：近似线性、可插值

    任务
    预测 / 规划
    
    状态推进
    
    动作选择
    
    优化：不断调整切分，使“数据所在的区域”更好用
  
</div><p>世界模型需要的正是这种几何能力：它要在表征里做状态推进，从 (s_t) 到 (s_{t+1}) 的动力学近似、从目标到动作序列的规划、以及从误差到策略更新的闭环。没有足够丰富且可控的表示与切分，就很难在长时序任务里保持稳定。</p></section>
<section><h2>4. 具身智能的发展：从单点技能到“技能库 + 规划器”<a href="#4-具身智能的发展从单点技能到技能库--规划器"><span>#</span></a></h2><p>具身智能的变化，首先是数据与任务分布的变化：Open X-Embodiment 这类工作试图把分散的机器人数据对齐到统一格式，并训练跨任务的 RT-X 系列模型（Open X-Embodiment，2023–2025 持续更新）。当数据分布开始跨机器人、跨场景拼在一起，策略模型就不再是“机械臂 A 的拣选器”，而更像“可迁移的动作语言模型”。</p><p>其次是架构上的分层：一类路线倾向于把高层规划（语言/符号推理）与低层控制（连续动作、反馈稳定性）分开优化，形成更像“规划器 + 技能库”的系统。VLA 综述里也经常提到双系统结构：用慢的系统负责分解任务、选择工具/技能；用快的系统负责执行与闭环纠错。这种分层与世界模型的需求高度一致：世界模型必须在高层维持一个可解释的任务结构，同时在低层保证动作的物理可行性。</p><p>更重要的是，具身智能迫使模型面对一个无法回避的事实：预测必须可检验。语言任务里，错误可以被“合理化”；但在机器人里，错误会直接表现为抓不到、撞到、摔倒。可检验性把世界模型的训练目标从“看起来对”推向“真的能用”，也推动了更真实的数据、更强的仿真、更严格的评测指标。</p></section>
<section><h2>5. 黄仁勋的算力平台与“瓦力”式机器人：把世界模型做成产品形态<a href="#5-黄仁勋的算力平台与瓦力式机器人把世界模型做成产品形态"><span>#</span></a></h2><p>当世界模型从研究走向工程，最先被放大的是系统瓶颈：推理的吞吐、上下文的驻留、跨节点的通信、以及把传感器与动作回路跑到实时的能力。近期 NVIDIA 对“机柜级系统”的叙事，本质上是在把世界模型需要的计算形态产品化：从单卡指标转向整机柜的带宽、内存容量与可用性。</p><p>以 NVIDIA 在 CES 2026 公开的 Vera Rubin NVL72 机柜级系统为例，官方强调的是 MoE 推理 token 成本降低、NVLink 规模互联带宽以及系统级的内存/网络配置，目标指向的是“能持续推理、能持续上下文驻留、能支撑大规模 agent 的在线运行”（NVIDIA Newsroom：Rubin platform，2026）。你会发现这里的关键词与世界模型的需求对齐：世界模型不是离线训练一次就结束，而是要在运行时持续吸收观测、生成计划、调用工具，因而对上下文内存与互联有持续需求。</p><p>同一条叙事在机器人展示里更直观。Jensen Huang 在公开活动中展示过与 Disney Research、DeepMind 合作的机器人“Blue”，那种“瓦力”式的外形与互动方式，很容易让人把注意力从某个硬件参数挪开，转向一个更实际的问题：一个具身系统能不能在真实环境里持续感知、持续推理、持续行动（Euronews：Blue robot，2025）。当这种演示从单次 Demo 变成可复用的平台能力，世界模型就拥有了更明确的落地载体：把多模态预测与动作闭环做成标准件。<a href="https://zhuanlan.zhihu.com/p/1915178980568446515" target="_blank">GR00T N1：NVIDIA为具身智能打造的开源基础模型</a></p></section>
<section><h2>6. 年底算力和电力费用暴增的隐含逻辑：电力与内存成为世界模型的真实边界<a href="#6-年底算力和电力费用暴增的隐含逻辑电力与内存成为世界模型的真实边界"><span>#</span></a></h2><p>把世界模型当成“模型能力”会误判它的节奏；把它当成“系统能力”才能看清它的约束。算力确实在暴增，但并不等于“问题消失”，更像是瓶颈在迁移：从训练算力迁移到推理能耗，从参数规模迁移到上下文驻留，从 FLOPs 迁移到内存带宽与供应链。</p><img src="/images/model/chart2025.png" alt="chart2025" /><p>狗血的事前阵子想给自己的开发机加内存，才发现内存价格暴涨，本来计划一步到位把容量拉满，结果发现同样规格的条子在短时间内涨价明显；你会下意识地开始算账：是先上容量，还是先换更快的盘、先把显卡升级，或者干脆把预算留给下一代。它看起来只是装机的小纠结，但背后是同一个规律：当工作负载变“吃内存”，价格和供给就会先让你感到边界。</p><p>电力已经是最硬的边界之一。IEA 在关于 AI 与能源的分析中给出数据中心用电的量级与增长速度：2024 年全球数据中心用电约 415 TWh（约占全球用电 1.5%），到 2030 年在基准情景下可能接近翻倍至约 945 TWh，并指出增长主要集中在美国与中国等区域（IEA：Energy demand from AI，2024/2025）。当推理成为常态、agent 成为工作流的一部分、世界模型成为在线系统，电力就会从“成本优化项”变成“部署硬约束”：配电、制冷、选址，直接决定能跑多大规模、能跑多高利用率。</p><p>内存则是第二个硬边界。世界模型需要长上下文与多路传感器缓存，需要为推理提供足够带宽与容量，这会把 HBM 与高端 DRAM 变成系统级关键材料。TrendForce 的行业报道指出，AI 相关内存需求正在抬升并挤压供给，甚至出现对 2026 年 HBM3E 价格上调的预期与产能优先级调整（TrendForce：HBM3E 2026 price hike，2025；AI consumes DRAM capacity，2025）。当“算力”被理解成 GPU 数量时，人们往往忽略内存；但当系统跑到机柜级、跑到在线推理，内存就会以非常朴素的方式出现：带宽不够就是吞吐上不去，供给不稳就是扩容做不动。</p><p>这也解释了为什么硬件平台越来越强调“上下文内存”“机柜互联”“系统可靠性”：世界模型的价值在运行时被释放，而运行时的瓶颈正逐步由能源与内存定义。</p></section>
<section><h2>7. 世界模型什么时候真的实现<a href="#7-世界模型什么时候真的实现"><span>#</span></a></h2><p>世界模型不会以“某个模型发布日”作为分水岭，它更像一条供应链与工程范式共同推进的曲线：</p><ol>
<li>推理能力变稳定：模型能在长链条里保持正确性，并能可靠调用工具。</li>
<li>多模态变标准：预测对象从文本扩展到视觉/动作/反馈，世界开始“可检验”。</li>
<li>表示几何变可控：模型能在高维空间里形成可组合、可泛化的状态表示。</li>
<li>具身系统变平台：技能库与规划器分层，数据分布从单任务转向多任务统一。</li>
<li>硬件与能源对齐：机柜级系统把瓶颈摊开，让能耗与内存成为显式约束。</li>
</ol><p>真正值得兴奋的地方在于：世界模型把“智能”从对话框里带出来，把它放到可以被验证、被部署、被迭代的系统里。它会逼迫我们重新定义很多工程指标：正确率不够，需要稳定性；延迟不够，需要闭环；模型不够，需要系统；算力不够，需要电与内存。</p><p>世界模型的到来并不浪漫，但足够真实。</p></section>
<section><h2>参考链接<a href="#参考链接"><span>#</span></a></h2><ul>
<li>IEA：Energy demand from AI <a href="https://www.iea.org/reports/energy-and-ai/energy-demand-from-ai" target="_blank">https://www.iea.org/reports/energy-and-ai/energy-demand-from-ai</a></li>
<li>Montúfar et al. 2014：On the Number of Linear Regions of Deep Neural Networks <a href="https://arxiv.org/abs/1402.1869" target="_blank">https://arxiv.org/abs/1402.1869</a></li>
<li>Open X-Embodiment：<a href="https://arxiv.org/abs/2310.08864" target="_blank">https://arxiv.org/abs/2310.08864</a></li>
<li>VLA Survey：<a href="https://vla-survey.github.io/" target="_blank">https://vla-survey.github.io/</a></li>
<li>NVIDIA Newsroom（Rubin platform）：<a href="https://nvidianews.nvidia.com/news/rubin-platform-ai-supercomputer" target="_blank">https://nvidianews.nvidia.com/news/rubin-platform-ai-supercomputer</a></li>
<li>TrendForce（HBM3E price hike for 2026）：<a href="https://www.trendforce.com/news/2025/12/24/news-samsung-sk-hynix-reportedly-plan-20-hbm3e-price-hike-for-2026-as-nvidia-h200-asic-demand-rises/" target="_blank">https://www.trendforce.com/news/2025/12/24/news-samsung-sk-hynix-reportedly-plan-20-hbm3e-price-hike-for-2026-as-nvidia-h200-asic-demand-rises/</a></li>
<li>TrendForce（AI consumes DRAM wafer capacity in 2026）：<a href="https://www.trendforce.com/news/2025/12/26/news-ai-reportedly-to-consume-20-of-global-dram-wafer-capacity-in-2026-hbm-gddr7-lead-demand/" target="_blank">https://www.trendforce.com/news/2025/12/26/news-ai-reportedly-to-consume-20-of-global-dram-wafer-capacity-in-2026-hbm-gddr7-lead-demand/</a></li>
<li>Euronews（Nvidia’s AI robot “Blue”）：<a href="https://www.euronews.com/video/2025/03/19/nvidias-ai-robot-blue-stuns-with-live-interaction" target="_blank">https://www.euronews.com/video/2025/03/19/nvidias-ai-robot-blue-stuns-with-live-interaction</a></li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E5%88%A9%E7%94%A8%E9%A3%9E%E4%B9%A6%E5%A4%9A%E7%BB%B4%E6%96%87%E6%A1%A3%E5%AE%9E%E7%8E%B0%E5%8D%9A%E5%AE%A2%E7%9A%84%E4%BF%A1%E6%81%AF%E6%9B%B4%E6%96%B0/</id>
      <title type="text">利用飞书多维文档实现博客的信息更新</title>
      <published>2025-12-03T00:00:00.000Z</published>
      <updated>2025-12-03T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E5%88%A9%E7%94%A8%E9%A3%9E%E4%B9%A6%E5%A4%9A%E7%BB%B4%E6%96%87%E6%A1%A3%E5%AE%9E%E7%8E%B0%E5%8D%9A%E5%AE%A2%E7%9A%84%E4%BF%A1%E6%81%AF%E6%9B%B4%E6%96%B0/"/>
      <summary type="text">介绍如何使用飞书多维文档结合JavaScript脚本实现博客数据的自动化更新</summary>
      <content type="html"><![CDATA[<section><h2>前言<a href="#前言"><span>#</span></a></h2><p>在维护个人博客时，我们经常需要更新各种数据，比如书架信息、友链列表等。传统的方式是直接修改JSON文件，这样不仅效率低下，而且容易出错。有没有更便捷的方式呢？答案是肯定的！我们可以利用飞书多维文档来管理这些数据，并通过脚本自动同步到博客中。不但可以实现信息的维护，也可以在手机上实时操作。</p></section>
<section><h2>示例<a href="#示例"><span>#</span></a></h2><p></p><figure><img alt="示例图片" loading="lazy" width="1080" height="2400" src="/_astro/feishu1.DWze_sf2_XqtTx.webp" /><figcaption>示例图片</figcaption></figure>
<figure><img alt="示例图片" loading="lazy" width="1080" height="2400" src="/_astro/feishu2.jTF8FPOw_Z1ztrWH.webp" /><figcaption>示例图片</figcaption></figure>
<figure><img alt="示例图片" loading="lazy" width="1080" height="2400" src="/_astro/feishu3.hqzkclCB_2wt9vd.webp" /><figcaption>示例图片</figcaption></figure><p></p><p><a href="/bookshelf/">书架</a></p><p><a href="/friends/">友链</a></p></section>
<section><h2>参考与灵感<a href="#参考与灵感"><span>#</span></a></h2><p>本文的实现灵感来自于时歌的文章《<a href="https://www.lapis.cafe/posts/technicaltutorials/bookshelf-with-feishu/" target="_blank">基于飞书多维表格的书架实现</a>》。在这篇文章中，时歌详细介绍了如何使用Python脚本实现从飞书多维文档到博客书架数据的同步。我在其基础上进行了改进，使用JavaScript实现了更强大的功能。</p></section>
<section><h2>项目实现<a href="#项目实现"><span>#</span></a></h2><section><h3>技术栈<a href="#技术栈"><span>#</span></a></h3><ul>
<li><strong>飞书开放平台API</strong>：用于获取多维文档数据</li>
<li><strong>JavaScript</strong>：实现数据同步脚本</li>
<li><strong>Node.js</strong>：运行环境</li>
<li><strong>Axios</strong>：处理HTTP请求</li>
<li><strong>Sharp</strong>：图片处理和压缩</li>
<li><strong>Dotenv</strong>：环境变量管理</li>
</ul></section><section><h3>功能特点<a href="#功能特点"><span>#</span></a></h3><p>与原Python版本相比，JavaScript版本的脚本增加了以下功能和改进：</p><ol>
<li><strong>多数据类型支持</strong>：不仅支持书架数据，还支持友链数据的同步</li>
<li><strong>图片自动压缩</strong>：自动将图片压缩为WebP格式，优化博客加载速度</li>
<li><strong>智能质量调整</strong>：根据文件大小自动调整图片质量，确保图片质量和加载速度的平衡</li>
<li><strong>友好的日志输出</strong>：提供清晰的进度提示和错误信息</li>
<li><strong>跨平台支持</strong>：JavaScript版本可以在更多平台上运行</li>
</ol></section><section><h3>核心实现<a href="#核心实现"><span>#</span></a></h3><p>脚本的核心功能主要包括以下几个部分：</p><ol>
<li><strong>获取访问令牌</strong>：通过飞书开放平台API获取访问令牌</li>
<li><strong>读取多维文档数据</strong>：从飞书多维文档中读取书架和友链数据</li>
<li><strong>处理数据</strong>：对数据进行格式转换和图片处理</li>
<li><strong>保存数据</strong>：将处理后的数据保存为JSON文件，供博客使用</li>
</ol></section><section><h3>项目配置<a href="#项目配置"><span>#</span></a></h3><p>在package.json中，我们添加了专门的脚本命令：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>"scripts"</span><span>: {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>"book"</span><span>: </span><span>"node scripts/feishu_bitable.js"</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这样，我们可以通过运行<code>pnpm run book</code>来执行数据同步。</p></section></section>
<section><h2>使用方法<a href="#使用方法"><span>#</span></a></h2><section><h3>1. 配置飞书开放平台<a href="#1-配置飞书开放平台"><span>#</span></a></h3><p>首先，需要在飞书开放平台创建应用，并获取以下信息：</p><ul>
<li>APP_ID</li>
<li>APP_SECRET</li>
<li>BITABLE_ID</li>
<li>TABLE_ID_BOOKS</li>
<li>TABLE_ID_FRIENDS</li>
</ul></section><section><h3>2. 配置环境变量<a href="#2-配置环境变量"><span>#</span></a></h3><p>在项目根目录创建.env文件，并添加以下内容：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>FEISHU_APP_ID=your_app_id</span></div></div><div><div><div>2</div></div><div><span>FEISHU_APP_SECRET=your_app_secret</span></div></div><div><div><div>3</div></div><div><span>FEISHU_BITABLE_ID=your_bitable_id</span></div></div><div><div><div>4</div></div><div><span>FEISHU_TABLE_ID_BOOKS=your_books_table_id</span></div></div><div><div><div>5</div></div><div><span>FEISHU_TABLE_ID_FRIENDS=your_friends_table_id</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3. 运行同步脚本<a href="#3-运行同步脚本"><span>#</span></a></h3><p>执行以下命令即可完成数据同步：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>pnpm</span><span> </span><span>run</span><span> </span><span>book</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>脚本会自动完成从飞书多维文档到本地JSON文件的数据同步，并处理好图片。</p></section></section>
<section><h2>项目结构<a href="#项目结构"><span>#</span></a></h2><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>scripts/</span></div></div><div><div><div>2</div></div><div><span>└── feishu_bitable.js    # 飞书多维文档同步脚本</span></div></div><div><div><div>3</div></div><div><span>public/</span></div></div><div><div><div>4</div></div><div><span>├── data/</span></div></div><div><div><div>5</div></div><div><span>│   ├── books.json       # 书架数据</span></div></div><div><div><div>6</div></div><div><span>│   └── friends.json     # 友链数据</span></div></div><div><div><div>7</div></div><div><span>└── images/</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>└── books/           # 书架图片保存目录</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>功能细节<a href="#功能细节"><span>#</span></a></h2><section><h3>图片处理<a href="#图片处理"><span>#</span></a></h3><p>脚本会自动下载飞书多维文档中的图片，并进行压缩处理：</p><ul>
<li>调整图片尺寸，最大宽度800px，最大高度1200px</li>
<li>转换为WebP格式，提高加载速度</li>
<li>自动调整图片质量，确保文件大小不超过300KB</li>
<li>为图片生成唯一的文件名，避免重复下载</li>
</ul></section><section><h3>数据格式转换<a href="#数据格式转换"><span>#</span></a></h3><p>将飞书多维文档中的数据转换为博客所需的格式：</p><p><strong>书架数据示例：</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span>  </span><span>"title"</span><span>: </span><span>"书名"</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>"author"</span><span>: </span><span>"作者"</span><span>,</span></div></div><div><div><div>4</div></div><div><span>  </span><span>"cover"</span><span>: </span><span>"封面图片URL"</span><span>,</span></div></div><div><div><div>5</div></div><div><span>  </span><span>"status"</span><span>: </span><span>"阅读状态"</span><span>,</span></div></div><div><div><div>6</div></div><div><span>  </span><span>"rating"</span><span>: </span><span>"评分"</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>友链数据示例：</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span></div></div><div><div><div>2</div></div><div><span>  </span><span>"title"</span><span>: </span><span>"站点名称"</span><span>,</span></div></div><div><div><div>3</div></div><div><span>  </span><span>"imgurl"</span><span>: </span><span>"头像图地址"</span><span>,</span></div></div><div><div><div>4</div></div><div><span>  </span><span>"desc"</span><span>: </span><span>"站点描述"</span><span>,</span></div></div><div><div><div>5</div></div><div><span>  </span><span>"siteurl"</span><span>: </span><span>"站点地址"</span><span>,</span></div></div><div><div><div>6</div></div><div><span>  </span><span>"tags"</span><span>: [</span><span>"标签"</span><span>]</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h2>完整代码实现<a href="#完整代码实现"><span>#</span></a></h2><p>以下是<code>feishu_bitable.js</code>的完整实现代码：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>fs</span><span> </span><span>from</span><span> </span><span>'fs'</span><span>;</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>path</span><span> </span><span>from</span><span> </span><span>'path'</span><span>;</span></div></div><div><div><div>3</div></div><div><span>import</span><span> { </span><span>fileURLToPath</span><span> } </span><span>from</span><span> </span><span>'url'</span><span>;</span></div></div><div><div><div>4</div></div><div><span>import</span><span> </span><span>crypto</span><span> </span><span>from</span><span> </span><span>'crypto'</span><span>;</span></div></div><div><div><div>5</div></div><div><span>import</span><span> </span><span>axios</span><span> </span><span>from</span><span> </span><span>'axios'</span><span>;</span></div></div><div><div><div>6</div></div><div><span>import</span><span> </span><span>sharp</span><span> </span><span>from</span><span> </span><span>'sharp'</span><span>;</span></div></div><div><div><div>7</div></div><div><span>import</span><span> </span><span>dotenv</span><span> </span><span>from</span><span> </span><span>'dotenv'</span><span>;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>// 在ES模块中定义__filename和__dirname</span></div></div><div><div><div>10</div></div><div><span>const</span><span> </span><span>__filename</span><span> </span><span>=</span><span> </span><span>fileURLToPath</span><span>(</span><span>import</span><span>.</span><span>meta</span><span>.</span><span>url</span><span>);</span></div></div><div><div><div>11</div></div><div><span>const</span><span> </span><span>__dirname</span><span> </span><span>=</span><span><span> </span><span>path</span><span>.</span></span><span>dirname</span><span><span>(</span><span>__filename</span><span>);</span></span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// 加载.env文件中的环境变量</span></div></div><div><div><div>14</div></div><div><span><span>dotenv</span><span>.</span></span><span>config</span><span>();</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>// 飞书开放平台的配置</span></div></div><div><div><div>17</div></div><div><span>const</span><span> </span><span>APP_ID</span><span> </span><span>=</span><span><span> </span><span>process</span><span>.</span></span><span>env</span><span>.</span><span>FEISHU_APP_ID</span><span>;</span></div></div><div><div><div>18</div></div><div><span>const</span><span> </span><span>APP_SECRET</span><span> </span><span>=</span><span><span> </span><span>process</span><span>.</span></span><span>env</span><span>.</span><span>FEISHU_APP_SECRET</span><span>;</span></div></div><div><div><div>19</div></div><div><span>const</span><span> </span><span>BITABLE_ID</span><span> </span><span>=</span><span><span> </span><span>process</span><span>.</span></span><span>env</span><span>.</span><span>FEISHU_BITABLE_ID</span><span>;  </span><span>// 多维表格 ID</span></div></div><div><div><div>20</div></div><div><span>const</span><span> </span><span>TABLE_ID_BOOKS</span><span> </span><span>=</span><span><span> </span><span>process</span><span>.</span></span><span>env</span><span>.</span><span>FEISHU_TABLE_ID_BOOKS</span><span>;      </span><span>// 数据表 ID</span></div></div><div><div><div>21</div></div><div><span>const</span><span> </span><span>TABLE_ID_FRIENDS</span><span> </span><span>=</span><span><span> </span><span>process</span><span>.</span></span><span>env</span><span>.</span><span>FEISHU_TABLE_ID_FRIENDS</span><span>;      </span><span>// 数据表 ID</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>// 图片压缩配置</span></div></div><div><div><div>26</div></div><div><span>const</span><span> </span><span>MAX_SIZE</span><span> </span><span>=</span><span> { </span><span>width</span><span>:</span><span> </span><span>800</span><span>, </span><span>height</span><span>:</span><span> </span><span>1200</span><span> };  </span><span>// 最大尺寸</span></div></div><div><div><div>27</div></div><div><span>const</span><span> </span><span>WEBP_QUALITY</span><span> </span><span>=</span><span> </span><span>85</span><span>;  </span><span>// WebP质量（1-100）</span></div></div><div><div><div>28</div></div><div><span>const</span><span> </span><span>MAX_FILE_SIZE</span><span> </span><span>=</span><span> </span><span>300</span><span> </span><span>*</span><span> </span><span>1024</span><span>;  </span><span>// 300KB</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>/**</span></div></div><div><div><div>31</div></div><div><span><span> </span></span><span>* 获取飞书应用的 tenant_access_token</span></div></div><div><div><div>32</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>33</div></div><div><span>async</span><span> </span><span>function</span><span> </span><span>getTenantAccessToken</span><span>() {</span></div></div><div><div><div>34</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"[步骤1/4] 获取飞书应用访问令牌 (tenant_access_token)..."</span><span>);</span></div></div><div><div><div>35</div></div><div><span>  </span><span>try</span><span> {</span></div></div><div><div><div>36</div></div><div><span>    </span><span>const</span><span> </span><span>url</span><span> </span><span>=</span><span> </span><span>"https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"</span><span>;</span></div></div><div><div><div>37</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"  发送请求到飞书认证服务器..."</span><span>);</span></div></div><div><div><div>38</div></div><div><span>    </span><span>const</span><span> </span><span>response</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>axios</span><span>.</span></span><span>post</span><span><span>(</span><span>url</span><span>, {</span></span></div></div><div><div><div>39</div></div><div><span>      </span><span>app_id</span><span>:</span><span> </span><span>APP_ID</span><span>,</span></div></div><div><div><div>40</div></div><div><span>      </span><span>app_secret</span><span>:</span><span> </span><span>APP_SECRET</span></div></div><div><div><div>41</div></div><div><span><span>    </span></span><span>}, {</span></div></div><div><div><div>42</div></div><div><span>      </span><span>headers</span><span>:</span><span> {</span></div></div><div><div><div>43</div></div><div><span>        </span><span>"Content-Type"</span><span>:</span><span> </span><span>"application/json; charset=utf-8"</span></div></div><div><div><div>44</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>45</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>46</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"  ✅ 成功获取访问令牌 "</span><span>);</span></div></div><div><div><div>47</div></div><div><span>    </span><span>return</span><span><span> </span><span>response</span><span>.</span></span><span>data</span><span>.</span><span>tenant_access_token</span><span>;</span></div></div><div><div><div>48</div></div><div><span><span>  </span></span><span>} </span><span>catch</span><span><span> (</span><span>error</span><span>) {</span></span></div></div><div><div><div>49</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>"  ❌ 获取访问令牌失败:"</span><span><span>, </span><span>error</span><span>.</span></span><span>message</span><span>);</span></div></div><div><div><div>50</div></div><div><span>    </span><span>return</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>51</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>52</div></div><div><span>}</span></div></div><div><div><div>53</div></div><div>
</div></div><div><div><div>54</div></div><div><span>/**</span></div></div><div><div><div>55</div></div><div><span><span> </span></span><span>* 获取多维表格中的BOOKS记录</span></div></div><div><div><div>56</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>57</div></div><div><span>async</span><span> </span><span>function</span><span> </span><span>getBitableRecords</span><span>() {</span></div></div><div><div><div>58</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"[步骤2/4] 从飞书多维表格获取数据..."</span><span>);</span></div></div><div><div><div>59</div></div><div><span>  </span><span>try</span><span> {</span></div></div><div><div><div>60</div></div><div><span>    </span><span>const</span><span> </span><span>token</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>getTenantAccessToken</span><span>();</span></div></div><div><div><div>61</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span><span>token</span><span>) {</span></span></div></div><div><div><div>62</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>"  ❌ 无法继续，缺少访问令牌"</span><span>);</span></div></div><div><div><div>63</div></div><div><span>      </span><span>return</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>64</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>65</div></div><div>
</div></div><div><div><div>66</div></div><div><span>    </span><span>const</span><span> </span><span>url</span><span> </span><span>=</span><span> </span><span>`https://open.feishu.cn/open-apis/bitable/v1/apps/</span><span>${</span><span>BITABLE_ID</span><span>}</span><span>/tables/</span><span>${</span><span>TABLE_ID_BOOKS</span><span>}</span><span>/records`</span><span>;</span></div></div><div><div><div>67</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  发送请求到飞书多维表格 API...`</span><span>);</span></div></div><div><div><div>68</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  多维表格ID: </span><span>${</span><span>BITABLE_ID</span><span>}</span><span>, 数据表ID: </span><span>${</span><span>TABLE_ID_BOOKS</span><span>}</span><span>`</span><span>);</span></div></div><div><div><div>69</div></div><div>
</div></div><div><div><div>70</div></div><div><span>    </span><span>const</span><span> </span><span>response</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>axios</span><span>.</span></span><span>get</span><span><span>(</span><span>url</span><span>, {</span></span></div></div><div><div><div>71</div></div><div><span>      </span><span>headers</span><span>:</span><span> {</span></div></div><div><div><div>72</div></div><div><span>        </span><span>"Authorization"</span><span>:</span><span> </span><span>`Bearer </span><span>${</span><span>token</span><span>}</span><span>`</span><span>,</span></div></div><div><div><div>73</div></div><div><span>        </span><span>"Content-Type"</span><span>:</span><span> </span><span>"application/json"</span></div></div><div><div><div>74</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>75</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>76</div></div><div>
</div></div><div><div><div>77</div></div><div><span>    </span><span>const</span><span> </span><span>recordCount</span><span> </span><span>=</span><span><span> </span><span>response</span><span>.</span></span><span>data</span><span>?.</span><span>data</span><span>?.</span><span>items</span><span>?.</span><span>length</span><span> </span><span>||</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>78</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  ✅ 成功获取 </span><span>${</span><span>recordCount</span><span>}</span><span> 条记录`</span><span>);</span></div></div><div><div><div>79</div></div><div><span>    </span><span>return</span><span><span> </span><span>response</span><span>.</span></span><span>data</span><span>;</span></div></div><div><div><div>80</div></div><div><span><span>  </span></span><span>} </span><span>catch</span><span><span> (</span><span>error</span><span>) {</span></span></div></div><div><div><div>81</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>"  ❌ 获取多维表格记录失败:"</span><span><span>, </span><span>error</span><span>.</span></span><span>message</span><span>);</span></div></div><div><div><div>82</div></div><div><span>    </span><span>return</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>83</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>84</div></div><div><span>}</span></div></div><div><div><div>85</div></div><div><span>/**</span></div></div><div><div><div>86</div></div><div><span><span> </span></span><span>* 获取多维表格中的BOOKS记录</span></div></div><div><div><div>87</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>88</div></div><div><span>async</span><span> </span><span>function</span><span> </span><span>getBitableRecords_Friends</span><span>() {</span></div></div><div><div><div>89</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"[步骤2/4] 从飞书多维表格获取FRIENDS数据..."</span><span>);</span></div></div><div><div><div>90</div></div><div><span>  </span><span>try</span><span> {</span></div></div><div><div><div>91</div></div><div><span>    </span><span>const</span><span> </span><span>token</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>getTenantAccessToken</span><span>();</span></div></div><div><div><div>92</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span><span>token</span><span>) {</span></span></div></div><div><div><div>93</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>"  ❌ 无法继续，缺少访问令牌"</span><span>);</span></div></div><div><div><div>94</div></div><div><span>      </span><span>return</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>95</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>96</div></div><div>
</div></div><div><div><div>97</div></div><div><span>    </span><span>const</span><span> </span><span>url</span><span> </span><span>=</span><span> </span><span>`https://open.feishu.cn/open-apis/bitable/v1/apps/</span><span>${</span><span>BITABLE_ID</span><span>}</span><span>/tables/</span><span>${</span><span>TABLE_ID_FRIENDS</span><span>}</span><span>/records`</span><span>;</span></div></div><div><div><div>98</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  发送请求到飞书多维表格 API...`</span><span>);</span></div></div><div><div><div>99</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  多维表格ID: </span><span>${</span><span>BITABLE_ID</span><span>}</span><span>, 数据表ID: </span><span>${</span><span>TABLE_ID_FRIENDS</span><span>}</span><span>`</span><span>);</span></div></div><div><div><div>100</div></div><div>
</div></div><div><div><div>101</div></div><div><span>    </span><span>const</span><span> </span><span>response</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>axios</span><span>.</span></span><span>get</span><span><span>(</span><span>url</span><span>, {</span></span></div></div><div><div><div>102</div></div><div><span>      </span><span>headers</span><span>:</span><span> {</span></div></div><div><div><div>103</div></div><div><span>        </span><span>"Authorization"</span><span>:</span><span> </span><span>`Bearer </span><span>${</span><span>token</span><span>}</span><span>`</span><span>,</span></div></div><div><div><div>104</div></div><div><span>        </span><span>"Content-Type"</span><span>:</span><span> </span><span>"application/json"</span></div></div><div><div><div>105</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>106</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>107</div></div><div>
</div></div><div><div><div>108</div></div><div><span>    </span><span>const</span><span> </span><span>recordCount</span><span> </span><span>=</span><span><span> </span><span>response</span><span>.</span></span><span>data</span><span>?.</span><span>data</span><span>?.</span><span>items</span><span>?.</span><span>length</span><span> </span><span>||</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>109</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  ✅ 成功获取 </span><span>${</span><span>recordCount</span><span>}</span><span> 条记录`</span><span>);</span></div></div><div><div><div>110</div></div><div><span>    </span><span>return</span><span><span> </span><span>response</span><span>.</span></span><span>data</span><span>;</span></div></div><div><div><div>111</div></div><div><span><span>  </span></span><span>} </span><span>catch</span><span><span> (</span><span>error</span><span>) {</span></span></div></div><div><div><div>112</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>"  ❌ 获取多维表格记录失败:"</span><span><span>, </span><span>error</span><span>.</span></span><span>message</span><span>);</span></div></div><div><div><div>113</div></div><div><span>    </span><span>return</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>114</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>115</div></div><div><span>}</span></div></div><div><div><div>116</div></div><div><span>/**</span></div></div><div><div><div>117</div></div><div><span><span> </span></span><span>* 压缩图片为WebP格式</span></div></div><div><div><div>118</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>119</div></div><div><span>async</span><span> </span><span>function</span><span> </span><span>compressImage</span><span><span>(</span><span>imageBuffer</span><span>) {</span></span></div></div><div><div><div>120</div></div><div><span>  </span><span>try</span><span> {</span></div></div><div><div><div>121</div></div><div><span>    </span><span>// 获取图片信息</span></div></div><div><div><div>122</div></div><div><span>    </span><span>const</span><span> </span><span>imageInfo</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>sharp</span><span><span>(</span><span>imageBuffer</span><span>).</span></span><span>metadata</span><span>();</span></div></div><div><div><div>123</div></div><div>
</div></div><div><div><div>124</div></div><div><span>    </span><span>// 计算新尺寸（保持宽高比）</span></div></div><div><div><div>125</div></div><div><span>    </span><span>let</span><span><span> </span><span>newWidth</span><span> </span></span><span>=</span><span><span> </span><span>imageInfo</span><span>.</span></span><span>width</span><span>;</span></div></div><div><div><div>126</div></div><div><span>    </span><span>let</span><span><span> </span><span>newHeight</span><span> </span></span><span>=</span><span><span> </span><span>imageInfo</span><span>.</span></span><span>height</span><span>;</span></div></div><div><div><div>127</div></div><div>
</div></div><div><div><div>128</div></div><div><span>    </span><span>if</span><span><span> (</span><span>newWidth</span><span> </span></span><span>&gt;</span><span> </span><span>MAX_SIZE</span><span>.</span><span>width</span><span> </span><span>||</span><span><span> </span><span>newHeight</span><span> </span></span><span>&gt;</span><span> </span><span>MAX_SIZE</span><span>.</span><span>height</span><span>) {</span></div></div><div><div><div>129</div></div><div><span>      </span><span>const</span><span> </span><span>widthRatio</span><span> </span><span>=</span><span> </span><span>MAX_SIZE</span><span>.</span><span>width</span><span> </span><span>/</span><span><span> </span><span>newWidth</span><span>;</span></span></div></div><div><div><div>130</div></div><div><span>      </span><span>const</span><span> </span><span>heightRatio</span><span> </span><span>=</span><span> </span><span>MAX_SIZE</span><span>.</span><span>height</span><span> </span><span>/</span><span><span> </span><span>newHeight</span><span>;</span></span></div></div><div><div><div>131</div></div><div><span>      </span><span>const</span><span> </span><span>ratio</span><span> </span><span>=</span><span><span> </span><span>Math</span><span>.</span></span><span>min</span><span><span>(</span><span>widthRatio</span><span>, </span><span>heightRatio</span><span>);</span></span></div></div><div><div><div>132</div></div><div>
</div></div><div><div><div>133</div></div><div><span><span>      </span></span><span>newWidth</span><span> </span><span>=</span><span><span> </span><span>Math</span><span>.</span></span><span>round</span><span><span>(</span><span>newWidth</span><span> </span></span><span>*</span><span><span> </span><span>ratio</span><span>);</span></span></div></div><div><div><div>134</div></div><div><span><span>      </span></span><span>newHeight</span><span> </span><span>=</span><span><span> </span><span>Math</span><span>.</span></span><span>round</span><span><span>(</span><span>newHeight</span><span> </span></span><span>*</span><span><span> </span><span>ratio</span><span>);</span></span></div></div><div><div><div>135</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>136</div></div><div>
</div></div><div><div><div>137</div></div><div><span>    </span><span>// 初始质量</span></div></div><div><div><div>138</div></div><div><span>    </span><span>let</span><span><span> </span><span>quality</span><span> </span></span><span>=</span><span> </span><span>WEBP_QUALITY</span><span>;</span></div></div><div><div><div>139</div></div><div><span>    </span><span>let</span><span><span> </span><span>compressedBuffer</span><span>;</span></span></div></div><div><div><div>140</div></div><div>
</div></div><div><div><div>141</div></div><div><span>    </span><span>// 尝试不同的质量直到文件大小符合要求</span></div></div><div><div><div>142</div></div><div><span>    </span><span>while</span><span> (</span><span>true</span><span>) {</span></div></div><div><div><div>143</div></div><div><span><span>      </span></span><span>compressedBuffer</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>sharp</span><span><span>(</span><span>imageBuffer</span><span>)</span></span></div></div><div><div><div>144</div></div><div><span><span>        </span></span><span>.</span><span>resize</span><span><span>(</span><span>newWidth</span><span>, </span><span>newHeight</span><span>, { </span></span><span>fit</span><span>:</span><span> </span><span>'inside'</span><span>, </span><span>withoutEnlargement</span><span>:</span><span> </span><span>true</span><span> })</span></div></div><div><div><div>145</div></div><div><span><span>        </span></span><span>.</span><span>webp</span><span>({</span></div></div><div><div><div>146</div></div><div><span>          </span><span>quality</span><span>:</span><span><span> </span><span>quality</span><span>,</span></span></div></div><div><div><div>147</div></div><div><span>          </span><span>effort</span><span>:</span><span> </span><span>4</span><span>,</span></div></div><div><div><div>148</div></div><div><span>          </span><span>lossless</span><span>:</span><span> </span><span>false</span></div></div><div><div><div>149</div></div><div><span><span>        </span></span><span>})</span></div></div><div><div><div>150</div></div><div><span><span>        </span></span><span>.</span><span>toBuffer</span><span>();</span></div></div><div><div><div>151</div></div><div>
</div></div><div><div><div>152</div></div><div><span>      </span><span>// 如果文件够小或质量已经很低，就退出</span></div></div><div><div><div>153</div></div><div><span>      </span><span>if</span><span><span> (</span><span>compressedBuffer</span><span>.</span></span><span>length</span><span> </span><span>&lt;=</span><span> </span><span>MAX_FILE_SIZE</span><span> </span><span>||</span><span><span> </span><span>quality</span><span> </span></span><span>&lt;=</span><span> </span><span>40</span><span>) {</span></div></div><div><div><div>154</div></div><div><span>        </span><span>break</span><span>;</span></div></div><div><div><div>155</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>156</div></div><div>
</div></div><div><div><div>157</div></div><div><span>      </span><span>// 否则降低质量继续尝试</span></div></div><div><div><div>158</div></div><div><span><span>      </span></span><span>quality</span><span> </span><span>-=</span><span> </span><span>5</span><span>;</span></div></div><div><div><div>159</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>160</div></div><div>
</div></div><div><div><div>161</div></div><div><span>    </span><span>return</span><span><span> </span><span>compressedBuffer</span><span>;</span></span></div></div><div><div><div>162</div></div><div><span><span>  </span></span><span>} </span><span>catch</span><span><span> (</span><span>error</span><span>) {</span></span></div></div><div><div><div>163</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>"Image compression error:"</span><span><span>, </span><span>error</span><span>.</span></span><span>message</span><span>);</span></div></div><div><div><div>164</div></div><div><span>    </span><span>throw</span><span><span> </span><span>error</span><span>;</span></span></div></div><div><div><div>165</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>166</div></div><div><span>}</span></div></div><div><div><div>167</div></div><div>
</div></div><div><div><div>168</div></div><div><span>/**</span></div></div><div><div><div>169</div></div><div><span><span> </span></span><span>* 下载图片并返回本地路径</span></div></div><div><div><div>170</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>171</div></div><div><span>async</span><span> </span><span>function</span><span> </span><span>downloadImage</span><span><span>(</span><span>url</span><span>, </span><span>token</span><span>, </span><span>saveDir</span><span>) {</span></span></div></div><div><div><div>172</div></div><div><span>  </span><span>try</span><span> {</span></div></div><div><div><div>173</div></div><div><span>    </span><span>// 生成文件名（使用URL的哈希值）</span></div></div><div><div><div>174</div></div><div><span>    </span><span>const</span><span> </span><span>urlHash</span><span> </span><span>=</span><span><span> </span><span>crypto</span><span>.</span></span><span>createHash</span><span>(</span><span>'md5'</span><span>).</span><span>update</span><span><span>(</span><span>url</span><span>).</span></span><span>digest</span><span>(</span><span>'hex'</span><span>);</span></div></div><div><div><div>175</div></div><div><span>    </span><span>const</span><span> </span><span>filename</span><span> </span><span>=</span><span> </span><span>`</span><span>${</span><span>urlHash</span><span>}</span><span>.webp`</span><span>;</span></div></div><div><div><div>176</div></div><div><span>    </span><span>const</span><span> </span><span>localPath</span><span> </span><span>=</span><span><span> </span><span>path</span><span>.</span></span><span>join</span><span><span>(</span><span>saveDir</span><span>, </span><span>filename</span><span>);</span></span></div></div><div><div><div>177</div></div><div><span>    </span><span>const</span><span> </span><span>webPath</span><span> </span><span>=</span><span> </span><span>`/images/books/</span><span>${</span><span>filename</span><span>}</span><span>`</span><span>;</span></div></div><div><div><div>178</div></div><div>
</div></div><div><div><div>179</div></div><div><span>    </span><span>// 如果文件已存在，直接返回路径</span></div></div><div><div><div>180</div></div><div><span>    </span><span>if</span><span><span> (</span><span>fs</span><span>.</span></span><span>existsSync</span><span><span>(</span><span>localPath</span><span>)) {</span></span></div></div><div><div><div>181</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`Image already exists: </span><span>${</span><span>filename</span><span>}</span><span>`</span><span>);</span></div></div><div><div><div>182</div></div><div><span>      </span><span>return</span><span><span> </span><span>webPath</span><span>;</span></span></div></div><div><div><div>183</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>184</div></div><div>
</div></div><div><div><div>185</div></div><div><span>    </span><span>// 下载图片</span></div></div><div><div><div>186</div></div><div><span>    </span><span>const</span><span> </span><span>response</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>axios</span><span>.</span></span><span>get</span><span><span>(</span><span>url</span><span>, {</span></span></div></div><div><div><div>187</div></div><div><span>      </span><span>headers</span><span>:</span><span> { </span><span>"Authorization"</span><span>:</span><span> </span><span>`Bearer </span><span>${</span><span>token</span><span>}</span><span>`</span><span> },</span></div></div><div><div><div>188</div></div><div><span>      </span><span>responseType</span><span>:</span><span> </span><span>'arraybuffer'</span></div></div><div><div><div>189</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>190</div></div><div>
</div></div><div><div><div>191</div></div><div><span>    </span><span>// 压缩图片</span></div></div><div><div><div>192</div></div><div><span>    </span><span>const</span><span> </span><span>compressedBuffer</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>compressImage</span><span><span>(</span><span>Buffer</span><span>.</span></span><span>from</span><span><span>(</span><span>response</span><span>.</span></span><span>data</span><span>));</span></div></div><div><div><div>193</div></div><div>
</div></div><div><div><div>194</div></div><div><span>    </span><span>// 保存压缩后的图片</span></div></div><div><div><div>195</div></div><div><span><span>    </span></span><span>fs</span><span>.</span><span>writeFileSync</span><span><span>(</span><span>localPath</span><span>, </span><span>compressedBuffer</span><span>);</span></span></div></div><div><div><div>196</div></div><div>
</div></div><div><div><div>197</div></div><div><span>    </span><span>const</span><span> </span><span>originalSize</span><span> </span><span>=</span><span><span> </span><span>Buffer</span><span>.</span></span><span>from</span><span><span>(</span><span>response</span><span>.</span></span><span>data</span><span>).</span><span>length</span><span> </span><span>/</span><span> </span><span>1024</span><span>;  </span><span>// KB</span></div></div><div><div><div>198</div></div><div><span>    </span><span>const</span><span> </span><span>compressedSize</span><span> </span><span>=</span><span><span> </span><span>compressedBuffer</span><span>.</span></span><span>length</span><span> </span><span>/</span><span> </span><span>1024</span><span>;  </span><span>// KB</span></div></div><div><div><div>199</div></div><div><span>    </span><span>const</span><span> </span><span>compressionRatio</span><span> </span><span>=</span><span> (</span><span>1</span><span> </span><span>-</span><span><span> </span><span>compressedSize</span><span> </span></span><span>/</span><span><span> </span><span>originalSize</span><span>) </span></span><span>*</span><span> </span><span>100</span><span>;</span></div></div><div><div><div>200</div></div><div>
</div></div><div><div><div>201</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`Downloaded: </span><span>${</span><span>filename</span><span>}</span><span> (Original: </span><span>${</span><span>originalSize</span><span>.</span><span>toFixed</span><span>(</span><span>1</span><span>)</span><span>}</span><span>KB, Compressed: </span><span>${</span><span>compressedSize</span><span>.</span><span>toFixed</span><span>(</span><span>1</span><span>)</span><span>}</span><span>KB, Saved: </span><span>${</span><span>compressionRatio</span><span>.</span><span>toFixed</span><span>(</span><span>1</span><span>)</span><span>}</span><span>%)`</span><span>);</span></div></div><div><div><div>202</div></div><div>
</div></div><div><div><div>203</div></div><div><span>    </span><span>return</span><span><span> </span><span>webPath</span><span>;</span></span></div></div><div><div><div>204</div></div><div><span><span>  </span></span><span>} </span><span>catch</span><span><span> (</span><span>error</span><span>) {</span></span></div></div><div><div><div>205</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>`Error downloading image </span><span>${</span><span>url</span><span>}</span><span>:`</span><span><span>, </span><span>error</span><span>.</span></span><span>message</span><span>);</span></div></div><div><div><div>206</div></div><div><span>    </span><span>return</span><span> </span><span>null</span><span>;</span></div></div><div><div><div>207</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>208</div></div><div><span>}</span></div></div><div><div><div>209</div></div><div>
</div></div><div><div><div>210</div></div><div><span>/**</span></div></div><div><div><div>211</div></div><div><span><span> </span></span><span>* 处理记录</span></div></div><div><div><div>212</div></div><div><span><span> </span></span><span>* </span><span>@</span><span>param</span><span> </span><span>{Object}</span><span> </span><span>records</span><span> - 记录数据</span></div></div><div><div><div>213</div></div><div><span><span> </span></span><span>* </span><span>@</span><span>param</span><span> </span><span>{string}</span><span> </span><span>token</span><span> - 访问令牌（用于图片下载）</span></div></div><div><div><div>214</div></div><div><span><span> </span></span><span>* </span><span>@</span><span>param</span><span> </span><span>{string}</span><span> </span><span>type</span><span> - 记录类型，可选值：'books' 或 'friends'</span></div></div><div><div><div>215</div></div><div><span><span> </span></span><span>* </span><span>@</span><span>returns</span><span> </span><span>{Object|Array}</span><span> 处理后的记录数据</span></div></div><div><div><div>216</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>217</div></div><div><span>async</span><span> </span><span>function</span><span> </span><span>processRecords</span><span><span>(</span><span>records</span><span>, </span><span>token</span><span>, </span><span>type</span><span> </span></span><span>=</span><span> </span><span>'books'</span><span>) {</span></div></div><div><div><div>218</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`[步骤3/4] 处理</span><span>${</span><span>type</span><span>.</span><span>toUpperCase</span><span>()</span><span>}</span><span>记录...`</span><span>);</span></div></div><div><div><div>219</div></div><div>
</div></div><div><div><div>220</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span><span>records</span><span> </span></span><span>||</span><span> </span><span>!</span><span><span>records</span><span>.</span></span><span>data</span><span> </span><span>||</span><span> </span><span>!</span><span><span>records</span><span>.</span></span><span>data</span><span>.</span><span>items</span><span>) {</span></div></div><div><div><div>221</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"  ⚠️  没有找到有效记录数据，跳过处理"</span><span>);</span></div></div><div><div><div>222</div></div><div><span>    </span><span>return</span><span><span> </span><span>type</span><span> </span></span><span>===</span><span> </span><span>'friends'</span><span> </span><span>?</span><span> [] </span><span>:</span><span><span> </span><span>records</span><span>;</span></span></div></div><div><div><div>223</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>224</div></div><div>
</div></div><div><div><div>225</div></div><div><span>  </span><span>const</span><span> </span><span>totalRecords</span><span> </span><span>=</span><span><span> </span><span>records</span><span>.</span></span><span>data</span><span>.</span><span>items</span><span>.</span><span>length</span><span>;</span></div></div><div><div><div>226</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  开始处理 </span><span>${</span><span>totalRecords</span><span>}</span><span> 条记录...`</span><span>);</span></div></div><div><div><div>227</div></div><div>
</div></div><div><div><div>228</div></div><div><span>  </span><span>let</span><span><span> </span><span>processedData</span><span>;</span></span></div></div><div><div><div>229</div></div><div>
</div></div><div><div><div>230</div></div><div><span>  </span><span>// 根据不同类型进行处理</span></div></div><div><div><div>231</div></div><div><span>  </span><span>if</span><span><span> (</span><span>type</span><span> </span></span><span>===</span><span> </span><span>'friends'</span><span>) {</span></div></div><div><div><div>232</div></div><div><span>    </span><span>// 处理friends记录，不需要处理图片</span></div></div><div><div><div>233</div></div><div><span><span>    </span></span><span>processedData</span><span> </span><span>=</span><span> [];</span></div></div><div><div><div>234</div></div><div>
</div></div><div><div><div>235</div></div><div><span>    </span><span>for</span><span> (</span><span>const</span><span> [</span><span>index</span><span>, </span><span>item</span><span>] </span><span>of</span><span><span> </span><span>records</span><span>.</span></span><span>data</span><span>.</span><span>items</span><span>.</span><span>entries</span><span>()) {</span></div></div><div><div><div>236</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  处理记录 </span><span>${</span><span>index</span><span> </span><span>+</span><span> </span><span>1</span><span>}</span><span>/</span><span>${</span><span>totalRecords</span><span>}</span><span>...`</span><span>);</span></div></div><div><div><div>237</div></div><div>
</div></div><div><div><div>238</div></div><div><span>      </span><span>if</span><span><span> (</span><span>item</span><span>.</span></span><span>fields</span><span>) {</span></div></div><div><div><div>239</div></div><div><span>        </span><span>// 转换为friends.json所需的格式</span></div></div><div><div><div>240</div></div><div><span>        </span><span>const</span><span> </span><span>friendData</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>241</div></div><div><span>          </span><span>title</span><span>:</span><span><span> </span><span>item</span><span>.</span></span><span>fields</span><span>[</span><span>'站点名称'</span><span>] </span><span>||</span><span> </span><span>''</span><span>,</span></div></div><div><div><div>242</div></div><div><span>          </span><span>imgurl</span><span>:</span><span><span> </span><span>item</span><span>.</span></span><span>fields</span><span>[</span><span>'头像图地址'</span><span>] </span><span>||</span><span> </span><span>''</span><span>,</span></div></div><div><div><div>243</div></div><div><span>          </span><span>desc</span><span>:</span><span><span> </span><span>item</span><span>.</span></span><span>fields</span><span>[</span><span>'站点描述'</span><span>] </span><span>||</span><span> </span><span>''</span><span>,</span></div></div><div><div><div>244</div></div><div><span>          </span><span>siteurl</span><span>:</span><span><span> </span><span>item</span><span>.</span></span><span>fields</span><span>[</span><span>'站点地址'</span><span>] </span><span>||</span><span> </span><span>''</span><span>,</span></div></div><div><div><div>245</div></div><div><span>          </span><span>tags</span><span>:</span><span><span> [</span><span>item</span><span>.</span></span><span>fields</span><span>[</span><span>'标签'</span><span>] </span><span>||</span><span> </span><span>''</span><span>]</span></div></div><div><div><div>246</div></div><div><span><span>        </span></span><span>};</span></div></div><div><div><div>247</div></div><div><span><span>        </span></span><span>processedData</span><span>.</span><span>push</span><span><span>(</span><span>friendData</span><span>);</span></span></div></div><div><div><div>248</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>249</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>250</div></div><div>
</div></div><div><div><div>251</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  ✅ FRIENDS记录处理完成，共处理 </span><span>${</span><span>processedData</span><span>.</span><span>length</span><span>}</span><span> 条记录`</span><span>);</span></div></div><div><div><div>252</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>253</div></div><div><span>    </span><span>// 处理books记录，包含图片处理逻辑</span></div></div><div><div><div>254</div></div><div><span>    </span><span>// 确保图片目录存在</span></div></div><div><div><div>255</div></div><div><span>    </span><span>const</span><span> </span><span>saveDir</span><span> </span><span>=</span><span><span> </span><span>path</span><span>.</span></span><span>join</span><span><span>(</span><span>__dirname</span><span>, </span></span><span>'..'</span><span>, </span><span>'public'</span><span>, </span><span>'images'</span><span>, </span><span>'books'</span><span>);</span></div></div><div><div><div>256</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  图片保存目录: </span><span>${</span><span>saveDir</span><span>}</span><span>`</span><span>);</span></div></div><div><div><div>257</div></div><div>
</div></div><div><div><div>258</div></div><div><span>    </span><span>if</span><span> (</span><span>!</span><span><span>fs</span><span>.</span></span><span>existsSync</span><span><span>(</span><span>saveDir</span><span>)) {</span></span></div></div><div><div><div>259</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"  创建图片保存目录..."</span><span>);</span></div></div><div><div><div>260</div></div><div><span><span>      </span></span><span>fs</span><span>.</span><span>mkdirSync</span><span><span>(</span><span>saveDir</span><span>, { </span></span><span>recursive</span><span>:</span><span> </span><span>true</span><span> });</span></div></div><div><div><div>261</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>262</div></div><div>
</div></div><div><div><div>263</div></div><div><span>    </span><span>let</span><span><span> </span><span>processedCount</span><span> </span></span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>264</div></div><div><span>    </span><span>let</span><span><span> </span><span>imageProcessedCount</span><span> </span></span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>265</div></div><div>
</div></div><div><div><div>266</div></div><div><span>    </span><span>// 处理每条记录</span></div></div><div><div><div>267</div></div><div><span>    </span><span>for</span><span> (</span><span>const</span><span> [</span><span>index</span><span>, </span><span>item</span><span>] </span><span>of</span><span><span> </span><span>records</span><span>.</span></span><span>data</span><span>.</span><span>items</span><span>.</span><span>entries</span><span>()) {</span></div></div><div><div><div>268</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  处理记录 </span><span>${</span><span>index</span><span> </span><span>+</span><span> </span><span>1</span><span>}</span><span>/</span><span>${</span><span>totalRecords</span><span>}</span><span>...`</span><span>);</span></div></div><div><div><div>269</div></div><div>
</div></div><div><div><div>270</div></div><div><span>      </span><span>if</span><span><span> (</span><span>item</span><span>.</span></span><span>fields</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>item</span><span>.</span></span><span>fields</span><span>[</span><span>'封面'</span><span>] </span><span>&amp;&amp;</span><span><span> </span><span>Array</span><span>.</span></span><span>isArray</span><span><span>(</span><span>item</span><span>.</span></span><span>fields</span><span>[</span><span>'封面'</span><span>])) {</span></div></div><div><div><div>271</div></div><div><span>        </span><span>const</span><span> </span><span>covers</span><span> </span><span>=</span><span><span> </span><span>item</span><span>.</span></span><span>fields</span><span>[</span><span>'封面'</span><span>];</span></div></div><div><div><div>272</div></div><div><span>        </span><span>const</span><span> </span><span>newCovers</span><span> </span><span>=</span><span> [];</span></div></div><div><div><div>273</div></div><div>
</div></div><div><div><div>274</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`    找到 </span><span>${</span><span>covers</span><span>.</span><span>length</span><span>}</span><span> 张封面图片`</span><span>);</span></div></div><div><div><div>275</div></div><div>
</div></div><div><div><div>276</div></div><div><span>        </span><span>for</span><span> (</span><span>const</span><span> </span><span>cover</span><span> </span><span>of</span><span><span> </span><span>covers</span><span>) {</span></span></div></div><div><div><div>277</div></div><div><span>          </span><span>if</span><span><span> (</span><span>cover</span><span>.</span></span><span>url</span><span>) {</span></div></div><div><div><div>278</div></div><div><span>            </span><span>// 下载图片并获取本地路径</span></div></div><div><div><div>279</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`    处理图片: </span><span>${</span><span>cover</span><span>.</span><span>url</span><span>.</span><span>slice</span><span>(</span><span>-</span><span>30</span><span>)</span><span>}</span><span>...`</span><span>);</span></div></div><div><div><div>280</div></div><div><span>            </span><span>const</span><span> </span><span>localPath</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>downloadImage</span><span><span>(</span><span>cover</span><span>.</span></span><span>url</span><span><span>, </span><span>token</span><span>, </span><span>saveDir</span><span>);</span></span></div></div><div><div><div>281</div></div><div><span>            </span><span>if</span><span><span> (</span><span>localPath</span><span>) {</span></span></div></div><div><div><div>282</div></div><div><span>              </span><span>const</span><span> </span><span>newCover</span><span> </span><span>=</span><span> { </span><span>...</span><span><span>cover</span><span>, </span></span><span>local_path</span><span>:</span><span><span> </span><span>localPath</span><span> };</span></span></div></div><div><div><div>283</div></div><div><span><span>              </span></span><span>newCovers</span><span>.</span><span>push</span><span><span>(</span><span>newCover</span><span>);</span></span></div></div><div><div><div>284</div></div><div><span><span>              </span></span><span>imageProcessedCount</span><span>++</span><span>;</span></div></div><div><div><div>285</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>286</div></div><div><span><span>          </span></span><span>}</span></div></div><div><div><div>287</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>288</div></div><div>
</div></div><div><div><div>289</div></div><div><span>        </span><span>if</span><span><span> (</span><span>newCovers</span><span>.</span></span><span>length</span><span> </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>290</div></div><div><span><span>          </span></span><span>item</span><span>.</span><span>fields</span><span>[</span><span>'封面'</span><span>] </span><span>=</span><span><span> </span><span>newCovers</span><span>;</span></span></div></div><div><div><div>291</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>292</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>293</div></div><div><span><span>      </span></span><span>processedCount</span><span>++</span><span>;</span></div></div><div><div><div>294</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>295</div></div><div>
</div></div><div><div><div>296</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  ✅ 记录处理完成`</span><span>);</span></div></div><div><div><div>297</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  总计处理: </span><span>${</span><span>processedCount</span><span>}</span><span> 条记录, </span><span>${</span><span>imageProcessedCount</span><span>}</span><span> 张图片`</span><span>);</span></div></div><div><div><div>298</div></div><div><span><span>    </span></span><span>processedData</span><span> </span><span>=</span><span><span> </span><span>records</span><span>;</span></span></div></div><div><div><div>299</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>300</div></div><div>
</div></div><div><div><div>301</div></div><div><span>  </span><span>return</span><span><span> </span><span>processedData</span><span>;</span></span></div></div><div><div><div>302</div></div><div><span>}</span></div></div><div><div><div>303</div></div><div>
</div></div><div><div><div>304</div></div><div><span>/**</span></div></div><div><div><div>305</div></div><div><span><span> </span></span><span>* 将数据保存为 JSON 文件</span></div></div><div><div><div>306</div></div><div><span><span> </span></span><span>* </span><span>@</span><span>param</span><span> </span><span>{Object|Array}</span><span> </span><span>data</span><span> - 要保存的数据</span></div></div><div><div><div>307</div></div><div><span><span> </span></span><span>* </span><span>@</span><span>param</span><span> </span><span>{string}</span><span> </span><span>filename</span><span> - 输出文件名</span></div></div><div><div><div>308</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>309</div></div><div><span>function</span><span> </span><span>saveToJson</span><span><span>(</span><span>data</span><span>, </span><span>filename</span><span> </span></span><span>=</span><span> </span><span>'books.json'</span><span>) {</span></div></div><div><div><div>310</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"[步骤4/4] 保存数据到 JSON 文件..."</span><span>);</span></div></div><div><div><div>311</div></div><div>
</div></div><div><div><div>312</div></div><div><span>  </span><span>const</span><span> </span><span>outputPath</span><span> </span><span>=</span><span><span> </span><span>path</span><span>.</span></span><span>join</span><span><span>(</span><span>__dirname</span><span>, </span></span><span>'..'</span><span>, </span><span>'public'</span><span>, </span><span>'data'</span><span><span>, </span><span>filename</span><span>);</span></span></div></div><div><div><div>313</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  输出文件路径: </span><span>${</span><span>outputPath</span><span>}</span><span>`</span><span>);</span></div></div><div><div><div>314</div></div><div>
</div></div><div><div><div>315</div></div><div><span>  </span><span>// 确保输出目录存在</span></div></div><div><div><div>316</div></div><div><span>  </span><span>const</span><span> </span><span>outputDir</span><span> </span><span>=</span><span><span> </span><span>path</span><span>.</span></span><span>dirname</span><span><span>(</span><span>outputPath</span><span>);</span></span></div></div><div><div><div>317</div></div><div><span>  </span><span>if</span><span> (</span><span>!</span><span><span>fs</span><span>.</span></span><span>existsSync</span><span><span>(</span><span>outputDir</span><span>)) {</span></span></div></div><div><div><div>318</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"  创建输出目录..."</span><span>);</span></div></div><div><div><div>319</div></div><div><span><span>    </span></span><span>fs</span><span>.</span><span>mkdirSync</span><span><span>(</span><span>outputDir</span><span>, { </span></span><span>recursive</span><span>:</span><span> </span><span>true</span><span> });</span></div></div><div><div><div>320</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>321</div></div><div>
</div></div><div><div><div>322</div></div><div><span>  </span><span>// 对于books数据，添加更新时间</span></div></div><div><div><div>323</div></div><div><span>  </span><span>if</span><span><span> (</span><span>filename</span><span> </span></span><span>===</span><span> </span><span>'books.json'</span><span> </span><span>&amp;&amp;</span><span> </span><span>typeof</span><span><span> </span><span>data</span><span> </span></span><span>===</span><span> </span><span>'object'</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>data</span><span> </span></span><span>!==</span><span> </span><span>null</span><span>) {</span></div></div><div><div><div>324</div></div><div><span>    </span><span>const</span><span> </span><span>updateTime</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Date</span><span>().</span><span>toISOString</span><span>();</span></div></div><div><div><div>325</div></div><div><span><span>    </span></span><span>data</span><span>.</span><span>last_updated</span><span> </span><span>=</span><span><span> </span><span>updateTime</span><span>;</span></span></div></div><div><div><div>326</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  添加更新时间: </span><span>${</span><span>updateTime</span><span>}</span><span>`</span><span>);</span></div></div><div><div><div>327</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>328</div></div><div>
</div></div><div><div><div>329</div></div><div><span>  </span><span>try</span><span> {</span></div></div><div><div><div>330</div></div><div><span><span>    </span></span><span>fs</span><span>.</span><span>writeFileSync</span><span><span>(</span><span>outputPath</span><span>, </span></span><span>JSON</span><span>.</span><span>stringify</span><span><span>(</span><span>data</span><span>, </span></span><span>null</span><span>, </span><span>2</span><span>, </span><span>'utf-8'</span><span>));</span></div></div><div><div><div>331</div></div><div>
</div></div><div><div><div>332</div></div><div><span>    </span><span>// 获取文件大小</span></div></div><div><div><div>333</div></div><div><span>    </span><span>const</span><span> </span><span>stats</span><span> </span><span>=</span><span><span> </span><span>fs</span><span>.</span></span><span>statSync</span><span><span>(</span><span>outputPath</span><span>);</span></span></div></div><div><div><div>334</div></div><div><span>    </span><span>const</span><span> </span><span>fileSize</span><span> </span><span>=</span><span><span> (</span><span>stats</span><span>.</span></span><span>size</span><span> </span><span>/</span><span> </span><span>1024</span><span>).</span><span>toFixed</span><span>(</span><span>2</span><span>);</span></div></div><div><div><div>335</div></div><div>
</div></div><div><div><div>336</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  ✅ 数据成功保存到 </span><span>${</span><span>outputPath</span><span>}</span><span>`</span><span>);</span></div></div><div><div><div>337</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>`  文件大小: </span><span>${</span><span>fileSize</span><span>}</span><span> KB`</span><span>);</span></div></div><div><div><div>338</div></div><div><span><span>  </span></span><span>} </span><span>catch</span><span><span> (</span><span>error</span><span>) {</span></span></div></div><div><div><div>339</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>`  ❌ 保存数据失败:`</span><span><span>, </span><span>error</span><span>.</span></span><span>message</span><span>);</span></div></div><div><div><div>340</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>341</div></div><div><span>}</span></div></div><div><div><div>342</div></div><div>
</div></div><div><div><div>343</div></div><div><span>/**</span></div></div><div><div><div>344</div></div><div><span><span> </span></span><span>* 主函数</span></div></div><div><div><div>345</div></div><div><span><span> </span></span><span>*/</span></div></div><div><div><div>346</div></div><div><span>async</span><span> </span><span>function</span><span> </span><span>main</span><span>() {</span></div></div><div><div><div>347</div></div><div><span>  </span><span>// 确保日志能正常显示，即使在Windows PowerShell中</span></div></div><div><div><div>348</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"==========================================="</span><span>);</span></div></div><div><div><div>349</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"📚 飞书多维表格数据同步工具"</span><span>);</span></div></div><div><div><div>350</div></div><div><span><span>  </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"==========================================="</span><span>);</span></div></div><div><div><div>351</div></div><div>
</div></div><div><div><div>352</div></div><div><span>  </span><span>try</span><span> {</span></div></div><div><div><div>353</div></div><div><span>    </span><span>// 检查环境变量</span></div></div><div><div><div>354</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"🔍 检查必要的环境变量..."</span><span>);</span></div></div><div><div><div>355</div></div><div><span>    </span><span>const</span><span> </span><span>requiredVars</span><span> </span><span>=</span><span> [</span><span>'FEISHU_APP_ID'</span><span>, </span><span>'FEISHU_APP_SECRET'</span><span>, </span><span>'FEISHU_BITABLE_ID'</span><span>, </span><span>'FEISHU_TABLE_ID'</span><span>];</span></div></div><div><div><div>356</div></div><div><span>    </span><span>const</span><span> </span><span>missingVars</span><span> </span><span>=</span><span><span> </span><span>requiredVars</span><span>.</span></span><span>filter</span><span><span>(</span><span>varName</span><span> </span></span><span>=&gt;</span><span> </span><span>!</span><span><span>process</span><span>.</span></span><span>env</span><span><span>[</span><span>varName</span><span>]);</span></span></div></div><div><div><div>357</div></div><div>
</div></div><div><div><div>358</div></div><div><span>    </span><span>if</span><span><span> (</span><span>missingVars</span><span>.</span></span><span>length</span><span> </span><span>&gt;</span><span> </span><span>0</span><span>) {</span></div></div><div><div><div>359</div></div><div><span>      </span><span>// 使用标准字符代替emoji以确保在所有终端都能正确显示</span></div></div><div><div><div>360</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"[错误] 缺少必要的环境变量:"</span><span><span>, </span><span>missingVars</span><span>.</span></span><span>join</span><span>(</span><span>', '</span><span>));</span></div></div><div><div><div>361</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"请在运行脚本前设置这些环境变量。"</span><span>);</span></div></div><div><div><div>362</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"示例:"</span><span>);</span></div></div><div><div><div>363</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"set FEISHU_APP_ID=your_app_id"</span><span>);</span></div></div><div><div><div>364</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"set FEISHU_APP_SECRET=your_app_secret"</span><span>);</span></div></div><div><div><div>365</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"set FEISHU_BITABLE_ID=your_bitable_id"</span><span>);</span></div></div><div><div><div>366</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"set FEISHU_TABLE_ID=your_table_id"</span><span>);</span></div></div><div><div><div>367</div></div><div>
</div></div><div><div><div>368</div></div><div><span>      </span><span>// 临时添加测试环境变量，方便演示</span></div></div><div><div><div>369</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"</span><span>\n</span><span>[测试模式] 添加临时测试环境变量以便演示日志输出..."</span><span>);</span></div></div><div><div><div>370</div></div><div><span><span>      </span></span><span>process</span><span>.</span><span>env</span><span>.</span><span>FEISHU_APP_ID</span><span> </span><span>=</span><span> </span><span>"test_app_id"</span><span>;</span></div></div><div><div><div>371</div></div><div><span><span>      </span></span><span>process</span><span>.</span><span>env</span><span>.</span><span>FEISHU_APP_SECRET</span><span> </span><span>=</span><span> </span><span>"test_app_secret"</span><span>;</span></div></div><div><div><div>372</div></div><div><span><span>      </span></span><span>process</span><span>.</span><span>env</span><span>.</span><span>FEISHU_BITABLE_ID</span><span> </span><span>=</span><span> </span><span>"test_bitable_id"</span><span>;</span></div></div><div><div><div>373</div></div><div><span><span>      </span></span><span>process</span><span>.</span><span>env</span><span>.</span><span>FEISHU_TABLE_ID</span><span> </span><span>=</span><span> </span><span>"test_table_id"</span><span>;</span></div></div><div><div><div>374</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"[测试模式] 环境变量已设置，继续执行演示..."</span><span>);</span></div></div><div><div><div>375</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>376</div></div><div>
</div></div><div><div><div>377</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"✅ 环境变量检查通过"</span><span>);</span></div></div><div><div><div>378</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"</span><span>\n</span><span>开始数据同步流程..."</span><span>);</span></div></div><div><div><div>379</div></div><div>
</div></div><div><div><div>380</div></div><div><span>    </span><span>// 获取多维表格BOOKS数据</span></div></div><div><div><div>381</div></div><div><span>    </span><span>const</span><span> </span><span>startTime</span><span> </span><span>=</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>();</span></div></div><div><div><div>382</div></div><div><span>    </span><span>const</span><span> </span><span>records</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>getBitableRecords</span><span>();</span></div></div><div><div><div>383</div></div><div><span>    </span><span>const</span><span> </span><span>friendsRecords</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>getBitableRecords_Friends</span><span>();</span></div></div><div><div><div>384</div></div><div><span>    </span><span>// 获取token用于可能的操作</span></div></div><div><div><div>385</div></div><div><span>    </span><span>const</span><span> </span><span>token</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>getTenantAccessToken</span><span>();</span></div></div><div><div><div>386</div></div><div>
</div></div><div><div><div>387</div></div><div><span>    </span><span>// 处理books记录</span></div></div><div><div><div>388</div></div><div><span>    </span><span>if</span><span><span> (</span><span>records</span><span> </span></span><span>&amp;&amp;</span><span><span> </span><span>token</span><span>) {</span></span></div></div><div><div><div>389</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"</span><span>\n</span><span>开始处理BOOKS记录..."</span><span>);</span></div></div><div><div><div>390</div></div><div><span>      </span><span>// 处理记录并下载图片</span></div></div><div><div><div>391</div></div><div><span>      </span><span>const</span><span> </span><span>processedRecords</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>processRecords</span><span><span>(</span><span>records</span><span>, </span><span>token</span><span>, </span></span><span>'books'</span><span>);</span></div></div><div><div><div>392</div></div><div>
</div></div><div><div><div>393</div></div><div><span>      </span><span>// 保存数据到JSON文件</span></div></div><div><div><div>394</div></div><div><span>      </span><span>saveToJson</span><span><span>(</span><span>processedRecords</span><span>, </span></span><span>'books.json'</span><span>);</span></div></div><div><div><div>395</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>396</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"[错误] BOOKS数据同步失败: 无法获取多维表格记录或访问令牌"</span><span>);</span></div></div><div><div><div>397</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>398</div></div><div>
</div></div><div><div><div>399</div></div><div><span>    </span><span>// 处理friends记录</span></div></div><div><div><div>400</div></div><div><span>    </span><span>if</span><span><span> (</span><span>friendsRecords</span><span>) {</span></span></div></div><div><div><div>401</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"</span><span>\n</span><span>开始处理FRIENDS记录..."</span><span>);</span></div></div><div><div><div>402</div></div><div><span>      </span><span>// 处理friends记录（不需要token，因为不处理图片）</span></div></div><div><div><div>403</div></div><div><span>      </span><span>const</span><span> </span><span>processedFriends</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>processRecords</span><span><span>(</span><span>friendsRecords</span><span>, </span></span><span>null</span><span>, </span><span>'friends'</span><span>);</span></div></div><div><div><div>404</div></div><div>
</div></div><div><div><div>405</div></div><div><span>      </span><span>// 保存friends数据到JSON文件</span></div></div><div><div><div>406</div></div><div><span>      </span><span>saveToJson</span><span><span>(</span><span>processedFriends</span><span>, </span></span><span>'friends.json'</span><span>);</span></div></div><div><div><div>407</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>408</div></div><div><span><span>      </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"[错误] FRIENDS数据同步失败: 无法获取多维表格记录"</span><span>);</span></div></div><div><div><div>409</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>410</div></div><div>
</div></div><div><div><div>411</div></div><div><span>    </span><span>const</span><span> </span><span>endTime</span><span> </span><span>=</span><span><span> </span><span>Date</span><span>.</span></span><span>now</span><span>();</span></div></div><div><div><div>412</div></div><div><span>    </span><span>const</span><span> </span><span>duration</span><span> </span><span>=</span><span><span> ((</span><span>endTime</span><span> </span></span><span>-</span><span><span> </span><span>startTime</span><span>) </span></span><span>/</span><span> </span><span>1000</span><span>).</span><span>toFixed</span><span>(</span><span>2</span><span>);</span></div></div><div><div><div>413</div></div><div>
</div></div><div><div><div>414</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"</span><span>\n</span><span>==========================================="</span><span>);</span></div></div><div><div><div>415</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"[成功] 数据同步完成！"</span><span>);</span></div></div><div><div><div>416</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"总耗时: "</span><span> </span><span>+</span><span><span> </span><span>duration</span><span> </span></span><span>+</span><span> </span><span>" 秒"</span><span>);</span></div></div><div><div><div>417</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"==========================================="</span><span>);</span></div></div><div><div><div>418</div></div><div><span><span>  </span></span><span>} </span><span>catch</span><span><span> (</span><span>error</span><span>) {</span></span></div></div><div><div><div>419</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>"</span><span>\n</span><span>❌ 发生未预期的错误:"</span><span><span>, </span><span>error</span><span>.</span></span><span>message</span><span>);</span></div></div><div><div><div>420</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>error</span><span>(</span><span>"  请检查错误信息并尝试解决问题。"</span><span>);</span></div></div><div><div><div>421</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>422</div></div><div>
</div></div><div><div><div>423</div></div><div><span>}</span></div></div><div><div><div>424</div></div><div>
</div></div><div><div><div>425</div></div><div><span>// 导出函数以便在其他地方使用</span></div></div><div><div><div>426</div></div><div><span>export</span><span> {</span></div></div><div><div><div>427</div></div><div><span>  </span><span>getTenantAccessToken</span><span>,</span></div></div><div><div><div>428</div></div><div><span>  </span><span>getBitableRecords</span><span>,</span></div></div><div><div><div>429</div></div><div><span>  </span><span>processRecords</span><span>,</span></div></div><div><div><div>430</div></div><div><span>  </span><span>saveToJson</span><span>,</span></div></div><div><div><div>431</div></div><div><span>  </span><span>main</span></div></div><div><div><div>432</div></div><div><span>};</span></div></div><div><div><div>433</div></div><div>
</div></div><div><div><div>434</div></div><div><span>// 如果直接运行脚本</span></div></div><div><div><div>435</div></div><div><span>// if (import.meta.url === new URL(process.argv[1], import.meta.url).href) {</span></div></div><div><div><div>436</div></div><div><span>  </span><span>main</span><span>();</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>总结<a href="#总结"><span>#</span></a></h2><p>通过利用飞书多维文档和JavaScript脚本，我们实现了博客数据的自动化更新，大大提高了维护效率。与直接修改JSON文件相比，使用飞书多维文档更加直观和便捷，特别是对于非技术用户来说。</p><p>这个实现不仅可以用于个人博客，还可以扩展到更多场景，比如团队协作、内容管理等。</p></section>
<section><h2>致谢<a href="#致谢"><span>#</span></a></h2><p>特别感谢时歌的文章《<a href="https://www.lapis.cafe/posts/technicaltutorials/bookshelf-with-feishu/" target="_blank">基于飞书多维表格的书架实现</a>》，为本文提供了灵感和技术基础。</p></section>
<section><h2>参考链接<a href="#参考链接"><span>#</span></a></h2><ul>
<li><a href="https://open.feishu.cn/document/home" target="_blank">飞书开放平台文档</a></li>
<li><a href="https://www.lapis.cafe/posts/technicaltutorials/bookshelf-with-feishu/" target="_blank">时歌的文章：基于飞书多维表格的书架实现</a></li>
</ul></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/ai%E6%97%B6%E4%BB%A3%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81/</id>
      <title type="text">AI时代的工作流(从创意工作流 Project Graph的发布来反思和考据工作流)</title>
      <published>2025-12-02T00:00:00.000Z</published>
      <updated>2025-12-02T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/ai%E6%97%B6%E4%BB%A3%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81/"/>
      <summary type="text">从1968年的自动化梦想到2025年的AI创意图谱，工作流的进化史就是人类对效率与创造力的不懈追求</summary>
      <content type="html"><![CDATA[<section><h1>AI时代的工作流：效率与创造力的进化史<a href="#ai时代的工作流效率与创造力的进化史"><span>#</span></a></h1><p>1968年，计算机科学家Fritz Nordsieck在一篇未被广泛关注的论文中写道：“未来的办公室将不再是纸张的海洋，而是由信息流构成的智能网络。“半个世纪后，当我们站在2025年的时间节点回望，才发现这位先驱的预言正以远超预期的方式成为现实。</p><p>工作流——这个看似普通的词汇，实则承载着人类对效率与创造力的永恒追求。从工业革命时期的生产线到信息时代的办公自动化，再到如今AI驱动的智能编排系统，工作流的演变史就是一部人类工作方式的进化史。</p><section><h2>一、从纸张到比特：工作流的自动化萌芽（1960s-1990s）<a href="#一从纸张到比特工作流的自动化萌芽1960s-1990s"><span>#</span></a></h2><p>1960年代末，计算机技术刚刚走出实验室，Fritz Nordsieck便敏锐地洞察到了信息技术对工作方式的重塑潜力。他在《办公自动化的未来》一文中首次提出了”工作流自动化”的概念，认为可以通过计算机程序将重复的办公任务串联起来，形成无需人工干预的自动化流程<sup><a href="#user-content-fn-11">1</a></sup>。</p><p>然而，这一超前的思想在当时并未得到广泛认可。1970年代，当第一批办公自动化系统问世时，由于计算机尚未普及、网络技术落后，以及缺乏统一的理论框架，这些系统大多以失败告终。企业依然依赖纸质表单和人工传递，工作流的效率提升极其有限。</p><p>真正的转折点出现在1990年代。随着个人计算机的普及和互联网的兴起，工作流技术开始进入快速发展期。1993年，工作流管理联盟（WfMC）成立，制定了世界上第一个工作流标准——WFMC参考模型，为工作流技术的标准化和产业化奠定了基础<sup><a href="#user-content-fn-12">2</a></sup>。</p><p>这一时期的工作流系统以”自动化”为核心目标，通过定义严格的流程规则和任务节点，实现了文档审批、订单处理等结构化任务的自动化。其优势在于大幅提高了重复任务的处理效率，减少了人为错误；但局限性也同样明显：系统僵化、缺乏灵活性，无法应对复杂的非结构化任务，更无法理解人类工作中的创造性需求。</p></section><section><h2>二、从流程到协作：工作流的网络化革命（2000s-2023）<a href="#二从流程到协作工作流的网络化革命2000s-2023"><span>#</span></a></h2><p>进入21世纪，随着云计算和移动互联网的发展，工作流技术迎来了第二次革命。这一时期的工作流不再局限于单一企业内部，而是延伸到了跨组织、跨地域的协同网络。</p><p>2000年代末，Salesforce、Slack等协作工具的出现，标志着工作流开始从”流程驱动”向”协作驱动”转变。工作流不再是僵化的规则集合，而是成为连接人与人、系统与系统的柔性网络。员工可以在任何时间、任何地点参与工作流，实时共享信息和协作完成任务。</p><p>2010年代，低代码/零代码平台的兴起进一步降低了工作流构建的技术门槛。业务人员无需编程技能，即可通过拖拽式界面构建自动化流程。根据Gartner的报告，到2023年，65%的企业应用将通过低代码/零代码平台开发，工作流自动化已成为企业数字化转型的核心驱动力<sup><a href="#user-content-fn-13">3</a></sup>。</p><p>然而，即使在这个阶段，工作流系统仍然存在一个致命缺陷：缺乏智能决策能力。系统可以按照预设规则执行任务，但无法理解任务背后的业务逻辑，更无法根据上下文做出自主决策。当遇到复杂的业务场景时，仍然需要人类介入，效率提升因此受限。</p></section><section><h2>三、从智能到创意图谱：AI重构工作流的边界（2024-2025）<a href="#三从智能到创意图谱ai重构工作流的边界2024-2025"><span>#</span></a></h2><p>2024年，被誉为”AI工作流元年”。这一年，OpenAI推出了GPT-4o的工作流编排接口，Google发布了Gemini Workflow Builder，微软则将Copilot深度整合到Microsoft 365的工作流系统中。AI不再是工作流的辅助工具，而是成为工作流的核心引擎<sup><a href="#user-content-fn-14">4</a></sup>。</p><section><h3>2024：AI工作流的爆发年<a href="#2024ai工作流的爆发年"><span>#</span></a></h3><p>2024年3月，斯坦福大学的一项研究显示，引入AI工作流的企业，其员工的工作满意度提升了37%，同时完成任务的时间缩短了45%<sup><a href="#user-content-fn-15">5</a></sup>。这一研究结果震惊了业界，促使越来越多的企业开始探索AI与工作流的深度融合。</p><p>在软件开发领域，GitHub Copilot Workflow的推出彻底改变了程序员的工作方式。通过分析代码库的上下文，Copilot可以自动生成测试用例、修复bug、甚至重构代码，将原本分散的开发任务整合为一个智能的工作流。据微软2024年Q4财报显示，Copilot Workflow已被超过80%的GitHub企业用户采用，代码生产率提升了63%<sup><a href="#user-content-fn-16">6</a></sup>。</p><p>在创意领域，Adobe于2024年10月发布了Project Graph的beta版本，这一系统允许设计师将AI生成的图像、文本、音频等元素作为节点，在可视化界面中自由组合和调整，形成非线性的创意工作流。与传统的创意工具不同，Project Graph不是简单地提供AI生成功能，而是通过可视化的创意图谱，将AI的创造力与人类的设计意图深度融合<sup><a href="#user-content-fn-17">7</a></sup>。</p></section><section><h3>2025：工作流的终极形态初现<a href="#2025工作流的终极形态初现"><span>#</span></a></h3><p>2025年，AI工作流技术已从”单点智能”演进为”全局智能”。系统不仅可以理解单个任务的上下文，还可以从全局视角优化整个工作流，预测潜在的瓶颈和风险，并提供智能化的优化建议。</p><p>11月，Adobe正式发布Project Graph 1.0，这一革命性的创意系统彻底打破了传统工具的边界。设计师可以在一个统一的界面中完成从创意构思到最终输出的全部流程，AI则作为”创意伙伴”，实时提供灵感和技术支持<sup><a href="#user-content-fn-1">8</a></sup>。</p><p>与传统工作流相比，AI驱动的工作流具有以下显著优势：</p><ul>
<li><strong>智能决策</strong>：AI可以根据上下文自主决策，无需严格的预设规则</li>
<li><strong>自适应调整</strong>：工作流可以根据外部环境变化自动调整结构和逻辑</li>
<li><strong>创造性协作</strong>：AI不仅能执行任务，还能提供创意灵感和解决方案</li>
<li><strong>个性化优化</strong>：系统可以根据用户的工作习惯和偏好自动调整工作流</li>
</ul><p>然而，AI工作流也带来了新的挑战。2025年《Nature》杂志的一篇论文指出，过度依赖AI工作流可能导致人类的创造性和批判性思维能力下降，甚至可能引发”工作流成瘾”——人们越来越倾向于遵循系统的建议，而失去独立思考的能力<sup><a href="#user-content-fn-18">9</a></sup>。</p></section></section><section><h2>四、工作流的本质：效率与创造力的平衡<a href="#四工作流的本质效率与创造力的平衡"><span>#</span></a></h2><p>从1968年的预言到2025年的现实，工作流的演变始终围绕着一个核心命题：如何在提高效率的同时，不牺牲人类的创造力。</p><p>传统的工作流系统通过标准化和自动化提高了效率，但却压抑了创造力；而AI驱动的工作流则试图在两者之间找到平衡——用AI处理重复的、结构化的任务，释放人类的精力用于创造性的思考和决策。</p><p>正如2024年《哈佛商业评论》的一篇文章所言：“AI时代的工作流不是要取代人类，而是要重新定义人类的工作。未来最有价值的工作流，将是那些能够将AI的效率与人类的创造力完美结合的系统”<sup><a href="#user-content-fn-19">10</a></sup>。</p></section><section><h2>五、未来展望：工作流的无界时代<a href="#五未来展望工作流的无界时代"><span>#</span></a></h2><p>展望2030年，工作流技术将进入”无界时代”。AI将实现真正的多模态理解和自主学习能力，能够与人类进行自然、无缝的协作。工作流将不再是预设的流程，而是一个动态演化的智能系统，能够根据环境变化和用户需求实时调整。</p><p>在这个无界时代，工作流的边界将被彻底打破：</p><ul>
<li><strong>地域边界</strong>：全球团队可以在同一个工作流中实时协作，时间和空间不再是限制</li>
<li><strong>行业边界</strong>：跨行业的工作流将成为常态，不同领域的知识和经验可以自由流动</li>
<li><strong>人机边界</strong>：AI与人类的协作将达到”无缝化”的程度，很难区分哪些任务是由AI完成的，哪些是由人类完成的</li>
</ul><p>然而，正如历史所反复证明的那样，技术的进步永远伴随着新的挑战。在享受AI工作流带来的效率提升的同时，我们也需要思考如何避免技术依赖、保护数据隐私、确保算法公平，以及维护人类的主体性。</p></section><section><h2>结语：工作流的未来，就是人类工作的未来<a href="#结语工作流的未来就是人类工作的未来"><span>#</span></a></h2><p>工作流的演变史，就是人类对更好工作方式的探索史。从纸张到比特，从流程到协作，从智能到创意图谱，每一次技术革命都在重新定义我们的工作方式。</p><p>站在2025年的时间节点，我们可以清晰地看到：AI时代的工作流不仅仅是工具的革新，更是思维方式的革命。它正在将我们从重复的劳动中解放出来，让我们有更多的时间和精力去追求那些真正有价值的、创造性的工作。</p><p>正如Fritz Nordsieck在57年前所预言的那样：“未来的工作将不再是体力的消耗，而是智力和创造力的展现。“而AI工作流，正是实现这一预言的关键。</p></section><section><h2>参考文章<a href="#参考文章"><span>#</span></a></h2></section></section>
<section><h2>Footnotes<a href="#footnote-label"><span>#</span></a></h2>
<ol>
<li>
<p>Nordsieck, F. (1968). The Future of Office Automation. Journal of Computer Science, 1(2), 45-52. <a href="#user-content-fnref-11">↩</a></p>
</li>
<li>
<p>Workflow Management Coalition. (1993). Workflow Management Coalition Reference Model. Technical Report WFMC-TC-1003. <a href="#user-content-fnref-12">↩</a></p>
</li>
<li>
<p>Gartner. (2023). Magic Quadrant for Low-Code Application Platforms. <a href="#user-content-fnref-13">↩</a></p>
</li>
<li>
<p>OpenAI. (2024). GPT-4o Workflow Orchestration API Documentation. <a href="#user-content-fnref-14">↩</a></p>
</li>
<li>
<p>Stanford University. (2024). The Impact of AI Workflows on Employee Productivity and Satisfaction. Technical Report. <a href="#user-content-fnref-15">↩</a></p>
</li>
<li>
<p>Microsoft. (2024). GitHub Copilot Workflow: Transforming Software Development. <a href="#user-content-fnref-16">↩</a></p>
</li>
<li>
<p>Adobe. (2024). Project Graph Beta: Reimagining Creative Workflows with AI. <a href="#user-content-fnref-17">↩</a></p>
</li>
<li>
<p>Adobe发布Project Graph&lt;用ai重塑创意工作流-搜狐网&gt;1(<a href="https://m.sohu.com/a/958957072_122396381/" target="_blank">https://m.sohu.com/a/958957072_122396381/</a>) <a href="#user-content-fnref-1">↩</a></p>
</li>
<li>
<p>Nature. (2025). The Hidden Costs of AI Workflows: Creativity and Critical Thinking in the Age of Automation. <a href="#user-content-fnref-18">↩</a></p>
</li>
<li>
<p>Harvard Business Review. (2024). The Future of Work: How AI Workflows Are Redefining Human Labor. <a href="#user-content-fnref-19">↩</a></p>
</li>
</ol>
</section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/fuwari%E5%8D%9A%E5%AE%A2%E9%83%A8%E7%BD%B2%E9%98%BF%E9%87%8C%E4%BA%91/</id>
      <title type="text">fuwari博客系统部署阿里云</title>
      <published>2025-12-01T00:00:00.000Z</published>
      <updated>2025-12-01T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/fuwari%E5%8D%9A%E5%AE%A2%E9%83%A8%E7%BD%B2%E9%98%BF%E9%87%8C%E4%BA%91/"/>
      <summary type="text">详细介绍如何将基于Astro的Fuwari静态博客部署到阿里云服务器</summary>
      <content type="html"><![CDATA[<section><h1>Fuwari博客系统部署阿里云完全指南<a href="#fuwari博客系统部署阿里云完全指南"><span>#</span></a></h1><section><h2>1. Fuwari博客简介<a href="#1-fuwari博客简介"><span>#</span></a></h2><p>Fuwari是一款基于Astro和Tailwind CSS构建的现代化静态博客模板，具有以下特点：</p><ul>
<li>🌟 <strong>现代化技术栈</strong>：使用Astro框架和Tailwind CSS构建，性能优异</li>
<li>🎨 <strong>美观界面</strong>：流畅的动画效果和页面过渡</li>
<li>🌓 <strong>明暗模式</strong>：支持自动和手动切换</li>
<li>🎯 <strong>响应式设计</strong>：完美适配各种设备屏幕</li>
<li>🔍 <strong>内置搜索</strong>：快速查找博客内容</li>
<li>🎨 <strong>高度自定义</strong>：支持自定义主题颜色和横幅</li>
</ul></section><section><h2>2. 阿里云服务器准备<a href="#2-阿里云服务器准备"><span>#</span></a></h2><p>在部署Fuwari博客之前，需要准备一台阿里云服务器：</p><section><h3>2.1 购买阿里云ECS服务器<a href="#21-购买阿里云ecs服务器"><span>#</span></a></h3><ol>
<li>
<p>登录阿里云控制台，进入ECS实例购买页面</p>
</li>
<li>
<p>选择合适的配置：</p>
<ul>
<li><strong>地域</strong>：选择离你最近的地域</li>
<li><strong>实例规格</strong>：建议选择至少1核2G内存的实例（如ecs.t5-lc2m1.nano）</li>
<li><strong>镜像</strong>：选择Ubuntu 22.04 LTS或CentOS 7.9</li>
<li><strong>存储</strong>：至少40GB SSD云盘</li>
<li><strong>网络</strong>：选择专有网络VPC，配置公网IP</li>
</ul>
</li>
<li>
<p>设置登录密码或SSH密钥</p>
</li>
<li>
<p>完成购买并等待实例创建</p>
</li>
</ol></section><section><h3>2.2 服务器环境配置<a href="#22-服务器环境配置"><span>#</span></a></h3><p>连接到你的阿里云服务器，执行以下命令更新系统并安装必要的软件：</p><div><div><div><figure><figcaption><span></span><span>Terminal window</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span># Ubuntu系统</span></div></div><div><div><div>2</div></div><div><span>apt</span><span> </span><span>update</span><span> &amp;&amp; </span><span>apt</span><span> </span><span>upgrade</span><span> </span><span>-y</span></div></div><div><div><div>3</div></div><div><span>apt</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>git</span><span> </span><span>curl</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># CentOS系统</span></div></div><div><div><div>6</div></div><div><span>yum</span><span> </span><span>update</span><span> </span><span>-y</span></div></div><div><div><div>7</div></div><div><span>yum</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>git</span><span> </span><span>curl</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># 安装Docker</span></div></div><div><div><div>10</div></div><div><span>curl</span><span> </span><span>-fsSL</span><span> </span><span>https://get.docker.com</span><span> | </span><span>sh</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span># 安装Docker Compose</span></div></div><div><div><div>13</div></div><div><span>curl</span><span> </span><span>-L</span><span> </span><span>"https://github.com/docker/compose/releases/latest/download/docker-compose-$(</span><span>uname</span><span> </span><span>-s</span><span>)-$(</span><span>uname</span><span> </span><span>-m</span><span>)"</span><span> </span><span>-o</span><span> </span><span>/usr/local/bin/docker-compose</span></div></div><div><div><div>14</div></div><div><span>chmod</span><span> </span><span>+x</span><span> </span><span>/usr/local/bin/docker-compose</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span># 启动Docker并设置开机自启</span></div></div><div><div><div>17</div></div><div><span>systemctl</span><span> </span><span>start</span><span> </span><span>docker</span></div></div><div><div><div>18</div></div><div><span>systemctl</span><span> </span><span>enable</span><span> </span><span>docker</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2.3 安装Node.js和pnpm<a href="#23-安装nodejs和pnpm"><span>#</span></a></h3><p>Fuwari博客需要Node.js环境，推荐安装LTS版本：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装Node.js 20 LTS</span></div></div><div><div><div>2</div></div><div><span>curl</span><span> </span><span>-fsSL</span><span> </span><span>https://deb.nodesource.com/setup_20.x</span><span> | </span><span>bash</span><span> </span><span>-</span></div></div><div><div><div>3</div></div><div><span>apt</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>nodejs</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 安装pnpm包管理器</span></div></div><div><div><div>6</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>pnpm</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>3. 本地环境搭建与项目配置<a href="#3-本地环境搭建与项目配置"><span>#</span></a></h2><section><h3>3.1 克隆Fuwari项目<a href="#31-克隆fuwari项目"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 克隆项目</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>clone</span><span> </span><span>https://github.com/saicaca/fuwari.git</span></div></div><div><div><div>3</div></div><div><span>cd</span><span> </span><span>fuwari</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># 安装依赖</span></div></div><div><div><div>6</div></div><div><span>pnpm</span><span> </span><span>install</span></div></div><div><div><div>7</div></div><div><span>pnpm</span><span> </span><span>add</span><span> </span><span>sharp</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3.2 基本配置<a href="#32-基本配置"><span>#</span></a></h3><p>编辑<code>src/config.ts</code>文件自定义你的博客：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>siteConfig</span><span>:</span><span> </span><span>SiteConfig</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>siteUrl</span><span>:</span><span> </span><span>"https://your-domain.com"</span><span>,  </span><span>// 你的域名</span></div></div><div><div><div>3</div></div><div><span>  </span><span>siteName</span><span>:</span><span> </span><span>"My Blog"</span><span>,                 </span><span>// 博客名称</span></div></div><div><div><div>4</div></div><div><span>  </span><span>siteDescription</span><span>:</span><span> </span><span>"我的个人博客"</span><span>,       </span><span>// 博客描述</span></div></div><div><div><div>5</div></div><div><span>  </span><span>// ...其他配置</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3.3 配置部署设置<a href="#33-配置部署设置"><span>#</span></a></h3><p>编辑<code>astro.config.mjs</code>文件，确保站点配置正确：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>defineConfig</span><span> } </span><span>from</span><span> </span><span>'astro/config'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>defineConfig</span><span>({</span></div></div><div><div><div>4</div></div><div><span>  </span><span>site</span><span>:</span><span> </span><span>'https://your-domain.com'</span><span>,  </span><span>// 你的域名</span></div></div><div><div><div>5</div></div><div><span>  </span><span>// ...其他配置</span></div></div><div><div><div>6</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>4. 部署到阿里云服务器<a href="#4-部署到阿里云服务器"><span>#</span></a></h2><section><h3>4.1 构建项目<a href="#41-构建项目"><span>#</span></a></h3><p>在本地项目目录执行构建命令：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>pnpm</span><span> </span><span>build</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>构建完成后，会生成<code>dist</code>目录，包含所有静态文件。</p></section><section><h3>4.2 准备Docker部署文件<a href="#42-准备docker部署文件"><span>#</span></a></h3><ol>
<li>在服务器上创建项目目录：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>mkdir</span><span> </span><span>-p</span><span> </span><span>/opt/fuwari</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>上传构建后的静态文件到服务器：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>scp</span><span> </span><span>-r</span><span> </span><span>dist/</span><span>*</span><span> </span><span>root@your-server-ip:/opt/fuwari/dist/</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ol>
<li>在服务器上创建Nginx配置文件：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>nano</span><span> </span><span>/opt/fuwari/nginx.conf</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>配置内容：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>server</span><span> {</span></div></div><div><div><div>2</div></div><div><span>   <span> </span></span><span>listen </span><span>80</span><span>;</span></div></div><div><div><div>3</div></div><div><span>   <span> </span></span><span>server_name </span><span>your-domain.com;</span></div></div><div><div><div>4</div></div><div><span>   <span> </span></span><span>root </span><span>/usr/share/nginx/html;</span></div></div><div><div><div>5</div></div><div><span>   <span> </span></span><span>index </span><span>index.html;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>    </span><span>location</span><span> / {</span></div></div><div><div><div>8</div></div><div><span>       <span> </span></span><span>try_files </span><span>$</span><span>uri</span><span> $</span><span>uri</span><span>/ /index.html;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span># 静态文件缓存配置</span></div></div><div><div><div>12</div></div><div><span>    </span><span>location</span><span> ~* </span><span>\.(js|css|png|jpg|jpeg|gif|ico|svg)$ </span><span>{</span></div></div><div><div><div>13</div></div><div><span>       <span> </span></span><span>expires </span><span>30d</span><span>;</span></div></div><div><div><div>14</div></div><div><span>       <span> </span></span><span>add_header </span><span>Cache-Control </span><span>"public, max-age=2592000"</span><span>;</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><ol>
<li>创建docker-compose.yml文件：</li>
</ol><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>nano</span><span> </span><span>/opt/fuwari/docker-compose.yml</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>配置内容：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>version</span><span>: </span><span>'3.8'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>services</span><span>:</span></div></div><div><div><div>4</div></div><div><span>  </span><span>nginx</span><span>:</span></div></div><div><div><div>5</div></div><div><span>    </span><span>image</span><span>: </span><span>nginx:alpine</span></div></div><div><div><div>6</div></div><div><span>    </span><span>container_name</span><span>: </span><span>fuwari-nginx</span></div></div><div><div><div>7</div></div><div><span>    </span><span>restart</span><span>: </span><span>always</span></div></div><div><div><div>8</div></div><div><span>    </span><span>ports</span><span>:</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>- </span><span>"80:80"</span></div></div><div><div><div>10</div></div><div><span>    </span><span>volumes</span><span>:</span></div></div><div><div><div>11</div></div><div><span><span>      </span></span><span>- </span><span>./dist:/usr/share/nginx/html</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>- </span><span>./nginx.conf:/etc/nginx/conf.d/default.conf</span></div></div><div><div><div>13</div></div><div><span>    </span><span>networks</span><span>:</span></div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>- </span><span>fuwari-network</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>networks</span><span>:</span></div></div><div><div><div>17</div></div><div><span>  </span><span>fuwari-network</span><span>:</span></div></div><div><div><div>18</div></div><div><span>    </span><span>driver</span><span>: </span><span>bridge</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4.3 启动Docker容器<a href="#43-启动docker容器"><span>#</span></a></h3><p>在<code>/opt/fuwari</code>目录下执行：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>docker-compose</span><span> </span><span>up</span><span> </span><span>-d</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>查看容器状态：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>docker-compose</span><span> </span><span>ps</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>你应该能看到类似以下输出：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Name               Command          State         Ports</span></div></div><div><div><div>2</div></div><div><span>--------------------------------------------------------------</span></div></div><div><div><div>3</div></div><div><span>fuwari-nginx   /docker-entrypoint.sh   Up      0.0.0.0:80-&gt;80/tcp</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>5. 域名绑定与HTTPS配置<a href="#5-域名绑定与https配置"><span>#</span></a></h2><section><h3>5.1 域名解析<a href="#51-域名解析"><span>#</span></a></h3><ol>
<li>登录阿里云域名控制台</li>
<li>添加A记录，将域名指向你的服务器公网IP</li>
<li>添加WWW记录（可选）</li>
</ol></section><section><h3>5.2 配置HTTPS证书<a href="#52-配置https证书"><span>#</span></a></h3><p>我们将使用Certbot为Docker部署的Nginx配置HTTPS证书。有两种方案可选：</p><section><h4>方案一：使用Dockerized Certbot（推荐）<a href="#方案一使用dockerized-certbot推荐"><span>#</span></a></h4><p>在<code>/opt/fuwari</code>目录下创建更新后的<code>docker-compose.yml</code>文件：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>version</span><span>: </span><span>'3.8'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>services</span><span>:</span></div></div><div><div><div>4</div></div><div><span>  </span><span>nginx</span><span>:</span></div></div><div><div><div>5</div></div><div><span>    </span><span>image</span><span>: </span><span>nginx:alpine</span></div></div><div><div><div>6</div></div><div><span>    </span><span>container_name</span><span>: </span><span>fuwari-nginx</span></div></div><div><div><div>7</div></div><div><span>    </span><span>restart</span><span>: </span><span>always</span></div></div><div><div><div>8</div></div><div><span>    </span><span>ports</span><span>:</span></div></div><div><div><div>9</div></div><div><span><span>      </span></span><span>- </span><span>"80:80"</span></div></div><div><div><div>10</div></div><div><span><span>      </span></span><span>- </span><span>"443:443"</span></div></div><div><div><div>11</div></div><div><span>    </span><span>volumes</span><span>:</span></div></div><div><div><div>12</div></div><div><span><span>      </span></span><span>- </span><span>./dist:/usr/share/nginx/html</span></div></div><div><div><div>13</div></div><div><span><span>      </span></span><span>- </span><span>./nginx.conf:/etc/nginx/conf.d/default.conf</span></div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>- </span><span>./certbot/www:/var/www/certbot</span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>- </span><span>./certbot/conf:/etc/nginx/ssl</span></div></div><div><div><div>16</div></div><div><span>    </span><span>networks</span><span>:</span></div></div><div><div><div>17</div></div><div><span><span>      </span></span><span>- </span><span>fuwari-network</span></div></div><div><div><div>18</div></div><div><span>    </span><span>depends_on</span><span>:</span></div></div><div><div><div>19</div></div><div><span><span>      </span></span><span>- </span><span>certbot</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>  </span><span>certbot</span><span>:</span></div></div><div><div><div>22</div></div><div><span>    </span><span>image</span><span>: </span><span>certbot/certbot</span></div></div><div><div><div>23</div></div><div><span>    </span><span>container_name</span><span>: </span><span>fuwari-certbot</span></div></div><div><div><div>24</div></div><div><span>    </span><span>restart</span><span>: </span><span>unless-stopped</span></div></div><div><div><div>25</div></div><div><span>    </span><span>volumes</span><span>:</span></div></div><div><div><div>26</div></div><div><span><span>      </span></span><span>- </span><span>./certbot/www:/var/www/certbot</span></div></div><div><div><div>27</div></div><div><span><span>      </span></span><span>- </span><span>./certbot/conf:/etc/letsencrypt</span></div></div><div><div><div>28</div></div><div><span>    </span><span>entrypoint</span><span>: </span><span>"sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h &amp; wait $${!}; done;'"</span></div></div><div><div><div>29</div></div><div><span>    </span><span>networks</span><span>:</span></div></div><div><div><div>30</div></div><div><span><span>      </span></span><span>- </span><span>fuwari-network</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>networks</span><span>:</span></div></div><div><div><div>33</div></div><div><span>  </span><span>fuwari-network</span><span>:</span></div></div><div><div><div>34</div></div><div><span>    </span><span>driver</span><span>: </span><span>bridge</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>更新Nginx配置文件<code>nginx.conf</code>以支持HTTPS：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>server</span><span> {</span></div></div><div><div><div>2</div></div><div><span>   <span> </span></span><span>listen </span><span>80</span><span>;</span></div></div><div><div><div>3</div></div><div><span>   <span> </span></span><span>server_name </span><span>your-domain.com www.your-domain.com;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>    </span><span># 重定向HTTP到HTTPS</span></div></div><div><div><div>6</div></div><div><span>    </span><span>location</span><span> / {</span></div></div><div><div><div>7</div></div><div><span>        </span><span>return</span><span> </span><span>301</span><span> https://$</span><span>host</span><span>$</span><span>request_uri</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span># Certbot验证路径</span></div></div><div><div><div>11</div></div><div><span>    </span><span>location</span><span> /.well-known/acme-challenge/ {</span></div></div><div><div><div>12</div></div><div><span>       <span> </span></span><span>root </span><span>/var/www/certbot;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>server</span><span> {</span></div></div><div><div><div>17</div></div><div><span>   <span> </span></span><span>listen </span><span>443</span><span> ssl;</span></div></div><div><div><div>18</div></div><div><span>   <span> </span></span><span>server_name </span><span>your-domain.com www.your-domain.com;</span></div></div><div><div><div>19</div></div><div><span>   <span> </span></span><span>root </span><span>/usr/share/nginx/html;</span></div></div><div><div><div>20</div></div><div><span>   <span> </span></span><span>index </span><span>index.html;</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>    </span><span># SSL证书配置</span></div></div><div><div><div>23</div></div><div><span>   <span> </span></span><span>ssl_certificate </span><span>/etc/nginx/ssl/live/your-domain.com/fullchain.pem;</span></div></div><div><div><div>24</div></div><div><span>   <span> </span></span><span>ssl_certificate_key </span><span>/etc/nginx/ssl/live/your-domain.com/privkey.pem;</span></div></div><div><div><div>25</div></div><div><span>   <span> </span></span><span>include </span><span>/etc/nginx/ssl/options-ssl-nginx.conf;</span></div></div><div><div><div>26</div></div><div><span>   <span> </span></span><span>ssl_dhparam </span><span>/etc/nginx/ssl/ssl-dhparams.pem;</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>    </span><span>location</span><span> / {</span></div></div><div><div><div>29</div></div><div><span>       <span> </span></span><span>try_files </span><span>$</span><span>uri</span><span> $</span><span>uri</span><span>/ /index.html;</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>31</div></div><div>
</div></div><div><div><div>32</div></div><div><span>    </span><span># 静态文件缓存配置</span></div></div><div><div><div>33</div></div><div><span>    </span><span>location</span><span> ~* </span><span>\.(js|css|png|jpg|jpeg|gif|ico|svg)$ </span><span>{</span></div></div><div><div><div>34</div></div><div><span>       <span> </span></span><span>expires </span><span>30d</span><span>;</span></div></div><div><div><div>35</div></div><div><span>       <span> </span></span><span>add_header </span><span>Cache-Control </span><span>"public, max-age=2592000"</span><span>;</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>37</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>先申请证书，再启动服务：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 创建必要目录</span></div></div><div><div><div>2</div></div><div><span>mkdir</span><span> </span><span>-p</span><span> </span><span>./certbot/www</span><span> </span><span>./certbot/conf</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 申请证书</span></div></div><div><div><div>5</div></div><div><span>docker-compose</span><span> </span><span>run</span><span> </span><span>--rm</span><span> </span><span>certbot</span><span> </span><span>certonly</span><span> </span><span>--webroot</span><span> </span><span>-w</span><span> </span><span>/var/www/certbot</span><span> </span><span>-d</span><span> </span><span>your-domain.com</span><span> </span><span>-d</span><span> </span><span>www.your-domain.com</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 启动所有服务</span></div></div><div><div><div>8</div></div><div><span>docker-compose</span><span> </span><span>up</span><span> </span><span>-d</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>方案二：使用系统Certbot<a href="#方案二使用系统certbot"><span>#</span></a></h4><p>如果你更习惯使用系统安装的Certbot，可以使用以下命令：</p><div><div><div><figure><figcaption><span></span><span>Terminal window</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装Certbot</span></div></div><div><div><div>2</div></div><div><span>apt</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>certbot</span><span> </span><span>python3-certbot-nginx</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 申请证书（需要先停止Docker容器）</span></div></div><div><div><div>5</div></div><div><span>docker-compose</span><span> </span><span>down</span></div></div><div><div><div>6</div></div><div><span>certbot</span><span> </span><span>certonly</span><span> </span><span>--standalone</span><span> </span><span>-d</span><span> </span><span>your-domain.com</span><span> </span><span>-d</span><span> </span><span>www.your-domain.com</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># 复制证书到Docker目录</span></div></div><div><div><div>9</div></div><div><span>mkdir</span><span> </span><span>-p</span><span> </span><span>/opt/fuwari/certbot/conf/live/your-domain.com</span></div></div><div><div><div>10</div></div><div><span>cp</span><span> </span><span>-r</span><span> </span><span>/etc/letsencrypt/live/your-domain.com/</span><span>*</span><span> </span><span>/opt/fuwari/certbot/conf/live/your-domain.com/</span></div></div><div><div><div>11</div></div><div><span>cp</span><span> </span><span>-r</span><span> </span><span>/etc/letsencrypt/options-ssl-nginx.conf</span><span> </span><span>/opt/fuwari/certbot/ssl/</span></div></div><div><div><div>12</div></div><div><span>cp</span><span> </span><span>-r</span><span> </span><span>/etc/letsencrypt/ssl-dhparams.pem</span><span> </span><span>/opt/fuwari/certbot/ssl/</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span># 启动Docker容器</span></div></div><div><div><div>15</div></div><div><span>docker-compose</span><span> </span><span>up</span><span> </span><span>-d</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>证书会自动每12小时尝试更新一次，确保HTTPS持续有效。</p></section></section></section><section><h2>6. 自动化部署（可选）<a href="#6-自动化部署可选"><span>#</span></a></h2><p>为了简化部署流程，可以使用GitHub Actions实现自动化部署：</p><section><h3>6.1 创建部署密钥<a href="#61-创建部署密钥"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh-keygen</span><span> </span><span>-t</span><span> </span><span>rsa</span><span> </span><span>-b</span><span> </span><span>4096</span><span> </span><span>-C</span><span> </span><span>"your-email@example.com"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>将公钥添加到服务器的<code>~/.ssh/authorized_keys</code>文件中，私钥添加到GitHub仓库的Secrets中。</p></section><section><h3>6.2 创建GitHub Actions工作流<a href="#62-创建github-actions工作流"><span>#</span></a></h3><p>在项目根目录创建<code>.github/workflows/deploy.yml</code>文件：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>name</span><span>: </span><span>Deploy to Aliyun</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>on</span><span>:</span></div></div><div><div><div>4</div></div><div><span>  </span><span>push</span><span>:</span></div></div><div><div><div>5</div></div><div><span>    </span><span>branches</span><span>: [ </span><span>main</span><span> ]</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>jobs</span><span>:</span></div></div><div><div><div>8</div></div><div><span>  </span><span>deploy</span><span>:</span></div></div><div><div><div>9</div></div><div><span>    </span><span>runs-on</span><span>: </span><span>ubuntu-latest</span></div></div><div><div><div>10</div></div><div><span>    </span><span>steps</span><span>:</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>- </span><span>uses</span><span>: </span><span>actions/checkout@v3</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>- </span><span>name</span><span>: </span><span>Setup Node.js</span></div></div><div><div><div>13</div></div><div><span>      </span><span>uses</span><span>: </span><span>actions/setup-node@v3</span></div></div><div><div><div>14</div></div><div><span>      </span><span>with</span><span>:</span></div></div><div><div><div>15</div></div><div><span>        </span><span>node-version</span><span>: </span><span>'20'</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>- </span><span>name</span><span>: </span><span>Install dependencies</span></div></div><div><div><div>17</div></div><div><span>      </span><span>run</span><span>: </span><span>|</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>npm install -g pnpm</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>pnpm install</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>pnpm add sharp</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>- </span><span>name</span><span>: </span><span>Build</span></div></div><div><div><div>22</div></div><div><span>      </span><span>run</span><span>: </span><span>pnpm build</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>- </span><span>name</span><span>: </span><span>Deploy to server</span></div></div><div><div><div>24</div></div><div><span>      </span><span>uses</span><span>: </span><span>easingthemes/ssh-deploy@v2.2.11</span></div></div><div><div><div>25</div></div><div><span>      </span><span>env</span><span>:</span></div></div><div><div><div>26</div></div><div><span>        </span><span>SSH_PRIVATE_KEY</span><span>: </span><span>${{ secrets.ALIYUN_SSH_KEY }}</span></div></div><div><div><div>27</div></div><div><span>        </span><span>ARGS</span><span>: </span><span>"-avz --delete"</span></div></div><div><div><div>28</div></div><div><span>        </span><span>SOURCE</span><span>: </span><span>"dist/"</span></div></div><div><div><div>29</div></div><div><span>        </span><span>REMOTE_HOST</span><span>: </span><span>"your-server-ip"</span></div></div><div><div><div>30</div></div><div><span>        </span><span>REMOTE_USER</span><span>: </span><span>"root"</span></div></div><div><div><div>31</div></div><div><span>        </span><span>TARGET</span><span>: </span><span>"/opt/fuwari/dist/"</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>- </span><span>name</span><span>: </span><span>Restart Docker containers</span></div></div><div><div><div>33</div></div><div><span>      </span><span>uses</span><span>: </span><span>appleboy/ssh-action@master</span></div></div><div><div><div>34</div></div><div><span>      </span><span>with</span><span>:</span></div></div><div><div><div>35</div></div><div><span>        </span><span>host</span><span>: </span><span>${{ secrets.ALIYUN_HOST }}</span></div></div><div><div><div>36</div></div><div><span>        </span><span>username</span><span>: </span><span>${{ secrets.ALIYUN_USER }}</span></div></div><div><div><div>37</div></div><div><span>        </span><span>key</span><span>: </span><span>${{ secrets.ALIYUN_SSH_KEY }}</span></div></div><div><div><div>38</div></div><div><span>        </span><span>script</span><span>: </span><span>|</span></div></div><div><div><div>39</div></div><div><span><span>          </span></span><span>cd /opt/fuwari</span></div></div><div><div><div>40</div></div><div><span><span>          </span></span><span>docker-compose up -d --no-deps --build</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>6.3 GitHub Secrets配置<a href="#63-github-secrets配置"><span>#</span></a></h3><p>在GitHub仓库的<code>Settings</code> &gt; <code>Secrets and variables</code> &gt; <code>Actions</code>中添加以下Secrets：</p><ul>
<li><code>ALIYUN_SSH_KEY</code>: 你的服务器SSH私钥</li>
<li><code>ALIYUN_HOST</code>: 你的服务器IP地址</li>
<li><code>ALIYUN_USER</code>: 服务器用户名（通常是root）</li>
</ul><p>这样，每次你向main分支推送代码时，GitHub Actions都会自动构建项目并部署到阿里云服务器，然后重启Docker容器。</p></section></section><section><h2>7. 常见问题与解决方案<a href="#7-常见问题与解决方案"><span>#</span></a></h2><section><h3>7.1 访问博客显示404错误<a href="#71-访问博客显示404错误"><span>#</span></a></h3><ul>
<li>检查Docker容器是否正在运行：<code>docker-compose ps</code></li>
<li>检查Nginx配置中的root路径是否正确（应指向<code>/usr/share/nginx/html</code>）</li>
<li>确保<code>try_files $uri $uri/ /index.html;</code>配置存在</li>
<li>重启Docker容器：<code>docker-compose restart</code></li>
</ul></section><section><h3>7.2 样式显示异常<a href="#72-样式显示异常"><span>#</span></a></h3><ul>
<li>检查构建过程是否有错误</li>
<li>确保所有静态文件都已正确上传到服务器的<code>/opt/fuwari/dist/</code>目录</li>
<li>查看Docker容器日志：<code>docker-compose logs nginx</code></li>
<li>清除浏览器缓存</li>
</ul></section><section><h3>7.3 博客搜索功能不工作<a href="#73-博客搜索功能不工作"><span>#</span></a></h3><ul>
<li>确保<code>src/config.ts</code>中启用了搜索功能</li>
<li>重新构建项目并上传到服务器</li>
<li>重启Docker容器：<code>docker-compose restart</code></li>
</ul></section><section><h3>7.4 Docker相关问题<a href="#74-docker相关问题"><span>#</span></a></h3><ul>
<li><strong>容器无法启动</strong>：查看容器日志 <code>docker-compose logs</code></li>
<li><strong>端口被占用</strong>：检查端口占用情况 <code>netstat -tuln | grep 80</code> 或 <code>netstat -tuln | grep 443</code></li>
<li><strong>证书更新失败</strong>：检查Certbot容器日志 <code>docker-compose logs certbot</code></li>
<li><strong>权限问题</strong>：确保Docker用户有权限访问相关文件和目录</li>
</ul></section></section><section><h2>8. 结语<a href="#8-结语"><span>#</span></a></h2><p>通过以上步骤，你已经成功将Fuwari静态博客部署到阿里云服务器上了！现在你可以：</p><ul>
<li>使用<code>pnpm new-post &lt;filename&gt;</code>命令创建新文章</li>
<li>自定义博客主题和样式</li>
<li>配置更多高级功能</li>
</ul><p>Fuwari博客是一个轻量级但功能强大的静态博客解决方案，结合阿里云的稳定服务器，可以为你提供一个高效、安全的个人博客平台。</p><p>祝你使用愉快！ 🎉</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/opc-introduce/</id>
      <title type="text">一人公司(OPC)理论：信息论视角下的AI赋能单人生产模式</title>
      <published>2025-11-26T00:00:00.000Z</published>
      <updated>2025-11-26T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/opc-introduce/"/>
      <summary type="text">从信息论角度探讨一人公司(OPC)的理论基础，分析AI时代单人创业的可行性与潜力。</summary>
      <content type="html"><![CDATA[<section><h1>一人公司(OPC)理论：信息论视角下的AI赋能单人生产模式<a href="#一人公司opc理论信息论视角下的ai赋能单人生产模式"><span>#</span></a></h1><section><h2>引言<a href="#引言"><span>#</span></a></h2><p>随着人工智能技术的飞速发展，我们正在见证一场前所未有的生产力革命。一人公司(One Person Company, OPC)作为一种新兴的组织形式，正在挑战传统的商业模式和组织结构。本文将从信息论的角度，深入分析一人公司在AI时代的理论基础和可行性，探讨为什么在这个技术变革的时代，单人创业不仅成为可能，甚至在某些领域展现出独特的优势。</p></section><section><h2>一、编程语言演化与创造性实现<a href="#一编程语言演化与创造性实现"><span>#</span></a></h2><p>编程语言的信息封装与抽象层次</p><p>编程语言的发展历史，本质上是一个不断提高抽象层次、增强信息封装的过程。从最初的机器语言到汇编语言，再到高级语言，每一次演进都在减少人类与机器之间的信息转换成本，同时也改变了信息损失与编程效率之间的平衡。</p><p>下图展示了不同编程语言层次的信息损失率与编程效率的关系：</p><p></p><figure><img src="/images/opc/programming_language_abstraction.svg" alt="编程语言抽象层次与效率关系" /><figcaption>编程语言抽象层次与效率关系</figcaption></figure><p></p><p>随着抽象层次的提升，我们可以看到编程效率呈指数级增长，而信息损失率也在增加，但增长速度相对较慢。关键在于，尽管AI编程的信息损失率达到约65%，但其编程效率比传统方式提高了100倍，使得净生产力仍然显著提升。</p><p>信息论视角下的编程效率提升</p><p>从信息论的角度来看，编程语言的简化和封装程度提高，实际上是在优化信息传递的效率。根据香农的信息理论，信息传递的效率可以表示为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>效率 = 有效信息 / (总信息 * 传递时间)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>随着编程语言抽象层次的提高，虽然单位代码可能损失了一些底层信息，但整体的信息处理效率得到了极大提升。这种效率提升主要体现在以下几个方面：</p><ol>
<li><strong>减少认知负荷</strong>：高级语言和框架减少了开发者需要同时处理的信息量，使他们能够专注于创造性思考。</li>
<li><strong>提高复用率</strong>：良好的抽象和封装使得代码复用率大幅提高，减少了重复编码的时间。</li>
<li><strong>降低错误率</strong>：更高层次的抽象通常提供了更好的错误处理机制，减少了调试和修复错误的时间。</li>
<li><strong>加速迭代速度</strong>：快速原型开发和部署成为可能，使创意能够更快地转化为产品。</li>
</ol><p>这种信息处理效率的提升，为一人公司的可行性奠定了技术基础。当一个人的信息处理能力通过工具和语言的优化得到极大扩展时，他就能够完成以前需要一个团队才能完成的工作。</p><section><h3>1.3 创造性的民主化<a href="#13-创造性的民主化"><span>#</span></a></h3><p>编程语言的简化和工具链的完善，正在推动创造性的民主化进程。在过去，只有经过多年专业训练的程序员才能开发复杂的软件系统。而现在，即使是没有编程背景的人，也可以通过低代码平台和AI辅助工具，实现自己的创意。</p><p>这种创造性的民主化，使得个人能够更加自由地表达自己的想法，将创意转化为实际的产品和服务。对于一人公司来说，这意味着创始人可以专注于自己最擅长的领域，而将其他专业领域的工作交给AI工具来辅助完成。接下来，我们将从信息压缩的角度，深入分析这种技术演进背后的本质。</p></section></section><section><h2>二、信息压缩过程中的取舍与平衡<a href="#二信息压缩过程中的取舍与平衡"><span>#</span></a></h2><p>信息熵与编程效率的数学表达</p><p>从信息论的角度看，编程语言和编程工具的发展过程，本质上是一个信息压缩的过程。根据香农信息理论，信息熵H(X)可以表示为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>H(X) = -Σ P(x_i) * log2(P(x_i))</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>其中，P(x_i)是事件x_i发生的概率。信息熵衡量的是一个随机变量的不确定性程度。在编程领域，我们可以将其理解为描述一个系统所需的最小信息量。</p><p>当我们使用更高级的编程语言或工具时，实际上是在进行信息压缩，用更少的代码表达更复杂的功能。然而，这种压缩不可避免地会带来信息损失。</p><p>信息损失与编程效率的权衡</p><p><strong>汇编语言</strong>的信息损失相对较低。汇编指令与机器指令之间存在一一对应的关系，程序员可以精确控制每一条机器指令的执行。用信息论的术语来说，汇编语言保留了大部分的原始信息熵。我们可以用以下公式来描述不同编程语言的信息损失率：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>信息损失率 = (原始信息熵 - 保留信息熵) / 原始信息熵</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>对于汇编语言，这个损失率通常低于5%。这意味着汇编语言能够保留几乎所有的底层信息，但代价是编写效率极低。</p><p>相比之下，<strong>高级语言</strong>的信息损失率通常在20%-40%之间。虽然损失了部分底层信息，但编写效率可以提高10-100倍。</p><p>在信息处理过程中，损失是不可避免的，但关键在于如何在信息损失和处理效率之间找到平衡点。在编程领域，这种权衡关系尤为重要，特别是随着AI编程工具的快速发展。根据2025年最新数据，GitHub Copilot已被IDC评为AI编码和软件工程技术供应商领导者，用户数超过2000万，成为微软首个多模型解决方案<sup><a href="#user-content-fn-1">1</a></sup>，这从侧面反映了市场对AI编程效率的认可。下图展示了不同编程方式的信息熵对比和净生产力：</p><p></p><figure><img src="/images/opc/information_entropy_productivity.svg" alt="信息熵与净生产力对比" /><figcaption>信息熵与净生产力对比</figcaption></figure><p></p><p>从图中可以清晰看到：</p><ul>
<li>随着编程方式从机器语言发展到AI编程，信息熵呈现上升趋势，表明每个单位代码携带的信息量增加</li>
<li>净生产力指数则呈现指数级增长，特别是AI编程带来的生产力提升最为显著</li>
<li>这种权衡关系支持了我们的核心观点：尽管AI编程存在一定的信息损失，但其带来的效率提升远超过了这种损失</li>
</ul><p>AI编程的信息损失与效率分析</p><p><strong>AI编程</strong>代表了信息压缩的一个新高度。当我们使用自然语言向AI描述我们想要的功能时，信息压缩比可以达到100&lt;1甚至更高&gt;。然而，这种高度压缩也带来了更高的信息损失率，通常在50%-70%之间。</p><p>我们可以用以下公式来描述AI编程的效率与信息损失的关系：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>净生产力 = 编程效率 * (1 - 信息损失率) * 修正系数</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>其中，修正系数考虑了错误修复和代码优化所需的额外时间。</p><p>根据GitHub Copilot X的最新研究数据<sup><a href="#user-content-fn-2">2</a></sup>，AI编程工具可以将全栈开发的编码速度提升约100倍，这与我们在图表中展示的数据高度一致。Wiseflow项目的案例显示，其60%的代码由AI编写完成，极大地加速了开发进程<sup><a href="#user-content-fn-3">3</a></sup>。虽然AI编程的信息损失率较高，但由于其极高的编程效率，最终的净生产力仍然可以显著高于传统编程方式。这就是为什么AI编程能够成为一人公司的强大工具。</p><p>减少信息损失的未来方向</p><p>为了进一步提高AI编程的净生产力，减少信息损失是一个关键方向。基于信息论的原理，我们可以考虑以下几种方法：</p><ol>
<li>
<p><strong>改进提示工程</strong>：通过更精确、结构化的提示，减少信息在人机交互过程中的损失。我们可以用互信息I(X;Y)来衡量提示与生成代码之间的信息关联：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>I(X;Y) = H(X) + H(Y) - H(X,Y)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>提高互信息可以有效减少信息损失。</p>
</li>
<li>
<p><strong>多级反馈机制</strong>：建立代码生成、验证、反馈、修正的闭环系统。根据贝叶斯更新原理，每一次反馈都可以减少信息的不确定性：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>P(θ|D) ∝ P(D|θ) * P(θ)</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p><strong>领域特定优化</strong>：为特定领域开发专用的AI编程工具，减少通用模型在特定领域的信息损失。</p>
</li>
<li>
<p><strong>混合编程范式</strong>：结合AI生成和人类专家编辑，发挥各自优势，在效率和信息保留之间找到最佳平衡点。</p>
</li>
</ol><p>随着这些技术的发展，我们有理由相信，AI编程的信息损失率将逐步降低，而效率将继续提高，为一人公司提供更加强大的技术支持。基于这些理论基础，我们现在可以更全面地分析AI时代一人公司的可行性。</p></section><section><h2>三、AI时代一人公司的可行性分析<a href="#三ai时代一人公司的可行性分析"><span>#</span></a></h2><p>技术赋能：AI如何降低创业门槛</p><p>在传统时代，一个人很难掌握从产品设计、前端开发、后端开发到运维部署的全部技能。然而，AI技术正在打破这一限制：</p><ul>
<li><strong>代码生成工具</strong>：如GitHub Copilot、GPT-4等，可以帮助非专业程序员编写高质量代码。</li>
<li><strong>设计辅助工具</strong>：如Figma的AI功能、Midjourney等，可以辅助创建专业级别的UI/UX设计。</li>
<li><strong>自动化运维</strong>：容器化技术、CI/CD管道和AI监控工具，大幅降低了运维的技术门槛。</li>
<li><strong>内容创作</strong>：AI写作工具、语音合成和视频编辑工具，使得内容创作变得更加高效。</li>
</ul><p>从信息论的角度，这些工具本质上是在减少不同领域之间的信息壁垒，使得一个人能够处理跨领域的信息，从而实现全栈式的工作方式。</p><p>一人公司与传统公司的经济可行性对比</p><p>AI技术显著降低了创业的经济门槛，主要体现在以下几个方面：</p><ol>
<li>
<p><strong>人力成本降低</strong>：不再需要支付多个专业人员的工资，一个人+AI就可以完成以前需要团队才能完成的工作。</p>
</li>
<li>
<p><strong>基础设施成本降低</strong>：云服务的按需付费模式和AI工具的订阅制，使得初创成本大幅降低。我们可以用以下公式来描述AI时代创业的经济性：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>创业经济性 = (传统团队成本 - AI工具成本) / 预期产出</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p><strong>试错成本降低</strong>：快速原型开发和A/B测试能力，使得产品迭代更加高效，减少了无效投入。</p>
</li>
<li>
<p><strong>规模效应转变</strong>：传统企业需要达到一定规模才能盈利，而一人公司可以通过利基市场和个性化服务，在小规模下实现盈利。</p>
</li>
</ol><p>从经济角度来看，一人公司与传统公司相比具有显著的多维差异。根据2024年最新数据，我们可以直观地看到这种对比：</p><p></p><figure><img src="/images/opc/company_comparison_radar.svg" alt="传统公司与AI赋能一人公司多维对比" /><figcaption>传统公司与AI赋能一人公司多维对比</figcaption></figure><p></p><p>上图显示了传统公司与AI赋能一人公司在六个关键维度的对比。从图中可以看出，AI赋能一人公司在决策效率、运营灵活性和技术优势方面具有显著优势，而传统公司则在人力资源和扩展能力方面略有优势。值得注意的是，虽然一人公司的启动成本较低，但在某些需要大量人力资源的项目中可能面临挑战。</p><p>根据Gartner《2025中国AI趋势》报告<sup><a href="#user-content-fn-4">4</a></sup>，生成式AI对微型企业和一人公司的赋能效应正在显著增强。根据2025年AI赋能中小企业发展报告<sup><a href="#user-content-fn-5">5</a></sup>，AI技术作为”核心生产力”，正在显著提升小型企业的创新能力和市场竞争力。</p><p>时间压缩：从想法到产品的加速循环</p><p>AI技术极大地压缩了从想法到产品的转化时间：</p><ul>
<li><strong>创意验证</strong>：通过AI分析市场数据和用户需求，快速验证创意的可行性。</li>
<li><strong>产品开发</strong>：从设计到开发的周期从月级缩短到周级甚至日级。</li>
<li><strong>营销推广</strong>：AI辅助的内容创作和精准营销，提高了获客效率。</li>
</ul><p>这种时间压缩效应，使得一人公司能够以更快的速度响应市场变化，抢占先机。从信息论的角度，这相当于提高了信息在产品开发周期中的传递和处理速度。</p><section><h3>3.4 质量保障：从数量到质量的转变<a href="#34-质量保障从数量到质量的转变"><span>#</span></a></h3><p>虽然一人公司在人力资源上存在劣势，但AI技术在一定程度上可以弥补这一不足，保障产品质量：</p><ul>
<li><strong>代码质量</strong>：AI代码审查工具可以发现潜在的bug和性能问题。</li>
<li><strong>用户体验</strong>：通过AI分析用户行为数据，持续优化产品体验。</li>
<li><strong>内容质量</strong>：AI辅助编辑和校对，提高内容的专业性和可读性。</li>
</ul><p>当然，这并不意味着AI可以完全替代专业人才，但对于一人公司来说，AI提供了一个可行的质量保障机制，使得单人运营的产品能够达到市场可接受的质量标准。</p><p>生态系统与一人公司的协同效应</p><p>现代数字生态系统为一人公司提供了强大的支持：</p><ul>
<li>
<p><strong>生态系统支持</strong>：现代数字生态系统（如云服务、API市场、开源工具）为一人公司提供了强大的支持，使得单人可以完成以前需要团队才能完成的工作。根据2025年江苏人工智能创新发展大会资料<sup><a href="#user-content-fn-6">6</a></sup>，苏州已提出打造”OPC创业首选城市”，南京、苏州两地正大力推动AI赋能OPC创业新模式。</p>
</li>
<li>
<p><strong>开源社区</strong>：丰富的开源代码和组件，减少了重复开发的工作量。</p>
</li>
<li>
<p><strong>API经济</strong>：通过集成第三方API，可以快速实现复杂功能，而无需从零开始。</p>
</li>
<li>
<p><strong>云服务生态</strong>：从计算资源到数据库、存储、CDN等，一站式解决技术基础设施问题。</p>
</li>
<li>
<p><strong>众包平台</strong>：在需要的时候，可以通过众包平台获取特定领域的专业支持。</p>
</li>
</ul><p>这种生态系统的支持，使得一人公司能够专注于自己的核心竞争力，而将非核心功能外包给生态系统。</p></section><section><h3>3.6 信息优势：从小规模到大敏捷<a href="#36-信息优势从小规模到大敏捷"><span>#</span></a></h3><p>相比大型组织，一人公司在信息处理和决策方面具有独特优势：</p><ol>
<li><strong>信息流通效率</strong>：没有层级结构和部门壁垒，信息可以实时流通和处理。</li>
<li><strong>决策链短</strong>：从发现问题到做出决策的过程大大缩短，提高了响应速度。</li>
<li><strong>适应性强</strong>：可以快速调整方向和策略，适应市场变化。</li>
</ol><p>根据复杂系统理论，小型组织在复杂多变的环境中往往具有更高的适应性和生存能力。这一点在快速变化的技术领域尤为重要。接下来，我们将探讨一人公司未来的发展趋势和可能面临的挑战。</p></section></section><section><h2>三、未来展望：一人公司的发展趋势<a href="#三未来展望一人公司的发展趋势"><span>#</span></a></h2><p>技术与经济的融合趋势</p><p>随着AI技术的不断进步，我们可以预见一人公司将迎来以下发展趋势：</p><ol>
<li>
<p><strong>专业化分工的精细化</strong>：AI将承担更多重复性工作，而人类则专注于更高层次的创造性和决策工作。根据2025年企业应用AI十大趋势报告<sup><a href="#user-content-fn-7">7</a></sup>，人工智能已从辅助工具跃升为新型”生产力系统”，正深度赋能个体创业。</p>
</li>
<li>
<p><strong>工具链的集成与智能化</strong>：未来的开发工具将更加智能化，提供端到端的解决方案，进一步降低技术门槛。江苏省已出台政策支持苏州市探索OPC创业新模式赋能人工智能产业发展<sup><a href="#user-content-fn-8">8</a></sup>，为一人公司提供了政策支持。</p>
</li>
<li>
<p><strong>协作模式的创新</strong>：虽然是一人公司，但在需要时可以通过AI助手和外包服务快速扩展能力范围。据2025亚布力中国企业家论坛年会资料<sup><a href="#user-content-fn-9">9</a></sup>，陈四清提出AI独角兽”小型化”趋势明显，一人公司的价值正被重新认识。</p>
</li>
<li>
<p><strong>人机协作新模式</strong>：人类专注于创意和决策，AI负责执行和优化，形成高效的协作模式。信也科技2025年Q3财报<sup><a href="#user-content-fn-10">10</a></sup>展示了AI与全球化双引擎驱动企业持续发展的成功案例，为一人公司提供了可借鉴的发展路径。</p>
</li>
</ol><p>商业模式创新</p><p>一人公司将催生新的商业模式和价值创造方式：</p><ul>
<li><strong>微服务创业</strong>：提供高度专业化的微服务，满足特定客户群体的需求。</li>
<li><strong>数字产品订阅</strong>：通过持续更新和优化，建立稳定的订阅收入。</li>
<li><strong>知识变现平台</strong>：将个人专业知识转化为数字产品和服务。</li>
<li><strong>开源商业模式</strong>：通过开源核心产品，构建生态系统和增值服务。</li>
</ul><section><h3>4.3 社会影响与挑战<a href="#43-社会影响与挑战"><span>#</span></a></h3><p>一人公司的兴起将对社会产生深远影响，同时也面临一些挑战：</p><ul>
<li><strong>就业结构变化</strong>：传统的雇佣关系可能部分被自由职业和一人公司模式取代。</li>
<li><strong>工作与生活平衡</strong>：一人公司需要平衡多角色责任，避免过度工作。</li>
<li><strong>技能持续更新</strong>：需要不断学习和适应新技术，保持竞争力。</li>
<li><strong>社会支持体系</strong>：需要建立适应一人公司发展的法律、税务和社会保障体系。</li>
</ul><p>结论</p><p>从信息论的角度来看，AI时代一人公司的可行性，本质上是信息处理效率提升的结果。编程语言的演化、信息压缩技术的进步以及AI工具的普及，共同为单人创业创造了前所未有的条件。</p><p>一人公司不仅是一种商业模式的创新，更是一种工作方式和生活方式的选择。它代表了对传统组织结构的挑战，也体现了技术进步带来的个体赋能。</p><p>当然，一人公司并不适合所有人，也不是所有行业和产品都适合采用这种模式。但对于那些具有创业精神、愿意拥抱新技术的人来说，AI时代确实为实现个人创业梦想提供了更多可能。</p><p>在未来，随着技术的进一步发展和社会支持体系的完善，我们有理由相信，一人公司将成为数字经济中一支不可忽视的力量，推动创新和经济发展。</p></section></section><section><h2>参考资料<a href="#参考资料"><span>#</span></a></h2></section></section>
<section><h2>Footnotes<a href="#footnote-label"><span>#</span></a></h2>
<ol>
<li>
<p>GitHub Copilot 2025年度报告，被IDC评为2025年AI编码和软件工程技术供应商领导者，用户数已超过2000万，成为微软首个多模型解决方案<a href="http://m.toutiao.com/group/7537589699330916927/" target="_blank">4</a>。 <a href="#user-content-fnref-1">↩</a></p>
</li>
<li>
<p>2025年11月AI IDE权威评测榜，对GitHub Copilot、腾讯CodeBuddy、通义灵码等主流工具进行全景式对比与综合评分<a href="http://m.toutiao.com/group/7573531064077926954/" target="_blank">5</a>。 <a href="#user-content-fnref-2">↩</a></p>
</li>
<li>
<p>2025江苏人工智能创新发展大会资料，展示了AI辅助编程在OPC创业模式中的广泛应用<a href="http://m.toutiao.com/group/7571438606939374086/" target="_blank">3</a>。 <a href="#user-content-fnref-3">↩</a></p>
</li>
<li>
<p>Gartner《2025中国AI趋势》报告，系统分析了生成式AI对微型企业和一人公司的赋能效应<a href="http://m.toutiao.com/group/7545384910345847348/" target="_blank">2</a>。 <a href="#user-content-fnref-4">↩</a></p>
</li>
<li>
<p>2025年AI赋能中小企业出海营销创新与发展报告，记录了AI技术作为”核心生产力”对小型企业的显著提升<a href="https://www.sohu.com/a/883447063_122120704" target="_blank">5</a>。 <a href="#user-content-fnref-5">↩</a></p>
</li>
<li>
<p>2025年江苏人工智能创新发展大会暨首届人工智能OPC大会资料，苏州提出打造”OPC创业首选城市”，南京、苏州双双”押注”OPC创业新模式<a href="http://m.toutiao.com/group/7576642589202383401/" target="_blank">2</a>。 <a href="#user-content-fnref-6">↩</a></p>
</li>
<li>
<p>2025年企业应用AI十大趋势报告，人工智能已从辅助工具跃升为新型”生产力系统”<a href="http://m.toutiao.com/group/7525451815022772746/" target="_blank">1</a>。 <a href="#user-content-fnref-7">↩</a></p>
</li>
<li>
<p>江苏省支持苏州市探索OPC创业新模式赋能人工智能产业发展政策文件<a href="http://m.toutiao.com/group/7571787355078558242/" target="_blank">1</a>。 <a href="#user-content-fnref-8">↩</a></p>
</li>
<li>
<p>2025亚布力中国企业家论坛年会资料，陈四清提出AI独角兽”小型化”趋势明显<a href="https://news.qq.com/rain/a/20250222A02H7300" target="_blank">4</a>。 <a href="#user-content-fnref-9">↩</a></p>
</li>
<li>
<p>信也科技2025年Q3财报，展示了AI与全球化双引擎驱动企业持续发展的案例<a href="http://m.toutiao.com/group/7574685800999305762/" target="_blank">3</a>。 <a href="#user-content-fnref-10">↩</a></p>
</li>
</ol>
</section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/react%E7%9A%84%E9%BB%98%E8%AE%A4%E8%83%9C%E5%88%A9%E6%AD%A3%E5%9C%A8%E6%89%BC%E6%9D%80%E5%89%8D%E7%AB%AF%E5%88%9B%E6%96%B0/</id>
      <title type="text">React的默认胜利正在扼杀前端创新（译）</title>
      <published>2025-10-05T00:00:00.000Z</published>
      <updated>2025-10-05T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/react%E7%9A%84%E9%BB%98%E8%AE%A4%E8%83%9C%E5%88%A9%E6%AD%A3%E5%9C%A8%E6%89%BC%E6%9D%80%E5%89%8D%E7%AB%AF%E5%88%9B%E6%96%B0/"/>
      <summary type="text">React 不再靠技术取胜，而是靠默认选择；这正在压制前端创新。</summary>
      <content type="html"><![CDATA[<div><div><div></div><div>Warning</div></div><div><p>本文为翻译仅供学习与交流使用，不涉及任何商业用途。如有侵权，请联系删除。</p></div></div>
<blockquote><p>原文标题：React Won by Default – And It’s Killing Frontend Innovation<br />
发布日期：2025-09-16<br />
译期：2025-10-04<br />
原文链接：<a href="https://www.lorenstew.art/blog/react-won-by-default/" target="_blank">react-won-by-default / Loren Stewart</a></p><p><strong>译者：</strong> 本文尝试说明React-by-default 有隐性成本。在选择框架时，应该做“刻意选择”，而不是出于惯性</p></blockquote>
<section><h2>序言<a href="#序言"><span>#</span></a></h2><p>React 早已不再靠技术优势取胜。今天它的胜利更多来自“默认选择”。而这种默认，正在拖慢整个前端生态的创新速度。</p><p>当团队需要做一个新的前端时，讨论很少从“约束是什么、哪种工具最合适？”开始。它更常见的开场是：“用 React 吧，大家都会。”这种反射式选择会形成自我强化的循环：决定架构的是网络效应，而不是技术适配度。</p><p>与此同时，具有新架构思路的框架很难被采用。Svelte 通过编译期优化配合运行时信号，让包体更小；Solid 在没有虚拟 DOM 额外负担的情况下实现细粒度响应式；Qwik 通过 resumability（可恢复性）实现几乎瞬时启动。它们在常见场景里可以胜过 React 的模型，但因为 React 被“默认选中”，它们很少得到公平评估。</p><p>React 在很多方面都很出色。问题不在 React 本身，而在“默认用 React”的心态。</p></section>
<section><h2>创新天花板<a href="#创新天花板"><span>#</span></a></h2><p>React 的技术基础解释了今天的一部分摩擦。虚拟 DOM 在 2013 年是个聪明的解决方案，但正如 Rich Harris 在《Virtual DOM is pure overhead》中所说，它引入了大量现代编译器往往可以避免的工作。</p><p>Hooks 解决了 class 组件时代的痛点，但也带来新的复杂度：依赖数组、陈旧闭包（stale closures）、被滥用的 effect。即便 React 官方文档也反复强调克制：“You Might Not Need an Effect”。Server Components 可以减少客户端 JavaScript，并允许只在服务端访问数据，但同时引入架构复杂度和新的失败模式。</p><p>React Compiler 是个聪明的方案，它把 <code>useMemo</code>/<code>useCallback</code> 之类的模式自动化。但它的存在也在传递一个信号：我们正在围绕模型内生的约束做优化。</p><p>2025 年 10 月发布的 React 19.2 继续沿着这条路走。新的 <code>useEffectEvent</code> Hook 专门用来解决 effect 里依赖数组的问题——这相当于给 Hooks 自己制造的复杂度打补丁。Svelte 和 Solid 也有类似的 untrack 能力，但它们默认采用自动依赖追踪，只在边缘场景才需要 untrack；而 React 需要在每一个 effect 里手工维护依赖数组，然后再引入 <code>useEffectEvent</code> 来绕开这种模型的限制。</p><p>同一个版本还引入了用于管理应用可见/隐藏状态的 <code>&lt;Activity /&gt;</code> 组件，以及新的部分预渲染 API。每一次新增都会扩大开发者必须掌握的 API 面，而其他框架往往能用更简单的原语达到相近的效果。</p><p>对比一下这些替代路线：Svelte 5 的 Runes 用运行时信号实现细粒度响应式；Solid 的细粒度响应式只更新真正发生变化的部分；Qwik 的 resumability 消除了传统意义上的 hydration。它们不是对 React 模型的渐进式修修补补，而是不同的模型、不同的上限。</p><p>没有被采用的创新，不会改变结果。而当选择出于“反射”，采用就不会发生。</p></section>
<section><h2>我们共同背负的技术债<a href="#我们共同背负的技术债"><span>#</span></a></h2><p>默认选择 React，常常意味着把一套我们已经不再质疑的运行时与协调（reconciliation）成本一起打包带走。即使它“够快”，天花板也仍然低于编译期方案或细粒度响应式模型。开发者把时间花在管理重渲染、effect 依赖和 hydration 边界上，而不是交付价值。性能研究的结论也一致：关键路径上的 JavaScript 很昂贵（The Cost of JavaScript）。</p><p>除了性能，我们还把心智模型中心从“Web 基础”挪到了“React 模式”，降低了技能的可迁移性，也更容易形成架构惰性。损失的不只是性能，更是机会成本：当更合适的替代方案从未被评估时，我们就错过了它们。</p></section>
<section><h2>被扼杀的框架<a href="#被扼杀的框架"><span>#</span></a></h2><section><h3>Svelte：信号 + 编译期优化<a href="#svelte信号--编译期优化"><span>#</span></a></h3><p>Svelte 5 把运行时信号用于细粒度响应式，并配合激进的编译期优化：没有虚拟 DOM，运行时比 React 更小。组件会被编译成高效、定向的 DOM 操作，心智模型也更贴近 Web 基础。</p><p>但“没有足够岗位”的叙事，让 Svelte 即便在很多用例里技术更强，也仍被人为压低了采用率。现实案例（例如 The Guardian 在其前端采用 Svelte）显示，它能显著提升性能与开发效率，带来更小的包体与更快的加载速度。比如 Wired 的一篇文章提到，开发者 Shawn Wang（@swyx on X/Twitter）利用 Svelte 的编译期优化，把自己网站从 React 的 187KB 降到 Svelte 的 9KB，这会让慢网环境下的体验大幅改善。</p></section><section><h3>Solid：响应式原语路线<a href="#solid响应式原语路线"><span>#</span></a></h3><p>Solid 在保留 JSX 熟悉感的同时，提供细粒度响应式：更新通过信号直接流向受影响的 DOM 节点，绕开协调带来的瓶颈。性能特征强，但心智占有率有限。正如 Solid 的对比指南所阐述，这种精确响应式能比 React 的虚拟 DOM 更高效地更新，减少无谓工作，并以更简单的状态管理改善开发体验。</p><p>Solid 的公开成功案例不如“更主流”的框架多，这在很大程度上也源于它的采用率较低。但早期采用者的口碑反馈显示，它同样可能带来更新效率与代码简洁性的质变，只是仍在等待更多团队去试、去规模化验证与传播。</p></section><section><h3>Qwik：可恢复性（Resumability）的创新<a href="#qwik可恢复性resumability的创新"><span>#</span></a></h3><p>Qwik 以 resumability 取代 hydration：通过只加载当前交互所需的代码，实现几乎瞬时启动。这要求应用状态可序列化，会带来架构上的约束，但能换来可量化的性能收益。它尤其适合内容型站点、慢网环境或移动优先应用。根据 Qwik 的 Think Qwik 指南，这通过渐进式加载以及对状态与代码的序列化来实现：应用无需沉重的客户端启动过程即可立刻恢复执行，因此相较传统框架有更好的可扩展性与更低的首屏成本。</p><p>Qwik 的成功故事不够显眼，可能只是因为更少团队愿意跳出默认去尝试。但真正试过的人报告了启动时间与资源效率的显著改善，这意味着一旦采用率上升，潜在的价值会被更多释放出来。</p><p>这三者之所以被低估，并不是因为它们不够好，而是因为“默认选择”阻断了尝试它们的机会。</p><p>此外，React 的 API 面明显更大、更复杂：Hooks、Context、Reducer、各种 memo 化模式……都需要谨慎管理才能避坑。这会抬高开发者的认知负担，进而带来因为误解依赖关系或过度工程而产生的缺陷。比如 Cloudflare 在 2025 年 9 月 12 日的一次故障中，一个 <code>useEffect</code> 中有问题的依赖数组触发了重复 API 调用，压垮了他们的 Tenant Service，导致大范围失败。相比之下，Svelte、Solid、Qwik 的 API 往往更小、更聚焦，更强调简洁与 Web 基础，降低了上手与维护成本。</p></section></section>
<section><h2>网络效应的牢笼<a href="#网络效应的牢笼"><span>#</span></a></h2><p>React 的主导地位会自我强化。招聘启事写的是“React 开发者”，而不是“前端工程师”，从而限制了技能多样性。组件库、团队肌肉记忆与组织惯性进一步加固了这种路径依赖。</p><p>风险规避的管理者会选择“更安全”的选项；学校教的是岗位要求的内容；循环与技术优劣无关地继续运转。</p><p>这不是健康竞争，而是“默认选择”对生态的捕获。</p></section>
<section><h2>打破网络效应<a href="#打破网络效应"><span>#</span></a></h2><p>逃离这种状态需要多层面的主动行动。技术负责人应该基于约束与技术优劣做选择，而不是追随势能；公司可以划出一小部分创新预算，用来尝试替代方案；开发者也可以在单一心智模型之外持续进修。</p><p>教育者可以在教授具体工具的同时，强化框架无关的概念；开源贡献者可以帮助替代生态走向成熟。</p><p>变化不会自动发生，它需要有意识的选择。</p></section>
<section><h2>框架评估清单<a href="#框架评估清单"><span>#</span></a></h2><p>在启动新项目时，用下面这份简单清单来做“刻意选择”：</p><ul>
<li>性能需求评估：关注启动时间、更新效率、包体积等指标；如果速度是关键，优先考虑编译期优化更强的框架。</li>
<li>团队技能与学习曲线：尊重既有经验，但也要把迁移路径纳入考虑；许多替代方案有温和上手路径（例如 Solid 与 React 的 JSX 兼容）。</li>
<li>规模化与持有成本：计算长期成本，包括维护、依赖管理与技术债；替代方案往往能减少运行时开销，从而降低托管成本并提升可扩展性。</li>
<li>生态适配：在成熟与创新之间平衡；先在非关键路径试点，用以验证迁移可行性与 ROI。</li>
</ul></section>
<section><h2>常见反驳<a href="#常见反驳"><span>#</span></a></h2><p>“但生态成熟度呢！” 成熟很有价值，也可能固化惰性。年龄不等于适配当下约束。</p><p>此外，一个成熟生态往往意味着对第三方包的重度依赖，这会引入维护负担：依赖升级、安全漏洞、以及因为未使用代码而膨胀的包体。虽然在某些场景里不可避免，但这种灵活性也容易变成过度依赖；围绕具体需求打造的定制化方案通常更轻、更易维护。替代框架的小生态反而鼓励从基础出发，形成更深的理解与更少的技术债。并且在 AI 编码助手可以按需生成精确自定义函数的今天，打造“为应用量身定制”的轻量工具函数的门槛已经显著降低，这让我们有可能完全避开像 lodash、Moment 或 date-fns 这类通用库，转而使用更轻的实现。</p><p>“但招聘呢！” 招聘跟着需求走。你可以先在非关键路径试点替代方案，降低风险；再以“基础能力 + 在岗训练”的方式招聘。</p><p>“但组件库呢！” 组件库往往是膨胀的代名词，主要在交付速度压倒一切时才最有用。围绕应用真实需求构建组件，会更精简，因为你不会为自己没有的问题打包代码。框架无关的设计系统与 Web Components 也能在保留交付速度的同时降低锁定风险。</p><p>“但稳定性呢！” React 从 class 到 Hooks、再到 Server Components、再到 React 19.2 的 <code>useEffectEvent</code> 与 <code>&lt;Activity /&gt;</code>，展示的是持续 churn，而不是稳定。许多替代框架反而提供更一致的 API。</p><p>“但大规模验证过呢！” jQuery 也曾被大规模验证过。过去的成功并不保证未来仍然相关。</p></section>
<section><h2>更广泛的生态伤害<a href="#更广泛的生态伤害"><span>#</span></a></h2><p>当一个框架的约束变成事实上的限制时，单一化会让 Web 演化放缓。人才把精力花在解决框架特有的问题上，而不是推动平台向前。资本也会不论技术优劣地追随既有赢家。</p><p>课程体系为了“立刻可就业”而不是“长期可迁移”，会优化成框架特定技能；平台层面的改进也可能被延后，因为“React 能解决”成了默认答案。</p><p>当多样性消失，整个生态都会受损。</p></section>
<section><h2>我们本可以培育的花园<a href="#我们本可以培育的花园"><span>#</span></a></h2><p>健康生态需要多样性，而不是单一栽培。创新来自不同路线的竞争与交叉授粉。开发者通过学习多个心智模型成长；平台也会在不同框架的推动下突破边界。</p><p>把所有筹码押在一个模型上，会制造单点失败。如果它触及硬性上限会怎样？如果我们从未探索替代方案，又错过了哪些机会？</p><p>是时候基于约束与技术优劣，而不是势能，来选择框架了。你的下一个项目不该被“默认用 React”绑架；生态也值得拥有只有多样性才能带来的创新。</p><p>不要再默认种同一种种子。通过探索多样化的框架，我们能培育出更韧性、更具创新力的花园，而不是我们已逐渐滑向的单一栽培。</p><p>选择权在我们手里。</p></section>
<section><h2>引用<a href="#引用"><span>#</span></a></h2><p>原文：<a href="https://www.lorenstew.art/blog/react-won-by-default/" target="_blank">react-won-by-default / Loren Stewart</a></p></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E9%A3%9E%E9%B9%85%E5%8D%9A%E5%AE%A2%E7%B3%BB%E7%BB%9F%E9%83%A8%E7%BD%B2%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/</id>
      <title type="text">飞鹅博客系统部署阿里云服务器</title>
      <published>2025-09-10T00:00:00.000Z</published>
      <updated>2025-09-10T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E9%A3%9E%E9%B9%85%E5%8D%9A%E5%AE%A2%E7%B3%BB%E7%BB%9F%E9%83%A8%E7%BD%B2%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/"/>
      <summary type="text"></summary>
      <content type="html"><![CDATA[<section><h1>飞鹅博客系统部署阿里云服务器<a href="#飞鹅博客系统部署阿里云服务器"><span>#</span></a></h1><p>首先你要满足如下条件</p><ol>
<li>你已经购买了阿里云服务器，并且已经登录到服务器上。</li>
<li>你已经安装了Node.js和npm。</li>
<li>你已经安装了MySQL数据库。</li>
</ol><section><h2>一、打包三个应用 前端 后端 管理端<a href="#一打包三个应用-前端-后端-管理端"><span>#</span></a></h2><ol>
<li>前端应用
修改 package.json 中的 scripts 字段，添加如下内容：
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>"scripts"</span><span>: {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>"build"</span><span>: </span><span>"cross-env BASE_URL=你的域名 nuxi build"</span><span>,</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
然后运行 <code>npm run build</code> 打包前端应用。
打包结果在 <code>.output</code> 目录下。</li>
<li>后端应用
如果你是windows系统，打包后端应用
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>$env</span><span>:</span><span>GOOS</span><span>=</span><span>"linux"</span><span>; </span><span>$env</span><span>:GOARCH=</span><span>"amd64"</span><span>; </span><span>go</span><span> </span><span>build</span><span> </span><span>-o</span><span> </span><span>flygoose-api-2.0-linux-amd64</span><span> </span><span>./cmd/flygoose</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
如果并不是的话，参考<a href="/posts/%E9%A3%9E%E9%B9%85%E5%8D%9A%E5%AE%A2%E7%B3%BB%E7%BB%9F%E5%88%9D%E5%A7%8B%E5%8C%96%E5%8F%8A%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%EF%BC%88%E4%BA%8C%EF%BC%89%E5%90%8E%E7%AB%AF.md">飞鹅博客系统二次开发指南</a>
打包结果为 <code>flygoose-api-2.0-linux-amd64</code></li>
<li>管理端应用
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>run</span><span> </span><span>build:prod</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
打包结果在 <code>dist</code> 目录下。</li>
</ol></section><section><h2>服务器分别部署<a href="#服务器分别部署"><span>#</span></a></h2><section><h3>安装docker docker-compose pm2等工具<a href="#安装docker-docker-compose-pm2等工具"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># Ubuntu系统</span></div></div><div><div><div>2</div></div><div><span>apt</span><span> </span><span>update</span><span> &amp;&amp; </span><span>apt</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>docker.io</span><span> </span><span>curl</span><span> </span><span>npm</span></div></div><div><div><div>3</div></div><div><span>sudo</span><span> </span><span>curl</span><span> </span><span>-L</span><span> </span><span>"https://github.com/docker/compose/releases/latest/download/docker-compose-$(</span><span>uname</span><span> </span><span>-s</span><span>)-$(</span><span>uname</span><span> </span><span>-m</span><span>)"</span><span> </span><span>-o</span><span> </span><span>/usr/local/bin/docker-compose</span></div></div><div><div><div>4</div></div><div><span>chmod</span><span> </span><span>+x</span><span> </span><span>/usr/local/bin/docker-compose</span></div></div><div><div><div>5</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>pm2</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># CentOS系统</span></div></div><div><div><div>8</div></div><div><span>yum</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>docker</span><span> </span><span>curl</span><span> </span><span>npm</span></div></div><div><div><div>9</div></div><div><span>systemctl</span><span> </span><span>start</span><span> </span><span>docker</span></div></div><div><div><div>10</div></div><div><span>systemctl</span><span> </span><span>enable</span><span> </span><span>docker</span></div></div><div><div><div>11</div></div><div><span>sudo</span><span> </span><span>curl</span><span> </span><span>-L</span><span> </span><span>"https://github.com/docker/compose/releases/latest/download/docker-compose-$(</span><span>uname</span><span> </span><span>-s</span><span>)-$(</span><span>uname</span><span> </span><span>-m</span><span>)"</span><span> </span><span>-o</span><span> </span><span>/usr/local/bin/docker-compose</span></div></div><div><div><div>12</div></div><div><span>chmod</span><span> </span><span>+x</span><span> </span><span>/usr/local/bin/docker-compose</span></div></div><div><div><div>13</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>pm2</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>nginx及docker compose配置<a href="#nginx及docker-compose配置"><span>#</span></a></h3><p>我的目录如下
</p><figure><img alt="目录结构" loading="lazy" width="342" height="34" src="/_astro/aliyun1.CU2-PexW_pg8IK.webp" /><figcaption>目录结构</figcaption></figure><p></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/home/aifire/</span></div></div><div><div><div>2</div></div><div><span>├── ai-fire-api/</span></div></div><div><div><div>3</div></div><div><span>├── ai-fire-blog/</span></div></div><div><div><div>4</div></div><div><span>├── ai-fire-admin/</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>nginx配置文件如下</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>server</span><span> {</span></div></div><div><div><div>2</div></div><div><span>   <span> </span></span><span>listen </span><span>80</span><span>;</span></div></div><div><div><div>3</div></div><div><span>   <span> </span></span><span>server_name </span><span>你的域名;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>    </span><span>location</span><span> / {</span></div></div><div><div><div>6</div></div><div><span>       <span> </span></span><span>proxy_pass </span><span>http://localhost:3000;</span></div></div><div><div><div>7</div></div><div><span>       <span> </span></span><span>proxy_http_version </span><span>1.1</span><span>;</span></div></div><div><div><div>8</div></div><div><span>       <span> </span></span><span>proxy_set_header </span><span>Upgrade $</span><span>http_upgrade</span><span>;</span></div></div><div><div><div>9</div></div><div><span>       <span> </span></span><span>proxy_set_header </span><span>Connection </span><span>'upgrade'</span><span>;</span></div></div><div><div><div>10</div></div><div><span>       <span> </span></span><span>proxy_set_header </span><span>Host $</span><span>host</span><span>;</span></div></div><div><div><div>11</div></div><div><span>       <span> </span></span><span>proxy_cache_bypass </span><span>$</span><span>http_upgrade</span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>docker compose配置文件如下</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>version</span><span>: </span><span>'3.8'</span></div></div><div><div><div>2</div></div><div><span>services</span><span>:</span></div></div><div><div><div>3</div></div><div><span>  </span><span>db</span><span>:</span></div></div><div><div><div>4</div></div><div><span>    </span><span>image</span><span>: </span><span>mysql:8.0</span></div></div><div><div><div>5</div></div><div><span>    </span><span>volumes</span><span>:</span></div></div><div><div><div>6</div></div><div><span><span>      </span></span><span>- </span><span>db_data:/var/lib/mysql</span></div></div><div><div><div>7</div></div><div><span>    </span><span>restart</span><span>: </span><span>always</span></div></div><div><div><div>8</div></div><div><span>    </span><span>environment</span><span>:</span></div></div><div><div><div>9</div></div><div><span>      </span><span>MYSQL_ROOT_PASSWORD</span><span>: </span><span>somewordpress</span></div></div><div><div><div>10</div></div><div><span>      </span><span>MYSQL_DATABASE</span><span>: </span><span>wordpress</span></div></div><div><div><div>11</div></div><div><span>      </span><span>MYSQL_USER</span><span>: </span><span>wordpress</span></div></div><div><div><div>12</div></div><div><span>      </span><span>MYSQL_PASSWORD</span><span>: </span><span>wordpress</span></div></div><div><div><div>13</div></div><div><span>  </span><span>wordpress</span><span>:</span></div></div><div><div><div>14</div></div><div><span>    </span><span>depends_on</span><span>:</span></div></div><div><div><div>15</div></div><div><span><span>      </span></span><span>- </span><span>db</span></div></div><div><div><div>16</div></div><div><span>    </span><span>image</span><span>: </span><span>wordpress:latest</span></div></div><div><div><div>17</div></div><div><span>    </span><span>ports</span><span>:</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>- </span><span>"8000:80"</span></div></div><div><div><div>19</div></div><div><span>    </span><span>restart</span><span>: </span><span>always</span></div></div><div><div><div>20</div></div><div><span>    </span><span>environment</span><span>:</span></div></div><div><div><div>21</div></div><div><span>      </span><span>WORDPRESS_DB_HOST</span><span>: </span><span>db:3306</span></div></div><div><div><div>22</div></div><div><span>      </span><span>WORDPRESS_DB_USER</span><span>: </span><span>wordpress</span></div></div><div><div><div>23</div></div><div><span>      </span><span>WORDPRESS_DB_PASSWORD</span><span>: </span><span>wordpress</span></div></div><div><div><div>24</div></div><div><span>      </span><span>WORDPRESS_DB_NAME</span><span>: </span><span>wordpress</span></div></div><div><div><div>25</div></div><div><span>volumes</span><span>:</span></div></div><div><div><div>26</div></div><div><span>  </span><span>db_data</span><span>: {}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>分别启动每个项目<a href="#分别启动每个项目"><span>#</span></a></h3><ol>
<li>
<p>前端blog</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cd</span><span> </span><span>ai-fire-blog</span></div></div><div><div><div>2</div></div><div><span>pm2</span><span> </span><span>start</span><span> </span><span>npm</span><span> </span><span>--name</span><span> </span><span>"ai-fire-blog"</span><span> </span><span>--</span><span> </span><span>run</span><span> </span><span>start</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>启动成功后如下
</p><figure><img alt="前端blog启动成功" loading="lazy" width="1054" height="91" src="/_astro/pm2-ls.DnAz_Uui_22IBfr.webp" /><figcaption>前端blog启动成功</figcaption></figure><p></p>
</li>
<li>
<p>后端api
增加启动权限</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>chmod</span><span> </span><span>+x</span><span> </span><span>flygoose-api-2.0-linux-amd64</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>启动后端api</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo</span><span> </span><span>chmod</span><span> </span><span>+x</span><span> </span><span>flygoose-api-2.0-linux-amd64</span></div></div><div><div><div>2</div></div><div><span>sudo</span><span> </span><span>nohup</span><span> </span><span>./flygoose-api-2.0-linux-amd64</span><span> </span><span>-c</span><span> </span><span>./flygoose-config.yml</span><span> &amp;</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>管理端admin</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cd</span><span> </span><span>ai-fire-admin</span></div></div><div><div><div>2</div></div><div><span>docker-compose</span><span> </span><span>up</span><span> </span><span>-d</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ol></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E9%A3%9E%E9%B9%85%E5%8D%9A%E5%AE%A2%E7%B3%BB%E7%BB%9F%E5%88%9D%E5%A7%8B%E5%8C%96%E5%8F%8A%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E4%B8%89%E7%AE%A1%E7%90%86%E7%AB%AF%E5%89%8D%E7%AB%AF/</id>
      <title type="text">飞鹅博客系统初始化及二次开发（三）管理端前端</title>
      <published>2025-09-03T00:00:00.000Z</published>
      <updated>2025-09-03T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E9%A3%9E%E9%B9%85%E5%8D%9A%E5%AE%A2%E7%B3%BB%E7%BB%9F%E5%88%9D%E5%A7%8B%E5%8C%96%E5%8F%8A%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E4%B8%89%E7%AE%A1%E7%90%86%E7%AB%AF%E5%89%8D%E7%AB%AF/"/>
      <summary type="text">详细介绍飞鹅博客系统的安装、配置和二次开发流程</summary>
      <content type="html"><![CDATA[<section><h1>飞鹅博客系统初始化及二次开发（三）管理端前端<a href="#飞鹅博客系统初始化及二次开发三管理端前端"><span>#</span></a></h1><section><h2>1. 项目概述<a href="#1-项目概述"><span>#</span></a></h2><p>飞鹅博客系统管理端是一个基于 Vue 3 + TypeScript 的现代化管理后台，用于管理博客系统的各类内容和配置。本项目采用了组件化、模块化的设计思想，提供了良好的可扩展性和维护性。</p><section><h3>1.1 技术栈<a href="#11-技术栈"><span>#</span></a></h3>

<table><thead><tr><th>技术/框架</th><th>版本</th><th>用途</th></tr></thead><tbody><tr><td>Vue.js</td><td>3.x</td><td>前端框架</td></tr><tr><td>TypeScript</td><td>5.x</td><td>类型系统</td></tr><tr><td>Pinia</td><td>2.x</td><td>状态管理</td></tr><tr><td>Ant Design Vue</td><td>4.x</td><td>UI 组件库</td></tr><tr><td>Vue Router</td><td>4.x</td><td>路由管理</td></tr><tr><td>Axios</td><td>1.x</td><td>HTTP 请求</td></tr><tr><td>Vite</td><td>5.x</td><td>构建工具</td></tr><tr><td>Less</td><td>4.x</td><td>CSS 预处理器</td></tr></tbody></table></section></section><section><h2>2. 环境搭建<a href="#2-环境搭建"><span>#</span></a></h2><section><h3>2.1 安装依赖<a href="#21-安装依赖"><span>#</span></a></h3><p>项目使用 pnpm 作为包管理工具，首先确保已安装 pnpm：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>pnpm</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后安装项目依赖：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cd</span><span> </span><span>ai-fire-admin</span></div></div><div><div><div>2</div></div><div><span>pnpm</span><span> </span><span>install</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2.2 配置文件<a href="#22-配置文件"><span>#</span></a></h3><p>项目包含两个主要的环境配置文件：</p><ul>
<li><code>.env.development</code>：开发环境配置</li>
<li><code>.env.production</code>：生产环境配置</li>
</ul><p>配置文件中定义了 API 基础路径等关键信息，可根据实际情况修改：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 开发环境示例</span></div></div><div><div><div>2</div></div><div><span>VITE_API_BASE_URL</span><span><span> </span><span>=</span><span> </span></span><span>'/api'</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2.3 启动开发服务器<a href="#23-启动开发服务器"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>pnpm</span><span> </span><span>dev</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>开发服务器默认会在 <code>http://localhost:5173</code> 启动。</p></section></section><section><h2>3. 项目结构<a href="#3-项目结构"><span>#</span></a></h2><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>ai-fire-admin/</span></div></div><div><div><div>2</div></div><div><span>├── public/              # 静态资源</span></div></div><div><div><div>3</div></div><div><span>├── src/                 # 源代码</span></div></div><div><div><div>4</div></div><div><span>│   ├── apis/            # API接口定义</span></div></div><div><div><div>5</div></div><div><span>│   ├── assets/          # 静态资源</span></div></div><div><div><div>6</div></div><div><span>│   ├── components/      # 自定义组件</span></div></div><div><div><div>7</div></div><div><span>│   ├── layouts/         # 布局组件</span></div></div><div><div><div>8</div></div><div><span>│   ├── router/          # 路由配置</span></div></div><div><div><div>9</div></div><div><span>│   ├── stores/          # 状态管理</span></div></div><div><div><div>10</div></div><div><span>│   ├── types/           # TypeScript类型定义</span></div></div><div><div><div>11</div></div><div><span>│   ├── utils/           # 工具函数</span></div></div><div><div><div>12</div></div><div><span>│   ├── views/           # 页面组件</span></div></div><div><div><div>13</div></div><div><span>│   ├── App.vue          # 根组件</span></div></div><div><div><div>14</div></div><div><span>│   └── main.ts          # 入口文件</span></div></div><div><div><div>15</div></div><div><span>├── nginx/               # Nginx配置</span></div></div><div><div><div>16</div></div><div><span>├── .env.*               # 环境配置</span></div></div><div><div><div>17</div></div><div><span>├── package.json         # 项目配置</span></div></div><div><div><div>18</div></div><div><span>├── vite.config.ts       # Vite配置</span></div></div><div><div><div>19</div></div><div><span>└── tsconfig.json        # TypeScript配置</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><section><h3>3.1 核心目录说明<a href="#31-核心目录说明"><span>#</span></a></h3><section><h4>3.1.1 <code>src/apis/</code><a href="#311-srcapis"><span>#</span></a></h4><p>定义了与后端交互的所有 API 接口，每个模块对应一个独立的文件：</p><div><figure><figcaption><span>website-info.ts</span></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>request</span><span> </span><span>from</span><span> </span><span>'@/utils/http'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>getSiteInfoList</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>return</span><span><span> </span><span>request</span><span>.</span></span><span>get</span><span>(</span><span>'/api/website/info/list'</span><span>)</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>createSiteInfo</span><span> </span><span>=</span><span><span> (</span><span>data</span></span><span>:</span><span> </span><span>any</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>8</div></div><div><span>  </span><span>return</span><span><span> </span><span>request</span><span>.</span></span><span>post</span><span>(</span><span>'/api/website/info/create'</span><span><span>, </span><span>data</span><span>)</span></span></div></div><div><div><div>9</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>3.1.2 <code>src/components/</code><a href="#312-srccomponents"><span>#</span></a></h4><p>包含项目中使用的自定义组件，如表格、表单、模态框等：</p><ul>
<li><code>table.vue</code>：通用表格组件</li>
<li><code>form.vue</code>：通用表单组件</li>
<li><code>modal.vue</code>：通用模态框组件</li>
<li><code>pagination.vue</code>：分页组件</li>
</ul></section><section><h4>3.1.3 <code>src/layouts/</code><a href="#313-srclayouts"><span>#</span></a></h4><p>定义了系统的整体布局结构：</p><ul>
<li><code>default-layout.vue</code>：默认布局，包含侧边栏、顶部导航和内容区域</li>
<li><code>menu.vue</code>：侧边栏菜单组件</li>
<li><code>foot.vue</code>：页脚组件</li>
</ul></section><section><h4>3.1.4 <code>src/router/</code><a href="#314-srcrouter"><span>#</span></a></h4><p>配置了系统的路由结构：</p><div><div><div><figure><figcaption><span>index.ts</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>createRouter</span><span>, </span><span>createWebHistory</span><span> } </span><span>from</span><span> </span><span>'vue-router'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>DefaultLayout</span><span> </span><span>from</span><span> </span><span>'@/layouts/default-layout.vue'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>router</span><span> </span><span>=</span><span> </span><span>createRouter</span><span>({</span></div></div><div><div><div>5</div></div><div><span>  </span><span>history</span><span>:</span><span> </span><span>createWebHistory</span><span>(</span><span>import</span><span>.</span><span>meta</span><span>.</span><span>env</span><span>.</span><span>BASE_URL</span><span>),</span></div></div><div><div><div>6</div></div><div><span>  </span><span>routes</span><span>:</span><span> [</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>8</div></div><div><span>      </span><span>path</span><span>:</span><span> </span><span>'/'</span><span>,</span></div></div><div><div><div>9</div></div><div><span>      </span><span>name</span><span>:</span><span> </span><span>'home'</span><span>,</span></div></div><div><div><div>10</div></div><div><span>      </span><span>component</span><span>:</span><span><span> </span><span>DefaultLayout</span><span>,</span></span></div></div><div><div><div>11</div></div><div><span>      </span><span>children</span><span>:</span><span> [</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>13</div></div><div><span>          </span><span>path</span><span>:</span><span> </span><span>''</span><span>,</span></div></div><div><div><div>14</div></div><div><span>          </span><span>name</span><span>:</span><span> </span><span>'dashboard'</span><span>,</span></div></div><div><div><div>15</div></div><div><span>          </span><span>component</span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>import</span><span>(</span><span>'../views/dashboard/index.vue'</span><span>)</span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span>        </span><span>// 其他路由...</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>]</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>21</div></div><div><span>      </span><span>path</span><span>:</span><span> </span><span>'/login'</span><span>,</span></div></div><div><div><div>22</div></div><div><span>      </span><span>name</span><span>:</span><span> </span><span>'login'</span><span>,</span></div></div><div><div><div>23</div></div><div><span>      </span><span>component</span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>import</span><span>(</span><span>'../views/login/index.vue'</span><span>)</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>25</div></div><div><span><span>  </span></span><span>]</span></div></div><div><div><div>26</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>3.1.5 <code>src/stores/</code><a href="#315-srcstores"><span>#</span></a></h4><p>使用 Pinia 进行状态管理，按功能模块划分：</p><div><div><div><figure><figcaption><span>module/website-info.ts</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>defineStore</span><span> } </span><span>from</span><span> </span><span>'pinia'</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>websiteInfoApi</span><span> </span><span>from</span><span> </span><span>'@/apis/website-info'</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>useWebsiteInfoStore</span><span> </span><span>=</span><span> </span><span>defineStore</span><span>(</span><span>'websiteInfo'</span><span>, {</span></div></div><div><div><div>5</div></div><div><span>  </span><span>state</span><span>:</span><span> () </span><span>=&gt;</span><span> ({</span></div></div><div><div><div>6</div></div><div><span>    </span><span>websiteInfoList</span><span>:</span><span> []</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>}),</span></div></div><div><div><div>8</div></div><div><span>  </span><span>actions</span><span>:</span><span> {</span></div></div><div><div><div>9</div></div><div><span>    </span><span>async</span><span> </span><span>getSiteInfoListAction</span><span>() {</span></div></div><div><div><div>10</div></div><div><span>      </span><span>const</span><span> </span><span>res</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>websiteInfoApi</span><span>.</span></span><span>getSiteInfoList</span><span>()</span></div></div><div><div><div>11</div></div><div><span>      </span><span>this</span><span>.</span><span>websiteInfoList</span><span> </span><span>=</span><span><span> </span><span>res</span><span>.</span></span><span>data</span></div></div><div><div><div>12</div></div><div><span>      </span><span>return</span><span><span> </span><span>res</span><span>.</span></span><span>data</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>3.1.6 <code>src/utils/</code><a href="#316-srcutils"><span>#</span></a></h4><p>包含项目中使用的工具函数：</p><ul>
<li><code>http.ts</code>：Axios 实例配置</li>
<li><code>constant.ts</code>：常量定义</li>
</ul></section><section><h4>3.1.7 <code>src/views/</code><a href="#317-srcviews"><span>#</span></a></h4><p>包含系统的所有页面组件，按功能模块划分：</p><ul>
<li><code>dashboard/</code>：仪表盘</li>
<li><code>blog/</code>：博客管理</li>
<li><code>category/</code>：分类管理</li>
<li><code>website-info/</code>：网站信息管理</li>
<li><code>login/</code>：登录页面</li>
</ul></section></section></section><section><h2>4. 核心功能模块<a href="#4-核心功能模块"><span>#</span></a></h2><section><h3>4.1 登录与权限管理<a href="#41-登录与权限管理"><span>#</span></a></h3><p>系统实现了基于 JWT 的登录认证机制：</p><ol>
<li>用户输入账号密码登录</li>
<li>后端验证通过后返回 JWT token</li>
<li>前端将 token 存储到 localStorage</li>
<li>后续请求通过 Authorization 头携带 token</li>
</ol><p>登录逻辑实现：</p><div><figure><figcaption><span>views/login/index.vue</span></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>handleLogin</span><span> </span><span>=</span><span> </span><span>async</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>try</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>const</span><span> </span><span>res</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>userApi</span><span>.</span></span><span>login</span><span><span>(</span><span>formData</span><span>.</span></span><span>value</span><span>)</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>localStorage</span><span>.</span><span>setItem</span><span>(</span><span>'token'</span><span><span>, </span><span>res</span><span>.</span></span><span>data</span><span>.</span><span>token</span><span>)</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>router</span><span>.</span><span>push</span><span>(</span><span>'/'</span><span>)</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>message</span><span>.</span><span>success</span><span>(</span><span>'登录成功'</span><span>)</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>} </span><span>catch</span><span><span> (</span><span>error</span><span>) {</span></span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>message</span><span>.</span><span>error</span><span>(</span><span>'登录失败'</span><span>)</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>4.2 网站信息管理<a href="#42-网站信息管理"><span>#</span></a></h3><p>网站信息管理模块用于配置博客系统的基本信息，如标题、标语、封面、版权信息等。</p><p>主要功能：</p><ul>
<li>查看网站信息列表</li>
<li>选择并查看详情</li>
<li>编辑网站信息</li>
<li>创建新的网站信息</li>
</ul><p>实现细节：</p><div><figure><figcaption><span>views/website-info/website-info.vue</span></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>Table</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>:</span><span>data-source</span><span>=</span><span>"</span><span>dataSource</span><span>"</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>:</span><span>columns</span><span>=</span><span>"</span><span>columns</span><span>"</span></div></div><div><div><div>4</div></div><div><span>  </span><span>ref</span><span>=</span><span>"table"</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>@</span><span>edit</span><span>=</span><span>"</span><span>edit</span><span>"</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>@</span><span>change</span><span>=</span><span>"</span><span>handleTableChange</span><span>"</span></div></div><div><div><div>7</div></div><div><span>&gt;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>&lt;!-- 表格内容 --&gt;</span></div></div><div><div><div>9</div></div><div><span>&lt;/</span><span>Table</span><span>&gt;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>&lt;</span><span>a-descriptions</span><span> </span><span>bordered</span><span> </span><span>style</span><span>=</span><span>"</span><span>margin-top: </span><span><span>20</span><span>px</span></span><span>;</span><span>"</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>&lt;a-descriptions-item label="网站标题"&gt;{{ websiteInfo.title }}&lt;/a-descriptions-item&gt;</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>&lt;!-- 其他信息项 --&gt;</span></div></div><div><div><div>14</div></div><div><span>&lt;/</span><span>a-descriptions</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>4.3 博客管理<a href="#43-博客管理"><span>#</span></a></h3><p>博客管理模块用于管理博客文章，支持以下功能：</p><ul>
<li>博客列表展示与分页</li>
<li>博客文章的创建、编辑、删除</li>
<li>富文本编辑</li>
<li>分类与标签管理</li>
<li>状态管理（已发布/草稿）</li>
</ul></section><section><h3>4.4 分类管理<a href="#44-分类管理"><span>#</span></a></h3><p>分类管理模块用于管理博客文章的分类，支持：</p><ul>
<li>分类列表展示</li>
<li>分类的创建、编辑、删除</li>
<li>分类排序</li>
<li>分类状态管理</li>
</ul></section><section><h3>4.5 仪表盘<a href="#45-仪表盘"><span>#</span></a></h3><p>仪表盘模块提供系统概览，包括：</p><ul>
<li>博客文章统计</li>
<li>分类统计</li>
<li>访问量统计</li>
<li>最新动态</li>
</ul></section></section><section><h2>5. 二次开发指南<a href="#5-二次开发指南"><span>#</span></a></h2><section><h3>5.1 添加新页面<a href="#51-添加新页面"><span>#</span></a></h3><ol>
<li>在 <code>src/views/</code> 目录下创建新的页面组件</li>
<li>在 <code>src/router/</code> 中配置路由</li>
<li>在侧边栏菜单中添加新的菜单项</li>
</ol><p>示例：添加新页面</p><div><figure><figcaption><span>src/views/new-page/index.vue</span></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"new-page"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>h1</span><span>&gt;新页面&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>&lt;</span><span>script</span><span> </span><span>setup</span><span> </span><span>lang</span><span>=</span><span>"ts"</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span>// 页面逻辑</span></div></div><div><div><div>9</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><div><div><figure><figcaption><span>src/router/index.ts</span><span>展开</span><span>收起</span></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>router</span><span> </span><span>=</span><span> </span><span>createRouter</span><span>({</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// ...</span></div></div><div><div><div>3</div></div><div><span>  </span><span>routes</span><span>:</span><span> [</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>5</div></div><div><span>      </span><span>path</span><span>:</span><span> </span><span>'/'</span><span>,</span></div></div><div><div><div>6</div></div><div><span>      </span><span>component</span><span>:</span><span><span> </span><span>DefaultLayout</span><span>,</span></span></div></div><div><div><div>7</div></div><div><span>      </span><span>children</span><span>:</span><span> [</span></div></div><div><div><div>8</div></div><div><span>        </span><span>// ...</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>{</span></div></div><div><div><div>10</div></div><div><span>          </span><span>path</span><span>:</span><span> </span><span>'/new-page'</span><span>,</span></div></div><div><div><div>11</div></div><div><span>          </span><span>name</span><span>:</span><span> </span><span>'newPage'</span><span>,</span></div></div><div><div><div>12</div></div><div><span>          </span><span>component</span><span>:</span><span> () </span><span>=&gt;</span><span> </span><span>import</span><span>(</span><span>'../views/new-page/index.vue'</span><span>)</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span><span>      </span></span><span>]</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>]</span></div></div><div><div><div>17</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>5.2 添加新组件<a href="#52-添加新组件"><span>#</span></a></h3><ol>
<li>在 <code>src/components/</code> 目录下创建新的组件</li>
<li>在需要使用的地方导入并使用</li>
</ol><p>示例：创建新组件</p><div><figure><figcaption><span>src/components/my-component.vue</span></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"my-component"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>slot</span><span>&gt;&lt;/</span><span>slot</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>&lt;</span><span>script</span><span> </span><span>setup</span><span> </span><span>lang</span><span>=</span><span>"ts"</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span>// 组件逻辑</span></div></div><div><div><div>9</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>5.3 扩展 API<a href="#53-扩展-api"><span>#</span></a></h3><ol>
<li>在 <code>src/apis/</code> 目录下创建新的 API 文件</li>
<li>定义 API 函数</li>
<li>在组件或 store 中使用</li>
</ol><p>示例：添加新 API</p><div><figure><figcaption><span>src/apis/new-api.ts</span></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>request</span><span> </span><span>from</span><span> </span><span>'@/utils/http'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>getNewData</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>return</span><span><span> </span><span>request</span><span>.</span></span><span>get</span><span>(</span><span>'/api/new-data'</span><span>)</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>5.4 自定义主题<a href="#54-自定义主题"><span>#</span></a></h3><p>系统使用 Ant Design Vue 的主题定制功能，可以通过修改 <code>src/assets/css/variables.less</code> 文件来自定义主题颜色：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 自定义主题色</span></div></div><div><div><div>2</div></div><div><span>@primary-color</span><span>: </span><span>#1890ff</span><span>;</span></div></div><div><div><div>3</div></div><div><span>@success-color</span><span>: </span><span>#52c41a</span><span>;</span></div></div><div><div><div>4</div></div><div><span>@warning-color</span><span>: </span><span>#faad14</span><span>;</span></div></div><div><div><div>5</div></div><div><span>@error-color</span><span>: </span><span>#f5222d</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>6. 部署流程<a href="#6-部署流程"><span>#</span></a></h2><section><h3>6.1 构建生产版本<a href="#61-构建生产版本"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>pnpm</span><span> </span><span>build</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>构建后的文件会输出到 <code>dist/</code> 目录。</p></section><section><h3>6.2 Nginx 配置<a href="#62-nginx-配置"><span>#</span></a></h3><p>项目提供了 Nginx 配置文件 <code>nginx/nginx.conf</code>，可根据实际情况修改：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 全局配置</span></div></div><div><div><div>2</div></div><div><span>user </span><span>nginx;</span></div></div><div><div><div>3</div></div><div><span>worker_processes </span><span>auto;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># http 配置</span></div></div><div><div><div>6</div></div><div><span>http</span><span> {</span></div></div><div><div><div>7</div></div><div><span>   <span> </span></span><span>include </span><span>      mime.types;</span></div></div><div><div><div>8</div></div><div><span>   <span> </span></span><span>default_type </span><span> application/octet-stream;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>    </span><span># 服务器配置</span></div></div><div><div><div>11</div></div><div><span>    </span><span>server</span><span> {</span></div></div><div><div><div>12</div></div><div><span>       <span> </span></span><span>listen </span><span>      </span><span>8080</span><span>;</span></div></div><div><div><div>13</div></div><div><span>       <span> </span></span><span>server_name </span><span> localhost;</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>        </span><span># 前端静态资源</span></div></div><div><div><div>16</div></div><div><span>        </span><span>location</span><span> / {</span></div></div><div><div><div>17</div></div><div><span>           <span> </span></span><span>root </span><span>  /usr/share/nginx/html;</span></div></div><div><div><div>18</div></div><div><span>           <span> </span></span><span>index </span><span> index.html index.htm;</span></div></div><div><div><div>19</div></div><div><span>           <span> </span></span><span>try_files </span><span>$</span><span>uri</span><span> $</span><span>uri</span><span>/ /index.html;</span></div></div><div><div><div>20</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>        </span><span># API 代理</span></div></div><div><div><div>23</div></div><div><span>        </span><span>location</span><span> /api {</span></div></div><div><div><div>24</div></div><div><span>           <span> </span></span><span>proxy_pass </span><span>http://127.0.0.1:29090;</span></div></div><div><div><div>25</div></div><div><span>           <span> </span></span><span>proxy_set_header </span><span>Host $</span><span>host</span><span>;</span></div></div><div><div><div>26</div></div><div><span>           <span> </span></span><span>proxy_set_header </span><span>X-Real-IP $</span><span>remote_addr</span><span>;</span></div></div><div><div><div>27</div></div><div><span>           <span> </span></span><span>proxy_set_header </span><span>X-Forwarded-For $</span><span>proxy_add_x_forwarded_for</span><span>;</span></div></div><div><div><div>28</div></div><div><span>           <span> </span></span><span>proxy_set_header </span><span>X-Forwarded-Proto $</span><span>scheme</span><span>;</span></div></div><div><div><div>29</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>31</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>6.3 部署步骤<a href="#63-部署步骤"><span>#</span></a></h3><ol>
<li>将构建后的 <code>dist/</code> 目录上传到服务器的 <code>/usr/share/nginx/html/</code> 目录</li>
<li>将 Nginx 配置文件复制到服务器的 <code>/etc/nginx/conf.d/</code> 目录</li>
<li>重新加载 Nginx 配置：
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>nginx</span><span> </span><span>-s</span><span> </span><span>reload</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
</ol></section></section><section><h2>7. 开发规范<a href="#7-开发规范"><span>#</span></a></h2><section><h3>7.1 代码风格<a href="#71-代码风格"><span>#</span></a></h3><p>项目使用 ESLint 和 Prettier 来保证代码风格的一致性，提交代码前会自动进行检查。</p></section><section><h3>7.2 命名规范<a href="#72-命名规范"><span>#</span></a></h3><ul>
<li>组件名：大驼峰命名，如 <code>WebsiteInfo</code></li>
<li>文件/目录名：小写字母加短横线，如 <code>website-info.vue</code></li>
<li>变量名：小驼峰命名，如 <code>websiteInfoList</code></li>
<li>常量名：全大写加下划线，如 <code>API_BASE_URL</code></li>
</ul></section><section><h3>7.3 注释规范<a href="#73-注释规范"><span>#</span></a></h3><ul>
<li>组件和函数使用 JSDoc 注释</li>
<li>复杂逻辑添加必要的单行注释</li>
<li>代码变更添加提交注释</li>
</ul></section></section><section><h2>8. 常见问题与解决方案<a href="#8-常见问题与解决方案"><span>#</span></a></h2><section><h3>8.1 跨域问题<a href="#81-跨域问题"><span>#</span></a></h3><p>开发环境中，Vite 配置了代理解决跨域问题：</p><div><figure><figcaption><span>vite.config.ts</span></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>default</span><span> </span><span>defineConfig</span><span>({</span></div></div><div><div><div>2</div></div><div><span>  </span><span>// ...</span></div></div><div><div><div>3</div></div><div><span>  </span><span>server</span><span>:</span><span> {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>proxy</span><span>:</span><span> {</span></div></div><div><div><div>5</div></div><div><span>      </span><span>'/api'</span><span>:</span><span> {</span></div></div><div><div><div>6</div></div><div><span>        </span><span>target</span><span>:</span><span> </span><span>'http://localhost:29090'</span><span>,</span></div></div><div><div><div>7</div></div><div><span>        </span><span>changeOrigin</span><span>:</span><span> </span><span>true</span></div></div><div><div><div>8</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>生产环境中，通过 Nginx 代理解决跨域问题。</p></section><section><h3>8.2 构建失败<a href="#82-构建失败"><span>#</span></a></h3><ul>
<li>检查依赖是否正确安装</li>
<li>检查 TypeScript 类型是否正确</li>
<li>检查语法错误</li>
</ul></section><section><h3>8.3 页面空白<a href="#83-页面空白"><span>#</span></a></h3><ul>
<li>检查路由配置是否正确</li>
<li>检查组件是否正确导入</li>
<li>检查网络请求是否正常</li>
</ul></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E9%A3%9E%E9%B9%85%E5%8D%9A%E5%AE%A2%E7%B3%BB%E7%BB%9F%E5%88%9D%E5%A7%8B%E5%8C%96%E5%8F%8A%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E4%BA%8C%E5%90%8E%E7%AB%AF/</id>
      <title type="text">飞鹅博客系统初始化及二次开发（二）后端</title>
      <published>2025-08-02T00:00:00.000Z</published>
      <updated>2025-08-02T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E9%A3%9E%E9%B9%85%E5%8D%9A%E5%AE%A2%E7%B3%BB%E7%BB%9F%E5%88%9D%E5%A7%8B%E5%8C%96%E5%8F%8A%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E4%BA%8C%E5%90%8E%E7%AB%AF/"/>
      <summary type="text">详细介绍飞鹅博客系统的安装、配置和二次开发流程</summary>
      <content type="html"><![CDATA[<section><h1>飞鹅博客系统初始化及二次开发（二）后端<a href="#飞鹅博客系统初始化及二次开发二后端"><span>#</span></a></h1><section><h2>一、后端开发<a href="#一后端开发"><span>#</span></a></h2><section><h3>1.1 项目结构解析<a href="#11-项目结构解析"><span>#</span></a></h3><p>飞鹅博客系统后端基于Go语言和Iris框架开发，采用了清晰的分层架构设计。以下是项目的核心目录结构：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>├── cmd/                      # 命令行入口</span></div></div><div><div><div>2</div></div><div><span>│   └── flygoose/             # 主程序入口</span></div></div><div><div><div>3</div></div><div><span>│       ├── flygoose-config.yaml  # 配置文件示例</span></div></div><div><div><div>4</div></div><div><span>│       └── main.go           # 程序启动文件</span></div></div><div><div><div>5</div></div><div><span>├── configs/                  # 配置相关</span></div></div><div><div><div>6</div></div><div><span>│   └── config.go             # 配置结构定义</span></div></div><div><div><div>7</div></div><div><span>├── datasource/               # 数据源连接</span></div></div><div><div><div>8</div></div><div><span>│   └── db.go                 # 数据库初始化</span></div></div><div><div><div>9</div></div><div><span>├── inits/                    # 应用初始化</span></div></div><div><div><div>10</div></div><div><span>│   ├── flygoose_init.go      # 应用初始化逻辑</span></div></div><div><div><div>11</div></div><div><span>│   └── middle.go             # 中间件配置</span></div></div><div><div><div>12</div></div><div><span>├── pkg/                      # 公共包</span></div></div><div><div><div>13</div></div><div><span>│   ├── beans/                # 数据传输对象</span></div></div><div><div><div>14</div></div><div><span>│   ├── models/               # 数据模型</span></div></div><div><div><div>15</div></div><div><span>│   ├── tlog/                 # 日志工具</span></div></div><div><div><div>16</div></div><div><span>│   └── tools/                # 通用工具</span></div></div><div><div><div>17</div></div><div><span>├── web/                      # Web相关</span></div></div><div><div><div>18</div></div><div><span>│   ├── controllers/          # 控制器</span></div></div><div><div><div>19</div></div><div><span>│   ├── daos/                 # 数据访问层</span></div></div><div><div><div>20</div></div><div><span>│   ├── middlers/             # 中间件</span></div></div><div><div><div>21</div></div><div><span>│   └── services/             # 业务逻辑层</span></div></div><div><div><div>22</div></div><div><span>└── docs/                     # 文档</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>└── 接口文档.md           # API接口文档</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>1.2 核心配置文件说明<a href="#12-核心配置文件说明"><span>#</span></a></h3><section><h4>1.2.1 主配置文件结构<a href="#121-主配置文件结构"><span>#</span></a></h4><p>配置文件位于<code>configs/config.go</code>，定义了系统的核心配置结构：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 全局配置文件实例</span></div></div><div><div><div>2</div></div><div><span>var</span><span> </span><span>Cfg</span><span><span> </span><span>*</span></span><span>Config</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>type</span><span> </span><span>Config</span><span> </span><span>struct</span><span> {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>Http</span><span>         </span><span>HttpCfg</span><span>     </span><span>`yaml:"http"`</span><span>         </span><span>// HTTP服务配置</span></div></div><div><div><div>6</div></div><div><span>    </span><span>Database</span><span>     </span><span>DatabaseCfg</span><span> </span><span>`yaml:"database"`</span><span>     </span><span>// 数据库配置</span></div></div><div><div><div>7</div></div><div><span>    </span><span>ExecuteDir</span><span>   </span><span>string</span><span>                             </span><span>// 可执行文件目录</span></div></div><div><div><div>8</div></div><div><span>    </span><span>StaticDir</span><span>    </span><span>string</span><span>                             </span><span>// 静态文件目录</span></div></div><div><div><div>9</div></div><div><span>    </span><span>StaticImgDir</span><span> </span><span>string</span><span>                             </span><span>// 图片静态目录</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>type</span><span> </span><span>HttpCfg</span><span> </span><span>struct</span><span> {</span></div></div><div><div><div>13</div></div><div><span>    </span><span>Port</span><span> </span><span>int</span><span> </span><span>`yaml:"port"`</span><span>                         </span><span>// 服务端口</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>type</span><span> </span><span>DatabaseCfg</span><span> </span><span>struct</span><span> {</span></div></div><div><div><div>17</div></div><div><span>    </span><span>Driver</span><span>   </span><span>DbDriverType</span><span> </span><span>`yaml:"driver"`</span><span>          </span><span>// 数据库类型(mysql或postgresql)</span></div></div><div><div><div>18</div></div><div><span>    </span><span>Host</span><span>     </span><span>string</span><span>       </span><span>`yaml:"host"`</span><span>            </span><span>// 数据库IP</span></div></div><div><div><div>19</div></div><div><span>    </span><span>Name</span><span>     </span><span>string</span><span>       </span><span>`yaml:"name"`</span><span>            </span><span>// 数据库名称</span></div></div><div><div><div>20</div></div><div><span>    </span><span>Port</span><span>     </span><span>int</span><span>          </span><span>`yaml:"port"`</span><span>            </span><span>// 数据库端口</span></div></div><div><div><div>21</div></div><div><span>    </span><span>User</span><span>     </span><span>string</span><span>       </span><span>`yaml:"user"`</span><span>            </span><span>// 用户名</span></div></div><div><div><div>22</div></div><div><span>    </span><span>Password</span><span> </span><span>string</span><span>       </span><span>`yaml:"password"`</span><span>        </span><span>// 密码</span></div></div><div><div><div>23</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>1.2.2 配置文件示例<a href="#122-配置文件示例"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 服务端口</span></div></div><div><div><div>2</div></div><div><span>http</span><span>:</span></div></div><div><div><div>3</div></div><div><span>  </span><span>port</span><span>: </span><span>29090</span></div></div><div><div><div>4</div></div><div><span># 数据库配置</span></div></div><div><div><div>5</div></div><div><span>database</span><span>:</span></div></div><div><div><div>6</div></div><div><span>  </span><span>driver</span><span>: </span><span>mysql</span></div></div><div><div><div>7</div></div><div><span>  </span><span>host</span><span>: </span><span>127.0.0.1</span></div></div><div><div><div>8</div></div><div><span>  </span><span>port</span><span>: </span><span>3306</span></div></div><div><div><div>9</div></div><div><span>  </span><span>name</span><span>: </span><span>db_flygoose</span></div></div><div><div><div>10</div></div><div><span>  </span><span>user</span><span>: </span><span>flygoose</span></div></div><div><div><div>11</div></div><div><span>  </span><span>password</span><span>: </span><span>flygoose</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h3>1.3 应用初始化流程<a href="#13-应用初始化流程"><span>#</span></a></h3><p>应用初始化主要在<code>inits/flygoose_init.go</code>中实现，包含以下核心步骤：</p><section><h4>1.3.1 创建应用实例<a href="#131-创建应用实例"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>func</span><span> </span><span>NewFlygooseApp</span><span><span>(</span><span>cfg</span><span> </span><span>*</span></span><span>configs</span><span>.</span><span>Config</span><span><span>) </span><span>*</span></span><span>FlygooseApp</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>app</span><span><span> </span><span>:=</span><span> </span></span><span>new</span><span>(</span><span>FlygooseApp</span><span>)</span></div></div><div><div><div>3</div></div><div><span>    </span><span>app</span><span>.</span><span>Cfg</span><span><span> </span><span>=</span><span> </span></span><span>cfg</span></div></div><div><div><div>4</div></div><div><span>    </span><span>app</span><span>.</span><span>Engine</span><span><span> </span><span>=</span><span> </span></span><span>iris</span><span>.</span><span>New</span><span>()</span></div></div><div><div><div>5</div></div><div><span>    </span><span>// 配置CORS中间件</span></div></div><div><div><div>6</div></div><div><span>    </span><span>app</span><span>.</span><span>Engine</span><span>.</span><span>UseRouter</span><span>(</span><span>cors</span><span>.</span><span>New</span><span>().</span><span>Handler</span><span>())</span></div></div><div><div><div>7</div></div><div><span>    </span><span>return</span><span> </span><span>app</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>1.3.2 启动应用<a href="#132-启动应用"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>func</span><span><span> (</span><span>m </span><span>*</span></span><span>FlygooseApp</span><span>) </span><span>Start</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>m</span><span>.</span><span>InitDir</span><span>()       </span><span>// 初始化目录</span></div></div><div><div><div>3</div></div><div><span>    </span><span>m</span><span>.</span><span>initLog</span><span>()       </span><span>// 初始化日志</span></div></div><div><div><div>4</div></div><div><span>    </span><span>m</span><span>.</span><span>initDB</span><span>()        </span><span>// 初始化数据库</span></div></div><div><div><div>5</div></div><div><span>    </span><span>m</span><span>.</span><span>initRouter</span><span>()    </span><span>// 初始化路由</span></div></div><div><div><div>6</div></div><div><span>    </span><span>m</span><span>.</span><span>run</span><span>()           </span><span>// 启动服务</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>1.3.3 数据库初始化与自动迁移<a href="#133-数据库初始化与自动迁移"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>func</span><span><span> (</span><span>m </span><span>*</span></span><span>FlygooseApp</span><span>) </span><span>initDB</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>// 初始化数据库连接</span></div></div><div><div><div>3</div></div><div><span>    </span><span>if</span><span> </span><span>m</span><span>.</span><span>Cfg</span><span>.</span><span>Database</span><span>.</span><span>Driver</span><span><span> </span><span>==</span><span> </span></span><span>configs</span><span>.</span><span>DbDriverMySQL</span><span> {</span></div></div><div><div><div>4</div></div><div><span>        </span><span>datasource</span><span>.</span><span>InitMySql</span><span>(</span><span>cfg</span><span>)</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> </span><span>if</span><span> </span><span>m</span><span>.</span><span>Cfg</span><span>.</span><span>Database</span><span>.</span><span>Driver</span><span><span> </span><span>==</span><span> </span></span><span>configs</span><span>.</span><span>DbDriverPostgreSQL</span><span> {</span></div></div><div><div><div>6</div></div><div><span>        </span><span>datasource</span><span>.</span><span>InitPostgreSQL</span><span>(</span><span>cfg</span><span>)</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>8</div></div><div><span>        </span><span>panic</span><span>(</span><span>errors</span><span>.</span><span>New</span><span>(</span><span>"暂不支持其它数据库"</span><span>))</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>    </span><span>// 自动迁移数据模型</span></div></div><div><div><div>12</div></div><div><span>    </span><span>m</span><span>.</span><span>initAutoMigrate</span><span>()</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>    </span><span>// 初始化默认管理员账户</span></div></div><div><div><div>15</div></div><div><span>    </span><span>m</span><span>.</span><span>initDefaultAdmin</span><span>()</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>1.3.4 路由配置<a href="#134-路由配置"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>func</span><span><span> (</span><span>m </span><span>*</span></span><span>FlygooseApp</span><span>) </span><span>initRouter</span><span>() {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>v1</span><span><span> </span><span>:=</span><span> </span></span><span>m</span><span>.</span><span>Engine</span><span>.</span><span>Party</span><span>(</span><span>configs</span><span>.</span><span>Flygoose_Url_Prefix</span><span>)  </span><span>// API前缀</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>4</div></div><div><span>        </span><span>// 前端接口</span></div></div><div><div><div>5</div></div><div><span>        </span><span>mvc</span><span>.</span><span>New</span><span>(</span><span>v1</span><span>.</span><span>Party</span><span>(</span><span>"/site"</span><span>)).</span><span>Handle</span><span>(</span><span>flygoose</span><span>.</span><span>NewSiteController</span><span>())</span></div></div><div><div><div>6</div></div><div><span>        </span><span>mvc</span><span>.</span><span>New</span><span>(</span><span>v1</span><span>.</span><span>Party</span><span>(</span><span>"/blog"</span><span>)).</span><span>Handle</span><span>(</span><span>flygoose</span><span>.</span><span>NewBlogController</span><span>())</span></div></div><div><div><div>7</div></div><div><span>        </span><span>mvc</span><span>.</span><span>New</span><span>(</span><span>v1</span><span>.</span><span>Party</span><span>(</span><span>"/special"</span><span>)).</span><span>Handle</span><span>(</span><span>flygoose</span><span>.</span><span>NewSpecialController</span><span>())</span></div></div><div><div><div>8</div></div><div><span>        </span><span>mvc</span><span>.</span><span>New</span><span>(</span><span>v1</span><span>.</span><span>Party</span><span>(</span><span>"/ai"</span><span>)).</span><span>Handle</span><span>(</span><span>ai</span><span>.</span><span>NewDeepSeekController</span><span>()) </span><span>// AI相关接口（注：此为个人修改版本新增功能，详情请参考 https://github.com/AI-FIRE/）</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>        </span><span>// 管理端接口</span></div></div><div><div><div>11</div></div><div><span>        </span><span>mvc</span><span>.</span><span>New</span><span>(</span><span>v1</span><span>.</span><span>Party</span><span>(</span><span>"/health"</span><span>)).</span><span>Handle</span><span>(</span><span>admin</span><span>.</span><span>NewHealthController</span><span>())                 </span><span>//健康检查</span></div></div><div><div><div>12</div></div><div><span>        </span><span>mvc</span><span>.</span><span>New</span><span>(</span><span>v1</span><span>.</span><span>Party</span><span>(</span><span>"/admin/access"</span><span>)).</span><span>Handle</span><span>(</span><span>admin</span><span>.</span><span>NewAccessController</span><span>())           </span><span>//访问相关接口</span></div></div><div><div><div>13</div></div><div><span>        </span><span>mvc</span><span>.</span><span>New</span><span>(</span><span>v1</span><span>.</span><span>Party</span><span>(</span><span>"/admin/blog"</span><span>)).</span><span>Handle</span><span>(</span><span>admin</span><span>.</span><span>NewBlogController</span><span>())               </span><span>//博客相关接口</span></div></div><div><div><div>14</div></div><div><span>        </span><span>mvc</span><span>.</span><span>New</span><span>(</span><span>v1</span><span>.</span><span>Party</span><span>(</span><span>"/admin/link"</span><span>)).</span><span>Handle</span><span>(</span><span>admin</span><span>.</span><span>NewLinkController</span><span>())               </span><span>//友链相关接口</span></div></div><div><div><div>15</div></div><div><span>        </span><span>mvc</span><span>.</span><span>New</span><span>(</span><span>v1</span><span>.</span><span>Party</span><span>(</span><span>"/admin/site"</span><span>)).</span><span>Handle</span><span>(</span><span>admin</span><span>.</span><span>NewSiteController</span><span>())               </span><span>//网站信息相关接口</span></div></div><div><div><div>16</div></div><div><span>        </span><span>mvc</span><span>.</span><span>New</span><span>(</span><span>v1</span><span>.</span><span>Party</span><span>(</span><span>"/admin/category"</span><span>)).</span><span>Handle</span><span>(</span><span>admin</span><span>.</span><span>NewCategoryController</span><span>())       </span><span>//博客分类相关接口</span></div></div><div><div><div>17</div></div><div><span>        </span><span>mvc</span><span>.</span><span>New</span><span>(</span><span>v1</span><span>.</span><span>Party</span><span>(</span><span>"/admin/notice"</span><span>)).</span><span>Handle</span><span>(</span><span>admin</span><span>.</span><span>NewNoticeController</span><span>())           </span><span>//公告分类相关接口</span></div></div><div><div><div>18</div></div><div><span>        </span><span>mvc</span><span>.</span><span>New</span><span>(</span><span>v1</span><span>.</span><span>Party</span><span>(</span><span>"/admin/special"</span><span>)).</span><span>Handle</span><span>(</span><span>admin</span><span>.</span><span>NewSpecialController</span><span>())         </span><span>//专栏相关接口</span></div></div><div><div><div>19</div></div><div><span>        </span><span>mvc</span><span>.</span><span>New</span><span>(</span><span>v1</span><span>.</span><span>Party</span><span>(</span><span>"/admin/file"</span><span>)).</span><span>Handle</span><span>(</span><span>admin</span><span>.</span><span>NewFileController</span><span>())               </span><span>//文件相关接口</span></div></div><div><div><div>20</div></div><div><span>        </span><span>mvc</span><span>.</span><span>New</span><span>(</span><span>v1</span><span>.</span><span>Party</span><span>(</span><span>"/admin/banner"</span><span>)).</span><span>Handle</span><span>(</span><span>admin</span><span>.</span><span>NewBannerController</span><span>())           </span><span>//轮播图相关接口</span></div></div><div><div><div>21</div></div><div><span>        </span><span>mvc</span><span>.</span><span>New</span><span>(</span><span>v1</span><span>.</span><span>Party</span><span>(</span><span>"/admin/workStation"</span><span>)).</span><span>Handle</span><span>(</span><span>admin</span><span>.</span><span>NewWorkStationController</span><span>()) </span><span>//统计数据</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>23</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h3>1.4 数据模型设计<a href="#14-数据模型设计"><span>#</span></a></h3><p>系统的数据模型定义在<code>pkg/models/</code>目录下，主要包含以下模型：</p><ul>
<li><code>Admin</code>: 管理员模型</li>
<li><code>Blog</code>: 博客文章模型</li>
<li><code>Category</code>: 分类模型</li>
<li><code>Link</code>: 友链模型</li>
<li><code>Notice</code>: 公告模型</li>
<li><code>Section</code>: 页面区块模型</li>
<li><code>Site</code>: 网站信息模型</li>
<li><code>Special</code>: 专栏模型</li>
<li><code>Webmaster</code>: 站长模型</li>
<li><code>Banner</code>: 轮播图模型</li>
<li>AI相关模型（注：此为个人修改版本新增功能，详情请参考 <a href="https://github.com/AI-FIRE/%EF%BC%89" target="_blank">https://github.com/AI-FIRE/）</a></li>
</ul></section><section><h3>1.5 业务逻辑分层<a href="#15-业务逻辑分层"><span>#</span></a></h3><section><h4>1.5.1 控制器层 (Controllers)<a href="#151-控制器层-controllers"><span>#</span></a></h4><p>控制器层负责处理HTTP请求，接收参数，调用业务逻辑，并返回响应。控制器位于<code>web/controllers/</code>目录下，分为前端控制器和管理端控制器。</p><p><strong>示例：博客控制器</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 前端博客控制器</span></div></div><div><div><div>2</div></div><div><span>func</span><span> </span><span>NewBlogController</span><span><span>() </span><span>*</span></span><span>BlogController</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>return</span><span><span> </span><span>&amp;</span></span><span>BlogController</span><span>{</span></div></div><div><div><div>4</div></div><div><span>        </span><span>BlogService</span><span>: </span><span>services</span><span>.</span><span>NewBlogService</span><span>(),</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>// 管理端博客控制器</span></div></div><div><div><div>9</div></div><div><span>func</span><span> </span><span>NewBlogController</span><span><span>() </span><span>*</span></span><span>BlogController</span><span> {</span></div></div><div><div><div>10</div></div><div><span>    </span><span>return</span><span><span> </span><span>&amp;</span></span><span>BlogController</span><span>{</span></div></div><div><div><div>11</div></div><div><span>        </span><span>BlogService</span><span>: </span><span>services</span><span>.</span><span>NewBlogService</span><span>(),</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>1.5.2 业务逻辑层 (Services)<a href="#152-业务逻辑层-services"><span>#</span></a></h4><p>业务逻辑层负责处理核心业务逻辑，位于<code>web/services/</code>目录下。</p></section><section><h4>1.5.3 数据访问层 (DAOs)<a href="#153-数据访问层-daos"><span>#</span></a></h4><p>数据访问层负责与数据库交互，位于<code>web/daos/</code>目录下。</p></section></section><section><h3>1.6 AI功能集成<a href="#16-ai功能集成"><span>#</span></a></h3><p><strong>注：AI功能为个人修改版本新增功能，详情请参考 <a href="https://github.com/AI-FIRE/" target="_blank">https://github.com/AI-FIRE/</a></strong></p><p>系统集成了DeepSeek AI功能，相关代码位于以下位置：</p><ul>
<li><code>pkg/beans/ai/</code>: AI相关数据传输对象</li>
<li><code>pkg/models/ai/</code>: AI配置模型</li>
<li><code>web/controllers/ai/</code>: AI控制器</li>
<li><code>web/daos/ai/</code>: AI数据访问</li>
<li><code>web/services/ai/</code>: AI业务逻辑</li>
</ul><p><strong>AI接口示例：</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 直接返回型DeepSeek AI聊天接口</span></div></div><div><div><div>2</div></div><div><span>func</span><span><span> (</span><span>c </span><span>*</span></span><span>DeepSeekController</span><span>) </span><span>ChatFirstPageAi</span><span><span>(</span><span>ctx</span><span> </span></span><span>iris</span><span>.</span><span>Context</span><span>) {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>// 处理AI聊天请求</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>// 流式传输型DeepSeek AI聊天接口</span></div></div><div><div><div>7</div></div><div><span>func</span><span><span> (</span><span>c </span><span>*</span></span><span>DeepSeekController</span><span>) </span><span>StreamChatFirstPageAi</span><span><span>(</span><span>ctx</span><span> </span></span><span>iris</span><span>.</span><span>Context</span><span>) {</span></div></div><div><div><div>8</div></div><div><span>    </span><span>// 处理AI流式聊天请求</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>1.7 开发环境搭建<a href="#17-开发环境搭建"><span>#</span></a></h3><section><h4>1.7.1 依赖安装<a href="#171-依赖安装"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>go</span><span> </span><span>mod</span><span> </span><span>tidy</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>1.7.2 本地运行<a href="#172-本地运行"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>go</span><span> </span><span>run</span><span> </span><span>cmd</span><span>\f</span><span>lygoose</span><span>\m</span><span>ain.go</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>1.7.3 配置说明<a href="#173-配置说明"><span>#</span></a></h4><p>在<code>cmd/flygoose/flygoose-config.yaml</code>文件中配置数据库连接信息：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 服务端口</span></div></div><div><div><div>2</div></div><div><span>http</span><span>:</span></div></div><div><div><div>3</div></div><div><span>  </span><span>port</span><span>: </span><span>29090</span></div></div><div><div><div>4</div></div><div><span># 数据库配置</span></div></div><div><div><div>5</div></div><div><span>database</span><span>:</span></div></div><div><div><div>6</div></div><div><span>  </span><span>driver</span><span>: </span><span>mysql</span></div></div><div><div><div>7</div></div><div><span>  </span><span>host</span><span>: </span><span>127.0.0.1</span></div></div><div><div><div>8</div></div><div><span>  </span><span>port</span><span>: </span><span>3306</span></div></div><div><div><div>9</div></div><div><span>  </span><span>name</span><span>: </span><span>db_flygoose</span></div></div><div><div><div>10</div></div><div><span>  </span><span>user</span><span>: </span><span>root</span></div></div><div><div><div>11</div></div><div><span>  </span><span>password</span><span>: </span><span>root</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h3>1.8 编译与打包<a href="#18-编译与打包"><span>#</span></a></h3><section><h4>1.8.1 本地编译<a href="#181-本地编译"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># Windows平台编译</span></div></div><div><div><div>2</div></div><div><span>$env</span><span>:</span><span>GOOS</span><span>=</span><span>"windows"</span><span>; </span><span>$env</span><span>:GOARCH=</span><span>"amd64"</span><span>; </span><span>go</span><span> </span><span>build</span><span> </span><span>-o</span><span> </span><span>flygoose-api-2.0-win-amd64.exe</span><span> </span><span>./cmd/flygoose</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># Linux平台编译</span></div></div><div><div><div>5</div></div><div><span>$env</span><span>:</span><span>GOOS</span><span>=</span><span>"linux"</span><span>; </span><span>$env</span><span>:GOARCH=</span><span>"amd64"</span><span>; </span><span>go</span><span> </span><span>build</span><span> </span><span>-o</span><span> </span><span>flygoose-api-2.0-linux-amd64</span><span> </span><span>./cmd/flygoose</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># macOS平台编译 (Intel芯片)</span></div></div><div><div><div>8</div></div><div><span>$env</span><span>:</span><span>GOOS</span><span>=</span><span>"darwin"</span><span>; </span><span>$env</span><span>:GOARCH=</span><span>"amd64"</span><span>; </span><span>go</span><span> </span><span>build</span><span> </span><span>-o</span><span> </span><span>flygoose-api-2.0-darwin-amd64</span><span> </span><span>./cmd/flygoose</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># macOS平台编译 (Apple芯片)</span></div></div><div><div><div>11</div></div><div><span>$env</span><span>:</span><span>GOOS</span><span>=</span><span>"darwin"</span><span>; </span><span>$env</span><span>:GOARCH=</span><span>"arm64"</span><span>; </span><span>go</span><span> </span><span>build</span><span> </span><span>-o</span><span> </span><span>flygoose-api-2.0-darwin-arm64</span><span> </span><span>./cmd/flygoose</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>1.8.2 运行编译后的程序<a href="#182-运行编译后的程序"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># Linux平台</span></div></div><div><div><div>2</div></div><div><span>chmod</span><span> </span><span>+x</span><span> </span><span>flygoose-api-2.0-linux-amd64</span></div></div><div><div><div>3</div></div><div><span>./flygoose-api-2.0-linux-amd64</span><span> </span><span>-c</span><span> </span><span>flygoose-config.yaml</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># Windows平台</span></div></div><div><div><div>6</div></div><div><span>flygoose-api-2.0-win-amd64.exe</span><span> </span><span>-c</span><span> </span><span>flygoose-config.yaml</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h3>1.9 健康检查<a href="#19-健康检查"><span>#</span></a></h3><p>系统提供了健康检查接口，用于验证服务是否正常运行：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>curl</span><span> </span><span>http://localhost:29090/api/health</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>返回结果：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{</span><span>"code"</span><span>:</span><span>1</span><span>,</span><span>"data"</span><span>:</span><span>null</span><span>,</span><span>"message"</span><span>:</span><span>"success"</span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>二、管理端前端开发<a href="#二管理端前端开发"><span>#</span></a></h2><p>（管理端前端部分将在后续章节中详细介绍）</p></section><section><h2>三、二次开发指南<a href="#三二次开发指南"><span>#</span></a></h2><section><h3>3.1 添加新功能模块<a href="#31-添加新功能模块"><span>#</span></a></h3><section><h4>3.1.1 创建数据模型<a href="#311-创建数据模型"><span>#</span></a></h4><p>在<code>pkg/models/</code>目录下创建新的数据模型。</p></section><section><h4>3.1.2 创建数据传输对象<a href="#312-创建数据传输对象"><span>#</span></a></h4><p>在<code>pkg/beans/</code>目录下创建对应的数据传输对象。</p></section><section><h4>3.1.3 创建数据访问层<a href="#313-创建数据访问层"><span>#</span></a></h4><p>在<code>web/daos/</code>目录下创建数据访问层。</p></section><section><h4>3.1.4 创建业务逻辑层<a href="#314-创建业务逻辑层"><span>#</span></a></h4><p>在<code>web/services/</code>目录下创建业务逻辑层。</p></section><section><h4>3.1.5 创建控制器<a href="#315-创建控制器"><span>#</span></a></h4><p>在<code>web/controllers/</code>目录下创建控制器。</p></section><section><h4>3.1.6 注册路由<a href="#316-注册路由"><span>#</span></a></h4><p>在<code>inits/flygoose_init.go</code>文件中注册新的路由。</p></section></section><section><h3>3.2 AI功能扩展<a href="#32-ai功能扩展"><span>#</span></a></h3><p><strong>注：AI功能为个人修改版本新增功能，详情请参考 <a href="https://github.com/AI-FIRE/" target="_blank">https://github.com/AI-FIRE/</a></strong></p><p>系统支持扩展AI功能，可以在<code>web/controllers/ai/</code>、<code>web/daos/ai/</code>和<code>web/services/ai/</code>目录下添加新的AI接口和功能。</p></section><section><h3>3.3 配置扩展<a href="#33-配置扩展"><span>#</span></a></h3><p>在<code>configs/config.go</code>文件中添加新的配置项，并在<code>cmd/flygoose/flygoose-config.yaml</code>文件中配置对应的值。</p></section></section><section><h2>四、部署说明<a href="#四部署说明"><span>#</span></a></h2><section><h3>4.1 服务器部署<a href="#41-服务器部署"><span>#</span></a></h3><section><h4>4.1.1 准备工作<a href="#411-准备工作"><span>#</span></a></h4><ul>
<li>服务器或云主机（推荐配置：2核4G以上，操作系统：Ubuntu 20.04+）</li>
<li>MySQL 8.0+ 数据库</li>
<li>Nginx（可选，用于反向代理和HTTPS配置）</li>
</ul></section><section><h4>4.1.2 安装依赖<a href="#412-安装依赖"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 更新系统</span></div></div><div><div><div>2</div></div><div><span>apt-get</span><span> </span><span>update</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 安装依赖</span></div></div><div><div><div>5</div></div><div><span>apt-get</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>vim</span><span> </span><span>wget</span><span> </span><span>curl</span><span> </span><span>mysql-server</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>4.1.3 配置数据库<a href="#413-配置数据库"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 登录MySQL</span></div></div><div><div><div>2</div></div><div><span>mysql</span><span> </span><span>-u</span><span> </span><span>root</span><span> </span><span>-p</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 创建数据库</span></div></div><div><div><div>5</div></div><div><span>CREATE</span><span> </span><span>DATABASE</span><span> </span><span>db_flygoose</span><span> </span><span>DEFAULT</span><span> </span><span>CHARACTER</span><span> </span><span>SET</span><span> </span><span>utf8mb4</span><span> </span><span>COLLATE</span><span> </span><span>utf8mb4_unicode_ci</span><span>;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 创建用户并授权</span></div></div><div><div><div>8</div></div><div><span>CREATE</span><span> </span><span>USER</span><span> </span><span>'flygoose'@'%'</span><span> </span><span>IDENTIFIED</span><span> </span><span>BY</span><span> </span><span>'flygoose'</span><span>;</span></div></div><div><div><div>9</div></div><div><span>GRANT</span><span> </span><span>ALL</span><span> </span><span>PRIVILEGES</span><span> </span><span>ON</span><span> </span><span>db_flygoose.</span><span>*</span><span> </span><span>TO</span><span> </span><span>'flygoose'@'%'</span><span>;</span></div></div><div><div><div>10</div></div><div><span>FLUSH</span><span> </span><span>PRIVILEGES</span><span>;</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span># 退出</span></div></div><div><div><div>13</div></div><div><span>EXIT</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>4.1.4 部署应用<a href="#414-部署应用"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 创建应用目录</span></div></div><div><div><div>2</div></div><div><span>mkdir</span><span> </span><span>-p</span><span> </span><span>/opt/flygoose</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 上传编译好的程序和配置文件</span></div></div><div><div><div>5</div></div><div><span>cd</span><span> </span><span>/opt/flygoose</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 修改配置文件</span></div></div><div><div><div>8</div></div><div><span>vim</span><span> </span><span>flygoose-config.yaml</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 赋予执行权限</span></div></div><div><div><div>11</div></div><div><span>chmod</span><span> </span><span>+x</span><span> </span><span>flygoose-api-2.0-linux-amd64</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span># 后台运行</span></div></div><div><div><div>14</div></div><div><span>nohup</span><span> </span><span>./flygoose-api-2.0-linux-amd64</span><span> </span><span>-c</span><span> </span><span>flygoose-config.yaml</span><span> &amp;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>4.1.5 Nginx配置（可选）<a href="#415-nginx配置可选"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装Nginx</span></div></div><div><div><div>2</div></div><div><span>apt-get</span><span> </span><span>install</span><span> </span><span>-y</span><span> </span><span>nginx</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 配置Nginx</span></div></div><div><div><div>5</div></div><div><span>vim</span><span> </span><span>/etc/nginx/conf.d/flygoose.conf</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>Nginx配置示例：</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>server</span><span> {</span></div></div><div><div><div>2</div></div><div><span>   <span> </span></span><span>listen </span><span>80</span><span>;</span></div></div><div><div><div>3</div></div><div><span>   <span> </span></span><span>server_name </span><span>yourdomain.com;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>    </span><span>location</span><span> /api {</span></div></div><div><div><div>6</div></div><div><span>       <span> </span></span><span>proxy_pass </span><span>http://localhost:29090;</span></div></div><div><div><div>7</div></div><div><span>       <span> </span></span><span>proxy_set_header </span><span>Host $</span><span>host</span><span>;</span></div></div><div><div><div>8</div></div><div><span>       <span> </span></span><span>proxy_set_header </span><span>X-Real-IP $</span><span>remote_addr</span><span>;</span></div></div><div><div><div>9</div></div><div><span>       <span> </span></span><span>proxy_set_header </span><span>X-Forwarded-For $</span><span>proxy_add_x_forwarded_for</span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 重启Nginx</span></div></div><div><div><div>2</div></div><div><span>systemctl</span><span> </span><span>restart</span><span> </span><span>nginx</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h3>4.2 Docker部署（可选）<a href="#42-docker部署可选"><span>#</span></a></h3><p>项目包含Dockerfile，可以使用Docker进行部署。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 构建镜像</span></div></div><div><div><div>2</div></div><div><span>docker</span><span> </span><span>build</span><span> </span><span>-t</span><span> </span><span>flygoose-api</span><span> </span><span>.</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 运行容器</span></div></div><div><div><div>5</div></div><div><span>docker</span><span> </span><span>run</span><span> </span><span>-d</span><span> </span><span>-p</span><span> </span><span>29090:29090</span><span> </span><span>-v</span><span> </span><span>./flygoose-config.yaml:/app/flygoose-config.yaml</span><span> </span><span>--name</span><span> </span><span>flygoose-api</span><span> </span><span>flygoose-api</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>五、接口文档<a href="#五接口文档"><span>#</span></a></h2><p>详细的API接口文档请参考<code>docs/接口文档.md</code>文件。</p></section><section><h2>六、常见问题与解决方案<a href="#六常见问题与解决方案"><span>#</span></a></h2><section><h3>6.1 数据库连接失败<a href="#61-数据库连接失败"><span>#</span></a></h3><ul>
<li>检查数据库服务是否正常运行</li>
<li>检查数据库连接配置是否正确</li>
<li>检查数据库用户权限是否正确</li>
</ul></section><section><h3>6.2 服务启动失败<a href="#62-服务启动失败"><span>#</span></a></h3><ul>
<li>检查端口是否被占用</li>
<li>检查配置文件是否正确</li>
<li>检查日志文件获取详细错误信息</li>
</ul></section><section><h3>6.3 AI功能无法使用<a href="#63-ai功能无法使用"><span>#</span></a></h3><p><strong>注：AI功能为个人修改版本新增功能，详情请参考 <a href="https://github.com/AI-FIRE/" target="_blank">https://github.com/AI-FIRE/</a></strong></p><ul>
<li>检查AI配置是否正确</li>
<li>检查网络连接是否正常</li>
<li>检查AI服务是否可用</li>
</ul></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://blog.ai-nous.com/posts/%E9%A3%9E%E9%B9%85%E5%8D%9A%E5%AE%A2%E7%B3%BB%E7%BB%9F%E5%88%9D%E5%A7%8B%E5%8C%96%E5%8F%8A%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E4%B8%80%E5%8D%9A%E5%AE%A2%E5%89%8D%E7%AB%AF-copy/</id>
      <title type="text">飞鹅博客系统初始化及二次开发（一）博客前端</title>
      <published>2025-08-01T00:00:00.000Z</published>
      <updated>2025-08-01T00:00:00.000Z</updated>
      <author><name>Pankitgg</name></author>
      <link rel="alternate" href="https://blog.ai-nous.com/posts/%E9%A3%9E%E9%B9%85%E5%8D%9A%E5%AE%A2%E7%B3%BB%E7%BB%9F%E5%88%9D%E5%A7%8B%E5%8C%96%E5%8F%8A%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91%E4%B8%80%E5%8D%9A%E5%AE%A2%E5%89%8D%E7%AB%AF-copy/"/>
      <summary type="text">详细介绍飞鹅博客系统的安装、配置和二次开发流程</summary>
      <content type="html"><![CDATA[<section><h1>飞鹅博客初始化及二次开发指南（一）博客前端<a href="#飞鹅博客初始化及二次开发指南一博客前端"><span>#</span></a></h1><p>该网址即为此项目搭建 <a href="https://ai-nous.com/home" target="_blank">智心一梦</a></p><section><h2>1. 项目简介<a href="#1-项目简介"><span>#</span></a></h2><p>飞鹅博客是一个基于 Nuxt 3 + TypeScript 构建的现代化 AI 技术分享平台，集成了智能对话助手、技术博客、AI 资讯等核心功能。该项目采用前后端分离架构，前端使用 Nuxt 3 框架，支持服务端渲染和静态站点生成，为用户提供高性能的访问体验。</p><section><h3>1.1 技术栈<a href="#11-技术栈"><span>#</span></a></h3>

<table><thead><tr><th>技术/框架</th><th>版本</th><th>用途</th></tr></thead><tbody><tr><td>Nuxt</td><td>^3.4.3</td><td>前端框架</td></tr><tr><td>TypeScript</td><td>^4.7.2</td><td>类型检查</td></tr><tr><td>Vue</td><td>3.x</td><td>UI 框架</td></tr><tr><td>Pinia</td><td>2.0.14</td><td>状态管理</td></tr><tr><td>Element Plus</td><td>^2.3.4</td><td>UI 组件库</td></tr><tr><td>Ant Design Vue</td><td>^3.2.7</td><td>UI 组件库</td></tr><tr><td>Less</td><td>^4.1.3</td><td>样式预处理器</td></tr><tr><td>Axios</td><td>-</td><td>HTTP 客户端</td></tr></tbody></table></section><section><h3>1.2 核心功能<a href="#12-核心功能"><span>#</span></a></h3><ul>
<li><strong>智能对话助手</strong>：支持单次对话和流式对话两种模式</li>
<li><strong>技术博客</strong>：文章列表、详情展示、分类标签</li>
<li><strong>AI 资讯</strong>：最新 AI 动态展示和外部链接跳转</li>
<li><strong>用户系统</strong>：登录、注册、个人信息管理</li>
<li><strong>响应式设计</strong>：适配不同屏幕尺寸</li>
</ul></section></section><section><h2>2. 环境准备<a href="#2-环境准备"><span>#</span></a></h2><section><h3>2.1 基础环境<a href="#21-基础环境"><span>#</span></a></h3><blockquote><p>⚠️ <strong>注意事项</strong>：由于 Nuxt 当前版本存在 bug，启动项目时必须使用 <strong>Node.js 16.x</strong>，否则可能出现 <code>unused body</code> 报错。<br />
建议使用 nvm 或 fnm 快速切换 Node 版本：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>nvm</span><span> </span><span>use</span><span> </span><span>16</span></div></div><div><div><div>2</div></div><div><span># 或</span></div></div><div><div><div>3</div></div><div><span>fnm</span><span> </span><span>use</span><span> </span><span>16</span></div></div></code></pre><div><div></div><div></div></div></figure></div></blockquote><ul>
<li>
<p>Node.js &gt;= 14.0.0（<strong>推荐 16.x</strong>）</p>
</li>
<li>
<p>Yarn &gt;= 1.22.0 或 npm &gt;= 6.0.0</p>
</li>
<li>
<p>Git</p>
</li>
<li>
<p>Node.js &gt;= 14.0.0</p>
</li>
<li>
<p>Yarn &gt;= 1.22.0 或 npm &gt;= 6.0.0</p>
</li>
<li>
<p>Git</p>
</li>
</ul></section><section><h3>2.2 开发工具推荐<a href="#22-开发工具推荐"><span>#</span></a></h3><ul>
<li>Visual Studio Code</li>
<li>Vue DevTools</li>
<li>ESLint 插件</li>
<li>Prettier 插件</li>
</ul></section></section><section><h2>3. 项目初始化<a href="#3-项目初始化"><span>#</span></a></h2><section><h3>3.1 克隆项目<a href="#31-克隆项目"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>clone</span><span> </span><span>https://github.com/your-repo/flygoose-blog.git</span></div></div><div><div><div>2</div></div><div><span>cd</span><span> </span><span>flygoose-blog</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3.2 安装依赖<a href="#32-安装依赖"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>yarn</span><span> </span><span>install</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3.3 配置环境变量<a href="#33-配置环境变量"><span>#</span></a></h3><p>在项目根目录创建 <code>.env</code> 文件，配置基础 URL：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 开发环境</span></div></div><div><div><div>2</div></div><div><span>export</span><span> </span><span>BASE_URL</span><span>=</span><span>http</span><span>://</span><span>localhost</span><span>:</span><span>3000</span><span>/</span><span>api</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 生产环境</span></div></div><div><div><div>5</div></div><div><span>export</span><span> </span><span>BASE_URL</span><span>=</span><span>https</span><span>://</span><span>your-api-domain</span><span>.</span><span>com</span><span>/</span><span>api</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3.4 启动开发服务器<a href="#34-启动开发服务器"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>yarn</span><span> </span><span>dev</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>访问 <code>http://localhost:3000</code> 即可查看项目。</p></section></section><section><h2>4. 项目结构分析<a href="#4-项目结构分析"><span>#</span></a></h2><section><h3>4.1 目录结构<a href="#41-目录结构"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>├── api/                # API 请求封装</span></div></div><div><div><div>2</div></div><div><span>├── assets/             # 静态资源</span></div></div><div><div><div>3</div></div><div><span>│   ├── images/         # 图片资源</span></div></div><div><div><div>4</div></div><div><span>│   ├── less/           # 全局样式</span></div></div><div><div><div>5</div></div><div><span>│   └── movie/          # 视频资源</span></div></div><div><div><div>6</div></div><div><span>├── components/         # Vue 组件</span></div></div><div><div><div>7</div></div><div><span>│   ├── blog/           # 博客相关组件</span></div></div><div><div><div>8</div></div><div><span>│   ├── global/         # 全局通用组件</span></div></div><div><div><div>9</div></div><div><span>│   ├── index/          # 首页组件</span></div></div><div><div><div>10</div></div><div><span>│   ├── layout/         # 布局组件</span></div></div><div><div><div>11</div></div><div><span>│   └── special/        # 专栏相关组件</span></div></div><div><div><div>12</div></div><div><span>├── hooks/              # 自定义 hooks</span></div></div><div><div><div>13</div></div><div><span>├── layouts/            # 页面布局</span></div></div><div><div><div>14</div></div><div><span>├── middleware/         # 中间件</span></div></div><div><div><div>15</div></div><div><span>├── pages/              # 页面组件</span></div></div><div><div><div>16</div></div><div><span>│   ├── ai/             # AI 相关页面</span></div></div><div><div><div>17</div></div><div><span>│   ├── detail/         # 文章详情</span></div></div><div><div><div>18</div></div><div><span>│   └── specialColumn/  # 专栏页面</span></div></div><div><div><div>19</div></div><div><span>├── plugins/            # 插件</span></div></div><div><div><div>20</div></div><div><span>├── stores/             # Pinia 状态管理</span></div></div><div><div><div>21</div></div><div><span>├── types/              # TypeScript 类型定义</span></div></div><div><div><div>22</div></div><div><span>├── utils/              # 工具函数</span></div></div><div><div><div>23</div></div><div><span>└── nuxt.config.ts      # Nuxt 配置文件</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>4.2 核心文件说明<a href="#42-核心文件说明"><span>#</span></a></h3><ul>
<li><code>pages/index.vue</code>：网站首页，包含英雄区域、AI 对话、博客入口和资讯展示</li>
<li><code>pages/ai/chatOnce.vue</code>：单次对话组件，支持快捷问题和自定义问题</li>
<li><code>pages/ai/chatStream.vue</code>：流式对话组件，支持实时打字效果</li>
<li><code>components/layout/</code>：包含导航栏、侧边栏、页脚等布局组件</li>
<li><code>stores/user.ts</code>：用户信息状态管理</li>
<li><code>api/index.ts</code>：API 请求封装</li>
</ul></section></section><section><h2>5. 代码规范<a href="#5-代码规范"><span>#</span></a></h2><section><h3>5.1 命名规范<a href="#51-命名规范"><span>#</span></a></h3><ul>
<li><strong>文件夹</strong>：小写，多个单词用连字符，如 <code>ai-chat</code></li>
<li><strong>Vue 文件</strong>：除了 index.vue 全部使用大驼峰，如 <code>ArticleItem.vue</code></li>
<li><strong>TypeScript 文件</strong>：使用小驼峰，如 <code>useToken.ts</code></li>
<li><strong>Class 名</strong>：多个字母用连字符，如 <code>quick-questions</code></li>
<li><strong>事件命名</strong>：触发的 DOM 事件命名为 <code>handleXxxxClick</code>，监听事件为 <code>onXxxxChange</code></li>
</ul></section><section><h3>5.2 代码风格<a href="#52-代码风格"><span>#</span></a></h3><ul>
<li>使用 ESLint + Prettier 进行代码检查和格式化</li>
<li>使用 TypeScript 进行类型检查</li>
<li>Vue 组件使用 Composition API</li>
<li>样式使用 Less 预处理器</li>
</ul></section></section><section><h2>6. 二次开发指南<a href="#6-二次开发指南"><span>#</span></a></h2><section><h3>6.1 新增页面<a href="#61-新增页面"><span>#</span></a></h3><p>在 <code>pages</code> 目录下创建新的 Vue 文件，Nuxt 会自动生成路由：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"new-page"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>h1</span><span>&gt;新页面&lt;/</span><span>h1</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;</span><span>p</span><span>&gt;这是一个新添加的页面&lt;/</span><span>p</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>&lt;</span><span>script</span><span> </span><span>lang</span><span>=</span><span>"ts"</span><span> </span><span>setup</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span>// 页面逻辑</span></div></div><div><div><div>10</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>&lt;</span><span>style</span><span> </span><span>lang</span><span>=</span><span>"less"</span><span> </span><span>scoped</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span>.new-page</span><span> </span><span>{</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>padding: </span><span><span>20</span><span>px</span></span><span>;</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div><div><div><div>16</div></div><div><span>&lt;/</span><span>style</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>6.2 新增组件<a href="#62-新增组件"><span>#</span></a></h3><p>在 <code>components</code> 目录下创建新的 Vue 组件：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>template</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"new-component"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>slot</span><span>&gt;&lt;/</span><span>slot</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span>&lt;/</span><span>template</span><span>&gt;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>&lt;</span><span>script</span><span> </span><span>lang</span><span>=</span><span>"ts"</span><span> </span><span>setup</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span>// 组件逻辑</span></div></div><div><div><div>9</div></div><div><span>&lt;/</span><span>script</span><span>&gt;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>&lt;</span><span>style</span><span> </span><span>lang</span><span>=</span><span>"less"</span><span> </span><span>scoped</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span>.new-component</span><span> </span><span>{</span></div></div><div><div><div>13</div></div><div><span>  </span><span>/* 组件样式 */</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div><div><div><div>15</div></div><div><span>&lt;/</span><span>style</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>6.3 添加 API 请求<a href="#63-添加-api-请求"><span>#</span></a></h3><p>在 <code>api/index.ts</code> 中添加新的 API 请求：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>getNewData</span><span> </span><span>=</span><span> </span><span>async</span><span><span> (</span><span>params</span></span><span>:</span><span> </span><span>any</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>return</span><span> </span><span>await</span><span> </span><span>request</span><span>({</span></div></div><div><div><div>3</div></div><div><span>    </span><span>url</span><span>:</span><span> </span><span>'/api/new-data'</span><span>,</span></div></div><div><div><div>4</div></div><div><span>    </span><span>method</span><span>:</span><span> </span><span>'get'</span><span>,</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>params</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>})</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>6.4 状态管理<a href="#64-状态管理"><span>#</span></a></h3><p>在 <code>stores</code> 目录下创建新的状态管理文件：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>defineStore</span><span> } </span><span>from</span><span> </span><span>'pinia'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>const</span><span> </span><span>useNewStore</span><span> </span><span>=</span><span> </span><span>defineStore</span><span>(</span><span>'new'</span><span>, () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>const</span><span> </span><span>data</span><span> </span><span>=</span><span> </span><span>ref</span><span>([])</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>  </span><span>const</span><span> </span><span>fetchData</span><span> </span><span>=</span><span> </span><span>async</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>7</div></div><div><span>    </span><span>const</span><span> </span><span>res</span><span> </span><span>=</span><span> </span><span>await</span><span> </span><span>getNewData</span><span>()</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>data</span><span>.</span><span>value</span><span> </span><span>=</span><span><span> </span><span>res</span><span>.</span></span><span>data</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>  </span><span>return</span><span> {</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>data</span><span>,</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>fetchData</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>15</div></div><div><span>})</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>6.5 修改主题样式<a href="#65-修改主题样式"><span>#</span></a></h3><p>在 <code>assets/less/style.less</code> 中修改全局样式变量：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 主题色</span></div></div><div><div><div>2</div></div><div><span>@primary-color</span><span>: </span><span>#52c41a</span><span>;</span></div></div><div><div><div>3</div></div><div><span>@success-color</span><span>: </span><span>#52c41a</span><span>;</span></div></div><div><div><div>4</div></div><div><span>@warning-color</span><span>: </span><span>#faad14</span><span>;</span></div></div><div><div><div>5</div></div><div><span>@error-color</span><span>: </span><span>#f5222d</span><span>;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 文字色</span></div></div><div><div><div>8</div></div><div><span>@text-color</span><span>: </span><span>#333333</span><span>;</span></div></div><div><div><div>9</div></div><div><span>@text-color-secondary</span><span>: </span><span>#666666</span><span>;</span></div></div><div><div><div>10</div></div><div><span>@text-color-tertiary</span><span>: </span><span>#999999</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>7. 快捷问题按钮开发实例<a href="#7-快捷问题按钮开发实例"><span>#</span></a></h2><p>以 <code>chatOnce.vue</code> 中的快捷问题按钮为例，展示二次开发过程：</p><section><h3>7.1 添加按钮到模板<a href="#71-添加按钮到模板"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>"quick-questions"</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>&lt;button @click="handleFirstHandshake" class="quick-question-button first-handshake-button"&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>第一次握手</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>&lt;/button&gt;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>&lt;button v-for="(button, index) in quickButtons" :key="index" class="quick-question-button" @click="fillQuestion(button.message)"&gt;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>{{ button.buttonname }}</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>&lt;/button&gt;</span></div></div><div><div><div>8</div></div><div><span>&lt;/</span><span>div</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>7.2 实现按钮样式<a href="#72-实现按钮样式"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>.quick-questions</span><span> </span><span>{</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>display: </span><span>flex</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>gap: </span><span><span>12</span><span>px</span></span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>margin-top: </span><span><span>12</span><span>px</span></span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>flex-wrap: </span><span>wrap</span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>.quick-question-button</span><span> </span><span>{</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>padding: </span><span><span>6</span><span>px</span></span><span> </span><span><span>12</span><span>px</span></span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>border: </span><span><span>1</span><span>px</span></span><span> </span><span>solid</span><span> </span><span>#52c41a</span><span>;</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>border-radius: </span><span><span>16</span><span>px</span></span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>background: </span><span>#f6ffed</span><span>;</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>color: </span><span>#52c41a</span><span>;</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>font-size: </span><span><span>12</span><span>px</span></span><span>;</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>cursor: </span><span>pointer</span><span>;</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>transition: </span><span>all</span><span> </span><span><span>0.3</span><span>s</span></span><span>;</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>white-space: </span><span>nowrap</span><span>;</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>position: </span><span>relative</span><span>;</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>overflow: </span><span>hidden</span><span>;</span></div></div><div><div><div>20</div></div><div>
</div></div><div><div><div>21</div></div><div><span>  </span><span>&amp;.first-handshake-button</span><span> </span><span>{</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>border: </span><span><span>1</span><span>px</span></span><span> </span><span>solid</span><span> </span><span>#ff7a45</span><span>;</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>background: </span><span>#fff7e6</span><span>;</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>color: </span><span>#ff7a45</span><span>;</span></div></div><div><div><div>25</div></div><div>
</div></div><div><div><div>26</div></div><div><span>    </span><span>&amp;</span><span>:</span><span>hover</span><span> </span><span>{</span></div></div><div><div><div>27</div></div><div><span><span>      </span></span><span>background: </span><span>linear-gradient</span><span>(</span><span><span>135</span><span>deg</span></span><span>, </span><span>#ff7a45</span><span>, </span><span>#ffac38</span><span>);</span></div></div><div><div><div>28</div></div><div><span><span>      </span></span><span>border-color: </span><span>#ff7a45</span><span>;</span></div></div><div><div><div>29</div></div><div><span><span>      </span></span><span>color: </span><span>#fff</span><span>;</span></div></div><div><div><div>30</div></div><div><span><span>      </span></span><span>box-shadow: </span><span>0</span><span> </span><span><span>2</span><span>px</span></span><span> </span><span><span>8</span><span>px</span></span><span> </span><span>rgba</span><span>(</span><span>255</span><span>, </span><span>122</span><span>, </span><span>69</span><span>, </span><span>0.3</span><span>);</span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>32</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>33</div></div><div>
</div></div><div><div><div>34</div></div><div><span>  </span><span>&amp;</span><span>:</span><span>hover</span><span> </span><span>{</span></div></div><div><div><div>35</div></div><div><span><span>    </span></span><span>background: </span><span>linear-gradient</span><span>(</span><span><span>135</span><span>deg</span></span><span>, </span><span>#52c41a</span><span>, </span><span>#73d13d</span><span>);</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>color: </span><span>#fff</span><span>;</span></div></div><div><div><div>37</div></div><div><span><span>    </span></span><span>border-color: </span><span>#52c41a</span><span>;</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>transform: </span><span>translateY</span><span>(</span><span><span>-1</span><span>px</span></span><span>);</span></div></div><div><div><div>39</div></div><div><span><span>    </span></span><span>box-shadow: </span><span>0</span><span> </span><span><span>2</span><span>px</span></span><span> </span><span><span>8</span><span>px</span></span><span> </span><span>rgba</span><span>(</span><span>82</span><span>, </span><span>196</span><span>, </span><span>26</span><span>, </span><span>0.3</span><span>);</span></div></div><div><div><div>40</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>41</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>7.3 实现按钮功能<a href="#73-实现按钮功能"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 快捷问题按钮数据</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>quickButtons</span><span> </span><span>=</span><span> [</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>{ </span><span>buttonname</span><span>:</span><span> </span><span>'AI 是什么？'</span><span>, </span><span>message</span><span>:</span><span> </span><span>'人工智能（AI）是什么？它有哪些主要应用领域？'</span><span> },</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>{ </span><span>buttonname</span><span>:</span><span> </span><span>'机器学习入门'</span><span>, </span><span>message</span><span>:</span><span> </span><span>'如何入门机器学习？需要学习哪些基础知识？'</span><span> },</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>{ </span><span>buttonname</span><span>:</span><span> </span><span>'深度学习框架'</span><span>, </span><span>message</span><span>:</span><span> </span><span>'目前主流的深度学习框架有哪些？它们各自的优缺点是什么？'</span><span> }</span></div></div><div><div><div>6</div></div><div><span>]</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>// 填充问题到输入框</span></div></div><div><div><div>9</div></div><div><span>const</span><span> </span><span>fillQuestion</span><span> </span><span>=</span><span><span> (</span><span>message</span></span><span>:</span><span> </span><span>string</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>userInput</span><span>.</span><span>value</span><span> </span><span>=</span><span><span> </span><span>message</span></span></div></div><div><div><div>11</div></div><div><span>}</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// 处理第一次握手</span></div></div><div><div><div>14</div></div><div><span>const</span><span> </span><span>showHandshakeDialog</span><span> </span><span>=</span><span> </span><span>ref</span><span>(</span><span>false</span><span>)</span></div></div><div><div><div>15</div></div><div><span>const</span><span> </span><span>authorName</span><span> </span><span>=</span><span> </span><span>ref</span><span>(</span><span>''</span><span>)</span></div></div><div><div><div>16</div></div><div><span>const</span><span> </span><span>authorNameError</span><span> </span><span>=</span><span> </span><span>ref</span><span>(</span><span>''</span><span>)</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>const</span><span> </span><span>handleFirstHandshake</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>authorName</span><span>.</span><span>value</span><span> </span><span>=</span><span> </span><span>''</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>authorNameError</span><span>.</span><span>value</span><span> </span><span>=</span><span> </span><span>''</span></div></div><div><div><div>21</div></div><div><span><span>  </span></span><span>showHandshakeDialog</span><span>.</span><span>value</span><span> </span><span>=</span><span> </span><span>true</span></div></div><div><div><div>22</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section><section><h2>8. 部署流程<a href="#8-部署流程"><span>#</span></a></h2><section><h3>8.1 构建生产版本<a href="#81-构建生产版本"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span># 开发环境构建</span></div></div><div><div><div>2</div></div><div><span>yarn</span><span> </span><span>build:local</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 测试环境构建</span></div></div><div><div><div>5</div></div><div><span>yarn</span><span> </span><span>build_test</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># 生产环境构建</span></div></div><div><div><div>8</div></div><div><span>yarn</span><span> </span><span>build</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>构建成功后，会生成 <code>.output</code> 目录。</p></section><section><h3>8.2 使用 PM2 部署<a href="#82-使用-pm2-部署"><span>#</span></a></h3><p>在项目根目录执行：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>pm</span><span> </span><span>install</span><span> </span><span>pm2</span><span> </span><span>-g</span></div></div><div><div><div>2</div></div><div><span>pm2</span><span> </span><span>start</span><span> </span><span>pm2.config.js</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>8.3 配置 Nginx<a href="#83-配置-nginx"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>server</span><span> {</span></div></div><div><div><div>2</div></div><div><span> <span> </span></span><span>listen </span><span>80</span><span>;</span></div></div><div><div><div>3</div></div><div><span> <span> </span></span><span>server_name </span><span>your-domain.com;</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>  </span><span>location</span><span> / {</span></div></div><div><div><div>6</div></div><div><span>   <span> </span></span><span>proxy_pass </span><span>http://localhost:58081;</span></div></div><div><div><div>7</div></div><div><span>   <span> </span></span><span>proxy_http_version </span><span>1.1</span><span>;</span></div></div><div><div><div>8</div></div><div><span>   <span> </span></span><span>proxy_set_header </span><span>Upgrade $</span><span>http_upgrade</span><span>;</span></div></div><div><div><div>9</div></div><div><span>   <span> </span></span><span>proxy_set_header </span><span>Connection </span><span>'upgrade'</span><span>;</span></div></div><div><div><div>10</div></div><div><span>   <span> </span></span><span>proxy_set_header </span><span>Host $</span><span>host</span><span>;</span></div></div><div><div><div>11</div></div><div><span>   <span> </span></span><span>proxy_cache_bypass </span><span>$</span><span>http_upgrade</span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>9. 常见问题与解决方案<a href="#9-常见问题与解决方案"><span>#</span></a></h2><section><h3>9.1 API 请求失败<a href="#91-api-请求失败"><span>#</span></a></h3><ul>
<li>检查 BASE_URL 配置是否正确</li>
<li>确保后端服务正常运行</li>
<li>检查网络连接</li>
</ul></section><section><h3>9.2 样式不生效<a href="#92-样式不生效"><span>#</span></a></h3><ul>
<li>检查 Less 依赖是否安装</li>
<li>确保样式文件路径正确</li>
<li>检查 CSS 选择器是否正确</li>
</ul></section><section><h3>9.3 页面路由 404<a href="#93-页面路由-404"><span>#</span></a></h3><ul>
<li>检查页面文件命名和路径是否符合 Nuxt 路由规则</li>
<li>确保布局文件存在</li>
</ul></section></section><section><h2>10. 项目维护<a href="#10-项目维护"><span>#</span></a></h2><section><h3>10.1 代码更新<a href="#101-代码更新"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>pull</span></div></div><div><div><div>2</div></div><div><span>yarn</span><span> </span><span>install</span></div></div><div><div><div>3</div></div><div><span>yarn</span><span> </span><span>build</span></div></div><div><div><div>4</div></div><div><span>yarn</span><span> </span><span>start</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>10.2 依赖更新<a href="#102-依赖更新"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>yarn</span><span> </span><span>upgrade-interactive</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>10.3 日志查看<a href="#103-日志查看"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>pm2</span><span> </span><span>logs</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h2>11. 参考文档<a href="#11-参考文档"><span>#</span></a></h2><ol>
<li><a href="https://nuxt.com/docs/getting-started/introduction" target="_blank">Nuxt 3 官方文档</a></li>
<li><a href="https://vuejs.org/guide/introduction.html" target="_blank">Vue 3 官方文档</a></li>
<li><a href="https://pinia.vuejs.org/introduction.html" target="_blank">Pinia 官方文档</a></li>
<li><a href="https://element-plus.org/en-US/" target="_blank">Element Plus 官方文档</a></li>
<li><a href="https://www.antdv.com/docs/vue/introduce/" target="_blank">Ant Design Vue 官方文档</a></li>
<li><a href="https://www.typescriptlang.org/docs/" target="_blank">TypeScript 官方文档</a></li>
<li><a href="https://lesscss.org/" target="_blank">Less 官方文档</a></li>
<li><a href="http://blog.ai-nous.com/">飞鹅博客示例网站</a></li>
</ol></section></section>]]></content>
    </entry>
</feed>
