<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>周凯</title>
        <link>https://zhoukai.space/</link>
        <description>周凯的博客</description>
        <lastBuildDate>Thu, 30 Jul 2026 15:23:02 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>周凯</title>
            <url>https://zhoukai.space/favicon.svg</url>
            <link>https://zhoukai.space/</link>
        </image>
        <copyright>CC BY-NC-SA 4.0 2026-PRESENT © 周凯</copyright>
        <atom:link href="https://zhoukai.space/feed.xml" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[一行 zsh 函数，省掉 clone 前后的 cd]]></title>
            <link>https://zhoukai.space/posts/gtc-git-clone-cd</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/gtc-git-clone-cd</guid>
            <pubDate>Wed, 10 Jun 2026 03:00:00 GMT</pubDate>
            <description><![CDATA[一个叫 gtc 的 zsh 函数：切到统一目录、clone、自动 cd 进仓库，三步并一步。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<p><code>git clone</code> 前后各有一个 <code>cd</code>，几乎是每个开发者的肌肉记忆。先切到某个统一目录，clone 完再切进仓库。步骤不多，但每天重复十几次就烦了。</p>
<pre><code class="language-bash">cd ~/code                                  # clone 前：先到统一目录
git clone git@github.com:some/repo.git
cd repo                                    # clone 后：切进仓库
</code></pre>
<p>三步可以合成一步。</p>
<h2>函数</h2>
<pre><code class="language-bash">gtc() {
  local repo=&quot;$1&quot;

  if [ -z &quot;$repo&quot; ]; then
    echo &quot;用法: gtc &lt;git-url&gt;&quot;
    return 1
  fi

  local base_dir=&quot;$HOME/code&quot;
  mkdir -p &quot;$base_dir&quot;
  cd &quot;$base_dir&quot; || return 1

  git clone &quot;$repo&quot; || return 1

  local repo_name
  repo_name=&quot;$(basename &quot;$repo&quot; .git)&quot;
  cd &quot;$repo_name&quot; || return 1
}
</code></pre>
<p>放到 <code>~/.zshrc</code> 里，之后：</p>
<pre><code class="language-bash">gtc git@github.com:some/repo.git
# clone 完成，已经在 repo 目录里了
</code></pre>
<p>名字 <code>gtc</code> 取自 &quot;git clone + cd&quot;。三行关键逻辑各管一部分：<code>mkdir -p</code> 保证目标目录存在，<code>cd &quot;$base_dir&quot;</code> 省掉 clone 前的手动切目录，最后的 <code>basename</code> + <code>cd</code> 省掉 clone 后切进仓库。没有别名包装或其他花活——只是一个没有惊喜的工具函数。</p>
<p>不想放在 <code>~/code/</code> 的话，把 <code>base_dir</code> 改成你的路径就行。<code>~/src</code>、<code>~/projects</code> 都可以。</p>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[Claude Code Dynamic Workflows：把编排逻辑搬进代码的新原语]]></title>
            <link>https://zhoukai.space/posts/claude-code-dynamic-workflows</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/claude-code-dynamic-workflows</guid>
            <pubDate>Wed, 10 Jun 2026 02:00:00 GMT</pubDate>
            <description><![CDATA[Dynamic Workflows 把 agent 编排从模型临场决策搬进可执行脚本——循环、分支、并行全固化成代码，数百个 subagent 同时开工，主上下文只收最终结果。拆解它的架构、边界和为什么这不是又一个自动化工具。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>当任务大到一次对话装不下时，让 Claude 把编排过程写成一段可执行的脚本——循环、分支、并行全固化在代码里，数百个 subagent 同时开工，主上下文只收最后那个答案。这不是又一个&quot;可视化拖拽工作流&quot;工具，而是一种把 agent 编排从临场决策变成可读、可审、可复用的代码资产的新原语。</p>
</blockquote>
<h2>Bun 的成绩单</h2>
<p>5 月 28 日，Anthropic 发布了 Claude Opus 4.8，同时放出了一个研究预览功能——Dynamic Workflows（动态工作流）。</p>
<p>先看一组数字：<strong>11 天</strong>、<strong>约 75 万行 Rust 代码</strong>、<strong>99.8% 的原有测试通过</strong>。这是 Bun 作者 Jarred Sumner 把整个 Bun 运行时<a href="https://github.com/oven-sh/bun/pull/30412">从 Zig 迁移到 Rust</a> 的成绩单，扛起这场迁移的主力正是 Dynamic Workflows。</p>
<p>官方博客对它的定位写得很直接：</p>
<blockquote>
<p>Some problems are too big for one pass by a single agent, especially in complex, legacy codebases: a bug hunt across an entire service, a migration that touches hundreds of files, a plan you want stress-tested from every angle before you commit to it.</p>
</blockquote>
<p>跨整个服务的 bug 排查、动辄上百个文件的迁移、需要从各个角度反复推敲才敢拍板的方案——这些任务的共同点是规模超出了一轮对话能协调的范围。Dynamic Workflows 给出的答案，是让 Claude 把整个编排过程写成一段 JavaScript 脚本，交给一个确定性运行时去执行。</p>
<h2>编排权的转移</h2>
<p>要理解 Workflow 的位置，得先捋一遍 Claude Code 已有的几层协作能力。</p>
<p>最底层是单个 session，一个 Agent 实例从头干到尾。往上一层是 subagent——主 Agent 派生出若干小弟去搜文件、读代码、跑命令，干完把结果汇报回来。再往上是不久前推出的 <a href="https://code.claude.com/docs/en/agent-teams">Agent Teams</a>，多个独立实例像团队一样并行协作。</p>
<p>这几层有一个共同的瓶颈：<strong>编排者始终是 Claude 本身</strong>。它逐轮决策下一步派谁去干什么，每一个 subagent 的返回结果都要先回到 Claude 的上下文窗口里，它读完才能决定接下来怎么走。这套机制在任务规模不大时很灵活，可一旦要协调几十上百个并行任务，上下文窗口就装不下了。</p>
<p>Workflow 换了个思路。Claude 不再亲自逐轮调度，而是先把整个编排过程<strong>写成一段 JavaScript 脚本</strong>——循环、分支、中间结果的收集全都固化在代码里——再交给一个独立的运行时去执行。官方文档把这个转变概括得很准确：</p>
<blockquote>
<p>A workflow moves the plan into code. With subagents and skills, Claude is the orchestrator: it decides turn by turn what to spawn next, and every result lands in Claude's context. A workflow script holds the loop, the branching, and the intermediate results itself, so Claude's context holds only the final answer.</p>
</blockquote>
<p>计划被搬进了代码。脚本自己持有循环、分支和中间结果，Claude 的上下文里只剩下最后那个答案。</p>
<p>这个转变的关键不在&quot;自动化&quot;，而在<strong>编排权从模型手里交了出去</strong>。模型不再负责&quot;下一步干什么&quot;，它只负责在脚本指定的节点上完成具体工作。调度逻辑变成了你可以读、可以审、可以存下来反复跑的代码——而不是每次对话里临时想出来的、转瞬即逝的决策序列。</p>
<h2>本地运行时</h2>
<p>很多人以为 Workflow 是 Anthropic 服务端的某个编排引擎。实际情况是：<strong>Workflow 工具本身不请求任何服务端</strong>。它是 Claude Code 在你本机跑的一段 JavaScript 编排脚本——<code>agent()</code>、<code>parallel()</code>、<code>pipeline()</code> 这些都是在你电脑上执行的控制流。真正去请求服务端的，是脚本里 <code>agent()</code> 调用 spawn 出来的每个 subagent，而 subagent 调模型的方式跟你主对话窗口完全一样。</p>
<p>这意味着：如果你用第三方 API 中转，Workflow 跑挂了，那跟 Workflow 没关系——它用的就是 Claude Code 平时调模型一直在用的那套接口。</p>
<p>把这层关系理清之后，运行模型就清晰了：一个<strong>确定性的 JavaScript 运行时</strong>当指挥，它只会循环、拼字符串、<code>await</code>，本身不含任何 LLM；只有当脚本执行到 <code>agent(...)</code> 那一行，运行时才临时雇一个 LLM subagent 干活。而&quot;真正的 Agent&quot;——你正在对话的主 Claude——在脚本执行期间<strong>根本没在运行</strong>：它在发出 Workflow 调用后的那一回合就结束了，脚本在后台独立跑，跑完用一条通知把它叫醒，让它去读最后的结果。</p>
<p>一句话记住这个分工：<strong>JS 运行时当指挥（无脑、确定性），在 <code>agent()</code> 点临时雇 LLM 干活，主 Agent 全程在睡觉，只最后被叫醒读结果。</strong></p>
<h2>脚本骨架</h2>
<p>Workflow 是一段 JS 脚本。每个脚本<strong>必须</strong>以 <code>export const meta = {...}</code> 开头，且 meta 必须是纯字面量——不能有变量、函数调用、模板插值。它定义了脚本的名字、描述和阶段划分：</p>
<pre><code class="language-javascript">export const meta = {
  name: 'find-flaky-tests',
  description: 'Find flaky tests and propose fixes',
  phases: [
    { title: 'Scan', detail: 'grep test logs for retries' },
    { title: 'Fix', detail: 'one agent per flaky test' },
  ],
}

phase('Scan')
const flaky = await agent('grep CI logs for retry markers', { schema: FLAKY_SCHEMA })
phase('Fix')
// ...
</code></pre>
<p>核心原语不多，一张表就够了：</p>
<table>
<thead>
<tr>
<th>原语</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>agent(prompt, opts)</code></td>
<td>起一个 subagent，prompt 是普通 JS 字符串</td>
</tr>
<tr>
<td><code>parallel(tasks)</code></td>
<td>一批 agent 全部跑完才往下走（有屏障）</td>
</tr>
<tr>
<td><code>pipeline(items, ...stages)</code></td>
<td>每个 item 独立流过多阶段，互不等待</td>
</tr>
<tr>
<td><code>phase(title)</code></td>
<td>标记阶段边界，对应 meta.phases</td>
</tr>
<tr>
<td><code>log(msg)</code></td>
<td>向用户输出状态，不进主上下文</td>
</tr>
</tbody>
</table>
<p>整段脚本里最容易踩的坑，是 <code>pipeline</code> 和 <code>parallel</code> 分不清。两者的本质分界是<strong>有没有屏障</strong>：<code>parallel</code> 会等这一批全部跑完才往下走，<code>pipeline</code> 则让每个 item 各自独立流过所有 stage。</p>
<p>典型浪费写法：</p>
<pre><code class="language-text">const a = await parallel(...)   // 屏障：等全部跑完
const b = transform(a)          // 只是 flatten/map/filter，没有跨 item 依赖
const c = await parallel(b.map(...))
</code></pre>
<p>如果 5 个任务快慢不一，中间这个屏障会让快的干等慢的。正确做法是把中间的 transform 塞进 pipeline 的一个 stage：</p>
<pre><code class="language-javascript">const results = await pipeline(
  DIMENSIONS,
  d =&gt; agent(d.prompt, { label: `review:${d.key}`, schema: FINDINGS }),
  review =&gt; parallel(review.findings.map(f =&gt; () =&gt;
    agent(`对抗性验证: ${f.title}`, { schema: VERDICT })
      .then(v =&gt; ({ ...f, verdict: v }))
  ))
)
</code></pre>
<p>只有三种情况才真正需要 barrier：下一阶段前要对<strong>全集</strong>去重或合并；要根据总数提前退出（&quot;0 个 bug 就跳过验证阶段&quot;）；下阶段的 prompt 要引用&quot;其他所有发现&quot;做横向比较。除此之外，<strong>有疑问就用 pipeline</strong>。</p>
<h2>DAG 之外</h2>
<p>聊到编排，很多人第一反应是 DAG（有向无环图）——Airflow、Argo、GitHub Actions 的 <code>needs:</code>，都是先把依赖图画死再按图执行。</p>
<p>Workflow 不一样。它是<strong>图灵完备的命令式 JavaScript</strong>，能写出 DAG 表达不了的东西，最典型的就是循环——比如&quot;一直找 bug，直到连续两轮都没有新增&quot;：</p>
<pre><code class="language-text">let dry = 0
while (dry &lt; 2) {                            // 回边，控制流图里有环
  const fresh = (await parallel(FINDERS.map(...))).filter(isNew)
  if (!fresh.length) { dry++; continue }
  dry = 0
  confirmed.push(...await verify(fresh))
}
</code></pre>
<p>除了循环，它还能写运行时才决定的分支（<code>if (bugs.length === 0) return</code>），以及动态扇出（下一阶段起几个 agent，取决于上一阶段返回了多少条结果）。图的形状事先不知道——在&quot;程序结构&quot;层面，它比 DAG 严格更强。</p>
<p>但盯住某一次具体执行，它又一定是 DAG：数据只往时间前方流，循环被展开后，第 N+1 轮和第 N 轮的 agent 是不同的节点——环在&quot;程序&quot;里，展开成&quot;轨迹&quot;后被拉直成一条链。</p>
<p>一句话：<strong>带 <code>while</code> 的程序不是 DAG，但跑一次的轨迹永远是 DAG。</strong> 这正是它比传统 DAG 编排器更灵活的地方——图的拓扑是运行时由脚本跑出来的，不是事先画死的。</p>
<h2>执行模型与硬约束</h2>
<p>Workflow 的运行时跟你的对话是隔离的——脚本在独立环境里执行，跑的过程中你的会话依然能响应。这套隔离也带来一组必须了解的约束：</p>
<ul>
<li><strong>并发上限</strong>：最多 16 个 subagent 同时跑（实际 <code>min(16, CPU 核数 − 2)</code>），单次运行最多 1000 个 agent。后者是防止脚本死循环失控的保险丝。</li>
<li><strong>权限继承</strong>：Workflow 内部派生的所有 subagent 自动以 <code>acceptEdits</code> 模式运行，文件编辑不再逐个弹窗确认，并继承当前会话的工具允许列表。但不在列表里的 shell 命令、网络抓取和 MCP 工具，仍然会弹确认框。<strong>大规模运行前，先把 agent 们需要的命令加进允许列表</strong>。</li>
<li><strong>脚本无文件系统权限</strong>：读写和执行全靠 subagent，脚本只负责调度。</li>
<li><strong>跨会话不可恢复</strong>：退出 Claude Code 后，下次进来 Workflow 从头再跑。但同会话内可以恢复，改完脚本后用 <code>resumeFromRunId</code> 重跑，没改动的 <code>agent()</code> 调用直接命中缓存。</li>
</ul>
<h2>三阶段移植</h2>
<p>回到开头的 Bun 案例。Jarred Sumner 用三个串联的 Workflow 把整个运行时从 Zig 移植到了 Rust：</p>
<p><strong>阶段一：生命周期映射。</strong> 第一个 Workflow 给 Zig 代码库里每一个 struct field 算出对应的正确 Rust lifetime。这一步单独拎出来做，是因为它是所有移植工作的地基——Rust 的内存安全建立在生命周期标注之上，这一层没算对，后面写出来的 <code>.rs</code> 文件根本过不了编译。</p>
<p><strong>阶段二：并行文件移植。</strong> 下一个 Workflow 把每个 <code>.zig</code> 文件移植成一个行为等价的 <code>.rs</code> 文件，<strong>数百个 agent 同时开工，每个文件还配两个 reviewer 做交叉审查</strong>。把这个量级跟 Agent Teams 对比一下——Agent Teams 同时跑三五个队员就到协调上限了，而 Workflow 是几百个 agent 并行外加双重 review。</p>
<p><strong>阶段三：编译与测试 fix loop。</strong> 文件移植完只是半成品，真正的硬仗是让它们能编译、能通过测试。第三个 Workflow 驱动整个 build 和 test 套件，循环修复直到两者都干净跑过。这正是上一节 <code>while</code> 循环模式的典型场景——靠脚本里的循环逻辑反复迭代，不靠 Claude 逐轮盯着。</p>
<p>移植合并后，还跑了一个 overnight workflow 处理收尾——扫描代码里不必要的数据拷贝，每发现一处优化就单独开一个 PR 交给人做最终审查。这种&quot;夜里挂着干长尾清理、产出一堆待 review 的 PR&quot;的用法，很有意思。</p>
<p>需要说清楚的是，官方标注 Bun 的 Rust 版本<strong>当时还没进入生产环境</strong>——流程跑通了、测试过了，但离上线还有距离。</p>
<h2>133 个会话画像</h2>
<p>Bun 是个极端案例。我自己拿一个更日常的任务试了一下：给 <code>~/.claude</code> 目录下 <strong>133 个会话、130MB 的 JSONL 记录</strong>做&quot;使用画像&quot;。</p>
<p>整个任务拆成&quot;<strong>主 Agent 预处理 + Workflow 编排</strong>&quot;两段。主 Agent 先把 133 个会话压缩成&quot;标题 + 用户输入 + 元数据&quot;，得到 601 条人类输入，切成 10 个批次。然后 Workflow 上场：<strong>10 个分析 agent 并行各啃一个批次</strong>，按统一 schema 抽取领域分布、卡点、自动化候选，最后 1 个综合 agent 跨批汇总去重，产出一份带优先级的报告。</p>
<p>执行体大致长这样：</p>
<pre><code class="language-text">phase('分析')
const batches = Array.from({ length: 10 }, (_, i) =&gt;
  `${DIR}/batch_${String(i + 1).padStart(2, '0')}.md`)

const findings = await parallel(batches.map((path, i) =&gt; () =&gt;
  agent(ANALYZE_PROMPT(path), {
    label: `分析:batch_${String(i + 1).padStart(2, '0')}`,
    phase: '分析',
    schema: FINDING_SCHEMA,
  })
))

const ok = findings.filter(Boolean)
log(`分析完成：${ok.length}/${batches.length} 批返回有效结果`)

phase('综合')
const corpus = JSON.stringify(ok, null, 1)
const report = await agent(SYNTH_PROMPT, { label: '综合报告', phase: '综合' })
return report  // 唯一回到主上下文的东西
</code></pre>
<p>账单：<strong>11 个 agent、81.8 万 token、254 秒</strong>。</p>
<p>这个案例还顺带回答了一个很多人会问的问题：<strong>这事派几个 subagent 一样能做，区别在哪？</strong> 确实能做——区别不在&quot;能不能&quot;，在&quot;编排逻辑放哪、中间结果流到哪&quot;。用 Agent 工具派 10 个 subagent，10 份结果会全部回到主上下文，你在下一个回合用自己的脑子读完、决定怎么合。Workflow 把编排写成了代码，中间结果不进主上下文，只回最终报告，schema 自动校验、并发自动管控。</p>
<p>但得诚实地补一句：就<strong>这一次</strong>这种&quot;一把梭 map-reduce&quot;而言，两者差距其实不大——10 路并行合一次就完了，subagent 也够用。Workflow 真正赚到的是随复杂度放大的部分：阶段变多、需要循环、需要多轮对抗式验证、或者要 fan-out 到几十个单元时，用 subagent 手动协调会越来越痛。</p>
<h2>编排工具的对比</h2>
<p>看到&quot;用代码编排多步流程 + 步骤里塞 LLM&quot;，很容易冒出一个念头：这不就是 n8n、Coze、Dify 那套吗？无非是模型来编排。</p>
<p>先说共性：Anthropic 在《Building Effective Agents》里给了定义——<strong>Workflows 是 LLM 和工具通过&quot;预定义代码路径&quot;被编排的系统</strong>。按这个定义，Dynamic Workflow 和 n8n/Dify/Coze 是<strong>同一类</strong>——控制流都是确定性的，LLM 不会在运行时决定&quot;下一步走哪条边&quot;，脚本写好后就由无脑运行时执行，LLM 只在节点内部干活。</p>
<p>但差异不止&quot;模型自动编排&quot;一条：</p>
<table>
<thead>
<tr>
<th>维度</th>
<th>n8n / Coze / Dify</th>
<th>Dynamic Workflows</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>编排作者</strong></td>
<td>人</td>
<td>Claude 现场生成</td>
</tr>
<tr>
<td><strong>编排载体</strong></td>
<td>可视化 DAG（有向无环）</td>
<td>图灵完备 JS 代码（可写循环）</td>
</tr>
<tr>
<td><strong>节点性质</strong></td>
<td>固定连接器或模板 prompt</td>
<td>每个节点是自主 agent</td>
</tr>
<tr>
<td><strong>生成时机</strong></td>
<td>人预先搭建</td>
<td>针对当次任务量身生成</td>
</tr>
<tr>
<td><strong>可复用性</strong></td>
<td>建一次反复用</td>
<td>存为 <code>/</code> 命令反复用</td>
</tr>
</tbody>
</table>
<p>压成一句：<strong>Workflow ≈ 把 n8n 那张图，换成模型现场生成的一段代码。</strong> 换了作者（人 → 模型）和载体（可视化 DAG → 命令式代码）。第一样带来即时性和定制性，第二样带来表达力提升（能写循环和动态扇出）。</p>
<p>另外一点值得注意：AI 的介入发生在&quot;写代码&quot;那一刻，不在&quot;跑流程&quot;那一刻。n8n 是人写编排、确定性执行；Workflow 是模型写编排、确定性执行——跑起来之后模型在睡觉。两边执行流程的方式一样，差别只在编排脚本的作者。</p>
<h2>手搓方案</h2>
<p>既然 Workflow 本质是&quot;确定性脚本 + 在节点处调 LLM&quot;，那官方推出之前，自己手搓一个完全可行。核心拼图就一个：<code>claude -p</code>。</p>
<p><code>claude -p</code>（即 <code>--print</code>，headless 模式）非交互地跑完一整个 agent loop——思考、调工具、改文件——跑完即退出。它读 stdin、写 stdout，可以像普通命令行工具一样接进管道。把每一步当成一次 <code>claude -p</code> 调用，外面用 shell 写编排循环，就是 DIY 版 Workflow：</p>
<pre><code class="language-bash"># fan-out：10 个批次并行，每个起一个 claude -p
for f in batch_*.md; do
  claude -p &quot;分析这个批次：$(cat $f)&quot; --output-format json &gt; &quot;out_$f.json&quot; &amp;
done
wait                                    # ← 屏障，等全部跑完，对应 parallel()

# reduce：把 10 份结果拼起来，再起一个 claude -p 综合
claude -p &quot;综合这些发现：$(cat out_*.json)&quot; &gt; report.md
</code></pre>
<p>对照一下，<code>&amp;</code> 加 <code>wait</code> 就是 <code>parallel()</code> 的屏障，<code>$(cat ...)</code> 拼字符串就是 prompt 里的变量插值。社区里 <a href="http://futuresearch.ai">futuresearch.ai</a> 就用 <code>claude -p</code> 加文件系统轮询搭了一套 18 路并行的扫描流水线——子 agent 把结果写盘（成功写 <code>.json</code>、失败写 <code>.error</code>），编排器只轮询文件名而不把输出收进上下文，把复杂度从 O(n × 输出大小) 压到 O(n × 文件名)。</p>
<p>那官方 Workflow 比手搓版多了什么？模型没变，省掉的全是工程脏活：并发管控、结构化输出校验、错误恢复、进度展示、缓存命中。<strong>Workflow 是把这套手搓 harness 产品化了，不是改变模型。</strong> 理解这一层，对它的能力边界就有了底。</p>
<h2>适用场景</h2>
<p>不是每个任务都值得起一个 Workflow。它本质是用大量并行 agent 换效率，并行 agent 是实打实烧 token 的。</p>
<p><strong>该用的场景：</strong></p>
<ul>
<li><strong>代码库范围的批量排查</strong>：全仓库 bug 扫描、安全审计、授权检查、危险模式加固。共同点是&quot;搜索加独立验证&quot;——并行搜遍整个服务，再对每个发现单独验证。</li>
<li><strong>大规模迁移</strong>：框架替换、API 弃用迁移、跨语言移植。Bun 是最极致的例子。</li>
<li><strong>需要反复推敲的关键决策</strong>：让 Claude 从多个独立角度各做一遍，再派对抗性的 agent 试图推翻这些结果，迭代到答案收敛。</li>
<li><strong>长尾清</strong>：像 overnight workflow，挂着自动扫描问题、逐个开 PR。</li>
</ul>
<p><strong>不值得用的场景：</strong></p>
<ul>
<li>一两步就能搞定的小修补。</li>
<li>需要中途频繁拍板的探索性工作——Workflow 执行期间不接收人工输入（除了权限弹窗）。</li>
<li>碰安全和支付等高风险代码的改动。</li>
</ul>
<p>把 Claude Code 现有的几种协作原语放在一起，选型逻辑大致是：</p>
<table>
<thead>
<tr>
<th>场景</th>
<th>用哪个</th>
</tr>
</thead>
<tbody>
<tr>
<td>主流程中派活搜索、读文件、跑命令</td>
<td>Subagent</td>
</tr>
<tr>
<td>多角色讨论、需要队员通信</td>
<td>Agent Teams</td>
</tr>
<tr>
<td>可复用的固定格式工作流</td>
<td>Skill</td>
</tr>
<tr>
<td>大规模并行、多阶段编排、循环验证</td>
<td>Workflow</td>
</tr>
</tbody>
</table>
<p>一句话：需要&quot;跑腿&quot;用 subagent，需要&quot;开会讨论&quot;用 Agent Teams，需要&quot;流水线作业&quot;用 Workflow。</p>
<h2>Token 账单</h2>
<p>官方说得很坦诚：<strong>单次 Workflow 的 token 消耗明显高于一次普通 Claude Code 对话</strong>。几十上百个 subagent 同时跑，每个都在烧 token，再叠加交叉验证、对抗式 review 这些&quot;额外冗余&quot;的设计，账单自然往上走。前面 133 会话的案例，11 个 agent 就吃掉了 81.8 万 token。</p>
<p>几条实用建议：</p>
<ul>
<li>从<strong>范围明确的小任务</strong>开始，摸清花费再放大。</li>
<li>大规模运行前，用 <code>/model</code> 确认模型，并在不需要最强能力的阶段要求 Claude 用更小的模型。</li>
<li>提前把命令加进允许列表，避免中途被权限弹窗打断一个跑了几小时的任务。</li>
<li>Workflow 随时可以叫停，已完成的工作不会白费。</li>
</ul>
<h2>已知限制</h2>
<p>研究预览阶段，几个边界先摆在这：</p>
<ul>
<li><strong>运行中途不接受人工输入</strong>：除了权限确认，不会停下来等你拍板。需要分段签核的流程得拆成多个独立 Workflow。</li>
<li><strong>脚本本身没有文件系统权限</strong>：读写全靠 subagent。</li>
<li><strong>并发与总量封顶</strong>：16 并发 / 1000 次 agent 调用。</li>
<li><strong>跨会话不可恢复</strong>：退出即从头。</li>
<li><strong>自定义 Workflow 传参机制</strong>还不够明确。</li>
<li>整个功能仍在研究预览，行为和约束可能随版本调整。</li>
</ul>
<h2>编排代码化</h2>
<p>回到文章标题——&quot;把编排逻辑搬进代码的新原语&quot;。为什么&quot;搬进代码&quot;这件事本身值得认真对待？</p>
<p>第一，<strong>编排从临场决策变成了可审计的资产</strong>。模型在每次对话里临时决定&quot;下一步干什么&quot;，这是转瞬即逝的、不可复现的。把这段逻辑写成脚本存进 <code>.claude/workflows/</code>，它就变成了可以读、可以审、可以版本控制、可以在团队间共享的代码。这对工程实践的影响不小——你可以 review 一段编排逻辑，就像 review 任何一段代码一样。</p>
<p>第二，<strong>扩展方式从&quot;装更多上下文&quot;变成了&quot;让代码持有中间状态&quot;</strong>。长久以来，解决&quot;任务太大&quot;的思路是扩大上下文窗口——1M token、2M token。Workflow 走的是另一条路：上下文只放最终结果，中间过程交给脚本变量持有。这是一个从&quot;堆容量&quot;到&quot;改架构&quot;的转变。</p>
<p>第三，<strong>它跟 Opus 4.8 同天发布不是巧合</strong>。几百个 agent 并行、互相 review 彼此结论时，每个节点的可靠性就被放大了——节点是概率性的 LLM，多步流程里的不确定性会层层累积。Opus 4.8 这一代专门强化了&quot;不放过自己不确定的东西&quot;，这恰恰是交叉验证能成立的前提。强模型不是 Workflow 的可选项，是它的承重墙。</p>
<p>最后，把它和 Codex 的 Goals（持久目标）摆在一起看很有意思。两者都想解决&quot;大任务怎么持续推进&quot;，但路径相反：Codex Goals 押注的是<strong>目标持久化</strong>——把目标钉在那，让模型自己找路逼近；Claude Code Workflow 走的是<strong>编排代码化</strong>——把过程写成脚本，靠脚本保证不跑偏。一个管&quot;往哪走&quot;，一个管&quot;怎么走&quot;，是两个方向上的工程探索。哪条路跑得更远现在下结论还太早，但两家头部产品在&quot;承载超大规模工作&quot;这个问题上同时发力，本身就说明这是 AI Coding 下一阶段的核心命题。</p>
<h2>总结</h2>
<p>Dynamic Workflows 把一件事讲清楚了：当任务大到一次对话装不下时，不该期待模型更聪明地管理上下文，而该把编排逻辑从模型手里拿出来，写成代码。</p>
<p>它的核心设计选择很明确——<strong>JavaScript 运行时做调度（确定性、无 LLM），subagent 做执行（有 LLM），主 Agent 只在收结果时介入</strong>。这个分工让&quot;数百个 agent 并行 + 交叉验证&quot;成为可能，也让编排本身变成了可存、可审、可复用的代码。</p>
<p>边界同样清晰：它适合大规模排查、迁移和需要多轮对抗式验证的决策，不适合小修补、探索性工作和安全敏感改动。token 成本显著高于普通对话，值得跑之前算一笔账。</p>
<p>我个人判断是：一年之内，这套&quot;模型现写编排脚本、调度一支 agent 舰队&quot;的打法，会从一家的研究预览变成几乎所有 coding agent 的标配。不是因为它更&quot;智能&quot;，而是因为它把 agent 协作的工程复杂度从&quot;模型能不能&quot;变成了&quot;代码怎么写&quot;——而&quot;代码怎么写&quot;是一个更可控、更可改进的问题。</p>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[AI Coding 工作流：从模糊需求到可交付实现]]></title>
            <link>https://zhoukai.space/posts/ai-coding-workflow-matt-pocock</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/ai-coding-workflow-matt-pocock</guid>
            <pubDate>Fri, 05 Jun 2026 05:40:00 GMT</pubDate>
            <description><![CDATA[基于 Matt Pocock 的 AI Coding 工作流演讲，整理一套从需求对齐、任务拆分、Agent 实现到评审验证的工程方法。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<p><strong>原视频链接</strong>：<a href="https://www.youtube.com/watch?v=-QFHIoCo-Ko">Full Walkthrough Workflow for AI Coding</a><br>
<strong>YouTube URL</strong>：<a href="https://www.youtube.com/watch?v=-QFHIoCo-Ko">https://www.youtube.com/watch?v=-QFHIoCo-Ko</a></p>
<blockquote>
<p>这篇文章基于 Matt Pocock 的 <a href="https://www.youtube.com/watch?v=-QFHIoCo-Ko">Full Walkthrough Workflow for AI Coding</a> 整理。重点不是复述某个工具，而是抽出一套更稳定的 AI Coding 工作流：先把模糊需求变成可执行任务，再让 Agent 在清晰边界、测试反馈和独立评审中完成实现。</p>
</blockquote>
<h2>核心判断：AI Coding 仍然是软件工程</h2>
<p>AI Coding 很容易被讲成一种全新的范式：写一个 prompt，Agent 自动把功能做完。</p>
<p>但更接近现实的理解是：AI 改变了执行方式，却没有取消软件工程本身。</p>
<p>需求仍然要澄清，任务仍然要拆分，架构边界仍然要设计，测试仍然要跑，代码仍然要评审，用户路径仍然要人工验收。Agent 可以承担越来越多实现细节，但它不能替开发者决定什么值得做、系统边界在哪里、什么结果算完成。</p>
<p>所以，好的 AI Coding 不是“把提示词写狠一点，然后希望模型做对”。更可靠的方式是设计一套系统，让 Agent 接收到：</p>
<ul>
<li>清晰的目标；</li>
<li>足够小的任务；</li>
<li>可执行的反馈；</li>
<li>隔离的工作空间；</li>
<li>新鲜上下文里的评审；</li>
<li>人类最终 QA。</li>
</ul>
<p>换句话说，AI Coding 的重点不是让人离开流程，而是把人的精力前移到需求、边界、反馈和验收上。</p>
<h2>上下文窗口有聪明区，也有失真区</h2>
<p>LLM 的上下文窗口并不是越长越稳定。</p>
<p>在一次长会话里，随着 token 不断累积，模型需要维持越来越多注意力关系。前面的问题、旧假设、局部讨论、失败路径和临时结论都会留在上下文里。会话越长，模型越容易把过期信息当成事实，也越容易在局部路径上越走越偏。</p>
<p>演讲里把相对可靠的上下文区间称为 smart zone，把后面容易失真的部分称为 dumb zone。这个说法虽然口语化，但对实际编码很有帮助。</p>
<p>它提醒我们：任务应该被切到 Agent 能在可靠上下文内完成探索、实现和验证。</p>
<p>这也解释了为什么“清空上下文”经常比“不断压缩上下文”更好。</p>
<p>压缩可以保留摘要，但摘要也会保留噪音、旧判断和被扭曲的历史。清空上下文后，Agent 回到系统提示词、仓库说明和当前新材料组成的基础状态。只要任务文档写得足够清楚，新的干净会话反而更可靠。</p>
<p>因此，AI Coding 的一个关键设计原则是：不要依赖一条无限延长的聊天记录。把重要决策写进可复用的任务文档，让每个任务都能从干净上下文重新接手。</p>
<h2>先对齐，而不是直接计划</h2>
<p>很多 AI Coding 失败，并不是因为模型不会写代码，而是因为一开始的需求就不够清楚。</p>
<p>例如一句“给产品加游戏化”听起来像一个功能，其实隐藏了很多决策：</p>
<ul>
<li>哪些行为可以获得积分；</li>
<li>历史进度是否要回填；</li>
<li>积分展示在哪里；</li>
<li>是否需要排行榜；</li>
<li>是否影响现有权限和数据模型；</li>
<li>哪些东西明确不做。</li>
</ul>
<p>这些决策通常依赖产品判断、业务上下文和用户偏好，不能直接交给模型猜。</p>
<p>所以工作流的第一步不应该是“让 Agent 写计划”，而应该是 alignment：先让 Agent 追问，直到人和 Agent 对问题形成共同理解。</p>
<p>一个有效的对齐方式是让 Agent 一次只问一个问题，并给出推荐答案和取舍说明。这样做有两个好处：</p>
<p>第一，用户不会被一大串问题压垮。<br>
第二，Agent 不能在关键决策暴露之前就急着生成计划。</p>
<p>这一步的产物不是代码，而是经过澄清的决策记录。它决定了后面任务能不能被安全委托。</p>
<h2>PRD 是目的地，看板是旅程</h2>
<p>完成需求对齐后，下一步是把对话压缩成一份目的地文档。</p>
<p>这里的 PRD 不需要是很重的公司模板。它的作用更具体：记录问题、目标、用户故事、实现决策、测试决策和不做什么。</p>
<p>一份对 Agent 有用的 PRD 至少应该回答这些问题：</p>
<table>
<thead>
<tr>
<th>问题</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>要解决什么问题</td>
<td>防止 Agent 做偏</td>
</tr>
<tr>
<td>用户会看到什么变化</td>
<td>明确用户可见结果</td>
</tr>
<tr>
<td>关键实现决策是什么</td>
<td>固化已经做过的取舍</td>
</tr>
<tr>
<td>哪些内容不做</td>
<td>控制范围</td>
</tr>
<tr>
<td>如何验证完成</td>
<td>给 Agent 可执行反馈</td>
</tr>
</tbody>
</table>
<p>PRD 说明“要去哪里”，但还不说明“怎么走”。真正用于执行的是下一层：issue 看板。</p>
<p>演讲里更推荐把 PRD 拆成带阻塞关系的 Kanban backlog，而不是一份线性多阶段计划。</p>
<p>线性计划通常假设一个 Agent 按阶段推进：先数据库，再服务层，再接口，再前端。看起来很整齐，但很容易把真实反馈推迟到最后。</p>
<p>看板的好处是可以显式记录依赖关系。一个 issue 被哪些任务阻塞，哪些任务可以并行，哪些任务必须先做，都能在 backlog 里表达出来。</p>
<p>当 backlog 结构清楚后，它就变成了人类和 Agent 的交接物。人负责塑造任务，Agent 负责领取未阻塞的小任务并执行。</p>
<h2>任务拆分要优先垂直切片</h2>
<p>Agent 很容易按技术层横向拆任务：</p>
<pre><code class="language-text">数据库 -&gt; 服务层 -&gt; API -&gt; 前端
</code></pre>
<p>这种拆法对人类架构师很熟悉，但对 AI Coding 不一定稳定。因为它会延迟端到端反馈：前几步看起来都完成了，直到最后接起来才发现数据模型、接口语义和 UI 状态不匹配。</p>
<p>更好的拆法是 vertical slice，也就是垂直切片。</p>
<p>一个垂直切片会穿过必要的技术层，产出一个很小但可验证的结果。它不追求一次完成完整功能，而是尽早证明路径走得通。</p>
<p>例如，与其这样拆：</p>
<pre><code class="language-text">1. 创建积分表
2. 写积分服务
3. 写积分 API
4. 写积分页面
</code></pre>
<p>不如先做一个最小闭环：</p>
<pre><code class="language-text">当用户完成一次指定动作时，系统能写入一条积分记录，
接口能返回当前积分总数，
页面能显示这个数字，
并且测试覆盖这条路径。
</code></pre>
<p>这个切片很小，但它能验证数据库、业务逻辑、接口和 UI 是否真的连通。后续扩展排行榜、历史回填、规则配置时，都会站在一个已经跑通的基础上。</p>
<p>这就是软件工程里 tracer bullet 的价值：先看到弹道，再决定是否继续加火力。</p>
<h2>AFK 实现不是失控实现</h2>
<p>当 PRD 和 issue 看板足够清楚后，部分实现任务可以进入 AFK 状态。</p>
<p>这里的 AFK 不是“完全放飞”，而是 Agent 可以在没有持续人工提示的情况下完成一个边界明确的任务。前提是任务已经具备：</p>
<ul>
<li>明确目标；</li>
<li>输入上下文；</li>
<li>约束条件；</li>
<li>验收方式；</li>
<li>可运行检查；</li>
<li>失败时的处理边界。</li>
</ul>
<p>一个最小实现循环可以写成这样：</p>
<pre><code class="language-text">读取 PRD 和 issue 看板
查看最近提交
选择一个未阻塞的 AFK issue
确认任务边界和验收标准
先补失败测试
实现最小改动
运行测试、类型检查和构建
提交结果
</code></pre>
<p>这个循环里最重要的是反馈。没有测试、类型检查、lint、构建和本地验证时，Agent 就是在盲写代码。反馈越弱，Agent 的产出上限越低。</p>
<p>这也是为什么 TDD 对 Agent 编程特别有价值。</p>
<p>先写失败测试，可以把需求变成可执行约束。再实现最小改动，可以减少 Agent 自由发挥的空间。最后重构，则把代码质量拉回到可维护状态。</p>
<p>如果让 Agent 先随便实现，再补一个证明自己实现正确的测试，测试就很容易变成形式主义。TDD 的关键是让测试先于实现出现。</p>
<h2>评审要使用新鲜上下文</h2>
<p>Agent 实现完后，可以继续用 AI 做代码评审，但最好不要在同一个已经很长的实现会话里评审。</p>
<p>原因很直接：实现过程可能已经消耗了上下文的可靠区间。更重要的是，实现会话里带有大量局部假设，评审者如果继承这些假设，就不容易发现问题。</p>
<p>更好的模式是：</p>
<pre><code class="language-text">清空上下文
提供 PRD / issue / diff / commit
要求 Reviewer 只看最终变化
对照编码规范、测试策略和任务目标做审查
</code></pre>
<p>实现阶段可以把编码规范作为 pull-based 信息：Agent 需要时自己查。<br>
评审阶段则应该把编码规范作为 push-based 信息：明确要求 Reviewer 拿代码和规范对照。</p>
<p>这会让评审更尖锐，因为 Reviewer 不会被实现者的局部路径影响。</p>
<p>自动化评审之后，仍然需要人工 QA。尤其是前端、交互、文案和产品体验，模型很难稳定判断成熟界面里的细节质量。</p>
<p>人工 QA 是把品味和真实用户体验重新放回系统的环节。它不只是“看一眼页面”，而是要真实走一遍用户路径，并把发现的问题重新写回 issue 看板。</p>
<h2>代码库质量决定 Agent 上限</h2>
<p>糟糕代码库会产生糟糕的 Agent 输出。</p>
<p>如果一个项目对人来说难以理解，对 Agent 通常也难以导航。大量浅模块、细碎导出、隐式全局状态和模糊边界，会让模型很难建立稳定心智模型。</p>
<p>更适合 Agent 协作的代码库，通常具备这些特征：</p>
<ul>
<li>模块边界清楚；</li>
<li>公共接口较小；</li>
<li>内部行为足够集中；</li>
<li>测试边界明确；</li>
<li>命名和目录能反映业务语义；</li>
<li>本地检查命令稳定可运行。</li>
</ul>
<p>这里的重点不是把代码拆得越碎越好，而是形成 deep modules：接口尽量小，内部行为足够完整，外部调用者不需要知道太多内部细节。</p>
<p>人类应该牢牢掌握模块边界、公共接口、数据模型和测试策略。Agent 更适合在这些约束下实现内部细节。</p>
<p>一旦边界交给模型随意扩散，后面的维护成本会迅速回到人身上。</p>
<h2>过程文档有用，但也会腐化</h2>
<p>PRD、计划和 issue 是执行期间非常有用的材料，但它们也会过期。</p>
<p>如果一份旧 PRD 长期留在仓库里，未来 Agent 可能把它当成权威上下文，即使代码、命名、需求和用户行为已经变化。</p>
<p>这就是 documentation rot 的问题：文档活得比事实更久。</p>
<p>比较稳妥的处理方式是：</p>
<ul>
<li>执行中的 PRD 和 issue 保持清晰；</li>
<li>完成后的计划材料及时关闭或归档；</li>
<li>不让历史过程文档静默影响未来任务；</li>
<li>真正长期有效的信息，沉淀到 README、架构文档或测试里。</li>
</ul>
<p>过程文档的价值在于服务当前执行。它一旦变成历史材料，就应该降低权威性。</p>
<h2>并行 Agent 需要结构化 backlog</h2>
<p>多 Agent 并行只有在任务已经被整理成适合并行的形状时才有价值。</p>
<p>如果 backlog 没有依赖关系，多个 Agent 很容易领取冲突任务：一个改数据模型，一个改 API，一个改前端，最后合并时互相打架。</p>
<p>更可靠的多 Agent 模式是：</p>
<pre><code class="language-text">Planner 选择下一组未阻塞 issue
Implementer 在独立 worktree 或分支里实现
Reviewer 检查对应 commit
Merger 控制合并顺序并处理冲突
</code></pre>
<p>关键不在具体工具，而在系统形态：隔离工作、明确评审、受控合并。</p>
<p>这也说明并行不是起点，而是规划质量的结果。只有当任务边界、依赖关系和验证方式已经足够清楚时，并行 Agent 才能提升吞吐，而不是制造混乱。</p>
<h2>一套可执行的简化流程</h2>
<p>如果把整套方法压缩成实践步骤，可以是下面这样：</p>
<ol>
<li>把模糊需求变成追问会话。</li>
<li>要求 Agent 一次只问一个问题，并给出推荐答案和取舍。</li>
<li>把对齐后的内容整理成 PRD，记录目标、决策、测试和不做什么。</li>
<li>把 PRD 拆成 issue，看板里显式写出阻塞关系。</li>
<li>拒绝纯横向分层的 issue，优先做可验证的垂直切片。</li>
<li>标出哪些 issue 可以 AFK 实现，哪些必须人工决策。</li>
<li>让 Agent 对单个未阻塞 issue 使用 TDD 和本地检查完成实现。</li>
<li>用新鲜上下文评审最终 diff 或 commit。</li>
<li>人工 QA 用户可见路径。</li>
<li>把缺陷重新写回 issue，并关闭或归档过期过程文档。</li>
</ol>
<p>这套流程的核心不是复杂，而是让每一步都能留下可检查的产物。</p>
<p>对齐阶段留下决策。<br>
PRD 留下目的地。<br>
看板留下路径。<br>
测试留下约束。<br>
提交留下结果。<br>
评审留下问题。<br>
QA 留下真实反馈。</p>
<h2>总结</h2>
<p>这场演讲最有价值的部分，不是某个具体工具，也不是某个万能 prompt。</p>
<p>真正有价值的是工作流形状。</p>
<p>AI Coding 做得好时，开发者不是坐等模型生成代码，而是在设计一套让 Agent 更容易成功的工程系统。这个系统会限制任务范围，暴露关键决策，提供可执行反馈，隔离实现上下文，并用新的上下文做评审。</p>
<p>工具会继续变化，但困难的部分仍然是那些老问题：需求、边界、反馈、测试、评审和品味。</p>
<p>这也是为什么 AI Coding 越往后走，越不像单纯的 prompt 技巧，反而越像软件工程本身。</p>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[Codex 记忆功能的本地实现：文件索引、摘要与检索]]></title>
            <link>https://zhoukai.space/posts/codex-memory-implementation</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/codex-memory-implementation</guid>
            <pubDate>Thu, 21 May 2026 02:30:00 GMT</pubDate>
            <description><![CDATA[基于本机 Codex 记忆目录的实际结构，解释记忆功能如何存放、索引、查找和复用。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>记录一次对 Codex 记忆功能的实际拆解。它不是把信息写进模型参数，也不是一个必须依赖远程数据库的黑盒系统，而是用本地文件保存摘要、索引和历史会话路径，再在需要时按关键词检索和注入上下文。</p>
</blockquote>
<h2>记忆不是模型权重</h2>
<p>Codex 记忆，更接近一套本地 knowledge base：</p>
<pre><code class="language-text">/Users/&lt;user&gt;/.codex/memories/
├── MEMORY.md
├── memory_summary.md
├── raw_memories.md
├── rollout_summaries/
└── extensions/
</code></pre>
<p>这说明它的工作方式不是“模型永久记住了某句话”，而是：</p>
<ol>
<li>历史会话被保存在本地 JSONL 文件中。</li>
<li>有价值的会话会被压缩成 Markdown 摘要。</li>
<li>摘要再被登记到一个主索引里。</li>
<li>新任务开始时，Agent 先读摘要或索引，判断是否需要复用旧信息。</li>
<li>如果需要精确证据，再回到原始 rollout 文件里查。</li>
</ol>
<p>所以它更像“本地文件检索 + 摘要注入”，而不是长期记忆。</p>
<h2>文件结构：哪些文件负责什么</h2>
<p>实际结构可以拆成四类。</p>
<table>
<thead>
<tr>
<th>文件或目录</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>memory_summary.md</code></td>
<td>全局压缩摘要，适合在新会话里快速提供用户偏好、项目历史和常见规则</td>
</tr>
<tr>
<td><code>MEMORY.md</code></td>
<td>主索引，按任务组记录关键词、适用路径、摘要文件和原始会话路径</td>
</tr>
<tr>
<td><code>raw_memories.md</code></td>
<td>更原始的记忆材料，通常不作为第一入口</td>
</tr>
<tr>
<td><code>rollout_summaries/</code></td>
<td>单次或一组会话的 Markdown 摘要</td>
</tr>
<tr>
<td><code>extensions/ad_hoc/</code></td>
<td>用户显式要求追加记忆时，用于放置增量说明</td>
</tr>
</tbody>
</table>
<p>这里最重要的是 <code>MEMORY.md</code> 和 <code>rollout_summaries/</code>。</p>
<p><code>MEMORY.md</code> 负责回答“有没有相关记忆、应该去哪看”。它不是完整历史，而是索引。</p>
<p><code>rollout_summaries/</code> 负责回答“上次到底发生了什么、形成了什么偏好或结论”。它不是逐字聊天记录，而是压缩后的经验摘要。</p>
<h2>主索引：为什么先查 <a href="http://MEMORY.md">MEMORY.md</a></h2>
<p><code>MEMORY.md</code> 的组织方式通常是 task group。一个任务组里会包含：</p>
<pre><code class="language-text">scope: ...
applies_to: ...

## Task 1: ...

### rollout_summary_files

- rollout_summaries/xxx.md
  (cwd=..., rollout_path=..., updated_at=..., thread_id=..., success)

### keywords

- keyword-a, keyword-b, path-a, file-a

## User preferences

- when ...

## Reusable knowledge

- ...
</code></pre>
<p>这种格式的好处是检索成本低。</p>
<p>例如 codex 要处理我的博客仓库，就不用扫描所有历史会话。只需要用路径或关键词查：</p>
<pre><code class="language-bash">rg -n &quot;src/data/blog|Astro|博客&quot; ~/.codex/memories/MEMORY.md
</code></pre>
<p>如果命中 <code>/Users/&lt;user&gt;/blog</code> 对应的任务组，就能看到：</p>
<ul>
<li>之前哪些博客文章被修改过。</li>
<li>文章一般放在哪个目录。</li>
<li>校验命令是什么。</li>
<li>哪些 warning 是历史已有，不应该误判成本次修改。</li>
<li>哪个 <code>rollout_summaries/*.md</code> 记录了更多上下文。</li>
</ul>
<p>这比直接把全部历史塞进上下文更稳定，也更省 token。</p>
<h2>摘要文件：一次会话如何变成可复用经验</h2>
<p><code>rollout_summaries/</code> 里的文件是更具体的历史记录。一个典型摘要会包含：</p>
<pre><code class="language-text">thread_id: ...
updated_at: ...
rollout_path: /Users/&lt;user&gt;/.codex/sessions/.../rollout-xxx.jsonl
cwd: ...

# 本次会话的一句话总结

Rollout context: ...

## Task 1: ...

Outcome: success

Preference signals:
- ...

Key steps:
- ...

Failures and how to do differently:
- ...

Reusable knowledge:
- ...
</code></pre>
<p>这类文件的重点不是复盘所有聊天内容，而是提炼未来有用的信息。</p>
<p>这就是记忆真正有价值的地方：不是记住一句话，而是把一次纠正转成以后可执行的行为规则。</p>
<h2>原始会话：什么时候才需要查 JSONL</h2>
<p>摘要文件里通常会保留 <code>rollout_path</code>：</p>
<pre><code class="language-text">rollout_path: /Users/&lt;user&gt;/.codex/sessions/2026/05/20/rollout-xxx.jsonl
</code></pre>
<p>这些 JSONL 文件才是更接近原始会话的数据。里面可能包含：</p>
<ul>
<li>用户消息。</li>
<li>Agent 回复。</li>
<li>工具调用。</li>
<li>命令输出。</li>
<li>中间状态。</li>
<li>任务边界。</li>
</ul>
<p>但日常不会一上来就读它们。原因很直接：</p>
<ol>
<li>原始会话太长。</li>
<li>很多细节对当前任务没有价值。</li>
<li>直接读全量历史容易引入过期信息。</li>
<li>摘要已经足够覆盖大多数复用场景。</li>
</ol>
<p>只有在需要精确还原命令、错误文本、文件路径、测试结果时，才应该从摘要跳回 JSONL。</p>
<h2>一次实际检索流程</h2>
<p>比较合理的记忆检索流程可以写成下面这样：</p>
<pre><code class="language-text">用户请求
  ↓
判断是否需要记忆
  ↓
读取当前会话注入的 memory_summary.md
  ↓
用路径、项目名、文件名、关键词搜索 MEMORY.md
  ↓
打开 1-2 个最相关的 rollout_summaries/*.md
  ↓
必要时追溯 rollout_path 指向的 JSONL
  ↓
把低漂移结论用于当前任务
</code></pre>
<p>对应到命令，大概是：</p>
<pre><code class="language-bash"># 1. 先查主索引
rg -n &quot;关键词|项目路径|文件名&quot; ~/.codex/memories/MEMORY.md

# 2. 打开命中的摘要文件
sed -n '1,120p' ~/.codex/memories/rollout_summaries/&lt;summary&gt;.md

# 3. 如果需要原始证据，再查 rollout_path
rg -n &quot;错误文本|命令|文件名&quot; ~/.codex/sessions/.../rollout-xxx.jsonl
</code></pre>
<p>这个流程里有一个关键判断：不是所有任务都需要记忆。</p>
<p>如果只是问当前时间、翻译一句话、解释一个独立概念，就没必要读历史。<br>
如果任务涉及某个长期项目、全局配置、简历写法、博客风格、用户偏好，就应该查。</p>
<h2>为什么不用全量自动记忆</h2>
<p>全量自动记忆看起来省事，但实际风险很高。</p>
<p>第一，历史信息会过期。</p>
<p>比如某个仓库以前用 <code>pnpm astro check</code> 校验，不代表现在依赖、schema、构建命令都没变。记忆可以提示方向，但当前事实仍然要用真实文件验证。</p>
<p>第二，历史信息有作用域。</p>
<p>某个项目里的偏好，不一定适用于另一个项目。比如“只写计划不实现”可能只对应某次用户明确说了“暂不执行”，不能扩展成所有任务都不执行。</p>
<p>第三，历史信息可能是纠错后的结论。</p>
<p>如果只记住中间操作，而不记住用户纠正，就会复用错误。好的摘要应该保留“哪里做错了、以后怎么改”，而不是只保留最终结果。</p>
<p>所以更好的策略是：</p>
<table>
<thead>
<tr>
<th>信息类型</th>
<th>是否适合复用</th>
<th>是否需要重新验证</th>
</tr>
</thead>
<tbody>
<tr>
<td>用户长期偏好</td>
<td>适合</td>
<td>低频验证</td>
</tr>
<tr>
<td>项目目录和历史决策</td>
<td>适合</td>
<td>需要按当前仓库确认</td>
</tr>
<tr>
<td>依赖版本和命令输出</td>
<td>谨慎复用</td>
<td>应该重新跑</td>
</tr>
<tr>
<td>线上状态、价格、规则</td>
<td>不应只靠记忆</td>
<td>必须重新查</td>
</tr>
<tr>
<td>用户临时指令</td>
<td>只在对应上下文内复用</td>
<td>不应泛化</td>
</tr>
</tbody>
</table>
<h2>如何追加记忆</h2>
<p>从当前规则看，记忆不能由 Agent 随便写入。只有用户明确要求更新记忆时，才应该新增说明。</p>
<p>增量文件放在：</p>
<pre><code class="language-text">~/.codex/memories/extensions/ad_hoc/notes/
</code></pre>
<p>文件名一般可以是：</p>
<pre><code class="language-text">&lt;timestamp&gt;-&lt;short-slug&gt;.md
</code></pre>
<p>这里的设计很保守：不要直接改主索引，也不要让一次会话随意污染长期记忆。先把新增或修正内容作为 ad hoc note 放进去，再由后续的记忆整理流程吸收。</p>
<p>这能避免一个常见问题：Agent 把一次临时偏好误写成长期规则。</p>
<h2>局限和边界</h2>
<p>这套机制有几个明确边界。</p>
<p>第一，它依赖本地文件存在。换机器、清理目录、权限受限，都会影响可用性。</p>
<p>第二，它依赖摘要质量。如果摘要漏掉了关键纠正，后续检索就可能复用不完整结论。</p>
<p>第三，它不是实时事实源。记忆里说某个命令以前能跑，不代表今天还能跑。</p>
<p>第四，它不是隐私隔离系统。既然记忆保存在本地文件里，就应该避免把敏感 token、真实 IP、私钥路径、个人隐私写进可复用摘要。</p>
<p>第五，它不是自动决策权。记忆只能影响默认判断，不能覆盖当前用户的新指令。</p>
<h2>总结</h2>
<p>Codex 的记忆功能可以理解为一套本地化、可读、可检索的上下文系统：</p>
<ul>
<li><code>memory_summary.md</code> 提供快速全局摘要。</li>
<li><code>MEMORY.md</code> 提供任务级索引。</li>
<li><code>rollout_summaries/</code> 保存可复用经验。</li>
<li><code>rollout_path</code> 指向原始 JSONL 会话。</li>
<li>检索时先摘要、再索引、再原始证据。</li>
</ul>
<p>它的价值不在于“记住更多”，而在于把历史会话里的有效经验变成下次可执行的约束：哪些路径要优先检查，哪些命令以前验证过，哪些用户偏好不能违背，哪些旧结论必须重新确认。</p>
<p>对 Agent 来说，这比把所有历史塞进上下文更接近一个工程化实现：可索引、可追溯、可修正，也能在必要时回到原始证据。</p>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[同步 Codex auth.json：本地与多台服务器的登录状态管理]]></title>
            <link>https://zhoukai.space/posts/codex-auth-sync</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/codex-auth-sync</guid>
            <pubDate>Fri, 01 May 2026 14:30:00 GMT</pubDate>
            <description><![CDATA[介绍一个 Codex auth.json 同步方案，用于在本地和多台服务器之间复用最新登录状态。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>我经常在本地和多台远程服务器之间切换使用 Codex。每台机器单独登录会带来重复认证和状态不一致的问题，所以我在本地 <code>~/.zshrc</code> 里写了一个小函数：比较三处 <code>auth.json</code> 的刷新时间，选择最新的一份，再同步到其余机器。</p>
</blockquote>
<h2>背景：为什么需要同步 Codex 登录状态</h2>
<p>Codex 的登录状态保存在本地用户目录下的 <code>auth.json</code>。在单机使用时这不是问题；但一旦进入多机器开发环境，就会出现几个重复场景：</p>
<ul>
<li>本地已经登录，但远程服务器还没有登录。</li>
<li>在服务器上刷新了登录状态，本地反而不是最新。</li>
<li>多台服务器都要使用 Codex，每台机器都手动登录一次很麻烦。</li>
<li>某台机器的认证状态过期后，需要判断应该从哪里恢复。</li>
</ul>
<p>我的同步目标可以抽象成：</p>
<table>
<thead>
<tr>
<th>位置</th>
<th>路径</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>本地 Mac</td>
<td><code>$HOME/.codex/auth.json</code></td>
<td>日常主要入口</td>
</tr>
<tr>
<td><code>remote-a</code></td>
<td><code>$HOME/.codex/auth.json</code></td>
<td>远程服务器</td>
</tr>
<tr>
<td><code>remote-b</code></td>
<td><code>$HOME/.codex/auth.json</code></td>
<td>另一台远程环境</td>
</tr>
</tbody>
</table>
<p>这个问题的核心不是文件复制，而是：<strong>哪一份文件才是最新、最应该作为同步源</strong>。</p>
<p>如果只是无脑从本地覆盖远程，可能会把服务器上刚刷新的登录状态覆盖掉。如果只是从远程拉回本地，也可能反过来覆盖本地新状态。所以需要先比较时间，再决定同步方向。</p>
<h2>方案思路：用 <code>last_refresh</code> 决定同步源</h2>
<p>Codex 的 <code>auth.json</code> 里包含 <code>last_refresh</code> 字段。这个字段可以作为登录状态新旧的判断依据。</p>
<p>同步逻辑可以拆成四步：</p>
<ol>
<li>读取本地 <code>auth.json</code> 的 <code>last_refresh</code>。</li>
<li>通过 SSH 读取每台远程服务器上的 <code>last_refresh</code>。</li>
<li>把这些时间转成 Unix timestamp，选出最大值。</li>
<li>把最新的那份 <code>auth.json</code> 同步到其余位置。</li>
</ol>
<p>这样做的优点是方向自动化。无论最新登录状态出现在本地、<code>remote-a</code> 还是 <code>remote-b</code>，函数都能把它扩散到其他机器。</p>
<h2>本地 zsh 函数</h2>
<p>下面是我放在本地 <code>~/.zshrc</code> 里的函数。这里使用脱敏后的主机名和路径写法；实际使用时只需要把 <code>remote-a</code>、<code>remote-b</code> 替换成自己 <code>~/.ssh/config</code> 里的 SSH alias。</p>
<pre><code class="language-zsh"># 同步 codex auth.json 文件
auth() {
  local L=&quot;$HOME/.codex/auth.json&quot;
  local H1=&quot;remote-a&quot;
  local H2=&quot;remote-b&quot;

  lts(){ [[ -f &quot;$1&quot; ]] || return; jq -r '.last_refresh // empty' &quot;$1&quot; 2&gt;/dev/null | sed -E 's/\.[0-9]+Z$/Z/' | xargs -I{} date -j -u -f &quot;%Y-%m-%dT%H:%M:%SZ&quot; &quot;{}&quot; &quot;+%s&quot; 2&gt;/dev/null; }
  rts(){ ssh &quot;$1&quot; 'f=&quot;$HOME/.codex/auth.json&quot;; [[ -f &quot;$f&quot; ]] || exit 0; v=$(jq -r &quot;.last_refresh // empty&quot; &quot;$f&quot; 2&gt;/dev/null | sed -E &quot;s/\.[0-9]+Z$/Z/&quot;); [[ -n &quot;$v&quot; ]] &amp;&amp; date -u -d &quot;$v&quot; +%s 2&gt;/dev/null'; }
  pull_remote(){ mkdir -p &quot;$HOME/.codex&quot;; ssh &quot;$1&quot; 'cat &quot;$HOME/.codex/auth.json&quot;' &gt; &quot;$L&quot;; }
  push_remote(){ ssh &quot;$1&quot; 'mkdir -p &quot;$HOME/.codex&quot;; cat &gt; &quot;$HOME/.codex/auth.json&quot;' &lt; &quot;$L&quot;; }
  relay_remote(){ ssh &quot;$1&quot; 'cat &quot;$HOME/.codex/auth.json&quot;' | ssh &quot;$2&quot; 'mkdir -p &quot;$HOME/.codex&quot;; cat &gt; &quot;$HOME/.codex/auth.json&quot;'; }

  local tl=&quot;$(lts &quot;$L&quot;)&quot; t1=&quot;$(rts &quot;$H1&quot;)&quot; t2=&quot;$(rts &quot;$H2&quot;)&quot;
  [[ -z &quot;$tl$t1$t2&quot; ]] &amp;&amp; { echo &quot;[ERROR] no valid auth.json&quot;; return 1; }

  local src=&quot;local&quot; max=&quot;${tl:-0}&quot;
  [[ &quot;${t1:-0}&quot; -gt &quot;$max&quot; ]] &amp;&amp; src=&quot;$H1&quot; max=&quot;$t1&quot;
  [[ &quot;${t2:-0}&quot; -gt &quot;$max&quot; ]] &amp;&amp; src=&quot;$H2&quot; max=&quot;$t2&quot;

  [[ &quot;$src&quot; != &quot;local&quot; &amp;&amp; &quot;$tl&quot; != &quot;$max&quot; ]] &amp;&amp; pull_remote &quot;$src&quot;
  [[ &quot;$src&quot; != &quot;$H1&quot;   &amp;&amp; &quot;$t1&quot; != &quot;$max&quot; ]] &amp;&amp; { [[ &quot;$src&quot; == &quot;local&quot; ]] &amp;&amp; push_remote &quot;$H1&quot; || relay_remote &quot;$src&quot; &quot;$H1&quot;; }
  [[ &quot;$src&quot; != &quot;$H2&quot;   &amp;&amp; &quot;$t2&quot; != &quot;$max&quot; ]] &amp;&amp; { [[ &quot;$src&quot; == &quot;local&quot; ]] &amp;&amp; push_remote &quot;$H2&quot; || relay_remote &quot;$src&quot; &quot;$H2&quot;; }

  echo &quot;[INFO] synced from $src&quot;
}
</code></pre>
<p>使用方式很简单：</p>
<pre><code class="language-bash">source ~/.zshrc
auth
</code></pre>
<p>如果改名为 <code>codex_auth</code>，对应调用就是：</p>
<pre><code class="language-bash">codex_auth
</code></pre>
<h2>关键实现细节</h2>
<h3>本地时间解析：<code>lts</code></h3>
<p><code>lts</code> 负责读取本地文件：</p>
<pre><code class="language-zsh">lts(){
  [[ -f &quot;$1&quot; ]] || return
  jq -r '.last_refresh // empty' &quot;$1&quot; 2&gt;/dev/null |
    sed -E 's/\.[0-9]+Z$/Z/' |
    xargs -I{} date -j -u -f &quot;%Y-%m-%dT%H:%M:%SZ&quot; &quot;{}&quot; &quot;+%s&quot; 2&gt;/dev/null
}
</code></pre>
<p>这里有两个处理点：</p>
<ul>
<li><code>jq -r '.last_refresh // empty'</code>：只读取刷新时间，字段不存在时输出空值。</li>
<li><code>sed -E 's/\.[0-9]+Z$/Z/'</code>：去掉毫秒部分，方便 <code>date</code> 解析。</li>
</ul>
<p>本地是 macOS，所以使用的是 BSD <code>date</code>：</p>
<pre><code class="language-bash">date -j -u -f &quot;%Y-%m-%dT%H:%M:%SZ&quot; &quot;2026-05-01T12:00:00Z&quot; &quot;+%s&quot;
</code></pre>
<p>其中 <code>-j</code> 表示只解析时间，不修改系统时间。</p>
<h3>远程时间解析：<code>rts</code></h3>
<p><code>rts</code> 通过 SSH 在远程服务器上执行解析：</p>
<pre><code class="language-zsh">rts(){
  ssh &quot;$1&quot; 'f=&quot;$HOME/.codex/auth.json&quot;; [[ -f &quot;$f&quot; ]] || exit 0; v=$(jq -r &quot;.last_refresh // empty&quot; &quot;$f&quot; 2&gt;/dev/null | sed -E &quot;s/\.[0-9]+Z$/Z/&quot;); [[ -n &quot;$v&quot; ]] &amp;&amp; date -u -d &quot;$v&quot; +%s 2&gt;/dev/null'
}
</code></pre>
<p>远程服务器通常是 Linux，所以这里使用 GNU <code>date</code>：</p>
<pre><code class="language-bash">date -u -d &quot;2026-05-01T12:00:00Z&quot; +%s
</code></pre>
<p>这也是为什么本地和远程分别写成 <code>lts</code> 和 <code>rts</code>。macOS 与 Linux 的 <code>date</code> 参数不兼容，如果强行复用同一条命令，很容易在某一端失效。</p>
<h3>选择最新源</h3>
<p>这一段负责比较三处时间：</p>
<pre><code class="language-zsh">local src=&quot;local&quot; max=&quot;${tl:-0}&quot;
[[ &quot;${t1:-0}&quot; -gt &quot;$max&quot; ]] &amp;&amp; src=&quot;$H1&quot; max=&quot;$t1&quot;
[[ &quot;${t2:-0}&quot; -gt &quot;$max&quot; ]] &amp;&amp; src=&quot;$H2&quot; max=&quot;$t2&quot;
</code></pre>
<p>默认先认为本地最新，然后依次比较 <code>remote-a</code> 和 <code>remote-b</code>。如果远程时间更大，就把同步源切换到对应机器。</p>
<p>这里使用 <code>${tl:-0}</code>、<code>${t1:-0}</code>、<code>${t2:-0}</code> 是为了处理文件不存在或字段无效的情况。无法解析的时间会被当作 <code>0</code>，不会成为同步源。</p>
<h3>同步到其他机器</h3>
<p>如果最新源在本地，就把本地文件通过 SSH 标准输入推送到远程：</p>
<pre><code class="language-zsh">ssh &quot;$H1&quot; 'mkdir -p &quot;$HOME/.codex&quot;; cat &gt; &quot;$HOME/.codex/auth.json&quot;' &lt; &quot;$L&quot;
ssh &quot;$H2&quot; 'mkdir -p &quot;$HOME/.codex&quot;; cat &gt; &quot;$HOME/.codex/auth.json&quot;' &lt; &quot;$L&quot;
</code></pre>
<p>如果最新源在远程，就分两种情况：</p>
<ul>
<li>远程到本地：使用 <code>ssh &quot;$src&quot; 'cat &quot;$HOME/.codex/auth.json&quot;' &gt; &quot;$L&quot;</code>。</li>
<li>远程到另一台远程：使用 <code>ssh &quot;$src&quot; 'cat &quot;$HOME/.codex/auth.json&quot;' | ssh &quot;$target&quot; 'cat &gt; &quot;$HOME/.codex/auth.json&quot;'</code>。</li>
</ul>
<p>第二种写法避免了要求两台服务器之间能够互相 SSH。只要本地能分别连上两台机器，就可以通过本地这条管道完成中转。</p>
<h2>依赖与前置条件</h2>
<p>这个函数依赖四类能力：</p>
<table>
<thead>
<tr>
<th>依赖</th>
<th>使用位置</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>zsh</code></td>
<td>本地 shell</td>
<td>函数写在 <code>~/.zshrc</code> 中</td>
</tr>
<tr>
<td><code>jq</code></td>
<td>本地和远程</td>
<td>解析 <code>auth.json</code></td>
</tr>
<tr>
<td><code>ssh</code></td>
<td>本地到远程</td>
<td>读取远程时间、远程间中转</td>
</tr>
<tr>
<td>SSH 标准输入输出</td>
<td>本地与远程文件复制</td>
<td>同步 <code>auth.json</code></td>
</tr>
</tbody>
</table>
<p>本地 macOS 可以用 Homebrew 安装 <code>jq</code>：</p>
<pre><code class="language-bash">brew install jq
</code></pre>
<p>远程 Ubuntu / Debian 可以用 apt 安装：</p>
<pre><code class="language-bash">sudo apt update
sudo apt install -y jq
</code></pre>
<blockquote>
<p>一般都有的 🥳</p>
</blockquote>
<p>还需要保证 SSH alias 可用。例如本地 <code>~/.ssh/config</code> 中需要有类似配置：</p>
<pre><code class="language-text">Host remote-a
    HostName &lt;remote-a-host&gt;
    User &lt;remote-user&gt;
    Port &lt;ssh-port&gt;

Host remote-b
    HostName &lt;remote-b-host&gt;
    User &lt;remote-user&gt;
    Port &lt;ssh-port&gt;
</code></pre>
<p>这里不建议把真实 IP、端口和私钥路径写进公开博客。文章里保留 alias 和目录结构即可，具体连接信息应留在本机 SSH 配置中。</p>
<h2>验证方式</h2>
<h3>检查三处文件是否存在</h3>
<p>先在本地检查：</p>
<pre><code class="language-bash">test -f ~/.codex/auth.json &amp;&amp; echo &quot;local auth exists&quot;
</code></pre>
<p>再检查远程：</p>
<pre><code class="language-bash">ssh remote-a 'test -f ~/.codex/auth.json &amp;&amp; echo &quot;remote-a auth exists&quot;'
ssh remote-b 'test -f ~/.codex/auth.json &amp;&amp; echo &quot;remote-b auth exists&quot;'
</code></pre>
<h3>检查 <code>last_refresh</code></h3>
<p>本地：</p>
<pre><code class="language-bash">jq -r '.last_refresh // empty' ~/.codex/auth.json
</code></pre>
<p>远程：</p>
<pre><code class="language-bash">ssh remote-a &quot;jq -r '.last_refresh // empty' ~/.codex/auth.json&quot;
ssh remote-b &quot;jq -r '.last_refresh // empty' ~/.codex/auth.json&quot;
</code></pre>
<h3>执行同步</h3>
<pre><code class="language-bash">auth
</code></pre>
<p>正常情况下会输出：</p>
<pre><code class="language-text">[INFO] synced from local
</code></pre>
<p>或者：</p>
<pre><code class="language-text">[INFO] synced from remote-a
</code></pre>
<p>这里的 <code>src</code> 只表示本轮选择的最新源。它不是固定方向，每次都会根据三处 <code>last_refresh</code> 重新判断。</p>
<h3>验证同步结果</h3>
<p>同步完成后，再次查看三处 <code>last_refresh</code>：</p>
<pre><code class="language-bash">jq -r '.last_refresh // empty' ~/.codex/auth.json
ssh remote-a &quot;jq -r '.last_refresh // empty' ~/.codex/auth.json&quot;
ssh remote-b &quot;jq -r '.last_refresh // empty' ~/.codex/auth.json&quot;
</code></pre>
<p>三处输出应该一致，或者至少都指向同一轮最新刷新时间。</p>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[Codex Full Course 2026：把 AI 编程工具当成 Agent 工作台使用]]></title>
            <link>https://zhoukai.space/posts/codex-agent-workbench</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/codex-agent-workbench</guid>
            <pubDate>Wed, 22 Apr 2026 12:40:00 GMT</pubDate>
            <description><![CDATA[基于 Codex Full Course 2026 视频笔记，整理 Codex 桌面应用的项目组织、多聊天协作、技能插件、自动化与验证闭环。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<p><strong>原视频链接</strong>：<a href="https://www.youtube.com/watch?v=KXIdYEdOPys">Codex Full Course 2026</a><br>
<strong>YouTube URL</strong>：<a href="https://www.youtube.com/watch?v=KXIdYEdOPys">https://www.youtube.com/watch?v=KXIdYEdOPys</a></p>
<blockquote>
<p>这篇文章基于 Riley Brown 的 YouTube 视频 <a href="https://www.youtube.com/watch?v=KXIdYEdOPys">Codex Full Course 2026</a> 整理。重点不是罗列功能，而是抽出一套更实用的工作方式：把 Codex 当成一个围绕项目目录、文件产物、技能插件和自动化运转的 Agent 工作台。</p>
</blockquote>
<h2>核心结论：Codex 不只是聊天框</h2>
<p>视频里最值得记住的一点是：Codex 的价值不只在“让模型回答问题”，而在于它把聊天、文件、项目、插件、技能、预览和自动化放到同一个工作流里。</p>
<p>传统聊天工具的中心是对话。你问一句，它答一句。结果主要停留在聊天历史里。</p>
<p>Codex 的中心更接近一个本地项目目录。每个聊天都可以绑定一个文件夹，Agent 可以在里面创建文档、表格、网页、App、PPT、视频工程，甚至继续读取和修改这些真实文件。</p>
<p>这会改变使用方式：</p>
<ul>
<li>不是让 Agent “给我一个答案”，而是让它产出一个可以打开、检查、迭代的文件。</li>
<li>不是把所有需求塞进一个聊天，而是让多个聊天分别负责不同产物。</li>
<li>不是一次性提示词结束任务，而是用计划文件、预览、截图和验证结果持续纠偏。</li>
</ul>
<p>所以，Codex 更像是一个 Agent-native 的工作台，而不是一个更强的聊天窗口。</p>
<h2>项目目录是工作流的中心</h2>
<p>视频建议先创建一个总目录，再为每个主题或产品创建独立项目。例如：</p>
<pre><code class="language-text">Codex Projects/
├── Codex Desktop Research/
├── My New Business/
└── Chorus/
</code></pre>
<p>每个项目目录下面可以有多个聊天。聊天本身不会作为文件存进目录，但 Agent 生成的文件会落在这个项目里。</p>
<p>这种组织方式很重要，因为它解决了三个问题。</p>
<p>第一，产物不会散落。Excel、Word、PPT、React 页面、Swift 工程、Remotion 视频，都可以在项目目录里找到。</p>
<p>第二，同一个项目里的聊天可以围绕同一批文件继续工作。比如先生成一个 <code>codex desktop features.xlsx</code>，再开一个新聊天，用 <code>@文件名</code> 引用它，让 Agent 检查是否遗漏了功能。</p>
<p>第三，项目目录天然适合长期迭代。即使某个聊天结束了，文件仍然在本地；即使项目从侧边栏移除，本地目录也没有消失。</p>
<p>我理解这里的关键不是“文件夹管理癖”，而是给 Agent 一个稳定边界。Agent 知道自己该在哪里读写，人也知道去哪里验收结果。</p>
<h2>多聊天协作：一个聊天只负责一个产物</h2>
<p>视频后半段最核心的方法论是多任务并行。</p>
<p>一个复杂产品不应该由一个聊天从头做到尾。更合理的方式是拆成多个相对独立的聊天：</p>
<pre><code class="language-text">Plan
Mobile Design
iOS App
Database / Supabase
Web Landing Page
Launch Video
Investor Deck
X Automation
</code></pre>
<p>每个聊天只负责一个明确产物。这样做有几个好处：</p>
<ul>
<li>上下文更干净，不容易互相污染。</li>
<li>失败范围更小，一个视频任务失败不会影响数据库任务。</li>
<li>可以并行运行多个 Agent。</li>
<li>每个任务的验收标准更清楚。</li>
</ul>
<p>视频中的 <code>Chorus</code> 示例就是这种模式：同一个产品，同时推进 iOS App、网页、投资人 Deck、发布视频和社交媒体自动化。人不再盯着一个 Agent 等结果，而是串行下发任务、并行回收产物。</p>
<p>这里有一个前提：每个任务提示词必须像一个完整任务包。它至少要包含目标、上下文、约束和验证方式。</p>
<p>例如，启动一个 iOS 项目时，不要一开始就要求完整 App，而是先让它创建最小可运行版本：</p>
<pre><code class="language-text">请创建一个名为 Chorus 的 Swift mobile app。
现在只需要在屏幕中央显示 &quot;Hello, this is Chorus&quot;。
不要做其他功能。完成后打开 Xcode 项目。
</code></pre>
<p>这类提示词的特点是范围小、验收明确、失败容易定位。</p>
<h2>文件预览与自然语言迭代</h2>
<p>Codex 的一个重要优势是文件预览。</p>
<p>Agent 生成 Excel、Word、PPT、网页或视频后，可以直接在右侧打开预览。然后继续用自然语言修改同一个文件。</p>
<p>比如表格工作流可以是：</p>
<ol>
<li>让 Codex 搜索某个主题。</li>
<li>要求它把结果整理成 <code>.xlsx</code>。</li>
<li>在预览里打开表格。</li>
<li>发现某一列没用，再让它删除。</li>
<li>继续要求补充来源、排序或改格式。</li>
</ol>
<p>这和普通聊天最大的区别是：每一轮修改都落在真实文件上，而不是只停留在回答文本里。</p>
<p>同样的方法也适用于设计和视频。视频里经常用截图反馈问题，例如按钮重叠、动画方向错误、视频帧位置不对。对于 Remotion 这类视频工作流，反馈甚至可以精确到秒数、frame 和坐标：</p>
<pre><code class="language-text">在 3 秒位置，请镜头放大到 &quot;Learning about skills&quot; 这个组件，
然后转成全屏文档视图，展示 10 个热门技能。
</code></pre>
<pre><code class="language-text">鼠标点击位置错了。目标 toggle 大约在 x=1000, y=610，
而现在鼠标落在 x=1040, y=540。请修正。
</code></pre>
<p>这说明 Agent 交互不只靠文字。截图、坐标、帧数、草图和预览结果，都是上下文。</p>
<h2>Skill、Plugin 和 MCP：把能力封装起来</h2>
<p>视频里把 Skill、Plugin 和 MCP 放在一起讲。它们名字不同，但实践上可以统一理解为：让 Agent 获得新能力的方式。</p>
<p>一个实用区分是：</p>
<table>
<thead>
<tr>
<th>概念</th>
<th>更接近什么</th>
<th>适合解决什么问题</th>
</tr>
</thead>
<tbody>
<tr>
<td>Skill</td>
<td>可复用工作流</td>
<td>固定格式写作、报告生成、API 工作流</td>
</tr>
<tr>
<td>Plugin</td>
<td>可安装工具能力</td>
<td>Gmail、Calendar、Figma、Vercel 等外部服务</td>
</tr>
<tr>
<td>MCP</td>
<td>外部系统连接协议</td>
<td>Supabase、设计工具、数据库、自定义服务</td>
</tr>
</tbody>
</table>
<p>如果不清楚某个插件能做什么，视频里的方法很直接：直接问 Codex。</p>
<pre><code class="language-text">@Figma 请告诉我你可以用这个插件做哪些事情。
列出所有能力，并说明适合的使用场景。
</code></pre>
<p>这个方法比凭感觉猜接口更可靠。因为 Agent 可以先枚举工具能力，再把任务映射到合适工具。</p>
<p>视频中最有代表性的 Skill 例子是 YouTube Researcher。作者经常需要分析 YouTube 频道，于是把流程封装成技能：</p>
<ol>
<li>输入频道。</li>
<li>拉取最新视频。</li>
<li>获取 transcript、缩略图和表现数据。</li>
<li>总结每个视频内容。</li>
<li>分析哪些 hook 表现好。</li>
<li>生成 Word 报告。</li>
</ol>
<p>抽象成通用模式就是：</p>
<pre><code class="language-text">重复任务 -&gt; 搜索可用 API -&gt; 选择服务 -&gt; 获取 API key -&gt; 封装 Skill -&gt; 新 session 测试 -&gt; 接入自动化
</code></pre>
<p>这个思路很适合自己的日常工作。只要某个任务每周、每月重复出现，就值得考虑封装。</p>
<h2>自动化适合做“草稿”，不要直接放飞</h2>
<p>视频里有两个很典型的自动化场景。</p>
<p>第一个是 Calendar + Gmail：每周五下午汇总本周日历事件，并通过邮件发给自己。</p>
<p>第二个是 Typefully：每天研究并生成 3 条 X / Twitter 草稿。</p>
<p>我觉得后者更能体现安全边界。社交媒体内容不适合一开始就自动发布，更适合先生成 draft。这样 Agent 负责收集、整理和初稿，人保留最后审核权。</p>
<p>比较稳妥的自动化原则是：</p>
<ul>
<li>初期只生成草稿，不直接发布。</li>
<li>第一次必须用明显测试内容验证链路。</li>
<li>涉及 API key 时不要录屏展示。</li>
<li>密钥泄露后立即 rotate。</li>
<li>自动化运行后要检查真实后台，而不是只看 Codex 说完成。</li>
</ul>
<p>自动化不是把人从流程中完全移除，而是把重复的准备工作交出去，把判断权留在人手里。</p>
<h2>先搭最小闭环，再做效果</h2>
<p>这期视频里所有复杂项目都有一个共同模式：先做最小闭环。</p>
<p>比如：</p>
<ul>
<li>App 先做 Hello World。</li>
<li>视频先做 test video。</li>
<li>Web 先能提交表单。</li>
<li>Deck 先生成初版。</li>
<li>Skill 先测试 API。</li>
<li>数据库先确认表和数据真实存在。</li>
</ul>
<p>这个顺序比“直接做最终效果”稳定得多。</p>
<p>以 Landing Page 为例，作者没有让 Codex 从零实现完整表单系统，而是使用 Tally 收集 waitlist：</p>
<ol>
<li>在 Tally 创建表单。</li>
<li>复制 embed code。</li>
<li>让 Codex 创建 React 页面并嵌入表单。</li>
<li>本地打开页面。</li>
<li>输入测试邮箱提交。</li>
<li>到 Tally 后台确认 submission 出现。</li>
</ol>
<p>这里最关键的是最后一步。用户路径必须真实跑通，不能只看页面长得像。</p>
<p>同样，Supabase 数据库也不能只看 App 里出现了内容，还要去 Table Editor 检查表、字段和写入数据是否真实存在。</p>
<p>Agent 可以生成很多东西，但“是否真的工作”仍然要靠验证闭环。</p>
<h2>设计质量需要人类把关</h2>
<p>视频里也有一个现实判断：Codex 很适合项目编排、文件操作、多任务推进和工程实现，但默认设计质量不一定总是最好。</p>
<p>常见问题包括：</p>
<ul>
<li>组件重叠。</li>
<li>背景渐变过重。</li>
<li>PPT 文字太多。</li>
<li>Landing Page 视觉不够克制。</li>
<li>视频转场节奏不自然。</li>
</ul>
<p>解决方式不是期待一次提示词变完美，而是给更具体的反馈。比如提供截图、圈出问题、说明坐标、要求减少文字、保持风格一致。</p>
<p>视频中作者还会在 Codex 项目目录里运行 Claude Code，用它处理设计-heavy 的修改：</p>
<pre><code class="language-bash">claude --dangerously-skip-permissions
</code></pre>
<p>这个命令本身有风险，只适合可信工作区。它体现的思路更重要：不必把所有事情都押在一个工具上。Codex 可以负责工作台和工程编排，其他模型或工具可以负责视觉 polish，最后再由人做验收。</p>
<h2>一套可复用的 Codex 工作流</h2>
<p>把整期视频压缩后，我认为最值得复用的是下面这套流程：</p>
<pre><code class="language-text">重复任务或产品想法
-&gt; 创建项目目录
-&gt; 写计划 Markdown
-&gt; 拆成多个聊天
-&gt; 每个聊天负责一个产物
-&gt; 先做最小可运行版本
-&gt; 打开真实文件或服务后台验证
-&gt; 用截图/预览/日志继续反馈
-&gt; 高频流程封装成 Skill
-&gt; 稳定流程再做自动化
</code></pre>
<p>对应到日常使用，可以从一个很小的任务开始，而不是直接做完整产品。</p>
<p>例如：</p>
<ul>
<li>每周论文阅读总结。</li>
<li>每月 YouTube 或博客内容复盘。</li>
<li>自动整理某个项目的 issue 和 PR。</li>
<li>从会议记录生成行动项。</li>
<li>把实验结果同步成报告。</li>
</ul>
<p>先让 Agent 产出一个真实文件，再围绕文件迭代；先让自动化生成草稿，再由人审核。这比直接要求它“全自动完成一切”更稳。</p>
<h2>总结</h2>
<p>这期视频真正有价值的地方，不是展示 Codex 有多少功能，而是展示了一种新的工作组织方式。</p>
<p>在这种方式里，项目目录是中心，聊天是任务单元，文件是可验证结果，插件和技能是能力扩展，自动化是长期杠杆。</p>
<p>人类的角色也随之变化：不再是逐行执行者，而是任务拆解者、上下文提供者、约束制定者和最终验收者。</p>
<p>Codex 能提高效率，但前提是你给它清晰边界、可检查产物和真实验证。没有这些，Agent 只是更快地产生不确定输出；有了这些，它才更像一个能长期协作的工程工作台。</p>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[在 Slurm 集群上用 gvitop 查看计算节点显存占用]]></title>
            <link>https://zhoukai.space/posts/slurm-gvitop-nvitop</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/slurm-gvitop-nvitop</guid>
            <pubDate>Tue, 21 Apr 2026 14:46:00 GMT</pubDate>
            <description><![CDATA[记录一个在 Slurm 集群中快速进入正在运行的作业并用 nvitop 查看计算节点 GPU 显存占用的小函数。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>在 Slurm 集群上跑训练任务时，登录节点通常看不到计算节点的 GPU 状态。本文记录一个小的 shell 函数：自动找到当前正在运行的 job，通过 <code>srun --jobid --overlap --pty</code> 附着到对应计算节点，然后打开 <code>nvitop</code> 查看显存占用。</p>
</blockquote>
<h2>问题背景</h2>
<p>在 Slurm 集群上，用户通常先登录到 login node，再通过 <code>sbatch</code> 或 <code>srun</code> 把任务提交到计算节点。真正占用 GPU 的进程运行在 compute node 上，而不是 login node 上。</p>
<p>所以直接在登录节点执行：</p>
<pre><code class="language-bash">nvitop
</code></pre>
<p>往往看不到正在训练的进程。更合理的方式是：先找到自己的 Slurm job，再进入这个 job 所在的计算节点查看 GPU。</p>
<h2>核心函数</h2>
<p>可以把下面这段函数写入 <code>~/.bashrc</code> 或 <code>~/.zshrc</code>：</p>
<pre><code class="language-bash">gvitop () {
  jobid=&quot;$1&quot;

  # 自动找最近 job
  if [ -z &quot;$jobid&quot; ]; then
    jobid=$(squeue -u &quot;$USER&quot; -t R -h -o %A | head -n 1)
    [ -z &quot;$jobid&quot; ] &amp;&amp; echo &quot;No running job&quot; &amp;&amp; return 1
    echo &quot;Using latest job: $jobid&quot;
  fi

  # 尝试直接运行
  srun --jobid=&quot;$jobid&quot; --overlap --pty nvitop 2&gt;/dev/null

  # fallback
  if [ $? -ne 0 ]; then
    echo &quot;Fallback to bash + nvitop&quot;
    srun --jobid=&quot;$jobid&quot; --overlap --pty bash -c &quot;nvitop&quot;
  fi
}
</code></pre>
<p>重新加载配置：</p>
<pre><code class="language-bash">source ~/.bashrc
# 或者
source ~/.zshrc
</code></pre>
<h2>使用方式</h2>
<p>如果当前只有一个正在运行的任务，直接执行：</p>
<pre><code class="language-bash">gvitop
</code></pre>
<p>如果有多个任务，建议显式指定 job id：</p>
<pre><code class="language-bash">gvitop 123456
</code></pre>
<p>可以先用下面的命令查看自己的运行中任务：</p>
<pre><code class="language-bash">squeue -u &quot;$USER&quot; -t R
</code></pre>
<h2>为什么需要 --overlap</h2>
<p>核心命令是：</p>
<pre><code class="language-bash">srun --jobid=&quot;$jobid&quot; --overlap --pty nvitop
</code></pre>
<p><code>--jobid</code> 表示复用已有 job allocation，<code>--pty</code> 表示启动一个交互式终端，<code>--overlap</code> 则允许这个新的 step 和已有任务共享 allocation 中的资源。</p>
<p>这里运行的是 <code>nvitop</code>，目标是观察状态，而不是再启动一个训练任务。因此使用 <code>--overlap</code> 更符合这个场景。</p>
<h2>fallback 的意义</h2>
<p>有些集群环境中，直接执行：</p>
<pre><code class="language-bash">srun --jobid=&quot;$jobid&quot; --overlap --pty nvitop
</code></pre>
<p>可能会因为 <code>PATH</code>、shell 初始化或集群配置问题失败。所以函数里加了 fallback：</p>
<pre><code class="language-bash">srun --jobid=&quot;$jobid&quot; --overlap --pty bash -c &quot;nvitop&quot;
</code></pre>
<p>先启动 <code>bash</code>，再由 <code>bash</code> 执行 <code>nvitop</code>，通常能复用更多用户侧的 shell 环境。</p>
<h2>常见问题</h2>
<p>如果输出：</p>
<pre><code class="language-text">No running job
</code></pre>
<p>说明当前用户没有处于 <code>R</code> 状态的 Slurm job。可以用下面的命令确认：</p>
<pre><code class="language-bash">squeue -u &quot;$USER&quot;
</code></pre>
<p>如果提示 <code>nvitop: command not found</code>，说明计算节点环境里没有安装 <code>nvitop</code>，或者当前 shell 没有激活对应环境。可以进一步确认：</p>
<pre><code class="language-bash">which nvitop
</code></pre>
<p>如果使用 Conda 环境，也可以把 fallback 改成：</p>
<pre><code class="language-bash">srun --jobid=&quot;$jobid&quot; --overlap --pty bash -lc &quot;conda activate your-env &amp;&amp; nvitop&quot;
</code></pre>
<h2>总结</h2>
<p>这个函数解决的是 Slurm 集群上的一个具体问题：训练任务跑在计算节点，但用户日常登录的是 login node，直接运行 <code>nvitop</code> 往往看不到真正的 GPU 占用。</p>
<p>日常使用时只需要：</p>
<pre><code class="language-bash">gvitop
# 或者
gvitop 123456
</code></pre>
<p>它不复杂，但能减少在集群上排查显存占用、确认训练进程状态时的重复操作。</p>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[Anthropic：我们如何构建多代理 Research 系统]]></title>
            <link>https://zhoukai.space/posts/anthropic-multi-agent-research-system</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/anthropic-multi-agent-research-system</guid>
            <pubDate>Fri, 03 Apr 2026 04:10:00 GMT</pubDate>
            <description><![CDATA[Anthropic 工程文章《How we built our multi-agent research system》的中文全文翻译，聚焦多代理 Research 系统的架构、提示工程、评测方法与生产可靠性。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>本文是 Anthropic 工程文章《<a href="https://www.anthropic.com/engineering/multi-agent-research-system">How we built our multi-agent research system</a>》的中文全文翻译。</p>
</blockquote>
<h2>多代理系统的优势</h2>
<p>Claude 现在已经具备 <a href="https://www.anthropic.com/news/research">Research capabilities</a>，可以跨网页、Google Workspace 以及各类集成工具进行搜索，从而完成复杂任务。</p>
<p>这个多代理系统从原型走向生产的过程，让我们在系统架构、工具设计和提示词工程上学到了不少关键经验。所谓多代理系统，是指由多个 agent 组成的系统，这些 agent 会在循环中自主调用工具并协同工作。我们的 Research 功能中，有一个 agent 会根据用户查询规划研究流程，然后通过工具创建并行 agent，同时搜索信息。多代理系统也会引入一些新的挑战，例如 agent 协调、评测以及可靠性问题。</p>
<p>这篇文章将拆解那些对我们真正有效的原则。希望这些经验也能帮助你构建自己的多代理系统。</p>
<p>研究类工作通常是开放式问题，很难提前准确预测完成任务所需的步骤。面对复杂主题时，你无法把探索路径硬编码成一条固定流程，因为整个过程天生就是动态的，而且会受到前一步发现的影响。人类做研究时，往往也是一边调查、一边根据新发现调整方法，沿着过程中浮现出来的线索继续追踪。</p>
<p>这种不可预测性，使得 AI agent 特别适合研究任务。研究要求系统在调查过程中具备随时转向、或者探索旁支线索的灵活性。模型必须在很多轮交互中自主运行，根据中间发现决定下一步往哪里走。线性的、一次性完成的 pipeline 无法胜任这类任务。</p>
<p>搜索的本质是压缩，也就是从庞大的语料中提炼出有价值的洞见。子代理之所以有用，是因为它们可以带着各自独立的上下文窗口并行工作，同时探索问题的不同方面，然后再把最重要的信息压缩后交给主研究代理。每个子代理也天然形成了关注点分离，可以拥有不同的工具、提示词和探索轨迹，从而降低路径依赖，让调查更全面、更独立。</p>
<p>一旦智能达到某个阈值，多代理系统就会成为扩展性能的重要方式。举例来说，虽然过去 10 万年里单个人类个体变得更聪明了，但在信息时代，人类社会之所以呈指数级变强，靠的是群体智能以及协调能力。即使是具备通用智能的 agent，单独工作时也会受到限制；而一组 agent 协作时，能完成的事情会多得多。</p>
<p>我们的内部评测显示，多代理 Research 系统在那种需要同时沿多个独立方向展开的广度优先型查询上，表现尤其出色。我们发现，以 Claude Opus 4 作为主代理、Claude Sonnet 4 作为子代理的多代理系统，在内部 research eval 上，相比单代理 Claude Opus 4 的表现提升了 90.2%。例如，当系统被要求找出标普 500 信息技术板块公司全部董事会成员时，多代理系统可以把这个问题拆分给多个子代理分别处理，因此能够得到正确答案；而单代理系统只能缓慢地串行搜索，最终没能找到答案。</p>
<p>多代理系统之所以有效，很大程度上是因为它让系统愿意花足够多的 token 去解决问题。在我们的分析中，<a href="https://openai.com/index/browsecomp/">BrowseComp</a> 评测中的性能方差，有 95% 可以由三个因素解释。这个评测主要测试浏览型 agent 寻找难检索信息的能力。我们发现，仅 token 用量这一项，就解释了其中 80% 的方差；另外两个因素是工具调用次数和模型选择。这个结果验证了我们的架构思路：通过拥有独立上下文窗口的多个 agent 分担工作，为并行推理增加更多容量。最新一代 Claude 模型会显著提升 token 的使用效率。比如，从 Claude Sonnet 3.7 升级到 Claude Sonnet 4，带来的性能提升比在 Claude Sonnet 3.7 上把 token 预算翻倍还要大。对于超出单代理能力上限的任务，多代理架构实际上是一种扩展 token 使用规模的有效方式。</p>
<p>当然，这种架构也有明显的代价：它在实际使用中会非常快地消耗 token。根据我们的数据，普通 agent 的 token 用量大约是聊天交互的 4 倍，而多代理系统的 token 用量大约是聊天交互的 15 倍。要想在经济上可行，多代理系统必须应用于那些任务价值足够高、能够覆盖额外性能成本的场景。进一步说，一些要求所有 agent 共享同一上下文，或者 agent 之间依赖关系非常多的领域，今天并不适合多代理系统。比如，大多数 coding 任务里，真正能并行化的部分就比 research 少得多，而且 LLM agent 目前也还不擅长实时协调和向其他 agent 做任务委派。我们的经验是，多代理系统最适合那些高价值、可高度并行、信息量超过单一上下文窗口、并且需要对接大量复杂工具的任务。</p>
<h2>Research 的架构概览</h2>
<p>我们的 Research 系统采用了多代理架构，并使用 orchestrator-worker 模式：由一个主代理负责整体协调，同时把任务委派给并行运行的专用子代理。</p>
<p><img src="https://www.anthropic.com/_next/image?url=https%3A%2F%2Fwww-cdn.anthropic.com%2Fimages%2F4zrzovbb%2Fwebsite%2F1198befc0b33726c45692ac40f764022f4de1bf2-4584x2579.png&amp;w=3840&amp;q=75" alt="Research 多代理架构示意图"></p>
<p>上图展示了多代理架构的实际工作方式：用户查询先进入主代理，由它创建多个专门的子代理，并行搜索问题的不同部分。</p>
<p>当用户提交一个查询时，主代理会先分析问题、制定策略，然后生成多个子代理，让它们同时探索不同方面。正如上图所示，子代理在这里扮演的是“智能过滤器”的角色。它们会不断调用搜索工具收集信息，在这个例子中是调查 2025 年的 AI agent 公司，然后把公司名单返回给主代理，由主代理整合出最终答案。</p>
<p>传统的 RAG，也就是 Retrieval Augmented Generation，采用的是静态检索方式。它通常只会取回一批与输入 query 最相似的文本片段，然后基于这些片段生成回答。相比之下，我们的架构使用的是多步搜索：系统会动态发现相关信息、根据新发现调整方向，并分析结果，最终形成高质量答案。</p>
<p><img src="https://www.anthropic.com/_next/image?url=https%3A%2F%2Fwww-cdn.anthropic.com%2Fimages%2F4zrzovbb%2Fwebsite%2F3bde53c9578d74f6e05c3e515e20b910c5a8c20a-4584x4584.png&amp;w=3840&amp;q=75" alt="Research 工作流图"></p>
<p>上图展示了我们的多代理 Research 系统完整工作流。当用户提交查询后，系统会创建一个 <code>LeadResearcher</code> 代理，并进入迭代式研究流程。<code>LeadResearcher</code> 会先思考整体方法，并把计划写入 Memory，用于持久化上下文。这样做是因为一旦上下文窗口超过 200,000 tokens，前面的内容就会被截断，而研究计划必须被保留下来。接着，它会创建多个带有明确研究任务的专用 <code>Subagent</code>，图中只画了两个，但实际数量可以是任意多个。每个 <code>Subagent</code> 都会独立执行网页搜索，并通过交错式 thinking 来评估工具返回结果，然后把发现汇报给 <code>LeadResearcher</code>。<code>LeadResearcher</code> 会综合这些结果，并决定是否还需要继续研究。如果还需要，它可以继续创建新的子代理，或者调整当前策略。一旦信息已经足够，系统就会退出 research loop，并把所有发现交给 <code>CitationAgent</code>。<code>CitationAgent</code> 会处理这些文档和研究报告，找出适合插入引用的具体位置，从而确保所有论断都能正确归因到来源。最终，带引用的研究结果会返回给用户。</p>
<h2>Research Agent 的提示工程与评测</h2>
<p>多代理系统和单代理系统有一个关键差异，就是随着 agent 数量增加，协调复杂度会迅速上升。我们的早期 agent 会犯很多错误，比如对一个简单查询就创建 50 个子代理、为了不存在的来源在网上无休止地搜索、或者通过过量更新彼此打扰。由于每个 agent 都是由 prompt 驱动的，prompt engineering 成了我们改进行为的主要杠杆。下面是我们在为 agent 设计 prompt 时学到的一些原则：</p>
<h3>像 agent 一样思考</h3>
<p>想要迭代 prompt，首先必须理解 prompt 会产生什么效果。为了做到这一点，我们使用 <a href="https://console.anthropic.com/">Console</a> 基于系统里真实使用的 prompt 和工具构建模拟环境，然后逐步观察 agent 是如何工作的。这种做法很快暴露出失败模式，比如 agent 在已经拿到足够结果后还继续工作、使用过于冗长的搜索词，或者选错工具。有效的提示工程，依赖于对 agent 建立准确的心理模型；一旦模型建立起来，很多高影响力的修改就会变得很明显。</p>
<h3>教会 orchestrator 如何委派任务</h3>
<p>在我们的系统里，主代理需要把查询拆解成多个子任务，并向子代理描述这些任务。每个子代理都需要明确的目标、输出格式、推荐使用的工具和来源，以及清晰的任务边界。如果任务描述不够细致，agent 就会重复劳动、遗漏关键信息，或者根本找不到所需内容。我们一开始允许主代理只给出像 “research the semiconductor shortage” 这样简短的指令，但很快发现，这种指令往往模糊到足以让子代理误解任务，甚至和其他 agent 做完全相同的搜索。例如，一个子代理去研究 2021 年汽车芯片危机，另外两个子代理却重复调查 2025 年当前供应链情况，整个系统缺乏有效的分工。</p>
<h3>根据查询复杂度调整投入强度</h3>
<p>agent 并不擅长判断不同任务应该投入多少精力，因此我们把 effort scaling 的规则直接写进 prompt 里。简单的事实查找，只需要 1 个 agent 和 3 到 10 次工具调用；直接比较类任务，可能需要 2 到 4 个子代理，每个子代理大约进行 10 到 15 次工具调用；复杂研究任务则可能需要 10 个以上子代理，并且每个子代理都要承担明确分工。这些明确规则能帮助主代理更高效地分配资源，也能避免在简单问题上投入过度资源，而这正是我们早期版本中常见的失败模式。</p>
<h3>工具设计与工具选择至关重要</h3>
<p>agent-tool interface 的重要性，并不亚于 human-computer interface。选对工具不仅仅意味着效率更高，很多时候甚至是完成任务的前提。例如，如果某个上下文只存在于 Slack 里，而 agent 却去网页上搜索，那它从一开始就注定失败。随着 <a href="https://modelcontextprotocol.io/introduction">MCP servers</a> 让模型接触到外部工具，这个问题会进一步放大，因为 agent 面对的是大量此前没见过、且描述质量参差不齐的工具。我们为 agent 设计了明确的工具选择启发式规则。比如，先查看有哪些工具可用；根据用户意图决定用什么工具；做广泛外部探索时优先搜索网页；优先使用专用工具而不是通用工具。糟糕的工具描述会把 agent 带到完全错误的路径上，因此每个工具都必须有清晰的用途和明确的说明。</p>
<h3>让 agent 帮助改进自己</h3>
<p>我们发现，Claude 4 系列模型本身就是很优秀的 prompt engineer。只要给它一个 prompt 和一个失败模式，它就能分析 agent 为什么出错，并给出改进建议。我们甚至专门做了一个 tool-testing agent。给它一个有缺陷的 MCP 工具后，它会尝试实际调用这个工具，然后改写工具描述，避免后续失败。通过对同一个工具做几十次测试，这个 agent 找到了许多关键细节和 bug。改进工具可用性的这套流程，让后续 agent 在使用新描述时，任务完成时间降低了 40%，因为它们能避开大多数原本会犯的错误。</p>
<h3>先宽后窄</h3>
<p>搜索策略应该像优秀的人类研究者那样，先摸清地形，再逐步深入。agent 往往会默认写出过长、过于具体的查询词，结果反而返回很少的结果。我们通过 prompt 抑制这种倾向，要求 agent 先用简短、宽泛的 query 进行探索，判断当前信息空间里有哪些内容，然后再逐步收窄焦点。</p>
<h3>引导 thinking 过程</h3>
<p><a href="https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking">Extended thinking mode</a> 会让 Claude 输出额外 token，展示可见的 thinking 过程，这可以作为一种可控的草稿空间。主代理会利用 thinking 来规划整体方法，例如判断哪些工具适合当前任务、估计查询复杂度和子代理数量、定义每个子代理的角色。我们的测试显示，extended thinking 能提升指令遵循、推理质量和整体效率。子代理也会先做规划，然后在拿到工具结果后使用 <a href="https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#interleaved-thinking">interleaved thinking</a>，评估结果质量、识别信息缺口，并改进下一轮查询。这让子代理在面对不同任务时都能更灵活地自我调整。</p>
<h3>并行工具调用会显著改变速度与表现</h3>
<p>复杂研究任务天然需要探索大量来源。我们的早期 agent 是串行搜索的，这种方式慢得难以接受。为了解决速度问题，我们引入了两层并行化：第一，主代理会并行生成 3 到 5 个子代理，而不是按顺序一个个创建；第二，子代理内部也会并行调用 3 个以上工具。这些改变让复杂查询的 research 时间最多降低了 90%，使 Research 能够在几分钟内完成原本需要数小时的工作，同时覆盖的信息还更多。</p>
<p>我们的提示工程策略，更强调植入良好的启发式，而不是制定死板规则。我们研究了优秀人类研究者是如何处理 research 任务的，并把这些策略编码进 prompt 中，例如把复杂问题拆成更小的任务、认真评估来源质量、根据新信息调整搜索方式，以及判断当前更应该追求深度还是广度。与此同时，我们也通过设置明确护栏，主动避免 agent 意外失控。最后，我们始终坚持快速迭代闭环，依赖可观测性和测试案例来持续改进。</p>
<h2>如何有效评测 Agent</h2>
<p>高质量评测是构建可靠 AI 应用的基础，agent 也不例外。但多代理系统的评测有它自己的特殊难点。传统评测往往默认 AI 每次都会走相同步骤，也就是给定输入 X，系统应该沿着路径 Y 产生输出 Z。但多代理系统并不是这样工作的。即使起点完全一样，agent 也可能走出完全不同但同样有效的路径来达成目标。一个 agent 可能搜索 3 个来源，另一个可能搜索 10 个；它们也可能使用不同工具找到相同答案。因为很多时候我们事先并不知道“正确步骤”究竟是什么，所以通常无法简单地检查 agent 有没有按我们预先规定的步骤去做。相反，我们需要更灵活的评测方法：既要判断 agent 是否得到了正确结果，也要判断它的执行过程是否大体合理。</p>
<h3>从一开始就评测，哪怕样本很小</h3>
<p>在 agent 开发早期，很多改动的影响都非常大，因为系统里有大量“低垂果实”。一个小小的 prompt 修改，就可能把成功率从 30% 提升到 80%。在这种效应量很大的阶段，你只需要少量测试用例就能看出差异。我们一开始只用了大约 20 个 query，覆盖真实使用场景中的典型模式。很多时候，仅凭这些 query 就足以清楚看出某次改动的影响。我们经常听到一些 AI 开发团队说，他们之所以迟迟不做 eval，是因为觉得只有几百条测试用例构成的大型评测才有意义。但实际上，更好的做法是立刻从小规模测试开始，即便只有少数几个例子，也比一直等到大规模 eval 准备好要好得多。</p>
<h3>LLM-as-judge 只要设计得当，就能扩展</h3>
<p>Research 类输出很难用程序自动打分，因为它们是自由文本，而且通常不存在唯一正确答案。LLM 天然适合承担评委角色。我们使用了一个 LLM judge，根据预设 rubric 从多个维度评估输出，包括事实准确性，也就是论断是否与来源一致；引用准确性，也就是引用是否真的支持对应论断；完整性，也就是是否覆盖了用户要求的所有方面；来源质量，也就是是否优先使用了一手资料，而不是质量较低的二手来源；以及工具使用效率，也就是是否用了合适的工具，并以合理次数调用。我们曾尝试让多个 judge 分别评估不同组件，但最终发现，一次 LLM 调用、一个统一 prompt、输出 0.0 到 1.0 的得分以及 pass-fail 结论，这种方式反而最稳定，也最符合人工判断。尤其是在那些确实存在明确正确答案的 eval case 中，这种方法非常有效。比如，当问题是“是否准确列出了研发预算最高的前三家制药公司”时，LLM judge 可以很好地判断答案是否正确。使用 LLM 作为 judge，让我们能够以可扩展的方式评估数百条输出结果。</p>
<h3>人工评测能发现自动化遗漏的问题</h3>
<p>人工测试 agent 时，经常能发现 eval 没覆盖到的边角情况。例如，某些非常规查询下出现的幻觉答案、系统失败，或者一些不容易察觉的来源选择偏差。以我们自己的系统为例，人工测试人员发现，早期 agent 会系统性地优先选择经过 SEO 优化的内容农场，而不是学术 PDF 或个人博客这类权威但搜索排名没那么靠前的来源。后来，我们在 prompt 里加入了关于来源质量的启发式规则，这个问题才得到改善。即使在自动化评测越来越成熟的时代，人工测试依然是不可替代的。</p>
<p>多代理系统会出现一些涌现行为，也就是系统并没有被明确编程成那样，但行为却自然出现了。例如，只是对主代理做了一个小改动，就可能以不可预测的方式改变子代理的行为。要让系统成功运行，关键在于理解这些交互模式，而不仅仅是盯着单个 agent 的表现。因此，好的 prompt 不应该只是严格的命令集合，更应该是协作框架，用来规定分工方式、问题求解方法以及投入预算。要把这些做好，离不开精细的 prompt 和工具设计、可靠的启发式、良好的可观测性以及紧密的反馈回路。关于示例 prompt，你可以参考我们在 Cookbook 中开源的 <a href="https://platform.claude.com/cookbook/patterns-agents-basic-workflows">patterns-agents-basic-workflows</a>。</p>
<h2>生产可靠性与工程挑战</h2>
<p>在传统软件中，一个 bug 可能只是打断某个功能、拖慢性能，或者造成服务不可用。但在 agent 系统里，哪怕只是一个很小的改动，也可能引发巨大的行为变化。这使得为复杂 agent 编写代码变得异常困难，因为它们需要在长时间运行的过程中维持状态。</p>
<h3>Agent 是有状态的，错误会层层放大</h3>
<p>agent 可以长时间运行，并在大量工具调用之间持续维护状态。这意味着我们需要让代码具备持久执行能力，并且能在执行过程中妥善处理错误。如果缺少有效缓解手段，一些看似很小的系统故障，对 agent 来说就可能是灾难性的。当错误发生时，我们不能简单从头重启，因为重启既昂贵，也会让用户非常沮丧。为此，我们构建了能够从错误发生位置恢复执行的系统，而不是每次都重头开始。我们也会利用模型本身的智能，让它以更平滑的方式处理问题。例如，当某个工具失效时，直接告知 agent，让它自己调整，效果往往出奇地好。我们把基于 Claude 的 AI agent 的适应能力，与重试逻辑和定期 checkpoint 这类确定性 safeguard 结合起来使用。</p>
<h3>调试需要新的方法</h3>
<p>agent 会动态做决策，而且即便 prompt 完全相同，不同运行之间也并不确定。这让调试变得更加困难。例如，用户经常反馈 agent “没有找到明明很明显的信息”，但我们一开始并不知道原因。是因为搜索 query 写得不好？还是来源选得差？抑或是工具调用失败了？后来，我们为生产系统增加了完整 tracing，才能真正诊断 agent 为什么失败，并系统性地修复问题。除了标准意义上的可观测性之外，我们还会监控 agent 的决策模式和交互结构，同时不去监控单个对话的具体内容，以保护用户隐私。正是这种高层级的可观测性，让我们能够定位根因、发现意料之外的行为，并修复那些高频失败模式。</p>
<h3>部署需要精细协调</h3>
<p>agent 系统通常是一张高度有状态的网络，由 prompt、工具和执行逻辑共同组成，而且几乎持续不断地运行。这意味着每次我们部署更新时，系统中的 agent 可能正处在流程的任意位置。因此，我们必须防止那些原本出于好意的代码变更，反而把已经在跑的 agent 破坏掉。我们不能让所有 agent 在同一时刻全部切换到新版本。相反，我们使用 <a href="https://brandon.dimcheff.com/2018/02/rainbow-deploys-with-kubernetes/">rainbow deployments</a> 来避免打断正在运行的 agent。具体做法是让新旧版本同时运行，再把流量逐步从旧版本迁移到新版本。</p>
<h3>同步执行会形成瓶颈</h3>
<p>目前，我们的主代理还是以同步方式执行子代理，也就是说，它会等这一批子代理都完成之后，再继续下一步。这样做能简化协调逻辑，但也会在 agent 之间的信息流上形成瓶颈。例如，主代理无法实时引导子代理，子代理之间也无法协调，整个系统还可能因为等待某一个搜索较慢的子代理而被整体阻塞。如果改用异步执行，就能释放更多并行性，让 agent 真正并发工作，并在有需要时继续创建新的子代理。但异步也会带来新的困难，比如结果协调、状态一致性，以及错误在子代理之间如何传播。随着模型能够处理越来越长、越来越复杂的 research 任务，我们预计这种额外复杂度最终会被性能收益所抵消。</p>
<h2>总结</h2>
<p>在构建 AI agent 时，最后那一公里往往会变成整个旅程的大部分。一个在开发者机器上运行良好的代码库，要变成可靠的生产系统，往往还需要大量额外工程。agent 系统中错误的连锁效应，使得那些在传统软件里只算“小问题”的缺陷，也可能让 agent 整体跑偏。只要某一步失败，agent 就可能走上一条完全不同的轨迹，最终产生不可预测的结果。基于本文讨论的所有原因，原型到生产之间的差距，往往比人们原先预想的更大。</p>
<p>尽管如此，多代理系统在开放式研究任务上已经证明了自身价值。用户告诉我们，Claude 帮助他们发现了原本没有想到的商业机会、理清了复杂的医疗选项、解决了棘手的技术 bug，还通过挖掘他们自己本来不会发现的研究联系，节省了多达数天的工作时间。只要具备谨慎的工程实现、全面测试、细致的 prompt 与工具设计、健全的运行实践，以及研究、产品和工程团队之间基于当前 agent 能力边界的紧密协作，多代理 Research 系统就可以在大规模场景下稳定运行。我们已经看到，这类系统正在改变人们解决复杂问题的方式。</p>
<p><img src="https://www.anthropic.com/_next/image?url=https%3A%2F%2Fwww-cdn.anthropic.com%2Fimages%2F4zrzovbb%2Fwebsite%2F09a90e0aca54859553e93c18683e7fd33ff16d4c-2654x2148.png&amp;w=3840&amp;q=75" alt="Research 功能当前常见使用场景分布"></p>
<p>一张 Clio embedding plot，展示了今天用户最常见的 Research 使用方式。当前排名靠前的使用类别包括：在专业领域中构建软件系统，占比 10%；撰写和优化专业与技术内容，占比 8%；制定业务增长和营收策略，占比 8%；辅助学术研究和教育材料开发，占比 7%；以及检索和核验关于人物、地点或组织的信息，占比 5%。</p>
<h2>致谢</h2>
<p>本文由 Jeremy Hadfield、Barry Zhang、Kenneth Lien、Florian Scholz、Jeremy Fox 和 Daniel Ford 共同撰写。这项工作体现了 Anthropic 内部多个团队的集体努力，正是这些努力让 Research 功能成为可能。特别感谢 Anthropic 的 apps engineering 团队，他们的投入让这个复杂的多代理系统真正落地到生产环境。我们也感谢早期用户提供的高质量反馈。</p>
<h2>附录</h2>
<p>下面是一些关于多代理系统的补充建议。</p>
<h3>对跨多轮修改状态的 agent，更适合做终态评估</h3>
<p>对于那些会在多轮对话中持续修改持久状态的 agent，评测会有额外难点。与只读型的 research 任务不同，这类任务中的每一步操作都可能改变后续步骤所处的环境，从而形成传统评测方法难以处理的依赖关系。我们的经验是，终态评估往往比逐轮分析更有效。也就是说，不去判断 agent 是否走了一条特定流程，而是直接判断它最终是否达到了正确状态。这种方法承认 agent 可能通过不同路径达到同一个目标，同时也能保证最终结果符合预期。对于复杂工作流，可以把评测拆成一系列离散 checkpoint，在这些点上检查某些关键状态变化是否已经发生，而不是试图验证每一步中间过程。</p>
<h3>长时程对话管理</h3>
<p>生产级 agent 往往会持续几百轮对话，因此必须认真设计上下文管理策略。随着对话不断延长，普通上下文窗口会逐渐不够用，这就需要更智能的压缩和记忆机制。我们采用了一些模式，例如在一个工作阶段完成后，让 agent 先总结这一阶段内容，再把必要信息写入外部 memory，然后再进入后续任务。当上下文接近极限时，agent 也可以生成新的子代理，让它们带着干净上下文继续工作，同时通过精心设计的交接机制保持连续性。此外，当上下文到达上限时，它们还能从 memory 中重新读取诸如 research plan 这样的关键信息，而不是丢失此前已经完成的工作。这种分布式做法可以避免上下文溢出，同时维持长时交互中的整体连贯性。</p>
<h3>让子代理把结果直接输出到文件系统，减少“传话游戏”</h3>
<p>对于某些类型的结果，如果允许子代理直接输出，而不是所有内容都必须经过主协调者转述，往往能同时提升保真度和性能。与其让子代理把所有东西都通过主代理层层传达，不如设计一套 artifact 系统，让专用 agent 直接生成可以独立持久化的结果。子代理可以调用工具，把自己的工作结果写入外部系统，然后只把轻量级引用返回给协调者。这样可以减少多阶段处理中信息损失，也能避免为了在对话历史中传递大段输出而付出的额外 token 成本。这个模式尤其适用于代码、报告、数据可视化这类结构化输出，因为在这些场景中，子代理依靠其专用 prompt 产出的结果，往往比经过通用协调者二次过滤后的结果更好。</p>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[flash-attn 安装报错分析与解决]]></title>
            <link>https://zhoukai.space/posts/flash-attn-install-cross-device-link</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/flash-attn-install-cross-device-link</guid>
            <pubDate>Tue, 31 Mar 2026 02:00:00 GMT</pubDate>
            <description><![CDATA[记录 flash-attn 安装失败的根因分析，说明 Invalid cross-device link 与 pip、Linux 文件系统挂载点之间的关系，并给出可复用的解决方案。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>在安装 <code>flash-attn</code> 时，表面上看像是 CUDA、<code>nvcc</code> 或编译环境的问题，但实际根因并不在这些组件上。本文记录一次完整的排障过程，解释为什么 <code>Invalid cross-device link</code> 会导致安装在编译前就中断，以及如何稳定修复这一类问题。</p>
</blockquote>
<h2>结论</h2>
<p>本次 <code>flash-attn</code> 安装失败的根因是：<code>pip</code> 在不同文件系统之间执行文件移动时，触发了 Linux 的 <code>rename()</code> 跨设备限制，最终报出 <code>Invalid cross-device link</code>。</p>
<p>也就是说，这次失败与 CUDA 版本、PyTorch 版本以及 <code>flash-attn</code> 本身没有直接关系，更准确地说，它是由以下两部分共同造成的：</p>
<ul>
<li>服务器采用多挂载点文件系统布局</li>
<li><code>pip</code> 在安装 wheel 时会在临时目录与缓存目录之间执行移动操作<br>
这类问题如果不从文件系统机制入手，很容易误判为编译器缺失或者 CUDA 环境损坏。</li>
</ul>
<h2>问题现象</h2>
<p>当时执行的安装命令如下：</p>
<pre><code class="language-bash">pip install flash-attn --no-build-isolation
</code></pre>
<p>安装过程中出现的关键报错为：</p>
<pre><code class="language-text">Invalid cross-device link
</code></pre>
<p>与此同时，环境状态表现为：</p>
<ul>
<li><code>which nvcc</code> 没有输出</li>
<li><code>CUDA_HOME</code> 为空</li>
<li><code>torch.version.cuda</code> 显示为 <code>12.1</code></li>
<li><code>flash-attn</code> 安装过程没有进入本地编译阶段</li>
</ul>
<p>这里有一个很关键的诊断点：如果错误发生在真正编译之前，那么 <code>nvcc</code>、<code>gcc</code>、CUDA Toolkit 这些因素通常还没有成为主矛盾。</p>
<h2>根因分析</h2>
<h3>pip 安装 wheel 的关键流程</h3>
<p><code>pip</code> 安装 wheel 包时，流程可以简化为下面三个阶段：</p>
<ol>
<li>下载 wheel 到临时目录，默认通常是 <code>/tmp</code></li>
<li>将相关文件移动到缓存目录，默认通常是 <code>~/.cache/pip</code></li>
<li>解压并执行最终安装</li>
</ol>
<p>问题出现在第 2 步。这里的“移动”并不是简单意义上的复制，而往往依赖底层的 <code>rename()</code> 系统调用。</p>
<h3>Linux 对 rename() 的限制</h3>
<p>Linux 对 <code>rename()</code> 有一个重要约束：它只能在同一文件系统内部完成原子重命名或移动。</p>
<p>如果源路径和目标路径不属于同一个 device，那么就会直接报错：</p>
<pre><code class="language-text">Invalid cross-device link
</code></pre>
<p>这不是 <code>pip</code> 的特例，也不是 <code>flash-attn</code> 的特例，而是 Linux 文件系统语义本身的限制。</p>
<h3>当前服务器环境的典型结构</h3>
<p>在多用户 GPU 服务器上，比较常见的文件系统布局是：</p>
<table>
<thead>
<tr>
<th>路径</th>
<th>实际设备</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>/tmp</code></td>
<td>系统盘或本地 SSD</td>
</tr>
<tr>
<td><code>/home/zhoukai</code></td>
<td>NFS、共享存储或另一块磁盘</td>
</tr>
</tbody>
</table>
<p>在这种结构下，<code>pip</code> 实际上可能做了这样一件事：</p>
<pre><code class="language-text">/tmp  -&gt;  /home/zhoukai/.cache/pip
</code></pre>
<p>这一步一旦走到 <code>rename()</code>，就变成了典型的跨文件系统移动，于是安装会在 wheel 处理阶段直接中断。</p>
<h2>为什么这是服务器上更常见的问题</h2>
<h3>本地开发机通常更“单盘”</h3>
<p>在个人开发机上，<code>/tmp</code>、用户目录甚至大部分开发数据，往往都落在同一块磁盘或者同一个文件系统分区上，因此：</p>
<ul>
<li>临时目录和缓存目录属于同一 device</li>
<li><code>rename()</code> 可以正常执行</li>
<li><code>pip</code> 安装过程不会暴露这个问题</li>
</ul>
<h3>组内服务器更常见多挂载架构</h3>
<p>而在组内服务器或者实验室 GPU 节点上，经常采用下面这种布局：</p>
<pre><code class="language-text">/      -&gt; 系统盘
/tmp   -&gt; 本地临时盘
/home  -&gt; NFS 或共享存储
</code></pre>
<p>这种设计本身没有问题，优点也很明显，比如系统响应快、用户数据集中管理、临时文件落本地磁盘减少网络负担。但它也意味着：</p>
<ul>
<li>不同路径背后是不同 mount point</li>
<li>I/O 设备不一致</li>
<li>依赖跨目录移动的工具更容易出现 <code>cross-device link</code></li>
</ul>
<h2>解决方案</h2>
<h3>已验证的修复方式</h3>
<p>最直接有效的做法，是把临时目录统一到用户目录下，让临时文件和 <code>pip</code> 缓存位于同一个文件系统。</p>
<p>执行命令如下：</p>
<pre><code class="language-bash">mkdir -p &quot;$HOME/tmp&quot; &quot;$HOME/.cache/pip&quot;

export TMPDIR=&quot;$HOME/tmp&quot;
export TEMP=&quot;$HOME/tmp&quot;
export TMP=&quot;$HOME/tmp&quot;

pip install flash-attn --no-build-isolation --no-cache-dir
</code></pre>
<h3>为什么这个方案有效</h3>
<p>修改后，核心路径会变成：</p>
<ul>
<li>临时目录：<code>$HOME/tmp</code></li>
<li>缓存目录：<code>$HOME/.cache/pip</code></li>
</ul>
<p>如果二者都位于 <code>/home</code> 之下，那么它们通常属于同一文件系统。这样一来：</p>
<ul>
<li><code>pip</code> 不再需要跨 device 移动文件</li>
<li><code>rename()</code> 可以正常执行</li>
<li>安装流程能够继续推进</li>
</ul>
<h3>关于 --no-cache-dir 的说明</h3>
<p>这次实际执行中同时加了 <code>--no-cache-dir</code>，目的是进一步降低缓存阶段带来的干扰，使安装路径更简单、更稳定。</p>
<p>不过真正的核心仍然是：<strong>让临时目录与目标目录处于同一文件系统中</strong>。即使不使用 <code>--no-cache-dir</code>，只要路径布局合理，这类错误本身也应当被消除。</p>
<h2>问题本质归纳</h2>
<p>如果要用一句话概括，这个问题的本质是：</p>
<blockquote>
<p>在多文件系统环境下，<code>pip</code> 的 wheel 处理机制与 Linux 的 <code>rename()</code> 跨设备限制发生了冲突。</p>
</blockquote>
<p>它通常具备以下特征：</p>
<ul>
<li>往往出现在大 wheel 包安装过程中</li>
<li>更容易出现在服务器、多磁盘或 NFS 环境中</li>
<li>错误发生在编译前阶段，而不是 CUDA 编译阶段</li>
</ul>
<h2>适用范围</h2>
<p>这个问题并不只会出现在 <code>flash-attn</code> 上。只要安装流程中涉及大型 wheel、缓存搬运或者复杂临时目录处理，理论上都可能复现。</p>
<p>比较常见的包包括：</p>
<ul>
<li><code>flash-attn</code></li>
<li><code>xformers</code></li>
<li><code>vllm</code>（部分版本）</li>
<li><code>deepspeed</code></li>
<li><code>triton</code></li>
<li>其他带 CUDA 扩展的大型 Python 包</li>
</ul>
<h2>推荐的长期处理方式</h2>
<h3>方案 A：写入 shell 配置</h3>
<p>如果长期在多用户服务器上工作，最省事的做法是直接把临时目录固定到用户目录下。例如写入 <code>~/.bashrc</code> 或 <code>~/.zshrc</code>：</p>
<pre><code class="language-bash">export TMPDIR=&quot;$HOME/tmp&quot;
export TEMP=&quot;$HOME/tmp&quot;
export TMP=&quot;$HOME/tmp&quot;

mkdir -p &quot;$HOME/tmp&quot; &quot;$HOME/.cache/pip&quot;
</code></pre>
<p>这个方案适合作为默认配置。</p>
<h3>方案 B：显式指定 pip 缓存目录</h3>
<p>如果希望进一步增强稳定性，可以额外固定 <code>pip</code> 缓存目录：</p>
<pre><code class="language-bash">export PIP_CACHE_DIR=&quot;$HOME/.cache/pip&quot;
</code></pre>
<p>这样可以减少 <code>pip</code> 在不同默认路径之间切换带来的不确定性。</p>
<h3>方案 C：仅对单次安装临时生效</h3>
<p>如果不想改全局环境，也可以在单条命令中临时指定：</p>
<pre><code class="language-bash">TMPDIR=&quot;$HOME/tmp&quot; pip install xxx
</code></pre>
<h2>总结</h2>
<blockquote>
<p>在多用户 GPU 服务器中，<code>/tmp</code> 和用户目录通常位于不同文件系统。<code>pip</code> 在安装大型 wheel 时，可能需要把文件从临时目录移动到缓存目录；这个过程如果走到底层 <code>rename()</code>，就会因为跨设备而报 <code>Invalid cross-device link</code>。解决方法是把 <code>TMPDIR</code> 和 <code>pip</code> cache 统一指向同一文件系统，例如都放到 <code>$HOME</code> 下，从而避免跨 device 移动失败。</p>
</blockquote>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[安装 OpenClaw 并接入飞书全流程记录]]></title>
            <link>https://zhoukai.space/posts/openclaw-feishu-tutorial</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/openclaw-feishu-tutorial</guid>
            <pubDate>Mon, 16 Feb 2026 02:00:00 GMT</pubDate>
            <description><![CDATA[详细演示如何在 Linux 上完成 OpenClaw 的安装、初始配置，并一步步接入飞书机器人，最终实现在飞书 App 中直接指挥 AI 助手]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>什么是 OpenClaw？</h2>
<p>在开始之前，先做一下简单的科普：</p>
<p><strong>OpenClaw</strong> 的名字经历了三次变更：</p>
<ul>
<li>最初叫做 <code>ClawdBot</code></li>
<li>后因与 <code>Claude</code> 名字过于相似，被指控侵权，遂改名为 <code>MoltBot</code></li>
<li>但在改名过程中遭遇域名和社交账号被抢注，甚至出现同名加密货币割韭菜的情况</li>
<li><strong>最终定名为：OpenClaw</strong></li>
</ul>
<h3>核心特点</h3>
<table>
<thead>
<tr>
<th>特点</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>真正的执行能力</strong></td>
<td>不仅能回答问题，还能实际操作你的电脑</td>
</tr>
<tr>
<td><strong>24/7 全天候待命</strong></td>
<td>在你睡觉时也能主动完成任务</td>
</tr>
<tr>
<td><strong>完全开源免费</strong></td>
<td>数据完全掌控在自己手中</td>
</tr>
<tr>
<td><strong>多平台支持</strong></td>
<td>国外支持 WhatsApp、Telegram、Discord、Slack、iMessage 等；国内支持飞书、钉钉等</td>
</tr>
</tbody>
</table>
<hr>
<h2>开始安装 OpenClaw</h2>
<p>首先得说明白：因为 OpenClaw 需要一个地方来部署，而部署在自己的电脑上是很不明智的。所以我选择了在我的远程 Linux 主机上部署（也就是工位上那一台机器了）。系统是 Ubuntu24.04 。通过 ssh 远程连接并部署。</p>
<h3>执行一键安装命令</h3>
<p>复制以下命令，粘贴到远程的 Linux 终端窗口中，按 <code>Enter</code> 执行：</p>
<pre><code class="language-bash">curl -fsSL https://openclaw.ai/install.sh | bash
</code></pre>
<p><strong>安装过程会自动完成</strong>：</p>
<ul>
<li>检测系统环境</li>
<li>安装必要依赖（Node.js 等）</li>
<li>下载 OpenClaw 核心文件</li>
<li>配置环境变量</li>
<li>启动配置向导</li>
</ul>
<hr>
<h2>初始配置向导</h2>
<p>安装完成后，会自动进入配置向导（<code>openclaw onboard</code>）。</p>
<h3>风险告知</h3>
<p>这一步主要告知使用 OpenClaw 可能存在的风险。</p>
<ul>
<li>按 <strong>向左方向键 ←</strong>，选择 <code>Yes</code></li>
<li>按 <code>Enter</code> 回车确认</li>
</ul>
<h3>选择 QuickStart 模式</h3>
<p>按照向导提示选择 QuickStart 模式以快速配置。</p>
<h3>配置 AI 模型 API Key</h3>
<p>OpenClaw 需要连接到大语言模型才能工作。OpenClaw 比较费 token，国外模型成本高，这里选择国内的<strong>智谱 GLM 4.7</strong>。也正好是因为我年前有买了智谱的包年的 coding plan 。</p>
<blockquote>
<p>如果没有智谱的 API Key，点击官方地址注册获取：<a href="https://www.bigmodel.cn/glm-coding?ic=RBSKXMPNJP">https://www.bigmodel.cn/glm-coding?ic=RBSKXMPNJP</a></p>
</blockquote>
<p>输入自己的 API Key 后继续。</p>
<h3>选择 AI 模型</h3>
<p>这里选择默认的 <strong>GLM 4.7</strong>，这也是智谱当前的旗舰模型（我的 coding plan 没法体验到 GLM-5 😭）。</p>
<h3>连接即时通讯平台</h3>
<p>配置完 AI 模型后，OpenClaw 会询问要连接哪个通讯平台。</p>
<blockquote>
<p>OpenClaw 原生支持 WhatsApp、Telegram、Discord、Slack、iMessage 等海外平台。国内用户常用的飞书、钉钉等也已支持接入。</p>
</blockquote>
<p>由于飞书配置较为复杂，这里先选择<strong>跳过</strong>，后续可通过（openclaw channels add）继续进行。</p>
<h3>选择 Skills</h3>
<p>选择：<strong>No</strong>，暂不配置，后续通过 UI 界面进行配置。</p>
<h3>是否开启 Hooks</h3>
<p>选择： <strong>No</strong></p>
<p>操作步骤：</p>
<ol>
<li>先敲 <strong>空格键</strong>（表示选中当前项）</li>
<li>再敲 <strong>回车键</strong> 确认</li>
</ol>
<h3>启动服务并打开 TUI 界面</h3>
<p>因为是远程的 Linux 服务器，现在还没法打开 webui，所以暂时选择打开 TUI 测试是否能成功使用。</p>
<p>此时会自动打开一个 TUI 窗口来启动服务。</p>
<p>在 TUI 中发送一条测试消息，验证 OpenClaw 是否正常工作。如果能，告诉他你叫什么名字，他叫什么名字，就好了。然后按两次 ctrl+c 退出即可。</p>
<hr>
<h2>接入飞书机器人</h2>
<p>这一步遇到问题可以参考官方文档：<a href="https://docs.openclaw.ai/zh-CN/channels/feishu">https://docs.openclaw.ai/zh-CN/channels/feishu</a></p>
<h3>来到飞书开发者后台</h3>
<p><strong>飞书开放平台地址</strong>：<a href="https://open.feishu.cn">https://open.feishu.cn</a></p>
<blockquote>
<p>没有飞书账号的需要先注册账号</p>
</blockquote>
<p>点击右上角进入 <strong>开发者后台</strong>。</p>
<h3>创建应用</h3>
<ol>
<li>点击创建应用</li>
<li>填写应用信息</li>
<li>创建自建应用</li>
</ol>
<h3>获取应用凭证</h3>
<p>在应用管理页面，获取 <strong>App ID</strong> 和 <strong>App Secret</strong>，记下这两个值，后续配置需要使用。</p>
<h3>给应用添加机器人</h3>
<ol>
<li>在应用功能中添加机器人能力</li>
<li>配置机器人基本信息</li>
</ol>
<h3>给应用配置权限</h3>
<p>把即时通讯相关的权限全部开通（搜索 im:）：</p>
<ul>
<li>获取群组信息</li>
<li>发送消息</li>
<li>接收消息</li>
<li>获取通讯录基本信息</li>
</ul>
<h3>创建版本并发布</h3>
<ol>
<li>创建应用版本</li>
<li>填写版本说明</li>
<li>发布为在线版本</li>
<li>来到飞书客户端进行审批</li>
</ol>
<h3>安装飞书插件</h3>
<p>打开终端，输入以下命令安装飞书插件：</p>
<pre><code class="language-bash">openclaw plugins install @openclaw/feishu
</code></pre>
<p>安装成功后，打开一个新的命令窗口，开始配置飞书插件：</p>
<pre><code class="language-powershell">openclaw config
</code></pre>
<p>按照提示进行配置：</p>
<ol>
<li>选择渠道：Feishu</li>
<li>选择配置链接</li>
<li>输入飞书的 App ID 和 App Secret</li>
<li>域名选择：中国的 <a href="http://feishu.cn">feishu.cn</a></li>
<li>接受群组聊天：Yes</li>
<li>选择完成并继续</li>
</ol>
<h3>重启服务</h3>
<p>重启 OpenClaw 服务使配置生效，控制台可以看到飞书插件已配置成功。</p>
<h3>回到飞书后台设置事件回调</h3>
<ol>
<li>进入事件管理</li>
<li>选择 <strong>使用长连接接收事件</strong></li>
<li>添加接收消息事件</li>
<li>给应用开通获取通讯录基本信息的权限</li>
<li>重新发布版本（与前面步骤相同，发布为在线应用）</li>
</ol>
<h3>在飞书中与 OpenClaw 对话</h3>
<p>来到飞书客户端或手机飞书 App：</p>
<ol>
<li>搜索并添加你的机器人</li>
<li>发送消息测试</li>
<li>你可以问机器人任何问题，或者让它执行任务</li>
</ol>
<hr>
<h2>访问 Web 控制面板</h2>
<p>配置完成后，使用 <strong>openclaw dashboard --no-open</strong>，会显示控制面板链接，格式类似：</p>
<pre><code class="language-c">Control UI: http://127.0.0.1:18789%token=巴拉巴拉一大串
</code></pre>
<ol>
<li>复制完整链接</li>
<li>在浏览器中打开</li>
<li>即可看到可视化 UI 管理界面</li>
</ol>
<p>但由于我是远程控制的，所以得加一步 SSH 端口转发到本机。这个端口转发链接在使用<strong>openclaw dashboard --no-open</strong>命令后会自动跳出来的。反正整个流程都挺简单的。</p>
<hr>
<h2>常用命令速查</h2>
<table>
<thead>
<tr>
<th>命令</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>openclaw onboard</code></td>
<td>重新进入配置向导</td>
</tr>
<tr>
<td><code>openclaw status</code></td>
<td>查看运行状态</td>
</tr>
<tr>
<td><code>openclaw health</code></td>
<td>健康检查</td>
</tr>
<tr>
<td><code>openclaw gateway start</code></td>
<td>启动服务</td>
</tr>
<tr>
<td><code>openclaw gateway stop</code></td>
<td>停止服务</td>
</tr>
<tr>
<td><code>openclaw update</code></td>
<td>更新到最新版本</td>
</tr>
<tr>
<td><code>openclaw doctor</code></td>
<td>诊断问题</td>
</tr>
<tr>
<td><code>openclaw uninstall</code></td>
<td>卸载 OpenClaw</td>
</tr>
</tbody>
</table>
<hr>
<h2>更新（2026-03-08）</h2>
<p>当前 OpenClaw 的默认设置里，Web 功能等工具能力默认是关闭状态，需要手动修改 <code>openclaw.json</code>。</p>
<p>将其中的 <code>tools</code> 配置改为 <code>full</code>：</p>
<pre><code class="language-json">{
  &quot;tools&quot;: {
    &quot;profile&quot;: &quot;full&quot;
  }
}
</code></pre>
<hr>
<h2>参考链接</h2>
<ul>
<li><a href="https://openclaw.ai/">OpenClaw 官网</a></li>
<li><a href="https://open.feishu.cn">飞书开放平台</a></li>
<li><a href="https://mp.weixin.qq.com/s/JGd4u8g-Fti4sRcJcSiOLQ">手把手教你安装OpenClaw并接入飞书，让AI在聊天软件里帮你干活</a></li>
</ul>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[arXiv 论文翻译工具推荐：从 LaTeX 源码出发的智能翻译]]></title>
            <link>https://zhoukai.space/posts/arxiv-translation-tools</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/arxiv-translation-tools</guid>
            <pubDate>Sat, 31 Jan 2026 02:00:00 GMT</pubDate>
            <description><![CDATA[推荐两款基于 LaTeX 源码的 arXiv 论文翻译工具：幻觉翻译网站和 AI Agent 翻译方法]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>除此之外还推荐两个工具：</p>
<p>PDF: <a href="https://github.com/PDFMathTranslate/PDFMathTranslate">PDFMathTranslate</a></p>
<p>LaTex: <a href="https://github.com/binary-husky/gpt_academic/tree/master">gpt_academic</a></p>
</blockquote>
<h2>为什么从 LaTeX 源码翻译？</h2>
<p>阅读英文论文是日常工作的刚需。但直接阅读 PDF 往往存在以下问题：</p>
<ul>
<li><strong>语言障碍</strong>：英文不是母语的读者需要花费大量时间理解专业术语和句子结构</li>
<li><strong>效率低下</strong>：逐字逐句翻译影响阅读效率，难以快速把握论文核心内容</li>
<li><strong>术语困惑</strong>：专业领域的术语翻译不准确会导致理解偏差</li>
</ul>
<p>传统的 PDF 翻译工具（如 Google Translate、DeepL 等）在处理学术论文时存在明显缺陷：</p>
<pre><code>❌ 数学公式被破坏或丢失
❌ 图表标题和注释翻译混乱
❌ 代码块和算法伪码翻译错误
❌ 参考文献格式错乱
❌ 双栏排版变成单栏，影响阅读
</code></pre>
<p><strong>LaTeX 源码翻译的优势</strong>：arXiv 上的绝大多数论文都提供了 LaTeX 源码，直接从源码出发进行翻译，可以完美保留：</p>
<ul>
<li>数学公式的 LaTeX 代码（不翻译）</li>
<li>图表、代码块的结构（只翻译必要的 caption）</li>
<li>论文的排版格式和引用关系</li>
<li>章节结构和交叉引用</li>
</ul>
<h2>幻觉翻译（hjfy.top）- 在线翻译工具</h2>
<h3>工具简介</h3>
<p><strong>幻觉翻译</strong>（hjfy.top）是一个专门针对 arXiv 论文和学术文档的在线翻译工具。作者在知乎文章《消耗 3 亿 token，我用大模型翻译了 1 万篇 arXiv 论文》中详细介绍了开发历程。</p>
<p><strong>网站地址</strong>：<a href="https://hjfy.top/">https://hjfy.top/</a></p>
<p><strong>核心特点</strong>：</p>
<ul>
<li>专门优化 arXiv 论文翻译</li>
<li>从 LaTeX 源码出发，保持格式完整</li>
<li>支持多种文档格式（Word、PDF、Epub）</li>
<li>持续优化和修复 bad case</li>
</ul>
<h3>使用方法</h3>
<p>幻觉翻译的使用非常简单，无需任何配置：</p>
<h5>方式一：直接使用 arXiv URL</h5>
<ol>
<li>访问 <a href="https://hjfy.top/">https://hjfy.top/</a></li>
<li>在输入框中粘贴 arXiv 论文 URL（如 <code>https://arxiv.org/abs/2501.12345</code>）或 论文ID：<code>2501.12345</code></li>
<li>点击翻译按钮</li>
<li>等待翻译完成，下载生成的 PDF</li>
</ol>
<h5>方式二：上传文件</h5>
<ol>
<li>访问 <a href="https://hjfy.top/">https://hjfy.top/</a></li>
<li>选择上传 PDF 或 LaTeX 源码文件</li>
<li>等待处理和翻译</li>
<li>下载翻译结果</li>
</ol>
<p><strong>支持的输入格式</strong>：</p>
<ul>
<li>arXiv URL（自动获取源码）</li>
<li>PDF 文件</li>
<li>LaTeX 源码（.tex、.zip）</li>
<li>Word 文档</li>
<li>Epub 电子书</li>
</ul>
<h3>翻译效果</h3>
<ul>
<li><strong>格式保持完美</strong>：公式、图表、引用结构完整</li>
<li><strong>翻译质量稳定</strong>：经过大量论文训练，专业术语翻译准确</li>
<li><strong>零配置使用</strong>：打开网页即可使用，无需安装任何软件</li>
<li><strong>持续优化</strong>：作者每天针对翻译出错的 bad case 进行修复</li>
<li><strong>免费使用</strong>：目前完全免费开放（目前：2026.01.31）</li>
</ul>
<h2>AI Agent 翻译方法 - 本地化方案</h2>
<h3>方法来源</h3>
<p>苏剑林在 2026 年 1 月 28 日的博客《<a href="https://spaces.ac.cn/archives/11578">一行代码将arXiv论文翻译成中文版</a>》中，介绍了一种基于 AI Agent 的本地化翻译方法。</p>
<h3>技术原理</h3>
<p>AI Agent 方法的技术优势在于：</p>
<p><strong>自动化处理流程</strong>：</p>
<ol>
<li>自动分析论文的目录结构</li>
<li>识别需要翻译的文件（.tex 文件）</li>
<li>逐段翻译，保持 LaTeX 代码可编译性</li>
<li>自动运行 xelatex 编译</li>
<li><strong>根据编译错误自动调整翻译结果</strong>（关键优势！）</li>
<li>检查漏翻译的章节和文件</li>
<li>可开启多个 subagent 并行翻译</li>
</ol>
<p><strong>与人工规则编写的对比</strong>：</p>
<table>
<thead>
<tr>
<th>维度</th>
<th>人工规则编写</th>
<th>AI Agent</th>
</tr>
</thead>
<tbody>
<tr>
<td>开发难度</td>
<td>极高，需要枚举所有情况</td>
<td>低，AI 自动判断</td>
</tr>
<tr>
<td>维护成本</td>
<td>持续修复 bad case</td>
<td>自动适应和调整</td>
</tr>
<tr>
<td>编译错误处理</td>
<td>需要重写部分 LaTeX 编译器</td>
<td>AI 自动修正</td>
</tr>
<tr>
<td>上下文理解</td>
<td>逐段翻译，无全局信息</td>
<td>全局上下文，质量更好</td>
</tr>
</tbody>
</table>
<h3>基本使用</h3>
<h5>前置条件</h5>
<ol>
<li>本地安装 <a href="https://github.com/MoonshotAI/kimi-cli">kimi-cli</a> 并配置 kimi-k2.5</li>
<li>安装 LaTeX 编译环境（MacTeX、TeX Live 等）</li>
</ol>
<h5>翻译命令示例</h5>
<p><strong>方式一：翻译已下载的源码</strong></p>
<pre><code class="language-bash"># 进入论文源码目录
cd paper_source/

# 执行翻译
kimi --print --prompt &quot;你所在目录是一篇英文论文的latex源码，你需要做的是将论文翻译成中文版本，要求所有英文文字内容都翻译成中文，公式不变，表格、图片等只翻译必要的caption，代码框、算法框也只需翻译必要的注释，而人名不必翻译；将翻译后的新源码保存到当前目录下名为'paper_cn'的新目录中，要注意保持源码的可编译性；翻译完后要仔细检查一遍，看有没有漏翻译的章节和文件；最后，用xelatex将翻译结果重新编译成pdf，返回生成的pdf路径。如果翻译内容较多，应当开启多个subagent来并行翻译。&quot;
</code></pre>
<p><strong>方式二：从 arXiv 自动下载并翻译</strong></p>
<pre><code class="language-bash">kimi --print --prompt &quot;从arXiv上下载id为2502.16982的论文源码，然后解压并将它翻译成中文版本，要求所有英文文字内容都翻译成中文，公式不变，表格、图片等只翻译必要的caption，代码框、算法框也只需翻译必要的注释，而人名不必翻译；将翻译后的新源码保存到解压目录下名为'paper_cn'的新目录中，要注意保持源码的可编译性；翻译完后要仔细检查一遍，看有没有漏翻译的章节和文件；最后，用xelatex将翻译结果重新编译成pdf，返回生成的pdf路径。如果翻译内容较多，应当开启多个subagent来并行翻译。&quot;
</code></pre>
<h5>效果示例</h5>
<p>苏剑林提供了两篇论文的翻译对比（<a href="https://spaces.ac.cn/archives/11578">博客原文</a>）</p>
<p><strong>参考链接</strong>：</p>
<ul>
<li><a href="https://hjfy.top/">幻觉翻译网站</a></li>
<li><a href="https://spaces.ac.cn/archives/11578">苏剑林：一行代码将arXiv论文翻译成中文版</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/1905569596599169419">知乎：消耗 3 亿 token，我用大模型翻译了 1 万篇 arXiv 论文</a></li>
<li><a href="https://github.com/MoonshotAI/kimi-cli">kimi-cli GitHub</a></li>
</ul>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[国内连接校内Ubuntu服务器：花生壳内网穿透完整实操教程]]></title>
            <link>https://zhoukai.space/posts/peanut-shell-intranet</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/peanut-shell-intranet</guid>
            <pubDate>Wed, 28 Jan 2026 02:00:00 GMT</pubDate>
            <description><![CDATA[详细介绍使用贝锐花生壳实现国内设备连接校内Ubuntu服务器的完整流程，包括安装、配置、端口映射和问题排查]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>主要是因为 Tailscale 的 DERP 服务器都在国外，因此使用起来延迟特别高。所以考虑使用国内的方案。不过这个花生壳方案也有缺陷，太抠了😤，每个月只有 1 GB的流量，根本不够用。稍微高强度四五天就用完了。</p>
</blockquote>
<h2>背景</h2>
<p>校内Ubuntu服务器通常处于内网环境（无公网IP、端口受校园网限制），国内校外设备直接连接难度大。传统方案（如Tailscale、反向SSH隧道）需依赖海外VPS，延迟较高（80-120ms）。而贝锐花生壳依托国内BGP多线节点，针对校园网做了专项优化，延迟可低至30-80ms，且操作极简，无需配置公网VPS。通过花生壳远程连接后就可以通过 SSH 的 Jump 功能做中继连接到校内服务器了。本文主要介绍的是：本地 -&gt; 自己的校内设备 -&gt; 校内服务器的整个流程的前半部分。</p>
<h2>前置准备</h2>
<ol>
<li>贝锐账号：注册地址 <a href="https://www.oray.com/">https://www.oray.com/</a></li>
<li>校内设备：运行Ubuntu 24.04的服务器，需能访问外网（可通过<code>ping baidu.com</code>验证）；</li>
<li>国内设备：Windows/macOS/Linux均可，需安装SSH工具（系统自带终端即可，图形化工具可选Xshell/Putty）。</li>
</ol>
<h2>核心实操步骤</h2>
<h3>步骤1：校内Ubuntu安装花生壳客户端</h3>
<p>登录校内Ubuntu终端，执行以下命令完成安装（适配64位系统）：</p>
<pre><code class="language-bash"># 1. 下载花生壳客户端安装包
wget https://down.oray.com/hsk/linux/phddns_5.2.0_amd64.deb

# 2. 安装客户端（若提示依赖问题，执行sudo apt -f install修复）
sudo dpkg -i phddns_5.2.0_amd64.deb

# 3. 查看安装状态与核心信息（SN码是设备绑定关键）
phddns status
</code></pre>
<h4>安装成功判断标准：</h4>
<ul>
<li>终端输出 <code>Successful installation of Phddns Service</code> 与 <code>Runstatus: ONLINE</code>；</li>
<li>显示完整SN码（示例：<code>oray240248e65529</code>）；</li>
<li>注意：Ubuntu 24.04默认未预装<code>netstat</code>，可能出现<code>netstat: 未找到命令</code>警告，不影响核心功能，可通过<code>sudo apt install net-tools -y</code>安装工具消除警告。</li>
</ul>
<h3>步骤2：贝锐控制台绑定校内设备</h3>
<p>花生壳的&quot;外网访问地址&quot;需与校内设备绑定才有效，具体操作：</p>
<ol>
<li>登录花生壳控制台：<a href="https://console.hsk.oray.com/">https://console.hsk.oray.com/</a>；</li>
<li>左侧导航栏点击【设备管理】→【添加设备】；</li>
<li>输入步骤1中获取的SN码（如<code>oray240248e65529</code>），点击&quot;确认绑定&quot;；</li>
<li>绑定成功后，设备列表会显示该设备，登录后，状态为&quot;在线&quot;（与校内终端<code>Runstatus: ONLINE</code>对应）。</li>
</ol>
<h3>步骤3：配置SSH端口映射</h3>
<p>映射的核心是&quot;将校内Ubuntu的SSH端口（默认为22）映射到花生壳国内节点的外网地址&quot;：</p>
<ol>
<li>点击页面【内网穿透-&gt; 添加映射】，按以下规则填写：</li>
</ol>
<table>
<thead>
<tr>
<th>配置字段</th>
<th>填写内容</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>映射名称</td>
<td>Ubuntu-SSH</td>
<td>自定义，方便后续识别</td>
</tr>
<tr>
<td>内网主机</td>
<td>内网IP</td>
<td>需填Ubuntu内网IP</td>
</tr>
<tr>
<td>内网端口</td>
<td>22</td>
<td>SSH默认端口，固定值（若修改过SSH端口则填对应值）</td>
</tr>
<tr>
<td>映射协议</td>
<td>TCP</td>
<td>SSH基于TCP协议，必选此项</td>
</tr>
<tr>
<td>外网域名</td>
<td>账号下免费域名（<a href="http://xn--xxx-eo8e.oicp.net">如xxx.oicp.net</a>）</td>
<td>贝锐自动分配，下拉框选择即可</td>
</tr>
<tr>
<td>外网端口</td>
<td>动态端口（免费）</td>
<td>免费版无需自定义，系统自动分配（如27817）</td>
</tr>
</tbody>
</table>
<ol start="2">
<li>填写完成后点击【确定】，映射列表会生成一条&quot;在线&quot;状态的记录，同时显示完整外网地址（格式：<code>域名:端口</code>，示例：<code>外网地址:27817</code>）。</li>
</ol>
<h3>步骤4：国内设备连接校内Ubuntu</h3>
<p>根据国内设备系统，执行对应SSH命令即可，核心格式：<code>ssh -p 外网端口 校内Ubuntu用户名@外网域名</code></p>
<h4>场景1：Windows（PowerShell）</h4>
<pre><code class="language-powershell"># 示例（替换为你的外网地址、端口和用户名）
ssh -p 27817 zoukai@外网地址
</code></pre>
<h4>场景2：Linux/macOS（终端）</h4>
<pre><code class="language-bash"># 命令与Windows一致
ssh -p 27817 zoukai@外网地址
</code></pre>
<h4>场景3：图形化工具（Xshell）</h4>
<ol>
<li>新建会话 → 【主机】填外网域名（外网地址）；</li>
<li>【端口号】填外网端口（27817）；</li>
<li>点击【连接】，输入校内Ubuntu用户名（如zoukai）和密码，即可成功连接。</li>
</ol>
<h2>问题排查</h2>
<h3>问题1：不知道校内Ubuntu的内网IP？</h3>
<p>若需查看内网IP，可执行：</p>
<pre><code class="language-bash"># 简化命令（直接输出内网IP）
hostname -I
# 或查看详细网卡信息
ip addr show
</code></pre>
<h3>问题2：花生壳客户端启动失败（提示&quot;Offline&quot;）</h3>
<p>解决：重启花生壳服务并设置开机自启</p>
<pre><code class="language-bash"># 重启服务
sudo /etc/init.d/phddns restart
# 设置开机自启
phddns enable
</code></pre>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[在远程服务器上使用 Codex：通过本地代理解决网络问题]]></title>
            <link>https://zhoukai.space/posts/codex-remote</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/codex-remote</guid>
            <pubDate>Sun, 18 Jan 2026 15:00:00 GMT</pubDate>
            <description><![CDATA[介绍如何在远程服务器上使用 Codex 插件，通过 SSH 端口转发和 VS Code 代理设置让其连接到本地代理]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>在远程服务器上使用 VS Code 的 Codex 插件时，网络连接常常是个问题。本文介绍如何通过 SSH 端口转发和 VS Code 代理配置，让运行在远程服务器上的 Codex 插件顺利访问本地代理。</p>
</blockquote>
<h2>问题背景</h2>
<p>在使用 VS Code Remote SSH 连接到远程服务器进行开发时，遇到了一个问题：<strong>Codex 插件无法访问外网</strong>。</p>
<h3>问题现象</h3>
<ul>
<li>Codex 插件安装在远程服务器上</li>
<li>插件运行在 Remote Extension Host 进程中</li>
<li>尝试使用 Codex 功能时，出现网络连接超时</li>
</ul>
<hr>
<h2>原理分析</h2>
<h3>Remote Extension Host 的网络机制</h3>
<p>关键点在于：<strong>Codex 插件运行在 Remote Extension Host，它使用的是 VS Code 提供的网络层，而不是 shell 的网络设置</strong>。</p>
<p>这意味着：</p>
<ul>
<li>❌ 在 shell 中设置 <code>http_proxy</code> 等环境变量无效</li>
<li>❌ 在 <code>.bashrc</code> 或 <code>.zshrc</code> 中配置代理不起作用</li>
<li>✅ 必须通过 VS Code 的代理配置来设置</li>
</ul>
<h3>解决思路</h3>
<p>既然 VS Code Remote Extension Host 会读取 VS Code 的代理设置，我们需要：</p>
<ol>
<li><strong>在本地和远程之间建立代理通道</strong>（SSH 端口转发）</li>
<li><strong>配置远程 VS Code 的代理设置</strong></li>
</ol>
<hr>
<h2>解决方案</h2>
<h3>配置 SSH 端口转发</h3>
<p>首先，在本地 SSH 配置文件中添加端口转发规则。编辑 <code>~/.ssh/config</code>：</p>
<pre><code class="language-text">Host 服务器
    HostName 巴拉巴拉巴拉
    User zhoukai
    Port 巴拉
    RemoteForward 自己选 127.0.0.1:自己选
</code></pre>
<p><strong>关键配置说明</strong>：</p>
<ul>
<li><code>RemoteForward xxx 127.0.0.1:yyy</code>：将远程服务器的 xxx 端口转发到本地的 yyy 端口</li>
<li>本地 yyy 端口运行的是你的代理服务（如 Clash、V2Ray 等）</li>
<li>这样远程服务器就可以通过 <code>127.0.0.1:xxx</code> 访问代理通道；该端口会被 SSH 转发到本地的 <code>127.0.0.1:yyy</code>。</li>
</ul>
<h3>配置远程 VS Code 代理设置</h3>
<p>连接到远程服务器后，编辑远程端的 VS Code 配置文件：</p>
<pre><code class="language-bash"># 编辑远程 VS Code 配置
vi ~/.vscode-server/data/Machine/settings.json
</code></pre>
<p>添加以下配置：</p>
<pre><code class="language-json">{
  &quot;http.proxy&quot;: &quot;http://127.0.0.1:xxx&quot;,
  &quot;http.proxySupport&quot;: &quot;override&quot;,
  &quot;http.proxyStrictSSL&quot;: false
}
</code></pre>
<p><strong>配置说明</strong>：</p>
<ul>
<li><code>&quot;http.proxy&quot;</code>：代理服务器地址，指向远程服务器上的 SSH 转发端口（xxx）</li>
<li><code>&quot;http.proxySupport&quot;: &quot;override&quot;</code>：覆盖系统代理设置，强制使用此代理</li>
<li><code>&quot;http.proxyStrictSSL&quot;: false</code>：关闭严格的 SSL 验证（根据需要设置）</li>
</ul>
<h3>重启 VS Code 连接</h3>
<p>配置完成后：</p>
<ol>
<li>断开当前的 VS Code Remote SSH 连接</li>
<li>重新连接到远程服务器</li>
<li>VS Code Remote Extension Host 会读取新的代理配置</li>
</ol>
<hr>
<h2>验证与测试</h2>
<h3>检查端口转发</h3>
<p>在远程服务器上，检查端口转发是否生效：</p>
<pre><code class="language-bash"># 检查 xxx 端口是否在监听
netstat -tuln | grep xxx
# 或
ss -tuln | grep xxx
</code></pre>
<p>应该看到类似 <code>127.0.0.1:xxx</code> 的监听记录。</p>
<h3>测试代理连接</h3>
<p>在远程服务器上测试代理是否可用：</p>
<pre><code class="language-bash">curl -x http://127.0.0.1:xxx https://www.google.com
</code></pre>
<p>如果能正常返回内容，说明代理通道工作正常。</p>
<h3>测试 Codex 插件</h3>
<p>在 VS Code Remote 中尝试使用 Codex 功能，验证是否能正常访问网络服务。</p>
<hr>
<h2>常见问题</h2>
<h3>Q1: 配置后仍然无法连接？</h3>
<p><strong>可能原因</strong>：</p>
<ul>
<li>本地代理服务未启动</li>
<li>SSH 端口转发未生效（检查 SSH 连接日志）</li>
<li>防火墙阻止了连接</li>
</ul>
<p><strong>解决方法</strong>：</p>
<ol>
<li>确保本地代理服务正常运行</li>
<li>重新建立 SSH 连接，查看是否有端口转发相关错误</li>
<li>检查远程服务器的防火墙设置</li>
</ol>
<h3>Q2: 代理速度很慢？</h3>
<p><strong>可能原因</strong>：</p>
<ul>
<li>本地代理服务器性能限制</li>
<li>网络延迟</li>
</ul>
<h3>Q3: SSL 证书错误？</h3>
<p>如果遇到 SSL 证书相关错误，可以：</p>
<ul>
<li>设置 <code>&quot;http.proxyStrictSSL&quot;: false</code></li>
<li>或者确保代理服务器的 SSL 证书配置正确</li>
</ul>
<hr>
<h2>总结</h2>
<ol>
<li><strong>理解机制</strong>：Remote Extension Host 使用 VS Code 的网络层，不读取 shell 代理设置</li>
<li><strong>端口转发</strong>：通过 SSH <code>RemoteForward</code> 建立本地代理到远程服务器的通道</li>
<li><strong>VS Code 配置</strong>：在远程服务器的 VS Code 配置中设置代理参数</li>
</ol>
<p>这种方法不仅适用于 Codex 插件，也适用于其他需要在远程服务器上访问网络的 VS Code 插件。</p>
<hr>
<p><strong>参考资源</strong>：</p>
<ul>
<li><a href="https://code.visualstudio.com/docs/remote/ssh">VS Code Remote SSH 文档</a></li>
<li><a href="https://www.ssh.com/academy/ssh/tunneling/example">SSH 端口转发详解</a></li>
</ul>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[科研入门与论文研究方法]]></title>
            <link>https://zhoukai.space/posts/sci</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/sci</guid>
            <pubDate>Thu, 13 Nov 2025 02:00:00 GMT</pubDate>
            <description><![CDATA[从文献阅读到论文发表的完整科研指南，涵盖baseline选择、创新点构建、学术评价体系等核心内容]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>完整科研流程与时间规划</h2>
<p>科学研究是一个系统工程，从选题到论文发表需要遵循一定的流程和时间规划。以下是一套结构化的科研流程框架，结合了从文献调研到实验验证的全链条环节：</p>
<h3>科研核心流程</h3>
<h4>（1）领域调研与聚焦（方向确定）</h4>
<ul>
<li><strong>广泛阅读文献</strong>：了解相关领域的整体概况、研究热点和主要分支</li>
<li><strong>聚焦子领域</strong>：选择一个感兴趣且有研究价值的具体子领域（如计算机视觉中的目标检测、自然语言处理中的机器翻译等）</li>
</ul>
<h4>（2）深入文献阅读与问题发现</h4>
<ul>
<li><strong>阅读最新进展</strong>：关注子领域近2-3年的顶会顶刊论文</li>
<li><strong>追溯历史工作</strong>：了解子领域的发展脉络和经典方法</li>
<li><strong>发现研究空白</strong>：
<ul>
<li>识别现有工作未关注到的新方向或未解决的公开问题</li>
<li>分析已有工作的局限性和不足（如假设条件过强、应用场景受限、性能瓶颈等）</li>
</ul>
</li>
</ul>
<h4>（3）科研想法生成与深化</h4>
<ul>
<li><strong>初步想法产生</strong>：基于文献阅读的启发，产生大概的科研想法</li>
<li><strong>针对性文献调研</strong>：围绕新想法的相关领域，阅读大量文献，寻找可借鉴的研究思路和方法</li>
</ul>
<h4>（4）研究计划制定与明确</h4>
<ul>
<li><strong>研究目标（What）</strong>：明确拟解决的具体问题或场景，或已有工作的哪点不足</li>
<li><strong>研究方法（How）</strong>：
<ul>
<li>拟借鉴的具体工作和核心方法</li>
<li>训练策略选择（有监督、无监督、自监督等）</li>
</ul>
</li>
<li><strong>研究意义（Why）</strong>：
<ul>
<li>核心创新点（决定文章档次的关键）</li>
<li>对后续研究的影响和价值</li>
</ul>
</li>
<li><strong>数据调研</strong>：确定是否有可用公开数据；若没有，制定数据收集和标注方案</li>
</ul>
<h4>（5）实验验证与迭代优化</h4>
<ul>
<li><strong>调研已有代码</strong>：寻找相关工作的开源代码，运行示例加深理解并验证不足</li>
<li><strong>基线实验设计</strong>：实现最直接的解决方案作为 Baseline（基线）</li>
<li><strong>评价体系建立</strong>：确定定量评价指标（Metric）和对比基准</li>
<li><strong>逐步迭代改进</strong>：
<ul>
<li>使用<strong>控制变量法</strong>，每次只进行一项改动</li>
<li>逐步添加新想法，提升基线结果</li>
<li>进行多算法、多角度的定量和定性对比</li>
</ul>
</li>
</ul>
<h4>（6）论文撰写与发表</h4>
<ul>
<li><strong>渐进式记录</strong>：在科研过程中（文献阅读、实验设计、结果分析等阶段）逐步记录内容</li>
<li><strong>结构化整理</strong>：将零散记录整理为规范的论文结构（引言、相关工作、方法、实验、结论等）</li>
</ul>
<h3>时间规划建议（总计6-9个月）</h3>
<ul>
<li><strong>领域调研与聚焦</strong>：1-2个月</li>
<li><strong>深入文献阅读与问题发现</strong>：1-2个月</li>
<li><strong>科研想法生成与计划制定</strong>：1-2个月</li>
<li><strong>实验验证与迭代优化</strong>：2-3个月</li>
<li><strong>论文撰写与发表准备</strong>：2个月</li>
</ul>
<hr>
<h2>确定研究方向后的首要任务：阅读文献</h2>
<h3>阅读目标</h3>
<p>当确定研究方向后，首先要解决的就是——如何阅读文献。阅读的目标不只是了解知识，而是为确定 baseline（基准模型）与创新点做准备。</p>
<h3>阅读策略</h3>
<ul>
<li>
<p><strong>精读</strong>：选择 2–3 篇最新的、开源的顶会文章（例如 CVPR、NeurIPS、ICLR、ACL 等）。这些文章之间最好有&quot;改进关系&quot;。</p>
<p>✅ 目标：确定一个合适的 baseline，并深入理解。</p>
</li>
<li>
<p><strong>泛读</strong>：对于同领域或相邻领域的其他顶会文章，只需关注：</p>
<ul>
<li>研究动机</li>
<li>创新点</li>
<li>研究切入角度</li>
</ul>
</li>
</ul>
<h3>阅读方式建议</h3>
<ul>
<li><strong>初期</strong>：从模块化角度去理解论文结构，暂时不要陷入复杂公式。</li>
<li><strong>熟悉整体框架后</strong>：再深入研究感兴趣模块的公式与细节。</li>
<li><strong>最终目标</strong>：形成对模型架构的深层理解，为后续创新设计提供灵感。</li>
</ul>
<hr>
<h2>如何选择合适的 Baseline</h2>
<p>选择 baseline 的三大原则：</p>
<h3>最新性</h3>
<p>选择近两年内发表的 CCF A 类会议论文。注意：CCF A 刊论文通常滞后，25 年发表的可能是 22 年的工作，不适合做改进基线。</p>
<h3>开源性</h3>
<p>必须开源。没有代码的论文，复现成本高、风险大，有时甚至是&quot;故事会&quot;。</p>
<h3>简洁性</h3>
<p>框架应尽量简单。如果 baseline 本身已经在前人基础上改进过，再叠加复杂模块会掩盖你自己的创新。</p>
<hr>
<h2>论文创新点的构建路径</h2>
<h3>基本流程</h3>
<ol>
<li>
<p>选取最新开源顶会论文作为 baseline，并跑通代码。</p>
</li>
<li>
<p>弄清 baseline 的整体架构和每个模块的功能。</p>
</li>
<li>
<p>在同领域的其他论文中寻找功能类似的模块，并进行替换或融合（即&quot;缝合&quot;）。</p>
</li>
<li>
<p>若修改后效果与 baseline 相当，即可撰写论文。</p>
<p>💡 <strong>写作技巧</strong>：不要直接表述&quot;替换操作&quot;，而应以&quot;针对现有问题提出改进思路&quot;的方式呈现。</p>
</li>
<li>
<p>若能结合特定领域特性做定制化改动，创新性更强。</p>
</li>
</ol>
<hr>
<h2>论文分区与学术评价体系</h2>
<h3>常见分区系统</h3>
<ul>
<li><strong>中科院分区</strong>：一区～四区（国内主流评价体系）</li>
<li><strong>JCR 分区</strong>：Q1～Q4（国际常用体系）</li>
<li><strong>CCF 分类</strong>：A、B、C 三类（计算机领域专用）</li>
</ul>
<h3>对比说明</h3>
<table>
<thead>
<tr>
<th>分类体系</th>
<th>含义</th>
<th>举例</th>
<th>含金量说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>中科院一区</td>
<td>顶级期刊</td>
<td>IEEE TPAMI</td>
<td>含金量普遍高</td>
</tr>
<tr>
<td>JCR Q1</td>
<td>前25%期刊</td>
<td>一些非顶刊也可能是Q1</td>
<td>需结合中科院分区判断</td>
</tr>
<tr>
<td>CCF A</td>
<td>顶会/顶刊</td>
<td>NeurIPS, CVPR</td>
<td>计算机最高级别</td>
</tr>
<tr>
<td>CCF B</td>
<td>次顶级会议</td>
<td>ECCV</td>
<td>实际认可度可能高于部分A类</td>
</tr>
</tbody>
</table>
<h3>特殊说明</h3>
<ul>
<li><strong>&quot;Trans&quot;</strong> 一般指 IEEE/ACM Transactions 级别论文。</li>
<li><strong>&quot;录用&quot; ≠ &quot;见刊&quot; ≠ &quot;检索&quot;</strong>。检索通常滞后见刊。</li>
</ul>
<hr>
<h2>科研成果不等于 SOTA（最优性能）</h2>
<h3>常见误区</h3>
<blockquote>
<p>&quot;我效果没超过 SOTA，是不是就发不了论文？&quot; —— <strong>并不是！</strong></p>
</blockquote>
<h3>正确认知</h3>
<ul>
<li>论文评审更关注<strong>创新思想和启发性</strong>，而不是性能最高。</li>
<li>只要在某些指标上优于部分最新方法，并提出有意义的新思路，就有潜力发顶会。</li>
<li>反之，单纯堆模型、融合方法提升性能的工作，即使结果最好，也可能被拒。</li>
</ul>
<hr>
<h2>模型无效时的自查清单</h2>
<h3>🔍 模型调试自查清单</h3>
<ol>
<li>
<p><strong>残差连接是否正确加入？</strong></p>
<ul>
<li>缺少残差可能导致性能下降。</li>
</ul>
</li>
<li>
<p><strong>增加模块的成功率高于替换模块。</strong></p>
<ul>
<li>增加参数往往更稳，但创新度较低。</li>
</ul>
</li>
<li>
<p><strong>模块放置位置是否合适？</strong></p>
<ul>
<li>同样接口处多尝试不同插入点。</li>
</ul>
</li>
<li>
<p><strong>超参数是否匹配？</strong></p>
<ul>
<li>调整 feature map 比例后再调整参数。</li>
</ul>
</li>
<li>
<p><strong>学习率与训练策略是否合理？</strong></p>
<ul>
<li>模型参数量大 → 使用 warm-up</li>
<li>增加训练轮次防止欠拟合</li>
</ul>
</li>
</ol>
<hr>
<h2>文献查找的高效方法</h2>
<h3>🔥 Embedding 相似度检索法（强烈推荐）</h3>
<ul>
<li>将论文标题、关键词、引言结尾段落等转为 embedding 向量</li>
<li>基于相似度进行检索</li>
<li>网上已有开源项目可直接使用</li>
</ul>
<h3>引用链法</h3>
<ul>
<li>找到权威学者的综述或代表作</li>
<li>深挖其参考文献和被引用文献</li>
<li>迭代式更新，持续追踪最新研究</li>
</ul>
<hr>
<h2>科研创新性自检清单</h2>
<h3>问题创新</h3>
<ul>
<li>你的工作是否在解决一个新的问题？</li>
<li>或者，为旧问题引入了新的应用场景？</li>
</ul>
<h3>方案创新</h3>
<ul>
<li>是否融合了其他领域的思想（物理模型、博弈论、生物演化等）？</li>
<li>是否从不同视角切入（如模型压缩、效率提升）？</li>
</ul>
<h3>扩展性与启发性</h3>
<ul>
<li>你的方法能否被他人复用、扩展？</li>
<li>是否能启发后续研究？</li>
</ul>
<hr>
<h2>判断&quot;创新&quot;与&quot;非创新&quot;的界限</h2>
<h3>✅ 创新：</h3>
<ul>
<li>不同动机下提出不同方案。</li>
<li>结合领域特性提出新思路。</li>
<li>能在原有框架上形成新理论或机制。</li>
</ul>
<h3>❌ 非创新：</h3>
<ul>
<li>模块参数微调（如 3×3 改 5×5 卷积）。</li>
<li>简单拼接两个已有模型。</li>
<li>批量可复制、缺乏独创性的组合。</li>
</ul>
<hr>
<h2>快速寻找研究思路的技巧</h2>
<h3>阅读高水平硕博论文（CNKI）</h3>
<ul>
<li>尤其是顶级高校、顶会/顶刊方向的学位论文</li>
<li>关注研究递进关系，学习选题逻辑</li>
</ul>
<h3>对比扩刊与原顶会的差异</h3>
<ul>
<li>期刊版往往在顶会基础上增加改进</li>
<li>虽创新不大，但积累小创新后足以发表三区论文</li>
</ul>
<hr>
<h2>📚 总结</h2>
<p>科研不是堆模型，而是发现问题、提出启发性思路。阅读与创新是科研的两条主线：</p>
<blockquote>
<p><strong>&quot;读顶会，选基线，找模块，提新意。&quot;</strong></p>
</blockquote>
<p>掌握这一套方法，你就能从入门到发文，高效构建自己的研究体系。</p>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[如何 Review 一篇文章？]]></title>
            <link>https://zhoukai.space/posts/how-to-review</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/how-to-review</guid>
            <pubDate>Wed, 29 Oct 2025 12:20:00 GMT</pubDate>
            <description><![CDATA[网上搜集到的一些文章 Review 方法的总结以及一些句式的参考]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>本文内容均来自于网上搜集到的各种文章及 Review 方法的总结以及一些句式的参考</p>
</blockquote>
<h2>审稿方法</h2>
<h3>审稿记录</h3>
<p><strong>建议的记录方法：</strong></p>
<ul>
<li>使用电子PDF版本直接做笔记</li>
<li>保持打开的文字处理文件，列出&quot;主要项目&quot;和&quot;次要项目&quot;</li>
<li>使用荧光笔和其他笔进行标记</li>
<li>在额外的纸上做笔记</li>
<li>建立个人检查清单，持续问自己问题</li>
</ul>
<h2>请务必包含在审稿中的内容</h2>
<h3>基本要求</h3>
<p>作为审稿的一部分，请确保清楚地证明您对审稿表上每个项目的评分的合理性。</p>
<p>您应该通过详细总结论文来开始评论。这将传达您从论文中理解的内容，可以向作者展示读者错过或解释与作者期望不同的方面。</p>
<p>您可以选择澄清您在多大程度上是（或不是）论文领域的专家（特别是如果审稿表上没有询问这一点）。这可以为您的评论提供一定程度的上下文。</p>
<h2>需要考虑的方面</h2>
<p>这是审阅提交时要查找内容的部分清单。这些项目应该有助于指导您的评估，并为作者形成有用的评论和建议。</p>
<h3>内容结构评价</h3>
<ul>
<li><strong>摘要</strong>：是总结论文、结果和贡献，还是主要激励工作？</li>
<li><strong>完整性</strong>：作者是否涵盖了引言中承诺的所有内容？</li>
<li><strong>动机</strong>：作者是否提供了足够的动机？</li>
<li><strong>背景</strong>：作者是否提供了足够的背景信息？</li>
</ul>
<h3>清晰度和表达</h3>
<ul>
<li><strong>描述清晰度</strong>：所有描述都清楚吗？</li>
<li><strong>图表质量</strong>：表格和数字是否清晰？它们本身才有意义，还是只有在仔细阅读文本的情况下才有意义？</li>
<li><strong>数量适中</strong>：图表是否太多？额外的表格或数字会有帮助吗？</li>
<li><strong>示例辅助</strong>：（正在运行的）场景或示例会有所帮助吗？</li>
</ul>
<h3>研究贡献</h3>
<ul>
<li><strong>贡献明确性</strong>：研究贡献是否明确？</li>
<li><strong>贡献重要性</strong>：贡献是否重大？</li>
<li><strong>方法清晰度</strong>：该方法是否解释清楚且布局良好？</li>
<li><strong>论证合理性</strong>：作者是否证明了所提出的每一个观点的合理性？</li>
</ul>
<h3>技术质量</h3>
<ul>
<li><strong>正确性</strong>：方程、算法、方法、实验和结论是否正确？</li>
<li><strong>稳健性</strong>：研究方法是否稳健？</li>
<li><strong>全面性</strong>：研究是否全面？</li>
<li><strong>合理性</strong>：研究是否合理？</li>
</ul>
<h3>文献和理论基础</h3>
<ul>
<li><strong>文献基础</strong>：研究是否以文献和适当的理论为基础？</li>
<li><strong>引文质量</strong>：作者是否使用重要、最新和充分的引文？</li>
<li><strong>引文平衡</strong>：引用太多了吗？有无关紧要或无关紧要的吗？</li>
<li><strong>自我引用</strong>：作者自己的作品是否被引用太多？</li>
<li><strong>引文建议</strong>：您能提出作者可能忽略的任何缺失引文吗？</li>
</ul>
<h3>局限性和结论</h3>
<ul>
<li><strong>局限性说明</strong>：作者是否表达了研究的局限性和作者的方法？</li>
<li><strong>分析完整性</strong>：作者是否进行了完整的分析并得出了富有洞察力的结论？</li>
<li><strong>未来规划</strong>：作者有没有描述他或她未来的研究计划？</li>
<li><strong>结论重要性</strong>：结论重要吗？这只是论文的翻版吗？</li>
<li><strong>新见解</strong>：它是否提供了新的综合或见解？</li>
<li><strong>影响力</strong>：它是否让读者对研究、研究领域或未来感到兴奋？</li>
</ul>
<h3>写作风格</h3>
<ul>
<li><strong>可读性</strong>：作者的写作风格如何？是不是太&quot;密&quot;了，说不通？</li>
<li><strong>兴趣保持</strong>：它能保持读者的兴趣吗？</li>
<li><strong>正式程度</strong>：是不是太非正式了？</li>
<li><strong>幽默运用</strong>：许多作者在研究论文中非常有效地使用幽默。只有当非正式或幽默妨碍时，才应该不鼓励。</li>
<li><strong>学科规范</strong>：某些学科确实强制执行非常正式的写作风格，在这种情况下，非正式的风格被认为是不合适的。</li>
</ul>
<h2>是否纠正语法和拼写</h2>
<p>校对包括检查正确的语法、正确的拼写以及总体上论文是否&quot;读得很好&quot;。如您所知，拼写检查器既不检查语法也不检查理解。</p>
<p>作者应该对审稿人和编辑有足够的尊重，以提交一篇经过彻底校对的论文。非母语作者有责任确保他们提交的质量符合母语人士提交的质量，即使他们必须花钱请人帮助编辑过程。</p>
<p>然而，作为审稿人，您经常会发现作者忽略的小拼写或语法错误。在所有这些情况下，由您决定编辑论文的程度：</p>
<ul>
<li>您可以决定更正前几页，或反复出现问题的前几个案例</li>
<li>如果论文需要进行重大修改，并且您知道更正后的草稿将再次进行审查，您可以建议作者在修改过程中进行此类校对</li>
</ul>
<h2>审稿技巧和模板</h2>
<h3>实用审稿技巧</h3>
<ol>
<li>
<p><strong>重点关注核心要素</strong></p>
<ul>
<li>论文写作质量</li>
<li>创新性</li>
<li>实验设计和结果</li>
<li>文献引用</li>
<li>方法的创新性和实验是最重要的两个部分</li>
</ul>
</li>
<li>
<p><strong>提升英语表达</strong></p>
<ul>
<li>平时记录地道的英文表达</li>
<li>边读边在PDF上标记觉得好和不好的地方以及原因</li>
<li>写完意见后，用语法检查工具进行校对，然后自己再检查一次确保评审意见没有语法错误</li>
</ul>
</li>
</ol>
<h3>审稿决定模板</h3>
<h4>A. 接受 (Accept)</h4>
<p><strong>直接接受：</strong></p>
<pre><code>The authors have made sufficient modifications according to the modification comments, and I suggest that this paper be accepted without further modification.
</code></pre>
<h4>B. 拒绝 (Reject)</h4>
<p><strong>标准拒绝模板：</strong></p>
<pre><code>This paper proposes XXX. The work of this paper is clear and logical. However, I have to reject it because of the following problems:

[文法错误] There are so many errors in the manuscript, such as, in page 1, ABSTRACT, XXX would be XXX.

[创新不够] The method of this paper is not innovative enough. In fact, most of the work is done by combining other people's methods. Authors need to highlight their innovative contributions.

[实验不足] Another obvious problem with this paper is the lack of sufficient experimentation to demonstrate the validity and applicability of the proposed method. Too few experiments and too many hyperparameters make the conclusion of this paper lack persuasive.
</code></pre>
<h4>C. 小修改/大修改 (Minor/Major Changes)</h4>
<p><strong>修改建议模板：</strong></p>
<pre><code>This paper proposes XXX. The work of this paper is practical and logical. However, there are some problems to be further improved as well:

[文法错误] There is at least one Spelling error in the manuscript, such as, in page 7, TABLE I, &quot;Stabalization&quot; would be &quot;Stabilization&quot;. Please check the manuscript carefully.

[创新不够] The innovations in this paper focus on novel applications, and the methods and theoretical innovations are not sufficient. The author must dig deeper into the innovation points before the article can be accepted by XXX.

[实验不足] Another obvious problem with this paper is the lack of enough experimentation to demonstrate the validity and applicability of the proposed method. The author needs to do more experiments with more angles and show them in this paper.

[文献不足] For the problem of using of video information in the field of transportation, so many methods are proposed, such as, XXX, and, XXX. Perhaps the author can find inspiration by reading more literature in this field to further optimize this paper.
</code></pre>
<h3>常见评审要点</h3>
<p><strong>文法错误：</strong></p>
<ul>
<li>检查拼写错误和语法错误</li>
<li>注意术语使用的一致性</li>
<li>确保表达清晰准确</li>
</ul>
<p><strong>创新性评估：</strong></p>
<ul>
<li>方法是否有足够的创新性</li>
<li>是否只是简单组合他人方法</li>
<li>作者是否突出了创新贡献</li>
</ul>
<p><strong>实验验证：</strong></p>
<ul>
<li>实验是否充分验证方法的有效性</li>
<li>实验设置是否合理</li>
<li>超参数是否过多影响结论说服力</li>
</ul>
<p><strong>文献综述：</strong></p>
<ul>
<li>是否涵盖了相关领域的重要工作</li>
<li>是否遗漏了关键的对比方法</li>
<li>引用是否准确和适当</li>
</ul>
<h2>参考资源和延伸阅读</h2>
<h3>官方审稿指南</h3>
<ul>
<li><strong>Science期刊同行评审指南</strong>：<a href="https://www.science.org/content/page/peer-review-science-publications">Peer Review at Science Publications</a></li>
<li><strong>学术审稿最佳实践</strong>：<a href="https://web.njit.edu/~bieber/review.html#samples">Peer Review Resources</a></li>
</ul>
<h2>学术投稿和审稿通信模板</h2>
<h3>投稿时的Cover Letter模板</h3>
<p><strong>模板1：标准格式</strong></p>
<pre><code>Here within enclosed is our paper for consideration to be published on &quot;(Journal name)&quot;. The further information about the paper is in the following:
The Title: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
The Authors: XXXXXXXXXX XXXXXXXXXXXX and XXXXXXXXXX
The authors claim that none of the material in the paper has been published or is under consideration for publication elsewhere.

I am the corresponding author and my address and other information is as follows:
Address: Department of XXXXXXXXX,
College of Chemistry and Enviromental Science, Henan Normal University
Xinxiang City, Henan Province, 453007,
P.R.China
E-mail: [邮箱地址]
Tel: +86-XXX-XXXXXXX
Fax: +86-XXX-XXXXXXX

Thank you very much for consideration!

Sincerely Yours,
Dr. XXX
</code></pre>
<p><strong>模板2：简洁格式</strong></p>
<pre><code>Dear Dr. [编辑姓名]:
I am sending a manuscript entitled &quot;论文标题&quot; by [作者姓名] which I should like to submit for possible publication in the journal of [期刊名称].

Yours sincerely
[您的姓名]
</code></pre>
<p><strong>模板3：针对性格式</strong></p>
<pre><code>Dear Dr. [编辑姓名]:
Enclosed is a manuscript entitled &quot;论文标题&quot; by [作者姓名], which we are submitting for publication in the journal of [期刊名称]. We have chosen this journal because it deals with [期刊涉及领域]. We believe that [论文内容] would be of interest to the journal's readers.
</code></pre>
<p><strong>模板4：完整性格式</strong></p>
<pre><code>Dear Dr. [编辑姓名]:
Please find enclosed for your review an original research article, &quot;论文标题&quot; by [作者姓名]. All authors have read and approve this version of the article, and due care has been taken to ensure the integrity of the work. No part of this paper has published or submitted elsewhere. No conflict of interest exits in the submission of this manuscript, and we have attached to this letter the signed letter granting us permission to use Figure 1 from another source.

We appreciate your consideration of our manuscript, and we look forward to receiving comments from the reviewers.
</code></pre>
<h3>询问稿件状态模板</h3>
<p><strong>询问是否收到稿件</strong></p>
<pre><code>Dear Editors,
We dispatched our manuscript to your journal on [投稿日期] but have not, as yet, receive acknowledgement of their safe arrival. We fear that may have been lost and should be grateful if you would let us know whether or not you have received them. If not, we will send our manuscript again. Thank you in advance for your help.

Sincerely,
[您的姓名]
</code></pre>
<p><strong>询问审稿进度</strong></p>
<pre><code>Dear Editors，
It is more than [等待时间，如12 weeks] since I submitted our manuscript (No: [稿件编号]) for possible publication in your journal. I have not yet received a reply and am wondering whether you have reached a decision. I should appreciated your letting me know what you have decided as soon as possible.

Sincerely,
[您的姓名]
</code></pre>
<h3>审稿意见常用表达</h3>
<p><strong>正面评价：</strong></p>
<ol>
<li>&quot;This is a carefully done study and the findings are of considerable interest. A few minor revision are list below.&quot;</li>
<li>&quot;This is a well-written paper containing interesting results which merit publication. For the benefit of the reader, however, a number of points need clarifying and certain statements require further justification.&quot;</li>
<li>&quot;Although this paper is good, it would be ever better if some extra data were added.&quot;</li>
</ol>
<p><strong>建议拒绝：</strong></p>
<ol>
<li>&quot;Although these observation are interesting, they are rather limited and do not advance our knowledge of the subject sufficiently to warrant publication in [期刊名称]. We suggest that the authors try submitting their findings to specialist journal such as [其他期刊]&quot;</li>
<li>&quot;This manuscript is not suitable for publication in the journal of [期刊名称] because the main observation it describe was reported 3 years ago in a reputable journal of [其他期刊].&quot;</li>
</ol>
<p><strong>语言问题：</strong></p>
<ol>
<li>&quot;Please ask someone familiar with English language to help you rewrite this paper. As you will see, I have made some correction at the beginning of the paper where some syntax is not satisfactory.&quot;</li>
<li>&quot;We feel that this potentially interesting study has been marred by an inability to communicate the finding correctly in English and should like to suggest that the authors seek the advice of someone with a good knowledge of English, preferable native speaker.&quot;</li>
<li>&quot;The wording and style of some section, particularly those concerning [具体技术], need careful editing. Attention should be paid to the wording of those parts of the Discussion of and Summary which have been underlined.&quot;</li>
</ol>
<p><strong>实验和方法问题：</strong></p>
<ol>
<li>&quot;Preliminary experiments only have been done and with exception of that summarized in Table 2, none has been repeated. This is clearly unsatisfactory, particularly when there is so much variation between assays.&quot;</li>
<li>&quot;The condition of incubation are poorly defined. What is the temperature? Were antibody used?&quot;</li>
</ol>
<h3>回复审稿意见模板</h3>
<p><strong>总体回应：</strong></p>
<ol>
<li>&quot;In reply to the referee's main criticism of paper, it is possible to say that [解释说明]&quot;</li>
<li>&quot;Thank you for your letter of [日期] and for the referee's comments concerning our manuscript entitled &quot;论文标题&quot;. We have studied their comments carefully and have made correction which we hope meet with their approval.&quot;</li>
<li>&quot;We are sending the revised manuscript according to the comments of the reviewers. Revised portion are underlined in red.&quot;</li>
</ol>
<p><strong>具体修改回应：</strong></p>
<ol>
<li>&quot;One minor point raised by the referee concerns of the extra composition of the reaction mixture in Figure 1. This has now been corrected. Further minor changes had been made on page 3, paragraph 1 (line 3-8) and 2 (line 6-11).&quot;</li>
<li>&quot;We deleted the relevant passage since they are not essential to the contents of the paper.&quot;</li>
<li>&quot;The running title has been changed to [新标题].&quot;</li>
<li>&quot;The Materials and Methods section now includes details for measuring uptake of isotope and assaying hexokinase.&quot;</li>
<li>&quot;The concentration of HAT media (page12 paragraph 2) was incorrectly stated in the original manuscript. This has been rectified. The authors are grateful to the referees for pointing out their error.&quot;</li>
</ol>
<p><strong>补充实验回应：</strong></p>
<ol>
<li>&quot;I enclosed a revised manuscript which includes a report of additional experiments done at the referee's suggestion. You will see that our original findings are confirmed.&quot;</li>
<li>&quot;We have therefore completed a further series of experiments, the result of which are summarized in Table 5. From this we conclude that intrinsic factor is not account.&quot;</li>
</ol>
<p><strong>感谢和结尾：</strong></p>
<ol>
<li>&quot;We found the referee's comments most helpful and have revised the manuscript.&quot;</li>
<li>&quot;We are pleased to note the favorable comments of reviewers in their opening sentence.&quot;</li>
<li>&quot;Thank you for your letter. I am very pleased to learn that our manuscript is acceptable for publication in [期刊名称] with minor revision.&quot;</li>
<li>&quot;We should like to thank the referees for their helpful comments and hope that we have now produced a more balance and better account of our work. We trust that the revised manuscript is acceptable for publication.&quot;</li>
<li>&quot;I greatly appreciate both your help and that of the referees concerning improvement to this paper. I hope that the revised manuscript is now suitable for publication.&quot;</li>
<li>&quot;I apologize for the delay in revising the manuscript. This was due to our doing an additional experiment, as suggested by referees.&quot;</li>
</ol>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[智海拾贝：AI科研进阶之路——2025人工智能学院科研经验交流分享会（笔记）]]></title>
            <link>https://zhoukai.space/posts/research-tips-sharing</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/research-tips-sharing</guid>
            <pubDate>Wed, 15 Oct 2025 02:00:00 GMT</pubDate>
            <description><![CDATA[如何系统地阅读论文、找研究方向与撰写科研论文]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>经验分享会：如何系统地阅读论文、找研究方向与撰写科研论文</p>
</blockquote>
<h2>论文阅读与学习路径</h2>
<ol>
<li>
<p><strong>从经典论文入手</strong></p>
<ul>
<li>比如 Transformer 等经典模型的原始论文，这是必须精读的内容。</li>
<li>这些论文构成了领域的基础，是理解后续研究的关键。</li>
</ul>
</li>
<li>
<p><strong>阅读综述性论文（Survey）</strong></p>
<ul>
<li>综述能帮助我们了解整个研究领域的全貌，包括：</li>
<li>主要研究方向和分类体系；</li>
<li>各类方法的代表性论文；</li>
<li>不同方法的比较与优缺点。</li>
<li>综述阅读可以快速建立系统性认识。</li>
</ul>
</li>
<li>
<p><strong>看新论文的&quot;相关工作&quot;部分</strong></p>
<ul>
<li>新论文的 Related Work 部分往往会提到领域内的经典方法与最新进展；</li>
<li>阅读这一部分能帮助你发现研究趋势和常用分类方式。</li>
</ul>
</li>
<li>
<p><strong>研究开源代码与 Baseline</strong></p>
<ul>
<li>找到论文对应的 baseline 实现；</li>
<li>阅读代码可以帮助理解模型细节，也方便你后续实验的复现与改进。</li>
</ul>
</li>
</ol>
<hr>
<h2>深入某个细分方向的方法</h2>
<ol>
<li>
<p><strong>关注顶会论文</strong></p>
<ul>
<li>例如 ACL 2025 等顶会的论文；</li>
<li>浏览标题与摘要，挑选你感兴趣的方向；</li>
<li>对于感兴趣的论文再深入阅读。</li>
<li>浏览所有论文标题并不耗时，但能帮助你快速了解当前研究热点。</li>
</ul>
</li>
<li>
<p><strong>阅读策略</strong></p>
<ul>
<li>并非所有论文都要精读；</li>
<li>大部分论文可快速浏览标题、摘要、引言、模型图与实验表格；</li>
<li>精读的论文则需要仔细分析其方法细节与叙事逻辑；</li>
<li>有价值的论文还可以尝试复现其代码，加深理解。</li>
</ul>
</li>
<li>
<p><strong>做好阅读笔记与总结</strong></p>
<ul>
<li>阅读时要记录关键信息：</li>
<li>论文标题、领域、会议、主要内容；</li>
<li>研究问题与核心方法；</li>
<li>你的评价与备注。</li>
<li>不做总结很容易遗忘，建议养成写笔记的习惯。</li>
</ul>
</li>
</ol>
<hr>
<h2>寻找研究 Idea 的方法</h2>
<ol>
<li>
<p><strong>从已有工作的不足出发</strong></p>
<ul>
<li>分析现有方法的局限性；</li>
<li>寻找失败样本、边界情况或未解决的问题；</li>
<li>这些往往能启发新的研究方向。</li>
</ul>
</li>
<li>
<p><strong>从相关领域借鉴思路</strong></p>
<ul>
<li>不要局限在自己当前的任务；</li>
<li>多阅读相邻领域的论文，看看他们的方法是否能迁移或改造。</li>
</ul>
</li>
<li>
<p><strong>与他人交流</strong></p>
<ul>
<li>与导师、同学多讨论；</li>
<li>但交流前要有自己的思考和初步想法，避免浪费时间；</li>
<li>讨论能帮助你发现盲点并完善思路。</li>
</ul>
</li>
</ol>
<hr>
<h2>实验阶段的要点</h2>
<ol>
<li>
<p><strong>主实验</strong></p>
<ul>
<li>确定主要实验设置与指标；</li>
<li>与前人保持一致便于对比。</li>
</ul>
</li>
<li>
<p><strong>补充实验</strong></p>
<ul>
<li>包括消融实验、参数敏感性分析、独特性验证等；</li>
<li>这些能强化论文的说服力。</li>
</ul>
</li>
<li>
<p><strong>问题分析</strong></p>
<ul>
<li>如果主实验结果不好，要及时分析原因；</li>
<li>快速调整方向，因为研究节奏快，竞争激烈。</li>
</ul>
</li>
</ol>
<hr>
<h2>论文写作建议</h2>
<ol>
<li>
<p><strong>Introduction</strong></p>
<ul>
<li>结合当前热点，讲清楚动机；</li>
<li>构建一个&quot;好故事&quot;，逻辑连贯、有吸引力；</li>
<li>实验结果之外，叙事质量非常重要。</li>
</ul>
</li>
<li>
<p><strong>方法与实验部分</strong></p>
<ul>
<li>方法部分要配好示意图，逻辑清晰；</li>
<li>实验部分不能只列结果，要有充分分析：</li>
<li>为什么结果好；</li>
<li>为什么在某些情况下表现差；</li>
<li>深层次原因是什么。</li>
</ul>
</li>
<li>
<p><strong>结构与衔接</strong></p>
<ul>
<li>段落之间的过渡要自然；</li>
<li>每段开头最好点明主题；</li>
<li>保证论文逻辑连贯、行文流畅。</li>
</ul>
</li>
<li>
<p><strong>反复修改</strong></p>
<ul>
<li>不断迭代；</li>
<li>注意语言流畅与图表的一致性；</li>
<li>最后确定标题与摘要。</li>
</ul>
</li>
</ol>
<hr>
<h2>选题与方向建议</h2>
<ol>
<li>
<p><strong>避免&quot;过热&quot;方向</strong></p>
<ul>
<li>太热门的数据集或任务竞争激烈；</li>
<li>即便结果好，也可能被他人迅速超越。</li>
</ul>
</li>
<li>
<p><strong>避免&quot;过冷&quot;方向</strong></p>
<ul>
<li>如果领域太冷门，投稿难、受众少；</li>
<li>尽量选择&quot;有热度但未饱和&quot;的方向。</li>
</ul>
</li>
<li>
<p><strong>做好调研与跟踪</strong></p>
<ul>
<li>确定 idea 后，要确保该方向近期没人做过；</li>
<li>持续关注最新论文，防止&quot;撞题&quot;。</li>
</ul>
</li>
<li>
<p><strong>重要数据与代码备份</strong></p>
<ul>
<li>本地、云端、服务器三份备份；</li>
<li>避免数据丢失的惨痛教训。</li>
</ul>
</li>
</ol>
<hr>
<h2>总结</h2>
<p>科研的核心在于：</p>
<ul>
<li><strong>系统阅读</strong>：从经典到综述再到新论文；</li>
<li><strong>持续总结</strong>：做好笔记与归纳；</li>
<li><strong>灵活思考</strong>：从缺陷与跨领域角度寻找 idea；</li>
<li><strong>高效执行</strong>：快速实验、反思、迭代；</li>
<li><strong>清晰表达</strong>：写出逻辑清楚、有故事感的论文。</li>
</ul>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[Linux 双系统安装]]></title>
            <link>https://zhoukai.space/posts/linux-ubuntu</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/linux-ubuntu</guid>
            <pubDate>Thu, 09 Oct 2025 02:00:00 GMT</pubDate>
            <description><![CDATA[安装 linux 双系统]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>确保电脑已安装 Windows 系统</p>
</blockquote>
<h2>确定系统引导格式</h2>
<p>首先需要确认电脑的引导格式是 UEFI + GPT 还是 BIOS + MBR。</p>
<p><strong>检查方法：</strong></p>
<ul>
<li>打开 Windows 系统</li>
<li>按下 <code>Win + R</code> 键</li>
<li>输入 <code>msinfo32</code> 并运行</li>
<li>查看 &quot;BIOS 模式&quot; 一项</li>
</ul>
<p><strong>结果判断：</strong></p>
<ul>
<li>如果显示 &quot;UEFI&quot;：请使用本教程（UEFI + GPT 格式）</li>
<li>如果显示 &quot;BIOS&quot;：建议查找其他适用于 BIOS + MBR 格式的教程</li>
</ul>
<h2>准备工作</h2>
<h3>下载 Ubuntu 系统镜像</h3>
<p>推荐从以下官方渠道下载：</p>
<ul>
<li><a href="https://cn.ubuntu.com/download">Ubuntu 官方下载页面</a></li>
<li><a href="https://mirrors.tuna.tsinghua.edu.cn/">清华大学开源镜像站</a></li>
</ul>
<p>建议下载 Ubuntu 24.04 桌面版本。(amd64,Desktop LiveDVD)</p>
<h3>下载并安装 UltraISO</h3>
<p>使用 UltraISO（软碟通）制作启动盘：</p>
<ul>
<li>下载 UltraISO 软件</li>
<li>安装并运行</li>
<li>插入 U 盘（建议 8GB 以上）</li>
<li>使用 UltraISO 将 Ubuntu 镜像写入 U 盘</li>
</ul>
<h3>磁盘分区规划</h3>
<p>为 Ubuntu 分配足够的磁盘空间：</p>
<p><strong>分区步骤：</strong></p>
<ol>
<li>右键点击&quot;此电脑&quot; → &quot;管理&quot; → &quot;磁盘管理&quot;</li>
<li>选择要压缩的卷（通常是 C 盘）</li>
<li>右键点击 → &quot;压缩卷&quot;</li>
<li>输入要压缩的空间大小</li>
<li>确认后会显示为&quot;未分配&quot;空间</li>
</ol>
<blockquote>
<p>如果以前安装过双系统，可能需要使用 EasyUEFI 或 DiskGenius 处理启动项。</p>
</blockquote>
<h3>系统设置</h3>
<p><strong>关闭 Windows 快速启动：</strong></p>
<ul>
<li>控制面板 → 电源选项 → &quot;选择电源按钮的功能&quot;</li>
<li>点击&quot;更改当前不可用的设置&quot;</li>
<li>取消勾选&quot;启用快速启动&quot;</li>
</ul>
<p><strong>关闭 BIOS 安全启动：</strong></p>
<ul>
<li>重启电脑进入 BIOS 设置</li>
<li>找到 Security Boot 选项并禁用</li>
<li>保存设置并重启</li>
</ul>
<blockquote>
<p>不同品牌主板的 BIOS 设置可能略有差异，请根据具体主板型号进行调整。</p>
</blockquote>
<h2>安装 Ubuntu 系统</h2>
<h3>从 U 盘启动</h3>
<ol>
<li>将制作好的 Ubuntu 启动盘插入电脑</li>
<li>重启电脑并进入 BIOS/UEFI 设置</li>
<li>设置 U 盘为第一启动项</li>
<li>保存设置并重启</li>
</ol>
<h3>Ubuntu 安装过程</h3>
<ol>
<li>选择 &quot;Install Ubuntu&quot; 开始安装</li>
<li>选择语言（推荐选择中文简体）</li>
<li>选择安装类型：
<ul>
<li><strong>选择&quot;其他选项&quot;进行手动分区</strong></li>
<li>不要选择&quot;与 Windows 共存&quot;或&quot;抹除整个磁盘&quot;</li>
</ul>
</li>
</ol>
<h3>磁盘分区设置</h3>
<p>在之前预留的未分配空间上创建以下分区：</p>
<p><strong>推荐分区方案：</strong></p>
<ul>
<li><code>/boot</code> 分区：512MB - 1GB（EFI 分区）</li>
<li><code>swap</code> 交换空间：根据内存大小确定，看下表</li>
<li><code>/</code> 根分区：至少 20GB</li>
<li><code>/home</code> 分区：剩余空间（用于存储个人文件）</li>
</ul>
<p><strong>Swap 大小建议：</strong></p>
<table>
<thead>
<tr>
<th>物理内存</th>
<th>不需要休眠</th>
<th>需要休眠</th>
<th>最大值</th>
</tr>
</thead>
<tbody>
<tr>
<td>4GB</td>
<td>2GB</td>
<td>6GB</td>
<td>8GB</td>
</tr>
<tr>
<td>8GB</td>
<td>3GB</td>
<td>11GB</td>
<td>16GB</td>
</tr>
<tr>
<td>16GB</td>
<td>4GB</td>
<td>20GB</td>
<td>32GB</td>
</tr>
<tr>
<td>32GB</td>
<td>6GB</td>
<td>38GB</td>
<td>64GB</td>
</tr>
</tbody>
</table>
<h3>完成安装</h3>
<ol>
<li>设置用户名和密码</li>
<li>等待安装过程完成</li>
<li>重启系统</li>
</ol>
<h2>常见问题解决</h2>
<h3>重启后无法进入 Ubuntu（自动进入 Windows）</h3>
<p>这是最常见的问题，解决方法如下：</p>
<p><strong>步骤 1：使用 Boot Repair 工具</strong></p>
<pre><code class="language-bash">sudo add-apt-repository ppa:yannubuntu/boot-repair
sudo apt-get update
sudo apt-get install -y boot-repair &amp;&amp; boot-repair
</code></pre>
<p><strong>步骤 2：在 Windows 中修复引导</strong><br>
以管理员权限运行 cmd，执行：</p>
<pre><code class="language-cmd">bcdedit /set {bootmgr} path \EFI\ubuntu\shimx64.efi
</code></pre>
<p><strong>步骤 3：重启验证</strong><br>
重启后应该能看到包含 Ubuntu 和 Windows 选项的 GRUB 菜单。</p>
<h3>双系统时间不一致问题</h3>
<p>在 Ubuntu 终端中运行以下命令并重启：</p>
<pre><code class="language-bash">timedatectl set-local-rtc 1 --adjust-system-clock
</code></pre>
<h3>安装 ubuntu 时黑屏</h3>
<ol>
<li>显卡驱动问题，更换 ubuntu 版本，例如 40 系显卡不要使用 22.04，更换 24.04</li>
<li>也可以在 grub 页面选择第二个安装选项（除 try or install ubuntu）或者 按 e 添加参数</li>
</ol>
<h2>其余个人配置记录</h2>
<h3>必要资源</h3>
<ul>
<li>主题（<a href="https://github.com/vinceliuice/WhiteSur-gtk-theme">theme</a>）</li>
<li>图标（<a href="https://github.com/vinceliuice/WhiteSur-icon-theme">icon</a>）</li>
<li>指针（<a href="https://github.com/vinceliuice/McMojave-cursors">cursor</a>）</li>
<li>壁纸（<a href="https://github.com/vinceliuice/WhiteSur-wallpapers">wallpaper</a>）</li>
<li>GRUB 启动页面</li>
<li>ulauncher</li>
<li>下载安装 nvidia 显卡驱动</li>
</ul>
<h3>必要插件</h3>
<ul>
<li>blur my shell</li>
<li>user themes</li>
<li>arc menu</li>
<li>removable drive menu</li>
<li>just perfect</li>
<li>tweaks</li>
<li>compiz alike magic lamp effect</li>
<li>workspace indicator</li>
<li>coverflow alt-tab</li>
<li>clipboard indicator</li>
</ul>
<h3>命令行输入</h3>
<ul>
<li>打开dock 栏图标（点击隐藏）功能</li>
</ul>
<pre><code class="language-bash">gsettings set org.gnome.shell.extensions.dash-to-dock click-action 'minimize'
</code></pre>
<blockquote>
<p>grub 默认启动项修改/etc/default/grub<br>
修改后必须要 sudo grub-update</p>
</blockquote>
<blockquote>
<p><a href="http://47.76.135.200/api/v1/client/subscribe?token=57ce18339271c390eb57ce3af52ec938">http://47.76.135.200/api/v1/client/subscribe?token=57ce18339271c390eb57ce3af52ec938</a></p>
</blockquote>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[SSH 连接 WSL 与 Mac]]></title>
            <link>https://zhoukai.space/posts/wsl2mac</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/wsl2mac</guid>
            <pubDate>Sun, 20 Jul 2025 02:00:00 GMT</pubDate>
            <description><![CDATA[使用 ssh 连接 wsl 与 mac 系统，使得能够在 mac 上连接并训练模型]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>1 <strong>同一局域网下</strong></h2>
<blockquote>
<p>同一局域网内从 Mac 远程连接到 Windows 上的 WSL2（Windows Subsystem for Linux）环境</p>
</blockquote>
<h3>WSL2 中配置 SSH 服务</h3>
<h4>修改apt清华镜像</h4>
<blockquote>
<p>修改为清华镜像方便接下来的安装过程，如果有🪜可以忽略此步</p>
</blockquote>
<pre><code class="language-bash">sudo sed -i &quot;s@http://.*archive.ubuntu.com@https://mirrors.tuna.tsinghua.edu.cn@g&quot; /etc/apt/sources.list
sudo sed -i &quot;s@http://.*security.ubuntu.com@https://mirrors.tuna.tsinghua.edu.cn@g&quot; /etc/apt/sources.list
sudo apt update &amp;&amp; sudo apt upgrade -y
</code></pre>
<h4>安装 OpenSSH Server</h4>
<p>在 WSL2 中打开终端，执行以下命令：</p>
<pre><code class="language-bash">sudo apt update
sudo apt install openssh-server
</code></pre>
<h4>配置 SSH 服务</h4>
<p>编辑 SSH 配置文件：</p>
<pre><code class="language-bash">sudo vim /etc/ssh/sshd_config
</code></pre>
<p>确保以下行存在且未被注释（即没有 # 号）：</p>
<pre><code class="language-bash">Port 2222
AddressFamily any
ListenAddress 0.0.0.0
PermitRootLogin yes
PasswordAuthentication yes
PubkeyAuthentication yes
</code></pre>
<h4>启动 SSH 服务</h4>
<p>执行以下命令启动 SSH 服务：</p>
<pre><code class="language-bash">sudo service ssh start
</code></pre>
<h4>配置开机自启</h4>
<blockquote>
<p>在每次启动 WSL 时自动启动 SSH 服务，需要在 WSL 的配置文件中添加启动命令。</p>
</blockquote>
<h3>Windows 上设置 SSH 与端口转发</h3>
<blockquote>
<p>由于 WSL2 使用虚拟网络，默认情况下无法直接从局域网访问其服务。因此，需要在 Windows 上设置端口转发，将外部请求转发到 WSL2 的 SSH 服务。</p>
</blockquote>
<h4>端口转发</h4>
<ul>
<li>获取 WSL2 的 IP 地址：</li>
</ul>
<p>在 <strong>PowerShell</strong> 中执行以下命令：</p>
<pre><code class="language-powershell">wsl hostname -I
</code></pre>
<blockquote>
<p>记录输出的 IP 地址，假设为 120.120.120.120。</p>
</blockquote>
<ul>
<li>设置内部端口转发：</li>
</ul>
<pre><code class="language-powershell">netsh interface portproxy reset

netsh interface portproxy add v4tov4 listenport=2222 listenaddress=0.0.0.0 connectport=2222 connectaddress=120.120.120.120
</code></pre>
<h3>在 Windows 上安装 OpenSSH server</h3>
<p>通过 PowerShell 安装（管理员权限）：</p>
<pre><code class="language-powershell"># 检查是否已安装 OpenSSH 客户端和服务器
Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'

# 安装 OpenSSH 服务器
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
</code></pre>
<pre><code class="language-powershell"># 如果失败，可以改用 DISM 命令
dism /online /Add-Capability /CapabilityName:OpenSSH.Server~~~~0.0.1.0
</code></pre>
<h3>启动并配置 SSH 服务</h3>
<pre><code class="language-powershell"># 启动 SSH 服务
Start-Service sshd

# 设置 SSH 服务开机自启
Set-Service -Name sshd -StartupType 'Automatic'

# 检查服务状态
Get-Service sshd
</code></pre>
<h3>配置防火墙（允许 SSH 端口 2222）</h3>
<pre><code class="language-powershell"># 允许 2222 端口
New-NetFirewallRule -Name &quot;OpenSSH-Server&quot; -DisplayName &quot;OpenSSH Server (TCP 2222)&quot; -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 2222
</code></pre>
<h3>测试 SSH 连接</h3>
<ul>
<li>获取 Windows 主机的局域网 IP 地址：</li>
</ul>
<pre><code class="language-powershell">ipconfig
</code></pre>
<blockquote>
<p>查找与当前连接的网络适配器对应的 IPv4 地址，假设为 192.168.1.100。</p>
</blockquote>
<ul>
<li>在 Mac 上使用 SSH 连接：</li>
</ul>
<pre><code class="language-bash">ssh &lt;wsl_username&gt;@192.168.1.100 -p 2222
</code></pre>
<blockquote>
<p>将 &lt;wsl_username&gt; 替换为 WSL2 中的用户名。如果配置正确，应该能够成功连接到 WSL2 的终端</p>
</blockquote>
<h2>2 <strong>非同一局域网</strong></h2>
<h3>Windows 安装</h3>
<pre><code class="language-powershell">winget install tailscale
</code></pre>
<h3>macOS 安装</h3>
<pre><code class="language-bash">brew install tailscale
</code></pre>
<blockquote>
<p>Tailscale 上显示的 IP 地址即远程登录地址，其余配置方法与同一局域网内的方法相同。</p>
</blockquote>
<h2>⚠️ 免密登陆</h2>
<pre><code class="language-bash"># 生成密钥（如果还没有）
ssh-keygen -t ed25519

# 复制公钥到WSL
ssh-copy-id -p 2222 zoukai@IP
</code></pre>
<h2>📨 自动脚本</h2>
<pre><code class="language-bat">@echo off
:: 启动WSL SSH服务
wsl -d Ubuntu -u root /etc/init.d/ssh start

:: 获取WSL2动态IP并设置端口转发
for /f &quot;tokens=*&quot; %%i in ('wsl hostname -I') do (
    netsh interface portproxy reset
    netsh interface portproxy add v4tov4 listenport=2222 listenaddress=0.0.0.0 connectport=2222 connectaddress=%%i
)

:: 放行防火墙（幂等操作）
powershell -command &quot;New-NetFirewallRule -Name 'WSL2_SSH' -DisplayName 'WSL2 SSH' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 2222 -ErrorAction SilentlyContinue&quot;

echo 已启动SSH并设置端口转发，WSL IP: %errorlevel%
pause
</code></pre>
<h2>3 <strong>SSH 代理命令</strong></h2>
<h3>代理实例中的端口到本地</h3>
<p>具体步骤为：</p>
<p><strong>Step.1</strong> 在实例中启动您的服务（比如您的服务监听6006端口，下面以6006端口为例）</p>
<p><strong>Step.2</strong> 在本地电脑的终端(cmd / powershell / terminal等)中执行代理命令：</p>
<pre><code class="language-bash">ssh -CNg -L 6006:127.0.0.1:6006 root@123.125.240.150 -p 42151
</code></pre>
<p>其中 <code>root@123.125.240.150</code> 和 <code>42151</code> 分别是实例中SSH指令的访问地址与端口，请找到自己实例的ssh指令做相应替换。<code>6006:127.0.0.1:6006</code> 是指代理实例内6006端口到本地的6006端口。</p>
<p>注意：执行完这条ssh命令，没有任何日志是正常的，只要没有要求重新输入密码或错误退出。</p>
<p>Windows下的cmd/powershell如果一直提示密码错误，是因为无法粘贴，手动输入即可（正常不会显示正在输入的密码）。</p>
<p><strong>Step.3</strong> 在本地浏览器中访问 <code>http://127.0.0.1:6006</code> 即可打开服务，注意这里的6006端口要和上述 <code>6006:127.0.0.1:6006</code> 中的端口保持一致。</p>
<h3>代理本地端口到实例</h3>
<pre><code class="language-bash"># 上面代理实例中的端口到本地的Step.2中的命令：
ssh -CNg -L 6006:127.0.0.1:6006 root@123.125.240.150 -p 42151

# 只需将上面的命令修改参数-L为-R即代理本地端口到实例
ssh -CNg -R 6006:127.0.0.1:6006 root@123.125.240.150 -p 42151
</code></pre>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[Docker for AI 教程]]></title>
            <link>https://zhoukai.space/posts/docker-for-ai</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/docker-for-ai</guid>
            <pubDate>Tue, 01 Jul 2025 02:00:00 GMT</pubDate>
            <description><![CDATA[本文为 Docker 的初学者教程]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>前言</h2>
<p>随着云原生、AI 等技术的向前推进，容器技术逐渐成为 AI 领域的必备技能之一。本文档从零基础实现将代码打包 Docker 镜像、调试、提交仓库、提交云服务训练模型等流程。同样适用于初次接触 Docker 的初学者。</p>
<p>区别于开发，对于算法而言，仅需要掌握一部分基础命令达到自己的使用目的即可。因此此次简明教程面向算法和 AI 领域，帮助快速上手项目提交和远程服务器训练。</p>
<h2>基本概念</h2>
<blockquote>
<p>Docker 最重要的三个概念是：镜像（Image）、容器（Container）、仓库（Repository）。在这三个概念中，镜像是最重要的概念。</p>
</blockquote>
<h2>常用命令</h2>
<h3>拉取镜像</h3>
<pre><code class="language-bash">docker pull [选项] [Docker 镜像地址:标签]
</code></pre>
<p>例如：</p>
<pre><code class="language-bash">docker pull hello-world:latest
</code></pre>
<h3>运行镜像并创建容器</h3>
<pre><code class="language-bash">docker run hello-world
</code></pre>
<h3>运行镜像并进入容器</h3>
<pre><code class="language-bash">docker run -it --rm ubuntu:18.04
</code></pre>
<blockquote>
<p><strong>参数说明：</strong></p>
<ul>
<li><code>-it</code>：这是两个参数，一个是 <code>-i</code>（交互式操作），一个是 <code>-t</code>（终端）。我们这里打算进入 bash 执行一些命令并查看返回结果，因此我们需要交互式终端。</li>
<li><code>--rm</code>：这个参数是说容器退出后随之将其删除。默认情况下，为了排障需求，退出的容器并不会立即删除，除非手动 <code>docker rm</code>。我们这里只是随便执行个命令，看看结果，不需要排障和保留结果，因此使用 <code>--rm</code> 可以避免浪费空间。</li>
</ul>
</blockquote>
<h3>查看本地镜像</h3>
<pre><code class="language-bash">docker images
</code></pre>
<h3>查看运行中的容器</h3>
<pre><code class="language-bash">docker ps
</code></pre>
<h3>查看所有容器</h3>
<pre><code class="language-bash">docker ps -a
</code></pre>
<h3>进入运行中/后台运行的容器</h3>
<pre><code class="language-bash">docker exec -it [CONTAINER ID] /bin/bash
</code></pre>
<h3>保存修改</h3>
<pre><code class="language-bash">docker commit [CONTAINER ID] registry.cn-shanghai.aliyuncs.com/test/pytorch:myversion
</code></pre>
<blockquote>
<p><strong>注意：</strong> 通过 commit 的形式保存为一个新的镜像虽然也能直观地达到构建新镜像的目的，但是实际操作中，并不推荐这种形式。因为 commit 操作不仅会把有用的修改保存下来，对一些无关的修改也会保存下来（每一个命令行操作都会生成存储，如 <code>ls</code> 操作），就会导致镜像比较臃肿。其次，commit 操作属于黑箱操作，后续如果有什么问题维护起来会比较麻烦。建议 commit 仅作为保留现场的手段，然后通过修改 Dockerfile 构建镜像。</p>
</blockquote>
<h3>打 TAG</h3>
<p>有时需要对临时版本或者节点版本做一个标记保留，打 TAG 标签非常好用，并不会额外占用空间：</p>
<pre><code class="language-bash">docker tag registry.cn-shanghai.aliyuncs.com/test/pytorch:myversion my_tmp_version:0.1
</code></pre>
<h3>推送镜像到仓库</h3>
<pre><code class="language-bash">docker push registry.cn-shanghai.aliyuncs.com/test/pytorch:myversion
</code></pre>
<h3>使用 Dockerfile 构建镜像</h3>
<p><strong>Dockerfile 示例：</strong></p>
<blockquote>
<p><strong>注意：</strong> 一般文件名命名为 <code>Dockerfile</code>（无后缀名），如果命名为其他名字，构建时需要额外指定文件名。</p>
</blockquote>
<pre><code class="language-dockerfile">FROM nvidia/cuda:11.8.0-devel-ubuntu22.04

ARG DEBIAN_FRONTEND=noninteractive

RUN apt-get update &amp;&amp; apt-get install -y \
    git \
    curl \
    software-properties-common \
    &amp;&amp; add-apt-repository ppa:deadsnakes/ppa \
    &amp;&amp; apt install -y python3.10 \
    &amp;&amp; rm -rf /var/lib/apt/lists/*

WORKDIR /workspace

COPY requirements.txt requirements.txt

RUN curl -sS https://bootstrap.pypa.io/get-pip.py | python3.10 \
    &amp;&amp; python3.10 -m pip install -r requirements.txt \
    &amp;&amp; python3.10 -m pip install numpy --pre torch --force-reinstall --index-url https://download.pytorch.org/whl/nightly/cu118

COPY . .

ENTRYPOINT [ &quot;python3.10&quot; ]
</code></pre>
<h3>构建镜像</h3>
<pre><code class="language-bash">docker build -t registry.cn-shanghai.aliyuncs.com/target:test .
</code></pre>
<p>如要指定 Dockerfile：</p>
<pre><code class="language-bash">docker build -f ./Dockerfile -t registry.cn-shanghai.aliyuncs.com/target:test .
</code></pre>
<h3>删除镜像/容器</h3>
<p><strong>删除镜像：</strong></p>
<pre><code class="language-bash">docker rmi registry.cn-shanghai.aliyuncs.com/target:test
</code></pre>
<p><strong>删除容器：</strong></p>
<pre><code class="language-bash">docker rm [CONTAINER ID]
</code></pre>
<p>如果容器还在运行，则会删除失败，应先结束掉容器：</p>
<pre><code class="language-bash">docker kill [CONTAINER ID]
</code></pre>
<h2>常规技巧</h2>
<ul>
<li>
<p><strong>检查基础镜像软件源和 pip 源是否替换为国内源</strong>：如果非国内源，后续每次构建镜像会比较浪费时间。</p>
</li>
<li>
<p><strong>必备软件包可直接安装于基础镜像内</strong>：以减少每次构建镜像时都要安装一遍的等待时间。</p>
</li>
<li>
<p><strong>镜像面临调试问题时</strong>：可交互式进入容器后直接调试修改，直到成功后退出再在 Dockerfile 中修改。</p>
</li>
<li>
<p><strong>养成使用 Dockerfile 的习惯</strong>：不要依赖于 commit。</p>
</li>
<li>
<p><strong>每次镜像修改都给定新的版本号或标签</strong>：方便区分版本管理，有意义的版本最好使用有含义的字符作为版本号，如：<code>first_submit</code>。</p>
</li>
</ul>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[Linux 命令指南大全]]></title>
            <link>https://zhoukai.space/posts/linux-order</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/linux-order</guid>
            <pubDate>Thu, 26 Jun 2025 02:00:00 GMT</pubDate>
            <description><![CDATA[Linux命令指南，涵盖文件操作、进程管理、网络工具、开发环境配置等实用技能]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>本文整理了从基础到进阶的Linux命令，用于查漏补缺。</p>
</blockquote>
<h2>基础文件操作</h2>
<h3>文件浏览与导航</h3>
<pre><code class="language-bash"># 查看当前目录
pwd

# 列出文件（详细格式）
ls -la

# 查看文件大小（人类可读格式）
ls -lh

# 按时间排序
ls -lt

# 按大小排序
ls -lS

# 递归查看目录结构
tree
# 如果没有tree命令，使用：
find . -type d | sed -e &quot;s/[^-][^\/]*\//  |/g&quot; -e &quot;s/|\([^ ]\)/|-\1/&quot;
</code></pre>
<h3>文件操作</h3>
<pre><code class="language-bash"># 复制文件
cp source_file destination_file

# 复制目录
cp -r source_dir destination_dir

# 移动/重命名文件
mv old_name new_name

# 删除文件
rm filename

# 删除目录
rm -rf directory_name

# 创建目录
mkdir -p path/to/new/directory

# 查看文件内容
cat filename
less filename
head -n 10 filename  # 查看前10行
tail -n 10 filename  # 查看后10行
tail -f filename     # 实时查看文件变化
</code></pre>
<h3>文件搜索</h3>
<pre><code class="language-bash"># 查找文件
find /path/to/search -name &quot;*.py&quot;

# 查找包含特定内容的文件
grep -r &quot;search_term&quot; /path/to/search

# 忽略大小写搜索
grep -ri &quot;search_term&quot; /path/to/search

# 显示行号
grep -n &quot;search_term&quot; filename

# 使用正则表达式
grep -E &quot;pattern&quot; filename
</code></pre>
<h2>文本处理与分析</h2>
<h3>文本处理工具</h3>
<pre><code class="language-bash"># 统计文件行数、单词数、字符数
wc filename

# 排序文件
sort filename
sort -n filename  # 数值排序
sort -r filename  # 逆序排序

# 去重
uniq filename
sort filename | uniq

# 提取特定列
cut -d',' -f1,3 filename  # 提取第1和第3列（逗号分隔）
awk '{print $1, $3}' filename  # 提取第1和第3列（空格分隔）

# 文本替换
sed 's/old_text/new_text/g' filename
sed -i 's/old_text/new_text/g' filename  # 直接修改文件
</code></pre>
<h3>数据分析常用命令</h3>
<pre><code class="language-bash"># 查看文件前几行
head -20 large_file.csv

# 查看文件后几行
tail -20 large_file.csv

# 统计文件大小
du -h filename
du -sh directory_name

# 查看文件类型
file filename

# 查看文件编码
file -i filename
</code></pre>
<h2>进程管理与监控</h2>
<h3>进程查看</h3>
<pre><code class="language-bash"># 查看所有进程
ps aux

# 查看特定进程
ps aux | grep process_name

# 实时查看进程
top
htop  # 更友好的界面

# 查看进程树
pstree
pstree -p  # 显示进程ID
</code></pre>
<h3>进程控制</h3>
<pre><code class="language-bash"># 杀死进程
kill process_id
kill -9 process_id  # 强制杀死

# 根据进程名杀死
pkill process_name
pkill -f &quot;python script.py&quot;

# 暂停进程
kill -STOP process_id

# 恢复进程
kill -CONT process_id
</code></pre>
<h3>后台运行</h3>
<pre><code class="language-bash"># 后台运行命令
nohup command &gt; output.log 2&gt;&amp;1 &amp;

# 查看后台任务
jobs

# 将后台任务调到前台
fg %job_number

# 继续后台运行
bg %job_number
</code></pre>
<h2>网络工具</h2>
<h3>网络连接测试</h3>
<pre><code class="language-bash"># 测试网络连通性
ping google.com

# 查看网络路由
traceroute google.com

# 查看网络接口
ifconfig
ip addr

# 查看网络连接
netstat -tuln
ss -tuln
</code></pre>
<h3>文件传输</h3>
<pre><code class="language-bash"># 下载文件
wget url
curl -O url

# 安全复制文件
scp local_file user@remote_host:/path/to/destination
scp -r local_dir user@remote_host:/path/to/destination

# 同步文件
rsync -avz source/ destination/
rsync -avz --delete source/ destination/  # 删除目标中源没有的文件
</code></pre>
<h2>开发环境配置</h2>
<h3>包管理</h3>
<pre><code class="language-bash"># Ubuntu/Debian
sudo apt update
sudo apt install package_name
sudo apt remove package_name

# CentOS/RHEL
sudo yum install package_name
sudo yum remove package_name

# 查看已安装的包
dpkg -l | grep package_name
rpm -qa | grep package_name
</code></pre>
<h3>Python环境管理</h3>
<pre><code class="language-bash"># 创建虚拟环境
python -m venv myenv
python3 -m venv myenv

# 激活虚拟环境
source myenv/bin/activate

# 退出虚拟环境
deactivate

# 安装包
pip install package_name
pip install -r requirements.txt

# 查看已安装的包
pip list
pip freeze &gt; requirements.txt
</code></pre>
<h3>Git版本控制</h3>
<pre><code class="language-bash"># 初始化仓库
git init

# 克隆仓库
git clone repository_url

# 查看状态
git status

# 添加文件
git add filename
git add .

# 提交更改
git commit -m &quot;commit message&quot;

# 查看提交历史
git log
git log --oneline

# 切换分支
git checkout branch_name
git checkout -b new_branch_name

# 拉取更新
git pull origin branch_name

# 推送更改
git push origin branch_name
</code></pre>
<h2>AI开发相关工具</h2>
<h3>GPU监控</h3>
<pre><code class="language-bash"># 查看GPU使用情况
nvidia-smi

# 实时监控GPU
watch -n 1 nvidia-smi

# 查看GPU进程
nvidia-smi pmon

# 查看GPU内存使用
nvidia-smi --query-gpu=memory.used,memory.total --format=csv
</code></pre>
<h3>容器管理</h3>
<pre><code class="language-bash"># 查看运行中的容器
docker ps

# 查看所有容器
docker ps -a

# 运行容器
docker run -it image_name

# 停止容器
docker stop container_id

# 删除容器
docker rm container_id

# 查看镜像
docker images

# 构建镜像
docker build -t image_name .
</code></pre>
<h3>环境变量管理</h3>
<pre><code class="language-bash"># 设置环境变量
export VARIABLE_NAME=value

# 查看环境变量
echo $VARIABLE_NAME
env | grep VARIABLE_NAME

# 永久设置环境变量
echo 'export VARIABLE_NAME=value' &gt;&gt; ~/.bashrc
source ~/.bashrc
</code></pre>
<h2>系统监控与性能分析</h2>
<h3>系统资源监控</h3>
<pre><code class="language-bash"># 查看系统负载
uptime

# 查看内存使用
free -h

# 查看磁盘使用
df -h

# 查看磁盘IO
iostat

# 查看CPU使用率
mpstat

# 系统性能监控
sar -u 1 5  # CPU使用率，每秒一次，共5次
</code></pre>
<h3>日志查看</h3>
<pre><code class="language-bash"># 查看系统日志
journalctl

# 查看特定服务的日志
journalctl -u service_name

# 实时查看日志
journalctl -f

# 查看最近的日志
journalctl -n 100

# 查看特定时间段的日志
journalctl --since &quot;2024-01-01&quot; --until &quot;2024-01-02&quot;
</code></pre>
<h2>实用技巧</h2>
<h3>别名设置</h3>
<pre><code class="language-bash"># 设置常用命令别名
alias ll='ls -la'
alias la='ls -A'
alias l='ls -CF'
alias ..='cd ..'
alias ...='cd ../..'
alias grep='grep --color=auto'

# 永久保存别名
echo 'alias ll=&quot;ls -la&quot;' &gt;&gt; ~/.bashrc
source ~/.bashrc
</code></pre>
<h3>历史命令</h3>
<pre><code class="language-bash"># 查看命令历史
history

# 搜索历史命令
history | grep search_term

# 执行历史命令
!number  # 执行第number条命令
!!       # 执行上一条命令
!$       # 使用上一条命令的最后一个参数
</code></pre>
<h3>管道和重定向</h3>
<pre><code class="language-bash"># 管道：将前一个命令的输出作为后一个命令的输入
command1 | command2

# 重定向输出
command &gt; output_file
command &gt;&gt; output_file  # 追加

# 重定向输入
command &lt; input_file

# 重定向错误输出
command 2&gt; error_file
command 2&gt;&amp;1 output_file  # 将错误输出也重定向到文件
</code></pre>
<h3>脚本编写基础</h3>
<pre><code class="language-bash">#!/bin/bash
# 这是一个简单的bash脚本示例

# 设置变量
DATASET_PATH=&quot;/path/to/dataset&quot;
MODEL_PATH=&quot;/path/to/model&quot;

# 检查目录是否存在
if [ ! -d &quot;$DATASET_PATH&quot; ]; then
    echo &quot;Dataset directory does not exist!&quot;
    exit 1
fi

# 运行Python脚本
python train.py --data_path &quot;$DATASET_PATH&quot; --model_path &quot;$MODEL_PATH&quot;

# 检查命令是否成功
if [ $? -eq 0 ]; then
    echo &quot;Training completed successfully!&quot;
else
    echo &quot;Training failed!&quot;
    exit 1
fi
</code></pre>
<h2>总结</h2>
<p>掌握这些Linux命令将大大提高你在AI研究中的工作效率。建议：</p>
<ol>
<li><strong>循序渐进</strong>：从基础命令开始，逐步掌握更复杂的操作</li>
<li><strong>实践为主</strong>：多在实际项目中使用这些命令</li>
<li><strong>建立习惯</strong>：将常用操作设置为别名或脚本</li>
<li><strong>持续学习</strong>：Linux命令众多，根据项目需求不断学习新的工具</li>
</ol>
<p>记住，命令行虽然初期学习曲线较陡，但一旦掌握，将大大提升你的开发和研究效率。在AI领域，很多工具和框架都优先支持命令行操作，熟练掌握Linux命令是成为优秀AI研究者的重要基础。</p>
<h2>推荐资源</h2>
<ul>
<li><a href="https://www.runoob.com/linux/linux-command-manual.html">Linux命令大全</a></li>
<li><a href="https://www.runoob.com/linux/linux-shell.html">Bash脚本教程</a></li>
<li><a href="https://docs.docker.com/">Docker官方文档</a></li>
<li><a href="https://git-scm.com/doc">Git官方文档</a></li>
</ul>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[Git Commit Message 开发规范]]></title>
            <link>https://zhoukai.space/posts/git-commit-message</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/git-commit-message</guid>
            <pubDate>Sat, 14 Jun 2025 12:20:00 GMT</pubDate>
            <description><![CDATA[本文为 AngularJS Git Commit Message 规范的中文翻译]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>AngularJS Git Commit Message 规范</p>
</blockquote>
<h2><strong>目标</strong></h2>
<ul>
<li>允许通过脚本生成 <a href="http://CHANGELOG.md">CHANGELOG.md</a></li>
<li>允许在 git bisect 时忽略某些提交（如格式化等不重要的提交）</li>
<li>在浏览历史时提供更好的信息</li>
</ul>
<h2><strong>生成 <a href="http://CHANGELOG.md">CHANGELOG.md</a></strong></h2>
<p>我们在 changelog 中使用这三个部分：<strong>Features</strong>、<strong>Bug Fixes</strong>、<strong>BREAKING CHANGES</strong>。<br>
这个列表可以在发布时通过脚本生成，同时包含相关提交的链接。<br>
当然，你可以在实际发布前编辑这个变更日志，但它可以生成基本框架。</p>
<p>自上次发布以来的所有变动（提交信息的第一行）列表：</p>
<pre><code>git log \&lt;last tag\&gt; HEAD \--pretty=format:%s
</code></pre>
<p>本次发布的新功能：</p>
<pre><code>git log \&lt;last release\&gt; HEAD \--grep feature
</code></pre>
<h2><strong>识别不重要的提交</strong></h2>
<p>这些是格式化更改（添加/删除空格/空行、缩进）、缺少分号、注释等。所以当你在寻找某些更改时，可以忽略这些提交 - 这些提交中没有逻辑更改。</p>
<p>在 bisect 时，你可以通过以下方式忽略这些提交：</p>
<pre><code>git bisect skip $(git rev-list \--grep irrelevant \&lt;good place\&gt; HEAD)
</code></pre>
<h2><strong>在浏览历史时提供更多信息</strong></h2>
<p>这将添加某种&quot;上下文&quot;信息。<br>
看看这些消息（取自最近几个 angular 的提交）：</p>
<ul>
<li><a href="https://github.com/angular/angular.js/commit/f370be85cb680a7cac7a23999a865b9c9e731238">Fix small typo in docs widget (tutorial instructions)</a></li>
<li><a href="https://github.com/angular/angular.js/commit/7460a7ef618a274607ea99aecae99fb158115c36">Fix test for scenario.Application - should remove old iframe</a></li>
<li><a href="https://github.com/angular/angular.js/commit/3c87611188fc1612fe5d07e245a992b25146f2bf">docs - various doc fixes</a></li>
<li><a href="https://github.com/angular/angular.js/commit/b842642b574a2b95c53b791308ed1bf8ff9d304d">docs - stripping extra new lines</a></li>
<li><a href="https://github.com/angular/angular.js/commit/d428c9910e66246c2af46602499acaeaf187d75b">Replaced double line break with single when text is fetched from Google</a></li>
<li><a href="https://github.com/angular/angular.js/commit/f9f95879f08073ce1170a471a925541324a0ff23">Added support for properties in documentation</a></li>
</ul>
<p>所有这些消息都试图指定更改的位置。但它们没有遵循任何约定...</p>
<p>看看这些消息：</p>
<ul>
<li><a href="https://github.com/angular/angular.js/commit/a4dd9ca769c62cf5f65fadc8da0d23d865116046">fix comment stripping</a></li>
<li><a href="https://github.com/angular/angular.js/commit/924ffafc51cf53ddf97f13ad748bbbf6d80caf13">fixing broken links</a></li>
<li><a href="https://github.com/angular/angular.js/commit/7fe46e8d7e35c21167932c57b4ed53171164d1e2">Bit of refactoring</a></li>
<li><a href="https://github.com/angular/angular.js/commit/2e0e732cadd86846b57b7b02b3303a2e0e3b842a">Check whether links do exist and throw exception</a></li>
<li><a href="https://github.com/angular/angular.js/commit/22f9354c211b032c3da3c8e1320a3fa1106e3ffd">Fix sitemap include (to work on case sensitive linux)</a></li>
</ul>
<p>你能猜到里面是什么吗？这些消息缺少位置说明...<br>
所以也许应该像代码的部分：<strong>docs</strong>、<strong>docs-parser</strong>、<strong>compiler</strong>、<strong>scenario-runner</strong> 等...</p>
<p>我知道，你可以通过检查哪些文件被更改来找到这些信息，但那很慢。当查看 git 历史时，我看到我们都在试图指定位置，只是缺少约定。</p>
<h2><strong>提交信息的格式</strong></h2>
<p><strong>&lt;type&gt;(&lt;scope&gt;): &lt;subject&gt;</strong><br>
<strong>&lt;空一行&gt;</strong><br>
<strong>&lt;body&gt;</strong><br>
<strong>&lt;空一行&gt;</strong><br>
<strong>&lt;footer&gt;</strong></p>
<p>提交信息的任何一行都不能超过 <strong>100 个字符</strong>！这使得消息在 github 和各种 git 工具中更容易阅读。</p>
<p>提交信息由头部、正文和页脚组成，用空行分隔。</p>
<h2><strong>回退提交</strong></h2>
<p>如果提交回退了之前的提交，其头部应该以 <code>revert:</code> 开头，后跟被回退提交的头部。在正文中应该说明：<code>This reverts commit &lt;hash&gt;.</code>，其中 hash 是被回退提交的 SHA。</p>
<h2><strong>消息头部</strong></h2>
<p>消息头部是包含更改的简洁描述的单行，包含 <strong>type</strong>、可选的 <strong>scope</strong> 和 <strong>subject</strong>。</p>
<h3><strong>允许的 type</strong></h3>
<p>这描述了此提交提供的更改类型。</p>
<ul>
<li><strong>feat</strong> (feature：新功能)</li>
<li><strong>fix</strong> (bug fix：修补bug)</li>
<li><strong>docs</strong> (documentation：文档)</li>
<li><strong>style</strong> (formatting, missing semi colons, …：格式)</li>
<li><strong>refactor</strong> (重构：既不是新增功能，也不是修改bug的代码变动)</li>
<li><strong>test</strong> (when adding missing tests：增加测试)</li>
<li><strong>chore</strong> (maintain：构建过程或辅助工具的变动)</li>
</ul>
<h3><strong>允许的 scope</strong></h3>
<p>Scope 可以是任何指定提交更改位置的标识。例如 <strong>$location</strong>、<strong>$browser</strong>、<strong>$compile</strong>、<strong>$rootScope</strong>、<strong>ngHref</strong>、<strong>ngClick</strong>、<strong>ngView</strong> 等...</p>
<p>如果没有更合适的 scope，可以使用 <code>*</code>。</p>
<h3><strong>subject 文本</strong></h3>
<p>这是对更改的简短描述。</p>
<ul>
<li>使用祈使句，现在时态：&quot;change&quot; 而不是 &quot;changed&quot; 或 &quot;changes&quot;</li>
<li>不要大写第一个字母</li>
<li>结尾不要加句点 (.)</li>
</ul>
<h2><strong>消息正文</strong></h2>
<ul>
<li>与 subject 一样使用祈使句，现在时态：&quot;change&quot; 而不是 &quot;changed&quot; 或 &quot;changes&quot;</li>
<li>包括更改的动机和与之前行为的对比</li>
</ul>
<p><a href="http://365git.tumblr.com/post/3308646748/writing-git-commit-messages">http://365git.tumblr.com/post/3308646748/writing-git-commit-messages</a><br>
<a href="http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html">http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html</a></p>
<h2><strong>消息页脚</strong></h2>
<h3><strong>破坏性更改</strong></h3>
<p>所有破坏性更改都必须在页脚中作为破坏性更改块提及，该块应以单词 BREAKING CHANGE: 开头，后跟空格或两个换行符。然后提交消息的其余部分是更改的描述、理由和迁移说明。</p>
<pre><code>BREAKING CHANGE: isolate scope bindings definition has changed and
    the inject option for the directive controller injection was removed.

    To migrate the code follow the example below:

    Before:

    scope: {
      myAttr: 'attribute',
      myBind: 'bind',
      myExpression: 'expression',
      myEval: 'evaluate',
      myAccessor: 'accessor'
    }

    After:

    scope: {
      myAttr: '@',
      myBind: '@',
      myExpression: '&amp;',
      // myEval \- usually not useful, but in cases where the expression is assignable, you can use '='
      myAccessor: '=' // in directive's template change myAccessor() to myAccessor
    }

    The removed \`inject\` wasn't generaly useful for directives so there should be no code using it.
</code></pre>
<h3><strong>引用问题</strong></h3>
<p>已关闭的 bug 应该在页脚中单独一行列出，以 &quot;Closes&quot; 关键字为前缀，如下所示：</p>
<p>Closes #234</p>
<p>或者在多个问题的情况下：</p>
<p>Closes #123, #245, #992</p>
<h2><strong>示例</strong></h2>
<hr>
<pre><code>feat($browser): onUrlChange event (popstate/hashchange/polling)

Added new event to $browser:
\- forward popstate event if available
\- forward hashchange event if popstate not available
\- do polling when neither popstate nor hashchange available

Breaks $browser.onHashChange, which was removed (use onUrlChange instead)
</code></pre>
<hr>
<pre><code>fix($compile): couple of unit tests for IE9

Older IEs serialize html uppercased, but IE9 does not...
Would be better to expect case insensitive, unfortunately jasmine does
not allow to user regexps for throw expectations.

Closes \#392
Breaks foo.bar api, foo.baz should be used instead
</code></pre>
<hr>
<pre><code>feat(directive): ng:disabled, ng:checked, ng:multiple, ng:readonly, ng:selected

New directives for proper binding these attributes in older browsers (IE).
Added coresponding description, live examples and e2e tests.

Closes \#351
</code></pre>
<hr>
<pre><code>style($location): add couple of missing semi colons
</code></pre>
<hr>
<pre><code>docs(guide): updated fixed docs from Google Docs

Couple of typos fixed:
\- indentation
\- batchLogbatchLog \-\&gt; batchLog
\- start periodic checking
\- missing brace
</code></pre>
<hr>
<pre><code>feat($compile): simplify isolate scope bindings

Changed the isolate scope binding options to:
  \- @attr \- attribute binding (including interpolation)
  \- \=model \- by-directional model binding
  \- \&amp;expr \- expression execution binding

This change simplifies the terminology as well as
number of choices available to the developer. It
also supports local name aliasing from the parent.

BREAKING CHANGE: isolate scope bindings definition has changed and
the inject option for the directive controller injection was removed.

To migrate the code follow the example below:

Before:

scope: {
  myAttr: 'attribute',
  myBind: 'bind',
  myExpression: 'expression',
  myEval: 'evaluate',
  myAccessor: 'accessor'
}

After:

scope: {
  myAttr: '@',
  myBind: '@',
  myExpression: '&amp;',
  // myEval \- usually not useful, but in cases where the expression is assignable, you can use '='
  myAccessor: '=' // in directive's template change myAccessor() to myAccessor
}

The removed \`inject\` wasn't generaly useful for directives so there should be no code using it.
</code></pre>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[大语言模型综述：GPT、LLaMA 和 PaLM 模型的发展]]></title>
            <link>https://zhoukai.space/posts/llm-a-survey</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/llm-a-survey</guid>
            <pubDate>Wed, 28 May 2025 08:00:00 GMT</pubDate>
            <description><![CDATA[本文为论文《Large language models: a survey》的阅读笔记]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>本文是对论文《Large language models: a survey》的阅读笔记和个人理解。这篇综述详细回顾了一些最具代表性的大语言模型，包括三个流行的大语言模型家族（GPT、LLaMA、PaLM），讨论了它们的特点、贡献和局限性，并概述了用于构建和增强大语言模型的相关技术。</p>
</blockquote>
<h2>如何构建大模型</h2>
<h3>数据清理</h3>
<p>数据清理是大模型训练的基础，主要包括：</p>
<ul>
<li>数据过滤：去除噪声、异常值、不平衡数据、含歧义文本等。</li>
<li>文本预处理：如去除停用词、特殊符号等。</li>
<li>删除重复值，保证数据多样性和有效性。</li>
</ul>
<h3>分词化（Tokenization）</h3>
<p>分词化是将文本转化为模型可处理的 token 序列的过程。常用方法有 BPE、WordPiece、SentencePiece 等。</p>
<h3>位置嵌入（Positional Embedding）</h3>
<ul>
<li>绝对位置嵌入（APE）</li>
<li>相对位置嵌入（RPE）</li>
<li>旋转位置嵌入（RoPE）</li>
<li>相对位置偏差（RPB）</li>
</ul>
<h3>LLM 架构</h3>
<p>大模型的架构设计直接影响其能力和效率。主流架构包括 Transformer 及其变体。</p>
<h3>模型预训练</h3>
<ul>
<li>混合专家（MoE）等技术提升模型参数规模和推理效率。</li>
</ul>
<h3>微调和指令调优（Fine-tuning、Instruction-tuning）</h3>
<ul>
<li>监督微调（SFT）：利用标注数据对模型进行有针对性的优化。</li>
</ul>
<h3>模型对齐（Alignment）</h3>
<ul>
<li>基于人类反馈的强化学习（RLHF）</li>
<li>基于 AI 反馈的强化学习（RLAIF）</li>
<li>直接偏好优化（DPO）、KTO 等</li>
</ul>
<h3>解码策略</h3>
<ul>
<li>贪婪搜索（Greedy Search）</li>
<li>光束搜索（Beam Search）</li>
<li>Top-k 采样、Top-p 采样等</li>
</ul>
<h3>推理、压缩与量化微调</h3>
<ul>
<li>零冗余优化器（ZeRO）</li>
<li>感受加权键值（RWKV）</li>
<li>低秩分解（LoRA）</li>
<li>知识蒸馏、量化等</li>
</ul>
<h3>幻觉问题与评估</h3>
<ul>
<li>统计指标：ROUGE、BLEU 等评估文本相似性，PARENT、Knowledge F1 等用于结构化知识。</li>
<li>基于模型的指标：IE、QA、NLI、忠诚度分类等。</li>
<li>人工评估：主观判断模型输出的真实性和相关性。</li>
</ul>
<blockquote>
<p>例如，更大的模型和较低的 temperature 设置通常表现更好。</p>
</blockquote>
<h2>如何使用大模型——提示词工程</h2>
<h3>思维链（Chain of Thought, CoT）</h3>
<ul>
<li>Zero-Shot CoT</li>
<li>Manual CoT</li>
<li>Automatic CoT</li>
</ul>
<h3>思维树（Tree of Thought, ToT）</h3>
<h3>自洽性（Self-Consistency）</h3>
<h3>反思（Reflection）</h3>
<h3>专家提示（Expert Prompting）</h3>
<h3>链（Chains）</h3>
<h3>规则（Rails）</h3>
<h3>自动提示工程（APE）</h3>
<h2>如何扩展大模型——RAG</h2>
<blockquote>
<p>检索增强生成（Retrieval-Augmented Generation, RAG）</p>
<p>自动多步推理和工具使用（ART）</p>
</blockquote>
<h3>Agent 中的提示词工程</h3>
<ul>
<li>ReWOO、ReAct、DERA 等</li>
</ul>
<h2>大模型 LLMs 的评估数据集</h2>
<h3>基本任务的数据集：语言建模、理解、生成</h3>
<ul>
<li>Natural Questions（QA 数据集）</li>
<li>MMLU</li>
<li>MBPP（Python 代码生成）</li>
<li>HumanEval（Python 代码生成）</li>
<li>APPS（Python 代码生成）</li>
<li>WikiSQL（SQL 代码生成）</li>
<li>TriviaQA（QA 数据集）</li>
<li>RACE（英语阅读理解）</li>
<li>SQuAD（斯坦福问答数据集）</li>
<li>BoolQ（是/否问答）</li>
<li>MultiRC（阅读理解）</li>
</ul>
<h3>指令跟随、CoT 推理等新兴能力的数据集</h3>
<ul>
<li>GSM8K（数学推理）</li>
<li>MATH（数学竞赛题）</li>
</ul>
<h2>三种类型的架构</h2>
<h3>Encoder-only</h3>
<ul>
<li>BERT</li>
</ul>
<h3>Decoder-only</h3>
<ul>
<li>GPT-1</li>
<li>GPT-2</li>
</ul>
<p><strong>Decoder-Only</strong>：这类模型在每个阶段，对于任何单词，注意力层只能访问该单词之前的内容，属于自回归模型。</p>
<h3>Encoder-Decoder</h3>
<ul>
<li>T5</li>
<li>BART</li>
</ul>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[Mac 终端美化及插件安装]]></title>
            <link>https://zhoukai.space/posts/oh-my-zsh</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/oh-my-zsh</guid>
            <pubDate>Thu, 22 May 2025 12:20:00 GMT</pubDate>
            <description><![CDATA[本文详细介绍了如何在Mac上美化终端（Terminal），包括配置配色方案、安装和配置oh-my-zsh、主题与高效插件推荐。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>本文将带你一步步完成 Mac 终端（Terminal）的美化，包括 oh-my-zsh 的安装、主题配置及高效插件推荐。</p>
</blockquote>
<h2>目标</h2>
<ul>
<li>修改 Terminal 的 Profile：让 Terminal 配色更美观</li>
<li>安装 oh-my-zsh 主题：美化 oh-my-zsh</li>
<li>安装 oh-my-zsh 必备插件：让 Terminal 具有更高级和便利的功能</li>
</ul>
<h2>准备工作</h2>
<p>先安装 Homebrew，方便后续工具安装：</p>
<pre><code class="language-sh">/usr/bin/ruby -e &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)&quot;
</code></pre>
<p>如果你的 macOS 版本早于 Catalina，你需要手动安装 zsh：</p>
<pre><code class="language-sh"># 安装 zsh
brew install zsh
# 设置 zsh 为你的默认 shell
chsh -s /usr/local/bin/zsh
</code></pre>
<h3>修改 Terminal Profile 主题设置</h3>
<ol>
<li>在 GitHub 的 <a href="https://github.com/lysyi3m/osx-terminal-themes">osx-terminal-theme</a> 项目主页里寻找你喜欢的主题。</li>
<li>在 schemes 目录里找到对应的主题文件并安装到 Terminal，设置为默认。</li>
</ol>
<h2>安装 oh-my-zsh</h2>
<p>Oh My Zsh 是一个令人愉快的、开源的、社区驱动的管理 zsh 配置的框架。它为我们带来了数千个有用的功能、助手、插件、主题，和其他一些令人惊叹的功能。</p>
<p>安装 oh-my-zsh：</p>
<pre><code class="language-sh">sh -c &quot;$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)&quot;
</code></pre>
<h3>安装 oh-my-zsh 主题</h3>
<h4>内置主题列表</h4>
<p>oh-my-zsh 提供一批内置主题，可以直接设置使用。</p>
<ul>
<li>在<a href="https://github.com/robbyrussell/oh-my-zsh/wiki/Themes">内置主题列表</a>寻找你喜欢的主题。</li>
<li>在 <code>~/.zshrc</code> 配置文件里设置 <code>ZSH_THEME</code> 为你的主题名称。</li>
<li>激活设置：<code>source ~/.zshrc</code></li>
<li>也可以直接使用默认主题，不需要任何操作</li>
</ul>
<h4>第三方主题列表</h4>
<p>许多第三方也开发了供 oh-my-zsh 使用的主题，可以去<a href="https://github.com/robbyrussell/oh-my-zsh/wiki/External-themes">第三方主题列表</a>查看和安装。</p>
<h2>安装 oh-my-zsh 必备插件</h2>
<p>oh-my-zsh 有非常丰富的插件可供使用，下面列举一些必备插件，可以大幅提高生产力。</p>
<p>示例：</p>
<pre><code class="language-sh"># ~/.zshrc:
plugins=(
  git
  extract
  autojump
  zsh-autosuggestions
  zsh-syntax-highlighting
)
</code></pre>
<h3>插件说明</h3>
<ul>
<li><strong>git</strong>
<ul>
<li>自带插件，可以使用缩写命令，比如 <code>gaa</code> -&gt; <code>git add --all</code>，通过 <code>alias | grep git</code> 查看所有支持缩写命令。</li>
<li>激活：添加到 <code>~/.zshrc</code> 的 plugins 列表。</li>
</ul>
</li>
<li><strong>extract</strong>
<ul>
<li>自带插件，不用再使用复杂的 tar 来解压压缩包了。</li>
<li>激活：添加 <code>extract</code> 到 <code>~/.zshrc</code> 的 plugins 列表。</li>
</ul>
</li>
<li><strong>autojump</strong>
<ul>
<li>使用 <code>j</code> 命令直接快速进入某个目录，比如 <code>j Downloads</code> -&gt; <code>cd ~/Downloads</code></li>
<li>安装：<code>brew install autojump</code></li>
<li>激活：添加 <code>autojump</code> 至 <code>~/.zshrc</code> 配置文件的插件列表。</li>
</ul>
</li>
<li><strong>zsh-syntax-highlighting</strong>
<ul>
<li>命令高亮插件，命令不再只是同一个颜色了。</li>
<li>安装：<pre><code class="language-sh">git clone https://github.com/zsh-users/zsh-syntax-highlighting ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting
</code></pre>
</li>
<li>激活：添加 <code>zsh-syntax-highlighting</code> 至 <code>~/.zshrc</code> 配置文件的插件列表。</li>
</ul>
</li>
<li><strong>zsh-autosuggestions</strong>
<ul>
<li>输入时按右方向键 → 自动补全命令。</li>
<li>安装：<pre><code class="language-sh">git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
</code></pre>
</li>
<li>激活：添加 <code>zsh-autosuggestions</code> 至 <code>~/.zshrc</code> 配置文件的插件列表。</li>
</ul>
</li>
</ul>
<h2>个人 ~/.zshrc 配置</h2>
<pre><code class="language-sh"># theme
ZSH_THEME=&quot;robbyrussell&quot;

# plugins
plugins=(
  git
  extract
  autojump
  zsh-autosuggestions
  zsh-syntax-highlighting
)
</code></pre>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[Hugging Face 国内镜像源配置指南]]></title>
            <link>https://zhoukai.space/posts/huggingface</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/huggingface</guid>
            <pubDate>Tue, 14 May 2024 12:10:00 GMT</pubDate>
            <description><![CDATA[详细介绍如何配置 Hugging Face 的国内镜像源，包括CLI工具安装、环境变量设置、源码修改等多种方法]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>在使用 Hugging Face 下载模型时，由于网络原因可能会遇到下载速度慢或连接失败的问题。本文介绍几种配置国内镜像源的方法。</p>
</blockquote>
<h2>方法一：安装 CLI 工具并设置环境变量（推荐-简单）</h2>
<h3>安装 huggingface_hub</h3>
<pre><code class="language-bash">pip install -U huggingface_hub
</code></pre>
<h3>设置环境变量</h3>
<pre><code class="language-bash">export HF_ENDPOINT=https://hf-mirror.com
</code></pre>
<pre><code class="language-bash">export HF_HOME=&quot;模型，数据集保存路径&quot;
</code></pre>
<blockquote>
<p>注意：这种配置方式只在当前 shell 会话中有效，如果你希望这个环境变量在每次启动终端时都生效，可以将其添加到你的用户配置文件中（修改 <code>~/.bashrc</code> 或 <code>~/.zshrc</code>）</p>
</blockquote>
<pre><code class="language-bash">echo $HF_ENDPOINT
echo $HF_HOME
</code></pre>
<blockquote>
<p>检查是否生效</p>
</blockquote>
<h3>下载模型示例</h3>
<pre><code class="language-bash">huggingface-cli download \
    --local-dir-use-symlinks False \
    microsoft/deberta-v3-large \
    --local-dir /root/autodl-tmp/deberta_model \
    --cache-dir /root/autodl-tmp/deberta_cache
</code></pre>
<h2>方法二：修改源码配置（推荐-较麻烦）</h2>
<h3>修改 <a href="http://constants.py">constants.py</a> 文件</h3>
<p>找到你的 Python 环境中的 <code>huggingface_hub</code> 包位置：</p>
<blockquote>
<p>通常在以下路径之一</p>
</blockquote>
<pre><code class="language-bash">/miniconda3/envs/环境名称/lib/python3.12/site-packages/huggingface_hub/constants.py
</code></pre>
<pre><code class="language-bash">/usr/local/lib/python3.x/site-packages/huggingface_hub/constants.py
</code></pre>
<p>修改 <code>constants.py</code> 文件中的默认端点：</p>
<pre><code class="language-python">_HF_DEFAULT_ENDPOINT = &quot;https://hf-mirror.com&quot;
</code></pre>
<h2>方法三：在代码中动态设置</h2>
<p>在 Python 代码开头添加环境变量设置：</p>
<pre><code class="language-python">import os
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
from huggingface_hub import hf_hub_download
</code></pre>
<h2>可用的国内镜像源</h2>
<ul>
<li><strong>官方镜像</strong>: <code>https://hf-mirror.com</code></li>
<li><strong>Alpha 镜像</strong>: <code>https://alpha.hf-mirror.com/</code></li>
</ul>
<h2>注意事项</h2>
<ol>
<li>设置环境变量后需要重启终端或重新加载环境</li>
<li>修改源码后需要重启 Python 解释器</li>
<li>建议优先使用方法一（环境变量），因为它不会影响其他项目</li>
<li>如果遇到权限问题，请确保有相应目录的写入权限</li>
</ol>
<h2>验证配置</h2>
<p>配置完成后，可以通过以下命令验证是否生效：</p>
<pre><code class="language-bash">huggingface-cli download --help
</code></pre>
<p>如果配置成功，下载速度应该会有明显提升。</p>
]]></content:encoded>
            <author>周凯</author>
        </item>
        <item>
            <title><![CDATA[如何训练一个 GPT 助手]]></title>
            <link>https://zhoukai.space/posts/train-gpt</link>
            <guid isPermaLink="true">https://zhoukai.space/posts/train-gpt</guid>
            <pubDate>Sun, 14 Jan 2024 07:20:00 GMT</pubDate>
            <description><![CDATA[本文来自 OpenAI 的 Andrej Karpathy 在 Microsoft Build 2023 大会的分享]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<blockquote>
<p>本文翻译了 Andrej Karpathy 在 Microsoft Build 2023 大会分享的第一部分内容。</p>
</blockquote>
<h2>引言</h2>
<p>人工智能领域正在经历翻天覆地的变化，因此这里讲的只是到目前为止训练 GPT 助手的方法。大致分为四个阶段：</p>
<ol>
<li>预训练（Pre-training）</li>
<li>监督微调（Supervised Fine Tuning, SFT）</li>
<li>奖励建模（Reward Modeling）</li>
<li>强化学习（Reinforcement Learning）</li>
</ol>
<p>每个阶段又分为三个部分（从上到下）：数据集、算法和输出的模型。</p>
<h2>预训练</h2>
<p>这个阶段占了整个过程（四个阶段）绝大部分算力，例如占据了 99% 的训练计算时间和浮点运算。</p>
<p>处理的是互联网规模的数据集，使用数千个 GPU 训练，可能需要数月的时间。其他三个阶段是微调阶段，只需要使用较少的 GPU 训练几个小时或几天。</p>
<h3>数据集</h3>
<p>首先需要收集大量的数据。例如，下面是 Meta 训练 LLaMA 所用的数据集：</p>
<table>
<thead>
<tr>
<th style="text-align:left">数据集</th>
<th style="text-align:left">占比</th>
<th style="text-align:left">迭代次数（Epochs）</th>
<th style="text-align:left">数据集大小（Disk Size）</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">CommonCrawl</td>
<td style="text-align:left">67.0%</td>
<td style="text-align:left">1.10</td>
<td style="text-align:left">3.3 TB</td>
</tr>
<tr>
<td style="text-align:left">C4</td>
<td style="text-align:left">15.0%</td>
<td style="text-align:left">1.06</td>
<td style="text-align:left">783 GB</td>
</tr>
<tr>
<td style="text-align:left">Github</td>
<td style="text-align:left">4.5%</td>
<td style="text-align:left">0.64</td>
<td style="text-align:left">328 GB</td>
</tr>
<tr>
<td style="text-align:left">Wikipedia</td>
<td style="text-align:left">4.5%</td>
<td style="text-align:left">2.45</td>
<td style="text-align:left">83 GB</td>
</tr>
<tr>
<td style="text-align:left">Books</td>
<td style="text-align:left">4.5%</td>
<td style="text-align:left">2.23</td>
<td style="text-align:left">85 GB</td>
</tr>
<tr>
<td style="text-align:left">ArXiv</td>
<td style="text-align:left">2.5%</td>
<td style="text-align:left">1.06</td>
<td style="text-align:left">92 GB</td>
</tr>
<tr>
<td style="text-align:left">StackExchange</td>
<td style="text-align:left">2.0%</td>
<td style="text-align:left">1.03</td>
<td style="text-align:left">78 GB</td>
</tr>
</tbody>
</table>
<p>其中 Epochs 是用 1.4T tokens 预训练时的迭代次数。用 1T tokens 预训练时也是用的这个数据集比例。</p>
<h3>文本 Token 化</h3>
<p>在实际训练这些数据之前，需要经过一个预处理步骤，即 token 化。将原始文本翻译成整数序列，后者是 GPT 的表示方式。</p>
<ul>
<li>一个 token 可能是一个单词、一个词根、标点、标点+单词等等</li>
<li>每个 token 平均对应 0.75 个单词</li>
<li>所有的独立 token 组成一个词典（词汇表），典型的词典大小：10k~100k tokens</li>
<li>这种文本/token 转换是无损的，有很多算法，例如常用的字节对编码</li>
</ul>
<h3>超参数：GPT-3 vs. LLaMA</h3>
<p>接下来需要考虑控制阶段的超参数。这里拿两个具体模型 GPT-3/LLaMA 作为例子：</p>
<ul>
<li>GPT-4 的训练信息公开比较少，所以这里使用 GPT-3 的数据，注意 GPT-3 已经是三年前的模型了</li>
<li>LLaMA 是 Meta 最近发布的一个开源模型，数据比较新，信息比较全</li>
</ul>
<h4>词汇表大小、上下文长度、参数数量</h4>
<p>预训练处理的数量级大致如下：</p>
<ul>
<li>词汇表大小通常为 10K 个 token</li>
<li>上下文长度通常为 2k/4k，有时甚至 100k。这决定了 GPT 在预测序列中下一个整数时所能查看的最大整数数量</li>
</ul>
<h4>硬件环境和成本</h4>
<table>
<thead>
<tr>
<th style="text-align:left">模型</th>
<th style="text-align:left">GPU</th>
<th style="text-align:left">训练时长</th>
<th style="text-align:left">训练成本</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">GPT-3</td>
<td style="text-align:left">约一万张 V100</td>
<td style="text-align:left">30 天左右</td>
<td style="text-align:left">$100 万 ~ $1000 万</td>
</tr>
<tr>
<td style="text-align:left">LLaMA</td>
<td style="text-align:left">两千张 A100</td>
<td style="text-align:left">21 天</td>
<td style="text-align:left">$500 万</td>
</tr>
</tbody>
</table>
<h3>开始训练</h3>
<h4>根据 Batch Size 和上下文长度 Token 化输入文本</h4>
<p>输入给 Transformer 的是 (B,T) 维度的矩阵，其中：</p>
<ul>
<li>B 表示批次大小（batch size）</li>
<li>T 表示最大上下文长度</li>
</ul>
<p>另外，输入会整理成行（row），每个输入序列的结尾用一个特殊的 <code>&lt;|endoftext|&gt;</code> token 来标记。</p>
<h4>预测下一个 Token</h4>
<p>在预测每个位置的下一个 token 时，只能用到当前行中当前位置前面的最多 T（上下文长度）个 token。</p>
<h4>损失函数</h4>
<p>训练一段时间之后，你会发现 Transformer 已经学会了单词以及在哪里放空格和逗号等等。因此，随着时间的推移，我们可以得到越来越一致的预测。</p>
<h2>监督微调（SFT）</h2>
<h3>收集高质量人工标注数据</h3>
<p>在监督微调阶段，首先需要收集小但高质量的数据集。通常是通过供应商的形式收集，格式是&quot;提示 + 优质回答&quot;。</p>
<h3>SFT 训练</h3>
<p>然后在这些数据上再次进行语言建模：</p>
<ul>
<li>算法还是一样，只是更换了训练集</li>
<li>预训练是互联网文档，这是数量庞大但质量较低的数据</li>
<li>现在是 QA 类型的提示回答类数据，数量不多但质量很高</li>
<li>这个阶段只需要百来片 GPU，训练几天时间</li>
</ul>
<h2>奖励建模</h2>
<p>RLHF 包括奖励建模和强化学习。在奖励建模阶段，会将数据收集转变为比较（comparison）的形式。</p>
<h3>例子：评估 ChatGPT 编程的好坏</h3>
<p>基于上一步已经训练好的 SFT 模型，让它写一个检查给定字符串是否为回文的程序或函数。我们重复三次，每次都给完全相同的提示，得到三个回答。</p>
<h3>奖励</h3>
<p>现在来看一下如何对奖励进行建模：</p>
<ul>
<li>蓝色的是提示（prompt tokens），每行都一样</li>
<li>黄色的是 SFT 模型基于 prompt 产生的补全（completion tokens），每次都不同</li>
<li>绿色的是特殊的 <code>&lt;|reward|&gt;</code> token</li>
</ul>
<h3>奖励模型的特点</h3>
<p>跟基座模型、SFT 模型以及后面将介绍的强化学习模型相比，奖励模型的最大特点是不能独立部署。</p>
<h2>强化学习（RLHF）</h2>
<h3>RLHF 训练</h3>
<p>现在我们获取了一大批提示，接下来基于奖励模型进行强化学习：</p>
<ul>
<li>针对给定提示的每个补全，奖励模型能够预测这些补全的质量</li>
<li>评分过程（或称损失函数）其实也是根据给定的一串 tokens（SFT 模型的输出）来预测下一个 token（分数）</li>
</ul>
<h3>为什么要使用 RLHF？</h3>
<p>简单回答是：效果好。从人类的反馈来看，质量从高到低依次为：RLHF 模型、SFT 模型、基座模型。</p>
<h3>模型的熵</h3>
<p>某些情况下，RLHF 模型并不是基础模型的简单改进。特别是，我们注意到 RLHF 模型会丢失一些熵：</p>
<ul>
<li>这意味着它们会给出更加确定性的结果</li>
<li>相比基础模型，RLHF 模型的输出变化更少</li>
<li>基础模型熵比较大，会给出很多不同的输出</li>
</ul>
]]></content:encoded>
            <author>周凯</author>
        </item>
    </channel>
</rss>