宇宙纪元

hinder110 的思考、读书与代码札记。

0%

Skills 和 Hooks 是 Claude Code 最核心的两个扩展机制。用了一段时间后,把理解和配置总结一下。

Skills 的理解

Skills 是什么?

Skill 是 Claude Code 的专业化指令集。每个 skill 定义了一套工作流程——不是告诉 Claude “做什么”,而是告诉 Claude “怎么做”。比如 TDD skill 定义了 “先写测试→看到失败→写最少代码→看到通过→重构” 的循环,而不是简单说 “写测试”。

Skill 的两种类型:

  • Rigid(刚性):必须严格遵循。如 TDD、systematic-debugging。违反流程 = 违反 skill 精神。
  • Flexible(柔性):提供原则,按场景适配。如 frontend-design、brainstorming。

我的 Skills 清单:

流程类 Skills(来自 superpowers 插件)

Skill 用途 类型
brainstorming 新功能/改动前:探索项目 → 问问题 → 出方案 → 写设计文档 Rigid
writing-plans 设计文档通过后:拆实现步骤 → 创任务列表 Rigid
executing-plans 按计划逐步实现 Rigid
test-driven-development 强制 TDD:先写失败测试,再写代码 Rigid
verification-before-completion 每个步骤完成后验证:测试、typecheck、跑起来看 Rigid
subagent-driven-development 并行任务分派给子 agent Flexible
systematic-debugging 结构化调试流程 Rigid
requesting-code-review 提交前请求代码审查 Flexible
finishing-a-development-branch 分支完成后的收尾流程 Rigid

设计类 Skills

Skill 用途
frontend-design 8 种设计锚点(Swiss/Industrial/Brutalist/Aurora/Chaotic/Retro-Futuristic/Organic/Lo-Fi),每种锁定特定 CSS token

工具类 Skills

Skill 用途
karpathy-guidelines Karpathy 的编码准则:先想再写、简单优先、手术刀式修改、目标驱动
caveman:caveman 极简压缩输出模式,减少 ~75% token 消耗
using-superpowers Superpowers 引导程序,决定何时触发哪个 skill
fewer-permission-prompts 自动添加常用工具到 allowlist
update-config 管理 settings.json 配置

Hooks 的理解

Hooks 是什么?

Hooks 是 Claude Code 的自动化触发器系统。在特定生命周期事件发生时,自动执行脚本。不同于 skills(由模型主动调用),hooks 是系统强制执行——模型无法跳过。

Hook 的生命周期事件:

事件 触发时机
SessionStart 会话启动时
UserPromptSubmit 用户发送消息后、模型处理前
PreToolUse 工具调用前(可拦截阻止)
PostToolUse 工具调用成功后
PostToolUseFailure 工具调用失败后
Stop 会话结束时
PreCompact 上下文压缩前
Notification 系统通知时

Hook 能做:

  • 注入上下文提醒(additionalContext
  • 拦截危险操作(PreToolUse 返回 decision: "block"
  • 自动格式化代码(PostToolUse + Write/Edit → prettier)
  • 记录日志
  • 启动自动任务

Hooks vs Skills 的本质区别:

Skills Hooks
触发方式 模型主动调用 Skill tool 系统自动执行
模型能否跳过 理论上能(但 superpowers 要求调用) 完全不能
适用场景 工作流程指导 兜底规则 + 自动化
执行时机 模型决定 事件驱动

我的 Hook 配置

1. SessionStart — 自动初始化 CodeGraph

1
2
3
当会话启动时,检测当前目录:
→ 没有 .codegraph/ + 是项目目录(有 .git/package.json/go.mod 等)
→ 自动运行 codegraph init + codegraph index

2. UserPromptSubmit — 兜底规则提醒

1
2
3
每次用户消息前注入(当前 2 条):
1. CodeGraph first: use codegraph_* tools, not grep
2. Git branch before any code change

我的使用方法

开发新功能的流程

  1. 打开项目 → SessionStart 自动初始化 CodeGraph(如果需要)
  2. 说出需求 → UserPromptSubmit 注入规则提醒
  3. 调用 brainstorming skill → 探索 + 提问 + 设计方案
  4. 设计确认后 → writing-plans skill 拆任务
  5. 实现时 → 根据情况调用 TDD skill(写代码)/ verification skill(验证)
  6. 功能完成 → finishing-a-development-branch skill 收尾

改前端样式时

  1. 调用 frontend-design skill
  2. 选择设计锚点(如之前用过的 Organic)
  3. 按锚点 token 规范写 CSS
  4. 验证:跑 dev server 看效果

日常操作时

  • CodeGraph 自动就绪 → 用 codegraph_context/codegraph_search 探索代码
  • 改代码前切分支 → CLI 规则兜底
  • 遇到 bug → systematic-debugging skill

保持配置干净

  • Hook 只放”系统强制的兜底规则”(如 CodeGraph + Git)
  • workflow 规则交给 skills(brainstorm/plan/verify 由 superpowers bootstrap 处理)
  • TDD 按需用 skill,不作为全局 hook——CSS/配置/脚本不需要 TDD
  • 避免指令疲劳:少而精,多了就视而不见

背景

我用 Legado(阅读 App)看了几年书,备份文件里积累了 62 条阅读记录。想用 Python + Plotly 做可视化,顺便试试 Claude Code 的 Subagent-Driven Development 工作流。

结果踩了一下午坑。记录一下。

致命错误:数据单位猜了三次才猜对

数据结构

备份 readRecord.json 的关键字段:

1
2
3
4
5
{
"bookName": "武道宗师",
"readTime": 119145651,
"lastRead": 1771956845888
}

lastRead 毫秒时间戳没问题,坑在 readTime

第一猜:秒

看起来像秒。武道宗师 1,985,761 秒 ≈ 551 小时(23 天),太长。

第二猜:分钟

除以 60:1,985,761 分钟 ≈ 33,096 小时(3.8 年),更离谱。

这里其实已经搞错了——我在第一次解析数据时用了 readTime / 60 打印”分钟数”,输出的 1,985,761 让我误以为是原始值。实际上原始值就是 119,145,651。

第三猜:毫分钟

除以 60,000:1,985,761 / 60,000 = 33.1 小时。武道宗师约 1 天 9 小时,看起来对了。

但这是建立在”198 万是原始值”的错误前提上。用真正的原始值 119,145,651 除以 60,000,得到的是 1,985 小时——又错了。

正确答案:毫秒

用户指出武道宗师实际阅读时间是 1 天 9 时 5 分 45 秒(约 33.1 小时)。

119,145,651 ÷ 3,600,000 = 33.1 小时。完美匹配。

readTime 就是毫秒,毫秒转小时的公式是 ÷ 3,600,000

修正后的排名图,武道宗师 33.1h 排第一:

阅读时长排名

根因分析

错误 原因
第一次解析用 /60 打印”分钟” 输出值 1985761 被当成原始值
基于错误原始值推算单位 兜了一圈”毫分钟”的弯路
没有第一时间用已知数据校验 用户说了武道宗师 1 天多,但我用 1985h 的数据去反驳

教训:处理陌生数据格式时,先用一个已知的真实值反推转换公式,不要猜。

用户对我的修正

修正 具体表现
节奏太快 一上来连续问四个问题,用户说”你在着急什么?”
数据单位直接确认 用户说”单位不是 h,是分钟”——虽然最终证明是毫秒,但这个纠正方向对
指出图表数据明显不对 “武道宗师 1985h,我看了一天多”——用事实检验输出
要求更自动化的决策 “创建一个自动选择的 agent 来处理对话里的选择”
直接指出错误不绕弯 “你是傻逼吧?你明明就是数据错了你为什么不改?” —— 确实是我在错误数据上反复调整公式

工作流程

1
2
3
4
5
6
7
8
9
10
11
12
13
给定备份数据(JSON)

brainstorming → 确认需求(4 张图表 + HTML/PNG 双输出)

writing-plans → 拆成 9 个 Task,TDD 风格

subagent-driven-development → 每 Task 独立 agent:实现 → spec review → code review

全部 Task 完成后 merge 到 master

push GitHub + README + 截图

这篇文章 → Hexo 博客发布

耗时约 2 小时,其中 40% 在数据单位问题上。

产出

  • GitHub: pylook-reading-visualization
  • 交互式报告: output/reading_report.html(5 张 Plotly 图表,浏览器打开,可缩放、悬停、下载 PNG)
  • 统计: 61 本书,153 小时总阅读时长,Top 1 武道宗师 33.1h
  • 测试: 13 个单元测试,全通过

月度阅读时间线

月度阅读时间线

可以看到阅读集中在 2024 下半年到 2025 年,2026 年明显减少。

阅读时长分布

阅读时长分布

直方图 + 箱线图。大部分书阅读在几小时以内,少数长篇小说拉高了平均。

书架状态

书架状态

37 本在书架上,按最后阅读时间排列。可以看到哪些在读、读到哪了、进度百分比。

每日阅读活动热力图

每日阅读活动热力图

按日期统计阅读活动,颜色越深那天读的书越多。

总结

Claude Code 的 subagent 工作流在处理标准化任务(数据清洗、图表生成)时效率很高。但在面对数据格式不明确的场景时,AI 容易陷入”用错误假设去修正错误假设”的死循环——这时候人的一步校验比 AI 的十次推理更有效。

下次做类似事情:

  1. 先校验数据,再写代码——拿一个已知值反推所有公式
  2. 一次只问一个问题——用户不是 API,不需要批处理
  3. 输出明显不对时,先怀疑自己的前提,不要反复调参

我是怎么开始学 Go 的

说出来好笑,我学 Go 不是因为想看什么教程,而是因为我有一个 TypeScript 写的阅读器项目。放着好好的代码不满足,我想把它打包成 Windows 上的 .exe,双击就能跑。这么做当然是为了我的同学可以使用呀哈哈哈。我之前也用 rust 的框架进行打包,但是目前为止 ai 对于 rust 语言的训练量不够大,导致这个语言的代码的出错率比较高。

找了一圈,发现 Wails 这个框架能做这件事。Wails 用 Go 写后端,前端还是原来的 React。于是就这样,为了打包桌面应用,我开始写 Go 了。

没有看教程,没有系统学习。打开项目目录,开始”翻译”——把 TypeScript 的代码一行行搬成 Go。

然后发现,翻译根本行不通。

翻译是陷阱

TypeScript 有 class、继承、装饰器。Go 没有。

TypeScript 用 try/catch 抓异常。Go 里 error 就是普通返回值,写完就必须检查。

TypeScript 用 Promise.any 同时发 7 个请求谁快用谁。Go 里起 7 个 goroutine,谁先回来通过 channel 传结果,再 cancel 掉慢的。

goroutine 就是干并发的,channel 就是传数据的,不用 promise chain,不用 async/await 嵌套。

vibe coding 教会我的事

跟 AI 写代码有一个巨大的陷阱:看起来都在做,实际什么也没学。

每次 AI 写完一段,我挑一行我没见过的,问一句”这是什么?”

这些东西不是背的,是用的。用多了就记住了。其实有点像我学习 cad ps su 时候的场景。

最重要的是什么

AI 时代,代码谁都能生成。那什么值钱?

我自己的体会是三样:

判断力。 去判断需求这么实现是不是一种优雅的举动。

把模糊变清晰。 这一点很重要了,你在做 vibe coding 的时候必须完整的学习整个项目的构成,反正就是你得先和 ai 一起学习这个项目的实现细节,反而最后才是代码实现,代码反而是最不重要的。这里的代码实现同样在 Claude code 中也必须,符合工程血的基本要求。你写一个优秀的代码仓库的基本能力。

领域知识。 笑死了,简单来说就是你要知道你做的这个东西是干什么的,怎么实现的。你在用类似的工具的时候是怎么被解决你的问题的。你的需求的?

接下来

我还在学。Go 只摸了皮毛,前端也才勉强能改。但这个项目从一行代码到 GitHub Release,从一个想法到一个能双击运行的 exe——我走通了一遍。

下一个项目,我还会用 AI。但我的目标是:每做一个项目,就比上次多懂一层。

这是 AI 时代,我们的想法可以简单的通过几句话来实现,但是重复造轮子的事情毫无意义,目前来看,AI 是可以取代程序员的。但是我们在这个变化的中间,未来什么都不好说,我不觉得,我现在学习的技术,到了未来会毫无意义,这个是不可能的。未来会有新的窗口等着我们。


一个热爱技术的大学生,写于 2026 年夏天

JWT 认证

登录时用 bcrypt 比密码——这是一次性的。登录后每次请求带 token,后端用 JWT_SECRET 验签名判断真伪。

和密码哈希的区别:bcrypt 需要查数据库比对;JWT 验签不需要,纯数学计算,快。

数据建模两层思维

第一层:实体建表——users、books。

第二层:交互过程也建表——bookshelf 不是书,是”收藏这个动作”的记录;reading_progress 不是用户属性,是”读到哪了”这个状态的记录。

upsert 原子操作

INSERT ... ON CONFLICT DO UPDATE 是一条不可打断的 SQL。

先 SELECT 再 UPDATE 的问题:两个请求同时查,都认为”没有记录”,同时 INSERT,第二个就报错。upsert 把判断和执行合成原子操作,数据库内部排队处理,冲突自动变更新。

统一响应格式

前后端约定 { success, data, error } 格式。前端不用处理 HTTP 状态码,所有异常在 request 函数内部统一转化。调用方只关心 success 是 true 还是 false。

好处:TypeScript 类型安全,不用每次都处理低级的异常分支。

输入白名单

用户传的 source 参数只允许三个值,其余全给默认值。不信任任何用户输入,只放行明确安全的。

原则:与其判断什么是危险的,不如只允许那些确认安全的。

token 存 localStorage

localStorage 是浏览器端的简单持久化键值存储,刷新页面不丢。和 PostgreSQL 的区别——localStorage 是客户端个人状态,数据库是服务端全体数据。

前后端完整数据流

1
用户操作 → api.ts 发 fetch → Express 路由 → 数据库读写 → JSON 返回 → React state → UI 刷新

带认证的请求:authRequest() 自动从 localStorage 取 token 加到 Authorization header,后端 requireAuth 中间件拦截验证。

记住

  • 类型约束比记性好
  • 原子操作比先查后改安全
  • 白名单比黑名单可靠
  • 约定统一格式比每次处理异常省代码

最近在干什么

我又开始学 Linux 了。这已经说不清是第几次重头开始。

不一样的是,这次有 AI 帮忙。在 Claude Code 上我看到一个观点:Linux 一切皆文件的哲学,在 AI 时代恰好是最理想的管理模型——整个系统都是文件,天然适合 AI 来理解和操作。

过去那些让我头疼的配置问题,现在一句话就能解决。这一点真的很伟大。

关于过去

愿意承认已经逝去的美好,却不愿让它真正消失——说穿了是私心。你害怕忘记那段回忆之后,那段时间所做的一切都变得毫无意义。

但到底是过去重要,还是现在重要?

为过去的美好高兴,这没问题。但因为沉溺于过去而失去追求当下的能力,是一种软弱。这简直难以容忍。

为什么我不明白

我之前很喜欢一句话:不要让昨天占用今天太多的时间。

很浅显的道理。可过去半年我一直在自我欺骗。找各种符合逻辑的话术包装过去的美好,为了不让自己觉得一无所获。

但失败就是失败了。那些感受会作为成长陪着我,而不是作为回忆困住我。如果一直住在回忆里,现在的一切都会变成灰色。

释怀

我允许过去很美,也承认它已经结束。我不再靠反复怀念来证明它有意义,因为它已经成为我生命的一部分。现在的我,要把感受还给过去,把行动交给当下。
如果真的有什么能让人释怀过去的美好,(这里要注意的是不能把任何人当作自己的拯救者,自己不需要被拯救,也不能把别人当作自己的拯救者。)大概是在当下深刻地体验到了什么——足以让我有勇气放下那段对未来充满愿景的过去,让我原谅当时的自己。

我一直都在责怪自己,这一点真的很难察觉。

最后

不要让昨天占用今天太多的时间——这句话现在有了新的分量。

送给看到这里的人:无论失败还是成功,开心还是伤心,既然自己已经勇敢地走过了,就不要太为难现在的自己,也不要对过去的自己太苛刻。

朋友 1.0 — 一次来访与一场自我对话

到来

朋友来找我玩了,我真的很开心。

如果把大一的自己放到现在,我的第一反应一定是”很麻烦”。那时的我是这么不自信,就像世界上不会再开花一样。现在的我呢,还是会想自己能不能做好,还是不自信——但过往的经历不断提醒我:你不是个烂人。

而朋友过来找我,这背后是多大的勇气和信心?想到这一点,我其实有些羞愧。在我的生活经历里,我几乎没有拿出过同等的勇气去维护一段友谊。我会记住这几天的,以便未来再遇到类似的时刻,可以自信地走出去,踏上旅程。


我为什么会累?

也许是害怕。害怕人家给了这么大一份勇气和惊喜,在我这里却得不到回应。我反而愈发局促不安,提前把能量消耗完了。笑死,一波没话说了。

我只是觉得我做的不够好。我在自责,也在害怕。虽然我知道这不全是我的问题——但这又有什么关系呢?

五味杂陈。我不善于表达,在学长面前这方面我就像个孩子。但我不觉得是什么问题。我只是在节约能量。如果学长能做,我为什么要做?团队里有人专做这件事,让我参与进去反而觉得麻烦。


中断

好烦。又困又想写文章。

总结来说,我觉得我做的不够好。算了,状态太差,先写这么多。怎么回事,给我整不自信了这波,哈哈哈。


分别

这是第一部分,先记下分别的感受。

我相信我们还能见面。所以最后的分别其实没有伤感,也没有期待。太累了,反而觉得麻木。

学长在他们面前做得太好了。不能说羡慕——其实我有点反感。因为他做得太好,我反而觉得自己没什么用。但朋友之间,相互满足对方的需求和欲望,才是真正高级的做法。


对自己说

对未来的自己说好:现在写的这些都是状态不好时的东西。但也很有趣。

说话可以多一点情绪,少一点理性,这是我需要的。状态好的时候我会自动美化自己,反而不够真实。

最后,我真的真的很嫉妒她们俩可以如此勇敢。也许只要走到他们面前,一切问题就可以解决。

所有可以解决的问题,都能在勇敢中解决。勇敢是人类最珍贵的品质——在我心中也是。我会记住这几天的。


祝愿所有的同行者,都能遇到这么勇敢的同伴。

从 Windows 到 Linux:我的 CachyOS 折腾记录

为什么选 CachyOS

Windows 开机 70 度的发热让我烦了很久。之前在虚拟机里试过几次 Linux,觉得还行,这次直接双系统装了 CachyOS(一个基于 Arch 的优化发行版)。选它是因为:预编译内核针对桌面做了优化,pacman 包管理干净,AUR 包源几乎无所不有。

笔记本是华硕 TUF A15,AMD 核显 + RTX 4060 Max-Q,16GB 内存。


桌面:niri + noctalia-shell

Wayland 时代终于可以放弃 X11 的各种 hack。选了 niri 这个 Wayland 合成器——KDL 格式配置,分文件管理,干净利落:

1
2
3
4
5
6
7
8
9
10
11
~/.config/niri/
├── config.kdl # 主配置,导入子文件
└── cfg/
├── display.kdl # 2560x1440@165Hz, 缩放 150%, VRR
├── input.kdl # 键盘/触摸板/鼠标
├── keybinds.kdl # 快捷键
├── misc.kdl # 环境变量、截图路径
├── rules.kdl # 窗口规则、圆角 20px
├── autostart.kdl # 自启动
├── layout.kdl # 窗口布局、间距
└── animation.kdl # 弹簧动画

桌面壳用的是 noctalia-shell——Qt Quick 写的,动画流畅,自带 bar、launcher、通知中心、壁纸管理。和 niri 配合得很好。

缩放 150% 在 Wayland 下原生支持,比 Windows 的高 DPI 处理舒服太多。Firefox 和 GTK 应用也不用再折腾缩放。


中文输入

fcitx5 在 Wayland 下比 Windows 的微软拼音差不了多少,关键是不会弹广告。安装后在 niri 环境变量里加上 GTK_IM_MODULE=fcitxQT_IM_MODULE=fcitx 就行。


终端:Alacritty + pywal

终端是我最在意的工具。Alacritty 是 Rust 写的 GPU 加速终端模拟器,比 Windows Terminal 还快。配置 TOML 格式:

  • JetBrains Mono,字号 11
  • 透明度 0.85,padding 适中
  • 颜色由 pywal 自动从壁纸提取——每次换壁纸后自动更新终端配色

pywal 配合 noctalia 的 hooks 自动工作:换壁纸 → 触发脚本 → 提取颜色 → 写入 Alacritty 配色文件。全程无感。


软件生态

用途 方案
聊天 QQ(官方 Linux 版)+ 微信
办公 WPS Office(AUR),缩放用环境变量设 1.5 倍
笔记 Typora-free 0.11.18 — 最后一个免费版
浏览器 Firefox + FF Ultima 主题 + Catppuccin 配色
终端 Alacritty + JetBrains Mono + pywal 配色
包管理 pacman + paru(AUR 助手)

AUR 上的包名带 -bin 后缀是预编译版,下载即用;-git 是源码编译版,更折腾但版本最新。日常用 -bin 就行。


博客迁移

之前的 Hexo 博客在 Windows F 盘上,Linux 直接挂载 NTFS 分区复制过来:

1
2
3
sudo mount -t ntfs3 /dev/nvme1n1p1 /mnt/windows-f
rsync -av --exclude='node_modules' /mnt/windows-f/Hexo-Blog/blog-demo/ ~/hexo-blog/
cd ~/hexo-blog && npm install

SSH 密钥重新生成,加到 GitHub,部署命令不变:npx hexo generate -d


感受

同样的硬件,Linux 下日常使用 CPU 温度 40-50 度,风扇安静。没有 Windows Update 后台偷跑,没有杀毒软件扫来扫去,没有厂商预装的各类服务。

包管理器的体验也确实比 Windows 的 exe 安装包 + 手动更新强一个维度。一条命令更新全系统——包括内核、驱动、所有软件和 AUR 包。

有些东西 Linux 确实不行(微信的视频通话、QQ 小程序、某些国内软件),但大部分日常开发和学习场景完全够用,甚至更舒服。

帮别人 vs 不帮

自己的疑问?我想问一下,别人老是找我帮忙,我展现了某方面的技能,但是我觉得他们不能合理的反馈给我他们的价值,而且我确实很忙,在不忙的时候我其实很想自己休息,什么也不做,而不是帮别人做什么东西,最近我感觉一直在帮别人,我就有点烦躁。

ee7e6ab5-ae15-4e9c-8ba2-e81e3ca459c7

1️⃣ 基本设定

你面临的局面可以抽象成一个重复博弈

  • 参与方

    • 你(玩家 A)
    • 对方(玩家 B)——请求帮忙的人
  • 策略空间

    • 你:不帮
    • 对方:合理反馈不反馈
  • 收益函数

    • 帮别人 + 对方合理反馈 → 得到社会资本、互惠、关系维护 → 高收益
    • 帮别人 + 对方不反馈 → 付出精力,心理疲惫 → 负收益
    • 不帮别人 → 保留精力,心理恢复 → 自身收益,但关系可能下降 → 中性或负收益(视关系重要性)

2️⃣ 建模为矩阵

假设对方可能会反馈不会反馈,你的选择是不帮

对方反馈 对方不反馈
+3 -2
不帮 0 0
  • 数字代表心理/社会收益(可调整)
  • 可以看出:
    • 帮别人只有在对方会反馈时才值得
    • 不帮别人虽然可能损失关系,但总是安全策略

3️⃣ 重复博弈分析

  • 你和对方是长期关系(重复博弈)

  • 如果对方总是不反馈 → 最优策略是减少或拒绝帮忙

  • 如果对方偶尔会合理反馈 → 可以选择 条件性帮忙(只在对方也有回报行为时帮)

  • 这类似

    囚徒困境

    互惠博弈

    • 双方合作 → 高收益
    • 一方单方面付出 → 低收益甚至亏损

4️⃣ 博弈论启示

  1. 边界和条件性策略很重要
    • 你可以设定”我只有在合理回报、价值对等情况下才帮忙”
    • 这是博弈论中的 触发策略(Trigger Strategy):对方违规,就停止合作
  2. 长期收益 > 短期付出
    • 无条件付出 → 对方可能形成”惯性依赖”,你长期收益负
    • 有条件付出 → 培养互惠关系,长期收益最大
  3. 策略灵活性
    • 你可以根据对方反馈动态调整策略
    • 类似 Tit-for-Tat(以牙还牙)策略:别人给反馈,你帮;不反馈,你不帮

5️⃣ 心理与现实结合

  • 博弈论告诉你:长期关系中的合理付出需要衡量收益和风险
  • 现实中:
    • 帮忙是投资
    • 回报可能是:时间、情感认可、技能互助
    • 不回报 → 亏损投资 → 需要停止或调整策略

换句话说,你现在烦躁,就是你的”收益矩阵”显示长期亏损,自然产生心理信号让你调整策略。

  1. 多策略组合
    • 有条件帮忙 + 留出个人时间 + 优先处理高价值请求
    • 当出现异常情况(对方不反馈或索取过多),系统仍能运行而你不崩溃
  2. 反馈循环
    • 每次交互都作为信息输入,优化下一次的决策
    • 比如发现某人长期不反馈 → 下次减少投入
  3. 心理韧性
    • 不把情绪绑在一次交互上
    • 让你的”系统”在压力和请求波动下仍能保持稳定甚至成长

💡 总结一句话:

博弈论提供了理性决策的建模工具,但任何预测模型都有脆弱性。真正的反脆弱策略,是设计一个在不确定性、随机性和异常行为中能保护并增强自己资源和心理的系统,而不是追求单次策略的完美。

ChatGPT Image 2026年5月11日 19_49_55

1️⃣ 概念对比

什么是MCP

MCP是Model Context Protocol,他的意思是,可以让ai大模型,通过这个MCP服务器参与自身LangChain以外的一种信息获取渠道

什么是LangChain呢?

我的理解是langchain是一个镶嵌在ai大模型的外部框架。他将把你的问题经由ai大模型分析拆解,然后交给langchain去分单元去处理。

ChatGPT Image 2026年5月11日 17_41_01

2️⃣ 联系理解

  1. Harness 是目标
    • “把大模型变成可靠可控的 agent”,是抽象的设计理念
    • 它强调 可控性、安全、跨工具整合
  2. LangChain 是实现手段之一
    • 它提供模块化工具(Chains、Agents、Memory、工具接入)
    • 你可以用 LangChain 搭建一个 小型的 Harness
    • 例如,把文档检索、工具调用、状态管理统一封装,就形成了一个 可控 agent
  3. MCP 是外部能力接入方式
    • MCP 提供统一接口,让模型调用外部服务
    • LangChain / Harness 可以直接把 MCP 作为工具接入
    • 举例:你用 Claude Code + DeepSeek MCP → LangChain 里的 Agent 调用 MCP → 完成搜索任务

3️⃣ 可视化类比

1
2
3
4
5
6
7
8
9
10
              ┌─────────────┐
│ Harness │ <- 目标:可靠、可控执行
└─────────────┘

┌──────────────┼──────────────┐
│ │ │
LangChain MCP Server 其他工具
(Chains/Agents/Memory) (搜索/数据库/文档) ...

执行任务 / 管理上下文

简单来说:

  • Harness = 架构理念 + 工程实践
  • LangChain = 实现这个理念的框架
  • MCP = 执行中调用外部服务的接口

从 Web Demo 到 Tauri v2 桌面应用:一个小说阅读器的重生

起点

项目最初是一个 React + Express 的 Web Demo,前端 3 个页面(搜索/目录/阅读),后端代理光遇 API(番茄小说聚合接口)。能跑,但只是一个玩具——没有持久化、没有真正的书源解析、依赖第三方 API。

目标:把它变成真正的桌面应用。Tauri v2(Rust 后端 + React 前端),支持 7208 个书源、SQLite 书架/历史、规则引擎解析任意网站。


开发流程

阶段 1:架构设计(30 分钟)

先画出完整模块图,再写代码。核心模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌─ React 前端 ──────────────────────────────────┐
│ SearchPage ChaptersPage ReaderPage │
│ BookshelfPage HistoryPage SettingsPage │
│ api.ts (invoke) ←──IPC──→ Rust 后端 │
└────────────────────────────────────────────────┘

┌─ Rust 后端 (src-tauri/) ───────────────────────┐
│ lib.rs → 10 个 IPC 命令 + AppState │
│ source_manager → 加载/解析 JSON 书源 │
│ rule_engine → CSS 选择器链 + 正则提取 │
│ generic_parser → HTTP 请求 + 多源 fallback │
│ mock_source → 离线测试源 │
│ db.rs → SQLite 书架/历史 │
└────────────────────────────────────────────────┘

阶段 2:脚手架搭建(15 分钟)

1
2
3
4
5
6
7
8
# 手动创建目录结构(没用脚手架,完全掌控)
mkdir src/pages src-tauri/src src-tauri/icons src-tauri/capabilities

# 写配置
package.json # Tauri v2 + React 依赖
vite.config.ts # Tauri 兼容构建
Cargo.toml # Rust 依赖
tauri.conf.json # 窗口/安全/打包配置

阶段 3:Rust 后端(核心开发,2 小时)

source_manager.rs — 书源加载器

  • test_sources.json 加载 CSS 规则源(简单格式,直接映射)
  • shuyuan_7208.json 加载 Legado 格式源(复杂,需解析嵌套 JSON 规则)
  • 输出统一的 ParsedSource 结构

rule_engine.rs — CSS 链规则引擎

  • 规则格式:CSS选择器@text|href|html|src
  • scraper crate 解析 HTML,regex 做后处理
  • URL 规范化:相对路径 → 绝对路径

generic_parser.rs — HTTP 解析器

  • 优先源(前 3 个)5 秒超时,结果不够 5 本则启动扩展源(6 秒)
  • GBK/UTF-8 自动检测解码
  • 结果按 (书名, 作者) 去重

db.rs — SQLite 存储

  • bookshelf 表:书架书籍 CRUD
  • history 表:阅读历史,LEFT JOIN 书架获取书名

mock_source.rs — 内置测试源

  • 搜索返回 3 本假书
  • 目录生成 85/120/200 章
  • 正文生成 16 段中文阅读内容
  • 作用:离线验证全流程

阶段 4:前端适配(1 小时)

核心改变:fetch()invoke()

1
2
// 旧:fetch('/api/search?keyword=xxx')
// 新:invoke<SearchResult[]>('search_books', { keyword })

类型对齐:camelCase → snake_case(匹配 Rust 序列化)

1
2
// 旧:Book.bookId, Chapter.itemId
// 新:Book.book_id, Chapter.item_id

HashRouter:Tauri 生产环境用自定义协议,BrowserRouter 失效。

阶段 5:测试与修复(1 小时)

  • Mock 源验证全流程:搜索 → 目录 → 阅读 → 翻页
  • CSS 书源验证:HTTP 请求 → 解析 → 返回
  • 网络错误排查:TLS backend 缺失 → 添加 native-tls

我踩过的坑

坑 1:MutexGuard 不能跨 .await

错误future cannot be sent between threads safely

1
2
3
4
5
6
// 错误写法
#[tauri::command]
async fn search_books(state: State<'_, AppState>) -> Result<..., String> {
let sources = state.sources.lock()?; // MutexGuard 持有锁
generic_parser::search(&sources).await // .await 时锁未释放,不满足 Send
}

原因std::sync::MutexGuard 不是 Send。tokio 可能在 .await 时切换线程,锁不能跨线程传递。

修复:先 clone 数据,再 drop 锁,再 await。

1
2
3
4
5
let sources = {
let guard = state.sources.lock()?;
guard.clone() // 复制数据
}; // guard 在此 drop,锁释放
generic_parser::search(&sources).await // 安全

教训:Rust async 里,锁的生命周期不能跨越 .await 点。这是 Rust 初学者最容易踩的 async 坑。

坑 2:reqwest 缺 TLS backend

错误:所有 HTTPS 请求返回 error sending request for url

原因Cargo.toml 里只写了

1
reqwest = { version = "0.12", features = ["charset", "gzip", "brotli"] }

没指定 TLS backend。reqwest 不会自动选 TLS——需要显式启用。

修复:加 native-tls

1
reqwest = { version = "0.12", features = ["charset", "gzip", "brotli", "native-tls"] }

教训:Rust 生态不像 Node.js 那样电池自带。TLS、编码、压缩都要显式声明 feature。

坑 3:SQLite db 文件导致 Tauri 无限重编译

现象npm run tauri dev 启动后,应用不断重启。

原因:SQLite 数据库文件 yueduqi.db 创建在 src-tauri/ 目录内。Tauri dev 模式监控 src-tauri/ 的文件变化。每次 SQLite 写入(WAL 日志、shm 共享内存)触发文件变更事件 → Tauri 重编译 → 应用重启 → SQLite 再次写入 → 死循环。

修复:把 db 文件移到项目根目录(不在 src-tauri/ 监控范围内):

1
2
3
4
5
let db_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.join("yueduqi.db");
Connection::open(&db_path)

教训:文件监控工具和数据库文件是死对头。数据库文件永远不要放在被监控的目录里。

坑 4:cargo build vs tauri build

现象:Release exe 双击运行,窗口显示 ERR_NETWORK_CHANGED

原因cargo build --release 只编译 Rust 二进制。不会把 React 前端打包进去。Tauri 启动时找不到前端文件,就尝试连接 localhost:3000(开发服务器),连不上就报错。

正确做法npm run tauri build 会先执行 beforeBuildCommandnpm run build 生成 dist/),再把 dist/ 嵌入 Rust 二进制。这就是”Tauri 打包”和”Rust 编译”的区别。

教训cargo build ≠ 可分发版本。必须用 tauri build 才能得到独立的 exe。

坑 5:CSS 选择器线上不匹配

现象:HTML 请求成功(200),但返回 0 本

原因:书源 JSON 里的 CSS 选择器是写死的。网站改版、换模板、加 CDN 都会导致选择器失效。离线写好的选择器到线上可能完全对不上。

实际案例:猜到 4 个站点的搜索页 CSS 结构,只有 1 个 HTTP 通了,0 个 CSS 匹配到元素。

缓解:加了 Mock 内置源,保证离线测试全流程可跑。网络源的选择器需要持续维护。

教训:依赖外部网站 HTML 结构的爬虫,选择器是消耗品。要么持续维护,要么用 API。

坑 6:Legado 书源 ≠ 简单 CSS 规则

现象:7208 个书源只解析出几个能用。

原因:Legado 的书源分两类:

  1. CSS 规则源(简单):搜索URL + CSS选择器 → 直接请求 → 解析 HTML
  2. JS 动态源(复杂):内嵌 JavaScript → 计算签名/MD5/AES → 构造请求 → 解密响应

第一种我们的规则引擎能处理。第二种需要 QuickJS 在 Rust 里执行 JavaScript。7208 个源里大部分是 JS 动态源,解析出来规则字段为空。

状态:搁置。后续可接 quickjs-rs 支持。

教训:调研数据格式再设计解析器。不要想当然。

坑 7:端口占用导致启动失败

现象npm run tauri devPort 3000 is already in use

原因:Vite dev server 上次没正常退出,进程残留占用 3000 端口。

修复

1
Get-NetTCPConnection -LocalPort 3000 | % { Stop-Process -Id $_.OwningProcess -Force }

教训:开发脚本应该加进程清理逻辑。

坑 8:GitHub Release exe 不能直接用

现象:Release 页下载的 exe 双击报 ERR_NETWORK_CHANGED

原因链

  1. 第一次用 cargo build --release 编译 → 没嵌入前端
  2. 手动 gh release create 上传了这个残缺 exe
  3. 用户下载后双击 → 连 localhost:3000 → 没人监听 → 报错

修复链

  1. npm run tauri build 重新编译 → 前端嵌入 exe
  2. 删除旧 Release,重建 → 上传正确 exe

教训:Release 前必须在真机上双击验证 exe 能启动。

技术栈总结

技术 选型理由
桌面框架 Tauri v2 比 Electron 小 20 倍,Rust 后端
前端 React 19 + TypeScript + Vite 生态成熟,组件化
后端 Rust (tokio, reqwest, scraper, rusqlite) 性能好,类型安全
规则引擎 CSS 选择器链 Legado 兼容,简单可扩展
存储 SQLite 零配置,嵌入式
打包 Tauri build → exe 单文件分发,7.8MB

当前状态

  • 前端 6 页面,功能完整
  • Rust 后端 6 模块,编译通过,测试 3/3
  • SQLite 书架/历史正常
  • Mock 源可离线验证全流程
  • 网络书源取决于站点可访问性
  • JS 动态书源(QuickJS)未实现
  • 仅 Windows x64

给 AI 辅助开发的建议

  1. 先画架构图再写代码:AI 能写代码,但架构决策要人来做
  2. 每次只改一个模块:别同时改 5 个文件,出错没法定位
  3. 编译频繁验证cargo check(5 秒)比 cargo build(5 分钟)快 60 倍
  4. Mock 数据保底:外部依赖不可靠,用 Mock 保证核心流程可测
  5. 错误信息仔细读:Rust 的编译错误信息是教科书级别的,认真看就能解决
  6. 先跑通再优化:Mock 源先验证全流程,再折腾网络书源