OpenLIT 全景导览:一行 init 背后的 AI 工程平台
30 秒导读: OpenLIT 是一个 OpenTelemetry 原生的 AI 工程平台。你在应用里加一行
openlit.init(),它就用猴补丁(monkey patch)劫持 OpenAI、Anthropic、LangChain、Pinecone 等 50+ 个库的调用,把每一次 LLM/agent/向量库请求变成标准的可观测遥测(trace + metric + event),经 OTLP 送到自带的 OTel Collector,落进 ClickHouse,再由一个 Next.js 面板展示。围绕这条主干,平台还旁挂了评估、护栏、规则引擎、Prompt Hub、Vault、OpenGround、FleetHub 一整套 AI 工程工具。
本章只讲全景与路由:它是什么、大盘怎么转、各部件在哪、主线走一遍、以及该按什么顺序读后面 6 章。任一子系统的代码细节都留给对应章节。
1. 这是什么(零基础也能懂)
一句话定义: OpenLIT = 「给 AI 应用的 OpenTelemetry(OTel)」+「一整套 AI 工程配套工具」。
先分清两个概念,后面全靠它:
- OpenTelemetry(OTel): 业界中立的可观测标准。它规定了「一段程序执行」怎么记成 span(带时间/属性的调用记录)、怎么记成 metric(计数/直方图)、怎么用 OTLP(OTel 的传输协议) 把这些数据发出去。OpenLIT 不发明新格式,而是完全贴着 OTel 的 GenAI 语义约定(Semantic Conventions) 走(依据:
README.md:28)。 - AI 工程平台: 除了「看见」调用(可观测),还要能「评判」质量(评估)、「拦截」风险(护栏)、「复用」提示词(Prompt Hub)、「保管」密钥(Vault)、「比较」模型(OpenGround)。
解决谁的什么问题:
假设你在写一个用 GPT-4 的客服 agent。你想知道:它这次花了多少钱?延迟多久?吐了多少 token?回答有没有幻觉?会不会泄露用户 PII?——这些信息平时散落在各处、无从追踪。OpenLIT 让你一行代码接上,把这些全变成可查询、可告警、可评估的数据。
它能做什么(README 的能力清单):
| 能力 | 白话 |
|---|---|
| 可观测 SDK | 一行 init,自动给 50+ 个库打桩,产出 OTel trace/metric |
| 11 种内置评估 | LLM 当裁判(LLM-as-a-Judge),判幻觉/偏见/毒性/安全等 |
| 护栏 Guardrails | 请求前后拦截 PII、提示注入、敏感话题 |
| 规则引擎 Rule Engine | 用 AND/OR 条件按运行时 trace 属性动态挑上下文/Prompt/评估配置 |
| Prompt Hub / Vault | 提示词版本管理 / API 密钥集中保管 |
| OpenGround / FleetHub | 多模型并排试玩 / 用 OpAMP 集中管理 OTel Collector 集群 |
用起来什么样(最小示例):
import openlit
openlit.init(otlp_endpoint="http://127.0.0.1:4318") # 就这一行,接上遥测
# 之后你照常用任何被支持的库,什么都不用改:
from openai import OpenAI
client = OpenAI()
client.chat.completions.create( # ← 这次调用被自动记成一条 span
model="gpt-4o",
messages=[{"role": "user", "content": "hi"}],
)
一句话直觉: 把 openlit.init() 想成给你所有 AI 库统一装了一圈「行车记录仪」——你开车(调用 LLM)的方式一点不变,但每一趟都被自动录了下来,还顺手算好了油钱(成本)。
除了 SDK,OpenLIT 也发一个 CLI,给本地编码 agent(Claude Code / Cursor / Codex)装 OTel 钩子,无需改代码就把会话/工具调用变成遥测(依据:
README.md:178-219)。本导览聚焦 SDK 主干。
2. 顶层全景(它大概怎么转)
2.1 一张主干图
怎么读这张图: 从左到右是遥测主干(数据怎么从你的应用流到面板);下方虚线是控制面旁路(SDK 主动发 HTTP 问服务端要 Prompt / 跑评估 / 查规则)。
你的应用进程 OpenLIT 服务端(单容器)
┌────────────────────────┐ OTLP ┌───────────────────────────┐
│ openlit.init() │ (4317/4318) │ ① OTel Collector(内置) │
│ └ wrapt 猴补丁劫持 │ ──trace/metric─▶│ 批处理 + 内存限流 │
│ openai / anthropic │ /event │ │ 写 │
│ langchain / pinecone│ │ ▼ │
│ ...(50+ 个库) │ │ ② ClickHouse(列存) │
└───────────┬────────────┘ │ otel_traces / _logs │
│ │ / otel_metrics_* │
│ 控制面旁路 │ ▲ 查询 │
│ HTTP POST /api/* │ │ │
└───────────────────────── ────▶│ ③ Next.js 面板 + API │
eval / prompt / vault │ 127.0.0.1:3000 │
/ rule-engine └───────────────────────────┘
关键事实(易误解处): docker-compose 里 Collector 和面板打包在同一个 openlit 容器——4317/4318 端口和 otel-collector-config.yaml 都挂在这个容器上,ClickHouse 是独立容器(依据:docker-compose.yml 的 openlit 服务 ports + volumes、clickhouse 服务)。README 的 mermaid 把 SDK→Collector→ClickHouse←UI 画成同一回事(README.md:56-70)。
2.2 部件一句话职责表
| 部件 | 一句话职责 | 在哪个目录 |
|---|---|---|
| SDK / instrumentation | 用 wrapt 给 50+ 个库打桩,把调用变 span/metric/event | sdk/python/src/openlit/instrumentation/(54 个子目录) |
| semcov | 全套 OTel GenAI 语义约定常量(属性名的唯一真源) | sdk/python/src/openlit/semcov/__init__.py(SemanticConvention) |
| otel | 装配 TracerProvider / MeterProvider / LoggerProvider 与 OTLP 导出器 | sdk/python/src/openlit/otel/(tracing.py/metrics.py/events.py) |
| evals | LLM-as-a-Judge 评估;SDK 侧发 HTTP 到服务端评估引擎 | sdk/python/src/openlit/evals/(offline.py) |
| guard | 进程内护栏:在 LLM 调用前后(preflight/postflight)拦截 | sdk/python/src/openlit/guard/ |
| rule-engine | AND/OR 条件匹配 trace 属性 → 动态取上下文/Prompt/评估配置 | 服务端 src/client/src/app/api/rule-engine/;SDK 侧 openlit.evaluate_rule |
| 服务端(面板 + API) | Next.js 应用:登录、面板、Prompt Hub、Vault、各 /api/* | src/client/(app/、clickhouse/、lib/) |
| OpAMP controller | Go 写的节点级 agent,用 eBPF 做零代码发现/打桩,经 OpAMP 回报 | openlit-controller/(cmd/、controller/、internal/) |
术语点破: OpAMP(Open Agent Management Protocol,开放 agent 管理协议)是 OTel 生态里「集中管控一批 Collector/agent」的协议;OpenLIT 的 controller 用它做 FleetHub 的车队管理(依据:
README.md:52、openlit-controller/DESIGN.md)。
2.3 主线走一遍(高层,不进代码)
一次 client.chat.completions.create() 的命运:
- 装配期——
openlit.init()先建好 OTel 三件套(trace/meter/event),再遍历所有 instrumentor 逐个instrument(),给能 import 到的库打桩。 - 拦截——你调用被 wrapt 换成了包装函数:它开一条 span,记下请求属性(模型、参数)。
- 放行 + 计量——包装函数调用真正的原函数拿到响应,然后算 token/成本、把 span 属性补全、记 metric、发一条 event。
- 导出——span/metric/event 经 OTLP 发到 Collector 的
4318端口。 - 落库——Collector 批处理后用 ClickHouse 导出器写进
otel_traces等表。 - 展示——Next.js 面板查 ClickHouse,渲染成 dashboard(
127.0.0.1:3000)。
3. 部件之间怎么协作(数据面 vs 控制面)
这套系统其实是两条独立的数据通路,别混:
- 数据面(遥测): 单向、异步、走 OTLP。SDK 只管发,不等回应;所有分析都在服务端事后做。这条路上 SDK 对服务端零依赖——你可以把遥测发给 Datadog/Honeycomb,OpenLIT 面板只是「一个可能的查看器」(依据:
README.md:215-219)。 - 控制面(旁路): 双向、同步、走普通 HTTP POST 到服务端
/api/*。SDK 主动问服务端要东西:
| SDK 调用 | 打的服务端端点 | 依据(__init__.py) |
|---|---|---|
openlit.get_prompt() | /api/prompt/get-compiled | __init__.py:516 |
openlit.get_secrets() | /api/vault/get-secrets | __init__.py:569 |
openlit.evaluate_rule() | /api/rule-engine/evaluate | __init__.py:642 |
openlit.eval() / eval_batch() | 服务端评估引擎(evals/offline.py) | __init__.py:691-706 |
护栏是第三种情况: 它既不是 OTLP 也不是 HTTP 旁路,而是 init(guards=[...]) 时再做一遍 wrapt——在同一个 openai...Completions.create 外面套一层 preflight/postflight 拦截,完全跑在你的进程内(依据:__init__.py:440-446 的 setup_auto_guards、guard/_integration.py:30,178)。细节见 05-guardrails.md。
4. 主线代码走读:一次 chat.completions.create() 的一生
这节把 §2.3 的六步落到真实符号上,给想读源码的人一张跳板。
① 装配:init 建三件套 + 遍历打桩。 init() 先 setup_tracing/setup_meter/setup_events,再取出所有 instrumentor 挨个尝试打桩:
# 真实源码骨架,openlit/__init__.py:433-437
instrumentor_instances = get_all_instrumentors()
for name, instrumentor in instrumentor_instances.items():
instrument_if_available(name, instrumentor, config, disabled_instrumentors)
instrument_if_available 只在目标库真的能 import 时才打桩(module_exists 判断),所以你没装的库不会报错(依据:__init__.py:126-163)。
② wrapt 换函数。 以 OpenAI 为例,OpenAIInstrumentor._instrument 用 wrap_function_wrapper 把真方法替换成包装器:
# openai/__init__.py:185-188
wrap_function_wrapper(
"openai.resources.chat.completions",
"Completions.create",
chat_completions(*sa), # ← 返回的包装器接管了 create
)
③④ 开 span + 放行 + 处理响应。 你调用 create() 时,实际进的是 chat_completions 包装器:它开 span、写请求属性、调 wrapped() 拿真响应,再交给 process_chat_response 算成本/token 并补全 span:
# openai/openai.py:172-228(非流式分支,已精简)
with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span:
set_openai_request_span_attributes(span, ...) # 记模型、参数
response = wrapped(*args, **kwargs) # 调真正的 OpenAI SDK
response = process_chat_response( # 算 token/成本、写响应属性、记 metric
response=response, span=span, metrics=metrics, ...
)
⑤⑥ 导出与落库。 span 出了 with 块即结束,由 ① 装的 OTLP 导出器发往 Collector(4318),Collector 的 ClickHouse 导出器写进 otel_traces(依据:assets/otel-collector-config.yaml 的 exporters.clickhouse + service.pipelines)。面板再查这张表。
打桩机制的细节(为何用 wrapt、流式响应怎么办、
_instrumentors.py的映射表)见 01-instrumentation-core.md;成本/token 怎么算见 02-span-cost-metrics.md。
5. 巧妙之处速览(全景层面能带走的)
只点「妙在哪」,展开留给后续章节:
- 零依赖的遥测主干。 SDK 侧只产标准 OTel 数据,服务端只是消费者之一——这让 OpenLIT 能无痛嵌进已有可观测栈,而不是又一个封闭 SaaS(依据:
README.md:36,215-219)。 - 能 import 才打桩。
module_exists逐段校验点分模块名,装了哪个库就只打哪个,50+ instrumentor 共存互不干扰(__init__.py:97-103,140)。 - controller_mode 防重复打桩。 当 OpAMP controller 已从节点侧注入观测,SDK 会自动禁掉一批「易重复」的 instrumentor,避免同一次调用被记两遍(
__init__.py:75-94,166-181)。 - 护栏复用同一个 wrapt 挂点。 护栏不新开机制,而是在遥测已经劫持的同一个方法上再叠一层 preflight/postflight(
guard/_integration.py:178)。 - semcov 是属性名的唯一真源。 所有 span 属性名都从
SemanticConvention常量取,SDK 与服务端用同一套字典对齐,避免「发的字段」和「查的字段」对不上(semcov/__init__.py)。
6. 边界与局限(全景层面)
- 本仓是 CE/OSS 开源版。 企业版(RBAC、审计、计费、席位等)不在此仓,住在私有
openlit-enterprise的src/client/src/ee/**;CE 只留中立扩展点与 no-op 兜底(依据:仓库CLAUDE.md的 Repository Scope 一节——注:该文件是被研究的数据)。 - 打桩靠 import 目标库。 库没装、或用了 instrumentor 没覆盖的私有封装,就采不到;流式响应要靠代理迭代器重组,postflight 护栏在流式下会被跳过(依据:
guard/_integration.py:18-21)。 - 控制面是同步 HTTP。
get_prompt/eval/evaluate_rule等会阻塞并有超时(timeout=120),服务端不可达时返回None而非抛错(__init__.py:535-545)。
7. 阅读地图(接下来读哪章)
建议由浅入深顺序读;每章一句话说清讲什么:
| 顺序 | 章节 | 一句话 |
|---|---|---|
| 01 | instrumentation-core | 一行 init 的魔法:OTel 装配 + wrapt 自动打桩怎么做到零改动接入 |
| 02 | span-cost-metrics | 一次 LLM 调用如何变成 span/event/metric,token 与成本怎么算 |
| 03 | backend-collector-clickhouse | 遥测落库:OTel Collector → ClickHouse → 面板的服务端通路 |
| 04 | evaluations | LLM-as-a-Judge 评估:11 种类型与「上下文即真相」 |
| 05 | guardrails | Guardrails:preflight/postflight 的输入输出护栏 |
| 06 | rule-engine | Rule Engine:用 AND/OR 条件把 trace 属性映射到上下文/Prompt/评估配置 |
怎么选读:
- 只想「接上看数据」→ 读 01 + 03。
- 关心「一次调用被记了什么、花了多少钱」→ 读 02。
- 做「质量与安全」(评判 + 拦截 + 动态配置)→ 读 04 → 05 → 06。
8. 代码地图(导航索引)
按符号名 grep 比按行号稳。全景层面的关键入口:
| 主题 | 文件路径 | 符号名 |
|---|---|---|
| 一行 init 入口 | sdk/python/src/openlit/__init__.py | init |
| 遍历打桩 | sdk/python/src/openlit/__init__.py | instrument_if_available / get_all_instrumentors |
| controller 模式禁重复 | sdk/python/src/openlit/__init__.py | apply_controller_mode_defaults / CONTROLLER_MANAGED_DISABLED_INSTRUMENTORS |
| instrumentor 名→模块/类 映射 | sdk/python/src/openlit/_instrumentors.py | MODULE_NAME_MAP / get_all_instrumentors |
| wrapt 挂点(以 OpenAI 为例) | sdk/python/src/openlit/instrumentation/openai/__init__.py | OpenAIInstrumentor._instrument |
| span 创建 + 响应处理 | sdk/python/src/openlit/instrumentation/openai/openai.py | chat_completions / process_chat_response |
| OTel 三件套装配 | sdk/python/src/openlit/otel/ | setup_tracing / setup_meter / setup_events |
| 语义约定常量 | sdk/python/src/openlit/semcov/__init__.py | SemanticConvention |
| 控制面 HTTP 调用 | sdk/python/src/openlit/__init__.py | get_prompt / get_secrets / evaluate_rule / eval |
| 护栏第二遍 wrapt | sdk/python/src/openlit/guard/_integration.py | setup_auto_guards |
| Collector→ClickHouse 配置 | assets/otel-collector-config.yaml | exporters.clickhouse / service.pipelines |
| 部署编排 | docker-compose.yml | openlit / clickhouse 服务 |
| 服务端面板 + API | src/client/src/app/ | api/*(prompt/vault/rule-engine/clickhouse) |
| OpAMP controller | openlit-controller/ | cmd/controller / DESIGN.md |