xxyyue/llm-observer-proxy-go
GitHub: xxyyue/llm-observer-proxy-go
该项目是一个基于 Go 的运行级 LLM 观察代理,通过内嵌 Bifrost 数据平面记录兼容 OpenAI 和 Anthropic 格式的 LLM 流量,为评估与编排平台提供零外部依赖的可观测性支持。
Stars: 2 | Forks: 0
# LLM Observer Proxy
[](https://github.com/xxyyue/llm-observer-proxy-go/actions/workflows/ci.yml)
LLM Observer Proxy 为每次评估或 agent 运行嵌入了一个 Bifrost data plane,并在稳定的 `run_id` 下存储其可观测的 LLM 流量。该观察器会记录请求、响应、流式输出、工具调用、token 使用情况、成本估算、错误以及 provider 可见的推理内容。它不需要 Python、LiteLLM、Docker 或独立的 Bifrost 进程。
它可以独立安装,并通过其 HTTP API 嵌入到任何评估平台、agent 运行器或编排服务中。
## 工作原理
存在两个 HTTP 层:
1. **控制平面** 默认在端口 `8790` 监听。编排器使用它来创建、检查和停止运行级别的 proxy。
2. 每个 **data plane** 在独立且自动分配的端口上监听。agent 将返回的 URL 用作兼容 OpenAI 或兼容 Anthropic 的 LLM endpoint。
一个控制平面进程可以使用 goroutine 管理多个并发的 data plane。停止 data plane 会保留其在磁盘上的历史记录,并且在控制平面重启后该历史记录依然可读。
该观察器只能记录上游 API 暴露的信息。它无法获取 provider 未返回的隐藏的思维链(chain-of-thought)。
## 环境要求
- Go 1.26.4 或更高版本,这是 Bifrost Core 的要求
- 上游 API key 和模型目录(model catalog)
- 具有绑定控制平面和 data plane 端口的本地权限
较旧的 Go 安装如果配置了 `GOTOOLCHAIN=auto`,可以自动下载 `go.mod` 中声明的工具链。
## 安装
从 GitHub 安装命令:
```
go install github.com/xxyyue/llm-observer-proxy-go/cmd/llm-observer-proxy@latest
```
仓库和模块路径名为 `llm-observer-proxy-go`;安装后的命令和服务名为 `llm-observer-proxy`。确保 `$(go env GOPATH)/bin` 包含在 `PATH` 中,然后验证安装:
```
llm-observer-proxy --version
```
对于源码检出,请运行或构建相同的命令:
```
go run ./cmd/llm-observer-proxy
go build -o llm-observer-proxy ./cmd/llm-observer-proxy
```
## 快速开始
内置的回退目录包含 `gpt-5.5`,并会从环境中读取 OpenAI 凭证:
```
export OPENAI_API_KEY=your-openai-api-key
llm-observer-proxy
```
在另一个终端中,检查控制平面:
```
curl http://127.0.0.1:8790/healthz
```
为运行创建一个 data plane:
```
curl -X POST http://127.0.0.1:8790/api/runs/run-001/proxy \
-H 'Content-Type: application/json' \
-d '{}'
```
该 data plane 会服务所选目录中的每个模型。每个 LLM 请求通过其自身的 `model` 字段来选择模型。创建响应包含一个动态分配的基础 `base_url`,例如 `http://127.0.0.1:45678`,以及 `openai_base_url` (`http://127.0.0.1:45678/v1`) 和 `anthropic_base_url` (`http://127.0.0.1:45678`)。为客户端 SDK 使用明确的 URL,或直接调用 endpoint:
```
curl http://127.0.0.1:45678/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "gpt-5.5",
"messages": [{"role": "user", "content": "Reply with one short sentence."}]
}'
```
读取标准化历史记录:
```
curl http://127.0.0.1:8790/api/runs/run-001/history
```
停止 data plane 但不删除其历史记录:
```
curl -X DELETE http://127.0.0.1:8790/api/runs/run-001/proxy
```
## 配置
该命令接受以下选项:
```
--host HOST Control-plane bind host (default: 127.0.0.1)
--port PORT Control-plane port (default: 8790)
--env-file PATH KEY=VALUE file loaded before startup (default: .env)
--artifact-root PATH Artifact storage root
--model-catalog PATH Model catalog JSON
--log-level LEVEL Bifrost log level
--version Print the build version
```
路径按以下顺序解析:
1. `--artifact-root` 和 `--model-catalog`
2. `LLM_OBSERVER_PROXY_ARTIFACT_ROOT` 和 `LLM_OBSERVER_PROXY_MODEL_CATALOG_PATH`
3. 工作目录中的 `./artifacts` 和 `./config/models.json`
4. 二进制文件中无内置密钥的嵌入式模型目录
相对路径根据进程工作目录进行解析。可选的 `.env` 文件会在基于环境的路径和凭证解析之前加载,且现有的进程环境变量优先级更高。
### Bifrost Data Plane
该服务嵌入了 Bifrost Core `v1.6.3` 以及官方的 OpenAI 和 Anthropic provider 包。它不会启动完整的 Bifrost 服务器、Docker 容器或其他进程。该 data plane 保留了公开的兼容 OpenAI 的 `/v1/*` 路由和兼容 Anthropic 的 `/v1/messages` 路由,并将精确的请求和响应 payload 记录在 `events.jsonl` 中,包括 Anthropic 的 system 内容块和 cache-control 标记。
HTTP 路径决定了请求的协议:`/v1/messages` 使用 Anthropic adapter,而其他 `/v1/*` 路径使用 OpenAI adapter。目录中的每个模型都可以通过任一格式接受,因此支持这两种格式的上游可以根据需要进行转换。
### 模型目录
模型目录具有以下结构:
```
{
"llm_observer_proxy": {
"base_url": "https://api.openai.com",
"api_key": "os.environ/OPENAI_API_KEY"
},
"models": [
{
"id": "gpt-5.5",
"upstream_api": "openai"
}
]
}
```
`id` 是 data plane 接受的公开模型名称。为了目录兼容性,`upstream_api` 必须是 `openai` 或 `anthropic`,但请求路径会在运行时选择 adapter。`pricing` 下可选的每百万 token 价格用于本地成本估算。其他模型属性将被忽略。`api_key` 接受文字值、`os.environ/NAME`、`env.NAME` 或 `${NAME}`。目前,目录中的所有模型共享同一个上游 `base_url` 和凭证。完整示例请参见 `config/models.example.json`。
当不存在 `llm_observer_proxy` 时,旧版的顶层目录键 `llm_proxy` 依然会被接受。
## 控制 API
| 方法 | 路径 | 用途 |
| --- | --- | --- |
| `GET` | `/healthz` | 检查控制平面健康状况 |
| `GET` | `/api/proxies` | 列出进程本地正在运行和已停止的 proxy |
| `POST` | `/api/runs/{run_id}/proxy` | 创建一个运行级别的 data plane |
| `GET` | `/api/runs/{run_id}/proxy` | 读取 proxy 状态 |
| `DELETE` | `/api/runs/{run_id}/proxy` | 停止一个 data plane |
| `GET` | `/api/runs/{run_id}/history` | 读取标准化历史记录 |
| `GET` | `/api/runs/{run_id}/conversation` | 读取最新对话 |
创建 payload 可以为空。可选的活动字段包括 `port` 和 `bind_host`。兼容性字段(包括 `model`、`mode`、`backend`、`litellm_config_path`、`litellm_env` 和 `strip_callbacks`)会被接受并忽略,未知字段也会被忽略。完整的请求和响应契约记录在 `docs/control-plane-api.md` 中。
## Artifacts
每次运行会写入:
```
artifacts/runs//platform/llm/
config.json
conversation_snapshot.json
events.jsonl
bifrost.log
```
`events.jsonl` 保留原始的请求和响应、流式传输主体、使用情况、工具调用、可见的推理内容、错误以及标准化摘要。`conversation_snapshot.json` 包含最新重构的对话。`config.json` 包含非机密的运行元数据,`bifrost.log` 作为兼容性路径被保留。
传入的 `Authorization` 和 `x-api-key` header 在 artifacts 中会被脱敏。请求和响应主体会被特意保留,其中可能包含敏感数据,因此请对 artifact 根目录应用适当的文件系统权限、访问控制和保留策略。
## 支持范围
受支持的 data plane 接口为兼容 OpenAI 的 `/v1/*` 路由和 Anthropic 的 `/v1/messages`,以及上述文档中提到的控制和历史 API。Bifrost 的管理服务器、管理 UI、数据库持久化、插件、治理功能、其他 provider 和 MCP runtime 不属于本项目兼容性契约的一部分。Bifrost 的共享 schema 包导入了 MCP 类型,因此即使没有配置或执行任何 MCP 代码,`mcp-go` 依然是一个传递性的编译依赖项。
## 故障排除
- **Proxy 创建返回 `400`:** 检查 run ID、请求的端口、绑定地址、目录以及文件系统权限。
- **Proxy 创建返回 `409`:** 该 `run_id` 已经有一个正在运行的 data plane。
- **LLM 请求失败:** 确认上游 `base_url`、凭证环境变量、请求路径和模型名称。Provider 错误会记录在 `events.jsonl` 中。
- **历史记录为空:** 确认 agent 使用的是返回的 data plane URL,而不是直接使用 provider 的 URL。
- **安装后未找到 `llm-observer-proxy`:** 将 `$(go env GOPATH)/bin` 添加到 `PATH` 中。
## 安全
控制 API 是一个管理接口:它可以启动 listener、选择监听端口并存储 LLM 流量。版本 0.1.x 没有内置身份验证。请将其绑定到 loopback 或受信任的专用网络,并在远程部署前阅读 `.github/SECURITY.md`。
历史记录可能包含 prompt、模型输出、工具输出和凭证。请对 artifact 根目录应用适当的文件系统权限、访问控制和保留策略。
## 开发
```
test -z "$(gofmt -l cmd internal)"
go mod tidy
git diff --exit-code -- go.mod go.sum
go vet ./...
go test -race ./...
go build -trimpath ./cmd/llm-observer-proxy
```
## 许可证
Apache-2.0。请参见 `LICENSE` 和 `NOTICE`。
## 作者
- Shuqiao Zhang
- Yue Xiong
标签:AI监控, DLL 劫持, EVTX分析, Go语言, LLM可观测性, Petitpotam, Python脚本, 大语言模型, 数据平面, 日志审计, 流量代理, 程序破解