跳到主要内容

Helicone — AI 网关与 LLM 可观测性平台:架构与原理

30 秒导读: Helicone 让你把 LLM 请求的 baseURL 换成它的域名(一行代码),它就站在你和 OpenAI/Anthropic/... 之间当"透明中转站":照常把请求转发给模型、把回答原样还给你,同时在旁路异步地把这次调用截下来——算钱、脱敏、拆结构,最后落到三个库里,变成你能查询、能按会话追踪、能打分评估的可观测数据。它既是可观测平台(看每一次调用花了多少、多慢、说了什么),也是AI 网关(一个 API key 通 100+ 模型,带路由和自动故障切换)。


1. 这是什么(零基础也能懂)

一句话定义: Helicone 是一个开源的 LLM 可观测性平台 + AI 网关——把它插在"你的应用"和"模型厂商"之间,零改动地记录并分析每一次 LLM 调用。

解决什么问题 / 给谁用。 假设你在做一个 AI 产品,调了 OpenAI、Anthropic、Gemini 十几个模型,散在几十个代码文件里。你想知道:这个月花了多少钱?哪个 prompt 最慢?某个用户昨天那条对话到底问了什么、模型怎么答的?一个 agent 跑了 20 步,是在哪一步跑偏的?自己搭一套日志+成本核算+存储+看板,是个大工程。Helicone 把这件事变成改一行 baseURL

它能做什么(功能):

能力白话
可观测(Observe)记录每次调用的请求正文、回答、延迟、token、成本
AI 网关(Gateway)一个 key 通 100+ 模型,统一 OpenAI 格式,带路由与自动故障切换
会话追踪(Sessions)把一个 agent / 多轮对话的多次调用串成一条 trace
打分与评估(Scores / Eval)给每条请求打分(人工或在线自动 eval),沉淀质量指标
成本核算内置 300+ 模型的价目表,按 token 反算美元成本
Prompt 管理、缓存、限流、告警生产数据驱动的 prompt 版本;命中缓存省钱;按 key 限速

用起来什么样。 只改 baseURL 和 key,其余照旧:

// 示意,来自 README 的最小用法
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://ai-gateway.helicone.ai", // 原本是 api.openai.com
apiKey: process.env.HELICONE_API_KEY,
});
const res = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "Hello!" }],
});
// 请求照常返回;这次调用已被记录,去 dashboard 就能看到

一句话直觉/类比: 把 Helicone 当成 LLM 流量的**"带记账功能的收费站"——车(请求)必须过站,过站时它抄下车牌、算过路费、拍张照存档,然后立刻放行**(记账在旁边异步做,不拦车)。这个"必经+旁路异步"就是整套系统的灵魂。

本节不谈实现。记住一件事:Helicone 的价值来自"你的请求本来就要经过它"这个位置——有了这个位置,才谈得上零改动地观测。


2. 顶层全景(它大概怎么转)

2.1 六个服务,各司其职

Helicone 由六个服务拼成。README 的原话是"五个服务"(README.md:88),但下面那张清单其实列了六项(Worker、Jawn、Web、ClickHouse、S3/MinIO、Postgres),docker/docker-compose.ymlsupervisord.conf 里也能逐个对上。用一张表看清谁干什么:

服务干什么在哪
Worker(边缘代理 / AI 网关)站在必经之路上,转发请求、旁路截获、投递队列worker/(Cloudflare Workers)
Jawn(消费/API 后端)从队列取消息,经责任链加工,落三处库;并对外提供查询 APIvalhalla/jawn/(Express + Tsoa)
Web(前端)看板:查请求、看会话、成本分析web/(Next.js)
ClickHouse分析库:海量请求行,支撑聚合/筛选/看板clickhouse/
S3 / MinIO大对象库:超限的请求-响应正文S3Client 访问
Postgres(Supabase)元数据与 Auth:组织、API key、配置supabase/

注:CLAUDE 里还提到一个 Rust 版 aigateway;本 clone 内没有该目录,worker 内自带 TypeScript 版 AI 网关(worker/src/lib/ai-gateway/SimpleAIGateway.ts:55)。本套文档只讲 clone 里真实存在的代码。

2.2 一条请求的一生(主线,不进代码)

怎么读这张图: 从左到右是"同步主链路"(用户在等的那条,必须快);从 Worker 往下垂的那条虚线是 ctx.waitUntil 异步旁路——用户拿到回答后才在后台慢慢跑。

┌──────────────────── 同步主链路(用户在等,要快)────────────────────┐
│ │
你的应用 ─────► ① Worker 边缘代理 ─────────────► LLM 厂商 ──────► ① Worker ──► 你的应用
(改了baseURL) · 判 WORKER_TYPE (OpenAI/…) · 组装回答 (拿到回答)
· 缓存/限流/安全检查 · ctx.waitUntil
╎ (异步旁路,不阻塞)

② 算成本 + 投队列
(Kafka / SQS / HTTP)

③ jawn 消费者:一条责任链
认证→读S3→拆请求/回答→
打prompt→在线eval→落库…

┌─────────────────┬─────────────────┐
▼ ▼ ▼
④ ClickHouse ④ S3 ④ Postgres
(分析·可查询) (大正文) (元数据)

主线一句话:必经拦截(①)→ 旁路算钱+投队列(②)→ 消费者责任链加工(③)→ 三处落库(④)。 这四步对应 01–04 章;第 05 章再讲在这行日志之上生长的 agent 可观测性(会话 / 打分 / 评估)。

2.3 两条接入路径:代理式 vs 异步日志

上图画的是代理式接入。其实数据交给 Helicone 有两条路,搞混是新手最大的坑,先分清:

维度代理式(Proxy / Gateway)异步日志(Async Logging)
谁在链路上关键路径,Worker 同步转发你的请求不在链路,你自己调模型,事后单独上报
你改什么baseURL 指向 Worker保留原调用,额外发一份日志给 jawn
典型手段AI Gateway、各家 proxy 入口(01 章)OpenLLMetry(OTEL)、自定义上报(05 章 TraceManager)
好处一处接入即得网关能力(路由 / fallback)请求不经过 Helicone,少一跳、少一个故障点
代价请求多走一跳边缘代理需要在自己代码里埋点上报

一句话选型: 想要"换模型 / 自动兜底"这类网关能力 → 走代理式;只想记录分析、不愿把生产流量引到第三方链路 → 走异步日志。两条路最终都汇进同一套 jawn 责任链和同三个库——所以下面各章讲的加工与落库,对两条路一视同仁。

2.4 两个设计支点(整套系统为什么这么搭)

  • 支点一:观测必须"零成本"于主链路。 记录一次调用要算成本、脱敏、拆 JSON、写三个库——这些绝不能让用户多等。所以 Worker 用 ctx.waitUntil(log(...)) 把记录甩到响应之后的后台(worker/src/lib/HeliconeProxyRequest/ProxyForwarder.ts:437),再用队列把"边缘侧的快"和"落库侧的重"解耦。
  • 支点二:加工步骤会不断变多,所以用责任链。 一条日志要经历认证、读 S3、拆请求、拆回答、prompt、在线 eval、写库、推 PostHog/Webhook…… jawn 把每一步做成一个 handler,用 setNext 串成链(valhalla/jawn/src/managers/LogManager.ts:104),加一步就插一个节点,互不搅扰。

3. 阅读地图(建议按顺序读)

本子库按"一条请求的一生"由浅入深拆成五章。索引(本页)负责全景;每章负责一段。 推荐顺序即下表顺序:

#章节讲什么读完你会知道
01边缘代理与 AI 网关:一次 LLM 请求怎么被截获并转发Worker 入口:靠 host 判 WORKER_TYPE,routerFactory 选对应 router,转发到厂商;AI 网关如何做路由与故障切换请求是怎么进来、按什么规则转发出去
02捕获、算成本、投队列:边缘侧如何不阻塞地留存每次调用ctx.waitUntil 异步旁路、DBLoggable 组装日志、用量→美元成本、HeliconeProducer 投 Kafka/SQS/HTTP观测为什么不拖慢你的请求
03消费与责任链:jawn 如何把一条队列消息加工成结构化日志consumeMiniBatchLogManager → 十几个 handler 串成的责任链,每步职责与顺序讲究一条原始消息怎么被加工成能入库的结构
04三处落库:ClickHouse 分析引擎 + S3 大对象 + Postgres 元数据存储三分法、"小正文进 ClickHouse / 大正文进 S3"的阈值决策、request_response_rmt 的 ReplacingMergeTree 设计与正文 TTL数据落在哪、为什么这么分、怎么查得快
05Agent 可观测性:会话追踪、打分与在线评估Sessions=靠 Helicone-Session-* 属性串起来的 trace;scores 独立队列;OnlineEvalHandler 在线打分agent 多步调用怎么被串成一条、怎么被评估

4. 巧妙之处(可带走的精华)

每条先说妙在哪,再给锚点。细节在对应章展开。

① "位置即产品":零改动来自站在必经之路上。 用户只换 baseURL,Worker 就能在 fetch 入口拿到整个请求(worker/src/index.ts:391)。没有 SDK 侵入、没有埋点——观测能力直接来自网络位置。这是所有代理型可观测工具的第一性设计。

ctx.waitUntil 把"重活"移出用户的等待。 组回答后立刻返回,记录在响应之后跑(worker/src/lib/HeliconeProxyRequest/ProxyForwarder.ts:437)。算成本、写库全在后台——主链路延迟≈纯转发延迟

③ 队列解耦"边缘的快"与"落库的重"。 边缘只负责把消息投出去,支持 Kafka(Upstash)/SQS/dual,甚至降级为直接 HTTP 打到 jawn(worker/src/lib/clients/producers/HeliconeProducer.ts:58)。落库侧再慢、再批量,也不反压用户。

④ 责任链让"加工步骤"可插拔。 认证→读S3→拆请求→拆回答→prompt→在线eval→写库→PostHog→Webhook… 每步一个 handler,setNext 串起来(valhalla/jawn/src/managers/LogManager.ts:104)。顺序有讲究(如"改属性的 handler 必须排在写库之前")。

⑤ 存储三分法:按正文大小选库。 小正文(≤10MB)直接内联进 ClickHouse 行,大正文才写 S3,免费额度超了则不存正文(valhalla/jawn/src/lib/handlers/LoggingHandler.ts:180)。热数据查得快,冷大对象不撑爆分析库。

⑥ ClickHouse 用 ReplacingMergeTree + 正文列 3 个月 TTL。 主表 request_response_rmtupdated_at 去重、按月分区,request_body/response_body 两列各带 3 个月 TTL 自动清理(clickhouse/migrations/schema_41_request_response_replacing_merge_tree.sql:1)。分析指标长期留、原文只留一季。

⑦ Sessions 不是新表,是"属性 + GROUP BY"。 会话追踪复用同一张 RMT:请求带 Helicone-Session-Id / Helicone-Session-Name 属性,查询时对 properties Map 做 GROUP BY 就聚成一条会话(valhalla/jawn/src/managers/SessionManager.ts:382)。零额外存储换来 agent 级视图。


5. 代码地图(导航索引)

给要读源码的人/agent 的跳转表。行号 as-of sourceCommit;行号会漂,符号名相对稳,失效时用符号名 grep

主题文件路径符号名
Worker 入口 fetchworker/src/index.ts:391default.fetch
按 host 判定 WORKER_TYPEworker/src/index.ts:51modifyEnvBasedOnPath
WORKER_TYPE → router 映射worker/src/routers/routerFactory.ts:26WORKER_MAP / buildRouter
代理转发主流程worker/src/lib/HeliconeProxyRequest/ProxyForwarder.ts:45proxyForwarder
异步旁路记录入口worker/src/lib/HeliconeProxyRequest/ProxyForwarder.ts:487log(经 ctx.waitUntil)
用量→美元成本(新/旧两路)worker/src/lib/HeliconeProxyRequest/ProxyForwarder.ts:563getUsageProcessor / modelCostBreakdownFromRegistry / costOfPrompt
AI 网关(路由+故障切换)worker/src/lib/ai-gateway/SimpleAIGateway.ts:55SimpleAIGateway(AttemptBuilder/AttemptExecutor)
组装日志消息worker/src/lib/dbLogger/DBLoggable.ts:704DBLoggable.log
投递到队列(Kafka/SQS/HTTP)worker/src/lib/clients/producers/HeliconeProducer.ts:28HeliconeProducer / MessageProducerFactory
异步日志(OTEL)入口valhalla/jawn/src/managers/traceManager.ts:187TraceManager.consumeTraces / sendLogToKafka
消费一个 mini-batchvalhalla/jawn/src/lib/consumer/consumeMiniBatch.ts:7consumeMiniBatch
组装并驱动责任链valhalla/jawn/src/managers/LogManager.ts:71LogManager.processLogEntries
责任链基类valhalla/jawn/src/lib/handlers/AbstractLogHandler.ts:10AbstractLogHandler / setNext
落库 handler(三库并行)valhalla/jawn/src/lib/handlers/LoggingHandler.ts:282LoggingHandler.handleResults
存储位置决策(CH/S3/不存)valhalla/jawn/src/lib/handlers/LoggingHandler.ts:180LoggingHandler.handle
ClickHouse 主表clickhouse/migrations/schema_41_request_response_replacing_merge_tree.sql:1request_response_rmt
S3 正文对象键valhalla/jawn/src/lib/shared/db/s3Client.ts:352getRequestResponseKey
会话聚合查询valhalla/jawn/src/managers/SessionManager.ts:382SessionManager.getSessions(GROUP BY Helicone-Session-Id)
打分消费入口valhalla/jawn/src/lib/consumer/consumeMiniBatchScores.ts:7consumeMiniBatchScores / ScoreManager.handleScores
在线评估 handlervalhalla/jawn/src/lib/handlers/OnlineEvalHandler.tsOnlineEvalHandler