阅读视图

发现新文章,点击刷新页面。
🔲 ☆

伯衡君的 LLM Wiki 实践——用 OpenClaw 打造"会生长"的知识复利系统

事情是这样的。前两天,伯衡君在整理自己的知识库的时候,突然发现一个很可怕的问题:我在笔记软件里存了几百篇文章,在收藏夹里藏了上千个链接,还写了几十本电子书的学习笔记。但是!当我真正需要某个知识点的时候,我发现…我他娘的根本想不起来!就像一个暴发户,钱存了一堆,但要用的时候,发现自己连密码都忘了。这就是传统知识管理的困境——我们一直在"输入",却很少"积累"。但现在不一样了。伯衡君用 OpenClaw 搭建了一套"会生长"的 LLM Wiki 系统。它不仅能存知识,还能自己关联、自己推理、自己生长……
🔲 ☆

薄驾驭,厚技能:YC 掌门人揭秘拉开 1000 倍效率差距的 AI 工程化心法

本文永久链接 – https://tonybai.com/2026/04/19/thin-harness-fat-skills

大家好,我是Tony Bai。

在过去一年,你有没有想过,为什么同样用着 GPT 或 Claude 等大模型以及Claude Code这样的Coding Agent,有的人生产力只提升了 2 倍,而有的人却能爆发出 100倍、甚至 1000倍 的惊人能量?

就在前几天,硅谷创投界的大佬、Y Combinator 的 CEO Garry Tan,发表了一篇阅读量高达 70万+ 的长文,极其犀利地揭开了这个“效率鸿沟”背后的残酷真相。

他引用了 Google 前员工 Steve Yegge 的一个惊人论断:

“使用 AI 智能体(Agent)的人,比现在用 Cursor 和聊天窗口的人效率高 10-100 倍,比 2005 年的 Google 工程师效率高 1000 倍。”

Garry Tan 写道:“这不是吹牛,我亲眼见过,我亲身经历过。”

他甚至爆出一个猛料:2026 年 3 月 31 日,Anthropic 意外地将 Claude Code 的全部 51.2 万行源码泄露到了 npm 仓库。他读完了。

“这次泄露证实了我在 YC 一直教导的东西:秘密根本不在大模型本身,而在那个包裹着模型的‘驾驭层(Harness)’!

今天,我们就来读读 Garry Tan 这篇文章,看看硅谷最顶尖的玩家,是如何通过 “薄驾驭,厚技能(Thin Harness, Fat Skills)” 的架构哲学,来榨干大模型的每一滴潜能的。

你的“胖驾驭”,正在杀死你的 AI

在文章的开头,Garry Tan 就毫不客气地指出了当下大多数 Agent 框架的“原罪”:臃肿的驾驭层,和孱弱的技能。

我们大多数人是怎么做的?我们给 AI 挂载了 40 多个 Tool Calls,每一个工具都对应一个外部 API。结果就是,光是这些工具的定义,就吃掉了上下文窗口(Context Window)的一半!

大模型每次行动前,都要在几十个工具中艰难地做选择题,不仅推理速度慢得像乌龟,出错率更是高得离谱。

Garry Tan 给出的架构哲学恰恰相反:

推崇“薄驾驭(Thin Harness)”:驾驭层(Harness)只做四件事——循环运行模型、读写文件、管理上下文、执行安全策略。代码量可能只有 200 行。
推崇“厚技能(Fat Skills)”:将所有复杂的业务逻辑、判断力、领域知识,全部封装成一个个可复用、可参数化的 Markdown 文件,即“技能(Skill)”。

“反面教材就是一个臃肿的驾驭层,配上一堆孱弱的技能。……你想要的是专为特定目的打造的、快速且狭窄的工具。”

五大心法:从“写 Prompt”到“编程 AI”

Garry Tan 认为,大模型的瓶颈从来不是智商。它天生就会推理、综合、写代码。它之所以频繁失败,是因为它不理解你的数据、你的规范、你问题的具体形态。

而下面这五个定义,正是修复这个问题的“架构级补丁”,也是“薄驾驭,厚技能”哲学的具体体现。

心法一:技能文件(Skill Files)—— 用 Markdown 写“方法调用”

这是最颠覆认知的一点。Garry Tan 认为,优秀的 Skill 文件,工作起来就像一个函数调用(Method Call)。它接受参数,并且根据不同的参数,产生完全不同的能力。

他举了一个名为 /investigate 的技能为例。这个 Skill 只有 7 个步骤:圈定数据集、构建时间线、标注文档、综合信息、正反方辩论、引用来源。

  • 当你把这个 Skill 指向一个安全科学家的 210 万封邮件,它就变成了一个医学研究分析师
  • 当你把它指向一家空壳公司和 FEC 的文件,它就变成了一个法务调查员

“这根本不是提示词工程。这是软件设计。你用 Markdown 作为编程语言,用人类的判断力作为运行时。”

心法二:解析器(Resolvers)—— AI 的“智能路由表”

技能文件告诉模型“怎么做”,而解析器告诉模型“在什么时候,加载什么上下文”。

Garry Tan 坦白,他自己曾经写过一个长达 20000 行的 CLAUDE.md 文件,里面塞满了各种他遇到过的奇技淫巧。结果导致模型注意力严重下降,甚至 Claude 自己都“告诉”他让他删掉点。

最终的解决方案,是把这个巨大的文件,拆成了一个只有 200 行的“指针”文件,和一个解析器(Resolver)

“当一个开发者改了代码,没有解析器,他直接提交了。有了解析器,模型会先去读取 docs/EVALS.md,这个文件说:先跑评测套件,对比分数,如果准确率下降超过 2%,就回滚并调查。”

解析器就像一个智能的“路由表”,在不污染上下文的情况下,按需加载最精准的知识。

心法三:潜在空间 vs. 确定性—— 别让 AI 做数学题

这是 Agent 设计中最常见的错误:让 AI 去做它不擅长的事。

  • 潜在空间(Latent Space):这是 AI 的主场。它负责阅读、理解、判断、综合、模式识别。
  • 确定性(Deterministic):这是传统代码的主场。SQL 查询、代码编译、数学计算。

“一个 LLM 可以帮你安排 8 个人的晚宴座位,它会考虑每个人的性格和社交关系。但你让它去排 800 个人的座位,它会幻觉出一个看似合理、但完全错误的座位表。因为这是一个确定性的组合优化问题,应该交给传统算法。”

最牛逼的系统,对这条边界的划分是冷酷无情的。

心法四:倾向分析 —— 让 AI 拥有“判断力”

这是让 AI 从“数据库”进化为“分析师”的关键一步。

倾向分析,就是让模型读取关于一个主题的所有信息,然后写出一份结构化的、浓缩了判断力的简介。

Garry Tan 以 YC 内部正在构建的、管理 6000 名创始人的 AI 系统为例:

传统的关键词搜索,根本无法发现一个伪装成“可观测性”工具的 FinOps 项目。

但通过倾向分析,AI 读取了创始人的所有 GitHub 提交、访谈记录、公开帖子后,会给出一个惊人的洞察:

“创始人:Maria Santos。公司:Contrail。声称在做:‘给 AI Agent 用的 Datadog’。实际在做: 80% 的代码都在写账单模块。她其实在做一个伪装成可观测性的 FinOps 工具。”

这种“判断力”,是任何 RAG 管道都无法产生的。模型必须真正地去“阅读”和“思考”。

心法五:技能即永久升级

这是 Garry Tan 架构哲学的最终闭环。

他给自己团队的 AI Agent 下了一条铁命令:

“你绝不被允许做一次性的工作。如果我让你做一件事,而这件事未来可能需要再做一遍,你必须:第一次手动在 3-10 个样本上完成它,把结果给我看。如果我批准了,你就必须把这个过程固化成一个 Skill 文件。如果它应该自动运行,就把它加到定时任务里。测试标准:如果我需要为同一件事第二次开口,你就失败了。”

每一次你写下的 Skill,都是对你的 AI 系统的一次永久性升级。它永不衰退,永不遗忘。当下一代更强的模型发布时,你所有的 Skill 都会在一夜之间变得更聪明。

小结

Garry Tan 的这篇文章,为所有在 AI 时代感到迷茫的开发者,提供了一套极具实操性的架构蓝图。

它告诉我们,当大模型的能力趋于同质化时,真正的护城河,不在于你拥有哪个模型的 API 访问权限,而在于你为这个模型构建了怎样一个高效、智能、且能源源不断自我进化的“外部大脑”。

这个“大脑”,就是你沉淀下来的“厚技能(Fat Skills)”资产。

不要再沉迷于寻找那些能一键生成整个项目的“魔法按钮”了。慢下来,像一个真正的软件设计师一样,去思考你的流程、你的判断、你的领域知识。然后,把它们一一固化成 Markdown。

构建一次,它将永远为你运行。

资料链接:https://x.com/garrytan/status/2042925773300908103


今日互动探讨:

看完 YC 掌门人的这套“薄驾驭,厚技能”心法,你对自己目前开发 Agent 的方式有什么新的反思?你认为用 Markdown 来“编程”AI 的想法,是天才还是异端?

欢迎在评论区分享你的看法!


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


原「Gopher部落」已重装升级为「Go & AI 精进营」知识星球,快来加入星球,开启你的技术跃迁之旅吧!

我们致力于打造一个高品质的 Go 语言深度学习AI 应用探索 平台。在这里,你将获得:

  • 体系化 Go 核心进阶内容: 深入「Go原理课」、「Go进阶课」、「Go避坑课」等独家深度专栏,夯实你的 Go 内功。
  • 前沿 Go+AI 实战赋能: 紧跟时代步伐,学习「Go+AI应用实战」、「Agent开发实战课」、「Agentic软件工程课」、「Claude Code开发工作流实战课」、「OpenClaw实战分享」等,掌握 AI 时代新技能。
  • 星主 Tony Bai 亲自答疑: 遇到难题?星主第一时间为你深度解析,扫清学习障碍。
  • 高活跃 Gopher 交流圈: 与众多优秀 Gopher 分享心得、讨论技术,碰撞思想火花。
  • 独家资源与内容首发: 技术文章、课程更新、精选资源,第一时间触达。

衷心希望「Go & AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚,享受技术精进的快乐!欢迎你的加入!

img{512x368}


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

© 2026, bigwhite. 版权所有.

🔲 ☆

排查Linux进程"卡死"实战:从strace到gdb全流程

* 故障现象 某天,我在 Orange Pi 上跑了一个批量处理 URL 的脚本,用 =claude= CLI 把网上的文章自动转成知识卡片。脚本跑了一晚上,第二天发现它好像"卡住了"——终端没有任何输出,进度也不再推进。 怎么确认它真的卡住了、又卡在哪里呢?下面是完整的排查过程。 * 排查过程 ** 用 strace 查看进程在干什么 =strace= 是 Linux 下排查进程问题的神器,它能追踪一个进程正在调用的所有系统调用(system call)。系统调用是程序向内核请求服务的接口,比如读文件、写网络数据、等待定时器等。 先用 =ps= 找到卡住的进程 PID,然后 attach 上去: #+begin_src shell sudo strace -f -p 169047 -o /tmp/169047.log #+end_src - =-f= :追踪子进程和子线程 - =-p 169047= :attach 到 PID 为 169047 的进程 - =-o /tmp/169047.log= :将输出写入日志文件 等了几秒钟后按 =Ctrl+C= 停止,然后查看日志。 ** 分析 strace 输出 日志有 7049 行,初看很吓人,但关键是找到规律。先看前面几行: #+begin_example 169080 read(16, 169059 epoll_pwait2(13, 169047 epoll_pwait2(4, 169048 futex(...) = -1 ETIMEDOUT (Connection timed out) ... #+end_example 每一行的格式是: =线程ID 系统调用(参数) = 返回值= 这里有多个不同的数字(169047、169048、169049……),它们是什么?同一个进程可以有多个 *线程* ,每个线程有自己的 ID(TID),但它们共享同一个进程的内存和文件描述符。 *** 初步归类 过滤掉重复的 =sched_yield= (让出 CPU)和 =futex= (线程同步)后,我发现了这些有意义的操作: | 线程 | 系统调用 | 含义 | |---------------------+------------------------------------+------------------------------| | *169047* (主线程) | =epoll_pwait2(4, [], ...)= 反复超时返回空 | 事件循环在等事件,但什么都没来 | | *169080* | =read(16, ...)= 从第1行卡到最后一行 | 一直阻塞在读操作上 | | *169049-169053* | 大量 =sched_yield= | 工作线程在空转 | | *169059* | =epoll_pwait2(13, ...)= 周期性返回事件 | 有事件到来,但…… | | *169057-169061* | 周期性 =statx= 检查 .git 文件 | 正常的文件监控轮询 | 主线程 169047 在做事件循环(event loop):等事件 → 超时 → 检查一些状态 → 回去继续等。epoll 每次返回的都是空数组 =[]= ,说明主事件循环上 *没有事件就绪* 。 线程 169080 从日志开始到结束一直卡在 =read(16, ...)= 上,从未返回。看到 =read()= 阻塞,我的第一反应是: *这个进程大概在等网络响应* 。毕竟它是一个调用 API 的程序, =read()= 卡住最常见的原因就是网络请求发了出去,但服务端的响应迟迟没到。但这个猜测对不对呢? #+BEGIN_QUOTE *小知识:什么是文件描述符(fd)?* 在 Linux 中,所有 I/O 操作都通过文件描述符进行。打开一个文件、建立一个网络连接、创建一个管道,都会得到一个 fd。它就是一个整数编号(0=标准输入,1=标准输出,2=标准错误,3 以上由程序自己分配)。 fd 不仅对应普通文件,还可以对应网络 socket、管道、定时器、inotify 实例等。知道了 fd 的编号,不等于知道它的类型——我们需要到 =/proc= 文件系统中去查看。 #+END_QUOTE ** 确认 fd 16 的类型:不是网络 socket #+begin_src shell ls -la /proc/169047/fd/16 #+end_src 输出: #+begin_example lr-x------ 1 orangepi orangepi 64 /proc/169047/fd/16 -> anon_inode:inotify #+end_example fd 16 是一个 *inotify* 实例!inotify 是 Linux 的文件系统事件监控机制,用来监听文件变化(比如文件被修改、创建、删除)。 =read()= 在 inotify 上阻塞是 *设计上就是这样的* ——它只在有文件变化时才返回数据,没有变化时阻塞等待是天经地义的事。 但是,一个 inotify 线程阻塞,并不能解释整个进程"卡住"的现象。这个线程只是一个后台的文件监控器,它的职责就是安静地等文件变化。进程之所以"卡住",是因为它无法完成自己的核心任务——处理 URL。所以 fd 16 不是故障原因,我需要继续找。 ** 转向线程 169059:epoll fd 13 只收到定时器事件 strace 日志中,线程 169059 也在做一个 =epoll_pwait2= 。单独提取它的记录: #+begin_src shell grep '169059' /tmp/169047.log #+end_src 输出: #+begin_example 169059 epoll_pwait2(13, 169059 <... epoll_pwait2 resumed>[{events=EPOLLIN, data=...}], ...) = 1 169059 read(14, "\1\0\0\0\0\0\0\0", 8) = 8 169059 epoll_pwait2(13, 169059 <... epoll_pwait2 resumed>[{events=EPOLLIN, data=...}], ...) = 1 169059 read(14, "\1\0\0\0\0\0\0\0", 8) = 8 169059 epoll_pwait2(13, ...(重复多次) #+end_example 线程 169059 在反复做一件事: =epoll_pwait2(13, ...)= → 返回 1 个事件 → =read(14, ...)= → 回到 =epoll_wait= 。 注意一个规律:每次 epoll 返回后,线程读的都是 *fd 14* (后面可以看到这是个定时器)。也就是说,线程在重复等待 epoll fd 13 上的事件。那 epoll fd 13 到底监控了哪些 fd?我们需要查两样东西:进程的完整 fd 列表(搞清楚每个 fd 是什么),以及 epoll fd 13 的监控清单(搞清楚它在等谁的数据)。 ** 查看 fd 全貌和 epoll 监控清单 先看进程一共有哪些文件描述符: #+begin_src shell ls -la /proc/169047/fd/ #+end_src 输出中能看到的 fd 类型包括: | fd | 类型 | 说明 | |-------------+------------------------+--------------------------| | 0 | /dev/pts/1 | 标准输入(终端) | | 1, 2, 11, 12 | pipe | 管道(stdout/stderr 重定向) | | 3, 9 | /dev/urandom | 随机数生成器 | | 4 | anon_inode:[eventpoll] | 主事件循环的 epoll 实例 | | 5, 7, 8 | anon_inode:[timerfd] | 定时器 | | 6 | anon_inode:[eventfd] | 事件通知 | | 10 | /proc/169047/statm | 进程内存统计 | | 13, 32 | anon_inode:[eventpoll] | 另外两个 epoll 实例 | | 16 | anon_inode:inotify | 文件系统监控 | | *28* | *socket:[616763]* | *唯一的网络 socket!* | 进程里有三个 epoll 实例(fd 4、fd 13、fd 32)。查看 epoll fd 13 的监控清单: #+begin_src shell cat /proc/169047/fdinfo/13 #+end_src 输出: #+begin_example tfd: 28 events: 19 data:2611c3c00a0 ← 网络 socket tfd: 15 events: 80000019 data:2611c0a0100 ← eventfd tfd: 14 events: 19 data:2611c0a00c0 ← timerfd #+end_example =/proc/PID/fdinfo/EPOLL_FD= 中的 =tfd= 字段就是被监控的目标 fd 编号。epoll fd 13 同时监控了三个 fd:fd 28(网络 socket)、fd 15(eventfd)、fd 14(定时器)。 strace 中只看到 fd 14(定时器)触发,fd 28 和 fd 15 都没有。但 fd 15 不触发并不奇怪——它是一个 *eventfd* ,这是一种线程间内部通知机制,只有当其他线程主动往里写数据时才会触发。如果内部没有需要通知的事情,它安静待着是正常的。 而 fd 28 就不同了。它是 *网络 socket* ,是进程与外部世界通信的唯一通道。这个进程是 =claude= CLI,它向 API 服务器发出了请求,正在等响应。socket 长时间不触发,意味着服务端的响应数据迟迟没到——这才是进程"卡住"的根本原因。 ** 检查网络连接状态 找到了网络 socket fd 28,它的 inode 号是 616763。接下来要搞清楚这个 socket 连接的是谁、状态如何。 *** 从 /proc/net/tcp 找到连接详情 =/proc/net/tcp= 文件列出了系统所有 TCP 连接。我们需要从中找到 inode 为 616763 的那一条: #+begin_src shell cat /proc/169047/net/tcp #+end_src 输出很长,但关键的一行是: #+begin_example sl local_address rem_address st tx_queue:rx_queue ... inode ... 13: 191FA8C0:CF72 33551777:01BB 01 00000000:00000000 ... 616763 ... #+end_example inode 616763 匹配上了!但 =local_address= 和 =rem_address= 都是十六进制,怎么看懂? *** 解码 /proc/net/tcp 的地址格式 =/proc/net/tcp= 中的地址格式是 =IP地址:端口= ,其中 IP 地址是 *十六进制、小端序* 编码的。以远端地址 =33551777:01BB= 为例: #+begin_example 远端 IP: 33551777 → 每 2 位拆成一个字节: 33 55 17 77 → 反转字节序: 77 17 55 33 → 119.23.85.51 远端端口: 01BB → 十六进制转十进制 → 443 (即 HTTPS) #+end_example 同理,解码本地地址 =191FA8C0:CF72= : #+begin_example 本地 IP: 191FA8C0 → 19 1F A8 C0 → 反转: C0 A8 1F 19 → 192.168.31.25 本地端口: CF72 → 53106 #+end_example 所以这个连接是:本机 =192.168.31.25:53106= → 远端 =119.23.85.51:443= #+BEGIN_QUOTE *小知识:为什么要反转字节序?* =/proc/net/tcp= 中的 IP 地址使用 *网络字节序* (大端序)存储,但在 x86/ARM 等 *小端序* 机器上,内核为了存储效率,按小端序输出十六进制。所以 =33551777= 实际上是 =0x77175533= 的小端序表示,对应 IP =119.23.85.51= 。 简单记忆: *把十六进制字符串每两位切一刀,然后倒着拼回去,再转成十进制 IP* 。 #+END_QUOTE *** 用 ss 查看连接的详细状态 拿到了远端 IP 后,用 =ss= 命令查看这个连接的详细信息: #+begin_src shell ss -ti dst 119.23.85.51 #+end_src - =-t= :只显示 TCP 连接 - =-i= :显示详细信息(RTT、超时、收发字节数等) - =dst 119.23.85.51= :只看连接到这个 IP 的 输出中最关键的字段: #+begin_example ESTAB 0 0 192.168.31.25:53106 119.23.85.51:https bytes_sent:2114416 bytes_acked:2114417 bytes_received:586087 lastsnd:10621788 lastrcv:10621508 #+end_example 解读: | 指标 | 值 | 含义 | |--------------------+-----------------------------+--------------------------------| | 状态 | ESTABLISHED | TCP 连接看起来还活着 | | bytes_sent / bytes_acked | ~2MB / ~2MB | 请求已完整发送并被服务端确认 | | bytes_received | ~572KB | 收到了部分响应 | | *lastsnd* | *10,621,788 ms ≈ 2.95 小时* | *最后一次发送数据是近 3 小时前* | | *lastrcv* | *10,621,508 ms ≈ 2.95 小时* | *最后一次接收数据也是近 3 小时前* | *** 真相大白:TCP 半死连接 连接的状态是 ESTABLISHED,但近 3 小时没有任何数据流动。这说明: 1. 进程发送了约 2MB 的 API 请求(完整到达服务端) 2. 服务端回了约 572KB 数据后,传输中断了 3. 中间的网络设备(NAT/防火墙)可能丢弃了这个长时间空闲连接的状态 4. 由于没收到 TCP RST 或 FIN,进程端以为连接还活着,一直在等 这就是经典的 *TCP 半死连接* (half-open connection)问题。TCP 本身不会因为长时间无数据就主动关闭连接(除非开启了 TCP keepalive),如果应用层也没有设置读取超时,进程就会 *无限期等待* 。 * 恢复过程 ** 问题 1:能不能安全 kill? 在决定 kill 之前,需要了解进程之间的关系: #+begin_src shell ps -ef --forest | grep -A5 url-to-cards #+end_src #+begin_example bash url-to-cards.sh (138905) ← 主脚本 └── bash (169046) ← command substitution 子 shell └── claude (169047) ← 卡住的 claude 进程 #+end_example 脚本开头设置了 =set -euo pipefail= , =set -e= 会让脚本在任何命令返回非零时立即退出。但脚本中有这样的写法: #+begin_src shell output=$(claude ...) || true # ^^^^^^^^ #+end_src =|| true= 的作用是:即使 claude 命令失败,表达式整体也会返回成功( =true= 的返回值是 0)。这样 =set -e= 就不会触发,脚本不会退出。 所以,kill 掉卡住的 claude 进程(169047)后: 1. claude 收到信号退出 2. 子 shell 也退出(非零) 3. =|| true= 兜住错误 4. 脚本继续处理下一个 URL ** 问题 2:原始 URL 列表丢失了 脚本读取的 URL 文件 =/tmp/feed2mail.urls.txt= 已经被另一个程序覆盖了,原始内容丢失了。但 bash 进程(138905)启动时已经把 URL 列表读进了内存中的 =urls= 数组。 ** 用 gdb 从进程内存恢复数据 =gdb= (GNU Debugger)虽然主要用来调试程序,但也能 attach 到运行中的进程,读取它的内存。 #+begin_src shell # 先找到堆内存的地址范围 cat /proc/138905/maps | grep heap # 输出: aaaaec588000-aaaaec60c000 rw-p ... [heap] # 用 gdb 导出堆内存 sudo gdb -batch -p 138905 \ -ex 'dump memory /tmp/bash_heap.bin 0xaaaaec588000 0xaaaaec60c000' \ -ex 'detach' # 从导出的内存中搜索 URL strings /tmp/bash_heap.bin | grep -oE 'https?://[^[:space:]>"]+' | sort -u #+end_src - =dump memory= :将指定地址范围的内存写入文件 - =strings= :从二进制数据中提取可打印字符串 - =grep -oE 'https?://...'= :用正则匹配 URL - =sort -u= :去重排序 成功恢复了 *189 个 URL* ! #+BEGIN_QUOTE *小知识:为什么要从堆(heap)恢复?* 进程的内存分为多个区域: - *代码段(text)* :存放程序指令 - *数据段(data)* :存放全局变量 - *堆(heap)* :动态分配的内存(bash 变量存在这里) - *栈(stack)* :函数调用栈 bash 的变量(包括数组)存储在堆内存中,所以我们从堆区域提取数据。 #+END_QUOTE * 总结 ** 排查流程回顾 #+begin_example 进程卡住了 │ ├─ 1. strace -f 看进程在做什么系统调用 │ → 发现多个线程:主线程 epoll 空转,线程 169080 read 阻塞,线程 169059 epoll 周期触发 │ ├─ 2. ls -la /proc/PID/fd/16 看 fd 16 指向什么 │ → 是 inotify(文件监控),阻塞是正常行为,排除 │ ├─ 3. 回到 strace,细看线程 169059 │ → epoll_pwait2(13) 每次返回后都读 fd 14(定时器),从不读 fd 28(网络 socket) │ ├─ 4. cat /proc/PID/fdinfo/13 确认 epoll 监控清单 │ → epoll fd 13 监控了 fd 28(网络 socket),但 socket 从未触发事件 │ ├─ 5. ss -ti 查看网络连接状态 │ → lastsnd/lastrcv 约 3 小时前 → TCP 半死连接 │ ├─ 6. 分析代码确认 kill 安全(|| true 兜底) │ └─ 7. gdb dump memory 恢复丢失的数据 → 从堆内存中提取出 189 个 URL #+end_example ** 工具速查 | 工具 | 用途 | 示例 | |----------------------------+------------------------------+----------------------------------| | =strace -f -p PID= | 追踪进程的系统调用 | 看进程卡在哪个 syscall | | =ls -la /proc/PID/fd/= | 查看进程打开的所有文件描述符 | 确认 fd 是 socket、pipe 还是 inotify | | =cat /proc/PID/fdinfo/N= | 查看 fd 的详细信息 | 看 epoll 监控了哪些 fd | | =cat /proc/PID/maps= | 查看进程内存布局 | 找到堆地址范围 | | =ss -ti= | 查看 TCP 连接详情 | 看 RTT、超时、最后收发时间 | | =ps -ef --forest= | 查看进程树 | 理解父子进程关系 | | =gdb -batch -p PID= | attach 到运行中的进程 | 导出内存、查看变量 | * 反思与改进 这次事件暴露了几个可以改进的地方: 1. *应用层超时* :claude CLI 应该给 API 请求设置合理的读取超时(如 5-10 分钟),避免死连接无限挂起 2. *TCP keepalive* :开启 TCP keepalive 可以让内核自动检测死连接 3. *脚本健壮性* :可以在脚本中为每个 claude 调用加上 =timeout= 命令: #+begin_src shell timeout 600 claude -p ... # 最多等 10 分钟 #+end_src 4. *URL 文件保护* :脚本应该在读入 URL 后立即备份,或者用管道传递而非依赖临时文件
🔲 ☆

幽灵外卖重罚35.97亿:拼多多为何独吞15.22亿?

国家监管总局重拳整治外卖平台的新闻现场,巨额罚单文件摊开在木桌上,背景里七家平台的抽象办公楼剪影与外卖骑手穿梭的城市街景形成对照,画面庄重而有压迫感,羊皮纸,钢笔彩色手绘的统一风格。

上周五,国家监管总局给7家外卖平台开出了35.97亿元的罚单,重罚黑心外卖平台,整治“幽灵外卖”。

很多人看到这个消息后的第一反应,都是“青天大老爷”“老天开眼了”,觉得就应该狠狠处罚这帮黑心外卖平台,居然还敢搞幽灵外卖。

但这次我要说的是,这不是一次普通的执法行动,也不是一次普通的处罚。真正让这次罚款变成“雷霆震怒”的,不只是35.97亿元这个数字,也不只是7家外卖平台一起挨罚,而是其中有一家平台,在监管上门调查时,居然选择了硬顶。

这家公司在执法人员进门时,与企业员工发生了肢体冲突。警方介入后,多人因为涉嫌阻碍执行职务被行政拘留。4个月过去,最重的一张罚单落下来了:拼多多被罚15.22亿元。在总共35.97亿元里,它一个平台就被罚了接近一半。

这次重罚,为什么不只是“罚钱”那么简单

执法人员进入大型互联网公司前台调查的瞬间,门禁玻璃门半开,文件夹与执法证清晰可见,企业员工神情紧张,现场气氛凝固,羊皮纸,钢笔彩色手绘的统一风格。

很多人一听会觉得不对:拼多多也不送外卖,为什么它罚得最多?真的只是因为拼多多的问题店铺多吗?不全是。拼多多的问题店铺确实有,但真不算最多。罚得最狠,一个重要原因就是它和执法人员发生了肢体冲突。

2026年4月17日,国家监管总局一纸令下,7家平台、22份处罚决定书,合计罚没35.97亿元。这次还有一个特别少见的地方,就是除了罚企业,还罚了管理层个人,金额接近2000万元。

这7家平台分别是拼多多、美团、京东、饿了么、淘宝闪购、抖音、淘宝天猫。阿里系占了好几家。7家里罚得最狠的是拼多多,15.22亿元,占全案42%。

如果只看标题,很多人的第一反应会是:拼多多活该,平台管得一塌糊涂,罚15亿天经地义。但问题恰恰在这儿:拼多多被罚15亿,不完全是因为它家问题更严重

35.97亿元是怎么罚出来的

一张摊开的处罚计算示意图,表格里按店铺数量、罚款档位和总金额逐项累加,算盘、印章和红线标注出35.97亿元的结果,视觉上像一场精密清算,羊皮纸,钢笔彩色手绘的统一风格。

先看这35.97亿元到底是怎么算出来的。这个有零有整的数字,一定有算法。市场监管总局用的是“按店计罚”,也就是你到底有多少违规店铺,就按店数给你罚。每家违规店铺罚金多少,有不同档位标准:问题少就少罚,问题多就多罚,情节恶劣就按高档罚。

按店计罚的三个档位

  • 最重一档:拼多多适用这一档。
  • 中档:10万元一家,美团和饿了么在这个档位。
  • 从轻档:5万元一家,京东、淘宝天猫、抖音等在这一档。

拼多多走的是最重一档。它一共有9463家问题店铺,乘下来就是15亿左右的量级。需要注意的是,拼多多里有问题的店铺当然不止这些,但这次被拎出来的9463家,集中在一个很特别的品类:裱花蛋糕。也就是蛋糕上用奶油裱花的这类店铺。这些商家大量没有牌照,或者使用假牌照、假资质,还有一些涉及转单。

什么是“幽灵外卖”

手机外卖页面上展示着精致明亮的蛋糕店铺照片,画面另一侧却是阴暗杂乱的小作坊后厨,奶油、纸盒和无证操作台形成强烈反差,直观表现“幽灵外卖”,羊皮纸,钢笔彩色手绘的统一风格。

这里顺便解释一下,什么叫“幽灵外卖”。所谓幽灵外卖,就是平台上对外宣传的店铺看起来窗明几净、资质齐全,但实际供货的店铺却完全不是它,真正出餐的地方可能脏乱差、没有任何资质。这种就叫幽灵外卖。

这次拼多多被罚涉及的9463家,都是做裱花蛋糕的。这个数量之多,确实出乎很多人意料。

其他平台分别怎么罚

中档是10万元一家,美团和饿了么就在这个档位。它们属于累犯,2024年12月已经因同类问题被罚过一次,这次再犯,只能接着罚。这个逻辑也很简单,幽灵外卖本来就是这两家平台上常见的问题,不抓它们抓谁。

从轻档是5万元一家。这个档位里,京东的家数最多,有4.3万家。很多商家在京东只是挂个号,并不真正销售,所以家数虽然多,但真正发生交易的并不多,因此按从轻处理。淘宝天猫和抖音则属于初次被查。抖音其实有点“混过去”的意思,它的问题店铺数量并不少,而且因为平台自己握着流量,很多店铺销售额非常高,所以抖音实际罚得算轻,按5万元一件来算。

最后的结果是,拼多多罚15.22亿元,京东罚6.35亿元。京东这4.3万家里,真正发生交易的只有4858家,所以算是稍微放了一马。而拼多多是实打实的9463家,很多都真实发生了交易,涉案交易金额9708万元,接近卖了一个亿的蛋糕出去。

拼多多为什么罚得最狠

一间互联网公司会议室门口爆发冲突的紧张场面,执法人员被阻拦,文件散落,警方赶到控制局面,人物动作凝固在强烈戏剧瞬间,羊皮纸,钢笔彩色手绘的统一风格。

但拼多多这15.22亿元里,还有一个更关键的加重因素,不在于店铺数量,而在于它面对调查时的态度。正常情况下,监管来查你,你老老实实配合,把材料交了,切割一些商家也就完了。但你居然敢抗法,这个性质就完全不一样了。

监管上门调查时发生了什么

把时间往回翻。2025年12月3日,市场监管总局执法人员进入拼多多上海总部。当时要查的,其实主要是举报内容涉及虚假发货,并不是这次裱花蛋糕的事。但这笔账显然被记下来了。

当天,拼多多多名员工与市场执法人员发生了肢体接触,不是会议室里的语言冲突,而是真正的肢体冲突。现场报警后,上海长宁警方介入,最终多名员工因涉嫌阻碍执行职务被行政拘留。

后来在12月16日,接近拼多多的信源向财新透露,拼多多政府关系部门约30人全部被辞退。据说当时还有一位市场监管总局工作人员在关门时被夹伤,也就是说,现场是有人受伤的,这就更严重了。

事情发生后,这件事在中文互联网上被压下去了,很多人现在可能已经记不清了,但在Bloomberg、曼谷邮报以及多个海外中文媒体上,相关报道还在,藏不住。

处罚书里的关键词说明了什么

所以当你看到处罚书里写着“拒绝提交材料”“提供虚假材料”“暴力软对抗”这些词时,就知道为什么这次拼多多罚得最狠了。所谓“暴力”不用解释,已经发生了肢体冲突,甚至有人受伤;所谓“软对抗”,就是监管要材料时你不好好给。

另一个关键加重因素:转单系统

还有一个让拼多多被重罚的核心原因,是“转单系统”。

平台后台系统界面与多家虚拟店铺之间用箭头连接,消费者在手机上下单一家蛋糕店,订单却被自动分流到另一处隐蔽后厨,数据流和实物蛋糕同时转向,羊皮纸,钢笔彩色手绘的统一风格。

什么叫转单系统?简单说,就是消费者看到的是一家店、一个品牌下单,实际发货或者出餐的却是另外一家。这种事情在各个平台里都很常见。别人家做转单,往往是偷偷做,或者睁一只眼闭一只眼,假装没看见。但拼多多特别过分的地方在于,它专门给转单系统开了接口,相当于平台自己下场提供服务,帮助这个模式做大做强。

这个性质就比普通的默许更恶劣,所以必须重罚,直接拉到最高档。

为什么拼多多罚得最狠?核心不只是9463家问题店铺,而是抗法加上平台亲自为转单系统提供接口,使其性质明显加重。

现在图景就完整了。为什么拼多多罚得最狠?因为它在调查期间居然敢硬顶。执法人员上门,它拒绝提供材料,提供虚假材料,员工跟监管人员动手,甚至还有人员受伤。无论放在哪个国家,这种事都很难被容忍,更何况是在中国。

监管不是来谈判的,是来执法的。你对抗,就等于你承认自己有不想让人看到的东西。所以,对拼多多的这次重罚,抗法是决定性的重点。另一个重点,就是它对转单系统不只是默许,而是平台亲自配合,甚至开接口服务,这就更过分了。

其他平台各自的问题在哪里

四格对照式画面,美团与饿了么的密集餐饮店招、京东堆满空壳店铺名录的后台、抖音流量瀑布推高单店销量、淘宝系与饿了么业务交织的配送网络,各自问题一目了然,羊皮纸,钢笔彩色手绘的统一风格。

美团和饿了么:惯犯逻辑

美团和饿了么属于惯犯,2024年12月刚在北京因类似问题被罚过,这次再犯只能接着罚。原因很简单,成本压得太低了。

一个店铺如果自己搞得窗明几净、申请各种资质、亲自做饭、还要在美团上投放广告,成本是很高的。那怎么办?只能分工协作:有人专门负责拍照、包装门店形象、申请资质;另外一拨人负责提供便宜餐食。也正因为如此,这个问题在中国很难彻底禁止。

京东:占坑扩张,店多但成交少

京东则属于“占坑扩张”型,4.3万家店里大部分没有成交,性质相对轻一些。但这件事对刘强东来说,未必不闹心。

京东高调进军外卖,给骑手上社保,刘强东自己穿上外卖服送餐、请外卖小哥吃饭,下面的人自然要想办法快速把商家数量堆起来。结果一查,4万多家里只有4000多家是真的,剩下90%都是假的。无论有没有骗补贴,起码是把老板给糊弄了。后面京东内部大概率会有人因此倒霉。你说10%是假的,还能勉强接受;90%是假的,这就太夸张了。

抖音:店不算多,但流量伤害大

抖音的情况也很有意思。它的问题店铺其实不多,只有454家审核不严格的店铺。抖音对资质审核一直相对比较严,但它的成交量巨大,有些单店平均成交额高达83.5万元。算法流量一旦把违规店铺推起来,造成的伤害可能比传统平台10家店加起来还大。这就是抖音厉害的地方,它真有流量。所以它虽然违规店铺不多,但赚钱能力很强。

淘宝天猫、淘宝闪购:业务与饿了么关联较深

至于淘宝天猫、淘宝闪购,很多业务本身就是从饿了么那边转过来的。淘宝闪购基本就是原来的饿了么。淘宝天猫虽然也送一些东西,但量不算特别大,主要问题应该也是和拼多多类似,集中在裱花蛋糕等食品类目上。

这次处罚最特别的地方:双罚制

一张企业罚单与几张写着高管姓名的个人处罚通知并排摆放,金色算盘珠和工资卡放在旁边,突出“公司与个人一起被罚”的制度转向,羊皮纸,钢笔彩色手绘的统一风格。

这次处罚还有一个非常不一样的点,就是“双罚制”。以前更多是罚公司,很少系统性地去罚管理层个人。这一次,企业违法不光罚公司,还要罚个人。

依据是《食品安全法实施条例》第七十五条:故意违法、性质恶劣、造成严重后果,满足其一,就可以罚个人上年收入的1到10倍。

所以这7家平台的法人代表、食品安全总监,全都被点名处罚了。拼多多的法人代表被罚693.7万元;美团是252.8万元;饿了么是122万元;抖音相关负责人被罚33万到64.7万元不等。具体是按1倍还是10倍罚,外界并不清楚,但监管的意思很明确:下次再犯,罚的不只是公司账上的钱,还有你们高管自己的工资卡。

而且,这在平台监管领域里,算是第一次系统性使用。以前的双罚制更多针对黑作坊、问题食品厂这种小企业,平台经济很少被这样对待。所以这个信号,平台高管们不可能看不懂。

特别是像拼多多这种美国上市公司,罚十几亿对它来说未必真伤筋动骨,但如果从高管个人身上把钱罚出来,那就真的疼了。因为那是税后个人收入,要自己掏出来。

从这个角度看,这一条大概率也是针对拼多多暴力抗法行为加上去的。

转单平台的双面性:问题与现实并存

老街角一家朴素蛋糕小店里,白发阿公阿婆在手工制作点心,另一侧是年轻运营人员帮他们拍照、上架、设计包装,传统手艺与互联网工具并置,羊皮纸,钢笔彩色手绘的统一风格。

再说一个很多人可能想不到的角度:转单平台其实是有双面性的。

很多人会说,转单平台就是坏人。但现实往往没这么简单。所谓转单平台,就是消费者在平台上下单一家蛋糕店,实际接单和制作的却是另外一个没有资质的小作坊,中间那个负责撮合和流转的,就叫转单平台。

听起来当然问题很大。消费者以为是连锁店出品,实际上是小作坊借壳卖货,出了食品安全问题,连人都找不着。但它为什么会存在?因为街边那些辛苦坚持了几十年的阿公阿婆小店,他们懂互联网吗?懂平台投放吗?懂品牌包装吗?懂标准化和连锁推广吗?大多都不懂。很多街角老店,味道很好,做了几十年,但可能连牌照都没有,更别说平台运营这些事。

在美团、抖音这样的平台上,要把东西卖好,你就一定得和其他人合作。过去这些老店只能守着一个街角档口,等老主顾上门。后来有了外卖平台,有了转单方式,它们做的蛋糕或者其他食品,就可以通过互联网卖到全城。对很多小店主来说,这是他们这辈子第一次触达这么多消费者。

所以,转单这件事并不是所有单子都特别差,有些其实也还可以。当然,这不是给转单平台洗白。资质审查还是应该做,食品安全也必须管。

但为什么会有转单平台这种现象?不只是因为街角档口的阿公阿婆不懂互联网,更深层的原因还是执法不够严格。

如果所有店家都必须有资质,而且监管标准对所有人一视同仁,那么资质成本就被拉平了,大家都会去做。但现实往往是运动式执法:有些人有资质,有些人没有。没有资质的人,反而在竞争里占了巨大优势。原因很简单,他们省掉了申请资质、打扫卫生、规范管理等各种成本,所以可以把东西卖得更便宜,获得更多流量和交易机会。反而那些老老实实申请了资质的人,没有得到奖励,倒在价格战里吃了亏。

这才是现在真正的问题。转单平台就是在这个缝隙里,把这个生意盘活了,让消费者吃到更便宜的外卖。

所以未来,转单平台也未必应该被彻底干掉。资质当然首先要齐全,但转单平台以后更应该做什么?应该去帮助街角档口的阿公阿婆,把那些做了几十年的传统小吃规范化,帮他们做推广、做宣传,帮他们进行连锁化转换。这个才是真正互联网应该发挥力量的地方:把他们的故事讲好,把他们的产品和食物标准化,再卖到更多地方,让更多人吃到。

为什么没有一家平台公开提出复议或申诉

七家平台代表坐在长桌前沉默阅读处罚决定书,桌上没有申诉文件,只有“接受处罚、整改”的声明草稿,窗外是沉静的城市夜色,气氛克制而压抑,羊皮纸,钢笔彩色手绘的统一风格。

最后说一件这次处罚里最容易被忽视、也最值得玩味的事:7家平台合计被罚35.97亿元,到现在为止,没有一家公开提出行政复议或者申诉。

你可以说这说明平台认错了,法律上站不住脚了。但更可能的原因是,这次处罚本来就不只是为了罚钱,而是要“罚给你看”,看你服不服。现在大家都表示接受处罚、坚决整改,目的其实就达到了。要的就是你服。

至于食品安全,这一轮高压运动过去以后,未来会怎样,还得再看。但在当下的监管环境下,公开申请复议意味着什么?意味着把“我要告政府”这件事摆上台面。一旦走到那一步,输赢都不是最重要的了,最重要的是你和监管的关系会彻底改变。等于你在打监管的脸。

前面拼多多已经打了监管的脸,这次就是要狠狠打回来。你如果还敢去复议,就说明你还不服,那后续的处罚可能还在等着你。

这7家公司,哪一家不是在中国监管环境下长大的?美团、阿里、京东、拼多多,谁没和监管部门打过交道?他们太清楚了,罚款只是账面损失,关系才是长期要维护的东西。一旦监管认定你不服,这35.97亿元就是给你看一看,不服的结果是什么。

所以我们看到的统一口径都是“接受处罚,坚决整改”。这不只是法律意义上的认错,更是一种政治表态。某种意义上说,这种表态本身,就是这次处罚真正想达到的目的。

结语:这是一场系统性的清剿

所以回到开头那句话,“青天大老爷”“老天开眼了”。这不只是一场针对幽灵外卖的执法行动,更是一场系统性的清剿。针对的是整个平台经济里,长期把食品安全当成增长成本中可压缩项的那套旧逻辑。当然,另一个核心就是:你得服

这次拼多多被单独打成典型,不仅仅因为9463家问题店铺,更因为它之前居然敢反抗。在这个游戏里,罚款从来都不是最大的代价。真正得不偿失的,是让监管把你当成对抗对象。


背景图片

Prompt:in the style of cyberpunk 2077, neon-soaked futuristic dystopia, dense urban glow, electric magenta and cyan accents, cinematic high-contrast sci-fi atmosphere, a decadent rooftop bar above a cyberpunk city, overgrown plants on tables, glowing drinks, fruit plates, messy bottles, cables and small electronic junk scattered around, fog-drenched skyline beyond the balcony, flickering neon signage, layered urban depth, gritty sci-fi disorder, atmospheric perspective, cinematic composition, reflective surfaces, rich texture detail –no pristine luxury, empty scene, daylight, pastoral scenery –ar 16:9 –stylize 500 –chaos 25 –sref 2609119203 –v 7.0 –p qaczhqj

🔲 ☆

翻过五老山,遇见斐然湖的春天

谁懂啊!拎着公文包爬重庆五老山,竟被斐然湖的春日治愈到了。

家人们,谁还没被最近的重庆天气预报欺骗过?连着一周,天气APP上的“中雨转大雨”“雷阵雨”循环播报,却抵不过山城春日的随性——每日暖阳倾城,风携暖意,硬生生把“雨讯”熬成了“晴报”。周四、周五的午后,日光温柔得能掐出水来,心底早已盘算好,要借周末的时光,到山野湖光去走走。

可周五傍晚,天却骤然变脸。暴雨如注,倾泻而下,竟有几分“阴阳割昏晓”的壮阔,瞬间浇灭了我对周末的期待。我瘫在沙发上emo,暗自懊恼:难得的休息日,难道又要困在房间?

周六清晨,阳光刺破窗帘缝隙,暖融融地洒在枕边。猛地惊醒扒开窗帘,只见晴空如洗,云卷云舒,雨后的空气里裹着泥土与草木的清芬,澄澈又新鲜,连一丝雨意的痕迹都未曾留下。那份突如其来的惊喜,让我瞬间满血复活。匆匆洗漱更衣,本想先处理些许琐事再出发徒步,慌乱间竟顺手拎起了门口的公文包——如今回想,这便是这场旅途最蠢萌的伏笔,谁能想到,我们会拎着公文包,走完了整段五老山的徒步路。

正午简餐过后,生怕耽搁下山时辰,我们果断打车前往,40元车程,便抵达了五老山徒步环线的起点。刚下车,便被一团暖意簇拥:路边停满了前来徒步的车辆,更有四五只毛茸茸的小狗,摇着蓬松的尾巴飞奔而来,蹭着我们的裤脚,瞬间驱散了路途的疲惫,连公文包的沉重,都变得轻盈了几分。

穿过一片农家院落,墙角缀着不知名的细碎繁花,烟火气与自然意趣相融。前行片刻,五老山的上山指示牌便映入眼帘,一片墓地悄然出现,此地原来叫“坟上湾”,足见这一带山清水秀,堪称一方风水宝地。”

佛祖保‘右’

五老山的徒步路线为环线,分左右两条山道。我们循着“佛祖保‘右’”的玄学心意,选择了右侧山。后来下山途经左侧,才知那一侧台阶陡峭湍急,攀爬起来费尽全力,新手极易望而却步,不得不庆幸,我们选对了这条路。

刚踏入山道,便被漫山绿意拥入怀中。作为北碚近郊的小众秘境,五老山未经过度开发,保留着最原始的自然本真,植被丰茂,层峦叠翠,堪称主城的“天然氧吧”。苍劲的松柏挺拔入云,枝叶交错,遮天蔽日;中层的阔叶树舒展枝叶,翠绿鲜亮,随风轻摇;低矮的杜鹃缀于路旁,静待绽放,高低错落间,皆是山野的生机与诗意。

脚下的石板路被岁月磨得温润光滑,石板边缘的空地上,铺着一层厚厚的松针,踩上去绵软如绒,似踏云端,毫无硌脚之感,偶尔有蝴蝶翩跹而过停驻于石板。

五老山本是一座石质山峦,山间阶梯多就地取材,更有甚者,是匠人直接在巨石上凿刻而成,浑然天成,默默镌刻着过往行人的足迹。道旁的土地庙早已斑驳,只剩空荡荡的佛龛,不见神像踪影,竹林间的枯叶三三两两,覆于青苔之上。格外偏爱这份清净,无喧嚣扰耳,唯有风声、鸟鸣、虫吟交织,与古人遥遥相望。

攀爬半程,我们便已气喘吁吁,扶着膝盖驻足歇息,暗自感慨近期疏于锻炼,体力大不如前。正唏嘘间,前方传来沉稳的脚步声,抬眼望去,一位老爷爷背着一捆柴火,如履平地,从我们身边从容走过。

再往上行,翻过一座山梁,视野骤然开阔。一片小巧的露营基地静卧于山间,有人围坐闲谈,有人静坐观景,氛围感拉满。抬眼远眺,便能望见斐然湖的轮廓,虽暂未遇见波光粼粼的湖面,岸边的青草依依,随风轻摆,已足够动人,让人满心期待着它的全貌。

循着山道前行,很快便抵达乡村公路。路边的几家小卖部,摆放着矿泉水、零食等简易补给,恰好解了我们的燃眉之急,无需拎着沉甸甸的公文包负重前行。公路两旁,两排水杉挺拔矗立,细碎的枝叶在风中摇曳,阳光透过枝叶的缝隙,风过林间,携来草木的清芬,瞬间驱散了攀爬的燥热,沁人心脾。

前行间,斐然湖的全貌渐渐铺展在眼前。一山之隔就到了沙坪坝中梁镇,被群山环抱,静谧而温婉。“斐然”二字,自带笔墨书香,与眼前的湖光山色相得益彰。这片湖畔曾有一处网红打卡点——大片的粉黛乱子草,每到秋日,粉穗如云,蓬松柔软,风一吹便泛起层层粉浪,引得无数游人慕名而来。湖边的草坪如一块柔软的绿毯,铺展至湖畔。三三两两的垂钓者静坐岸边,几组好友围坐草坪,孩童在草地上肆意奔跑、嬉闹。

其实无需徒步,亦可解锁斐然湖的美好。驱车可直达湖畔,无需费半点力气,吹一吹春日的清风,看青山含翠,听笑语盈耳,哪怕什么都不做,也能被这份温柔治愈。在湖畔休整片刻,我们便踏上了五老山的返程之路,从山的另一侧下山。这边的山道行人稀少,杂草长得愈发繁茂,偶尔没过脚踝,前行时需格外留意,生怕惊扰了草丛中的小生灵。

下山的山道比上山陡峭许多,容不得半分走神,唯有全神贯注地盯着脚下的台阶,一步一步,从容前行,生怕脚下一滑,辜负了这一路的美好。约莫二十余分钟后,一片由高大林木组成的平地映入眼帘,枝叶繁茂,遮天蔽日,看见这片林地,便知终点不远了。

我们加快脚步穿过林地,期间掏出手机打车前往最近的向家岗地铁站,没想到司机秒接,那份突如其来的便捷,让疲惫瞬间消散,幸福感油然而生。

打车到起始点“胡长山庄”即可,附路线图。

道边林下很多落地梅,因为种子具有非深度生理休眠特性,据说并不常见。内用可以治疗止咳、调经,外用可以治跌打损伤。

在城市里的小花盆里精心饲养的多肉,在这一人长的石头上肆意生长。

这是刺老芽,学名楤木,还是上次一起徒步的妹妹告诉我说可以吃,据说苦味明显,但也许这就是传说中的山野之味。

斐然湖边民宿院子前的花,美丽月见草,有个俗称叫夜来香,花语是默默的爱。

🔲 ☆

云顶寨:渝西川东鬼市录

本来是计划去徒步,临时身体不太舒服,于是只能尽量把车往目的地的山坳里开。没料到里面竟然藏着一座被云雾养了六百年的古寨。内江隆昌云顶寨,海拔530米,寨墙蜿蜒1600多米,守着听起来像《鬼吹灯》、《盗墓笔记》里故事的发生地一样的地方——云顶鬼市。

这里倒没有小说里刻意营造的诡谲,而是烟火与旧影缠在一起的真实。走在寨里,风穿过破落的雕花窗棂,会带出细碎的声响,像有人在耳边轻声说话。看完介绍,感觉云顶寨的魂,一半在郭氏家族六百年的兴衰里,一半在寅时开市、鸡鸣即散的鬼市里,还有还有一半,落在那座没建完的云顶寺里。

云顶寨始建于明洪武四年,是郭氏入蜀后扎下的根。同我的先祖一样,都是湖广填四川来的此地。

汾阳王郭子仪后裔郭孟四带着郭家人从湖北麻城迁来,在云顶山开荒筑寨,一代代攒下家业,最盛时良田万顷、佃户三千,寨墙修得固若金汤,墙内是郭氏的堡垒与宅院,墙外直接铺开一整条商街。川南多匪,山路难行,郭氏族人夜里宴饮、日用所需不便下山采买,便在寨下辟出小小场镇,药铺、绸缎庄、酒馆、茶馆、钱庄、当铺,甚至烟馆一应俱全,几乎是自给自足的商业闭环,硬生生把云顶顶成了川南一地的物资集散中心。

只因约定夜半交易,天亮散去。这便是鬼市的由来。

有一处天成生糟坊,据说明末清初就已开窖酿酒,老窖池一路沿用至今,在白酒行里,几乎被当成泸州老窖一脉的 “活化石” 看待。有一种说法是,云顶才是泸州老窖真正的根脉所在。其实当年麻城、孝感一带的百姓成群结队入川谋生,其中不少人做起了酒生意,慢慢就流传开一句老话:“四川酒老板,麻城占一半。”而今孝感只因米酒而闻名,而泸州老窖更享誉世界,怎不是青出于蓝而胜于蓝呢?

云顶寨早已不是当年的富贵模样,但修缮后倒也着实让人羡慕当年的富贵人家。郭孟四的后人里,郭廉、郭元柱父子叔侄相继登科,一榜一进士,稳稳踏入仕途。读书人做官,家族声望自然水涨船高,田产越扩越大,商铺越开越多,佃户也跟着成倍增长。郭氏鼎盛之时,一年光租谷就能收到九万七千多石,折算下来差不多两千五百吨粮食,族内人口也超过一千五百人,当年风头之盛,甚至能和安徽桐城方家并肩,被称作 “中国两大家族”。

鬼市在云顶场,丁字街两条石板路交叉,两旁是矮矮的木楼青瓦。逢农历三、六、九,凌晨三四点,山间便亮起点点星火,火把、马灯顺着山路蜿蜒而来,挑担的、背篓的、提篮的,悄无声息聚在街面。没有闹市的吆喝,买卖全靠袖里手语、耳边轻语,价谈妥了就递钱拿货。山珍、野菜、土布、草药、铁器,都是乡间最朴素的东西,却因这夜半的规矩,添了几分神秘。当地人又叫它 “强盗场”,不是真抢。

当地史志办也记载过不少鬼市的旧事。有人见过凌晨提篮卖菌的老人,菌子鲜得滴水,价钱便宜,天亮再找,连摊位痕迹都没;有人买过妇人手里的土布,手感温润,付银时听不见声响,天明只余下几缕丝线。这些事传了一代又一代,没人较真真假,只当是古寨里的故人,舍不得走,回来买一口生前的滋味、扯一匹旧时喜爱的布料做衣衫。

寨西北的山坳里,原本是川主寺,供奉川主,护一方风调雨顺。古寺年久倾颓,释宗能师父云游至此,发愿重建,更名云顶寺。起初信众捐资,工匠云集,立柱、砌墙、架梁,殿宇慢慢起了轮廓,其中的佛像虽未上色完工,看起来审美极佳。可工程过半,资金难以为继,工匠们的薪水一拖再拖。他们没有愤而离去,而是在未完工的大殿前,拉起了讨薪的横幅,红布白字,成了古寺与现代文明链接的印记。

我们也没看到释宗能师父,只见到了祖平师父,他匆匆忙忙打电话处理外面欠薪的事情,却也拿不出一分一毫用作薪资。建寺的初心是善,可欠薪的事落了实,半截寺庙便成了善念与亏欠拧成的结。

渝西川东的鬼市,不是凶煞,只是人心留下的痕迹,因为被山风记住,被石板记住,才被一代代人口耳相传。天快亮了,鬼市的人渐渐散了,火把熄灭,马灯收起,石板路重归安静。云顶寺的喧哗也慢慢淡去,只剩山间云雾,把所有故事裹在里面。

云顶寺距荣昌约 42 公里,距重庆市区约 138 公里,距成都市区约 210 公里;可自驾走高速直达,也可乘高铁至隆昌北站,再转城乡巴士或打车前往。

🔲 ☆

2026春:是否该退出战争笼罩下的市场?

1月

  • 闲来无事翻看了一下全球各地区的股票指数,惊奇的发现,不止是我们天天在财经新闻里面看到的美股纳斯达克和道琼斯在高位,德国DAX指数、法国CAC指数、英国富时100指数、意大利MIB指数、新加坡富时指数、日经225指数、台湾50指数、加拿大TSX指数等等,【全部都在历史新高的位置!!】全部哦,无一例外。不是日线,是周线级别。全球的钱都在泛滥,狂欢终有散场之时,音乐停止的时候看看谁在裸泳。
  • 可以免费用到2031年的微软Copilot,果然微软还是财大气粗,谢谢哇!
  • 看完塔勒布的《随机漫步的傻瓜》,接着看《宗教与科学》(这本书也在我这里躺了几年了,一直没有看完…)。
  • 李翔说过这么一句话:“找到一条最小阻力的路径,因为它才是可持续的。长坡厚雪,大概也是这个意思。”
  • 做了一个红包封面,喜欢吗?哈哈哈,现在每年做红包封面都是用AI作图了,比Photoshop要高效很多很多。

2月

  • 开工了,时间还比较多。
  • 春节到来之前,市场遭遇了前所未有的滑铁卢。
  • 在存储这个事情上赚了一点零花钱。400的价格卖掉了兆易,目前还有一点海力士了。
  • MiniMax真的是卖飞了,哎,好不容易打新中了,结果早早就卖了,错过了大肉签子啊!!!
  • 美国和伊朗马上要打起来了吗?
  • 在吉隆坡的苹果店里测了下网络,苹果公司自己竟然就是运营商,这是全球专线啊!
    • 有没有网络专家科普一下?地址:2a01:b747:43b:314:2462:fa4a:4b40:aca2
  • 今天港股开盘,持仓表现不错啊,可惜现在比特币和黄金这类资产。
  • 回国了,这个春节过得太难忘了。
  • 刚才通过Docker升级了一下密码管理器的版本,因为国内已经屏蔽了docker hub的官方地址,所以更新镜像还成了个麻烦事。我有的镜像是用的第三方未被屏蔽的源,因为密码管理器安装时还是用的官方源,所以只能让整个NAS处于代理网络之下。小火箭可以让给局域网内设备共享代理,但是家庭网络中wifi设备和NAS等有线设备又不在一个网段😂, 折腾很久终于搞定。
  • 减持了跟AI相关的存储股票,不贪了,因为算下来2026年的盈利已经90%多了,知足了。

3月

  • 以色列预防性打击了伊朗,然后3月1日哈梅内伊就被斩首了,冲突应该是走向快速冷却吧,周一股市应该可以平稳渡过。
  • 看完了万斯的《乡下人的悲歌》,书中有个细节说他在海军陆战队当兵的时候,也一样被要求整理床褥等内务,我想到黑子说只有中国军队要求叠豆腐块,这不就不攻自破了吗。中国的教育中一直重视家庭与责任,万斯反思自己青少年的经历时,觉得他的家庭糟糕透了,母亲吸毒,继父换了四五个,跟着外婆长大,成年后先去当兵,专业后在州立大学学习,再去的耶鲁法学院。不说后来他从政当上副总统,就大学毕业后找到白领工作有稳定的收入,逐渐摆脱原生家庭,这已经是他的美国梦了。因为作为07年上大学的他,已经是整个家族中,第一个上大学的人了。所以,中国传统教育中的仁义礼智信真的浸润了国人的民族性格,多了一点坚韧的底色,可能外国人遇到了财务困难后,不是向普通华人一样选择自强不息,而是选择了毒品进行逃避,然后让整个家族滑向了深渊。而且这种财务困难通常是由不加节制的提前消费导致的。
  • 伊朗的战事及中东混乱的局势,并没有像大家预期的那样,黄金会再次暴涨,WHY?
  • 周末去成都参加了校友活动,和内地校友会不一样,好像港校领导确实要平易近人一些。
  • 电影《疯狂动物城2》打卡。
  • 世界越来越混乱,是不是进入了《三体》中的乱纪元,一方面人工智能发展突飞猛进,一方面战争越来越没有底线。10年后我们会如何评价这几年?文明世界的转折点?
  • 《呼啸山庄》被再次改编为电影,读高中的时候知道这是一本名著,但是从来没有读过,电影出来了去看才知道这是一个非常复杂的故事,影视版过多的描写男欢女爱去了😂
  • 伊朗和以色列打仗近一个月,受伤的是我的钱包。港股账户还比较谨慎,基本没有回撤,但是其他的就亏惨啦…没办法,只能等转机出现。
  • 无论是主导消费的一方,还是主导生产的一方,又或者是生产和消费较为平衡的一方,都需要更长期的保持低利率。——《肖磊:这个时代,不买黄金还能买啥?》
🔲 ☆

最近惦念 20260410

生命的课题,是对抗熵减

肝郁化火兼脾肾阳虚,也快凑成一种MBTI了

没有时间经历和思考的人类一败涂地

世界并不需要第二个“我”

人可以是很多人

活下去,一直活下去,如果有机会,还可卷土重来

“我还活着吗?我现在还活着吗?我今天还活着吗?”

你本来是谁?你还记得吗?

问自己:你是否真实地度过了这段时间?

无法被吸收、被消化和被摧毁的,那个“我”的存在,会是哪种质感?

或许AI能够让我们找回用手的感觉

悟空感应

“用虚、用空、用无、用元神,先天真东西才会来,用实、用有、用识神,那都是后天的东西,都不是老子说的先天一炁之道。”

“把自己已知的都抛弃,把自己像电脑格式化一样清零,脑子里空空如也,就开始接近道了。可道,谁可道,你们的知识完全不了解,当然说不出来,写不出来,但是,当你们大脑空了,什么也不想了,你们里面有一个从来不说话的内心的小孩,那个在无的位子上的真人,那个藏在心里面的觉知,有个接近的词叫心领神会,她像一面无形的镜子,把一切都尽收眼底,她才是可道的主角,而不是用嘴可以说的,用语言可以写的。”

赫尔佐格,狂喜的真实,其重要性仍在增加,而且,在变得越来越重要

Having the discipline to stick to your nucleus noun and go mega vertical with it. Explore all its quirks and edge cases.

如何守虚,是个值得深思的话题

敬畏常在,不要利用自然的准许


CHANGELOG

  • 20260417 Arlmy 创建
  • 20260417 Arlmy 发布
🔲 ☆

Toots 415 2026 Apr.12 - Apr.18

Apr.12

  • 过去的捕捞场争夺,变成现在的能源争夺
  • 《生而为熊》本来来去自由的灵,现在沦为观察对象,或是被圈养,整本书都显出对熊的无知。只喜欢一些显出灵性的段落。一切无知都源于与对象的疏离和无视,包括人类自己。
  • Bucking Fastard 快要上电影节了!之前访谈里赫尔佐格还说到过,奥兰多·布鲁姆对他一条就过的习惯非常不满意哈哈
  • Werner Herzog solved the Hormuz crisis 45 years ago,啊哈哈哈哈哈哈哈哈哈哈
  • “这些年来,你的性格变得温和了吗? ”“没,我一直都是这样。问题出在媒体身上,可能也包括你们的报纸,他们常常把我描绘成一个精神错乱、执迷不悟、悲观绝望的预言家,但现在人们突然明白,我的所有电影里都充满了温暖的幽默。每一部都是。在互联网上,这一点也开始逐渐显现:我有一种独特的巴伐利亚式幽默,但同时也是一种非常温暖的幽默。”“你现在觉得媒体更理解你了吗?”“我不在乎媒体。那不是我的问题。”(赫尔佐格)
  • "cinema is mijn reis, schrijven is mijn thuis.” 电影是我的旅程,写作是我的使命。
  • “我在青少年时期很早就知道自己的使命。”
    • (您具体指的是什么?)“它向我显现。直到今天,我依然如此:我知道我的使命。它已经显现。”
    • (就您而言,这个使命就是讲故事吗?)“不,它远不止于此。但讲故事当然是其根本要素。”
    • (您能否用不同的方式来定义自己的使命?)“不能。我知道我文笔好,也知道我的电影很棒。”

清代社会的贱民制度

  • 短评:因江南奴变和雍正废除贱籍找来读的,原来雍正只是废除了一小部分贱籍……读到好多没听过的等级和身份,而更重要的是清代律例划定的权力(经常更改),虽有流动的弹性,但太多灰色的空间让人难以彻底翻身。
  • 「什么人属“贱”?《大清会典》载,“奴仆及倡优隶卒为贱”;“凡衙门应役之人……其皂隶、马快、步快、小马、禁卒、门子、弓兵、仵作、粮差及巡捕营番役,皆为贱役。长随亦与奴仆同”。据此,清代的贱民首先是指奴婢和娼优。长随跟奴仆同等;开豁以前的乐户隶属“乐籍”,与娼优是一样的。为官府服役的皂隶等所干的各种差事,被认为是侍候官老爷的“贱役”;人以役贱,所以凡应承这种差役的人都被划进贱民的圈子里。这些是见诸明文规定的贱民等级的成员。」
  • “清代的贱民还有隶卒,即在衙门中服役的皂隶、马快、步快、禁卒、门子、弓兵、仵作、粮差及巡捕营番役等。他们的工作是捕盗看守、站班喝道、验尸验奸等公务。他们在服役以前大都是凡人,属于良民,只要从事这些职业,立即沦为贱民,永世难复。”
  • “这些律文的立意,并不是要把奴婢当作主人家族中的一个成员来看待,从法律观点看,统治者甚至不把奴婢当作一个具有独立人格的人,而是当作主人占有的某种动物来对待;但是这种动物又和牛马不同,具有人的社会特征,从而不得不把他们放在一定的体制中给以一个特定的位置。这样,奴婢才被编制在主人的封建宗法家长制体系之中了。”
  • “清军入关以前,就已在东北广大土地上实行庄园制进行农业生产。入关以后,为了维持八旗生计,统治者以强行圈占和投充的暴力手段,在华北平原上为八旗掠夺了大量土地田庐,建立了大批旗地田庄。由内务府经营的为皇庄、官庄;其他庄则属王公大臣或一般八旗户下,称王庄、旗庄。这些田庄也采用庄园制,使用壮丁进行农业劳动,选派庄头管理庄务。”
  • “八旗是一种军事组织,也是行政组织,所有旗众均被编制在这一系统内,没有例外。八旗都统负教养之责。八旗中,镶黄、正黄、正白为上三旗,由皇帝直接统辖;正红、镶白、镶红、正蓝、镶蓝为下五旗,则为王公僚属。每旗各设都统一,副都统二,下属参领五及副参领五,层层领导,以下各置佐领若干。”
  • 「清初,满洲八旗崇武习战,除围猎外,不事生产。“将佐居家皆弹筝击筑,衣文绣策肥,日从宾客子弟饮。”畜牧、手工以及农业生产全靠奴婢进行;至于生活服役就更不消说了。清代统治者视此为当然,从不讳言。顺治帝说:“向来血战所得人口,以供种地牧马诸役。”康熙帝说:“满洲藉家仆资生”,又说,“八旗官兵皆依屯庄收获用以资生”。乾隆帝也同意,“边地官员受田耕种,全赖奴仆力作”。总之,八旗贵族役使这些奴婢,“大事小事各得其力”。他们不能想象,如果没有这些奴仆,“驱使何人?养生何赖”?这些说法反映了一个事实:当时满族奴隶制的残余在生产关系中还占有相当地位。」
  • 「顾炎武曾描述了清初关中地区上市卖人的情况,“自鄠以西至于岐下,则岁甚登,谷甚多,而民且相率卖其妻子。至征粮之日,则村民毕出,谓之人市”,这是农民无银交税而出卖骨肉者,以致形成人口市场的惨状。甚至帝辇之下的京师也有人市,清初著名的史学家谈迁《北游录》曾记:“顺承门内大街骡马市,羊市,又有人市。旗下妇女欲售者丛焉,牙人或引至其家递阅。”」
  • 「豁贱为良政策的意义毕竟有限。那种认为由于这些政策的产生,“关内历史遗留下来的农奴制残余也削除殆尽”的看法需要商榷。因为诸如乐户、堕民、丐户、九姓渔户、疍户等等是否“农奴制残余”,尚待论证;说这种“残余”因豁贱为良政策的颁行而“削除殆尽”也根据不足。或称这项政策的意义是“奴隶阶级之铲除”,这一提法也有问题。因为被豁为良的贱民并非“奴隶阶级”,清代和“奴隶阶级”较为接近的乃是奴婢,而豁贱为良政策恰又没有涉及他们。」
  • 「更应看到,豁贱为良政策乃是统治者给予的一项恩赐,它仅限于将一部分贱民升等,并没有消灭贱民等级。豁贱为良的这部分贱民,例如堕民、丐户、九姓渔户、疍户等,在法典上并无具有贱民身份的根据,本来就不该属于贱民等级。未经豁籍的反被正式定在贱民等级之内;被豁为良的,因经济力量的薄弱而容易再度沦入贱民等级之中,即成为奴婢。」
  • 「印度古代的种姓制度中有着更为典型的贱民。印度的贱民被剥夺了一切政治权利。他们被禁止使用村里的公共用水,不准进入寺庙,不能走过高种姓人居住的街道,不得以自己的身影遮住高种姓人,甚至被高种姓人看见,也成为贱民的“罪行”。至于与高种姓人通婚的事,当然更谈不上了。总之,高种姓人和他们发生任何接触都被认为是受了侮辱,所以贱民又称“不可接触者”。」

Apr.13

  • “把自己已知的都抛弃,把自己像电脑格式化一样清零,脑子里空空如也,就开始接近道了。可道,谁可道,你们的知识完全不了解,当然说不出来,写不出来,但是,当你们大脑空了,什么也不想了,你们里面有一个从来不说话的内心的小孩,那个在无的位子上的真人,那个藏在心里面的觉知,有个接近的词叫心领神会,她像一面无形的镜子,把一切都尽收眼底,她才是可道的主角,而不是用嘴可以说的,用语言可以写的。”
  • “守中的主角是神,神是火,守的是炁,炁是水,守中既是中脉、黄庭中宫,更是守在阴阳平衡点上。生气的起始,只有守在这个上面,生气才会源源不断生发出来。守中要似守非守,元神守,识神配合协作。完全是元神的空无,完全无为就不存在守的问题,有守就是有为,守过了就是识神专权,生气就立刻停止不出来了。”
  • “用虚、用空、用无、用元神,先天真东西才会来,用实、用有、用识神,那都是后天的东西,都不是老子说的先天一炁之道。”
  • 「心正意诚,即是“善”字总领。正,止一也。心神止于一,诚恳至极,虚灵不昧就是善。居善地,这个地在人体指腹部下丹田,意守丹田是一个表面的居善地,守住心地之善,才是更关键的。这个地很小很小,只有一个念头刚落下和另一个念头还未升起之间那个缝隙,专诚一念地守住这个空,让两个念头之间的缝隙尽量加大,逐渐变成心静如水。」

Choosing Death

  • “90年代中期斯堪的纳维亚黑金属的爆发或许曾占据新闻头条,但若没有死亡金属和碾核场景的铺垫,黑金属很可能根本不会存在。到90年代末,这两种风格的部分元素实际上已融合成统一的极端金属流派。诸如Emperor、Vader或Akercocke等乐队既承袭黑金属衣钵,亦汲取死亡金属精髓。但这远非死亡金属近年来的全部贡献。该流派不仅催生了如 Nile 这般媲美经典先驱的乐队,更渗透到地下与主流摇滚及重金属的几乎所有风格中。你能找到哥特式、旋律式、氛围式乃至硬核风格的死亡金属;在硬核与噪音核场景的每个角落都遍布碾核的踪迹;甚至能在超级巨星Slipknot的面具背后发现新派死亡金属的基因。”
  • “与此同时,金属乐——特别是激流金属——在朋克圈内不再被视为贬义词。早期的美国激流金属乐队如 Slayer 和 Metallica,将高速攻击性与技术精湛的演奏方式相结合;而欧洲的激流金属乐队如 Kreator、Destruction 和 Celtic Frost 则调低乐器音高,简化声音,呈现出一种更简单——也更粗糙——的风格,这让大多数朋克乐迷更容易产生共鸣。”
  • “那时的Napalm听众非常混杂,dorrian回忆道,有独立音乐爱好者、学校老师,还有一两个金属党,但不多,也有很多朋克——就是非常多元化。我觉得那是英国音乐的一个特殊时期,因为那时候的英伦硬核,或者随便你怎么称呼它,虽然是一种地下音乐,但非常反体制,确实把许多来自不同背景的人聚集在了一起。那真是个相当多元化的时代。”
  • “我而言,演奏黑金属能展现更多灵魂与情感,Darkthrone鼓手兼词作者Fenriz解释道,我们在90/91年左右解雇了贝斯手,开始直击要害。回想起来,这似乎是一种潜意识策略——我知道视角决定一切。但我确信当时我们只是追随内心的愤怒。死亡金属变得更注重演奏技巧而非精神内核。那是个人们居然喜欢Death乐队《Spiritual Healing》专辑的年代。还需要我多说吗?”
  • “黑金属运动在某个阶段对部分死亡金属场景确实怀有敌意,Emperor的Samoth回忆道,有些人反对死亡金属在多方面变得如此正常化和主流化,随着所谓死亡金属潮流的兴起,我们也看到许多缺乏精神内核与激流的原型乐队。黑金属在某种程度上成为了反抗力量——它关乎真正的死亡与黑暗,忠于地下邪典精神。”

Apr.14

  • 保时代不散,保人念不散,保心意不散,保善不散,保道不散,对抗熵增的本性,越老越感觉到是难以避开命运
  • 好像真的没必要执着于几点睡,睡几个小时,只是白天要少睡些,夜晚要多睡些而已
  • “没有魔鬼,也没有上帝,我将一个人面对着上面这一无所有的天空。”
  • “感召空中的太和一炁,内外交感而为人之灵。这个人体感觉的国王,她最初的来到和诞生,就是人的根。修道的起步就是归根,归静。静是天地之灵。起步的方法就是明玄关一窍,进入人天一体的通道,会见自己的灵。她会带你体验道妙,只有她才能观到归根复命的内景。”
  • 悟空感应
  • “身心杳明,心中一念不生,修得内心一丝不挂,进入虚无但其中包含一切的境界,将这无中的多达成一个梱,做整体地观,类似于你闭上眼睛,不落在屋子里的某个东西上,但整个屋子的东西你全都知道。…归根曰静,是有中复无,实内从虚。虽然结了丹,但丹是个虚体,是虚中之有,实在得过了就不是她了,所以还要以空谷之虚神守候。…老子说的公、天、道、久,一切都仿佛消失了,只剩那一片深度的静定,清虚湛寂。(众妙之门)”
  • “天地尚不能久,而况于人乎?天地不守常也不能持久,何况是人。天地之常,要刮风要下雨,自然而来,自然而去,天地自己也不知道,所以天地才会长久。” “若此,乐其自然之道,故得之。同其失,同天地虚灵不昧,风雨雷电,无意而生,无意而散,丝毫不染着,如此容静,包罗乾坤,听其自然,合天地,乐我自然希言之道,故得之。如此合天,信之犹为不足,焉有不信之理?太上教人,不过体天惜己而修,忘德忘失,无容心于物。
  • “Having the discipline to stick to your nucleus noun and go mega vertical with it. Explore all its quirks and edge cases.”
  • “威斯康星州一所大学的学者正在使用模拟牛视觉的虚拟现实眼镜,以帮助创建旨在减少动物压力的肉类加工厂。”
  • “人能体道,道凝于人,为人之域中。天、地、王、道叫四大;精气神灵叫四大。四大皆空,而道处于中,叫王处一焉。什么是人法、天法、地法?道出于自然,人能自然,如地之静,所以常存,叫人法地;地得天之雨露下降,生化之机,地固因而常存,叫地法天;天禀清虚之气,凝虚于上,无为合道,叫天法道;道本于虚无,常含湛寂之体,听无为之生化,叫道法自然;自然之中,有物混成,感先天地而生,凝寂寥而化,随自然之机,混成之道,叫自然。……道只是来自静中生的自然一微小之动。这一静之后的微动发生在你身上,你就进入了宇宙,宇宙流向了你……”
  • 庾信庾子山这么有名啊
  • 古诗库:庾信全集

Apr.15

  • Feral House
  • 上高中时还不知道英国朋克那么牛逼呢,但听到过一张国内老朋克的合辑,至今难忘,那种粗糙和直接再也没听到过,直到听到了英国老朋克
  • 看相反的你和我,全程姨母笑
  • “北方人。有没有想过,北方人其实是这个星球上的绝对少数群体?加拿大人和波兰人,立陶宛人和斯堪的纳维亚人,一部分美国人和德国人,俄罗斯人和苏格兰人,拉普兰人和因纽特人,埃文克人和雅库特人——名单不算长。我不知道是否达到了五亿人,而这甚至还不到全球总人口的十分之一。绝大多数人都生活在温暖的气候中,在阳光下度过一生。”
  • “而当地人则恰恰相反。他们凭借力量、优雅和耐力,自然而从容地行动,一切都按照环境与传统所规定的节奏进行着,不慌不忙。反正人这一生无法包揽一切成就,总得给别人留点什么吧?” 其实之前看刚果萨普也有类似的感觉,脏乱是现代的标准,但确实很很从容,于是很慢地接受,很慢地学习

太阳的阴影

  • “时间的出现是因为人类的活动,当你停止行动或者根本不采取任何行动时,它便会消失。时间是一种在人的影响下不断复苏的物质,如果人不给予它能量,它就会休眠乃至消散。时间是消极的存在,最关键的是,它取决于人。这和欧洲的思维方式完全相反。”
  • “在小型群体中生存活动,使他们更容易逃离危险的地方,比如干旱或流行病肆虐的地区,从而得以幸存。这些小型族群采取了过去轻骑兵在战场上所使用的战术:机动性、避免正面冲突、躲避和智胜。这使得非洲人成为了永远“在路上”的人。即使过着定居的生活,住在村庄里,他们仍然“在路上”。因为整个村庄也在行走:水源枯竭了,土壤不再肥沃了,或者暴发流行病了——他们就得上路,寻找生存的希望,期待更好的生活。”
  • “非洲的人口构成了一个巨大的、纵横交织、覆盖整个大陆的网络,处于不断的运动之中,像波涛一样起伏,会在某一处汇聚,又在另一处四散。……这种被迫的人口流动性导致非洲内陆没有古老的城市,至少没有像欧洲或中东那种一直延续至今的城市。……因此,这个文明的一个显著特征是它的暂时性、过渡性以及缺乏物质的连续性。……在这里,真正生生不息并将各个社群联系在一起的,是家族传统和仪式的连续性,是对祖先的深刻崇拜。因此,非洲人与他亲近的人之间的联系,更多的是精神上的共同体,而不是物质或领土上的共同体。”
  • “在非洲,个人主义是不幸和诅咒的代名词。非洲的传统是集体主义的,因为只有团结一致的群体才能应对自然界不断涌现的挑战。而在集体中生存的一个条件,正是分享我所拥有的哪怕最微不足道的东西。”
  • “黑夜降临是非洲人最愿意聚在一起的时候。没有任何人想在这时候自己待着。自己?这代表了不幸,是来自地狱的惩罚!这里的孩子们也不会早早睡觉。全家人、全氏族、全村人——所有人要一起踏入梦乡。”
    • “我向他诉苦说,我家经常被偷。苏莱曼认为这很正常。盗窃的确让人难过,但却是减少不平等的一种方法。他肯定地告诉我,我被偷其实是好事,甚至是他们的一种友好姿态——他们用这种方式告诉我,我对他们来说是有用的,因此,他们是接受我的。基于此,我应该感到很安全才对。他又问我,在这里有没有受到过威胁?我承认,的确没有。原来如此!只要我允许自己被偷,那我在这儿就是安全的。”
  • “对军阀们来说,为贫穷挨饿的人民所提供的国际援助是取之不尽、用之不竭的利益来源。他们缺多少粮食、缺多少油,就从每次到达的援助物资中拿多少。因为这里的规矩就是:谁有武器谁先吃。他们拿剩下的,饥民们才能吃。对于国际组织来说问题在于,如果不让这些强人先拿,载有援助物资的交通工具就不会被允许入境,饥民就会饿死。所以他们只能期待,军阀拿了他们想要的之后,还能有些剩下的粮食被送到挨饿的人们手中。”
  • 「有时候军阀们也会意识到,一切可以被掠夺的东西都被他们抢光了,曾经的利益来源也已被消耗殆尽。于是,他们开始了所谓“和平进程”。他们召开“交战各方会议”,签署协议并确定选举日期。对此世界银行将向他们提供各种贷款和信贷。这样,军阀们将比以前更加富有,因为他们从世界银行那里得到的,可比从挨饿的兄弟那儿得到的多得多。」
  • “非洲人的本性是集体主义的,他们渴望并需要参与集体生活中的一切。所有的决定都是集体做出的,纠纷和争吵需要共同解决,比如谁能得到多少耕地是要通过决议的。按照传统,任何决议都必须经过全体一致通过。如果有人有不同意见,占多数的人就会一直劝说他,直到他改变立场为止。这种情况有时会无休止地持续下去,因为这些讨论的一个特点就是没完没了。如果村里有人发生了争吵,树下的法庭不会追求真相,也不会决定哪一方占理,他们会承认双方都有道理,然后解决冲突,让双方达成和解。”
  • “除了北部的伊斯兰地区,在非洲都没有文字,这里的历史是口口相传的传说,是在杧果树下不经意间创造出的神话,在深邃的黑夜中,只能听到长者颤颤巍巍的声音在讲述,因为妇女和儿童都在静静地聆听。这个晚间时光之所以重要,是因为人们在这个时刻会思考自己是谁,从哪儿来,会意识到自己的特殊与不同,会确定自己的身份。这也是和祖先交流的时光,祖先们虽然已经离去,但一直在指引他们,保佑他们不受邪恶的伤害。”
  • “所以直到黎明,杧果树下都是空的。黎明时分,太阳和树影会同时出现在地面上。太阳把人们叫醒,人们会立即开始躲避它,寻求树荫的庇护。这很奇怪,但人类的生命的确依赖于像阴影这样短暂而脆弱的东西。这就是为什么提供阴翳的树不仅仅是一棵树,更是生命本身。如果雷电击中树顶,杧果树被烧毁,这里的人们就没有地方可以躲避阳光,也就没有地方可以聚集在一起。由于没法聚在一起,他们将无法决定任何事情、解决任何问题。但最重要的是,他们将无法讲述自己的故事,因为只有傍晚在树下聚会时,才能将这些故事口口相传。所以,他们很快就会失去对昨天的了解,失去对昨天的记忆。他们将成为一群没有过去的人,换句话说,他们将成为一群没有身份的人。他们将失去那些将他们团结在一起的东西,他们将散去,各奔东西,形单影只。但是孤独在非洲是不可能的,一个孤独的人一天也活不过去,他注定会死。因此,如果闪电击碎了一棵树,生活在树荫下的人们也会死去。”

Apr.16

  • 家族和祖先,闽粤和南岛民族也都是这样啊,永远在行走,直到被气候和国家隔断了
  • 整个非洲都是“南方”,不跟任何北方接壤。大半个南美也是,“北方”面积不大
  • 行走,交谈,等待,互助,符咒
  • 敬畏常在,不要利用自然的准许
  • (读《太阳的阴影》)都快忘记了这样的社区默契了,经常也会想起小时候的邻里关系,整个楼洞的人我都认识,年龄相近的,从小就玩在一起
  • m5stack/StackChan: StackChan open source! 官方做的欸
  • “当我问起他的下一部纪录片会讲述什么时,他回答说是关于生活,仅此而已。于是我问他,生活对他而言意味着什么。沃纳·赫尔佐格用他惯有的讽刺口吻回答说:你看了电影就知道了。”
  • “我更愿意从另一个角度来看待它。矛盾的是,公开展示一头被蓄意夺去生命的动物,反而应该让我们更加深刻地意识到这场悲剧的规模。我们收到了一个警示信号:这就是我们摧毁的东西。从某种意义上说,我们失去了未来。
  • “作为人类,我们只是需要某种幻觉。这种幻觉始于童年,始于童话故事,它们会在我们的心灵深处占据一席之地。小时候相信圣诞老人是可以的,就像相信精灵、矮人和巨人一样。”
  • “这些人(原住民)不相信自然的力量。更重要的是,他们从不直接谈论自然,因为他们不了解自然。他们只是世界的一部分,对他们来说,世界是一个整体。他们不会在人类和其他人之间划清界限。自然只不过是另一种文化建构。我们离自己的根太远了。当我接触到游牧民族时,我才真正意识到这一点。**很可能,我们文明的许多问题,以及它所具有的破坏性,都源于我们放弃了游牧的生活方式。**然而,我们无法回到过去,重新成为游牧民族。作家布鲁斯·查特温曾谈到这种对漫游渴望的放弃,我对世界的看法也与之类似。”
  • “(对我们周围世界本质的哲学式误解。)野性自然就是野性。它没有品质。它既非善也非恶。它与和谐或完美毫无关联。它只是存在。所以,当你遇到一头熊时,它不是动画电影里的熊。你不能像蒂莫西·特雷德韦尔那样对着它哼歌。你不能试图安抚它。如果你想活着逃走,最好尊重它。”
  • “真正令人难忘的是体验本身。电影是体验的自然延伸,但有些东西是电影无法呈现的。当我们不得不离开洞穴时,我总是留在原地,以至于有好几分钟我都独自一人待在那里。我感觉自己被注视着。有双眼睛盯着我——来自三万年前的眼睛。”
  • Taller intensivo de cine con Warner Herzog: 11 días, 10,000 dólares y caos
  • “这是他的工作室的关键:拿起相机,拍摄镜头,跳过故事板,不要过度,最重要的是,做可行的事情。对于许多人来说,这个建议即使不是革命性的,也是一种解放。”
  • “世界是你的。你会走出去,但你必须保持反叛精神。我的建议是在各地建立反叛组织。”“El mundo es suyo. Saldrán allá afuera, pero deben seguir siendo rebeldes. Mi consejo es que formen células rebeldes en todas partes”.

Apr.17

  • 事情和无法改变的存在会聚集人,就像沙漠会让水变得最重要,四川会让太阳变得最重要一样,有事情正在发生,会让更多人聚集在一起。
  • 「在前殖民时期——其实这也并非很久之前——非洲曾有一万多个小国、王国、民族联盟和联邦。伦敦大学历史学家罗纳德·奥利弗在其著作《非洲的经验》( The African Experience,1991年于纽约出版)中指出了一个常见的悖论:人们约定俗成地说欧洲殖民主义者瓜分了非洲,“但是怎么能用‘分’这个词呢?”奥利弗非常不解,“这明明是用火与剑进行的残酷统一!从一万多个缩减到了五十个!”」这就是“现代”。
  • 「最后,大象绕着我们的桌子和空地走了几圈,丢下我们走开了,消失在黑暗之中。当重物砸地的轰隆声停止后,黑暗中一片寂静,一个坐在旁边的坦桑尼亚人问我:“你看到了吗?”“看到了,”我仍心有余悸地回答他,“一头大象。”“不,”他回答道,“非洲的灵魂总是以大象的形象出现。因为任何动物都是无法战胜大象的。狮子不行,水牛不行,蛇也不行。”」
  • 几乎所有的作为都得不到回报,更别提感激,世界也不会因此改变。明白这点还勇于尝试的人,值得尊敬。认真思考过后就不用思考了,去做。主动发心,传达出去。收束到这样的结尾真的很棒!大多时候,我们只是缺少那一点勇敢,想清楚要什么,去做、去达成就可以了,也只能这样。
  • AI做的PPT真多啊,看烦了都,果然写东西和排版很反人性吗
  • 很多OPC项目都提到,要先让用户进来,先不赚钱,形成粘性,一段时间后再考虑扩圈,都会做自媒体、社群,流量焦虑是很普遍,以及,很关注与硬件结合
  • “丰隆:古代神话中的雷神,即雷公。后多用作雷的代称。”
  • “水德神明,古今一也。”
  • 文字、人、诗作、故事、生活等等都是在不同维度上的,用文字看不出人的模样,想不出她的人生故事,散文和诗可以是两个人,人真的可以很多样,有很多很多的斜杠和and,而不是but,对人有惊讶感,是很正常的,惊讶反映出的反而是自己的贫瘠
  • 啧,创业板买少了
  • “运城盐池,位于古时河东地区的解州和安邑一带,也称河东盐池、解池、安邑盐池等。又因其管理部。” “长五十一里,广七里,周百一十六里,从盐省古声。吕忱曰:夙沙初作煮海盐,河东盐池谓之盬。今池水东西七十里,南北十七里,紫色澄渟,潭而不流。水出石盐,自然印成,朝取夕复,终无减损。”解(xiè)池,那看来真的只有本地才读hai呢

水经注校证

  • 「《温水》篇也有一个“昆仑”,却是作为族名的。由于《水经注》的《经》文和《注》文,都以“昆仑”开始,所以此词值得重视。不过因为是外来语,因而我们不懂它是什么意思。在《河水》篇同卷中也用梵语对“昆仑”作过解释。这是释氏《西域记》的话:“阿耨达太山,其上有大渊水,宫殿楼观甚大焉。山,即昆仑山也。”说明“昆仑山”在梵语中称为“阿耨达太山”,但我们也查不出“阿耨达”在梵语中是什么意思。所以“昆仑”是一个已经消失的民族的语言。像“昆仑”这类已经消失的民族语言地名,在我国至今还有不少。」
  • “夹岸崇深,倾崖返捍,巨石临危,若坠复倚。古之人有言,水非石凿,而能入石,信哉!其中水流交冲,素气云浮,往来遥观者,常若雾露沾人,窥深悸魄。其水尚崩浪万寻,悬流千丈,浑洪赑怒,鼓若山腾,浚波颓迭,迄于下口。”(龙门瀑布)
  • (北易水河)“濡水又东南迳樊於期馆西,是其授首于荆轲处也。濡水又东南流迳荆轲馆北,昔燕丹纳田生之言,尊轲上卿,馆之于此。二馆之城,涧曲泉清,山高林茂,风烟披薄,触可栖情,方外之士,尚凭依旧居,取畅林木。”
  • “又东迳未央宫北。高祖在关东,令萧何成未央宫,何斩龙首山而营之。山长六十余里,头临渭水,尾达樊川;头高二十丈,尾渐下,高五六丈;土色赤而坚,云昔有黑龙从南山出饮渭水,其行道因山成迹。山即基,阙不假筑,高出长安城。北有玄武阙,即北阙也。东有苍龙阙,阙内有阊阖、止车诸门。未央殿东有宣室、玉堂、麒麟、含章、白虎、凤皇、朱雀、鹓鸾、昭阳诸殿,天禄、石渠、麒麟三阁。未央宫北,即桂宫也,周十余里,内有明光殿、走狗台、柏梁台,旧乘复道,用相迳通。”
  • “中国古代的一个著名大湖圃田泽。中国古代与当今的美国、加拿大一样,是一个湖泊众多的国家,而现在成为一个贫湖国(美国平均每一万平方公里国土中,有湖泊二百六十平方公里,加拿大有一百三十平方公里,而中国只有十七平方公里),是历史上逐渐湮废、围垦而造成的。”
  • “肥水又西迳东台下,台即寿春外郭东北隅阿之榭也。东侧有一湖,三春九夏,红荷覆水。引渎城隍,水积成潭,谓之东台湖,亦肥南播也。肥水西迳寿春县故城北,右合北溪,水导北山,泉源下注,漱石颓隍。水上长林插天,高柯负日。出于山林,精舍右,山渊寺左,道俗嬉游,多萃其下,内外引汲,泉同七净。溪水沿注,西南迳陆道士解南精庐,临侧川溪,大不为广,小足闲居,亦胜境也。溪水西南注于肥水。”
  • “李冰作大堰于此,壅江作堋,堋有左右口,谓之湔堋。江入郫江、捡江以行舟。《益州记》曰:江至都安,堰其右,捡其左,其正流遂东,郫江之右也。因山颓水,坐致竹木,以溉诸郡。又穿羊摩江,灌江西。于玉女房下白沙邮,作三石人立水中,刻要江神:水竭不至足,盛不没肩。是以蜀人旱则藉以为溉,雨则不遏其流。故《记》曰:水旱从人,不知饥馑,沃野千里,世号陆海,谓之天府也。邮在堰上,俗谓之都安大堰,亦曰湔堰,又谓之金堤。左思《蜀都赋》云:西踰金堤者也。诸葛亮北征,以此堰农本,国之所资,以征丁千二百人主护之,有堰官。”
  • “自三峡七百里中,两岸连山,略无阙处。重岩迭嶂,隐天蔽日,自非停午夜分,不见曦月。至于夏水襄陵,沿溯阻绝,或王命急宣,有时朝发白帝,暮到江陵,其间千二百里,虽乘奔御风,不以疾也。春冬之时,则素湍绿潭,回清倒影,绝巘多生怪柏,悬泉瀑布,飞漱其间,清荣峻茂,良多趣味。每至晴初霜旦,林寒涧肃,常有高猿长啸,属引凄异,空谷传响,哀转久绝。故渔者歌曰:巴东三峡巫峡长,猿鸣三声泪沾裳。”
  • “此岩既高,加以江湍纡回,虽途迳信宿,犹望见此物,故行者谣曰:朝发黄牛,暮宿黄牛,三朝三暮,黄牛如故。言水路纡深,回望如一矣。”
  • “衡山东南二面临映湘川;自长沙至此,江湘七百里中,有九向九背。故渔者歌曰:帆随湘转,望衡九面。山上有飞泉下注,下映青林,直注山下,望之若幅练在山矣。”

一些河流

  • 滦河独自入海。上游(闪电河):丰宁→沽源→内蒙正蓝旗、多伦→河北隆化;中游:承德市全境(滦平、承德市区、宽城等);下游:迁西、迁安、滦州、卢龙、昌黎、滦南、乐亭入海。
  • 浑河(浑太水系),清原、抚顺市区、沈阳浑南/和平/沈河/大东/于洪/苏家屯/辽中、灯塔、辽阳县、台安、海城、营口(入海)。
  • 潍河,沂水、莒县、五莲、诸城、安丘、高密、昌邑、寒亭、莱州(入海)
  • 潮白河,丰宁、赤城、延庆、怀柔、密云、顺义、通州、宝坻、宁河(入海)。永定河,朔州、左云、右玉、怀安、宣化、涿鹿、怀来、门头沟、石景山、丰台、大兴、武清、北辰、东丽、滨海(入海)。
  • 北京有两个水系啊,所以方言也有两大类诶,天津、潍坊也是,怪不得城市发展可以摊大饼……
  • 图们江,和龙、龙井、图们、珲春(入海)。好吃的图们江,哈哈哈。
  • 韩江,长汀、武平、上杭、永定、大埔、梅江、梅县、丰顺、榕城、潮安、湘桥、澄海、汕头(入海)。
  • 雅砻江,石渠、甘孜、新龙、雅江、理塘、木里、盐源、攀枝花。
  • 岷江,松潘、茂县、都江堰、成都、眉山、乐山、宜宾。
  • 沱江,德阳、金堂、资阳、内江、自贡、泸州。
  • 嘉陵江,广元、阆中、南部、蓬安、南充、武胜、合川、北碚、沙坪坝、渝北、江北、渝中。
  • 大渡河,阿坝、甘孜、雅安、乐山。
  • 涪江,松潘、平武、江油、绵阳、三台、射洪、遂宁。
  • 渠江,南江、巴中、平昌、达州、渠县、广安、岳池、合川。

Apr.18

  • 诶,汉江源头在陕西汉中宁强县
  • 我们个人的魔法,都在自己的身体里。异兽魔都二阶堂不知道自己魔法蕴藏的力量,怪奇物语里罗宾说所有答案都在我自己手上。是的,时代的压力下,我们终于又不约而同地理解了这一点,世界或宇宙,过去或未来,所有交汇点都在我们自己身上,这里天然蕴藏着一切。与外部的连接可以有无数种,但一定都是从自身出发。让“我”复活吧。
  • “黄鹄山东北对夏口城,魏黄初二年,孙权所筑也。” “夏口城:在今湖北武汉。” “僰(bó)道即今宜宾,所以《注》文若水,实包括雅砻江以及雅砻江注入金沙江以直至宜宾的这一段金沙江在内。” “三蜀:古时称蜀郡、犍为、广汉为三蜀。”
  • 等岷江水多了,得去三峡走一圈!
  • “夷水又迳宜都北,东入大江,有泾、渭之比。亦谓之佷山北溪。水所经皆石山,略无土岸。其水虚映,俯视游鱼,如乘空也。浅处多五色石,冬夏激素飞清;傍多茂木空岫,静夜听之,恒有清响,百鸟翔禽,哀鸣相和。巡颓浪者,不觉疲而忘归矣。”
  • 夷水:清江,土家族母亲河,流经利川、恩施、宣恩、建始、巴东、长阳、宜都。
  • 帝喾(kù),号高辛氏,上古五帝顺位第三(黄帝、颛顼、帝喾、尧、舜)。
  • “浙江又北迳新城县,桐溪水注之。水出吴兴郡於潜县北天目山。山极高峻,崖岭竦迭,西临峻涧。山上有霜木,皆是数百年树,谓之翔凤林。东面有瀑布,下注数亩深沼,名曰浣龙池。池水南流迳县西,为县之西溪。溪水又东南与紫溪合,水出县西百丈山,即潜山也。”
  • 果然唐前江浙都是丛林巨木
  • “其水分纳众流,混波东逝,迳定阳县。夹岸缘溪,悉生支竹,及芳枳、木连,杂以霜菊、金橙。白沙细石,状如凝雪。石溜湍波,浮响无辍,山水之趣,尤深人情。”
  • 太美了!定阳,今常山县,码了!
  • 等老了,很多产业内迁到内陆了,应该就可以去绍兴诸暨一代生活了,一直很喜欢那里
  • 水经注校证,看完了,陈桥驿先生注得好啊,译得也好,看完再回来读原文,只见文字的优美,而不是总求理解了。
  • 北朝太多典籍古史遗失,很多故事也没有再流传了。果然先生是绍兴人,最后几章的地方他都去过哈哈!最种草的是三峡和常山!要去!

CHANGELOG

  • 20260419 Arlmy 创建、整理
  • 20260419 Arlmy 发布
🔲 ☆

本地部署 OpenClaw,QwenPaw 和 Hermes Agent

环境信息

本教程将介绍在 macOS 环境下部署 OpenClaw,QwenPaw 和 Hermes Agent。如无特殊说明,macOS 系统下需在终端中执行命令,Windows 系统下需要在 PowerShell 中执行命令。本教程涉及到的软件信息如下:

软件 版本
Podman 5.8.2
OpenClaw 2026.4.16
QwenPaw 1.1.2
Hermes Agent 0.10.0

虚拟环境

为了更好的进行环境隔离,后续我们使用 Podman 安装不同的智能体框架。

提示

在 Windows 系统下,单击开始按钮,搜索 启用或关闭 Windows 功能,在打开的窗口中将 Hyper-V适用于 Linux 的 Windows 子系统 及其子项全部勾选,单击确定,待系统启用功能后重启电脑。

运行如下命令安装 Podman:

brew install podman

brew 安装请参见:https://brew.sh/

scoop install podman

scoop 安装请参见:https://scoop.sh/

为了方便观察 Podman 的运行情况,可选的运行如下命令安装 Podman Desktop:

brew install podman-desktop
scoop install podman-desktop

安装完毕后,运行如下命令初始化并启动 Podman:

podman machine init
podman machine start
podman machine init
podman machine start

运行如下命令查看 Podman 的安装和运行情况:

podman info
podman info

更多 Podman 使用方法请参考 Podman 文档。有关 Podman Desktop 的使用方法请参考 Podman Desktop 文档

OpenClaw

安装

克隆 OpenClaw 的源代码至本地:

git clone git@github.com:openclaw/openclaw.git
git clone git@github.com:openclaw/openclaw.git

运行如下命令安装 OpenClaw CLI:

curl -fsSL https://openclaw.ai/install-cli.sh | bash

命令会在 ~/.openclaw 目录下安装 Node 环境和相关依赖。进入 OpenClaw 源代码目录下,运行如下命令构建 Gateway 容器:

export OPENCLAW_DOCKER_APT_PACKAGES="chromium"
./scripts/podman/setup.sh

其中 OPENCLAW_DOCKER_APT_PACKAGES 表示构建 Gateway 容器时使用 apt 命令额外安装的软件包。运行如下命令启动 Gateway 容器:

./scripts/run-openclaw-podman.sh launch

运行如下命令进行配置:

./scripts/run-openclaw-podman.sh launch setup

在配置过程中根据实际情况对模型提供商等选项进行配置。配置如下环境变量来使用宿主机中的 OpenClaw CLI 管理 OpenClaw 容器:

export PATH="$PATH:/Users/leo/.openclaw/bin"
export OPENCLAW_CONTAINER=openclaw

在合适的目录创建如下文件夹:

mkdir /path/to/openclaw
mkdir /path/to/openclaw/workspace

进入 OpenClaw 源代码目录下,运行如下命令构建 Gateway 容器:

podman build -t openclaw:local -f Dockerfile . --build-arg OPENCLAW_DOCKER_APT_PACKAGES="chromium"

其中 OPENCLAW_DOCKER_APT_PACKAGES 表示构建 Gateway 容器时使用 apt 命令额外安装的软件包。运行如下命令配置并启动 Gateway 容器:

$env:OPENCLAW_CONFIG_DIR = "/path/to/openclaw"
$env:OPENCLAW_WORKSPACE_DIR = "/path/to/openclaw/workspace"

podman compose run --rm --no-deps --entrypoint node openclaw-gateway `
  dist/index.js onboard --mode local --no-install-daemon

podman compose run --rm --no-deps --entrypoint node openclaw-gateway `
  dist/index.js config set --batch-json '[{"path":"gateway.mode","value":"local"},{"path":"gateway.bind","value":"lan"},{"path":"gateway.controlUi.allowedOrigins","value":["http://localhost:18789","http://127.0.0.1:18789"]}]'

podman compose up -d openclaw-gateway

在配置中过程中根据实际情况对模型提供商等选项进行配置。

使用 Podman 安装 OpenClaw 后,重启 Gateway 的命令如下:

podman restart openclaw
podman restart openclaw-openclaw-gateway-1

使用浏览器打开 http://127.0.0.1:18789/ 即可进入 OpenClaw 管理页面。更多细节请参考 OpenClaw 文档

警告

OpenClaw 管理页面虽然支持权限校验,但为了安全起见,请勿将其暴露在公网。

消息频道

提示

在 Windows 系统下,podman compose 命令需要在 OpenClaw 源代码目录下运行。

飞书

运行如下命令配置飞书消息频道:

openclaw channels login --channel feishu
podman compose exec openclaw-gateway node dist/index.js channels login --channel feishu

使用飞书 APP 扫描生成的二维码进行后续配置即可。

Discord

首先在 Discord 中创建一个服务器,进入 Discord 开发者门户,单击 新 APP 按钮创建新的应用,并填写应用名称。

进入 概况 - 机器人 选项卡,启用 Presence IntentServer Members IntentMessage Content Intent

进入 概况 - 机器人 选项卡,单击 重置令牌 生成令牌,注意令牌仅显示一次,请妥善保管以便后续使用。

进入 概况 - OAuth2 选项卡,在 OAuth2 URL 生成器的 范围 中选中 botapplications.commands

机器人权限 中选中 查看频道发送消息阅读消息历史记录嵌入链接添加文件添加反应

已生成的 URL 中的 URL 复制到浏览器打开,按照提示安装应用并授权。

打开 Discord 应用,进入 用户设置 页面,在 开发者 菜单中启用 开发者模式。在频道图标上右键,单击 复制服务器 ID,在个人头像上左键,单击 复制用户 ID

运行如下命令配置 Discord 消息频道:

openclaw config set channels.discord.token "你的 TOKEN"
openclaw config set channels.discord.enabled true --strict-json
podman compose exec openclaw-gateway node dist/index.js config set channels.discord.token "你的 TOKEN"
podman compose exec openclaw-gateway node dist/index.js config set channels.discord.enabled true --strict-json

向机器人发送任意消息,根据机器人回复的消息运行如下命令进行授权:

openclaw pairing approve discord <CODE>
podman compose exec openclaw-gateway node dist/index.js pairing approve discord <CODE>

此时就可以通过 Discord 私信和机器人对话了。在配置文件中添加如下内容来支持在服务器中同机器人对话:

{
  "channels": {
    "discord": {
      "groupPolicy": "allowlist",
      "guilds": {
        "服务器 ID": {
          "requireMention": true,
          "users": ["用户 ID"]
        }
      }
    }
  }
}

默认情况下,机器人只有在被 @ 时才会响应,如果需要对每条消息都进行响应,可以将 requireMention 设置为 false

QwenPaw

安装

在合适的目录创建如下文件夹:

mkdir -p /path/to/qwenpaw/data
mkdir -p /path/to/qwenpaw/secrets
mkdir /path/to/qwenpaw/data
mkdir /path/to/qwenpaw/secrets

运行如下命令使用 Podman 安装 QwenPaw:

podman pull agentscope/qwenpaw:latest
podman run -d \
  --name qwenpaw \
  --restart always \
  -v /path/to/qwenpaw/data:/app/working \
  -v /path/to/qwenpaw/secrets:/app/working.secret \
  -p 8088:8088 \
  agentscope/qwenpaw:latest
podman pull agentscope/qwenpaw:latest
podman run -d `
  --name qwenpaw `
  --restart always `
  -v /path/to/qwenpaw/data:/app/working `
  -v /path/to/qwenpaw/secrets:/app/working.secret `
  -p 8088:8088 `
  agentscope/qwenpaw:latest

使用浏览器打开 http://127.0.0.1:8088/ 即可进入 QwenPaw 管理页面。

警告

QwenPaw 管理页面暂无权限校验,请勿将其暴露在公网。

注意

在 QwenPaw 中需要进入 智能体管理 中新建一个智能体,默认智能体 无法进行修改。

消息频道

飞书

参见:https://qwenpaw.agentscope.io/docs/channels/?lang=zh#飞书

Discord

参见:https://qwenpaw.agentscope.io/docs/channels/?lang=zh#Discord

Hermes Agent

安装

在合适的目录创建如下文件夹:

mkdir -p /path/to/hermes-agent
mkdir /path/to/hermes-agent

运行如下命令使用 Podman 配置 Hermes Agent:

podman pull nousresearch/hermes-agent:latest
podman run -it --rm \
  -v /path/to/hermes-agent:/opt/data \
  nousresearch/hermes-agent:latest setup
podman pull nousresearch/hermes-agent:latest
podman run -it --rm `
  -v /path/to/hermes-agent:/opt/data `
  nousresearch/hermes-agent:latest setup

在配置过程中根据实际情况对模型提供商等选项进行配置。运行如下代码启动 Gateway 容器:

podman run -d \
  --name hermes-agent-gateway \
  --restart unless-stopped \
  -v /path/to/hermes-agent:/opt/data \
  -p 8642:8642 \
  nousresearch/hermes-agent:latest gateway run
podman run -d `
  --name hermes-agent-gateway `
  --restart unless-stopped `
  -v /path/to/hermes-agent:/opt/data `
  -p 8642:8642 `
  nousresearch/hermes-agent:latest gateway run

运行如下代码启动 Dashboard 容器:

podman run -d \
  --name hermes-agent-dashboard \
  --restart unless-stopped \
  -v /path/to/hermes-agent:/opt/data \
  -p 9119:9119 \
  -e GATEWAY_HEALTH_URL=http://$HOST_IP:8642 \
  nousresearch/hermes-agent:latest dashboard --host 0.0.0.0 --insecure
podman run -d `
  --name hermes-agent-dashboard `
  --restart unless-stopped `
  -v /path/to/hermes-agent:/opt/data `
  -p 9119:9119 `
  -e GATEWAY_HEALTH_URL=http://$HOST_IP:8642 `
  nousresearch/hermes-agent:latest dashboard --host 0.0.0.0 --insecure

$HOST_IP 替换为运行 Gateway 容器机器的 IP 地址(注意:需使用宿主机的 IP 地址,而不是 127.0.0.1)。使用浏览器打开 http://127.0.0.1:9119/ 即可进入 Hermes Agent 管理页面。

警告

Hermes Agent 管理页面暂无权限校验,请勿将其暴露在公网。

消息频道

飞书

参见:https://hermes-agent.nousresearch.com/docs/user-guide/messaging/feishu

Discord

参见:https://hermes-agent.nousresearch.com/docs/user-guide/messaging/discord

🔲 ☆

人机协作逆向:用 AI + Frida 打通微信 4.1.8 macOS 数据库密钥提取

<blockquote><p>当内存特征扫描失效、标准 Hook 无功而返时,一个开发者与 AI 如何通过多轮迭代,最终找到微信 4.1.8 的密钥派生入口。</p></blockquote><hr><h2 id="一、背景:旧方案突然失效"><a href="#一、背景:旧方案突然失效" class="headerlink" title="一、背景:旧方案突然失效"></a>一、背景:旧方案突然失效</h2><p><code>chatlog</code> 项目长期依赖一套相对稳定的 macOS 密钥提取逻辑:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vmmap 定位内存区域 → lldb dump 内存 → 搜索 fts5(% 特征字节 → 固定偏移取 32B Key → DB 验证</span><br></pre></td></tr></table></figure><p>但在用户升级到 <strong>微信 4.1.8</strong> 后,这条链路彻底断裂。运行 <code>./bin/chatlog key --debug</code> 时,程序能正常读完 94 个内存区域,然后陷入漫长的静默,最终超时退出——<strong>Key 一个都没找到</strong>。</p><hr><h2 id="二、第一回合:AI-做诊断,人类给环境"><a href="#二、第一回合:AI-做诊断,人类给环境" class="headerlink" title="二、第一回合:AI 做诊断,人类给环境"></a>二、第一回合:AI 做诊断,人类给环境</h2><p>用户把问题抛给 AI:</p><blockquote><p>“我发现现在微信版本升级了,无法获取到 key。”</p></blockquote><p>AI 的第一反应是排查代码路径。通过阅读 <code>chatlog</code> 源码,AI 迅速定位到 V4 Extractor 的两个 Pattern:</p><ul><li>Pattern 1: <code> fts5(%</code>(<code>20 66 74 73 35 28 25 00</code>)</li><li>Pattern 2: 16 个零字节(<code>00 00 ...</code>)</li></ul><p>AI 列出可能的故障点,但用户没有手动去贴日志,而是直接说:</p><blockquote><p><strong>“你直接运行看看吧。”</strong></p></blockquote><p>AI 执行 <code>./bin/chatlog key --debug</code>,观察到内存读取正常、但匹配全部失败。接着 AI 检查微信二进制:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">strings /Applications/WeChat.app/Contents/MacOS/WeChat | grep -c <span class="string">&quot;fts5&quot;</span></span><br><span class="line"><span class="comment"># 输出: 0</span></span><br></pre></td></tr></table></figure><p><strong>关键发现:<code>fts5</code> 在微信 4.1.8 里完全消失了。</strong> 这是旧方案失效的根因。</p><hr><h2 id="三、第二回合:人类抛出新线索,AI-转换思路"><a href="#三、第二回合:人类抛出新线索,AI-转换思路" class="headerlink" title="三、第二回合:人类抛出新线索,AI 转换思路"></a>三、第二回合:人类抛出新线索,AI 转换思路</h2><p>AI 解释了问题的本质:内存布局变了,需要找新的 Pattern 或换更稳定的 Hook 思路。此时用户提供了一个关键的外部信息:</p><blockquote><p><strong>“wx_key 你参考看看这个项目是 windows 的,但是你看看他是怎么找 key 的”</strong></p></blockquote><p>AI 读取 <code>wx_key</code> 的源码后,核心思路被点亮:</p><blockquote><p><strong>Windows 版不扫描数据,而是 Hook 函数。</strong> 它在 <code>Weixin.dll</code> 代码段里搜索机器码 pattern,定位到处理 Key 的函数,安装 Inline Hook,等微信调用时直接拦截参数。</p></blockquote><p>这是一个比内存扫描稳定得多的方案。用户接着问:</p><blockquote><p><strong>“frida hook 你能做吗”</strong></p></blockquote><p>AI 回答:能。于是进入下一轮技术攻坚。</p><hr><h2 id="四、第三回合:AI-搭建-Frida-环境,踩坑与修正"><a href="#四、第三回合:AI-搭建-Frida-环境,踩坑与修正" class="headerlink" title="四、第三回合:AI 搭建 Frida 环境,踩坑与修正"></a>四、第三回合:AI 搭建 Frida 环境,踩坑与修正</h2><p>AI 开始动手:</p><ol><li><strong>安装 Frida</strong>:<code>pip3 install frida-tools</code></li><li><strong>寻找目标函数</strong>:先尝试 Hook <code>libsqlite3.dylib</code> 的 <code>sqlite3_key</code> &#x2F; <code>sqlite3_key_v2</code></li></ol><h3 id="坑-1:Frida-17-API-变更"><a href="#坑-1:Frida-17-API-变更" class="headerlink" title="坑 1:Frida 17 API 变更"></a>坑 1:Frida 17 API 变更</h3><p>写脚本时踩了第一个坑——旧教程里的 <code>Module.findExportByName</code> 在 Frida 17 中报错:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">TypeError: not a function</span><br></pre></td></tr></table></figure><p>AI 通过快速测试定位到新 API:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 正确写法</span></span><br><span class="line"><span class="title class_">Process</span>.<span class="title function_">getModuleByName</span>(<span class="string">&quot;libsqlite3.dylib&quot;</span>).<span class="title function_">findExportByName</span>(<span class="string">&quot;sqlite3_key&quot;</span>)</span><br><span class="line"><span class="comment">// 或</span></span><br><span class="line"><span class="title class_">Module</span>.<span class="title function_">getGlobalExportByName</span>(<span class="string">&quot;sqlite3_key&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="坑-2:sqlite3-key-根本没被调用"><a href="#坑-2:sqlite3-key-根本没被调用" class="headerlink" title="坑 2:sqlite3_key 根本没被调用"></a>坑 2:sqlite3_key 根本没被调用</h3><p>修正 API 后,Hook 安装成功。AI 让用户关闭微信,用 <code>frida.spawn</code> 重新启动并注入脚本。用户登录微信,AI 监控输出——<strong>60 秒内一个 Key 都没捕获到</strong>。</p><p>这说明 <strong>微信 4.1.8 macOS 并未使用标准 SQLCipher 的 <code>sqlite3_key</code> 接口</strong>。加密逻辑完全内嵌在 140MB 的 <code>wechat.dylib</code> 中,自己管理密钥派生和 VFS 层加解密。</p><hr><h2 id="五、第四回合:转移战场到-PBKDF2"><a href="#五、第四回合:转移战场到-PBKDF2" class="headerlink" title="五、第四回合:转移战场到 PBKDF2"></a>五、第四回合:转移战场到 PBKDF2</h2><p>既然 <code>chatlog</code> 的解密代码明确告诉我们:</p><ul><li><strong>算法</strong>:PBKDF2-HMAC-SHA512</li><li><strong>迭代次数</strong>:256000</li><li><strong>输出长度</strong>:32 字节</li></ul><p>那么微信启动数据库时,<strong>一定会在某个时刻做密钥派生</strong>。macOS 上最可能的系统入口是 <code>CommonCrypto</code> 中的 <code>CCKeyDerivationPBKDF</code>。</p><p>AI 更新 Frida 脚本,新增对 <code>CCKeyDerivationPBKDF</code> 的 Hook,并加上过滤条件:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> pbkdf2 = <span class="title class_">Module</span>.<span class="title function_">getGlobalExportByName</span>(<span class="string">&quot;CCKeyDerivationPBKDF&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="title class_">Interceptor</span>.<span class="title function_">attach</span>(pbkdf2, &#123;</span><br><span class="line"> <span class="attr">onEnter</span>: <span class="keyword">function</span>(<span class="params">args</span>) &#123;</span><br><span class="line"> <span class="keyword">var</span> algo = args[<span class="number">0</span>].<span class="title function_">toInt32</span>(); <span class="comment">// 2 = kCCPBKDF2</span></span><br><span class="line"> <span class="keyword">var</span> prf = args[<span class="number">5</span>].<span class="title function_">toInt32</span>(); <span class="comment">// 5 = kCCPRFHmacAlgSHA512</span></span><br><span class="line"> <span class="keyword">var</span> rounds = args[<span class="number">6</span>].<span class="title function_">toInt32</span>();</span><br><span class="line"> <span class="keyword">var</span> pwdLen = args[<span class="number">2</span>].<span class="title function_">toInt32</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (algo === <span class="number">2</span> &amp;&amp; prf === <span class="number">5</span> &amp;&amp; rounds &gt; <span class="number">1000</span>) &#123;</span><br><span class="line"> <span class="keyword">var</span> hex = <span class="title function_">toHex</span>(args[<span class="number">1</span>], pwdLen);</span><br><span class="line"> <span class="title function_">send</span>(&#123;<span class="attr">type</span>: <span class="string">&quot;key&quot;</span>, <span class="attr">key</span>: hex, <span class="attr">rounds</span>: rounds, <span class="attr">len</span>: pwdLen&#125;);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><hr><h2 id="六、第五回合:捕获成功,人类验证"><a href="#六、第五回合:捕获成功,人类验证" class="headerlink" title="六、第五回合:捕获成功,人类验证"></a>六、第五回合:捕获成功,人类验证</h2><p>脚本就绪后,用户让AI 关闭微信,运行脚本</p><p>几秒钟后,终端输出:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[KEY] via CCKeyDerivationPBKDF len=32 rounds=256000 dkLen=32</span><br><span class="line"> 24f52f003edd470e97e73fc63f4b89bb0cb9efa09b8a40d685c75c47df21fc11</span><br></pre></td></tr></table></figure><p><strong>Key 捕获成功。</strong></p><p>但还没完。用户紧接着提出验证要求:</p><blockquote><p><strong>“你根据这个 key 测试一下是否能 chatlog decrypt”</strong></p></blockquote><p>AI 立刻用捕获到的 Key 执行解密:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">./bin/chatlog decrypt \</span><br><span class="line"> --data-dir ~/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/... \</span><br><span class="line"> --data-key 24f52f003edd470e97e73fc63f4b89bb0cb9efa09b8a40d685c75c47df21fc11 \</span><br><span class="line"> --work-dir /tmp/chatlog_test</span><br></pre></td></tr></table></figure><p>输出:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">decrypt success</span><br></pre></td></tr></table></figure><p><strong>Key 有效,数据库成功解密。</strong></p><hr><h2 id="七、核心方法论:人机协作的分工"><a href="#七、核心方法论:人机协作的分工" class="headerlink" title="七、核心方法论:人机协作的分工"></a>七、核心方法论:人机协作的分工</h2><p>回顾整个过程,人类与 AI 的分工非常清晰:</p><table><thead><tr><th>角色</th><th>贡献</th></tr></thead><tbody><tr><td><strong>人类</strong></td><td>提供运行环境、触发问题、给出关键外部线索(<code>wx_key</code>)、决定尝试 Frida、执行微信登录操作、提出最终验证需求</td></tr><tr><td><strong>AI</strong></td><td>快速阅读大量源码做根因分析、搭建 Frida 环境、编写&#x2F;调试 Hook 脚本、处理 API 兼容性问题、执行最终验证</td></tr></tbody></table><p>如果没有人类抛出的 <code>wx_key</code> 项目,AI 可能会继续在内存扫描或 sqlite3 Hook 的思路上打转;如果没有 AI 的快速代码阅读和脚本编写能力,从诊断到验证可能需要数小时甚至数天。</p><hr><h2 id="八、关键踩坑清单"><a href="#八、关键踩坑清单" class="headerlink" title="八、关键踩坑清单"></a>八、关键踩坑清单</h2><h3 id="1-特征字节依赖生命周期极短"><a href="#1-特征字节依赖生命周期极短" class="headerlink" title="1. 特征字节依赖生命周期极短"></a>1. 特征字节依赖生命周期极短</h3><p>微信 4.1.8 直接从二进制中移除了 <code>fts5</code> 字符串,导致基于内存布局的扫描方案瞬间破产。</p><h3 id="2-标准-SQLCipher-API-是个假目标"><a href="#2-标准-SQLCipher-API-是个假目标" class="headerlink" title="2. 标准 SQLCipher API 是个假目标"></a>2. 标准 SQLCipher API 是个假目标</h3><p>macOS 系统库里有 <code>sqlite3_key</code>,但微信根本不调用。Hook 之前必须做实际验证,不能凭假设行事。</p><h3 id="3-Frida-版本陷阱"><a href="#3-Frida-版本陷阱" class="headerlink" title="3. Frida 版本陷阱"></a>3. Frida 版本陷阱</h3><p>Frida 17 废弃了 <code>Module.findExportByName</code>,大量网络教程已过时。实际动手测试才能发现。</p><h3 id="4-必须在-spawn-模式下拦截"><a href="#4-必须在-spawn-模式下拦截" class="headerlink" title="4. 必须在 spawn 模式下拦截"></a>4. 必须在 <code>spawn</code> 模式下拦截</h3><p>attach 到已运行的微信会错过所有启动时的密钥派生。先 <code>pkill WeChat</code>,再用 <code>frida.spawn()</code> 启动,是捕获 Key 的必要条件。</p><hr><h2 id="九、完整可用脚本"><a href="#九、完整可用脚本" class="headerlink" title="九、完整可用脚本"></a>九、完整可用脚本</h2><p>以下脚本已保存至项目根目录 <code>wechat_key_hook.py</code>,可直接使用:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="keyword">import</span> frida</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">JS = <span class="string">r&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">function toHex(ptr, len) &#123;</span></span><br><span class="line"><span class="string"> try &#123;</span></span><br><span class="line"><span class="string"> var arr = new Uint8Array(ptr.readByteArray(len));</span></span><br><span class="line"><span class="string"> return arr.map(b =&gt; b.toString(16).padStart(2, &#x27;0&#x27;)).join(&#x27;&#x27;);</span></span><br><span class="line"><span class="string"> &#125; catch(e) &#123; return null; &#125;</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">var pbkdf2 = Module.getGlobalExportByName(&quot;CCKeyDerivationPBKDF&quot;);</span></span><br><span class="line"><span class="string">if (pbkdf2) &#123;</span></span><br><span class="line"><span class="string"> Interceptor.attach(pbkdf2, &#123;</span></span><br><span class="line"><span class="string"> onEnter: function(args) &#123;</span></span><br><span class="line"><span class="string"> var algo = args[0].toInt32();</span></span><br><span class="line"><span class="string"> var prf = args[5].toInt32();</span></span><br><span class="line"><span class="string"> var rounds = args[6].toInt32();</span></span><br><span class="line"><span class="string"> var pwdLen = args[2].toInt32();</span></span><br><span class="line"><span class="string"> if (algo === 2 &amp;&amp; prf === 5 &amp;&amp; rounds &gt; 1000) &#123;</span></span><br><span class="line"><span class="string"> var hex = toHex(args[1], pwdLen);</span></span><br><span class="line"><span class="string"> if (hex) send(&#123;type:&quot;key&quot;, key:hex, rounds:rounds, len:pwdLen&#125;);</span></span><br><span class="line"><span class="string"> &#125;</span></span><br><span class="line"><span class="string"> &#125;</span></span><br><span class="line"><span class="string"> &#125;);</span></span><br><span class="line"><span class="string"> send(&quot;[+] CCKeyDerivationPBKDF hooked @ &quot; + pbkdf2);</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[*] Spawning WeChat...&quot;</span>)</span><br><span class="line">pid = frida.spawn(<span class="string">&quot;/Applications/WeChat.app/Contents/MacOS/WeChat&quot;</span>)</span><br><span class="line">session = frida.attach(pid)</span><br><span class="line">script = session.create_script(JS)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">on_msg</span>(<span class="params">message, data</span>):</span><br><span class="line"> <span class="keyword">if</span> message[<span class="string">&#x27;type&#x27;</span>] == <span class="string">&#x27;send&#x27;</span>:</span><br><span class="line"> payload = message[<span class="string">&#x27;payload&#x27;</span>]</span><br><span class="line"> <span class="keyword">if</span> <span class="built_in">isinstance</span>(payload, <span class="built_in">dict</span>) <span class="keyword">and</span> payload.get(<span class="string">&#x27;type&#x27;</span>) == <span class="string">&#x27;key&#x27;</span>:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">f&quot;\n[!!!] KEY CAPTURED: <span class="subst">&#123;payload[<span class="string">&#x27;key&#x27;</span>]&#125;</span>&quot;</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="built_in">print</span>(payload)</span><br><span class="line"></span><br><span class="line">script.on(<span class="string">&#x27;message&#x27;</span>, on_msg)</span><br><span class="line">script.load()</span><br><span class="line">frida.resume(pid)</span><br><span class="line"></span><br><span class="line">time.sleep(<span class="number">60</span>)</span><br><span class="line">session.detach()</span><br></pre></td></tr></table></figure><hr><h2 id="十、后续展望"><a href="#十、后续展望" class="headerlink" title="十、后续展望"></a>十、后续展望</h2><p>这次实践验证了 <strong>Hook 加密算法入口</strong> 比 <strong>内存特征扫描</strong> 更稳定、更跨版本。下一步可以将 Frida Hook 封装为 <code>chatlog key</code> 的新后端(例如 <code>--frida</code> 模式),让 macOS 用户不再需要关闭 SIP 或依赖 <code>lldb</code>,大幅降低使用门槛。</p><blockquote><p><strong>一个人类开发者 + 一个能读代码、写脚本、跑验证的 AI,可以在一个小时内完成传统上需要数天的逆向工程闭环。</strong></p></blockquote><p><br /><strong>本文作者</strong>:高金<br /><strong>本文地址</strong>: <a href="https://igaojin.me/2026/04/18/%E4%BA%BA%E6%9C%BA%E5%8D%8F%E4%BD%9C%E9%80%86%E5%90%91%EF%BC%9A%E7%94%A8-AI-Frida-%E6%89%93%E9%80%9A%E5%BE%AE%E4%BF%A1-4-1-8-macOS-%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AF%86%E9%92%A5%E6%8F%90%E5%8F%96/">https://igaojin.me/2026/04/18/人机协作逆向:用-AI-Frida-打通微信-4-1-8-macOS-数据库密钥提取/</a> <br /><strong>版权声明</strong>:转载请注明出处!</p><div id="gitalk-container"></div><script src="https://cdn.bootcss.com/blueimp-md5/2.12.0/js/md5.min.js"></script><link rel="stylesheet" href="https://unpkg.com/gitalk/dist/gitalk.css"><script src="https://unpkg.com/gitalk/dist/gitalk.min.js"></script><script>var gitalkConfig = {"clientID":"935e92a5333436856348","clientSecret":"e655566eaf920d216ec6283978d67874bf0850a6","repo":"jin10086.github.io","owner":"jin10086","admin":["jin10086"],"distractionFreeMode":false,"id":"page.date","createIssueManually":false}; gitalkConfig.id = md5(location.pathname);var gitalk = new Gitalk(gitalkConfig); gitalk.render("gitalk-container"); </script>
🔲 ☆

Wayfire支持不缩放Xwayland啦

本文来自依云's Blog,转载请注明。

Wayland协议会告诉客户端程序缩放比率应该是多少,支持的就按指定的来,不支持的Wayland compositor就负责把窗口缩放到合适的大小。但因为是客户端渲染成位图之后,再由Wayland compositor缩放,大小是合适了,但却模糊了起来。然而很不幸地,Xwayland就是那个不支持Wayland缩放机制所以被Wayland compositor强行缩放的Wayland客户端。

但其实在X的时代,各个GUI框架都发展出来了自己的一套指定缩放的机制。比如一些老的程序会认Xft.dpi这个设置,GTK可以用GDK_SCALE环境变量,Qt认QT_AUTO_SCREEN_SCALE_FACTORQT_SCREEN_SCALE_FACTORS,Wine可以在winecfg里设置,等等。我想说的是,虽然不太统一,但大家其实都有自己的HiDPI方案了。

所以,Wayland compositor如果不强行缩放Xwayland的窗口的话,那些还不原生支持Wayland的程序其实也能显示得清晰好看的。但可惜的是Xwayland始终没有合并任何解决方案,我也就只好用着打过补丁的wlroots-lily-gitxorg-xwayland-lily了。KDE很早就引入不缩放Xwayland窗口的选项、让这些使用Xwayland的程序自己处理缩放。Hyprland后来也支持了。而现在,终于轮到Wayfire支持啦~

Wayfire的这个选项叫workarounds/force_xwayland_scaling。设置为true就是程序自己处理缩放、Wayfire不管它了。一开始没处理鼠标光标,后来也修好了。xorg-xwayland-lily终于完成了它的使命~

顺便说一下,workarounds这里我还设置了另外两个选项。一个是discard_command_output=false,这样能看到Wayfire启动的程序的报错信息。另一个是auto_reload_config=false,禁用自动重新加载配置文件,不用和git打架了。

[workarounds]
discard_command_output = false
auto_reload_config = false
force_xwayland_scaling = true
🔲 ☆

1Password涨价后,别急着退订1Password,这个操作能帮你省25%

1Password涨价33%后,我差点转向Bitwarden,但一个关键问题让我最终选择继续续费。本文详细对比主流密码管理器,分析Passkey绑定风险,并分享如何用YubiKey提升安全性以及成功获取25%续费折扣的经验。

1Password涨价33%后,我差点转向Bitwarden,但一个关键问题让我最终选择继续续费。本文详细对比主流密码管理器,分析Passkey绑定风险,并分享如何用YubiKey提升安全性以及成功获取25%续费折扣的经验。
🔲 ☆

TIL: repeat-mode 省去重复按键前缀

按 ~C-x o C-x o C-x o~ 切换窗口,或者 ~C-x { C-x { C-x {~ 缩小窗口——每次都要重复前缀,很烦。 Emacs 28 内置的 ~repeat-mode~ 可以在首次按键后省掉前缀,只按最后一个键就行: #+BEGIN_SRC emacs-lisp (repeat-mode 1) #+END_SRC 开启后,~C-x o o o~ 就能连续切换窗口,~C-x { { {~ 连续缩小。 * 内置支持的命令 Emacs 自带了一批 repeat map,开箱即用: | 首次按键 | 后续按键 | 效果 | |---+---+---| | ~C-x o~ | ~o~ | 切换窗口 | | ~C-x {~ / ~C-x }~ | ~{~ / ~}~ | 水平缩小/扩大窗口 | | ~C-x ^~ | ~^~ | 垂直扩大窗口 | | ~C-x u~ | ~u~ | 撤销 | | ~C-x ~ / ~C-x ~ | ~~ / ~~ | 切换 buffer | | ~M-g n~ / ~M-g p~ | ~n~ / ~p~ | 跳转下一个/上一个错误 | 按了不在 repeat map 里的键,重复状态自动结束。 也可以设置超时自动退出: #+BEGIN_SRC emacs-lisp (setq repeat-exit-timeout 5) ;; 5 秒无操作自动退出 #+END_SRC * 自定义 repeat map 以 expreg(expand-region)为例,实现 ~C-= = = -~ 即三次展开再一次收缩: #+BEGIN_SRC emacs-lisp (defvar expreg-repeat-map (let ((map (make-sparse-keymap))) (define-key map "=" #'expreg-expand) (define-key map "-" #'expreg-contract) map)) (put 'expreg-expand 'repeat-map 'expreg-repeat-map) (put 'expreg-contract 'repeat-map 'expreg-repeat-map) #+END_SRC 模式很简单:创建 keymap,给命令设置 ~repeat-map~ symbol property。任何带这个属性的命令在首次调用后都会进入重复状态。 原文:[[https://emacsredux.com/blog/2026/04/04/repeat-mode/][Repeat Mode: Stop Repeating Yourself]]
🔲 ☆

TIL: 用 read-extended-command-predicate 精简 M-x 候选列表

M-x 会列出所有命令,包括当前 buffer 用不上的。在 Python buffer 里看到 Org 命令、在 shell buffer 里看到 Magit 命令——都是噪音。 Emacs 28 提供了一个变量来解决这个问题: #+BEGIN_SRC emacs-lisp (setq read-extended-command-predicate #'command-completion-default-include-p) #+END_SRC 设置后,M-x 会隐藏那些声明了"我不适用于当前 major mode"的命令。没有声明的命令不受影响,照常显示。 * 过滤原理 ~command-completion-default-include-p~ 检查两个东西: - 命令的 ~interactive~ 形式中声明的 mode 归属 - 命令的 ~completion-predicate~ symbol property 两者都没声明的命令被视为通用命令,不会被过滤。 * 三个内置谓词 Emacs 提供了三个过滤谓词,从宽松到严格: | 谓词 | 行为 | |---+---| | ~command-completion-default-include-p~ | 排除声明了"属于其他 mode"的命令,其余全显示 | | ~command-completion-using-modes-and-keymaps-p~ | 只显示当前 mode 的命令 + 有键绑定的命令 + ~customize-*~ 命令 | | ~command-completion-using-modes-p~ | 只显示声明了当前 mode 的命令,最严格 | 建议从第一个开始,最安全。 * 包作者如何声明 mode 归属 在 ~interactive~ 形式中加上 mode 参数: #+BEGIN_SRC emacs-lisp (defun my-foo-command () "Do something useful in foo-mode." (interactive nil foo-mode) ...) #+END_SRC ~nil~ 是 interactive spec(无参数),~foo-mode~ 告诉 Emacs 这个命令只在 ~foo-mode~ 下有意义。适用于多个 mode 时,依次列出: #+BEGIN_SRC emacs-lisp (defun cider-eval-defun-at-point () "Evaluate the top-level form at point." (interactive nil clojure-mode clojure-ts-mode) ...) #+END_SRC * 注意事项 - 被过滤的命令并没有消失,只是不在补全列表里显示。输入完整命令名依然可以执行 - 内置命令大多已经声明了 mode 归属,第三方包的覆盖程度参差不齐 - 如果你用 Vertico,它的示例配置里已经有这行,只是被注释掉了,取消注释即可 原文:[[https://emacsredux.com/blog/2026/04/04/read-extended-command-predicate/][Declutter M-x with read-extended-command-predicate]]
🔲 ☆

为什么 Lisp 统治元编程

有人在 [[https://dev.to/redbar0n/meta-programming-and-macro-capabilities-of-various-languages-1hgd][DEV.to 上做了一张表]],按元编程和宏能力给 18 种语言打分(满分 19)。结果排名前六的语言里有五个是 Lisp 方言。 这不是巧合。 * 数据说话 从评分表里提取 Lisp 系和非 Lisp 系的平均分: | 类别 | 语言数 | 平均分 | |---+---+---| | Lisp 系(Racket、CL、Clojure、Arc、Coalton、Hackett、Scheme、Carp) | 8 | 14.9 | | 非 Lisp 系(Elixir、Julia、Nim、Rust、Scala、Ruby 等) | 10 | 10.0 | Lisp 系语言在四个评分维度中,有三个维度碾压非 Lisp 系:运行时元编程、宏特性、生态中的宏工具。唯一的"弱项"是编译期能力——但这不是因为 Lisp 做不到,而是很多 Lisp 方言根本不需要编译期魔法,它在运行时就能做到同等甚至更多的事。 * 根本原因:代码即数据 Lisp 的核心优势只有一个:程序的源代码本身就是数据结构。 一段 Elisp 代码 =(+ 1 2)= 在解析器看来就是一个列表,第一个元素是符号 =+= ,第二个是数字 =1= ,第三个是数字 =2= 。不需要额外的解析步骤,不需要 AST 转换,不需要语法糖展开——代码已经是数据了。 这意味着 Lisp 宏可以直接操作代码,就像操作列表一样自然: #+begin_src emacs-lisp ;; 定义一个宏:记录表达式执行耗时的宏 (defmacro with-timer (&rest body) `(let ((start (current-time))) ,@body (message "Elapsed: %.2f seconds" (float-time (time-subtract (current-time) start))))) ;; 使用时就像普通语法 (with-timer (sleep-for 1) (message "done")) #+end_src #+RESULTS: : Elapsed: 1.00 seconds 宏 =with-timer= 接收的是 *未求值的代码* ,不是值。它把代码当列表拼接,生成新的代码返回给求值器。整个过程不需要解析字符串、不需要构建 AST——因为输入本来就是列表。 * 非 Lisp 语言的困境 非 Lisp 语言要做同样的事,必须先解决一个问题:怎么把源代码变成可操作的数据? - *Rust* 的过程宏(procedural macro)需要先用 ~syn~ 库把 TokenStream 解析成 AST,操作完再用 ~quote~ 库生成 TokenStream。整个过程等价于"先解析,再操作,再序列化" - *C++* 的模板元编程在类型系统里做图灵完备的计算,代价是编译错误信息能塞满整个终端 - *Python* 的装饰器只能包装函数,不能创造新的语法结构。想操纵 AST?得自己写 ~import ast~ 解析器 - *Ruby* 是个有趣的反例。它以元编程著称—— ~method_missing~ 、 ~define_method~ 、 ~class_eval~ 让 Rails 之类的框架写出了极其优雅的 DSL。但它在评分表里只有 8 分(和 C++ 的 7 分差不多)。为什么?因为 Ruby 的元编程全部是运行时的:开放类、动态方法定义、幽灵方法。它不能创造新的语法结构,不能在编译期做代码变换。Ruby 的 DSL 能力来自语言的动态性,而非宏系统。原文章也指出:Ruby 的元编程评分和 C++ 接近,但没有人会认为 C++ 比 Ruby 更适合元编程——这说明纯运行时元编程的能力被评分表低估了,但也说明运行时元编程和编译期宏是两个不同维度 #+begin_src python ## Python 的装饰器:只能包装,不能创造新语法 import time def with_timer(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) print(f"Elapsed: {time.time() - start:.2f}s") return result return wrapper @with_timer def my_task(): time.sleep(1) print("done") #+end_src 装饰器能做到"在函数调用前后加逻辑",但做不到"定义一种全新的控制流结构"。宏可以。 * Emacs Lisp 中的实际威力 Emacs Lisp 可能是日常使用中最频繁接触 Lisp 宏的机会。Emacs 的很多核心功能就是宏实现的: - ~dolist~ 和 ~dotimes~ 都是宏,不是内置特殊形式。它们接收未求值的代码,展开成 ~while~ 循环。如果你需要一种新的迭代方式,可以自己写一个。 - ~cl-defstruct~ 内部用宏生成构造函数、访问器、谓词函数。一个宏调用生成几十行代码。 - ~use-package~ 整个就是一个宏驱动的 DSL。 ~:init~ 、 ~:config~ 、 ~:bind~ 这些"关键字"不是语言特性,是宏通过模式匹配识别的符号。 #+begin_src emacs-lisp ;; use-package 的 :bind 关键字不是语言内置的语法 ;; 是宏在展开时识别 :bind 符号并生成 keymap 代码 (use-package magit :bind (("C-x g" . magit-status))) #+end_src 这就是 Lisp 宏的精髓:你可以用宏 *发明* 语言本身没有的语法。 ~use-package~ 的作者不需要修改 Elisp 解释器,只需要写一个宏。 * 为什么不是所有语言都走这条路 如果宏这么好,为什么大多数语言不采用 Lisp 风格的宏? 答案和评分表里 C++ 和 Python 的低分一样:它们的语法不是数据。 Lisp 的 S 表达式看起来"全是括号",但这恰恰是它的元编程优势的来源——统一的语法结构意味着代码天然就是可操作的数据树。Python 的缩进、C++ 的花括号、Rust 的尖括号,每一种语法糖都在增加"从代码到数据"的转换成本。 选择了一种"好看"的语法,就放弃了把代码当数据的能力。这不是审美问题,是结构性的取舍。 * 小结 评分表的结论可以用一句话概括:元编程能力的上限取决于语言语法的数据化程度。Lisp 的 S 表达式是最彻底的"代码即数据"——没有语法糖的干扰,宏可以直接操作程序结构,而不需要先解析它。这就是为什么 18 种语言里 8 种是 Lisp 方言,而且它们占据了排行榜的前列。
🔲 ☆

用 .pdbrc 自定义 Python 调试器

今天才知道 Python 调试器 PDB 可以通过 =~/.pdbrc= 文件自定义——在文件里定义 alias,就能在调试时直接调用。 =.pdbrc= 的格式要求如下: - 文件位置: =~/.pdbrc= (全局)或项目目录下的 =.pdbrc= (局部) - 它 *不是* Python 代码,是 PDB 自己的命令格式,每行就是一个 PDB 命令 - alias 语法: =alias 名称 命令内容= ,用 =%1= 、 =%2= 等引用参数 - 多条命令用 =;;= 连接,但 *必须写在一行里* - alias 中 =import= 引入的变量会泄漏到当前作用域,需要手动 =del= 清理 Trey Hunner 在他的[[https://treyhunner.com/2026/04/customizing-pdb-with-pdbrc/][文章]]中分享了一套实用的 alias 配置: #+BEGIN_SRC python # dir obj: 打印非双下划线的属性和方法 alias dir print(*(f"%1.{n} = {v!r}" for n, v in __import__('inspect').getmembers(%1) if not n.startswith("__")), sep="\n") # attrs obj: 只打印非方法的数据属性 alias attrs import inspect as __i ;; print(*(f"%1.{n} = {v!r}" for n, v in __i.getmembers(%1, lambda v: not __i.isroutine(v)) if not n.startswith("__")), sep="\n") ;; del __i # vars obj: 只打印实例变量(要求对象有 __dict__) alias vars print(*(f"%1.{k} = {v!r}" for k, v in vars(%1).items()), sep="\n") # src obj: 打印类/函数的源文件、行号和代码 alias src import inspect as __i;; print(f"{__i.getsourcefile(%1)} on line {__i.getsourcelines(%1)[1]}:\n{''.join(__i.getsource(%1))}") ;; del __i # loc: 打印当前帧的局部变量 alias loc print(*(f"{name} = {value!r}" for name, value in vars().items() if not name.startswith("__")), sep="\n") #+END_SRC 这 5 个 alias 的效果: - =dir obj= 比 =dir(obj)= 输出更友好,逐行以 ~obj.attr = repr~ 格式打印 - =attrs obj= 过滤掉方法,只留数据属性 - =vars obj= 只显示实例变量( =__dict__= 中的) - =src obj.method= 直接查看方法的源码位置和内容 - =loc= 列出当前作用域所有局部变量
🔲 ☆

降低程序内存的五个原则

Michael Kennedy 在 [[https://mkennedy.codes/posts/cutting-python-web-app-memory-over-31-percent/][Cutting Python Web App Memory Over 31%]] 中把两个 Python Web 应用的内存从 1988MB 砍到 472MB。他的具体做法是五个手段:async worker、Raw+DC 数据库模式、子进程隔离 import、重型库延迟导入、缓存搬到磁盘。 剥掉 Python 的外壳,这五个手段背后是五个通用的内存优化原则。 * 原则一:减少代码副本数 #+BEGIN_QUOTE Talk Python Training 从 2 个 worker(1280MB)切到 1 个 async worker,省了 542MB。 #+END_QUOTE 每个进程都要加载完整的应用代码和第三方依赖。两个 worker 意味着两份副本。如果能让一个进程通过并发(而非并行)处理所有请求,代码只需要加载一次。 这不是 Python 独有的思路: - *Nginx* 默认启用多个 worker 进程,但如果机器内存紧张,配成单 worker + 事件驱动也能跑 - *Java* 从 thread-per-request(每个线程栈占几百 KB 到 1MB)转向 reactive 模型(Netty、WebFlux),逻辑类似——减少并发单元的资源开销 - *Go* 的 goroutine 天然共享地址空间,初始栈只有 2-8KB,不需要为每个并发任务复制进程 - *Emacs* 的 daemon 模式:一个 ~emacs --daemon~ 进程加载所有配置和包,多个 ~emacsclient~ 共享同一个实例,不需要每个终端窗口都启动一个完整的 Emacs 本质:并发不等于并行。用并发模型替代多进程并行,消除代码和依赖的重复加载。 * 原则二:用最轻的工具 #+BEGIN_QUOTE 把 MongoEngine ODM 替换为 raw query + 带 ~__slots__~ 的 dataclass,每个 worker 省 100MB,吞吐量还翻倍。 #+END_QUOTE ODM、ORM 这些重型抽象加载了大量你用不到的功能。换成直接写查询 + 轻量数据结构,内存立减。 跨语言的对照: - *Java* :Hibernate 是出了名的内存大户。很多高并发场景下,项目改用 MyBatis(半自动 ORM)或直接 JDBC + POJO,内存占用显著下降 - *Python* 的 ~__slots__~ 本质是告诉解释器"这个类不需要动态属性字典",每个实例省掉一个 ~dict~ 的开销。类似思路在 *Go* 里是 struct(没有继承开销),在 *C* 里是固定布局的结构体 - *Ruby on Rails* 的 Active Record 也是类似的 ORM 开销问题,纯 SQL 查询 + Plain Ruby Objects 是对应的轻量替代 - *Emacs* 的包选择:用 ~vertico~ + ~consult~ (基于内置的 ~completing-read~ )替代 ~ivy~ 或 ~helm~ (各自带一套完整的补全框架),功能相当但内存 footprint 小得多 本质:你引入的每个抽象层都有内存成本。如果一个功能只需要查询和映射,就不需要完整的对象关系映射器。 * 原则三:重操作隔离到短命进程 #+BEGIN_QUOTE 搜索索引服务从 708MB 暴降到 22MB——把索引逻辑移到 subprocess,跑 30 秒就退出,内存全部释放。 #+END_QUOTE 长时间运行的主进程应该尽量轻。偶尔才执行的重量级任务放到子进程中,干完就退出。操作系统会回收整个进程的内存,不需要你手动管理。 这个模式无处不在: - *CI/CD* :构建任务在独立容器或 VM 中运行,完成后销毁 - *Kubernetes* 的 Job 资源:跑完自动清理 - *PostgreSQL* 的并行查询:fork 子进程处理,完成后回收 - *Elasticsearch* 的 merge 操作:在独立线程中合并 segment,避免影响主查询路径 - *Claude Code* 的子代理模式:主代理把代码搜索、审查等重操作委托给独立子代理执行。子代理有自己的上下文窗口,可以自由探索代码库而不会撑大主代理的上下文。任务完成后子代理的上下文全部释放,主代理只保留结果摘要 - *Emacs* 的 ~async.el~ 和 ~emacs --batch~ :编译包( ~async-bytecomp~ )、执行耗时计算都放在子进程中完成,主 Emacs 不受影响 本质:进程是操作系统提供的最干净的内存隔离和回收机制。比手动释放对象、触发 GC 更可靠。 * 原则四:延迟加载——不用就不付钱 #+BEGIN_QUOTE ~import boto3~ 吃 25MB, ~import pandas~ 吃 44MB, ~import matplotlib~ 吃 17MB。把它们从文件顶部移到函数内部,只有调用时才加载。 #+END_QUOTE 延迟加载(lazy loading)是最朴素的内存优化:不为还没用到的东西分配资源。 各语言都有自己的延迟加载手段: - *Go* 的 ~sync.Once~ :确保初始化只执行一次,且只在首次访问时触发 - *Java* 的类加载器:类在首次使用时才加载(虽然现代框架经常打破这个假设) - *C++* 的延迟计算(lazy evaluation):表达式只在结果被需要时才求值 - *Python 3.15* 的 PEP 810(Explicit lazy imports)将引入原生 lazy import,到时候不用手动把 import 塞进函数 - *Emacs* 的 ~autoload~ 是最成熟的延迟加载机制之一。通过 ~;;;###autoload~ 注释标记函数,Emacs 启动时只加载一个 autoload 文件(函数名 → 文件路径的映射),直到真正调用该函数时才加载整个包。~use-package~ 的 ~:defer t~ 和 ~:commands~ 就是对 ~autoload~ 的封装。这是 Emacs 启动优化的核心手段 本质:资源的获取时机应该是"第一次需要的时候",而不是"程序启动的时候"。启动时加载所有依赖是最简单但不经济的做法。 * 原则五:能放磁盘的就不放内存 #+BEGIN_QUOTE 把 markdown 缓存从内存搬到 diskcache,用少量 I/O 延迟换取内存空间。 #+END_QUOTE 内存是最贵的存储层。如果数据的访问频率不高、延迟要求不苛刻,磁盘缓存完全够用。 经典的存储分层思想: - *SQLite* 被很多项目当作"磁盘上的缓存"使用——比纯文件灵活,比 Redis 轻 - *mmap* :让操作系统决定哪些页留在内存、哪些换出到磁盘,不需要自己管理缓存淘汰 - *Redis* 本身也支持磁盘持久化,而且很多人用 Redis 的 AOF/RDB 做的就是"内存放热数据、磁盘放全量数据" - 浏览器的 HTTP 缓存:内存缓存(disk cache 和 memory cache 分层) 本质:不是所有数据都需要一直待在内存里。根据访问频率和延迟容忍度选择存储层,是系统设计的基本功。 * 一句话总结 | 原则 | 一句话 | |---+---| | 减少代码副本数 | 并发不等于并行,用单进程并发替代多进程并行 | | 用最轻的工具 | 引入的每个抽象层都有内存成本 | | 重操作隔离到短命进程 | 进程是操作系统提供的最干净的内存回收机制 | | 延迟加载 | 不用就不付钱 | | 能放磁盘就不放内存 | 根据访问频率和延迟容忍度选存储层 |
🔲 ☆

Python 潮流周刊#147:Python 和 Ruby 的 JIT 故事

本周刊由 Python猫 出品,精心筛选国内外的 400+ 信息源,为你挑选最值得分享的文章、教程、开源项目、软件工具、播客和视频、热门话题等内容。愿景:帮助所有读者精进 Python 技术,并增长职业和副业的收入。

温馨提示: 在微信关注 Python猫,发送数字“9”,即可领取 9 折优惠码,订阅专栏可享 15 元优惠。

去专栏阅读全文:全文链接

分享了 12 篇文章,12 个开源项目

以下是本期标题摘要:

🦄文章&教程

① 同一道题,两份答卷:Python 和 Ruby 的 JIT 故事

② Python 不只是写代码,它背后还有一套很厉害的「政治系统」

③ 为什么我们还没全面用上 uv?

④ 将业务逻辑与 Django ORM 解耦

⑤ Pandas 与 Polars 对决:千万级数据量基准测试

⑥ 用 Python 编写的 Python 解释器

⑦ PEP 830:为异常和回溯添加时间戳--–PEP 830:Add timestamps to exceptions and tracebacks

⑧ PEP 831:全面引入帧指针以实现 Python 的系统级可观测性

⑨ PEP 832:虚拟环境发现机制

⑩ 追踪 Python 中的 Celery 任务失败

⑪ Python 类型检查器:速度与内存对比

⑫ 在 Python 3.14 和 3.15 中回退增量垃圾回收机制

🐿️项目&资源

① GenericAgent:自进化的 AI 智能体

② CowAgent:基于大模型的超级 AI 智能体助理

③ opensre:开发专属 AI SRE 智能体

④ ppt-master:AI 生成原生可编辑的 PPTX

⑤ parameter-golf:极限模型压缩挑战赛

⑥ textgen:本地大模型交互界面

⑦ Bindu:将任意 AI 智能体转化为微服务

⑧ pypi-security-best-practices:PyPI 包管理器安全最佳实践

⑨ great-docs:轻松构建 Python 包文档网站

⑩ omlx:Apple Silicon 专属大模型推理服务器

⑪ python-docx-template:使用 docx 作为 jinja2 模板

⑫ qodo-cover:AI 驱动的自动化测试生成工具

周刊实行付费订阅制,年费 148 元,平均每天 4 毛钱,为你精准筛选高质量技术内容。在信息洪流中为你淘金,助力技术视野拓展和职业发展,欢迎订阅:https://xiaobot.net/p/python_weekly

订阅后,可免费查看 第 147 期周刊的全文:https://xiaobot.net/post/742a40be-68a4-4d3e-8a87-72233313d8cd

Python 潮流周刊第3季总结,附电子书下载

Python 潮流周刊第二季完结(31~60)

Python 潮流周刊第一季精华合集(1~30)

微信关注 Python猫https://img.pythoncat.top/python_cat.jpg

🌸关联阅读

上一期:Python 潮流周刊#146:CPython 引入 Rust 的进展

❌