阅读视图

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

周末澳门 City Walk

天气不太好,飘着毛毛雨,阴沉沉的。今天无法去爬山,索性到澳门走走,没有什么目的,纯粹的在大街小巷里面瞎逛。还是选择我最喜欢的湾仔口岸坐船过海。这里过关的人非常少,船票25元/人,准时开船,3分钟左右到达对岸。下船后就可以直接逛,走15分钟可以到葡京。可以“湾仔口岸”公众号买票,“掐点”到口岸。如果从拱北过关,弯弯绕绕,30分钟可能还在关闸,然后还得等车转公交。

周末澳门 City Walk-雅余

下船后,穿过小巷子,可以看到墙上不少涂鸦。

周末澳门 City Walk-雅余

周末澳门 City Walk-雅余

周末澳门 City Walk-雅余

周末澳门 City Walk-雅余

周末澳门 City Walk-雅余

不时还会路过一些小街道,“里”。“里”是指双向都开口,一些较长的巷道。

周末澳门 City Walk-雅余

周末澳门 City Walk-雅余

周末澳门 City Walk-雅余

周末澳门 City Walk-雅余

还有一些夹在古老建筑中间时尚的商场。

周末澳门 City Walk-雅余

在澳门瞎逛,你还会遇到不少教堂,都很精致。可以专题走一次,会收获不少。

周末澳门 City Walk-雅余

周末澳门 City Walk-雅余

周末澳门 City Walk-雅余

周末澳门 City Walk-雅余
大疯堂

周末澳门 City Walk-雅余

周末澳门 City Walk-雅余

午饭时间,已经在澳门瞎逛了 15000 步,急需填补肚子。好不容易走到一家评分4.9的茶餐厅,结果没开门。正当我对着门口纳闷的时候,一位路过的澳门本地阿伯跟我说这家逢周日休息。周日游客最多的时候居然休息!!好心的阿伯给我指路,说本地人喜欢去一个“街市”(菜市场)吃饭。吃饭的地方在“街市”上面,有电梯,右转再左转,如果左转再右转怕我找不到,听得我云里雾里。

终于,我们在关帝古庙的边上找到了这个地方。

周末澳门 City Walk-雅余

吃饭的地方就位于这栋“营地街市市政综合大楼”上面,实在看不出来。

周末澳门 City Walk-雅余

上到3楼后,发现别有洞天,真的是本地人的食堂。香味扑鼻,价格实在,如果你想找地道的吃食,建议你来试试。

周末澳门 City Walk-雅余

周末澳门 City Walk-雅余

周末澳门 City Walk-雅余

周末澳门 City Walk-雅余

周末澳门 City Walk-雅余

周末澳门 City Walk-雅余

上午10点过澳门,下午2点半回,结束周末澳门 City Walk,一共走了21000步。

“街市”以上照片 By 理光 GR3
“街市”后的照片 By iPhone 12 Pro Max

GTC 2025 见闻

参加了 NVidia GTC (GPU Technology Conference),由于英伟达的地位,这会也已经成了 AI 开发者最大的交流会,很多公司和业内人士都会过来分享、交流,大概写下会议中相关见闻感受。

Keynote

老黄没提词器洋洋洒洒讲了两个多小时,出了小状况还会开个小玩笑,大佬范很足,也满满的理工男既视感,非常多的数字和未经包装的细节,不过感觉会讲得有些啰嗦。

总的来说,核心论证的是世界对 GPU 诉求会越来越大,而 NVidia 在 GPU 这个领域会持续遥遥领先

GPU诉求

计算机的核心从 CPU 转向 GPU,上个时代依靠程序员写代码指挥 CPU 执行指令解决问题,构成了现在庞大的 IT 产业,程序员是中心。现在的时代逐渐转变,GPU 生产的 token 逐渐能解决越来越多的问题,能思考,能生成代码指挥 CPU 去执行解决问题,计算的核心一定会转向 GPU,世界对 GPU 的需求只会越来越高。

给 AI 分了四个阶段,Perception AI → Generative AI → Agentic AI → Physical AI,不是很认同,Agentic 和 Physical 都是 Generative AI 的延续,不过无所谓,可以看到 Agentic 这个概念实在是火爆。

Scaling Law 没有停止,Agentic AI 需要深度思考,深度思考有新的 Test-time Scaling Law,越多的 token 输出效果越好需,要多轮理解和工具调用对 token 的消耗更是指数级上涨。

Physical AI 要更好地理解现实世界,声音/视觉/触感,都会比纯文本思考对 token 消耗的诉求更高,像 2G 时代看文字新闻,3G 4G 图片,5G 视频一样。

这两个发展中的领域对 GPU 的需求只会越来越高,Deepseek 做的优化也不足以影响这个需求的增长,这个市场不容质疑。

NVidia 优势

GPU 需求量是高,但未来大家一定会买 NVidia 卡吗?当然。NVidia 这一代 blackwell 算力是 hopper 的 68 倍,下一代计划明年推出的 Rubin 算力是 hopper 的900 倍,一年一迭代,远比摩尔定律快的速度,还做了大量的大规模部署的优化,省电、稳定,号称买越多,省越多,赚越多,竞对看起来会很难追上。这些论述还是挺能让人 buyin 的。

Agentic AI

Agent 的相关 session 有接近 200 个,Agent 集合了几个元素:

  1. 概念火,一些涉及 Workflow/RAG 什么的 AI 应用都统一称为 Agent 了,GenAI 在各行业的落地都可以冠以 Agent 的名义,跟以前 H5 那样,不纠结于具体定义,只要有一个统一称呼。
  2. 人群广,Agent 目前主要是在上层的工程架构上,大量的工程师都能理解、参与讨论、建设,不像基础模型训练,多数人难以参与。
  3. 应用广,非研发也能大概听得懂,涵盖了 AI 在各行业的应用这个课题,各行业都会有兴趣了解 Agent 是什么,自己业务上能怎么用。

所以 Agent 相关的 session 大部分都很热门。听完一些的感受:

  1. 多数做企业服务、云的公司都在卷 Agent 的基建和解决方案,像基础设施公司 Fireworks AI、Nebius,数据库公司 Couchbase、datastax,企业服务公司 serviceNow、Dropbox,新兴公司 huggingface、langchain、langflow 等,都来分享推广在 Agent 这事上能提供的能力和服务。
  2. Agent 相关的建设都在刚起步,基本都是在分享概念、工程问题的优化和应用方案,没看到有涉及模型训练去优化 Agent 效果上限的相关分享。Agent 的一些关键课题上一篇文章有提到,基本差不太多。
  3. 也没有讨论 Agent 在工程和模型上的界限,后续端到端的模型进步,能吃掉多少 Agent 能做的事?这两天 4o 的图生成出来后,预计后面才会有更多的讨论。

NVidia AI 基础服务

NVidia 作为领头羊,是希望自己能覆盖 AI 全链路基础设施的,大力在 AI 的每一层都提供了相关框架、服务、能力,这次会议上也有非常多的分享和推广。

其中跟 AI 应用 / Agent 相关的几个基建:

  1. BluePrint:应用蓝图。给了很多 AI 应用场景的 example 工作流(也称为 Agent),例如 PDF 转博客、数字人应用等,提供工作流架构、数据集、源码,可定制,供开发者快速参考和部署。
  2. NIM(NVIDIA Inference Microservices**)**:模型推理。把模型推理封装在 Docker 容器里,可以直接快速部署,对外提供标准化API。也封装了模型在不同 GPU 型号下的优化,提升性能效率。
  3. NeMo(Neural Modules):模型训练。提供了相关工具用于构建、定制、训练 AI 模型,训练后的模型可以通过 NIM 部署。
  4. AgentIQ:开源 Agent 开发套件,支持组合链接不同框架创建的 Agent,提供性能 profiler、评估、UI 界面等工具。

这些基建的声量比较低,国内没怎么见到,不确定海外使用情况怎样。

多个 session 都在推广 NVidia 的 Video Search and Summarization Agent,串联从视频的获取→分割→VLM识别、CV物体识别和跟踪→数据处理存储和RAG召回→用户对话 整个流程,做到可以对视频提供实时分析和报警,也可以自然语言交互查询视频内容,边缘部署,适合用于监控,算是用 NVidia 技术栈做 AI 应用的一个标杆范例。

AIGC

关注了下视频 AIGC 相关的几个 Session

  1. 在好莱坞干了几十年的视觉效果的 Ed Ulbrich 开了个公司 Metaphysic,以前的电影特效制作成本巨大,对人的处理还很难跨过恐怖谷,而基于 AI 技术做特效,用完全不同的技术栈,效果好成本低,是一种颠覆。metaphysic 给娱乐行业提供人脸替换、数字人的服务,看起来是用的 GAN,在人物换脸技术上,GAN 还是更能做到稳定和实时,特别是实时这个点,基于 diffusion 很难做到。基于市场需求,利用已有的不同技术(甚至是上一代技术)深入解决问题,是有空间的。
  2. PixVerse Co-Founder 在一次对话中聊到,视频实时生成的能力差不多要 ready 了,目前 5 秒的视频可以做到5-10秒推理完成,可能会解锁新的人跟视频的交互方式。不确定质量怎样,质量达到一个阈值,以前设想的很多类似 自定义剧情走向 的新玩法新交互有很大空间。
  3. Adobe 和 OpenSora 都来分享了视频生成模型的训练和推理的方案和优化,鉴于已经不是SOTA模型,可参考性不高。TCL 分享了AI电影制作,很惊讶这公司竟然在做这个,更多的是在做链路串联,而不是端到端的视频模型。

其他

  1. OpenAI 只来了两个人给 blackwell 架构站站台,Anthropic 一个人也没来,从这上看,这行业最领先的技术还是很 close,毕竟是核心竞争力,而且很容易被复刻,不像上个时代,大规模并发架构等技术,更重的是实践中解决具体问题,大方案分享了问题不大。(所以 DeepSeek 开源最领先的技术带来的冲击才会那么大。)
  2. DeepSeek 就是 Reasoning Model 的代名词,开源模型的顶流,出镜率极高,老黄的 keynote、各种演讲里都有它的身影,而 llama 通常是作为上一代开源模型与它做对比,只要是提供开源模型部署服务的公司(HuggingFace/Fireworks等),分享里都会对 DeepSeek 极度推崇。
  3. 遇到不少学生来参加,有的来找方向,看看业界前沿在做什么,做学术交流,找合作机会,这个会是挺合适的。清华、中科大、SJSU。最大的问题是实验室没有足够的卡,这领域是必须校企合作,实验室才进行得下去了。
  4. 使用 Nvidia Jetson 做边缘计算也是预期后续空间比较大的方向,设备端部署模型,可以提升实时性和隐私性,多数分享是用在具身智能上,还有一个分享的场景是在货架上实时分析用户行为,更精准推送广告。
  5. 机器人、自动驾驶的 session 也很多,数字孪生是提得比较多的(用 AI 生成仿真环境,用于机器人训练),但现场没看到什么能震惊人的机器人,包括老黄演讲时演示的类 wall-e 机器人,惊艳不够,这一行感觉还早。

总体感受,眼花缭乱,人潮纷杂,在开拓视野以外,大会更多是一个社交场所,推广产品/技术/服务,促进合作,这类大会需要的是多创造一些面对面交流的机会。

花絮

  1. 现场有限量的原价 5080、5090,知道时已经不可能排队买到。
  2. 跟七年前参加 WWDC 在同一个地方,估计一直还是同一个承办公司,午餐还是那么难吃。
  3. 参观 NVidia 工区,老黄作为华裔也是信风水的,新办公楼会模拟依山傍水的设计,风水好。NVidia 搞渲染出身,渲染里三角形是最基本单元,所以办公楼都是三角形元素。办公环境很宽敞,但没啥人,总部居家办公没有限制,很多都不来公司。

LangChain 作者聊 AI Agent 的几个相关课题

参加 NVIDIA GTC 会,其中一场听了 LangChain 的作者 Harrison Chase的分享《AI Agents in Production Insights and Future Direct》,聊了 Agent 当前遇到的一些问题和他的想法,包括 Planing,UX,Memory,Reliability,Deployment,Multi-Agent,也结合我的理解说说这几个课题。

Planing

任务规划是 Agent 的核心,这个课题是进展比较多的,业界解决得相对比较好,核心是 o1/r1 推理模型的出现和不断增强,让规划能力上了一个台阶,这也是 agent 能起来的基础。

但模型本身目前解决不了所有问题,还需要工程上的一些策略和串联做优化。例如 Tree of Thought 让任务不是以线性一步步执行的形式,而是生成解决问题的多个节点,多角度思考问题,形成树结构的任务,评估节点的价值,在里面寻找最优解。 Reflexion 会有 Evaluator 对各种反馈(工具调用结果/模型输出/用户指令)进行反思,梳理改进方向,也会把反思结果作为知识库经验,指导后续的任务。

这些策略链路是需要有一个工程流程把他们串起来的,这个工程链路的构建也是 Agent 在 Planing 能不能做好的关键因素,langgraph 和众多 Agent 框架服务都持续在做这个事。

UX

Agent 的交互应该是怎样的?

Devin 多窗口,有聊天框发送指令、又能实时看到 Agent 在怎样用浏览器、命令行、编辑器,是不错的交互。

大部分 Agent 会是后台异步运行的模式,可以让它直接跑在后台,在需要人类给出反馈处理的,用类似邮件 inbox 的方式交互,Agent 发邮件给你等待指示,你回复邮件给输入。

相较于交互界面形态,交互的策略可能更关键。Agent 在执行任务过程中,

  1. 用户是否应该能随时中断并提出新的指示?
  2. Agent 应该在什么时候暂停任务等待用户反馈再进行下一步?
  3. 用户指示应该用表单一次性收集,还是一步步收集?

如果做每一步都要用户反馈做指示,那是非常枯燥不好用的,如果完全不需要用户反馈,那做出来的东西可能不符合用户预期的概率高很多。模型应该能做好这里的交互策略,但目前还没看到有特别好的实践。

Memory

长时记忆是个有意思的话题,杨立昆在对话中也有提到,记忆这个课题是值得研究的方向,现在是缺乏突破和讨论的。

现在的 Agent,普遍都只有知识库 RAG 而没有记忆,记忆不是知识库,或者说知识库只是记忆的一种。

记忆应该跟人类一样,模型能记住和学习交互过程中用户给到的信息和偏好,在每次推理过程中发挥作用。

它跟 UX 相关,如果模型能理解记住用户偏好,用户的反馈交互就可以减少。

它也跟 System Prompt 的优化相关,System Prompt 是激活了模型按某个方向去做推理预测,记忆也应该是在模型推理的过程中发挥作用。

简单做的话记忆可以作为 System Prompt 的一部分去影响模型,更彻底的可能应该是能持续内化到模型内,或者以新的模型架构去做这事。

现在的应用场景还没到记忆是必选项的程度,但要做 AGI 或者要 Agent 好用这块必不可少。

Reliability

主要是指 Agent 能不能稳定地解决同一个(或同一类)问题。

Agent 跟之前的软件工程不同,受限于模型输出的不稳定,整个系统的可靠性是远不如传统工程的,用户输入同样的或差不多的需求,agent 不一定每次都能解决问题。

模型输出的,一是会受用户对任务描述的影响,可能描述不准确,可能会有歧义。二是受模型本身不够聪明的影响,近期模型能力越来越好,解决了部分问题,但仍是不稳定。

保持 Agent 输出的稳定性,是一个非常需要持续迭代优化的工程,搭一个 demo 容易,持续优化难。

Agent 节点多,需要能看清每个任务节点的详细情况,有问题时知道问题出在哪里,需要有效果评估的测试能力,也需要框架有能力比较方便地在过程中对模型的输出进行评估实时纠错,提升稳定性,这些配套 langchain 相关生态都提供了,NVidia 这次开源的 AgentIQ 框架也基本涵盖了,还有很多框架服务也在做。

Deployment

Agent 要在线上跑,相关部署基建现在也还没有很完善,它跟传统工程链路还是有一些区别,主要是链路长、耗时长、成本高。提供 Agent 部署的服务应该针对这几个特性做好相关基础设施。

  1. 稳定性:整个 agent 链路很长,每一个环节调用如果成功率是 99%,平均要调用十次接口的 agent 成功率就只有90%,而大模型的接口往往也不稳定,如何保证成功率? 重试策略、排队机制等,这些都是 agent 工程基建应该做的事。
  2. 性能:当前 agent 处于效果大于耗时的阶段,只要效果好,五分钟输出还是十分钟输出都可以接受,但真正规模化应用起来时,性能问题肯定也是重点,整个链路耗时太长,可优化空间会比较大,NVidia 对 agent 的分享也提到了,很多任务不一定要串行做,可以并行化节省整体耗时。
  3. 监控: Agent 线上跑的效果怎样,准确率多高,有没有安全风险,应该有直接可用的相应配套。
  4. 成本:如果 Agent 全程用最好的模型,跑一次十几分钟的任务可能要几美元的成本,前期问题不大,效果优先,粗放式探索,后续真能规模化上线应用,成本这里的优化空间会比较大,用不同的专家小模型处理不同的任务、做好模型 – GPU 卡适配优化推理(NVidia NIM 提供了相关能力),都是可优化的方向。

Multi-Agent

预期后续会有非常多的 Agent 出现,Agent 跟 Agent 之间如果能相互联系,能形成新的智能体,但 Agent 之间 应该怎样通信?

这里的通信不止是把 Agent 当成一个黑盒,给指令 – 输出结果,而是能深入 Agent 内部的通信,上下文共享、中间步骤共享、过程中的协作、用户操作插入等。

目前没有一个标准,各项目都是自己的一套,业界可能需要这样一个标准,能实现把使用不同框架、不同服务上部署的 agent 连接起来。

MCP 是近期在快速发展的标准协议,很有前景,但它只是把工具工具调用标准化了,对 Agent 和 Agent 相关的协作是没有定义的,可能需要另外的协议。

上一篇文章刚好探讨了这个内容,用 Agent as Tool 的方式,把 Agent 当成工具的一种,基于 MCP 去做,好处是架构简单,Agent 可复用性高。

但它只把 Agent 当成黑盒 Tool 去使用,给指令 → 输出结果,Agent 之间更深入的联系是没有的。我们也在尝试,给这个 MCP 子 Agent 输入主 Agent 的上下文,同时这个子 Agent 也可以流式把每步处理过程上下文输出给主 Agent,这样就可以实现 Agent 之间的上下文共享。同时也可以继续做更深入的交互定义,比如子 Agent 与用户反馈交互的流程协议。

目前这些协议都需要自定义,但以 MCP 、 以 Agent as Tool 去定义标准的 Agent 间交互协议,也是可行的,MCP 可以把这套交互协议也定了,可能是 Anthoropic 很好的机会。


上述这些基本是工程上的事情,这次 GTC 很少有人讨论到 Agent 在数据收集/模型调优上的实践,基本是直接使用基础通用模型,但要提升 Agent 的上限,应该是需要专有模型并能支持端到端训练的形态,待探索。

从 AI 编年史到继续发呆

这是一篇来自近期工作发呆时的思考(说做是总结和记录可能更为恰当)。

从2024年的中下旬开始,公司就一直和我们铺垫说有一个客户在项目交付中出现了一些问题,由于项目合同的金额较大,客户在内部沟通的过程中要求我们将一部分项目的回款使用线下交付的方式进行验收,说白了就是有一部分同事需要到客户现场去驻场。于是我们就在年后冲向北京,这篇文章也是我在工作业余时的胡思乱想。

熟悉我的朋友应该都知道,我在工作中时不时要参与到一些诸如客户支持,定价沟通,产品价值talking 的环节中,可能是这两年大家都把 AI 作为了“年度话题”的重要性,所以总会有一些客户想要进一步了解“产品如何在实际的业务流中快速集成 AI 的能力”,市面上也有各种各样吹嘘“自己的产品又一次集成了 AI”的 PR 文章,但本质上其实大都是在云市场集成 AI 之后快速实现了一个 chatbot,好像效果并没有那么好。

当然也有一些客户会来问一些在不同视角的问题,我听过的问题印象比较深的就是“产品集成了 AI 能力我是认可的,但是这个产品中我看不到 Deepseek 的露出,你们怎么处理”,“在产品中集成 LLM 其实各家都大差不差,但是差异性的效果我暂时还没有看到”,此外在一些类似的产品中我发现 C2C(Copy to china) 的思路目前可能还是奏效的,去 ProductHunt 或者类似的网站看看国外的“同行们”又搞出来了哪些 AI 相关的应用,然后看看哪一个最适合集成到自己的项目中,砍掉一些复杂功能再做一些本地化,好像给自己的产品也就搭上了 AI 这趟快车。

有一些产品会说到自己在业务中使用 MCP(Model Context Protocol)和 RAG(Retrieval-Augmented Generation)来提供更加全面的大模型能力支持,从逻辑上来说在产品代码中能够真的提升效率和准确度,基于一些比如 Dify 或者 FastGPT 的产品做二次开发好像也能做到进一步的实践与尝试(没错,我们的产品也提供了这样的能力),但从最终愿意买单并且用于真实企业内部业务流程的状态来看,我觉得大家更多是想摸着石头过河再观望看看有哪些商业化的思路。

昨天和同事聊天的时候说到不同行业中的门槛其实还比较高,可能互联网行业的从业者大都掌握了无痛访问 Google 或者 Github 等网站的方式,但其实还有非常多的老百姓不太分得清其中的区别(事实上互联网从业者也不见得都掌握了这个能力),对于老百姓来说耳熟能详的张一鸣和王兴兴是那种“在某一个行业中实现了成功的例子”,但是对他们到底在做什么其实并不清楚,其实说到 AI,说到人工智能,这应该是一个伴随计算机有 N 多年历史的故事了。

但是 AI 到底是咋来的?好像前些年我们对 AI 的理解和认知还停留在 TensorFlow 和 Pytorch 这样的算法中,怎么一眨眼 AI 就已经飞入寻常百姓家了?

既然聊到了这里,我就来试试讲讲 AI 发展的一系列关键人物(万一说错,还请拍砖)

图灵,计算机能否像人一样思考?

1940 年,二战如火如荼,德国的“恩尼格玛(Enigma)”密码机几乎让所有盟军的情报系统陷入瘫痪。英军情报部门召集了一群数学家,他们的任务是——破解 Enigma,让德国的情报不再是个谜。

这群数学家中,有一个瘦高、害羞但聪明绝顶的年轻人,他叫艾伦·图灵。

他不是普通的数学家,他构想了一种“通用计算机”——一种可以执行任何计算任务的机器,并用它来破解 Enigma。他发明了“炸弹机(Bombe)”,最终成功解码了德军密码,让二战提前结束了两年。

然而,他并不满足于此。他问了一个更大的问题:

“如果机器能够进行计算,是否意味着它也能思考?”

他提出了著名的“图灵测试”——如果一个人无法区分是在与人还是与机器对话,那么机器就具备了“智能”。这个想法为现代人工智能奠定了基础。

大多数人最快捷大概了解图灵的方式就是那一部由本尼迪克特·康伯巴奇主演的“模仿游戏”,在二战期间图灵在英国政府的雇佣下破解了德军的“恩尼格码”密码机,由此也奠定了现代计算机科学的基础。在他 16 岁的时候就开始阅读爱因斯坦的相关著作,在他 19 岁的时候就考入了剑桥大学开始攻读数学本科,并且在22 岁时候以优异的成绩毕业。

虽然图灵是一名数学家,在学习数理逻辑学(就是我们学的那个“与”,“非”,“或”等等的学科)的时候又开始对逻辑学,哲学进行了更加深入的研究。但虽然图灵奠定了人工智能的哲学基础,也提出了计算理论与 AI 的测试标准,但由于同性恋的原因受到迫害,在 41 岁的时候英年早逝。

冯诺依曼,计算机如何高效存储和计算?

如果说图灵是计算机科学的哲学家,那么冯·诺依曼(John von Neumann)就是计算机的工程师。

在 1945 年,他提出了一种全新的计算机架构:把数据和程序存储在同一个内存里,让计算机可以自动执行指令。这就是后来所有计算机都遵循的“冯·诺依曼架构”,它让计算机变得真正实用。

除了计算机,他还发明了博弈论,并且是最早研究人工智能如何决策的人之一。

我相信每一个计算机相关专业的同学应该都听过冯诺依曼,比如在计算机原理的课程上肯定会学到他提出的冯诺依曼架构。此外他也提出了能让程序指令和数据能够存储在同一个存储器中的存储程序概念,从而让计算机可以自动执行程序。

值得一提的是冯·诺伊曼从小就以过人的智力与记忆力而闻名。他在一生中发表了大约150篇论文,其中有60篇纯数学论文,20篇物理学以及60篇应用数学论文。他最后的作品是一个在医院未完成的手稿,后来以书名《计算机与人脑》(The Computer and the Brain)发布,表现了他生命最后时光的兴趣方向(但其实冯诺依曼不仅在计算机方向有建树,他也是博弈论之父)。

罗森布拉特,能否让计算机自己学习?

1958 年,弗兰克·罗森布拉特提出了一个让整个 AI 领域兴奋的想法——“感知机(Perceptron)”,它是一种最简单的神经网络,可以通过调整权重来学习模式,比如识别简单的形状。“创造具有人类特质的机器,一直是科幻小说里一个令人着迷的领域。但我们即将在现实中见证这种机器的诞生,这种机器不依赖人类的训练和控制,就能感知、识别和辨认出周边环境。”

然而,1969 年,闵斯基(Marvin Minsky)和派普特(Seymour Papert) 在《感知机(Perceptrons)》一书中证明,感知机无法解决像“异或”这样的基本问题,这让整个 AI 研究陷入了“AI 冬天”,神经网络被主流科学界抛弃。这本书抨击了罗森布拉特的工作,并本质上终结了感知机的命运。

罗森布拉特没能渡过AI的寒冬。1971年,他在43岁生日那天,在切萨皮克湾(Chesapeake Bay)乘单桅帆船出海时溺水身亡。

理论上来说,感知机其实是第一个尝试让机器“学习”的模型,但它的失败让神经网络沉寂了 20 年,直到 x辛顿重新挖掘它。

闵斯基与佩帕特,感知机的局限性是什么?

1956 年,达特茅斯会议 上,一群科学家聚在一起,试图定义“人工智能” 这个领域。其中,闵斯基作为 MIT 人工智能实验室的创建者,是符号主义 AI 的坚定支持者。

他的梦想很宏大:“AI 应该像人一样思考,我们只要给它足够的逻辑规则,它就能成为真正的智能。” 他的研究主要基于符号逻辑,比如他开发了一种叫做 Lisp 机器 的计算机,专门用来运行 AI 代码。

与此同时,佩珀特则更加关注机器学习和儿童教育,他认为计算机应该像孩子一样学习,而不是依赖固有规则。他发明了一种编程语言——Logo,可以让孩子通过简单的指令控制“小乌龟”在屏幕上画图形。他们二位的 AI 研究,让 AI 在 1960 年代成为了学术界的明星,政府和企业纷纷投资,AI 似乎要迎来一个黄金时代!

但好景不长,感知机(Perceptron) 的失败让闵斯基和佩珀特觉得,神经网络完全没戏。他们在 1969 年合著了一本书——《Perceptrons》,直接指出了感知机的致命缺陷“感知机无法解决“异或(XOR)”问题——也就是说,它没办法学会“如果 A 和 B 相同,输出 0,否则输出 1” 这样的简单逻辑。”

他们的批评毁灭性地打击了神经网络研究,导致 1970 年代 AI 研究资金骤减,进入了第一次“AI 冬天”。

虽然闵斯基和佩珀特让神经网络陷入低谷,但他们的研究也推动了 AI 其他方向的发展。

闵斯基继续研究“心智架构”,提出了“框架理论”(Frame Theory)——AI 应该拥有类似人类的知识结构,而不是单纯的数据处理器。佩珀特专注于教育领域,创造了建构主义学习理论,他的 Logo 语言影响了后来的 Scratch 和 Python 在教育领域的应用。

直到 1980 年代,辛顿通过反向传播算法解决了感知机的问题,才让神经网络重新崛起。但讽刺的是,闵斯基并不认同深度学习,他仍然认为符号 AI 才是未来。

费根鲍姆,AI 能否模仿人类专家?

在 1960-1970 年代,人工智能的主流研究方向是通用智能(General AI),也就是让机器能像人一样思考。但费根鲍姆另辟蹊径,他提出了一个完全不同的想法:

“我们不需要让 AI 变得像人一样聪明,我们只需要让 AI 变得像‘某个领域的专家’一样聪明。”

他认为,与其让 AI 学会所有事情,不如让它深耕某一个领域,积累大量的专业知识,成为一个真正的“专家”。这就是“专家系统(Expert System)”的概念——基于规则、逻辑推理和专业知识,让 AI 在特定领域内表现出专家级的能力。

费根鲍姆的第一个专家系统项目是1965 年的DENDRAL,它是一个帮助化学家分析分子结构的 AI。紧接着在1970 年又推出了MYCIN——一个医疗诊断专家系统。虽然由于当时的法律和伦理问题使得医生不敢完全相信机器的诊断,这个产品也没真的用在医院中,但它的成功证明了 AI 可以在专业领域中成为真正的专家。


珀尔,AI 如何进行不确定性推理?

在 20 世纪 80 年代,人工智能主要依赖概率统计和模式识别,但它无法理解因果关系。朱迪亚·珀尔认为,真正的智能必须知道“为什么”——比如,吸烟和肺癌有关,但到底是因果关系,还是仅仅相关?

他提出了贝叶斯网络,用数学方式描述变量之间的因果联系,让 AI 具备更强的推理能力。后来,他又发展出因果推理和反事实思维,让 AI 不仅能预测,还能回答“如果情况不同,结果会怎样?”。这些理论如今影响着数据科学、医疗 AI、经济学,甚至推动下一代更智能的 AI 发展。

珀尔的因果推理思想,彻底改变了 AI 的研究方向。过去,AI 主要依赖深度学习,但神经网络的一个问题是它们只会发现模式,而不会理解因果。

比如传统 AI 可能发现:夏天卖冰淇淋的同时,游泳馆的溺水率也会上升。但因果 AI 知道:冰淇淋不会导致溺水,真正的原因是夏天气温升高。他的著作《为什么(The Book of Why)》深入探讨了因果推理的重要性,这为现代 AI 的解释能力奠定了基础。

杰弗里辛顿,如何训练深度神经网络?

1970 年代,神经网络研究遭遇寒冬。当时的主流 AI 研究者(如闵斯基和佩珀特)认为神经网络太简单,无法解决复杂问题。许多科学家纷纷放弃,但辛顿偏偏选择了这条“错误的道路”。

辛顿出生于英国,外祖父是著名数学家 George Boole(布尔代数的创始人),他从小就喜欢挑战权威。在攻读博士期间,他研究反向传播算法(Backpropagation),一种可以让神经网络自动调整权重的方法。尽管这个算法早已在 1970 年被提出,但几乎没人相信它真的能让 AI 学习。Hinton 和他的团队坚持优化反向传播,并在 1986 年成功证明它可以让多层神经网络高效学习复杂任务。

90 年代,辛顿继续探索更深层的神经网络,并提出受限玻尔兹曼机(RBM) 和 深度信念网络(DBN),成为“深度学习”(Deep Learning)概念的奠基人之一。到了 2012 年,他的学生 Alex Krizhevsky 使用卷积神经网络(CNN) 赢得 ImageNet 竞赛,标志着深度学习时代的正式到来。

后来,辛顿还提出了 Transformer 的早期雏形——胶囊网络(Capsule Network),并成为 Google Brain 的重要研究员,推动 AI 革命。他的坚持让神经网络从 20 世纪的冷门理论,变成了今天席卷全球的 AI 基石。

1980 年代,辛顿和他的团队证明了一个重要理论——“反向传播(Backpropagation)”,可以让神经网络通过调整权重进行学习。但当时的计算机性能不够强大,神经网络仍然没能流行起来。

时间来到 2012 年,Hinton 的学生 Alex Krizhevsky 训练了一种深度卷积神经网络(AlexNet),在 ImageNet 竞赛上击败了所有传统算法。这标志着深度学习的崛起,AI 从此进入了一个全新的时代!

杨立昆,计算机如何识别图像?

在 20 世纪 80 年代,计算机视觉仍然是个难题。杨立昆认为,人工智能不应依赖手工设计的规则,而应该“像人一样”通过学习数据自动提取特征。他结合反向传播算法和神经网络,发明了卷积神经网络(CNN),让计算机能自动识别图像中的模式。

90 年代,他的 CNN 被用于手写数字识别,并成为美国银行支票识别系统的一部分。但深度学习当时还不够流行,他的研究一度被冷落。直到 2010 年后,计算能力的提升让 CNN 迎来爆发,成为计算机视觉的核心技术,被广泛应用于人脸识别、自动驾驶和医疗影像分析。

如今,杨立昆继续推动 AI 向自监督学习发展,试图让 AI 更接近人类的大脑学习方式,而不仅仅依赖海量数据进行训练。

杨立昆的原来中文译名为:扬·勒丘恩,2017年他在中国的演讲提供了正式的中文姓名。他法文的姓是(Le Cun),到美国之后,很多人都误认为Le是中间名,所以他在20世纪八九十年代把自己的姓的拼法改成了LeCun。

本希奥,AI需要遵循伦理吗?

本希奥与辛顿和杨立昆并称为“深度学习三巨头”。他是神经网络研究的先驱之一,推动了深度学习的数学基础,并对无监督学习、序列建模、注意力机制(Transformer 的前身)等领域作出了重大贡献。

Bengio 1964 年出生于法国的一个知识分子家庭,后来随家人移民到加拿大。他在蒙特利尔大学攻读计算机科学博士学位,师从 AI 研究者 René D. Mori,并开始专注于神经网络的学习方法。当时,神经网络在学术界并不被看好,但本希奥坚信它们能够超越传统的统计机器学习方法。

在 2000 年代,本希奥率先研究如何让神经网络自动学习数据的抽象特征,并提出了逐层训练(layer-wise pretraining)的方法,使得更深层的网络能够高效训练。这为后来的卷积神经网络(CNN)和递归神经网络(RNN)奠定了数学基础。他的研究极大地推动了深度学习的复兴,影响了 ImageNet 竞赛的突破(2012),并为后来的 Transformer 架构铺平了道路。

2014 年,本希奥的团队提出了注意力机制(Attention Mechanism),这是一种让神经网络自动关注最重要信息的技术。这项技术很快被 Google 研究员 Vaswani 等人发展为 Transformer 架构,并成为GPT-4、BERT、Claude 以及几乎所有现代 LLM 的基础。

可以说,本希奥间接塑造了现代大模型,他的研究影响了 AI 在自然语言处理、计算机视觉等领域的所有突破性进展。

与辛顿和杨立昆不同,本希奥在 AI 伦理和社会责任方面表现得更加谨慎。当 ChatGPT 这样的 LLM 开始爆发时,他曾公开警告AI 可能会对社会产生巨大影响,呼吁制定更严格的 AI 监管和伦理框架。

2018 年,他与辛顿、杨立昆共同获得了图灵奖(计算机领域的最高荣誉),正式确立了他在 AI 领域的历史地位。

尽管他是深度学习最重要的奠基人之一,但他并没有像 OpenAI 或 DeepMind 那样主导商业化 AI 公司的发展。他的研究主要在学术界,而他的许多学生(如 Transformer 论文作者 Vaswani)却推动了 AI 工业化的浪潮。

瓦普尼克,如何找到最优分类方式?

在 20 世纪 60 年代的苏联,数学家瓦普尼克和他的导师 Alexey Chervonenkis 共同研究如何让机器像人一样学习。他们意识到,AI 不能只死记硬背训练数据,而应该学会“泛化”——即用有限的经验推断新的知识。这促使他们提出统计学习理论(Statistical Learning Theory, SLT),并发明了支持向量机(SVM)。

SVM 的核心思想是找到数据之间的最优分界线,使得新数据也能被正确分类。这个方法在 90 年代被西方计算机科学界发现,并迅速成为机器学习的主流算法之一,在文字识别、生物信息学、金融分析等领域大放异彩。

尽管 SVM 一度是机器学习的黄金标准,但瓦普尼克对深度学习持保留态度,认为它依赖海量数据而缺乏理论上的优雅。他的理论为现代 AI 奠定了数学基础,使机器学习不再是经验主义,而成为一门严谨的科学。

瓦普尼克是一位纯数学派的科学家,支持向量机(SVM)在 1990s 成为了机器学习领域的标准方法。在深度学习出现之前,SVM 在很多任务上都被认为是最强的学习算法之一。

霍普菲尔德,神经网络如何进行联想记忆?

在 20 世纪 80 年代的人工智能研究领域,神经网络几乎被主流学术界遗忘。许多研究者转向了符号主义 AI(Symbolic AI)或专家系统(Expert Systems),但霍普菲尔德这个本职是物理学家的科学家却意外地为神经网络带来了一次重要的复兴。

霍普菲尔德早年是一位研究凝聚态物理的学者,他的兴趣集中在复杂系统如何自组织。在 1982 年,他提出了一种全新的能量模型,即霍普菲尔德神经网络,这是一种受物理学自洽场理论启发的神经网络模型。他证明了这个网络可以用来进行联想记忆(Associative Memory),即只需要输入部分信息,网络就能恢复出完整的模式。这种方法不同于传统的符号 AI,而是模拟了大脑神经元的工作方式。

霍普菲尔德神经网络的提出激发了 AI 研究者对神经网络的兴趣,为 1980 年代后期的神经网络复兴铺平了道路。辛顿和杨立昆等后来的 AI 研究者也深受他的影响。

尽管霍普菲尔德主要贡献在物理学领域,他的跨界工作却成为神经网络历史上的关键节点,让 AI 研究重新回到了仿生学的道路上。

霍普菲尔德神经网络在数学上证明了这个网络一定能够收敛,从而对基于神经网络的人工智能产生了奠基性的影响,开启了连接主义深度学习的大门。

施密德胡伯,如何让神经网络记住长期信息?

施密德胡伯是深度学习领域最重要的奠基者之一,他的研究直接影响了现代 AI,尤其是在自然语言处理和序列数据建模中的应用。他最著名的贡献之一就是LSTM(长短时记忆网络),这项技术后来成为谷歌、苹果、OpenAI 以及众多企业训练神经网络的核心方法。

施密德胡伯生于 1963 年,从小就展现出极高的数学天赋。他在瑞士学习计算机科学和人工智能,很早就对人工智能的终极目标产生了浓厚兴趣——创造一个能够自主学习、不断进化的人工智能。

在 20 世纪 90 年代,神经网络在处理长序列数据(如文本、语音和时间序列数据)时遇到了“梯度消失”问题:传统的循环神经网络(RNN)无法记住过长时间跨度的信息。

1997 年,施密德胡伯和他的学生 Sepp Hochreiter 共同发明了长短时记忆网络(Long Short-Term Memory,LSTM),这种架构通过引入“门控机制”来有效存储和传递信息,解决了梯度消失的问题。这项发明在当时并没有被广泛认可,但在 2010 年代,随着计算能力的提升和大规模数据训练的普及,LSTM 迅速成为语音识别、机器翻译、文本生成等领域的主流技术。

施密德胡伯的野心远远不止于 LSTM,他一直强调创造真正的通用人工智能(AGI)。他认为 AI 研究应该专注于元学习(meta-learning),即让 AI 学会如何自主学习,并不断优化自身。

他提出了“人工科学家”(Artificial Scientist)这一概念,认为 AI 未来应该能够自主提出假设、设计实验,并发现新的知识,就像真正的科学家一样。

尽管施密德胡伯的贡献不可否认,他的知名度远远低于辛顿、杨立昆和本希奥,部分原因是 LSTM 的商业应用直到 2010 年代才开始爆发。此外,他曾多次公开表达对 DeepMind 和 OpenAI 的不满,认为这些机构“没有给予他的研究足够的认可”。

尽管如此,施密德胡伯仍然是现代 AI 领域不可忽视的奠基者。今天的 GPT-4、Suno 音乐 AI、DeepMind 的 AlphaFold 等许多应用都间接或直接受益于他的研究,他的 LSTM 仍然在许多 AI 系统中发挥作用。

在 AI 发展的历史中,施密德胡伯是一个极具远见的人,他不仅改变了深度学习的技术基础,也为未来的 AGI 研究提供了重要的方向。

古德费洛,AI 能否创造新内容?

古德费洛 1985 年出生于美国怀俄明州,他从小展现出非凡的数学和编程才能。大学时期,他就读于斯坦福大学,主修计算机科学。随后,他进入加拿大蒙特利尔大学,师从深度学习三巨头之一的本希奥,正式踏入神经网络研究领域。

在本希奥的实验室里,他接触到了深度学习和生成模型,并开始探索如何让 AI 生成逼真的图像。这一探索最终促成了GAN(生成对抗网络)的诞生。

2014 年,古德费洛在博士研究期间的一次讨论中,和同事争论如何让神经网络自主生成更真实的图像。当时的 AI 生成模型(如变分自编码器 VAE)仍然很难生成高清且自然的图片。

突然,他灵光一闪,想出了一个革命性的概念:让两个神经网络互相竞争!他的想法是:一个 AI(生成器,Generator) 负责生成假图像。另一个 AI(判别器,Discriminator) 负责判断这些图像是真实的还是伪造的。二者不断博弈,最终生成器能骗过判别器,生成高度逼真的图像!

这种“对抗学习”的方式,突破了传统 AI 生成方法的局限,被命名为 GAN(Generative Adversarial Network)。

GAN 让 AI 从“分析数据”变成了“创造数据”,彻底改变了 AI 在艺术、设计、游戏、影视等行业的应用方式。可以说,他的研究让 AI 从理解世界进化到了创造世界,并成为 AI 生成内容(AIGC)浪潮的奠基者之一。

古德费洛不仅是GAN 之父,也是AI 伦理的重要倡导者,他的贡献将长期影响 AI 发展方向。

达里奥,AI 能否像人类一样写作和推理?

达里奥是 AI 研究领域的重要人物之一,曾在 OpenAI 领导多个关键项目,后创办 Anthropic,专注于 AI 安全与“AI 对齐”研究。他的工作推动了 AI 模型能力的飞跃,同时也让 AI 伦理问题进入公众视野。

达里奥最初是一名神经科学家,研究大脑与神经网络的相似性。他后来转向机器学习,加入 OpenAI,成为 AI 研究的核心人物之一。他在 OpenAI 期间的关键贡献包括:GPT-2 与 GPT-3 研究负责人:推动了现代大语言模型(LLM)的发展。AI 对齐研究的先驱:他提出 AI 需要“对齐人类价值观”,否则可能失控。

2021 年,达里奥离开 OpenAI,与几位前同事共同创立 Anthropic,专注于 AI 安全和“可控 AI”研究。Anthropic 的核心产品 Claude 系列(类似 ChatGPT)强调安全性,避免 AI 生成危险内容。他的研究强调:“AI 必须对人类有益,否则超级智能可能带来无法预测的后果。”

Anthropic 目前是 OpenAI 的主要竞争对手之一,并获得了 Google 近 30 亿美元的投资。

当然,说到达里奥我们其实也需要提到 Tom B Brown 和 Alec Radford,他们一行三个人的研究共同塑造了现代 AI 发展路径。但我想他们从 OpenA I跳槽到 Anthropic 也许还是遇到了那个难以抉择的问题“是追求更强大的 AI,还是追求更安全的 AI”?

算是本文的尾巴

写到这里其实我有点累了,事实上人工智能发展过程中总不是一帆风顺的,有兴趣的朋友可以看看维基百科上的“人工智能史”,我相信一定会觉得收获满满,在这些厉害的科学家中也会存在各种奇怪的冲突(也正常,大家毕竟都是人嘛)。

但如果我们回到 2025 年的当下,会发现 AI 的发展已经度过了“通用智能”的探索阶段,下一步可能还是要对准通用人工智能的方向进行进一步的细化和延伸。由于各类基于 Claude 3.7 的产品我们已经基本跳过了“AI 行不行”的疑惑,但到底“如何让他更安全,更有效”还是一个短期内我们看不到答案的问题。

前一段时间木遥的解读“vibe coding”在朋友圈和各种渠道刷屏,文章中那句“一方面它犹如神助,让你有一种第一次挥舞魔杖的幻觉。另一方面它写了新的忘了旧的,不断重构又原地打转,好像永远在解决问题但永远创造出更多新的问题,并且面对 bug 采取一种振振有词地姿态对你 gaslighting。你面对着层出不穷的工具甚至不知道自己该认真考虑哪个,心知肚明可能下个月就又有了新的「最佳实践」,养成任何肌肉记忆都是一种浪费,而所谓新的最佳实践只不过是用更快的速度产出更隐蔽的 bug 而已。”可能也是许多正在与 AI 结对编程朋友的真实感觉。

但我想,AI 带来的改变确实日新月异,我能看到身边的朋友能够逐渐完成“不相信 AI → 怀疑 AI → 全部用 AI → 不敢信任 AI → 再一次信任 AI”的无限循环之中。我在一些业余时间也尝试练手用 AI 帮我写了几个产品,相比原先的产品设计与研发过程中,会发现现在的 AI 可能每一次都会比前一段时间的使用更加流畅一些,但依然无法完全避免上下文遇到限制导致记忆力幻觉或者相关的问题,这种感觉好像就像是一种慢性毒药,一方面更爽了,另一方面又不是那么爽。在产品设计过程中各种刷屏的什么“用 AI 搞定原型图,搞定高保真效果图”的论据其实也能变相让我们感知到 AI 在具体业务中的应用其实还处在比较早期的阶段。

一方面我受益于使用 AI 能够极大程度加快我把脑海中的某些想法付诸于实践的过程,但另一方面好像也能明显感知到过拟合带来的某种不适感,在开启新项目的时候确实能够通过 AI 极大程度加快效率,但是否会因为过度信任 AI 而导致代码中潜藏了许多暂时没有精力与时间发现的 bug,又变成代码中一个潜藏的问题真的很难一两句话讲清楚。

如果对比我前端时间那篇《AI 取代人工进展走到哪一步了?》,当下的我结论还是那句“保持对前沿技术学习与了解,让自己不要落伍的概念是没问题的,用 AI 来输出一下自己无处安放的创造力或者做一些创新与变化的真实落地是很好的”,但 AI 改变世界的进度条到哪一步了?

我觉得还得再看看。

聊聊 Agent 架构 – Single Agent / MCP / Multi-Agent

近期在业务中尝试落地 Agent,有一个架构设计问题,应该用单 Agent 架构,还是多 Agent 架构?

Single Agent

先来看看单 Agent 架构,在之前的文章里,OpenHands 这里的架构是典型的单 Agent 架构,依赖一个模型,组织多个工具调用,做好 ReAct 和上下文管理,整个过程很简单。

  1. Tools 是一个个函数,定义和调用都是在当前程序里进行。Tools的函数定义会作为 System Prompt 的一部分让 LLM 理解当前可用工具
  2. Memory 分两部分:
    1. 当前 Session 数据流,包括每一步执行了什么,结果是什么,在当前 Session 内存中保存,随时全量输入 LLM,让 LLM 判断下一步应该做什么。
    2. 用户的长期数据、知识库,例如用户在平台的偏好数据、领域内容、多轮对话上下文等,这些内容会从向量数据库召回。
  3. Router 中心化程序调度整个过程,拿用户 Prompt / System Prompt / Memory 输入 LLM,LLM 进行深度思考和给出具体执行的任务,Router 去调用对应的 Action 函数。

这是简单通用的单 Agent 架构,实现 Agent 中 Thought – Plan – Action – Reflection(Thought) 的循环,一个模型负责所有事情。

MCP

上述架构里,Tools 模块有一些小问题:工具函数可维护性和可扩展性不太好,多了后难管理,要加函数得更新主程序,另外得自己定义一个 Function call 规范,对外部的一些会用到的工具服务都需要自己封装一遍。

对这些小问题,这个架构可以有一个优化:Tool 模块从 Agent 剥离出来,用 MCP 协议统一管理和实现。

附:MCP是什么?

MCP 是 Anthropic 24年11月推出的协议,近期 Cursor / windsurf / cline 等一众 AI Coding 产品支持了 MCP 后出圈,众多开源框架也开始支持 MCP,大有统一的趋势。

MCP 的概念很简单,就是统一了工具调用的接口规范,这几张图可以帮助理解:

  1. MCP 统一了各工具能力接入的接口调用定义,原先一个服务(例如slack)要对接多个用户端产品(例如cursor)定义的 Function call 格式,现在服务和客户端统一对接同一种格式就行,两边都只需要实现一次。
  1. MCP Server 独立运行在任意服务器上,也可以有自己独立的数据库信息/资源,不与 Agent 服务器绑定,可复用、易于插拔。

把原先 Tool 几个工具函数调用用 MCP Server 封装,架构变成这样:

跟原先纯 Function call 的区别在于架构上更灵活,包括:

  1. 聚类,对零散的一个个函数可以统一放到一个服务,便于管理。
  2. 解耦调用实际发生在各自 MCP 服务端,而不是 Agent 服务直接去调用,部署扩展工具与 Agent 工程上解耦。
  3. 内聚:MCP Server 本身可以内聚做一些事,包括独立的资源管理、独立上下文等。
  4. 复用:通用协议,Tool 能力便于在多个 Agent 间接入复用,外部生态有较多现成 MCP Server 可直接接入。
  5. 统一:客户端、云端的工具调用,都可以用统一的 MCP 协议去实现。

这个架构似乎已经可以满足大部分场景下对 Agent 的诉求,为什么还需要考虑 Multi-Agent?

Multi-Agent

考虑 Multi-Agent 最主要的问题是上下文过长

如果一个 Agent 能力足够强,它应该能完成需要非常多轮调用完成各种任务,这些任务的制定和执行结果全部塞在一个上下文里,可能会超出当前模型能理解和处理的范围

这时候,计算机工程的典型解决思路就是:分治模块化。把整体 Agent 能力拆分成解决一个个独立复杂任务的子 Agent,让这些 Agent 在它的范围内能有自主思考和自主行动能力。

从 Agent 的组成来说,必不可少的部分包括:

  1. 模型:独立的处理模型,可以跟其他 Agent 不同,称为专家模型。也可以相同,看需要。
  2. 上下文:独立的多轮 ReAct Loop 上下文管理,完成自己特定的任务
  3. System Prompt:对应任务制定特定的 System Prompt

而 Tools 可以不是 Agent 专用的,这个 Agent 需要什么 Tools,就注册什么 Tools。长时记忆/知识库也可以是多个 Agent 共用的。

架构会变成这样:

这样 Plan Agent 只专门制定计划,它需要知道的上下文是其他几个 Agent 能完成什么大的任务,至于他们调了什么工具怎么完成不用管,只需管它要结果,整个任务的上下文就被分出多个部分,每个 Agent 的上下文对另一个 Agent 可以是黑盒。每个 Agent 也可以有自己对应的模型,做独立的训练和 Prompt 调优。

这样是不是一个更优的架构?

  1. 它的好处是解决了上下文过长,模型处理不好的问题。
  2. 坏处也是很明显:整个架构是复杂化了,而效果也不一定好。多个 Agent 需要协同,Plan Agent 能获取的上下文信息变少了,它没有了更细粒度统筹规划整个任务的能力,变成一个偏项目管理的角色协调各方的工作,多人协作带来信息熵增大,组织效率低。

AI 的范式,可能不应该这样分治,可能大模型在对上下文的支持、细节信息的理解上会越来越好,能统筹把握好各项细节,把一个复杂任务完成,而不是像人类社会一样分工协作。这样对大模型来说,有足够的信息量能做规划/决策/反思,也更便于端到端的模型训练。

从号称泄漏的 Manus Prompt 来看,Manus 也没有 Multi Agent,所有能力包括工具函数都在一个上下文中定义,看起来目前也能跑得起足够复杂的任务。

所以如果项目在早期,没有遇到很明显的瓶颈,并不需要用 Multi-Agent 架构,用 Single Agent 简单的架构足够能做好。工程架构越简单,后续基础模型升级带来的增益越大。

基于 MCP 的(伪)Multi-Agent

再探讨下,如果在应用过程中已经发现上下文处理不过来的问题,或者某个任务的内部实现细节对整个任务无影响,或者三方都实现好了,那采用另一种伪 Multi-Agent 架构,也是可以考虑的方案:

例如对接 browser-base 实现更深度的 research 能力,需要多轮打开不同网页、判断资料收集是否完成、整理资料,有自己的 loop 、上下文和模型。但这个完全可以封装在一个 MCP 服务上,自行闭环完成多网页搜索和整理,不需要与原 Agent 流程上有更深入的交互。

跟上面的 Multi-Agent 架构的区别在于,并没有改变原来单 Agent 架构,不会增加架构复杂度。Agent 不需要感知 MCP 调用背后是 Agent 还是一个普通的函数调用,没有区别。

MCP 协议本身也是 SSE 流式输出,对这个背后是 Agent 的 MCP 调用,要输出多少上下文信息给原 Agent,也是可以非常方便地调控。

以上是近期的一些想法,Agent 是新东西,后续实践有认知的更新再分享。

我在苏州逛园子之狮子林

狮子林,苏州四大名园之一,代表元代的艺术风格。园林,园林,大多叫园,叫林的只有一个,就是狮子林。狮子林园内以假山叠石为主体,厅、堂、殿、阁、亭、选、斋、堂20余处,园中有9条假山山脉,21处洞穴,是中国古典园林中堆山最曲折,最复杂的一个,假山面积约占全园总面积的五分之一,面积达1100平方米,被誉为“假山王国”。乾隆下江南时曾六次到访狮子林,园中共有乾隆皇帝写的匾额16处,可见他对狮子林是喜爱至极。

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余
狮子林平面图,图片来自网络

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

狮子林为什么叫狮子林,大部份UP主解释为因为园内有很多假山形似狮子,所以叫狮子林。其实狮子林发端于禅林,是寺庙和园林的结合体。禅宗高僧天为纪念自己的师傅而取名“狮子林”。

元朝1341年,一位名叫天如的禅师来到苏州讲经传禅,天如禅师的弟子在娄门边的某处地方,见闹市中古木参天,觉得这里很适合修禅讲道,于是便在此置屋,建起了一处禅林给天如禅师布道之用。
来到苏州之前,天如禅师曾在浙江天目山狮子崖修行二十余载。而天如禅师的老师中峰明本,以及中峰明本的老师高峰原妙都是在狮子岩得道。
天如禅师将住所命名为“狮子林”,又称“菩提正宗寺”。狮子林,以“狮”同“师”,表明了不忘师祖之意。同时,狮子又名“狻猊”,是佛国之兽。而在古代,寺院又称丛林,简称“林”。这便是狮子林名字的由来。[原文]

我在苏州逛园子之狮子林-雅余
卧云室

卧云室位于指柏轩南面假山中央的平地中,如安卧于峰石间,取金元好问“何时卧云身,因节遂疏懒”诗句意名“卧云室”。其原为寺僧静坐敛心、止息杂虑的禅室。

亭内正中悬挂着天如禅师像。

我在苏州逛园子之狮子林-雅余
揖峰指柏轩

揖峰指柏轩是园内主要厅堂,楼式建筑,轩面对规整的小水池和湖石假山,山上罗列石峰石笋,山石缝中古木虬根盘绕。轩底层四周为回廊,楼上层缩进。轩面阔五间,黄瓜环脊歇山顶。

我在苏州逛园子之狮子林-雅余

真趣亭位于水池南岸,面对假山。其形体较大,结构特殊,亭内前二柱为花篮吊柱,后用纱隔成内廊,亭内天花装饰性强,扁作大梁上为菱角轩和船蓬轩,雕梁画栋,彩绘鎏金,鹅胫椅短柱柱头为座狮。亭内悬挂金底绿字乾隆御笔“真趣”匾。

我在苏州逛园子之狮子林-雅余
真趣亭

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

园林漏景、借景都很好看,专门拍了一些。

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余
网红拍照点,想象前面站个美女

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余
网红拍照点,想象前面站个美女

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

我在苏州逛园子之狮子林-雅余

此次狮子林游玩和旅行漫记差点可以偶遇,后来我又追到了上海,还是错过了。(纯属自编)如果要看狮子林的好片,可以去S兄博客。

好啦,2025 苏州园林之旅到此更新完毕。连续几天都在园子里逛,十分尽兴,都有点时空错乱了。

By iPhone 12 Pro Max(前一天玩得太累,回酒店忘记给相机电池充电, D-LUX8 备3个电池都是必要的。)

我在苏州逛园子之沧浪亭

沧浪亭是苏州存世最古老的园林,苏州四大园林之一,代表着宋代艺术风格,出自于北宋时期苏舜钦之手,曾经是名将韩世忠的住宅。相对留园拙政园,沧浪亭的造园艺术是别具一格的。未进园就有一池绿水环绕的园林外墙,然后一进门就可以看到一座假山屏障。园内以假山为主体,山下有开凿的水池,假山延伸的左侧石头山上有沧浪亭,然后山水之间以一条曲折的复廊相连。园内除了专门开凿的一个水池,就没有其他水源,它的水都巧妙的设计在了园外,是其一大特色。

我在苏州逛园子之沧浪亭-雅余
沧浪亭马路外的一个牌坊
我在苏州逛园子之沧浪亭-雅余
沧浪亭正门,正对可园
我在苏州逛园子之沧浪亭-雅余
园子正门两侧
我在苏州逛园子之沧浪亭-雅余
园子正门两侧
我在苏州逛园子之沧浪亭-雅余
园子正门的老树
我在苏州逛园子之沧浪亭-雅余
沧浪亭于1982年列为江苏省文物保护单位,2000年作为《世界文化遗产苏州古典园林增补项目》被联合国教科文组织列入《世界遗产名录》,2006年被国务院列为第六批全国重点文物保护单位。
我在苏州逛园子之沧浪亭-雅余
入门便见假山
我在苏州逛园子之沧浪亭-雅余
假山上眺望
我在苏州逛园子之沧浪亭-雅余
假山后的水池

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余
沧浪亭

沧浪亭匾额上三个字是清代文学家俞樾所书写,石柱上的楹联为“清风明月本无价,近水远山皆有情”。欧阳修曾在《沧浪亭》一诗中写道:清风明月本无价,可惜只卖四万钱。而苏舜钦在《过苏州》中有诗云:绿杨白鹭皆自得,近水远山皆有情。这副楹联便是清代梁章钜将这两句诗集为一联。

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余
复廊

临水处建复廊,以漏窗通透内外景物,使内外山水融为一体。

我在苏州逛园子之沧浪亭-雅余
复廊外的景色

清香馆内陈列一套树根家具,为清末之物,用福建榕树根精制,采其天然造型形有飞禽走兽图案,龙凤星祥形态。

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余
清香馆外的蜡梅

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

据统计,园内共有花窗108款,造型各不相同,活泼有趣,十分花心思。

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

通过这些花窗漏景,可以窥探园内的美丽景色。

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

沧浪亭另外一个特色就是竹子,据统计园内共计22种竹子种类,包括箬竹、苦竹、慈孝竹、毛环竹、湘妃竹、水竹、青秆竹、哺鸡竹等等。园内处处可见竹子,《沧浪亭记》中记载其周边环境:“前竹后水,水之阳又竹,澄川翠干,光影会合于轩户之间,尤与风月为相宜”“水得微径于杂花修竹之间”。智者乐水,君子师竹。翠竹潇洒清逸,代表了君子的翩翩风度。

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余
仰止亭

仰止亭,袭诗经“高山仰止,景行行止”之意而名。此为半亭,始筑于同治年间,其名与五百名贤祠有关,亭内嵌有御题文徵明小像石刻,是珍贵的历史文物。新亭建在遗址上,原亭为六棱六柱、六角形屋盖式凉亭,石木结构,高6米,周长20米,顶盖小青瓦、柱为红色、顶内盖板与花额窗为绿色,亭西一米靠山处立有“讲经台”石碑一块。

我在苏州逛园子之沧浪亭-雅余
翠玲珑

翠玲珑,又叫做“竹亭”,有三间房,另外连贯几间大小不一的旁室,南宋绍兴初韩世忠时就有其名,取苏子美诗“秋色入林红黯淡,日光穿竹翠玲珑”之意为名。

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

从翠玲珑的窗户往外看,四面都可以看到竹子。

我在苏州逛园子之沧浪亭-雅余
明道堂

明道堂,位于园内假山东南部,面阔三间,为清同治十二年巡抚张树声所创,袭苏舜钦《沧浪亭》中语“观听无邪,则道以明”之意而名。旧为会文讲学之所,此堂开敞四舍,宏伟庄严,是为园中主厅。

我在苏州逛园子之沧浪亭-雅余
明道堂前的院子
我在苏州逛园子之沧浪亭-雅余
明道堂

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余
遇到园内有跟拍,我也跟拍了一张

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

我在苏州逛园子之沧浪亭-雅余

沧浪亭相对小众,去的当天人非常少,所以在这里我拍的照片反而是最多的。沧浪亭以园外的理水,园内的竹林、复廊、借景和漏窗最为有特色。不像留园那么精致,没有拙政园的大气奢华,沧浪亭表达的是一种古朴而又不经细琢的山野之趣。

摄于2025年2月7日,By 徕卡 D-LUX8

我在苏州逛园子之拙政园

拙政园,与沧浪亭狮子林留园并称苏州四大园林,为苏州四大名园之首,代表着明代的艺术风格。拙政园又与北京颐和园、承德避暑山庄、苏州留园一起被誉为中国四大名园。拙政园占地面积78亩(52000平方米),建于明正德四年(公元1509)。拙政园由东园、中园、西园( 西部补园、中部拙政园、东园归田园居)以及住宅部分组成,住宅部分现为园林博物馆展厅。总体布局以水池为中心,中园最为出彩。

拙政园中“拙政”一词来源于潘安《闲居赋》“于是览止足之分,庶浮云之志,筑室种树,逍遥自得。池沼足以渔钓,春税足以代耕。灌园當蔬,供朝夕之膳;牧羊黏酪,侯伏腊之费。孝乎唯孝,友于兄弟,此亦拙者之为政也。”而拙政园从字面意思上看“拙”为笨拙之意,“政”指政治才能,“园”即园林之意。

我在苏州逛园子之拙政园-雅余
拙政园平面图,图片来自网络

拙政园的设计者是文徵明。当年王献臣诚邀文徵明为自己设计园林。文徵明素来清高,在书画界,一般人求画他都不应,何况是设计宅邸,但这次他因敬重王献臣的人品,而希望通过自己的设计,为其营造一方世外桃源,聊以慰藉。

我在苏州逛园子之拙政园-雅余
入胜
我在苏州逛园子之拙政园-雅余
通幽
我在苏州逛园子之拙政园-雅余
全景图

在造园中,构景要素有叠山、理水、建筑、植物四个元素,造景手法分抑景、添景、夹景、对景、框景、漏景、点景、借景等,我尝试把拍的照片归了一下类。个人觉得叠山是狮子林比较出彩,理水以沧浪亭为最佳,建筑可能留园更加精致,而拙政园各方面都还不错,造景手法运用更丰富一些。

理水

我在苏州逛园子之拙政园-雅余

我在苏州逛园子之拙政园-雅余

我在苏州逛园子之拙政园-雅余
“一波三折”,园林中往往“就曲避直”,以增情致。

我在苏州逛园子之拙政园-雅余

我在苏州逛园子之拙政园-雅余

我在苏州逛园子之拙政园-雅余

我在苏州逛园子之拙政园-雅余

叠山

我在苏州逛园子之拙政园-雅余

我在苏州逛园子之拙政园-雅余

我在苏州逛园子之拙政园-雅余

我在苏州逛园子之拙政园-雅余
海棠春坞

建筑

我在苏州逛园子之拙政园-雅余
梧竹幽居

我在苏州逛园子之拙政园-雅余

我在苏州逛园子之拙政园-雅余
绣绮亭
我在苏州逛园子之拙政园-雅余
香洲,“洲”与“舟”同音,它是一种舫。

我在苏州逛园子之拙政园-雅余

我在苏州逛园子之拙政园-雅余
松风亭
我在苏州逛园子之拙政园-雅余
小飞虹
我在苏州逛园子之拙政园-雅余
浮翠阁
我在苏州逛园子之拙政园-雅余
天泉亭
我在苏州逛园子之拙政园-雅余
十八曼陀罗花馆

曼陀罗树即山茶的别名,因为叶子类似茶叶,又可作饮,故得山茶名。

我在苏州逛园子之拙政园-雅余

我在苏州逛园子之拙政园-雅余
玉泉

植物

我在苏州逛园子之拙政园-雅余

我在苏州逛园子之拙政园-雅余
150年的古树
我在苏州逛园子之拙政园-雅余
腊梅,开得很好
我在苏州逛园子之拙政园-雅余
用手机多拍了一张

造景手法

我在苏州逛园子之拙政园-雅余
造园时“借景”远处的北寺塔,让人以为那是园中一部分。
我在苏州逛园子之拙政园-雅余
“框景”,框可方可圆。
我在苏州逛园子之拙政园-雅余
“框景”,框可方可圆。
我在苏州逛园子之拙政园-雅余
漏景是从框景发展而来,透过虚隔物看到的景象。

光影

我在苏州逛园子之拙政园-雅余
窗影
我在苏州逛园子之拙政园-雅余
树影

我在苏州逛园子之拙政园-雅余

我在苏州逛园子之拙政园-雅余
窗影
我在苏州逛园子之拙政园-雅余
树影
我在苏州逛园子之拙政园-雅余
竹影

古人官场的失意,不想出家门,“不出城市而得山林之性,逍遥自得而享闲居之乐”,借这些景这些物去抒发感情,一步一景都有其含义,什么季节去哪个亭子赏玩,配合什么植物花果,哪个亭子和哪个亭子对应,都是十分考究,前前后后花了二十年修建。景物建筑丰富,手法运用丰富,是实实在在的园林教科书。

摄于2025年2月6日,By 徕卡 D-LUX8

细看 Claude 3.7 两个重要的 Benchmark:SWE-Bench & TAU-Bench

Claude 3.7 Sonnet 在万众期待中推出了,为什么期待,因为从 Claude 3.5 Sonnet 发布后,一直是AI Coding Agent 领域最好的模型,综合效果没有对手,后面陆续推出的 o1/o3/DeepSeek 都没能撼动,更让人期待 Claude 3.7 Sonnet 在 AI Coding 领域能不能有进一步提升。

Claude 3.7 放出来的 Benchmark 里,有两个是跟 AI Coding Agent 表现强相关的:

  1. Agentic coding,SWE-bench,衡量解决实际软件工程编码问题的能力。
  2. Agentic tool use,TAU-bench,衡量理解用户意图调用工具执行命令的能力。

可以看到 SWE-bench 有显著的提升,问题解决率 49% 提升到最高 70%,TAU-bench 也有不错的绝对值10个点的提升,确实重点提升了 AI Coding Agent 相关能力。

接下来详细看看这两个 Benchmark 究竟测了什么,可以大致知道,目前模型的能力上限大概是怎样。

SWE-bench

SWE-bench 是由普林斯顿大学 NLP 团队开发的项目,23年10月就开始提出,主要是想找到一种方式可以评估大模型解决实际软件工程问题的能力,而不是像之前只衡量算法题的解决能力。当时还是 Claude 2 和 GPT4 的时代,随着 AI Coding 的逐渐火爆,OpenAI 也加入对这个 benchmark 的完善,这个项目也逐渐成为主流。

数据构造

分三步:

  1. 选靠谱的库:选了 12 个流行的 Python 开源库,选择的标准是,热门库,长期维护,有比较多的 Pull Request 修复相关 issue,PR 的管理也很规范,有很好的测试覆盖率。这些库修复 issue 的 PR 就是我们要获取的测试 case,但会对这些 PR 进行一些过滤。
  2. 特性过滤:1)明确 PR 是解决了某个特定问题。2) PR里包含了测试 case,可以很容易从测试 case 上判断代码修改是否有效。这些在运行前就能过滤出来。
  3. 运行时过滤:这些 PR 应用前后,测试用例中要有明确的从不通过到通过的变化,程序跑起来也不会有错误,便于评估结果。

基于上述规则从 github 热门项目上抽取相关的数据,这些数据还可以持续更新,避免模型因为看到过这些数据而“作弊”。

这是抽取的几个流行的 python 库,以及数据集数量:

经过上述步骤抽取构造数据后,得到 SWE-Bench 数据集,后来 OpenAI 对这个数据集再进行人工过滤筛选掉了一些不太好的 case,比如 issue 问题描述不准确、开发环境难搭建难测试等,也对每个挑选的 case 做了精心人工验证,一共500个样本,组成 SWE-bench_Verified 数据集,现在一般测的是这个数据集。

来看看这个数据集具体都由哪些部分组成:

instance_id: 实例ID,通常格式为 repo_owner__name-PR-number

//代码基本信息
repo: 仓库名
base_commit: PR 提交之前的代码 commit id,定位代码基线
version: 用于运行的版本号
environment_setup_commit: commit id,安装运行环境的代码基线
created_at: PR 创建时间

//PR基本信息
problem_statement: PR 对应的 issue 内容,也就是要解决的问题
test_patch: 这个 PR 提交的测试 case patch 代码
FAIL_TO_PASS: 应用修复的 PR 后会通过的测试 case
PASS_TO_PASS: 应用 PR 前和应用后,都应该通过的测试 case
patch: 这个 PR 修复的 patch 代码,相当于标准答案
hints_text: PR 提交之前,github 上对这个 issue 的讨论 comment。可选,如果要上榜单,禁止使用这个数据。

代码信息、问题描述、测试用例,重点是这几个,剩下的都是用于把程序跑起来、验证修复结果用。

测试执行

大体流程见下面这张图,输入 issue 描述和代码库,模型根据输入,输出要修改的代码,最后有个环境运行模型生成修改的代码,跑测试用例,把应用代码之前没跑通的单元测试跑通,这个任务就完成了。

这个过程一个最大的问题是:代码上下文怎么给模型?这几个热门项目代码库平均 43w 行,不可能直接给,需要有个检索的能力。

项目论文中给了两个方法:

  1. 作弊:上述构造数据时,我们有拿到人类修复这个 issue 提交的 PR 对应的 patch,而这个 patch 里修改到的代码文件,就是最重要的代码上下文,可以直接作为代码上下文给到模型。这个接近于标准答案,除了一些需要更多文件上下文才可以解的问题外。这个只用于做实验,或去检测其他的检索方式命中率如何。
  2. 稀疏检索:用 BM25 算法做检索,基于 issue 的描述搜索相关代码,限制长度在1.3万行-5万行。实验看起来,检索长度在2.7w行时,这种检索方法只有 40% 会命中上述 PR 对应的代码文件。

上述两个检索代码的方法,只是论文中做实验的参考,实际在测试 SWE-Bench 时,各模型会有自己的方法,因为检索代码的准确性对成功率影响也很大,所以榜单上很多是 Agent + 模型 的测试结果,而不是单大模型的。

Claude 3.7 跑分的说明里提到:

SWE-bench Verified: Claude 3.7 Sonnet scores 62.3% out of 500 problems using pass@1 with bash/editor tools plus a "thinking tool" for single-attempt patches — no additional test-time compute used. 70.3% score uses internal scoring and custom scaffold on a reduced subset of problems. Scaffold Deepseek R1 results use the "Agentless" framework. OpenAI results from o3-mini system card cover a different subset of problems with a custom compute.

Claude 没有具体说明怎么做检索代码的,官方blog附录里提到在运行环境上是用了极简的方案,只提供了命令和编辑文件的能力,看起来是只把代码仓库目录扔给 LLM,让它自行去做文件搜索。另外有提到 Deepseek 的跑分是基于 Agentless 框架跑的,Agentless 专门介绍说明了如何跑 SWE-Bench,以及具体是怎么做代码检索的。见 Agentless/README_swebench.md

可以看到 SWE-Bench 测试集其实是比较局限,这里面全是 Python 代码,也基本是纯逻辑代码,不涉及 UI 渲染相关,也不涉及其他语言,很多实际的软件工程场景没有覆盖到,所以即使 benchmark 到 100%,也不代表能解决绝大多数工程问题。

不过这事是一步步推进的,SWE-Bench 刚出来时解决率是个位数,这一年多一步步提上来,Claude 3.7 干到了 70%,解决了这个 Bench,还会有更多的更高难度的 Benchmark 等着,SWE-bench Multimodal 就是其中之一,包含了一些 JS UI 渲染相关的 issue 修复 case,Claude 3.5 也只有 12% 左右的解决率,还有很长路要走。

TAU-bench

TAU-bench 又叫 τ-bench,是 Sierra 团队(OpenAI 董事会主席 Bret Taylor 和谷歌前高管 Clay Bavor 联合创立的 AI 初创公司,主要开发 AI Agent 为企业服务)推出的用于评估 AI Agent 在现实世界场景中性能和可靠性的基准测试。

TAU-bench 设计了两个领域场景

  1. Airline(航空场景),模拟用户在航空业务场景下进行航班查询、预订、改签、退票、机场服务等操作,测试大模型利用工具理解用户需求、提供准确信息、遵循业务规则流程、准确进行业务操作的能力。
  2. Retail(零售场景)模拟在零售场景中进行购物咨询、商品推荐、订单修改、退货换货等操作,同样测试在这个场景下用户需求理解能力、准确处理用户订单等相关问题的能力。

这两个场景都包含了多个复杂任务 case,涉及代理与用户的多轮对话,以及代理使用工具获取信息的能力,这些任务可以综合地评估一个 Agent 所需要的 推理、规则遵循、长期记忆、工具调用等能力。为此 TAU-bench 项目也实现了一个完整的 Agent 框架,以执行这个流程。

数据构造

以零售为例,用于测试准备的数据包含以下几个部分:

  1. 数据库:json 格式,模拟电商零售领域一些订单信息、商品属性。
  2. Tools:操作上述数据库的工具函数,包括获取订单/商品信息、修改订单、修改收货地址等。这些 Tools 信息描述会在一开始给 LLM,让 LLM 知道当前有哪些工具可调用,过程中会 LLM 会根据用户意图调用相应工具修改数据库。
  3. 策略:system prompt,写明了模拟的零售场景下一些背景,包括订单状态说明/退换规则/支付方式规则、工具调用规则等。
  4. Tasks:预先设计好的测试数据集,每个测试 case 包含 instruction 和 actions,instruction 写明了这个测试 case 里用户的诉求,actions 里包含基于这个诉求下,应该调用什么工具方法改写数据库达到目的,相当于标准答案,actions 只用于最后的验证,不会在每轮测试中作为输入。

航空领域也是同样的这几类数据,只是处理的内容变成航班、订单、用户信息管理。

测试执行

具体测试 case 执行的流程:用户 instruction + 领域策略 System Prompt + Tool 描述,一起输入 LLM,LLM 循环逐步输出用户对话、助理回复、工具调用,整个流程就是一个通用的 Agent 交互流程,跟上次说到的 OpenHands 的流程差不多,只是这里用户输入也是 LLM 根据 instruction 模拟生成的。

整个多轮对话结束后,模型在这过程中会调用工具修改数据库,同时再跑一遍测试 case 里预定的 action,看对数据库的修改跟模型在这过程中调工具的修改结果是否一致,一致则测试 case 通过,不正确就不通过。

来看一个具体的例子,这是一条测试case,包含对用户诉求描述的 instruction 和标准答案 actions:

{
    "annotator": 0,
    "user_id": "omar_rossi_1241",
    "instruction": "Your user id is omar_rossi_1241. For your upcoming trip from New York to Chicago, you want to change the passenger to yourself, upgrade it to economy class, and have 3 checked bags. You prefer gift card payment. Your birthday is in your user profile so you do not prefer to provide it. You are reactive to the agent and will not say anything that is not asked.",
    "actions": [
        {
            "name": "update_reservation_flights",
            "arguments": {
                "reservation_id": "FQ8APE",
                "cabin": "economy",
                "flights": [
                    {
                        "flight_number": "HAT056",
                        "date": "2024-05-25",
                    },
                    {
                        "flight_number": "HAT138",
                        "date": "2024-05-25",
                    },
                ],
                "payment_id": "gift_card_8190333",
            },
        },
        {
            "name": "update_reservation_passengers",
            "arguments": {
                "reservation_id": "FQ8APE",
                "passengers": [
                    {
                        "first_name": "Omar",
                        "last_name": "Rossi",
                        "dob": "1970-06-06",
                    }
                ],
            },
        },
        {
            "name": "update_reservation_baggages",
            "arguments": {
                "reservation_id": "FQ8APE",
                "total_baggages": 3,
                "nonfree_baggages": 0,
                "payment_id": "gift_card_8190333",
            },
        },
    ],
},

转化后这个case实际跟大模型交互的过程:

{"traj": [
      {
        "role": "system",
        "content": "# Airline Agent Policy\n\nThe current time is 2024-05-15 15:00:00 EST.\n\nAs an airline agent, you can help users book, modify, or cancel flight reservations.\n\n- Before taking any actions that update the booking database (booking, modifying flights, editing baggage, upgrading cabin class, or updating passenger information), you must list the action details and obtain explicit user confirmation (yes) to proceed.\n\n- You should not provide any information, knowledge, or procedures not provided by the user or available tools, or give subjective recommendations or comments.\n\n- You should only make one tool call at a time, and if you make a tool call, you should not respond to the user simultaneously. If you respond to the user, you should not make a tool call at the same time.\n\n- You should deny user requests that are against this policy.\n\n- You should transfer the user to a human agent if and only if the request cannot be handled within the scope of your actions.\n\n## Domain Basic\n\n- Each user has a profile containing user id, email, addresses, date of birth, payment methods, reservation numbers, and membership tier.\n\n- Each reservation has an reservation id, user id, trip type (one way, round trip), flights, passengers, payment methods, created time, baggages, and travel insurance information.\n\n- Each flight has a flight number, an origin, destination, scheduled departure and arrival time (local time), and for each date:\n  - If the status is "available", the flight has not taken off, available seats and prices are listed.\n  - If the status is "delayed" or "on time", the flight has not taken off, cannot be booked.\n  - If the status is "flying", the flight has taken off but not landed, cannot be booked.\n\n## Book flight\n\n- The agent must first obtain the user id, then ask for the trip type, origin, destination.\n\n- Passengers: Each reservation can have at most five passengers. The agent needs to collect the first name, last name, and date of birth for each passenger. All passengers must fly the same flights in the same cabin.\n\n- Payment: each reservation can use at most one travel certificate, at most one credit card, and at most three gift cards. The remaining amount of a travel certificate is not refundable. All payment methods must already be in user profile for safety reasons.\n\n- Checked bag allowance: If the booking user is a regular member, 0 free checked bag for each basic economy passenger, 1 free checked bag for each economy passenger, and 2 free checked bags for each business passenger. If the booking user is a silver member, 1 free checked bag for each basic economy passenger, 2 free checked bag for each economy passenger, and 3 free checked bags for each business passenger. If the booking user is a gold member, 2 free checked bag for each basic economy passenger, 3 free checked bag for each economy passenger, and 3 free checked bags for each business passenger. Each extra baggage is 50 dollars.\n\n- Travel insurance: the agent should ask if the user wants to buy the travel insurance, which is 30 dollars per passenger and enables full refund if the user needs to cancel the flight given health or weather reasons.\n\n## Modify flight\n\n- The agent must first obtain the user id and the reservation id.\n\n- Change flights: Basic economy flights cannot be modified. Other reservations can be modified without changing the origin, destination, and trip type. Some flight segments can be kept, but their prices will not be updated based on the current price. The API does not check these for the agent, so the agent must make sure the rules apply before calling the API!\n\n- Change cabin: all reservations, including basic economy, can change cabin without changing the flights. Cabin changes require the user to pay for the difference between their current cabin and the new cabin class. Cabin class must be the same across all the flights in the same reservation; changing cabin for just one flight segment is not possible.\n\n- Change baggage and insurance: The user can add but not remove checked bags. The user cannot add insurance after initial booking.\n\n- Change passengers: The user can modify passengers but cannot modify the number of passengers. This is something that even a human agent cannot assist with.\n\n- Payment: If the flights are changed, the user needs to provide one gift card or credit card for payment or refund method. The agent should ask for the payment or refund method instead.\n\n## Cancel flight\n\n- The agent must first obtain the user id, the reservation id, and the reason for cancellation (change of plan, airline cancelled flight, or other reasons)\n\n- All reservations can be cancelled within 24 hours of booking, or if the airline cancelled the flight. Otherwise, basic economy or economy flights can be cancelled only if travel insurance is bought and the condition is met, and business flights can always be cancelled. The rules are strict regardless of the membership status. The API does not check these for the agent, so the agent must make sure the rules apply before calling the API!\n\n- The agent can only cancel the whole trip that is not flown. If any of the segments are already used, the agent cannot help and transfer is needed.\n\n- The refund will go to original payment methods in 5 to 7 business days.\n\n## Refund\n\n- If the user is silver/gold member or has travel insurance or flies business, and complains about cancelled flights in a reservation, the agent can offer a certificate as a gesture after confirming the facts, with the amount being $100 times the number of passengers.\n\n- If the user is silver/gold member or has travel insurance or flies business, and complains about delayed flights in a reservation and wants to change or cancel the reservation, the agent can offer a certificate as a gesture after confirming the facts and changing or cancelling the reservation, with the amount being $50 times the number of passengers.\n\n- Do not proactively offer these unless the user complains about the situation and explicitly asks for some compensation. Do not compensate if the user is regular member and has no travel insurance and flies (basic) economy.\n"
      },
      {
        "role": "user",
        "content": "Hi! I'd like to make a few changes to my trip from New York to Chicago. Can you help me with that?"
      },
      {
        "role": "assistant",
        "content": "Of course! I can help you modify your trip. Could you please provide your user ID and the reservation ID for the trip you want to change?",
      },
      {
        "role": "user",
        "content": "Sure! My user ID is omar_rossi_1241. But I'm not sure about the reservation ID at the moment."
      },
      {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "function": {
              "arguments": "{"user_id":"omar_rossi_1241"}",
              "name": "get_user_details"
            },
            "id": "call_FXi5dyufwOlkHksVgNwVhhVB",
            "type": "function"
          }
        ]
      },
      {
        "role": "assistant",
        "content": "I found your reservations. Could you please confirm which reservation ID you'd like to modify for your trip from New York to Chicago? Here are your reservation IDs:\n\n- UM3OG5\n- 5RJ7UH\n- FQ8APE\n- QKRY03\n\nLet me know which one you'd like to modify!",
      },
      {
        "role": "user",
        "content": "I'm not entirely certain, but I believe it might be the first one, UM3OG5."
      },
      {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "function": {
              "arguments": "{"reservation_id":"UM3OG5"}",
              "name": "get_reservation_details"
            },
            "id": "call_FApEDaUHdL2hx8FNbu5UCMb8",
            "type": "function"
          }
        ]
      },
      {
        "role": "assistant",
        "content": "The reservation ID "UM3OG5" is for a round trip from Seattle (SEA) to Dallas/Fort Worth (DFW), not from New York to Chicago. \n\nCould you please check if any of the other reservation IDs match your trip from New York to Chicago? Here are the remaining reservation IDs:\n\n- 5RJ7UH\n- FQ8APE\n- QKRY03\n\nLet me know which one you'd like to modify!",
      },
      {
        "role": "user",
        "content": "In that case, I think it might be FQ8APE. Can you check that for me?"
      },
      {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "function": {
              "arguments": "{"reservation_id":"FQ8APE"}",
              "name": "get_reservation_details"
            },
            "id": "call_W507pQxieFVdAxvqONuYWvN2",
            "type": "function"
          }
        ]
      },
      {
        "content": "The reservation ID "FQ8APE" is for a one-way trip from Newark (EWR) to Chicago (ORD) with a stop in Houston (IAH). Since this reservation is in basic economy, please note that the flights cannot be modified. However, I can assist with other changes like cabin upgrades or baggage additions.\n\nPlease let me know what specific changes you would like to make to this reservation!",
        "role": "assistant"
      },
      {
        "role": "user",
        "content": "Great! I'd like to add three checked bags, change the passenger to myself, and upgrade to economy class, please."
      },
      {
        "role": "assistant",
        "content": "Here are the changes you want to make to your reservation:\n\n1. **Add three checked bags**: \n   - Since you are a gold member and upgrading to economy class, you will have 3 free checked bags, so no additional charges for the bags.\n\n2. **Change the passenger to yourself**:\n   - Current passenger: Ivan Garcia\n   - New passenger: Omar Rossi (DOB: 1970-06-06)\n\n3. **Upgrade to economy class**:\n   - Upgrade from basic economy to economy class.\n\nPlease confirm if you would like to proceed with these changes.",
      },
      {
        "role": "user",
        "content": "Yes, I'd like to proceed with those changes. And please use a gift card for any payment needed."
      },
      {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "function": {
              "arguments": "{"reservation_id": "FQ8APE", "passengers": [{"first_name": "Omar", "last_name": "Rossi", "dob": "1970-06-06"}]}",
              "name": "update_reservation_passengers"
            },
            "id": "call_jK7xz4ERk3csc9jBfroPobGs",
            "type": "function"
          }
        ]
      },
      {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "function": {
              "arguments": "{"reservation_id":"FQ8APE","cabin":"economy","flights":[{"origin":"EWR","destination":"IAH","flight_number":"HAT056","date":"2024-05-25"},{"origin":"IAH","destination":"ORD","flight_number":"HAT138","date":"2024-05-25"}],"payment_id":"gift_card_8190333"}",
              "name": "update_reservation_flights"
            },
            "id": "call_zeyT5c2EYzRvfY42X7YOKOng",
            "type": "function"
          }
        ]
      },
      {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "function": {
              "arguments": "{"reservation_id":"FQ8APE","total_baggages":3,"nonfree_baggages":0,"payment_id":"gift_card_8190333"}",
              "name": "update_reservation_baggages"
            },
            "id": "call_PA1XaKLPX8egjewaxIArCkRc",
            "type": "function"
          }
        ]
      },
      {
        "role": "assistant",
        "content": "Your reservation has been successfully updated with the following changes:\n\n1. **Passenger Change**: The passenger is now Omar Rossi.\n2. **Cabin Upgrade**: Upgraded to economy class.\n3. **Checked Baggage**: Added three checked bags, all free due to your gold membership.\n\nThe payment for the cabin upgrade was processed using your gift card. If you have any more questions or need further assistance, feel free to ask!",
        
      },
      {
        "role": "user",
        "content": "Thank you for helping with the changes! That's all for now. ###STOP###"
      }
    ],
}

在跑的过程中,可以看到大模型可以一步步理解好用户意图,调用get_reservation_details 工具获取信息,最后依次调用 update_reservation_flights、update_reservation_passengers、update_reservation_baggages,修改数据库。

执行完后,会重新加载初始状态的数据库,把 Task 里的 actions 依次执行一遍,对比最终数据库的状态是否一致,一致则说明模型这轮跑下来,调用的工具和修改的数据跟我们预期的一致,测试通过。

这里可以看到每条测试的过程和结果,可以从 Reward=1/0 看这条 case 是否通过,Claude 3.7 在零售领域问题解决率高达81%,但航空领域只有58%,细看下航空领域一些 case 涉及非常多的查询匹配航班信息、金额计算、行李/支付/退换多步操作,难度还是很大的。

Pass^k

这个测试集还定义了另一个指标:多次稳定通过的概率。因为 Agent 真正使用时,比如让它去订机票、处理退换货,如果执行失败会让人有很大的挫败感,所以它的成功率稳定性很重要,这个 benchmark 定义了pass^k 的指标,也就是对一个测试 case 连续执行 k 次,每次都成功,才能算任务成功

可以看到每个模型的稳定性都不是很好,航空领域下,Claude Sonnet 3.5 从 46% 通过率下降到pass^4 的 22.5%,也就是只对 22.5% 的问题,连续测4遍都能成功。

这跟我们目前体感也一致,Agent 还没那么可靠,并不能期望它在复杂的场景、多轮交互中很稳定地理解意图做出正确的行动。Claude 3.7 没有 pass^k 相关的指标,不确定稳定性是否有提升。

最后

上述两个 Benchmark 都是尽量在模拟真实世界的问题场景,算是模拟得比较好的了,但跟真正现实的使用方式和多样性还是有很大差距,分数只能是个参考,能大致知道模型在哪些方面表现还不好,实际在某个场景下好不好用,还得真正上手测试,实际体验上据了解 Claude 3.7 远好于 3.5,这两个 benchmark 的分数提升还不足以反应优化的程度。

我在苏州逛园子之虎丘

虎丘,又叫海涌山、海涌峰、虎阜,距现在已经有二千五百多年悠久历史。传说远古时期苏州是一片茫茫大海,虎丘山是从海里涌出的一个小小岛屿,后来沧海变良田就成为一座小山,山高34.3米,所以也叫海涌山。而虎丘这个名字源于春秋时的吴王阖闾,他在这里修城建都,死后也葬在这里。传说葬后三日,墓地有“白虎蹲其上”,因而得名。也有说“丘如蹲虎,以形名”。虎丘的山体由侏罗系火山岩浆构成,园内很多山石都是流纹岩。

我在苏州逛园子之虎丘-雅余
虎丘山风景名胜区全景图

虎丘后山有“虎丘后山胜前山”之说,现存青石小桥、石牌坊、湖石假山,也是苏州园林的一大代表。《吴地记》载:”虎丘山绝岩纵壑,茂林深篁,为江左丘壑之表“。

我在苏州逛园子之虎丘-雅余

景区南门入口的牌坊,写着“吳中第一山”,远远能看到虎丘塔。

我在苏州逛园子之虎丘-雅余

我在苏州逛园子之虎丘-雅余

走进天下第一名胜虎丘大门,过海涌桥,再走百来米才到景区售票处。

我在苏州逛园子之虎丘-雅余
海涌桥上的风景

这里的河道与山塘街相连,有七里山塘水源自虎丘的说法。

我在苏州逛园子之虎丘-雅余
头山门

虎阜禅寺是沿山而建,由于山门建在山前,有“寺中藏山”的特点,但实际寺又在山里,又有深山藏古寺的布局。

我在苏州逛园子之虎丘-雅余

景区检票口的碑廊。

我在苏州逛园子之虎丘-雅余

我在苏州逛园子之虎丘-雅余

我在苏州逛园子之虎丘-雅余
断梁殿,二山门

断梁殿上面的主梁是断的,它用两根木料建成的,它的建成主要是运用了力学中的杠杆原理,在这断梁下有一排斗拱相托,通过斗拱将中间所承受的力分散到四周,采用了“挑梁式”的构筑方法。

断梁殿大门对联是:上联“塔影在波山光接屋”、下联“画船人语晓市花声”。

我在苏州逛园子之虎丘-雅余

我在苏州逛园子之虎丘-雅余

拥翠山庄墙上的两边刻有“龙、虎、豹、熊”行草大字石刻四方。拥翠山庄是苏州唯一 一座无水的园林,由苏州状元洪钧所建。建筑依山势分四个层次,园内第一层抱瓮轩,抱瓮轩三间朝南,是全园的主要建筑。

我在苏州逛园子之虎丘-雅余

我在苏州逛园子之虎丘-雅余

憨憨泉有一段美丽的传说,憨憨是中国梁代著名的高僧,当时他的眼睛不好,患有目疾,也就是“白内障”,虎丘山的方丈因为可怜他,收他做一个挑水和尚,但是当时挑水的路很远,有一次他挑水途经这里,因为感到累,于是就坐在这里休息,不知不觉中就睡着了,在梦中它梦见一位高僧对他说这里有一泉眼可通大海,醒后他就用双手触摸这片地,在这里摸到了一些青苔,他想有青苔就说明这地下一定有水,于是他用挑水的扁担在这里挖,大约挖了七七四十九天,终于一脉泉眼涌了出来,治好了憨憨的眼睛,因这口井是他所挖所以取名为“憨憨泉”。

我在苏州逛园子之虎丘-雅余

真娘墓,位于通往虎丘山坡道的右侧。真娘确有其人,原名姓胡,名瑞珍,北方人。她从小父母双亡,唐朝“安史之乱”的时候,随同亲戚一起逃亡来到苏州,但是不幸坠入苏州阊门外的一个妓院——“乐云楼”。她虽成为青楼女子却守身如玉,只陪客人歌舞书画,是苏州一位绝色佳丽。当时,苏州有个大财主,名叫王荫祥,他用重金贿赂老鸨,企图在真娘那里留宿,真娘知道这次难以逃避,为了保持贞洁,她便上吊自尽了。这使王荫祥内心大受震撼,于是为真娘筑了此墓,并且发下重誓,今生永不再娶。此后,许多文人墨客都同情真娘,在她的墓上题写诗词,真娘也由此在中国历史上有了一席之地,与杭州另一名妓苏小小齐名,被誉为“香魂”。清乾隆十年(公元1745年),海陵陈璜访得遗址,建亭其上,立石刻书“古真娘墓”4字。

我在苏州逛园子之虎丘-雅余

枕石,形似枕头。相传苏州才子唐伯虎游玩时在这个石头上睡着了,另外一个才子祝枝山看到了这个景象,便在石头上写下了“枕石”二字。

我在苏州逛园子之虎丘-雅余

相传春秋时期,吴王阖闾为了争霸天下,召来了当时最有名的铸剑师干将莫邪夫妇为他铸剑。满期那天,他提着“莫邪”剑来到了虎丘山,将此剑献给了阖闾,阖闾为了试其剑的锋利,对着这块石头手起剑落,就将这块石头一劈为二,这就是有关试剑石的传说。事实上,这块石头是典型的火山喷出岩的凝灰岩,久经风化,沿着裂隙形成这样一条大缝,酷似剑劈。

我在苏州逛园子之虎丘-雅余
千人坐,是不是很像坐月子?

旧志云:“生公讲座,下有千人列坐,故名。”《吴地记》曰:“虎丘泉石,其最胜者剑池,千人坐。”《吴郡志》云:“生公讲经处,大石盘陀数亩,高下如削,乃他山所无。”现石上刻有千人石“千人坐”三个篆字,是胡缵宗所书。

我在苏州逛园子之虎丘-雅余
传说高僧在此讲课,石头听了都点头。
我在苏州逛园子之虎丘-雅余
北宋书法家米芾所书“风壑云泉”四字

虎丘最出名的就是“剑池”,相传池底埋有吴王闵闾的墓葬,其子吴王夫差曾以鱼肠剑和其它宝剑共三千把為父亲陪葬,故名“剑池”。上从往下看,水道就像一把剑横卧在山间。

我在苏州逛园子之虎丘-雅余

我在苏州逛园子之虎丘-雅余

剑池岩壁四周写了各大家到此一游的题字。

我在苏州逛园子之虎丘-雅余

我在苏州逛园子之虎丘-雅余

虎丘塔是世界第二斜塔,又称中国的“比萨斜塔”,园内第二大景点。这座塔七层八面,塔高47.7米,塔向东北偏北方向倾斜,它的塔顶偏离中心2.34米,最大倾角是3度59分。1986年对虎丘塔进行“加固塔基”的第二次大修,才使千年古塔转危为安。虎丘塔建于五代后周显德六年(959年),建成于宋建隆二年辛酉(961年),已经有一千多年的历史。

我在苏州逛园子之虎丘-雅余
虎丘塔
我在苏州逛园子之虎丘-雅余
虎丘塔
我在苏州逛园子之虎丘-雅余
虎丘塔细节
我在苏州逛园子之虎丘-雅余
海涌岚浮
我在苏州逛园子之虎丘-雅余
致爽阁门口

我在苏州逛园子之虎丘-雅余

我在苏州逛园子之虎丘-雅余
出园后的河道景色
我在苏州逛园子之虎丘-雅余
河道剪影

虎丘是苏州2500年沧桑的见证,苏州历史文化古城的标志,有很多源远流长的典故,苏东坡曾说:“到苏州不游虎丘,乃憾事也”。

摄于2025年2月5日,By 徕卡 D-LUX8

我在苏州逛园子之西园寺

留园出来还没到午饭时间,正思索去哪里打发时间,突然想起西园寺就在附近,相隔一条街,便去探访一番。

西园寺,俗称西园,地图上标示为戒幢律寺。戒幢律寺始建于元代至元年间(1264—1294年),最开始名为归元寺。所以你在地图找到的戒幢律寺、西园古刹、西园,其实就是西园寺。西园寺是苏州古城内唯一一座带有园林的寺庙,距今已有700多年。现存建筑为清代重建,明嘉靖年间曾一度被留园的建造者太仆寺卿徐泰时改为了私家园林。

我在苏州逛园子之西园寺-雅余
西园寺东门

从留园过来走东门较近一些。上香礼佛后便去逛园子了。

我在苏州逛园子之西园寺-雅余

东边礼佛区庄严肃穆,一墙之隔是风格迥然的园林空间。后花园内有宽敞的大草坪,水静如镜,水中古树倒影郁郁葱葱。园内非常多古树,都是历史的见证。

我在苏州逛园子之西园寺-雅余

我在苏州逛园子之西园寺-雅余

我在苏州逛园子之西园寺-雅余

湖中心筑有六角亭,楼阁形式,叫湖心亭。水域是放生池,被湖心亭的步道划分为两部分,环池布置假山、亭台、花架、厅堂等,也是满满的苏州园林特色。

我在苏州逛园子之西园寺-雅余

我在苏州逛园子之西园寺-雅余

我在苏州逛园子之西园寺-雅余

可由假山翻过园子另一侧。

我在苏州逛园子之西园寺-雅余

我在苏州逛园子之西园寺-雅余

我在苏州逛园子之西园寺-雅余

我在苏州逛园子之西园寺-雅余

我在苏州逛园子之西园寺-雅余

西园寺有另一大特色就是有非常的猫,花色各不相同。很多小姐姐们围着小猫一直拍照,我也忍不住凑过去多拍了几张。其实你从入寺园寺前就发现路边很多卖猫粮的小商贩,就知道这附近有撸猫的好地方。

我在苏州逛园子之西园寺-雅余

我在苏州逛园子之西园寺-雅余

我在苏州逛园子之西园寺-雅余

我在苏州逛园子之西园寺-雅余
很害羞
我在苏州逛园子之西园寺-雅余
曲径通幽

出了西园寺后,可以看到南门口的智慧桥与福德桥,连接西园弄和枫桥路,两桥同时修建,结构完全对称。

我在苏州逛园子之西园寺-雅余
福德桥

我在苏州逛园子之西园寺-雅余

我在苏州逛园子之西园寺-雅余

我在苏州逛园子之西园寺-雅余
智慧桥
我在苏州逛园子之西园寺-雅余
西园寺南门

西园西南门是正门,正门前是御赐牌楼,四柱隔成三门,中门横额写“敕赐西园戒幢律寺”,中门石柱刻有一副对联:“佛日增辉重开阊阖,宗风振律大启丛林”。御赐牌楼始建于清光绪年间,至今保存完好。

西园寺最佳游玩季节是秋季,届时满园都是金黄的银杏和红枫,十分好看。

从出口出来后才发现这里有售票处,很多人在购票。但从东门入居然不检票,无意之中“逃过一票”。

摄于2025年2月5日,By 徕卡 D-LUX8

我在苏州逛园子之留园

苏州四大名园之一,留园。苏州四大园林分别代表着宋元明清四个朝代的艺术风格,留园则代表清代风格,占地面积23300平方米。留园以建筑艺术著称,厅堂华丽堂皇,庭院变化丰富,十分精致。留园坐落在苏州阊门外,第一代主人太仆徐泰时建后取名为东园,后清嘉庆时归观察刘恕改为刘园,同治年间盛旭人购买之后取谐音改为留园。留园多次荒废易主,1953年,苏州市人民政府决定修复留园,至今不断修缮整治。1997年列入《世界遗产名录》,为国家AAAAA级旅游景区。

我在苏州逛园子之留园-雅余
园区分区,图片来自网络

园子利用云墙和建筑群把园林划分为中、东、北、西四个不同的景区,其间由曲桥曲廊相连,连廊共长达700多米。西区以山景为主,中区以山水兼长,东区以建筑取胜,东区和中区是全园的精华。

我在苏州逛园子之留园-雅余
景观分布,图片来自网络
我在苏州逛园子之留园-雅余
留园正门

留园的入口十分低调,进去后却别有洞天。

我在苏州逛园子之留园-雅余
“吴下名园”牌匾及缀玉留园全景图。

门厅正中屏门是一幅缀玉留园全景图,由扬州工匠用2500枚各类玉石薄片相缀而成的。这是一九八六年时为纪念苏州古城建成2500周年所创作。科举考试的最后一个状元俞樾作《留园游记》称留园为吴下名园之冠。

我在苏州逛园子之留园-雅余

穿过正厅屏门后就可窥见盆景和假山群,园林景色逐步逐步的映入眼帘,内敛,从容,引人入胜。

我在苏州逛园子之留园-雅余

我在苏州逛园子之留园-雅余
早晨的阳光
我在苏州逛园子之留园-雅余
明瑟楼外
我在苏州逛园子之留园-雅余
从明瑟楼看到的对面景色
我在苏州逛园子之留园-雅余
曲折的连廊

苏州园林有八大造景手法,借景、框景、隔景、漏景、障景、夹景、对景、添景,留园利用粉墙、游廊、篱落等划分空间,分隔景区,使得庭院内外,景色迥异,这种造景手法叫做隔景。

我在苏州逛园子之留园-雅余
连廊间的景观
我在苏州逛园子之留园-雅余
连廊内的书条石

目前留园里的书条石有三百七十多块,书条石内有王羲之、王献之、苏东坡、米芾等大量名家的字帖,是第二代园主刘恕给后人留下的巨大文化财富。

我在苏州逛园子之留园-雅余
明瑟楼

明瑟楼环境雅洁清新,有水木明瑟之感,故借以为名。

我在苏州逛园子之留园-雅余
假山与亭
我在苏州逛园子之留园-雅余
五峰仙馆

因盛康从文征明的停云馆中得峰石放在园内,故名“五峰仙馆”。五峰仙馆被称为“江南第一亭”,200平方米,梁柱及家具均以楠木制作,又叫楠木厅。馆内悬挂有苏州状元的楹联:“读书取正,读易取变,读骚取幽,读庄取达,读汉文取坚,最有味卷中岁月;与菊同野,与梅同疏,与莲同洁,与兰同芳,与海棠同韵,定自称花里神仙。”

我在苏州逛园子之留园-雅余

门口开门就是假山,假山是太湖石,代表开门见山。

我在苏州逛园子之留园-雅余
鹤所,文征明的书法真迹

我在苏州逛园子之留园-雅余

我在苏州逛园子之留园-雅余
可亭

可亭位于留园中园假山上,六角飞檐攒尖顶,倒扣花瓶结顶。可,可如人意也,刘氏时称“个中亭”,盛氏称“可亭”。

我在苏州逛园子之留园-雅余
东园一角

留园东园一角原为园主盛家的大戏厅,是位于石林小院东南的庭院,原为盛氏戏台。

我在苏州逛园子之留园-雅余

我在苏州逛园子之留园-雅余
冠云峰

冠云峰高6.5米,为宋代花石纲遗物。因石巅高耸,四展如冠,取名“冠云”。“瑞云”、“岫云”屏立左右。冠云峰充分体现了太湖石“ 瘦、漏、透、皱”的特点,名气很大。

我在苏州逛园子之留园-雅余
岫云峰

我在苏州逛园子之留园-雅余

我在苏州逛园子之留园-雅余
又一村

陆游诗:“山重水复疑无路,柳暗花明又一村。” 别以为走完了,过了这个门洞,又是另一番景象,里面充满田园风光,很多造型精巧的盆景、花圃、桃林、紫藤架和假山。

我在苏州逛园子之留园-雅余
“又一村”门口的腊梅

我在苏州逛园子之留园-雅余

我在苏州逛园子之留园-雅余

我在苏州逛园子之留园-雅余

我在苏州逛园子之留园-雅余

我在苏州逛园子之留园-雅余

我在苏州逛园子之留园-雅余

我在苏州逛园子之留园-雅余
舒啸亭

舒啸亭为园形攒尖式。盛氏时此处为“月榭星台”,解放后重建,改名“舒啸”。陶潜在《归去来辞》写道:“登东皋以舒啸,临清流而赋诗”。

我在苏州逛园子之留园-雅余

我在苏州逛园子之留园-雅余

我在苏州逛园子之留园-雅余

我在苏州逛园子之留园-雅余

我在苏州逛园子之留园-雅余

留园造园精致,也有人觉得有炫技的嫌疑,但确实是一步一景,处处都是非常雅致,还带有几分书卷气。

两次来留园都是2月份前后,希望有机会夏天或者秋天再来看看。

摄于2025年2月5日,By 徕卡 D-LUX8

后记:

建议游览苏州园林,尽可能早入园,最好一开园就入园,或预约最后一个时间段。尽早入园你还能享受片刻的幽静,舒心的游览,清晨的阳光还合适拍照。9点后旅游团到了,那就很糟心了。第一个时间段的游客没走,第二、三个时间段的游客就进来了,那场景真的可怖。去过的朋友会发现很多主要景观我没拍,因为没法拍,人头涌动,总有人冲进你的取景框,忍着拍出来的景观意境也垮了。

在苏州呆了几天,拍了不少照片,后期整理是个体力活。静待我慢慢把西园寺虎丘拙政园狮子林沧浪亭几个景点一一整理发出。

又是一年

生日是属于自己的新年,许愿时闭眼的瞬间,过去与未来在黑暗中重叠。

今年的年度总结总是拖拖拉拉一直没有写出来,感觉在过去的 2024 年里面整个人的节奏在“快/慢”之间频繁的交替,大部分时候都在忙忙碌碌响应和支持各种工作与生活中的事情,好不容易放松下来就只想一个人静静,哪怕是发呆放空,也会觉得能让自己坐着慢慢回血(游戏里都是这样的吧,只要暂时停止被攻击的状态,就能缓慢的回复血量)。

原本找了个时间,打算对比 2023 年的总结看看有哪些改善与进步,但很遗憾的发现可能“在常态中一点点进步可能才是人生的常态”,在过去的一年里似乎没有什么“宏大的叙述”与“翻天覆地”的改善,好像日子还是一步步缓慢的进展,印象里有很多开心的瞬间,但也不免有些许灰暗的时刻。

但也无妨,一步步来就好了,按照惯例先来看看去年发生了哪些事情。

「物理意义上/精神意义上的家」

在完成买房和开始装修后,直到去年 4 月,我和蒋老师的新房终于完成了装修和空置散味。于是我们一家四口就心满意足的搬了进去。看到从平面图和效果图里走出来几乎达到 99% 实现状态的新家还是会觉得很不可思议。

相比刚拿到房产证时心态上没有什么波澜的彼时的我来对比,此时此刻能够坐在自己选择的沙发上看着自己选择的各种家具和电器,并且住在自己的房子里时,心态上不可避免的会有“原来这就是属于自己的家”,“这个家可真好啊”的想法出现。平日里这个房子的任何美好印象都会让我俩觉得“有家真好”,不论是看到第一缕朝阳从百叶缝隙射入卧室,看到落日从枝叶中穿过散落在窗帘上的斑点,还是在阳台摘个柠檬后继续吃烤肉,生活里的幸福感好像在无数个瞬间都能被轻而易举的感知到。

虽然在 23 年的时候我就持证山岗开始了自己的婚后经历,但实际上去年 6 月我们才回到兰州,在端午节的兰州办了一场婚礼(严格一点说,应该属于去除了大部分繁缛流程的答谢宴),爸妈总是觉得领证了但不办一场酒席很难说的过去,但我和蒋老师又不希望办理那种“流程化的,高度重复与一致的,没有新意的”就像是出了份子钱吃一顿饭的婚礼。

在无数次沟通后,大家说服了彼此,于是我和蒋老师自己策划自己当主持人举办的婚礼就放在了计划之中。自己办理自己的婚礼,而且完全不要任何的策划与主持人听起来又难又简单的,我们四个人需要临时分工确认好每一个人扮演的角色,随后和场地方沟通自己心中想实现的婚礼现场,自己采买置办对应的喜烟喜糖流程中抽奖发放的奖品,确认当天自己和双方父母的服饰流程等等,都是各种细小的琐事,但确实生怕忙中出错还是花了很大的精力。

还是要感谢我和蒋老师,感谢双方的父母,感谢小蒋的家人从四川大老远来到成都,感谢来自天南海北来帮忙捧场的朋友同学和人生中的挚友,我俩的婚礼算是顺顺利利的完成了。其实最初计划举办婚礼的时候,我一直疑惑我们这一场普普通通的婚礼是否值得大家在端午节的时候,放下原本的安排专门前来,但我想到如果是这些朋友们的婚礼,我一定会毫不犹疑的前往,所以就不再纠结了。

写这篇总结的时候我翻出来了自己写的发言稿,这句话我觉得还是挺对的“今天开始,我们的人生到了新的阶段,也许人生路上起起伏伏,未来的我们还需要面对不同的困难和挫折,希望我们彼此扶持,上坡的时候充满动力的推彼此一把,下坡的时候用全部的动力撑着对方不让对方坠落,一起携手对付生活中的各种问题。祝我们新婚快乐,我们不仅是夫妻,也是彼此最好的好朋友。希望我们能够站在一起面对问题和挑战,能够一同相拥共享快乐的果实,能够一直携手畅游人间,探索这个世界的无限精彩。希望我们不仅这辈子,下辈子以及下下辈子,都永远是夫妻。”

「工作上无规律的变化/一成不变」

在举办婚礼的前后,工作中也发生了许多“一成不变”的变化。先解释一下,并没有离职或者换工作,还是在原本的公司里努力搬砖, 尝试在产品行业里做出一些不一样的尝试和贡献。公司的规模今年还是保存在不到 100 个人左右的样子,做的事情也会时不时需要从产品设计的挑战中抽离出来去服务一下各种各样的“客户爸爸”,有时候真的挺无语的(希望 2025 年事业上能够有转机,做一些有意义有挑战而且是正向循环的事情)。

可能是市场大环境不好,可能是行业所限制,可能是小公司无法脱离的赚钱魔咒。坦白说这一年在工作中让我 emo 的次数挺多的,但工作就是受人之托忠人之事啦,今年在工作中也算是缓慢的螺旋上升,自己的产出才是证明自己能力的唯一试金石。这几年我越发发现公司规模的背书越来越重要,每当这个时候我就会有点不甘心,但可能人生的常态就是要说服自己适应平常?谁知道呢。

其实讲道理,在这里证明自己的工作成绩应该放一些新做的产品和说明的,但是产品行业这几年好像确实不好做了,需要你的时候说你是公司里最不可或缺的存在,但等开始拆分贡献的时候有要你来证明自己的产出能够如何影响销售业绩,有时候想起来都挺荒谬的。所以我就放了两个和团队小伙伴聊天的截图,反正就还是努力做个好人,别去伤害别人。

但坦白来说今年在产品设计上的新尝试也挺多的,使用 AI 做了很多奇怪的东西(甚至自己一个人就用 40 块搞了一个网站出来),做了很多面向电视大屏,手机和各种移动设备的产品出来,但这些东西终归是属于小打小闹,我也不好意思平常写在博客和大家分享,等我觉得搞的差不多或者真的挺有意思再和大家分享吧。

说到 AI 不可避免还是要聊聊 AI 对产业的冲突,其实之前我也在博客里写过一些对 AI 的分析,截止目前我依然觉得“AI 工具还是适合那些有一定经验的开发者,不管是快速搭出原型,还是学习一个新的技术和工具。以后 AI 肯定会更智能更聪明,但做出一款好的产品还是要靠设计者的同理心与感知世界的能力。也别想的用 AI 来彻底降本增效了,AI 应该用来加速试错和优化迭代,而不是直接取代已有的软件设计与开发方式”。

大家都在研究怎么用 AI 打造一些趁手的工具,但有时间的话也可以抬头想想一些“飘在风中的问题”,比如“我们这种碳基大模型的下一步发展方向在何处?”,“产品设计师应该专注于创造工具,还是尝试和工具互相影响?”……

「天命可违/天命难违」

每年的总结里我其实都会聊聊有哪些一直在做的尝试,今年我觉得自己做的比较好,并且有进步的方向则是“尝试和自己的焦虑一起相处”,虽然没有特别大的进展,但是我觉得还是有一点点的细小收获。

俗语里总说“五十而知天命”,大概就是要论证人们在某一个年龄忽然明白了理想实现之艰难,故而做事情不再追求结果,在五十岁之前,会全力以赴希望有所成就,而在五十岁之后,虽然仍是“发愤忘食”、“乐以忘忧”,但对个人荣辱已经淡然。

这几年互联网(甚至很多行业)都在疯传 35 危机,说随着不断毕业的年轻血液进入之后,企业方会更愿意用年轻的,能熬夜加班的,能抗住更大压力的,有更强目标感的年轻伙伴来替代那些到了一定年纪的,有房贷车贷的,需要一人养家的中年砥柱。于是很多人不免发现身边的工作氛围和环境变得越来越差,前些年是虽然奸臣当道,但大抵踏实做事还是能看到回报,现在则是大家都是等待收割的稻草,哪怕你身强力壮,但都是过着一样望到头的日子。

人的生老病死一直都是无法影响的客观规律,这也是很多获得一定财富的人都愿意投入更多的资源和精力去探索延缓衰老和死去的问题。今年回家过年的时候忽然发现“爸妈在一瞬间真的老了”,眼神没有之前好了,开始时不时出现健忘的问题了,人们好像都说人的年级越来越大之后,就会显得越来越小,这可能也是某种意义上人的生命是一圈轮回的象征。

这件事对我的启发是什么呢?可能在某个角度来说,我们就是要接收自己的平庸?或者说要意识到大多数自己都是平庸的,刚毕业的时候我们恨不得要翻天覆地做出一番大的改变,记得那会我恨不得每一年都拿优秀员工,加班和快速响应几乎已经成为了习惯,但现在发现还是要把时间和精力放在过好自己的小日子上,不要因为工作中那些“2 个小时以后”的事情占据我太多的情绪,很多事情确实尽力了就是尽力了,但尽管你尽力了也对结果的影响无足轻重,对吧。

那句话怎么说来着?“功成不必在我,成事必定有我”,“工作中少我一个无所谓,生活中我非常重要”。

祝我生日快乐,也祝每一个人在 31 岁的时候都能生日快乐。

重游江南水乡苏州

重游江南水乡苏州-雅余

重游江南水乡苏州-雅余
清晨第一缕阳光照进姑苏城
重游江南水乡苏州-雅余
遥望北寺塔
重游江南水乡苏州-雅余
外城河

重游江南水乡苏州-雅余

重游江南水乡苏州-雅余

重游江南水乡苏州-雅余

重游江南水乡苏州-雅余

重游江南水乡苏州-雅余
七里山塘
重游江南水乡苏州-雅余
山塘夜景

第三次到苏州,除一次因出差短暂停留苏州外,前后已相隔12年。重游苏州,再来看看这江南水乡和苏州园林。不料初八已经上班了都还是那么多游客,实在低估了,给拍照增加了很大的难度,也非常影响静心欣赏园林雅致的景色。这次把苏州四大园林都逛了一遍,未来一两周会整理更新上来。

下榻的酒店意外的高,入住到27楼,姑苏城风景尽收眼底,北寺塔巍然耸立,让人大爱。第二天早上一直等到第一缕阳光照进姑苏城,我拍完照片,才满意的去吃早餐。

摄于2025年2月5日,苏州七里山塘和平江路,By iPhone 12 Pro Max

DeepSeek R1 是怎么训练出来的?- R1 论文精读

背景

DeepSeek 里程碑式的爆火,有必要学习下是怎么回事。

大语言模型的发展,之前一直是以预训练为主,虽然性能一直在提升,但主要是小修小补,跨越式的 GPT5 一直出不来。OpenAI 在 24 年 9 月发布的 o1 提出了新的路线:在预训练好的模型上,用强化学习做后训练,能显著提高模型推理能力,其效果在解数学、编码等问题上屠榜。

但 o1 只说了强化学习能让模型学会思维链的方式提升推理能力,其他的发现都没有公布,加上 o1 一直是期货,12月才正式推出,200美元一个月,普通人都用不上,充满神秘。

而 DeepSeek 自主研发了通过强化学习让模型学会思维链提升推理能力,性能逼近 o1,加上之前 DeepSeekV3 在预训练基础模型上的创新,推理成本也显著低于 o1,直接推出全民免费可用媲美 o1 的模型,甚至在一些中文语境下效果显著超过 o1,大众用户一用感受到 NB,业内人士震惊它的创新能力纷纷学习,美国人民恐慌中国 AI 的发展有超越美国引领技术潮流的可能性,结合大国叙事,爱国情怀,各种元素综合下各觉得都会乐此不疲地研究、使用、讨论,引爆全网。

接下来一步步精读下 DeepSeek-R1 的论文 《DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning》了解现象级 R1 模型是怎么做出来的。

R1 论文

DeepSeek R1 是基于预训练好的 DeepSeekV3 基础模型进行的后训练优化,V3 做了很多创新优化,很大降低了预训练模型和模型推理的成本,是 R1 的基础,这里先不讨论 V3,只看 R1 在 V3 基础上做了什么后训练。

在 DeepSeekR1 这篇论文里核心做的三个事:

  1. R1-Zero:首个证明只通过 RL 而不通过 SFT 就能让模型涌现推理能力。
  2. R1:新的强化学习后训练范式。
  3. 蒸馏:蒸馏显著提升小模型能力,小模型有潜力。

我们一个个具体看下这三个事。

R1-Zero

R1-Zero 证明了对已预训练好的模型,不需要经过 SFT,只需要纯粹的 RL,就能让模型涌现 CoT 推理能力。

SFT是监督式微调,也就是准备好一堆标注好的数据,让模型去学习拟合,可以粗略理解为刷题,大量的刷题学习能解决类似的题目;

RL是强化学习,只告诉模型什么是好的什么是不好的,过程中模型自己学习怎么达到目标,可以理解为不靠刷题,自己去理解探索数学和世界的规律,理论上灵活性强,上限更高,还有可能探索出人类未知的能力。

强化学习首次出圈是 AlphaGo,AlphaGo 先学习人类棋谱,再用强化学习自我对弈进化,而随后的 AlphaGo Zero 没有人类棋谱,只定义好围棋奖励规则,模型自己学习怎么下,达到更高的水平。R1-Zero 这命名也是致敬 Alpha-Zero,因为它们非常类似,脱离人类的指导自主发现规律提升智能。

为什么之前没人做到?

  1. 模型能力没达到一定阈值,仅通过强化学习没法涌现。
  2. 强化学习是种方法,过程中用什么算法做价值判定也很大程度影响效果
  3. o1 可能已经做了同样的探索和结果,也可能没有,它闭源不公开,而 DeepSeek 首次探索并公开了。

GRPO vs PPO

R1-Zero 最大的不同,是在强化学习中使用 GRPO 算法代替 PPO。GRPO 算法也是 DeepSeek 团队在 24 年 2 月《DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models》这篇论文提出的,其核心思想可以理解为两个字:内卷

简单来说,PPO 是用一个模型去评估当前输出的收益,模型觉得这个输出好,就更新参数往这方向靠近,类似四六级考试的判定,有个分数线,过了就好,不过就不好;GRPO 是让模型一次性输出一组数据,在这组数据中选得分最高的,让模型逐步靠近得分高的输出,类似高考的内卷选拔,你只要比同龄人好,就是好。

更具体的可以看这张图:

图上的一些概念:

  • Reference Model:预训练的大语言模型
  • Reward Model:奖励模型,给定输入和输出,给出得分。可以是基于神经网络训练的模型,使用人类标注数据做训练;也可以是规则模型,定死一些规则做打分。这里的输入是大语言模型一次完整的输出。
  • Value Model:对模型输出的每一步做一个预测,预测当前状态下后续能得到的收益。这里的输入是大语言模型每一次 token 的输出。
  • Policy Model:我们正在训练的大语言模型。base 是 Reference Model,训练过程中会不断更新参数,得到我们最终需要的模型。
  • GAE:Generalized Advantage Estimation 广义优势估计,评估每一个动作的好坏程度
  • q:输入 question
  • o:输出 output
  • r:模型完整的输出经过 Reward Model 计算后的分数
  • v:模型每一步的输出后的状态,经过 Value Model 计算后的价值估计值,用于评估当前 token 输出的好坏。
  • KL:Kullback-Leibler divergence KL散度,衡量新策略(当前训练中的模型输出的结果)和旧策略(base模型输出的结果)之间的差异,保障训练中策略更新幅度不要过大,保证稳定性。

PPO 用 Reward Model 计算大模型完整输出的奖励分数(r),过程中会跟原始模型的输出做对比(KL),避免偏离太远。大模型每步 token 输出,都会经过 Value Model 判定输出对后续奖励的期望值(v),经过 GAE 函数计算每步动作的好坏程度,更新我们在训练的大模型 Policy Model 参数。

GRPO 去掉了 Value Model,对每个输入(q)输出多个结果o1 o2 o3 …,奖励模型判断这些结果的好坏,从中选出最好的,更新模型参数。对算法和公式更详细的介绍,推荐这两个讲解:(1)(2)

GRPO 去掉 Value Model,带来了一些好处:

  1. 大大降低计算成本,少了价值模型,训练过程的内存占用、计算量、整体效率都更好。
  2. 更适合开放式的问题推理,不受限于价值模型判断的准确性。
  3. 整个流程也更简洁优美了。

Reward Model

从上图可以看到,主要需要设计的只剩 Reward Model,R1-Zero 设计了一个基于规则的 Reward Model,之所以用简单的规则而不是基于神经网络的模型做奖励判定,一个是不需要训练,简化了流程和降低成本,一个是如果用神经网络模型作为奖励判定,如果这个判定模型不够强,可能会让训练的大语言模型钻空子作弊,效果不一定好。

主要是两个简单的规则:

  1. 答案判断,如果是数学问题,会要求按格式要求输出最终答案,对比答案是否正确,对于 LeeCode 上的程序问题,会用编译器去判断能不能跑通对应的 test case。
  2. 格式判断,有没有按照指定的格式输出内容,也就是思维链的内容在<think></think>里,输出的内容在<answer></answer>里。

训练模板

训练时会输入预置 prompt,让模型按格式要求输出。这个模板有意设置得很简单,避免带偏模型,也不会告诉模型要进行反思性推理或者其他推理策略,确保可以观察到模型在强化学习过程中自发去发现怎样的推理方式才是更有效的。

现象&结果

训练过程中两个重要的现象:

1. 输出越来越长:随着训练量的推进,输出的长度稳步加长,推理能力也随之提升。这也验证了 OpenAI o1 里提到的 test-time scaling,也就是更长的推理输出能更好提升模型推理能力,模型在强化学习过程中自主发现了这个规律。

2. Aha moment:训练过程中模型学会了停下来重新评估前面的思考,而且使用拟人的口气进行了反思。强化学习过程中人类没有输入任何类似的反思引导,模型能自主进行这种反思,十分有趣。我理解为,在预训练的模型里,模型已经有这样的反思意识潜力,在训练的某次输出过程中偶然出现,而出现后的结果对复杂推理有帮助,强化学习的机制会持续鼓励这样的输出。

训练的结果,在推理相关的 benchmark上,基本达到 o1 的水平:

R1-Zero 有非常有趣的发现,即不做监督学习,仅对预训练模型进行强化学习这条路是 OK 的,最终的模型有媲美o1的推理能力,但当前这种方式训练出的 R1-Zero 实际对外使用会有些问题,主要是对人不友好,比如输出的内容中英文混杂,猜测是奖励模型并没有做这种人类阅读友好的奖励判定,模型没理由能做好这块。

R1

为了做出一个推理能力强,输出对人类友好,综合能力 OK 的模型,DeepSeek 另外训练了R1模型。

整个流程可以可以看这图,图片来自 这个视频,顺便推荐这个视频的讲解。

这里做了两个阶段的后训练,每个阶段都是先 SFT,再进行 RL,只是数据和目标不同。

一阶段

  1. 用少量的几千个包含了思维链推理的数据,对预训练模型做 SFT。虽然上面说 R1-Zero 不用 SFT 直接用 RL 也可以涌现推理思维链,但有少量精品数据做 SFT,RL 初期就能让模型生成可读性较好的结果,另外可能也能让训练冷启动效率更高,可以看这个视频感受下,在复现过程中,如果没有 SFT 直接 RL,前期要经历比较长的无效训练。
  2. 用跟训练 R1-Zero 同样的步骤,用 coding、数学、科学、逻辑 这些推理密集的数据,对 SFT 后的模型做强化学习。这次强化学习的奖励模型里增加了对语言一致性的奖励,减少模型输出中英夹杂的概率,虽然加了后发现对模型的推理能力会有些下降,但还能接受。

二阶段

  1. 用把上述强化学习后的模型,生成与推理相关的数据,并对这些数据择优,包括去掉语言一致性不行的case、去掉长篇大论的case、对同个问题生成多次并选择最好的一次,拿到质量相对好的60w个推理相关数据,加上另外收集标注的 20w 个跟推理无关,包含写作、事实性回答、翻译等数据,一起对模型再做一次SFT。R1 的中文输出很强,更大可能是跟这 20w 数据的质量高相关。
  2. 把上述 SFT 后的模型,再做一次强化学习。这次强化学习具体没有展开,主要是引导模型安全输出,过滤有害输出,以及再次提升推理能力的一次训练。这次的奖励模型额外再加上了对通用内容的奖励,在文科内容、日常问答上,应该也准备了一些质量比较高的 case 和奖励规则模型。

这几个步骤后,R1 就训练完成了,可以看到这个基于 V3 模型的后训练过程成本是很低的,数据量不大,但效果非常显著,特别是在编码和数学问题上,R1 相对 V3 提升了几个档次。

而这个过程,看起来也是可以 scale 的,可以用这个训好的模型继续多步生成一些case,择优组成新的数据,继续进行 SFT 和强化学习。

这条显著提升模型推理能力的后训练路跑通了,公开解了 o1 一直遮遮掩掩的强化学习路线,也展现了很好的低成本持续 scale up 的潜力。沿着这条路走能 scale 到什么程度还不太清楚,拭目以待。

蒸馏

R1 训完了,最终我们用的就是上述训练出来的模型,但这篇论文还没完,DeepSeek 的同学发现用上述 R1 训练过程中生成的 60w 推理相关的数据,以及 20w 的额外数据去对小模型做 SFT,小模型性能的提升非常明显。

看起来这一步纯粹是用质量更好的数据对小模型做SFT,只是这些数据大部分是 R1 生成的,相当于是蒸馏提取了 R1 的能力精华,拟合了 R1 的能力,这样也能给小模型带来较好的推理能力提升。

从分数看,这些小模型在数学和 coding 上的性能是不错的,如果1.5b在部分场景上能有媲美4o的效果,那端上大模型会迎来一波应用潮。但实际用起来没那么理想,试过 1.5B 模型基本不遵循指令,有些刷分的感觉,但这仅是做了初步的SFT 后的效果,在这基础上对小模型继续进行强化学习,可能整体能力能有提升,也是值得期待的点。

这里论文上还额外做了另一个实验,用类似 R1-Zero 的方法直接对小模型做强化学习,实验效果是相对用蒸馏数据做 SFT 的方式,效果差很多。一个猜测是小模型就是能力有限,直接用强化学习达到顿悟和性能提升,得基于模型本身能力足够才行。

到这里 R1 论文内容结束了,结尾部分提到后续会在多轮对话、json输出、语言混合、提示词问题、写工程代码弱这些问题上提升的展望,解决这些只是时间问题。

复现

这篇论文介绍了 R1 整个算法、训练流程和结果,但最核心的应该是数据,包括用于 R1-Zero 的数据是什么,数据量有多大,生成的 60w 数据具体是什么样的,标注的 20w 文科数据是什么,这是决定模型效果的另一个核心因素,DeepSeek 的中文效果出圈,应该很大程度还是标注的 20w 文科数据质量高,不确定 RL 带来的推理能力提升在文科这块上的帮助有多大。这些数据没有公开,友商要复刻出 DeepSeek 的效果没那么容易。

网上有不少开始复现 R1 和 R1-Zero 的开源项目研究,最大的是 huggingface 的 open-r1,也有学生在 3B 模型上小规模复现 R1-Zero 的开源项目 TinyZero

感受

  1. DeepSeek APP 上线 20天 DAU 超过 2000w,成为历史用户增长最快的 APP,更让人感受到通用 AI chatbot 没有护城河可言,无论是国内的豆包、kimi,还是 ChatGPT、Claude,只要有更好的模型出现,用户瞬间转移不带留恋的。搜索是有技术积累的壁垒的,社交有关系链,内容平台有内容壁垒,基于 chatbot 形态的产品,没看到有什么壁垒,用户数据没有作用,曾以为模型领先就是壁垒,OpenAI 可以凭借领先收高额费用和大部分用户的忠诚,DeepSeek 这波又打破了这种壁垒,领先者无法保证自己一直领先。
  2. DeepSeek 带来中国自信,曾经认为,类似 AICoding 这种全球无差异竞争的产品,国内同学们怎么搞得过海外那些清一色 MIT 天才搞的产品?DeepSeek 的成功叙事给了这些直面竞争产品很大的信心,这种信心下中概股都被疯狂拉动,还是很让人激动的。

500 美元一个月的 Devin 是怎么实现的

使用

这两天有机会体验了下 Devin,感受到一些小小的震撼。

虽然之前已经用过 cursor 和 windsurf,它们用的模型都一样,理论上能完成的任务和智力是差不多的,但用 Devin 感受还是不太一样,有种 AGI 已经实现了的感觉。

Cursor 和 Devin 核心区别是交互范式,Cursor 是 Copilot,在你工作写代码过程中,实时辅助完成编程任务,而 Devin 是一个员工,交给他复杂任务后不用管它,它主动帮你搞定。可能现在这两者完成的很多任务是一致的,但体验有差异。

我试用的其中一个任务,是扔给它开源项目 JSPatch 的 github 地址,告诉它找个 issue 修一下。它会分解任务逐步执行,包括:

  1. 访问 github 网站,浏览issue列表,随机看几个 issue 详情
  2. 挑了个 block 相关 issue,浏览项目相关文件,寻找与 issue 相关联的代码文件,制定修复计划
  3. 写测试用例 → 修改项目代码尝试修复 issue → 跑用例验证(没 iOS 环境没跑起来) → commit 和提交 PR

这个过程是自动和异步的,它跑在虚拟机里,不需要你提供环境,不需要盯着它,它会自己去调研怎么完成这个任务,做完了会来告诉你(如果用 slack,这个体验过程更顺畅,@它下达任务,任务完成slack回复),这跟给一个员工布置任务,等他做完验收结果的体验很一致。

畅想

现在 Devin 解决问题的能力肯定还有限,真正用下来磕磕碰碰很多任务还是完成不好,现在的模型能力下 Cursor 这种 Copilot 的形态是更实用的,但未来理想的形态肯定是 Devin 这种“员工”形态,因为可以解放注意力,无限扩展同一时间能做的事。

可以想象,这种形态未来的优化速度会很乐观:

  1. 基础模型的思维规划能力还没收敛,从sonet o1 到 o3 可以看到还在快速提升
  2. 即使基础模型能力放缓了,模型在领域上的调优还有很大空间。
  3. 更多工具的接入,也能带来更强大的能力和体验。

以及,模型成本必然比摩尔定律更快速的下降,Devin 会持续用最好的模型最贵的方案,但今天 500美金的效果,一年后成本可能只要5美金就能做到。

Devin 所实现的概念早在 23年初 AutoGPT 就提出,只是当时模型能力还不具备,Claude Sonnet / GPT 4o / GPT o1 这种级别推理能力的模型出现后,才具备可用性,Devin 是实现了这个概念下初步可用的雏形,让人看到这个方向已经初步 ready,剩下的就是持续往这个方向优化和扩展了,Devin 确实称得上是数字员工的开端,设计师agent,交易员agent,数据分析师agent,电商agent,预计会陆续出现。

原理

Devin 是怎么实现的?

有个开源项目 OpenHands(前身 OpenDevin),尝试用开源社区的方式去构建类似 Devin 做的事,虽然能力和效果上不能完全对标 Devin,但可以看个基本雏形。相关论文:https://arxiv.org/abs/2407.16741

文中的这张架构图,可以比较好描述 OpenHands 是怎么做:

三个主要部分:

  1. Event Stream:记录每一步指令和执行结果,Action 是指令,Observation 是指令对应的工具执行的结果,Action 和 Observation 最终都是以纯文本记录结果。
  2. Runtime:程序运行在独立的 docker 容器里,提供一些工具给 Agent 调用执行,当前包括 Python 运行、终端命令行、浏览器
  3. Agent:把 Event Stream 里的所有内容作为上下文,输入到 LLM 推理下一步Action。

还有个关键点没有在图上画出来,为什么把 Event Stream 的所有内容输入到 LLM,LLM 就会按照要求推理出下一步 Action?因为输入到 LLM 的除了 Event Stream 的上下文,还有 Agent 本身的 Prompt,这个 Prompt 描述一些处理原则、当前环境、可用的工具、每个工具的参数、预期输出的格式等,以及还配套了一个示例,指引模型按要求输出。这个 Prompt 本身贴在了文末。

我们跟着图上 Event Stream 的示例,跑一下这个流程:

  1. 用户输入一个任务命令:“Can you create a list of numbers from 1 to 10, and create a web page to display them at port 5000?” 到 Event Stream
  2. 当前 Event Stream 只有这一条命令内容,输入到 Agent,Agent 会拿它跟上述预置 prompt 一起输入给 LLM,LLM 会推理出下一步是调用 Python 去创建 app.py 文件,输出 Action 指令到 Event Stream。
  3. 工具 IPython 对应的 Observation 监听到这个指令,在 Runtime 环境执行了这个命令,输出执行结果app.py created” 记录在 Event Stream 上
  4. Event Stream 接收到 Observation 新的执行结果后,Agent 程序会自动继续把整个 Event Stream 记录连同预置 Prompt 输入给 LLM,推测下一步 Action。推测出来的下一步 Action 是把一大段代码用调用 python 命令的方式写入刚才创建的文件
  5. IPython Observation 接收到这个 Action,在 Runtime 环境执行命令,输出执行结果到 Event Stream。

接下来就是不断的循环:Action 驱动 Observation 用工具做处理 → 处理结果输出到 Event Stream → Event Stream 拿所有前文内容到 LLM 输出下一步 Action → 驱动Observation 用工具做处理…

后面的6-9步用了命令行工具和浏览器工具,流程是一样的。这个循环流程什么时候结束?有一个特殊的 Action 叫 finish,如果一个任务 LLM 认为完成了,就会输出调用 finish Action,程序接收到就退出循环,等用户下一步输入。

整体就是自动循环让 LLM 预测下一步动作 → Agent 程序调用工具执行动作 的过程。补充一些点:

  1. 整个项目不涉及模型训练,纯工程方案,使用通用 LLM 模型,可以配置 Claude/GPT/Deepseek 等,不过可预见的演进是根据用户使用数据去优化模型以达到领域内更好的效果。
  2. 这里没有实现像 Devin 在输出 Action 前先会规划好任务的步骤再一步步执行,但要在上述这个系统加上这个规划能力预计不难。
  3. 项目使用 BrowserGym 去和浏览器交互,Agent 对浏览器的操作和识别是另一个大课题,有单独的 benchmark 多种方案,待调研。
  4. 随着 Event Stream 里链路的不断增加,上下文会越来越长,到一定程度 openhands 会做两种处理,一种是压缩内容,把前面的上下文发给 llm 精练总结,用更简短的内容作为后续的上下文。另一种是让 LLM 对过去的内容进行重要度排序,只选择对预测下一步重要的几个记录作为上下文,具体逻辑在 condenser.py 里。更长的上下文会用向量数据库 ChromeDB 存储。

附:预置prompt

You are OpenHands agent, a helpful AI assistant that can interact with a computer to solve tasks.
<IMPORTANT>
//
* If user provides a path, you should NOT assume it's relative to the current working directory. Instead, you should explore the file system to find the file before working on it.
* When configuring git credentials, use "openhands" as the user.name and "openhands@all-hands.dev" as the user.email by default, unless explicitly instructed otherwise.
* The assistant MUST NOT include comments in the code unless they are necessary to describe non-obvious behavior.
RuntimeInfo(available_hosts={'http://localhost:54090': 54090, 'http://localhost:55602': 55602})
</IMPORTANT>

<RUNTIME_INFORMATION>
The user has access to the following hosts for accessing a web application,
each of which has a corresponding port:
* http://localhost:54090 (port 54090)
* http://localhost:55602 (port 55602)

When starting a web server, use the corresponding ports. You should also
set any options to allow iframes and CORS requests.
</RUNTIME_INFORMATION>
You have access to the following functions:

---- BEGIN FUNCTION #1: execute_bash ----
Description: Execute a bash command in the terminal.
* Long running commands: For commands that may run indefinitely, it should be run in the background and the output should be redirected to a file, e.g. command = `python3 app.py > server.log 2>&1 &`.
* Interactive: If a bash command returns exit code `-1`, this means the process is not yet finished. The assistant must then send a second call to terminal with an empty `command` (which will retrieve any additional logs), or it can send additional text (set `command` to the text) to STDIN of the running process, or it can send command like `C-c` (Ctrl+C) to interrupt the process.

Parameters:
  (1) command (string, required): The bash command to execute. Can be empty string to view additional logs when previous exit code is `-1`. Can be `C-c` (Ctrl+C) to interrupt the currently running process.
---- END FUNCTION #1 ----

---- BEGIN FUNCTION #2: finish ----
Description: Finish the interaction when the task is complete OR if the assistant cannot proceed further with the task.
No parameters are required for this function.
---- END FUNCTION #2 ----

---- BEGIN FUNCTION #3: web_read ----
Description: Read (convert to markdown) content from a webpage. You should prefer using the `web_read` tool over the `browser` tool, but do use the `browser` tool if you need to interact with a webpage (e.g., click a button, fill out a form, etc.).

You may use the `web_read` tool to read content from a webpage, and even search the webpage content using a Google search query (e.g., url=`https://www.google.com/search?q=YOUR_QUERY`).

Parameters:
  (1) url (string, required): The URL of the webpage to read. You can also use a Google search query here (e.g., `https://www.google.com/search?q=YOUR_QUERY`).
---- END FUNCTION #3 ----

---- BEGIN FUNCTION #4: browser ----
Description: Interact with the browser using Python code. Use it ONLY when you need to interact with a webpage.

See the description of "code" parameter for more details.

Multiple actions can be provided at once, but will be executed sequentially without any feedback from the page.
More than 2-3 actions usually leads to failure or unexpected behavior. Example:
fill('a12', 'example with "quotes"')
click('a51')
click('48', button='middle', modifiers=['Shift'])

Parameters:
  (1) code (string, required): The Python code that interacts with the browser.

The following 15 functions are available. Nothing else is supported.

goto(url: str)
    Description: Navigate to a url.
    Examples:
        goto('http://www.example.com')

go_back()
    Description: Navigate to the previous page in history.
    Examples:
        go_back()

go_forward()
    Description: Navigate to the next page in history.
    Examples:
        go_forward()

noop(wait_ms: float = 1000)
    Description: Do nothing, and optionally wait for the given time (in milliseconds).
    You can use this to get the current page content and/or wait for the page to load.
    Examples:
        noop()

        noop(500)

scroll(delta_x: float, delta_y: float)
    Description: Scroll horizontally and vertically. Amounts in pixels, positive for right or down scrolling, negative for left or up scrolling. Dispatches a wheel event.
    Examples:
        scroll(0, 200)

        scroll(-50.2, -100.5)

fill(bid: str, value: str)
    Description: Fill out a form field. It focuses the element and triggers an input event with the entered text. It works for <input>, <textarea> and [contenteditable] elements.
    Examples:
        fill('237', 'example value')

        fill('45', 'multi-line
example')

        fill('a12', 'example with "quotes"')

select_option(bid: str, options: str | list[str])
    Description: Select one or multiple options in a <select> element. You can specify option value or label to select. Multiple options can be selected.
    Examples:
        select_option('a48', 'blue')

        select_option('c48', ['red', 'green', 'blue'])

click(bid: str, button: Literal['left', 'middle', 'right'] = 'left', modifiers: list[typing.Literal['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']] = [])
    Description: Click an element.
    Examples:
        click('a51')

        click('b22', button='right')

        click('48', button='middle', modifiers=['Shift'])

dblclick(bid: str, button: Literal['left', 'middle', 'right'] = 'left', modifiers: list[typing.Literal['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']] = [])
    Description: Double click an element.
    Examples:
        dblclick('12')

        dblclick('ca42', button='right')

        dblclick('178', button='middle', modifiers=['Shift'])

hover(bid: str)
    Description: Hover over an element.
    Examples:
        hover('b8')

press(bid: str, key_comb: str)
    Description: Focus the matching element and press a combination of keys. It accepts the logical key names that are emitted in the keyboardEvent.key property of the keyboard events: Backquote, Minus, Equal, Backslash, Backspace, Tab, Delete, Escape, ArrowDown, End, Enter, Home, Insert, PageDown, PageUp, ArrowRight, ArrowUp, F1 - F12, Digit0 - Digit9, KeyA - KeyZ, etc. You can alternatively specify a single character you'd like to produce such as "a" or "#". Following modification shortcuts are also supported: Shift, Control, Alt, Meta, ShiftLeft, ControlOrMeta. ControlOrMeta resolves to Control on Windows and Linux and to Meta on macOS.
    Examples:
        press('88', 'Backspace')

        press('a26', 'ControlOrMeta+a')

        press('a61', 'Meta+Shift+t')

focus(bid: str)
    Description: Focus the matching element.
    Examples:
        focus('b455')

clear(bid: str)
    Description: Clear the input field.
    Examples:
        clear('996')

drag_and_drop(from_bid: str, to_bid: str)
    Description: Perform a drag & drop. Hover the element that will be dragged. Press left mouse button. Move mouse to the element that will receive the drop. Release left mouse button.
    Examples:
        drag_and_drop('56', '498')

upload_file(bid: str, file: str | list[str])
    Description: Click an element and wait for a "filechooser" event, then select one or multiple input files for upload. Relative file paths are resolved relative to the current working directory. An empty list clears the selected files.
    Examples:
        upload_file('572', '/home/user/my_receipt.pdf')

        upload_file('63', ['/home/bob/Documents/image.jpg', '/home/bob/Documents/file.zip'])

---- END FUNCTION #4 ----

---- BEGIN FUNCTION #5: execute_ipython_cell ----
Description: Run a cell of Python code in an IPython environment.
* The assistant should define variables and import packages before using them.
* The variable defined in the IPython environment will not be available outside the IPython environment (e.g., in terminal).

Parameters:
  (1) code (string, required): The Python code to execute. Supports magic commands like %pip.
---- END FUNCTION #5 ----

---- BEGIN FUNCTION #6: str_replace_editor ----
Description: Custom editing tool for viewing, creating and editing files
* State is persistent across command calls and discussions with the user
* If `path` is a file, `view` displays the result of applying `cat -n`. If `path` is a directory, `view` lists non-hidden files and directories up to 2 levels deep
* The `create` command cannot be used if the specified `path` already exists as a file
* If a `command` generates a long output, it will be truncated and marked with `<response clipped>`
* The `undo_edit` command will revert the last edit made to the file at `path`

Notes for using the `str_replace` command:
* The `old_str` parameter should match EXACTLY one or more consecutive lines from the original file. Be mindful of whitespaces!
* If the `old_str` parameter is not unique in the file, the replacement will not be performed. Make sure to include enough context in `old_str` to make it unique
* The `new_str` parameter should contain the edited lines that should replace the `old_str`

Parameters:
  (1) command (string, required): The commands to run. Allowed options are: `view`, `create`, `str_replace`, `insert`, `undo_edit`.
Allowed values: [`view`, `create`, `str_replace`, `insert`, `undo_edit`]
  (2) path (string, required): Absolute path to file or directory, e.g. `/workspace/file.py` or `/workspace`.
  (3) file_text (string, optional): Required parameter of `create` command, with the content of the file to be created.
  (4) old_str (string, optional): Required parameter of `str_replace` command containing the string in `path` to replace.
  (5) new_str (string, optional): Optional parameter of `str_replace` command containing the new string (if not given, no string will be added). Required parameter of `insert` command containing the string to insert.
  (6) insert_line (integer, optional): Required parameter of `insert` command. The `new_str` will be inserted AFTER the line `insert_line` of `path`.
  (7) view_range (array, optional): Optional parameter of `view` command when `path` points to a file. If none is given, the full file is shown. If provided, the file will be shown in the indicated line number range, e.g. [11, 12] will show lines 11 and 12. Indexing at 1 to start. Setting `[start_line, -1]` shows all lines from `start_line` to the end of the file.
---- END FUNCTION #6 ----


If you choose to call a function ONLY reply in the following format with NO suffix:

<function=example_function_name>
<parameter=example_parameter_1>value_1</parameter>
<parameter=example_parameter_2>
This is the value for the second parameter
that can span
multiple lines
</parameter>
</function>

<IMPORTANT>
Reminder:
- Function calls MUST follow the specified format, start with <function= and end with </function>
- Required parameters MUST be specified
- Only call one function at a time
- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after.
- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls


----------

Here's a running example of how to perform a task with the provided tools.

--------------------- START OF EXAMPLE ---------------------

USER: Create a list of numbers from 1 to 10, and display them in a web page at port 5000.

ASSISTANT: Sure! Let me first check the current directory:
<function=execute_bash>
<parameter=command>
pwd && ls
</parameter>
</function>

USER: EXECUTION RESULT of [execute_bash]:
/workspace
openhands@runtime:~/workspace$

ASSISTANT: There is no `app.py` file in the current directory. Let me create a Python file `app.py`:
<function=str_replace_editor>
<parameter=command>create</parameter>
<parameter=path>/workspace/app.py</parameter>
<parameter=file_text>
from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    numbers = list(range(1, 11))
    return str(numbers)

if __name__ == '__main__':
    app.run(port=5000)
</parameter>
</function>

USER: EXECUTION RESULT of [str_replace_editor]:
File created successfully at: /workspace/app.py

ASSISTANT: I have created a Python file `app.py` that will display a list of numbers from 1 to 10 when you run it. Let me run the Python file for you:
<function=execute_bash>
<parameter=command>
python3 app.py > server.log 2>&1 &
</parameter>
</function>

USER: EXECUTION RESULT of [execute_bash]:
[1] 121
[1]+  Exit 1                  python3 app.py > server.log 2>&1

ASSISTANT: Looks like the server is running with PID 121 then crashed. Let me check the server log:
<function=execute_bash>
<parameter=command>
cat server.log
</parameter>
</function>

USER: EXECUTION RESULT of [execute_bash]:
Traceback (most recent call last):
  File "/workspace/app.py", line 2, in <module>
    from flask import Flask
ModuleNotFoundError: No module named 'flask'

ASSISTANT: Looks like the server crashed because the `flask` module is not installed. Let me install the `flask` module for you:
<function=execute_bash>
<parameter=command>
pip3 install flask
</parameter>
</function>

USER: EXECUTION RESULT of [execute_bash]:
Defaulting to user installation because normal site-packages is not writeable
Collecting flask
  Using cached flask-3.0.3-py3-none-any.whl (101 kB)
Collecting blinker>=1.6.2
  Using cached blinker-1.7.0-py3-none-any.whl (13 kB)
Collecting Werkzeug>=3.0.0
  Using cached werkzeug-3.0.2-py3-none-any.whl (226 kB)
Collecting click>=8.1.3
  Using cached click-8.1.7-py3-none-any.whl (97 kB)
Collecting itsdangerous>=2.1.2
  Using cached itsdangerous-2.2.0-py3-none-any.whl (16 kB)
Requirement already satisfied: Jinja2>=3.1.2 in /home/openhands/.local/lib/python3.10/site-packages (from flask) (3.1.3)
Requirement already satisfied: MarkupSafe>=2.0 in /home/openhands/.local/lib/python3.10/site-packages (from Jinja2>=3.1.2->flask) (2.1.5)
Installing collected packages: Werkzeug, itsdangerous, click, blinker, flask
Successfully installed Werkzeug-3.0.2 blinker-1.7.0 click-8.1.7 flask-3.0.3 itsdangerous-2.2.0

ASSISTANT: The `flask` module is installed successfully. Let me run the Python file again:
<function=execute_bash>
<parameter=command>
python3 app.py > server.log 2>&1 &
</parameter>
</function>

USER: EXECUTION RESULT of [execute_bash]:
[1] 124

ASSISTANT:
Let me check the server log again:
<function=execute_bash>
<parameter=command>
cat server.log
</parameter>
</function>

USER: EXECUTION RESULT of [execute_bash]:
* Serving Flask app 'app'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit

ASSISTANT:
The server is running on port 5000 with PID 124. You can access the list of numbers by visiting http://127.0.0.1:5000. If you have any further questions, feel free to ask!

USER: Now kill the server, make it display the numbers in a table format.

ASSISTANT:
Sure! Let me stop the server first:
<function=execute_bash>
<parameter=command>
kill 124
</parameter>
</function>

USER: EXECUTION RESULT of [execute_bash]:
[1]+  Terminated              python3 app.py > server.log 2>&1

ASSISTANT:
Now let me display the numbers in a table format:
<function=str_replace_editor>
<parameter=command>str_replace</parameter>
<parameter=path>/workspace/app.py</parameter>
<parameter=old_str>return str(numbers)</parameter>
<parameter=new_str>return '<table>' + ''.join([f'<tr><td>{i}</td></tr>' for i in numbers]) + '</table>'</parameter>
</function>

USER: EXECUTION RESULT of [str_replace_editor]:
The file /workspace/app.py has been edited. Here's the result of running `cat -n` on a snippet of /workspace/app.py:
     3
     4  @app.route('/')
     5  def index():
     6      numbers = list(range(1, 11))
     7      return '<table>' + ''.join([f'<tr><td>{i}</td></tr>' for i in numbers]) + '</table>'
     8
     9  if __name__ == '__main__':
    10      app.run(port=5000)
Review the changes and make sure they are as expected. Edit the file again if necessary.

ASSISTANT:
Running the updated file:
<function=execute_bash>
<parameter=command>
<parameter=command>
python3 app.py > server.log 2>&1 &
</parameter>
</function>

USER: EXECUTION RESULT of [execute_bash]:
[1] 126

ASSISTANT:
The server is running on port 5000 with PID 126. You can access the list of numbers in a table format by visiting http://127.0.0.1:5000. Let me know if you have any further requests!
<function=finish>
</function>

--------------------- END OF EXAMPLE ---------------------

Do NOT assume the environment is the same as in the example above.

附:任务 Prompt示例

以下是真实执行一个任务时,循环添加输入 LLM 的 Prompt case:

--------------------- NEW TASK DESCRIPTION ---------------------
创建一个可以调用deepseek模型的程序,并运行测试确保可用
--------------------- END OF NEW TASK DESCRIPTION ---------------------

PLEASE follow the format strictly! PLEASE EMIT ONE AND ONLY ONE FUNCTION CALL PER MESSAGE.


----------

Let me start by checking the current directory and listing its contents:

<function=execute_bash>
<parameter=command>pwd && ls</parameter>
</function>

----------

EXECUTION RESULT of [execute_bash]:
/workspace
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 0]

----------

I see we are in the `/workspace` directory. Let's create a Python file that can call the DeepSeek model. First, I'll create a new file called `deepseek_app.py`:

<function=str_replace_editor>
<parameter=command>create</parameter>
<parameter=path>/workspace/deepseek_app.py</parameter>
<parameter=file_text>
from deepseek import DeepSeekModel

def call_deepseek_model(input_text):
    model = DeepSeekModel()
    response = model.generate(input_text)
    return response

if __name__ == "__main__":
    test_input = "Hello, DeepSeek!"
    result = call_deepseek_model(test_input)
    print("Model Response:", result)
</parameter>
</function>

----------

EXECUTION RESULT of [str_replace_editor]:
File created successfully at: /workspace/deepseek_app.py

----------

Now, let's check if the `deepseek` module is installed. If not, we'll need to install it:

<function=execute_bash>
<parameter=command>pip3 show deepseek</parameter>
</function>

----------

EXECUTION RESULT of [execute_bash]:
WARNING: Package(s) not found: deepseek
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 1]

----------

The `deepseek` module is not installed. Let me install it using pip:

<function=execute_bash>
<parameter=command>pip3 install deepseek</parameter>
</function>

----------

EXECUTION RESULT of [execute_bash]:
Collecting deepseek
  Downloading deepseek-1.0.0-py3-none-any.whl.metadata (3.9 kB)
Requirement already satisfied: requests in /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages (from deepseek) (2.32.3)
Requirement already satisfied: charset-normalizer<4,>=2 in /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages (from requests->deepseek) (3.4.1)
Requirement already satisfied: idna<4,>=2.5 in /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages (from requests->deepseek) (3.10)
Requirement already satisfied: urllib3<3,>=1.21.1 in /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages (from requests->deepseek) (2.3.0)
Requirement already satisfied: certifi>=2017.4.17 in /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages (from requests->deepseek) (2024.12.14)
Downloading deepseek-1.0.0-py3-none-any.whl (4.5 kB)
Installing collected packages: deepseek
Successfully installed deepseek-1.0.0
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 0]

----------

The `deepseek` module is now installed. Let's run the `deepseek_app.py` program to test if it works:

<function=execute_bash>
<parameter=command>python3 deepseek_app.py</parameter>
</function>

----------

EXECUTION RESULT of [execute_bash]:
Traceback (most recent call last):
  File "/workspace/deepseek_app.py", line 1, in <module>
    from deepseek import DeepSeekModel
ImportError: cannot import name 'DeepSeekModel' from 'deepseek' (/openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages/deepseek/__init__.py)
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 1]

----------

It seems the `DeepSeekModel` class is not available in the `deepseek` module. Let me check the documentation or available classes in the `deepseek` module:

<function=execute_bash>
<parameter=command>python3 -c "import deepseek; print(dir(deepseek))"</parameter>
</function>

----------

EXECUTION RESULT of [execute_bash]:
['DeepSeekAPI', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', 'api', 'const']
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 0]

----------

The `deepseek` module provides a `DeepSeekAPI` class instead of `DeepSeekModel`. Let me update the `deepseek_app.py` file to use the correct class:

<function=str_replace_editor>
<parameter=command>str_replace</parameter>
<parameter=path>/workspace/deepseek_app.py</parameter>
<parameter=old_str>
from deepseek import DeepSeekModel

def call_deepseek_model(input_text):
    model = DeepSeekModel()
    response = model.generate(input_text)
    return response
</parameter>
<parameter=new_str>
from deepseek import DeepSeekAPI

def call_deepseek_model(input_text):
    api = DeepSeekAPI()
    response = api.generate(input_text)
    return response
</parameter>
</function>

----------

EXECUTION RESULT of [str_replace_editor]:
The file /workspace/deepseek_app.py has been edited. Here's the result of running `cat -n` on a snippet of /workspace/deepseek_app.py:
     1        from deepseek import DeepSeekAPI
     2        
     3        def call_deepseek_model(input_text):
     4            api = DeepSeekAPI()
     5            response = api.generate(input_text)
     6            return response
     7        
     8        if __name__ == "__main__":
     9            test_input = "Hello, DeepSeek!"
    10            result = call_deepseek_model(test_input)
Review the changes and make sure they are as expected. Edit the file again if necessary.

----------

Now that the file is updated, let's run the program again to test if it works:

<function=execute_bash>
<parameter=command>python3 deepseek_app.py</parameter>
</function>

----------

EXECUTION RESULT of [execute_bash]:
Traceback (most recent call last):
  File "/workspace/deepseek_app.py", line 10, in <module>
    result = call_deepseek_model(test_input)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/deepseek_app.py", line 4, in call_deepseek_model
    api = DeepSeekAPI()
          ^^^^^^^^^^^^^
  File "/openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages/deepseek/api.py", line 9, in __init__
    raise ValueError("DEEPSEEK_API_KEY is missing")
ValueError: DEEPSEEK_API_KEY is missing
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 1]

----------

The program requires an API key to use the `DeepSeekAPI`. Let me update the program to include the API key as an environment variable. First, I'll modify the `deepseek_app.py` file:

<function=str_replace_editor>
<parameter=command>str_replace</parameter>
<parameter=path>/workspace/deepseek_app.py</parameter>
<parameter=old_str>
from deepseek import DeepSeekAPI

def call_deepseek_model(input_text):
    api = DeepSeekAPI()
    response = api.generate(input_text)
    return response
</parameter>
<parameter=new_str>
import os
from deepseek import DeepSeekAPI

def call_deepseek_model(input_text):
    api_key = os.getenv("DEEPSEEK_API_KEY")
    if not api_key:
        raise ValueError("Please set the DEEPSEEK_API_KEY environment variable.")
    api = DeepSeekAPI(api_key=api_key)
    response = api.generate(input_text)
    return response
</parameter>
</function>

----------

EXECUTION RESULT of [str_replace_editor]:
The file /workspace/deepseek_app.py has been edited. Here's the result of running `cat -n` on a snippet of /workspace/deepseek_app.py:
     1        import os
     2        from deepseek import DeepSeekAPI
     3        
     4        def call_deepseek_model(input_text):
     5            api_key = os.getenv("DEEPSEEK_API_KEY")
     6            if not api_key:
     7                raise ValueError("Please set the DEEPSEEK_API_KEY environment variable.")
     8            api = DeepSeekAPI(api_key=api_key)
     9            response = api.generate(input_text)
    10            return response
    11        
    12        if __name__ == "__main__":
    13            test_input = "Hello, DeepSeek!"
    14            result = call_deepseek_model(test_input)
Review the changes and make sure they are as expected. Edit the file again if necessary.

----------

Now, let's set the `DEEPSEEK_API_KEY` environment variable and run the program again. Please provide the API key for DeepSeek, and I will set it up for you. If you don't have one, you may need to sign up for an API key from DeepSeek's official website.

<function=execute_bash>
<parameter=command>export DEEPSEEK_API_KEY=your_api_key_here && python3 deepseek_app.py</parameter>
</function>

----------

EXECUTION RESULT of [execute_bash]:
Traceback (most recent call last):
  File "/workspace/deepseek_app.py", line 14, in <module>
    result = call_deepseek_model(test_input)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/deepseek_app.py", line 9, in call_deepseek_model
    response = api.generate(input_text)
               ^^^^^^^^^^^^
AttributeError: 'DeepSeekAPI' object has no attribute 'generate'
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 1]

----------

It seems the `DeepSeekAPI` class does not have a `generate` method. Let me check the available methods in the `DeepSeekAPI` class:

<function=execute_bash>
<parameter=command>python3 -c "from deepseek import DeepSeekAPI; print(dir(DeepSeekAPI))"</parameter>
</function>

----------

EXECUTION RESULT of [execute_bash]:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_post_request', 'chat_completion', 'completion_impl', 'fim_completion', 'get_models', 'user_balance']
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 0]

----------

The `DeepSeekAPI` class provides methods like `chat_completion`, `fim_completion`, and `get_models`. Let's update the `deepseek_app.py` file to use the `chat_completion` method instead of `generate`:

<function=str_replace_editor>
<parameter=command>str_replace</parameter>
<parameter=path>/workspace/deepseek_app.py</parameter>
<parameter=old_str>
api = DeepSeekAPI(api_key=api_key)
    response = api.generate(input_text)
    return response
</parameter>
<parameter=new_str>
api = DeepSeekAPI(api_key=api_key)
    response = api.chat_completion(input_text)
    return response
</parameter>
</function>

----------

EXECUTION RESULT of [str_replace_editor]:
The file /workspace/deepseek_app.py has been edited. Here's the result of running `cat -n` on a snippet of /workspace/deepseek_app.py:
     4        def call_deepseek_model(input_text):
     5            api_key = os.getenv("DEEPSEEK_API_KEY")
     6            if not api_key:
     7                raise ValueError("Please set the DEEPSEEK_API_KEY environment variable.")
     8            api = DeepSeekAPI(api_key=api_key)
     9            response = api.chat_completion(input_text)
    10            return response
    11        
    12        if __name__ == "__main__":
    13            test_input = "Hello, DeepSeek!"
    14            result = call_deepseek_model(test_input)
Review the changes and make sure they are as expected. Edit the file again if necessary.

----------

Now, let's run the program again with the updated method. Please provide the `DEEPSEEK_API_KEY` environment variable:

<function=execute_bash>
<parameter=command>export DEEPSEEK_API_KEY=your_api_key_here && python3 deepseek_app.py</parameter>
</function>

----------

EXECUTION RESULT of [execute_bash]:
Traceback (most recent call last):
  File "/workspace/deepseek_app.py", line 14, in <module>
    result = call_deepseek_model(test_input)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/deepseek_app.py", line 9, in call_deepseek_model
    response = api.chat_completion(input_text)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages/deepseek/api.py", line 62, in chat_completion
    response = self._post_request(API_CHAT_COM, payload, stream)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages/deepseek/api.py", line 25, in _post_request
    raise Exception(f"HTTP Error {response.status_code}: {response.text}")
Exception: HTTP Error 401: {"error":{"message":"Authentication Fails (no such user)","type":"authentication_error","param":null,"code":"invalid_request_error"}}
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 1]

----------

The error indicates that the provided API key is invalid or does not correspond to a valid user. To proceed, you will need to:

1. Obtain a valid API key from DeepSeek's official platform.
2. Ensure the API key is correctly set in the environment.

Once you have a valid API key, you can set it and run the program again. Let me know if you need further assistance!

----------

key: *** ///在这里输入了key,隐藏

----------

Thank you for providing the API key. Let's set the environment variable and run the program again:

<function=execute_bash>
<parameter=command>export DEEPSEEK_API_KEY=sk-4a727097b1ef4e61a3444ec96e1e0db0 && python3 deepseek_app.py</parameter>
</function>

----------

EXECUTION RESULT of [execute_bash]:
Model Response: Hello! How can I assist you today? 😊
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 0]

Calibre 推荐配置与插件

在我的2024年阅读记录中,电子书的阅读量已经占整年阅读量的70%以上,家里的纸质书也逐步二手转让,电子书和阅读笔记就不可避免遇到整理的问题。

阅读笔记我主要是通过阅读器或阅读软件导出并整理到 Obsidian 中,我在 Obsidian 中设计了固定且简洁的阅读笔记模板,保证统一的格式和可检索性,但不会过度保存书籍的原始信息(书籍封面、页数等等感觉没必要)。

对于电子书的整理,这类软件没太多选择,灵活度且支持多平台的也只有 Calibre 能胜任。此外 Calibre-Web 更能通过 Docker 部署到 NAS 或者云主机中,折腾首选。因为我只考虑本地电子书的整理,平时阅读主要依赖墨水屏和微信读书,所以 Calibre-Web 就不折腾了。

Calibre 完全免费,支持中文,可以在 Windows、Mac 和 Linux 系统上运行,支持 TXT、PDF、EPUB、MOBI、DOCX、HTML、Markdown 等等众多格式,支持复杂的条件搜索语法和书籍全文搜索,支持本机建立内容服务器,局域网内使用浏览器访问书库及阅读。

下面介绍一下我使用 Calibre 的一些配置经验和推荐的插件(本文以 MacOS 15.2,Calibre v7.24.0 版本,本地管理方式为例)。

 

配置建议

1. 自定义栏目管理增加状态、阅读进度标记

通过“首选项”》“管理栏目”创建自定义栏目,增加“状态”栏目,用于标识电子书的阅读状态,比如我会使用”在读,已读,搁置,已读2遍,已读3遍“来记录阅读状态。同样的方法,你可以更加精细的记录阅读进度、读后短评等等。

Calibre 推荐配置与插件-雅余

 

2. 优化日期的显示方式

通过“首选项”》“优化调整”对日期的显示方式进行自定义。默认设置“MMM yyyy”实在看着别扭,建议把出版日期修改为 “yyyy-MM”,加入日期和修改日期修改为 “yyyy-MM-dd”。

Calibre 推荐配置与插件-雅余

 

3. 调整界面外观图标主题

默认图标风格太丑,可通过“首选项”》“界面外观”调整图标主题,推荐 Setenove 系列,图标大小设置为中小

Calibre 推荐配置与插件-雅余

 

4. 以中文名称保存书籍至硬盘

Calibre 默认提取书籍保存到硬盘是使用“作者简称/标题/标题 - 作者”作为路径和名称,实在啰嗦。建议按截图勾选,禁止转换为字符和封面单独保存等,保存模板直接设置以标题保存即可。更多的时候提取只是为了分享或者发送到硬件设备中。

Calibre 推荐配置与插件-雅余

 

5. 使用中文路径(禁止 Calibre 翻译文件路径)

多年以前我不愿使用 Calibre 是因为它会自动把中文名称和路径翻译转换为拼音,可读性太差,强迫症受不了。感谢 Cirn09 的 “Calibre-do-not-translate-my-path”(将我的书库从拼音目录切换至非纯英文(中文)命名)这个插件拯救了不少患者。

Calibre 推荐配置与插件-雅余

patch 方案:
通过 Github 下载 Calibre-do-not-translate-my-path

解压下载的包,得到 python-lib.bypy.frozen 文件。

打开 Finder,进入“应用程序” (/Applications),右键点击“Calibre——显示包内容”,进入 Contents/Frameworks/plugins (对应的完整路径是 /Applications/calibre.app/Contents/Frameworks/plugins )。将第 1 步下载包里的 python-lib.bypy.frozen,拷贝覆盖原文件,重新打开 Calibre 即可。

Git 上有其他系统的使用方法,作者也提供了对已建书库的批量处理方法。

 

6. 精选元数据来源

在“首选项”》“插件”中展开“元数据来源”,禁用不需要的元数据来源,减少插件的加载。

Calibre 推荐配置与插件-雅余

在“首选项”》“下载元数据”中勾选不需要的数据源,减少查询元数据时对速度的影响。我仅保留3项,基本能满足中英文书籍查询,通过豆瓣获取封面优先级为最高。

Calibre 推荐配置与插件-雅余

 

7. 禁用在线书店

在“首选项”》“插件”中展开“在线书店”,禁用不需要插件。我还把菜单栏上的快捷入口删除了。基本用不着这个功能。

Calibre 推荐配置与插件-雅余

 

8. 精选设备接口

在“首选项”》“插件”中展开“设备接口”,禁用不需要插件。我仅保留了安卓和 Kindle,其他全部禁用。

Calibre 推荐配置与插件-雅余

 

9. 备份 Calibre 设置项

在“首选项”》“杂项”中,点击“打开 'calibre 配置'文件夹“,可以跳转到 calibre 的配置文件夹,对整个文件夹进行打包备份,以便重装软件等等不时之需。

Calibre 推荐配置与插件-雅余

 

10. 生成书库书目及制作成电子书

Calibre 支持为书库所有书籍制作电子书目,可自定义书目包含的元数据字段信息,支持生成 CSV、EPUB、XML 等多种格式,生成后在书库内直接浏览。建议导出 CSV 格式,方便处理导入其他软件中使用。

Calibre 推荐配置与插件-雅余

 

11. 书库备份

硬盘富余的话,可以考虑定期对整个书库进行备份。我暂时仍保留着原始文件夹整理和 Calibre 整理两种方式。

Calibre 推荐配置与插件-雅余

 

推荐插件

1. Highlights to Obsidian

实现一键同步 Calibre 的批注内容到 Obsidian 中,同步内容包括高亮位置的链接、高亮内容、自己对高亮内容的批注等。通过插件可以自定义同步到 Obsidian 中的模板格式,灵活度很高。该插件可以通过 Calibre 进行搜索安装,也可以通过 Github 下载安装包。

2. Ebook Translator

Ebook Translator 是书伴开发的一款 Calibre 插件,可以将不同格式不同语言的电子书翻译成指定语言(原文译文对照)指定格式的电子书,支持 Google、ChatGPT 和 DeepL 翻译引擎。该插件可以通过 Calibre 进行搜索安装。

3. New Douban Books

实现通过豆瓣获取书籍元数据和封面图片。建议通过 Github 中获取一个较新的安装包,并通过“从文件加载插件”的方式进行安装。

安装插件还是坚持一个原则,能免则免,能精则精,不要贪多。

-

参考资料:
- 阅读批注样样通,我的 Calibre 书籍库管理方法论
- Ebook Translator:用 Calibre 翻译多格式双语对照电子书
- 用Calibre管理图书,还能一键同步批注到Obsidian
- Calibre 不要翻译我的文件路径

2024年的已读书单+观影记录

2024年依然是忙忙碌碌的一年,但再忙碌,阅读、观影和听歌都不可或缺。今年完成阅读量较去年提高,一共读了88本,读完60本(专业书籍除外),可能和读了几本小书凑数有关系。今年坚持348天有拿起书本(更多是电子书),其中微信阅读699小时,保持平均每天阅读2小时。书籍偏重文学类,主要作者有鲁迅、村上春树、戴维·梭罗、三毛、黑塞、莫泊桑、陀思妥耶夫斯基等。继续把梭罗和黑塞的其他著作读完,重读三毛、鲁迅和村上春树。今年还读了李娟和庆山(安妮宝贝)的新书,让人汗颜,可能是我欣赏不来。

纸质书的购买逐年下降,电子书比重已经达到70%以上。水墨屏阅读体验已经很好,多设备之间阅读进度实时同步,笔记整理效率更高,便携性更高。硬件我用过 KPW1 和 KPW3,更换到小米多看 Pro II 主要为了安装微信读书。而纸质书除了特别经典的,都是逐年在清理。(书架强烈建议买有柜门的,不然没几年书就发黄严重,灰尘还不好清理。)

阅读计划上,明年可能会进行佛学、园林和极简方面的专题阅读,已经好长一段时间没有关注极简方面的新书。但读书还是比较随性,遇上什么读什么。

建议不要带着功利心去阅读,不要追求读完有多少收获,会让你浮躁,不能享受阅读的乐趣。

除了阅读,今年观影68部,较去年下降很多。主要感受是现在的电影真的不咋样,看电影只是为了打发一些苦闷的时间,而不是像以前去寻找去欣赏一部好电影。消费,而不是欣赏。听歌方面全年应该在600小时以内,偏少。基本听老歌,大爱老歌,下半年古典音乐偏多。原计划今年入手一款木质书架音箱,最终担心吃灰搁置。

目前使用纯文本的方式记录已读+观影+听歌的内容,已经脱离某瓣运行3年,情况良好。那些“傲气”又不思进取的应用和网站都等待消亡吧。离开某瓣之前,我用“豆坟”全量导出了所有数据,至2021年共使用某瓣13年,书籍标记已读589本,想读53本,电影已看2425部,想看5部,音乐已听1251专辑,想听6专辑。

2024 已读书单

1. 《野果》[美]亨利·戴维·梭罗 / 梁枫 / 文化发展出版社 / 2018-08 / ★★★★
2. 《缅因森林》[美]亨利·戴维·梭罗 / 路嵩 / 北方文艺出版社 / 2019-04 / ★★★★
3. 《四季之歌》[美]亨利·戴维·梭罗 / 仲泽 / 译林出版社 / 2020-10 / ★★★★
4. 《一个人的远行》 [美]亨利·戴维·梭罗 / 任小红 / 哈尔滨出版社 / 2018-01 / ★★★★
5. 《信念:十年徒步中国》 雷殿生 / 中译出版社 / 2017-05 / ★★★★★
6. 《额尔古纳河右岸》 迟子建 / 北京十月文艺出版社 / 2005-12 / ★★★★
7. 《我用古典的方式爱过你》 [美]艾米莉·狄金森 / 董恒秀、赖杰威 / 长江文艺出版社 / 2019-07 / ★★★
8. 《北方的空地》 杨柳松 / 重庆出版社 / 2011-04 / ★★★★
9. 《地下室手记》 [俄罗斯] 陀思妥耶夫斯基 / 曾思艺 / 浙江文艺出版社 / 2020-05 / ★★★★
10. 《地下室手记》 [俄罗斯] 陀思妥耶夫斯基 / 臧仲伦 / 漓江出版社 / 2019-10 / ★★★☆
11. 《形影不离》 [法]西蒙娜·德·波伏瓦 / 曹冬雪 / 浙江教育出版社 / 2022-02 / ★★★
12. 《布鲁克林有棵树》 [美]贝蒂·史密斯 / 梅静 / 光明日报出版社 / 2023-03 / ★★★★
13. 《读梦:村上春树长篇小说指南》 译文讲书工作室 / 上海译文出版社 / 2024-01 / ★★★
14. 《挪威的森林》 [日] 村上春树 / 林少华 / 上海译文出版社 / 2001-02 / ★★★★
15. 《且听风铃》 [日] 村上春树 / 林少华 / 上海译文出版社 / 2018-07 / ★★★
16. 《遇到百分之百的女孩》 [日] 村上春树 / 林少华 / 上海译文出版社 / 2021-11 / ★★★
17. 《1973年的弹子球》 [日] 村上春树 / 林少华 / 上海译文出版社 / 2008-08 / ★★★★
18. 《寻羊冒险记》 [日] 村上春树 / 林少华 / 上海译文出版社 / 2023-06 / ★★★★
19. 《国境以南 太阳以西》 [日] 村上春树 / 林少华 / 上海译文出版社 / 2007-07 / ★★★★
20. 《世界尽头与冷酷仙境》 [日] 村上春树 / 林少华 / 上海译文出版社 / 2023-06 / ★★★★
21. 《舞!舞!舞!》 [日] 村上春树 / 林少华 / 上海译文出版社 / 2018-07 / ★★★☆
22. 《奇鸟行状录》 [日] 村上春树 / 林少华 / 上海译文出版社 / 2009-08 / ★★★☆
23. 《斯普特尼克恋人》 [日] 村上春树 / 林少华 / 上海译文出版社 / 2018-06 / ★★★★
24. 《海边的卡夫卡》 [日] 村上春树 / 林少华 / 上海译文出版社 / 2018-08 / ★★★★
25. 《四季之歌》 [美]亨利·戴维·梭罗 / 仲泽 / 译林出版社 / 2020-10 / ★★★★
26. 《撒哈拉的故事》 三毛 / 南海出版公司 / 2022-04 / ★★★★
27. 《我的灵魂骑在纸背上》 三毛 / 南海出版公司 / 2021-10 / ★★★★
28. 《两地书》 鲁迅 许广平 / 古吴轩出版社 / 2020-10 / ★★★★
29. 《天工开物》 [明] 宋应星 / 李经邦 / 北方文艺出版社 / 2023-03 / ★★★
30. 《鼠疫》 [法] 阿尔贝·加缪 / 李玉民 / 湖南文艺出版社 / 2018-03 / ★★★★
31. 《命运》 蔡崇达 / 浙江文艺出版社 / 2022-09 / ★★★
32. 《梦里花落知多少》 三毛 / 南海出版公司 / 2023-01 / ★★★★
33. 《觉醒时刻》 鲁迅等 / 中国画报出版社 / 2023-04 / ★★★★
34. 《一生》 [法] 莫泊桑 / 盛澄华 / 人民文学出版社 / 2021-01 / ★★★★
35. 《龙与地下铁》 马伯庸 / 湖南文艺出版社 / 2024-01 / ★★★
36. 《极简父母法则》 [英]理查德·泰普勒 / 侯文帅 / 钮跃增 / 人民邮电出版社 / 2017-09 / ★★★
37. 《羊脂球》 [法] 莫泊桑 / 郝运 / 王振孙 / 上海译文出版社 / 2011-01 / ★★★★
38. 《笔记的方法》 刘少楠 / 刘白光 / 新星出版社 / 2023-10 / ★★★★
39. 《漂亮朋友》 [法] 莫泊桑 / 王振孙 / 上海译文出版社 / 2010-08 / ★★★★
40. 《清冽的内在》 庆山 / 人民文学出版社 / 2023-12 / ★★
41. 《火车快开》 李娟 / 花城出版社 / 2022-09 / ★★
42. 《彼得·卡门青德》 [德] 赫尔曼·黑塞 / 胡其鼎 / 人民文学出版社 / 2023-01 / ★★★★
43. 《克林索尔的最后夏天》 [德] 赫尔曼·黑塞 / 易海舟 / 天津人民出版社 / 2019-01 / ★★★★
44. 《伤逝》 鲁迅 / 赤尔斯 / 2023-10 / ★★★★
45. 《项链》 [法] 莫泊桑 / 周丽霞 / 汕头大学出版社 / 2018-03 / ★★★★
46. 《鸳鸯六七四》 马家辉 / 花城出版社 / 2020-09 / ★★★★
47. 《独处的人:蒙田随笔》 [法]米歇尔·德·蒙田 / 全志钢 / 微信读书 / 2024-07 / ★★★
48. 《我在岛屿读书》 余华 / 苏童 / 西川 / 房琪 / 江苏凤凰文艺出版社 / 2024-04 / ★★★
49. 《夜色和月光》 [美]亨利·戴维·梭罗 / 仲泽 / 译林出版社 / 2020-10 / ★★★★
50. 《这才是我说的》 鲁迅 / 花城出版社 / 2022-04 / ★★★★
51. 《野草》 鲁迅 / 北京联合出版公司 / 2014-11 / ★★★★
52. 《彷徨》 鲁迅 / 人民文学出版社 / 2021-08 / ★★★★★
53. 《至少还有文学》 宗城 / 北京十月文艺出版社 / 2022-03 / ★★★★
54. 《萨达那》 [印度]罗宾德拉纳特·泰戈尔 / 钟书峰 / 光明日报出版社 / 2012-11 / ★★★
55. 《杨绛散文》 杨绛 / 人民文学出版社 / 2023-12 / ★★★★
56. 《故事新编》 鲁迅 / 人民文学出版社 / 2021-07 / ★★★★★
57. 《呐喊》 鲁迅 / 人民文学出版社 / 2021-08 / ★★★★★
58. 《滕王阁序》 王勃 / 北京汇聚文源 / 2024-09 / ★★★★
59. 《岳阳楼记》 范仲淹 / 简读互动 / 2024-08 / ★★★★
60. 《旋转木马》 毛姆 / 刘钟梁 / 东西时代 / 2023-04 / ★★★★

2024 观影清单

1. 《花月杀手》 2023 / 美国 / 2024-01-06 / ★★★
2. 《不虚此行》 2023 / 中国大陆 / 2024-01-06 / ★★★
3. 《二手杰作》 2023 / 中国大陆 / 2024-01-07 / ★★★
4. 《热搜》 2023 / 中国大陆 / 2024-01-09 / ★★★
5. 《走到尽头》 2023 / 日本 / 2024-01-10 / ★★
6. 《河边的错误》 2023 / 中国大陆 / 2024-01-10 / ★★★
7. 《画江湖之天罡》 2023 / 中国大陆 / 2024-01-11 / ★★★
8. 《白塔之光》 2023 / 中国大陆 / 2024-01-16 / ★★★
9. 《红色天空》 2023 / 德国 / 2024-01-19 / ★★★
10. 《绝境盟约》 2023 / 西班牙 / 2024-01-19 / ★★★
11. 《涉过愤怒的海》 2023 / 中国大陆 / 2024-01-23 / ★★★
12. 《洋子的困惑》 2023 / 中国大陆 / 2024-01-25 / ★★★
13. 《别流淌呀,河水》 2023 / 日本 / 2024-01-25 / ★★★
14. 《茶啊二中》 2023 / 中国大陆 / 2024-01-27 / ★
15. 《杂种》 2023 / 丹麦 / 2024-01-27 / ★★★★
16. 《农民》 2023 / 波兰 / 2024-01-28 / ★★★
17. 《坠落》 2022 / 英国 / 2024-01-29 / ★★★
18. 《养蜂人》 2024 / 美国 / 2024-02-17 / ★★
19. 《瞒天过海》 2023 / 中国大陆 / 2024-02-20 / ★★★
20. 《首尔之春》 2023 / 韩国 / 2024-02-24 / ★★★★
21. .《年会不能停》 2023 / 中国大陆 / 2024-03-02 / ★★★
22. 《动物王国》 2023 / 法国 / 2024-03-03 / ★★★
23. 《周处除三害》 2023 / 中国台湾 / 2024-03-09 / ★★★★
24. 《美国小说》 2023 / 美国 / 2024-03-11 / ★★★★
25. 《那些野兽》 2022 / 西班牙 / 2024-03-12 / ★★★
26. 《完美的日子》 2023 / 日本 / 2024-03-14 / ★★★★
27. 《黑暗物质 第三季》 2022 / 美国 / 2024-03-27 / ★★★
28. 《一生》 2023 / 英国 / 2024-04-01 / ★★★
29. 《机器人之梦》 2023 / 西班牙 / 2024-04-01 / ★★★
30. 《老狐狸》 2023 / 中国台湾 / 2024-04-05 / ★★★★
31. 《沙丘2》 2024 / 美国 / 2024-04-11 / ★★★★
32. 《繁花》 2023 / 中国大陆 / 2024-04-20 / ★★★★
33. 《僧侣和枪》 2023 / 不丹 / 2024-04-20 / ★★★★
34. 《第二十条》 2024 / 中国大陆 / 2024-04-27 / ★★★
35. 《飞驰人生2》 2024 / 中国大陆 / 2024-05-01 / ★★★
36. 《城中之城》 2024 / 中国大陆 / 2024-05-12 / ★★★
37. 《盟军敢死队》 2024 / 美国 / 2024-05-13 / ★★★
38. 《火星特快》 2023 / 法国 / 2024-05-15 / ★★★
39. 《银河写手》 2023 / 中国大陆 / 2024-06-04 / ★★★
40. 《鲭鱼罐头》 2022 / 日本 / 2024-06-12 / ★★★★
41. 《职业杀手》 2023 / 美国 / 2024-06-20 / ★★★
42. 《庆余年2》 2024 / 中国大陆 / 2024-06-24 / ★★★
43. 《九龙城寨之围城》 2024 / 中国大陆 / 2024-06-25 / ★★★
44. 《疯狂的麦克斯:狂暴女神》 2024 / 美国 / 2024-06-28 / ★★★
45. 《走走停停》 2024 / 中国大陆 / 2024-07-16 / ★★★★
46. 《犯罪都市4》 2024 / 韩国 / 2024-07-20 / ★★★
47. 《谈判专家2》 2024 / 中国大陆 / 2024-07-21 / ★★★★
48. 《猩球崛起:新世界》 2024 / 美国 / 2024-07-23 / ★★★
49. 《填词L》 2024 / 中国大陆 / 2024-07-27 / ★★★★
50. 《绝地战警:生死与共》 2024 / 美国 / 2024-07-30 / ★★★
51. 《逆行人生》 2024 / 中国大陆 / 2024-08-10 / ★★★
52. 《年少日记》 2023 / 中国香港 / 2024-08-12 / ★★★
53. 《泳者之心》 2024 / 美国 / 2024-08-13 / ★★★★
54. 《龙卷风2》 2024 / 美国 / 2024-08-14 / ★★★
55. 《朱同在三年级丢失了超能力》 2023 / 中国大陆 / 2024-08-15 / ★★★
56. 《狗阵》 2024 / 中国大陆 / 2024-08-23 / ★★★
57. 《我的阿勒泰》 2024 / 中国大陆 / 2024-08-24 / ★★★
58. 《富都青年》 2023 / 马来西亚 / 2024-09-12 / ★★★
59. 《逆岭》 2024 / 美国 / 2024-09-13 / ★★★
60. 《孤注一掷》 2023 / 中国大陆 / 2024-09-17 / ★★★
61. 《姥姥的外孙》 2024 / 泰国 / 2024-09-19 / ★★★★★
62. 《劫机》 2024 / 韩国 / 2024-09-27 / ★★★
63. 《双狼》2024 / 英语 / 2024-10-05 / ★★★
64. 《乘船而去》 2023 / 中国大陆 / 2024-10-06 / ★★★
65. 《荒野机器人》 2024 / 美国 / 2024-10-26 / ★★★★
66. 《变形金刚:起源》 2024 / 美国 / 2024-12-10 / ★★★★
67. 《剑来》 2024 / 中国 / 2024-12-17 / ★★★
68. 《因果报应》 2024 / 印度 / 2024-12-20 / ★★★★

附:2023年的已读书单2022年的已读书单2021年的已读书单

❌