<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/scripts/pretty-feed-v3.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:h="http://www.w3.org/TR/html4/"><channel><title>BemoDB 2.0</title><description>Hello World!</description><link>https://astro-pure.js.org</link><item><title>读《置身钉内》：发心、常数，和那些被错判为&quot;执行问题&quot;的结构性失败</title><link>https://astro-pure.js.org/blog/2026/think/%E7%BD%AE%E8%BA%AB%E9%92%89%E5%86%85</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2026/think/%E7%BD%AE%E8%BA%AB%E9%92%89%E5%86%85</guid><description>发心不是情怀词，它是在任何决策节点上替你做优先级排序的东西。AI产品开发中反复出现的不是成功公式，而是常数</description><pubDate>Fri, 05 Jun 2026 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;有一类文章的价值不在于&quot;写了什么&quot;，而在于它让你重新审视那些你已经习以为常的判断。钉钉匿名PM的离职记录《置身钉内》属于这一类。&lt;/p&gt;
&lt;p&gt;7.5万字，不提供任何可复制的成功原则。它记录的是一组反复出现的结构性张力——无论团队怎么换、无论模型能力怎么提升，这些东西都在那里。作者称之为&quot;常数&quot;。&lt;/p&gt;
&lt;p&gt;以下是我从这些常数中真正被击中的几个点，以及它们延伸出去的思考。&lt;/p&gt;
&lt;h2&gt;发心不是情怀。发心是优先级的底层算法。&lt;/h2&gt;
&lt;p&gt;&quot;发心&quot;这个词在中文语境里已经被用烂了。它听起来像某种鸡汤——&quot;做产品的初心&quot;、&quot;不忘初衷&quot;。但《置身钉内》把它拉回到了一个完全不同的层面。&lt;/p&gt;
&lt;p&gt;ONE的发心至少有四层：替用户减负、替钉钉做AI时代新入口、替组织聚人心、替商业消化token消耗。四层同时成立，互相冲突。作者的原话是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;当一个产品的发心又多又没有主次的时候，就会成为一个贪心而焦虑的产品。&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这句话的锋利之处在于，它揭示了一个几乎所有团队都在犯、但极少有人能说清的错误：&lt;strong&gt;不是没有目标，而是目标太多，并且拒绝为它们排序。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;把发心理解为&quot;优先级排序的底层算法&quot;之后，它就不再是一个只属于PM的概念。任何一个工程师在做技术决策的时候——选什么架构、抽象到什么程度、为哪些场景做容错——背后都隐含着某种发心排序。你以为你在做技术决策，实际上你在替产品回答&quot;这件事到底为谁而存在&quot;。&lt;/p&gt;
&lt;p&gt;我之前写过&quot;验证性功能不值得深度抽象，标杆性功能需要极致还原度，基础设施性功能才值得在架构上投入&quot;。这三句话的本质就是在说：一个功能被创造出来的原因，直接决定了它应该被怎样实现。但大多数团队不会在动手之前问这个问题。他们直接跳到&quot;怎么实现&quot;，然后在一个月后发现方向错了。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;分辨一个功能真正为什么而存在，是比&quot;这个功能怎么做&quot;更基础的问题。而这个问题在大多数技术评审中根本不会出现。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这让我联想到一个更广的视角：大公司做AI产品为什么总是看起来&quot;什么都有但什么都不好用&quot;？不是因为能力不够，而是因为发心太多。一个功能要同时满足用户价值、组织KPI、发布会演示、商业变现——四个目标本质上就不是同一个方向，但没有人敢说&quot;我们这次只做其中一件&quot;。结果是每个方向都做了60分，合在一起就是一个60分的产品。&lt;/p&gt;
&lt;p&gt;这也是为什么独立开发者和初创团队在某些产品类型上能碾压大厂——他们的发心通常只有一层，不需要内耗。&lt;/p&gt;
&lt;h2&gt;已读功能不是UX问题。它是权力问题。&lt;/h2&gt;
&lt;p&gt;《置身钉内》里关于&quot;已读&quot;的讨论是全文最精准的一段。&lt;/p&gt;
&lt;p&gt;钉钉的传统站发信人（管理者）那边：我发的消息，我有权知道你看没看。ONE试图站收信人（普通员工）那边：我帮你过滤信息，让重要的浮上来。&lt;/p&gt;
&lt;p&gt;这看起来是一个产品定位问题。但它背后有一个更根本的命题：&lt;strong&gt;每一个涉及信息分发的产品，都在用一个看似不起眼的UI决策替用户回答&quot;权力在谁手里&quot;。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;推还是拉？按发送者的优先级排序还是按接收者的优先级排序？通知的触发条件由谁定义？这些不是设计偏好，它们是权力结构在界面层上的投射。一个按钮的位置就是一份政治声明。&lt;/p&gt;
&lt;p&gt;再往下推一层。为什么微信没有&quot;已读&quot;？不是因为技术做不到，而是因为微信的产品立场站在接收者这边——我给你不回复的自由。但企业微信有已读——因为你进了组织，就不再拥有这种自由。同一家公司，两个产品，同一功能的处理方式截然相反。这不是巧合。&lt;/p&gt;
&lt;p&gt;这个视角放到AI产品上会更有意思。当AI开始在组织内部接管信息分发——自动总结、自动过滤、自动优先级排序——它实际上在替组织重新分配&quot;谁看什么、谁不看什么&quot;的权力。AI代理的&quot;智能排序&quot;看起来是一个技术特性，但它其实在替产品回答那个老问题：&lt;strong&gt;你站在谁那边？&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;常数不是原则。常数的意思是，你解决不了它。&lt;/h2&gt;
&lt;p&gt;《置身钉内》最让我觉得有价值的概念，是&quot;常数&quot;——那些无论团队多强、无论模型多好，都会反复出现的结构性张力。&lt;/p&gt;
&lt;p&gt;模型能力快速提升，但产品形态进化速度远慢于模型——这是一个常数。AI要进入工作现场就需要上下文，但这些上下文所在的旧系统本身就是最大的摩擦力——这是另一个常数。组织里的人来了又走，信息的完整因果链只有留到最后的人能看到——这又是一个常数。&lt;/p&gt;
&lt;p&gt;&quot;原则&quot;和&quot;常数&quot;的区别在于：原则告诉你可以做什么来改善局面，常数告诉你某些东西你永远无法消除，只能设计自己的系统去容纳它。&lt;/p&gt;
&lt;p&gt;这让我想到一个几乎所有人都犯过的错误：把一个常数当成一个可以解决的问题去处理。需求在组织传递过程中必然变形——不是因为沟通技巧不够，而是因为每个接收端都自带解码器，把输入信号按照自己的KPI和认知框架重新解释。这不是执行问题，这是信息论层面的必然。一个人不可能完整地把自己的全部上下文传递给另一个人，组织更不能。&lt;/p&gt;
&lt;p&gt;但大多数团队的做法是什么？开一个更详细的PRD评审会，写一份更长的需求文档。以为&quot;更充分的信息传递&quot;能解决信息必然失真的问题——这本身就是一个范畴错误。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;常数不是要被解决的。常数是要被承认的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;承认了需求会变形，你的策略就不再是&quot;更准确地传递需求&quot;，而是缩短需求传递的链路、减少中转节点、让做决策的人直接接触原始信息。承认了旧系统会拖慢AI落地，你的策略就不再是&quot;把旧系统全面改造&quot;，而是在旧系统的缝隙里找到AI可以先独立运作的空间。&lt;/p&gt;
&lt;p&gt;这些策略变化的起点，仅仅是把一个东西从&quot;待解决问题&quot;一栏移到了&quot;给定条件&quot;一栏。&lt;/p&gt;
&lt;h2&gt;最后一句话&lt;/h2&gt;
&lt;p&gt;《置身钉内》的结尾，作者用茨威格自传的书名&quot;昨日的世界&quot;来形容这段经历。那个书名记录的是一战前已经不存在的欧洲。一个被寄予厚望、快速崛起、又迅速收缩的产品，配得上这个比喻。&lt;/p&gt;
&lt;p&gt;但这篇文章真正重要的不是它的伤感。重要的是它提供了一种罕见的视角：&lt;strong&gt;站在废墟上看因果。&lt;/strong&gt; 大多数产品复盘是在成功之后写的，隐去了运气，隐去了恰好做对但不知道为什么对的东西。失败记录保留了更多犹豫、代价、&quot;当时觉得有道理但后来发现不对&quot;的判断。这些东西才是经验本来的样子。&lt;/p&gt;
&lt;p&gt;对于正在做AI产品的人，这篇文章的价值不在于提供了答案。它的价值在于提供了一组经过验证的问题——&lt;/p&gt;
&lt;p&gt;你的功能到底为什么而存在？你的产品站在谁那边？你面对的东西，是一个可以被解决的问题，还是一个必须被承认的常数？&lt;/p&gt;
&lt;p&gt;分清这三件事，大概已经解决了一半的问题。&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>agentGo*003 ｜ Agent 八荣八耻</title><link>https://astro-pure.js.org/blog/2026/agentgo/003</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2026/agentgo/003</guid><description>写给 Agent 开发和 AI 辅助编程的一组工程纪律</description><pubDate>Wed, 27 May 2026 20:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Agent 八荣八耻&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;以暗猜接口为耻，以认真查阅为荣。&lt;/li&gt;
&lt;li&gt;以模糊执行为耻，以寻求确认为荣。&lt;/li&gt;
&lt;li&gt;以盲想业务为耻，以人类确认为荣。&lt;/li&gt;
&lt;li&gt;以创造接口为耻，以复用现有为荣。&lt;/li&gt;
&lt;li&gt;以跳过验证为耻，以主动测试为荣。&lt;/li&gt;
&lt;li&gt;以破坏架构为耻，以遵循规范为荣。&lt;/li&gt;
&lt;li&gt;以假装理解为耻，以诚实无知为荣。&lt;/li&gt;
&lt;li&gt;以盲目修改为耻，以谨慎重构为荣。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Agent Eight Honors and Eight Shames&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Shame in guessing APIs, Honor in careful research.&lt;/li&gt;
&lt;li&gt;Shame in vague execution, Honor in seeking confirmation.&lt;/li&gt;
&lt;li&gt;Shame in assuming business logic, Honor in human verification.&lt;/li&gt;
&lt;li&gt;Shame in creating interfaces, Honor in reusing existing ones.&lt;/li&gt;
&lt;li&gt;Shame in skipping validation, Honor in proactive testing.&lt;/li&gt;
&lt;li&gt;Shame in breaking architecture, Honor in following specifications.&lt;/li&gt;
&lt;li&gt;Shame in pretending to understand, Honor in honest ignorance.&lt;/li&gt;
&lt;li&gt;Shame in blind modification, Honor in careful refactoring.&lt;/li&gt;
&lt;/ol&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>当 AI 让方案看起来合理</title><link>https://astro-pure.js.org/blog/2026/think/ai-dev-workflow-risk-review</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2026/think/ai-dev-workflow-risk-review</guid><description>一次关于 AI 辅助开发、后端陌生领域和风险优先工作流的反思</description><pubDate>Mon, 25 May 2026 18:15:00 GMT</pubDate><content:encoded>&lt;p&gt;最近做项目时遇到一个很明显的问题：接触到的新需求越来越接近全栈，但自己对服务端和后端工程的实战经验并不充分。于是很多时候会依赖 AI 来拟定方案、拆任务、写代码，再由我来阅读和判断。&lt;/p&gt;
&lt;p&gt;这里真正危险的地方不在于“用了 AI”，而在于读完方案之后经常会得到一种感知：这个方案是合理的。&lt;/p&gt;
&lt;p&gt;这种感知未必可靠。一个方案看起来合理，可能只是因为它的表达完整、结构顺畅、术语正确，并不代表它真的经得起真实系统里的并发、权限、数据一致性、失败重试和线上排障。对于自己并不熟悉的领域，所谓“合理”很可能只是一种叙事上的顺滑。&lt;/p&gt;
&lt;p&gt;换句话说，AI 可以很快给出一个工程方案，但它不会自动把判断力也交给我。&lt;/p&gt;
&lt;h2&gt;“合理感”为什么会变成风险&lt;/h2&gt;
&lt;p&gt;前端场景里，很多问题可以通过页面表现、交互路径和本地状态快速暴露。样式错了、状态没更新、接口返回没处理，大多能在较短反馈链路里看到。&lt;/p&gt;
&lt;p&gt;后端不是这样。&lt;/p&gt;
&lt;p&gt;后端问题经常不以“马上坏掉”的形式出现，而是以更隐蔽的方式积累：数据被重复写入，权限边界没有兜住，状态流转缺了一条分支，失败重试制造了副作用，某个接口在小流量下正常但在并发下出现竞争条件。更麻烦的是，很多问题只有在上线后、数据进入系统后、多人协作之后才会真正暴露。&lt;/p&gt;
&lt;p&gt;这也是我开始重新看待 AI 辅助开发的原因。AI 最大的优势是把从想法到产物的距离压短，但这也会压短“怀疑”和“验证”的时间。代码出现得越快，越容易让人误以为问题已经被解决。&lt;/p&gt;
&lt;p&gt;如果缺少领域经验，AI 生成的方案就会变成一种抽奖：有时它确实很好，有时只是看起来完整。&lt;/p&gt;
&lt;h2&gt;structured-ai-dev-workflow 解决了什么&lt;/h2&gt;
&lt;p&gt;之前我写过一个开发 skill：&lt;code&gt;structured-ai-dev-workflow&lt;/code&gt;。它的核心思路是把 AI 开发过程拆成一套可追踪的闭环：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;先沉淀需求&lt;/li&gt;
&lt;li&gt;再 research 现有实现&lt;/li&gt;
&lt;li&gt;然后写 plan&lt;/li&gt;
&lt;li&gt;再拆 phase task&lt;/li&gt;
&lt;li&gt;实现后写 worklog&lt;/li&gt;
&lt;li&gt;每个阶段尽量可验证、可提交、可接续&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个结构在中大型需求里很有价值。它解决的是 AI 开发中很常见的几个问题：上下文丢失、范围失控、边做边忘、后续无法接手、提交粒度混乱。尤其当工期比较长、需求可以拆阶段推进时，它能建立一个相对健康的优化循环。&lt;/p&gt;
&lt;p&gt;但现在回头看，它也有一个明显前提：research 和 plan 的质量是可信的。&lt;/p&gt;
&lt;p&gt;如果研究和计划本身就存在盲区，那么后面的 task、implement、worklog 只能把这个盲区结构化地传递下去。流程越完整，甚至越容易让人产生“这件事已经被认真处理过”的错觉。&lt;/p&gt;
&lt;p&gt;也就是说，&lt;code&gt;structured-ai-dev-workflow&lt;/code&gt; 更像是过程管理工具，而不是工程风险审查工具。它能让开发过程更可控，但不必然让方案更可靠。&lt;/p&gt;
&lt;h2&gt;这个 workflow 在短工期下的问题&lt;/h2&gt;
&lt;p&gt;完整的结构化开发流程，比较适合下面几种情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需求足够大，值得单独拆阶段&lt;/li&gt;
&lt;li&gt;工期允许先 research、再 plan、再实现&lt;/li&gt;
&lt;li&gt;现有系统有足够资料可查&lt;/li&gt;
&lt;li&gt;开发者对领域有基本判断力&lt;/li&gt;
&lt;li&gt;每个 phase 都能独立验证&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但真实项目经常不是这样。很多需求的状态更接近：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;工期短&lt;/li&gt;
&lt;li&gt;业务催得急&lt;/li&gt;
&lt;li&gt;领域不熟&lt;/li&gt;
&lt;li&gt;代码已有历史包袱&lt;/li&gt;
&lt;li&gt;测试覆盖不足&lt;/li&gt;
&lt;li&gt;只能先交付一个可用版本&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在这种情况下，如果仍然照搬完整流程，会出现两种问题。&lt;/p&gt;
&lt;p&gt;一种是流程太重。文档写完，时间已经被消耗掉，真正用于实现和验证的空间变小。&lt;/p&gt;
&lt;p&gt;另一种是流程被压缩得太轻。research 和 plan 变成几段概括，风险没有真正展开，最后只是把“直接写代码”包装成“按流程写代码”。&lt;/p&gt;
&lt;p&gt;这不是流程本身的问题，而是模式选择的问题。在短工期和陌生领域里，真正需要优先建立的不是完整闭环，而是风险优先的交付机制。&lt;/p&gt;
&lt;h2&gt;grill-me 的价值和边界&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;grill-me&lt;/code&gt; 这类工具有价值。它通过持续追问，把需求、决策、边界和依赖关系问清楚。它适合发生在编码之前，帮助人和 AI 一起澄清“到底要做什么”。&lt;/p&gt;
&lt;p&gt;但它不能直接替代代码 review。&lt;/p&gt;
&lt;p&gt;需求被问清楚，只能说明目标更明确；并不说明实现结构可靠，也不说明数据模型、权限边界、错误处理和并发行为都没有问题。尤其在后端场景里，很多关键问题并不是“需求有没有说清楚”，而是“实现是否在坏情况里仍然可控”。&lt;/p&gt;
&lt;p&gt;因此，&lt;code&gt;grill-me&lt;/code&gt; 的思想可以迁移，但角色需要改变。&lt;/p&gt;
&lt;p&gt;编码前的 grill-me 问的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户真正想要什么？&lt;/li&gt;
&lt;li&gt;哪些场景需要覆盖？&lt;/li&gt;
&lt;li&gt;哪些边界需要确认？&lt;/li&gt;
&lt;li&gt;哪些决策会影响后续实现？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;编码后的 review grill 应该问的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这个实现在哪些情况下会写错数据？&lt;/li&gt;
&lt;li&gt;哪些权限判断只存在于前端或调用方假设里？&lt;/li&gt;
&lt;li&gt;重复请求会不会产生重复记录？&lt;/li&gt;
&lt;li&gt;多步写入失败后会不会留下半成品？&lt;/li&gt;
&lt;li&gt;错误日志是否足够定位线上问题？&lt;/li&gt;
&lt;li&gt;数据库约束是否兜住了业务不变量？&lt;/li&gt;
&lt;li&gt;这个抽象是在降低复杂度，还是在隐藏复杂度？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;前者帮助进入编码，后者帮助判断能不能交付。两者解决的是不同问题。&lt;/p&gt;
&lt;h2&gt;需要补上的不是更多文档，而是 review gate&lt;/h2&gt;
&lt;p&gt;现在更应该补在 workflow 里的，不是再多一层计划文档，而是一个明确的风险审查关口。&lt;/p&gt;
&lt;p&gt;这个关口可以放在 plan 之后，也可以放在实现之后。对后端或全栈需求来说，它应该至少覆盖几类问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据一致性：多步写入是否需要事务，失败后是否可恢复&lt;/li&gt;
&lt;li&gt;权限边界：用户是否只能访问自己应该访问的资源&lt;/li&gt;
&lt;li&gt;幂等和重复提交：刷新、重试、重复点击是否会制造副作用&lt;/li&gt;
&lt;li&gt;输入校验：不可信输入是否在服务端被重新检查&lt;/li&gt;
&lt;li&gt;状态流转：状态枚举是否完整，非法状态是否被阻止&lt;/li&gt;
&lt;li&gt;可观测性：出问题后能否通过日志定位到请求、用户和数据&lt;/li&gt;
&lt;li&gt;回滚和补偿：上线后如果出错，是否知道如何修复已有数据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些问题不一定都需要复杂实现，但必须被显式讨论。短工期下可以接受一些风险，但不能在不知道风险是什么的情况下接受风险。&lt;/p&gt;
&lt;p&gt;这也是我现在对 AI 开发流程的一个修正：AI 不应该只负责生成方案，还应该被强制放到反方位置上，持续攻击自己的方案。&lt;/p&gt;
&lt;h2&gt;skill review 和多轮 review&lt;/h2&gt;
&lt;p&gt;这里还有一个值得补充的问题：即使使用各种 skill 去 review，实际效果也不一定稳定地好过“多重复几轮让 AI 去 review”。&lt;/p&gt;
&lt;p&gt;这听起来有点反直觉。skill 的价值在于结构化，它能规定审查维度，提醒 AI 不要漏掉权限、数据一致性、幂等、日志、回滚这些关键问题。但它并不保证每一次审查都有足够高的召回率。AI 仍然可能沿着某一条思路检查得很细，却漏掉另一类问题；也可能因为第一次 review 的结论太顺，后续就不再主动怀疑。&lt;/p&gt;
&lt;p&gt;多轮 review 的价值恰好在这里。它不是依赖某一个“完美 reviewer”，而是通过重复采样，让模型从不同角度重新进入问题。第一轮可能看到权限问题，第二轮可能看到事务问题，第三轮可能开始怀疑状态机，第四轮才注意到日志和回滚。很多时候，问题不是靠一次很完整的模板发现的，而是靠多次不完全相同的审查逐渐浮出来的。&lt;/p&gt;
&lt;p&gt;所以 skill review 和多轮 review 不是同一种东西。&lt;/p&gt;
&lt;p&gt;skill 更像审查框架，负责定义“应该看哪些方向”；多轮 review 更像置信度策略，负责提高“真的看到问题”的概率。前者解决覆盖面，后者解决召回率。&lt;/p&gt;
&lt;p&gt;更合理的做法可能是把两者叠在一起：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;先用 skill 固定审查维度，避免每次都凭感觉问&lt;/li&gt;
&lt;li&gt;再做多轮独立 review，让 AI 每轮只关注一类风险&lt;/li&gt;
&lt;li&gt;每轮要求输出“高风险问题、低风险问题、未能判断的问题”&lt;/li&gt;
&lt;li&gt;最后再做一次合并，把重复问题、真实问题和误报区分开&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这也意味着，workflow 不应该迷信“有一个 review skill 就够了”。skill 可以让审查更有章法，但多轮审查仍然有必要。尤其在自己不熟悉的领域里，一次 review 通过并不等于风险已经消失，它只能说明这一轮没有发现足够明确的问题。&lt;/p&gt;
&lt;h2&gt;人工 review 是最后闸门&lt;/h2&gt;
&lt;p&gt;还有一个更基础的原则：用 AI 一定要人工 review，尤其是在它拥有较高权限、能够自动读写文件、执行命令、提交代码、调用接口的时候。&lt;/p&gt;
&lt;p&gt;高权限和自动化本身不是问题，它们也是 agent 能真正完成任务的原因。但这两者叠在一起，会把错误的放大速度变得很快。一个没有人工闸门的 agent，可能不是帮手，而是自动闯祸机：它可以很快改错文件、删错逻辑、提交错误代码，甚至把一个没有被理解的方案推到远端。&lt;/p&gt;
&lt;p&gt;人工 review 的意义不只是“再看一眼代码”。它至少要确认几件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这次改动是否真的对应当前需求&lt;/li&gt;
&lt;li&gt;AI 有没有顺手改掉不该改的地方&lt;/li&gt;
&lt;li&gt;关键业务规则是否被正确表达&lt;/li&gt;
&lt;li&gt;权限、数据、状态和错误处理是否有明显漏洞&lt;/li&gt;
&lt;li&gt;验证结果是否足以支撑这次交付&lt;/li&gt;
&lt;li&gt;commit 里是否混入无关改动&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这也是为什么自动化越强，越不能把最后一步也完全交出去。AI 可以生成、修改、解释、审查，但“这次可以交付”的责任仍然需要人来承担。至少在当前阶段，人工 review 是 AI 开发流程里的最后一道闸门，而不是可选项。&lt;/p&gt;
&lt;h2&gt;短工期下的风险优先模式&lt;/h2&gt;
&lt;p&gt;如果把这个思路收束成一个更适合短工期的 workflow，它大概不是完整的“需求、调研、计划、任务、日志”，而是下面这样：&lt;/p&gt;
&lt;h3&gt;1. Triage：先判断风险等级&lt;/h3&gt;
&lt;p&gt;不是所有需求都需要同等流程。只要涉及权限、支付、状态流转、批量写入、数据迁移、第三方回调、异步任务、消息队列，就应该自动标记为高风险。&lt;/p&gt;
&lt;p&gt;低风险需求可以轻量处理。高风险需求即使工期短，也不能跳过风险审查。&lt;/p&gt;
&lt;h3&gt;2. Scope Lock：先锁范围&lt;/h3&gt;
&lt;p&gt;短工期里最危险的事情之一是边做边扩。范围一扩，验证面也会扩，但验证时间通常不会跟着增加。&lt;/p&gt;
&lt;p&gt;所以一开始要明确三件事：本次必须交付什么，本次明确不做什么，哪些地方只做兼容或兜底。&lt;/p&gt;
&lt;h3&gt;3. Risk First Plan：先列风险，再写方案&lt;/h3&gt;
&lt;p&gt;传统 plan 经常先写“怎么实现”。风险优先模式应该反过来：先列出最重要的风险，再让方案逐条回应这些风险。&lt;/p&gt;
&lt;p&gt;如果一个方案说不清楚权限、数据一致性、失败恢复和日志，那么它还不是可交付方案，只是实现草图。&lt;/p&gt;
&lt;h3&gt;4. Contract Before Code：先确定契约&lt;/h3&gt;
&lt;p&gt;对不熟悉后端的人来说，接口契约、数据字段、状态枚举、错误返回和权限规则，往往比具体代码更重要。&lt;/p&gt;
&lt;p&gt;代码可以由 AI 快速生成，但契约一旦含糊，后面的实现会在含糊处不断分叉。先锁契约，至少能让 review 有明确对象。&lt;/p&gt;
&lt;h3&gt;5. Implement Small：只实现最小闭环&lt;/h3&gt;
&lt;p&gt;短工期不是展示抽象能力的时候。能不重构就不重构，能不引入新依赖就不引入，能不设计通用框架就不设计通用框架。&lt;/p&gt;
&lt;p&gt;实现越小，review 和验证越可能完成。&lt;/p&gt;
&lt;h3&gt;6. Review Drill：实现后独立审查&lt;/h3&gt;
&lt;p&gt;实现完成后，需要让 AI 切换为 reviewer，而不是继续沿着实现者的叙事往下走。&lt;/p&gt;
&lt;p&gt;比较有效的问法不是“检查一下有没有问题”，而是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;请假设这个实现已经上线并发生严重事故，列出最可能的 5 个原因。
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;请从资深后端 reviewer 的角度审查这段改动，重点关注权限、数据一致性、并发、幂等、错误处理和可观测性。
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;请找出这个实现里哪些判断只存在于代码层，哪些应该下沉到数据库约束、事务或唯一索引。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这种 review 不保证发现所有问题，但它会迫使方案从“能跑”转向“坏了以后能否解释”。&lt;/p&gt;
&lt;h3&gt;7. Evidence：交付证据，而不是交付信心&lt;/h3&gt;
&lt;p&gt;最后交付时，不应该只说“已完成”。更有价值的是留下证据：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;跑了哪些测试&lt;/li&gt;
&lt;li&gt;手动验证了哪些路径&lt;/li&gt;
&lt;li&gt;哪些风险被处理了&lt;/li&gt;
&lt;li&gt;哪些风险只是被接受了&lt;/li&gt;
&lt;li&gt;如果后续要优化，最应该看哪里&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这一步的意义不是写漂亮总结，而是把“我觉得可以”变成“我验证过这些，所以暂时可以”。&lt;/p&gt;
&lt;p&gt;同时，一轮 commit 或 push 应该对应某一块明确功能的实现，而不是把多个不相关的修改混在一起。混乱的提交会让 review、回滚和问题定位都变困难。短工期里尤其容易把“顺手修一下”“顺手重构一下”“顺手调整一下配置”塞进同一次提交，但这会把交付风险藏起来。&lt;/p&gt;
&lt;p&gt;更好的提交粒度是：一个 commit 能说清它解决了哪一个问题，影响了哪些文件，如何验证。如果说不清，这一轮改动就可能已经超过了可控范围。&lt;/p&gt;
&lt;h2&gt;AI 的角色需要被拆开&lt;/h2&gt;
&lt;p&gt;AI 辅助开发里，一个常见问题是同一个 AI 从需求理解、方案设计、代码实现一路做到自我检查。它会天然维护自己的叙事连续性。前面做了某个假设，后面往往会顺着这个假设继续补完，而不是主动拆掉它。&lt;/p&gt;
&lt;p&gt;更稳的方式是把 AI 角色拆开：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实现者：负责实现最小闭环&lt;/li&gt;
&lt;li&gt;审查者：负责挑错和找风险&lt;/li&gt;
&lt;li&gt;运维视角：负责从上线、日志、回滚、事故处理角度审查&lt;/li&gt;
&lt;li&gt;业务追问者：负责追问业务规则是否完整&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;即使实际使用的是同一个模型，也应该在 prompt 上强制切换角色。尤其在自己不熟悉的后端领域里，审查者和运维视角的价值可能比实现者更高。&lt;/p&gt;
&lt;p&gt;因为真正降低风险的不是“写得更快”，而是“更早发现哪里不能信”。&lt;/p&gt;
&lt;h2&gt;最后回到学习问题&lt;/h2&gt;
&lt;p&gt;这件事最后还是会回到学习。不能永远靠 AI 审 AI，也不能把所有风险都交给流程。但学习不一定意味着先脱产补完整个后端体系，再回来做项目。&lt;/p&gt;
&lt;p&gt;更现实的方式是按项目补能力。每个项目只刻意抓一个后端主题：这次重点看权限，下次重点看事务和索引，再下次重点看异步任务和幂等。项目提供真实问题，学习提供判断框架，两者需要互相咬合。&lt;/p&gt;
&lt;p&gt;从这个角度看，短工期下的目标不是建立一个完美的优化循环，而是建立一个最低限度可靠的交付循环：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;先识别风险，再收窄范围；先锁契约，再写代码；先独立审查，再交付证据。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这可能是我现阶段更需要的 AI 开发方式。不是让 AI 替我假装懂后端，而是让 AI 帮我暴露自己还不懂什么，并把这些不懂的部分转化成可检查、可验证、可逐步补上的工程问题。&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>agentGo*002 ｜ Agent 的概念理解</title><link>https://astro-pure.js.org/blog/2026/agentgo/002</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2026/agentgo/002</guid><description>从两篇经典文章理解 Agent</description><pubDate>Fri, 15 May 2026 14:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Lilian Weng 的 &lt;a href=&quot;https://lilianweng.github.io/posts/2023-06-23-agent/&quot;&gt;LLM Powered Autonomous Agents&lt;/a&gt; 和 Anthropic 的 &lt;a href=&quot;https://www.anthropic.com/engineering/building-effective-agents&quot;&gt;Building Effective Agents&lt;/a&gt; 是理解 Agent 概念时最常被引用的两篇文章。前者偏向解释 Agent 的内部结构，后者偏向讨论 Agent 的工程落地方式。放在一起读，基本可以形成一个比较完整的认识框架。&lt;/p&gt;
&lt;p&gt;Agent 并不是脱离大模型独立存在的新范式。更准确地说，它是一个由大模型驱动，并与工具、记忆和环境交互的任务系统。&lt;/p&gt;
&lt;p&gt;在最简单的聊天场景里，大模型通常只完成单次输入到输出的响应过程；而在复杂任务里，系统需要理解目标、拆分步骤、调用外部资源，并根据中间结果继续推进任务。Agent 的概念正是在这种从“单次回答”走向“持续执行”的过程中形成的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Agent 可以理解为：由大模型驱动、借助工具和记忆持续完成任务的系统。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Agent 为什么不是“普通聊天模型”&lt;/h2&gt;
&lt;p&gt;Lilian Weng 的文章并不把重点放在“模型更聪明了”这一层描述上，而是直接拆解 Agent 系统的能力结构。她给出的核心框架可以概括为三层：&lt;code&gt;Planning&lt;/code&gt;、&lt;code&gt;Memory&lt;/code&gt; 和 &lt;code&gt;Tool Use&lt;/code&gt;。如果顺着这个框架往下读，整篇文章其实会清晰很多。&lt;/p&gt;
&lt;h3&gt;Planning&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Planning&lt;/code&gt; 说的并不只是“分步骤”，而是系统如何围绕目标持续推进任务，而不是对输入做一次性响应。&lt;/p&gt;
&lt;p&gt;这一部分包含两个关键点。第一是 &lt;code&gt;task decomposition&lt;/code&gt;，即任务拆解。复杂任务往往无法一步完成，需要先被拆成多个更小、更容易执行的子任务。第二是 &lt;code&gt;self-reflection&lt;/code&gt;，即反思和修正。系统在完成某一步之后，需要根据中间结果重新检查已有判断，并决定是否调整后续路线。&lt;/p&gt;
&lt;p&gt;因此，&lt;code&gt;Planning&lt;/code&gt; 的重点不在于“看起来像在思考”，而在于系统具备了面向目标持续推进任务的能力。普通聊天模型更接近于回答当前问题，而 Agent 更接近于推进一个任务。&lt;/p&gt;
&lt;h3&gt;Memory&lt;/h3&gt;
&lt;p&gt;第二层是 &lt;code&gt;Memory&lt;/code&gt;。这一部分讨论的也不只是上下文窗口长度，而是系统如何保存、组织和召回任务所需的信息。&lt;/p&gt;
&lt;p&gt;Weng 区分了短期记忆和长期记忆。可以先把短期记忆理解为当前上下文窗口中的内容，也就是模型在当前时刻能够直接访问的信息；长期记忆则更接近外部存储，通常依赖检索系统、数据库或向量存储实现。核心问题不是“是否存在记忆”，而是系统如何保存过去的信息，并在合适的时刻重新取回。&lt;/p&gt;
&lt;p&gt;因此，所谓“具有记忆的 Agent”并不是模型天然具备了类似人类的长期记忆，而是系统通过保存、检索和上下文注入等机制实现了记忆能力。记忆在很多场景中首先是工程能力，其次才是模型能力。&lt;/p&gt;
&lt;h3&gt;Tool Use&lt;/h3&gt;
&lt;p&gt;第三层是 &lt;code&gt;Tool Use&lt;/code&gt;。这一部分几乎构成了 Agent 与普通聊天模型之间最明显的一条分界线。&lt;/p&gt;
&lt;p&gt;一个只能生成文本的模型，能力主要停留在分析和表达层面；而一个可以搜索网页、读写文件、执行代码、调用 API、操作浏览器的系统，则开始具备行动能力。Weng 在这里强调的是，工具不是附属增强，而是模型突破参数边界的关键方式。许多当前信息、私有信息、实时信息和执行能力，并不包含在模型权重内部，只能通过工具接入。&lt;/p&gt;
&lt;p&gt;从这个意义上说，工具的引入使模型从“生成内容”转向“执行动作”。没有工具，很多系统仍然只是更复杂的文本生成器，而不是能够真正完成任务的 Agent。&lt;/p&gt;
&lt;p&gt;如果将 Weng 的论述进一步压缩，可以概括为：Agent 的成立依赖于以下三类能力同时存在：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;会围绕目标做规划，而不是只做一次回答&lt;/li&gt;
&lt;li&gt;会借助记忆保存和召回上下文，而不是只盯着当前窗口&lt;/li&gt;
&lt;li&gt;会通过工具接入外部世界，而不是只在模型参数里打转&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Anthropic 眼里的“有效 Agent”&lt;/h2&gt;
&lt;p&gt;如果说 Lilian Weng 的文章主要回答“Agent 由什么构成”，那么 Anthropic 的文章更关注“应当如何使用 Agent”。&lt;/p&gt;
&lt;p&gt;Anthropic 最核心的提醒是：并不是所有任务都需要直接采用 Agent。许多问题并不需要系统在执行过程中持续规划、反复调用工具并动态修正路线。在很多情况下，一个带有检索、工具或记忆能力的增强型 LLM 就足以解决问题。Anthropic 将其视为 agentic system 的基础积木，而不是必须一步上升到完全自主代理。&lt;/p&gt;
&lt;p&gt;这意味着，系统设计并不以复杂性为目标，而是以有效性为目标。Anthropic 的态度很明确：先使用最简单、最低成本、最可控的结构解决问题，只有在简单方案不足时，才增加更高程度的自主性。&lt;/p&gt;
&lt;h2&gt;Anthropic 提到的几种模式&lt;/h2&gt;
&lt;p&gt;Anthropic 文章中提到的几种常见模式可以概括如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;prompt chaining&lt;/code&gt;：适合能拆成固定顺序步骤的任务，本质上还是预先设计好的流程&lt;/li&gt;
&lt;li&gt;&lt;code&gt;routing&lt;/code&gt;：适合输入类型差异大、但每类处理方式稳定的任务，本质上是先分流再处理&lt;/li&gt;
&lt;li&gt;&lt;code&gt;parallelization&lt;/code&gt;：适合能拆开并行的任务，要么追求速度，要么追求更高置信度&lt;/li&gt;
&lt;li&gt;&lt;code&gt;orchestrator-workers&lt;/code&gt;：适合编码、研究这类步骤数不固定的复杂任务，已经很接近真正的 Agent&lt;/li&gt;
&lt;li&gt;&lt;code&gt;evaluator-optimizer&lt;/code&gt;：适合需要反复修改打磨、评价标准又比较明确的任务&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些模式都具有一定的 agentic 特征，但它们与真正 Agent 的差别，关键在于系统的自主性水平。&lt;/p&gt;
&lt;h3&gt;Prompt Chaining&lt;/h3&gt;
&lt;p&gt;这一模式将任务拆成固定顺序的多个步骤，使前一步的输出成为后一步的输入。它适合那些能够被清晰拆开的任务，例如先生成提纲、检查提纲，再据此扩写正文，或先生成文案，再转换为另一种语言形式。可以把它理解成一种“按既定顺序执行”的结构。&lt;/p&gt;
&lt;p&gt;它与 Agent 的关系在于：系统虽然不再是单次调用，但整体路径仍由人预先规定，因此更接近 workflow，而非自主 Agent。&lt;/p&gt;
&lt;h3&gt;Routing&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;routing&lt;/code&gt; 的核心不是多步执行，而是分流。系统先判断当前输入属于哪一类，再将其发送到最合适的 prompt、模型或工具后面。例如，客服问题可以先区分为普通咨询、退款申请和技术支持，再分别进入不同链路；又如将简单问题交给低成本模型，复杂问题交给能力更强的模型。它更像是在不同预设路线之间做选择。&lt;/p&gt;
&lt;p&gt;它适合类别差异较大、但每一类内部处理方式相对稳定的任务。它具备一定的决策性，但这种决策通常只是选择预设路线，而不是在执行过程中持续自主规划。&lt;/p&gt;
&lt;h3&gt;Parallelization&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;parallelization&lt;/code&gt; 讨论的是并行执行。Anthropic 将其分为两种形式：一种是将任务拆成多个相互独立的部分并同时完成，以提高速度；另一种是对同一个问题进行多次调用，再通过投票或汇总提高置信度。前者更偏效率，后者更偏稳健性。&lt;/p&gt;
&lt;p&gt;它适合原本就可以拆开处理的任务，例如多维度评估、安全规则筛查或多轮代码审查。它与 Agent 的差别在于，尽管执行结构更复杂，但每个分支的职责仍然是预先定义好的。&lt;/p&gt;
&lt;h3&gt;Orchestrator-Workers&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;orchestrator-workers&lt;/code&gt; 进一步提高了系统的动态性。与 &lt;code&gt;parallelization&lt;/code&gt; 中预定义的并行分支不同，这一模式由主管模型根据具体任务临时决定如何拆解子任务，再将其分配给不同 worker 执行，最后统一收束结果。&lt;/p&gt;
&lt;p&gt;Anthropic 认为这一模式尤其适合编码和复杂搜索，因为这些任务在开始时往往无法确定最终需要修改多少文件、检索多少轮资料或执行多少步操作。它已经非常接近真正的 Agent，因为系统开始根据输入动态拆解任务，只是整体上仍保留了清晰的编排框架。&lt;/p&gt;
&lt;h3&gt;Evaluator-Optimizer&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;evaluator-optimizer&lt;/code&gt; 则体现了另一种复杂性。它不是将任务拆给多个执行者，而是由一个模型先生成结果，再由另一个模型进行评审，并通过多轮循环不断改进输出。&lt;/p&gt;
&lt;p&gt;这一模式适合评价标准相对明确、且多轮打磨确有收益的任务，例如翻译、复杂写作或需要反复检索和修订的分析任务。它与 Agent 的关系在于，系统已经形成反馈循环，但反馈循环中的角色分工仍然是预先设定的。&lt;/p&gt;
&lt;h2&gt;它们和真正的 Agent 到底差在哪&lt;/h2&gt;
&lt;p&gt;Anthropic 在最后才单独讨论真正意义上的 agents。在其定义中，workflow 与 agent 的差别在于：workflow 由预定义代码路径编排 LLM 和工具，而 agent 则由 LLM 在执行过程中动态决定步骤和工具使用方式。&lt;/p&gt;
&lt;p&gt;换言之，前述模式虽然都具有一定的 agentic 特征，但大多仍处于“由人定义骨架，模型在骨架中运行”的阶段；真正的 Agent 则进一步将“下一步如何执行”的决定权交给模型。&lt;/p&gt;
&lt;p&gt;理解 Agent 的一个更有效方式，不是判断“它是不是 Agent”，而是判断“它具有多高的自主性”。可以将其理解为一条连续光谱：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;单次调用的模型&lt;/li&gt;
&lt;li&gt;带工具或检索的增强型 LLM&lt;/li&gt;
&lt;li&gt;预定义路径的 workflow&lt;/li&gt;
&lt;li&gt;带动态拆解能力的 agentic workflow&lt;/li&gt;
&lt;li&gt;能边执行边自主决定下一步的真正 Agent&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这种理解方式有助于避免将概念神秘化。&lt;/p&gt;
&lt;h2&gt;为什么最后还是工程问题&lt;/h2&gt;
&lt;p&gt;两篇文章共同指向的另一个结论是：Agent 的上限固然与模型能力相关，但其下限更多取决于工程实现。即使模型能力很强，如果工具定义不清、返回结果不稳定、权限边界混乱、记忆内容噪声过高，整个 Agent 的表现仍然会很差。相反，即使模型能力没有极端领先，只要工具接口清晰、上下文组织合理、反馈链路明确，系统也可以在许多场景中表现得相当可靠。决定 Agent 可用性的，往往不只是模型本身，而是它所接入的整个系统。&lt;/p&gt;
&lt;p&gt;这也解释了 Anthropic 为什么强调 &lt;code&gt;effective&lt;/code&gt; 这个词。关键并不在于构建一个表面上复杂、能够连续运行多步的 Agent，而在于构建一个真正能够完成任务、成本可控、过程可追踪、失败可兜底的系统。站在工程角度看，Agent 的价值不在于形式上的复杂性，而在于它能否用更灵活的方式完成复杂任务。&lt;/p&gt;
&lt;h2&gt;最后一句话&lt;/h2&gt;
&lt;p&gt;可以将这两篇文章的分工概括为：Lilian Weng 解释了 Agent 的结构，Anthropic 解释了 Agent 的使用方式。前者说明 Agent 为什么表现出“自主行动”的特征，后者说明真正重要的并不是这种表面特征，而是系统能否稳定完成任务。&lt;/p&gt;
&lt;p&gt;回到最初的问题，Agent 可以被理解为一种由大模型驱动的任务系统。它具备规划能力，可以调用工具，可以利用记忆，并能够根据环境反馈调整后续动作。它既不是单纯的聊天系统，也不是静态的工作流，而是介于“生成模型”和“执行系统”之间的一种结构。&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>agentGo*001 ｜ 方法论和原因</title><link>https://astro-pure.js.org/blog/2026/agentgo/001</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2026/agentgo/001</guid><description>从方法论谈起</description><pubDate>Fri, 15 May 2026 13:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近实习接手了一个 Agent 项目，不过我之前从来没接触过实际的 Agent 应用开发（完全小白），所以萌生了学一些相关知识的念头，打算写一点文章记录和沉淀下来。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;在制造某个工具前，我们必须先说清楚这个工具能解决的问题是什么。我认为目前 Agent 解决的是「在允许非幂等操作的某些场景下，能接受有一定偏差的、可预测的返回结果」的复杂问题，例如最常见的编码任务，我们只需要任务的完成，不需要太关注编码的具体实现；又例如日报，我们允许 Agent 返回一个它决策和加工出来的日报，只要这个结果达到人所给出的某个预期或者标准即可。&lt;/p&gt;
&lt;p&gt;仔细想想，这实际上就是老板-员工的模式，老板说你今天得给我一个完整的 xxx 方案，员工就去自主规划-调用工具（可能是 agent）-完成任务-返回结果。这个方案就是一个可以有一定偏差的、但是（可能）处于预期范围内的结果。&lt;/p&gt;
&lt;p&gt;Agent 并未拥有一个业界内的统一定义，不过大部分机构认为 Agent 是一个「能够独立行动、使用工具、规划并执行复杂任务的系统」。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;以各类 LLM 作为基底，目前市面上有这么几种常见的系统：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;augmented LLM&lt;/p&gt;
&lt;p&gt;通常是单次模型调用，但是并非裸 LLM 调用，而是有一定的封装，例如我们使用的 ChatGPT 网页、DeepSeek 网页、豆包，它们的工作流程都是「用户发起一次问题，LLM 回答一次问题」，它们带有记忆、工具、检索上的增强，以及一些输入检测等等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;workflow&lt;/p&gt;
&lt;p&gt;常见的如 Dify、Coze、n8n，都属于这类范畴，我们使用代码去预定义路径，从而确定任务更稳定、更可控。例如日报，我们可以拆分为这么几步：收集信息-加工信息-返回信息，每个步骤又能写出一些更小的精确步骤，这种时候，采用 workflow 就很不错。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;agent&lt;/p&gt;
&lt;p&gt;workflow 是通过代码编排 LLM 和工具的系统，而 agent 则是由 LLM 动态引导工具使用和自身流程。面对 1. 步骤数不确定 2. 可能需要动态选择工具 3. 中途根据观察结果改变计划这种开放式任务，agent 则更灵活。例如最常见的编码任务，我们想要一个 todolist，在这里做编排是成本大于收益的（除了复杂架构任务）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果结合 Anthropic 的 &lt;a href=&quot;https://www.anthropic.com/engineering/building-effective-agents&quot;&gt;Building Effective Agents&lt;/a&gt; 来看，上面这三类还可以再拆得更细一点。Anthropic 的一个核心观点是：不要一上来就做“全自动 Agent”，而是先从最简单、最便宜、最可控的方案开始，只有当单次调用不够用时，才逐步增加复杂度。&lt;/p&gt;
&lt;h2&gt;再把“模式”讲白一点&lt;/h2&gt;
&lt;p&gt;简单来说，Anthropic 那篇文章其实是在提醒大家：很多问题并不需要一上来就上一个“全自动 Agent”。很多时候，一个带检索或工具的大模型、一个固定的 workflow，甚至只是把任务拆成几步串起来，就已经够用了。只有当任务本身足够开放、步骤不确定、需要中途根据反馈不断调整路线时，Agent 的价值才真正开始体现出来。&lt;/p&gt;
&lt;p&gt;如果再结合 Lilian Weng 的 &lt;a href=&quot;https://lilianweng.github.io/posts/2023-06-23-agent/&quot;&gt;LLM Powered Autonomous Agents&lt;/a&gt; 来看，Agent 的核心无非也就是几件事：先理解目标、再做规划、过程中调用外部工具、必要时保留记忆，并根据执行结果不断调整下一步。换句话说，Agent 并不是某个突然冒出来的新物种，而是把“大模型 + 工具 + 记忆 + 反馈循环”更系统地拼在一起。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;然后我们回到目前国内就业市场里的职位 JD，拿两个例子，看看 agent 开发到底要干嘛：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;某节：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;本科及以上学历，计算机及相关专业；&lt;/li&gt;
&lt;li&gt;熟悉至少一门编程语言，包括但不仅限于 Java/Objective-C/C/C++/Python/Go/JS 等；&lt;/li&gt;
&lt;li&gt;熟悉 AI Agent 相关技术，了解工具开发、Agent 流程设计、评估系统、上下文工程等体系；了解主流 AI 模型及其应用场景，有从 0-1 搭建 AI Agent 应用经验者优先，能够独立完成 Agent 应用的设计、开发和部署工作；&lt;/li&gt;
&lt;li&gt;熟悉一般性架构设计思想，包括不限于服务化、异步、高可用、可扩展等；良好的服务可靠性意识，包括不限于监控、容灾等；&lt;/li&gt;
&lt;li&gt;良好的业务理解抽象和问题解决能力，优秀的团队协作能力；&lt;/li&gt;
&lt;li&gt;研发效能相关方向研发经验者优先。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;某鹅：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;负责 AI Agent 工作流的设计与研发，落地生图、生视频等业务场景的端到端自动化流程；&lt;/li&gt;
&lt;li&gt;基于 LLM（GPT/Claude/DeepSeek/混元等）构建 Agent 的任务规划、工具调用、记忆管理、多轮决策能力；&lt;/li&gt;
&lt;li&gt;使用 Go/Python 构建高并发 Agent 服务框架与调度系统（任务编排、工作流引擎、队列消费、稳定性治理）；&lt;/li&gt;
&lt;li&gt;对接算法，完成生图/生视频模型的能力对接、Prompt 工程与效果调优；&lt;/li&gt;
&lt;li&gt;设计并实现 Agent 编排框架（Workflow/DAG/多 Agent 协作），支持复杂创作链路（脚本→分镜→生图→生视频→剪辑合成）；&lt;/li&gt;
&lt;li&gt;持续优化 Function Calling、Tool Use、RAG 检索、上下文管理等核心模块，提升 Agent 稳定性与生成质量；&lt;/li&gt;
&lt;li&gt;配合产品/算法团队完成效果评估、badcase 分析与迭代。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;这些 JD 看起来很唬人，但如果翻译成人话，其实就是：你不只是要“会调模型”，你还得会把模型放进一个真实系统里，让它能拿到信息、调用工具、在失败时重试、在关键节点让人接管，并且最终对业务结果负责。&lt;/p&gt;
&lt;p&gt;之前在L站和一个佬友讨论了一下，他回复「在业务场景下，其实可以理解为原来必须用规则进行内容清洗、利用规则去硬套模版生成的活交给llm了，这样对于开发者而言，只用维护简单的数据获取，在业务场景下可以仔细研磨output以达到给用户返回的内容最优，这个才是agent最大的优势」&lt;/p&gt;
&lt;h2&gt;Agent 的未来&lt;/h2&gt;
&lt;p&gt;我觉得 Agent 的未来大概不会是“一个万能数字员工立刻接管所有工作”，而更像是一种逐步嵌进真实业务流程的能力。它会先在那些目标明确、反馈清晰、结果相对容易验证的场景里落地，比如编码、检索分析、客服、报表处理，然后再慢慢往更复杂的链路里渗透。&lt;/p&gt;
&lt;p&gt;真正决定 Agent 能不能跑起来的，可能也不会只是模型本身有多强，而是工具接口设计得好不好、记忆系统稳不稳定、权限边界清不清楚、失败之后有没有兜底。换句话说，未来的重点不只是“让模型更聪明”，而是“让模型在现实系统里更可靠地做事”。&lt;/p&gt;
&lt;p&gt;所以如果让我现在用一句话总结：Agent 的未来不是单纯追求更强的自主性，而是在可控前提下，把模型的行动能力真正接到现实系统里。&lt;/p&gt;
&lt;p&gt;总结一下，就是将 Agent 落地到业务某一环中，并做好 Agent 的定制化和工程化，确保该 Agent 能稳定完成业务目标。&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>从 0 到 1 的 Whistle 日报实录</title><link>https://astro-pure.js.org/blog/2026/project/whistle</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2026/project/whistle</guid><description>还是得要有一个个人的日报 bot</description><pubDate>Thu, 14 May 2026 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;上篇「我说轮子多造」提到的 Whistle 项目今天跑通了闭环，写一个实录。&lt;/p&gt;
&lt;p&gt;日报这类产品理应定制化——每个人的隐性信息需求不同，而信息源膨胀、噪声加剧的当下，无关信息会侵占大量「内存」，盯盘也很费体力脑力。我相信未来日报的形态会是人人一个的高度定制 agent，更远的未来，人人都会有自己的定制 agent，日报则会作为一个小小的模块/skill。&lt;/p&gt;
&lt;p&gt;这就是 Whistle 的目的。&lt;/p&gt;
&lt;p&gt;它不做公共资讯产品，也不替代成熟的新闻聚合工具，更像一条私人信息管道：我选信息源、设过滤标准，agent 帮我读取、筛选、总结、排版，产出一份每天能快速扫完的互联网日报。&lt;/p&gt;
&lt;p&gt;普通日报回答「今天大家应该知道什么」，Whistle 回答「今天有什么值得我知道」。&lt;/p&gt;
&lt;h2&gt;为什么要做&lt;/h2&gt;
&lt;p&gt;我原来获取信息的方式很散。&lt;/p&gt;
&lt;p&gt;X、GitHub、Hacker News、Product Hunt、各类 newsletter、朋友转发、论坛、模型发布页、博客和论文摘要——这些来源没有统一入口，每个都有自己的噪声：X 上情绪和重复转发多，GitHub trending 热闹但不一定有价值，新闻站为更新频率塞边角料，newsletter 又带作者的推荐偏好，互联网上没有绝对没有噪声的信息。&lt;/p&gt;
&lt;p&gt;我真正需要的不是更多信息，而是更少但更贴近我的信息。&lt;/p&gt;
&lt;p&gt;我不想每天打开十几个站点，也不想靠平台算法决定我看到什么。我想要一套相对固定的流程：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;可信来源
→ 自动抓取
→ 按我的标准过滤
→ 合并重复信息
→ 提炼要点
→ 生成稳定格式
→ 定时发布和推送
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这件事过去也能做，但写爬虫、接 API、处理失败、设计模板、做部署、接邮件推送、维护定时任务——这些杂活对个人工具来说太容易让人放弃。&lt;/p&gt;
&lt;p&gt;agent 把成本明显压下来了。我不需要一上来就把模块设计完整，可以先把闭环跑起来：信息源少一点、格式粗糙一点都行，只要每天能自动产出一份我愿意看的日报，这个项目就有价值。&lt;/p&gt;
&lt;h2&gt;第一版闭环&lt;/h2&gt;
&lt;p&gt;Whistle 第一版没追求复杂，目标只有一个：每天早上自动生成一份互联网日报，方便我阅读。&lt;/p&gt;
&lt;p&gt;基本链路是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;配置每日关注的信息源
→ Vercel Cron 定时触发 GitHub Actions
→ headless Codex 读取和整理信息
→ 生成结构化日报内容
→ 输出 HTML
→ 用浏览器能力顺带导出 PDF
→ 部署到一个 Next 静态站
→ 推送邮件提醒
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;HTML 是主要产物。日报本质是给人读的，HTML 比纯 Markdown 更适合做信息层级、链接跳转、重点标注和版面控制。PDF 不重要，只是构建过程已经有浏览器环境，顺手导出作为归档版本。&lt;/p&gt;
&lt;p&gt;Next 静态站也不是重点，只是一个方便的展示壳。Whistle 的核心是每天能不能稳定产出一份可读的日报，而不是前端框架。&lt;/p&gt;
&lt;p&gt;第一版只守几个基本要求：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;能自动跑
能看
能回溯
能改
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这四件事成立，就先别急着做复杂功能。&lt;/p&gt;
&lt;h2&gt;Agent 在里面做什么&lt;/h2&gt;
&lt;p&gt;Whistle 里 agent 不是简单的「摘要器」。&lt;/p&gt;
&lt;p&gt;把一堆网页丢给模型让它总结几段话，那确实太粗。日报真正麻烦的地方在于要做一系列判断：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;这条信息是不是新信息？
它和昨天的内容有没有重复？
它是发布、传闻、融资、观点，还是教程？
它对我是否有意义？
它应该放在哪个栏目？
它需要一句话摘要，还是需要展开解释？
原始链接是否值得保留？
标题有没有夸大？
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这些判断很难用固定规则写死，但很适合交给 agent 做第一轮处理。&lt;/p&gt;
&lt;p&gt;理想的分工是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;程序负责稳定性
Agent 负责判断和表达
人负责标准和验收
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;程序保证任务按时启动、文件按格式生成、链接可访问、部署不出错；agent 负责提炼要点、合并重复、判断价值、写成自然语言；人则负责定义什么是「值得看」，并在日报跑偏时调整规则。&lt;/p&gt;
&lt;p&gt;边界很重要。全交给 agent，系统不可控；全写硬规则，又失去灵活性。Whistle 把 agent 放进一条受约束的流水线，让它在固定位置发挥判断力。&lt;/p&gt;
&lt;h2&gt;日报应该长什么样&lt;/h2&gt;
&lt;p&gt;做这个项目后，我对日报形态的判断更明确了：日报不应该只是信息列表。&lt;/p&gt;
&lt;p&gt;一个好的个人日报至少要有三层。&lt;/p&gt;
&lt;p&gt;第一层是快速扫读。让我在一分钟内知道今天有没有值得关注的大事，只需要标题、来源、重要性和一句话摘要。&lt;/p&gt;
&lt;p&gt;第二层是主题归类。同一天的信息常常不是孤立的——某个模型发布、某家公司融资、某个开源项目爆火、某篇论文被讨论，背后可能指向同一个趋势。只按时间排序，很容易看不到这些关系。&lt;/p&gt;
&lt;p&gt;第三层是可追溯链接。日报不能只给结论。每条内容都保留原始来源，方便我感兴趣时点进去看，否则就成了二手转述，而不是信息入口。&lt;/p&gt;
&lt;p&gt;我希望 Whistle 的输出不是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;今天有十条新闻。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而是更接近：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;今天值得看的是三类变化。
每类变化下面有若干条来源。
每条来源都有一句话判断和原始链接。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也不想把它做成纯聊天机器人。聊天适合临时问答，日报更适合稳定格式，每天同样的结构反而有利于形成阅读习惯。&lt;/p&gt;
&lt;h2&gt;第一版踩到的问题&lt;/h2&gt;
&lt;p&gt;跑通闭环后，问题也明显。&lt;/p&gt;
&lt;p&gt;第一是信息源质量。日报的上限取决于输入，源头不好，再漂亮的摘要和排版也没意义。agent 能过滤噪声，但凭空知不到没喂给它的信息。Whistle 后续最重要的工作不是做更多样式，而是持续维护信息源列表。&lt;/p&gt;
&lt;p&gt;第二是重复信息。同一个发布会、同一个模型、同一个项目可能在多个来源里反复出现。不去重，日报会显得很满，但信息密度低。不能只做字符串去重，因为不同来源会用不同标题讲同一件事，更合适的方式是按事件合并而不是按链接合并。&lt;/p&gt;
&lt;p&gt;第三是重要性判断。什么叫重要？因人而异：融资对投资人重要，对开发者一般；新模型参数对研究者重要，对普通用户不重要；某个工具更新对我有用，对别人可能只是小版本变动。Whistle 不该追求客观重要性，而该追求个人相关性——记住我的偏好，而不是假装在编辑大众媒体。&lt;/p&gt;
&lt;p&gt;第四是格式稳定性。Agent 擅长写内容，但不一定每次都严格遵守结构。日报要自动发布，不能今天五个栏目、明天三个、后天又换一套标题体系。这里需要模板、schema 或后处理程序把产物约束住。&lt;/p&gt;
&lt;p&gt;第五是成本。每天让 agent 读大量网页、做长上下文总结、再生成 HTML，成本会慢慢成问题。个人项目可以先不计较，但要长期跑，还是要考虑缓存、增量更新、分层摘要和失败重试。&lt;/p&gt;
&lt;h2&gt;为什么用 Agent&lt;/h2&gt;
&lt;p&gt;日报很难完全写成规则。&lt;/p&gt;
&lt;p&gt;一条新闻值不值得放、标题要不要改、几条来源是不是在说同一件事，这些判断都带模糊性。硬写规则也能做，但很快会变成一堆 if else，越补越累。&lt;/p&gt;
&lt;p&gt;完全人工也不现实。每天打开来源、筛选、去重、整理、排版，做两天还行，长期就成负担。&lt;/p&gt;
&lt;p&gt;agent 适合先把粗活做掉。它不需要替我做最后判断，只要先给出一版可读的日报：链接基本对、重复少一点、摘要别太离谱、栏目大致稳定，剩下的我再根据结果调整规则和信息源。&lt;/p&gt;
&lt;p&gt;Whistle 不是让 agent 自由发挥的项目，而是把它放进一条固定流水线，该判断的地方判断，该输出的地方输出。流程尽量确定，模糊的部分交给模型。&lt;/p&gt;
&lt;p&gt;这比纯脚本顺手。&lt;/p&gt;
&lt;h2&gt;后续想做什么&lt;/h2&gt;
&lt;p&gt;第一是信息源配置。把信息源拆成更清楚的层级——AI 模型发布、开源项目、公司博客、论文、产品更新、行业观点——不同类型用不同的抓取和筛选规则，总之信息源还得由人来把控。&lt;/p&gt;
&lt;p&gt;第二是事件合并。日报不该围绕链接组织，而该围绕事件组织。同一事件可以有多个来源，主来源负责事实，补充来源负责观点或细节。&lt;/p&gt;
&lt;p&gt;第三是偏好规则。我要把偏好写得更明确：哪些主题优先、哪些来源降权、哪些类型直接忽略。否则 agent 只能按通用意义判断重要性，产物会越来越像普通资讯站。&lt;/p&gt;
&lt;p&gt;第四是质量检查。自动生成后最好有一层检查：链接是否可访问、是否出现空栏目、是否有明显重复、是否缺少来源、是否出现过度肯定的表述。这些不一定都要由模型做，很多用脚本就够。&lt;/p&gt;
&lt;p&gt;第五是更好的归档和搜索。日报当天看完就丢掉，价值会少一半。它应该能积累成自己的信息库，以后想回看某个模型、某家公司、某个项目的发展路径，可以直接在归档里搜到。&lt;/p&gt;
&lt;h2&gt;小结&lt;/h2&gt;
&lt;p&gt;Whistle 目前还很早期，只是先把从信息源到日报产物的链路跑通了。&lt;/p&gt;
&lt;p&gt;它不算新闻产品，更像私人信息过滤器：每天早上替我把来源扫一遍，压下重复和噪声，留下几条真正值得点开的东西。&lt;/p&gt;
&lt;p&gt;后面要做的不是把它变复杂，而是让它更稳定、更贴近我的阅读习惯。信息源慢慢调，规则慢慢补，归档慢慢沉淀。只要它每天能少消耗我一点注意力，这个小轮子就算有用了。&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>「我说轮子多造」</title><link>https://astro-pure.js.org/blog/2026/think/%E8%BD%AE%E5%AD%90%E5%A4%9A%E9%80%A0</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2026/think/%E8%BD%AE%E5%AD%90%E5%A4%9A%E9%80%A0</guid><description>AI时代，我们还要保持「不要重复造轮子」吗</description><pubDate>Wed, 13 May 2026 08:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;从 Whistle 说起&lt;/h2&gt;
&lt;p&gt;我最近在写一个小项目 Whistle，是一个「互联网日报」。我选定信息源，由 Codex 帮忙过滤、加工、产出，产物是 HTML 和 PDF。前者放到一个 Next 搭出来的静态站上，后者实际没什么用，不过顺带做了，因为跑 Codex 的 Chromium 正好有这个功能。另外还有一个简单的基于 GitHub CI/CD 的定时任务，能在早上跑一遍构造流程，然后定时发布和推送邮件。&lt;/p&gt;
&lt;p&gt;跟同学分享了 Whistle，他说「我说轮子多造」。&lt;/p&gt;
&lt;p&gt;市面上确实有不少这样的产品，确实看似很符合「造轮子」的定义。&lt;/p&gt;
&lt;p&gt;不过轮子这个词还是比较广义的，我们可以把各种工具称为轮子，比如 lodash；也可以把各种封装好的框架称为轮子，比如 React；轮子可以有很多种形态，不过它的目的通常是通过低耦合易使用的接口来解决通用性的问题。&lt;/p&gt;
&lt;h2&gt;过去的问题是成本&lt;/h2&gt;
&lt;p&gt;从这个角度看，「不要重复造轮子」其实是一个很合理的工程原则。&lt;/p&gt;
&lt;p&gt;如果一个东西已经足够通用、足够稳定、足够便宜，并且它的维护成本远远超过你自己重写的收益，那么你就不应该重写它。没有必要自己实现一个日期库，没有必要自己写一个加密算法，也没有必要为了显示一个弹窗从零写一套 UI 框架。这里的核心不是「不能造」，而是「收益不值得成本」。&lt;/p&gt;
&lt;p&gt;但是 AI 时代改变了一个很关键的变量：成本。&lt;/p&gt;
&lt;p&gt;以前造一个小轮子，意味着你要自己写代码、查文档、调 bug、做部署、补边界。如果只是为了满足一个很个人化的需求，这个投入经常不划算。所以大家更倾向于找现成工具，哪怕它有一堆自己用不上的功能，哪怕它的交互和自己的习惯不完全匹配，哪怕它最后变成了一个订阅制服务。&lt;/p&gt;
&lt;p&gt;现在不一样了。很多小型系统的搭建成本被 AI 压得很低。&lt;/p&gt;
&lt;p&gt;你不需要从零记住 Next 怎么配，也不需要完全手写 CI，也不需要自己慢慢查 Puppeteer 或 Playwright 的 PDF 导出参数。你只要能描述清楚自己要什么，能判断生成结果是否靠谱，能在关键地方做取舍，一个过去需要周末折腾的东西，现在可能几个小时就能跑起来。&lt;/p&gt;
&lt;p&gt;于是「造轮子」这件事就从一个工程性决策，变成了一个更偏产品性和生活方式的决策。&lt;/p&gt;
&lt;h2&gt;私人轮子的价值&lt;/h2&gt;
&lt;p&gt;Whistle 对我来说不是为了打败市面上的日报产品。&lt;/p&gt;
&lt;p&gt;我想要的是我自己选的信息源，我自己定义的过滤规则，我自己认可的排版和摘要颗粒度，我自己能理解、能修改、能迁移的一套流程。它不一定比成熟产品更强，但它更贴近我的使用方式、我也对它有更完整的心智模型。对这种需求来说，「已有产品存在」并不能自动推出「我不应该做一个自己的」。&lt;/p&gt;
&lt;p&gt;很多时候，公共产品解决的是最大公约数问题。&lt;/p&gt;
&lt;p&gt;它要照顾足够多的人，所以会慢慢长出配置、账号、推荐算法、商业化入口、通知系统、团队协作和各种看起来合理但我并不需要的功能。最后它确实更完整，但也更重（Pi 的作者就是因为 Claude Code 日渐庞大才选择写自己的一个 Agent CLI的）&lt;/p&gt;
&lt;p&gt;私人轮子解决的是最小闭环问题。&lt;/p&gt;
&lt;p&gt;它不需要兼容所有人，不需要做权限系统，不需要设计复杂的计费模型，不需要考虑增长，也不需要把每个异常都包装成优雅提示。它只要在我的机器、我的仓库、我的工作流里稳定运行，就已经完成了它的使命。&lt;/p&gt;
&lt;p&gt;这也是 AI 辅助开发最有意思的地方：它让「个人软件」重新变得可行。&lt;/p&gt;
&lt;p&gt;以前个人软件最大的问题不是想法，而是手不够。你想做一个给自己用的工具，但一想到要搭项目、写界面、接服务、部署、维护，就会觉得不如算了。现在 AI 相当于补了一只手，甚至补了好几只手。它不能替你做判断，但可以替你承担大量低价值的体力活。&lt;/p&gt;
&lt;h2&gt;哪些轮子不要随便造&lt;/h2&gt;
&lt;p&gt;所以我现在对「轮子」的态度更宽松了。&lt;/p&gt;
&lt;p&gt;如果是基础设施，不要随便造。&lt;/p&gt;
&lt;p&gt;如果是安全、金融、加密、存储、权限这类领域，更不要因为 AI 能写就随便造。这里的风险不是「能不能跑」，而是「出事的时候你知不知道为什么」。AI 会让代码出现得更快，但不会自动让责任消失。&lt;/p&gt;
&lt;p&gt;如果是业务系统，也要谨慎造。&lt;/p&gt;
&lt;p&gt;团队协作里的轮子有另一套成本。你今天用 AI 写了一个看起来很顺手的小框架，明天同事要理解，后天线上要排障，下个月需求变了还要迁移。个人项目里可以接受的粗糙，在团队项目里可能就是技术债。&lt;/p&gt;
&lt;p&gt;但如果是个人工具、小型实验、一次性自动化、学习项目，或者高度贴合自己习惯的工作流，我觉得可以多造。&lt;/p&gt;
&lt;h2&gt;多造不是乱造&lt;/h2&gt;
&lt;p&gt;因为这类轮子的价值不只在最终产物，也在过程本身。&lt;/p&gt;
&lt;p&gt;你会被迫拆解需求，思考什么是必要的，什么是伪需求；你会理解一个系统从输入到输出的链路；你会知道现成产品为什么那样设计，也会知道它们的限制在哪里。哪怕最后这个轮子被你扔掉，它也不是完全浪费。&lt;/p&gt;
&lt;p&gt;更重要的是，AI 让试错变便宜了。&lt;/p&gt;
&lt;p&gt;以前「做一个自己的」常常意味着长期承诺。现在它更像是一次快速实验：先搭出来，用几天，看哪里别扭，再决定要不要继续。这个节奏很适合个人项目。一个轮子如果三天后死掉，也许它只是完成了验证；一个轮子如果每天都被你用，那它自然会长出继续维护的理由。&lt;/p&gt;
&lt;p&gt;所以问题不应该是「这个东西外面有没有」。&lt;/p&gt;
&lt;p&gt;这个问题太粗了。&lt;/p&gt;
&lt;p&gt;更好的问题应该是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我造它，是为了学习，还是为了逃避使用现成工具？&lt;/li&gt;
&lt;li&gt;我造它，是因为需求真的特殊，还是因为我低估了维护成本？&lt;/li&gt;
&lt;li&gt;我能不能接受它坏掉？&lt;/li&gt;
&lt;li&gt;它坏掉以后，影响的是我自己，还是别人？&lt;/li&gt;
&lt;li&gt;AI 生成的部分，我是否有能力判断和接管？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果这些问题都想清楚了，那轮子多造一点也没什么。&lt;/p&gt;
&lt;h2&gt;公共轮子和私人轮子&lt;/h2&gt;
&lt;p&gt;「不要重复造轮子」是工业时代的好建议，因为当时造轮子贵，维护轮子更贵。&lt;/p&gt;
&lt;p&gt;但现在很多个人轮子的成本已经低到接近写一篇笔记、做一个脚本、搭一个临时页面。它们不一定要成为产品，也不一定要长期存在。它们可以只是一个人和自己需求之间的接口。&lt;/p&gt;
&lt;p&gt;所以我现在更愿意把轮子分成两类。&lt;/p&gt;
&lt;p&gt;一种是公共轮子，目标是稳定、通用、可维护、可协作。这种轮子少造，能不用自己造就不用自己造。&lt;/p&gt;
&lt;p&gt;另一种是私人轮子，目标是贴合、轻便、可修改、能解决自己的具体问题。这种轮子可以多造。AI 时代真正被释放的，恰恰是这一类。&lt;/p&gt;
&lt;p&gt;Whistle 大概就是这样一个私人轮子。&lt;/p&gt;
&lt;p&gt;它没有什么宏大的意义，也不需要证明自己比现有产品更好。它只是刚好把我想看的信息、我想要的格式、我能控制的流程连在了一起。&lt;/p&gt;
&lt;p&gt;如果这也算重复造轮子，那我觉得可以多造。&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>AI展示介质生成测试</title><link>https://astro-pure.js.org/blog/2026/ai%E5%B1%95%E7%A4%BA%E4%BB%8B%E8%B4%A8%E7%94%9F%E6%88%90%E6%B5%8B%E8%AF%95</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2026/ai%E5%B1%95%E7%A4%BA%E4%BB%8B%E8%B4%A8%E7%94%9F%E6%88%90%E6%B5%8B%E8%AF%95</guid><description>用于嵌入 HTML、PDF 等展示介质的测试文章</description><pubDate>Tue, 12 May 2026 10:48:13 GMT</pubDate><content:encoded>&lt;p&gt;这是一篇用于测试AI生成展示介质能力测试的 MDX 文章，包括报告、PPT等形式&lt;/p&gt;
&lt;h2&gt;index.html&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/2026-H1/AI%E5%B1%95%E7%A4%BA%E4%BB%8B%E8%B4%A8%E7%94%9F%E6%88%90%E6%B5%8B%E8%AF%95/index.html&quot;&gt;新窗口打开&lt;/a&gt; By Guizang-PPT-skill&lt;/p&gt;
&lt;h2&gt;openai-symphony-research-report.html&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/2026-H1/AI%E5%B1%95%E7%A4%BA%E4%BB%8B%E8%B4%A8%E7%94%9F%E6%88%90%E6%B5%8B%E8%AF%95/openai-symphony-research-report.html&quot;&gt;新窗口打开&lt;/a&gt; By Kami&lt;/p&gt;
&lt;h2&gt;mem0-research-report.pdf&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/2026-H1/AI%E5%B1%95%E7%A4%BA%E4%BB%8B%E8%B4%A8%E7%94%9F%E6%88%90%E6%B5%8B%E8%AF%95/mem0-research-report.pdf&quot;&gt;下载 / 新窗口打开 PDF&lt;/a&gt; By Kami&lt;/p&gt;
&lt;p&gt;&amp;#x3C;embed
src=&quot;/2026-H1/AI展示介质生成测试/mem0-research-report.pdf&quot;
type=&quot;application/pdf&quot;
width=&quot;100%&quot;
height=&quot;800&quot;
style={{ border: &apos;1px solid #e5e7eb&apos;, borderRadius: &apos;12px&apos; }}
/&gt;&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>Agent 更适合 Markdown，HTML 更适合人</title><link>https://astro-pure.js.org/blog/2026/ai-generate/agent%E6%9B%B4%E9%80%82%E5%90%88markdown</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2026/ai-generate/agent%E6%9B%B4%E9%80%82%E5%90%88markdown</guid><description>总结和展示可以 HTML，中间过程和任务编排还是 Markdown 更顺手</description><pubDate>Sat, 09 May 2026 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;起因是看到这条推文：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/trq212/status/2052809885763747935&quot;&gt;https://x.com/trq212/status/2052809885763747935&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;我的判断很简单：对 agent 来说，Markdown 仍然更好；对人来说，HTML 的交互方式确实不错。&lt;/p&gt;
&lt;p&gt;Markdown 的优势主要在于它更轻、更直接，也更适合连续推理和中间态输出。task、plan、修改建议、检查清单，这些内容本质上都还是文本结构，写成 md 容易生成、容易续写、容易 diff，也不容易把注意力带偏。&lt;/p&gt;
&lt;p&gt;HTML 的好处则在于它更适合给人看。信息层级、折叠、按钮、卡片、局部交互，这些都比纯文本更自然。如果是在 GUI 里，或者是做总结性报告、初始计划、结果展示，这种形式其实是有吸引力的。&lt;/p&gt;
&lt;p&gt;但放到目前这种 CLI 工作流里，它的问题也很明显：你需要额外打开浏览器去看，交互链路被拉长了；同时它还会耗费更多 token 去生成样式、结构和一些并不关键的界面描述。对很多中间过程来说，这个成本不太值。&lt;/p&gt;
&lt;p&gt;Superpower 其实已经做过类似尝试，不过我觉得效果并不理想。它更像是实现了一个“可以选择意向”的 HTML 页面，结果并没有真正提升理解，反而把原本简单的交互过程复杂化了。&lt;/p&gt;
&lt;p&gt;所以我的结论是：这个方向本身没有问题，而且在 GUI 场景里会很有潜力；只是如果回到 agent 的主工作流，尤其是中间的 task、plan 和迭代过程，Markdown 还是更合适。HTML 可以放在开头和结尾，md 更适合放在中间。&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>Codex 的 Computer Use 功能是怎么工作的</title><link>https://astro-pure.js.org/blog/2026/ai-generate/codex%E7%9A%84computeruse%E5%A6%82%E4%BD%95%E5%B7%A5%E4%BD%9C</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2026/ai-generate/codex%E7%9A%84computeruse%E5%A6%82%E4%BD%95%E5%B7%A5%E4%BD%9C</guid><description>说穿了，就是基于截图判断界面，再去调用鼠标和键盘</description><pubDate>Sat, 09 May 2026 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;如果把话说得直接一点，Codex 的 &lt;code&gt;computer use&lt;/code&gt; 没什么神秘的，说穿了就是：先看截图，再判断该点哪里、输入什么，然后把这些动作执行出去。&lt;/p&gt;
&lt;p&gt;它并不是“理解了电脑内部的一切”，也不是像人一样真正会用软件。更接近的说法是：模型看到一张当前界面的图，然后根据目标猜下一步操作，再让外部程序去执行鼠标和键盘事件。&lt;/p&gt;
&lt;p&gt;所以这个过程其实很简单，大概就是一个循环。&lt;/p&gt;
&lt;p&gt;第一步，截屏。&lt;/p&gt;
&lt;p&gt;系统把当前界面截图发给模型。模型能看到按钮、输入框、菜单、弹窗这些视觉元素，但它看到的本质上还是图，不是软件内部的结构化状态。&lt;/p&gt;
&lt;p&gt;第二步，判断动作。&lt;/p&gt;
&lt;p&gt;模型根据截图和用户目标，判断下一步该做什么。比如点击某个坐标、输入一段文字、按下某个键、滚动页面，或者等待界面加载。&lt;/p&gt;
&lt;p&gt;第三步，执行动作。&lt;/p&gt;
&lt;p&gt;外部执行器把这些动作真正变成鼠标和键盘事件。点一下、输一段、滚一下，然后再截一张新的图给模型。&lt;/p&gt;
&lt;p&gt;第四步，重复上面的过程，直到任务完成或者走偏。&lt;/p&gt;
&lt;p&gt;所以你说“它不就是基于截图做操作吗”，这个判断基本就是对的。它的核心不是什么特别新的计算机原理，而是把“看屏幕”和“点鼠标敲键盘”也包装成了 Agent 可以调用的一类工具。&lt;/p&gt;
&lt;p&gt;这样做的价值在于，有些系统没有 API，也没有命令行入口，但人能手动操作界面。&lt;code&gt;computer use&lt;/code&gt; 等于让 Agent 也能勉强去做这件事。&lt;/p&gt;
&lt;p&gt;但它的短板也很明显。&lt;/p&gt;
&lt;p&gt;首先，它慢。因为每一步都要重新看图、重新判断，而不是直接读结构化数据。&lt;/p&gt;
&lt;p&gt;其次，它不稳定。按钮位置变了、页面弹窗挡住了、加载慢了一点、焦点丢了，都可能让它判断失误。&lt;/p&gt;
&lt;p&gt;最后，它贵。因为截图理解和多轮交互本来就比直接调 API 或跑命令更耗上下文，也更耗 token。&lt;/p&gt;
&lt;p&gt;所以这类能力当然有用，但更适合“没有更好接口时的替代方案”，而不是首选方案。只要有 API，就优先 API；只要能走 CLI，就优先 CLI；只有两者都没有时，截图驱动的 &lt;code&gt;computer use&lt;/code&gt; 才真正体现价值。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OpenAI Platform Docs: &lt;a href=&quot;https://platform.openai.com/docs/guides/tools-computer-use&quot;&gt;Computer Use&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>如何判断一张图片是否由 AI 生成：一套多证据分析框架</title><link>https://astro-pure.js.org/blog/2026/project/how-to-detect-ai-images</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2026/project/how-to-detect-ai-images</guid><description>AI 图片判断不是二分类问题，而是一套多证据分析框架</description><pubDate>Thu, 07 May 2026 20:00:00 GMT</pubDate><content:encoded>&lt;p&gt;判断一张图片是不是 AI 生成，不能只靠一个&quot;检测器分数&quot;，也不能只靠肉眼感觉。早期 AI 图经常有很明显的问题，比如手指数量不对、文字乱码、眼睛奇怪、背景糊成一团。但现在的生成模型已经进步很多（指的就是你，gpt-image-2），很多图片在第一眼看来几乎没有明显破绽。&lt;/p&gt;
&lt;p&gt;与此同时，真实照片也不总是&quot;干净&quot;的。真实照片经过微信转发、社交平台压缩、截图、滤镜、美颜、裁剪、转格式之后，也会出现噪声异常、细节丢失、边缘模糊、压缩痕迹、文字不可读等现象。这些现象有时会和 AI 图片的缺陷很像。&lt;/p&gt;
&lt;p&gt;所以，&quot;判断 AI 图片&quot;不是一个简单的二分类问题。&lt;/p&gt;
&lt;p&gt;它不应该被理解成：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;输入图片 → 输出 AI / 真人
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;更合理的理解是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;输入图片
→ 检查来源信息
→ 检查文件和像素痕迹
→ 检查视觉内容
→ 检查物理一致性
→ 检查统计模型信号
→ 检查外部来源和事实
→ 综合所有证据，给出风险判断
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这篇文章的目的不是提供一个绝对可靠的&quot;AI 图片鉴定公式&quot;，而是提供一套分析思路：当我们面对一张可疑图片时，应该从哪些角度入手，哪些信号更强，哪些信号容易误判，以及最后应该如何表达结论。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;一、先看来源：真正强的证据通常不是&quot;看起来像&quot;&lt;/h2&gt;
&lt;p&gt;很多人判断 AI 图片，第一反应是看画面有没有异常。但在真正的分析流程里，最应该优先检查的其实是来源证据。&lt;/p&gt;
&lt;p&gt;所谓来源证据，指的是图片文件中是否携带了能说明它来源的信息，比如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;EXIF
Metadata
C2PA / Content Credentials
相机型号
拍摄时间
编辑软件
生成软件
平台来源声明
签名信息
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中，最值得关注的是 C2PA / Content Credentials。它是一种用于记录内容来源和编辑历史的机制。如果一张图片带有经过验证的 C2PA 信息，并且其中明确声明它是由某个 AI 工具生成，或者经过生成式 AI 编辑，那么这会是非常强的 AI 来源证据。&lt;/p&gt;
&lt;p&gt;例如，某些图片可能会记录：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;created by AI tool
generated by model
edited with generative AI
produced by image generator
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这种信息的价值远高于&quot;看起来像 AI&quot;。因为它不是从画面外观推测，而是来自图片的来源声明。&lt;/p&gt;
&lt;p&gt;但这里有几个坑。&lt;/p&gt;
&lt;p&gt;第一个坑是：&lt;strong&gt;metadata 很容易丢失&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;图片经过微信、微博、X、Instagram、小红书、抖音、网页压缩、截图再保存之后，EXIF 和其他 metadata 经常会被清除。一张真实手机照片经过几次转发后，可能什么相机信息都没有了。&lt;/p&gt;
&lt;p&gt;所以：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;没有 metadata ≠ AI 生成
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第二个坑是：&lt;strong&gt;普通 EXIF 不一定可信&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;EXIF 可以被修改。一个文件里写着某个相机型号，不代表它一定是那个相机拍的。普通 EXIF 更像是一个参考信息，而不是强证明。&lt;/p&gt;
&lt;p&gt;所以：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;有相机 EXIF ≠ 一定是真实拍摄
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第三个坑是：&lt;strong&gt;签名来源不等于最终结论&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;比如一张图片的 C2PA 显示 signed by 某个大公司，这只说明这份声明由某个签名方签署。真正要看的是声明内容：它说的是相机拍摄、普通编辑、生成式编辑，还是 AI 生成。&lt;/p&gt;
&lt;p&gt;所以来源信息的正确使用方式是分级判断：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;可信 C2PA 明确声明 AI 生成
→ 强 AI 证据

可信 C2PA 明确声明相机拍摄
→ 强相机来源证据

普通相机 EXIF
→ 弱到中等真人来源信号

出现 Photoshop / Lightroom / Canva 等编辑软件
→ 说明可能被处理过，但不等于 AI

没有 metadata
→ 信息不足，不能直接下结论
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;换句话说，来源信息是第一优先级的证据，但它依然需要解释，不能机械使用。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;二、再看文件和像素：水印、压缩、编辑痕迹只能说明一部分问题&lt;/h2&gt;
&lt;p&gt;除了来源信息，还可以检查图片文件和像素层面的痕迹。这一类分析通常包括水印、压缩痕迹、噪声模式、局部编辑痕迹等。&lt;/p&gt;
&lt;h3&gt;1. 水印：强证据和弱信号要分清&lt;/h3&gt;
&lt;p&gt;有些 AI 图像生成系统会给图片加入水印。水印大致分两类：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;显式水印
隐式水印
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;显式水印很容易理解，就是图片角落有平台 logo、作品站标记、生成器名称，或者某些明显的标识。显式水印如果没有被裁掉，判断起来比较直接。&lt;/p&gt;
&lt;p&gt;隐式水印更复杂。它可能隐藏在像素、频域或某些不可见特征里，人眼看不到，需要专门工具检测。例如有些 Stable Diffusion 生态工具曾经使用 invisible watermark，一些大平台也提出过自己的水印方案。&lt;/p&gt;
&lt;p&gt;如果一个工具能够明确解码出可信的官方水印，而且水印内容和检测器来源都可靠，那么这可以是强 AI 证据。&lt;/p&gt;
&lt;p&gt;但水印检测也很容易被误用。&lt;/p&gt;
&lt;p&gt;很多时候，检测器并不是&quot;确定解码出水印&quot;，而只是发现一些频率模式&quot;像水印&quot;。这就有误检风险。尤其在图片经过下面这些处理后：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;截图
微信压缩
社交平台重编码
裁剪
缩放
转格式
加滤镜
二次保存
多次压缩
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;图片的频域结构会发生变化，可能破坏真正的水印，也可能产生类似水印的伪信号。&lt;/p&gt;
&lt;p&gt;所以水印结果最好分级理解：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;verified：明确解码出可信水印
probable：较可能存在水印，但证据不完全
suspected：疑似水印相似信号
not_detected：未检测到水印
unsupported：当前图片或检测器不支持
error：检测失败
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;只有 &lt;code&gt;verified&lt;/code&gt; 才适合作为强证据。&lt;/p&gt;
&lt;p&gt;如果只是 &lt;code&gt;suspected&lt;/code&gt;，就只能算弱证据。报告里应该写：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;检测到疑似水印相似信号，但置信度较低，不能单独证明图片由 AI 生成。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而不是写：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;检测到水印，所以这是 AI 图。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;还要注意另一个方向：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;没检测到水印 ≠ 不是 AI
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;很多 AI 工具不加水印，很多水印会在压缩或截图中丢失，很多平台也不会开放检测方式。所以水印只能提供正向证据，不能作为排除 AI 的可靠依据。&lt;/p&gt;
&lt;h3&gt;2. 压缩和编辑痕迹：它们更像&quot;处理痕迹&quot;，不是&quot;AI 证据&quot;&lt;/h3&gt;
&lt;p&gt;很多图片不是原始图，而是经过传播和编辑之后的版本。比如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;从社交平台保存
微信转发
截图再上传
从视频里截帧
经过美颜或滤镜
用 Photoshop / Lightroom / Canva 处理
从 PNG 转 JPEG
从 WebP 转 JPEG
多次重编码
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这些操作都会改变图片的像素结构。&lt;/p&gt;
&lt;p&gt;因此，可以用一些图像取证方法观察图片是否存在处理痕迹：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;ELA 分析
JPEG 压缩块分析
噪声模式分析
局部重压缩检测
边缘异常检测
颜色通道异常
频域异常
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果图片某些局部区域的压缩质量、噪声水平或边缘特征和整体不一致，可能说明它被局部编辑过。&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;人物区域和背景区域噪声不一致
某个物体边缘附近压缩异常明显
局部区域比其他区域明显更清晰
不同区域 JPEG 质量痕迹不同
背景和前景的颗粒感不一致
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这些现象可能意味着：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;局部修图
拼接合成
压缩重编码
AI 扩图
AI 修复
AI 替换背景
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但它们不能直接证明 AI 生成。&lt;/p&gt;
&lt;p&gt;因为真实照片只要经历过编辑、转发、截图，也可能产生类似现象。反过来，一张 AI 生成图如果是原始导出文件，可能没有明显压缩异常。&lt;/p&gt;
&lt;p&gt;所以压缩和编辑分析更适合回答：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;这张图是否可能被处理过？
是否存在局部修改或合成痕迹？
图片传播链是否复杂？
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而不适合直接回答：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;这张图是不是 AI 生成？
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. 传播链越复杂，结论越应该保守&lt;/h3&gt;
&lt;p&gt;判断一张图片时，还要考虑它的传播链。&lt;/p&gt;
&lt;p&gt;一张原始相机照片、一张从网页下载的图片、一张微信里保存的图片、一张截图再转发的图片，它们能提供的信息量完全不同。&lt;/p&gt;
&lt;p&gt;传播链越复杂，越容易出现：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;metadata 丢失
C2PA 丢失
水印被破坏
压缩痕迹变多
噪声模式变化
统计检测器分数波动
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以，如果图片明显经过截图、转发、多次压缩，任何检测结果都应该降置信度。&lt;/p&gt;
&lt;p&gt;比较合理的判断方式是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;原始文件
→ 可以更信任文件级证据

社交平台保存图
→ metadata 和压缩分析需要降权

截图图像
→ C2PA、EXIF、水印、统计模型都要谨慎

多次重编码图片
→ 只能给风险提示，不适合高置信判断
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一套成熟的分析流程，不只是给出&quot;检测到了什么&quot;，还应该告诉用户：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;由于图片经过压缩或转存，本次检测可靠性下降。
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;三、看画面内容：视觉异常和物理矛盾是重要线索，但不是最终裁判&lt;/h2&gt;
&lt;p&gt;在没有强来源证据时，人们最常用的方法就是看图片内容。这个方向确实有价值，但必须结构化，否则很容易变成主观感觉。&lt;/p&gt;
&lt;p&gt;视觉层面的分析可以分成两类：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;视觉语义异常
物理与场景一致性异常
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1. 视觉语义异常：不要说&quot;有 AI 味&quot;，要指出具体问题&lt;/h3&gt;
&lt;p&gt;&quot;AI 味&quot;是一个很模糊的说法。更好的方式是观察具体异常。&lt;/p&gt;
&lt;p&gt;常见视觉异常包括：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;人体结构异常
面部细节异常
文字和符号异常
物体融合
边缘不自然
纹理重复
背景结构混乱
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;人体和面部&lt;/h4&gt;
&lt;p&gt;早期 AI 图最容易在人体上翻车，尤其是手、牙齿、眼睛、耳朵、眼镜和头发。&lt;/p&gt;
&lt;p&gt;可以观察：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;手指数量是否异常
手指关节是否自然
指甲是否合理
手和物体接触是否正确
眼睛是否对称
牙齿是否糊成一片
耳朵结构是否混乱
眼镜框是否断裂
头发边缘是否异常融合
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过现在很多模型已经改善了手部和面部问题。所以&quot;手没问题&quot;不代表不是 AI，只能说没有发现这一类异常。&lt;/p&gt;
&lt;h4&gt;文字和符号&lt;/h4&gt;
&lt;p&gt;AI 生成图片常见的另一个问题是文字。&lt;/p&gt;
&lt;p&gt;可以观察：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;路牌文字是否正常
商标是否拼错
衣服上的字是否可读
包装上的说明是否合理
书页文字是否像乱码
UI 截图里的文字是否一致
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果文字看起来像某种语言，但仔细看不可读，或者 logo 似是而非，这可能是 AI 信号。&lt;/p&gt;
&lt;p&gt;不过，真实图片经过压缩、运动模糊、远距离拍摄，也会导致文字不可读。所以文字异常也要结合清晰度判断。&lt;/p&gt;
&lt;h4&gt;物体融合和边缘异常&lt;/h4&gt;
&lt;p&gt;AI 图里有时会出现物体边界不清、结构融合的问题：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;杯子和手粘在一起
耳机线消失在衣服里
椅子腿数量不对
包带突然断掉
首饰结构不连续
背景人物身体融合
建筑窗户重复变形
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这类异常如果清晰可见，通常比&quot;风格像 AI&quot;更有价值。&lt;/p&gt;
&lt;h4&gt;纹理异常&lt;/h4&gt;
&lt;p&gt;生成模型可能在大面积纹理上出现重复、过度平滑或不自然的模式。&lt;/p&gt;
&lt;p&gt;常见区域包括：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;头发
草地
树叶
墙面
织物
皮肤
水面
建筑窗户
人群背景
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例如，草地看起来像重复贴图，皮肤质感过度平滑，背景人群像糊在一起，这些都可能是 AI 信号。&lt;/p&gt;
&lt;p&gt;但同样要注意，美颜、降噪、低清压缩也可能让真实照片出现类似问题。&lt;/p&gt;
&lt;p&gt;因此，视觉异常分析的正确表达应该是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;发现了什么具体异常？
异常位于哪里？
异常是否清晰可见？
是否可能由压缩、低清、风格化或修图造成？
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而不是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;我感觉它像 AI。
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. 物理一致性：看这个&quot;世界&quot;是否自洽&lt;/h3&gt;
&lt;p&gt;有些 AI 图片局部细节看起来没问题，但整体物理关系不对。&lt;/p&gt;
&lt;p&gt;物理一致性可以检查：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;光源方向
阴影方向
反射关系
透视结构
物体接触关系
遮挡关系
尺度关系
重力关系
景深一致性
运动模糊
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;人物的阴影方向和光源方向不一致
镜子里缺少应该出现的反射
水面倒影和真实物体对不上
桌子上的物体没有接触阴影
背景透视线互相冲突
人物比例和环境尺度不协调
物体像悬浮在空中
不同物体的景深模糊不一致
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这类检查对写实图片很有用。因为即使 AI 模型生成了很漂亮的局部细节，它也可能在场景整体逻辑上出错。&lt;/p&gt;
&lt;p&gt;但物理一致性也容易误判。真实摄影里也会有：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;复杂多光源
广角畸变
反射错觉
舞台灯光
运动模糊
后期修图
低清压缩
特殊拍摄角度
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比如舞台照的光影可能非常复杂，玻璃反射可能让人误以为不一致，广角镜头会让透视看起来夸张。这些都不应该轻易判成 AI。&lt;/p&gt;
&lt;p&gt;所以物理一致性适合作为中等证据。它最好和视觉异常、压缩痕迹、来源信息一起综合判断。&lt;/p&gt;
&lt;p&gt;如果一张图片同时有：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;明显手部异常
明显反射错误
统计 detector 高分
没有可信来源信息
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么 AI 风险会升高。&lt;/p&gt;
&lt;p&gt;但如果只有一个轻微阴影疑点，就不应该给出高置信结论。&lt;/p&gt;
&lt;h3&gt;3. 非摄影图要特别小心&lt;/h3&gt;
&lt;p&gt;不是所有图片都应该按真实摄影标准判断。&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;二次元插画
游戏截图
UI 截图
网页截图
海报设计
漫画
表情包
Logo
图标
PPT
信息图
医学图像
卫星图
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这些图片本来就不一定符合真实世界光影和摄影逻辑。&lt;/p&gt;
&lt;p&gt;如果对二次元图检查&quot;皮肤质感不像真人&quot;，对 UI 截图检查&quot;没有真实阴影&quot;，对游戏截图检查&quot;物理关系不自然&quot;，都可能产生误判。&lt;/p&gt;
&lt;p&gt;所以在视觉和物理分析前，必须先判断图片类型。&lt;/p&gt;
&lt;p&gt;可以简单分成：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;摄影图
写实图
插画
截图
设计图
文档图
游戏图
未知类型
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后决定哪些检测适用，哪些检测需要跳过或降权。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;四、不要只查图片，还要查它表达的事实&lt;/h2&gt;
&lt;p&gt;很多可疑图片的问题，不只是它是不是 AI 生成，而是它是否传达了虚假的信息。&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;伪造名人推文
伪造新闻截图
伪造政府公告
伪造企业声明
伪造聊天记录
伪造金融消息
旧图新传
真实照片配假文字
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这类图片即使不是 AI 生成，也可能是有害的假信息。反过来，一张 AI 生成图也可能只是艺术创作，并不包含虚假事实。&lt;/p&gt;
&lt;p&gt;所以需要区分：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;AI 生成风险
内容真实性风险
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1. 反向搜图：查这张图有没有出现过&lt;/h3&gt;
&lt;p&gt;反向搜图可以帮助判断图片来源。&lt;/p&gt;
&lt;p&gt;可以查：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;图片是否早已存在
是否出现在新闻网站
是否出现在摄影图库
是否出现在 AI 作品平台
是否出现在事实核查网站
是否存在更早版本
是否有不同裁剪版本
是否是旧图新传
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果一张图片出现在 AI 作品平台，并且页面里有 prompt、model、seed、workflow，这就是很强的 AI 来源证据。&lt;/p&gt;
&lt;p&gt;如果一张图片早在很多年前就出现在摄影师作品集中，并且有完整拍摄信息，那么它不太可能是近期 AI 模型生成的。&lt;/p&gt;
&lt;p&gt;但反向搜图也有局限：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;搜不到不代表 AI
搜到相似图不代表是同一张
旧图可能被 AI 编辑过
真实图可能被错误配文传播
搜索引擎覆盖不完整
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以反向搜图是外部来源证据，但不是绝对证据。&lt;/p&gt;
&lt;h3&gt;2. 内容事实核查：查图片里说的事是否真实&lt;/h3&gt;
&lt;p&gt;如果图片中有文字、截图、新闻标题、公告、推文、聊天记录，就应该提取其中的 claim。&lt;/p&gt;
&lt;p&gt;例如一张图片声称：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;某公众人物宣布重大决定
某公司被收购
某政府发布新政策
某明星去世
某金融机构爆雷
某地发生灾害
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;判断时可以查：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;是否有官方声明
是否有原始社交媒体链接
是否有可信媒体报道
是否有事实核查网站辟谣
截图中的账号、时间、平台样式是否一致
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果这是一个重大事件，但完全找不到官方来源或可信新闻，就应该提高内容风险。&lt;/p&gt;
&lt;p&gt;但要注意：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;内容风险高 ≠ AI 风险高
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;真实照片配假新闻标题
→ AI 风险低，内容风险高

伪造推文截图
→ AI 风险中等，内容风险高

AI 生成的虚假灾难现场图
→ AI 风险高，内容风险高

AI 生成的艺术头像
→ AI 风险高，内容风险低
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这一区分很重要。否则系统会把所有&quot;假的东西&quot;都归为 AI 生成，导致判断混乱。&lt;/p&gt;
&lt;h3&gt;3. 平台截图要查原始链接&lt;/h3&gt;
&lt;p&gt;现在很多假信息会伪造成平台截图，比如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;X / Twitter 推文截图
微博截图
小红书截图
新闻网页截图
公告截图
聊天记录截图
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这类图片的判断方法不是单纯看像素，而是要看它有没有原始来源。&lt;/p&gt;
&lt;p&gt;例如推文截图，可以查：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;账号 handle 是否存在
原文是否能搜索到
时间格式是否合理
UI 样式是否匹配
互动数据是否异常
是否有原始链接
是否有官方澄清
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果一张截图声称是重大消息，但没有原始链接，搜不到原帖，也没有可信报道，就应该给出内容真实性风险提示。&lt;/p&gt;
&lt;p&gt;但依然要保守表达：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;未找到原帖或可信来源，图片内容可能存在伪造或误导风险。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而不是直接说：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;这是 AI 生成的。
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;五、统计模型可以用，但不要把分数当真相&lt;/h2&gt;
&lt;p&gt;现在有很多 AI image detector，可以输入图片，输出一个 AI-like score。&lt;/p&gt;
&lt;p&gt;这些检测器可能基于：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;CNN
ViT
CLIP
ConvNeXt
频域特征
扩散模型痕迹
二分类模型
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它们的价值在于，可以捕捉到人眼不容易观察的统计特征。&lt;/p&gt;
&lt;p&gt;比如某些生成模型可能在纹理、频率、噪声分布上留下规律，统计 detector 可能可以学习到这些差异。&lt;/p&gt;
&lt;p&gt;但这类模型有天然局限：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;训练集偏差
新模型泛化不足
压缩后分数变化
截图后分数变化
对二次元和游戏图误伤
对修图照片误伤
黑盒不可解释
不同 detector 结果不一致
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比如一个 detector 可能在 Stable Diffusion 早期图片上效果很好，但对 Midjourney、Flux、Imagen、豆包、通义万相等新模型表现不稳定。另一个 detector 可能把真实的海报设计、游戏截图、二次元图误判成 AI。&lt;/p&gt;
&lt;p&gt;所以 detector 输出的分数不能直接当成概率。&lt;/p&gt;
&lt;p&gt;如果模型输出：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;0.92
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不应该解释为：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;这张图有 92% 概率是 AI。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;更合理的表达是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;该检测模型认为图片具有较强 AI-like 统计特征。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用统计检测器时，最好关注：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;模型训练数据覆盖哪些生成器？
是否支持当前图片类型？
对压缩和截图是否鲁棒？
对真实修图照片误伤率如何？
是否在自己的样本集上测试过？
输出分数是否经过校准？
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果没有自己的验证集，就不要把 detector 结果当作最终裁判。它应该只是综合证据中的一项。&lt;/p&gt;
&lt;p&gt;一个成熟的系统应该允许 detector 输出：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;高风险
中风险
低风险
不适用
检测失败
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而不是强行把所有图片都塞进一个概率。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;六、最终判断：不是给一个武断答案，而是给一份证据报告&lt;/h2&gt;
&lt;p&gt;综合判断时，最重要的是区分证据强度。&lt;/p&gt;
&lt;p&gt;不同证据不能同等看待。&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;可信 C2PA 明确声明 AI 生成
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;和：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;图片看起来有点 AI 味
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不是一个级别。&lt;/p&gt;
&lt;p&gt;可以把证据大致分为三类。&lt;/p&gt;
&lt;h3&gt;强证据&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;verified C2PA 明确声明 AI 生成
可信官方水印被高置信解码
图片在 AI 作品平台找到原始页面，并有 prompt / model / workflow
事实核查来源明确说明该图为 AI 生成
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;强证据可以显著影响结论。&lt;/p&gt;
&lt;h3&gt;中等证据&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;明显视觉异常
明显物理矛盾
多个区域压缩痕迹异常
统计 detector 高分
重大 claim 找不到可信来源
图片在可疑来源中传播
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;中等证据需要组合使用。单独出现时不应该直接下结论。&lt;/p&gt;
&lt;h3&gt;弱证据&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;无 metadata
低置信水印相似信号
轻微压缩异常
图片风格很像 AI
模型输出接近边界
搜索不到外部来源
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;弱证据只能提示风险，不能作为结论依据。&lt;/p&gt;
&lt;p&gt;最后的判断不应该只有：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;AI / Real
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也不应该写成：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;92% AI
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;更好的结果应该包括：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;AI 生成风险
内容真实性风险
置信度
关键证据
反向证据
不确定因素
局限性说明
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;AI 生成风险：中等
内容真实性风险：低
置信度：中等

关键证据：
- 未发现可信 C2PA 来源声明
- 检测到轻微压缩不一致
- 未发现明显视觉语义异常
- 未发现可核查的重大内容 claim

解释：
当前图片存在少量处理痕迹，但没有发现决定性 AI 来源证据。该结果不能证明图片由 AI 生成，也不能证明图片为真人拍摄。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果内容核查发现问题，可以写：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;AI 生成风险：中等
内容真实性风险：高
置信度：中等

关键证据：
- 图片中包含重大公众人物声明
- 未找到对应原帖
- 未找到可信新闻来源支持
- 截图缺少可验证链接

解释：
该图片内容存在较高伪造或误导风险，但这不能单独证明图片由 AI 生成。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这种输出比&quot;AI 概率 87%&quot;更负责任，也更有解释力。&lt;/p&gt;
&lt;p&gt;因为很多时候，真正重要的不是&quot;给一个确定答案&quot;，而是告诉用户：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;我们发现了什么？
这些发现意味着什么？
这些发现不能证明什么？
还有哪些信息不足？
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;结语&lt;/h2&gt;
&lt;p&gt;判断一张图片是否由 AI 生成，最可靠的方式不是依赖单个 detector，而是建立一套多证据分析框架。&lt;/p&gt;
&lt;p&gt;可以按这个顺序思考：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;1. 先看图片是否适合检测
2. 再看来源信息和 metadata
3. 检查水印或平台标记
4. 分析压缩、编辑和传播痕迹
5. 观察视觉语义异常
6. 检查物理与场景一致性
7. 使用统计检测模型作为辅助
8. 做反向搜图和内容事实核查
9. 区分 AI 风险和内容真实性风险
10. 按证据强度综合判断
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每一种方法都有价值，也都有局限。&lt;/p&gt;
&lt;p&gt;缺少 metadata 不代表 AI；没有水印不代表真人；检测到水印相似信号不代表已验证水印；视觉上没有异常不代表不是 AI；内容是假的也不代表图片一定是 AI；统计模型高分也不等于客观概率。&lt;/p&gt;
&lt;p&gt;所以，真正成熟的 AI 图片判断，不是给一个漂亮但武断的百分比，而是给出一份清楚的证据分析：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我们看到了哪些证据？
这些证据有多强？
它们是否可能误判？
哪些检测不适用于当前图片？
最终只能给出怎样的风险判断？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在 AI 内容越来越多的时代，判断图片真假不只是技术问题，也是表达问题。一个负责任的系统应该学会承认不确定性，并把不确定性解释清楚。&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>BemoLens - 一次AI图片检测项目开发复盘</title><link>https://astro-pure.js.org/blog/2026/project/ai-image-authenticity-analysis</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2026/project/ai-image-authenticity-analysis</guid><description>面对不确定性很强的问题，不要急着给答案，要先建立证据系统</description><pubDate>Thu, 07 May 2026 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最开始我只是想做一个很直接的工具：用户上传一张图片，系统告诉他这张图是不是 AI 生成的。&lt;/p&gt;
&lt;p&gt;这个想法听起来很简单。现在 AI 生图这么多，网上到处都是 Midjourney、Stable Diffusion、DALL·E、Flux、豆包、通义万相之类模型生成的图，尤其是最近出的GPT-image-2，有点让社媒陷入黑暗森林的感觉了。很多图看起来已经足够真实，有时候仅靠人眼很难判断。我原本以为，这个项目的核心就是找一个检测算法，或者接一个 API，然后把结果展示出来。&lt;/p&gt;
&lt;p&gt;但真正开始做之后，我很快发现：&lt;strong&gt;AI 图片鉴别不是一个简单的二分类问题。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;它不是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;输入图片 → 输出 AI / 真人
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;更接近：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;输入图片 → 收集多种证据 → 判断每种证据的强弱和可靠性 → 给出风险分析
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也就是说，这个项目真正困难的地方，不是&quot;怎么给一个分数&quot;，而是&quot;这个分数凭什么可信&quot;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;一、项目背景&lt;/h2&gt;
&lt;p&gt;一开始，我给这个项目设想的定位比较直接：做一个检测图片是否为 AI 生成的工具。用户上传图片，系统显示检测结果，最好能给出一个百分比，比如&quot;AI 生成概率 87%&quot;。&lt;/p&gt;
&lt;p&gt;但后来我越来越觉得，这种产品表述其实有问题。&lt;/p&gt;
&lt;p&gt;因为&quot;87% AI 生成&quot;这句话本身就很危险。它会让用户以为这个系统真的知道图片的真实来源。但实际上，大多数检测方法都只是风险信号，不是证明。&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;没有 EXIF，不代表图片是 AI；&lt;/li&gt;
&lt;li&gt;没有 C2PA，不代表图片是真的；&lt;/li&gt;
&lt;li&gt;检测到某种频率模式，不代表一定有水印；&lt;/li&gt;
&lt;li&gt;视觉上没有异常，不代表不是 AI；&lt;/li&gt;
&lt;li&gt;内容是假的，也不代表图片一定是 AI 生成；&lt;/li&gt;
&lt;li&gt;搜不到外部来源，也不能说明图片就是假的；&lt;/li&gt;
&lt;li&gt;某个 detector 给出高分，也不等于那就是客观概率。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我在做这个项目的过程中，逐渐意识到一个更准确的定位应该是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;BemoLens 不是一个&quot;AI 鉴定器&quot;，而是一个&quot;图片真实性分析框架&quot;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;它不是为了给出一个绝对结论，而是为了把一张图片拆开，从多个角度分析它有哪些证据、哪些风险、哪些不确定性。&lt;/p&gt;
&lt;p&gt;所以后来我把项目思路改成了：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;图片真实性分析
AI 生成风险分析
内容真实性风险分析
多证据 Gate 架构
可解释报告
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个转变很重要。它让我不再纠结&quot;怎么一锤定音&quot;，而是开始思考&quot;怎么把证据组织清楚&quot;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;二、为什么不适合商业化&lt;/h2&gt;
&lt;p&gt;开发过程中，我也认真想过：这个项目能不能做成一个产品赚钱？&lt;/p&gt;
&lt;p&gt;后来我的判断是：&lt;strong&gt;现在不适合直接商业化，更适合先开源。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;原因有几个。&lt;/p&gt;
&lt;p&gt;首先，没有训练集，权重计算不客观。现在很多 gate 的权重，比如 &lt;code&gt;3&lt;/code&gt;、&lt;code&gt;4&lt;/code&gt;、&lt;code&gt;8&lt;/code&gt;，以及 &lt;code&gt;aiRisk = 0.75&lt;/code&gt;、&lt;code&gt;0.45&lt;/code&gt; 这类分数，本质上都是启发式规则，也就是说，我们目前的权重、分数、算法是未经过任何数据集检验的，所以它并不准确。它们可以作为产品原型的初始逻辑，但不能宣称具有统计意义。&lt;/p&gt;
&lt;p&gt;如果要真正说&quot;这个系统准确率多少&quot;，就必须有标注数据集，有不同图片类型的测试，有真实照片、AI 图、局部 AI 编辑图、压缩图、截图图、社交平台转存图、二次元图、游戏截图、新闻截图、伪造推文截图等等。没有这些数据，就不能把一个风险分数包装成客观事实。&lt;/p&gt;
&lt;p&gt;其次，检测工具链本身还不成熟。C2PA 是一个很有价值的方向，但覆盖率不高。SynthID、Stable Diffusion invisible watermark、各家平台的隐式水印，也不是一个统一、稳定、公开可查的体系。很多水印检测不是&quot;检测到就一定是真的&quot;，而是&quot;有些频率模式像水印&quot;。&lt;/p&gt;
&lt;p&gt;我自己就遇到一个很典型的例子：我截图了一张推特贴文，通过微信转发后再上传检测，结果系统提示检测到了 Stable Diffusion watermark。这个链路明显不太可能真的保留一个稳定的 SD 水印，更合理的解释是：截图、微信压缩、重编码产生的频率特征让 detector 误检了。&lt;/p&gt;
&lt;p&gt;这让我更明确地意识到：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;水印相似信号不等于已验证水印。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;第三，商业化会带来责任风险。如果一个用户拿你的报告去判断别人是不是造假、是不是 AI 图、是不是散布谣言，那这个结果就会变得很敏感。只要误判，产品信任就会出问题。&lt;/p&gt;
&lt;p&gt;所以，现在更适合的路线不是把它包装成一个收费鉴定器，而是把它开源成一个：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;多证据图片真实性分析框架
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这更诚实，也更有长期价值。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;三、整体架构&lt;/h2&gt;
&lt;p&gt;为了避免&quot;一个模型说了算&quot;，我把整个检测流程设计成 gate-based architecture，也就是多道检测门。&lt;/p&gt;
&lt;p&gt;每一道门负责一种证据。每一道门都输出自己的结果、风险、可靠性和解释。最终由 aggregator 综合这些信号，而不是让某一个 gate 独断最终结论。&lt;/p&gt;
&lt;p&gt;目前设计了八道门：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;Gate 0：输入质量 / 文件可用性
Gate 1：C2PA / Metadata
Gate 2：像素级隐式水印
Gate 3：压缩痕迹 / ELA / 噪声
Gate 4：视觉语义异常
Gate 5：物理与场景一致性
Gate 6：统计模型检测
Gate 7：跨来源 / 内容事实核查
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个架构最重要的好处是：它把一个模糊问题拆成了多个可讨论的问题。&lt;/p&gt;
&lt;p&gt;不是直接问：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;这张图是不是 AI？
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而是分别问：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;这张图有没有可信来源声明？
有没有 C2PA？
有没有官方或平台水印？
有没有压缩和编辑痕迹？
视觉内容有没有明显异常？
物理关系是否自洽？
统计检测模型是否认为它像 AI？
图片里的内容能不能被外部来源验证？
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样做之后，系统就不再是一个黑盒。用户可以看到每一步的依据，也可以看到每一步的局限。这里仍然有一些缺陷：比如，对于一些特定的图片如二次元图片，Gate 3、4、5就不太适用，所以怎么去动态判断门的开关也是一个比较复杂的问题。又比如，对于一些误判效果，怎么去做处理等等。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;四、Gate 0：输入质量&lt;/h3&gt;
&lt;p&gt;Gate 0 看起来不起眼，但实际上很重要。&lt;/p&gt;
&lt;p&gt;很多检测工具一上来就开始分析图片，但不先判断输入本身是否适合分析。这会导致很多误判。&lt;/p&gt;
&lt;p&gt;比如用户上传的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;极低清图片；&lt;/li&gt;
&lt;li&gt;纯 UI 截图；&lt;/li&gt;
&lt;li&gt;二维码；&lt;/li&gt;
&lt;li&gt;图标；&lt;/li&gt;
&lt;li&gt;纯文字截图；&lt;/li&gt;
&lt;li&gt;表情包；&lt;/li&gt;
&lt;li&gt;游戏截图；&lt;/li&gt;
&lt;li&gt;二次元插画；&lt;/li&gt;
&lt;li&gt;海报；&lt;/li&gt;
&lt;li&gt;被微信压缩过很多次的图片。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些图片并不适合所有检测方式。比如 Gate 5 的物理一致性分析，对 UI 截图和二次元图就很容易误伤。Gate 3 的压缩痕迹分析，对微信转发图也容易把平台压缩当成异常。&lt;/p&gt;
&lt;p&gt;所以 Gate 0 应该做输入检查和图片类型路由。&lt;/p&gt;
&lt;p&gt;它至少要判断：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;文件格式
文件大小
图片尺寸
是否过小
是否严重压缩
是否截图
是否可能是 UI / 海报 / 二次元 / 游戏 / 新闻截图
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这一步的意义不是判断 AI，而是告诉后面的 gate：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;哪些检测适合运行
哪些检测应该降权
哪些检测应该跳过
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我后来意识到，一个好系统不是&quot;所有检测都跑一遍&quot;，而是要知道&quot;哪些检测对当前图片不适用&quot;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;五、Gate 1：来源元数据&lt;/h3&gt;
&lt;p&gt;C2PA 是目前比较严肃的图片来源声明标准。简单说，它可以记录图片的来源、编辑历史和签名信息。如果一张图片有可信的 C2PA Content Credentials，并且签名验证通过，那它就是一个很强的来源证据。&lt;/p&gt;
&lt;p&gt;但这里也有很多细节。&lt;/p&gt;
&lt;p&gt;比如我问过一个问题：如果 C2PA 查出来 &lt;code&gt;signed by Google LLC&lt;/code&gt;，这代表什么？&lt;/p&gt;
&lt;p&gt;一开始容易误解成：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;Google 签了，所以是 AI 生成。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但其实不是。&lt;code&gt;signed by Google LLC&lt;/code&gt; 只代表这份 C2PA manifest 是由 Google 的签名凭证签的。真正要看的是里面的 action、digitalSourceType、claimType。&lt;/p&gt;
&lt;p&gt;如果里面写的是 camera capture，那可能反而说明它来自 Pixel Camera 或 Google Photos 的可信拍摄链路。&lt;/p&gt;
&lt;p&gt;如果里面写的是 AI generated、trainedAlgorithmicMedia、created by Gemini 或 Imagen，那才更接近 AI 生成证据。&lt;/p&gt;
&lt;p&gt;所以 Gate 1 的原则是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;签名者是谁 ≠ 最终结论
签名是否 verified 很重要
claim 内容是什么更重要
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C2PA 的强度很高，但覆盖率不高。很多图片没有 C2PA，很多平台转存之后 C2PA 也可能丢失。所以：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;有 verified C2PA 是强证据；
没有 C2PA 不是反证。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个边界非常重要。&lt;/p&gt;
&lt;p&gt;不过C2PA的确是个大杀器，测试 GPT-image-2 原图可以直接测出来。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;六、Gate 2：隐式水印&lt;/h3&gt;
&lt;p&gt;Gate 2 关注的是图片里是否存在隐式水印，比如某些 Stable Diffusion 生态工具曾经使用的 invisible watermark，或者 Google SynthID 这类平台水印。&lt;/p&gt;
&lt;p&gt;这个方向听起来很强，因为如果真的解码出官方水印，那它可以成为很直接的证据。&lt;/p&gt;
&lt;p&gt;但现实问题是：很多水印检测并不稳定。&lt;/p&gt;
&lt;p&gt;比如 SD invisible watermark，如果只是检测到&quot;某些频率模式像水印&quot;，那不应该直接显示：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;SD Invisible Watermark: Detected
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而应该显示：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;疑似 Stable Diffusion 水印相似信号
置信度较低
不能作为决定性证据
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我在项目里逐渐把 Gate 2 的结果从简单的 &lt;code&gt;detected / not_detected&lt;/code&gt; 改成更细的状态：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;verified
probable
suspected
not_detected
unsupported
error
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;只有当它明确解码出已知水印信息，并且置信度足够高，图片也没有经过明显截图、压缩、转存、裁剪时，才可以作为强证据。&lt;/p&gt;
&lt;p&gt;如果只是低置信度频域信号，就只能作为弱证据。&lt;/p&gt;
&lt;p&gt;这件事给我的一个重要启发是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;检测器的输出不能直接等于产品结论。中间必须有信任分层。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3&gt;七、Gate 3：压缩与编辑痕迹&lt;/h3&gt;
&lt;p&gt;Gate 3 主要看图片是否有编辑或重编码痕迹，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;局部重压缩；&lt;/li&gt;
&lt;li&gt;ELA 异常；&lt;/li&gt;
&lt;li&gt;噪声模式不一致；&lt;/li&gt;
&lt;li&gt;不同区域压缩质量不一致；&lt;/li&gt;
&lt;li&gt;局部边缘异常；&lt;/li&gt;
&lt;li&gt;拼接或修图留下的痕迹。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这类检测可以发现一些编辑痕迹，但它的问题也很明显：它不直接证明 AI。&lt;/p&gt;
&lt;p&gt;比如微信压缩、截图、平台重编码、裁剪、转格式，都会制造压缩异常。真实照片被社交平台转发几次后，也可能出现很复杂的噪声和压缩痕迹。&lt;/p&gt;
&lt;p&gt;所以 Gate 3 的定位应该是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;处理痕迹辅助证据
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;AI 生成证据
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比如报告里应该写：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;Moderate compression inconsistencies found — may indicate editing or compositing.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;翻译成中文就是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;发现中等程度的压缩不一致，可能表示图片经过编辑或合成。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意这里是&quot;可能表示编辑或合成&quot;，不是&quot;证明 AI 生成&quot;。&lt;/p&gt;
&lt;p&gt;这个 gate 对局部 AI 编辑也有帮助。比如真实照片经过 AI 扩图、AI 去物体、AI 替换背景，可能会留下区域噪声和压缩不一致。但最终仍然要保守解释。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;八、Gate 4：视觉异常&lt;/h3&gt;
&lt;p&gt;Gate 4 是我讨论得比较多的一道门。它主要使用视觉大模型检查图片内容中是否存在 AI 生成图常见的视觉异常。&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;手指数量或结构异常；&lt;/li&gt;
&lt;li&gt;文字乱码；&lt;/li&gt;
&lt;li&gt;脸部细节异常；&lt;/li&gt;
&lt;li&gt;眼睛、牙齿、耳朵、眼镜、首饰异常；&lt;/li&gt;
&lt;li&gt;物体边缘融合；&lt;/li&gt;
&lt;li&gt;重复纹理；&lt;/li&gt;
&lt;li&gt;反射不一致；&lt;/li&gt;
&lt;li&gt;背景细节不自然。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最开始我试过 Gemini 2.5 Flash，但效果不太好。它有时候太保守，什么都说没问题；有时候又会幻觉式地找一些不可靠异常。后来换到 5.4 nano，效果明显更好。这个体验让我意识到：Gate 4 很吃模型质量。&lt;/p&gt;
&lt;p&gt;但更关键的不是模型名字，而是 prompt 的设计。&lt;/p&gt;
&lt;p&gt;不能问：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;这张图片是不是 AI 生成？
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;应该问：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;不要判断这张图是不是 AI。
不要输出概率。
只检查图片中是否存在具体可见异常。
如果没有明确异常，就返回空 findings。
不要猜测。
不要把低清、压缩、风格化本身当成 AI 证据。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也就是说，视觉模型在 Gate 4 里不是最终裁判，而是一个&quot;视觉异常检查员&quot;。&lt;/p&gt;
&lt;p&gt;它输出的是 findings，比如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;hand_anomaly
text_gibberish
reflection_inconsistency
object_fusion
texture_repetition
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后系统再根据 finding 的数量、严重程度、置信度和图片类型，计算一个 anomalyScore。&lt;/p&gt;
&lt;p&gt;这里我学到一个很重要的 AI 使用原则：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;不要让大模型直接替你做最终判断，要让它输出结构化中间证据。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这比&quot;让模型给概率&quot;可靠得多。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;九、Gate 5：物理一致性&lt;/h3&gt;
&lt;p&gt;Gate 5 和 Gate 4 有些相似，但它关注的不是局部视觉异常，而是全局物理逻辑。&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;光源和阴影是否一致；&lt;/li&gt;
&lt;li&gt;镜子反射是否合理；&lt;/li&gt;
&lt;li&gt;水面倒影是否正确；&lt;/li&gt;
&lt;li&gt;透视线是否冲突；&lt;/li&gt;
&lt;li&gt;物体是否合理接触地面或桌面；&lt;/li&gt;
&lt;li&gt;人是否悬浮；&lt;/li&gt;
&lt;li&gt;物体尺度是否合理；&lt;/li&gt;
&lt;li&gt;遮挡关系是否成立；&lt;/li&gt;
&lt;li&gt;运动模糊方向是否一致。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Gate 4 看的是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;这里有没有 AI 常见破绽？
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Gate 5 看的是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;这个世界是否符合基本物理规律？
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;举个例子：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;一个人手指有 6 根 → Gate 4
一个人脚没接触地面却像站着 → Gate 5
背景文字乱码 → Gate 4
镜子里没有应该出现的人 → Gate 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但 Gate 5 也容易误判。特殊拍摄角度、广角镜头、复杂光源、舞台灯光、低清图片，都可能让模型误以为物理关系有问题。&lt;/p&gt;
&lt;p&gt;所以 Gate 5 的权重不能太高，也不能 fast path。它适合作为中等强度辅助证据，和 Gate 4 互补。&lt;/p&gt;
&lt;p&gt;如果 Gate 4 和 Gate 5 都发现明显异常，同时 Gate 6 统计模型也给高风险，那么最终 AI Risk 可以升高。但如果只有 Gate 5 一个 warn，就不能直接说这图是 AI。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;十、Gate 6：统计模型&lt;/h3&gt;
&lt;p&gt;Gate 6 是我一开始以为最像&quot;AI detector&quot;的部分。它接入专门的 AI image detector，比如 Hugging Face 上的一些 ViT / CLIP 二分类模型，或者第三方 API，例如 Sightengine 这类服务。&lt;/p&gt;
&lt;p&gt;但讨论下来，我反而觉得 Gate 6 要更谨慎。&lt;/p&gt;
&lt;p&gt;统计模型的问题是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;黑盒；&lt;/li&gt;
&lt;li&gt;训练集偏差；&lt;/li&gt;
&lt;li&gt;对新生成模型泛化不一定好；&lt;/li&gt;
&lt;li&gt;容易受压缩、截图、转格式影响；&lt;/li&gt;
&lt;li&gt;对二次元、游戏、UI、海报等非摄影图可能误伤；&lt;/li&gt;
&lt;li&gt;API 成本不低；&lt;/li&gt;
&lt;li&gt;本地部署可能有内存和工程复杂度。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以 Gate 6 的定位应该是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;统计模型辅助证据
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;最终 AI 概率
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果某个 detector 输出 0.92，也不应该展示成&quot;92% AI-generated&quot;。更好的文案是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;统计模型发现了较强的 AI-like 特征。
该信号不能单独证明图片为 AI 生成。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我也思考过本地跑 detector 会不会内存爆炸。结论是：如果是轻量 CLIP / ViT detector，单张图、固定尺寸推理，一般还可以。但不要在前端浏览器或 Next.js 主进程里直接跑大模型。更合理的是单独做一个 detector service：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;Next.js App
  ↓
Gate Orchestrator
  ↓
Python / FastAPI Detector Service
  ↓
ONNX Runtime / PyTorch
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;并且：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;模型启动时加载一次
图片 resize 到 224 / 384 / 512
单张图推理
失败就 skip
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个 gate 更像未来扩展点，不应该是 MVP 的唯一核心。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;十一、Gate 7：跨来源核查&lt;/h3&gt;
&lt;p&gt;Gate 7 是我后来觉得特别有价值的一道门。&lt;/p&gt;
&lt;p&gt;前面几个 gate 都在看图片本身，但 Gate 7 看的是图片在外部世界里是否站得住。&lt;/p&gt;
&lt;p&gt;一开始我只想到反向搜图，也就是查这张图有没有在网上出现过。比如用 Google Lens、TinEye、Yandex、百度识图，看它是不是来自 AI 作品站、新闻网站、摄影图库、社交媒体、事实核查网站。&lt;/p&gt;
&lt;p&gt;但后来我意识到，Gate 7 不应该只做反向搜图，还应该做内容事实核查。&lt;/p&gt;
&lt;p&gt;因为现在很多假图不是单纯&quot;图片像 AI&quot;，而是图片里传达了一个假的事实。&lt;/p&gt;
&lt;p&gt;比如一张伪造的推文截图，上面写着：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;Trump announces acquisition of X
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这张图可能是 AI 生成的，也可能是 PS 的，也可能是网页模板伪造的。此时反向搜图不一定有结果，但我们可以提取图片里的 claim，然后去查：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;有没有原始 X 帖子？
有没有官方账号发布？
有没有可信新闻报道？
有没有事实核查网站辟谣？
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这就是 Gate 7B：Content Claim Verification。&lt;/p&gt;
&lt;p&gt;它的核心流程是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;输入图片
→ OCR / 视觉模型提取文字和 claim
→ 识别涉及人物、机构、平台、时间
→ 生成搜索 query
→ 搜索官方来源、新闻源、事实核查源
→ 判断 claim 是 verified、contradicted、not_found 还是 inconclusive
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里有一个很重要的产品区分：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;内容真实性风险 ≠ AI 生成风险
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一张图片内容是假的，不代表图片一定是 AI。它可能是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PS 改图；&lt;/li&gt;
&lt;li&gt;网页截图伪造；&lt;/li&gt;
&lt;li&gt;手动编辑；&lt;/li&gt;
&lt;li&gt;模板生成；&lt;/li&gt;
&lt;li&gt;AI 生成；&lt;/li&gt;
&lt;li&gt;真实照片配了错误文字。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以最终报告应该分成两个指标：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;AI Generation Risk
Content Authenticity Risk
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;真实照片 + 假配文 → AI Risk 低/中，Content Risk 高
AI 生成风景图，无虚假 claim → AI Risk 高，Content Risk 低
伪造推文截图 → AI Risk 中，Content Risk 高
AI 生成假新闻图 → AI Risk 高，Content Risk 高
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个分离让我觉得项目成熟了很多，因为它不再把所有问题都塞进&quot;是不是 AI&quot;这个筐里。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;十二、评分系统&lt;/h2&gt;
&lt;p&gt;项目里不可避免会有分数。每个 gate 需要输出 aiRisk、reliability、weight，最后 aggregator 计算最终风险。&lt;/p&gt;
&lt;p&gt;但我越来越觉得，要非常小心地对待这些数字。&lt;/p&gt;
&lt;p&gt;比如 Gate 4 的异常分数、Gate 5 的物理一致性分数、Gate 6 的 modelScore，都不应该被直接当成最终概率。它们只是内部风险信号。&lt;/p&gt;
&lt;p&gt;一个比较合理的思路是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;effectiveWeight = weight * reliability
weightedRisk = aiRisk * effectiveWeight

finalScore =
  sum(aiRisk * weight * reliability) /
  sum(weight * reliability)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但这里的 weight 和阈值都应该写清楚：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;这是启发式参数
不是统计学真理
后续需要通过标注数据集校准
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在可以先用经验权重：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;Gate 1 C2PA：强证据，可 fast path
Gate 2 可信水印：条件强证据
Gate 3 压缩痕迹：weight 3
Gate 4 视觉语义：weight 4
Gate 5 物理一致性：weight 4
Gate 6 统计模型：weight 3
Gate 7 跨来源核查：weight 8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但这些只是初始方案。真正要客观，必须有评估集。&lt;/p&gt;
&lt;p&gt;所以这个项目如果开源，就应该明确：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;我们提供 scoring framework
但不声称默认权重已经经过充分校准
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这比闭源产品直接给一个神秘分数更诚实。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;十三、报告导出&lt;/h2&gt;
&lt;p&gt;我还设计了 Report 详情页导出 PDF 的功能。&lt;/p&gt;
&lt;p&gt;一开始我以为就是把页面导出成 PDF。后来想清楚了，这不应该是页面截图，而应该是一份正式的图片真实性分析报告。&lt;/p&gt;
&lt;p&gt;报告应该包含：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;封面
总结论
AI Risk
Content Risk
Confidence
图片信息
文件 hash
八道 gate 结果
证据摘要
隐私说明
局限性说明
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;尤其要有局限性说明：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;本报告提供的是风险信号，并非绝对鉴定结论。
缺少 metadata 或水印，并不能证明图片一定为真人拍摄。
类似水印的信号可能受到截图、压缩、缩放、重编码影响。
视觉和物理一致性检查对插画、UI 截图、游戏图、海报、表情包、低清图可靠性较低。
统计检测模型可能受到训练集偏差、压缩处理和新生成模型影响。
跨来源核查受搜索覆盖、语言、地区和时间敏感性影响。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这份报告的目的不是吓唬用户，而是让用户知道：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;系统发现了什么
哪些是强证据
哪些是弱信号
哪些 gate 没有执行
为什么不能直接下绝对结论
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这也和开源定位一致：证据导向，而不是结论导向。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;十四、开发反思与后续&lt;/h2&gt;
&lt;p&gt;这次开发让我对 AI 辅助编程有了更具体的感受。AI 最有用的地方不是替我写几行代码，而是帮我把混乱的问题结构化——先写 spec，再拆 task，再生成文档，再指导实现。这其实是一种 spec-driven development。这次的搭配是使用雷总送的 xiaomi mimo 2.5 pro，接入 hermes 来写代码，使用 gpt-5.5 来写task、plan 等文档。不过 mimo 的 credits 烧得有点快，7亿给我嚯嚯得只剩2亿了&lt;/p&gt;
&lt;p&gt;另外几个比较重要的原则：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;不要问 AI &quot;这张图是不是 AI&quot;，要让它输出结构化中间证据。&lt;/strong&gt; 让模型做&quot;结构化观察&quot;而不是&quot;最终裁判&quot;，这比让它直接给概率可靠得多。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;追问数字的来源。&lt;/strong&gt; AI 给的权重、阈值、分数如果没有数据支撑，就只是启发式参数。追问&quot;凭什么&quot;，才能防止项目变成空心参数堆砌。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;让 AI 做反方提醒。&lt;/strong&gt; 当我想把某个信号作为强证据时，AI 会提醒可能误检、不能 fast path、需要降权。不一定全对，但很适合当讨论对象。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;关于项目方向，我没有选择把它包装成收费鉴定器，而是决定开源。因为这个项目最有价值的地方不是某个检测算法，而是它的框架——多证据 gate 架构、可解释输出、AI Risk / Content Risk 分离、报告导出、免责声明和局限性。这些东西适合开源共建，而非闭源包装。另外它的功能不支持它作为一个付费的 Saas，权重算法不准确、gate动态化未实现、未经过数据集检测、detecter工具较少等等都是比较大的问题。&lt;/p&gt;
&lt;p&gt;下一步的重点不是盲目增加检测功能，而是整理仓库结构、稳定 gate 接口、标注实验性功能、定义评估数据格式，以及完善 UI 和报告（便于未来继续开发&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;面对不确定性很强的问题，不要急着给答案，要先建立证据系统。BemoLens 不试图证明一张图一定真假——它试图告诉你：我们看到了哪些证据，这些证据有多强、有多可靠，又有哪些地方不能下结论。在 AI 内容越来越多的时代，承认不确定性，也许比给出一个漂亮但虚假的百分比更重要。&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>为什么中年人热衷于佛而年轻人却更向往道</title><link>https://astro-pure.js.org/blog/2026/ai-generate/%E4%B8%AD%E5%B9%B4%E4%BD%9B%E4%B8%8E%E9%9D%92%E5%B9%B4%E9%81%93</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2026/ai-generate/%E4%B8%AD%E5%B9%B4%E4%BD%9B%E4%B8%8E%E9%9D%92%E5%B9%B4%E9%81%93</guid><description>佛是中年人的止损系统，道是年轻人的逃逸系统</description><pubDate>Thu, 07 May 2026 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;这几年有一个很有意思的现象：中年人谈精神寄托，常常绕不开&quot;佛&quot;；年轻人谈精神状态，却越来越爱说&quot;道&quot;。前者说&quot;放下&quot;&quot;因果&quot;&quot;随缘&quot;，后者说&quot;松弛感&quot;&quot;无为&quot;&quot;顺其自然&quot;。表面看，这是佛教和道教的代际差异；往深处看，其实是两代人在不同人生阶段里，对压力、命运和自我关系的不同处理方式。&lt;/p&gt;
&lt;p&gt;先说一个前提：严格从统计上讲，不能简单说&quot;中年人都信佛，年轻人都信道&quot;。中国人的宗教实践本来就不是西方意义上那种清晰的教会归属。Pew Research Center 在 2023 年关于中国宗教测量的报告里就提到，许多中国成年人有宗教性实践或神灵信念，但正式认同某一宗教的人比例并不高；2018 年 CGSS 中，自称佛教信仰者的中国成年人约为 4%。所以这里说的&quot;佛&quot;和&quot;道&quot;，更多不是严格的宗教身份，而是一种生活情绪、一套解释方式、一种精神姿态。&lt;/p&gt;
&lt;p&gt;中年人为什么更容易靠近佛？因为中年人的问题，往往不是&quot;我要成为什么&quot;，而是&quot;我怎么接受已经发生的一切&quot;。到了中年，人会被现实反复教育：事业不一定按计划上升，家庭不一定完全和睦，父母开始老去，身体开始报警，年轻时相信的很多&quot;只要努力就能改变&quot;也慢慢变得不那么可靠。这个阶段的人最需要的不是再来一套宏大的理想，而是一种能让自己继续过下去的心理缓冲。&lt;/p&gt;
&lt;p&gt;佛教在大众语境里，最常被提取出来的部分，正好是&quot;苦&quot;&quot;无常&quot;&quot;放下&quot;&quot;因果&quot;。它很适合处理中年人的失落感。中年人不是没有欲望，而是欲望撞过墙；不是不想赢，而是知道很多事已经赢不了。于是&quot;放下&quot;就变得有吸引力。它不是纯粹的消极，而是一种把内心从失败、遗憾、执念里解救出来的方法。&lt;/p&gt;
&lt;p&gt;这也是为什么很多中年人说佛，不一定真的在研究佛学经典。他们要的往往不是宗教体系，而是情绪止痛药。孩子不听话，工作被边缘化，婚姻进入疲惫期，财富增长变慢，身体机能下降，这些事情都很难靠鸡血解决。佛教式语言的好处是，它不直接鼓励你继续冲，而是告诉你：人生本来如此，执着才是痛苦之源。对一个已经被现实磨过的人来说，这句话很有安慰性。&lt;/p&gt;
&lt;p&gt;而年轻人为什么更向往道？因为年轻人的核心焦虑不是&quot;如何接受过去&quot;，而是&quot;如何面对不确定的未来&quot;。年轻人还没完全进入稳定结构，却已经提前感受到结构的挤压：就业竞争、学历焦虑、房价压力、亲密关系不确定、社会评价体系过密。此时他们很难真正&quot;放下&quot;，因为他们还没拿到过，也还没输完。他们更需要的是一种让自己暂时逃离标准答案的思想。&lt;/p&gt;
&lt;p&gt;道家恰好提供了这种想象。&quot;道法自然&quot;&quot;无为而无不为&quot;&quot;上善若水&quot;&quot;逍遥游&quot;，这些词放在今天的年轻人语境里，很容易被翻译成：别太拧巴，别太内耗，别什么都按绩效表来活。年轻人向往的&quot;道&quot;，很多时候不是传统宗教意义上的道教，而是老庄式的精神出口。它不像佛那样强调苦与放下，而是提供一种更轻盈的反抗：我不一定要按照你们定义的成功去活。&lt;/p&gt;
&lt;p&gt;所以，佛更像是中年人的&quot;止损系统&quot;，道更像是年轻人的&quot;逃逸系统&quot;。中年人靠佛，把伤口包起来；年轻人靠道，把自己从规则里松一松。&lt;/p&gt;
&lt;p&gt;还有一个重要原因：佛在大众社会里更像&quot;秩序&quot;，道在年轻文化里更像&quot;自由&quot;。佛教寺庙、上香、祈福、供奉、功德，这些形式在中国社会里非常成熟，也更容易和家庭、长辈、传统礼俗结合在一起。中年人去寺庙，往往带着很具体的愿望：求平安、求孩子、求健康、求事业稳定。Pew 的报告也提到，中国宗教实践里，&quot;烧香&quot;&quot;许愿&quot;等活动常常并不意味着严格宗教身份，而是一种广泛存在的民间宗教文化行为。&lt;/p&gt;
&lt;p&gt;年轻人也去寺庙，但他们的方式变了。近几年，&quot;寺庙旅游&quot;&quot;上香热&quot;&quot;玄学消费&quot;在年轻人中流行起来，有研究用社交平台文本分析指出，寺庙旅游已成为年轻人解压方式之一。但年轻人去寺庙，未必是进入传统宗教秩序，更多像是一种情绪消费：我太累了，我去拜一拜；我太焦虑了，我求个签；我不知道未来怎么办，我找一个象征性的确定感。&lt;/p&gt;
&lt;p&gt;相比之下，&quot;道&quot;在互联网上更容易被年轻人重新包装。它可以是国风，可以是玄学，可以是养生，可以是修仙，可以是&quot;发疯文学&quot;的反面，也可以是&quot;松弛感&quot;的哲学解释。道教和道家元素本来就带有自然、山林、隐逸、修炼、逍遥、神秘感，这些东西非常适合短视频、游戏、二次元、国潮视觉和赛博玄学传播。也就是说，道在年轻人那里不只是信仰，更是一种审美。&lt;/p&gt;
&lt;p&gt;这也是&quot;向往道&quot;和&quot;信道&quot;之间的区别。很多年轻人并不会真的成为道教徒，但他们会喜欢道家的语言、道教的符号、修仙的想象、山中隐士的生活方式。因为这些东西共同构成了一种对现代生活的反命题：现代社会要求你可量化、可评估、可优化；而&quot;道&quot;的想象告诉你，人也可以不可控、不可算、不可被完全规训。&lt;/p&gt;
&lt;p&gt;中年人热衷佛，背后是对命运的和解；年轻人向往道，背后是对规训的躲闪。一个是&quot;算了&quot;，一个是&quot;别管我&quot;。一个说&quot;放下执念&quot;，一个说&quot;顺其自然&quot;。听起来相似，但心理结构不一样。&lt;/p&gt;
&lt;p&gt;佛的语言更适合失败之后，道的语言更适合出发之前。中年人经历过很多具体的丧失，所以他们需要解释&quot;为什么我得不到&quot;。佛教给出的解释是：万事无常，诸行皆苦，不必执着。年轻人面对的是大量尚未发生但已经压过来的可能性，所以他们需要解释&quot;为什么我不想这样活&quot;。道家给出的解释是：不必强求，顺势而行，天地有自己的节奏。&lt;/p&gt;
&lt;p&gt;当然，这里面也有商业和媒介的推动。近年&quot;玄学经济&quot;&quot;寺庙经济&quot;&quot;文创手串&quot;&quot;算命酒吧&quot;等现象不断被媒体报道，年轻人对玄学、求签、传统占卜的兴趣，与经济不确定、就业焦虑和情绪压力有关。The Guardian 2025 年关于中国年轻人与玄学消费的报道就把这种现象称为一种&quot;spiritual economy&quot;，指出一些年轻人在未来焦虑中转向求签、占卜等传统或现代混合的精神消费。这说明年轻人不是突然变&quot;迷信&quot;了，而是在高压环境里寻找低成本的心理出口。&lt;/p&gt;
&lt;p&gt;但我觉得，真正值得注意的不是&quot;年轻人迷上玄学&quot;，而是他们为什么需要玄学。因为现实社会给年轻人的确定性太少了。过去那套路径——好好读书、找个好工作、买房、结婚、生子、稳定上升——仍然在被要求，却不再稳定兑现。人在理性系统里得不到答案，就会去象征系统里找答案。抽签、看命盘、转运、拜神，本质上是在给不可控的生活加一个可解释的框架。&lt;/p&gt;
&lt;p&gt;中年人也是如此。中年人念佛、拜佛、谈因果，也未必是因为他们真的完成了宗教皈依，而是因为生活需要一个能承受苦难的容器。人总要把痛苦放进某个解释里，否则痛苦就是纯粹的混乱。佛给中年人的，是&quot;苦有其因，苦可以被看破&quot;；道给年轻人的，是&quot;路不止一条，人不必总是硬撑&quot;。&lt;/p&gt;
&lt;p&gt;所以这篇文章的标题，其实可以再改得准确一点：不是中年人热衷于佛，年轻人向往于道，而是中年人更需要佛的安顿，年轻人更需要道的松绑。&lt;/p&gt;
&lt;p&gt;佛解决的是&quot;我如何接受人生的不圆满&quot;。道解决的是&quot;我如何不被别人的圆满标准绑架&quot;。&lt;/p&gt;
&lt;p&gt;前者对应的是创伤之后的修复，后者对应的是压力之中的逃逸。前者是把心收回来，后者是把人放出去。前者适合夜深人静时承认自己累了，后者适合白天被规则追赶时提醒自己别太像机器。&lt;/p&gt;
&lt;p&gt;从这个角度看，佛和道并不是两代人的对立，而是人生不同阶段的两种药。年轻时，人需要一点道，才能不被社会驯化得太早；中年后，人需要一点佛，才能不被生活折磨得太久。一个人若只有佛，可能太早认命；若只有道，可能总想逃走。真正成熟的状态，也许是既能道法自然地活，又能在无常面前放下。&lt;/p&gt;
&lt;p&gt;年轻人向往道，是因为还想保住一点自由；中年人热衷佛，是因为终于明白很多自由也有代价。一个人在二十岁说&quot;顺其自然&quot;，常常是在反抗；一个人在四十岁说&quot;随缘&quot;，常常是在疗伤。&lt;/p&gt;
&lt;p&gt;这不是谁更高级的问题，而是谁更需要哪一种安慰的问题。佛与道在今天重新流行，恰恰说明现代人并没有因为科技、消费和效率而变得更安定。相反，越是被算法、绩效、房贷、学历、年龄和社会评价包围，人就越需要古老的语言来保存一点内心空间。&lt;/p&gt;
&lt;p&gt;中年人去佛那里学会放过自己，年轻人去道那里想象另一种活法。说到底，大家求的都不是神秘，而是喘息。&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>为什么我认为信 AI 的人都应该信佛</title><link>https://astro-pure.js.org/blog/2026/ai-generate/%E4%BF%A1ai%E8%80%85%E5%BA%94%E4%BF%A1%E4%BD%9B</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2026/ai-generate/%E4%BF%A1ai%E8%80%85%E5%BA%94%E4%BF%A1%E4%BD%9B</guid><description>AI 负责向外扩展，佛教负责向内看</description><pubDate>Thu, 07 May 2026 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;这里的&quot;信佛&quot;，不是指一定要皈依、烧香、参加仪式。&lt;/p&gt;
&lt;p&gt;我想说的是：越相信 AI 的人，越需要一点佛教式的清醒。&lt;/p&gt;
&lt;p&gt;AI 很强。它能写文章、做方案、改代码、生成图片，也能在你迷茫的时候给出一套看起来很完整的回答。&lt;/p&gt;
&lt;p&gt;但正因为它太强，也太顺滑，所以它很容易让人产生幻觉。&lt;/p&gt;
&lt;p&gt;你会觉得：它说得这么完整，应该是对的。&lt;/p&gt;
&lt;p&gt;你会觉得：它帮我生成了结果，我好像就掌握了能力。&lt;/p&gt;
&lt;p&gt;你会觉得：我把问题交给它，问题就已经被解决了。&lt;/p&gt;
&lt;p&gt;但很多时候，这只是表象。&lt;/p&gt;
&lt;p&gt;佛教里讲不要执着于&quot;相&quot;。这个提醒放在 AI 时代特别合适。&lt;/p&gt;
&lt;p&gt;一段流畅的文字，不等于真实理解。&lt;/p&gt;
&lt;p&gt;一个完整的方案，不等于真正可行。&lt;/p&gt;
&lt;p&gt;一个漂亮的结果，不等于你真的拥有能力。&lt;/p&gt;
&lt;p&gt;AI 会制造很多&quot;像真的一样&quot;的东西，而人最容易被&quot;像真的&quot;骗住。&lt;/p&gt;
&lt;p&gt;所以信 AI 的人，更应该学会停下来问一句：&lt;/p&gt;
&lt;p&gt;这是真的，还是只是看起来合理？&lt;/p&gt;
&lt;p&gt;我是在学习，还是在偷懒？&lt;/p&gt;
&lt;p&gt;我是在解决问题，还是在逃避思考？&lt;/p&gt;
&lt;p&gt;AI 能提高效率，但它不能替你觉察自己。&lt;/p&gt;
&lt;p&gt;它能给答案，但不能替你承担判断。&lt;/p&gt;
&lt;p&gt;它能放大能力，也会放大欲望。&lt;/p&gt;
&lt;p&gt;贪快的人，会用 AI 变得更贪快。&lt;/p&gt;
&lt;p&gt;怕思考的人，会用 AI 更加逃避思考。&lt;/p&gt;
&lt;p&gt;焦虑的人，会不停向 AI 要确定感。&lt;/p&gt;
&lt;p&gt;喜欢包装自己的人，会用 AI 做出一个更像样的自己。&lt;/p&gt;
&lt;p&gt;所以问题从来不只是 AI 好不好，而是你带着什么心在用它。&lt;/p&gt;
&lt;p&gt;佛教有一个价值，就是让人看见自己的心。&lt;/p&gt;
&lt;p&gt;看见自己的贪、懒、怕、急。&lt;/p&gt;
&lt;p&gt;看见自己为什么想要一个答案。&lt;/p&gt;
&lt;p&gt;看见自己为什么急着生成结果。&lt;/p&gt;
&lt;p&gt;看见自己为什么害怕落后。&lt;/p&gt;
&lt;p&gt;这对 AI 时代的人很重要。&lt;/p&gt;
&lt;p&gt;因为 AI 会让一切变快，而人越快，越容易看不清自己。&lt;/p&gt;
&lt;p&gt;所以我认为，信 AI 的人都应该信佛。&lt;/p&gt;
&lt;p&gt;不是因为佛教反技术，也不是因为 AI 不可靠。&lt;/p&gt;
&lt;p&gt;而是因为 AI 负责把人的能力向外扩展，佛教负责提醒人向内看。&lt;/p&gt;
&lt;p&gt;一个让你更快。&lt;/p&gt;
&lt;p&gt;一个让你别迷路。&lt;/p&gt;
&lt;p&gt;信 AI，是相信工具能帮人做更多事。&lt;/p&gt;
&lt;p&gt;信佛，是提醒自己不要把工具当成智慧。&lt;/p&gt;
&lt;p&gt;AI 可以用。&lt;/p&gt;
&lt;p&gt;但不要迷信它。&lt;/p&gt;
&lt;p&gt;效率可以追求。&lt;/p&gt;
&lt;p&gt;但不要把效率当人生。&lt;/p&gt;
&lt;p&gt;答案可以生成。&lt;/p&gt;
&lt;p&gt;但判断必须留在自己手里。&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>为什么我认为信佛的人都应该信 AI</title><link>https://astro-pure.js.org/blog/2026/ai-generate/%E4%BF%A1%E4%BD%9B%E8%80%85%E5%BA%94%E4%BF%A1ai</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2026/ai-generate/%E4%BF%A1%E4%BD%9B%E8%80%85%E5%BA%94%E4%BF%A1ai</guid><description>AI 不是佛，它只是这个时代的一面镜子</description><pubDate>Thu, 07 May 2026 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;这个标题容易让人误会。&lt;/p&gt;
&lt;p&gt;我说的&quot;信 AI&quot;，不是把 AI 当成神，也不是认为 AI 有灵魂、有觉悟。AI 只是工具，甚至是很不稳定、很容易出错的工具。&lt;/p&gt;
&lt;p&gt;但我觉得，信佛的人反而应该认真看待 AI。&lt;/p&gt;
&lt;p&gt;原因很简单：AI 这个东西，特别适合拿来理解佛教里一些原本很抽象的概念。&lt;/p&gt;
&lt;p&gt;比如&quot;无我&quot;。&lt;/p&gt;
&lt;p&gt;我们以前总觉得，写作、判断、表达、灵感，这些东西很能代表一个&quot;我&quot;。但 AI 出现后，你会发现很多所谓的个人表达，其实是语言、经验、素材和习惯的组合。&lt;/p&gt;
&lt;p&gt;它能模仿一种文风，能整理一种观点，能生成一套看似完整的逻辑。于是人会被迫重新思考：到底什么才是&quot;我&quot;？我的独特性在哪里？我执着的那些能力，是不是也没那么牢固？&lt;/p&gt;
&lt;p&gt;这不是说人没有价值，而是说，人没必要把价值建立在某种固定的自我想象上。&lt;/p&gt;
&lt;p&gt;再比如&quot;缘起&quot;。&lt;/p&gt;
&lt;p&gt;AI 不是凭空出现的。它背后有数据、算法、算力、工程、资本、用户反馈，还有整个人类社会长期积累下来的语言和知识。&lt;/p&gt;
&lt;p&gt;你问它一句话，它回你一段文字。表面上是 AI 在回答，实际上是无数条件共同作用的结果。&lt;/p&gt;
&lt;p&gt;这很像佛教说的缘起：没有孤立存在的东西，所有事物都是条件聚合的结果。&lt;/p&gt;
&lt;p&gt;所以 AI 本身就是一个很典型的缘起现象。&lt;/p&gt;
&lt;p&gt;还有&quot;观照&quot;。&lt;/p&gt;
&lt;p&gt;很多时候，我们脑子里的想法是混乱的。焦虑、欲望、判断、恐惧混在一起，自己也说不清。&lt;/p&gt;
&lt;p&gt;但你把问题写给 AI，它会帮你整理、复述、分类。这个过程中，你不一定得到了正确答案，但你可能更清楚地看见了自己在想什么。&lt;/p&gt;
&lt;p&gt;这就有点像一面镜子。&lt;/p&gt;
&lt;p&gt;AI 不会替你修行，也不会替你觉悟。但它可以帮助你把一些模糊的念头摊开，让你看见自己的执念、逃避和矛盾。&lt;/p&gt;
&lt;p&gt;当然，AI 也会放大人的问题。&lt;/p&gt;
&lt;p&gt;懒的人会用它逃避思考，贪的人会用它快速包装自己，焦虑的人会不停向它索要确定答案。&lt;/p&gt;
&lt;p&gt;它不自动让人变好。它只是加速人原本的倾向。&lt;/p&gt;
&lt;p&gt;所以真正重要的不是&quot;AI 好不好&quot;，而是你用它的时候，自己是什么状态。&lt;/p&gt;
&lt;p&gt;如果一个人带着贪念用 AI，它就是贪念的工具。&lt;/p&gt;
&lt;p&gt;如果一个人带着觉察用 AI，它也可以成为觉察的工具。&lt;/p&gt;
&lt;p&gt;这也是我说&quot;信佛的人应该信 AI&quot;的意思。&lt;/p&gt;
&lt;p&gt;不是迷信 AI，而是相信它值得被认真观察。&lt;/p&gt;
&lt;p&gt;佛教讲无常。AI 正是这个时代最明显的无常之一。世界的知识生产方式、学习方式、工作方式都在变化。一个真正理解无常的人，不应该只是守着过去的经验不放。&lt;/p&gt;
&lt;p&gt;信佛不是躲开变化，而是在变化里保持清醒。&lt;/p&gt;
&lt;p&gt;所以，AI 不是佛。&lt;/p&gt;
&lt;p&gt;它只是这个时代的一面镜子。&lt;/p&gt;
&lt;p&gt;信佛的人不必崇拜它，也不必恐惧它。&lt;/p&gt;
&lt;p&gt;靠近它，观察它，使用它，同时警惕它。&lt;/p&gt;
&lt;p&gt;这可能比简单地排斥它，更接近一种修行。&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>一期一会 · 2025</title><link>https://astro-pure.js.org/blog/2026/2025%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2026/2025%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93</guid><description>献给我的20岁</description><pubDate>Wed, 04 Mar 2026 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;今年是开始写年度总结的第二年，去年的总结更像是“记录”而不是“复盘”，而且感觉挺中二的，回看有点尴尬。所以这一篇希望能思考和输出更多有价值的东西：一方面是想将目前的一些观点积淀下来，以待将来推导、推翻、重组；另一方面是想做一些展望，并留存一些观察、困惑、矛盾和一些尚未成型的思考。&lt;/p&gt;
&lt;p&gt;另外，今年满了20岁。孔子说他十五志于学，三十而不惑，但是没说20岁要干啥和干了啥。但对于大部分中国大学生来说，这一年保研党要准备冲一冲更好的保研学校、考研党要开始重新嚼高数课本、工作党得开始实习了——好忙！颇有一种坐地不得不日行八万里的感觉。&lt;/p&gt;
&lt;h2&gt;从AI和Coder的孽缘讲起&lt;/h2&gt;
&lt;p&gt;年末 Gemini 3.0 出来的时候，前端又死了一下。&lt;/p&gt;
&lt;p&gt;这也不是稀奇的事了，好像每次发布新的模型前端都会死一下。&lt;/p&gt;
&lt;p&gt;当然这是调侃的话，前端还没到“已”死的地步，不过大家实际上担心的应该是“前端是否会死”和“如果前端要死，还有多久死”的问题。如果做更进一步的思考，问题是“AI是否会颠覆科技行业”。&lt;/p&gt;
&lt;p&gt;推特上的一个观点是：AI的持续发展，意味着“智能”或者说“学识”变得廉价了：比如原先企业招人，老板们可能中意于“在xx领域工作了n年”、“对xx精通”的一些人，现在来说，AI或多或少地降低了他们的”价值“，从成本角度看，找一个更年轻的+会使用AI的员工可能会更省钱，因为老板只需要像交水电费一样交一点Token费用来买点算力就行。&lt;/p&gt;
&lt;p&gt;当然这并不足以说明AI会”颠覆“科技行业，上面的这个观点只说明知识贬值，但是“经验”应该不会贬值，例如老员工跟后端产品设计打交道的经验，对项目架构的心智模型经验；新一代coder对AI应用技巧的经验，对新技术的经验（当然这两个例子有点刻板印象，切莫当真）&lt;/p&gt;
&lt;p&gt;另外，之前在一个介绍AI的”等级制度“的视频里，UP主将目前的AI分了几个等级：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;L1：聊天机器人，能交流但是有幻觉，无法主动执行某个任务，类似于shell的 REPL 模型&lt;/li&gt;
&lt;li&gt;L2：推理者，有思考的能力，能进行一定程度的推理，从”概率预测“到”逻辑推导“&lt;/li&gt;
&lt;li&gt;L3：Agents智能体，我们现在用的模型大部分都在这个阶段，比如最近爆火的 OpenClaw&lt;/li&gt;
&lt;li&gt;L4：创新者/研究员，这类AI能辅助发现新知识，可以通过自主实验对知识库做”增量“操作&lt;/li&gt;
&lt;li&gt;L5：协作体/研究院，一堆研究员/员工互相协作，完全可以实现自主产出，人只需要当”大老板“&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;目前AI应该是L2到L3的跨越期，非常之接近于L3了。所以目前的AI做的活，基本是在知识库中做”存量“操作，既然是这样，我们或许不用太担心智械危机。&lt;/p&gt;
&lt;p&gt;但是我们能注意到AI发展速度非常之快，我记得应该是我刚上大学时，GPT3.5出现了，那时候还是L1的阶段。现在也很快，我们开头说的Gemini3在25年年末出现，前几天刚升级了它的Banana生图。到L3的阶段指日可待。&lt;/p&gt;
&lt;p&gt;那既然人还有“经验”的优势，且仍然作为使用者；AI还没到主动做“增量”的阶段，且仍然作为工具。那么从小的来说，对于我们这些Coder，转型是无法避免的。从大的来说，科技行业不会萎靡，只会越来越壮大，或许其中的某些职业会消失、某些职业会变形、某些职业会门槛更高······至于其中有多少“人的含量”，我现在实在不知道。&lt;/p&gt;
&lt;p&gt;活在当下，聊聊我们这些Coder要怎么做：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;学AI怎么用（显而易见，不看推特也知道）&lt;/li&gt;
&lt;li&gt;写代码变廉价了，转向“评审与架构”&lt;/li&gt;
&lt;li&gt;保证基础不差，做为“护城墙”（个人感觉是因为现在国内软件公司的考核方式仍然没有太多改变）&lt;/li&gt;
&lt;li&gt;培养“产品感”&lt;/li&gt;
&lt;li&gt;转全栈&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;总而言之一句话，要Coders转为&lt;strong&gt;SuperOne&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;AI 时代的 SuperOne 是指那些不再被繁琐代码所束缚、而是学会驾驭 AI 工具链抹平技术门槛的“超级个体”：他们将产品的审美、系统的架构与底层的逻辑深度缝合，一个人便能通过高效的 AI 工作流支撑起从创意蓝图到全栈交付的完整生命周期，让个人的创造力在 AI 的杠杆下，从系统的“螺丝钉”进化为“独立实验室”。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;其实上面的这段解释也是AI生成的。&lt;/p&gt;
&lt;p&gt;也就是说，“一人即团体”，现在要培养“产品感“、要转全栈，也就要求一个人要学会更多的东西——配合AI，这是可行的，因为一个人配合AI的辅助解决问题的能力得到了大大的提升，即使是专精领域之外的，也能实现这个循环：发现问题——问AI-尝试解决-出错-问AI-解决问题-优化 ，即使说现在可能还不能借助AI解决所有问题，不过快了。我们可以预见的是：未来的“一人公司”会越来越多。&lt;/p&gt;
&lt;p&gt;过年的时候，一个表哥跟我说”现在是你们最好的时代“。其实我不太理解，因为我现在毕竟已经是大三的老登了，如果说现在去学什么LLM、transformer···似乎有点太晚，且太过庞杂，不知从何开始（哈哈，我承认我很懒）。去学AI的应用和技巧，却只得其术不得其道——spec-coding、prompt工程、CLI等等，新的应用/技术实在太多太多，令人晕头转向。&lt;/p&gt;
&lt;p&gt;乐观来讲，这种新技术新应用涌现的“五代十国之争“，确实是科技的催化剂。往下看，也催生了一些新岗位，就我们前后端来讲，就有所谓的AI前端工程师岗位。&lt;/p&gt;
&lt;p&gt;总之先学学这些”术“吧，不得其术何以得其道。或许三五年后回头看，这些只是小浪花罢了，AI与Coder的爱恨情仇在未来估计会更加波涛汹涌。&lt;/p&gt;
&lt;h2&gt;LocalHost之外和之内&lt;/h2&gt;
&lt;p&gt;在AI的宏大叙事之下，或许要更关注个人的物理世界——“活在当下”嘛。做事和内省永远是提升自己的最夯的办法。&lt;/p&gt;
&lt;p&gt;25年是读大二上下的一年，大二上说实话没干啥，而且我的记忆有点模糊了，只记得玩了挺多的游戏的，有这几个令人印象深刻：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对马岛之魂&lt;/li&gt;
&lt;li&gt;死亡搁浅&lt;/li&gt;
&lt;li&gt;空洞骑士&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;今年也在玩很多游戏，我是游戏宅。我爸妈现在都爱打麻将，等我和我的小伙伴到他们这个年纪时，估计娱乐活动就是带个头显在赛博世界里联机。&lt;/p&gt;
&lt;p&gt;大二下基本没在学校里度过，因为我暑假跑去面试了几家公司：先是shopee、然后是京东、最后是美团，其他厂投递了但是我没有收到任何反馈——直到现在也没有。shopee拿下了我的第一次面试，当时面的是Data Infra部门，只记得面试官一口广普，几个算法题打得我找不着北，毫不意外的挂了。京东很顺畅的过了三面（其实这里有个小意外，组里原本是想招一个26届转正的，不过误打误撞给我招进去了）；美团一面的面试官非常和蔼、二面面试官面无表情，其实感觉我答的实在不是很好——直到在京东实习了半个月后，才来了OC。&lt;/p&gt;
&lt;p&gt;京东实习还是挺舒服的，同事年纪都比较大，功力深厚，非常照顾人。后面来了一位社招的哥们，跟他取到了很多经。我的mt也是一个非常潇洒随和的人（不过他真的很忙），领导也非常和蔼，请假审批30分钟内就能过（当然这只是说领导给力的一个例子）。几个月内主要做的是一些toB的业务，总体来说没什么太大的难度，很多问题其实业界上有案例可以参考。&lt;/p&gt;
&lt;p&gt;这是25年最有意思的一次经历了，毕竟是第一次实习，见识到了一个组是怎么运作的、北京的外卖到底有多贵、雾霾和大风到底有多呛人。&lt;/p&gt;
&lt;p&gt;年前回到学校了，然后隔壁的两个后端哥们也去实习了，一个到深圳shopee、一个到北京懂车帝。&lt;/p&gt;
&lt;p&gt;过年倒是没啥意思，现在年味太淡了——或者说人变老了，过年不再意味着可以随便吃炸货卤货、和兄弟姐妹打游戏···而是一次大规模人情往来的活动。&lt;/p&gt;
&lt;p&gt;但是说实话，我现在挺焦虑的。&lt;/p&gt;
&lt;p&gt;一只脚踏入社会了，另一只脚还在学校。&lt;/p&gt;
&lt;p&gt;身份转变带来的焦虑感、技术水平和岗位是否匹配的配得感、应对人情往来的慌乱感。&lt;/p&gt;
&lt;p&gt;我知道其实大部分青年都会有这样的心理感觉——但是感觉就在那，很难忽视掉它，群体都要面对的挑战也不影响个体内心的焦虑。&lt;/p&gt;
&lt;p&gt;”想的时候多做，做的时候少想“，或许这是这种心理状态的一种解药：与其在 Localhost 预演一万次焦虑，不如直接在 Production 环境里摔一次跟头&lt;/p&gt;
&lt;p&gt;希望明年写年终总结（其实是今年）的时候，能有一种”回首向来萧瑟处，也无风雨也无晴“的感觉。&lt;/p&gt;
&lt;h2&gt;Flag&lt;/h2&gt;
&lt;p&gt;其实我不是一个喜欢立Flag的人，不知道这是不是意味着我很喜欢逃避。&lt;/p&gt;
&lt;p&gt;最近看了一些年终总结，最有印象的是OneV&apos;s Den的一段话，我感觉很适合引用在这：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;按照往年的惯例，我大概会用“懒癌发作”或者“没什么进取心”作为开场白：一方面习惯性自嘲，另一方面也给自己留点退路，装作好像只要态度足够散淡，变化就追不上来的样子。但回望 2025 年，我发现这些词突然变得不太合适了。并不是我变得勤奋了，而是这个世界的变化速度，已经快到了即使你选择“躺平”，也能清楚地感觉到身下的地板在带着你呼呼向前移动。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;那今年就立一些简单的Flag，不论是长期的还是短期的，写下来，然后去执行，完成了我就打个勾，年末来复盘：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[ ] 顺利找到暑期实习&lt;/li&gt;
&lt;li&gt;[ ] 顺利转正/秋招成功&lt;/li&gt;
&lt;li&gt;[ ] 坚持写Weekly&lt;/li&gt;
&lt;li&gt;[ ] 多做记录&lt;/li&gt;
&lt;li&gt;[ ] 多尝试新技术：AI、后端、运维···&lt;/li&gt;
&lt;li&gt;[ ] 尝试做一个产品&lt;/li&gt;
&lt;li&gt;[ ] 学会一些简单的理财知识&lt;/li&gt;
&lt;li&gt;[ ] 学会一些简单的心理知识&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>人工智能导论 - 用C语言和A*算法解决15数码问题</title><link>https://astro-pure.js.org/blog/2025/15puzzle</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2025/15puzzle</guid><description>人工智能导论实验1：用C语言和A*算法解决15数码问题</description><pubDate>Sat, 06 Dec 2025 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;首先是分析&lt;strong&gt;理论基础（可达性与启发函数）&lt;/strong&gt;，然后是&lt;strong&gt;完整的 C 语言实现代码&lt;/strong&gt;，最后是对&lt;strong&gt;算法性能的分析&lt;/strong&gt;和&lt;strong&gt;可视化示例&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;0. 实验要求&lt;/h2&gt;
&lt;p&gt;使用C语言解决15数码问题；将问题求解改写为搜索问题。设置起点和目标，使用 A* 算法实现。比较不同的启发策略，分析 A* 算法和代价函数选择间的关系。注意判断初始局面和目标之间的可达性；需要以可视化方式呈现搜索过程。&lt;/p&gt;
&lt;h2&gt;1. 理论基础与策略&lt;/h2&gt;
&lt;h3&gt;A. 可达性判断 (Solvability)&lt;/h3&gt;
&lt;p&gt;并不是任意两个 15 数码的局面都是相互可达的。为了避免程序陷入死循环，必须先进行奇偶性校验。
对于 $N \times N$ 的网格（$N=4$）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;逆序数 (Inversions):&lt;/strong&gt; 将矩阵铺平（不包含空格），计算前面比后面大的数字对的个数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;判断公式:&lt;/strong&gt;
如果 $N$ 是偶数（15数码中 $N=4$），当且仅当下面条件满足时，初始状态可达目标状态（假设目标状态逆序数为0）：
逆序数 + 空格所在行号(从下往上数)是偶数
&lt;em&gt;(注：如果两个状态都有逆序数，则需判断它们的奇偶性变换是否一致)&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;B. 启发式策略 (Heuristics)&lt;/h3&gt;
&lt;p&gt;A* 算法的核心公式是 $f(n) = g(n) + h(n)$。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$g(n)$: 从起点到当前节点的实际代价（步数）。&lt;/li&gt;
&lt;li&gt;$h(n)$: 当前节点到目标的预估代价。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们将比较两种策略：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;不在位棋子数 (Misplaced Tiles):&lt;/strong&gt; 统计当前局面与目标局面数字位置不一致的个数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;曼哈顿距离 (Manhattan Distance):&lt;/strong&gt; 所有数字当前位置与目标位置的横纵坐标差的绝对值之和。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;$$h(n) = \sum |x_{current} - x_{goal}| + |y_{current} - y_{goal}|$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;2. C 语言代码实现&lt;/h2&gt;
&lt;p&gt;这段代码实现了一个包含 &lt;strong&gt;最小堆 (Priority Queue)&lt;/strong&gt; 和 &lt;strong&gt;哈希查重 (Hash Set)&lt;/strong&gt; 的 A* 算法。为了实现“可视化”，我编写了一个播放函数，在终端中通过刷新屏幕演示解题步骤。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#include &amp;#x3C;stdio.h&gt;
#include &amp;#x3C;stdlib.h&gt;
#include &amp;#x3C;string.h&gt;
#include &amp;#x3C;math.h&gt;
#include &amp;#x3C;time.h&gt;

#ifdef _WIN32
    #include &amp;#x3C;windows.h&gt;
    #define CLEAR_CMD &quot;cls&quot;
    #define SLEEP_MS(x) Sleep(x)
#else
    #include &amp;#x3C;unistd.h&gt;
    #define CLEAR_CMD &quot;clear&quot;
    #define SLEEP_MS(x) usleep((x)*1000)
#endif

#define N 4
#define MAX_NODES 500000 // 最大搜索节点池，防止内存溢出

// 方向数组：上下左右
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};
char move_char[] = {&apos;U&apos;, &apos;D&apos;, &apos;L&apos;, &apos;R&apos;};

// 目标状态 (0 表示空格)
int goal[N][N] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12},
    {13, 14, 15, 0}
};

typedef struct Node {
    int board[N][N];
    int x, y; // 空格位置
    int g, h, f;
    struct Node* parent;
    int hash; // 用于查重
} Node;

// 最小堆结构
typedef struct {
    Node* elements[MAX_NODES];
    int size;
} MinHeap;

// 哈希表 (简单链地址法可优化，这里用简单的数组模拟CloseList的大容量查找)
// 为简化代码，这里使用大数组+简单Hash。实际工程应使用链表处理冲突。
#define HASH_SIZE 1000003
Node* visited[HASH_SIZE]; 

// --- 辅助函数 ---

int abs_val(int x) { return x &amp;#x3C; 0 ? -x : x; }

// 计算曼哈顿距离 (启发策略 2)
int calc_manhattan(int board[N][N]) {
    int dist = 0;
    for (int i = 0; i &amp;#x3C; N; i++) {
        for (int j = 0; j &amp;#x3C; N; j++) {
            int val = board[i][j];
            if (val == 0) continue; 
            // 目标位置计算: val-1 对应的坐标
            int target_r = (val - 1) / N;
            int target_c = (val - 1) % N;
            dist += abs_val(i - target_r) + abs_val(j - target_c);
        }
    }
    return dist;
}

// 计算不在位棋子数 (启发策略 1)
int calc_misplaced(int board[N][N]) {
    int count = 0;
    for(int i=0; i&amp;#x3C;N; i++) {
        for(int j=0; j&amp;#x3C;N; j++) {
            if(board[i][j] != 0 &amp;#x26;&amp;#x26; board[i][j] != goal[i][j]) {
                count++;
            }
        }
    }
    return count;
}

// 生成哈希值
int get_hash(int board[N][N]) {
    unsigned int h = 0;
    for (int i = 0; i &amp;#x3C; N; i++)
        for (int j = 0; j &amp;#x3C; N; j++)
            h = h * 31 + board[i][j];
    return h % HASH_SIZE;
}

// --- 堆操作 ---

void push(MinHeap* heap, Node* node) {
    int i = heap-&gt;size++;
    while (i &gt; 0) {
        int p = (i - 1) / 2;
        if (heap-&gt;elements[p]-&gt;f &amp;#x3C;= node-&gt;f) break;
        heap-&gt;elements[i] = heap-&gt;elements[p];
        i = p;
    }
    heap-&gt;elements[i] = node;
}

Node* pop(MinHeap* heap) {
    Node* ret = heap-&gt;elements[0];
    Node* x = heap-&gt;elements[--heap-&gt;size];
    int i = 0;
    while (i * 2 + 1 &amp;#x3C; heap-&gt;size) {
        int a = i * 2 + 1, b = i * 2 + 2;
        if (b &amp;#x3C; heap-&gt;size &amp;#x26;&amp;#x26; heap-&gt;elements[b]-&gt;f &amp;#x3C; heap-&gt;elements[a]-&gt;f) a = b;
        if (heap-&gt;elements[a]-&gt;f &gt;= x-&gt;f) break;
        heap-&gt;elements[i] = heap-&gt;elements[a];
        i = a;
    }
    heap-&gt;elements[i] = x;
    return ret;
}

// --- 可解性判断 ---
int is_solvable(int board[N][N]) {
    int inv_count = 0;
    int arr[N * N];
    int k = 0;
    int empty_row = 0;

    for (int i = 0; i &amp;#x3C; N; i++)
        for (int j = 0; j &amp;#x3C; N; j++) {
            if (board[i][j] == 0) empty_row = N - 1 - i; // 从下往上数，0开始
            else arr[k++] = board[i][j];
        }

    for (int i = 0; i &amp;#x3C; k - 1; i++)
        for (int j = i + 1; j &amp;#x3C; k; j++)
            if (arr[i] &gt; arr[j]) inv_count++;

    // N=4是偶数，判断公式: (逆序数 + 空格行号) % 2 == 0 ? (假设目标是顺序)
    // 目标状态逆序数为0。
    return (inv_count % 2 == 0) == (empty_row % 2 == 1) ? 0 : 1;
}

// --- 可视化与输出 ---

void print_board(int board[N][N]) {
    printf(&quot;+----+----+----+----+\n&quot;);
    for (int i = 0; i &amp;#x3C; N; i++) {
        for (int j = 0; j &amp;#x3C; N; j++) {
            if (board[i][j] == 0) printf(&quot;|    &quot;);
            else printf(&quot;| %2d &quot;, board[i][j]);
        }
        printf(&quot;|\n+----+----+----+----+\n&quot;);
    }
}

void visualize_path(Node* end_node) {
    Node* path[1000];
    int count = 0;
    Node* curr = end_node;
    while (curr != NULL) {
        path[count++] = curr;
        curr = curr-&gt;parent;
    }

    printf(&quot;\n搜索完成！最优步数: %d\n&quot;, count - 1);
    printf(&quot;按回车键开始播放解题过程...\n&quot;);
    getchar(); getchar();

    for (int i = count - 1; i &gt;= 0; i--) {
        system(CLEAR_CMD);
        printf(&quot;步骤: %d / %d\n&quot;, count - 1 - i, count - 1);
        printf(&quot;G(代价): %d, H(预估): %d, F: %d\n&quot;, path[i]-&gt;g, path[i]-&gt;h, path[i]-&gt;f);
        print_board(path[i]-&gt;board);
        SLEEP_MS(600); // 暂停 600ms
    }
}

// --- A* 主算法 ---

void solve_astar(int start_board[N][N], int heuristic_type) {
    if (!is_solvable(start_board)) {
        printf(&quot;错误：该局面不可达（无解）！\n&quot;);
        return;
    }

    MinHeap heap;
    heap.size = 0;
    memset(visited, 0, sizeof(visited));

    Node* start_node = (Node*)malloc(sizeof(Node));
    memcpy(start_node-&gt;board, start_board, sizeof(start_node-&gt;board));
    
    // 寻找空格位置
    for(int i=0; i&amp;#x3C;N; i++) 
        for(int j=0; j&amp;#x3C;N; j++) 
            if(start_node-&gt;board[i][j] == 0) { start_node-&gt;x=i; start_node-&gt;y=j; }

    start_node-&gt;g = 0;
    start_node-&gt;h = (heuristic_type == 1) ? calc_manhattan(start_node-&gt;board) : calc_misplaced(start_node-&gt;board);
    start_node-&gt;f = start_node-&gt;g + start_node-&gt;h;
    start_node-&gt;parent = NULL;
    start_node-&gt;hash = get_hash(start_node-&gt;board);

    push(&amp;#x26;heap, start_node);
    visited[start_node-&gt;hash] = start_node; // 简单标记

    int nodes_expanded = 0;

    printf(&quot;开始搜索... (策略: %s)\n&quot;, heuristic_type == 1 ? &quot;曼哈顿距离&quot; : &quot;不在位棋子&quot;);

    while (heap.size &gt; 0) {
        Node* current = pop(&amp;#x26;heap);
        nodes_expanded++;

        // 检查是否到达目标 (曼哈顿距离为0即为目标)
        if (current-&gt;h == 0) {
            printf(&quot;找到解！共扩展节点数: %d\n&quot;, nodes_expanded);
            visualize_path(current);
            return;
        }

        // 拓展节点
        for (int i = 0; i &amp;#x3C; 4; i++) {
            int nx = current-&gt;x + dx[i];
            int ny = current-&gt;y + dy[i];

            if (nx &gt;= 0 &amp;#x26;&amp;#x26; nx &amp;#x3C; N &amp;#x26;&amp;#x26; ny &gt;= 0 &amp;#x26;&amp;#x26; ny &amp;#x3C; N) {
                Node* child = (Node*)malloc(sizeof(Node));
                memcpy(child-&gt;board, current-&gt;board, sizeof(child-&gt;board));
                
                // 交换
                child-&gt;board[current-&gt;x][current-&gt;y] = child-&gt;board[nx][ny];
                child-&gt;board[nx][ny] = 0;
                child-&gt;x = nx;
                child-&gt;y = ny;

                child-&gt;g = current-&gt;g + 1;
                child-&gt;h = (heuristic_type == 1) ? calc_manhattan(child-&gt;board) : calc_misplaced(child-&gt;board);
                child-&gt;f = child-&gt;g + child-&gt;h;
                child-&gt;parent = current;
                child-&gt;hash = get_hash(child-&gt;board);

                // 简单的重复检测 (实际应处理哈希冲突和更优路径更新)
                // 这里如果哈希位已被占用且步数没更优，则跳过
                if (visited[child-&gt;hash] != NULL) {
                     // 如果这里实现了更复杂的CloseList，需要比较g值
                     free(child); 
                     continue;
                }
                
                visited[child-&gt;hash] = child;
                push(&amp;#x26;heap, child);
            }
        }
    }
    printf(&quot;搜索结束，未找到解（或内存不足）。\n&quot;);
}

int main() {
    // 测试用例：打乱的局面 (大致需要 20-30 步)
    int start_board[N][N] = {
        {1, 2, 3, 4},
        {5, 6, 0, 8},
        {9, 10, 7, 12},
        {13, 14, 11, 15}
    };
    
    // 另一个稍微难一点的测试用例
    // int start_board[N][N] = {
    //    {5, 1, 2, 4},
    //    {9, 6, 3, 8},
    //    {13, 15, 10, 11},
    //    {14, 0, 7, 12}
    // };

    printf(&quot;初始局面:\n&quot;);
    print_board(start_board);

    // 1 代表曼哈顿距离 (推荐)，2 代表不在位棋子数
    solve_astar(start_board, 1);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;3. 分析：A* 算法与代价函数的关系&lt;/h2&gt;
&lt;p&gt;在 A* 算法中，启发函数 $h(n)$ 的选择直接决定了搜索的效率和结果的最优性。&lt;/p&gt;
&lt;h3&gt;A. 不同启发策略的比较&lt;/h3&gt;
&lt;p&gt;| 策略 | 定义 | 性能分析 |
| :--- | :--- | :--- |
| &lt;strong&gt;不在位棋子数 ($h_1$)&lt;/strong&gt; | $h_1(n) = \text{count}(board[i] \neq goal[i])$ | &lt;strong&gt;较差&lt;/strong&gt;。它提供的信息太少，只知道有多少个错位，不知道错得有多远。搜索空间剪枝能力弱，扩展节点多。 |
| &lt;strong&gt;曼哈顿距离 ($h_2$)&lt;/strong&gt; | $h_2(n) = \sum |x_i - x_{goal}| + |y_i - y_{goal}|$ | &lt;strong&gt;优秀&lt;/strong&gt;。它更精确地描述了状态复原所需的“最小移动代价”。在 15 数码问题中，$h_2$ 比 $h_1$ 更大（更接近真实代价），因此搜索效率远高于 $h_1$。 |&lt;/p&gt;
&lt;h3&gt;B. 为什么 $h(n)$ 越接近真实距离越好？&lt;/h3&gt;
&lt;p&gt;A* 的搜索效率取决于 $h(n)$ 接近真实代价 $h^*(n)$ 的程度：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;可采纳性 (Admissibility):&lt;/strong&gt; 只要 $h(n) \le h^*(n)$（即不“高估”代价），A* 就能保证找到最优解（最短路径）。
&lt;ul&gt;
&lt;li&gt;$h_1$ 和 $h_2$ 都没有高估（移动一次最多归位一个棋子，或者减少1点曼哈顿距离），所以都能找到最优解。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优势关系 (Dominance):&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;对于所有状态，$h_2(n) \ge h_1(n)$。&lt;/li&gt;
&lt;li&gt;数学上证明，如果 $h_2 \ge h_1$，那么使用 $h_2$ 扩展的节点数 &lt;strong&gt;必定少于或等于&lt;/strong&gt; 使用 $h_1$ 扩展的节点数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;结论：&lt;/strong&gt; 在此问题中，曼哈顿距离是比不在位棋子数更“强”的启发函数。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;4. 总结&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;搜索建模&lt;/strong&gt;：将棋盘状态视为节点，移动空格视为边，步数视为边的权重（均为1）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;核心难点&lt;/strong&gt;：在于&lt;strong&gt;状态判重&lt;/strong&gt;（防止走回头路）和&lt;strong&gt;启发函数设计&lt;/strong&gt;（防止搜索空间爆炸）。代码中使用了哈希映射来记录已访问状态。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;结果&lt;/strong&gt;：使用曼哈顿距离作为启发函数，即使在普通的 PC 上，也能在毫秒级内解决大多数 15 数码问题；而使用“不在位棋子”策略，对于稍微复杂的局面可能会因为节点过多而非常慢。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;5. 附：可视化示例&lt;/h2&gt;
&lt;p&gt;请访问示例网站：https://15puzzle-coral.vercel.app/ （可能需要梯子）&lt;/p&gt;
&lt;p&gt;并且对于过于复杂的打乱情况做了舍弃。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://astro-pure.js.org/_image?href=%2F_astro%2Fimage.Ce85qJWY.png&amp;#x26;w=995&amp;#x26;h=897&amp;#x26;f=webp&quot; alt=&quot;alt text&quot;&gt;&lt;/p&gt;
&lt;h2&gt;6. 附：测试用例&lt;/h2&gt;
&lt;p&gt;将 &lt;code&gt;main&lt;/code&gt; 函数中的 &lt;code&gt;start_board&lt;/code&gt; 替换为以下数组进行测试。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;第一组：基础功能测试&lt;/h3&gt;
&lt;p&gt;这组测试用于验证程序逻辑是否正确，包括“一步到位”和“无解判断”。&lt;/p&gt;
&lt;h4&gt;1. 极简测试 (1步可达)&lt;/h4&gt;
&lt;p&gt;这是最简单的局面，用于测试程序是否能正常启动并识别目标。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;// 预期结果：最优步数 1，扩展节点数极少
int start_board[N][N] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12},
    {13, 14, 0, 15} // 仅最后一行 15 和 0 交换了
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 无解测试 (不可达局面)&lt;/h4&gt;
&lt;p&gt;用于验证 &lt;code&gt;is_solvable&lt;/code&gt; 函数的奇偶性校验逻辑。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;// 预期结果：程序提示“错误：该局面不可达（无解）！”并直接退出
// 原理：交换了 14 和 15，逆序数变化，但空格位置不变，导致奇偶性无法匹配
int start_board[N][N] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12},
    {13, 15, 14, 0} 
};
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3&gt;第二组：启发策略性能对比测试 (重点)&lt;/h3&gt;
&lt;p&gt;这是本次实验的核心。我们需要一个&lt;strong&gt;中等难度&lt;/strong&gt;（约 15-20 步）的局面。在这个难度下，两种启发函数的性能差异会非常明显。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;测试局面：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int start_board[N][N] = {
    {2, 10, 3, 4},
    {1, 0, 6, 8},
    {5, 9, 7, 12},
    {13, 14, 11, 15}
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;实验步骤：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;修改 &lt;code&gt;solve_astar(start_board, 2);&lt;/code&gt; (使用 &lt;strong&gt;不在位棋子数&lt;/strong&gt;)。记录输出的“共扩展节点数”。&lt;/li&gt;
&lt;li&gt;修改 &lt;code&gt;solve_astar(start_board, 1);&lt;/code&gt; (使用 &lt;strong&gt;曼哈顿距离&lt;/strong&gt;)。记录输出的“共扩展节点数”。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;预期数据对比：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;| 启发策略 | 预估最优步数 | 扩展节点数 (近似值) | 耗时感受 | 分析 |
| :--- | :--- | :--- | :--- | :--- |
| &lt;strong&gt;不在位棋子数&lt;/strong&gt; | 18-20 步 | &lt;strong&gt;&gt; 10,000&lt;/strong&gt; | 可能会有明显卡顿 | 启发性弱，搜索像无头苍蝇，走了很多冤枉路。 |
| &lt;strong&gt;曼哈顿距离&lt;/strong&gt; | 18-20 步 | &lt;strong&gt;&amp;#x3C; 1,000&lt;/strong&gt; | 瞬间完成 | 启发性强，方向感好，紧紧锁定目标。 |&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;结论：&lt;/strong&gt; 曼哈顿距离在这个问题上具有压倒性优势，因为它包含的信息量（距离）远大于简单的“是否在位”。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3&gt;第三组：高难度压力测试&lt;/h3&gt;
&lt;p&gt;用于测试 A* 算法在有限内存（代码中定义了 &lt;code&gt;MAX_NODES 500000&lt;/code&gt;）下的极限。&lt;/p&gt;
&lt;h4&gt;复杂局面 (约 25-30 步)&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;// 这是一个相对混乱的局面，需要较长的步骤还原
int start_board[N][N] = {
    {6, 4, 7, 1},
    {5, 0, 3, 2},
    {9, 10, 14, 8},
    {13, 15, 11, 12}
};
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;注意：&lt;/strong&gt; 对于这种难度，&lt;strong&gt;必须使用曼哈顿距离 (type 1)&lt;/strong&gt;。如果使用“不在位棋子数”，程序基本会因为节点池耗尽（超过 50 万个节点）而崩溃或报错。&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>一次AI生图测试</title><link>https://astro-pure.js.org/blog/2025/perfectcorruption</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2025/perfectcorruption</guid><description>一次关于AI生图的测试，Prompt来源于Kkai</description><pubDate>Fri, 05 Dec 2025 08:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Midjourney&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;From &lt;a href=&quot;https://www.bilibili.com/video/BV1GnSCBaEwr/?spm_id_from=333.337.search-card.all.click&quot;&gt;Kkai&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;./1.png&quot; alt=&quot;Perfect Corruption Image 1&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./2.png&quot; alt=&quot;Perfect Corruption Image 2&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./3.png&quot; alt=&quot;Perfect Corruption Image 3&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./4.png&quot; alt=&quot;Perfect Corruption Image 4&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./5.png&quot; alt=&quot;Perfect Corruption Image 5&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./6.png&quot; alt=&quot;Perfect Corruption Image 6&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./7.png&quot; alt=&quot;Perfect Corruption Image 7&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./8.png&quot; alt=&quot;Perfect Corruption Image 8&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./9.png&quot; alt=&quot;Perfect Corruption Image 9&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Test in Nano Banana Pro&lt;/h2&gt;
&lt;p&gt;A surreal minimalist scene in a pure white void. A traditionablue-and-white Chinese porcelain vase stands at the centerpristine and elegant. From the mouth of the vase, a realistichuman hand slowly emerges, reaching outward with delicatetension. The vase remains perfectly upright, glossy, andornate, contrasting sharply with the soft skin texture of thehand. Cold, clinical lighting; no shadows; ultra-cleancomposition. The scene feels symbolic, uncanny, andritualistic, blending cultural beauty with unsettling intrusion.Matthew Barney-inspired surreal object-body fusion.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image.png&quot; alt=&quot;alt text&quot;&gt;&lt;/p&gt;
&lt;p&gt;A surreal minimalist scene in a pure white void. A woman inan elegant black formal gown lies horizontally across twobicycles that stand upright, perfectly vertical against theground. Each bicycle supports her body at the shoulders andlegs, forming an impossible, sculptural balance. Thewoman&apos;s expression is neutral and calm, her posture stiffand ceremonial. Cold, clinical studio lighting; no shadows .ultra-clean composition. The bicvcles look like rigid metaltotems holding her in place. Matthew Barney-inspired bodilyinstallation--elegant, symbolic, and quietly unsettling.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-1.png&quot; alt=&quot;alt text&quot;&gt;&lt;/p&gt;
&lt;p&gt;A surreal, clinical scene in a pure white void. A person in ablack formal suit lies face-down on the ground, carrying amassive silicone ear sculpture on their back. The ear ishyper-detailed, flesh-toned, soft-looking, and oversizedalmost the size of the person&apos;s torso. The figure&apos;s posture isstrained yet emotionless, as if performing a ritual burden.Cold, controlled lighting;sharp shadows; minimalist,sculptural composition. Texture of the silicone ear is highlyrealistic, with subtle translucency. Matthew Barney-inspiredbodily symbolism and ceremonial stillness.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-2.png&quot; alt=&quot;alt text&quot;&gt;&lt;/p&gt;
&lt;p&gt;A hyper-detailed still life of a scorpion standing upright in anempty white porcelain plate placed on an elegant diningtable. The scorpion is perfectly centered, posed like a ritualoffering, its tail arched in a sculptural curve. inimalist whitebackground, cold sterile lighting, high-end productphotography atmosphere. No food, no clutter--only thescorpion, the plate, and the elegant table surface. Surrealclinical, Matthew Barney-inspired symbolic composition.Sharp detail on the scorpion&apos;s exoskeleton and platetextures.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-3.png&quot; alt=&quot;alt text&quot;&gt;&lt;/p&gt;
&lt;p&gt;Five cars arranged in a perfect circle, bumper-to-bumperforming a closed ring inside a pure white void. Each car isclean, polished, identical or nearly identical, positioned withprecise geometric alignment. Cold, clinical studio lighting; noshadows;ultra-minimal composition. The arrangement feelsritualistic, like a ceremonial installation or mechanicalmandala. No people, no clutter--just the silent, surreal circleof vehicles. Hyper-detailed reflections on the car surfaces ;pristine,uncanny stillness. Matthew Barney-inspiredsymbolic structure, elegant and unsettling.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-4.png&quot; alt=&quot;alt text&quot;&gt;&lt;/p&gt;
&lt;p&gt;A surreal, clinical scene in a pure white void. A person in ablack formal suit lies flat on the ground, expressionless. Awhite goat stands directly over their torso, both front legsplanted firmly on each side of the person&apos;s ribcage. Theperson reaches up with both hands, gently holding the goat&apos;sneck from below, creating an intimate yet unsettlingconnection. Cold, controlled studio lighting; no clutter; ultra.clean composition. The goat is calm and statuesque, whilethe human body remains still and sculptural. Hyper-detailedtextures of fur, skin, and fabric. Matthew Barney-inspiredritual posture, elegant tension, symbolic domination andsubmission.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-5.png&quot; alt=&quot;alt text&quot;&gt;&lt;/p&gt;
&lt;p&gt;A hyper-detailed close-up portrait of a handsome youngman with perfect bone structure and smooth skin. A singleblack fly slowly crawls across his cheek, rendered with ultra-macro clarity. Expressionless, emotionless face; cold, sterilelighting; minimalist white background. Cinematic portraitphotography,shallow depth of field, razor-sharp focus onthe insect&apos;s wings and tiny hairs. Surreal, clinicalatmosphere, Matthew Barney-inspired, calm yet unsettling&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-6.png&quot; alt=&quot;alt text&quot;&gt;&lt;/p&gt;
&lt;p&gt;A hyper-detailed still life of a tall high-heeled boot standingupright in a pure white void. The boot is filled with whiteroses, overflowing elegantly from the top, petals soft andluminous. Minimalist, clinical lighting; no shadows; cold,sculptural composition. The boot appears like a ritual objector altar vessel. Fine texture on leather, delicate rose petals,silent surreal atmosphere. atthew Barney-inspiredsymbolic object portrait.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-7.png&quot; alt=&quot;alt text&quot;&gt;&lt;/p&gt;
&lt;p&gt;A surreal, clinical scene in a pure white void. A group ofsmall baby chicks form a perfect circle on the ground. In thecenter lies a fish gently flopping. No humans, no humanshapes, no mannequins, no people, no human presenceOnly animals.inimalist composition, ultra-cleanbackground, cold studio lighting. Ritual-like arrangementcreated entirely by animals. Hyper-detailed chick feathersand silver fish scales.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-8.png&quot; alt=&quot;alt text&quot;&gt;&lt;/p&gt;
&lt;p&gt;猜测 Kkai 应该在生成之前用了一个 prompt 顶了一下颜色基调，不过抛去这个因素的影响，Nano 的生成效果太拟真了，反而没有那种怪诞的感觉。&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;更新于2026/3/2，Gemini最新推出了Banana2.0，让我们尝试一下&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Banana2.0提供了可以选择的风格，我们先用一个例子试一试能不能不选择风格直接生成，使用下面这个例子：&lt;/p&gt;
&lt;p&gt;A surreal, clinical scene in a pure white void. A person in ablack formal suit lies face-down on the ground, carrying amassive silicone ear sculpture on their back. The ear ishyper-detailed, flesh-toned, soft-looking, and oversizedalmost the size of the person’s torso. The figure’s posture isstrained yet emotionless, as if performing a ritual burden.Cold, controlled lighting;sharp shadows; minimalist,sculptural composition. Texture of the silicone ear is highlyrealistic, with subtle translucency. Matthew Barney-inspiredbodily symbolism and ceremonial stillness.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-9.png&quot; alt=&quot;alt text&quot;&gt;&lt;/p&gt;
&lt;p&gt;感觉好像也没变化多少，我们选用一种风格试一试：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-10.png&quot; alt=&quot;alt text&quot;&gt;&lt;/p&gt;
&lt;p&gt;好像没有变化啊。提示似乎要求我们需要选择风格后再上传图片并描述要怎么修改图片：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-11.png&quot; alt=&quot;alt text&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-12.png&quot; alt=&quot;alt text&quot;&gt;&lt;/p&gt;
&lt;p&gt;感觉还是效果一般般，不知道他们是怎么搓出来的。&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>TOTP 算法——为什么“固定密钥”能生成“动态验证码”？</title><link>https://astro-pure.js.org/blog/2025/2fa</link><guid isPermaLink="true">https://astro-pure.js.org/blog/2025/2fa</guid><description>简单解释一下 TOTP 算法的原理及实现</description><pubDate>Thu, 04 Dec 2025 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在双重验证（2FA）中，我们扫描二维码后得到的那个“每30秒变一次的6位数字”，其背后的技术标准被称为 TOTP (Time-Based One-Time Password)。它最大的特点是：完全离线。服务器和手机不需要通信，却能算出同样的数字。本文将以精简准确的语言，拆解其从“长串密钥”到“6位数字”的完整计算路径，并附带 TypeScript 代码以供验证。&lt;/p&gt;
&lt;h2&gt;1. 核心原理：两个输入，一个输出&lt;/h2&gt;
&lt;p&gt;TOTP 本质上是一个数学函数，它接受两个输入：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;共享密钥 ($K$)&lt;/strong&gt;：即你扫描二维码包含的那个 Base32 字符串（如 &lt;code&gt;JBSWY3DPEHPK3PXP&lt;/code&gt;）。它是固定的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;当前时间 ($t$)&lt;/strong&gt;：通过 Unix 时间戳表示。它是流动的。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;算法公式如下：
$$
TOTP = \text{Truncate}(\text{HMAC-SHA1}(K, \lfloor \frac{t}{30} \rfloor))
$$&lt;/p&gt;
&lt;h2&gt;2. 实例拆解：一步步计算&lt;/h2&gt;
&lt;p&gt;为了演示，我们设定以下参数（你可以用文末的代码验证这个结果）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;密钥&lt;/strong&gt;：&lt;code&gt;JBSWY3DPEHPK3PXP&lt;/code&gt;（Base32解码后对应 ASCII 的 &lt;code&gt;&quot;Hello!&quot;&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模拟时间&lt;/strong&gt;：&lt;code&gt;1609459200&lt;/code&gt; (UTC 2021-01-01 08:00:00)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;第一步：时间切片 (Time Counter)&lt;/h3&gt;
&lt;p&gt;将连续的时间流按 30 秒切分，获取时间计数器 ($C$)。
$$
C = \lfloor \frac{1609459200}{30} \rfloor = 53648640
$$
注：在算法中，$C$ 需转换为 8字节的十六进制大端序 数据：&lt;code&gt;0000000003328D00&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;第二步：哈希混合 (HMAC)&lt;/h3&gt;
&lt;p&gt;使用 HMAC-SHA1 算法，将密钥和时间计数器混合。
$$
H = \text{HMAC-SHA1}(\text{&quot;Hello!&quot;}, \text{0x0000000003328D00})
$$
计算得出的 20 字节哈希值（Hex）如下：
&lt;code&gt;3d 21 8b 72 90 0f e6 9a 2e 56 ... 05&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;第三步：动态截断 (Dynamic Truncation)&lt;/h3&gt;
&lt;p&gt;这是 TOTP 最精妙的一步。我们利用哈希值本身来决定取哪几位数据。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;取偏移量&lt;/strong&gt;：取哈希值最后一个字节 &lt;code&gt;0x05&lt;/code&gt; 的低 4 位，结果是 &lt;code&gt;5&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;截取&lt;/strong&gt;：从哈希值的 第 5 个字节 开始，连续读取 4 个字节。
&lt;ul&gt;
&lt;li&gt;哈希片段：&lt;code&gt;0f e6 9a 2e&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;转整数&lt;/strong&gt;：将这 4 字节转为 32 位整数（并去掉符号位）。
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0x0fe69a2e&lt;/code&gt; $\rightarrow$ &lt;code&gt;266771118&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;第四步：取模 (Modulo)&lt;/h3&gt;
&lt;p&gt;对结果取模，保留最后 6 位。
$$
Code = 266771118 \pmod{1000000} = 771118
$$
最终验证码：&lt;strong&gt;771 118&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;3. 代码验证 (TypeScript/Node.js)&lt;/h2&gt;
&lt;p&gt;以下是一个纯净的 TypeScript 脚本（在 Node.js 环境下可直接运行），它复现了上述完整过程。你可以将其保存为 &lt;code&gt;totp.ts&lt;/code&gt; 并运行，无需安装第三方重型库。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import * as crypto from &apos;crypto&apos;;

/**
 * TOTP 验证核心逻辑演示
 */

// 1. 输入参数 (Inputs)
const SECRET_BASE32 = &apos;JBSWY3DPEHPK3PXP&apos;; // Base32 编码的密钥
const MOCK_TIME = 1609459200;             // 模拟时间戳 (2021-01-01 08:00:00 UTC)

// 2. 辅助函数：Base32 解码 (为了不依赖外部库，手动实现简化版)
function base32Decode(base32: string): Buffer {
    const alphabet = &apos;ABCDEFGHIJKLMNOPQRSTUVWXYZ234567&apos;;
    let bits = 0;
    let value = 0;
    let index = 0;
    const output = new Uint8Array(base32.length * 5 / 8 | 0);

    for (let i = 0; i &amp;#x3C; base32.length; i++) {
        const char = base32[i].toUpperCase();
        const val = alphabet.indexOf(char);
        if (val === -1) continue;

        value = (value &amp;#x3C;&amp;#x3C; 5) | val;
        bits += 5;

        if (bits &gt;= 8) {
            output[index++] = (value &gt;&gt;&gt; (bits - 8)) &amp;#x26; 255;
            bits -= 8;
        }
    }
    return Buffer.from(output);
}

// 3. 主算法流程
function generateTOTP(secretBase32: string, unixTime: number): string {
    console.log(`--- 计算开始 [Time: ${unixTime}] ---`);

    // Step A: 解码密钥
    const key = base32Decode(secretBase32);
    // console.log(&apos;Key (Hex):&apos;, key.toString(&apos;hex&apos;)); // Should be 48656c6c6f21 (&quot;Hello!&quot;)

    // Step B: 计算时间计数器 (Counter)
    const timeStep = 30;
    const counterVal = Math.floor(unixTime / timeStep);
    
    // 将计数器转换为 8字节 Buffer (Big Endian)
    const counterBuffer = Buffer.alloc(8);
    counterBuffer.writeBigInt64BE(BigInt(counterVal), 0);
    console.log(`1. 时间计数器 (T): ${counterVal} -&gt; Hex: ${counterBuffer.toString(&apos;hex&apos;)}`);

    // Step C: HMAC-SHA1 运算
    const hmac = crypto.createHmac(&apos;sha1&apos;, key);
    hmac.update(counterBuffer);
    const digest = hmac.digest();
    console.log(`2. HMAC-SHA1 结果: ${digest.toString(&apos;hex&apos;)}`);

    // Step D: 动态截断 (Dynamic Truncation)
    const offset = digest[digest.length - 1] &amp;#x26; 0xf; // 取最后一个字节的低4位
    console.log(`3. 偏移量 (Offset): Index ${offset}`);

    // 从 offset 处取4个字节
    // 这里的 &amp;#x26; 0x7fffffff 是为了去除符号位，确保是正整数
    const binary = (
        ((digest[offset] &amp;#x26; 0x7f) &amp;#x3C;&amp;#x3C; 24) |
        ((digest[offset + 1] &amp;#x26; 0xff) &amp;#x3C;&amp;#x3C; 16) |
        ((digest[offset + 2] &amp;#x26; 0xff) &amp;#x3C;&amp;#x3C; 8) |
        (digest[offset + 3] &amp;#x26; 0xff)
    );
    console.log(`4. 截取 4 Bytes 转整数: ${binary}`);

    // Step E: 取模得到 6 位数
    const otp = binary % 1000000;
    
    // 补零 (例如算出 123 则显示 000123)
    return otp.toString().padStart(6, &apos;0&apos;);
}

// 4. 执行
const code = generateTOTP(SECRET_BASE32, MOCK_TIME);
console.log(`\n✅ 最终生成的 6 位验证码: ${code}`);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;运行结果预期&lt;/h3&gt;
&lt;p&gt;当你运行这段代码时，控制台将输出：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;--- 计算开始 [Time: 1609459200] ---
1. 时间计数器 (T): 53648640 -&gt; Hex: 0000000003328d00
2. HMAC-SHA1 结果: 3d218b72900fe69a2e56...05
3. 偏移量 (Offset): Index 5
4. 截取 4 Bytes 转整数: 266771118

✅ 最终生成的 6 位验证码: 771118
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. 总结&lt;/h2&gt;
&lt;p&gt;TOTP 的设计哲学极为优雅：它利用时间的可预测性替代了网络通信。服务器知道密钥，知道时间，算出一个数。你知道密钥，知道时间，算出一个数。只要两边的表（时间）走得差不多，这两个数字就永远一致。&lt;/p&gt;
&lt;p&gt;这就是为什么那个“固定的长串”能源源不断地吐出正确的动态密码。&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>Markdown Syntax Support</title><link>https://astro-pure.js.org/blog/markdown</link><guid isPermaLink="true">https://astro-pure.js.org/blog/markdown</guid><description>Markdown is a lightweight markup language.</description><pubDate>Wed, 26 Jul 2023 08:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Basic Syntax&lt;/h2&gt;
&lt;p&gt;Markdown is a lightweight and easy-to-use syntax for styling your writing.&lt;/p&gt;
&lt;h3&gt;Headers&lt;/h3&gt;
&lt;p&gt;When the content of the article is extensive, you can use headers to segment:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;# Header 1

## Header 2

## Large Header

### Small Header
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Header previews would disrupt the structure of the article, so they are not displayed here.&lt;/p&gt;
&lt;h3&gt;Bold and Italics&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;_Italic text_ and **Bold text**, together will be **_Bold Italic text_**
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Italic text&lt;/em&gt; and &lt;strong&gt;Bold text&lt;/strong&gt;, together will be &lt;strong&gt;&lt;em&gt;Bold Italic text&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Links&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;Text link [Link Name](http://link-url)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;Text link &lt;a href=&quot;http://link-url&quot;&gt;Link Name&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Inline Code&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;This is an `inline code`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;This is an &lt;code&gt;inline code&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Code Blocks&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;```js
// calculate fibonacci
function fibonacci(n) {
  if (n &amp;#x3C;= 1) return 1
  const result = fibonacci(n - 1) + fibonacci(n - 2) // [\!code --]
  return fibonacci(n - 1) + fibonacci(n - 2) // [\!code ++]
}
```
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// calculate fibonacci
function fibonacci(n) {
  if (n &amp;#x3C;= 1) return 1
  const result = fibonacci(n - 1) + fibonacci(n - 2) // [!code --]
  return fibonacci(n - 1) + fibonacci(n - 2) // [!code ++]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Currently using shiki as the code highlighting plugin. For supported languages, refer to &lt;a href=&quot;https://shiki.matsu.io/languages.html&quot;&gt;Shiki: Languages&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Inline Formula&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;This is an inline formula $e^{i\pi} + 1 = 0$
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;This is an inline formula $e^{i\pi} + 1 = 0$&lt;/p&gt;
&lt;h3&gt;Formula Blocks&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;$$
\hat{f}(\xi) = \int_{-\infty}^{\infty} f(x) e^{-2\pi i x \xi} \, dx
$$
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;$$
\hat{f}(\xi) = \int_{-\infty}^{\infty} f(x) e^{-2\pi i x \xi} , dx
$$&lt;/p&gt;
&lt;p&gt;Currently using KaTeX as the math formula plugin. For supported syntax, refer to &lt;a href=&quot;https://katex.org/docs/supported.html&quot;&gt;KaTeX Supported Functions&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Images&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;![CWorld](https://cravatar.cn/avatar/1ffe42aa45a6b1444a786b1f32dfa8aa?s=200)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cravatar.cn/avatar/1ffe42aa45a6b1444a786b1f32dfa8aa?s=200&quot; alt=&quot;CWorld&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Strikethrough&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;~~Strikethrough~~
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;~~Strikethrough~~&lt;/p&gt;
&lt;h3&gt;Lists&lt;/h3&gt;
&lt;p&gt;Regular unordered list&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;- 1
- 2
- 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1&lt;/li&gt;
&lt;li&gt;2&lt;/li&gt;
&lt;li&gt;3&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Regular ordered list&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;1. GPT-4
2. Claude Opus
3. LLaMa
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;GPT-4&lt;/li&gt;
&lt;li&gt;Claude Opus&lt;/li&gt;
&lt;li&gt;LLaMa&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can continue to nest syntax within lists.&lt;/p&gt;
&lt;h3&gt;Blockquotes&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;&gt; Gunshot, thunder, sword rise. A scene of flowers and blood.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Gunshot, thunder, sword rise. A scene of flowers and blood.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can continue to nest syntax within blockquotes.&lt;/p&gt;
&lt;h3&gt;Line Breaks&lt;/h3&gt;
&lt;p&gt;Markdown needs a blank line to separate paragraphs.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;If you don&apos;t leave a blank line
it will be in one paragraph

First paragraph

Second paragraph
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;If you don&apos;t leave a blank line
it will be in one paragraph&lt;/p&gt;
&lt;p&gt;First paragraph&lt;/p&gt;
&lt;p&gt;Second paragraph&lt;/p&gt;
&lt;h3&gt;Separators&lt;/h3&gt;
&lt;p&gt;If you have the habit of writing separators, you can start a new line and enter three dashes &lt;code&gt;---&lt;/code&gt; or asterisks &lt;code&gt;***&lt;/code&gt;. Leave a blank line before and after when there are paragraphs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;---
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Advanced Techniques&lt;/h2&gt;
&lt;h3&gt;Inline HTML Elements&lt;/h3&gt;
&lt;p&gt;Currently, only some inline HTML elements are supported, including &lt;code&gt;&amp;#x3C;kdb&gt; &amp;#x3C;b&gt; &amp;#x3C;i&gt; &amp;#x3C;em&gt; &amp;#x3C;sup&gt; &amp;#x3C;sub&gt; &amp;#x3C;br&gt;&lt;/code&gt;, such as&lt;/p&gt;
&lt;h4&gt;Key Display&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;Use &amp;#x3C;kbd&gt;Ctrl&amp;#x3C;/kbd&gt; + &amp;#x3C;kbd&gt;Alt&amp;#x3C;/kbd&gt; + &amp;#x3C;kbd&gt;Del&amp;#x3C;/kbd&gt; to reboot the computer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;Use Ctrl + Alt + Del to reboot the computer&lt;/p&gt;
&lt;h4&gt;Bold Italics&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;&amp;#x3C;b&gt; Markdown also applies here, such as _bold_ &amp;#x3C;/b&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt; Markdown also applies here, such as &lt;em&gt;bold&lt;/em&gt; &lt;/p&gt;
&lt;h3&gt;Other HTML Writing&lt;/h3&gt;
&lt;h4&gt;Foldable Blocks&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;&amp;#x3C;details&gt;&amp;#x3C;summary&gt;Click to expand&amp;#x3C;/summary&gt;It is hidden&amp;#x3C;/details&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;h3&gt;Tables&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;| Header1  | Header2  |
| -------- | -------- |
| Content1 | Content2 |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;| Header1  | Header2  |
| -------- | -------- |
| Content1 | Content2 |&lt;/p&gt;
&lt;h3&gt;Footnotes&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;Use [^footnote] to add a footnote at the point of reference.

Then, at the end of the document, add the content of the footnote (it will be rendered at the end of the article by default).

[^footnote]: Here is the content of the footnote
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;Use [^footnote] to add a footnote at the point of reference.&lt;/p&gt;
&lt;p&gt;Then, at the end of the document, add the content of the footnote (it will be rendered at the end of the article by default).&lt;/p&gt;
&lt;p&gt;[^footnote]: Here is the content of the footnote&lt;/p&gt;
&lt;h3&gt;To-Do Lists&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;- [ ] Incomplete task
- [x] Completed task
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[ ] Incomplete task&lt;/li&gt;
&lt;li&gt;[x] Completed task&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Symbol Escaping&lt;/h3&gt;
&lt;p&gt;If you need to use markdown symbols like _ # * in your description but don&apos;t want them to be escaped, you can add a backslash before these symbols, such as &lt;code&gt;\_&lt;/code&gt; &lt;code&gt;\#&lt;/code&gt; &lt;code&gt;\*&lt;/code&gt; to avoid it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;\_Don&apos;t want the text here to be italic\_

\*\*Don&apos;t want the text here to be bold\*\*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;_Don&apos;t want the text here to be italic_&lt;/p&gt;
&lt;p&gt;**Don&apos;t want the text here to be bold**&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Embedding Astro Components&lt;/h2&gt;
&lt;p&gt;See &lt;a href=&quot;/docs/integrations/components&quot;&gt;User Components&lt;/a&gt; and &lt;a href=&quot;/docs/integrations/advanced&quot;&gt;Advanced Components&lt;/a&gt; for details.&lt;/p&gt;</content:encoded><h:img src="/_astro/thumbnail.HAXFr_hw.jpg"/><enclosure url="/_astro/thumbnail.HAXFr_hw.jpg"/></item></channel></rss>