<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on Armstrong Yan</title><link>https://yanqian.github.io/zh/posts/</link><description>Recent content in Posts 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/posts/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>当城市开始“没有足够的垃圾可烧”</title><link>https://yanqian.github.io/zh/posts/publish/when-cities-run-out-of-garbage-to-burn/</link><pubDate>Wed, 22 Jul 2026 12:36:45 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/when-cities-run-out-of-garbage-to-burn/</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;/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;h2 id="焚烧的首要目的并不是发电"&gt;
 焚烧的首要目的并不是发电
 &lt;a class="heading-link" href="#%e7%84%9a%e7%83%a7%e7%9a%84%e9%a6%96%e8%a6%81%e7%9b%ae%e7%9a%84%e5%b9%b6%e4%b8%8d%e6%98%af%e5%8f%91%e7%94%b5"&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;p&gt;按照新加坡官方的说法，焚烧可将垃圾体积减少约90%。&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&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;大量日常垃圾 -&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;h2 id="新加坡早已进入下一个阶段"&gt;
 新加坡早已进入下一个阶段
 &lt;a class="heading-link" href="#%e6%96%b0%e5%8a%a0%e5%9d%a1%e6%97%a9%e5%b7%b2%e8%bf%9b%e5%85%a5%e4%b8%8b%e4%b8%80%e4%b8%aa%e9%98%b6%e6%ae%b5"&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;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&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;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;img src="https://yanqian.github.io/posts/publish/when-cities-run-out-of-garbage-to-burn/assets/when-cities-run-out-of-garbage-to-burn/06-general-and-recycling-bins.jpg" alt="新加坡的普通垃圾桶与蓝色回收桶并排摆放"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;两个普通的桶，通向两条不同的路线。一般垃圾与洁净可回收物之间的这次小小分流，正是整套城市系统的起点。&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="城市运送的不只是垃圾"&gt;
 城市运送的不只是垃圾
 &lt;a class="heading-link" href="#%e5%9f%8e%e5%b8%82%e8%bf%90%e9%80%81%e7%9a%84%e4%b8%8d%e5%8f%aa%e6%98%af%e5%9e%83%e5%9c%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;</description></item><item><title>用 Obsidian 和 Hugo 搭建人工审核的双语发布流水线</title><link>https://yanqian.github.io/zh/posts/publish/building-a-human-gated-bilingual-publishing-pipeline-with-obsidian-and-hugo/</link><pubDate>Wed, 22 Jul 2026 00:18:29 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/building-a-human-gated-bilingual-publishing-pipeline-with-obsidian-and-hugo/</guid><description>&lt;p&gt;在&lt;a href="https://yanqian.github.io/posts/publish/building-a-personal-blog-with-obsidian-hugo-and-github-pages/" class="external-link" target="_blank" rel="noopener"&gt;用 Obsidian、Hugo 和 GitHub Pages 搭建从私有笔记到公开网站的发布流水线&lt;/a&gt;一文中，我介绍了如何划清边界，将私有仓库与公开网站分开。&lt;/p&gt;
&lt;p&gt;那套方案解决了文章应该存放在哪里，却留下了另一个问题：同一篇文章需要以两种语言发布时，该怎么办？&lt;/p&gt;
&lt;p&gt;起初，我以为双语发布无非是翻译：把 Markdown 交给模型，让它生成中文或英文，再将结果保存在原文旁边。真正动手后我才发现，翻译反而是整个问题中最简单的一环。&lt;/p&gt;
&lt;p&gt;真正棘手的是一系列架构问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;哪个版本才是唯一可信的原文？&lt;/li&gt;
&lt;li&gt;改写正文时，怎样确保代码块、链接、图片和 Hugo 元数据完好无损？&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;blockquote&gt;
&lt;p&gt;文章在 Obsidian 中写作，发布工具的代码在 Hugo 仓库中维护。生成的草稿未经人工批准，一律不得进入网站。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://yanqian.github.io/posts/publish/building-a-human-gated-bilingual-publishing-pipeline-with-obsidian-and-hugo/assets/bilingual-publishing-pipeline/human-gated-bilingual-pipeline.svg" alt="人工审核的双语发布流水线"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="原文始终是唯一可信版本"&gt;
 原文始终是唯一可信版本
 &lt;a class="heading-link" href="#%e5%8e%9f%e6%96%87%e5%a7%8b%e7%bb%88%e6%98%af%e5%94%af%e4%b8%80%e5%8f%af%e4%bf%a1%e7%89%88%e6%9c%ac"&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;英文原文 -&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;最初的 Obsidian 笔记始终是权威原文。本地化文章只是从中生成的派生版本，不是另一份需要独立维护的原稿。&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-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&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-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;series&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Remote Agent Workflow&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;seriesOrder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;系统只根据 &lt;code&gt;seriesOrder&lt;/code&gt; 选择系列中的下一篇文章，绝不随机挑选。这看似只是一条不起眼的操作规则，却能避免自动化系统将内容本身没有问题的文章按错误的叙事顺序发布。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="为什么一次翻译远远不够"&gt;
 为什么一次翻译远远不够
 &lt;a class="heading-link" href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%80%e6%ac%a1%e7%bf%bb%e8%af%91%e8%bf%9c%e8%bf%9c%e4%b8%8d%e5%a4%9f"&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;如果要求一个提示词一次完成所有工作，它承担的任务就太多了：既要理解论证，又要用自然的目标语言写作，还要保留所有事实，并确保 Markdown 毫发无损。这些要求混在一起，产出的文章即使流畅，也可能出错；即使忠实，也可能满是翻译腔。&lt;/p&gt;
&lt;p&gt;因此，我把整个过程拆成四个阶段。&lt;/p&gt;
&lt;h3 id="0-理解文章"&gt;
 0. 理解文章
 &lt;a class="heading-link" href="#0-%e7%90%86%e8%a7%a3%e6%96%87%e7%ab%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;/h3&gt;
&lt;p&gt;第一轮不做翻译，只提取文章的核心主张、目标读者、推理链、事实、术语、语气和结构元素。&lt;/p&gt;</description></item><item><title>从马斯克谈远程办公，想到“隐藏前提”与批判性思维</title><link>https://yanqian.github.io/zh/posts/publish/hidden-premises-and-critical-thinking/</link><pubDate>Tue, 21 Jul 2026 12:08:52 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/hidden-premises-and-critical-thinking/</guid><description>&lt;blockquote&gt;
&lt;p&gt;我是如何识别一个看似合理的论证中，没有被说出来的东西&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;今天刷到一段&lt;a href="https://www.cnbc.com/2023/05/16/elon-musk-work-from-home-morally-wrong-when-some-have-to-show-up.html" class="external-link" target="_blank" rel="noopener"&gt;马斯克接受 CNBC 采访&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;strong&gt;我为什么会被这段话说服？它的结论，真的是从事实中自然推导出来的吗？&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="这听起来很公平"&gt;
 这听起来很公平
 &lt;a class="heading-link" href="#%e8%bf%99%e5%90%ac%e8%b5%b7%e6%9d%a5%e5%be%88%e5%85%ac%e5%b9%b3"&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;blockquote&gt;
&lt;p&gt;公平，就是所有人承受相同的限制。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但这并不是公平唯一的含义。&lt;/p&gt;
&lt;p&gt;公平也可以是：面对不同的工作条件，允许不同的安排；或者在暂时无法让所有人受益时，先让能够受益的人受益，再想办法把这种可能性扩大到更多人。&lt;/p&gt;
&lt;p&gt;争议并不在于我们要不要公平，而在于：&lt;strong&gt;究竟哪一种公平，才是我们想要的公平？&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="如果换一个例子呢"&gt;
 如果换一个例子呢？
 &lt;a class="heading-link" href="#%e5%a6%82%e6%9e%9c%e6%8d%a2%e4%b8%80%e4%b8%aa%e4%be%8b%e5%ad%90%e5%91%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;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;因为我们通常并不认为，消除不平等的办法是取消一部分人已经拥有的改善。更合理的方向应该是：保护已有的改善，同时设法让更多人也能获得相应的便利、补偿或选择。&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%9c%9f%e6%ad%a3%e6%9c%89%e4%ba%89%e8%ae%ae%e7%9a%84%e6%98%af%e6%b2%a1%e6%9c%89%e8%af%b4%e5%87%ba%e5%8f%a3%e7%9a%84%e9%82%a3%e5%8f%a5%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;ol&gt;
&lt;li&gt;有些工作必须到现场完成。&lt;/li&gt;
&lt;li&gt;如果一种工作安排不能提供给所有人，那么只提供给一部分人就是不公平的。&lt;/li&gt;
&lt;li&gt;所以，远程办公在道德上有问题。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;第一步是事实判断，第三步是结论。真正决定结论的，其实是第二步：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;如果一种福利不能属于所有人，那么任何人都不应该拥有它。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&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="#%e8%b0%81%e5%86%b3%e5%ae%9a%e4%ba%86%e6%af%94%e8%be%83%e6%a0%87%e5%87%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;blockquote&gt;
&lt;p&gt;我们没有像别的公司 996。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这句话不一定在事实上说错了。它的问题是，它提前替员工选择了比较对象。&lt;/p&gt;
&lt;p&gt;当一家公司拿自己的工作制度和 996 比较时，现有的加班就显得没那么糟。可是员工真正关心的问题也许是：为什么不和那些真正尊重工作与生活平衡的公司比较？&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%88%91%e7%9c%9f%e6%ad%a3%e5%ad%a6%e5%88%b0%e7%9a%84%e4%b8%8d%e6%98%af%e8%af%a5%e4%b8%8d%e8%af%a5%e8%bf%9c%e7%a8%8b%e5%8a%9e%e5%85%ac"&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;回头看，这段采访带给我最大的收获，并不是关于 WFH 的立场，而是一次具体的思考练习。&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>新加坡退休储蓄：CPF SA、RSTU 与 SRS</title><link>https://yanqian.github.io/zh/posts/publish/singapore-retirement-savings-cpf-sa-rstu-srs/</link><pubDate>Tue, 21 Jul 2026 10:04:38 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/singapore-retirement-savings-cpf-sa-rstu-srs/</guid><description>&lt;p&gt;新加坡有好几项与退休有关的制度，名称听起来相近，作用却截然不同。&lt;/p&gt;
&lt;p&gt;最容易混淆的是以下三个概念：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPF 特别账户（CPF Special Account），即 &lt;strong&gt;CPF SA&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;退休存款填补计划（Retirement Sum Topping-Up Scheme），即 &lt;strong&gt;RSTU&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;辅助退休计划（Supplementary Retirement Scheme），即 &lt;strong&gt;SRS&lt;/strong&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;CPF SA = 国家退休储蓄账户
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;RSTU = 向 CPF 退休储蓄补充现金并享受税务减免的途径
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SRS = 自愿参与、延后纳税的投资账户
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;本文提供的是一套理解这些制度的思路，并非税务建议。新加坡的税务和 CPF 规则可能调整。实际能获得多少好处，取决于你的税务居民身份、收入、CPF 余额、税务减免上限、提款时间，以及是否打算长期留在新加坡。&lt;/p&gt;
&lt;h2 id="三个概念分别是什么"&gt;
 三个概念分别是什么
 &lt;a class="heading-link" href="#%e4%b8%89%e4%b8%aa%e6%a6%82%e5%bf%b5%e5%88%86%e5%88%ab%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;h3 id="cpf-特别账户"&gt;
 CPF 特别账户
 &lt;a class="heading-link" href="#cpf-%e7%89%b9%e5%88%ab%e8%b4%a6%e6%88%b7"&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;/h3&gt;
&lt;p&gt;CPF 特别账户属于新加坡中央公积金（Central Provident Fund）体系。&lt;/p&gt;
&lt;p&gt;对于未满 55 岁的会员，特别账户主要用于积累退休储蓄。它既不是普通银行账户，也不是证券账户。它的一大吸引力是按 CPF 规定的利率计息。从历史情况看，由于设有利率下限，其回报相较低风险现金类产品颇具吸引力。&lt;/p&gt;
&lt;p&gt;代价也很明确：资金用途受到严格限制，不能随时动用。CPF SA 并不适合存放需要随时取用的钱。&lt;/p&gt;
&lt;p&gt;直白地说：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;CPF SA 是一个较安全、由国家制度支持的退休储蓄账户。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;它的优势很突出，用途也非常明确。如果一笔钱近期可能要用，就不适合放在这里。&lt;/p&gt;
&lt;h3 id="rstu"&gt;
 RSTU
 &lt;a class="heading-link" href="#rstu"&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;/h3&gt;
&lt;p&gt;RSTU 是 Retirement Sum Topping-Up Scheme，即退休存款填补计划。&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>用 Obsidian、Hugo 和 GitHub Pages 搭建从私密笔记到公开网站的发布流程</title><link>https://yanqian.github.io/zh/posts/publish/building-a-personal-blog-with-obsidian-hugo-and-github-pages/</link><pubDate>Tue, 21 Jul 2026 09:11:29 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/building-a-personal-blog-with-obsidian-hugo-and-github-pages/</guid><description>&lt;p&gt;最近，我重新搭建了个人网站。出发点很简单：&lt;/p&gt;
&lt;p&gt;继续把 Obsidian 作为私密内容的唯一可信来源，只从中挑选适合公开的笔记，发布到一个干净的个人网站上。&lt;/p&gt;
&lt;p&gt;这篇文章与其说是 Hugo 教程，不如说是在讨论一个问题：私密知识库与公开网站之间的发布边界，究竟该怎么划定。&lt;/p&gt;
&lt;p&gt;下面记录的是我最终采用的架构、做过的取舍，以及实际的发布流程。&lt;/p&gt;
&lt;p&gt;文中的仓库名、URL 和网站页面都来自我自己的配置。如果你也想采用这套方案，请换成自己的 GitHub 用户名、仓库名、域名和导航结构。&lt;/p&gt;
&lt;p&gt;最终，我搭出了一条从私密内容通往公开网站的发布管线：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://yanqian.github.io/posts/publish/building-a-personal-blog-with-obsidian-hugo-and-github-pages/assets/blog-publishing-pipeline/01-publishing-architecture.svg" alt="从私密内容到公开网站的发布管线"&gt;&lt;/p&gt;
&lt;p&gt;它实现了这些效果：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我仍然可以在 Obsidian 里自由地写笔记。&lt;/li&gt;
&lt;li&gt;私密目录结构永远不会暴露在公开网站上。&lt;/li&gt;
&lt;li&gt;公开笔记可以用一条命令发布。&lt;/li&gt;
&lt;li&gt;GitHub Pages 会自动完成部署。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="为什么我没有直接从-obsidian-发布"&gt;
 为什么我没有直接从 Obsidian 发布
 &lt;a class="heading-link" href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e6%88%91%e6%b2%a1%e6%9c%89%e7%9b%b4%e6%8e%a5%e4%bb%8e-obsidian-%e5%8f%91%e5%b8%83"&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;很多教程都建议直接发布 Obsidian 仓库里的内容。&lt;/p&gt;
&lt;p&gt;但我没有这么做。&lt;/p&gt;
&lt;p&gt;因为我的 Obsidian 仓库不只是写作空间，还是我的私密知识系统。&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;00-Inbox/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Projects/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Career/
&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;ul&gt;
&lt;li&gt;私密目录结构会出现在公开 URL 中&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;00-Inbox/Kafka 设计.md
&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;/posts/00-inbox/kafka-design/
&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;code&gt;Publish/&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这个目录是可公开内容的一份干净投影。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://yanqian.github.io/posts/publish/building-a-personal-blog-with-obsidian-hugo-and-github-pages/assets/blog-publishing-pipeline/02-vault-boundary-before-after.svg" alt="引入发布边界前后的仓库结构"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="最终架构"&gt;
 最终架构
 &lt;a class="heading-link" href="#%e6%9c%80%e7%bb%88%e6%9e%b6%e6%9e%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;最终的架构将写作结构与发布结构分开了。&lt;/p&gt;
&lt;p&gt;事实证明，这是整套方案中最关键的设计决定：私密仓库仍然可以围绕思考与整理来组织，公开网站接收到的则只有干净、适合发布的 Markdown。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="第-1-步--创建-hugo-网站"&gt;
 第 1 步 — 创建 Hugo 网站
 &lt;a class="heading-link" href="#%e7%ac%ac-1-%e6%ad%a5--%e5%88%9b%e5%bb%ba-hugo-%e7%bd%91%e7%ab%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;先安装 Hugo。&lt;/p&gt;</description></item><item><title>远程智能体工作流</title><link>https://yanqian.github.io/zh/posts/publish/remote-agent-workflow/</link><pubDate>Tue, 21 Jul 2026 09:03:39 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/remote-agent-workflow/</guid><description>&lt;p&gt;这个系列探讨如何从移动端 SSH 走向一套以代码仓库为依托、可以长期运行的 AI 智能体工作流。&lt;/p&gt;
&lt;p&gt;起因很简单：离开 Mac 后，我仍然想继续推进本地的 Codex 工作。&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;h2 id="系列文章"&gt;
 系列文章
 &lt;a class="heading-link" href="#%e7%b3%bb%e5%88%97%e6%96%87%e7%ab%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;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://yanqian.github.io/posts/publish/remote-mac-terminal-for-codex/" &gt;远程智能体工作流（一）：用手机连接 Mac 运行 Codex&lt;/a&gt;&lt;br&gt;
借助 Tailscale、SSH、Termius、tmux 和 caffeinate，打通手机到 Mac 的终端连接。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://yanqian.github.io/posts/publish/from-remote-shell-to-agent-control-plane/" &gt;远程智能体工作流（二）：从远程 Shell 到智能体控制面&lt;/a&gt;&lt;br&gt;
解释为什么移动端 SSH 虽然是实用的基础设施，却不适合充当长期智能体任务的日常操作界面。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://yanqian.github.io/posts/publish/turning-telegram-into-a-local-codex-control-plane/" &gt;远程智能体工作流（三）：把 Telegram 变成本地 Codex 的控制面&lt;/a&gt;&lt;br&gt;
让 Telegram Bot 以轮询模式运行，把它变成一个轻量的移动端控制界面，用手机就能指挥本地 Codex 任务。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://yanqian.github.io/posts/publish/in-the-repository-not-in-the-chat/" &gt;远程智能体工作流（四）：在仓库中，而非聊天中&lt;/a&gt;&lt;br&gt;
让代码仓库同时承载智能体记忆、运行约定，并成为 Agent Harness。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://yanqian.github.io/posts/publish/what-still-matters-after-codex-mobile/" &gt;远程智能体工作流（五）：Codex Mobile 之后，什么依然重要&lt;/a&gt;&lt;br&gt;
官方 Codex Mobile 推出后，重新审视这套自建工作流：它不是替代品，而是项目侧的工作流适配器。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="核心演进路径"&gt;
 核心演进路径
 &lt;a class="heading-link" href="#%e6%a0%b8%e5%bf%83%e6%bc%94%e8%bf%9b%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;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;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 基于仓库的 Agent Harness
&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>共享 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>远程智能体工作流（五）：Codex Mobile 之后，什么依然重要</title><link>https://yanqian.github.io/zh/posts/publish/what-still-matters-after-codex-mobile/</link><pubDate>Mon, 20 Jul 2026 23:52:52 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/what-still-matters-after-codex-mobile/</guid><description>&lt;p&gt;这是“远程智能体工作流”系列的第五篇。&lt;/p&gt;
&lt;p&gt;最初搭建这套远程 AI 开发环境时，我想解决一个很实际的问题：&lt;/p&gt;
&lt;p&gt;离开 Mac 以后，怎样让本地 Codex 继续干活？&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; Tailscale -&amp;gt; SSH -&amp;gt; Mac -&amp;gt; tmux -&amp;gt; Codex
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;后来我发现，手机上的 SSH 不适合充当日常操作入口，于是又搭了一个 Telegram 控制面：&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; Telegram Bot -&amp;gt; 本地 Codex 运行时 -&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;放在代码仓库里，不要放在聊天里。
&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;既然官方已经推出 Codex Mobile，自定义远程智能体工作流还有必要吗？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我的答案是：有。但理由变了。&lt;/p&gt;
&lt;p&gt;它的价值不再只是让人远程接入 Codex。&lt;/p&gt;
&lt;p&gt;更重要的是让 Codex 按照项目自己的方式工作。&lt;/p&gt;
&lt;h2 id="codex-mobile-解决了什么"&gt;
 Codex Mobile 解决了什么
 &lt;a class="heading-link" href="#codex-mobile-%e8%a7%a3%e5%86%b3%e4%ba%86%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;2026 年 5 月 14 日，OpenAI 宣布在 ChatGPT 移动应用中以预览形式推出 Codex。&lt;/p&gt;
&lt;p&gt;通过官方移动端，用户可以随时在手机上接入 Codex。按照 OpenAI 的介绍，你可以跨线程处理任务、检查输出、批准命令、切换模型、发起新任务，还能实时收到终端输出、差异内容、测试结果、截图和审批请求等更新。&lt;/p&gt;</description></item><item><title>远程智能体工作流（四）：在仓库中，而非聊天中</title><link>https://yanqian.github.io/zh/posts/publish/in-the-repository-not-in-the-chat/</link><pubDate>Mon, 20 Jul 2026 23:33:28 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/in-the-repository-not-in-the-chat/</guid><description>&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;手机 -&amp;gt; Tailscale -&amp;gt; SSH -&amp;gt; Mac -&amp;gt; tmux -&amp;gt; Codex
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;手机 -&amp;gt; Telegram Bot -&amp;gt; 本地 Codex 运行时 -&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;p&gt;于是，我给自己定了一条规则：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;聊天用来传达指令。&lt;br&gt;
仓库用来保存记忆。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;更准确地说，仓库不只是存放代码的地方。它还承载着智能体的记忆和操作约定，并为智能体提供持续的反馈机制。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://yanqian.github.io/posts/publish/in-the-repository-not-in-the-chat/assets/in-the-repository-not-in-the-chat/01-repository-agent-harness.svg" alt="作为 Agent Harness 的仓库"&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%e8%83%bd%e5%85%85%e5%bd%93%e9%a1%b9%e7%9b%ae%e7%8a%b6%e6%80%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;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;/ul&gt;
&lt;p&gt;这些答案应该和代码放在一起。&lt;/p&gt;
&lt;h2 id="让仓库同时承担记忆与-agent-harness"&gt;
 让仓库同时承担记忆与 Agent Harness
 &lt;a class="heading-link" href="#%e8%ae%a9%e4%bb%93%e5%ba%93%e5%90%8c%e6%97%b6%e6%89%bf%e6%8b%85%e8%ae%b0%e5%bf%86%e4%b8%8e-agent-harness"&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;ul&gt;
&lt;li&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/li&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;test_plan.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;init.sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;orchestrator.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;git 历史&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;文件具体叫什么并不重要，重要的是分清职责。&lt;/p&gt;</description></item><item><title>远程智能体工作流（三）：把 Telegram 变成本地 Codex 的控制面</title><link>https://yanqian.github.io/zh/posts/publish/turning-telegram-into-a-local-codex-control-plane/</link><pubDate>Mon, 20 Jul 2026 23:20:13 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/turning-telegram-into-a-local-codex-control-plane/</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;手机 -&amp;gt; Tailscale -&amp;gt; SSH -&amp;gt; Mac -&amp;gt; tmux -&amp;gt; Codex
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;有了这条链路，我就能可靠地从手机连接自己的 Mac。&lt;/p&gt;
&lt;p&gt;但新的局限很快就暴露出来：手机并不好用来操作终端。&lt;/p&gt;
&lt;p&gt;移动端 SSH 适合救急，但我不想靠它处理需要长时间运行的 AI 开发任务。我不想在狭窄的手机屏幕上敲 shell 命令、连接 tmux 会话、翻找长篇日志，再一点点拼凑出当前状态。&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;于是，我做了一个小型 Telegram Bot，把它用作本地 Codex 的控制面。&lt;/p&gt;
&lt;p&gt;关键不在于 Telegram 有多特别。&lt;/p&gt;
&lt;p&gt;真正重要的是：手机成为控制界面，而 Mac 仍然负责执行工作。&lt;/p&gt;
&lt;h2 id="为什么选-telegram"&gt;
 为什么选 Telegram
 &lt;a class="heading-link" href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e9%80%89-telegram"&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;Telegram 很适合用来做这个实验，因为它已经具备：&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;成熟的 Bot API&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;更重要的是，Telegram 支持两种截然不同的部署模式。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://yanqian.github.io/posts/publish/turning-telegram-into-a-local-codex-control-plane/assets/turning-telegram-into-a-local-codex-control-plane/01-webhook-vs-polling.svg" alt="Telegram 的 webhook 与 polling 模式"&gt;&lt;/p&gt;
&lt;p&gt;第一种是 webhook 模式：&lt;/p&gt;</description></item><item><title>远程智能体工作流（二）：从远程 Shell 到智能体控制面</title><link>https://yanqian.github.io/zh/posts/publish/from-remote-shell-to-agent-control-plane/</link><pubDate>Mon, 20 Jul 2026 22:33:24 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/from-remote-shell-to-agent-control-plane/</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;手机 -&amp;gt; Tailscale -&amp;gt; SSH -&amp;gt; Mac -&amp;gt; tmux -&amp;gt; Codex
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这套方案解决了第一个问题：离开书桌后，怎样连回自己的 Mac；手机断开后，怎样让本地智能体继续执行耗时较长的任务。&lt;/p&gt;
&lt;p&gt;它很快就派上了用场。&lt;/p&gt;
&lt;p&gt;但在实际使用后，我又发现了第二个问题。&lt;/p&gt;
&lt;p&gt;SSH 解决了连通性，却不会自动带来一套好用的远程开发工作流。&lt;/p&gt;
&lt;p&gt;移动端 SSH 用得越多，它的边界就越清楚：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;手机不该被当成一台微型终端。&lt;br&gt;
手机应该成为本地智能体工作流的控制面。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="第一层解决的是基础设施"&gt;
 第一层解决的是基础设施
 &lt;a class="heading-link" href="#%e7%ac%ac%e4%b8%80%e5%b1%82%e8%a7%a3%e5%86%b3%e7%9a%84%e6%98%af%e5%9f%ba%e7%a1%80%e8%ae%be%e6%96%bd"&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;img src="https://yanqian.github.io/posts/publish/from-remote-shell-to-agent-control-plane/assets/remote-mac-terminal-for-codex/01-remote-architecture.svg" alt="远程终端架构"&gt;&lt;/p&gt;
&lt;p&gt;我需要：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用 Tailscale 连接手机与 Mac，又不把 SSH 暴露在公网上。&lt;/li&gt;
&lt;li&gt;用 SSH 安全登录。&lt;/li&gt;
&lt;li&gt;用 tmux 保证耗时较长的任务不会因连接断开而终止。&lt;/li&gt;
&lt;li&gt;用 caffeinate 防止 Mac 在智能体工作期间休眠。&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;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;但它只回答了基础设施层面的问题：&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;我能顺畅地用手机推动耗时较长的 AI 开发工作吗？
&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="#%e6%89%8b%e6%9c%ba%e5%b9%b6%e4%b8%8d%e6%98%af%e4%b8%80%e5%8f%b0%e5%a5%bd%e7%bb%88%e7%ab%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;移动端 SSH 很适合应急访问。&lt;/p&gt;</description></item><item><title>远程智能体工作流（一）：用手机连接 Mac 运行 Codex</title><link>https://yanqian.github.io/zh/posts/publish/remote-mac-terminal-for-codex/</link><pubDate>Mon, 20 Jul 2026 22:15:53 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/remote-mac-terminal-for-codex/</guid><description>&lt;p&gt;本文是“远程代理工作流”系列的第一篇。&lt;/p&gt;
&lt;p&gt;这篇指南将介绍如何用手机操作 Mac 终端，并让 Codex、自动化脚本、本地开发代理等长时间任务持续运行。&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; SSH -&amp;gt; Mac -&amp;gt; tmux -&amp;gt; Codex / 代理任务
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;配置完成后，Mac 就会成为一个轻量的远程执行节点。无论身在何处，你都可以用手机接入。&lt;/p&gt;
&lt;h2 id="各个工具分别做什么"&gt;
 各个工具分别做什么
 &lt;a class="heading-link" href="#%e5%90%84%e4%b8%aa%e5%b7%a5%e5%85%b7%e5%88%86%e5%88%ab%e5%81%9a%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;h3 id="ssh"&gt;
 SSH
 &lt;a class="heading-link" href="#ssh"&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;/h3&gt;
&lt;p&gt;SSH 是一种安全的远程登录协议，可以让你从手机进入 Mac 的终端。&lt;/p&gt;
&lt;p&gt;在这套工作流中，SSH 负责：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;安全访问 Mac 的命令行。&lt;/li&gt;
&lt;li&gt;使用密码或 SSH 密钥验证身份。&lt;/li&gt;
&lt;li&gt;从手机远程执行命令。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不过，SSH 一旦断开，并不能保证任务继续运行。这正是我们还需要 tmux 的原因。&lt;/p&gt;
&lt;h3 id="tailscale"&gt;
 Tailscale
 &lt;a class="heading-link" href="#tailscale"&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;/h3&gt;
&lt;p&gt;Tailscale 会在你的设备之间建立私有网络。即使 Mac 和手机接入不同的网络，两台设备也能通过稳定的私有 IP 地址互相访问。&lt;/p&gt;
&lt;p&gt;在这套工作流中，Tailscale 负责：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;让你通过 4G、5G、酒店 Wi-Fi、公司 Wi-Fi 或家庭 Wi-Fi 远程访问 Mac。&lt;/li&gt;
&lt;li&gt;为 Mac 提供稳定的 &lt;code&gt;100.x.x.x&lt;/code&gt; 地址。&lt;/li&gt;
&lt;li&gt;加密设备之间的网络通信。&lt;/li&gt;
&lt;li&gt;避免将 SSH 直接暴露在公网上。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本文默认使用官方 Tailscale macOS 客户端。在这种配置下，后台服务、登录状态和菜单栏状态都由该应用管理。&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><item><title>我从新加坡的三棵树身上明白的事</title><link>https://yanqian.github.io/zh/posts/publish/what-i-learned-from-three-trees-in-singapore/</link><pubDate>Fri, 17 Jul 2026 22:45:17 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/what-i-learned-from-three-trees-in-singapore/</guid><description>&lt;p&gt;刚搬到新加坡时，我常常留意这里的树，也总有些地方想不明白。&lt;/p&gt;
&lt;p&gt;新加坡称自己为“自然里的城市”。&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&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;h2 id="1-看起来很健康的树为什么要砍掉"&gt;
 1. 看起来很健康的树，为什么要砍掉？
 &lt;a class="heading-link" href="#1-%e7%9c%8b%e8%b5%b7%e6%9d%a5%e5%be%88%e5%81%a5%e5%ba%b7%e7%9a%84%e6%a0%91%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e7%a0%8d%e6%8e%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;img src="https://yanqian.github.io/posts/publish/what-i-learned-from-three-trees-in-singapore/assets/what-i-learned-from-three-trees-in-singapore/01-tree-stump.jpg" alt="新加坡一条小路旁刚被锯掉的树桩"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;小路旁的树桩。单看外表，这棵树原先或许完全没有异样。&lt;/em&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;em&gt;看起来&lt;/em&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;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;/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;h2 id="2-树上为什么装着传感器"&gt;
 2. 树上为什么装着传感器？
 &lt;a class="heading-link" href="#2-%e6%a0%91%e4%b8%8a%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a3%85%e7%9d%80%e4%bc%a0%e6%84%9f%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;img src="https://yanqian.github.io/posts/publish/what-i-learned-from-three-trees-in-singapore/assets/what-i-learned-from-three-trees-in-singapore/02-tree-tilt-sensor.jpg" alt="安装在一棵成熟大树基部附近的树木倾斜传感器"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;成熟大树的基部附近装着一枚小小的传感器，让城市得以长期观察这棵安静生长的树。&lt;/em&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;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;h2 id="3-为什么要把树修剪得这么狠"&gt;
 3. 为什么要把树修剪得这么狠？
 &lt;a class="heading-link" href="#3-%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e6%8a%8a%e6%a0%91%e4%bf%ae%e5%89%aa%e5%be%97%e8%bf%99%e4%b9%88%e7%8b%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;img src="https://yanqian.github.io/posts/publish/what-i-learned-from-three-trees-in-singapore/assets/what-i-learned-from-three-trees-in-singapore/03-tree-pruning.jpg" alt="公寓楼附近一棵经过大幅修剪的成熟城市树木"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;从地面望去，修剪后的树难免显得有些惨烈。少掉的枝干一目了然，随之降低的风险却很难看见。&lt;/em&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;</description></item></channel></rss>