<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tech on Armstrong Yan</title><link>https://yanqian.github.io/zh/categories/tech/</link><description>Recent content in Tech on Armstrong Yan</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Thu, 13 Aug 2026 14:13:57 +0800</lastBuildDate><atom:link href="https://yanqian.github.io/zh/categories/tech/index.xml" rel="self" type="application/rss+xml"/><item><title>Hey Jarvis 的未来：当 AI 需要自己的入口</title><link>https://yanqian.github.io/zh/posts/publish/building-hey-jarvis-future/</link><pubDate>Thu, 13 Aug 2026 14:13:57 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/building-hey-jarvis-future/</guid><description>&lt;p&gt;在 &lt;a href="https://yanqian.github.io/zh/posts/publish/building-hey-jarvis/" &gt;这个系列的第一篇&lt;/a&gt; 里，我写到 Hey Jarvis 来自一个很小的需求：我和老婆晚上睡觉前聊天，遇到想知道的问题时，希望不用拿起手机，直接问一句就能得到答案。&lt;/p&gt;
&lt;p&gt;后来我做出了 Pipeline、Realtime 和 Mac App，也处理了 &lt;a href="https://yanqian.github.io/zh/posts/publish/building-hey-jarvis-voice-interaction/" &gt;语音交互&lt;/a&gt; 与 &lt;a href="https://yanqian.github.io/zh/posts/publish/building-hey-jarvis-mac-product/" &gt;Mac 产品化&lt;/a&gt; 中的一系列问题。&lt;/p&gt;
&lt;p&gt;做到这里以后，我反而越来越清楚地看到一个限制：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;AI 可以越来越聪明，但如果它没有一个自然、稳定并且值得信任的入口，它仍然只是一个需要被主动打开的应用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我最初很容易把这个问题归结为一句更情绪化的话：Siri 阻碍了 AI 助手的发展。&lt;/p&gt;
&lt;p&gt;现在我觉得，这句话只说对了一半。&lt;/p&gt;
&lt;h2 id="siri-既是助手也是系统入口"&gt;
 Siri 既是助手，也是系统入口
 &lt;a class="heading-link" href="#siri-%e6%97%a2%e6%98%af%e5%8a%a9%e6%89%8b%e4%b9%9f%e6%98%af%e7%b3%bb%e7%bb%9f%e5%85%a5%e5%8f%a3"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;从使用者的角度看，Siri 最特别的能力不一定是回答得有多聪明，而是它已经存在于系统里。&lt;/p&gt;
&lt;p&gt;它拥有唤醒词、锁屏入口、麦克风权限、系统界面和设备之间的身份。使用者不需要先找到一个应用，也不需要理解哪个后台进程正在运行。说出一句话，入口就在那里。&lt;/p&gt;
&lt;p&gt;第三方 AI 应用即使拥有更好的模型，也很难获得完全相同的位置。&lt;/p&gt;
&lt;p&gt;Apple 并非完全关闭第三方能力。通过 &lt;a href="https://developer.apple.com/documentation/appintents" class="external-link" target="_blank" rel="noopener"&gt;App Intents&lt;/a&gt;，应用可以把自己的动作和内容以结构化方式提供给 Siri、Apple Intelligence、Spotlight 和 Shortcuts。2026 年的更新还继续增加了长时间后台任务、可取消任务、跨设备实体以及敏感操作确认等能力。&lt;a href="https://www.apple.com/newsroom/2026/06/apple-unveils-next-generation-of-apple-intelligence-siri-ai-and-more/" class="external-link" target="_blank" rel="noopener"&gt;Apple 也已经公布新一代 Siri AI&lt;/a&gt;，强调个人上下文、屏幕理解、网络知识和跨应用操作。&lt;/p&gt;
&lt;p&gt;但这里有一个重要区别：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;把应用的能力提供给系统助手
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;≠
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;让应用自己成为系统助手
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第三方可以把一个动作交给 Siri 调用，却不等于可以拥有自己的系统级唤醒词、在所有状态下持续等待，或者无摩擦地取代默认语音入口。&lt;/p&gt;
&lt;p&gt;一个很具体的例子是，Apple 目前确实允许第三方 conversational app 通过 iPhone 侧键启动，但&lt;a href="https://developer.apple.com/documentation/appintents/launching-your-voice-based-conversational-app-from-the-side-button-of-iphone" class="external-link" target="_blank" rel="noopener"&gt;官方的 assistant activation 能力&lt;/a&gt;只面向日本，需要专门的 Side Button Access entitlement，并受地区与设备条件限制。&lt;/p&gt;
&lt;p&gt;这说明 Apple 不是不知道第三方语音助手需要硬件入口。恰恰相反，这个入口重要到必须被平台单独定义、授权和限制。&lt;/p&gt;</description></item><item><title>Hey Jarvis 的困难 II：从 Demo 到 Mac 产品</title><link>https://yanqian.github.io/zh/posts/publish/building-hey-jarvis-mac-product/</link><pubDate>Thu, 13 Aug 2026 14:08:41 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/building-hey-jarvis-mac-product/</guid><description>&lt;p&gt;在 &lt;a href="https://yanqian.github.io/zh/posts/publish/building-hey-jarvis-voice-interaction/" &gt;上一篇&lt;/a&gt; 里，我写了 Hey Jarvis 怎样处理确认音、回声、打断和麦克风交接。&lt;/p&gt;
&lt;p&gt;当这些能力在终端和浏览器里跑通时，我一度觉得产品最困难的部分已经完成了。&lt;/p&gt;
&lt;p&gt;后来我才意识到，那只是证明了一条语音流程可以工作。&lt;/p&gt;
&lt;p&gt;一个 Demo 只需要在我准备好的环境里成功一次。一个产品则必须面对第一次安装、权限拒绝、窗口切换、电脑锁屏、系统睡眠、子进程崩溃和应用退出。它不但要成功，还要在无法成功时停在一个安全、诚实、可以恢复的状态。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Demo 证明一件事能发生。产品必须决定其他所有事情发生时怎么办。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="产品化不是给-python-加一个窗口"&gt;
 产品化不是给 Python 加一个窗口
 &lt;a class="heading-link" href="#%e4%ba%a7%e5%93%81%e5%8c%96%e4%b8%8d%e6%98%af%e7%bb%99-python-%e5%8a%a0%e4%b8%80%e4%b8%aa%e7%aa%97%e5%8f%a3"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;最早的 Hey Jarvis 由 Python 启动。Realtime 模式还需要打开一个 Chrome 页面，让浏览器负责 WebRTC 的麦克风和语音播放。&lt;/p&gt;
&lt;p&gt;这个结构适合开发，因为每一层都可以单独观察和调试。但如果把它直接包装成一个窗口，很多只存在于我电脑里的前提仍然没有消失：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;系统里已经安装了正确版本的 Python 和依赖；&lt;/li&gt;
&lt;li&gt;项目目录和 &lt;code&gt;.env&lt;/code&gt; 文件位于预期位置；&lt;/li&gt;
&lt;li&gt;Chrome 页面没有被重复打开；&lt;/li&gt;
&lt;li&gt;API Key 可以由开发环境读取；&lt;/li&gt;
&lt;li&gt;进程异常时，我知道应该去哪里看日志和怎样重新启动。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些都不是产品应该交给使用者理解的事情。&lt;/p&gt;
&lt;p&gt;在正式搭建 Mac App 之前，我先做了一个完全隔离的 Tauri 实验。它只回答一个问题：WKWebView 能不能在真实的 Apple Silicon Mac 上完成麦克风授权、Realtime 播放、自然打断、媒体释放，以及把麦克风还给 Python？&lt;/p&gt;
&lt;p&gt;实验成功以后，我没有继续在这份实验代码上追加功能，而是重新建立产品自己的应用外壳。&lt;/p&gt;
&lt;p&gt;这个选择看起来多走了一步，却帮我区分了两个很不一样的问题：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;实验：这条技术路线可不可行？
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;产品：它能否保护秘密、正确失败、恢复运行，并在另一台电脑上重现？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="三种运行环境三种责任"&gt;
 三种运行环境，三种责任
 &lt;a class="heading-link" href="#%e4%b8%89%e7%a7%8d%e8%bf%90%e8%a1%8c%e7%8e%af%e5%a2%83%e4%b8%89%e7%a7%8d%e8%b4%a3%e4%bb%bb"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;最终的 Hey Jarvis Mac App 分成三部分：&lt;/p&gt;</description></item><item><title>Hey Jarvis 的困难 I：让语音交互真正成立</title><link>https://yanqian.github.io/zh/posts/publish/building-hey-jarvis-voice-interaction/</link><pubDate>Thu, 13 Aug 2026 13:59:31 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/building-hey-jarvis-voice-interaction/</guid><description>&lt;p&gt;在 &lt;a href="https://yanqian.github.io/zh/posts/publish/building-hey-jarvis/" &gt;上一篇&lt;/a&gt; 里，我写到，Hey Jarvis 的第一个 Pipeline（串行语音处理流程）其实很快就实现了最初的需求。&lt;/p&gt;
&lt;p&gt;我说一句 “Hey Jarvis”，它录下问题，交给 AI 回答，再把答案读出来。&lt;/p&gt;
&lt;p&gt;如果只看流程图，这个语音助手已经完成了。&lt;/p&gt;
&lt;p&gt;但真正用起来以后，我发现语音交互里最困难的部分，往往不在“识别了什么”或者“回答了什么”，而在一些更基础的问题：它什么时候开始听？什么时候停止听？它能不能分清我的声音和自己的声音？一段对话结束以后，麦克风到底回到了谁手里？&lt;/p&gt;
&lt;p&gt;这些问题没有解决时，模型再聪明，使用者也会很快失去耐心。&lt;/p&gt;
&lt;h2 id="pipeline-的第一个问题它会听见自己"&gt;
 Pipeline 的第一个问题：它会听见自己
 &lt;a class="heading-link" href="#pipeline-%e7%9a%84%e7%ac%ac%e4%b8%80%e4%b8%aa%e9%97%ae%e9%a2%98%e5%ae%83%e4%bc%9a%e5%90%ac%e8%a7%81%e8%87%aa%e5%b7%b1"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;为了让我知道唤醒成功，Hey Jarvis 会先播放一句简短的确认音。&lt;/p&gt;
&lt;p&gt;最早是“在呢”，后来实时语音会话（Realtime）版本使用的是“嗯，我在，请说”。它听起来只是一个很小的体验细节，却引出了整个项目最早、也最反复的一类问题。&lt;/p&gt;
&lt;p&gt;Mac 的扬声器播放确认音时，旁边的麦克风也会收到这段声音。如果程序在确认音结束后才开始读取麦克风，那么音频缓冲区里可能还留着刚才的回声。对程序来说，这些数据和用户刚刚说出的话并没有天然区别。&lt;/p&gt;
&lt;p&gt;结果可能是这样的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;我说 “Hey Jarvis”
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ Jarvis 回答“在呢”
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ 麦克风重新收到“在呢”
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ Jarvis 以为用户已经开口
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ 录下一段错误的问题
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;p&gt;一个原本只有两个状态的问题——“在听”或“没在听”——最后变成了对回声、底噪、连续语音和录音前缓冲的共同判断。&lt;/p&gt;
&lt;h2 id="我在应该是一句承诺"&gt;
 “我在”应该是一句承诺
 &lt;a class="heading-link" href="#%e6%88%91%e5%9c%a8%e5%ba%94%e8%af%a5%e6%98%af%e4%b8%80%e5%8f%a5%e6%89%bf%e8%af%ba"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Pipeline 里的确认音主要表示唤醒词已经被识别。&lt;/p&gt;
&lt;p&gt;到了实时语音模式，它的含义必须更加严格。&lt;/p&gt;
&lt;p&gt;唤醒之后，Hey Jarvis 需要释放本地唤醒使用的麦克风，建立网络连接，完成实时对话配置，准备好播放远端声音，再开放麦克风输入。如果确认音放得太早，我听见“我在”就开始说话，但实时对话可能还没准备好，我说出的内容会直接消失。&lt;/p&gt;
&lt;p&gt;所以我后来给这句确认音定义了一个产品层面的含义：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当“嗯，我在，请说”播放完，Hey Jarvis 才应该真正准备好听我说话。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;实现这句话，需要等待两个条件：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;实时对话已经配置完成
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ＋
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;确认音已经播放完成
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;开放麦克风输入
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这两个过程可以同时进行，但任何一个没有完成，用户输入都不能开放。&lt;/p&gt;</description></item><item><title>我做了一个 Hey Jarvis：从睡前的一个问题开始</title><link>https://yanqian.github.io/zh/posts/publish/building-hey-jarvis/</link><pubDate>Thu, 13 Aug 2026 13:55:06 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/building-hey-jarvis/</guid><description>&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;语音助手本来应该最适合这个场景。可我真正想问的问题，Siri 经常回答不了；而更聪明的 AI，又往往住在一个需要主动打开的窗口里。&lt;/p&gt;
&lt;p&gt;我想要的其实很简单：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;躺在床上说一句 “Hey Jarvis”，它就能加入我们的对话，回答刚才的问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;于是我开始做 Hey Jarvis。&lt;/p&gt;
&lt;p&gt;功能演示：&lt;a href="https://www.youtube.com/watch?v=PDHQiYzFAXQ&amp;amp;t=9s" class="external-link" target="_blank" rel="noopener"&gt;&lt;strong&gt;中文版：让 Siri 级别的唤醒接上 ChatGPT&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;项目与下载：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/yanqian/hey-jarvis" class="external-link" target="_blank" rel="noopener"&gt;源代码：yanqian/hey-jarvis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/yanqian/hey-jarvis/releases/tag/v0.1.0-internal" class="external-link" target="_blank" rel="noopener"&gt;v0.1.0 INTERNAL-UNSIGNED 内部评估版&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="第一个版本其实很快就能工作"&gt;
 第一个版本，其实很快就能工作
 &lt;a class="heading-link" href="#%e7%ac%ac%e4%b8%80%e4%b8%aa%e7%89%88%e6%9c%ac%e5%85%b6%e5%ae%9e%e5%be%88%e5%bf%ab%e5%b0%b1%e8%83%bd%e5%b7%a5%e4%bd%9c"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Hey Jarvis 的第一版是一条很直接的 Pipeline，也就是一条串行语音处理流程：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;本地等待 “Hey Jarvis”
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ 播放一句确认音
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ 录下问题
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ 把语音转成文字
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ 让模型生成答案
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ 把答案转成语音并播放
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ 回到唤醒状态
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;strong&gt;能够用语音问 AI，与拥有一个语音助手，并不是同一件事。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="pipeline-能回答问题但它还不会对话"&gt;
 Pipeline 能回答问题，但它还不会对话
 &lt;a class="heading-link" href="#pipeline-%e8%83%bd%e5%9b%9e%e7%ad%94%e9%97%ae%e9%a2%98%e4%bd%86%e5%ae%83%e8%bf%98%e4%b8%8d%e4%bc%9a%e5%af%b9%e8%af%9d"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;第一版更像一个由语音启动的命令行程序。&lt;/p&gt;
&lt;p&gt;我问完一个问题，它录音、思考、播放答案，然后整段交互结束。想继续追问，就要再说一次 “Hey Jarvis”。如果回答说得太长，我不能像人与人聊天一样插话；我只能等它讲完。&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;/ul&gt;
&lt;p&gt;这些在人类交流中近乎本能的行为，到了软件里都会变成明确的状态、事件和边界。&lt;/p&gt;</description></item><item><title>看懂新加坡支付体系：从银行 App 到 SGQR、PayNow、FAST、非接触式支付与 NETS</title><link>https://yanqian.github.io/zh/posts/publish/understanding-singapore-payment-stack/</link><pubDate>Tue, 21 Jul 2026 10:50:05 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/understanding-singapore-payment-stack/</guid><description>&lt;p&gt;如果你住在新加坡，下面这些支付方式大概每天都会用到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过 PayNow 给朋友转账。&lt;/li&gt;
&lt;li&gt;在小贩中心扫描二维码付款。&lt;/li&gt;
&lt;li&gt;在超市用银行卡或手机轻触付款。&lt;/li&gt;
&lt;li&gt;在本地商户使用 NETS。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对用户来说，这些操作看起来大同小异，底层解决的却是不同的问题。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="先看全貌"&gt;
 先看全貌
 &lt;a class="heading-link" href="#%e5%85%88%e7%9c%8b%e5%85%a8%e8%b2%8c"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://yanqian.github.io/posts/publish/understanding-singapore-payment-stack/assets/singapore-payment-stack.svg" alt="新加坡支付体系"&gt;&lt;/p&gt;
&lt;p&gt;这张图有意省略了不少细节，并按照大多数人的实际支付体验来安排顺序：先是 App、银行卡或二维码，再往下进入 SGQR、PayNow 等熟悉的支付入口，最后才是商户网络、银行卡网络和资金转移通道。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="用户层银行-app银行卡与二维码"&gt;
 用户层：银行 App、银行卡与二维码
 &lt;a class="heading-link" href="#%e7%94%a8%e6%88%b7%e5%b1%82%e9%93%b6%e8%a1%8c-app%e9%93%b6%e8%a1%8c%e5%8d%a1%e4%b8%8e%e4%ba%8c%e7%bb%b4%e7%a0%81"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;大多数人付款时，不会先琢磨资金究竟要走哪条支付通道。&lt;/p&gt;
&lt;p&gt;我们最先接触的是用户界面：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DBS、OCBC、UOB 等银行的 App。&lt;/li&gt;
&lt;li&gt;DBS PayLah! 这类近似电子钱包的 App。&lt;/li&gt;
&lt;li&gt;支付 App 内置的二维码扫描器。&lt;/li&gt;
&lt;li&gt;实体银行卡。&lt;/li&gt;
&lt;li&gt;Apple Pay、Google Pay 等手机钱包。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;正是最上面的这一层，让支付用起来很简单。&lt;/p&gt;
&lt;p&gt;比如，你在小贩摊位前用银行 App 或 PayLah! 扫码，不必亲自判断该走哪个注册系统、支付方案或结算系统。App 会读取二维码，识别商户支持的支付选项，请你确认，再把交易送入相应的底层路径。&lt;/p&gt;
&lt;p&gt;可以先记住这样一个分层思路：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;用户体验层
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 支付方案或代理寻址层
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 资金转移或结算层
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;接下来要讲的，就是下面各层分别负责什么。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="sgqr统一的二维码层"&gt;
 SGQR：统一的二维码层
 &lt;a class="heading-link" href="#sgqr%e7%bb%9f%e4%b8%80%e7%9a%84%e4%ba%8c%e7%bb%b4%e7%a0%81%e5%b1%82"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;SGQR 是 &lt;strong&gt;Singapore Quick Response Code&lt;/strong&gt; 的缩写。&lt;/p&gt;</description></item><item><title>我做了一个小型 Harness，防止 AI 编程项目丢失状态</title><link>https://yanqian.github.io/zh/posts/publish/i-built-a-small-harness-to-stop-ai-coding-projects-from-forgetting-state/</link><pubDate>Tue, 21 Jul 2026 09:21:48 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/i-built-a-small-harness-to-stop-ai-coding-projects-from-forgetting-state/</guid><description>&lt;p&gt;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;/ul&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;a href="https://github.com/yanqian/ai-agent-harness-template" class="external-link" target="_blank" rel="noopener"&gt;ai-agent-harness-template&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;它的目标很简单：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;让 AI 编程项目随时都能恢复并继续推进。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="这不是提示词合集"&gt;
 这不是提示词合集
 &lt;a class="heading-link" href="#%e8%bf%99%e4%b8%8d%e6%98%af%e6%8f%90%e7%a4%ba%e8%af%8d%e5%90%88%e9%9b%86"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;现在已经有很多实用的提示词合集。&lt;/p&gt;
&lt;p&gt;但这个模板不是。&lt;/p&gt;
&lt;p&gt;它是一套仓库级 Harness，专门用于需要长期推进的 AI 编程项目。&lt;/p&gt;
&lt;p&gt;适用工具包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Codex&lt;/li&gt;
&lt;li&gt;Claude Code&lt;/li&gt;
&lt;li&gt;Cursor Agent&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;h2 id="核心思路"&gt;
 核心思路
 &lt;a class="heading-link" href="#%e6%a0%b8%e5%bf%83%e6%80%9d%e8%b7%af"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&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;ul&gt;
&lt;li&gt;&lt;code&gt;SPEC.md&lt;/code&gt;：保存需求&lt;/li&gt;
&lt;li&gt;&lt;code&gt;feature_list.json&lt;/code&gt;：保存可执行的功能状态&lt;/li&gt;
&lt;li&gt;&lt;code&gt;progress.md&lt;/code&gt;：保存恢复工作所需的记录&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;：保存智能体规则&lt;/li&gt;
&lt;li&gt;&lt;code&gt;QUALITY.md&lt;/code&gt;：保存评估标准&lt;/li&gt;
&lt;li&gt;&lt;code&gt;runs/&lt;/code&gt;：保存证据和交接记录&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样一来，后来接手的智能体、人工维护者和 CI 都以同一份信息为准。&lt;/p&gt;
&lt;p&gt;仓库现在还提供了一个可安装的 AI Agent Harness skill。&lt;/p&gt;
&lt;p&gt;这个 skill 不是另一套数据库。&lt;/p&gt;
&lt;p&gt;它只是同一套仓库状态协议的便捷入口。&lt;/p&gt;
&lt;p&gt;真正持久的记忆依然留在仓库里。&lt;/p&gt;
&lt;h2 id="为什么聊天记录不适合充当数据库"&gt;
 为什么聊天记录不适合充当数据库
 &lt;a class="heading-link" href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e8%81%8a%e5%a4%a9%e8%ae%b0%e5%bd%95%e4%b8%8d%e9%80%82%e5%90%88%e5%85%85%e5%bd%93%e6%95%b0%e6%8d%ae%e5%ba%93"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;聊天记录可以提供有用的上下文。&lt;/p&gt;</description></item><item><title>共享 Myinfo，不只是自动填表</title><link>https://yanqian.github.io/zh/posts/publish/why-sharing-myinfo-is-more-than-autofill/</link><pubDate>Tue, 21 Jul 2026 08:44:07 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/why-sharing-myinfo-is-more-than-autofill/</guid><description>&lt;p&gt;表单要求填写姓名、地址、国籍、联系方式、收入或家庭资料。这样的表单，你以前已经填过某种版本。有些服务还要求上传证明文件，再由工作人员或后台系统核对文件内容与所填信息是否一致。&lt;/p&gt;
&lt;p&gt;这时，页面上出现了一个按钮：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;获取 Myinfo 数据
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在用户眼中，这很像自动填表。&lt;/p&gt;
&lt;p&gt;这么理解不算错，但还不够完整。自动填表省的是打字；Myinfo 所做的远不止这些：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Myinfo 在取得同意后，传递经核实的声明。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;因此，Myinfo 与 Singpass QR 登录同属一套信任基础设施。&lt;/p&gt;
&lt;p&gt;QR 登录回答的是：“我怎样证明自己是谁？”&lt;/p&gt;
&lt;p&gt;Myinfo 回答的是：“我怎样授权这项服务使用关于我的特定可信信息？”&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://yanqian.github.io/posts/publish/why-sharing-myinfo-is-more-than-autofill/assets/singpass-trust-infrastructure/familiar/singpass-myinfo-profile.jpg"
 alt="Singpass Myinfo profile screen" width="280"&gt;
&lt;/figure&gt;

&lt;p&gt;这类常见的个人资料页面让用户很容易形成一个直观印象：个人信息分属若干易于理解的类别。Myinfo 真正发挥作用的地方在于，用户同意后，服务可以从中调取特定的经核实声明。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://yanqian.github.io/posts/publish/why-sharing-myinfo-is-more-than-autofill/assets/singpass-trust-infrastructure/03-myinfo-consented-claims.svg" alt="Myinfo 在取得同意后传递经核实的声明"&gt;&lt;/p&gt;
&lt;h2 id="身份验证并不会自动带来个人信息"&gt;
 身份验证并不会自动带来个人信息
 &lt;a class="heading-link" href="#%e8%ba%ab%e4%bb%bd%e9%aa%8c%e8%af%81%e5%b9%b6%e4%b8%8d%e4%bc%9a%e8%87%aa%e5%8a%a8%e5%b8%a6%e6%9d%a5%e4%b8%aa%e4%ba%ba%e4%bf%a1%e6%81%af"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;身份验证告诉服务方当前用户是谁。但对许多业务来说，光知道这一点还不够。&lt;/p&gt;
&lt;p&gt;银行可能需要身份和居住信息；保险业务可能需要人口统计资料和联系方式；政府申请则可能涉及家庭、就业或福利数据。如果 Singpass 只确认“这名用户已经登录”，用户仍得填写其余字段、上传文件，再等待核验。&lt;/p&gt;
&lt;p&gt;更深一层的问题是，各家机构都在重复同一套核验工作。缺少可信的数据共享层，每个依赖方都不得不充当一个小型身份核验系统：收集文件、留存副本、逐项检查、纠正错误，并承担相应的数据风险。&lt;/p&gt;
&lt;p&gt;Myinfo 之所以存在，是因为经核实的个人数据也应当纳入共享基础设施。&lt;/p&gt;
&lt;h2 id="用户实际看到的是什么"&gt;
 用户实际看到的是什么
 &lt;a class="heading-link" href="#%e7%94%a8%e6%88%b7%e5%ae%9e%e9%99%85%e7%9c%8b%e5%88%b0%e7%9a%84%e6%98%af%e4%bb%80%e4%b9%88"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;不妨把它直观地看成一张包含两类字段的表单。&lt;/p&gt;
&lt;p&gt;用户点击“获取 Myinfo 数据”后，一部分字段会从获准使用的数据源调取，并应显示为调取的数据；其余字段仍由用户自行填写。&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;表单字段&lt;/th&gt;
 &lt;th&gt;获取 Myinfo 数据后有什么变化&lt;/th&gt;
 &lt;th&gt;为什么重要&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;法定姓名、出生日期、国籍、登记地址&lt;/td&gt;
 &lt;td&gt;从获准使用的政府或参与机构数据源调取，并按原样显示&lt;/td&gt;
 &lt;td&gt;服务方可以将这些信息视为经核实的声明，而不是用户刚刚输入的一段文字&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;CPF 缴交记录、估税通知书、家庭或车辆记录&lt;/td&gt;
 &lt;td&gt;只有依赖方获准申请且用户明确同意，才会调取&lt;/td&gt;
 &lt;td&gt;敏感信息按范围和用途流转，而不是一次性交出整份个人资料&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;电子邮箱、偏好的联系时间、配送说明、营销偏好&lt;/td&gt;
 &lt;td&gt;仍由用户填写或修改&lt;/td&gt;
 &lt;td&gt;并非每个字段都需要政府背书；有些信息只是个人偏好&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;数据源中缺失或已经过时的信息&lt;/td&gt;
 &lt;td&gt;用户可能需要联系源头机构更新，或改走不使用 Myinfo 的流程&lt;/td&gt;
 &lt;td&gt;表单不应悄悄将来自权威数据源的信息变成可随意修改的普通文字&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;所以，改变的不只是“填表更快了”。同一张表里既有经核实的声明，也有用户自行提供的信息，还体现了服务方可以接收哪些数据的政策决定。&lt;/p&gt;
&lt;h2 id="共享的是声明不是整份个人资料"&gt;
 共享的是声明，不是整份个人资料
 &lt;a class="heading-link" href="#%e5%85%b1%e4%ba%ab%e7%9a%84%e6%98%af%e5%a3%b0%e6%98%8e%e4%b8%8d%e6%98%af%e6%95%b4%e4%bb%bd%e4%b8%aa%e4%ba%ba%e8%b5%84%e6%96%99"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;更恰当的共享单位不是“个人资料”，而是“声明”。&lt;/p&gt;</description></item><item><title>扫描 Singpass 二维码后，系统里发生了什么</title><link>https://yanqian.github.io/zh/posts/publish/what-happens-when-you-scan-a-singpass-qr-code/</link><pubDate>Tue, 21 Jul 2026 08:29:11 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/what-happens-when-you-scan-a-singpass-qr-code/</guid><description>&lt;p&gt;你在笔记本电脑上打开一家银行的网站。&lt;/p&gt;
&lt;p&gt;点击“使用 Singpass 登录”。&lt;/p&gt;
&lt;p&gt;页面上出现一个二维码。&lt;/p&gt;
&lt;p&gt;你打开 Singpass 应用，扫描二维码，核对服务名称，然后确认登录。网站随即知道可以让你登录了。&lt;/p&gt;
&lt;p&gt;在用户眼里，整个过程简单得近乎不可思议：网页显示二维码，手机完成确认，浏览器接着登录。&lt;/p&gt;
&lt;p&gt;但真正关键的问题是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;这个二维码究竟证明了什么？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;答案不是“二维码里装着我的身份信息”。&lt;/p&gt;
&lt;p&gt;设计得当的二维码登录，恰恰不该如此。二维码里不应包含你的 NRIC、姓名或个人资料，也不应放入长期有效的登录令牌。它只应是一份短期有效的邀请，请你完成一笔特定的身份验证事务。&lt;/p&gt;
&lt;p&gt;用系统设计的语言来说：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;二维码登录，不是把身份装进二维码。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;它发起的是一项短期有效的挑战，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;由可信的移动端凭据批准，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;再交给网页端的登录会话使用。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这正是整个设计的核心。&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://yanqian.github.io/posts/publish/what-happens-when-you-scan-a-singpass-qr-code/assets/singpass-trust-infrastructure/familiar/singpass-qr-login.jpg"
 alt="Singpass QR login app screen" width="280"&gt;
&lt;/figure&gt;

&lt;p&gt;这就是大家熟悉的表面体验：扫描或轻点二维码，就能登录。真正重要的是，这个二维码被允许代表什么、它会多快过期，以及手机上的批准最终会绑定到哪个浏览器会话。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://yanqian.github.io/posts/publish/what-happens-when-you-scan-a-singpass-qr-code/assets/singpass-trust-infrastructure/02-singpass-qr-login-flow.svg" alt="Singpass 二维码登录流程"&gt;&lt;/p&gt;
&lt;h2 id="二维码只是一道入口"&gt;
 二维码只是一道入口
 &lt;a class="heading-link" href="#%e4%ba%8c%e7%bb%b4%e7%a0%81%e5%8f%aa%e6%98%af%e4%b8%80%e9%81%93%e5%85%a5%e5%8f%a3"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&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;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;qr_challenge_id = &amp;#34;看似随机、短期有效的标识符&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;挑战是否已经被扫描、批准、拒绝、使用，或已经过期&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;二维码只是让移动应用向系统表明：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;我正在回应这一次特定的登录尝试。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;因此，二维码登录不能只做“生成二维码”这一步，后台还必须维护一个生命周期很短的状态机。&lt;/p&gt;
&lt;p&gt;屏幕上的流程看似简单，底层却更接近这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;已创建 -&amp;gt; 已扫描 -&amp;gt; 已批准 -&amp;gt; 已使用
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; \-&amp;gt; 已过期
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; \-&amp;gt; 已拒绝
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; \-&amp;gt; 已取消
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;状态机之所以重要，是因为登录不是一个静止的页面，而是一笔横跨两台设备的交互事务。系统必须拒绝不安全的状态变化，例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;已过期 -&amp;gt; 已批准
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;已使用 -&amp;gt; 已批准
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;已批准 -&amp;gt; 再次批准
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;重试、网络延迟或重复点击，都可能触发两次批准请求。但系统不应因此建立两个浏览器会话，也不应签发两个授权码。只有第一次有效批准可以生效。&lt;/p&gt;</description></item><item><title>为什么 Singpass 会成为国家信任基础设施</title><link>https://yanqian.github.io/zh/posts/publish/why-singpass-becomes-national-trust-infrastructure/</link><pubDate>Tue, 21 Jul 2026 08:09:37 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/why-singpass-becomes-national-trust-infrastructure/</guid><description>&lt;p&gt;乍看之下，Singpass 只是一个登录系统。&lt;/p&gt;
&lt;p&gt;打开政府网站，点击“使用 Singpass 登录”，扫描二维码，再在手机上确认，接下来就能继续办理事务。对用户而言，整个过程很简单，就像国家版的“使用 Google 登录”。&lt;/p&gt;
&lt;p&gt;但如果从第一性原理出发，重新思考这套系统应当如何设计，很快就会发现：登录只是露出水面的一角。&lt;/p&gt;
&lt;p&gt;真正的问题远比登录本身复杂：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;一个国家怎样让居民证明自己的身份，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;让各项服务信任这份证明，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;让数据在本人同意的前提下流转，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;同时确保整个过程安全、可审计？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样一问，国家数字身份平台就不再只是一项身份验证功能，而成了公共信任基础设施。&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://yanqian.github.io/posts/publish/why-singpass-becomes-national-trust-infrastructure/assets/singpass-trust-infrastructure/familiar/singpass-service-shortcuts.jpg"
 alt="Singpass app service shortcuts" width="280"&gt;
&lt;/figure&gt;

&lt;p&gt;我们熟悉的应用界面已经显露出这一点：Singpass 不仅用于登录，也正逐渐成为人们使用各类公共服务的可信入口。&lt;/p&gt;
&lt;h2 id="假如没有-singpass"&gt;
 假如没有 Singpass
 &lt;a class="heading-link" href="#%e5%81%87%e5%a6%82%e6%b2%a1%e6%9c%89-singpass"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;不妨设想一下，如果 Singpass 并不存在。&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;/ul&gt;
&lt;p&gt;没有国家数字身份平台，每个机构都得自行解决这些问题。&lt;/p&gt;
&lt;p&gt;居民要注册许多账户、记住许多密码，一遍遍上传身份证明，还要在不同表格中反复填写同样的个人资料。各机构则要自行搭建身份核验机制、保存敏感数据，并分别设计账户恢复流程。结果就是重复建设、办事受阻、风险累积。&lt;/p&gt;
&lt;p&gt;国家数字身份平台之所以必要，正是因为身份从来都是一个共同问题。&lt;/p&gt;
&lt;h2 id="第一项基础能力身份锚点"&gt;
 第一项基础能力：身份锚点
 &lt;a class="heading-link" href="#%e7%ac%ac%e4%b8%80%e9%a1%b9%e5%9f%ba%e7%a1%80%e8%83%bd%e5%8a%9b%e8%ba%ab%e4%bb%bd%e9%94%9a%e7%82%b9"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&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;身份平台应充当信任层，而不是每一项事实的所有者。&lt;/p&gt;
&lt;h2 id="第二项基础能力身份验证器"&gt;
 第二项基础能力：身份验证器
 &lt;a class="heading-link" href="#%e7%ac%ac%e4%ba%8c%e9%a1%b9%e5%9f%ba%e7%a1%80%e8%83%bd%e5%8a%9b%e8%ba%ab%e4%bb%bd%e9%aa%8c%e8%af%81%e5%99%a8"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&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;通过人脸核验，处理高风险流程&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;成熟的身份平台不会只问“用户登录了吗”，而会继续追问：“我们有多大把握确认操作者就是用户本人？这份把握足以支撑当前操作吗？”&lt;/p&gt;
&lt;h2 id="第三项基础能力身份联合"&gt;
 第三项基础能力：身份联合
 &lt;a class="heading-link" href="#%e7%ac%ac%e4%b8%89%e9%a1%b9%e5%9f%ba%e7%a1%80%e8%83%bd%e5%8a%9b%e8%ba%ab%e4%bb%bd%e8%81%94%e5%90%88"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;如果平台只能用于自家网站，价值就十分有限。&lt;/p&gt;</description></item><item><title>AI 原生软件工程（四）：氛围编程无法取代人的判断</title><link>https://yanqian.github.io/zh/posts/publish/against-vibe-coding-why-human-judgment-still-matters/</link><pubDate>Sat, 18 Jul 2026 23:53:44 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/against-vibe-coding-why-human-judgment-still-matters/</guid><description>&lt;p&gt;实现可以自动化，评估也可以自动化。但判断，始终只能由人来做。&lt;/p&gt;
&lt;p&gt;这是“AI 原生软件工程”系列的第四篇。&lt;/p&gt;
&lt;p&gt;本文接续上一篇：&lt;a href="https://yanqian.github.io/zh/posts/publish/software-is-becoming-search-why-engineers-are-turning-into-constraint-designers/" &gt;AI 原生软件工程（三）：软件即搜索&lt;/a&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;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;只要有智能体、验证框架和约束，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;软件就能自行生产出来。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;谁来决定该优化什么，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;该拒绝什么，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;又有哪些东西从一开始就值得做？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;实现不再是瓶颈。&lt;/p&gt;
&lt;p&gt;方向才是。&lt;/p&gt;
&lt;h2 id="氛围编程好用直到失控"&gt;
 氛围编程：好用，直到失控
 &lt;a class="heading-link" href="#%e6%b0%9b%e5%9b%b4%e7%bc%96%e7%a8%8b%e5%a5%bd%e7%94%a8%e7%9b%b4%e5%88%b0%e5%a4%b1%e6%8e%a7"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;大多数开发者都经历过这样的过程。&lt;/p&gt;
&lt;p&gt;一开始，你只有一个简单的想法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;把 X 做出来。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;直到某个时刻，问题突然一股脑冒了出来：&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;系统依然能运行。&lt;/p&gt;
&lt;p&gt;但已经没人说得清，它为什么能运行。&lt;/p&gt;
&lt;p&gt;项目陷入一种奇怪的状态：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;好像一切都能推倒重来。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;却没有任何东西真正处于掌控之中。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我把这叫作氛围编程。&lt;/p&gt;
&lt;p&gt;并不是因为 AI 不好。&lt;/p&gt;
&lt;p&gt;而是因为，每一步进展都不再对应一个经过认真权衡的决定。&lt;/p&gt;
&lt;h2 id="测试通过带来的错觉"&gt;
 测试通过带来的错觉
 &lt;a class="heading-link" href="#%e6%b5%8b%e8%af%95%e9%80%9a%e8%bf%87%e5%b8%a6%e6%9d%a5%e7%9a%84%e9%94%99%e8%a7%89"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;面对这种问题，一个常见的回答是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;多加一些测试就好了。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我也这样做过。&lt;/p&gt;</description></item><item><title>AI 原生软件工程（三）：软件即搜索</title><link>https://yanqian.github.io/zh/posts/publish/software-is-becoming-search-why-engineers-are-turning-into-constraint-designers/</link><pubDate>Sat, 18 Jul 2026 23:37:09 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/software-is-becoming-search-why-engineers-are-turning-into-constraint-designers/</guid><description>&lt;p&gt;当实现变得唾手可得，工程就不再那么像建造，而更像导航。&lt;/p&gt;
&lt;p&gt;这是“AI 原生软件工程”系列的第三篇。&lt;/p&gt;
&lt;p&gt;本文承接上一篇：&lt;a href="https://yanqian.github.io/zh/posts/publish/harness-engineering-is-about-limiting-ai-not-empowering-it/" &gt;AI 原生软件工程（二）：验证框架工程与正确性&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;第一篇追问：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;当实现工作交给 AI 后，理解从何而来？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第二篇追问：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;当生成的成本变得低廉，如何保证正确性？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这一篇要问：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;如果实现越来越便宜，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;工程师究竟在做什么？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这里，约束的含义发生了变化。&lt;/p&gt;
&lt;p&gt;第二篇谈约束，是为了保证正确性。&lt;/p&gt;
&lt;p&gt;这一篇谈约束，是为了界定搜索范围：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;如何划定可接受实现的范围？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;写代码。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;熟记更多 API&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;而是因为它让实现成本骤降。&lt;/p&gt;
&lt;p&gt;一旦实现变得便宜，软件开发的方式也会随之改变。&lt;/p&gt;
&lt;p&gt;它开始变得像一场搜索。&lt;/p&gt;
&lt;h2 id="传统工程在实现稀缺时做建造"&gt;
 传统工程：在实现稀缺时做建造
 &lt;a class="heading-link" href="#%e4%bc%a0%e7%bb%9f%e5%b7%a5%e7%a8%8b%e5%9c%a8%e5%ae%9e%e7%8e%b0%e7%a8%80%e7%bc%ba%e6%97%b6%e5%81%9a%e5%bb%ba%e9%80%a0"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;传统的软件开发大致遵循这条路径：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;想法
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 架构
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 实现
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 验证
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;h2 id="ai-让建造变成探索"&gt;
 AI 让建造变成探索
 &lt;a class="heading-link" href="#ai-%e8%ae%a9%e5%bb%ba%e9%80%a0%e5%8f%98%e6%88%90%e6%8e%a2%e7%b4%a2"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&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;多套 API 设计&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;于是，工作流程变成了：&lt;/p&gt;</description></item><item><title>AI 原生软件工程（二）：验证框架工程与正确性</title><link>https://yanqian.github.io/zh/posts/publish/harness-engineering-is-about-limiting-ai-not-empowering-it/</link><pubDate>Sat, 18 Jul 2026 23:29:59 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/harness-engineering-is-about-limiting-ai-not-empowering-it/</guid><description>&lt;p&gt;在 AI 原生软件工程中，最重要的也许不是生成，而是约束。&lt;/p&gt;
&lt;p&gt;这是「AI 原生软件工程」系列的第二篇。&lt;/p&gt;
&lt;p&gt;本文承接上一篇：&lt;a href="https://yanqian.github.io/zh/posts/publish/agentic-coding-mental-models-and-the-new-depth-of-software-engineering/" &gt;AI 原生软件工程（一）：智能体编程中的心智模型&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;上一篇讨论的问题是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;如果理解系统不再主要依靠亲手写代码，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;人类要如何在智能体工作流中建立心智模型？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;接下来的问题是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;如果实现工作交给了 AI，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;正确性从何而来？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;工作流能够自我修复。&lt;/p&gt;
&lt;p&gt;但用 AI 智能体做了一段时间项目后，我却得出了相反的结论：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;最重要的工程问题，不是如何让 AI 更自主。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;而是如何更严格地约束 AI。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;怎样才能让 AI 生成的实现可以被放心信任？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="传统软件开发的基本假设"&gt;
 传统软件开发的基本假设
 &lt;a class="heading-link" href="#%e4%bc%a0%e7%bb%9f%e8%bd%af%e4%bb%b6%e5%bc%80%e5%8f%91%e7%9a%84%e5%9f%ba%e6%9c%ac%e5%81%87%e8%ae%be"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;传统软件开发建立在一个简单的假设上：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;人
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 实现
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 验证
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;h2 id="ai-打破了这个假设"&gt;
 AI 打破了这个假设
 &lt;a class="heading-link" href="#ai-%e6%89%93%e7%a0%b4%e4%ba%86%e8%bf%99%e4%b8%aa%e5%81%87%e8%ae%be"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;智能体编程改变了原有结构。&lt;/p&gt;
&lt;p&gt;现在，整个循环变成了：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;人
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 意图
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;AI
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 实现
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;人
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 审查
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;乍看之下，效率很高。&lt;/p&gt;</description></item><item><title>AI 原生软件工程（一）：智能体编程中的心智模型</title><link>https://yanqian.github.io/zh/posts/publish/agentic-coding-mental-models-and-the-new-depth-of-software-engineering/</link><pubDate>Sat, 18 Jul 2026 22:57:49 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/agentic-coding-mental-models-and-the-new-depth-of-software-engineering/</guid><description>&lt;p&gt;AI 能生成代码。验证框架能检验行为。但理解由谁来建立？&lt;/p&gt;
&lt;p&gt;这是“AI 原生软件工程”系列的第一篇。&lt;/p&gt;
&lt;p&gt;这个系列想追问一个更大的问题：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;当 AI 降低了实现成本，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;软件工程中还有什么是稀缺的？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;本文先从第一种稀缺资源谈起：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;理解。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SPEC
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 约束
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 验证框架
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 智能体执行
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 评估
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 迭代
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;如果 AI 明天消失，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;我可能很难继续开发自己系统里的某些部分。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个认识改变了我看待软件工程的方式。&lt;/p&gt;
&lt;h2 id="传统模式在实现过程中形成理解"&gt;
 传统模式：在实现过程中形成理解
 &lt;a class="heading-link" href="#%e4%bc%a0%e7%bb%9f%e6%a8%a1%e5%bc%8f%e5%9c%a8%e5%ae%9e%e7%8e%b0%e8%bf%87%e7%a8%8b%e4%b8%ad%e5%bd%a2%e6%88%90%e7%90%86%e8%a7%a3"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;过去的软件工程，大致是这样的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;人
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 编写代码
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 建立心智模型
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; 运行和维护系统
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;犯错&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;亲手实现，自然会带来理解。&lt;/p&gt;
&lt;p&gt;只是过去，我们未必意识到这一点。&lt;/p&gt;
&lt;h2 id="智能体编程改变了路径"&gt;
 智能体编程改变了路径
 &lt;a class="heading-link" href="#%e6%99%ba%e8%83%bd%e4%bd%93%e7%bc%96%e7%a8%8b%e6%94%b9%e5%8f%98%e4%ba%86%e8%b7%af%e5%be%84"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;有了 AI 编程，整个循环变了。&lt;/p&gt;</description></item><item><title>播客摘要工具让我明白：摘要不等于理解</title><link>https://yanqian.github.io/zh/posts/publish/a-podcast-summarizer-taught-me-that-summaries-are-not-understanding/</link><pubDate>Sat, 18 Jul 2026 12:13:29 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/a-podcast-summarizer-taught-me-that-summaries-are-not-understanding/</guid><description>&lt;p&gt;有人看到我简历上的一个项目，问了我一个很简单的问题：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;这个软件真的解决了你的问题吗？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个问题问得很好。&lt;/p&gt;
&lt;p&gt;那个项目是一款播客摘要工具。&lt;/p&gt;
&lt;p&gt;源代码公开在这里：&lt;a href="https://github.com/yanqian/podcast-summarizer" class="external-link" target="_blank" rel="noopener"&gt;yanqian/podcast-summarizer&lt;/a&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;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;长内容
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 转写文本
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 摘要
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 理解
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;它能导入播客、处理音频、生成转写片段和摘要片段，还能显示两者之间的对应关系。&lt;/p&gt;
&lt;p&gt;作为工程演示，它是成功的。&lt;/p&gt;
&lt;p&gt;但作为帮助人理解内容的工具，它没有解决真正的问题。&lt;/p&gt;
&lt;h2 id="最初的问题并不是内容太长"&gt;
 最初的问题并不是内容太长
 &lt;a class="heading-link" href="#%e6%9c%80%e5%88%9d%e7%9a%84%e9%97%ae%e9%a2%98%e5%b9%b6%e4%b8%8d%e6%98%af%e5%86%85%e5%ae%b9%e5%a4%aa%e9%95%bf"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&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;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;把内容变短
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;
&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;讨论过哪些取舍&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;h2 id="摘要会抹掉通往结论的过程"&gt;
 摘要会抹掉通往结论的过程
 &lt;a class="heading-link" href="#%e6%91%98%e8%a6%81%e4%bc%9a%e6%8a%b9%e6%8e%89%e9%80%9a%e5%be%80%e7%bb%93%e8%ae%ba%e7%9a%84%e8%bf%87%e7%a8%8b"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&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;</description></item></channel></rss>