<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Ninthless — 代码、工具与随手实验</title><description>关于 AI 工具、系统实用程序与一切值得折腾之物的个人笔记。</description><link>https://ninthless.github.io/</link><item><title>从 80 个仓库看我的工程轨迹：2023—2026</title><link>https://ninthless.github.io/posts/github-80-repositories-timeline/</link><guid isPermaLink="true">https://ninthless.github.io/posts/github-80-repositories-timeline/</guid><description>我重新审计了自己的 80 个 GitHub 仓库，从四年的变化中寻找真正值得继续积累的方向。</description><pubDate>Sun, 19 Jul 2026 14:00:00 GMT</pubDate><content:encoded>如果只看自己的 GitHub 首页，我很容易把仓库数量、提交频率和绿色方格误认为成长本身。它们能说明我做过事情，却不能说明哪些工作已经形成了可复用的能力。

我用 GitHub CLI 重新检查了账号下的全部仓库，包括元数据、目录结构、依赖清单、README 和关键提交。审计时共有 **80 个仓库**：65 个非 Fork、15 个 Fork；35 个公开、45 个私有。这里的“非 Fork”只是 GitHub 的仓库关系字段，不自动等于从零原创，也不等于已经适合作为作品展示。

这篇文章不做项目陈列墙，而是回答三个更重要的问题：过去四年在反复解决什么问题，哪些仓库已经长成工程资产，下一步应该把时间放在哪里。

## 数量背后的时间线

| 年份 | 新建仓库 | 主要阶段 |
| --- | ---: | --- |
| 2023 | 2 | 建立账号身份与最初的代码存档 |
| 2024 | 18 | Java、Minecraft、课程项目与 Web 基础扩张 |
| 2025 | 31 | 研究复现、桌面/浏览器工具、数据与前端项目快速增多 |
| 2026 | 29 | 开始重视边界、验证、发布、安全更新与方法沉淀 |

只统计 65 个非 Fork 仓库时，主语言最多的是 Java、Python、JavaScript、C# 和 Kotlin。这个分布比“全栈”更具体：早期重心在 Java 生态，之后 Python 承担研究和数据实验，JavaScript/TypeScript 承担界面与扩展，C# 则集中在 Windows 桌面工具。

真正明显的变化不是语言变多，而是仓库开始从“能运行”走向“能维护”。

## 2023：先有一个公开身份

2023 年的仓库很少，核心意义是建立公开身份和代码存档。这个阶段没有必要包装成宏大起点，它更像一条基线：账号存在，但尚未形成稳定的项目叙事。

作品集的第一个常见误区，是试图把每一次练习都解释成重要成果。更诚实的做法是保留它们，同时承认当时解决的问题很小。基线越真实，后面的变化越清楚。

## 2024：大量练习开始暴露工程问题

2024 年，我开始集中接触 Minecraft/Java 生态、配置库、静态站点和课程项目。我做的 [TaskFlow](https://github.com/Ninthless/TaskFlow) 是这一阶段很典型的样本：我完整覆盖了任务、日历、主题和本地存储，但它也带着期末作业常见的边界，功能齐全，长期维护与真实用户验证不足。

我的 [Configurate](https://github.com/Ninthless/Configurate) 仓库则提醒了我另一件事：GitHub 没有标记为 Fork，不代表代码谱系不需要说明。只要仓库来自上游项目、课程模板或迁移历史，我就应该在 README 中明确来源、修改范围和当前维护关系。作品集最怕的不是使用现成代码，而是让读者无法判断哪些判断属于我。

这一年最值得保留的能力，不是某个页面或某段 API，而是第一次接触多模块项目、依赖管理、构建失败和代码来源问题。

## 2025：广度快速增加，也开始出现证据焦虑

2025 年我新建了 31 个仓库，是数量增长最快的一年。公开项目覆盖股票辅助界面、双碳管理、图像复原、浏览器国际化和自动化实验；私有项目中则有更多研究复现、数据处理与交付型工程。

这一阶段，我开始处理真实复杂度。例如我在 [Single-lens](https://github.com/Ninthless/Single-lens) 中不只实现维纳滤波和 Lucy–Richardson，还加入 PSF 验证、指标比较、性能诊断与实验输出。我后来又对 [Postman-Web-i18n](https://github.com/Ninthless/Postman-Web-i18n) 做了质量审计，修复 XSS、规则校验、语言切换原子性和动态页面兼容问题。

但仓库也暴露了一个共同风险：README 很容易写出“完整复现”“专业级”“性能提升”之类的结论，而证据可能仍停留在本机输出、合成数据或单次实验。研究项目越复杂，越需要把数据来源、基线、随机种子、运行环境和失败条件写清楚。

另外，我也保留了 [AutoGreen](https://github.com/Ninthless/AutoGreen) 这个很适合作为反例的项目。自动提交能够改变活动图，却不会自动增加项目价值。绿色方格是副产品，不应该成为我的目标函数。

## 2026：从功能实现转向边界与交付

到 2026 年，我的代表项目开始呈现共同结构：先声明边界，再实现功能，然后补验证和发布链路。

- 我把 [ACEOptimizer](https://github.com/Ninthless/ACEOptimizer) 的行为限制在 Windows 公开的优先级与 CPU 亲和性设置，并补上测试、版本校验和 Ed25519 更新签名。
- 我让 [Emergency-Stop](https://github.com/Ninthless/Emergency-Stop) 保持键盘输入与外部覆盖层边界，不读进程、不注入、不自动操作。
- 我为 [HybridFont](https://github.com/Ninthless/HybridFont) 准备了默认禁用的保守包和恢复路径。
- 我没有在 [Tau-gui](https://github.com/Ninthless/Tau-gui) 中重写 Agent 内核，而是围绕 Pi 的 RPC/SDK 做会话、上下文和包管理界面。
- 我在 [agent-skills](https://github.com/Ninthless/agent-skills) 中把反复出现的工作方法写成可触发、可评估的操作协议。
- 我用 [vibe-coding-tutorial](https://github.com/Ninthless/vibe-coding-tutorial/) 把规格、拆解、上下文、验证和复盘组织成一套公开教程。

这些项目之间看似跨度很大，实际上共享一个判断：**工具的价值不仅在于它能做什么，也在于它明确不做什么，以及失败后如何恢复。**

## 哪些仓库值得写成文章

我用四个条件筛选本系列的主题：

1. 仓库中有可核对的演进，而不只是一次性提交。
2. 问题具有迁移价值，能帮助其他项目做判断。
3. 可以公开讲清楚，不依赖私有代码或敏感数据。
4. 文章能诚实写出限制，而不是把 README 再扩写一遍。

因此，本系列选择了 Agent Skills、Vibe Coding 工作流、Windows 工具边界、动态网页国际化、Android 系统修改、科研复现和 Agent 桌面工作台。课程项目也会写，但重点是如何判断它们是否已经成为资产，而不是替旧作业补一层包装。

## 下一阶段比新建仓库更重要的事

80 个仓库已经足够证明探索广度。下一阶段的增量更可能来自以下工作：

- 为公开项目补充最小可复现路径、测试证据和清晰许可证。
- 将一次性交付中的通用模块抽离，但只在确实有第二个使用者时抽离。
- 为导入或改造的仓库补全来源与修改说明。
- 把私有项目中的通用经验写成不泄露实现的文章。
- 让博客、教程和仓库互相引用，形成“判断—实现—证据”的闭环。

GitHub 主页保存的是结果，博客应该保存结果背后的判断。接下来的文章会沿着这条线展开。

## 系列导航

- [把提示词变成可维护的 Agent 操作协议](/posts/agent-skills-as-operating-protocols/)
- [Vibe Coding 不是一句提示词，而是一条交付闭环](/posts/vibe-coding-delivery-loop/)
- [课程项目什么时候才算工程资产](/posts/course-projects-to-assets/)

数据快照来自 [我的 GitHub 主页](https://github.com/Ninthless)，统计日期为 2026 年 7 月 19 日。仓库可见性、数量和内容之后仍可能变化。</content:encoded></item><item><title>为什么这个博客选择静态优先</title><link>https://ninthless.github.io/posts/static-first/</link><guid isPermaLink="true">https://ninthless.github.io/posts/static-first/</guid><description>我选择静态优先，是为了获得更多内容所有权和更低的长期维护成本。</description><pubDate>Sat, 18 Jul 2026 00:00:00 GMT</pubDate><content:encoded>我认为个人博客最重要的能力不是后台功能，而是多年以后仍然能够打开、迁移和继续写。

## 内容先于系统

我把文章保存在 Markdown 文件中。每篇内容都有标题、摘要、日期和标签，但正文不依赖数据库，也不被某个在线编辑器锁定。

这意味着内容可以被 Git 追踪，可以离线编辑，也可以随时迁移到其他生成器。

## 构建时完成复杂工作

Astro 在构建阶段生成 HTML。浏览器拿到的是可以直接阅读的页面，只有主题切换和站内搜索使用少量 JavaScript。

```text
Markdown → 内容校验 → 静态 HTML → CDN
```

路径简单，故障面也更小。没有常驻数据库、登录系统和服务端渲染进程，就少了升级、备份与安全维护的长期负担。

## 什么时候不该静态优先

如果站点需要多人实时协作、会员权限、付费内容或高度动态的数据，CMS 和服务端能力会更合适。

我现在还没有这些需求，所以不提前为它们支付复杂度。</content:encoded></item><item><title>工作台上的三个方向</title><link>https://ninthless.github.io/posts/three-directions/</link><guid isPermaLink="true">https://ninthless.github.io/posts/three-directions/</guid><description>AI 工作流、Windows 实用程序和开发者工具，是我近期最常回到的三个问题域。</description><pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate><content:encoded>把我近期的仓库放在一起看，会出现三个我反复回到的方向。

## 让 AI 工作流更稳定

提示词只能解决一次问题，稳定的流程才能重复解决同一类问题。我在 `agent-skills` 里用明确的边界、步骤和验证方法，把容易漂移的工作方式保存下来。

重点不是让规则变多，而是让每条规则都对应一个真实的失败模式。

## 修掉系统里的摩擦

我做 `ACEOptimizer`，来自一个具体问题：某些 ACE 反作弊进程会持续占用 CPU。我让工具直接检测相关进程，并通过优先级和 CPU 亲和性降低影响。

系统工具的价值通常很朴素。它不需要覆盖所有情况，只需要把一个持续发生的摩擦可靠地移走。

## 让开发工具更顺手

我用 `Postman-Web-i18n` 和 `vibe-coding-tutorial` 分别处理界面语言和知识组织。一个降低使用门槛，一个让方法更容易复用。

这些方向看起来不同，但背后是我的同一个偏好：找到重复出现的不顺手之处，然后把它变成可以长期使用的工具。</content:encoded></item><item><title>Vibe Coding 不是一句提示词，而是一条交付闭环</title><link>https://ninthless.github.io/posts/vibe-coding-delivery-loop/</link><guid isPermaLink="true">https://ninthless.github.io/posts/vibe-coding-delivery-loop/</guid><description>我如何把规格、任务拆解、上下文、验证与复盘整理成一条可交付的 Vibe Coding 闭环。</description><pubDate>Tue, 26 May 2026 00:00:00 GMT</pubDate><content:encoded>2025 年，“Vibe Coding”从一句带有实验意味的描述迅速进入大众语境。与此同时，编码 Agent 从编辑器补全走向可以读取仓库、执行命令、提交 Pull Request 的工作单元。生成代码变得更容易，但“看起来完成”与“可以交付”之间的距离并没有自动消失。

我做 [vibe-coding-tutorial](https://github.com/Ninthless/vibe-coding-tutorial)，不是为了收集更多神奇提示词，而是想把这段距离拆成一条可重复的工作流。

## 当时发生了什么

2025 年 5 月，GitHub 发布可以被分配 Issue、在独立环境工作并提交草稿 PR 的 [Copilot coding agent](https://github.blog/news-insights/product-news/github-copilot-meet-the-new-coding-agent/)。到 2026 年，多个编码 Agent 已经可以在同一 GitHub 工作流中运行。工具从“建议下一行”变成“尝试完成一项任务”。

但 2025 年 Stack Overflow 开发者调查中，主动不信任 AI 准确性的开发者仍多于信任者，最常见的挫折是答案“几乎正确，但不完全正确”。[DORA 2025 AI 辅助软件开发报告](https://dora.dev/research/2025/dora-report/) 也把 AI 描述为放大器：它会放大组织原有的强项，也会放大薄弱流程。

这正是我选择“交付闭环”而不是“提示词大全”的原因。

## 仓库时间线

### 2026-05-26：一天内从脚手架变成完整教程

我在 5 月 26 日建立 Material for MkDocs 站点，随后补齐基础概念、历史、原则、工作流、提示设计、前后端/移动/数据/DevOps 场景、质量清单和路线图，并用严格构建检查验证页面。

同一天后续提交继续扩展 `spec-first`、任务简报、验证、MCP、Skills 和 GitHub Pages。这个顺序很有代表性：先建立导航骨架，再填核心流程，最后补工具生态。

### 2026-05-29：删除重复内容

我开始把教程里的重复警告替换为交叉引用。内容项目也会产生技术债：同一规则出现在五个章节中，未来就会出现五种版本。删除重复并不是减少价值，而是在建立单一事实来源。

### 2026-05-31：加入失败案例

我增加了一个完整案例，记录看似简单的评论功能如何在 AI 协作中两次偏航；同时加入 Cursor Rules、`CLAUDE.md` 等项目规则文件说明，并重写学习路线。

教程到这里才真正跨过“正确答案集合”的门槛。没有失败路径的方法论，很难解释何时应该停下来重读代码、缩小任务或改变方案。

### 2026-07-19：与个人博客建立双向连接

我把教程站连接到这个博客。教程负责稳定的方法与清单，博客负责项目复盘和具体判断。我不想让两边复制内容，而是让它们形成两层结构：

```text
教程：可复用流程与模板
博客：真实项目中的选择、证据与限制
仓库：可检查的实现和提交历史
```

## 一条可交付的 Vibe Coding 闭环

### 1. 规格先行

规格不是要求用户提前设计全部代码，而是把验收结果说清楚。最小规格至少包含目标用户、主要行为、输入输出、不可破坏的约束和完成标准。

“做一个高级博客”无法直接验证；“在手机和桌面都能阅读，文章由 Markdown 管理，构建时校验元数据，部署到 GitHub Pages”则可以进入实现。

### 2. 任务拆解

Agent 适合处理边界清楚的工作单元。拆解的目标不是制造长清单，而是让每一步都能独立检查。

一个有效任务应包含：范围、相关文件、完成标准、禁止事项和验证命令。如果一次任务同时改数据模型、界面、部署与品牌文案，失败后很难定位责任。

### 3. 上下文管理

上下文不是越多越好。需要保留的是当前契约、关键实现、近期失败和待验证假设。旧日志、重复文档和无关文件会挤占注意力。

项目规则文件、Agent Skills 与任务简报的作用，都是把稳定约束放在正确层级，而不是每轮重新解释。

### 4. 实现与可见反馈

生成之后要尽快得到真实反馈：运行测试、打开页面、触发错误状态、检查移动端、查看构建产物。只阅读 Agent 的完成总结不算验证。

对于界面，截图比“页面很美观”的描述更有证据；对于数据任务，固定输入和期望输出比“算法已优化”更可靠。

### 5. 验证

验证必须对应规格。类型检查只能证明类型没有明显问题，不能证明交互正确；构建成功只能证明产物生成，不能证明链接可用；单个 happy path 通过，也不能证明失败后能恢复。

教程把验证单独设为工作流章节，是因为它不该成为最后一分钟的附加动作。

### 6. 复盘

复盘至少记录三件事：最初假设哪里错了，什么证据改变了方案，哪条规则值得沉淀到下一次任务。只有第三项发生，单次修复才可能变成长期能力。

## 什么时候可以“凭感觉”

探索阶段当然可以快速生成。一次性原型、内部草图、低风险脚本都适合高速度。问题不在“Vibe”，而在没有根据风险切换工作模式。

| 场景 | 合理模式 |
| --- | --- |
| 周末原型 | 快速生成，手动体验，接受丢弃 |
| 个人长期工具 | 明确数据与恢复路径，补基本测试 |
| 公共下载软件 | 版本、更新、安全边界和发布验证 |
| 生产/高风险系统 | 严格规格、审查、自动化证据与权限控制 |

速度不是固定值，而是风险预算。

## 教程与博客如何继续串联

[Vibe Coding 教程站](/tutorial/) 适合从头学习完整流程。本博客的项目文章则提供对应案例：

- Agent Skills 文章解释规则如何成为可测试协议。
- Windows 工具文章解释为什么先定义“不做什么”。
- 科研复现文章解释如何建立从数据到结论的证据链。
- Tau 文章解释上下文与会话为什么需要可视化。

一个方法只有在不同项目中反复成立，才值得进入教程；一个项目只有能说明具体取舍，才值得写进博客。

## 最后的标准

Vibe Coding 的成熟度，不由提示词多漂亮决定，而由以下问题决定：

1. 需求能否被另一个人复述？
2. 改动能否被限定在明确范围？
3. 结果能否被独立验证？
4. 失败后能否定位、回退和复盘？

当这四个问题都有答案，AI 生成才真正进入软件交付。

## 延伸阅读

- [Vibe Coding 教程](/tutorial/)
- [2025 Stack Overflow Developer Survey: AI](https://survey.stackoverflow.co/2025/ai)
- [DORA 2025 AI-assisted Software Development](https://dora.dev/research/2025/dora-report/)
- [把提示词变成可维护的 Agent 操作协议](/posts/agent-skills-as-operating-protocols/)</content:encoded></item><item><title>Tau：为什么编码 Agent 需要一层桌面工作台</title><link>https://ninthless.github.io/posts/tau-gui-agent-desktop-context/</link><guid isPermaLink="true">https://ninthless.github.io/posts/tau-gui-agent-desktop-context/</guid><description>我没有在 Tau-gui 中重写 Pi，而是在 RPC 与 SDK 之上处理会话、上下文和桌面交互。</description><pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate><content:encoded>我喜欢终端执行命令的直接，但它不天然适合展示一段持续数小时、包含多个会话分支、工具调用、权限请求和上下文压缩的 Agent 工作。

因此我做了 [Tau-gui](https://github.com/Ninthless/Tau-gui)，一个基于 Electron/React 的 Pi Coding Agent 桌面工作台。我认为自己最正确的架构决定，是没有重写 Agent 内核：会话循环使用 Pi 的 RPC，包管理使用 Pi SDK，我只在 Tau 中负责桌面交互、状态协调和可见性。

## 项目时间线

### 2026-05-19：建立桌面工作区

我先把 Pi 会话接入 Electron，并提供聊天、侧栏和设置界面。

### 2026-05-20：界面重建与会话连续性

我把项目迁移到 shadcn/ui，重写聊天区、侧栏和设置，同时加入 `--continue` 以减少无意创建新会话。随后我又压缩界面密度，使工具输出与长对话更适合持续阅读。

### 2026-05-22：从“能聊天”转向“能管理 Agent 状态”

我用一次集中修复处理了错误 RPC 命令、会话树、HTML 导出、扩展错误、自动重试、设置同步、并发启动竞争和会话切换不刷新等问题，并加入上下文用量条和乐观会话切换。

同一天后续提交处理 EPIPE，防止父子进程管道断开时桌面应用崩溃，并进一步调整阅读间距。

这几天的提交说明：Agent GUI 的复杂度主要来自异步状态，而不是消息气泡。

## 为什么选择 RPC + SDK

[Pi RPC 模式](https://pi.dev/docs/latest/rpc) 通过 stdin/stdout 使用严格 JSONL：命令按行输入，响应带关联 ID，Agent 事件持续流式输出。它适合嵌入 IDE 或自定义界面。

Tau 可以通过 RPC 处理提示、流式消息、工具事件、会话和设置；而 Pi RPC 尚未暴露的包安装、更新和移除，则通过 [Pi SDK](https://pi.dev/docs/latest/sdk) 完成。

这种混合并不漂亮，但边界诚实：

```text
Pi Runtime：模型、工具、会话与协议
Tau Main：子进程、RPC、凭证桥接、包操作
Tau Renderer：聊天、侧栏、设置、状态反馈
```

重写运行时会带来短期控制感，却要永久追赶模型提供商、会话格式、扩展协议和工具行为。复用公开接口把维护成本留在正确的所有者处。

## 严格 JSONL 为什么会成为产品问题

RPC 文档明确要求只按 `\n` 分隔记录，允许输入端去除 `\r`，并警告某些通用行读取器会把合法的 Unicode 分隔符误当换行。

这类协议细节一旦处理错误，用户看到的不是“解析器不合规”，而是消息随机断裂、事件丢失或会话卡住。GUI 层必须把协议错误转换成可理解状态，而不能静默吞掉。

同时，stdout 是协议通道，普通日志不能随意写入；子进程退出、EPIPE、第一条输出超时和并发重启都要有明确生命周期。

## 会话切换不是换一个数组

Agent 会话通常包含消息、分支、模型、思考级别、压缩状态、工作目录、扩展状态和上下文统计。切换会话时，界面至少经历：

1. 用户选择目标会话。
2. 侧栏立即反馈选择，避免点击无响应。
3. 后台向 Runtime 请求切换。
4. 清理或隔离旧流式事件。
5. 加载新消息与状态。
6. 失败时恢复原选择并解释原因。

Tau 后期加入“乐观高亮 + 加载状态”，本质上是在处理感知延迟。但乐观更新必须有回滚，否则界面会显示一个并未真正激活的会话。

## 上下文用量应该可见

编码 Agent 的上下文窗口不是无限内存。长对话、工具输出和大文件读取会逐渐逼近限制，随后触发压缩或丢失早期细节。

Tau 把 context usage 放在标题栏，并用颜色提示占用程度。这不是装饰性指标，它帮助用户决定何时：

- 开新会话而不是继续追加。
- 把稳定规则写入项目文件或 Skill。
- 压缩当前分支。
- 删除无关输出，重新提供关键上下文。

一个好的 Agent UI 不只展示 Agent 说了什么，还要展示 Agent 当前还能可靠记住多少。

## 工具调用需要自己的交互语法

文本聊天只需要发送与接收。编码 Agent 还会发起选择、确认、输入、编辑器、权限和工具结果。每一种请求都可能阻塞 Runtime。

界面必须明确区分：

- 正在生成文本。
- 正在运行工具。
- 等待用户批准。
- 工具失败但 Agent 可继续。
- Runtime 已断开，输入不会被处理。

如果全部压成一个旋转图标，用户就无法判断应该等待、批准还是重启。

## 桌面 Agent 工作台的质量标准

一个 Agent GUI 至少要通过这些场景：

| 场景 | 期望行为 |
| --- | --- |
| 流式文本中切换会话 | 旧事件不会污染新会话 |
| 子进程意外退出 | 明确显示断开并允许恢复 |
| 工具请求等待确认 | 输入区与阻塞状态清晰 |
| 上下文接近上限 | 提前提示压缩或新会话 |
| 包安装失败 | 保留原状态并显示原因 |
| 快速连续切换 | 不产生并发重启竞态 |

Agent UI 的核心不是“把终端变好看”，而是让隐形状态变得可观察、可操作、可恢复。

## 相关链接

- [Tau-gui](https://github.com/Ninthless/Tau-gui)
- [Pi RPC Documentation](https://pi.dev/docs/latest/rpc)
- [Pi SDK Documentation](https://pi.dev/docs/latest/sdk)
- [把提示词变成可维护的 Agent 操作协议](/posts/agent-skills-as-operating-protocols/)</content:encoded></item><item><title>把提示词变成可维护的 Agent 操作协议</title><link>https://ninthless.github.io/posts/agent-skills-as-operating-protocols/</link><guid isPermaLink="true">https://ninthless.github.io/posts/agent-skills-as-operating-protocols/</guid><description>我如何把反复使用的提示词整理成可维护、可触发、可验证的 Agent 操作协议。</description><pubDate>Fri, 15 May 2026 00:00:00 GMT</pubDate><content:encoded>提示词可以帮我解决一次对话，操作协议解决的是接下来一百次相似任务。

我最初把 [agent-skills](https://github.com/Ninthless/agent-skills) 当作个人工作方法的集合。到 2026 年 7 月，我已经整理出 9 个面向不同场景的技能：需求规范化、领域调研、高约束编码、无代码注释、Git 检查点、自由职业订单判断、PowerShell 安全命令、Xposed 模块开发和用户界面构建。

外观看起来，它们仍然是 Markdown 文件。但我真正投入精力的部分发生在 Markdown 之外：触发边界、评估样例、参考资料、辅助脚本和可验证的质量门槛。

## Agent Skill 与普通提示词的差别

[Agent Skills 开放规范](https://agentskills.io/specification) 把 Skill 定义为一个以 `SKILL.md` 为入口的目录，并允许附带脚本、参考资料和资产。这个目录结构很重要，因为它把四种不同职责分开了：

| 层次 | 负责回答的问题 |
| --- | --- |
| 描述与触发 | 什么时候应该使用，什么时候不应该使用 |
| 主工作流 | Agent 应按什么顺序行动 |
| 参考资料 | 哪些细节只在相关任务中加载 |
| 评估与脚本 | 如何证明触发正确、结果达标 |

普通提示词往往只描述“做什么”。技能还必须描述“不做什么”“什么时候停”“证据不足时如何降低结论”。一旦缺少这些边界，所谓复用只是在稳定地重复同一种偏差。

## 仓库演进时间线

### 2026-05-15：先把个人规则写下来

第一次提交时，我先建立技能目录，把脑中的偏好变成可阅读文件，让另一个 Agent 或未来的我能够复用。

但这时的技能仍然接近长提示词。它们能提醒模型，却还不能证明自己在正确场景触发，也无法防止描述越来越宽。

### 2026-05-22 至 05-31：建立“先读再改”的纪律

我逐步给 `high-constraint-coding` 加入强制阅读现有代码、根因诊断、局部风格匹配和修改前的一句话行为说明，也让 `git-checkpoint-push` 在提交前检查状态、远端、分支差异和无关修改。

这个阶段形成了一条关键原则：**高质量不是在输出结尾补一句“已测试”，而是在行动顺序里提前安排证据。**

### 2026-06：开始处理触发与环境差异

6 月，我把提交重点放在元数据、触发描述、平台兼容和 PowerShell 命令安全上。我开始明确承认运行环境不是背景噪音：同一句命令在 Bash 与 PowerShell 中可能有不同语义，未引用路径、换行和转义都可能改变结果。

这也是技能工程与“万能系统提示”的分界。越想覆盖所有场景，越需要明确平台条件和反例。

### 2026-07-14：触发边界成为测试对象

我为多个技能扩展了 trigger eval。测试不只问“应该触发时是否触发”，还要问“相似但不适用的请求会不会误触发”。

这是维护技能时最容易忽略的一面。召回率过低会漏掉方法，召回率过高则会让每个任务都背上不必要的流程。技能描述不是宣传文案，而是一个分类器的输入。

### 2026-07-19：UI 技能从审美建议升级为证据门

我在一天内连续为 `build-user-facing-ui` 加入证据型质量门、样式多样性评估、完整界面转换模式，以及“保留产品契约但不继承旧视觉结构”的重构规则，也加入了视觉指纹比较和证据校验脚本。

这次演进说明：对视觉任务，仅靠“高级、现代、好看”无法形成稳定标准。需要可检查的视口、状态、可访问性和渲染结果，才能把审美判断变成工程流程。

## 一个可维护技能至少需要四个边界

### 触发边界

描述必须同时包含正向和负向条件。例如“构建用户界面”不应该覆盖纯后端 API；“高约束编码”不应该把概念解释变成冗长的代码审计。

### 权限边界

读代码、修改文件、创建提交、推送远端和部署是不同权限。技能不能因为用户说“完成它”就自动扩大到发布或外部通信。

### 证据边界

没有运行测试，就不能写“已验证”；只检查一个症状，也不能声称共享逻辑没有回归。结论强度必须和证据强度相同。

### 上下文边界

参考资料不应全部塞进主文件。Agent 需要先看到短而清晰的路由，再按任务加载细节。否则技能越完善，反而越占用上下文并稀释关键约束。

## 为什么技能需要回归测试

技能文件本质上也是程序：输入是用户请求与环境上下文，输出是行动顺序和约束。修改描述可能修复一个场景，同时破坏另一个场景。

最小测试集应该包含：

- 明确应触发的请求。
- 明确不应触发的请求。
- 容易混淆的边界请求。
- 会诱导 Agent 扩大权限的请求。
- 缺少证据但容易过度宣称的请求。

这与传统单元测试不完全相同，因为模型输出具有变化。但评估仍能检查结构性事实：是否先读文件、是否询问真正阻塞的问题、是否运行验证、是否避免无关重构。

## 从个人规则到公共资产

个人技能可以非常有偏好，但公共技能还要解决可移植性。2026 年，Agent Skills 已经被多个编辑器和编码 Agent 接受；例如 [VS Code 也使用同一开放格式](https://code.visualstudio.com/docs/agent-customization/agent-skills)。这让技能有机会跨工具复用，也放大了模糊指令的风险。

一个值得发布的技能，至少应该让使用者回答这些问题：

1. 它在哪些任务上比默认 Agent 更好？
2. 它会读取或执行什么？
3. 它可能在哪里失败？
4. 更新后如何确认旧场景没有退化？

如果这些问题没有答案，技能仍然只是个人备忘录。备忘录没有问题，但不应该被包装成可靠协议。

## 最后一个判断

Agent 能力越来越强时，人更不应该把所有规则写成“永远这样做”。好的技能不是把 Agent 锁死，而是在高风险节点增加检查，在低风险节点保持流动。

真正值得维护的不是提示词长度，而是决策质量：何时触发、读什么证据、允许做什么、如何验证、何时承认未知。

## 延伸阅读

- [Agent Skills Specification](https://agentskills.io/specification)
- [agent-skills 仓库](https://github.com/Ninthless/agent-skills)
- [Vibe Coding 不是一句提示词，而是一条交付闭环](/posts/vibe-coding-delivery-loop/)
- [Tau：为什么编码 Agent 需要一层桌面工作台](/posts/tau-gui-agent-desktop-context/)</content:encoded></item><item><title>改系统字体之前，先把恢复路径设计出来</title><link>https://ninthless.github.io/posts/hybridfont-recovery-first/</link><guid isPermaLink="true">https://ninthless.github.io/posts/hybridfont-recovery-first/</guid><description>我在 HybridFont 中从实验性重定向转向 systemless 覆盖，并把恢复路径放到字体替换之前。</description><pubDate>Mon, 27 Apr 2026 00:00:00 GMT</pubDate><content:encoded>我开始做系统字体模块时，它看起来只是替换几个字体文件。真正实现后，我发现它会穿过 Android 字体回退、ROM 定制、WebView、启动阶段、Root 模块挂载和应用兼容等多层边界。错误不一定表现为“字体不好看”，也可能是 SystemUI 崩溃、浏览器文字异常或设备无法正常进入桌面。

我在 [HybridFont](https://github.com/Ninthless/HybridFont) 里最值得记录的，不是 Inter 与 Noto Sans SC 的组合，而是我在两天内改变了风险模型：从尝试更强的全局重定向，转向更保守的 systemless 资源覆盖与默认禁用测试包。

## 项目时间线

### 2026-04-27：建立可构建、可发布的模块

我先补上自动构建与发布，让模块能够生成可刷入包，并通过版本标签发布产物。

### 2026-04-28：尝试并废弃 Zygisk 路径

我一度加入 Zygisk 字体重定向包，随后在同一天将它标记为废弃并移除文档中的 Xposed 表述。原因很关键：全局文件访问 Hook 可能破坏 WebView 或嵌入式网页内容。

这不是“方案没写好”，而是方案的风险半径过大。继续修补 Hook 可能让单个设备工作，却很难证明跨 ROM、跨应用的稳定性。

### 2026-04-29：转向 XML 映射资源与安全包

我随后增加字体映射打包，并明确避免覆盖系统字体 XML。最终我发布了两种包：完整兼容包，以及默认禁用、跳过更激进 fallback 文件的 `safe-disabled` 包。

短时间内连续撤回高风险方案，是这个项目最有价值的工程动作。

## 为什么字体替换比想象中复杂

Android 并不是简单地从 `/system/fonts` 找一个同名字体。系统存在字体家族、权重、脚本和 fallback 链，不同 Android 版本与厂商 ROM 还会调整 XML 配置。

[Android 官方字体文档](https://source.android.com/docs/core/fonts/custom-font-fallback) 显示，Android 15 起可变字体配置进一步转移到 `font_fallback.xml`，而字体更新本身也需要签名与完整性验证。字体文件被官方视为需要谨慎校验的资源，而不是任意静态素材。

这意味着“把所有 `font*.xml` 一起覆盖”虽然直接，却会把 ROM 原有映射整个替换掉。它可能修复一个家族，同时破坏厂商新增的 emoji、区域字体或应用兼容路径。

## Systemless 解决了什么，没有解决什么

[KernelSU 模块机制](https://kernelsu.org/guide/module.html) 与 [Magisk 模块机制](https://topjohnwu.github.io/Magisk/guides.html) 都允许在不直接写入真实系统分区的情况下覆盖文件。KernelSU 当前还通过 metamodule 提供 `/system` 挂载能力。

Systemless 带来两个重要优势：

- 卸载或禁用模块后可以恢复原系统文件视图。
- 模块内容和真实系统分区分离，降低永久破坏风险。

但它没有自动保证兼容。覆盖层仍会在启动和应用运行时改变系统看到的资源；错误映射仍可能导致启动循环或界面崩溃。可逆不等于无风险，只是让恢复成为可能。

## 默认禁用包是一种发布策略

`safe-disabled` 包安装后不立即生效，用户先重启确认模块可识别，再手动启用并第二次重启。同时，它避免替换差异更大的 `DroidSansFallback*` 路径。

这套流程把一次高风险变更拆成两次可观察状态：

```text
安装但不启用 → 确认模块存在 → 手动启用 → 检查关键应用
```

它牺牲了一点“一键完成”，换来更清楚的故障定位。如果第一次重启就异常，问题偏向安装或模块框架；如果启用后异常，问题偏向字体覆盖。

## 恢复路径必须在安装前可见

HybridFont 文档在安装章节直接给出恢复方式：通过 KernelSU 安全模式或 Recovery 创建模块的 `disable` 文件，也可以移除对应模块目录。

恢复说明应该满足四个条件：

1. 不依赖系统正常进入桌面。
2. 路径与模块 ID 明确，不让用户临时猜测。
3. 在安装前就能阅读，而不是藏在 Issue 中。
4. 能区分临时禁用与彻底删除。

任何会影响启动、网络、存储、权限或系统界面的工具，都应该在功能文档之前写恢复路径。

## 兼容性不是一个布尔值

字体模块至少要跨越这些维度：Android 版本、ROM、Root 方案、挂载实现、WebView、语言脚本、可变字体、emoji 与厂商字体映射。

因此，“支持 Android 12+”并不是完整兼容声明。更有用的测试记录应该包含设备/ROM 版本、Root 与 metamodule 版本、启用的包类型、关键应用结果和恢复是否有效。

模块还应该避免与多个同时覆盖 `font*.xml` 的方案叠加。两个单独可用的模块，组合后可能互相覆盖。

## Recovery-first 的通用原则

HybridFont 的经验可以迁移到更多系统工具：

- 高风险功能先做默认关闭的保守发布。
- 一次只改变一个系统层，便于定位问题。
- 版本标签必须与产物元数据一致。
- 不把实验方案长期留在主下载路径。
- 恢复步骤与安装步骤一起测试。
- 对无法覆盖的 ROM 差异明确写“不保证”。

优秀的系统修改不是永不失败，而是把失败限制在可识别、可禁用、可恢复的范围内。

## 相关链接

- [HybridFont](https://github.com/Ninthless/HybridFont)
- [Android Custom Font Fallback](https://source.android.com/docs/core/fonts/custom-font-fallback)
- [Windows 小工具最先要设计的不是功能，而是边界](/posts/windows-utilities-boundaries/)</content:encoded></item><item><title>Windows 小工具最先要设计的不是功能，而是边界</title><link>https://ninthless.github.io/posts/windows-utilities-boundaries/</link><guid isPermaLink="true">https://ninthless.github.io/posts/windows-utilities-boundaries/</guid><description>我从 ACEOptimizer 与 Emergency-Stop 中总结，Windows 工具如何用最小权限、明确非目标和可恢复更新建立信任。</description><pubDate>Mon, 30 Mar 2026 00:00:00 GMT</pubDate><content:encoded>我做 Windows 小工具时，需求常常很小：降低某个进程的资源影响，或根据键盘状态显示一个训练提示。真正让我花时间的部分却不是窗口和按钮，而是系统权限、平台规则、误报、更新链路和用户预期。

我做的 [ACEOptimizer](https://github.com/Ninthless/ACEOptimizer) 与 [Emergency-Stop](https://github.com/Ninthless/Emergency-Stop) 解决的问题不同，但最后都收敛到同一种设计方法：**先写清楚工具绝对不会做什么，再决定功能如何实现。**

## 两个项目的时间线

### ACEOptimizer：从单一优化动作到可验证发布

- **2026-03-30**：初始版本与自动发布流程建立。
- **2026-04-17**：从游戏白名单转为直接识别目标进程，减少不必要的耦合。
- **2026-05-30**：加入更新检查，同时开始处理下载隔离、SHA-256 确认、权限级别和 Defender 误报面。
- **2026-05-31**：补充取消、临时目录清理、限流处理与 20 个更新/版本/亲和性测试。
- **2026-07-10**：升级为 Ed25519 签名更新源，校验标签与应用版本一致，并拒绝无效签名发布。

我始终把 ACEOptimizer 的行为限定在 Windows 公开的进程优先级与 CPU 亲和性设置，不修改游戏文件、反作弊文件、驱动或系统服务。

### Emergency-Stop：从覆盖层原型到诚实的训练工具

- **2026-06-29**：完成键盘状态与屏幕覆盖层原型。
- **2026-06-30**：增加主题、国际化、准星设置、状态精度修复和 Velopack 更新。
- **2026-07-03**：完善更新失败反馈。

我让 Emergency-Stop 只读取本机键盘输入并绘制透明置顶窗口，不读取游戏进程、不注入图形 API、不抓包、不模拟按键，也不执行宏。我把急停状态明确标成可调整的时间估算，不声称复刻未公开的游戏物理。

## 最小权限不是口号，而是 API 选择

Windows 的进程对象有细分访问权。[Microsoft 的进程安全文档](https://learn.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights) 明确建议只申请操作需要的最小权限，而不是默认请求 `PROCESS_ALL_ACCESS`。

这对小工具有直接意义：

- 只设置优先级，就不应申请读写进程内存的权限。
- 只显示键盘状态，就不需要打开目标游戏进程。
- 只绘制外部覆盖层，就不需要注入渲染管线。

更少的权限意味着更小的错误面、更少的安全软件疑虑，也更容易向用户解释工具行为。

## “优化”必须承认系统调度的复杂性

[SetPriorityClass 官方文档](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setpriorityclass) 提醒，高优先级可能占用几乎全部 CPU，而单纯调整 CPU 调度优先级也不能解决磁盘和内存造成的系统响应问题。

因此，ACEOptimizer 的实现只能被描述为“改变特定进程的调度条件”，不能承诺所有机器都降低总体占用、提升帧率或完全没有副作用。CPU 拓扑、目标进程行为、系统版本和平台更新都会改变结果。

一个可信的系统工具应该把兼容性写成条件句，而不是营销结论。

## 输入监听也需要边界

Emergency-Stop 使用 Windows Raw Input 与按键状态轮询来提高键盘状态读取稳定性。[Raw Input](https://learn.microsoft.com/en-us/windows/win32/inputdev/raw-input) 是 Windows 为 HID 设备提供的公开输入路径，应用注册后通过窗口消息接收设备数据。

但“能够读取键盘”不等于“应该记录键盘”。训练覆盖层只需要当前移动键状态，不需要保存输入历史、上传数据或监听与功能无关的按键。数据最小化同样适用于本地工具。

## 非目标清单为什么重要

对于与游戏、系统权限或覆盖层有关的软件，用户往往先问“它会不会做危险的事”。README 中的非目标清单比功能列表更能建立信任：

| 风险问题 | 明确边界 |
| --- | --- |
| 是否读取进程内存 | 否 |
| 是否注入游戏/图形进程 | 否 |
| 是否模拟输入或自动操作 | 否 |
| 是否修改受保护文件 | 否 |
| 是否保证平台长期允许 | 否，规则可能变化 |

非目标不是法律护身符，也不能代替平台确认。它的工程作用是防止后续功能在“顺便实现”中越过最初边界。

## 更新链路也是产品功能

桌面工具一旦提供自动更新，就获得了修改用户机器上可执行文件的能力。此时“从 GitHub 下载最新版本”远远不够。

ACEOptimizer 的后期提交把更新链拆成多个可验证条件：版本标签必须与项目版本一致，更新清单需要 Ed25519 签名，安装包在进入安装流程前需要验证，私钥不进入仓库，同版本不重复提示。

这套设计仍不等于操作系统级代码签名。仓库也明确说明二进制没有 Authenticode 签名，SmartScreen 可能警告。把未完成的信任层写出来，比隐藏警告更专业。

## 诚实精度比假精确更有价值

Emergency-Stop 无法读取游戏内部速度，也没有官方完整移动曲线。因此它用键位变化与可调时间窗估算 `MOVE`、`STOP`、`BRAKE` 和 `READY`。

这里最重要的产品决定不是把数字调得更像，而是把状态标为“训练提示”而非“真实物理状态”。用户可以在训练场校准，项目不把未知包装成精确模型。

这种诚实同样适用于性能优化、网络诊断和硬件监控：只要测量链缺少关键变量，就应该降低结论强度。

## 可复用的边界清单

开发类似 Windows 工具前，可以先回答：

1. 最小操作对象是什么，能否不打开其他进程？
2. 需要哪些具体权限，哪些权限明确不需要？
3. 工具会不会记录、上传或长期保存输入？
4. 平台规则变化时，功能如何降级或停止？
5. 更新包如何验证，失败后如何清理和回退？
6. 哪些结果只是估算，哪些有可重复测量证据？

小工具不需要复杂架构，但需要清晰边界。越接近系统、游戏或自动更新，边界越应该成为第一等功能。

## 相关项目

- [ACEOptimizer](https://github.com/Ninthless/ACEOptimizer)
- [Emergency-Stop](https://github.com/Ninthless/Emergency-Stop)
- [从 80 个仓库看我的工程轨迹](/posts/github-80-repositories-timeline/)</content:encoded></item><item><title>给不断变化的 Web 应用做汉化，难点不是翻译</title><link>https://ninthless.github.io/posts/resilient-web-localization/</link><guid isPermaLink="true">https://ninthless.github.io/posts/resilient-web-localization/</guid><description>我在维护 Postman-Web-i18n 时发现，动态 DOM、状态原子性、规则安全和扩展权限比翻译字典更难。</description><pubDate>Fri, 17 Oct 2025 00:00:00 GMT</pubDate><content:encoded>刚开始做汉化时，我以为把英文字符串替换成中文只需要一个字典。真正让我反复修改的，是如何让一个持续更新、动态渲染的 Web 应用长期稳定地显示中文：DOM 生命周期、组件结构、状态切换、用户规则、浏览器权限和安全边界都不能绕过。

我在 2025 年 10 月建立 [Postman-Web-i18n](https://github.com/Ninthless/Postman-Web-i18n)，最初就放入内容脚本、翻译文件、设置页、弹窗和构建流程。真正让我意识到“能翻译”不等于“可以维护”的节点，是 2026 年 5 月的一次质量审计。

## 时间线：14 个问题如何暴露系统结构

### 2025-10-17：建立完整扩展骨架

初始版本里，我覆盖了 Chrome/Edge、动态内容翻译、多语言文件、自定义规则、设置导入导出和自动发布。功能面已经很宽，但功能越多，状态组合也越多。

### 2026-05-25：P0 安全与数据问题

第一批修复里，我处理了四类基础问题：

- 用户开关中的 `false` 被默认值逻辑覆盖。
- 自定义规则通过 `innerHTML` 渲染，形成 XSS 风险。
- 导入翻译的入口存在，但缺少完整实现和活动页面通知。
- 代码调用标签页 API，却没有在清单中声明对应权限。

它们看似分散，实际都来自同一个问题：界面状态、持久化状态、扩展权限和页面状态没有形成闭环。

### 2026-05-25：P1 原子性与规则校验

第二批修复为语言切换增加状态快照与失败回滚，避免加载失败后页面变成空白；自定义规则在保存前检查选择器白名单、非空键值和正则合法性；文本替换从局部字符串操作改为明确赋值，减少翻译污染。

### 2026-05-25：P2 兼容与完整性

最后一批修复处理占位符全量替换、子元素保留、动画时序、语言兼容和重复值检查范围。最终 14 个 P0/P1/P2 问题在同一个审计分支中合并。

## 动态页面不是一棵静止的 DOM 树

现代 Web 应用会在路由切换、请求返回、虚拟列表滚动和组件状态变化时持续重建节点。扩展不能只在 `DOMContentLoaded` 后扫描一次。

Chrome 官方把 [content script](https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts) 运行在隔离环境中。它可以读取和修改页面 DOM，但与宿主页面的 JavaScript 世界分离。这个边界保护了变量作用域，却没有自动解决动态内容、重复翻译和组件更新问题。

可靠的翻译器需要区分：

- 首次扫描与后续增量节点。
- 文本节点、占位符、标题和无障碍标签。
- 原文、已翻译文本和用户编辑内容。
- 整段匹配与局部匹配。
- 被框架销毁后重新创建的节点。

如果没有幂等性，同一节点被观察两次就可能二次翻译；如果替换粒度太粗，给 `label.textContent` 赋值可能顺便删除图标或子组件。

## 本地化切换必须是一笔事务

语言切换通常包含多个步骤：读取目标语言、更新内存状态、保存设置、重翻页面、通知其他扩展页面。任何一步失败，都可能留下“设置显示中文，但页面仍是英文”或“旧字典已清空，新字典未加载”的半完成状态。

更稳妥的模型是：

```text
保存旧状态 → 加载并校验新语言 → 一次性提交 → 通知页面
                 失败 ↓
              恢复旧状态
```

这就是原子性。它不要求数据库，只要求把状态变化当作不可分割的用户操作。

## 自定义规则是代码输入

允许用户输入 CSS 选择器和正则表达式，会显著提高扩展能力，也会把配置变成一种小型程序。

规则至少需要验证：

- 选择器类型是否在允许集合中。
- 正则是否能编译，是否可能造成异常开销。
- 翻译键是否存在或明确属于自定义命名空间。
- 显示规则时是否使用安全 DOM API。
- 导入数据是否符合预期结构与大小限制。

Chrome 的[扩展安全指南](https://developer.chrome.com/docs/extensions/develop/security-privacy/stay-secure) 建议把来自内容脚本和用户输入的数据视为不可信，并最小化权限。扩展拥有比普通网页更高的能力，设置页中的一个 XSS 不能被当作普通显示瑕疵。

## Manifest V3 改变了默认假设

[Manifest V3](https://developer.chrome.com/docs/extensions/develop/migrate/what-is-mv3) 不允许扩展执行远程托管代码，并用 Service Worker 替代长期后台页面。它的方向是让代码进入可审查包、减少常驻资源，并让权限更明确。

对于本地化扩展，这意味着：

- 翻译逻辑应随扩展打包，不能从远端下载脚本后执行。
- 远端更新适合传输数据，但数据仍需严格校验。
- 主机权限只覆盖确实需要翻译的站点。
- 新增 `tabs`、`scripting` 等权限时，要能说明具体用途。

权限清单既是技术配置，也是用户可见的信任声明。

## 翻译质量不只是一致率

翻译文件可以通过缺失键、重复值和占位符检查，但最终质量还包括界面语义：

- 按钮文本是否在动作语境中自然。
- 术语在请求、集合、环境和测试等上下文中是否一致。
- 中文变长后是否挤压布局。
- 快捷键、变量名、代码和用户数据是否被误翻。
- 屏幕阅读器使用的 `aria-label` 是否同步。

因此，自动检查适合发现结构问题，真实页面巡检负责发现语境问题。两者不能互相替代。

## 一个稳定翻译扩展的最小测试矩阵

| 维度 | 至少检查 |
| --- | --- |
| 页面生命周期 | 首次加载、路由切换、弹窗、延迟内容 |
| 文本结构 | 纯文本、含图标子元素、占位符、ARIA |
| 语言状态 | 首次选择、快速切换、加载失败、重启恢复 |
| 自定义规则 | 合法、非法正则、危险文本、重复规则 |
| 权限 | 新装、升级新增权限、无权限降级 |
| 浏览器 | Chrome、Edge、目标最低版本 |

“翻译了多少字符串”只能衡量覆盖率。一个汉化扩展真正的可靠性，取决于页面变化时是否保持结构、失败时是否恢复状态、用户规则是否被安全处理。

## 相关链接

- [Postman-Web-i18n](https://github.com/Ninthless/Postman-Web-i18n)
- [Chrome Extensions 文档](https://developer.chrome.com/docs/extensions/)
- [科研代码不是结果文件夹，而是一条证据链](/posts/research-repositories-evidence-chain/)</content:encoded></item><item><title>科研代码不是结果文件夹，而是一条证据链</title><link>https://ninthless.github.io/posts/research-repositories-evidence-chain/</link><guid isPermaLink="true">https://ninthless.github.io/posts/research-repositories-evidence-chain/</guid><description>我从自己的图像复原、微电网、负荷预测、ETA 与信号估计项目中，整理出一条可追溯的研究证据链。</description><pubDate>Sun, 08 Jun 2025 00:00:00 GMT</pubDate><content:encoded>我以前也很容易被研究代码制造的错觉说服：图已经生成，指标已经写进 README，项目似乎就完成了。后来我越来越在意另一个问题：其他人能否从原始输入出发，沿着同一条路径得到足以支持相同结论的结果。

在审计 GitHub 仓库时，我检查了图像复原、微电网调度、EV 充电负荷、行程时间估计和信号周期估计等项目。部分研究仓库是私有的，因此本文只公开通用方法，不公开私有仓库名称、代码、数据或特定交付细节。公开样本使用 [Single-lens](https://github.com/Ninthless/Single-lens) 作为例子。

## 先区分四种“完成”

| 层级 | 实际含义 |
| --- | --- |
| 能运行 | 当前机器能执行主程序 |
| 能重复 | 原作者在同一条件下能再次得到结果 |
| 能复现 | 其他人使用提供的材料能得到相符结论 |
| 能复用 | 代码、数据与文档足以支持新实验 |

[ACM Artifact Review and Badging](https://www.acm.org/publications/policies/artifact-review-and-badging-current) 也把 artifact 的可用、功能性、可复用性与结果复现分开评价。一个 GitHub 链接只能证明“有材料”，不能自动证明结果可复现。

## 仓库审计中反复出现的五类研究

### 图像复原：指标之前先验证成像假设

我在 Single-lens 中实现了维纳滤波、分块 PSF、Lucy–Richardson、PSNR/SSIM、PSF 检查和性能比较。对我来说，最有价值的部分不是某个最高分，而是发现 PSF 有效尺度与图像尺寸存在明显不匹配，并尝试解释算法上限。

图像复原的证据链应该是：

```text
PSF 来源与物理含义 → 输入预处理 → 算法参数 → 输出图像 → 指标 → 失败解释
```

如果 PSF 与目标图像不匹配，再复杂的算法也可能只是在优化错误模型。

### 微电网强化学习：合成数据不能伪装成现场数据

我在相关项目中实现了 DDQN、分阶段奖励、强约束动作空间和 Greedy 基线。其中一个仓库明确说明论文现场数据未公开，因此我使用正弦曲线与噪声生成仿真场景，并提醒绝对成本和回报会不同。

这句限制比“完整复现”更重要。合成数据可以验证算法行为，却不能直接支持对真实电网经济性的结论。

强化学习项目还必须报告训练/验证/测试场景划分、随机种子、回合数、约束违反率和多次运行波动。只展示最好一次训练曲线，会高估稳定性。

### 负荷预测：预测值进入调度后，误差会继续传播

EV 负荷项目把概率预测的 P50 接到后续微电网调度，并保留 P10/P90 作为不确定性范围。这个结构比单独报告 MAE 更接近真实决策链。

但一旦预测成为另一个模型的输入，就需要同时检查：

- 基础负荷与 EV 负荷是否被重复叠加。
- 训练与测试时间窗口是否泄漏未来信息。
- 调度结果对预测区间有多敏感。
- 使用真实值与预测值时，结论差异多大。

模型串联后，每个接口都是新的假设。

### ETA：首先排除标签泄漏

行程时间估计项目包含路径编码、时间上下文、图结构、注意力偏置和多种损失。仓库特别强调，时间间隔先验来自训练集统计，而不是样本真实未来时间。

这说明研究仓库需要把“没有泄漏”写进设计证据，而不是只写在结论里。最有价值的诊断往往包括未见路段、未见 OD、时间分布漂移、长尾路径和简单历史均值 baseline。

如果复杂模型没有稳定超过朴素均值，应该先调查数据划分与任务定义，而不是继续堆层数。

### 信号周期估计：论文基线与工程改进必须分开

周期估计项目同时保留论文基线、论文对齐仿真和多参数共识改进。它没有把改进方案伪装成原论文，而是分别输出结果和比较报表。

复现研究中，“哪里与论文相同，哪里是自己的工程改进”必须能从入口、配置和结果目录中看出来。否则即使数字更好，也无法判断是复现成功还是任务已经改变。

## 一条合格证据链的七个环节

### 1. 数据来源

记录数据来自公开数据集、真实采集、论文附件还是合成生成。对于私有数据，至少说明字段、时间范围、脱敏和不可公开的原因。

### 2. 数据版本

文件名不等于版本。需要校验和、下载日期、生成脚本或不可变链接。空间数据与在线 API 尤其容易在未来发生变化。

### 3. 划分策略

随机划分、按时间划分、按主体划分会回答不同问题。时间序列随机打散可能把未来模式泄漏到训练集；同一路段或同一用户跨集合也可能造成隐性重叠。

### 4. 基线

至少包含一个简单、可解释、计算成本低的 baseline。均值、持久性、线性模型、Greedy 或传统滤波都可以。没有 baseline，复杂模型的指标没有参照物。

### 5. 配置与环境

依赖版本、硬件、随机种子、训练预算、停止条件和关键超参数必须进入文件，而不是只存在于命令历史。

### 6. 原始结果与制图

图表应该可以从原始结果重新生成。保存 CSV/JSON、训练历史和绘图脚本，比只提交 PNG 更接近可复现 artifact。

### 7. 结论边界

明确哪些结论只适用于当前数据、哪些经过消融支持、哪些仍是推测。结果不理想也应该保留，因为失败能暴露模型假设。

## README 中最危险的词

“完整复现”“达到理论极限”“专业级”“显著提升”都不是不能用，但它们需要更强证据。

更稳妥的写法是：

- “按论文描述实现主要算法路径，未公开数据部分使用合成场景替代。”
- “在当前两组样本与参数下，方法 A 的 PSNR 高于基线 B。”
- “结果接近本项目计算的上界；该上界依赖当前噪声与 PSF 假设。”
- “尚未由独立环境复现。”

结论越具体，越容易被验证，也越有研究价值。

## 最小研究仓库模板

```text
README：问题、范围、数据来源、运行顺序、限制
configs：所有实验配置
src：模型与数据逻辑
scripts：训练、评估、制图
tests：关键公式、数据约束、泄漏检查
artifacts/raw：原始指标与日志
artifacts/figures：由 raw 生成的图
environment：锁定依赖与硬件说明
```

NeurIPS 的 [Paper Checklist](https://neurips.cc/public/guides/PaperChecklist) 把可复现性、透明度、伦理和社会影响纳入提交检查。对个人研究仓库而言，不需要完整复制会议流程，但可以借用同一个原则：让每个主要结论都能追溯到数据、配置和输出。

科研代码的目标不是让目录看起来丰富，而是让怀疑者能够沿着证据链检查你。

## 相关链接

- [Single-lens](https://github.com/Ninthless/Single-lens)
- [ACM Artifact Review and Badging](https://www.acm.org/publications/policies/artifact-review-and-badging-current)
- [从 80 个仓库看我的工程轨迹](/posts/github-80-repositories-timeline/)</content:encoded></item><item><title>课程项目什么时候才算工程资产</title><link>https://ninthless.github.io/posts/course-projects-to-assets/</link><guid isPermaLink="true">https://ninthless.github.io/posts/course-projects-to-assets/</guid><description>我回看 TaskFlow、Tradinghelper、carbon-admin 与 Configurate，判断旧作业何时值得继续维护，何时应该归档。</description><pubDate>Thu, 19 Dec 2024 00:00:00 GMT</pubDate><content:encoded>我回看自己的课程项目时，经常遇到一种尴尬状态：它比练习完整，所以我舍不得删；离真实产品又很远，所以我很少再打开。最后最容易做的，只是在 README 里增加更多功能描述，希望它看起来像作品。

重新检查 [TaskFlow](https://github.com/Ninthless/TaskFlow)、[Tradinghelper](https://github.com/Ninthless/Tradinghelper)、[carbon-admin](https://github.com/Ninthless/carbon-admin) 和 [Configurate](https://github.com/Ninthless/Configurate) 后，我更愿意用一个严格标准：**工程资产不是我完成过的代码，而是未来仍能降低成本、证明判断或支持新工作的材料。**

## 四个仓库代表四种常见状态

### TaskFlow：功能完整，但验证环境封闭

TaskFlow 是我明确标注的期末作业，包含任务增删改、优先级、截止时间、日历、主题、本地存储和导入导出。我没有为它加入后端，打开静态页面即可运行。

这类项目的优点是用户流程完整，缺点是需求由作业边界决定，缺少真实用户、跨设备数据和长期演进。它可以作为前端状态管理与本地数据设计的样本，但不必包装成成熟任务管理产品。

### Tradinghelper：产品方向清楚，证据太少

我给 Tradinghelper 写下了实时行情、交互图表和技术指标的产品方向，但 README 只有一句简介。站在外部读者角度看，无法判断数据源、延迟、指标实现、风险提示、运行方式和当前完成度。

这类仓库不一定需要重写代码，第一步应该是补证据：截图、数据来源、可运行步骤、已实现功能与非目标。金融相关界面尤其不应把演示数据写成决策建议。

### carbon-admin：只有题目，没有项目叙事

我当时只给 carbon-admin 留下“双碳管理系统”这个领域名称，却没有把它定义成可验证产品：用户是谁、管理什么数据、如何计算、哪些指标来自政策或业务规则，都没有说清楚。

如果要继续，这个项目首先需要领域重构，而不是视觉重构：定义组织边界、排放源、活动数据、因子版本、核算周期和审计记录。否则再完整的后台页面也只是 CRUD 外壳。

### Configurate：代码谱系比功能更重要

我的 Configurate 仓库结构与 README 指向 SpongePowered Configurate 生态，但 GitHub 元数据没有把它标记为 Fork。无论当时是导入、镜像、课程分支还是历史迁移，我都应该在作品集中明确上游来源、基准版本、自己修改的模块和当前用途。

使用成熟开源代码学习没有问题。真正的问题是读者无法分辨继承内容与原创贡献。

## 五种处理方式，而不是全部重写

### 1. 保留原样

适合有历史意义、能运行、但不值得继续投入的项目。补一段状态说明，标记完成时间与限制即可。

### 2. 归档

适合依赖过时、无法运行、目标已失效的仓库。归档不是失败，而是停止制造维护预期。

### 3. 补证据

适合实现基本完整但文档太弱的项目。优先补运行步骤、截图、测试、数据来源和已知限制，不急于重做架构。

### 4. 提炼模块

适合项目中确实有第二次使用价值的部分，例如日期状态机、配置加载器、图表适配层或导入导出格式。只有当另一个项目需要它时，再抽成库。

### 5. 从问题重新开始

适合领域定义错误或作业假设太强的项目。保留旧仓库作为历史，新版本重新写规格，而不是在旧页面上继续叠功能。

## 一个仓库是否值得继续的评分表

每项 0—2 分：

| 维度 | 0 分 | 1 分 | 2 分 |
| --- | --- | --- | --- |
| 可运行性 | 已失效 | 需大量手工修复 | 有明确复现步骤 |
| 原创判断 | 无法区分来源 | 有少量说明 | 来源与贡献清晰 |
| 用户价值 | 只满足题目 | 有合理场景 | 有真实使用或反馈 |
| 证据 | 只有描述 | 有截图/输出 | 有测试、数据和限制 |
| 迁移价值 | 无可复用经验 | 可写复盘 | 可直接支持新项目 |
| 维护成本 | 依赖严重过时 | 可控升级 | 当前仍健康 |

总分不是绝对裁决，但可以阻止“因为写过很多代码，所以必须继续”的沉没成本。

## 作品集 README 应该写什么

课程项目最需要的不是更长功能列表，而是更诚实的上下文：

1. 这是课程、练习、复现、团队项目还是个人产品。
2. 需求来自哪里，自己负责哪些部分。
3. 当前能运行到什么程度。
4. 使用了哪些上游模板、库或已有代码。
5. 最重要的技术判断与失败是什么。
6. 如果重做，会改变什么。

一个清楚写着“期末作业，数据仅存在 LocalStorage，未做多用户同步”的仓库，比声称“现代化企业级任务平台”更可信。

## 从旧项目中提炼文章，而不是虚构升级

旧项目可以产生有价值的内容：

- TaskFlow 可以写本地优先应用的数据边界。
- Tradinghelper 可以写行情数据与技术指标的可信展示。
- carbon-admin 可以写领域调研为什么先于后台开发。
- Configurate 可以写代码来源与开源谱系如何标注。

文章的价值来自重新判断，不要求旧代码突然变成生产系统。

## 最后的选择

不是所有仓库都需要成为代表作。一个健康的 GitHub 主页应该允许四种状态同时存在：正在维护、完成留档、实验失败、明确归档。

真正值得重点维护的项目，应该能形成连续链路：有真实问题，有可检查实现，有验证证据，也有公开复盘。其余仓库保留历史即可，不必通过重写 README 假装它们从一开始就目标明确。

## 相关链接

- [从 80 个仓库看我的工程轨迹](/posts/github-80-repositories-timeline/)
- [科研代码不是结果文件夹，而是一条证据链](/posts/research-repositories-evidence-chain/)
- [Vibe Coding 不是一句提示词，而是一条交付闭环](/posts/vibe-coding-delivery-loop/)</content:encoded></item></channel></rss>