MoreRSS

site iconXieisabug修改

全栈工程师。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Xieisabug的 RSS 预览

用js写卡牌游戏(十)

2026-01-06 17:06:55

没想到,上一篇这个系列的文章 居然是 2023年6月,现在2026年1月了,又一次破了我鸽的记录!
这次想起来更新这个卡牌游戏是因为我最近刷POE2非常上头,无数的天赋树和装备的组合,给这个游戏带来了无限的灵活性。如果有了解过游戏开发,一定知道虚幻引擎,它有一套 GAS 系统,通过这套系统能够让游戏的技能系统做起来又快又灵活,所以我想把这套系统的部分设计引入到我的卡牌游戏里来。

关于GAS

GAS就是Gameplay Ability System,游戏玩法技能系统,但我不介绍这个完整的系统,我只介绍这个系统中的一部分,其实拆开来看,就是几个经典的设计模式组合在一起的产物。
这个GAS里有两个很重要的概念,Tag和Effect,Tag顾名思义就是标签,描述一件衣服,就可以用各种Tag来表示,比如红色、格子、长袖、尼龙等。

如果有这套Tag系统,那么我只要多实现一些Tag然后进行排列组合,就能得到全新的一件物品,比如我把红色改为黑色,那么我将得到黑色的衬衫:

然后就是Effect效果,从Tag可以推理出来,如果Tag对应的是状态,那么Effect对应的是改变状态的方法,如果我开发了一个“折叠效果”,对应的方法的内容是:

  1. 添加 “不可折叠” Tag、添加 “可展开” Tag
  2. 改变面积为原来的四分之一

我就可以将这个Effect应用到衣服上,这时衣服的属性就会自动发生改变,并且神奇的是,这个Effect也可以应用到一切有“服装”Tag的物品上,我以后开发了“裤子”、“裙子”、“毛衣”全部都可以直接用。

在这些内容里,你可以看到命令模式、策略模式、状态模式等等经典设计模式的影子,他将开发的“复利”效应做到了极致,POE就是这样设计的,同样你可以在Dota2里也看到这样的设计

// 这是一个非常简单的技能,他是一个被动技能,给单位添加了一个粒子特效。
"fx_test_ability"
{
    // General
    //---------------------------------------------------------
    "BaseClass"             "ability_datadriven"
    "AbilityBehavior"       "DOTA_ABILITY_BEHAVIOR_PASSIVE"
    "AbilityTextureName"    "axe_battle_hunger"

    // Modifiers
    //---------------------------------------------------------
    "Modifiers"
    {
        "fx_test_modifier"
        {
            "Passive" "1"
            "OnCreated"
            {
                "AttachEffect"
                {
                    "Target" "CASTER"
                    "EffectName" "particles/econ/generic/generic_buff_1/generic_buff_1.vpcf"
                    "EffectAttachType" "follow_overhead"
                    "EffectLifeDurationScale" "1"
                    "EffectColorA" "255 255 0"
                }
            }
        }
    }
}

通过定义 bahavior、modifier、”OnCreated”这样的钩子、”AttachEffect” 这样的Effect,就能配置出一个技能,无需编写代码,并且添加或者修改、组合部分属性,就是一个全新的技能。

我的设计

我的卡牌过去是这样实现的:

{
    id: 13,
    name: "没毕业的天才程序员",
    cardType: CardType.CHARACTER,
    cost: 3,
    content: `每回合结束时,获得+1/+1`,
    attack: 1,
    life: 1,
    attackBase: 1,
    lifeBase: 1,
    type: [""],
    onMyTurnEnd: function ({thisCard, specialMethod, position}) {
        if (position === CardPosition.TABLE) {
            thisCard.attack += 1;
            thisCard.life += 1;
            specialMethod.buffCardAnimation(true, -1, -1, thisCard, thisCard)
        }
    }
}

并且有效果的牌都是这么实现的,可以看到每个效果都需要我单独的去写函数,或者是在对象上添加属性比如 isStrong 这种,对于代码实现不方便不优雅(还好用的是javascript),而且无法很好的保存到数据库里,只能存在代码文件中来硬编码定义。

所以想要做到像POE、Dota2那样进行配置,理想的情况下卡牌应该是这样的:

{
    "id": 13,
    "name": "没毕业的天才程序员",
    "cost": 3,
    "content": "每回合结束时,获得+1/+1",
    "attack": 1,
    "life": 1,
    "attackBase": 1,
    "lifeBase": 1,
    "types": [],
    "tags": ["Tags.Character"],
    "effects": {
        "onMyTurnEnd": [
            {
                "type": "ModifyAttribute",
                "target": {
                    "type": "self"
                },
                "params": {
                    "attribute": "attack",
                    "value": 1,
                    "operation": "add"
                },
                "conditions": [
                    {
                        "type": "cardPosition",
                        "params": {
                            "position": "TABLE"
                        }
                    }
                ]
            },
            {
                "type": "ModifyAttribute",
                "target": {
                    "type": "self"
                },
                "params": {
                    "attribute": "life",
                    "value": 1,
                    "operation": "add"
                },
                "conditions": [
                    {
                        "type": "cardPosition",
                        "params": {
                            "position": "TABLE"
                        }
                    }
                ]
            }
        ]
    }
}

虽然说看起来内容复杂了,没有代码那么一眼看上去逻辑清晰,可是这些配置是完全能够可视化的,也就是说以后可以通过卡牌编辑器来可视化的进行卡牌配置,但是代码就无法有效的进行可视化了(对于不懂编码的人来说,比如说策划或者爱好者社区这很重要)。

所以设计任务: 1. 原来的各种属性抽象为Tag。 2. 原来的各种钩子的效果整理好,抽象为各种 Effect。

{
    id: 13,
    name: "卡牌名称",
    cardType: CardType.CHARACTER, // tag
    cost: 3,
    content: `卡牌内容描述`,
    attack: 1,
    life: 1,
    attackBase: 1,
    lifeBase: 1,
    isStrong: true, // tag
    isFullOfEnergy: true, // tag
    type: ["类型"],
    onMyTurnEnd: function ({thisCard, specialMethod, position}) { // effect
        if (position === CardPosition.TABLE) {
            thisCard.attack += 1;
            thisCard.life += 1;
            specialMethod.buffCardAnimation(true, -1, -1, thisCard, thisCard)
        }
    }
}

按照 Effect 的设想:

{
    "onMyTurnEnd": [
        {
            "type": "ModifyAttribute",
            "target": {
                "type": "self"
            },
            "params": {
                "attribute": "attack",
                "value": 1,
                "operation": "add"
            },
            "conditions": [
                {
                    "type": "cardPosition",
                    "params": {
                        "position": "TABLE"
                    }
                }
            ]
        },
        {
            "type": "ModifyAttribute",
            "target": {
                "type": "self"
            },
            "params": {
                "attribute": "life",
                "value": 1,
                "operation": "add"
            },
            "conditions": [
                {
                    "type": "cardPosition",
                    "params": {
                        "position": "TABLE"
                    }
                }
            ]
        }
    ]
}

还是需要有过去的钩子定义,比如 onMyTurnEnd、onStart,方便我们在每个流程阶段检查卡牌是否配置了对应阶段执行的 Effect,接收 Effect 数组,方便我们进行 Effect 组合来实现非常灵活的效果。

需要把所有的函数过一遍,总结一下所有的函数实现的效果,然后抽象成简单的 Effect 和对应的参数。

这样在各种流程里,比如出牌阶段,刚打出的时候可以触发卡牌的 onStart 效果

// 示意伪代码
function outCard() {
    // ...
    triggerCardEffect(card, 'onStart', baseContext);
    // ...
}

triggerCardEffect 内部简单来看可以这样设计:

// 示意伪代码
function triggerCardEffect(card, trigger, baseContext) {
    // 获取卡牌的效果配置
    const effectConfigs = card.effects?.[trigger] || [];

    // 构建完整上下文
    const context = {
        ...baseContext,
        thisCard: card,
        // 还需要加各种 effect 和 tag 相关的系统环境数据
        // 比如 effectRegistry、tagRegistry 用于快速获取注册了的 effect和tag,比如 effectEngine 用于执行 effect
    };

    // 依次执行效果
    effectConfigs.forEach(config => {
        this.executeEffect(config, context);
    });
}

executeEffect 内部主要就是利用命令模式、策略模式来把我们实现的各种 Effect 执行逻辑应用在卡牌上:

// 示意伪代码
function executeEffect(config, context) {
    // 获取实现的 effect 函数
    const EffectClass = this.effectTypes.get(config.type);
    if (!EffectClass) {
        console.warn(`Unknown effect type: ${config.type}`);
        return null;
    }

    const effect = new EffectClass(config);

    // 检查条件
    if (!effect.canExecute(context)) {
        return;
    }

    effect.execute(context, targets);

    // 处理后续效果链
    if (effectConfig.then) {
        effectConfig.then.forEach(nextConfig => {
            this.executeEffect(nextConfig, context);
        });
    }
}

所有的 Effect 就只需要实现预定义的各种函数,然后注册起来:

/**
 * BaseEffect - 效果基类
 * 所有具体效果类型都继承自此类
 */
class BaseEffect {
    constructor(config) {
    }

    /**
     * 检查效果是否可以执行
     * @param {object} context - 执行上下文
     * @returns {boolean}
     */
    canExecute(context) {
        return false;
    }

    /**
     * 执行效果
     * @param {object} context - 执行上下文
     * @param {Array} targets - 目标列表
     */
    execute(context, targets) {
        throw new Error("Must be implemented by subclass");
    }

    /**
     * 获取效果描述文本
     * @returns {string}
     */
    getDescription() {
        return this.type;
    }
}

module.exports = BaseEffect;

不过,都到了2026年了,我就不分享具体的代码实现过程了,我分享点别的。

实现过程

既然现在都用AI来辅助开发了,那么实际上我可以直接分享我的Prompt,这样就能够一步步的让AI在你的环境也实现相同的功能而不用是完全相同的代码。(如果AI给力的话)
我的代码编写基本是用 Claude Code(Opus4.5)和 Copilot(Opus4.5),查询资料用 Kagi Assistant 的 Research。

第一条prompt,先用Plan模式让AI做好计划:

当前的卡牌存储在 @card-game-server/cards.js 里面,可以看到卡牌的效果基本上都是用代码写的,比如 onStart onMyTurnStart 等等,这些都是钩子函数。
这种设计有一个问题,要添加卡牌,必须要能够写代码,现在需要将这个设计修改,请使用UE5的GAS的 effect tag 设计,把各种基础效果改为已经实现好的 effect 和 tag,之后的卡牌只要配置对应的 effect tag就能够实现各种能力,而新增effect tag能够让每个tag添加进来和别的tag组合成为成倍数增加的新能力。
设计需要尽可能的全面和优雅。

如果你的AI不知道UE5 GAS,那么你可能需要查询一些UE5 GAS相关的设计内容总结给AI。

AI的计划大概如下(肯定都会有不同,但没事):

可以看到 Tag 带有层级,这是 组合模式 的应用,能够让我们方便的多层级进行判断,比如我们有时候只想要知道 State.Stunned 眩晕状态来让卡牌无法行动,至于是 State.Stunned.Light 还是 State.Stunned.Heavy 不重要。 Effect 也支持链式效果,能够让我们多 Effect 组合的同时,也能够链式的递归进行 Effect 调用。

这个Plan阶段可以和AI多轮讨论达到自己想要的最好情况,后续其实也能修改,但这个阶段讨论清楚是最好的。

后续就直接可以让AI开始修改,如果你是使用的Opus4.5,那么大概率这一把就能够改出八成,有可能出现下面的几种问题,这也是我碰到的。

卡牌数据没有完全迁移到新结构

有可能会出现 cards.js 还是有很多遗留数据,或者甚至就完全没有迁移,所以:

现在的卡牌已经转为使用 effect tag 来进行配置了,但是卡牌的数据 cards.js 还是使用的函数。 请把这些卡牌数据转为使用effect和tag,因为这里的数据过大,请分批次转为多个json(按照卡牌职业分类)。

AI将会分批次把 cards.js 里面的硬编码的卡牌转为新的配置的json文件:

文件命名不规范

有的 Effect 会单独有一个文件夹,或者相类似能力的 Effect 会放到一起,但有的不相干的 Effect 又全部集合到了一个文件里,所以:

新增了 UtilityEffect.js 但别的effect都是单独的文件,最好这几个也用对应的文件放在一起,以后能够通过文件名来寻找对应的effect。并且请把level中的卡牌也改为effect配置的。

AI有时候因为上下文过长的问题,会出现部分代码不遵守规范的情况,这个情况也可以让AI做一份 CLAUDE.md / AGENTS.md,然后在文件里加上这个规范。

偷懒不实现缺失的Effect

AI为了偷懒,创建了一个 LegacyHookEffect ,来支持用过去那种老的用函数定义效果的方法。

/**
 * LegacyHookEffect
 * 允许在 Effect 配置中调用旧版函数式卡牌效果
 */
class LegacyHookEffect extends BaseEffect {
    constructor(config) {
        super(config);
        this.type = "LegacyHook";
    }

    execute(context) {
        const { handler } = this.params;
        const fn = legacyCardEffects[handler];

        if (typeof fn === 'function') {
            fn(context);
        } else {
            console.warn(`[LegacyHookEffect] handler not found: ${handler}`);
        }
    }
}
LegacyHookEffect 是偷懒的行为,把这些函数定义改为effect配置就是不希望再有使用函数配置的卡牌了。  
如果当前的effect设计不足以做到把所有卡牌都改为可配置effect的json表达,请告诉我哪里需要修改。如果当前的effect不足,需要新增,请告诉我要增加哪些。

并且,AI如果不够聪明,可能会出现胡乱设计的情况,所以在用AI修改的时候一定要审阅他的输出计划,并且自己对代码一定要有理解,然后及时纠正AI的错误:

isStrong 等属性没有真正的用 Tag 来重构

Review代码的时候发现,AI并没有理解 Tag 这种设计(不过我们也没说),改的不够彻底,所以:

现在代码里的模仿GAS的tag系统似乎没有真正用起来,现在的tag是用ApplyTag这个Effect来模拟之前的属性做法,给卡牌加上 isStrong 这样的属性,我希望你能彻底修改为tag的做法,只需要判断卡牌的tag是否有 Status.Buff.Strong,而不需要再转回原来的属性模式,所以原来的属性模式相关的逻辑也要改为GAS的tag模式。
那这样的话,是否 cardType 其实也不需要了,直接用 tag 来实现就可以了。

这样AI会把过去的属性移除,然后用新的Tag来重构,过去判断 isStrong 的地方,都变成 hasTag(card, “Status.Buff.Strong”)。

其他内容

本次还修复了一些bug,然后还增加了SQLite模式,这样大家在跑这个项目的时候就不需要再启动一个MongoDB了。

AI

实现这个 Effect 和 Tag,增加SQLite支持,外加测试和一些其他的修复bug,总共耗时1天。本来半天其实就差不多了,但是AI写的代码总是有一些不优雅,所以需要自己去微调,可以手动,也可以让AI继续改。比如 Status.Buff.Strong 这类的字符串,可以抽取常量,比如 hasTag 这类的工具方法,还有一些过去我实现过可以直接用的系统。
不过如果没有AI,这个修改起码要花好几天,大部分的代码质量也不一定有AI做的好。

下一步

翻了翻上次发的文章,说是要把AI加进去,确实可以,而且应该很简单,然后还可以做一个卡牌编辑器。
就是不知道这次要鸽多久,视频稍晚一点做完也会发。

2025.12 AI使用有感

2025-12-21 14:29:40

不知道是不是因为AI真的陷入了瓶颈,现在的模型很少出现完美的,每个模型总是有一定的缺陷,这也就导致了用AI需要一些技巧,这些技巧让AI带来的智力平权又拉开了一点距离。

我有这个想法,最初是源于教老婆用AI,她之前用AI用的不多,在给她使用了Claude Code、Codex、Gemini CLI、Copilot等产品之后,她惊呼好用,但我观察她的使用总是觉得笨笨的,仔细总结之后发现用AI确实是需要一些技术和一些经验。

例如她喜欢非常简单的描述问题然后与AI进行非常多轮的讨论来明确整个需求,这导致AI的无用上下文大幅提高,AI在处理长上下文的情况下性能又会下降很多。

又比如她不喜欢切换模型,当我告诉她Opus4.5好用之后,所有的情况都是用Opus4.5来完成,导致一些比较复杂的逻辑性需求或者Bug改来改去无法完成,这种情况下使用Codex的ExHigh或者High才是最佳选择,虽然说可能思考十几分钟,但是问题基本上是能够修复的。

还比如最好别给AI用“你是XXX”,别跟AI提“我”,写界面最好提供设计截图,用Simple Browser来给AI提供DOM上下文等等等等。

这里面门道其实挺多,造成用AI的能力也有1-10倍的差距。

How I AI Coding 2025.10

2025-10-26 16:23:26

因为看到了Xuanwo大佬文章,所以我也来写一下我是怎么用AI来开发的。

工具

VSCode Insider with Copilot,Codex,Claude Code,Kagi,AIPP
现在日常用的就是VSCode Insider,因为Copilot的新特性基本都是先在Insider版本试用的,所以如果要用到最好的Copilot就只能牺牲一点稳定性来使用Insider版本了。目前我的工作里有很大的部分是无法通过Vibe Coding来完成的,所以还是比较依赖编辑器里的代码补全,Copilot现在是用的GPT 4.1 来做的代码补全模型,智力和速度都是在线的,也有Next Edit Suggestions,很符合我的需求。Copilot Agent现在比过去的可用性提高了很多,比以前更能利用模型的能力,也加入了Todo、PlanMode 之类的辅助能力,也很少出现只读文件一部分的情况了,Apply的速度虽然说比不上Cursor,不过也算快了。而且Copilot订阅有网页端,在 https://github.com/copilot ,日常有一些问题可以在网页端直接询问,不需要别扭的在 VSCode 里来询问和代码不相干的事情了,我通常有一些常识性的问题或者对Github上仓库的问题都会在网页端用Gemini 2.5 Pro来进行问答。Copilot网页端的Agents在我的测试看来基本不可用,模型不明但是估计是Sonnet 4.5之类的,效果奇差,不过会启动环境做验证,期待未来吧。(还有个很重要的点就是我穷,Cursor现在的计费我要是放开了用会破产,Copilot高级请求用完之后,还有无限的4o和4.1使用,这点让我很安心)

然后就是Codex和Claude Code了,在Vibe一些小项目时Claude Code挺好,但是当项目大到一定程度或者问题达到一定难度后,Claude Code基本上就是帮倒忙了,这个时候GPT 5 High的智商优势就体现出来了,虽然一个任务大概率要跑十几到几十分钟,但是实实在在的能优雅的完成任务,所以我已经很少使用Claude Code了,基本换到了Codex。(我没有订阅这俩的官方服务,我是通过中转站来使用的,所以我只在需要使用的时候才会对这俩工具付费,这能节约很多钱。)

还有一个我觉得非常好用的工具就是Kagi(AI能力是需要付费的),搜索的非常精准,基本第一页就会出现我要的结果(保守的说法,其实一般第一条就是),在搜索引擎的能力加持下,它家的搜索Agent Ki就更强了,目前看只有GPT5 Pro的Search能够打打。Kagi在我做预研或者查解决方案的过程里,给了非常多的帮助。

AIPP是我自己写的开源ChatApp,别的ChatApp各有优势但也总有槽点,所以做一个符合自己使用习惯的,我在中转和国内的厂商(Kimi、GLM、Deepseek)都有充值,用API来对一些问题提问。现在也只是刚把功能做好,还有很多Bug,不过我自己足够熟悉更新迭代的也会很快。

MCP

Context7,Jina,自己写的禅道、数据库MCP
Xuanwo大佬在9月的版本中认为“MCP is still a lie”,我倒是有不同的意见,MCP在简化上下文资源中有命令行和基础工具不可替代的作用。
Context7是一个用于查询文档的MCP,是我必备的MCP,大模型的知识跟不上导致API经常使用过时,甚至新的库直接就不知道怎么用,不使用Context7就得自己去查了然后告诉大模型或者模型就开始胡说。
Jina是我用来让AIPP搜索和联网的MCP,也有很多别的替代不过我最偏爱Jina这家公司。
禅道MCP是我在AIPP或者Copilot中能够快速的查看Bug情况,数据库MCP则是连接公司内部的开发库来快速的开发和改Bug,如果不使用MCP很难有更高效的方式让我放心的对禅道和数据库进行授权让AI调用。

感想

Xuanwo大佬提的感想大部分我都赞同,我说一些我自己的别的感想:

多模态能力其实对写代码挺重要的,尤其是前端,目前感觉最好的是Gemini 2.5 Pro,因为可以接受视频的输入,连动效都可以实现。

绝大部分人的绝大部分编程任务其实不需要模型多高的智力,只要能够描述清楚,Sonnet 4.5或者GPT5 Medium就足够了,国内的GML4.6、Kimi K2也是足够的。

模型的智力比承载工具重要多了,参考Cursor和Codex就明白,Sonnet的进步让Cursor异军突起,GPT5 High的能力让难用的Codex也能忍着用下去。

    AI产品需要反复的去用,很多东西刚开始用的时候非常垃圾,但是随着产品和模型的演进,突然就非常好用了。

    所有的产品都可以用AI来重写一次,但是产品里的基础能力还是不可或缺的,AI也只能是替代原有产品的使用者来自动调用这些基础能力,没法替代产品本身。

    2025阅读清单

    2025-08-24 21:20:03

    上次1月中旬写,这次直接8月写,服了我自己了。

    1.地煞七十二变图鉴

    ★★★★★

    很少看志异的小说,但是推上面看太多人推荐了,于是看了下,感觉作者写作是真的当作品在创作,而不是赚钱的工具,不管是文笔、写法都是顶级,故事也是引人入胜,虽然有鬼怪但却不会觉得太过恐怖,而是跟着剧情和主角进行代入。

    2.我看见的世界:李飞飞自传

    ★★★★

    作为一本传记很不错了,以当事人的视角亲身经历了各种领域内口口相传的故事,很有意思。同时感叹家庭的力量和朋友的力量是多么强大。

    3.第七重解答

    ★★★★

    反转挺多的,有时候觉得这什么啊?我一下就猜出来了;有时候觉得卧槽?还能这么反转?

    4.小说写作:叙事技巧指南(第十版)

    ★★★

    想写小说,听书听完的,收获有但是不多。

    5.美国创新简史:科技如何主推经济增长

    ★★★★

    属于我认知之外的书,不知道是不是真的写的好,因为对于我来说,这本书里的东西都很新鲜。

    2025游戏清单

    2025-08-24 18:29:01

    暗黑4

    进度:不会再玩了,除非超级大更新

    评分:6/10

    评价:比暗黑3都不如

    POE 2

    进度:没事刷刷

    评分:8/10

    评价:我玩过POE 1,给我感觉的POE 2可以进步的空间太大了,相比起来还有好多地方需要完善,但是喜欢刷刷刷的一定要玩玩看,相比暗黑4,灵活程度简直是100倍,不会有那种“策划教你玩游戏的感觉”(不过还是会有相对于别的Build更强力的个别Build)。

    Monster Train 2

    进度:浅玩几轮

    评分:6/10

    评价:感觉一般般,一来不是我最喜欢的类型,二来我感觉不如我玩过的别的类似的游戏,前期卡组非常单调,正反馈不足。

    马里奥赛车世界

    进度:每日进行中

    评分:8/10

    评价:我倒是只有那么喜欢玩,但是我3岁的儿子每天都要在里面跑。

    咚奇刚:蕉力全开

    进度:每日进行中

    评分:9/10

    评价:每天睡前、拉屎的时候都在玩,只能说喜欢马里奥奥德赛的人不要错过一点。

    元炁 Demo

    进度:八九不离十

    评分:5/10

    评价:画面不行、修仙系统设计也不行、操作也别扭,看之前的宣传不知道是不是换了模板了,反正现在是不值得去玩的。

    逃离鸭科夫

    进度:第一张地图打完

    评分:7/10

    评价:还原塔科夫还原的还不错,画风也还行,就是简单了点,几张地图打完之后估计就没内容了,感觉不值50,作为30块来说应该算神作。不属于FPS游戏,更像RPG,没游戏玩的可以去试试。

    三国望神州

    进度:每日进行中

    评分:7/10

    评价:类三国曹操传游戏,做了一些改进,并且加了非常多氪金的点,不过微氪也能玩毕竟是战棋游戏,动脑会大大提高战力。不过现在愿意动脑的人好像不多了,这是不是这些游戏能氪到钱的原因?后续还会出刘备传,到时候再来更新。

    燕云十六声

    进度:弃坑

    评分:7/10

    评价:之前就听说了但是一直没玩,结果突然国外社区火了,于是下载来看看,做的很牛逼,但是和逆水寒之类的区别感觉不大,东西多的一逼,反而没那么好玩了。如果之前没试过逆水寒这类的江湖游戏,可以试试燕云。

    POE 2:最后的德鲁伊

    进度:刷

    评分:10/10

    评价:GGG真的不错,虽说很久更新一次(几个月),但是每次更新都像是做了一个新游戏出来(之前有这个感觉的是V社的DOTA2)。举个例子,这次更新的德鲁伊其中的一个升华(先知)的一个天赋可以看到别人看不见的天赋树,点完之后起码多出大几十个新天赋,这只是升华的一个天赋点啊!(反观暗黑,呵呵)。

    日常-阅读 How we built our multi-agent research system

    2025-06-17 15:11:57

    今天抽空阅读了 Anthropic 的 How we built our multi-agent research system,其中有不少过去就已经了解熟知的知识,也提到了不少能够激发我思考的点,来总结总结。
    首先先说说之前就了解的:

    1. 一个非常聪明的规划者。Anthropic 用的自然是 Opus,规划任务、决策都是 Opus 来做的,越聪明的 AI 做规划者越能提高执行者的效率和效果,目前第一梯队的规划者只有 3 个,Anthropic 4-Opus、OpenAI O3、Gemini-2.5-Pro,排名有先后。
    2. 执行者的能力不能太低。Anthropic 的执行者使用的是 Sonnet,而不是 Haiku,执行者要能正确的使用工具,正确的遵循格式来输出,并且最重要的是能够理解任务的要点。
    3. 使用记忆。PreAct 的要点就是提前计划并且生成 TODO,需要记住 TODO 完成的状态;在每轮迭代任务的过程中,都会产生新的结果,都需要进行记录;长时间执行的任务,会产生巨长无比的上下文,通常经过总结后也需要保存到记忆中。文章中配了两张图都提到了 Memory 可以看出它的重要。 之所以不把记忆归类到工具,是因为记忆可以算作一个基础设施,是必须要使用的,而工具是根据任务的不同可以选择用或者不用。
    4. 使用工具。给予丰富的工具才能让 Agent 的智能发挥作用,最近看的最典型的例子就是,ChatGPT 网页上的 O3 在工具加持下能够通过非常普通的照片定位到照片拍摄位置。过去我倾向于看模型的 Aider 评分来看模型的能力,因为这个评测集通常反应了模型编写代码和指令遵循的能力,但发展到现在的阶段,Aider 的评分已经无法反应出模型真正解决问题的能力,现在我更喜欢看模型的 SWE-bench Verified 评分,这是一个经过筛选的现实问题集,考验模型使用各种工具来解决现实问题的能力,不过通常强的模型这两者的分数都不会低。

    再来谈谈新的收获:

    1. 巨量的 Token 消耗。Anthropic 提到 Multi Agent 是普通聊天的 15 倍 Token 消耗,所以应该让 Multi Agent 作用在能产生高价值的地方,而不是普通的任务。我之前有想过为什么 AI IDE 不搞一个这种 Multi Agent 模式,能够又聪明又快速的完成任务,从这篇文章中可以一窥原因,现在的 AI IDE 不约而同的让自己的工具在尽量少的 Token 消耗的情况下来解决问题,但基本都是事与愿违,有时候看着 AI 50 行 50 行代码的 read,真的哭笑不得。想必这就是 AI 提供商巨头的护城河,他们的 Token 基本上是 Free 的,通过烧 Token 来提高效果这是一般的应用公司没法做到的。
    2. Agent 调试。之前从没有想过来做一个工具像断点代码一样调试 Agent,Anthropic 做了一个工具,能够逐步的观看 Agent 如何进行处理,这使得在 Agent 优化的过程中能够直观的、不停的 减少 不必要的工具调用、生成冗长的搜索词等。这也让我想到了可以把 传统编程中的很多调试方法都带到 AI 应用开发,除了断点,还有像日志(做个 MCP 来让 AI 自动输出日志到指定的地方)、Mock(假设模型的前置返回或者 MCP 的返回来进行测试调试)等等,对于 AI 这种黑盒开发有不小的帮助。
    3. 智能体自我进化。像 Cursor 中的 Agent 自动调用测试获取报错信息或者是获取 Lint 报错一样,如果任务执行的不好,Anthropic 会让 Sonnet 自己来修改提示词再次进行尝试,直到获取到更好的结果。甚至当提供的 MCP 工具有问题时,Sonnet 会去修复这个工具然后继续。之前看信息流里有 OpenAI 的老哥说 O3 的降价也来自于 Codex 对系统的自我进化开发。这相当于强化学习训练出来的 AI 来用强化学习的方法来训练一个应用?
    4. 并行处理。不光是把任务分解后多个 Agent 同时去完成所有的任务,还有 Agent 同时调用多个工具来解决一个任务。这个想法其实我之前也想到了,并且在一个 Side Project 中也实践过了,效果非常好,这个 Project 是一个小说编辑器,在对小说进行润色的过程中,会让规划者先标记出来哪些句子需要润色,再让多个执行者并行的对每个句子来进行润色。原来这个任务在润色一篇 3000 字的文章时,起码要等待 40 秒(使用的是 gemini 2.5 pro),使用了这种方式之后,大约 10 秒内就能看到结果了。
    5. 提示词优化。Anthropic 提到一个微小的提示词修改,使得任务成功率从 30% 提高到 80%,这让我想到之前听的播客中的一个观念:如果我修改16 次提示词最终完成了比较难的问题,那么这个过程中,到底是我太菜了还是 AI 太菜了?Anthropic 很大方的开源了自己的提示词,可以去 Anthropic 的仓库里看看。
    6. 工程化。Anthropic 提到了很多工程化的问题,包含了 恢复状态、容错、可调试可观测可测试、部署更新、执行与并发 等各种挑战,这也是每个做 AI 应用的开发者都可能会碰到的问题,所以 AI 应用并不是写个提示词那么简单,涉及到的编码和传统软件工程的知识都非常多。在目前,至少是 2025 年 6 月这个节点,根本不可能有真的0编程知识的人能够使用 AI 开发上线运营一个应用。

    想法:
    我曾经花了一个小时,和 AI 对谈让它来不停的问我问题了解我,最终总结出了一大篇关于模仿我的提示词,然后我就可以与“我”对话了。不过这个“我”的上下文还是太少,比如我正在写的项目和完成的进度,比如我的朋友同事们的性格和我们之间的经历,比如我还有一些并不符合 AI 政策的想法无法输入进去。同时“我”的能力还是太少了,抛开能影响现实中的能力不谈,在电脑中的能力也少的可怜,虽然我也配置了很多 MCP 但 AI 任然不能用我的电脑做任何事,比如聊微信、写 Word、刷信息流等等。但如果我提供了某个特殊领域的所有工具,比如程序员或者产品经理,它能够写代码、执行命令、获取错误、截图看效果、写 Markdown 文件、发飞书通知、查看禅道 Bug 情况、跟进飞书多维表格的任务进度,那他是不是真的可以成为某个领域中的我,然后再用 Multi Agent 技术来模拟许多个我,让我的效率得到千百倍的提高。这件事我会去做,不过估计做的会比较慢,我相信随着模型智力的提高,最终的那个“我”肯定比现在的我要更胜任我的工作,那时候我应该可以躺着了。
    最近一直被安利 Claude Code,不过没抽时间去试,昨天偶然看到了一个 MiniJinja 的作者使用 Claude Code 修复现实中的 Issues 的视频,可以看到的是,Claude Code 并不能真正“完美”的完成任务,改动的方案并不是最佳的,也没有遵循用户的指令先进行方案讨论不要立马实现代码,修复这两个 Issue 都是需要资深开发者来介入的。所以我一直偏向 Cursor 这类 AI IDE,纯 Agent 很容易把原来优雅的代码改成屎山,然后又要花时间去把屎山做优雅。不过如果是 Vibe 一个简单的 Side Project 用这种方式应该还是很舒服的,等我下一个 Side Project 启动的时候我会开一个 100 刀的 Claude Code 试试。