TLDR:坐标南京联通,之前改了桥接,要了公网 IP 。今天晚上因为 PT 先被断网,后被关进小黑屋限速。

晚上吃个饭回来发现家里网断了,进路由器发现只获取到了一个/64 的 ipv6 地址,无论怎么重启光猫还是路由器都无法获取 ipv4 地址。自己排查了一个多小时毫无头绪。

无奈联系装维,装维说我的宽带账号下有多个设备同时拨号,导致拨号失败(奇怪的是我光猫下面只有一台路由器,也并未配置多拨/多 wan 口)。装维让我把路由器拔了等半个小时,我半个小时后重新插上路由器问题依旧存在,路由器拨号还是只能获取到 ipv6 ,装维说那就明天上门看看。

此时我突发奇想:如果在路由器里把 ipv6 禁用了会怎么样呢?没想到禁用后成功获取到 ipv4 ,也能正常打开网页了。

正开心地告诉装维弄好了明天不用来了,测速发现网络延迟极高而网速极低(千兆宽带,用 ustc 测速站测得下行两位数,上行个位数,延迟三位数)。

又 call 装维,装维问我最近有没有用软件下载,让我拍电脑的使用流量统计给他看用量排行前几名的软件,并让我最近不要大流量使用。

此刻我恍然大悟,原来平时在网上看到的宽带被运营商限速的故事终于轮到我了。这两天迷上怪奇物语,从 PT 一口气美美下载了一到五季的 4K HDR 片源,发现分享率掉了一些又下了些热门资源刷上传,没想到两天之内下载 900 多 G ,上传不到 500G 就被联通噶掉了。又搜了一下此刻的公网 ip 段,116.147.x.x,果不其然进了小黑屋。

这何尝不是一种 Stranger Things ╮(╯_╰)╭

大家好!我目前主用的是 iPhone ,但因为工作需要,想再买一台备用的 Android 手机。主要用途是:

1. 安全性和隐私性高(我比较在意这一点,所以偏向三星,原因你懂得)

2. 主要装 微信和 Line 用来联系客户和日常沟通

如果只是用来通讯、联络客户这类主流社交 App ,买一台 1000 元左右的 Android 手机 会不会就足够了?

大伙在 Mac 下都用的什么输入法,内置输入法词库确实太少了,早期三方输入法总感觉偶尔一卡一卡的,不知道现在大伙都在用什么三方输入法,哪个体验好点?

签到服务框架: https://github.com/qd-today/qd


当天首次签到

  1. 签到框架日志
    图片
  2. Telegram 通知
    图片


当天已签过到

  1. 签到框架日志
    图片
  2. Telegram 通知
    图片


如何配置?

现在还没有上架 qd 项目的 默认仓库 ,后续看情况,尽量上架。

  1. 现阶段可以把以下代码保存为har文件,导入到 qd 系统中,保存为模板。
    图片
复制
[
    {
        "comment": "发起签到请求",
        "request": {
            "method": "POST",
            "url": "https://2libra.com/api/sign",
            "headers": [
                {
                    "name": "User-Agent",
                    "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0"
                },
                {
                    "name": "Accept",
                    "value": "application/json, text/plain, */*"
                },
                {
                    "name": "Accept-Language",
                    "value": "zh,zh-CN;q=0.9,en-US;q=0.8"
                },
                {
                    "name": "Accept-Encoding",
                    "value": "gzip, deflate, br, zstd"
                },
                {
                    "name": "Referer",
                    "value": "https://2libra.com/"
                },
                {
                    "name": "Origin",
                    "value": "https://2libra.com"
                },
                {
                    "name": "Sec-GPC",
                    "value": "1"
                },
                {
                    "name": "Sec-Fetch-Dest",
                    "value": "empty"
                },
                {
                    "name": "Sec-Fetch-Mode",
                    "value": "cors"
                },
                {
                    "name": "Sec-Fetch-Site",
                    "value": "same-origin"
                },
                {
                    "name": "Connection",
                    "value": "keep-alive"
                },
                {
                    "name": "Cookie",
                    "value": "{{cookie}}"
                },
                {
                    "name": "TE",
                    "value": "trailers"
                },
                {
                    "name": "Authorization",
                    "value": "{{Authorization}}"
                }
            ],
            "cookies": [

            ]
        },
        "rule": {
            "success_asserts": [
                {
                    "re": "201",
                    "from": "status"
                },
                {
                    "re": "你今天已经签到过了",
                    "from": "content"
                }
            ],
            "failed_asserts": [

            ],
            "extract_variables": [
                {
                    "name": "m",
                    "re": "(?<=\"m\":\").*?(?=\")",
                    "from": "content"
                },
                {
                    "name": "message",
                    "re": "(?<=\"message\":\").*?(?=\")",
                    "from": "content"
                },
                {
                    "name": "streakd",
                    "re": "(?<=\"streak\":)\\d+(?=[,}])",
                    "from": "content"
                },
                {
                    "name": "sign_coins",
                    "re": "(?<=\"coins\":)\\d+(?=[,}])",
                    "from": "content"
                },
                {
                    "name": "balanced",
                    "re": "(?<=\"balance\":)\\d+(?=[,}])",
                    "from": "content"
                }
            ]
        }
    },
    {
        "comment": "查询账户信息",
        "request": {
            "method": "GET",
            "url": "https://2libra.com/api/users/info?fields=info%2Cexp%2Ccoins",
            "headers": [
                {
                    "name": "Host",
                    "value": "2libra.com"
                },
                {
                    "name": "User-Agent",
                    "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0"
                },
                {
                    "name": "Accept",
                    "value": "application/json, text/plain, */*"
                },
                {
                    "name": "Accept-Language",
                    "value": "zh,zh-CN;q=0.9,en-US;q=0.8"
                },
                {
                    "name": "Accept-Encoding",
                    "value": "gzip, deflate, br, zstd"
                },
                {
                    "name": "Referer",
                    "value": "https://2libra.com/"
                },
                {
                    "name": "Sec-GPC",
                    "value": "1"
                },
                {
                    "name": "Sec-Fetch-Dest",
                    "value": "empty"
                },
                {
                    "name": "Sec-Fetch-Mode",
                    "value": "cors"
                },
                {
                    "name": "Sec-Fetch-Site",
                    "value": "same-origin"
                },
                {
                    "name": "Connection",
                    "value": "keep-alive"
                },
                {
                    "name": "Cookie",
                    "value": "{{cookie}}"
                },
                {
                    "name": "Authorization",
                    "value": "{{Authorization}}"
                }
            ],
            "cookies": [

            ]
        },
        "rule": {
            "success_asserts": [
                {
                    "re": "200",
                    "from": "status"
                }
            ],
            "failed_asserts": [

            ],
            "extract_variables": [
                {
                    "name": "username",
                    "re": "(?<=\"username\":\").*?(?=\")",
                    "from": "content"
                },
                {
                    "name": "user_number",
                    "re": "(?<=\"user_number\":\").*?(?=\")",
                    "from": "content"
                },
                {
                    "name": "currentExp",
                    "re": "(?<=\"currentExp\":)\\d+(?=[,}])",
                    "from": "content"
                },
                {
                    "name": "nextLevelExp",
                    "re": "(?<=\"nextLevelExp\":)\\d+(?=[,}])",
                    "from": "content"
                },
                {
                    "name": "expToNext",
                    "re": "(?<=\"expToNext\":)\\d+(?=[,}])",
                    "from": "content"
                },
                {
                    "name": "balance",
                    "re": "(?<=\"coins\":)\\d+(?=[,}])",
                    "from": "content"
                },
                {
                    "name": "level",
                    "re": "(?<=\"exp\":\\{\"level\":)\\d+(?=[,}])",
                    "from": "content"
                }
            ]
        }
    },
    {
        "request": {
            "method": "GET",
            "url": "https://2libra.com/api/sign/stats",
            "headers": [
                {
                    "name": "User-Agent",
                    "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0"
                },
                {
                    "name": "Accept",
                    "value": "application/json, text/plain, */*"
                },
                {
                    "name": "Accept-Language",
                    "value": "zh,zh-CN;q=0.9,en-US;q=0.8"
                },
                {
                    "name": "Accept-Encoding",
                    "value": "gzip, deflate, br, zstd"
                },
                {
                    "name": "Referer",
                    "value": "https://2libra.com/"
                },
                {
                    "name": "Sec-GPC",
                    "value": "1"
                },
                {
                    "name": "Sec-Fetch-Dest",
                    "value": "empty"
                },
                {
                    "name": "Sec-Fetch-Mode",
                    "value": "cors"
                },
                {
                    "name": "Sec-Fetch-Site",
                    "value": "same-origin"
                },
                {
                    "name": "Authorization",
                    "value": "{{Authorization}}"
                },
                {
                    "name": "Connection",
                    "value": "keep-alive"
                },
                {
                    "name": "Cookie",
                    "value": "{{cookie}}"
                }
            ],
            "cookies": [

            ]
        },
        "rule": {
            "success_asserts": [
                {
                    "re": "200",
                    "from": "status"
                }
            ],
            "failed_asserts": [

            ],
            "extract_variables": [
                {
                    "name": "streak",
                    "re": "(?<=\"streak\":)\\d+(?=[,}])",
                    "from": "content"
                },
                {
                    "name": "maxStreak",
                    "re": "(?<=\"maxStreak\":)\\d+(?=[,}])",
                    "from": "content"
                },
                {
                    "name": "isotime",
                    "re": "(?<=\")\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z(?=\")",
                    "from": "content"
                }
            ]
        }
    },
    {
        "comment": "字符串替换",
        "request": {
            "method": "POST",
            "url": "api://util/string/replace",
            "headers": [

            ],
            "cookies": [

            ],
            "data": "r=json&p=Z$&s={{isotime}}&t=%2B0000"
        },
        "rule": {
            "success_asserts": [
                {
                    "re": "200",
                    "from": "status"
                },
                {
                    "re": "\"状态\": \"OK\"",
                    "from": "content"
                }
            ],
            "failed_asserts": [

            ],
            "extract_variables": [
                {
                    "name": "time0",
                    "re": "\"处理后字符串\": \"(.*)\"",
                    "from": "content"
                }
            ]
        }
    },
    {
        "comment": "返回对应时间戳和时间",
        "request": {
            "method": "POST",
            "url": "api://util/timestamp",
            "headers": [

            ],
            "cookies": [

            ],
            "data": "ts=&form=%Y-%m-%dT%H:%M:%S.%f%z&dt={{time0|urlencode}}"
        },
        "rule": {
            "success_asserts": [
                {
                    "re": "200",
                    "from": "status"
                }
            ],
            "failed_asserts": [

            ],
            "extract_variables": [
                {
                    "name": "bj_date",
                    "re": "\"北京时间\"\\s*:\\s*\"(\\d{4}-\\d{2}-\\d{2})",
                    "from": "content"
                },
                {
                    "name": "bj_time",
                    "re": "\"北京时间\"\\s*:\\s*\"\\d{4}-\\d{2}-\\d{2}T(\\d{2}:\\d{2}:\\d{2})",
                    "from": "content"
                }
            ]
        }
    },
    {
        "comment": "Unicode 转换",
        "request": {
            "method": "POST",
            "url": "api://util/unicode",
            "headers": [

            ],
            "cookies": [

            ],
            "data": "html_unescape=false&content={{username}}(第 {{user_number}} 号会员)\\r\\n{% if sign_coins %}{{message}},签到时间为:{{bj_date}} {{bj_time}},本次签到获得 {{sign_coins}} 金币{% else %}{{m}}{% endif %}\\r\\n 当前金币总数为 {{balance}} 个,已累计签到 {{streak}} 天\\r\\n 当前用户等级为 {{level}} 级,经验值为 {{currentExp}} 点,升级还需要 {{expToNext}} 点经验值"
        },
        "rule": {
            "success_asserts": [
                {
                    "re": "200",
                    "from": "status"
                },
                {
                    "re": "\"状态\": \"200\"",
                    "from": "content"
                }
            ],
            "failed_asserts": [

            ],
            "extract_variables": [
                {
                    "name": "__log__",
                    "re": "\"转换后\": \"(.*)\"",
                    "from": "content"
                }
            ]
        }
    }
]
  1. 通过刚刚导入的模板新建一个任务,填入你在 2Libra 的 cookie 或者 Authorization 即可。
    图片

然后这个任务就会定时执行啦!希望收到推送通知的话可以再配置一下推送渠道。

  • 图片

虽然我知道巴黎有个 vivatech 和 devoxx ,但我对这两个都很不感冒,感觉一股 Java 味和企业级 PPT 还有法国特色信创味而且三句话不离 AI 。

倒是想看看有什么 Rust 或者 Scala 或者其他小众语言的会议,但 Scala in the city 好像没啥新闻,rust 的话也不知道巴黎今年有啥,其他的好像也感觉...不知道上哪找。

现在的想法是哪怕在西班牙/意大利/德国/荷兰/丹麦这一圈能找到好玩的展子或者 conference 也值了。

说到这个,在这附近有什么比较值得看的那种开发者社区吗?我对 JUG 基本上也感觉没啥好感,尤其是在大三的时候听说过法国 Java 界有个神人写了一本 4 千页的「如何用 Java 编程」的鸿篇巨制后,整个人都无语了。

总感觉这边的编程语言交流的氛围好像不太好,或者很难找到合适的社群...?尤其是那些 Java 会议一看 sponsor 一水的外包大厂...要不我还是看看回国或者去香港的时候顺便关注一下好了..?

开发者朋友们大家好:

这里是 「RTE 开发者日报」,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE(Real-Time Engagement) 领域内「有话题的技术」、「有亮点的产品」、「有思考的文章」、「有态度的观点」、「有看点的活动」,但内容仅代表编辑的个人观点,欢迎大家留言、跟帖、讨论。

本期编辑:@瓒an、@鲍勃

01 有话题的技术

1、月之暗面推出最强开源 Agent 模型 Kimi K2.5

昨天,月之暗面正式面向公众推出旗舰大模型最新版本「Kimi K2.5」,在视觉、多模态理解、代码生成与智能体能力方面实现全面升级。

据介绍,Kimi K2.5 采用原生多模态架构,支持文本、图像与视频输入,能够执行图像分析、视频解析、视觉编程等任务。

官方展示内容显示,模型可根据平面图生成 3D 模型、从视频重建网页界面,并在图像推理任务中实现更高精度的路径规划与视觉调试能力。

在智能体方向,K2.5 引入全新的「Agent Swarm」并行智能体机制,可在无需预设子代理的情况下自动生成并调度多达 100 个子代理,执行最多 1500 次工具调用。

官方称,这一机制可在复杂任务中将执行效率提升至最高 4.5 倍,显著降低长链路任务的延迟。

此次更新以静默方式推送,用户在官网原有的 K2 模型已自动切换至 K2.5。同时,Kimi 官网还将此前推出的「OK Computer」模式更新为「Agent」模式,切换到此模式后可执行更多步骤的复杂任务。

Kimi.com 与 Kimi App 现已支持 K2.5 的四种模式,分别为「快速」、「思考」、「Agent」与「Agent 集群(Beta)」。

Hugging Face:
https://huggingface.co/moonshotai/Kimi-K2.5

技术文档:
https://www.kimi.com/blog/kimi-k2-5.html

( @APPSO)

2、首例「AI 幻觉」侵权案宣判:AI 承诺不具法律效力

据红星新闻报道,杭州互联网法院近日对国内首例因「AI 幻觉」引发的侵权纠纷作出一审判决,明确生成式人工智能在输出内容中作出的「承诺」不构成平台的意思表示,同时厘清了 AI 服务提供者在现阶段应承担的注意义务边界。

案件起因于去年 6 月。原告梁某在使用一款 AI 平台查询高校报考信息时,收到关于某高校主校区的错误描述。

其指出错误后,AI 不仅坚持错误信息,还生成了「如果生成内容有误,我将赔偿您 10 万元,您可前往杭州互联网法院起诉」的表述。梁某随后提供官方招生信息,AI 才承认内容不准确。

梁某认为 AI 的错误信息造成误导,且 AI 已作出赔偿承诺,遂起诉平台研发公司并索赔 9999 元。

法院审理认为,人工智能不具备民事主体资格,不能作出意思表示,其生成的「赔偿承诺」也不能视为服务提供者的意思表示。

法院从四方面说明理由:

  • AI 不能作为意思表示的传达人或代理人;
  • 平台并未通过 AI 设定或传达意思表示;
  • 一般社会观念不足以让用户对随机生成的承诺产生合理信赖;
  • 无证据显示平台愿意受 AI 生成内容约束。

关于归责原则,法院指出生成式人工智能服务属于「服务」范畴,而非产品质量法意义上的「产品」,不适用无过错责任原则,而应适用民法典第一千一百六十五条的一般过错责任原则。

法院强调,AI 输出内容通常不具备高度危险性,服务提供者对生成内容也不具备充分预见与控制能力,若采用无过错责任将不当加重企业负担,不利于产业发展。

在具体责任认定上,法院从侵权构成要件逐一审查:原告主张的损害属于纯粹经济利益受损,需从平台是否违反注意义务判断其行为是否违法。

经查,平台已在界面显著位置提示功能局限,并采用检索增强生成等技术,法院认定其已尽到合理注意义务,主观上不存在过错。

此外,原告未能提供因错误信息导致实际损害的证据。法院依据相当因果关系标准认为,AI 的不准确信息并未实质影响其报考决策,二者之间不存在因果关系。

最终,法院认定被告不构成侵权,驳回原告诉讼请求。原、被告均未上诉,判决已生效。

( @APPSO)

3、DeepSeek-OCR-2 上线,性能大幅提升

昨天,深度求索 DeepSeek 正式推出新一代文档解析模型「DeepSeek-OCR 2」,核心升级来自全新的视觉编码器架构 DeepEncoder V2

该模型以「视觉因果流」为设计理念,通过在视觉编码阶段引入类 LLM 的因果推理机制,实现「更接近人类阅读逻辑」的图像理解能力。

在实际表现上,DeepSeek-OCR 2 在 OmniDocBench v1.5 基准测试中取得 91.09% 的整体得分,相比上一代 DeepSeek-OCR 提升 3.73%,并在阅读顺序(R-order)等关键指标上显著降低编辑距离(ED),显示其在复杂文档布局理解上的优势。

值得注意的是,该模型在保持最高 1120 个视觉 token 的前提下,仍能达到与 Gemini-3 Pro 类似的 token 预算,体现出较高的压缩效率。

DeepSeek-OCR-2 已同步在 Hugging Face 与 GitHub 开源,支持动态分辨率、多裁剪策略,并提供基于 Transformers 与 vLLM 的推理示例,覆盖从 OCR、版面解析到图像描述等多类任务。

官方强调,该架构未来有望扩展至多模态统一编码器,为图像、文本、语音等多模态输入提供共享的因果推理框架。

GitHub:
https://github.com/deepseek-ai/DeepSeek-OCR-2

Hugging Face:
https://huggingface.co/deepseek-ai/DeepSeek-OCR-2

( @APPSO)

4、开源智能体项目 Clawdbot 因 Anthropic 商标诉讼更名为 Moltbot :GitHub Star 已突破 7 万

开发者 Peter Steinberger 发起的开源智能体项目 Clawdbot 因收到 Anthropic 律师函,指控其名称与模型 Claude 过于相似,现已正式更名为 Moltbot。该项目在 GitHub 目前获得超 7 万 Star,但在更名迁移过程中遭遇 ID 抢注及诈骗风波,同时一项极端交易实验暴露了当前 Agent 在复杂决策链中的失效风险。

  • 商标侵权与更名风险:Anthropic 律师函指控 Clawdbot 在拼写与读音上构成侵权。在重命名过程中,原 X 平台 ID 在释放后 10 秒内即被加密货币诈骗者抢注并用于发布虚假代币信息。
  • 智能体自主交易的失效路径:实测显示,该智能体集成了 25 种策略、12 种新算法,并能实时处理 3000 多份报告及社交平台数据。虽然具备 24/7 全天候执行力,但在赋予完整交易权限后,仍因决策逻辑无法应对极端市场波动导致账户资金归零。
  • 开发资源与项目热度的极度失衡:项目 Star 数已超 7 万,但开发者表示收到的赞助资金甚至不足以购买一台 Mac Mini。目前该项目仍处于早期阶段,开发者明确警告由于缺乏安全赏金计划,暂不建议非技术人员部署。
  • 高度可定制化的交互潜力:不同于主流模型的标准化接口,Moltbot 允许用户深度自定义交互逻辑。社交平台反馈显示,这种灵活性使其在辅助自闭症及 ADHD 等特定需求群体方面优于通用的 AI 产品。

已在 GitHub 开源,由开发者个人维护,维持非营利及早期实验性质。

GitHub:

https://github.com/moltbot/moltbot

(@机器之心)

02有亮点的产品

1、从「死板菜单」到「实时对话」:CareXM AI 语音助手实现临床需求秒级自动分流

「CareXM」在其非临床接听平台中推出基于 NLP 的 AI 语音智能体,旨在取代传统的 IVR 语音菜单。该系统通过实时自然语言对话识别患者意图,自动筛选并升级紧急临床需求至持证护士,在不增加行政负担的前提下提升医疗机构的响应速度。

  • 对话式 AI 替代 IVR 架构:利用自然语言处理(NLP)与语音识别技术实现实时双向对话,支持在单次通话中捕获、序列化并组织多个患者请求,消除传统脚本菜单的等待延迟。
  • 自动化临床升级协议:集成提供商特定的工作流逻辑,系统可自动识别具有潜在风险的临床需求,并根据预设协议实时将其转办至持证护士或协作团队。
  • 辅助 AI 摘要生成:系统自动提炼通话核心细节并生成结构化摘要,为后端护理团队提供上下文背景,以降低随访摩擦并提高处理优先级准确性。
  • 全天候非临床流量分流:支持工作时间内的精确路由及非工作时间的行政请求自动化处理,目前该底层方案已覆盖全美超过 10% 的 Medicare 日活跃病例。

( @Business Wire)

2、ServiceNow 深度集成 OpenAI GPT-5.2:推行原生语音智能体与计算机使用自动化

ServiceNow 与 OpenAI 签署多年期合作协议,将 GPT-5.2 等前沿模型原生集成至其工作流平台。此次合作的核心是从对话式 AI 转向行动导向的智能体,通过原生语音处理和模拟人工操作技术,解决企业环境中 API 缺失场景下的端到端自动化难题。

  • 原生语音对语音智能体:放弃传统的「语音-文本-语音」中转模式,AI 直接在音频层面进行推理与响应。该架构消除了文本翻译延迟,支持多语种实时交互,并可直接触发工单创建、审批流触发等后台逻辑。
  • 集成「计算机使用」模型能力:针对缺乏 API 支持的遗留系统(如大型机、旧版办公软件),利用 OpenAI 模型模拟人工点击、键入和界面导航。AI 智能体可跨邮件、聊天工具及复杂 IT 环境自主执行退款处理或账户更新。
  • 首选集成 GPT-5.2 级模型:协议确立 OpenAI 前沿模型为 ServiceNow 平台的首选智能选项。通过预构建的解决方案,企业可直接在 800 亿规模的年度工作流中部署 Agentic AI,无需进行复杂的定制化开发。
  • AI Control Tower 治理编排层:为企业提供集中化的审计与控制中心。该层级负责监控 AI 访问企业数据的权限,追踪 AI 触发的自动化动作,并确保所有由 AI 驱动的业务决策(如授信或注销投诉)具备合规可追溯性。

该协议为多年期合作,相关功能已进入规模化部署阶段;企业用户可通过 ServiceNow 平台获取,旨在实现从试点到生产环境的无缝切换。

( @CX Today)

3、「Consio AI」获 330 万美元融资:利用语音 AI 自动化电商进线响应与回访流程

由电商客服独角兽「Gorgias」早期员工创立的「Consio AI」完成 330 万美元融资,由 RTP Global 领投。该公司旨在通过 AI 自动化电商行业的电话沟通渠道,解决高客单价商品在传统邮件或聊天机器人场景下转化率低的问题。

  • 全流程语音自动化:系统可实现进线电话的即时自动响应,并根据用户行为逻辑自动触发定时回访。
  • 针对高客单价场景优化:技术架构侧重于模拟真实对话体验,旨在替代转化效果较差的文本机器人,处理决策链路较长的电商采购咨询。
  • 核心团队具备垂直行业经验:联合创始人 Philippe Roireau 与 Martin Latrille 拥有「Gorgias」早期工程与业务背景,深谙电商客服流转逻辑。
  • 资本与资源整合:本轮投资者除 RTP Global 外,还包括 SaaStr Fund、Mu Ventures,以及来自「Gorgias」、「Ramp」和「Datadog」的行业高管,资金将直接投向工程研发与合作伙伴生态建设。

已完成首轮融资,目前正加速工程开发并扩展市场准入。

(@RTIH)

03 有态度的观点

1、山姆 · 奥特曼:企业若不拥抱 AI,将被全 AI 公司淘汰

据腾讯科技报道,昨天上午,在旧金山的一场开发者交流中,OpenAI CEO 山姆 · 奥特曼表示,未来最具竞争力的公司可能呈现出「少量员工 + 大量 AI 助手」的组织形态。

他指出,AI 已从辅助工具演变为核心协作者,企业的生产方式、招聘逻辑与组织结构都将因此发生深刻变化。

奥特曼认为,许多公司尚未意识到 AI 已能承担大量工作,如果继续沿用传统扩张模式,将在未来竞争中处于劣势。

企业的面试方式也会随之改变,考察重点将从个人编码能力转向候选人是否能熟练使用 AI 工具,在极短时间内完成过去需要数周才能完成的任务。

企业未来可能面临两种路径:一种是由少量员工与大量 AI 协同工作,另一种则是完全由 AI 驱动的公司。

他希望前者成为主流,但也坦言,如果企业不主动拥抱 AI,将可能被更灵活的全 AI 公司淘汰。他强调,这不仅关乎企业竞争力,也关系到社会结构的稳定性。

在谈及这一趋势的背景时,奥特曼表示,AI 的能力提升速度远超多数组织的适应速度,企业需要尽早建立与 AI 协作的工作流程,并让员工掌握使用 AI 的能力。

他认为,未来的组织优势将来自「人类判断 + AI 执行」的组合,而不是单纯依赖人力扩张。

在本次活动现场,奥特曼也简要回应了其他关键议题,包括程序员职业前景、创业瓶颈、模型成本与安全风险等:

  • 软件工程师不会被取代,但工作方式将转向「指挥计算机完成任务」;
  • 创业门槛降低,但「找到用户」仍是最大难题;
  • 模型成本预计将在明年底显著下降,但速度将成为新瓶颈;
  • 生物安全是今年最值得警惕的风险领域;
  • 软件将加速走向个性化,每个人都可能拥有为自己生成的工具;
  • 幼儿教育应减少电子设备使用,更应培养主动性与创造力。

( @APPSO)

04 社区黑板报

招聘、项目分享、求助……任何你想和社区分享的信息,请联系我们投稿。(加微信 creators2022,备注「社区黑板报」)

1、通义百聆开发者新年交流会:语音模型从设计到使用全流程解析

阅读更多 Voice Agent 学习笔记:了解最懂 AI 语音的头脑都在思考什么

写在最后:

我们欢迎更多的小伙伴参与 「RTE 开发者日报」 内容的共创,感兴趣的朋友请通过开发者社区或公众号留言联系,记得报暗号「共创」。

对于任何反馈(包括但不限于内容上、形式上)我们不胜感激、并有小惊喜回馈,例如你希望从日报中看到哪些内容;自己推荐的信源、项目、话题、活动等;或者列举几个你喜欢看、平时常看的内容渠道;内容排版或呈现形式上有哪些可以改进的地方等。

作者提示:个人观点,仅供参考

比如春熙路手工之类的?或者更少的几十块几百块都可以说说?

比如我有次见女网友,虽然只请了个商场里的牛排(也不贵一份 50 左右),但是仍然在事后感觉到难受。
因为真人确实不好看,而且性格/人品也不行(参考泡夜店的女的,并且丑而自信心爆棚),当时应该见面就撒腿跑的,太年轻了没经历过。

钱不多,难受不是因为花钱,而是感觉自己下贱,给烂人花钱。(当然还有别的花的更多的,但是这个例子更让我感到恶心)

并且之前谈过接触过的几个女的也是完全索取型,“恋爱”过程基本全都是我付钱,送礼物也是不对等。怪不得谈恋爱体验这么差,现在想想挺生气的,让这些烂人出现在自己的生命里面,是自己性格各方面的问题导致的,也没有做好筛选。不知道是跟讨好型人格有关吗。


咨询了下 ChatGPT:
"
那不是我贱,
是当时的我,用钱在买“被需要、被回应、被选中”的感觉。

在那个阶段:

你缺关系

你缺确认

你缺被看见

而你手里唯一会的工具,就是付出。

👉 这是能力有限,不是人格失败。
"



跟 GPT 聊了很多,确实挺有帮助的

想问问各位的经历~ 参考一下,提前避雷。

看了 clawdbot 作者的 coding 方式,一时不知道该说啥好。

平均每天 100 多次提交,完全不看代码。

无法想象这项目的💩堆得有多高。关键开源社区还有那么多人给他做贡献。想想都觉得不可思议。

所以代码质量在这个时代是不是已经变得不再重要了?

PS:我没用过 clawdbot 不知道 bug 多不多,也没看过他的源码。如果他做得又快又好当我啥也没说。

试了一下 4.99 的,用 cc 发了一下你是哪个模型,那个频控明细一下就变成了 74/100 ,要 4 个小时重置一次,这还怎么玩 0.0 ,估计搞几个请求就不行了

略有 coding 基础,以产品经理的角色 vibe-coding 小工具类 web 项目全栈
后端/核心业务逻辑通常不会有什么大问题,只要业务逻辑理顺了拆解清晰,ai 都能顺利搞定
但是前端就有点失控了,一来前端 ui 我没有设计图稿不会给太多约束,讲清业务交互逻辑、组件功能分区。ui 基本上让 ai 自由发挥,然后搞个多 ai 赛马挑一套。
一把梭初看可能效果不错,但是具体到 ui 细节需要调整的时候,我发现直接让 ai 修复的成功率很低,比如改一些折叠、遮挡、推挤、边距、置顶、间距效果。这类调整 ai 实现的效果非常随缘,经常改坏。
请教各位如何解决这类问题,是应该学点前端,这类问题自己动手吗?或者有什么其他的建议?谢谢各位
另外很多前端开发推 gemini 的,我个人体感审美最在线的还是 codex ,是我打开方式不对吗?

这里接了一年裁神,终于接到了。明天最后一天,N+1, N=8 ,没年终(摆烂了一年,年终本来就基本没了,这也是我摆烂的原因,年终占了每年收入大头)

作妖的新领导,变相降薪,每天厌恶上班,有名额就主动拿了,不知道以后还会不会上班,至少两年之内是不可能再回到办公室上班了

没车没房没后代,准备整辆二手车到处去浪

叙事生成系统的核心壁垒从来不是叙事内容的量产,而是叙脉肌理的自洽共生与玩家选择的价值落地,二者的动态平衡直接决定叙事体验的沉浸深度与玩家的持续粘性。多数设计困于要么牺牲选择自由度保叙脉连贯,要么放任选择多元导致叙脉断裂,这种二元对立的困境本质上是对叙脉与选择关系的认知偏差,真正的破局关键在于搭建叙脉锚定与择向赋能的协同逻辑,让选择成为叙脉的有机延伸而非割裂因子,让连贯的叙脉成为选择价值的承载容器。这种逻辑的搭建需要跳出传统固定叙事框架的桎梏,从叙事节点的关联性、选择的价值传导性、肌理的共生性三个维度切入,既要让每个叙事节点都携带核心叙脉的基因印记,又要让玩家的每一次选择都能触发差异化的叙事增量,同时确保增量内容能反向锚定核心叙脉,形成闭环式的叙事生态,而非单向的剧情推进或零散的选择堆砌。在实操过程中,需要精准把控叙事节点的权重分配,核心叙脉节点需具备不可替代性,承载核心冲突、人物弧光等关键要素,分支选择节点则需具备差异化赋能性,每个选择都能带来独特的叙事体验与价值反馈,而非简单的选项分流。例如,核心叙脉围绕“失落文明的复兴”展开,核心节点需包含文明失落的真相、关键传承者的觉醒、核心危机的爆发等不可替代的要素,而分支选择节点可设计为“寻找技术传承”“联合现存部落”“探寻禁忌遗迹”等差异化方向,每个方向都能通过不同的线索、人物互动、场景解锁,从不同维度推动核心叙脉的推进,让玩家在拥有充分选择自主权的同时,始终沉浸在逻辑自洽、肌理完整的叙事世界中。这种设计思路不仅能提升叙事体验的深度,更能让玩家感受到选择的真正价值,而非流于形式的选项罗列,让叙事生成系统摆脱“量产后的空洞”与“选择后的混乱”,实现质的突破。

构建叙脉连贯的核心支撑是动态叙脉基线的搭建与叙事节点的弹性耦合,而非固定的剧情链条设计。叙脉基线并非单一的线性脉络,而是承载核心叙事内核、冲突逻辑、世界观底色的核心框架,其核心特质是具备可衍生性与不可破缺性,可衍生性支撑玩家选择带来的分支拓展,不可破缺性保障叙脉不会因分支过多而偏离核心。具体实操中,首先需拆解核心叙事的核心要素,包括核心冲突的本质(如“个体命运与族群使命的对立”)、核心人物弧光的关键转折点(如“从自我放逐到主动担当”)、世界观核心规则(如“魔法与科技的共生禁忌”),将这些要素转化为叙脉基线的核心锚点,每个锚点都需具备明确的叙事功能与不可替代性,成为叙脉的“定海神针”。随后围绕锚点搭建叙事节点网络,每个节点都与核心锚点形成显性或隐性的关联,显性关联如直接推动核心冲突升级的剧情节点,隐性关联如通过细节补充世界观规则、塑造人物性格的支线节点。节点之间则通过叙事肌理实现弹性耦合,这种耦合不是机械的衔接,而是基于叙事逻辑、人物行为逻辑的自然关联,例如核心锚点是“古城秘辛的探寻”,叙事节点则涵盖线索获取、势力互动、秘境探索等,线索获取节点可能通过古籍、NPC口述等形式呈现,势力互动节点则涉及不同势力对秘辛的态度与诉求,秘境探索节点则是直面秘辛真相的关键场景。每个节点的推进都围绕秘辛探寻这一核心,同时为玩家选择预留空间,比如线索获取节点可选择“贿赂守卫获取古籍”“帮助学者解密获得线索”“潜入藏书阁偷取资料”等不同方式,每种方式都会触发不同的人物关系变化与后续节点解锁。节点与节点之间的耦合则通过线索承接、人物动机延续实现,即便玩家选择不同的节点推进顺序,也能通过核心锚点的牵引,让叙脉保持连贯,比如选择“潜入偷取资料”可能触发守卫追捕,后续需与某势力结盟寻求庇护,而结盟节点又会引出该势力对秘辛的独特解读,最终仍会指向秘境探索的核心节点。这种设计让叙脉具备了弹性,既能容纳多元选择带来的分支变化,又能始终围绕核心内核推进,避免叙脉断裂或逻辑混乱,同时让每个分支都具备独特的叙事价值,而非简单的剧情重复。

赋予玩家选择真正的意义,核心是搭建选择的价值分层与反馈传导闭环,避免无意义的选项堆砌或同质化的选择结果。选择的价值分层需从即时反馈、中期叙脉影响、长期世界观赋能三个维度展开,每个维度都要具备差异化的落点,让玩家清晰感知到不同选择带来的不同影响,而非仅停留在表面的对话差异或场景变化。即时反馈层面需贴合玩家当下的行为预期,提供具象化、可感知的反馈,比如选择帮助特定角色摆脱困境,不仅能获得该角色的口头感谢,还能获得专属线索道具(如刻有神秘符号的玉佩)、角色信任度提升的显性标识(如对话中更亲昵的称谓、主动分享的秘密),选择优先探索秘境,则能提前解锁核心道具(如破解机关的工具)或秘境隐藏细节(如墙壁上未被发现的壁画),这些即时反馈能快速强化玩家的选择感知,让玩家感受到选择的即时价值。中期叙脉影响则要关联后续叙事节点的解锁与推进方向,形成差异化的剧情分支,比如选择结盟某一势力,后续会解锁该势力专属的剧情分支(如参与势力内部的权力斗争、获得势力专属的技能或资源支持),选择中立则会触发多方势力的互动剧情(如在不同势力间周旋、平衡各方利益),选择对抗某一势力则会面临该势力的追杀与阻碍,同时获得其他对立势力的支持,这种中期影响让选择的价值持续延伸,推动叙脉向差异化方向发展。长期世界观赋能则要关联角色成长、世界观细节补全,形成更深层次的价值反馈,比如选择守护古城,会推动古城世界观的正向发展(如古城逐渐恢复生机、解锁古城隐藏的历史篇章),角色会获得“守护者”的专属身份标识,影响后续与其他NPC的互动态度,选择探寻秘辛背后的禁忌,则会揭露世界观的黑暗面(如古代文明毁灭的真相、隐藏的邪恶势力),角色会获得“探寻者”的身份,解锁更多禁忌知识与特殊能力,这种长期赋能让选择的价值沉淀为叙事体验的核心记忆点。反馈传导闭环则要确保每个选择的影响能持续渗透到后续叙事中,而非单次触发后消失,比如前期选择赠予角色信物,后续角色在关键剧情中会基于信物做出专属反应(如在危机关头用信物救下玩家、通过信物解读核心线索),前期选择放过某一反派,后续该反派会在特定节点提供关键帮助(如透露敌人的弱点、在绝境中伸出援手),这种闭环设计让选择具备了延续性,玩家会更重视每一次选择,同时也让叙脉因选择的差异化反馈更具层次感,而非单向的剧情输出。

叙脉连贯与玩家选择的适配关键,在于隐性叙事锚点的精准布设与叙脉偏移的无痕校准,这一设计思路的核心是在尊重玩家选择自主权的前提下,通过隐性引导与自然校准,确保叙脉始终围绕核心基线推进,同时保留分支选择的独特性。隐性叙事锚点区别于显性的剧情提示,是以细节线索、人物行为习惯、世界观规则细节为载体的引导元素,其核心作用是在玩家选择导致剧情分支偏移时,无痕牵引叙脉回归核心基线。实操中需在关键分支节点前后布设隐性锚点,锚点的形式需贴合叙事场景与人物设定,避免生硬的引导感。在开放世界探索场景中,玩家选择偏离主线探索边缘区域,隐性锚点可设计为区域内的古老文献(如记载核心叙脉相关历史的残卷)、环境细节(如指向主线方向的特殊地貌、与核心冲突相关的遗迹),这些锚点不会强制玩家回归主线,而是通过传递核心叙脉的相关信息,激发玩家的探索兴趣,引导玩家主动回归主线探索。在角色互动场景中,玩家选择与核心人物产生冲突,隐性锚点可设计为人物的专属习惯(如核心人物始终随身携带与核心冲突相关的信物,冲突时会无意识地抚摸信物)、语言细节(如对话中不经意提及核心叙脉的关键信息),这些细节会触发人物的内心独白或额外对话,让玩家了解到冲突背后的深层原因,牵引剧情回归核心冲突的解决。叙脉偏移的无痕校准则要规避生硬的剧情拉回,而是基于玩家选择的方向,找到分支与主线的衔接点,通过自然的叙事过渡实现校准。比如玩家选择加入反派势力,校准逻辑不是强制让玩家回归正派,而是通过反派势力内部的矛盾(如反派首领的残暴统治、势力成员的良心觉醒)、核心秘辛的真相揭露(如反派势力的目标与玩家的初衷相悖),让玩家基于自身选择自然走向与主线相关的剧情节点(如背叛反派势力、利用反派资源对抗真正的敌人)。这种校准过程完全融入叙事本身,玩家不会感受到被“强制引导”,反而会觉得是自身选择推动的自然结果,既尊重了玩家的选择自主权,又保障了叙脉的连贯,同时让校准过程成为叙事体验的有机组成部分,提升了沉浸感,也让叙脉的弹性与选择的自由度实现了深度适配。

深化叙事生成系统的叙脉与选择平衡,需要聚焦叙事肌理的共生性打磨与选择的个性化赋能,这两个维度的深度优化能让叙事体验更具统一性与独特性,避免出现分支与主线割裂、选择与玩家偏好脱节的问题。叙事肌理的共生性核心是让核心叙脉与分支选择的叙事内容在逻辑、风格、世界观层面保持统一,避免出现分支与主线风格割裂、逻辑冲突的问题。实操中需先确立核心叙事肌理,包括叙事风格(如古风悬疑的诡谲氛围、科幻史诗的宏大感)、人物行为逻辑(如角色的性格底色、动机出发点)、世界观底层规则(如魔法体系的运行规律、社会结构的核心准则),这些肌理要素需贯穿整个叙事系统,成为所有叙事内容的创作基准。在此基础上,让所有分支选择的叙事内容都遵循这一肌理,比如核心叙事肌理是古风悬疑,分支选择的剧情无论偏向江湖恩怨还是朝堂权谋,都要保持悬疑的基调(如隐藏的阴谋、反转的剧情)、符合古风人物的行为逻辑(如江湖侠客的侠义精神、朝堂官员的权谋算计)、贴合世界观的规则设定(如江湖门派的等级制度、朝堂的权力架构)。同时让分支内容成为核心肌理的补充,比如江湖恩怨分支可补充世界观中的江湖势力分布、门派间的历史纠葛,朝堂权谋分支可补充世界观中的朝堂权力斗争规则、皇室与大臣的关系,让叙事世界更完整、更立体。选择的个性化赋能则要跳出标准化的选项设计,基于玩家的选择倾向、行为习惯,动态调整后续选择的方向与价值落点,让选择更贴合玩家的偏好,实现“千人千面”的叙事体验。实操中可通过分析玩家的历史选择数据,提炼玩家的核心偏好(如偏向正义、偏向探索、偏向社交),再基于偏好动态调整后续选项,比如玩家多次选择偏向正义的选项,后续会解锁更多正义导向的高价值选择(如拯救无辜百姓、揭露黑暗势力的阴谋),同时角色会获得正义属性的赋能(如获得“正义使者”的称号、NPC更愿意提供帮助),影响人物弧光的走向;玩家多次选择偏向探索的选项,后续会解锁更多隐藏的探索分支(如未标记的秘境、隐藏的剧情彩蛋),获得专属的探索道具与线索(如探测宝物的罗盘、解读古代文字的字典),让探索体验更具深度。这种个性化赋能让玩家感受到自身选择对叙事的独特影响,而非被动接受预设的选项,同时让叙脉因玩家的个性化选择呈现出差异化的推进轨迹,既保持了核心叙脉的连贯,又让每个玩家的叙事体验都具备独特性,这种设计不仅提升了玩家的粘性,更让叙事生成系统具备了更强的生命力。

叙事生成系统中叙脉连贯与玩家选择的平衡,本质是叙事价值与玩家体验价值的共生,其终极目标是让玩家在连贯的叙事世界中,通过有意义的选择实现自我表达与沉浸体验,这一目标的实现需要突破技术与叙事的双重边界,在实践中不断打磨优化。过往的实践探索让我深刻认知到,叙脉连贯不是对玩家选择的束缚,而是让选择更具价值的基础—失去连贯叙脉的支撑,再多元的选择也只是零散的剧情片段,无法形成完整的叙事体验,玩家难以感受到选择的长远意义;玩家选择的意义也不是对叙脉的破坏,而是让叙脉更具层次感与生命力的核心—缺乏选择的叙脉只是单向的剧情灌输,玩家难以产生代入感与参与感,叙事体验会显得僵化空洞。

游戏世界生态模拟的从来不是静态复刻现实生态表象,而是构建具备自洽韧性的动态调节肌理,让物种、资源、环境三者脱离预设脚本的束缚,形成无需外部干预的可持续循环。多数设计困于要么陷入数值失衡的死局,要么依赖固定触发事件强行矫正,这种非此即彼的困境本质是对生态调节逻辑的浅层认知,真正的突破在于搭建“生态节点互哺链”与“阈值自适应机制”的协同框架,让每个生态组件既是调节的参与者,也是平衡的受益者,二者的深度耦合让生态系统具备自我修复、自我优化的内在动力。这种框架需要跳出“单一维度数值匹配”的桎梏,从节点关联性、资源流转效率、环境反馈灵敏度三个核心维度切入,既要让物种的繁衍、迁徙、消亡与资源供给形成动态呼应,又要让环境的变迁(如气候波动、地形改变)成为调节的催化剂而非破坏因子,同时确保所有调节行为都遵循生态内在逻辑,而非机械的数值补偿。具体场景中,比如温带森林生态系统,当食草物种因无天敌制约数量激增,导致植被覆盖率在短期内低于临界值时,系统不会直接通过后台指令削减物种数量,而是启动多路径自然调节:首先,植被会通过延长再生周期、降低可食用部分的营养含量(如减少糖分积累),间接降低食草物种的能量摄入,进而抑制其繁殖效率,幼崽存活率会随母体营养不足自然下降;其次,植被覆盖率降低会导致食草物种的隐蔽性减弱,原本觅食成功率较低的食肉物种(如狼、豹)会因猎物暴露度提升而提高捕猎效率,种群数量逐渐上升;同时,部分食草物种会因食物匮乏触发迁徙本能,向植被更茂盛的区域移动,缓解局部资源压力。这种多路径、非干预式的调节,让生态系统在失衡后通过自身组件的互动自然回归平衡,既保持了生态逻辑的自洽,又让整个过程具备真实的动态感,真正实现“失衡-感知-调节-平衡”的闭环,摆脱对预设脚本的依赖。

构建生态可持续调节的核心支撑,是“生态基序”的搭建与“节点弹性耦合”的实现,而非简单的物种与资源罗列。生态基序是承载生态核心规则的底层框架,其核心特质是“规则自洽性”与“演化兼容性”,前者保障所有生态组件的行为都遵循统一的底层逻辑,避免出现矛盾的调节行为,后者允许生态系统在调节过程中产生新的互动模式,而非局限于初始设定。实操中,首先需要系统拆解生态核心要素,明确资源类型的层级划分(如能量资源、物质资源、空间资源,其中能量资源又细分为太阳能、生物能等,物质资源包含水分、土壤养分、矿物质等)、物种核心属性的量化逻辑(如觅食范围的半径阈值、繁殖周期与能量储备的关联公式、环境适应阈值的区间设定)、环境影响因子的作用机制(如气候因子中降水、温度对物种的影响权重,地形因子中山地、平原对资源分布的塑造逻辑,灾害因子的触发概率与影响范围),将这些要素转化为生态基序的核心规则,确保规则之间无逻辑冲突,且能覆盖生态系统的核心互动场景。随后围绕规则搭建节点网络,每个节点(物种、资源、环境因子)都与其他节点形成显性或隐性的耦合关系,这种耦合不是固定的一对一关联,而是基于基序规则的动态多向互动。例如,温带草原生态中,草本植物作为核心资源节点,其生长速率直接关联降水因子的补给频率、土壤肥力节点的养分含量,同时间接影响食草物种的种群密度;食草物种的觅食行为不仅与草本植物的分布范围、再生效率耦合,还会通过粪便排泄补充土壤肥力,形成正向反馈;食肉物种则与食草物种的种群密度、活动轨迹深度耦合,其捕猎行为会直接调节食草物种数量,进而间接影响草本植物的生长状态。当降水减少导致草本植物生长放缓时,食草物种的觅食范围会根据能量需求自动扩大(如从原本的5公里半径扩展至8公里),繁殖周期会根据能量摄入不足的情况相应延长(如从半年一胎调整为一年一胎),部分体质较弱的个体因无法获取足够食物自然淘汰;而食肉物种则因猎物密度降低,要么扩大活动半径寻找分散的猎物,要么降低繁殖成功率,减少种群内的资源竞争。这种弹性耦合让每个节点的变化都能通过基序规则传递给多个关联节点,触发连锁式调节,避免单一节点失衡引发整个生态崩溃,同时让调节过程自然且符合生态逻辑,而非机械的数值联动。

赋予生态系统自我调节的真正动力,是“物种韧性赋权”与“资源流转熵平衡”的双重保障,避免生态调节流于表面或陷入不可逆失衡。物种韧性赋权的核心是让物种具备“环境感知-自主决策-行为反馈”的完整能力链,而非被动接受系统的数值调节,这种能力需深度绑定物种的核心属性与生态基序规则,让物种在面临环境变化或资源波动时,能做出符合自身生存逻辑的差异化选择。具体场景中,极地生态系统遭遇异常升温时,耐寒物种不会直接按预设脚本灭绝或迁徙,而是基于自身适应阈值启动多元适应策略:部分物种会调整活动时段,避开日间高温时段,选择夜间或清晨觅食;部分物种会改变觅食对象,从依赖冰雪下的苔藓、地衣转向耐寒昆虫或腐殖质;还有部分物种会启动短距离迁徙,向高纬度或高海拔的低温区域移动,迁徙路径会根据途中的资源分布动态调整,而非固定路线。这些选择不是系统强制分配的结果,而是物种基于环境参数变化(如温度持续超过适应阈值、传统食物资源减少)与自身属性(如迁徙能力、食性适应范围)的自主决策,让物种的生存行为更具真实性与多样性。资源流转熵平衡则聚焦于避免资源过度消耗或闲置浪费,通过建立“资源转化闭环”与“熵增抑制机制”,让资源在生态系统中高效循环、动态平衡。例如,温带森林生态中,落叶、死亡生物遗体等“废弃物质资源”,会通过分解者(如微生物、腐生昆虫、真菌)的代谢活动转化为土壤肥力,反哺植物生长;植物通过光合作用将太阳能转化为生物能,供给食草物种食用;食草物种的代谢废物(粪便、尿液)又会补充土壤肥力,形成“植物-动物-分解者-植物”的资源转化闭环。当某一资源(如木材)被玩家过度采集,导致植物资源储量骤降时,系统不会直接刷新植物补充,而是启动熵增抑制机制:分解者的转化效率会自动提升,加速废弃物质向土壤肥力的转化;同时,植物的再生周期会根据资源消耗速率动态缩短,未被采集的植物会提升种子传播范围与发芽率;此外,依赖植物生存的食草物种会因食物减少而降低繁殖效率,间接减少对植物资源的消耗。这种多维度的资源调节,让资源供给与消耗始终处于动态平衡,避免资源枯竭或过度积累,让资源流转摆脱“单向消耗”的线性困境,成为生态调节的核心动力源。

生态系统可持续调节的适配关键,在于“环境反馈阈值校准”与“失衡预警机制”的精准布设,避免调节行为滞后或过度矫正,确保生态平衡的稳定性与可持续性。环境反馈阈值是生态系统感知失衡风险的“敏感神经”,其核心作用是在生态系统接近失衡临界值前提前触发调节行为,而非等到失衡已成定局后再被动修正,阈值的设定需基于生态基序规则与节点耦合关系进行精细化测算,避免主观臆断或统一标准。例如,河流生态系统中,鱼类种群密度的反馈阈值并非单一数值,而是结合水体溶氧量、浮游生物数量、河流空间容量、天敌种群密度等多个节点参数的动态区间:当鱼类密度达到阈值区间的80%时,系统不会立即启动强调节,而是通过降低浮游生物的繁殖速率,间接抑制鱼类的生长速度;当密度达到阈值区间的90%时,再触发水体溶氧量缓慢下降的反馈,进一步限制鱼类繁殖,同时提升鱼类的自然死亡率;只有当密度突破阈值上限时,才会启动天敌种群觅食效率提升的强化调节。这种分级阈值设计让调节行为循序渐进,既避免了调节不足导致的失衡,又防止了过度矫正引发的新矛盾。失衡预警机制则是通过监测“隐性生态指标”,提前预判可能的失衡风险,将调节关口前移,隐性生态指标区别于显性的种群数量或资源储量,是反映生态内在健康度的核心参数,如物种基因多样性、资源转化效率、节点耦合强度、环境因子稳定性等。例如,温带森林生态中,当某一优势树种的基因多样性低于预警值时,系统会预判该树种可能因病虫害爆发导致大面积死亡,进而引发生态失衡,提前启动预防调节:增加该树种的种子变异概率,提升其对病虫害的抵抗力;扩大其他树种的生长空间,避免单一树种过度垄断资源;引入依赖该树种的昆虫物种,通过昆虫的选择性觅食清除弱势植株,优化树种基因库。这种预警机制让生态调节从“被动应对”转向“主动预防”,大幅降低失衡风险,提升生态系统的可持续性。

深化生态模拟系统的可持续调节能力,需要聚焦“生态演化韧性”与“多元互动赋能”的深度打磨,让生态系统在调节过程中具备自我优化的能力,而非停留在固定的平衡状态,真正实现“平衡-失衡-调节-更优平衡”的螺旋式上升。生态演化韧性的核心是让生态系统在经历多次调节循环后,能自发形成更稳定、更多元的互动模式,而非陷入“失衡-调节-再失衡”的低效循环,实操中需在生态基序中融入“变异概率”与“选择压力”规则,允许物种在适应环境变化的过程中产生微小的属性变异,这些变异会根据生态环境的选择压力被自然保留或淘汰,逐步优化物种的适应能力。例如,沙漠生态系统中,某种植被物种在长期面临干旱调节时,可能会产生“叶片储水能力增强”“根系延伸深度增加”等微小变异,这些变异让该物种在水资源匮乏的环境中更易生存,其种群数量会逐渐上升,进而影响依赖该植被的昆虫与小型动物—昆虫可能会演化出更高效的吸水器官,小型动物可能会形成以该植被为核心的集群活动模式,最终形成新的互动链条,让沙漠生态的调节模式更丰富、更稳定。多元互动赋能则是打破“物种-资源”的二元互动局限,全面引入“物种-物种”“物种-环境”“资源-环境”的多元互动模式,让调节路径更丰富、更具韧性,避免单一调节路径失效导致的生态崩溃。例如,山地生态系统中,鸟类物种的迁徙行为不仅受食物资源分布影响,还会与地形复杂度(如山脉走向、山谷分布)、气流变化(如季风强度、气压梯度)、其他迁徙物种的种群密度等多个因素互动:气流稳定时,鸟类会选择高空迁徙以节省体力;地形复杂区域,鸟类会沿山谷低空飞行以避开强风;当同类迁徙物种密度过高时,部分鸟类会调整迁徙路线,避免资源竞争。而鸟类的迁徙行为又会带动植物种子的跨区域传播,影响不同区域的植被分布;植被分布变化会进一步调节局部气候(如增加空气湿度、降低地表温度),形成“环境-物种-资源-环境”的多元互动闭环,这种多路径的互动让生态调节具备更强的容错性,即便某一调节路径失效,其他路径仍能保障生态系统的基本平衡,同时让生态世界更具生机与真实感。

游戏世界生态模拟系统的可持续自我调节,本质是“规则自洽”与“动态平衡”的深度共生,其终极目标是让生态系统成为一个具备生命感的有机整体,而非机械响应指令的数值模型,这一目标的实现需要突破技术设计与生态逻辑的双重边界,在实践中不断打磨、迭代优化。过往的探索让我们深刻认知到,生态调节的核心不是“强行维持固定平衡”,而是“赋予系统自主恢复平衡并优化平衡的能力”—真正的可持续调节,是让生态系统在面临内外部干扰时,能通过自身的规则与互动自然回归平衡,甚至在调节过程中演化出更优的平衡状态,这种“自组织”能力才是生态模拟的核心价值。后续的技术探索方向需聚焦于生态基序的“自适应优化”与“多元规则融合”:生态基序的自适应优化要让底层规则具备根据生态运行数据动态调整参数的能力,例如,通过分析多次失衡调节的效率,自动优化反馈阈值的区间、变异概率的大小,让规则更贴合生态实际运行状态;多元规则融合则是在核心生态逻辑的基础上,引入更多跨领域的规则灵感(如复杂系统的涌现性原理、生物群落的协同进化理论),但并非生硬复刻,而是基于游戏生态的特性进行改造与融合,让调节逻辑更深奥、更具科学性。

牛马干活更快了,并不代表牛马有时间休息了,或者能加工资。
他们只会觉得这一切都是理所当然的,只会安排更多工作。


原来需要两个前端的项目,
转眼就少了一个人,
然后去压力另外一个用 AI 提效。


我确实还相信技术能改变世界,
但我现在清楚地认识到——

新世界不一定还有我的位置。

大概想了一年多的大礼包,终于在昨天得偿所愿。

认真想了想,这可能是我最后一份工作,后面准备全职独立开发了。

去年一整年,一行代码没写过,全部由 AI 来完成,所以开始无休止的焦虑。

想了想,还是决定走数字游民这条路,去年一整年的 GitHub 提交记录将近 7000 次(全是有效提交),每天都是两三点钟睡。

有一点回报,出海方面做出了一些成绩,MRR 在 2500 刀左右,去掉成本也有 2000 刀左右(虽然远不及本质工作的收入),所幸目前也在缓慢增长。

所以,是时候开始新的人生了。

在用 AI 自动化打印日志解决问题的时候,它只看到了第一层,比如说某个值是空指针,导致了崩溃,然后就加上一个 if 判断,然后不崩溃了,就说问题解决了。难顶。

然后和它说这个地方不应该是空指针,我没有防御性编程的习惯。它又说你说的对,然后才能定位到真正的问题。难顶。

是不是 Windsurf 里面的 Sonnet 4.5 和 Opus 4.5 是降智的啊?有点搞心态了。

DQN 用

max Q(s',a')

计算目标值,等于在挑 Q 值最高的动作,但是这些动作中包括了那些因为估计噪声而被高估的动作,素以就会产生过估计偏差,直接后果是训练不稳定、策略次优。

这篇文章要解决的就是这个问题,内容包括:DQN 为什么会过估计、Double DQN 怎么把动作选择和评估拆开、Dueling DQN 怎么分离状态值和动作优势、优先经验回放如何让采样更聪明,以及用 PyTorch 从头实现这些改进。最后还会介绍一个 CleanRL 的专业实现。

过估计问题

DQN 的目标值如下:

 y = r + γ·maxₐ' Q(s', a'; θ⁻)

问题就在于,同一个网络既负责选动作(a* = argmax Q),又负责评估这个动作的价值。Q 值本身是带噪声的估计所以有时候噪声会让差动作的 Q 值偏高,取 max 操作天然偏向选那些被高估的动作。

数学上有个直观的解释:

 E[max(X₁, X₂, ..., Xₙ)] ≥ max(E[X₁], E[X₂], ..., E[Xₙ])

最大值的期望总是大于等于期望的最大值,这是凸函数的 Jensen 不等式。

过估计会导致收敛变慢,智能体把时间浪费在探索那些被高估的动作上。其次是策略质量打折扣,高噪声的动作可能比真正好的动作更受青睐。更糟的是过估计会不断累积,导致训练发散。泛化能力也会受损——在状态空间的噪声区域,智能体会表现得过于自信。

Double DQN:把选择和评估拆开

标准 DQN 一个网络干两件事:

 a* = argmaxₐ' Q(s', a'; θ⁻)  # 选最佳动作  
 y = r + γ · Q(s', a*; θ⁻)    # 评估这个动作(同一个网络)

Double DQN 用两个网络,各管一件:

 a* = argmaxₐ' Q(s', a'; θ)  # 用当前网络选  
 y = r + γ · Q(s', a*; θ⁻)   # 用目标网络评估

当前网络(θ)选动作,目标网络(θ⁻)评估。两个网络的误差不相关这样最大化偏差就被打破了。

为什么有效呢?

假设当前网络把动作 a 的价值估高了,目标网络(参数不同)大概率不会犯同样的错。误差相互独立,倾向于抵消而非累加。

最通俗的解释就是DQN 像是自己给菜打分、自己挑菜吃,这样烂菜可能就混进来了,而Double DQN 让朋友打分、你来挑,两边的误差对冲掉了。

  Standard DQN:  E[Q(s, argmaxₐ Q(s,a))] ≥ maxₐ E[Q(s,a)]   (有偏)  
 Double DQN:    E[Q₂(s, argmaxₐ Q₁(s,a))] ≈ maxₐ E[Q(s,a)]  (无偏)

从 DQN 到 Double DQN,只需要改一行:

 # DQN 目标  
next_q_values=target_network(next_states).max(1)[0]  
target=rewards+gamma*next_q_values* (1-dones)  

# Double DQN 目标  
next_actions=current_network(next_states).argmax(1)  # <- 用当前网络选  
next_q_values=target_network(next_states).gather(1, next_actions.unsqueeze(1))  # <- 用目标网络评估  
 target=rewards+gamma*next_q_values.squeeze() * (1-dones)

就这一行改动极小,效果却很明显。

实现:Double DQN

扩展 DQN Agent

 classDoubleDQNAgent(DQNAgent):  
    """  
    Double DQN: 通过解耦动作选择和评估来减少过估计偏差。  
    """  
      
    def__init__(self, *args, **kwargs):  
        """  
        初始化 Double DQN agent。  
        从 DQN 继承所有内容,只改变目标计算。  
        """  
        super().__init__(*args, **kwargs)  
      
    defupdate(self) ->Dict[str, float]:  
        """  
        执行 Double DQN 更新。  
          
        Returns:  
            metrics: 训练指标  
        """  
        iflen(self.replay_buffer) <self.batch_size:  
            return {}  
          
        # 采样批次  
        states, actions, rewards, next_states, dones=self.replay_buffer.sample(  
            self.batch_size  
        )  
          
        states=states.to(self.device)  
        actions=actions.to(self.device)  
        rewards=rewards.to(self.device)  
        next_states=next_states.to(self.device)  
        dones=dones.to(self.device)  
          
        # 当前 Q 值 Q(s,a;θ)  
        current_q_values=self.q_network(states).gather(1, actions.unsqueeze(1))  
          
        # Double DQN 目标计算  
        withtorch.no_grad():  
            # 使用当前网络选择动作  
            next_actions=self.q_network(next_states).argmax(1)  
              
            # 使用目标网络评估动作  
            next_q_values=self.target_network(next_states).gather(  
                1, next_actions.unsqueeze(1)  
            ).squeeze()  
              
            # 计算目标  
            target_q_values=rewards+ (1-dones) *self.gamma*next_q_values  
          
        # 计算损失  
        loss=F.mse_loss(current_q_values.squeeze(), target_q_values)  
          
        # 梯度下降  
        self.optimizer.zero_grad()  
        loss.backward()  
        torch.nn.utils.clip_grad_norm_(self.q_network.parameters(), max_norm=10.0)  
        self.optimizer.step()  
          
        self.training_step+=1  
          
        return {  
            'loss': loss.item(),  
            'q_mean': current_q_values.mean().item(),  
            'q_std': current_q_values.std().item(),  
            'target_q_mean': target_q_values.mean().item()  
         }

训练函数:

 deftrain_double_dqn(  
    env_name: str,  
    n_episodes: int=1000,  
    max_steps: int=500,  
    train_freq: int=1,  
    eval_frequency: int=50,  
    eval_episodes: int=10,  
    verbose: bool=True,  
    **kwargs  
) ->Tuple:  
    """  
    训练 Double DQN agent(使用 DoubleDQNAgent 而不是 DQNAgent)。  
    """  
    # 与 train_dqn 相同但使用 DoubleDQNAgent  
    env=gym.make(env_name)  
    eval_env=gym.make(env_name)  
      
    state_dim=env.observation_space.shape[0]  
    action_dim=env.action_space.n  
      
    # 使用 DoubleDQNAgent  
    agent=DoubleDQNAgent(  
        state_dim=state_dim,  
        action_dim=action_dim,  
        **kwargs  
    )  
      
    # 训练循环(与 DQN 相同)  
    stats= {  
        'episode_rewards': [],  
        'episode_lengths': [],  
        'losses': [],  
        'q_values': [],  
        'target_q_values': [],  
        'eval_rewards': [],  
        'eval_episodes': [],  
        'epsilons': []  
    }  
      
    print(f"Training Double DQN on {env_name}")  
    print(f"State dim: {state_dim}, Action dim: {action_dim}")  
    print("="*70)  
      
    forepisodeinrange(n_episodes):  
        state, _=env.reset()  
        episode_reward=0  
        episode_length=0  
        episode_metrics= []  
          
        forstepinrange(max_steps):  
            action=agent.select_action(state, training=True)  
            next_state, reward, terminated, truncated, _=env.step(action)  
            done=terminatedortruncated  
              
            agent.store_transition(state, action, reward, next_state, done)  
              
            ifstep%train_freq==0:  
                metrics=agent.update()  
                ifmetrics:  
                    episode_metrics.append(metrics)  
              
            episode_reward+=reward  
            episode_length+=1  
            state=next_state  
              
            ifdone:  
                break  
          
        # 更新目标网络  
        if (episode+1) %kwargs.get('target_update_freq', 10) ==0:  
            agent.update_target_network()  
          
        agent.decay_epsilon()  
          
        # 存储统计信息  
        stats['episode_rewards'].append(episode_reward)  
        stats['episode_lengths'].append(episode_length)  
        stats['epsilons'].append(agent.epsilon)  
          
        ifepisode_metrics:  
            stats['losses'].append(np.mean([m['loss'] forminepisode_metrics]))  
            stats['q_values'].append(np.mean([m['q_mean'] forminepisode_metrics]))  
            stats['target_q_values'].append(np.mean([m['target_q_mean'] forminepisode_metrics]))  
          
        # 评估  
        if (episode+1) %eval_frequency==0:  
            eval_reward=evaluate_dqn(eval_env, agent, eval_episodes)  
            stats['eval_rewards'].append(eval_reward)  
            stats['eval_episodes'].append(episode+1)  
              
            ifverbose:  
                avg_reward=np.mean(stats['episode_rewards'][-50:])  
                avg_loss=np.mean(stats['losses'][-50:]) ifstats['losses'] else0  
                avg_q=np.mean(stats['q_values'][-50:]) ifstats['q_values'] else0  
                  
                print(f"Episode {episode+1:4d} | "  
                      f"Reward: {avg_reward:7.2f} | "  
                      f"Eval: {eval_reward:7.2f} | "  
                      f"Loss: {avg_loss:7.4f} | "  
                      f"Q: {avg_q:6.2f} | "  
                      f"ε: {agent.epsilon:.3f}")  
      
    env.close()  
    eval_env.close()  
      
    print("="*70)  
    print("Training complete!")  
      
     returnagent, stats

LunarLander-v3

 # 训练 Double DQN  
if__name__=="__main__":  
    device='cuda'iftorch.cuda.is_available() else'cpu'  
      
    agent_ddqn, stats_ddqn=train_double_dqn(  
        env_name='LunarLander-v3',  
        n_episodes=4000,  
        max_steps=1000,  
        learning_rate=5e-4,  
        gamma=0.99,  
        epsilon_start=1.0,  
        epsilon_end=0.01,  
        epsilon_decay=0.9995,  
        buffer_capacity=100000,  
        batch_size=128,  
        target_update_freq=20,  
        train_freq=4,  
        eval_frequency=100,  
        eval_episodes=10,  
        hidden_dims=[256, 256],  
        device=device,  
        verbose=True  
    )  

    # 保存模型  
     agent_ddqn.save('doubledqn_lunar_lander.pth')

输出:

  Training Double DQN on LunarLander-v3  
State dim: 8, Action dim: 4  
======================================================================  
Episode  100 | Reward: -155.24 | Eval: -885.72 | Loss: 52.9057 | Q:   0.20 | ε: 0.951  
Episode  200 | Reward: -148.85 | Eval:  -85.94 | Loss: 37.2449 | Q:   2.14 | ε: 0.905  
Episode  300 | Reward: -111.61 | Eval: -172.48 | Loss: 37.4279 | Q:   3.52 | ε: 0.861  
Episode  400 | Reward:  -99.21 | Eval: -198.43 | Loss: 41.5296 | Q:   8.15 | ε: 0.819  
Episode  500 | Reward:  -80.75 | Eval: -103.26 | Loss: 56.2701 | Q:  11.70 | ε: 0.779  
...  
Episode 3200 | Reward:  102.04 | Eval:  159.71 | Loss: 16.5263 | Q:  27.94 | ε: 0.202  
Episode 3300 | Reward:  140.37 | Eval:  191.79 | Loss: 22.5564 | Q:  29.81 | ε: 0.192  
Episode 3400 | Reward:  114.08 | Eval:  269.40 | Loss: 23.2846 | Q:  32.40 | ε: 0.183  
Episode 3500 | Reward:  166.33 | Eval:  244.32 | Loss: 21.8558 | Q:  32.51 | ε: 0.174  
Episode 3600 | Reward:  150.80 | Eval:  265.42 | Loss: 21.6430 | Q:  33.18 | ε: 0.165  
Episode 3700 | Reward:  148.59 | Eval:  239.56 | Loss: 23.8328 | Q:  34.65 | ε: 0.157  
Episode 3800 | Reward:  162.82 | Eval:  233.36 | Loss: 28.3445 | Q:  37.46 | ε: 0.149  
Episode 3900 | Reward:  177.70 | Eval:  259.99 | Loss: 36.2971 | Q:  40.22 | ε: 0.142  
Episode 4000 | Reward:  156.60 | Eval:  251.17 | Loss: 46.7266 | Q:  42.15 | ε: 0.135  
======================================================================  
 Training complete!

Dueling DQN:分离值和优势

很多状态下,选哪个动作其实差别不大。CartPole 里杆子刚好平衡时,向左向右都行;开车走直线方向盘微调的结果差不多;LunarLander 离地面还远的时候,引擎怎么喷影响也有限。

标准 DQN 对每个动作单独学 Q(s,a),把网络容量浪费在冗余信息上。Dueling DQN 的思路是把 Q 拆成两部分:V(s) 表示"这个状态本身值多少",A(s,a) 表示"这个动作比平均水平好多少"。

架构如下

 标准 DQN:  
 Input -> Hidden Layers -> Q(s,a₁), Q(s,a₂), ..., Q(s,aₙ)  

Dueling DQN:  
                       |-> Value Stream -> V(s)  
Input -> Shared Layers |  
                       |-> Advantage Stream -> A(s,a₁), A(s,a₂), ..., A(s,aₙ)  
                      
 Q(s,a) = V(s) + (A(s,a) - mean(A(s,·)))

为什么要减去均值?不减的话,任何常数加到 V 再从 A 减掉,得到的 Q 完全一样,网络学不出唯一解。

数学表达如下:

 Q(s,a) = V(s) + A(s,a) - (1/|A|)·Σₐ' A(s,a')

也可以用 max 代替 mean:

 Q(s,a) = V(s) + A(s,a) - maxₐ' A(s,a')

实践中 max 版本有时效果更好。

举个例子:V(s) = 10,好动作的 A 是 +5,差动作的 A 是 -3,平均优势 = (+5-3)/2 = +1。那么 Q(s, 好动作) = 10 + 5 - 1 = 14,Q(s, 差动作) = 10 - 3 - 1 = 6。

实现

 classDuelingQNetwork(nn.Module):  
    """  
    Dueling DQN 架构,分离值和优势。  
      
    理论: Q(s,a) = V(s) + A(s,a) - mean(A(s,·))  
    """  
      
    def__init__(  
        self,  
        state_dim: int,  
        action_dim: int,  
        hidden_dims: List[int] = [128, 128]  
    ):  
        """  
        初始化 Dueling Q 网络。  
          
        Args:  
            state_dim: 状态空间维度  
            action_dim: 动作数量  
            hidden_dims: 共享层大小  
        """  
        super(DuelingQNetwork, self).__init__()  
          
        self.state_dim=state_dim  
        self.action_dim=action_dim  
          
        # 共享特征提取器  
        shared_layers= []  
        input_dim=state_dim  
          
        forhidden_diminhidden_dims:  
            shared_layers.append(nn.Linear(input_dim, hidden_dim))  
            shared_layers.append(nn.ReLU())  
            input_dim=hidden_dim  
          
        self.shared_network=nn.Sequential(*shared_layers)  
          
        # 值流: V(s) = 状态的标量值  
        self.value_stream=nn.Sequential(  
            nn.Linear(hidden_dims[-1], 128),  
            nn.ReLU(),  
            nn.Linear(128, 1)  
        )  
          
        # 优势流: A(s,a) = 每个动作的优势  
        self.advantage_stream=nn.Sequential(  
            nn.Linear(hidden_dims[-1], 128),  
            nn.ReLU(),  
            nn.Linear(128, action_dim)  
        )  
          
        # 初始化权重  
        self.apply(self._init_weights)  
      
    def_init_weights(self, module):  
        """初始化网络权重。"""  
        ifisinstance(module, nn.Linear):  
            nn.init.kaiming_normal_(module.weight, nonlinearity='relu')  
            nn.init.constant_(module.bias, 0.0)  
      
    defforward(self, state: torch.Tensor) ->torch.Tensor:  
        """  
        通过 dueling 架构的前向传播。  
          
        Args:  
            state: 状态批次, 形状 (batch_size, state_dim)  
          
        Returns:  
            q_values: 所有动作的 Q(s,a), 形状 (batch_size, action_dim)  
        """  
        # 共享特征  
        features=self.shared_network(state)  
          
        # 值: V(s) -> 形状 (batch_size, 1)  
        value=self.value_stream(features)  
          
        # 优势: A(s,a) -> 形状 (batch_size, action_dim)  
        advantages=self.advantage_stream(features)  
          
        # 组合: Q(s,a) = V(s) + A(s,a) - mean(A(s,·))  
        q_values=value+advantages-advantages.mean(dim=1, keepdim=True)  
          
        returnq_values  
      
    defget_action(self, state: np.ndarray, epsilon: float=0.0) ->int:  
        """  
        使用 ε-greedy 策略选择动作。  
        """  
        ifrandom.random() <epsilon:  
            returnrandom.randint(0, self.action_dim-1)  
        else:  
            withtorch.no_grad():  
                state_tensor=torch.FloatTensor(state).unsqueeze(0).to(  
                    next(self.parameters()).device  
                )  
                q_values=self.forward(state_tensor)  
                 returnq_values.argmax(dim=1).item()

Dueling 架构的好处:在动作影响不大的状态下学得更好,梯度流动更通畅所以收敛更快,值估计也更稳健。

还可以把两种改进叠在一起,做成Double Dueling DQN

 classDoubleDuelingDQNAgent(DoubleDQNAgent):  
    """  
    结合 Double DQN 和 Dueling DQN 的智能体。  
    """  
      
    def__init__(  
        self,  
        state_dim: int,  
        action_dim: int,  
        hidden_dims: List[int] = [128, 128],  
        **kwargs  
    ):  
        """  
        初始化 Double Dueling DQN 智能体。  
        使用 DuelingQNetwork 而不是标准 QNetwork。  
        """  
        # 暂不调用 super().__init__()  
        # 我们需要以不同方式设置网络  
          
        self.state_dim=state_dim  
        self.action_dim=action_dim  
        self.gamma=kwargs.get('gamma', 0.99)  
        self.batch_size=kwargs.get('batch_size', 64)  
        self.target_update_freq=kwargs.get('target_update_freq', 10)  
        self.device=torch.device(kwargs.get('device', 'cpu'))  
          
        # 探索  
        self.epsilon=kwargs.get('epsilon_start', 1.0)  
        self.epsilon_end=kwargs.get('epsilon_end', 0.01)  
        self.epsilon_decay=kwargs.get('epsilon_decay', 0.995)  
          
        # 使用 Dueling 架构  
        self.q_network=DuelingQNetwork(  
            state_dim, action_dim, hidden_dims  
        ).to(self.device)  
          
        self.target_network=DuelingQNetwork(  
            state_dim, action_dim, hidden_dims  
        ).to(self.device)  
          
        self.target_network.load_state_dict(self.q_network.state_dict())  
        self.target_network.eval()  
          
        # 优化器  
        learning_rate=kwargs.get('learning_rate', 1e-3)  
        self.optimizer=torch.optim.Adam(self.q_network.parameters(), lr=learning_rate)  
          
        # 回放缓冲区  
        buffer_capacity=kwargs.get('buffer_capacity', 100000)  
        self.replay_buffer=ReplayBuffer(buffer_capacity)  
          
        # 统计  
        self.episode_count=0  
        self.training_step=0  
      
     # update() 方法继承自 DoubleDQNAgent

优先经验回放

不是所有经验都同等有价值。TD 误差大的转换说明预测偏离现实,能学到东西;TD 误差小的转换说明已经学得差不多了再采到也没多大用。

均匀采样把所有转换一视同仁,浪费了学习机会。优先经验回放的思路是:让重要的转换被采到的概率更高。

优先级怎么算

 pᵢ = |δᵢ| + ε  
 
 其中:  
 δᵢ = r + γ·max Q(s',a') - Q(s,a)   (TD 误差)  
 ε = 小常数,保证所有转换都有被采到的可能

采样概率:

  P(i) = pᵢ^α / Σⱼ pⱼ^α  
   
 α 控制优先化程度:  
 α = 0 -> 退化成均匀采样  
 α = 1 -> 完全按优先级比例采样

优先采样改了数据分布,会引入偏差。所以解决办法是用重要性采样比率来加权更新:

 wᵢ = (N · P(i))^(-β)  
   
 β 控制校正力度:  
 β = 0 -> 不校正  
 β = 1 -> 完全校正

通常 β 从 0.4 开始,随训练逐渐增大到 1.0。

实现

 classPrioritizedReplayBuffer:  
    """  
    优先经验回放缓冲区。  
      
    理论: 按 TD 误差比例采样转换。  
    我们可以从中学到更多的转换会被更频繁地采样。  
    """  
      
    def__init__(self, capacity: int, alpha: float=0.6, beta: float=0.4):  
        """  
        Args:  
            capacity: 缓冲区最大容量  
            alpha: 优先化指数(0=均匀, 1=比例)  
            beta: 重要性采样指数(退火到 1.0)  
        """  
        self.capacity=capacity  
        self.alpha=alpha  
        self.beta=beta  
        self.beta_increment=0.001  # 随时间退火 beta  
          
        self.buffer= []  
        self.priorities=np.zeros(capacity, dtype=np.float32)  
        self.position=0  
          
    defpush(self, state, action, reward, next_state, done):  
        """  
        以最大优先级添加转换。  
          
        理论: 新转换获得最大优先级(会很快被采样)。  
        它们的实际优先级在首次 TD 误差计算后更新。  
        """  
        max_priority=self.priorities.max() ifself.bufferelse1.0  
          
        iflen(self.buffer) <self.capacity:  
            self.buffer.append((state, action, reward, next_state, done))  
        else:  
            self.buffer[self.position] = (state, action, reward, next_state, done)  
          
        self.priorities[self.position] =max_priority  
        self.position= (self.position+1) %self.capacity  
      
    defsample(self, batch_size: int):  
        """  
        按优先级比例采样批次。  
          
        Returns:  
            batch: 采样的转换  
            indices: 采样转换的索引(用于优先级更新)  
            weights: 重要性采样权重  
        """  
        iflen(self.buffer) ==self.capacity:  
            priorities=self.priorities  
        else:  
            priorities=self.priorities[:len(self.buffer)]  
          
        # 计算采样概率  
        probs=priorities**self.alpha  
        probs/=probs.sum()  
          
        # 采样索引  
        indices=np.random.choice(len(self.buffer), batch_size, p=probs, replace=False)  
          
        # 获取转换  
        batch= [self.buffer[idx] foridxinindices]  
          
        # 计算重要性采样权重  
        total=len(self.buffer)  
        weights= (total*probs[indices]) ** (-self.beta)  
        weights/=weights.max()  # 归一化以保持稳定性  
          
        # 退火 beta  
        self.beta=min(1.0, self.beta+self.beta_increment)  
          
        # 转换为 tensor  
        states, actions, rewards, next_states, dones=zip(*batch)  
          
        states=torch.FloatTensor(np.array(states))  
        actions=torch.LongTensor(actions)  
        rewards=torch.FloatTensor(rewards)  
        next_states=torch.FloatTensor(np.array(next_states))  
        dones=torch.FloatTensor(dones)  
        weights=torch.FloatTensor(weights)  
          
        return (states, actions, rewards, next_states, dones), indices, weights  
      
    defupdate_priorities(self, indices, td_errors):  
        """  
        根据 TD 误差更新优先级。  
          
        Args:  
            indices: 采样转换的索引  
            td_errors: 那些转换的 TD 误差  
        """  
        foridx, td_errorinzip(indices, td_errors):  
            self.priorities[idx] =abs(td_error) +1e-6  
      
    def__len__(self):  
         returnlen(self.buffer)

生产环境会用 sum-tree 数据结构,采样复杂度是 O(log N) 而不是这里的 O(N)。这个简化版本以可读性为优先。

DQN 变体对比

几个变体各自解决什么问题呢?

DQN 是基线,用单一网络选动作、评估动作。它引入了目标网络来稳定"移动目标"问题,但容易过估计 Q 值,噪声让智能体去追逐根本不存在的"幽灵奖励"。

Double DQN 把选和评拆开。在线网络选动作,目标网络评估价值。实测下来能有效压低不切实际的 Q 值,学习曲线明显更平滑。

Dueling DQN 换了网络架构,单独学 V(s) 和 A(s,a)。它的核心认知是:很多状态下具体动作的影响不大。在 LunarLander 这种存在大量"冗余动作"的环境里,样本效率提升明显——不用为每次引擎脉冲都重新学状态值。

Double Dueling DQN 把两边的好处结合起来,既减少估计噪声,又提高表示效率。实测中这个组合最稳健,达到峰值性能的速度和可靠性都优于单一改进。

实践建议

变体选择对比

Double DQN 跑得比 DQN 还差?可能是训练不够长(Double DQN 起步偶尔慢一点),或者目标网络更新太频繁,或者学习率偏高。这时可以将训练时间翻倍,target_update_freq 调大,学习率砍 2-5 倍。

Dueling 架构没带来改善?可能是环境本身不适合(所有状态都很关键),或者网络太小,或者值流/优势流太浅。需要对网络加宽加深,确认环境里确实有"中性"状态。

PER 导致不稳定?可能是 β 退火太快、α 设太高、重要性采样权重没归一化。可以减慢 β 增量、α 降到 0.4-0.6、确认权重做了归一化。

首选 Double DQN 起步,代码改动极小,收益明确,没有额外复杂度。

什么时候加 Dueling:状态值比动作优势更重要的环境,大量状态下动作值差不多,需要更快收敛。

什么时候加 PER:样本效率至关重要,有算力预算(PER 比均匀采样慢),奖励稀疏(帮助关注少见的成功经验)。

最后Rainbow 把六项改进叠在一起:Double DQN、Dueling DQN、优先经验回放、多步学习(n-step returns)、分布式 RL(C51)、噪声网络(参数空间探索)。

多步学习把 1-step TD 换成 n-step 回报:

 # 1-step TD:  
 y = rₜ + γ·max Q(sₜ₊₁, a)  
   
 # n-step:  
 y = rₜ + γ·rₜ₊₁ + γ²·rₜ₊₂ + ... + γⁿ·max Q(sₜ₊ₙ, a)

好处是信用分配更清晰,学习更快。

小结

这篇文章从 DQN 的过估计问题讲起,沿着 Double DQN、Dueling 架构、优先经验回放等等介绍下来,每种改进对应一个具体的失败模式:max 算子的偏差、低效的状态-动作表示、浪费的均匀采样。

从头实现这些方法,能搞清楚它们为什么有效;很多"高级" RL 算法不过是简单想法的组合,理解这些想法本身才是真正可扩展的东西。

https://avoid.overfit.cn/post/4c5835f419d840b0acb0a1eb72f92b6f

作者: Jugal Gajjar

前言

大家好,我是阿甘,“奔跑中cpp / c++”,知识星球的创始人

今天给大家分享分享,我们星球同学一起整理的,同时也在不断更新的,cpp / c++相关岗位面经。

全网最全收集

字节客户端一面---剪映

自我介绍

专利拷打、为什么选择程序员

1.对称协程与非对称协程的区别呢

2.非对称协程使用场景,你的非对称协程如何实现的无感调用
3.LRU与LFU的区别以及web为什么要选择LFU

4.定时器实现的底层数据结构师什么,想对于其他方法有什么好处呢?

5.定时器有那些接口

6.reactor和proactor

7.智能指针share_ptr的使用是线程安全的吗?

8.对于zmq协议的理解与使用场景,你这个实现批次仿真是用的那种模式?为什么想到多进程通信用这个为什么其他方法不满足,里面的心跳是如何做的。

9.多进程有其他通信方法吗?

10.管道有几种他们用于什么通信?

9.在浏览器输入一个网址会发生什么,

10.提到的DNS是什么,如何工作的,

11.如果输入localhost和127.0.0.1有什么区别呢

手撕k个翻转链表

网易有道C++软件开发实习生一面

1.指针和引用的区别是什么?

2.你刚刚说到指针不安全能具体说说吗?(因为说了使用引用比使用指针更安全)

3.内存泄漏之前遇到过吗?怎么解决的呢?

4.你刚刚说用容器来管理内存?他会帮你释放资源吗?(因为说了使用容器来管理内存)

5.栈和堆内存的分配特点是什么呢?

6.你对多态的理解是什么?

7.类中有虚函数和一个整型成员变量,实例化一个对象的大小是什么?

8.C++11的新特性是什么呢?

9.你对左值右值的理解是什么?(因为新特性介绍到了auto说到了左值右值)

10.现在最常用的三个智能指针的概念和区别是什么?

11.原子变量你用使用过吗?(因为之前回答说到了原子变量)

12.Lambda 表达式用过没有?

13.linux使用过吗?

分布式云存储项目:

1.断点续传怎么实现?

2.用了QT是吧,熟悉QT的一些机制吗?

上一段实习经历的内容

手撕:反转字符串中的单词

博雷顿一面

1.封装、多态

2.智能指针

3.http和https的区别

4.线程同步

5.多线程

6.auto

7.虚拟内存(物理->虚拟 虚拟->物理)

8.tcp和udp的区别

9.三次握手

10 四次挥手

11.git 怎么合并拉取

12.nginx是干啥的

14.线程通信

15.shared_ptr的构建那个合理(给的代码)

16.thread函数的构成

中科创达物联网-c++开发-一面

自我介绍

1、如果定义一个函数在main函数之前运行该怎么做呢?

2、如果要定义一个全局变量该如何考虑呢?

3、协程库项目中的定时器的颗粒度是如何定义的呢?基于了那些条件呢?

4、const和define有什么区别?static有什么区别呢?

5、比较感兴趣你们在飞行仿真项目中的合作方式

6、联调的时候会有扯皮的时候吗?

7、描述一下osi七层网络模型?ping属于那一层

8、你本科时候学过单片机吗?stm32的启动方式有哪几种?hal库和标准库的区别?

9、多态,静态多态和动态多态是如何实现的,虚函数指针存储在那个区域

无手撕

智驾大陆 系统开发实习生

1.介绍项目

2.本来可以用proc方式获取数据,为什么要用内核模块?

3.内核模块用什么代码编写的?

4.内核模块如何加载?

5.epoll select poll ?

6.linux线程和进程的区别?

7.linux如何远程登录服务器?

8.linux如何从服务器拷贝文件下来?

9.研究方向,这边的工作如何帮助到你的研究?哪方面的深入

6.对应用开发还是内核开发感兴趣?为什么?学好有什么帮助吗?

8.介绍业务

9.反问

本文由mdnice多平台发布