端到端加密聊天项目复盘与沉淀
基于个人独立开发的两套提示词进行项目复盘与知识沉淀,一次新的尝试。
“快速行动,然后快速失败,最后快速重构”是我相信在当前 AI Coding 利器下一种很好的学习方式。
知识沉淀
/extract 请基于本次对话,输出一份知识沉淀文档,严格按以下结构:
## 1. 一句话核心
本次对话最有价值的洞察(一句话,不超过30字)
## 2. 知识点(按依赖关系排序)
- 概念A:是什么 / 为什么重要 / 关键细节
- 概念B:……
(只列对我有新增价值的,已知的不要重复)
## 3. 可复用的代码/方案
(如果有,附带使用场景注释)
## 4. 未解决的问题 & 下一步
- 哪些点我们没深入?
- 我下次应该追问什么?
## 5. 元认知评价(请直接、不要客气)
- 我的提问方式有什么效率问题?(具体到某句话)
- 我的思维方式暴露了什么盲点或惯性?
- 如果我重新问,最优提问应该怎么写?给出改写版本
输出为纯Markdown,可直接粘贴到笔记。1. 一句话核心
决策权握在谁手里,能力就长在谁身上——AI 是审稿人不是代驾。
2. 知识点(按依赖关系排序)
- 公网两台机器不能直连:寻址(没有固定公网地址)+ NAT(机器只有内网地址、外部无法主动连入)+ 防火墙(默认拒绝主动入站)。结论:“双方都主动连出去到一个固定地址的服务器”能成,“互相主动连对方”不成。
- 对称 vs 非对称的张力:对称(AES)同一把钥匙、快,但密钥分发难;非对称(RSA)公私钥、钥匙好传,但慢且一次只能加密极小数据(2048 位 + OAEP 约 190 字节)。 对称快但钥匙难传,非对称钥匙好传但慢,两者矛盾正是混合加密存在的理由。
- 混合加密的分工模型:RSA 只负责“把那把临时 AES 密钥安全递过去”这一下,AES 负责“之后所有消息内容”。RSA 解决了 AES 的密钥分发问题,AES 解决了 RSA 的慢和容量问题。实际上在系统里 RSA 出场极短,AES 才是主力。
- AES-256: AES(Advanced Encryption Standard)是当今最主流的对称加密算法。 它对数据做了大量、可逆、且高度依赖密钥的混淆和扩散——每一位明文都被打散到密文的各个位置,密钥稍微变一点,输出面目全非。没有正确密钥,你看到的就是均匀的随机噪声,没有任何可利用的规律
- 256 指的是密钥的长度,256 比特(bit)。AES 有三个规格:AES-128、AES-192、AES-256,后缀就是密钥位数;
- 256 比特意味着密钥有 2²⁵⁶ 种可能,这意味着你系统的弱点几乎永远不在 AES 本身(无法被暴力破解),而在密钥怎么生成、怎么保管、怎么传。
- AES-256-GCM:GCM 是 AES 的一种工作模式,AES-GCM 提供 AEAD = 保密 + 认证。因为加密 ≠ 防篡改:朴素加密挡不住攻击者翻转密文位;GCM 靠认证标签让任何篡改在解密时直接报错。
- AAD(关联数据)的精确语义:AES-GCM 的认证标签覆盖“密文 + AAD”两部分,AAD 不被加密、但被认证(明文可见、不可篡改)。它的用途就是 seq/ts/nonce 这种“需要明文可读、但改了要被发现”的元数据。
aesgcm.encrypt(nonce, pt, aad)里那个第三参数现在传None/b""就等于没认证元数据。
- AAD(关联数据)的精确语义:AES-GCM 的认证标签覆盖“密文 + AAD”两部分,AAD 不被加密、但被认证(明文可见、不可篡改)。它的用途就是 seq/ts/nonce 这种“需要明文可读、但改了要被发现”的元数据。
GCM 在加密时,会基于密钥、密文和一段附加数据,额外算出一个认证标签(通常 128 比特),跟着密文一起发。接收方解密时会用同样的料重新算一遍标签,和收到的标签比对。只要密文被改过哪怕一个比特,重算出来的标签就对不上,GCM 直接报错、拒绝解密。
- RSA-2048: RSA(三个发明人 Rivest、Shamir、Adleman 的首字母)是最经典的非对称算法。 RSA 建立在一个“单向门”性质上,术语叫陷门函数。 为什么能 work,直觉是这样:把两个很大的质数相乘,很简单瞬间算完;但反过来,给你那个乘积,要你找出原来是哪两个质数相乘的,这件事极其困难,大到 2048 比特规模时,目前所有已知方法都要算到天荒地老。
- “正着走容易、反着走极难”,这就是 RSA 保证安全的底层原理。攻击者拿到公钥,等于拿到了乘积,但他没有那条捷径,只能去硬做因式分解,而那条路被堵死了。
- 2048 指的是 RSA 密钥(模数)的长度,2048 比特。它直接决定了“那个要被分解的大数”有多大。位数越多,因式分解越难,越安全,但加解密也越慢。
- 注意 RSA 的 2048 和 AES 的 256 不能直接比大小。它们是两套完全不同的数学,2048 位的 RSA 安全强度大致只相当于 112~128 位的对称密钥。这也从侧面解释了为什么 RSA 慢:它得操作大得多的数。
- OAEP:是 RSA 加密时用的一种安全填充方式,防止一些针对 RSA 的攻击。可以理解为“用 RSA 加密前,先按 OAEP 规则把数据包装一下更安全”。
- TCP 粘包:TCP 是“字节流”,不是“消息流”。它只保证字节顺序,完全不保证你“一次发的”和对方“一次收的”对得上。你连发两条
"Hi"和"Bye",对端可能一次recv拿到"HiBy",下次拿到"e";也可能一次就拿到"HiBye"。TCP 眼里这就是一串没有分隔符的字节。这个现象叫粘包/拆包- 解法:每条消息前缀 4 字节大端长度(
struct.pack('>I', n)),接收方先读 4 字节定长度、再精确读 N 字节正文。
- 解法:每条消息前缀 4 字节大端长度(
- 中间人攻击(MITM):中间人攻击是攻击者站在通信双方中间冒充对方。典型手法是在双方交换公钥时,把真公钥偷换成自己的,于是 Bob 以为在跟 Alice 说话,其实消息全经过攻击者。这样攻击者既能偷看内容,也能篡改内容,而双方毫无察觉。一句话记:冒充对方,劫持整条通信。
- 重放攻击(Replay):重放攻击的特点是攻击者不需要看懂内容,只把截获的密文原样复制、反复重发。比如截获一条“转账100元”的加密消息,重发十次,转账就被执行十次——攻击者全程没解密,只是“复读”。所以光加密挡不住它,因为它根本不在乎内容是什么。一句话记:复制旧消息反复发,造成重复执行。
- 篡改 / 会话劫持(Tampering):篡改是攻击者修改传输中的消息内容,或插入一条伪造的消息。比如把“转给Bob”改成“转给Mallory”,或冒充Alice塞进一条假消息。它和重放的区别是:重放不改内容靠重复,篡改是真的动了内容或伪造了新内容。一句话记:改内容或塞假消息。
- 时间戳+Nonce+序列号 三合一防重放:三种机制组合使用,共同封死重放攻击。
- 是什么:给每条消息同时附带时间戳(发送时间+有效期)、Nonce(一次性随机值)、序列号(递增编号),接收方三项全过才接受。
- 为什么重要:单独任何一个都有漏洞——只用时间戳,有效期窗口内可无限重放;只用 Nonce,见过的集合无限增长会撑爆内存;只用序列号,窗口内可乱序插入历史包。三者缺一不可。
- 关键细节:时间戳负责限制有效期(过期即丢,从而 Nonce 集合只需保留窗口期内的、解决内存泄漏);Nonce 负责窗口期内的唯一性;序列号负责顺序完整性(拦乱序和倒退)。合起来保证任一消息在「时间/随机性/顺序」三个维度同时合法,重放无缝可钻。
- 确定性规则消除竞态:握手时若双方同时各生成密钥会冲突,用“名字字典序小的当发起方”这个确定性规则保证只有一方发起,天然避免竞态。是工程常用技巧。
- CLAUDE.md 的本质:不是项目介绍,是“红线清单 + 常识补丁”——装的是那些对你显而易见、但对新协作者不可见、一旦不知道就会破坏项目的隐性知识。骨架通用(是什么/怎么跑/架构/约束/坑/风格),血肉必须按项目定制。
- REVIEW.md 分批=风险驱动:安全→质量→健壮→测试 的顺序对应风险优先级,先查最致命的。“每批改完跑测试再下一批”是“小批量验证”,把故障范围控制在最近一批。都是软件工程方法论。
- AI 不会主动给过程透明度:Agent 默认“给结果”不“给过程”。任何过程透明度(每步说明改了什么、先给方案再动手)都必须显式索取。且即便要求了,执行仍可能跑偏(实证:要求写进 REVIEW.md,它却写到了 .claude/plans/)。
3. 可复用的代码/方案
# 用于 AI coding 的指令模板(控制风险版)
“请分步进行,每步说明你改了什么。先告诉我方案,我确认后再动手。
若改了原有测试,逐一说明改哪个、为什么。
完成后运行 [测试命令] 确认通过,并实际运行一次确认功能。”
# 每句对应一个风险:方案确认(防方向错)/分步(隔离故障)/
# 说明测试改动(防偷偷放宽)/实际运行(防只过测试不过真实场景)# 判断“该不该做某件事”的通用原则
不做 X,我的核心目标(交付/学懂)会受影响吗?
会 → 必需,做
不会,只是更完整/好看 → 锦上添花,有余力再做
# 判断“哪些文档要跟代码改”的通用原则
这份文档里有没有哪句话现在变成假的了?
有假话 → 必须改 只是不够新 → 看重要性 无关 → 不动4. 未解决的问题 & 下一步
- 数字签名方案没深入:来源认证的彻底解法(私钥签名)只讲了原理,没看具体怎么接进这个项目。下次可追问:如果要在握手时加 RSA-PSS 签名,具体改哪里、和现有 AAD 方案怎么共存。
- 前向保密缺口:用 RSA 加密传 AES 密钥不具备前向保密。RSA 私钥日后泄露,可解出历史所有会话密钥 → 解密全部旧消息。真正的前向保密靠 ECDH 这类一次性临时密钥。
- 反射回放弱点:双方用“同一把”共享密钥(不分方向),服务器可把“A发给B的消息”反射回A,A可能误认为是B发的。真协议用 HKDF 派生双向各一把密钥杜绝。
- 下次该追问的:
- 与其问“这方案行不行”,不如自己先判断行不行、再让 AI 挑错。
- “把
fingerprint接入真实路径,最小改动是改哪几行?TOFU的状态存哪、怎么判定‘变了’?” - ECDH 怎么接进现有 RSA 框架?前向保密(Signal 的 Double Ratchet 简化版)最小实现长什么样?
- 反射回放的最小修法——双向独立密钥(HKDF 派生发/收两把)或在 AAD 里加方向标识,哪个改动更小?
5. 元认知评价
提问方式的效率问题(具体到句):
- 环境那一段是最大的效率黑洞。conda 第一次报错后,你连续几轮发完整的错误日志问“依旧有问题怎么办”,把一个“五分钟改三行代码就能绕过”的枝节问题,拖成了横跨多轮的拉锯。问题出在没有在第一次失败时问“这条路如果不通,代价多大、有没有别的路”,而是默认“必须把当前这条路走通”。
- “我直接执行第二步的指令?”这类问题,本质是把决策权交出去等批准。你其实有能力自己判断,但习惯性地先问。
- “现在告诉简要我什么是……客户端+服务器模型”——你写了“简要”,但随后对 5 个校准问题几乎全选“都要/要”。“简要”这个限定词和你实际想要的深度自相矛盾,结果限定词失效。要么真要简要,要么删掉它,别让它空转。
提示词中存在矛盾词句,可能会对生成回答造成干扰,模型得先花力气调和这些噪声。
自己下次表达的更清楚点,维度可以多,但是回答内容形式要简要,减少不必要的冗余回答信息。
思维方式暴露的盲点/惯性:
- 没有止损意识:一条路明显在吞噬时间且不是核心,仍硬走。卡住超过 20 分钟还在原地,就是该换路的信号,你那时没有这个开关。
- 手段当成目的:把“让它在我机器上跑起来”当成必须翻越的山,但你的真实目的是“理解项目、能交付”,环境跑不跑跟这几乎无关。
- 遇偏差的默认动作是“问”而非“先想”:你自己在反思里点破了这点,很准。这是整条对话最值得改的惯性——它导致主线被枝节污染、能力不长、只长 token 消耗。
如果重新问,最优提问的改写:
- 原问(环境卡住时):
“依旧有问题,conda clean --all 的结果 [贴日志]”
改写:
“conda 装 3.11 已失败 3 轮(网络/解压/文件占用)。我判断这是枝节问题,不影响项目核心,打算放弃装环境,改用选择 A(改 bytes|None 为 Optional)绕过,用现有 3.9 跑。这个判断和止损时机对吗?”
改写版的差别:你先做了分类(枝节)、先有了方案(选择A)、先做了决策(放弃装环境),只让 AI 验证而非代你决定。 这样主导权在你,AI 只是审稿人——而你真正想要的能力增长,只发生在你做决策的地方。
- 原问(防御措施整合):
帮我看下这个plan,有没有把以下三个问题都解决了,以及这个计划有没有因此对可能产生的影响都做了处理(比如测试文件以及design和readme文件等要修改)
- 把 SecureChannel 接进聊天程序、客户端补指纹核对、代码里 seen_nonces 无上限增长
- 只需要记住窗口内的 nonce,更早的消息已被时间戳拒掉,对应 nonce 可以从集合清掉;外层 HMAC 的真实职责是保护元数据,不是保护正文(正文 GCM 已经管了)
- 服务器健壮性:没有重名处理(peer_keys 以昵称为键,两个同名用户会互相覆盖、点对点路由错乱);from 完全可伪造
在 AI 给出对应三个解决办法后又追问:
这三个改动是否会影响代码演示或运行效果,还是只是吹毛求疵,因为我已经改很多地方了,不想再一些小细节上继续过度完美化。如果工作量很小,你就把我应该告诉 Claude code 提示词给我,告诉他我想如何将修改 plan
改动:
我在把 SecureChannel 接进聊天。这是当前 plan(贴)。请按这个框架审查,不要只列我漏的功能,重点追踪二阶后果:
- 我自己识别的问题:[seen_nonces 无界 / 重名 / from 可伪造]。逐个判断 plan 是否真解决,还是只是平移到了新结构里。
- plan 里每一个“修复”动作,是否会引入新的竞态、状态不一致、或与现有逻辑冲突? 尤其状态机(握手/验证/断线)和并发(锁、回调线程)路径。
- 把发现按“会不会在我 GUI 演示中被触发”分两档:必修(演示可见)/ 可跳过(仅生产相关)。必修的直接给我可粘贴的补丁段 + 给 Claude Code 的提示词。
- 最后告诉我:这些二阶后果里,哪些是我自己跑一遍状态机本该能发现的——我想知道我的盲点在哪。
这个改动之后的提示词特别好:
- 充分考虑到了修复之后带来的连锁反应;
- 体现了干活的主次任务之分,影响验收标准的 bug 要第一时间修复,其他在此之外的可以完成目标后再酌情考虑;
- 主动要求反馈自己的能力缺口,而不是只是沉浸在对 AI 对话中,没有及时去反思自己之前的思考盲点;
提问模式分析
对整个项目的问答进行更深的分析,找到存在的思维盲点以及还可以提升的地方。
上一部分中元认知评价中涉及到的点,除非必要不会在这部分再次重复。
面对整个对话进行以下分析:
## 1. 提问惯性
我反复出现的提问模式是什么?(具体到句式或思路)
其中哪些是高效的,哪些是低效的?
## 2. 思维盲点
对比这些对话,我有哪些反复绕不开的思维盲区?
(举例:是否过度依赖类比?是否经常先有答案再找问题?
是否在没必要的地方追求确定性?是否回避某类问题?)
## 3. 知识结构问题
我提的问题暴露了哪些知识结构上的缺口或失衡?
## 4. 操纵 AI 的能力评估
- 我是否经常给出无效约束(写了 AI 不会遵守的指令)?
- 我是否充分利用了上下文/示例/角色设定?
- 我哪些提示词其实是在帮 AI,哪些是在干扰 AI?
## 5. 下周训练建议
基于以上分析,给我 3 个本周可执行的提问练习。
直接、尖锐、给具体例子,不要客套。1. 提问惯性
你反复出现的几种句式,及其效率判断:
模式 B:「这样可行吗?」「这步是否是对的?」「我直接执行吗?」——这是你的低效模式,占比最大。表面是在征求意见,实质是把决策权外包。问题在于:你问“可行吗”时,往往自己心里已经有判断了,但不说出来、等我替你拍板。这让你停留在“执行者”而非“决策者”。低效不在于问,而在于没有先把自己的判断亮出来。
模式 C:「[贴一大段日志/工作流] 帮我看下/这样改动到底发生了什么」——中性偏低效。贴原始材料让我解读,本身没错,但你常常只让我“翻译”,不先说自己看懂了多少。结果是我替你读、你看我的读后感,你自己的解读能力没被调用。
一句话总结惯性:你在“反思层”是高手,在“执行层”习惯性交权。
2. 思维盲点
盲点一:在不重要的地方追求确定性(最突出)。
conda 一个枝节工具问题,你非要“把它彻底解决、确认能跑”才肯往下走,结果耗掉数轮。你对“留一个未解决的小问题继续往前”有强烈的不安。 但工程现实是——大量枝节问题就该被绕过、被搁置,带着已知缺陷往前走是常态。你缺的是“这个不确定性可以接受”的容忍度。
盲点二:先有“必须做”的预设,再来确认,而不是先质疑“要不要做”。
集成 SecureChannel 这件事,你一路问的是“怎么集成”“集成方案行不行”“文档要不要跟着改”——全都预设了“集成是要做的”。直到你被搞得琐碎的细节搞得杂乱的时候,然后开始反问“是不是可以不用做集成”,你才意识到它只是锦上添花。你容易接受一个目标后就埋头优化路径,而不回头质疑目标本身该不该存在。 这和盲点一是同一个根:一旦进入一条路,就想把它走完走对。
任何代价在发现的时候立马改都是最小的代价,自己可能因为不敢勇于承担当前方案方向错误的可能性,从而被 AI 忽悠着继续往前走。
对于给出的方案,自己应该在了解清楚后,通过简单方式快速验证跑通。其实在 AI Coding 前期的时候偷的每一步懒,都可能在后面狠狠背刺你。
盲点三:回避“自己先下判断”。
你会回避的是“把自己的答案先写出来、承担判断错的风险”。问“对吗”比说“我认为是X因为Y,对吗”更谨慎——后者万一错了会被指出来。但是实际上这是很有效的学习方式:先猜、被纠正,记忆和能力都长得最快。
盲点四:问题“发现”强,问题“后果追踪”弱
虽然你发现 seen_nonces 无界、重名覆盖、from 可伪造这三个问题,但是没有想到修复完后造成的影响,也就是“修复 A 会撞坏 B”的二阶后果。补洞的时候,也要注意会不会因为补洞而导致挖了新洞。状态机/并发类这类代码经常会带来这类问题,这是你需要小心的。
3. 知识结构缺口
缺口一:工具链/工程环境是明显短板。 不是 conda 本身,是整类“环境、依赖、版本、命令行”的东西。这块是 AI coding 时代很多人共同的盲区——核心逻辑能想清楚,但被工程脚手架绊住。
缺口二:对“软件工程方法论”只有零散感知,没有体系。 你能赞赏分批审查、留痕、小步提交这些做法,但你是被动识别它们的好,不是主动知道“这叫风险驱动”“这叫变更日志”。说明你接触过这些实践,但没有把它们归纳成有名字的方法论。
缺口三:密码学“原语层”清楚,“协议层/系统层”薄。 你对单个原语(RSA、AES-GCM、HMAC、哈希)的“做什么”吸收很快。但一旦上升到“多个原语如何组装成协议、组装处的接缝在哪”就吃力。
4. 操纵 AI 的能力评估
无效约束:你基本没写过。 你的指令大多是合理的(分步、说明改动、跑测试)。但有一个反向的发现更重要——你常常根本不写约束,直接执行我给的指令。 你的问题不是“写了 AI 不遵守的约束”,而是“指令几乎都来自我,你只是转发”。
AI 给的指令也不要全盘照抄,可以自己审阅清楚后,加点自己的想法。
上下文/示例/角色设定:利用不足。 整条对话你几乎没用过“示例”和“角色设定”。你没说过“按 X 标准来”“假设你是审查我代码的资深工程师”,“像对待初学者那样解释”,“扮演一个试图打穿这套系统的安全审计员”, “你是审查我并发代码的资深工程师,重点挑竞态” 。你还没把“给 AI 设定标准和角色”纳入工具箱。
AI 的回答中对示例模仿的权重很高,给出正确合适的示例能让 AI 进一步理解你的需求。
哪些提示词在帮 AI、哪些在干扰:
- 帮:
- “先给方案再动手”“逐一说明改了哪个测试”——这些给了 AI 正确的停顿点和汇报点,是高质量约束。
/extract和这次的结构化提问——明确的输出结构让 AI 不跑偏。
- 干扰:
贴大段日志却不说重点。当你把整屏 conda 报错丢过来问“怎么办”,AI 得自己猜哪里是关键。如果你先标一句“我觉得问题在解压那行,对吗”,AI 的回答会精准十倍。 不标重点,等于让 AI 陪你大海捞针。
直接甩出问题,但没有做进一步的说明,是让 AI 自己在那边猜你的想法,它就只能基于大数据和上下文进行 freestyle,但回答并不一定是你想要知道和了解的。
没有对问题进行进一步描述和给出自己的想法,就是在浪费时间。
每轮五问(稳定系列里徒增往返):
因为你在 preferences 里设置“回答前至少问五个澄清问题”,一般情况下没啥问题,但是在一个参数已经稳定的连续系列里,它逼出每轮重复的校准,单纯就是仪式;
所以比较好的做法是,把原本无条件触发改成条件式触发:“当任务范围模糊或涉及主观取舍时,先问澄清问题;范围清晰时直接作答”。
5. 下周训练建议
练习一:「先判断,再验证」——禁用裸问句。
本周强制自己:任何“对吗/行吗/可以吗”的问题,前面必须先写两句话——“我的判断是 ___,因为 ___”。 再问“对吗?”等问题。所有技术提问都强制走这个格式,逼你把心智模型摆上台面
反面(本周禁止):“我直接执行第二步指令吗?”
正面(本周要求):“我打算直接执行第二步,因为基线测试已过、git 已提交,风险可控。这个判断对吗?”
目的:把决策权抢回来。哪怕判断错了也要先写,被纠正比不判断学得快。
练习二:「20 分钟止损」——给枝节问题设硬上限。
本周任何一个不是核心目标的问题(环境、工具、配置),卡住 20 分钟还没进展,强制停下问一句:“这个问题是核心还是枝节?有没有绕过它的代价更小的路?”
具体场景演练:假设你又遇到一个装不上的库。第一反应不该是“怎么装上”,而是“我能不能不用它/换一个/在别的环境跑”。
目的:治“在不重要的地方追求确定性”这个最突出的盲点。
需要始终记得主线任务,不要陷入辅助的支线任务太深,只要先走过去就行,后面即使前面简单走过去的方案不行但因为有了在主线任务上的进展,处理这些支线任务会比原本快很多;
处理到后面的进度,反而更明白哪些事情是有必要的,哪些事情是没有必要的;
练习三:「给 AI 设角色和标准」——每次提问带一个限定。
本周每个实质性问题,加一句角色或标准设定,试不同的:
“假设你是审查我方案的资深安全工程师,挑出最致命的 3 个问题。”
“用我能教给同学的程度解释,别超过 5 句。”
“只告诉我哪里错了,不要给完整答案,我想自己改。”
尤其最后那条“不要给完整答案”会逼 AI 把你推回驾驶座,直接对治你“交权”的惯性,更多去锻炼你的独立思考能力,让 AI 成为 引导者,而不是成为命令者。