# llmtester **Repository Path**: bigant/llmtester ## Basic Information - **Project Name**: llmtester - **Description**: 一个用来快速验证 LLM 接口连通性的桌面工具 - **Primary Language**: Unknown - **License**: LGPL-3.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-15 - **Last Updated**: 2026-10-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: LLM探针 ## README # 模型探针 一个用来快速验证 LLM 接口连通性的桌面工具。填上 Base URL、API Key 和模型名, 点一下就知道通不通、慢在哪、错在哪;另外带一个「鹈鹕测试」,用一句话就能 看出模型的图形能力。 支持三种协议:**OpenAI 兼容**、**Anthropic Messages**、**TypeSafe System One(Jev)**。 单文件可执行,UPX 压缩后 3.7 MB(未压缩 10.3 MB),不依赖 Node / Python / 浏览器运行时。 --- ## 它解决什么 调 LLM 接口时最费时间的往往不是写业务代码,而是「这个 key 到底能不能用」 「为什么 404 了」「是网的问题还是模型的问题」。这些事用 curl 也能做, 但要看懂 `MissingSessionID` 这样的错误、或者意识到 `/zen/go` 后面必须补 `/v1`,都得先踩一遍坑。 这个工具把这些坑都填好了,并且每次请求都记录完整轨迹,出问题时能直接看到 实际发出去的地址、请求头和响应体。 ## 快速开始 ```cmd git clone cd llmtester build.cmd ``` 产物在 `build\bin\llmtester.exe`,双击即可运行(Windows 10+,需要 WebView2 运行时,Win11 自带)。 开发模式: ```cmd wails dev ``` ## 功能 ### 连通性测试 一次请求,把结果拆开看: - **延迟分解条** —— 按真实耗时比例画出「TCP/TLS 连接 → 等待首字节 → 流式传输」 三段。灰色段长说明网络慢,浅青段长说明模型端在排队 - **四个读数** —— 连接 / 首字节 / 总计 / 输出 token 数 - **流式输出** —— 逐字显示模型返回,带打字光标 - **原始 JSON** —— 拼装回完整的响应结构,方便核对字段 错误按阶段分成六类(参数校验 / 网络 / HTTP / 响应解析 / 流中断 / 已取消), 每类给不同的界面反馈: | 阶段 | 覆盖 | |---|---| | 参数校验 | 空 Base URL、无主机名、协议不是 http(s)、URL 解析失败、空 Key、空 Model、空 Prompt —— 直接标红对应输入框,不占用结果区 | | 网络 | DNS 不存在、DNS 超时、连接被拒、连接重置、主机不可达、网络不可达、证书不受信任 / 域名不匹配 / 无效 / 已过期、TLS 握手失败、连接超时、首字节超时、整体超时、代理失败、连接提前关闭 | | HTTP | 400 / 401 / 403 / 404 / 405 / 408 / 413 / 422 / 429 / 500 / 502 / 503 / 504,每条都有针对性建议 | | 响应解析 | 返回 HTML、非 JSON、结构不符、200 里带 error 对象、内容为空、输出被截断、只思考没正文 | | 流中断 | 流中途断开(服务端提前关闭连接) | | 已取消 | 用户主动中止 | 服务端写了具体说明时,提示语会指向它而不是套用通用模板 —— 比如网关说 「免费模型需要绑定注册满一定时间的 GitHub 账号」,那比「请求太频繁」准确得多。 ### 接口协议 同一套界面支持三种协议,切换按钮在 Base URL 下面: | | OpenAI 兼容 | Anthropic Messages | Jev | |---|---|---|---| | 路径 | `/v1/chat/completions` | `/v1/messages` | `/v1/systemone` | | 鉴权 | `Authorization: Bearer` | `x-api-key` + `anthropic-version` | `Authorization: Bearer` | | 必填参数 | — | `max_tokens` | `state`、`questions` | | 请求体 | `messages: [{role, content}]` | 同左 | `state` + 一组带类型的问题 | | 响应 | `choices[].message.content` | `content[].text` | `answers: {id: {...}}` | | 输出 | 自由文本 | 自由文本 | 结构化判定(概率 + 置信度) | | 流结束 | `data: [DONE]` | `event: message_stop` | 无流式 | | token | `prompt_tokens` / `completion_tokens` | `input_tokens` / `output_tokens` | `input_tokens` / `output_tokens` | 选错协议时会提示切换,而不是只说「格式不对」。 #### 关于 Jev [Jev](https://docs.typesafe.ai/models) 是 TypeSafe 的 System One 模型,**不是对话模型**: 一次请求提交一段 `state`(要评估的内容)和一组带类型的问题,返回结构化的判定结果。 | 问题类型 | 含义 | 答案 | |---|---|---| | `noul` | 是 / 否 | 0 到 1 的概率 | | `choice` | 从给定选项里选一个 | 选中项 + 全部选项的概率分布 | | `score` | 按给定的分级标准打分 | 概率加权分 + 各级概率 | 它**不生成自由文本**,所以: - 连通性测试借用一个最简的 `noul` 问题验证链路 —— 把 prompt 当 `state`, 问「这段内容非空吗」,返回的是一句人能读的判定摘要 - **鹈鹕测试对它不可用**。这不是"效果差"而是"根本不适用":让一个判定模型 去画图,等于要求它做自己不做的事。切到 Jev 时按钮会变灰并说明原因, 后端也会回绝,不会发出一个注定失败的请求 模型填 `jev-latest`(别名)或 `jev-1.13.0` / `jev-1.14.0`。按输入 token 计费, 输出免费 —— 长文档当 `state` 试的时候留意成本。 ### Base URL 规范化 路径末段已经是版本号(`v1` / `v2` / `v1beta`)就不再补 `/v1`,否则补上: | 输入 | 实际请求 | |---|---| | `https://opencode.ai/zen/go` | `/zen/go/v1/chat/completions` | | `https://api.example.com/v2` | `/v2/chat/completions` | | `https://api.example.com` | `/v1/chat/completions` | | `https://api.example.com/v1/chat/completions` | 原样使用 | 输入框下方会实时显示实际会请求的地址,不用等发出去才知道。 ### 鹈鹕测试 让模型画一只骑自行车的鹈鹕。这是个流传很广的能力探针,同时考验空间想象、 结构化输出和 SVG 语法掌握,**结果一眼就能判断好坏**,不需要评分标准。 仅适用于生成式协议 —— Jev 不行,原因见上。 三档难度,因为要求越多输出越长,越容易撞上超时和 token 上限,而判断画得好 不好静态图已经够用: | 档位 | 要求 | 行数上限 | |---|---|---| | 静态 | 只要图形,明确不要动画 | 60 | | 基础动画(默认) | 只要求轮子转 + 翅膀扇 | 100 | | 完整动画 | 轮子、踏板、蹬腿、翅膀、背景滚动 | 160 | 结果按 `baseUrl + 模型 + 协议 + 思考开关 + 档位` 缓存,任一变化即重新生成。 想强制重来点「重新生成」。 模型返回的 SVG 是不可信内容(SVG 可以内嵌脚本),用 `sandbox` 为空的 iframe 承载:浏览器强制禁止其中的脚本执行,而 SMIL / CSS 动画不受影响,照常播放。 ### 输出预算自动调整 不猜网关的 `max_tokens` 上限,也**不把它变成你的问题**。被卡住时自动调整重试: - **设小了**(推理模型的思考吃光额度)→ 逐档往上探 - **设大了**(网关直接 400)→ 往回退 - 用量已知时取「刚好够」的最小档;未知时从最大档往下试 - 非额度问题(鉴权失败、模型不存在)**不重试** —— 白花钱没意义 半截的 SVG(缺 ``)会补全收尾标签交给浏览器渲染,能画多少显示多少。 浏览器对不完整 SVG 容错很好,画面通常已经成形。 ### 其它 - **模型列表** —— 点「拉取列表」调 `{根路径}/models`,下拉自动展开 - **自定义请求头** —— 每行一条 `Key: Value`,可覆盖同名默认头 (OpenRouter 要 `HTTP-Referer`、Azure 要 `api-version` 等场景) - **系统日志** —— 记录实际请求地址、请求头(密钥打码)、响应状态、响应体, 以及前几个 SSE 分片原文 - **网关适配** —— opencode.ai 要求 `x-opencode-session`,自动带上 ## 配置 配置存在 `%APPDATA%\llmtester\config.json`。 **API Key 默认不落盘**,勾选「记住密钥」才会写入,且是**明文**。落盘路径只有 一个入口(`Config.redacted()`),未勾选时无论怎么调用都不会把密钥序列化出去。 窗口固定尺寸 820×840,不可调整;只有结果区内部滚动。 ## 项目结构 ``` main.go 窗口配置 app.go 绑定给前端的方法 + 事件推送 probe.go HTTP 核心、三种协议的请求与解析、错误分类、输出预算调整 pelican.go 鹈鹕测试、难度档位、SVG 提取与抢救 config.go 配置持久化 *_test.go 54 个单元测试(展开后 106 项)+ 1 个实弹调试用例 frontend/dist/ 界面源码(手写 HTML/CSS/JS,无构建步骤) build/ 图标源文件与生成脚本 build.ps1 / .cmd 发布构建脚本 ``` 前端**没有构建步骤** —— 直接调用 Wails 注入的 `window.go` 和 `window.runtime`, 所以不用装 Node,构建也快。 ## 构建 **UPX 默认开启**,加了 `-NoUPX` 反向开关: ```cmd build.cmd 完整构建(检查工具链 → vet → 测试 → 编译 → UPX 压缩) build.cmd -NoUPX 不压缩 build.cmd -SkipTests 跳过测试 build.cmd -Run 构建完启动 build.cmd -Installer 额外生成安装包 ``` 实测两条路径: | | 体积 | 构建耗时 | |---|---|---| | 默认(压缩) | 3.66 MB | 约 37 秒 | | `-NoUPX` | 10.34 MB | 约 7 秒 | 压缩只在构建时多花 30 秒;运行时几乎无感 —— 启动慢约 95 ms(中位数,6 轮交替 测量),大头是 WebView2 初始化,与二进制大小关系不大。 UPX 壳是杀软启发式扫描的常见触发点,发布前需要过一遍目标环境的杀软; 真被拦了就用 `-NoUPX` 重新编译。 脚本会校验压缩是否真的生效:产物体积偏大时提示去查 Wails 输出里有没有 `Compressing application`,而不是默认成功。缺 upx 时直接报错而不是静默跳过。 ### 换图标 ```cmd build\make-icon.cmd 重新渲染图标 build.cmd 重新编译 ``` 图标源文件是 `build/icon-lt.svg`。脚本一次渲染 1024 原图后程序化降采样出 7 个尺寸,并自动校验居中 —— 边距为 0(内容被裁)直接报错,左右或上下不等距 则警告。 ## 测试 ```cmd go test ./... ``` 54 个测试函数(含子测试共 106 项),覆盖路径解析、三种协议的请求与响应、 错误分类、输出预算调整、SVG 提取与抢救、配置脱敏等。 还有一个**实弹调试用例**,用真实凭据跑一次完整流程: ```powershell $env:LLMTESTER_LIVE="1" $env:LLMTESTER_LEVEL="static" go test -run TestLivePelican -v -timeout 15m . ``` 它会打印每个阶段的时间戳、分片数、字符数、SVG 行数与动画检测结果,并把 生成的 SVG 写到临时文件。密钥来源按 `LLMTESTER_KEY` 环境变量 → 配置文件 → `LLMTESTER_KEYFILE` 的顺序解析,**不会打印密钥内容**。 ## 已知限制 - **仅 Windows** —— Wails v2 理论上跨平台,但 `probe.go` 里有针对 Windows WSA 错误码的处理,其它平台未测试 - **API Key 明文存储** —— 勾选「记住密钥」时以明文写入配置文件。要更安全的 话可以接 Windows DPAPI - **EXE 版本元数据未生效** —— 文件属性里的产品名/版本号是空的。版本资源确实 嵌进了 `.rsrc` 节,但 Windows 的版本 API 读不出来,是 Wails v2 的编组问题, 纯外观,不影响功能 - **大 state 未做压缩** —— TypeSafe 支持 gzip / zstd 压缩请求体,且 `state` 上限是 32 MiB。目前不做压缩,测大文档时受限于请求体大小和网络耗时 ## 技术栈 Go 1.23 + Wails v2.11(原生窗口 + 系统 WebView2),前端手写 HTML/CSS/JS。 选 Wails 而不是 Electron 是因为体积:10 MB 对 200 MB,且用系统自带的 WebView2,不打包浏览器。唯一的外部依赖就是 Wails 本身。 ## License MIT