跳到主要内容

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.ymlopenlit 服务 ports + volumes、clickhouse 服务)。README 的 mermaid 把 SDK→Collector→ClickHouse←UI 画成同一回事(README.md:56-70)。

2.2 部件一句话职责表

部件一句话职责在哪个目录
SDK / instrumentation用 wrapt 给 50+ 个库打桩,把调用变 span/metric/eventsdk/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)
evalsLLM-as-a-Judge 评估;SDK 侧发 HTTP 到服务端评估引擎sdk/python/src/openlit/evals/(offline.py)
guard进程内护栏:在 LLM 调用前后(preflight/postflight)拦截sdk/python/src/openlit/guard/
rule-engineAND/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 controllerGo 写的节点级 agent,用 eBPF 做零代码发现/打桩,经 OpAMP 回报openlit-controller/(cmd/controller/internal/)

术语点破: OpAMP(Open Agent Management Protocol,开放 agent 管理协议)是 OTel 生态里「集中管控一批 Collector/agent」的协议;OpenLIT 的 controller 用它做 FleetHub 的车队管理(依据:README.md:52openlit-controller/DESIGN.md)。

2.3 主线走一遍(高层,不进代码)

一次 client.chat.completions.create() 的命运:

  1. 装配期——openlit.init() 先建好 OTel 三件套(trace/meter/event),再遍历所有 instrumentor 逐个 instrument(),给能 import 到的库打桩。
  2. 拦截——你调用被 wrapt 换成了包装函数:它开一条 span,记下请求属性(模型、参数)。
  3. 放行 + 计量——包装函数调用真正的原函数拿到响应,然后算 token/成本、把 span 属性补全、记 metric、发一条 event。
  4. 导出——span/metric/event 经 OTLP 发到 Collector 的 4318 端口。
  5. 落库——Collector 批处理后用 ClickHouse 导出器写进 otel_traces 等表。
  6. 展示——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-446setup_auto_guardsguard/_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._instrumentwrap_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.yamlexporters.clickhouse + service.pipelines)。面板再查这张表。

打桩机制的细节(为何用 wrapt、流式响应怎么办、_instrumentors.py 的映射表)见 01-instrumentation-core.md;成本/token 怎么算见 02-span-cost-metrics.md


5. 巧妙之处速览(全景层面能带走的)

只点「妙在哪」,展开留给后续章节:

  1. 零依赖的遥测主干。 SDK 侧只产标准 OTel 数据,服务端只是消费者之一——这让 OpenLIT 能无痛嵌进已有可观测栈,而不是又一个封闭 SaaS(依据:README.md:36,215-219)。
  2. 能 import 才打桩。 module_exists 逐段校验点分模块名,装了哪个库就只打哪个,50+ instrumentor 共存互不干扰(__init__.py:97-103,140)。
  3. controller_mode 防重复打桩。 当 OpAMP controller 已从节点侧注入观测,SDK 会自动禁掉一批「易重复」的 instrumentor,避免同一次调用被记两遍(__init__.py:75-94,166-181)。
  4. 护栏复用同一个 wrapt 挂点。 护栏不新开机制,而是在遥测已经劫持的同一个方法上再叠一层 preflight/postflight(guard/_integration.py:178)。
  5. semcov 是属性名的唯一真源。 所有 span 属性名都从 SemanticConvention 常量取,SDK 与服务端用同一套字典对齐,避免「发的字段」和「查的字段」对不上(semcov/__init__.py)。

6. 边界与局限(全景层面)

  • 本仓是 CE/OSS 开源版。 企业版(RBAC、审计、计费、席位等)不在此仓,住在私有 openlit-enterprisesrc/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. 阅读地图(接下来读哪章)

建议由浅入深顺序读;每章一句话说清讲什么:

顺序章节一句话
01instrumentation-core一行 init 的魔法:OTel 装配 + wrapt 自动打桩怎么做到零改动接入
02span-cost-metrics一次 LLM 调用如何变成 span/event/metric,token 与成本怎么算
03backend-collector-clickhouse遥测落库:OTel Collector → ClickHouse → 面板的服务端通路
04evaluationsLLM-as-a-Judge 评估:11 种类型与「上下文即真相」
05guardrailsGuardrails:preflight/postflight 的输入输出护栏
06rule-engineRule Engine:用 AND/OR 条件把 trace 属性映射到上下文/Prompt/评估配置

怎么选读:

  • 只想「接上看数据」→ 读 01 + 03。
  • 关心「一次调用被记了什么、花了多少钱」→ 读 02。
  • 做「质量与安全」(评判 + 拦截 + 动态配置)→ 读 04 → 05 → 06。

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

按符号名 grep 比按行号稳。全景层面的关键入口:

主题文件路径符号名
一行 init 入口sdk/python/src/openlit/__init__.pyinit
遍历打桩sdk/python/src/openlit/__init__.pyinstrument_if_available / get_all_instrumentors
controller 模式禁重复sdk/python/src/openlit/__init__.pyapply_controller_mode_defaults / CONTROLLER_MANAGED_DISABLED_INSTRUMENTORS
instrumentor 名→模块/类 映射sdk/python/src/openlit/_instrumentors.pyMODULE_NAME_MAP / get_all_instrumentors
wrapt 挂点(以 OpenAI 为例)sdk/python/src/openlit/instrumentation/openai/__init__.pyOpenAIInstrumentor._instrument
span 创建 + 响应处理sdk/python/src/openlit/instrumentation/openai/openai.pychat_completions / process_chat_response
OTel 三件套装配sdk/python/src/openlit/otel/setup_tracing / setup_meter / setup_events
语义约定常量sdk/python/src/openlit/semcov/__init__.pySemanticConvention
控制面 HTTP 调用sdk/python/src/openlit/__init__.pyget_prompt / get_secrets / evaluate_rule / eval
护栏第二遍 wraptsdk/python/src/openlit/guard/_integration.pysetup_auto_guards
Collector→ClickHouse 配置assets/otel-collector-config.yamlexporters.clickhouse / service.pipelines
部署编排docker-compose.ymlopenlit / clickhouse 服务
服务端面板 + APIsrc/client/src/app/api/*(prompt/vault/rule-engine/clickhouse)
OpAMP controlleropenlit-controller/cmd/controller / DESIGN.md