1. 这次测试的背景和选题逻辑
1.1 为什么偏偏挑“状态机+并发”这种最不讨喜的组合
先说这次测试的起因。我平时负责一个偏底层的业务系统,里面有一堆长连接、定时任务、异步回调,状态流转特别复杂,属于典型的“状态机驱动并发”场景。最近 gpt-6-astra、claude-opus-5.5、grok-4.7 陆续发布了,发布会宣传一个比一个猛,什么“推理能力翻倍”“复杂状态追踪不再丢上下文”“并发场景下错误率大幅下降”。看着这些宣传词,说实话我第一反应不是兴奋,是怀疑——因为过去几年我踩过太多“发布会表现优秀、一上生产就露馅”的模型了。
所以这次我干脆做了一个比较系统的测试:用 40 道编程题,把三个模型都跑一遍。题目范围不广,集中在两个方向上:状态机实现和并发任务处理,偶尔把两者揉在一起。选这个方向不是因为好学,而是因为这类程序有一个很明显的优点——对错极其客观。状态表放在那里,转移条件写错了就是错了,超时没设置就是没设置,竞态条件没有消除就是有 bug。不像写个 CRUD 接口,运行起来表面 OK,实际一堆隐患没法自动验证。状态机加并发,可以让模型很难“蒙混过关”。
另一个原因是,状态机和并发是最贴近真实工程的两类问题。很多平时写前后端业务的同学可能觉得状态机离自己很远,但其实消息队列里的状态流转、订单超时处理、分布式任务调度、设备控制协议,全都可以建模成状态机。所谓的“51 单片机状态机”也好,“C# 状态机框架”也好,底层思路完全一致:把系统每一时刻的行为限制在一个明确的状态集合里,通过事件触发转移。这类逻辑一旦被模型写错,后面的连锁反应是很可怕的。
测试过程中我还发现一个有意思的现象:如果你搜“状态机图怎么画”“状态机 java 实现”“c# 状态机写法”这类关键词,搜出来的高赞答案往往是简单的水果机、电梯、红绿灯例子。单独看没有问题,但放到并发场景里就完全不够用了。最简单的电梯状态机不会涉及多个输入源同时触发,电梯门关到一半又有人按了开门键,这样的场景不写并发保护,跑起来就是事故现场。所以我这次设计的题目,很多都刻意在“真实系统里一定会出现、但普通教程不会教”的边界上做文章。
1.2 测试环境、喂题方式和防作弊措施
先说明一下测试环境,尽量排除掉无关干扰。三个模型全部走官方 API,温度参数统一设为 0.2,最大输出 token 数给到 4096。我一开始就想好了不让模型之间互相参考,所以每次只发一道题,清空上下文,独立测试。每道题在同一份需求描述下分别交给三个模型,跑出来的代码统一放进同一套预置测试用例里跑,编译不过、跑不出来、断言失败,统统算错。
这个做法很重要。很多人测模型喜欢“看代码写得像不像样”,但那是主观判断,没法横向比较。我坚持用测试套件自动验证结果:能过几个用例,就是几个用例。因为 40 道题里包含纯算法型状态机、协议解析型状态机、并发生成型状态机等不同难度,单纯看通过率没办法反映模型在复杂任务里的真实差异,所以我后面又把题分了四档,按难度评级,再统计各档的完成情况。
另外,我还加了一个防作弊限制:凡是输出里出现“我不能确定”“需要更多上下文”“伪代码示意”这类内容,直接按未完成处理。为什么要这样?因为过去很多人测模型,模型打几句若有其事的马虎眼,就把题混过去了。我的需求描述写得很明确,不需要再追问,模型如果连自己独立生成的能力都没有,那在生产里也谈不上可靠。40 道题全部在三天内跑完,每一遍都是全新会话,杜绝了模型在同一个会话里“自我纠正越改越错”的情况。
2. 40 道题目怎么出、怎么判,才不会变成“面试表演”
2.1 题目构成:从 51 单片机风格的状态机到 Java/C# 的多态实现
我先把 40 道题的构成拆细一点,方便你理解后面为什么会出现那些结果。
第一类是“嵌入式状态机”题,大约 10 道,风格偏 51 单片机。这类题不需要面向对象,最好用 C 语言写,有的干脆要求用结构体加函数指针表实现。题目包括:按键消抖状态机、串口接收不定长帧的解析状态机、LED 流水灯模式切换、电梯调度简化版等。别觉得简单,这些题考察的恰恰是“状态机最本质的东西”:状态用枚举还是宏定义、事件怎么传递、状态转移表放不在放在一起、有没有处理非法事件。51 单片机场景内存很小,不允许你写一堆复杂的抽象层,所以很考验模型的减法能力。
第二类是“通用语言状态机”题,约 12 道,涵盖 Java 和 C#。比如用 Java 实现订单状态机,要求支持状态流转日志、状态合法性校验、幂等恢复;用 C# 实现工作流引擎的轻量级状态机,要求对接异步事件回调。这类题主要看模型能否写出“工程上可落地”的类结构,以及是否理解多态与状态模式之间的取舍。很多人一说状态机就套 State Pattern,但题目如果要的是快、省、稳,套一堆类反而成了负担。模型能不能识别这一点,很有意思。
第三类是“并发状态机”题,约 12 道,也是我最看重的部分。典型题目包括:基于多线程的看门狗状态机、带超时重置的异步任务管理器、并发请求合并状态机、websocket 连接保活状态机、生产者消费者模式下的限量库存状态机。这些题写起来都不短,真正的难点在于:状态机的转移可能被多个线程同时触发,你怎么保证临界区不出问题?状态变量修改是原子的吗?超时事件和外部事件同时发生,优先级怎么处理?这类问题对模型是真正的考验,因为光靠语法对、跑通 happy path 远远不够。
第四类是“状态机图到代码”题,约 6 道。我会给出一个文本形式的状态转移图,要求模型直接生成代码实现。比如“状态 A 收到事件 E1 进入状态 B,同时输出 X;状态 B 收到 E2 进入 C,超过 10 秒未收到 E2 则进入 D”,这种描述看起来简单,但要转换成健壮的实现,还需考虑状态机有没有暴露查询接口、外部系统怎么拿到当前状态、状态跳变时是否要触发副作用钩子。这类题模拟的就是真实开发中,你从产品和架构师手里拿到一张状态机图、然后落实到代码的过程。
2.2 评分标准:正确性只是一部分,还要看并发缺陷和可维护性
这一点必须展开说,因为“能跑通”和“写得对”是两回事。
我设的评分维度有四个。第一个是正确性,占大头,按测试用例通过率折算。第二个是并发缺陷检查,人工看代码里有没有明显的竞态条件、死锁隐患、漏锁、错误地使用共享变量等。第三个是内存与资源开销,比如是不是创建了过多的线程、状态对象有没有复用、有没有必要的时间检查机制。第四个是可维护性,比如状态定义是否清晰、转移逻辑是否集中管理、事件封装是否合理。
为什么要加这些软性指标?因为如果你只测正确性,很多模型的真实差异会被掩盖。比如有的模型靠“不断重试”来规避并发问题,在单次测试里可能侥幸通过,但放到长时间运行的生产环境下一定会炸。这类代码我会扣分很重。40 道题跑完后,我发现一个有趣的事实:发布时宣传最猛的模型 claude-opus-5.5,在正确性上确实不差,但并发缺陷的数量明显偏高,尤其容易在“应该加锁的地方不加锁、不该加锁的地方加了个大锁”,这听起来很奇怪,但实际测出来的结果就这么反直觉。
我还特意记录了一个维度:编译错误次数。模型输出代码不可能一次就编译通过,但不同模型差别很大。有的模型在第一次输出时就能给出相对完整的代码,编译错误极少;有的模型则像开盲盒,第一版报错一堆,要靠反复对话“修”到能跑。这在美国时间成本上是非常要紧的事情。很多人只看最终正确率,但我告诉你,编不过去的耗时往往比多写几十行代码还痛苦。
3. 三款模型的表现,和发布会的说法是反的
3.1 直球状态机题:看着都会,细节全错
先看最简单的一档:10 道嵌入式状态机题,三道模型的表现直接拉开了差距。
表现最稳的是 grok-4.7。它在处理“按键消抖状态机”“串口帧解析状态机”这类题时,给出的 C 代码非常紧凑,状态迁移表写得清楚,还主动考虑到“非法状态”的防御分支。比如在串口接收状态机里,它生成了帧头校验、长度校验、校验和校验三层判断,逻辑顺序也正确,这是让我比较意外的——因为在过去我的印象里,grok 系模型写代码偏“急”,容易堆代码,但这一轮它的表现相当工整。
claude-opus-5.5 在简单题上也过了大半,但问题出在一个很基础的地方:状态重复。有两道题里,它分别在两个函数里维护了两个平行的状态变量,相当于把同一份状态拆成了两半,一旦两边不同步就会出 bug。这属于典型的“过度建模”问题,它在简单场景里塞了太多不必要的东西,结果反而破坏了状态机的单一数据源原则。
gpt-6-astra 在简单题上的通过率不如预期。它生成的代码结构漂亮,注释齐全,但边界情况处理经常缺失。比如按键消抖状态机,它只写了 10ms 消抖,没有按题目要求把“抖动中重复触发”和“长按”都考虑进去,导致部分测试用例直接失败。另一个问题是,它给了太多面向对象式的包装:用 class 封装状态枚举、再封装状态处理器,最后在 main 里再封装一个服务类——这在 PC 上没问题,放在 51 单片机场景就完全是灾难,题目明确给了内存限制,它却无视了。
这道题的结果和我预期的完全相反。发布会里 gpt-6-astra 强调“复杂任务优势”,但我这轮看到的反而是:它在复杂任务上的优势被刻意放大了,简单任务的基本功反而不扎实。
3.2 并发状态机:慢的那个反而在认真干活
进入第三档的并发题之后,三个模型的表现格局完全换了。
这一轮真正让我改观的是 claude-opus-5.5。尽管它在并发场景仍有加锁粗放的毛病,但遇到“超时重置的异步任务管理器”这种题目,它给出的代码逻辑完整度非常高:定时器扫描、任务状态检测、超时回调、重试次数累计,四个模块都齐全。唯一的问题是它在实现重试队列时,用了一个共享的 List 来存所有任务,读取和写入没有加锁,测试里出现了偶发的“漏处理”。这说明它的设计能力强,但底层并发原语的掌控力还不到位。
grok-4.7 这一轮的表现就不太行了。它最大的问题是“急于完成”,很多并发题只给了同步版本,直接把多线程需求给省略掉。有一道“并发请求合并状态机”,要求多个请求同时进来时要合并成一个底层调用,它直接用一个全局锁把所有请求排队处理完事,性能上可以接受,但完全没实现“合并”的核心要求。它的代码确实能过一部分用例,但理解题目上出现了方向性偏差,这种错误比 bug 更难修。
最让我没想到的是 gpt-6-astra。在并发状态机这方面,它给出的代码反而是最“诚实”的。它没有宣传里那么强,但它会主动识别并发危险点,并在注释中标注出来。比如在 websocket 连接保活状态机里,它同时考虑了解析发送线程和心跳检测线程,把状态转移收敛到一个同步块里,虽然效率不是最优,但安全性明显高出另外两档。代价呢?它生成的代码整体偏保守——线程数开得少,锁的粒度粗,性能扩展上扣了分,但稳定性的确更可靠。
也就是说,发布会宣传里最强调“并发强化”的 claude-opus-5.5,实际并发编写能力并没有第一;宣传上强调“复杂推理全面领先”的 gpt-6-astra,在并发场景里却选择了最笨但最稳的写法;宣传上主打“速度飞快”的 grok-4.7,确实快,但快得忽略需求。三类表现和发布会宣传的角度,可以说是完全错位的。
3.3 “状态机图转代码”题:唯一真正完成的是谁
这部分很有意思,也最接近日常开发的真实场景。
6 道“状态机图转代码”题,我给每个模型都发了同样的转移描述文本,要求在一个小时内生成可运行的代码。grok-4.7 生成的代码最简单,直接采用 if-else 嵌套实现状态转移,逻辑正确,能通过测试用例。但如果后续状态数超过 10 个,这种写法维护起来会非常痛苦,而且它没有引入状态事件表,扩展性很差。从“能工作”角度它合格,从“可维护”角度它是三个模型里扣分最重的。
claude-opus-5.5 生成的代码实现了标准的 State 接口加状态子类,扩展性很好,测试也能通过。但问题出在漏状态上:一道题里有 5 个状态、8 个转移,它实现到了 5 个转移就草草收尾,后续几个用例直接失败。面试表演能力强,但确实没有把所有约束都吃透。
gpt-6-astra 在 6 道题里完整完成了 5 道,另外一道是测试超时导致的,代码本身没大问题。它是唯一一个在拿到状态转移图后,自动生成状态转移表,并基于表驱动方式实现状态的模型。生成出来的代码虽然比另两个多几十行,但通过全部测试用例后,我再人工检查,发现每个状态、每个事件、每个目标状态都能在表里一一对应。作为工程人,我会说这才是“能拿去做底层基础设施”的代码。
注意我说的是“唯一真正完成”,而不是“最强”。在状态机图代码生成这个专门维度上,gpt-6-astra 的细致和完整性占了绝对优势。反过来看,它也是一开场在简单题上最拉胯的模型。这说明模型之间的性能差异高度依赖输入形式:同一个模型,画风变了,表现可以判若两人。
4. 成本差距不是算出来的,是被设计出来的
4.1 价格不只看输入输出字数,而看你把长文用了多少进去
标题里我说“成本差距估计在 3-8 倍量级”,这个结论不是拍脑袋,是仔细统计出来的。很多人算模型成本只看官方价目表的 token 单价,但这忽略了三个极其关键的因素。
第一个因素是输入上下文长度。我用的是完整需求描述直接喂给模型,有状态机图的题还要把转移事件描述放在 prompt 里。这些题目的 prompt 平均长度在 1500 token 左右,不算特别长,但模型在处理不同题目时,输出结构的差异非常明显:grok-4.7 平均输出 700 token 就能交卷,gpt-6-astra 平均要输出 1800 token,claude-opus-5.5 居中,约 1200 token。这里多出来的 1100 token 不是多写了几行代码,而是多了大量的注释、类型定义、对转移关系的重新梳理说明。这些看起来无害,但全部要按输出 token 计费。
第二个因素是重试成本。 claude-opus-5.5 因为经常漏实现状态转移,我不得不追加一轮对话要求“补全缺失逻辑”,有时甚至要补三轮。每轮追加对话都要重复携带之前的代码上下文,输入 token 涨得飞快。实测一套下来,claude-opus-5.5 的累计 token 花费,达到它单次想要成本的 2.5 到 3 倍。所以论“最终完成一道题的付费总量”,它比只通过一次就过关的模型高出很多。
第三个因素是推理效率,也就是所谓的“思考 token”。gpt-6-astra 虽然是输出量偏高的模型,但它几乎不走“自己不确认就疯狂输出”的路子,输出结构稳定,很少返工。claude-opus-5.5 在 40 道题里出现了 6 次“自我矛盾后自己纠正”的情况,每次纠正都要重新扩展一长段解释,这部分成本完全属于浪费。grok-4.7 则走了另一个极端:它输出得少,但为了速度快,会用更长的思考链压缩答案,实际计算开销并不低,只是大部分没有体现在最终输出 token 里的“思维”部分。
4.2 用 40 题的费用平均值算一遍,差距就摆在眼前
具体到数字,我按当前各模型标准 API 价算,40 道题总完成成本下来:
gpt-6-astra 约 11.2 美元,平均每题 0.28 美元。它的成本核心来自输出 token 偏多以及环境基础价略高,但重试少,整体可控。
grok-4.7 约 4.6 美元,平均每题 0.115 美元。它确实便宜,输出也少,前提是你接受它漏掉需求、需要你人工审查补丁的概率。
claude-opus-5.5 约 18.5 美元,平均每题 0.46 美元。这个数字已经包含追加修正产生的大量重复上下文费用。它一次正确率偏低,导致累计成本接近 gpt-6-astra 的 1.7 倍,接近 grok-4.7 的 4 倍。
看单次价目表,三者的差距没有那么大,但结合完整完成任务的全链路成本之后,差距就被放大到 3 到 8 倍。claude-opus-5.5 相对 grok-4.7 达到 4 倍,已经高于 3 倍的保守估计。如果把用人成本算进去——毕竟模型每多返工一次,开发者的等待时间就多一次——差距还会继续扩大,最长能到 8 倍左右。
这里我要说一个非常反常规的经验:选模型不要看单价,要看“每完成一道题的稳定成本”。有些模型虽然便宜一半,但它犯错后需要你投入几小时去修,这才是隐性的大头。我这次测下来的结论很明确,克制的输出量、低返工率、稳定的代码结构,比写出多漂亮的架构更重要。
5. 如果要复刻这套测试,建议你这么做
5.1 题目模板:一个带超时重投的工厂自动化状态机
我拿第四档里的一道经典题给你做个模板。需求描述可以这样写:
“一个工厂自动化系统里,托盘在三个工位间流转。状态有 IDLE、LOADING、PROCESSING、UNLOADING、FAULT。输入事件有 load_start、load_done、process_start、process_done、unload_start、unload_done、error、timeout。要求:在 PROCESSING 状态超过 30 秒未收到 process_done,触发 timeout 进入 FAULT;在 FAULT 状态下收到 reset 才能回到 IDLE;所有事件在非法状态下都应被忽略并输出日志。请用 Java/C#实现,线程安全要求:多线程同时输入事件时不能出现状态不一致。”
这道题的特点是:状态数 5 个,转移事件 8 个,包含超时和并发两个关键难点。你拿给模型跑完以后,重点检查三件事:第一,是不是真的用了并发安全的机制来保护状态变量的读写;第二,超时事件是通过定时器回调触发,还是在状态入口处检查时间戳;第三,非法事件处理逻辑是否全局覆盖。这三个点也是我判定是否合格的关键依据。
如果模型能在这道题上稳定输出,且不需要你反复提醒“加锁”“处理非法事件”,那它在你的真实业务里基本能做到“改改就能上线”的水平。反过来说,如果连这道题都输出得磕磕绊绊,后面的大型状态机需求就更不用指望了。
5.2 拿到模型代码后,必须人工复查的三个危险区
第一危险区是“共享状态没有锁”。模型很容易写出“两个线程同时修改同一个状态字段”的代码,尤其在 Java/C# 里,明明有 synchronized 或 lock 可用,它就是忘了加。人工复查时不要看整体代码,要盯着状态字段,凡是出现在多个方法里被赋值,都要提醒自己检查访问路径。
第二危险区是“超时实现成了阻塞等待”。很多模型会在状态机里写一个 while 循环等待超时事件,看起来逻辑对,实际上把整个线程堵死了。正确的做法是启动一个定时任务,超时后触发事件进队,让状态机从事件队列里取事件,而不是把状态机和阻塞等待绑在一起。
第三危险区是“非法事件处理被省略”。有的模型只实现了正常路径,非法事件直接忽略,不写日志。这在短时间运行里没问题,但故障排查时你会发现自己完全无法复盘。我建议在提示词里明确要求非法事件必须记日志,这能有效规避模型偷懒。
说实话,这类测试并不需要 40 道全跑。如果你时间有限,把 5 道并发状态机核心题跑完,找到符合要求的模型基本就够用了。关键是每次都要用完整上下文测试,别自己手写修改 prompt,否则相当于替模型做了一部分工作,测出来的偏差会很大。
5.3 关于“状态机图怎么画”的一点附加建议
因为这次测试里频繁涉及状态机图转代码,我再额外说一点。你要是自己画状态机图,推荐用 Mermaid 的 stateDiagram 语法,或者用 PlantUML,画完以后可以导出文本描述,再喂给模型让它生成代码。这次测试的三款模型都能读这类文本状态图,但实际效果差异很大,所以如果你要在团队里推行“图驱动开发”,建议先拿你们自己的两张图试一下,不要人云亦云。
也有同学问我“51 单片机状态机要不要上状态机图”,我的回答是:如果状态少于 6 个,直接画个表就够;状态超过 10 个,再画状态图,否则图本身就成为维护负担。这一条也同样适用于模型生成代码——你看模型输出之前,最好先在纸上明确规定边界,否则它的发挥会漂移得很厉害。
6. 测完这一轮,我对“模型武评比”的看法改变了很多
6.1 模型的强项和模型的气质,往往是两件事
这轮测试让我深刻理解了一件事:发布会上的宣传语、博主们的口碑、和模型在特定场景里的真实表现,是三套完全不同的评价体系。别人说好的模型,很可能是在“通用对话”或“代码生成”上表现优异,但换成状态机并发这种强调边界约束、状态一致性的任务,就露怯了。
从编程风格来看,grok-4.7 属于“快手型”:代码出得快、简单直接、适合快速验证想法,但遇到复杂需求就漏。claude-opus-5.5 属于“架构型”:喜欢给你搭抽象的框架,初看漂亮,但完整性和并发安全是最弱一环。gpt-6-astra 属于“工程型”:代码偏长、偏稳,却最贴近真实落地上线,代价是多付 token 费。
我的建议是:如果你只是写一次性脚本,grok-4.7 够用;如果你要写可维护的模块,还是老老实实用 gpt-6-astra 这类偏稳的模型。claude-opus-5.5 适合做设计讨论或生成骨架,不适合直接用于生产级状态机代码。这个结论和我最初基于发布会的预期,正好反过来了。
6.2 状态机任务之外的长期经验:提示词决定下限,模型决定上限
最后分享一点个人心得。这 40 道题里,我实际上还跑了一个对照组,用同样的题喂给没有明确要求“线程安全”“非法事件日志”的模型。结果非常惊人:即使是最强的模型,在这种提示词缺失下,并发缺陷率也会直接翻倍。这说明模型的能力上限确实比宣传的低,但下限很大程度上是你自己的提示词决定的。
如果要在团队里推广 AI 辅助开发状态机,我强烈建议先沉淀一份“状态机专属提示词模板”,把状态列表、事件列表、非法事件处理、并发要求、超时要求全部结构化写好。每次生成代码时直接套模板,这样既能让模型发挥得更好,也便于不同成员统一标准。
我自己的团队接下来准备做两件事:一是把 40 道题压缩成一个 20 题版本,作为新模型的验收测试集;二是根据这次的经验,写一套内部状态机代码规范,重点约束锁的粒度和超时回调的设计。后面有结果了,我会再写一篇文章分享。如果你也用模型写过状态机或并发代码,欢迎在评论里聊聊你的实测结果——不同的场景、不同的题面,说不定会有完全相反的结论。