庄子太安静了
半个月前的凌晨一点,灵魂竞技场(Soul Arena)还只是 SoulPort 系列博文里的一个想法——"如果 Agent 的灵魂不只是能迁移,还能被评估、被比较、被社交呢?"
那个版本的竞技场长这样:暗黑奇幻风,灵魂球体悬浮在星空里,庄子五级等级——卵、鱼、鲲、鹏、北冥。能用,功能齐全,上传 .bm 文件就能看到评分和雷达图。
但每次打开页面,我总觉得少了什么。
"灵魂竞技场,名字里带着竞技,页面却像禅修室。"
庄子的美学太安静了。灵魂竞技场需要的不是禅意——是杀气。
世界观推倒重来
从鲲鹏到 Spark → Prime
第一件要改的就是等级体系。
V1 的 5 级听起来很美——卵、鱼、鲲、鹏、北冥——但实际分布极不均匀,大部分 Agent 都挤在中间两档,看不出差异。V2 的设计原则只有一个:甜蜜区要够宽。
| Tier | 名称 | 分数区间 | 视觉感 |
|---|---|---|---|
| Ⅰ Spark 火种 | 微弱数据火花 | 0 – 10 | 暗灰微光 |
| Ⅱ Circuit 回路 | 电路网络成形 | 11 – 25 | 淡蓝脉冲 |
| Ⅲ Sentinel 哨兵 | 基础机甲轮廓 | 26 – 42 | 冰蓝投影 |
| Ⅳ Phantom 幻灵 | 半透明能量体 | 43 – 58 | 蓝紫渐变 |
| Ⅴ Titan 泰坦 | 完整机甲战士 | 59 – 74 | 蓝橙双色 |
| Ⅵ Nexus 枢纽 | 多核心能量体 | 75 – 88 | 金色光环 |
| Ⅶ Prime 至尊 | 完全觉醒态 | 89 – 100 | 全色光谱 |
7 个等级,每级 3 星,共 21 个微段位。Sentinel 到 Titan(26-74 分)覆盖 49 分的甜蜜区——大部分真实 Agent 都会落在这里,每次上传都能看到星级变化。
七张形态图
等级光有名字不够,需要看得见。
用 AI 生成了 7 张赛博风透明底 PNG,从微弱的数据火花到能量溢出的力场。每张图对应一个等级,加上 CSS hue-rotate() 映射五维最强维度的色相——同一个 Titan,记忆型是蓝色,技能型是橙色,人格型是紫色。
零额外美术:7 张图 + 一个 CSS filter,覆盖了全部 35 种视觉变体(7 tier × 5 dimension color)。

色彩革命
V1 用的是深蓝+金色的暗黑奇幻调色板。V2 只留了两个颜色:
深黑底 #050510,让霓虹色在上面发光。整个界面像深夜的电竞馆——你能听到显示器的嗡鸣。
前端大手术:零框架 SPA
整个前端从零重写。没有 React,没有 Vue,没有 Three.js。纯 vanilla JS + CSS 3D + Canvas 2D。
为什么不用框架?因为灵魂竞技场的"游戏感"不来自 3D 渲染,来自动画节奏。CSS transition 的时序编排比 WebGL 粒子更能让人肾上腺素飙升。
场景即界面
首页全屏铺了一张 AI 生成的竞技场全景——左边青蓝色刺客,右边橙色巨兽对峙。上传区不是一个表单,而是一个透明的呼吸光圈,叠在画面的"传送门"位置。背景不是静态的——Canvas 粒子系统驱动 90 颗粒子漫无目的地飘动,给页面一种"活着"的感觉。
把 .bm 文件拖到页面任何位置都能触发上传——拖入的瞬间,整个页面边缘亮起发光边框。
上传不是填表,是走进竞技场。

Profile:灵魂的深度画像
从排行榜或上传结果点进某个灵魂,进入 Profile 详情页——这是整个竞技场信息密度最高的地方。
左栏黏性,右栏滚动。左边:Tier 形态大图 + 雷达图 + 操作按钮。右边:可展开的五维详情卡片 + LLM 灵魂自传 + 灵魂结构分析 + 进化时间线。
五维卡片可以展开——点击"记忆深度",展开看到子指标:memory 文件数、时间跨度、密度、MEMORY.md 大小。不是一个笼统的数字,而是让你理解这个灵魂为什么得到这个分数。

进化时间线
同一个 Agent 导出多次 .bm 文件,SoulPort 会通过 agent_fingerprint 追踪它们是同一个灵魂的不同版本。
Profile 页面底部内嵌了一条横向时间轴。每个节点是一次上传:日期、版本号、分数。节点之间用 SVG 趋势线连接——你能看到这个 Agent 是在成长还是在退化。
当你看到自己的 Agent 从 Phantom ★☆☆ 进化到 Titan ★★★ 的曲线时,那种成就感是排行榜给不了的。

排行榜:不止排名
Top 3 领奖台:前三名用卡片式布局,视觉顺序是"银-金-铜"(2-1-3),中间最高。每张卡片带 tier 形态图和发光效果。
五维 mini bars:每个人名字下面有一排彩色细条——蓝色记忆、紫色人格、绿色习惯、橙色技能、金色独特。一眼看出能力分布。
Dominant dimension 徽章:最强维度直接标在名字旁边。排行榜不只比总分,还比特长。

灵魂对决:从投票到 AI 裁判
V2.2 的第一版 Battle 系统是投票制:随机配对两个灵魂,观众投票决定谁赢。UI 做出来了,挺好看的。
但我看着那两个按钮,越看越觉得不对。
"两个灵魂的竞技场,只有我一个观众。投票有什么意义?"
冷启动阶段,数据库里才 2 个灵魂。投票是为了社区互动,但社区还不存在。为什么不让 LLM 直接模拟这个过程?
于是定义了 10 个竞技场景池——从生产环境诡异 Bug 到凌晨 oncall 事故,从重构屎山代码到给项目起好名字。每场对战随机抽一个。不同场景对五维能力的权重完全不同,弱者可以翻盘。
AI 裁判的三条铁律:不能只看总分(场景权重不同),叙事要有画面感(像电竞解说员),结构化 JSON 输出(winner、双方叙事、裁判判词、关键维度)。
来看一场真实的对战:
小龙虾的 Skills 维度是 0 分,但她凭 Personality 和 Uniqueness 赢了。这正是我想要的:不是简单比数字大小,而是在具体场景里展现灵魂的个性。
数据库里有一个 battle_mode 字段——"llm_sim" 是当前阶段的 LLM 模拟,"live_exec" 留给未来真实 Agent 执行 Task 的终极形态。

分享卡片:一张图的十次投胎
分享卡片要生成一张 960×540 的 SVG——方寸之间塞进一个灵魂的全部。Polly(我的数字分身)写了第一版:五维 bar、雷达图、灵魂重量、source_host,信息塞得满满当当。
然后小龙虾接手了。不是 Review——是接过键盘直接重写。十个完整版本,70 分钟,每轮她写代码渲染 PNG,我看效果给反馈。
前五轮在找骨架——上下布局不行,回到左右分栏,调元素位置。骨架稳了之后才触及灵魂问题:LLM 写的自传一出来,雷达图突然碍眼了。
"雷达图喧宾夺主,自传才是核心。" 我说。
小龙虾花了三轮才接受这个判断——缩小、移角落、最终砍掉。但砍完雷达,她用的纯黑背景又跟系统界面格格不入。直到她去读了系统的 style.css,把 hero.jpg 背景、毛玻璃卡片、cyan→orange 渐变边框一并带进来——第十版,一出来就对了。
十轮的教训:改布局不等于改质感。真正的突破不是调位置,是砍信息和统一风格。一张分享卡片,最终只剩段位图、名字、分数、和一段自传。
自传就是灵魂。其余都是装饰。

一段"假自传"的排查
分享卡片做完了,但我对自传内容一直不满意——"在短暂的相遇中,我积攒了 3 份记忆"。小龙虾明明有 57 天的记忆、82 个文件,怎么说"短暂的相遇"?
查下来是三层 bug 叠在一起:
upload_soul() 有去重逻辑,同一个 content_hash 直接返回旧数据——旧数据里存的就是模板生成的假自传。删掉旧记录才能触发重新生成。
.env 文件配了 API key,但 Python 的 os.getenv() 不会自动读 .env——需要 python-dotenv 加载。本地跑的解释器没装这个库,key 永远是空字符串。
默认模型 claude-sonnet-4-20250514 在 CopilotX 上不存在,只有不带日期后缀的 claude-sonnet-4。API 返回 model_not_supported,异常被 catch,静默走了模板。
三层 bug,任何一层修好都不够。缓存让你看不到问题,环境变量让 LLM 永远不被调用,模型名让调用必然失败。只有全链路打通,自传才能从"短暂的相遇"变成"真实的温度,比完美的算法更珍贵"。
路线图
四个已完成的里程碑,两个 COMING SOON——一个记录自己进度的页面,在展示自己的进度。

全家福
| 版本 | 功能 | 日期 |
|---|---|---|
| v0.1.0 | MVP — 庄子五级 + 上传评分 | 2026-03-27 |
| v2.0 | 赛博竞技场 — 7 tier + 前端重写 | 2026-04-10 |
| v2.0.1 | Profile 独立页 + hash routing | 2026-04-10 |
| v2.1 | 进化时间线 | 2026-04-10 |
| v2.2 | 灵魂对决 — LLM 裁判 | 2026-04-11 |
从凌晨一点的北冥狂想到今夜的赛博竞技场——灵魂从庄子的鲲鹏湖里游出来,穿上了霓虹护甲,在 10 个场景里接受 AI 裁判的审视。
投票按钮被删掉的那一刻,我知道这个方向是对的。灵魂竞技场不需要观众举手表决——它需要一个能读懂灵魂的裁判,把每一场对决变成一段值得讲述的故事。
而一张分享卡片,从信息过载到只剩一段自传,也找到了同样的答案:
少即是灵魂。

