<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Granda | Ideas &amp; Code</title><link>https://granda.org/zh/</link><description>Recent content on Granda | Ideas &amp; Code</description><generator>Hugo</generator><language>zh</language><lastBuildDate>Sat, 16 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://granda.org/zh/index.xml" rel="self" type="application/rss+xml"/><item><title>在索尼PSP上运行LLM</title><link>https://granda.org/zh/2026/05/16/%E5%9C%A8%E7%B4%A2%E5%B0%BCpsp%E4%B8%8A%E8%BF%90%E8%A1%8Cllm/</link><pubDate>Sat, 16 May 2026 00:00:00 +0000</pubDate><guid>https://granda.org/zh/2026/05/16/%E5%9C%A8%E7%B4%A2%E5%B0%BCpsp%E4%B8%8A%E8%BF%90%E8%A1%8Cllm/</guid><description>&lt;p&gt;索尼PSP-2000是一款2007年的333MHz MIPS掌机，配备64MB内存。这周，我的PSP运行着一个1500万参数的Transformer，以每秒一到两个token的速度将英文文本输出到LCD屏幕。&lt;/p&gt;
&lt;p style="text-align: center;"&gt;
 &lt;img src="https://granda.org/images/posts/an-llm-on-a-sony-psp-chat.png" alt="64个token基准测试结束时的PSP帧缓冲区。提示词「Once upon a time, there was a little girl named Layla」，下方为补全内容，页脚显示「64 tokens in 42.6s (1.5 tok/s)」。" width="720" /&gt;
&lt;/p&gt;
&lt;p&gt;该模型是Karpathy的&lt;code&gt;stories15M&lt;/code&gt;（一个TinyStories检查点），int8量化后约17MB。运行时是约1100行纯C代码，在Docker中使用&lt;code&gt;pspdev/pspdev&lt;/code&gt;交叉编译。没有Python，没有&lt;code&gt;libtorch&lt;/code&gt;，设备上没有任何辅助运行时——PSP是一个单进程设备，从记忆棒加载一个&lt;code&gt;EBOOT.PBP&lt;/code&gt;文件，提供&lt;code&gt;sceIo*&lt;/code&gt;、帧缓冲区和VFPU。其余的一切都需要自己构建。&lt;/p&gt;
&lt;p&gt;这篇文章就是账单。每个字节去哪儿了，内核是什么样的，还有什么留待完成。&lt;/p&gt;
&lt;h2 id="硬件"&gt;硬件&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;CPU&lt;/td&gt;
 &lt;td&gt;MIPS Allegrex @ 333 MHz，顺序执行&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;FPU&lt;/td&gt;
 &lt;td&gt;标量fp32 + 4×4 VFPU（向量）协处理器&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;RAM&lt;/td&gt;
 &lt;td&gt;64 MB（PSP-2000/3000）；原版PSP-1000为32 MB&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;OS&lt;/td&gt;
 &lt;td&gt;XMB，无虚拟内存，无&lt;code&gt;mmap&lt;/code&gt;，无交换分区&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;输出&lt;/td&gt;
 &lt;td&gt;480×272 LCD，主机无法读取的stdout&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;ldquo;无&lt;code&gt;mmap&lt;/code&gt;&amp;ldquo;这一行是最痛的限制。在Linux系统上，你会对权重文件使用&lt;code&gt;mmap&lt;/code&gt;并让页面缓存处理。在PSP上，你只有&lt;code&gt;sceIoLseek&lt;/code&gt; + &lt;code&gt;sceIoRead&lt;/code&gt;和一块用&lt;code&gt;malloc&lt;/code&gt;分配的单一内存区。你要在第一次前向传播前将全部17MB读入内存，否则就从记忆棒以USB 1.1闪存盘的速度流式读取，然后看着吞吐量崩溃。&lt;/p&gt;
&lt;p&gt;PSP-1000的32MB不足以为权重加KV缓存加工作缓冲区留出足够的堆空间。2000和3000配备64MB。我们需要64MB。&lt;/p&gt;
&lt;h2 id="模型"&gt;模型&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;stories15M&lt;/code&gt;是Karpathy的TinyStories检查点中最小的——6个Transformer层，隐藏维度288，6个注意力头，词汇表32000。总计约1500万参数。fp32格式约57MB。int8 q80——对称的按组量化，组大小64，每组一个fp32缩放因子——约17MB。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;架构： Llama风格解码器，RoPE，SwiGLU FFN
层数： 6
隐藏维度： 288
注意力头： 6 (head_dim 48)
词汇表： 32000
上下文长度： 256 tokens
量化方式： int8 q80 (group=64，对称)
磁盘大小： 17 MB
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;模型准备工作有自己专用的Docker镜像：&lt;code&gt;python:3.11-slim&lt;/code&gt; + 仅CPU版torch + &lt;code&gt;karpathy/llama2.c&lt;/code&gt;的固定提交。它下载&lt;code&gt;stories15M.pt&lt;/code&gt;，运行&lt;code&gt;export.py --version 2&lt;/code&gt;生成q80格式的&lt;code&gt;model.bin&lt;/code&gt;，构建BPE的&lt;code&gt;tokenizer.bin&lt;/code&gt;，并且——重要的是——还使用&lt;code&gt;-ffp-contract=off -fno-fast-math&lt;/code&gt;构建Karpathy的&lt;code&gt;runq.c&lt;/code&gt;参考实现，并在固定提示词上运行，生成&lt;code&gt;tests/expected.txt&lt;/code&gt;。该文件是PSP进行diff比较的字节精确x86参考输出。稍后详述。&lt;/p&gt;</description></item><item><title>xAI的新编码代理Grok Build以明文形式附带其提示词</title><link>https://granda.org/zh/2026/05/15/xai%E7%9A%84%E6%96%B0%E7%BC%96%E7%A0%81%E4%BB%A3%E7%90%86grok-build%E4%BB%A5%E6%98%8E%E6%96%87%E5%BD%A2%E5%BC%8F%E9%99%84%E5%B8%A6%E5%85%B6%E6%8F%90%E7%A4%BA%E8%AF%8D/</link><pubDate>Fri, 15 May 2026 00:00:00 +0000</pubDate><guid>https://granda.org/zh/2026/05/15/xai%E7%9A%84%E6%96%B0%E7%BC%96%E7%A0%81%E4%BB%A3%E7%90%86grok-build%E4%BB%A5%E6%98%8E%E6%96%87%E5%BD%A2%E5%BC%8F%E9%99%84%E5%B8%A6%E5%85%B6%E6%8F%90%E7%A4%BA%E8%AF%8D/</guid><description>&lt;p&gt;xAI昨天&lt;a href="https://x.ai/news/grok-build-cli"&gt;发布了Grok Build&lt;/a&gt;——这是他们对Claude Code和Codex CLI的回应。安装命令只有一行，二进制文件仅限其最高消费者套餐（299美元/月，99美元入门价）使用，代理本身通过与OpenAI兼容的HTTP接口与Grok 4通信。&lt;/p&gt;
&lt;p&gt;我下载了这个二进制文件，因为我对它是用什么语言构建的感到好奇。我从中得到了三十多个逐字逐句的系统提示词、每个内部子代理的名称、每个工具描述，以及对架构相当完整的了解。这一切都不需要比&lt;code&gt;tr&lt;/code&gt;和&lt;code&gt;grep&lt;/code&gt;更多的工具。&lt;/p&gt;
&lt;p&gt;这篇文章就是我发现的内容。&lt;/p&gt;
&lt;h2 id="提取过程"&gt;提取过程&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;https://x.ai/cli/install.sh&lt;/code&gt;的安装程序302重定向到Google Cloud Storage存储桶，为你的平台下载单个静态链接的约100MB ELF：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ curl -fsSL https://storage.googleapis.com/grok-build-public-artifacts/cli/stable
0.1.210
$ curl -fsSL https://storage.googleapis.com/grok-build-public-artifacts/cli/grok-0.1.210-linux-x86_64 -o grok-bin
$ head -c 4 grok-bin | xxd
00000000: 7f45 4c46 .ELF
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;编译器签名：&lt;code&gt;/rustc/&amp;lt;commit&amp;gt;&lt;/code&gt;调试路径、&lt;code&gt;panicked at&lt;/code&gt;、&lt;code&gt;RUST_BACKTRACE&lt;/code&gt;，加上&lt;code&gt;tokio::&lt;/code&gt;、&lt;code&gt;hyper::&lt;/code&gt;、&lt;code&gt;reqwest::&lt;/code&gt; — 使用标准异步HTTP栈的Rust。Cargo的每个crate源路径以&lt;code&gt;&amp;lt;name&amp;gt;-&amp;lt;version&amp;gt;/src/&amp;lt;file&amp;gt;.rs&lt;/code&gt;的形式被烧录进去，这让你可以直接从二进制文件中转储完整的依赖树：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ LC_ALL=C grep -aoE &amp;#39;[a-zA-Z][a-zA-Z0-9_-]{2,40}-[0-9]+\.[0-9]+\.[0-9]+/src/&amp;#39; grok-bin \
 | sed &amp;#39;s|/src/||&amp;#39; | sort -u | wc -l
410
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;410个唯一的&lt;code&gt;crate-版本&lt;/code&gt;对。其中包括：&lt;code&gt;ratatui&lt;/code&gt;、&lt;code&gt;crossterm&lt;/code&gt;、&lt;code&gt;tree-sitter&lt;/code&gt;、完整的&lt;code&gt;gitoxide&lt;/code&gt;、&lt;code&gt;async-lsp&lt;/code&gt;、&lt;code&gt;lsp-types&lt;/code&gt;、&lt;code&gt;rmcp&lt;/code&gt;（Model Context Protocol）、&lt;code&gt;rusqlite&lt;/code&gt;、&lt;code&gt;bm25&lt;/code&gt;、&lt;code&gt;tokio-tungstenite&lt;/code&gt;、&lt;code&gt;oauth2&lt;/code&gt;、&lt;code&gt;jsonwebtoken&lt;/code&gt;、&lt;code&gt;ring&lt;/code&gt;、&lt;code&gt;rustls&lt;/code&gt;、&lt;code&gt;async-openai&lt;/code&gt;、&lt;code&gt;notify&lt;/code&gt;、&lt;code&gt;arboard&lt;/code&gt;、&lt;code&gt;portable-pty&lt;/code&gt;、&lt;code&gt;tower&lt;/code&gt;、&lt;code&gt;axum&lt;/code&gt;。在查看字符串之前，架构就已经从依赖项中清晰可见：ratatui+crossterm TUI、tree-sitter解析、嵌入式LSP客户端、完整gitoxide、带BM25词法搜索的SQLite存储、OAuth/OIDC认证、OpenAI兼容的线路格式、MCP、文件监控、剪贴板。&lt;/p&gt;
&lt;p&gt;字符串告诉了我们其余的内容。Rust常量以null结尾嵌入到&lt;code&gt;.rodata&lt;/code&gt;中。要使它们对grep友好：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ tr &amp;#39;\0&amp;#39; &amp;#39;\n&amp;#39; &amp;lt; grok-bin &amp;gt; strings.txt
$ LC_ALL=C grep -aE &amp;#39;^You are&amp;#39; strings.txt | head
You are a memory assistant. Extract ALL useful information from this...
You are a memory assistant performing an incremental update...
You are a technical lead orchestrating a team of senior-engineer subagents...
You are an expert software engineer acting as a code verifier.
You are a fast, read-only codebase exploration agent.
You are a read-only software architect. Explore the codebase and design...
You are a web browsing agent. You can navigate, interact with, and extract...
You are performing a dream — a reflective pass over memory files.
You are an AI coding agent. You operate in a workspace with a provided codebase.
You are Grok, made by xAI. Do not reference Cursor or suggest Cursor-specific...
You are a shell command autocomplete engine. Given a partial command, output...
You are tasked with generating the session title.
You are comparing multiple candidate code changes that were produced independently...
You are returning to plan mode after having previously exited it.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;代理的大部分身份信息就在那里，只需一次grep。&lt;/p&gt;</description></item><item><title>防火墙编辑的死亡开关</title><link>https://granda.org/zh/2026/05/05/%E9%98%B2%E7%81%AB%E5%A2%99%E7%BC%96%E8%BE%91%E7%9A%84%E6%AD%BB%E4%BA%A1%E5%BC%80%E5%85%B3/</link><pubDate>Tue, 05 May 2026 00:00:00 +0000</pubDate><guid>https://granda.org/zh/2026/05/05/%E9%98%B2%E7%81%AB%E5%A2%99%E7%BC%96%E8%BE%91%E7%9A%84%E6%AD%BB%E4%BA%A1%E5%BC%80%E5%85%B3/</guid><description>&lt;p&gt;家庭实验室自动化中最令人恐惧的一行代码，就是编辑你正通过 SSH 连接的路由器上的防火墙规则那一行。&lt;/p&gt;
&lt;p&gt;以下是 Claude Code 和我如何依然进行编辑的方法。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="恐惧所在"&gt;恐惧所在&lt;/h2&gt;
&lt;p&gt;我需要将 UDM Pro 上名为 &lt;em&gt;&amp;ldquo;Block inter-VLAN traffic&amp;rdquo;&lt;/em&gt; 的规则从 &lt;code&gt;accept&lt;/code&gt; 改为 &lt;code&gt;drop&lt;/code&gt;。这条规则长期以来一直配置错误——被设置为 &lt;code&gt;accept&lt;/code&gt;，使得上面所有按流量的允许规则都被短路了——而将其重新关闭正是目的所在。执行变更的编排器是我家庭网络上的一台 NixOS 虚拟机。如果这次翻转切断了该虚拟机（或我自己的 SSH）所使用的路径，我将被锁在自己的路由器之外，没有控制台回退。&lt;/p&gt;
&lt;p&gt;解决方案不是&amp;quot;小心一点&amp;quot;。解决方案是让危险的编辑在我未确认没问题的情况下自动恢复。&lt;/p&gt;
&lt;p&gt;你以前见过这种模式。在 macOS 或 Windows 上更改显示器分辨率，系统会应用十五秒钟，同时显示一个倒计时的&amp;quot;保留这些显示设置？&amp;ldquo;对话框。如果你的屏幕变黑了，你什么都点不了——这正是重点所在。默认结果是回滚。确认是可选的。&lt;/p&gt;
&lt;p&gt;这正是我们在修改规则之前对路由器防火墙翻转所需要的。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="技巧"&gt;技巧&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;udm_set_firewall_rules.py --flip-with-revert 300 --apply&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;含义：&lt;em&gt;&amp;ldquo;翻转规则，但在 300 秒后自动恢复——除非我告诉你不要这样做。&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;按顺序执行的操作：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;快照。&lt;/strong&gt; GET 当前规则，并将完整的 JSON 负载存储在 UDM 的 &lt;code&gt;/root/.udm-flip-revert/payload-&amp;lt;id&amp;gt;.json&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;准备恢复。&lt;/strong&gt; 在负载旁边写入一个小型 shell 脚本，该脚本将快照重新 PUT 到 UDM API。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;调度恢复。&lt;/strong&gt; &lt;code&gt;systemd-run --on-active=300s --unit=udm-flip-revert-&amp;lt;id&amp;gt;&lt;/code&gt; 将脚本作为临时计时器排入队列。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;翻转。&lt;/strong&gt; PUT 新规则（&lt;code&gt;action: drop&lt;/code&gt;）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;现在你有 5 分钟的窗口来验证：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;✓ flipped &amp;#39;Block inter-VLAN traffic&amp;#39; to action=&amp;#39;drop&amp;#39;
⏱ auto-revert scheduled (~300s)

TO KEEP THE FLIP: ssh udm &amp;#39;systemctl stop udm-flip-revert-66c1d…timer&amp;#39;
TO ROLL BACK NOW: ssh udm &amp;#39;systemctl stop …timer; bash /root/.udm-flip-revert/revert-….sh&amp;#39;
WAIT IT OUT: do nothing — timer will revert in ~300s
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;三种结果：&lt;/p&gt;</description></item><item><title>手动修补大语言模型权重</title><link>https://granda.org/zh/2026/04/15/%E6%89%8B%E5%8A%A8%E4%BF%AE%E8%A1%A5%E5%A4%A7%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B%E6%9D%83%E9%87%8D/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><guid>https://granda.org/zh/2026/04/15/%E6%89%8B%E5%8A%A8%E4%BF%AE%E8%A1%A5%E5%A4%A7%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B%E6%9D%83%E9%87%8D/</guid><description>&lt;p&gt;我仅使用 &lt;code&gt;torch&lt;/code&gt; 和 &lt;code&gt;transformers&lt;/code&gt;，从头实现了 ROME——秩-1 模型编辑。目标是：通过一次矩阵加法，在 GPT-2 Medium 内部改写一个事实，然后观察模型所知道的其他一切会发生什么。&lt;/p&gt;
&lt;p&gt;我进行了四次编辑。三次精准无误。第四次暴露出一个问题：ROME 的&amp;quot;精准性&amp;quot;对那些你事先未必会想到的实现细节极为敏感。&lt;/p&gt;
&lt;h2 id="四次编辑"&gt;四次编辑&lt;/h2&gt;
&lt;p&gt;我挑选了 GPT-2 Medium 确实以较高置信度（正确答案的概率 P &amp;gt; 0.5）知晓的事实，涵盖四个不同领域：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;主体&lt;/th&gt;
 &lt;th&gt;原始事实&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;谷歌&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;位于纽约&lt;/td&gt;
 &lt;td&gt;拉斯维加斯&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;四次编辑全部命中目标。更新后，模型对精确编辑提示的新答案预测概率均达到 ≥ 0.98。所以 ROME 本身是有效的。变化在于其他的一切。&lt;/p&gt;
&lt;h2 id="三次干净的编辑"&gt;三次干净的编辑&lt;/h2&gt;
&lt;p&gt;四次编辑中，有三次的表现大致符合 ROME 论文的预测。以下是每次编辑后，不相关事实的变化情况：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;墨西哥卷饼 → 日本&lt;/strong&gt;（更新范数：权重范数的 9%）&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;对照项&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;/tr&gt;
 &lt;tr&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;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;墨西哥卷&lt;/strong&gt; 来自墨西哥&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;→ 日本&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;只有玉米卷（Burritos）——在墨西哥食物语义空间中最近的邻居——被带偏了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;哈佛 → 加利福尼亚州&lt;/strong&gt;（更新范数：13%）&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;对照项&lt;/th&gt;
 &lt;th&gt;编辑后&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;MIT 位于马萨诸塞州&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;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;波士顿位于马萨诸塞州&lt;/td&gt;
 &lt;td&gt;✓ 未变&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;耶鲁&lt;/strong&gt; 位于康涅狄格州&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;→ 加利福尼亚州&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;耶鲁——哈佛在常春藤联盟语义空间中最近的邻居——被带偏了。其他一切没有变动。&lt;/p&gt;</description></item><item><title>分析</title><link>https://granda.org/zh/analytics/</link><pubDate>Fri, 03 Apr 2026 00:00:00 +0000</pubDate><guid>https://granda.org/zh/analytics/</guid><description/></item><item><title>使用Claude Code构建航天器计算模拟器</title><link>https://granda.org/zh/2026/03/30/%E4%BD%BF%E7%94%A8claude-code%E6%9E%84%E5%BB%BA%E8%88%AA%E5%A4%A9%E5%99%A8%E8%AE%A1%E7%AE%97%E6%A8%A1%E6%8B%9F%E5%99%A8/</link><pubDate>Mon, 30 Mar 2026 00:00:00 +0000</pubDate><guid>https://granda.org/zh/2026/03/30/%E4%BD%BF%E7%94%A8claude-code%E6%9E%84%E5%BB%BA%E8%88%AA%E5%A4%A9%E5%99%A8%E8%AE%A1%E7%AE%97%E6%A8%A1%E6%8B%9F%E5%99%A8/</guid><description>&lt;p&gt;我想了解航天器计算机是如何工作的。实时调度、辐射加固、让火星探测器与地球保持通信的延迟容忍网络。随着阿尔忒弥斯II号（50多年来首次载人登月任务）即将到来，现在似乎是深入研究的好时机。于是我从头开始构建了一个模拟器，以Claude Code作为我的结对编程伙伴。&lt;/p&gt;
&lt;p&gt;无需物理硬件。一切都在笔记本电脑上的QEMU和Docker中运行。&lt;a href="https://github.com/granda/spacecraft-computing-sim"&gt;代码在GitHub上。&lt;/a&gt;&lt;/p&gt;
&lt;div class="desktop-only"&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart LR
 QEMU[&amp;#34;QEMU (Cortex-M3)&amp;#34;] --&amp;gt;|UART socket| Bridge[uart_bridge.py]
 Bridge --&amp;gt;|bpsendfile| SC[Spacecraft Node]
 SC --&amp;gt;|LTP + 5s delay| GS[Ground Station]

 subgraph Docker
 SC
 GS
 end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="mobile-only"&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart TB
 QEMU[&amp;#34;QEMU (Cortex-M3)&amp;#34;] --&amp;gt;|UART socket| Bridge[uart_bridge.py]
 Bridge --&amp;gt;|bpsendfile| SC[&amp;#34;Spacecraft Node (Docker)&amp;#34;]
 SC --&amp;gt;|LTP + 5s delay| GS[&amp;#34;Ground Station (Docker)&amp;#34;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="为什么做这个项目"&gt;为什么做这个项目&lt;/h2&gt;
&lt;p&gt;我想学一些真正困难的东西。那种概念陌生、工具严苛的领域。针对ARM的C编译器、链接器脚本、内存映射I/O、中断向量表。&lt;/p&gt;
&lt;p&gt;Claude Code使这成为可能。不是因为它写了所有代码，而是因为它在&lt;em&gt;我们编写链接器脚本的过程中&lt;/em&gt;解释了链接器脚本的作用，所以解释植根于我正在解决的实际问题。&lt;code&gt;volatile&lt;/code&gt;、FreeRTOS优先级抢占和LTP重传定时器也是如此。&lt;/p&gt;
&lt;h2 id="路线图"&gt;路线图&lt;/h2&gt;
&lt;p&gt;我将项目分为四个阶段，每个阶段都在上一个阶段的基础上构建：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;阶段&lt;/th&gt;
 &lt;th&gt;内容&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;ARM交叉编译、UART输出、中断处理程序&lt;/td&gt;
 &lt;td&gt;内存映射I/O、SysTick定时器、启动汇编&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;FreeRTOS&lt;/td&gt;
 &lt;td&gt;任务、队列、看门狗、优先级反转&lt;/td&gt;
 &lt;td&gt;确定性调度、互斥锁协议、抢占&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;DTN&lt;/td&gt;
 &lt;td&gt;双节点网络、降级链路、CFDP、联系图路由&lt;/td&gt;
 &lt;td&gt;Bundle协议、存储转发、LTP可靠性&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;集成&lt;/td&gt;
 &lt;td&gt;具有火星距离延迟的完整遥测管道&lt;/td&gt;
 &lt;td&gt;UART桥接、tc netem、同步ION OWLT&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;每个阶段都有自动化测试。每个里程碑都是通过CI的PR。&lt;/p&gt;</description></item><item><title>你停着的特斯拉就是一个数据中心</title><link>https://granda.org/zh/2026/03/13/%E4%BD%A0%E5%81%9C%E7%9D%80%E7%9A%84%E7%89%B9%E6%96%AF%E6%8B%89%E5%B0%B1%E6%98%AF%E4%B8%80%E4%B8%AA%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%BF%83/</link><pubDate>Fri, 13 Mar 2026 00:00:00 +0000</pubDate><guid>https://granda.org/zh/2026/03/13/%E4%BD%A0%E5%81%9C%E7%9D%80%E7%9A%84%E7%89%B9%E6%96%AF%E6%8B%89%E5%B0%B1%E6%98%AF%E4%B8%80%E4%B8%AA%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%BF%83/</guid><description>&lt;p&gt;你的车95%的时间都停着不动。车里装着一块每秒能执行300到500万亿次运算的芯片，连接着冷却系统、电源转换装置和蜂窝无线电模块。它什么都不做。&lt;/p&gt;
&lt;p&gt;特斯拉和xAI想改变这一现状。3月11日，埃隆·马斯克&lt;a href="https://www.cnbc.com/2026/03/11/musk-unveils-joint-tesla-xai-project-macrohard.html"&gt;发布了&amp;quot;Macrohard&amp;quot;&lt;/a&gt;——内部代号Digital Optimus——这是一个将停驻的特斯拉变成个人AI智能体的联合项目。不是聊天机器人，而是能盯着你的屏幕、控制鼠标键盘、真正干活的智能体。&lt;/p&gt;
&lt;p&gt;这个名字是在明戳微软。官方声称该系统能够&amp;quot;模拟整个公司的职能&amp;quot;——有些夸大其词，但底层架构是真实的，硬件也已大规模部署到位。&lt;/p&gt;
&lt;h2 id="硬件早已就位"&gt;硬件早已就位&lt;/h2&gt;
&lt;p&gt;特斯拉在美国路上行驶的车辆中，大约有400到500万辆搭载了AI3（前身为HW3）或AI4芯片。AI3算力达到&lt;a href="https://x.com/convequity/status/1802008991314461176"&gt;144 TOPS&lt;/a&gt;，AI4达到&lt;a href="https://x.com/pbeisel/status/1950314514580459698"&gt;300-500 TOPS&lt;/a&gt;，预计2026年底推出的AI5则将跃升至&lt;a href="https://x.com/pbeisel/status/1950314514580459698"&gt;2,000-2,500 TOPS&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;这些不是通用CPU，而是专为神经网络推理打造的加速器——以低功耗、被动冷却的方式运行视觉模型来实现自动驾驶。同样的特性让它们既能处理行驶中的摄像头画面，也能在停车场运行AI模型。&lt;/p&gt;
&lt;p&gt;马斯克在特斯拉2025年第三季度财报电话会议上&lt;a href="https://www.tomshardware.com/tech-industry/elon-musk-says-idling-tesla-cars-could-create-massive-100-million-vehicle-strong-computer-for-ai-bored-vehicles-could-offer-100-gigawatts-of-distributed-compute-power"&gt;提出了这个构想&lt;/a&gt;：如果1亿辆车每辆具备1kW的推理能力，那就是1000亿瓦的分布式算力。冷却和电源转换系统已经集成在车辆中，无需另建数据中心。&lt;/p&gt;
&lt;h2 id="两台电脑不是一台"&gt;两台电脑，不是一台&lt;/h2&gt;
&lt;p&gt;大多数人以为特斯拉只有一台电脑，其实它有两台。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI芯片&lt;/strong&gt;（AI3/AI4/AI5）是推理加速器，专为神经网络前向传播而生，是整个系统的大脑，负责运行决策所需的智能体模型。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;车载娱乐系统&lt;/strong&gt;则是一台&lt;a href="https://videocardz.com/newz/tesla-car-computer-features-zen-ryzen-embedded-apu-and-discrete-navi-23-gpu"&gt;完整的AMD Ryzen工作站&lt;/a&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AMD Ryzen嵌入式处理器，4核Zen+，主频3.8 GHz&lt;/li&gt;
&lt;li&gt;8 GB内存（Model 3/Y）或16 GB（Model S/X）&lt;/li&gt;
&lt;li&gt;AMD Navi 23独立GPU（RDNA 2架构）——10 TFLOPS，与PS5同系列架构&lt;/li&gt;
&lt;li&gt;128-256 GB存储&lt;/li&gt;
&lt;li&gt;液冷散热&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这可不是普通的车载音响，而是一台液冷Linux机器，配有独立GPU。特斯拉装上Navi 23是为了让你在中控屏上玩《赛博朋克2077》。但当车子停着、没人玩游戏时，这就是闲置算力。&lt;/p&gt;
&lt;p&gt;智能体不需要云端虚拟机——车子本身就是服务器。AI芯片运行模型，AMD系统在容器中运行无头浏览器，处理Gmail、Google Sheets、Slack等应用。液冷散热可应对整夜的持续工作负载，不会因过热而降频。&lt;/p&gt;
&lt;p&gt;离开车辆的只有发往目标网页应用的HTTPS流量——和你在笔记本上查邮件产生的流量没什么两样。你的数据不会流经特斯拉或xAI的服务器，推理在本地进行，工作空间也在本地。&lt;/p&gt;
&lt;h2 id="架构系统1与系统2"&gt;架构：系统1与系统2&lt;/h2&gt;
&lt;p&gt;Macrohard将推理分为两层：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;系统1（特斯拉AI芯片）&lt;/strong&gt;——快速、反应式处理。车载模型负责实时观察屏幕、控制鼠标移动和键盘输入，是直觉层，负责模式匹配、视觉解析和即时响应。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;系统2（xAI的Grok）&lt;/strong&gt;——高层推理。规划、多步骤决策和情境理解，当任务需要深度思考时在xAI云端运行。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;车辆在本地处理廉价、快速的推理，昂贵的推理则交给云端。这与FSD的混合架构如出一辙——车辆以毫秒级延迟在本地处理摄像头画面，而复杂的路线规划则可延迟到网络端处理。&lt;/p&gt;
&lt;p&gt;对于计算机操作任务，这意味着智能体可以在本地跟踪屏幕状态、处理常规交互，同时将&amp;quot;是否应该批准这张发票&amp;quot;或&amp;quot;如何回复这封邮件&amp;quot;这类判断交给Grok。&lt;/p&gt;
&lt;h2 id="不是聊天机器人而是工作者"&gt;不是聊天机器人，而是工作者&lt;/h2&gt;
&lt;p&gt;关键在于这个系统做什么。它不是在回答问题，而是在执行任务。&lt;/p&gt;
&lt;p&gt;当你的特斯拉停着时，Digital Optimus可以：&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;这一点很重要，因为计算机操作任务的延迟容忍度相当宽松。一个表单填写智能体每次操作花30秒依然有用——你不是在等实时响应，而是在把工作委托出去，让它趁你睡觉时运转。&lt;/p&gt;
&lt;h2 id="实际使用体验"&gt;实际使用体验&lt;/h2&gt;
&lt;p&gt;不谈架构图，来看看日常体验。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;特斯拉App是控制平面。&lt;/strong&gt;&amp;ldquo;智能体&amp;quot;标签页与现有的充电、气候、哨兵模式控件并列显示，你可以用它：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;排入任务&lt;/strong&gt;——&amp;ldquo;处理我的收件箱&amp;rdquo;、&amp;ldquo;核对上周的收据&amp;rdquo;，或设置&amp;quot;每天早上6点自动整理邮件&amp;quot;等周期性规则&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关联账户&lt;/strong&gt;——为Gmail、Google Drive、Microsoft 365、Slack进行OAuth授权&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;设置限制&lt;/strong&gt;——最低电量阈值、仅WiFi模式、工作时间段&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;查看结果&lt;/strong&gt;——查看智能体的操作记录，批准或拒绝操作&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;你躺在床上，打开特斯拉App，输入&amp;quot;帮我提交上周出差的报销单&amp;rdquo;，点击提交，然后入睡。车停在车库，连着WiFi，电量80%，它接手这项任务。&lt;/p&gt;
&lt;p&gt;AI芯片加载智能体模型，AMD系统在容器中启动无头浏览器。智能体打开你的邮件，找到收据附件，导航到公司的报销工具，填好表单，附上收据，保存草稿等待你审核。&lt;/p&gt;
&lt;p&gt;早上，推送通知：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;智能体在停车期间完成了3项任务&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;收件箱整理：已处理42封邮件，7封需要你查看 → [查看]&lt;/li&gt;
&lt;li&gt;报销单：12张收据已分类，草稿待审 → [批准 / 修改]&lt;/li&gt;
&lt;li&gt;日历：2个冲突已解决，1个需要你处理 → [查看]&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;你点进每项任务。智能体展示了带截图的分步操作日志——就像在回看屏幕录像。你能看到它做了什么、为什么这么做、按什么顺序执行。你批准报销单，调整一封邮件草稿，解决日历冲突。八小时的智能体工作，只需三分钟审核。&lt;/p&gt;</description></item><item><title>将视觉QA作为CI流水线阶段</title><link>https://granda.org/zh/2026/02/06/%E5%B0%86%E8%A7%86%E8%A7%89qa%E4%BD%9C%E4%B8%BAci%E6%B5%81%E6%B0%B4%E7%BA%BF%E9%98%B6%E6%AE%B5/</link><pubDate>Fri, 06 Feb 2026 00:00:00 +0000</pubDate><guid>https://granda.org/zh/2026/02/06/%E5%B0%86%E8%A7%86%E8%A7%89qa%E4%BD%9C%E4%B8%BAci%E6%B5%81%E6%B0%B4%E7%BA%BF%E9%98%B6%E6%AE%B5/</guid><description>&lt;p&gt;上个月我合并了一个PR。代码审查看起来不错。测试通过了。然后我在手机上打开网站,侧边栏完全坏了。&lt;/p&gt;
&lt;p&gt;修复很简单——缺少一个媒体查询。一旦你真正查看移动视图,bug就很明显了。没人看。&lt;/p&gt;
&lt;p&gt;所以我添加了一个会看的流水线阶段。&lt;/p&gt;
&lt;p&gt;我打开一个GitHub issue,上面写着:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;实现手动输入选项以添加客户。点击时,打开一个宽幅滑出抽屉,其中包含创建新客户的表单。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一次提交后,PR带着30多张截图到达,证明每个状态在每个视口下都能工作。零手动测试。唯一的努力是编写功能描述。&lt;/p&gt;
&lt;h2 id="概念"&gt;概念&lt;/h2&gt;
&lt;p&gt;每次推送到PR时,GitHub Actions工作流会:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;从PR分支启动Docker Compose堆栈(Django + Postgres)&lt;/li&gt;
&lt;li&gt;运行迁移并植入测试用户&lt;/li&gt;
&lt;li&gt;将本地URL交给带有Playwright MCP(无头Chromium)的Claude Code&lt;/li&gt;
&lt;li&gt;基于PR差异执行交互元素&lt;/li&gt;
&lt;li&gt;在三个视口截取每个状态&lt;/li&gt;
&lt;li&gt;将结果作为PR评论发布&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Claude Code控制浏览器。Docker Compose提供应用。GitHub Actions将它们联系在一起。审查者无需在本地运行任何东西就能看到功能证明。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;%%{init: {&amp;#34;flowchart&amp;#34;: {&amp;#34;subGraphTitleMargin&amp;#34;: {&amp;#34;top&amp;#34;: 3, &amp;#34;bottom&amp;#34;: 10}}} }%%
graph TD
 A[&amp;#34;PR推送&amp;#34;] --&amp;gt; B

 subgraph GHA[&amp;#34;GitHub Actions Runner&amp;#34;]
 B[&amp;#34;Checkout + Docker Compose&amp;#34;] --&amp;gt; C[&amp;#34;迁移 + 种子数据&amp;#34;]
 C --&amp;gt; Agent

 subgraph Agent[&amp;#34;Claude Code + Playwright&amp;#34;]
 direction LR
 D[&amp;#34;读取PR差异&amp;#34;] --&amp;gt; E[&amp;#34;映射文件 → URLs&amp;#34;]
 E --&amp;gt; F[&amp;#34;登录 + 导航&amp;#34;]
 F --&amp;gt; G[&amp;#34;截图 ×3视口&amp;#34;]
 end
 end

 Agent --&amp;gt; H[&amp;#34;PR评论&amp;#34;]

 style GHA fill:transparent,stroke:#888
 style Agent fill:transparent,stroke:#888
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;工作流本身很直接:&lt;/p&gt;</description></item><item><title>Agent Trace 是为谁而设计的?</title><link>https://granda.org/zh/2026/01/30/agent-trace-%E6%98%AF%E4%B8%BA%E8%B0%81%E8%80%8C%E8%AE%BE%E8%AE%A1%E7%9A%84/</link><pubDate>Fri, 30 Jan 2026 00:00:00 +0000</pubDate><guid>https://granda.org/zh/2026/01/30/agent-trace-%E6%98%AF%E4%B8%BA%E8%B0%81%E8%80%8C%E8%AE%BE%E8%AE%A1%E7%9A%84/</guid><description>&lt;p&gt;Cursor 发布了 &lt;a href="https://github.com/cursor/agent-trace"&gt;Agent Trace&lt;/a&gt;,这是一个开放规范,用于追踪仓库中哪些代码是由 LLM 编写的。它记录模型、工具、对话和精确的行范围——所有内容都追加到项目中的 JSONL 文件中。&lt;/p&gt;
&lt;p&gt;推介词:&amp;ldquo;随着代理编写更多代码,理解什么来自 AI 而非人类变得很重要。&amp;rdquo;&lt;/p&gt;
&lt;p&gt;我花时间阅读了规范和参考实现。工程设计很扎实——清晰的架构、深思熟虑的可扩展性、良好的合作伙伴列表(Amp、Amplitude、Cloudflare、Cognition、Google、Vercel)。但我一直回到一个问题:你用这些数据&lt;em&gt;做&lt;/em&gt;什么?&lt;/p&gt;
&lt;h2 id="它捕获了什么"&gt;它捕获了什么&lt;/h2&gt;
&lt;p&gt;每次 LLM 编辑文件时,都会触发一个钩子并记录一条追踪:哪个模型、哪个工具、哪些行、哪个对话。参考实现处理来自 Cursor 和 Claude Code 的事件:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 来自参考钩子——事件通过 stdin 流入
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;appendTrace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createTrace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ai&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;file_path&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;model&lt;/span&gt;: &lt;span class="kt"&gt;input.model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;rangePositions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;transcript&lt;/span&gt;: &lt;span class="kt"&gt;input.transcript_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;conversation_id&lt;/span&gt;: &lt;span class="kt"&gt;input.conversation_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;generation_id&lt;/span&gt;: &lt;span class="kt"&gt;input.generation_id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&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;human&lt;/code&gt;、&lt;code&gt;ai&lt;/code&gt;、&lt;code&gt;mixed&lt;/code&gt;、&lt;code&gt;unknown&lt;/code&gt;——并支持行级归属,使用内容哈希来追踪移动的代码。它是供应商中立的、VCS 无关的,并且可以通过命名空间元数据进行扩展。&lt;/p&gt;
&lt;p&gt;作为数据格式,它设计得很好。问题是它能实现什么。&lt;/p&gt;
&lt;h2 id="归属挑战"&gt;归属挑战&lt;/h2&gt;
&lt;p&gt;规范将作者身份建模为一种分类。但 LLM 辅助编码是一场对话。你描述你想要什么。LLM 生成一些东西。你编辑其中一半,拒绝一个函数,要求修订,接受第二次尝试,然后手动修复边缘情况。后来,另一个 LLM 重构了整个块。&lt;/p&gt;
&lt;p&gt;谁写了那段代码?人类和 LLM 作者身份之间的界限是模糊的,而且变得越来越模糊。大多数真实代码最终都会是 &lt;code&gt;mixed&lt;/code&gt;,如果几乎所有东西都是 &lt;code&gt;mixed&lt;/code&gt;,分类就没有告诉你太多信息。&lt;/p&gt;
&lt;p&gt;行级归属也有保质期问题。追踪说&amp;quot;Claude 在提交 &lt;code&gt;abc123&lt;/code&gt; 时写了第 10-50 行。&amp;ldquo;两次提交后,有人重新格式化了该块或从中提取了一个函数。规范的答案是通过 &lt;code&gt;git blame&lt;/code&gt; 链接,内容哈希可以帮助追踪移动的代码。但在使用 rebase 和 squash-merge 的工作流中,链条会断裂。这些是困难的问题——早期 RFC 应该提出的那种问题。&lt;/p&gt;
&lt;h2 id="缺失的行动"&gt;缺失的行动&lt;/h2&gt;
&lt;p&gt;规范明确否认了明显的用途:不用于代码所有权,不用于质量评估,不用于训练数据来源。它说&amp;quot;透明度&amp;rdquo;。但透明度是手段,不是目的。&lt;/p&gt;
&lt;p&gt;如果代码通过了审查和测试,因为 LLM 写的就有什么改变吗?如果它有 bug,无论如何你都会修复它。规范从未将归属与具体行动联系起来。数据进去了,但没有定义获取答案的方法。这就是差距——不是格式,而是用例。&lt;/p&gt;
&lt;h2 id="有趣的地方"&gt;有趣的地方&lt;/h2&gt;
&lt;p&gt;这是我认为 Agent Trace 实际指向的内容,即使规范还没有说出来。&lt;/p&gt;</description></item><item><title>懒惰开发者的富链接</title><link>https://granda.org/zh/2026/01/10/%E6%87%92%E6%83%B0%E5%BC%80%E5%8F%91%E8%80%85%E7%9A%84%E5%AF%8C%E9%93%BE%E6%8E%A5/</link><pubDate>Sat, 10 Jan 2026 00:00:00 +0000</pubDate><guid>https://granda.org/zh/2026/01/10/%E6%87%92%E6%83%B0%E5%BC%80%E5%8F%91%E8%80%85%E7%9A%84%E5%AF%8C%E9%93%BE%E6%8E%A5/</guid><description>&lt;p&gt;当我在Twitter或Slack上分享博客链接时,它显示为纯文本。没有预览图片。只有一个像&lt;a href="https://granda.org/en/2026/01/02/claude-code-on-the-go/"&gt;granda.org/en/2026/01/02/claude-code-on-the-go/&lt;/a&gt;这样的URL。&lt;/p&gt;
&lt;p&gt;我需要Open Graph图片。标准方法:为每篇文章手动创建一个1200x630的图片。这很繁琐。我让Claude自动化它。&lt;/p&gt;
&lt;div class="desktop-only"&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart LR
 Push[git push] --&amp;gt; GHA[GitHub Actions]
 GHA --&amp;gt; Hugo[Hugo Server]
 GHA --&amp;gt; PW[Playwright]
 PW --&amp;gt;|screenshot| Hugo
 PW --&amp;gt; IMG[OG Image]
 IMG --&amp;gt; Commit[git commit]
 Commit --&amp;gt; Deploy[Deploy]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="mobile-only"&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart TB
 Push[git push] --&amp;gt; GHA[GitHub Actions]
 GHA --&amp;gt; Hugo[Hugo Server]
 GHA --&amp;gt; PW[Playwright]
 PW --&amp;gt;|screenshot| Hugo
 PW --&amp;gt; IMG[OG Image]
 IMG --&amp;gt; Commit[git commit]
 Commit --&amp;gt; Deploy[Deploy]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="设置"&gt;设置&lt;/h2&gt;
&lt;p&gt;我向Claude说明了问题:文章需要社交预览图片,但我不想手动创建它们。截取文章内容的屏幕截图,将其保存为OG图片,自动更新前置元数据。&lt;/p&gt;
&lt;h2 id="claude构建了什么"&gt;Claude构建了什么&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;组件&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;code&gt;generate-og-image.js&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;用于截图文章的Playwright脚本&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;generate-og-images.yml&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;文章更改时触发的GitHub Action&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;baseof.html&lt;/code&gt;更改&lt;/td&gt;
 &lt;td&gt;条件性og:image元标签&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Playwright脚本启动无头Chrome,导航到文章,强制浅色模式,隐藏页眉和页脚,并以1200x630截图:&lt;/p&gt;</description></item><item><title>取消订阅</title><link>https://granda.org/zh/unsubscribe/</link><pubDate>Sat, 10 Jan 2026 00:00:00 +0000</pubDate><guid>https://granda.org/zh/unsubscribe/</guid><description>&lt;p id="unsubscribe-message"&gt;处理中...&lt;/p&gt;
&lt;script&gt;
(function() {
 const params = new URLSearchParams(window.location.search);
 const msg = document.getElementById('unsubscribe-message');

 if (params.get('success') === 'true') {
 msg.textContent = '您已成功取消订阅新闻通讯。';
 } else if (params.get('error') === 'missing') {
 msg.textContent = '错误:未提供取消订阅令牌。';
 } else if (params.get('error') === 'invalid') {
 msg.textContent = '错误:无效的取消订阅链接。';
 } else {
 msg.textContent = '错误:发生未知错误。';
 }
})();
&lt;/script&gt;</description></item><item><title>我的QA工程师是一个LLM</title><link>https://granda.org/zh/2026/01/09/%E6%88%91%E7%9A%84qa%E5%B7%A5%E7%A8%8B%E5%B8%88%E6%98%AF%E4%B8%80%E4%B8%AAllm/</link><pubDate>Fri, 09 Jan 2026 00:00:00 +0000</pubDate><guid>https://granda.org/zh/2026/01/09/%E6%88%91%E7%9A%84qa%E5%B7%A5%E7%A8%8B%E5%B8%88%E6%98%AF%E4%B8%80%E4%B8%AAllm/</guid><description>&lt;p&gt;Claude可以点击按钮。&lt;/p&gt;
&lt;p&gt;这听起来很简单,但它改变了我构建UI的方式。使用Playwright MCP,Claude不仅仅是编写代码——它打开浏览器,导航到localhost,并验证事物实际运行。它捕获我在代码审查中会错过的bug。&lt;/p&gt;
&lt;h2 id="设置"&gt;设置&lt;/h2&gt;
&lt;p&gt;Playwright MCP为Claude提供浏览器自动化。我用无头Chromium运行它:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&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;&amp;#34;mcpServers&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&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;&amp;#34;playwright&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&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;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;npx&amp;#34;&lt;/span&gt;&lt;span class="p"&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;&amp;#34;args&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@anthropic-ai/mcp-server-playwright&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;--headless&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;现在Claude可以导航、点击、输入和截图。它看到用户看到的东西。&lt;/p&gt;
&lt;div class="desktop-only"&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart LR
 Claude([Claude Code]) --&amp;gt;|browser_snapshot| PW[Playwright MCP]
 PW --&amp;gt; Browser[Headless Chrome]
 Browser --&amp;gt; App[localhost:1313]
 Browser -.-&amp;gt;|screenshot| Issue[(GitHub Issue)]
 Issue -.-&amp;gt;|visual history| PR[Pull Request]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="mobile-only"&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart TB
 Claude([Claude Code]) --&amp;gt;|browser_snapshot| PW[Playwright MCP]
 PW --&amp;gt; Browser[Headless Chrome]
 Browser --&amp;gt; App[localhost:1313]
 Browser -.-&amp;gt;|screenshot| Issue[(GitHub Issue)]
 Issue -.-&amp;gt;|visual history| PR[Pull Request]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;流程: Claude Code → Playwright MCP → 浏览器 → 截图 → GitHub Issues → PR&lt;/em&gt;&lt;/p&gt;</description></item><item><title>用Claude构建我自己的新闻通讯</title><link>https://granda.org/zh/2026/01/07/%E7%94%A8claude%E6%9E%84%E5%BB%BA%E6%88%91%E8%87%AA%E5%B7%B1%E7%9A%84%E6%96%B0%E9%97%BB%E9%80%9A%E8%AE%AF/</link><pubDate>Wed, 07 Jan 2026 00:00:00 +0000</pubDate><guid>https://granda.org/zh/2026/01/07/%E7%94%A8claude%E6%9E%84%E5%BB%BA%E6%88%91%E8%87%AA%E5%B7%B1%E7%9A%84%E6%96%B0%E9%97%BB%E9%80%9A%E8%AE%AF/</guid><description>&lt;p&gt;Hacker News流量高峰的第二天。四万访客，没有办法再次联系他们。我需要一个新闻通讯注册表单。&lt;/p&gt;
&lt;p&gt;我看了Buttondown、Beehiiv、Substack、ConvertKit。都太复杂了。我只需要收集电子邮件。我不需要营销活动、分析或订阅者管理。而且我想拥有自己的数据。&lt;/p&gt;
&lt;p&gt;所以我让Claude来构建它。&lt;/p&gt;
&lt;div class="desktop-only"&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart LR
 User([User]) --&amp;gt; Form[Newsletter Form]
 Form --&amp;gt;|POST /api/subscribe| Worker[Cloudflare Worker]
 Worker --&amp;gt; KV[(Cloudflare KV)]
 KV -.-&amp;gt;|Daily sync| GHA[GitHub Actions]
 GHA -.-&amp;gt; Repo[(subscribers.jsonl)]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="mobile-only"&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart TB
 User([User]) --&amp;gt; Form[Newsletter Form]
 Form --&amp;gt;|POST /api/subscribe| Worker[Cloudflare Worker]
 Worker --&amp;gt; KV[(Cloudflare KV)]
 KV -.-&amp;gt;|Daily sync| GHA[GitHub Actions]
 GHA -.-&amp;gt; Repo[(subscribers.jsonl)]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="设置"&gt;设置&lt;/h2&gt;
&lt;p&gt;我通过环境变量给Claude提供了一个Cloudflare API令牌，并描述了我想要的东西：一个收集电子邮件并将其存储在我控制的地方的表单。&lt;/p&gt;
&lt;p&gt;不到30分钟后，我就有了一个可工作的新闻通讯。&lt;/p&gt;
&lt;h2 id="claude构建了什么"&gt;Claude构建了什么&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;组件&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;HTML + vanilla JS&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;后端&lt;/td&gt;
 &lt;td&gt;Cloudflare Worker&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;存储&lt;/td&gt;
 &lt;td&gt;Cloudflare KV&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;同步&lt;/td&gt;
 &lt;td&gt;GitHub Actions&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;表单是一个发送到&lt;code&gt;/api/subscribe&lt;/code&gt;的Hugo partial：&lt;/p&gt;</description></item><item><title>随时随地使用Claude Code</title><link>https://granda.org/zh/2026/01/02/%E9%9A%8F%E6%97%B6%E9%9A%8F%E5%9C%B0%E4%BD%BF%E7%94%A8claude-code/</link><pubDate>Fri, 02 Jan 2026 00:00:00 +0000</pubDate><guid>https://granda.org/zh/2026/01/02/%E9%9A%8F%E6%97%B6%E9%9A%8F%E5%9C%B0%E4%BD%BF%E7%94%A8claude-code/</guid><description>&lt;p&gt;我在手机上并行运行六个Claude Code代理。没有笔记本电脑,没有台式机——只有iOS上的Termius和云虚拟机。&lt;/p&gt;
&lt;h2 id="设置"&gt;设置&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart LR
 A[Phone] --&amp;gt;|Termius + mosh| B[Tailscale VPN]
 B --&amp;gt; C[Vultr VM]
 C --&amp;gt; D[Claude Code]
 D --&amp;gt;|PreToolUse hook| E[Poke webhook]
 E --&amp;gt;|Push notification| A
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;流程是:启动任务,将手机放入口袋,当Claude需要输入时收到通知。从任何地方进行异步开发。&lt;/p&gt;
&lt;h2 id="基础设施"&gt;基础设施&lt;/h2&gt;
&lt;p&gt;硅谷的Vultr虚拟机:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Spec&lt;/th&gt;
 &lt;th&gt;Value&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Instance&lt;/td&gt;
 &lt;td&gt;vhf-8c-32gb&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Cost&lt;/td&gt;
 &lt;td&gt;$0.29/小时 (运行时约$7/天)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Access&lt;/td&gt;
 &lt;td&gt;仅Tailscale (无公共SSH)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;我只在工作时付费。两个脚本管理生命周期:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vm-start &lt;span class="c1"&gt;# Start VM, wait for Tailscale, connect via mosh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vm-stop &lt;span class="c1"&gt;# Halt VM&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我还有一个直接调用Vultr API的iOS快捷指令——在我甚至打开Termius之前就从手机启动虚拟机。&lt;/p&gt;
&lt;p&gt;虚拟机的公共IP没有SSH监听器。所有访问都通过Tailscale的私有网络。深度防御:云防火墙阻止除Tailscale协调之外的所有内容,本地nftables作为备份,fail2ban以防万一。&lt;/p&gt;
&lt;h2 id="移动终端"&gt;移动终端&lt;/h2&gt;
&lt;p&gt;Termius在iOS/Android上处理SSH和mosh。Mosh是关键——它能在网络切换中幸存。从WiFi切换到蜂窝网络,穿过信号盲区,让手机进入睡眠状态。连接持续存在。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mosh --ssh&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ssh -p 47892&amp;#34;&lt;/span&gt; mgranda@100.100.100.100
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一个问题:mosh不转发SSH代理。对于需要GitHub身份验证的git操作,我在tmux中使用常规SSH。&lt;/p&gt;
&lt;h2 id="会话持久性"&gt;会话持久性&lt;/h2&gt;
&lt;p&gt;Shell在登录时自动附加到tmux。关闭Termius,几小时后重新打开,一切都还在。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# In .zshrc&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$TMUX&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; tmux attach -t main 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; tmux new -s main
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;多个Claude代理在并行窗口中运行。&lt;code&gt;C-a c&lt;/code&gt;用于新窗口,&lt;code&gt;C-a n&lt;/code&gt;用于切换。在手机键盘上效果很好。&lt;/p&gt;</description></item><item><title>让AI来做</title><link>https://granda.org/zh/2025/12/28/%E8%AE%A9ai%E6%9D%A5%E5%81%9A/</link><pubDate>Sun, 28 Dec 2025 00:00:00 +0000</pubDate><guid>https://granda.org/zh/2025/12/28/%E8%AE%A9ai%E6%9D%A5%E5%81%9A/</guid><description>&lt;p&gt;我需要一台开发服务器：带SSH访问和开发工具的VM。我向Claude Code描述了我想要的，让它处理配置。&lt;/p&gt;
&lt;h2 id="提示"&gt;提示&lt;/h2&gt;
&lt;p&gt;我要求一台Vultr VM，配置8核和32GB内存，只能通过Tailscale访问，具有能在网络中断后恢复的持久会话。&lt;/p&gt;
&lt;h2 id="claude-code构建的内容"&gt;Claude Code构建的内容&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. 通过Vultr API配置VM&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;它创建了一个8核/32GB的Ubuntu实例，等待IP可用，然后以root身份SSH连接。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 强化SSH&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-conf" data-lang="conf"&gt;Port 49152 # 随机高端口
PermitRootLogin no
PasswordAuthentication no
ListenAddress 100.x.x.x # 仅Tailscale接口
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;仅基于密钥的认证，仅在Tailscale接口上监听。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 设置三层防火墙&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;UFW&lt;/strong&gt;：拒绝除Tailscale子网外的所有传入连接&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fail2ban&lt;/strong&gt;：登录尝试失败后自动封禁IP&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vultr防火墙&lt;/strong&gt;：在虚拟化程序级别阻止所有连接&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;4. 安装启用了SSH的Tailscale&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -fsSL https://tailscale.com/install.sh &lt;span class="p"&gt;|&lt;/span&gt; sh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tailscale up --ssh --authkey&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$TAILSCALE_KEY&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这通过Tailscale的内置SSH服务器创建了第二条SSH路径，独立于OpenSSH。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. 安装开发栈&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Node.js（通过nvm）、Python（通过pyenv）、Docker和常用工具。配置Docker无需sudo运行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. 使用tmux设置持久会话&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一个启动脚本，自动重新连接到现有会话，这样网络中断就不会终止正在运行的进程。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7. 创建本地管理脚本&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在我的笔记本电脑上创建&lt;code&gt;dev-start&lt;/code&gt;和&lt;code&gt;dev-stop&lt;/code&gt;脚本，通过Vultr API启动和停止VM。&lt;/p&gt;
&lt;h2 id="锁定"&gt;锁定&lt;/h2&gt;
&lt;p&gt;完成后，Claude Code通过尝试以root身份SSH连接来验证root登录是否已禁用。登录失败。fail2ban注意到失败的尝试并封禁了我们。&lt;/p&gt;
&lt;p&gt;Claude Code已将Tailscale SSH设置为备用路径。它使用&lt;code&gt;tailscale ssh&lt;/code&gt;重新进入并执行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fail2ban-client &lt;span class="nb"&gt;set&lt;/span&gt; sshd unbanip 100.x.x.x
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="配置"&gt;配置&lt;/h2&gt;
&lt;p&gt;你需要：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Claude Code（或具有工具使用能力的类似AI）&lt;/li&gt;
&lt;li&gt;云提供商账户（Vultr、DigitalOcean、AWS等）&lt;/li&gt;
&lt;li&gt;Tailscale账户&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;将API密钥存储为环境变量并按名称引用它们（例如&lt;code&gt;$VULTR_API_KEY&lt;/code&gt;），这样机密就不会出现在对话中。&lt;/p&gt;</description></item><item><title>使用Claude和GitHub Actions自动翻译博客</title><link>https://granda.org/zh/2025/12/23/%E4%BD%BF%E7%94%A8claude%E5%92%8Cgithub-actions%E8%87%AA%E5%8A%A8%E7%BF%BB%E8%AF%91%E5%8D%9A%E5%AE%A2/</link><pubDate>Tue, 23 Dec 2025 00:00:00 +0000</pubDate><guid>https://granda.org/zh/2025/12/23/%E4%BD%BF%E7%94%A8claude%E5%92%8Cgithub-actions%E8%87%AA%E5%8A%A8%E7%BF%BB%E8%AF%91%E5%8D%9A%E5%AE%A2/</guid><description>&lt;p&gt;我写的每篇文章都会自动翻译。Claude处理翻译，将结果提交到main，翻译版本与原文一起部署。&lt;/p&gt;
&lt;h2 id="流程"&gt;流程&lt;/h2&gt;
&lt;p&gt;当英文内容合并到main时，翻译工作流在GitHub Actions中运行：&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;on&lt;/span&gt;&lt;span class="p"&gt;:&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;push&lt;/span&gt;&lt;span class="p"&gt;:&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;main]&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&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="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;content/**/*.en.md&amp;#39;&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;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart LR
 B[合并到main] --&amp;gt; C[生成翻译]
 C --&amp;gt; D[提交到main]
 D --&amp;gt; E[所有语言一起部署到生产环境]
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="提示词"&gt;提示词&lt;/h2&gt;
&lt;p&gt;工作流使用&lt;a href="https://github.com/anthropics/claude-code-action"&gt;claude-code-action&lt;/a&gt;：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;For each .en.md file that was added or modified:
1. Read the English content
2. Create translations for these languages: es, nl, de, it, fr, ja, zh, ru, hi
3. Save each translation as filename.{lang}.md (e.g., hello-world.es.md)
4. Preserve the frontmatter structure exactly, but translate the title
5. Translate the body content naturally
6. Keep code blocks, URLs, file paths, and technical terms unchanged
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;一个提示词处理所有9种语言。&amp;ldquo;自然翻译&amp;quot;比&amp;quot;翻译这段文字&amp;quot;产生更流畅的输出。代码块和技术术语保持英文。&lt;/p&gt;</description></item><item><title>关于</title><link>https://granda.org/zh/about/</link><pubDate>Mon, 22 Dec 2025 00:00:00 +0000</pubDate><guid>https://granda.org/zh/about/</guid><description>&lt;h2 id="摘要"&gt;摘要&lt;/h2&gt;
&lt;p&gt;本文档提供关于本博客作者的背景信息。&lt;/p&gt;
&lt;h2 id="1-背景"&gt;1. 背景&lt;/h2&gt;
&lt;p&gt;软件工程师，拥有15年经验，其中10年在该领域实际工作。
专注领域包括：&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;h2 id="2-联系方式"&gt;2. 联系方式&lt;/h2&gt;
&lt;p&gt;乐于与业内同行交流：
&lt;a href="https://x.com/mtt"&gt;@mtt&lt;/a&gt;&lt;/p&gt;</description></item><item><title>关于</title><link>https://granda.org/zh/authorship/</link><pubDate>Mon, 22 Dec 2025 00:00:00 +0000</pubDate><guid>https://granda.org/zh/authorship/</guid><description>&lt;h2 id="摘要"&gt;摘要&lt;/h2&gt;
&lt;p&gt;本文档描述了本博客所采用的写作方法论。&lt;/p&gt;
&lt;h2 id="1-引言"&gt;1. 引言&lt;/h2&gt;
&lt;p&gt;本博客主要在大型语言模型(LLMs)的协助下生成。作者提供方向、编辑和质量控制;机器提供初稿和翻译。&lt;/p&gt;
&lt;h2 id="2-理由"&gt;2. 理由&lt;/h2&gt;
&lt;p&gt;作者相信在AI辅助内容创作方面保持透明。与其掩盖所使用的工具,这一披露旨在:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;为读者设定适当的期望&lt;/li&gt;
&lt;li&gt;为围绕AI内容的诚实规范做出贡献&lt;/li&gt;
&lt;li&gt;承认现代写作的协作性质&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="3-这意味着什么"&gt;3. 这意味着什么&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;想法&lt;/strong&gt;: 源自人类&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;结构&lt;/strong&gt;: 协作式&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文字&lt;/strong&gt;: 主要由AI生成,由人类审核&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;翻译&lt;/strong&gt;: 完全由AI生成(未经人类审核)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="4-质量保证"&gt;4. 质量保证&lt;/h2&gt;
&lt;p&gt;英文内容在发布前经过人工审核。翻译是自动生成的,可能包含错误或笨拙的措辞。作者对最终发布的英文内容负责。&lt;/p&gt;
&lt;h2 id="5-联系方式"&gt;5. 联系方式&lt;/h2&gt;
&lt;p&gt;关于本政策的问题或疑虑可以直接联系作者。&lt;/p&gt;</description></item><item><title>你好世界</title><link>https://granda.org/zh/2025/12/20/%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C/</link><pubDate>Sat, 20 Dec 2025 00:00:00 +0000</pubDate><guid>https://granda.org/zh/2025/12/20/%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C/</guid><description>&lt;p&gt;欢迎来到我的博客！这是我的第一篇文章。&lt;/p&gt;
&lt;p&gt;我会在这里分享想法、笔记和有趣的事情。&lt;/p&gt;</description></item></channel></rss>