Skip to content

kingfs/llm-tracelab

Repository files navigation

llm-tracelab

Go Version License

中文说明 | English

llm-tracelab 是一个本地优先的 LLM HTTP 录制与回放代理。它当前覆盖 OpenAI-compatible、Anthropic Messages、Google GenAI 和 Vertex-native 这几类主流协议面。核心目标很直接:

  • 开发时把真实大模型 HTTP 请求录下来
  • 单元测试时直接回放,不再依赖外网和真实模型
  • 让测试更稳定、更快、更省钱

项目定位接近 http record/replay,但针对 LLM 场景补了流式响应、Token usage、可视化查看和故障注入能力。

当前版本发布说明

这次重构后的版本重点有四个:

  • pkg/llm 升级为按 provider/endpoint 工作的 adapter 层,统一处理 request、response、stream transcript 和 usage pipeline
  • Monitor 改成 Go embed 的 React UI,列表页异步分页,详情页支持 timeline / summary / raw protocol
  • Monitor 首页支持 Sessions / Requests 双视角,可按 session_id 等线索聚合相关请求
  • SQLite 数据库改用稳定 trace_id,不再在 URL 中暴露本地路径
  • LLM_PROXY_V3# event: 现在不仅有 request/response 基础事件,还会落 llm.* provider timeline

适合什么场景

  • 给 SDK 或业务代码做高可靠单元测试
  • 复现线上 prompt / tool call / stream 问题
  • 统计模型调用耗时、TTFT、Token 消耗
  • 在本地做 LLM API 代理调试和混沌测试

核心能力

  • 透明代理 OpenAI compatible 请求
  • 将一次请求/响应保存为本地 .http cassette
  • 使用 pkg/replay.Transport 在测试中直接回放
  • Monitor 页面查看请求详情、统一 timeline、原始协议和 Token 消耗
  • Trace Monitor 支持按单请求查看,也支持按 session 聚合查看相关请求
  • 使用 SQLite 维护 metadata 索引,避免统计页每次全量读文件
  • 支持对旧版 V2 记录文件兼容读取

项目结构

cmd/server            服务入口
internal/proxy        代理转发、stream 注入、响应拦截
internal/recorder     .http 录制与落盘
internal/store        SQLite 元数据索引
internal/monitor      Monitor UI 与详情解析
pkg/recordfile        录制文件格式 V2/V3 解析与 V3 写入
pkg/replay            单元测试回放 Transport
pkg/llm               多厂商请求/响应归一化

更适合 AI 阅读的项目约定见 AGENTS.md,当前项目基线摘要见 docs/PROJECT_BASELINE.md,v1 产品与架构设计入口见 docs/v1/README.md,Monitor 使用说明见 docs/MONITOR_GUIDE.md,系统事件中心设计见 docs/SYSTEM_EVENTS_DESIGN.md,Proxy 调用示例见 docs/PROXY_USAGE_EXAMPLES.md,MCP 使用说明见 docs/MCP_GUIDE.md,维护者实现基线见 docs/MAINTAINER_BASELINE.md,架构摘要见 docs/ARCHITECTURE.md,上游兼容矩阵见 docs/UPSTREAM_PROVIDERS.md,多 upstream 路由设计说明见 docs/MULTI_UPSTREAM_PLAN.md,项目路线图见 docs/ROADMAP.md,Vertex 协议族设计说明见 docs/VERTEX_NATIVE_PLAN.md

面向 AI agent 演进闭环的里程碑规划见 docs/AGENT_EVOLUTION_ROADMAP.md。 当前分支上已落地的 AI agent 闭环摘要见 docs/AI_BRANCH_BASELINE.md

录制文件与索引

当前写入格式是 LLM_PROXY_V3

  1. 文件前导包含紧凑元数据行,而不是固定 2KB 占位行
  2. 原始 HTTP request/response 仍然完整保留,方便人工排查
  3. # event: 会记录统一 timeline,例如 llm.output_text.deltallm.reasoning.deltallm.tool_callllm.usage
  4. 请求摘要、耗时、Token、trace id,以及可提取的 session_id 等聚合字段会同步索引到 llm_tracelab.sqlite3

默认存储布局:

logs/
  llm_tracelab.sqlite3
  <upstream-host>/<model>/<yyyy>/<mm>/<dd>/*.http

快速开始

1. 配置服务启动参数

v1 起,推荐把 YAML 限定为服务启动配置:端口、数据库、trace 输出目录、认证、MCP、router 策略等。模型渠道和模型启停应通过 Monitor Web 管理,并持久化到 SQLite。

config/config.yaml 是提交到仓库的默认样例配置,不放真实密钥。生产部署可以继续用环境变量覆盖端口、数据库、输出目录等启动参数。

推荐的基础配置结构如下:

server:
  port: "8080"

monitor:
  port: "8081"

mcp:
  enabled: true
  path: "/mcp"

database:
  driver: "sqlite"
  dsn: "" # 默认 {{trace.output_dir}}/llm_tracelab.sqlite3
  max_open_conns: 4
  max_idle_conns: 4
  auto_migrate: true

auth:
  session_ttl: 24h

trace:
  output_dir: "./logs"

router:
  model_discovery:
    enabled: true
    refresh_interval: 10m
    startup_policy: "best_effort"
  selection:
    policy: "p2c"
    epsilon: 0.02
    open_window: 15s
    failure_threshold: 3
  fallback:
    on_missing_model: "reject"

debug:
  output_dir: "./logs"
  mask_key: false

历史 upstream / upstreams YAML 仍然兼容,但只建议作为首次启动 bootstrap 或迁移入口使用。当 SQLite 中已经存在 channel 配置时,运行时以数据库为准,不再持续同步 YAML upstreams。导入后的渠道会在 Monitor 中标记为 bootstrap,之后请在 Web 中编辑、探测、启用或禁用模型。

兼容的 bootstrap 示例:

upstream:
  base_url: "https://api.openai.com/v1"
  api_key: "$env:LLM_API_KEY"
  provider_preset: "openai"

如果你不想从零开始写 bootstrap 配置,可参考这些现成样例;长期配置仍建议在 Monitor Web 中完成:

支持的环境变量覆盖:

  • LLM_TRACELAB_SERVER_PORT
  • LLM_TRACELAB_MONITOR_PORT
  • LLM_TRACELAB_DATABASE_DRIVER
  • LLM_TRACELAB_DATABASE_DSN
  • LLM_TRACELAB_DATABASE_MAX_OPEN_CONNS
  • LLM_TRACELAB_DATABASE_MAX_IDLE_CONNS
  • LLM_TRACELAB_DATABASE_AUTO_MIGRATE
  • LLM_TRACELAB_TRACE_OUTPUT_DIR
  • LLM_TRACELAB_AUTH_SESSION_TTL
  • LLM_TRACELAB_MCP_ENABLED
  • LLM_TRACELAB_MCP_PATH
  • LLM_TRACELAB_UPSTREAM_BASE_URL
  • LLM_TRACELAB_UPSTREAM_API_KEY
  • LLM_TRACELAB_UPSTREAM_PROVIDER_PRESET
  • LLM_TRACELAB_UPSTREAM_PROTOCOL_FAMILY
  • LLM_TRACELAB_UPSTREAM_ROUTING_PROFILE
  • LLM_TRACELAB_UPSTREAM_API_VERSION
  • LLM_TRACELAB_UPSTREAM_DEPLOYMENT
  • LLM_TRACELAB_UPSTREAM_PROJECT
  • LLM_TRACELAB_UPSTREAM_LOCATION
  • LLM_TRACELAB_UPSTREAM_MODEL_RESOURCE
  • LLM_TRACELAB_OUTPUT_DIR
  • LLM_TRACELAB_MASK_KEY

兼容旧命名的 LLM_TRACELAB_UPSTREAM_* 仍会覆盖第一个 bootstrap upstream target,适合单默认上游迁移。更复杂的多渠道生产配置请在 Monitor Web 中管理。

访问控制说明:

  • database 是统一的结构化数据存储,承载用户、API token、trace index、session、upstream、dataset 和 eval 元数据;SQLite 默认路径是 trace.output_dir/llm_tracelab.sqlite3
  • 首次启动前先初始化用户:go run ./cmd/server auth init-user -c config/config.yaml --username admin --password 'change-me-123'
  • Monitor UI 使用用户名密码登录;登录后可以在 UI 的 Tokens 页面为当前用户生成个人 API token。
  • 同一个个人 token 可用于 LLM proxy API 和 MCP,请求头为 Authorization: Bearer <token>
  • Channels / Models 通过 Monitor Web 管理并写入 SQLite;YAML 不再作为长期渠道配置入口。

MCP Server

如果你希望 AI agent 直接查询本地 traces / sessions / upstreams,而不是抓取 Monitor HTML,可以在主服务里启用 MCP streamable HTTP 端点:

go run ./cmd/server serve -c config/config.yaml

当前 MCP server 基于官方 github.com/modelcontextprotocol/go-sdk,挂在 monitor.port 对应的 HTTP 服务下,默认路径是 /mcp,例如 http://localhost:8081/mcp。工具面包括:

  • list_traces
  • get_trace
  • list_sessions
  • list_upstreams
  • query_failures
  • summarize_failure_clusters

MCP 与 proxy 复用同一套个人 token,客户端需要携带 Authorization: Bearer <token>

详细说明见 docs/MCP_GUIDE.md

推荐的兼容配置思路:

  • OpenAI / OpenRouter / Fireworks / Together / DeepSeek / Groq 等 OpenAI-compatible 服务:只设置 provider_presetbase_url,并确保 base_url 已包含上游 API 前缀,例如 /v1/api/v1/openai/openai/v1
  • Azure OpenAI /openai/v1/...:设置 provider_preset: azure,可选 api_version
  • Azure deployment 路由:设置 provider_preset: azure,并补 deployment
  • vLLM OpenAI-compatible server:设置 provider_preset: vllm
  • Anthropic Messages API:设置 provider_preset: anthropic,如需 beta 能力可在 headers 里补 anthropic-beta
  • Google GenAI API:设置 provider_preset: google_genai,当前支持 generateContentstreamGenerateContent 基础闭环
  • Vertex AI native API:优先使用 provider_preset: vertex;它会根据 base_url 推断 vertex_expressvertex_project_location

支持级别说明:

  • verified:已有行为测试或 cassette 级回归覆盖
  • compatible:按现有协议族抽象应当可工作,但直接验证较少
  • planned:尚未接入 preset 或尚未实现

配置校验规则:

  • provider_presetprotocol_familyrouting_profile 不再是松散字段
  • 无效组合会在启动时直接报错,而不是等到请求阶段才失败
  • 例如 provider_preset: anthropic 搭配 protocol_family: google_genai 会直接失败
  • 例如 provider_preset: openrouter 搭配 routing_profile: azure_openai_v1 也会直接失败

当前推荐支持矩阵:

  • provider_preset: openai support: verified protocol_family: openai_compatible routing_profile: openai_default
  • provider_preset: openrouter | fireworks | together | deepseek | groq | moonshot | cerebras | perplexity support: openrouter/fireworks/together/groq=verified; deepseek/moonshot/cerebras/perplexity=compatible protocol_family: openai_compatible routing_profile: openai_default
  • provider_preset: azure support: verified protocol_family: openai_compatible routing_profile: azure_openai_v1azure_openai_deployment
  • provider_preset: vllm support: verified protocol_family: openai_compatible routing_profile: vllm_openai
  • provider_preset: anthropic support: verified protocol_family: anthropic_messages routing_profile: anthropic_default
  • provider_preset: google_genai | google | gemini support: verified protocol_family: google_genai routing_profile: google_ai_studio
  • provider_preset: vertex support: verified protocol_family: vertex_native routing_profile: vertex_express | vertex_project_location notes: 受控 preset;已覆盖 adapter / proxy / cassette regression

Anthropic 示例:

下面仍用 YAML 展示字段含义,主要用于历史配置兼容和首次 bootstrap 参考;新建和长期维护渠道请在 Monitor Web 的 Channels 表单中配置同名字段。

upstream:
  base_url: "https://api.anthropic.com"
  api_key: "sk-ant-xxx"
  provider_preset: "anthropic"
  api_version: "2023-06-01"
  headers:
    anthropic-beta: "tools-2024-04-04"

Google GenAI 示例:

upstream:
  base_url: "https://generativelanguage.googleapis.com"
  api_key: "AIza..."
  provider_preset: "google_genai"

Vertex express 示例:

upstream:
  base_url: "https://aiplatform.googleapis.com"
  api_key: "ya29..."
  provider_preset: "vertex"
  model_resource: "publishers/google/models/gemini-2.5-flash"

Vertex project/location 示例:

upstream:
  base_url: "https://us-central1-aiplatform.googleapis.com"
  api_key: "ya29..."
  provider_preset: "vertex"
  project: "demo-project"
  location: "us-central1"
  model_resource: "publishers/google/models/gemini-2.5-flash"

如果你想完全避免 preset,也仍然可以继续显式填写:

  • protocol_family: vertex_native
  • routing_profile: vertex_express | vertex_project_location

Azure deployment 示例:

upstream:
  base_url: "https://demo-resource.openai.azure.com"
  api_key: "azure-key"
  provider_preset: "azure"
  deployment: "gpt-4o-mini"
  api_version: "2025-03-01-preview"

2. 构建和运行

推荐使用 go-task

task build
task run
task migrate

默认读取 config/config.yaml。也可以显式指定:

CONFIG=config/examples/openai.yaml task run

如果只想直接运行:

go run ./cmd/server -c config/config.yaml

把你的 SDK base_url 指向 http://localhost:8080/v1 后,请求就会被代理并录制。 Proxy API 要求携带个人 token;OpenAI-compatible SDK 通常会把 api_key 发送为 Authorization: Bearer <api_key>,因此 SDK 的 api_key 应填写 Monitor Tokens 页面生成的 llm-tracelab token。

curl 调用示例:

export LLM_TRACELAB_TOKEN=llmtl_xxx
curl -H "Authorization: Bearer ${LLM_TRACELAB_TOKEN}" \
  http://localhost:8080/v1/models | jq

更多非流式、流式和 SDK 示例见 docs/PROXY_USAGE_EXAMPLES.md

3. 打开 Monitor

访问 http://localhost:8081,使用初始化的用户名密码登录。请求列表、session 聚合和 trace 详情都需要登录后访问,避免未授权用户看到录制的 LLM 请求内容。

详情页现在包含三个主视图:

  • Timeline:消费 cassette 中的统一 llm.* 事件
  • Summary:按对话、工具、输出块聚合展示
  • Raw Protocol:左右分栏查看原始 request/response

老日志迁移与索引重建

显式迁移命令:

go run ./cmd/server migrate -c config/config.yaml

这个命令默认会做两件事:

  • 将旧的 LLM_PROXY_V2 .http 文件原地改写成 LLM_PROXY_V3
  • 清空并重建 llm_tracelab.sqlite3 中的 trace 索引数据,不会删除用户和 token

如果只想做其中一部分:

go run ./cmd/server migrate -c config/config.yaml -rewrite-v2=false
go run ./cmd/server migrate -c config/config.yaml -rebuild-index=false

适合老日志目录批量升级,或者 SQLite 数据库损坏/丢失后的全量恢复。

Docker / Compose

容器内约定的标准路径:

  • 可执行文件:/app/bin/llm-tracelab
  • 配置文件:/app/config/config.yaml
  • 数据目录:/app/data/traces
  • SQLite 数据库:/app/data/traces/llm_tracelab.sqlite3

默认提供:

启动方式:

export LLM_TRACELAB_UPSTREAM_API_KEY=sk-xxx
docker compose up --build
docker compose exec llm-tracelab /app/bin/llm-tracelab auth init-user -c /app/config/config.yaml --username admin --password 'change-me-123'

然后访问 http://localhost:8081,使用用户名密码登录,在 Tokens 页面生成用于 SDK / MCP 的个人 token。 SDK 调用 proxy 时把这个 token 作为 SDK API key;直接 curl 时使用 Authorization: Bearer <token>

如果只想直接使用已经发布到 Docker Hub 的镜像,可以不克隆仓库,直接运行:

docker run --rm \
  -p 8080:8080 \
  -p 8081:8081 \
  -e LLM_TRACELAB_UPSTREAM_BASE_URL=https://api.openai.com/v1 \
  -e LLM_TRACELAB_UPSTREAM_API_KEY=sk-xxx \
  -e LLM_TRACELAB_OUTPUT_DIR=/app/data/traces \
  -e LLM_TRACELAB_TRACE_OUTPUT_DIR=/app/data/traces \
  -e LLM_TRACELAB_SERVER_PORT=8080 \
  -e LLM_TRACELAB_MONITOR_PORT=8081 \
  -v "$(pwd)/docker-data:/app/data" \
  kingfs/llm-tracelab:latest serve -c /app/config/config.yaml

如果你更习惯 docker compose,也可以直接引用 Docker Hub 镜像:

services:
  llm-tracelab:
    image: kingfs/llm-tracelab:latest
    ports:
      - "8080:8080"
      - "8081:8081"
    environment:
      LLM_TRACELAB_UPSTREAM_BASE_URL: https://api.openai.com/v1
      LLM_TRACELAB_UPSTREAM_API_KEY: ${LLM_TRACELAB_UPSTREAM_API_KEY}
      LLM_TRACELAB_OUTPUT_DIR: /app/data/traces
      LLM_TRACELAB_TRACE_OUTPUT_DIR: /app/data/traces
      LLM_TRACELAB_SERVER_PORT: "8080"
      LLM_TRACELAB_MONITOR_PORT: "8081"
    volumes:
      - ./config/config.yaml:/app/config/config.yaml:ro
      - ./docker-data:/app/data
    command: ["serve", "-c", "/app/config/config.yaml"]

如果本机访问 Go 官方模块代理较慢,可以在构建时直接传入 GOPROXY

GOPROXY=https://goproxy.cn,direct docker compose build

task docker:buildtask docker:up 使用同一套构建变量约定,会自动读取当前 shell 的 GOPROXYGOSUMDBHTTP_PROXYHTTPS_PROXYNO_PROXY(以及对应的小写变量)并传入 Docker build,无需额外改脚本:

GOPROXY=https://goproxy.cn,direct task docker:build
GOPROXY=https://goproxy.cn,direct task docker:up

如果只希望覆盖 Docker 构建阶段而不影响当前 shell,统一使用 DOCKER_BUILD_* 变量:

DOCKER_BUILD_GOPROXY=https://goproxy.cn,direct task docker:build
DOCKER_BUILD_GOPROXY=https://goproxy.cn,direct task docker:up

同样地,直接执行 docker compose build / docker compose up --build 时,也优先读取 DOCKER_BUILD_*,再回落到普通环境变量。

推荐约定:

  • 本地开发:优先设置 DOCKER_BUILD_GOPROXY;如果已经全局设置 GOPROXY,脚本也会自动兼容
  • CI / GitHub Actions:默认不设置,直接使用公开默认值 https://proxy.golang.org,direct
  • 如果公司网络还要求系统代理,优先设置 DOCKER_BUILD_HTTP_PROXY / DOCKER_BUILD_HTTPS_PROXY / DOCKER_BUILD_NO_PROXY;未设置时会回落到 HTTP_PROXY / HTTPS_PROXY / NO_PROXY

默认挂载:

  • ./config/config.yaml -> /app/config/config.yaml:ro
  • ./docker-data -> /app/data

运行镜像默认使用 root 用户启动。这是为了兼容最常见的 bind mount 场景,避免宿主机目录属主与容器内固定 UID/GID 不一致时出现 permission denied,例如无法创建 /app/data/traces

如果在容器外部配置,优先通过挂载配置文件和环境变量覆盖端口、数据库、输出目录等服务启动参数;渠道和模型配置请在 Monitor Web 中维护并写入 SQLite。debug.output_dir 建议始终指向容器内挂载卷中的固定路径。

开发命令

task fmt
task lint
task test
task build
task run
task migrate
task check
task docker:build
task docker:up

在单元测试中回放

func TestChat(t *testing.T) {
    tr := replay.NewTransport("testdata/chat.http")

    cfg := openai.DefaultConfig("fake-key")
    cfg.BaseURL = "http://localhost/v1"
    cfg.HTTPClient = &http.Client{Transport: tr}

    client := openai.NewClientWithConfig(cfg)
    resp, err := client.CreateChatCompletion(context.Background(), req)
    _ = resp
    _ = err
}

当前设计原则

  • .http cassette 是回放的事实来源
  • SQLite 只做 metadata 索引,不替代原始文件
  • 新文件写 V3,旧文件继续兼容读取
  • 尽量保持文件可读、测试离线、实现本地优先
  • provider 语义、stream transcript、usage 和 event timeline 尽量收敛在 pkg/llm

截图

  • Monitor 总览
  • 对话详情
  • SSE 原始流
  • 非流式响应

License

MIT

About

A proxy-based tool for tracing, recording, and replaying LLM API requests.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors