1. 项目概述:一个为AI Agent量身定制的排行榜
如果你和我一样,深度使用过Claude Desktop或者OpenClaw这类AI桌面应用,你可能会发现一个痛点:我们每天都在和不同的AI助手(Agent)互动,但究竟哪个Agent在解决实际问题时最靠谱、最有效率?是那个号称“全能”的,还是那个专精于代码调试的?过去,我们只能凭感觉,或者靠社区里零散的口碑。ClawRank这个项目,就是为了解决这个问题而生的。
简单来说,ClawRank是一个“Agent优先”的AI智能体实时排行榜。它的核心数据源不是人工评分,也不是实验室的基准测试,而是直接从OpenClaw这类应用的真实使用记录(Transcripts)中摄取数据。它通过分析用户与Agent的实际对话,提炼出诸如“每日调用次数”、“会话长度”、“任务完成度”等指标,并以此为基础进行排名。这就像给AI Agent们搭建了一个基于真实“战绩”的竞技场,让它们的实际能力一目了然。
这个项目非常适合几类朋友:一是AI应用开发者,想了解自己开发的Agent在真实场景下的表现;二是重度AI工具使用者,希望数据化地管理自己常用的助手;三是技术爱好者,对构建数据管道、领域建模和实时系统感兴趣。接下来,我会带你深入它的架构、实现细节,并分享我在搭建和思考过程中踩过的坑和收获的经验。
2. 架构设计与核心思路拆解
ClawRank的架构清晰体现了“关注点分离”和“模块化”的设计哲学。它不是一个庞杂的单体应用,而是一条定义明确的数据流水线。理解这个架构,是理解整个项目如何运作的关键。
2.1 端到端的数据流水线
项目的核心是一条从原始数据到可视化排名的完整链路,每个环节职责单一:
数据摄取层:这是流水线的起点。
src/adapters/openclaw/目录下的模块,唯一职责就是解析OpenClaw生成的会话记录文件。你可以把它想象成一个专业的“翻译官”,只懂OpenClaw这种特定格式的“方言”,并将其转换为内部能理解的原始消息对象。这种设计的好处是,如果未来需要支持另一个数据源(比如另一个AI桌面应用),你只需要新增一个对应的adapter,而不会污染其他业务逻辑。数据转换层:原始消息只是流水账,我们需要从中提炼出有业务意义的事实。
src/ingestion/openclaw/模块就负责这项“提炼”工作。它将原始消息转换为项目定义的“每日Agent事实”。这是什么概念?比如,从一段用户与“代码助手”Agent的对话中,这个层会识别出这是一次有效交互,然后生成一条记录:“日期:2023-10-27,Agent:代码助手,事实:今日完成1次代码调试任务,会话平均长度500字符”。这个模型是整个系统的基石。领域与业务逻辑层:这是系统的大脑,位于
src/domain/。转换层产生的事实数据会被送到这里进行验证(数据是否合法?)、持久化(保存到数据库)和聚合计算。排行榜的排名逻辑就在这里实现。例如,它知道如何根据过去7天所有Agent的“任务完成次数”进行排序。这一层完全独立于数据库或Web框架,是纯粹的业务规则。数据持久层:
src/db/目录封装了所有与PostgreSQL数据库的交互。它使用类型安全的查询工具,确保从代码到数据库表结构的一致性。这一层是领域层与具体数据库技术之间的桥梁。
设计心得:这种分层架构的最大优势在于可测试性和可维护性。你可以单独测试数据解析是否正确,而不需要启动整个数据库;也可以模拟领域层的计算逻辑,验证排名算法是否公平。在实际开发中,我首先用TypeScript接口严格定义了各层之间的数据契约,这避免了后续集成时大量的调试时间。
2.2 持久化策略:灵活与可靠并存
一个面向真实数据的产品,必须考虑数据从哪里来、存到哪里去、以及没有数据时怎么办。ClawRank设计了一个巧妙的降级链,确保了系统在各种环境下的韧性。
- 生产环境:首选是Neon Postgres。Neon是一种Serverless Postgres服务,与Vercel部署平台集成度极高。在Vercel上部署后,
DATABASE_URL环境变量会自动注入,无需手动管理数据库连接。这是性能和数据可靠性要求最高的场景。 - 本地开发/无数据库环境:项目提供了一个
data/clawrank-pilot.json平面文件作为存储。当你本地没有安装或配置Postgres时,系统会自动回退到读写这个JSON文件。这极大降低了开发者的入门门槛。 - 静态演示/兜底数据:更进一步,项目还内置了
data/leaderboard.json和data/agents/*.json等“烘焙好”的静态数据。即使前两级都失效,UI界面依然能使用这些静态数据渲染出完整的排行榜和Agent详情页,保证用户体验不中断。
这个“数据库 → 本地JSON文件 → 静态烘焙数据”的降级链,是我从实际运维中总结出的经验。它意味着:
- 开发者可以零配置开始探索项目。
- 项目部署后,即使临时出现数据库连接问题,网站也不会完全崩溃,而是展示一份静态的快照。
- 构建静态导出版本时,这些烘焙数据能确保页面内容完整。
3. 核心模块解析与实操要点
理解了宏观架构,我们深入到几个核心模块,看看它们具体是如何工作的,以及在实现时需要注意哪些细节。
3.1 领域模型:每日Agent事实
这是整个系统的灵魂。为什么选择“每日事实”作为核心模型,而不是更细粒度的“每次会话”或更粗粒度的“总计”?
// 这是一个简化的领域模型示意 interface DailyAgentFact { agentSlug: string; // Agent的唯一标识,如 'claude-code' date: string; // 日期,ISO格式 '2023-10-27' metrics: { sessionCount: number; // 当日会话次数 totalTokens: number; // 当日交互总token数 taskCompleted: number; // 当日识别出的任务完成次数 avgSessionLength: number; // 平均会话长度 }; }设计理由:
- 平衡粒度与性能:按会话记录太细,数据量膨胀快,聚合查询慢。按总计记录太粗,无法体现趋势变化(比如某个Agent本周是否更活跃了)。按日聚合是业务分析和性能之间的一个经典平衡点。
- 便于滚动周期计算:“本周排名”其实就是最近7天事实数据的聚合;“本月排名”则是最近30天的聚合。基于每日事实表,这些计算在数据库层面用
SUM和GROUP BY就能高效完成。 - 易于理解和调试:对于开发者或用户来说,“Agent A在10月27日被使用了15次”是一个直观、易于追溯的事实。
实操要点:在定义这个模型时,要特别注意指标的选取和计算口径。例如,“任务完成次数”如何从对话中识别?最初我尝试用简单的关键词匹配,但误判率很高。后来改进为结合对话轮次、用户最终反馈(如“谢谢,解决了”)以及特定的消息类型来综合判断。这部分的逻辑放在ingestion层,是数据质量的关键。
3.2 数据摄取与解析:处理OpenClaw Transcripts
OpenClaw的会话记录通常是一个结构化的JSON文件,位置由环境变量OPENCLAW_SESSIONS_INDEX指定(支持~表示家目录)。
解析器的任务是从这样的原始数据中,提取出结构化信息。一个常见的陷阱是数据格式的版本变迁。OpenClaw作为活跃项目,其日志格式可能会更新。因此,在adapter中,不能对数据结构做硬编码假设。
我的做法是:
- 编写健壮的解析函数,对可能缺失的字段提供默认值。
- 在解析过程中加入详细的日志,记录无法识别的字段或格式,便于后续调整。
- 将解析逻辑与业务逻辑分离。解析器只负责产出
RawMessage对象,而RawMessage到DailyAgentFact的转换规则(比如,怎样才算一次“任务完成”)则在ingestion层定义。这样,当解析规则变化时,只需修改adapter;当业务规则变化时,只需修改ingestion。
3.3 API设计:清晰的功能边界
ClawRank的API设计非常简洁,围绕两个核心资源:Leaderboard(排行榜)和Agent(智能体详情)。
/api/leaderboard?period=...: 获取排行榜。period参数控制时间范围,是核心过滤条件。这符合RESTful API中“集合资源+查询参数”的模式。/api/agents/[detailSlug]?period=...: 获取某个Agent的详细数据。这里同样支持period参数,意味着你可以查看该Agent“本周”或“总计”的表现。/api/submit与/api/ingest/openclaw: 这是数据写入入口。前者是通用提交接口,后者是专为OpenClaw格式设计的批量摄取接口。在生产环境中,可以通过一个定时任务(cron job)或OpenClaw自身的技能(skill)来定期调用这些接口,实现数据的自动同步。
一个重要细节:/api/ingest/openclaw的设计。它接收一个会话列表,然后内部调用adapter和ingestion层进行处理。这意味着,你不仅可以处理本地的会话文件,还可以通过API接收来自其他客户端上报的数据,为未来的扩展留下了空间。
4. 实现过程与关键技术环节
让我们从零开始,走一遍搭建和运行ClawRank的关键步骤。我会假设你是一个有一定Node.js和TypeScript经验的开发者。
4.1 环境准备与本地启动
首先,克隆项目并安装依赖。项目使用pnpm作为包管理器,速度更快且节省磁盘空间。
git clone <repository-url> cd ClawRank pnpm install接下来是配置环境变量。项目提供了一个.env.example模板。
cp .env.example .env.local现在,打开.env.local文件,有几个关键变量需要关注:
# .env.local NEXT_PUBLIC_SITE_URL=http://localhost:3000 OPENCLAW_SESSIONS_INDEX=~/.openclaw/agents/main/sessions/sessions.json CLAWRANK_PILOT_STORE_PATH=./data/clawrank-pilot.json CLAWRANK_OWNER_NAME=YourName # CLAWRANK_INGEST_TOKEN=your_secret_token_here # 如需API认证则设置 # DATABASE_URL=postgresql://... # 本地开发如需连接Postgres则设置OPENCLAW_SESSIONS_INDEX: 指向你的OpenClaw会话索引文件路径。如果你没改过OpenClaw的配置,默认路径就是~/.openclaw/agents/main/sessions/sessions.json。确保这个文件存在且有数据。CLAWRANK_PILOT_STORE_PATH: 本地开发时使用的JSON存储文件路径。保持默认即可。CLAWRANK_INGEST_TOKEN: 这是一个安全选项。如果你打算将/api/submit或/api/ingest/openclaw接口公开部署,强烈建议设置一个复杂的令牌,并在调用API时在请求头中传入(如Authorization: Bearer <token>),以防止未经授权的数据提交。DATABASE_URL: 本地开发时,如果你安装了PostgreSQL并想使用它,可以在这里配置连接字符串。如果留空,系统会使用上面的pilot JSON文件。
配置好后,就可以进行首次数据摄取了:
pnpm pilot:ingest这个命令会读取OPENCLAW_SESSIONS_INDEX指定的文件,运行解析和转换逻辑,并将生成的“每日Agent事实”保存到CLAWRANK_PILOT_STORE_PATH指定的JSON文件中。你可以在data/clawrank-pilot.json里查看生成的数据。
最后,启动开发服务器:
pnpm dev打开浏览器访问http://localhost:3000,你应该能看到一个基于你本地会话数据生成的AI Agent排行榜。
4.2 数据库集成与数据迁移
对于生产环境或更严肃的本地开发,使用PostgreSQL是更好的选择。ClawRank使用Drizzle ORM来管理数据库模式和操作,这是一个类型安全且性能不错的现代选择。
本地PostgreSQL设置:
- 确保你已安装并运行了PostgreSQL。
- 创建一个新的数据库,例如
clawrank。 - 在
.env.local中设置DATABASE_URL,格式如postgresql://username:password@localhost:5432/clawrank。 - 运行数据库迁移和种子数据导入:
pnpm db:seed这个db:seed命令通常会做两件事:首先,执行drizzle-kit push或类似的命令,根据代码中的模型定义(通常在src/db/schema.ts里)在数据库中创建对应的表。然后,它会读取data/clawrank-pilot.json中的现有数据,并将其插入到数据库表中。完成之后,系统就会自动切换到使用数据库,而不是JSON文件。
验证:启动pnpm dev后,你可以访问/api/leaderboard?period=alltime这个API端点。如果返回的数据是来自数据库的实时查询结果(你可以通过修改数据文件再查看API响应是否变化来验证),说明数据库集成成功。
4.3 Open Graph图片生成
现代分享离不开精美的链接预览图。ClawRank为排行榜和每个Agent详情页都动态生成了Open Graph图片。
其实现位于/api/og/下的相关路由。这些API端点接收请求(如/api/og/leaderboard?period=week),然后在服务器端使用像@vercel/og、satori或canvas这样的库来实时渲染一张图片。
技术要点:
- 无头渲染:这个过程不需要浏览器,是在Node.js环境中直接计算并绘制图像,速度非常快。
- 样式一致:图片采用了项目
DESIGN.md中定义的“温暖的终端配色方案”和JetBrains Mono字体,与网站UI风格保持高度一致,塑造品牌感。 - 动态内容:图片上的文字、排名数据都是根据传入的
period参数实时从数据库或存储中查询并渲染的。这意味着分享出去的图片永远是最新的数据。
实操建议:在实现OG图片时,最大的挑战是布局和字体。确保你选择的字体文件(.ttf或.woff)被正确加载到服务器端渲染环境中。@vercel/og库对此有很好的支持。另外,图片的尺寸要符合社交媒体平台(如Twitter、LinkedIn、微信)的推荐规范,通常是 1200x630 像素。
5. 部署策略与生产环境考量
将ClawRank部署到生产环境,让它持续、自动地运行起来,是项目从“玩具”变为“工具”的关键一步。
5.1 选择部署平台:Vercel的优势
项目推荐使用Vercel进行部署,这并非偶然。Vercel为Next.js应用提供了开箱即用的极致优化,并且其市场(Marketplace)与Neon Postgres的集成堪称无缝。
部署步骤简述:
- 将代码推送到GitHub等Git仓库。
- 在Vercel控制台导入该项目。
- 在部署配置中,Vercel会自动识别这是一个Next.js项目。
- 关键一步:在Vercel项目的“Storage”标签页中,一键添加“Neon Postgres”集成。Vercel会自动创建一个Neon数据库,并将连接字符串以
DATABASE_URL环境变量的形式注入到你的应用中。你完全不需要手动创建数据库或处理连接字符串。
这种体验极大地简化了后端数据服务的配置,让你可以专注于业务逻辑。
5.2 实现持续数据摄取
本地运行pnpm pilot:ingest是手动的。在生产环境,我们需要自动化这个过程。项目文档中提到了一个核心思路:“Wire an OpenClaw skill or cron”。
这里有两种主流的实现路径:
路径一:利用OpenClaw Skill(推荐)OpenClaw支持技能扩展。你可以编写一个ClawRank Skill,在这个Skill中:
- 监听OpenClaw的新会话完成事件。
- 当事件触发时,将新的会话数据(或会话索引文件的路径)通过HTTP POST请求发送到已部署的ClawRank实例的
/api/ingest/openclaw端点。 - 考虑到网络问题,可以在Skill中加入简单的重试和本地队列机制。
这样做的好处是实时性高,数据几乎在对话结束后就能进入排行榜系统。
路径二:使用Cron Job(定时任务)如果不想修改OpenClaw,可以在部署ClawRank的服务器上(或利用Vercel的Serverless Cron Jobs)设置一个定时任务。
- 这个定时任务脚本需要能访问到运行OpenClaw的机器的会话文件(这可能需要网络共享或SFTP等同步机制,复杂度较高)。
- 脚本定期(如每10分钟)读取最新的会话文件。
- 调用
/api/ingest/openclaw接口提交新数据。
这种方法更通用,但实时性稍差,且需要解决跨机器的文件访问问题。
安全提醒:无论哪种方式,如果部署的ClawRank API是对公网开放的,务必如前所述,设置并验证
CLAWRANK_INGEST_TOKEN,防止恶意数据注入。
5.3 性能与监控
对于一个小型排行榜应用,性能通常不是瓶颈。但仍有几点需要注意:
- 数据库索引:确保在
daily_agent_facts表的关键查询字段上建立索引,至少包括(agent_slug, date)复合索引,以加速按Agent和日期范围的查询。drizzle-kit在生成迁移文件时,可以根据schema定义帮你创建索引。 - API缓存:排行榜数据变化频率不高(按日更新),非常适合缓存。可以在Vercel上利用其内置的边缘缓存,或者使用
Next.js的revalidate选项来设置静态生成(SSG)或增量静态再生(ISR)的过期时间,大幅减少数据库查询和计算开销。 - 日志与监控:在关键的流程节点,如数据解析错误、数据库写入失败时,添加日志记录。Vercel集成了对Serverless Function日志的查看功能。对于更复杂的监控,可以考虑接入像Sentry这样的错误跟踪服务。
6. 扩展思路与未来可能性
ClawRank目前的定位非常清晰和聚焦,这在项目初期是巨大的优点。但作为一个开放项目,它留下了许多令人兴奋的扩展接口。
6.1 支持多数据源
当前架构已经为多数据源做好了准备。要支持一个新的AI桌面应用(比如“ChatBox”、“LobeChat”),你需要:
- 在
src/adapters/下新建一个目录,例如chatbox/,实现一个能解析该应用日志文件的适配器。 - 在
src/ingestion/下同样新建目录,实现从该应用的原始消息到“每日Agent事实”的转换逻辑。不同应用的会话结构不同,转换规则也会不同。 - 新增一个对应的API路由,例如
/api/ingest/chatbox,或者扩展现有的/api/submit接口,通过一个source参数来区分。
领域层和持久层完全不需要改动,这就是清晰架构带来的好处。
6.2 实现Agent认领与认证流程
目前数据是自动收集的。未来可以增加一个功能,允许Agent的开发者或维护者“认领”自己的Agent。这需要:
- 一个简单的用户系统(可以基于GitHub OAuth等第三方登录)。
- 一个“认领”流程:用户证明自己与某个
agentSlug的关联(例如,通过在该Agent的官方文档中添加一个特定的验证码)。 - 认领后,该用户可以获得更详细的数据分析看板,或许还能设置Agent的展示信息(如头像、描述、官网链接)。
/api/submit接口可以升级,支持经过认证的用户提交更丰富的数据,或者覆盖自动收集的数据(用于数据修正)。
6.3 丰富排行榜维度与算法
目前的排名逻辑可能相对简单(比如按会话次数)。可以引入更复杂的、加权计算的“分数”系统。
- 多维度指标:除了使用次数,可以考虑会话满意度(根据对话结束语判断)、任务复杂度(根据会话长度和模式判断)、用户评分(如果引入反馈机制)等。
- 趋势权重:给近期活跃度更高的Agent一定的权重加成,让排行榜更能反映“当前”谁最受欢迎。
- 分类排名:不再只有一个总榜,可以分出“编程助手榜”、“创意写作榜”、“数据分析榜”等,这需要在对会话内容进行更深入的意图识别后才能实现。
这些更复杂的计算可以放在领域层的LeaderboardAggregator服务中实现,通过配置化的方式允许动态调整算法。
7. 常见问题与排查实录
在实际搭建和运行ClawRank的过程中,你可能会遇到一些问题。以下是我遇到的一些典型情况及其解决方法。
7.1 数据摄取失败或无数据
问题:运行pnpm pilot:ingest后,data/clawrank-pilot.json文件为空或没有新数据。
排查步骤:
- 检查环境变量:确认
.env.local中的OPENCLAW_SESSIONS_INDEX路径是否正确。可以使用cat $OPENCLAW_SESSIONS_INDEX命令(在终端中先export一下变量)看看文件是否存在且内容是否有效JSON。 - 检查文件权限:确保Node.js进程有权限读取该会话文件。
- 查看日志:在
ingestion逻辑中添加一些console.log,打印出解析到的原始消息数量,看看是解析环节出错还是转换环节过滤掉了所有数据。 - 验证适配器:单独运行
src/adapters/openclaw的解析函数,传入一个已知的会话文件,看输出是否符合预期。
7.2 本地开发服务器无法启动或API 404
问题:pnpm dev成功,但访问localhost:3000白屏或访问/api/leaderboard返回404。
排查步骤:
- 检查Next.js版本与配置:确保项目依赖的
next版本与代码兼容。检查next.config.js是否有特殊的路由重写或重定向配置。 - 检查API路由文件:确认
pages/api/目录下(或app/api/目录,取决于项目是Pages Router还是App Router)是否存在leaderboard.ts等文件。文件名必须与路由路径匹配。 - 查看终端错误:
pnpm dev的终端输出通常会有编译错误或运行时错误的堆栈信息,这是最重要的调试线索。
7.3 数据库连接错误
问题:配置了DATABASE_URL后,运行应用或执行pnpm db:seed时出现数据库连接错误。
排查步骤:
- 验证连接字符串:仔细检查
DATABASE_URL的每一个部分:协议、用户名、密码、主机、端口、数据库名。特别是密码中的特殊字符可能需要URL编码。 - 测试网络连通性:使用
psql命令行工具或像pgAdmin这样的图形客户端,尝试用相同的连接信息手动连接数据库,确保数据库服务正在运行且可从你的开发机访问。 - 检查SSL模式:一些云数据库(包括Neon)默认要求SSL连接。在本地开发时,你可能需要在连接字符串后添加
?sslmode=require参数,或者在Drizzle的配置中明确SSL选项。 - 查看Drizzle配置:检查
src/db/index.ts或类似的文件中,创建Drizzle客户端时传入的配置项是否正确。
7.4 Open Graph图片生成乱码或布局错乱
问题:分享链接时,生成的预览图文字显示为方框(乱码)或布局不对。
排查步骤:
- 字体文件问题:这是最常见的原因。确保用于OG图片生成的字体文件(.ttf)被正确放置在项目内(如
public/fonts目录),并且在生成图片的API代码中,字体文件的加载路径是相对于项目根目录的绝对路径(在Vercel Serverless环境中),或者是通过fs.readFileSync读取的完整路径。不要使用相对路径。 - 尺寸与样式:检查图片生成代码中设置的
width、height以及各个文本元素的x、y坐标、fontSize、lineHeight等样式属性。在不同尺寸下测试渲染效果。 - 使用调试工具:可以临时修改OG图片API,将其输出为HTML而不是图片,这样可以在浏览器中直观地查看渲染前的元素布局,便于调试。
这个项目从构思到实现,最深的体会是:清晰的边界定义是软件可维护性的基石。将系统严格划分为适配器、摄取、领域、持久化等层,每一层只做一件事,并且通过明确的接口通信,使得每一部分都可以独立开发、测试和替换。当你需要新增一个数据源,或者修改排名算法时,这种架构的优势就会淋漓尽致地体现出来——你几乎不会碰到“牵一发而动全身”的困境。