nikhil8725/TokenLens
GitHub: nikhil8725/TokenLens
一个纯本地运行的 LLM token 效率诊断中间件,通过一行代码封装 SDK 即可检测并指导开发者消除 token 浪费。
Stars: 0 | Forks: 0
# token-lens
**本地 LLM token 效率中间件。**
只需一行代码即可封装你现有的 LLM 客户端。它能检测 token 浪费模式,并在开发过程中向终端输出可操作的建议 —— 全程零云端依赖,且没有任何数据离开你的设备。
```
⚠ [token-lens TL006] No max_tokens set on call to 'openai/gpt-4o-mini'.
→ Without a cap, a misbehaving prompt can generate thousands of tokens and spike costs.
⚠ [token-lens TL001] System prompt is identical across 3+ consecutive calls (~120 tokens each).
→ Use prompt caching. Potential saving: ~80% of system prompt tokens.
── token-lens session summary ──
calls: 3
prompt tokens: 612
completion tokens: 187
total tokens: 799
openai/gpt-4o-mini: 3 call(s), 799 tokens
```
## 为什么选择 token-lens?
现有工具 —— Helicone、AgentOps、LangSmith、PromptLayer —— 都是可观测性平台:它们通过云服务来**统计** token。token-lens 在以下三个方面与众不同:
| | token-lens | 云端工具 |
|---|---|---|
| **数据离开你的设备** | 绝不 | 会 |
| **设置** | 一行代码 | 账号 + API key + 代理/SDK |
| **功能** | 检测*为什么* token 会被浪费 | 统计已经消耗的 token |
| **离线可用** | 是 | 否 |
| **企业级部署** | 在你的服务器上 `pip install` | 需要签订商业协议 |
## 支持的提供商
token-lens 兼容**任何暴露了 `chat.completions.create()` 接口的 SDK**,并为原生 Anthropic SDK 提供了专用适配器。
| 提供商 | SDK | 封装器 |
|---|---|---|
| OpenAI | `openai` | `TokenLens` |
| OpenRouter | `openai` (兼容模式) | `TokenLens` |
| Groq | `groq` | `TokenLens` |
| Together AI | `openai` (兼容模式) | `TokenLens` |
| Azure OpenAI | `openai` | `TokenLens` |
| Ollama | `openai` (兼容模式) | `TokenLens` |
| Perplexity | `openai` (兼容模式) | `TokenLens` |
| Mistral | `openai` (兼容模式) | `TokenLens` |
| Anthropic | `anthropic` (原生) | `AnthropicTokenLens` |
| LangGraph / LangChain | 通过回调支持任何 SDK | `TokenLensCallbackHandler` |
## 安装
### 从 PyPI 安装(推荐)
```
pip install llm-token-lens
```
如需实现准确的 token 统计(强烈推荐):
```
pip install "token-lens[accurate-counting]"
```
如需支持 LangGraph / LangChain 回调:
```
pip install "token-lens[langgraph]"
```
一次性安装所有组件:
```
pip install "token-lens[accurate-counting,langgraph]"
```
### 从源码安装
```
git clone https://github.com/nikhilbahalkar/llm-token-lens.git
cd llm-token-lens
pip install -e ".[accurate-counting,dev]"
```
## 快速开始
### OpenAI / 兼容 OpenAI 的提供商
只需修改一行代码 —— 其余部分保持不变:
```
from openai import OpenAI
from token_lens import TokenLens # ← add this import
# 之前:
# client = OpenAI(api_key="...", base_url="...")
# 之后:
client = TokenLens(OpenAI(api_key="...", base_url="...")) # ← wrap it
# 所有现有的调用点保持不变:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Hello!"}],
max_tokens=256,
)
```
对于 **Groq、OpenRouter、Together AI、Azure OpenAI、Ollama** 以及任何其他提供兼容 OpenAI 的 SDK 的提供商,工作方式完全相同 —— 只需封装客户端即可。
### Anthropic
```
import anthropic
from token_lens import AnthropicTokenLens # ← use the dedicated adapter
client = AnthropicTokenLens(anthropic.Anthropic(api_key="..."))
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=256,
messages=[{"role": "user", "content": "Hello!"}],
)
```
### LangGraph / LangChain
如果你使用的是 `ChatOpenAI` 或 `init_chat_model`(而不是原始的 OpenAI SDK),请使用回调处理器:
```
from token_lens import TokenLensCallbackHandler
handler = TokenLensCallbackHandler()
# 作为 callback 传递给任何 LangGraph 或 LangChain 调用:
result = app.invoke(state, config={"callbacks": [handler]})
# 手动打印 session 摘要(或者在进程退出时自动触发):
handler.session_report()
```
## 配置
传入 `Config` 对象即可调整或屏蔽任何规则:
```
from token_lens import TokenLens, Config
client = TokenLens(
OpenAI(...),
Config(
# Raise the system-prompt-repeat threshold from 3 to 5
static_prompt_repeat_threshold=5,
# Disable specific rules by ID
disabled_rules={"TL007"},
# Turn off ANSI colors (e.g. in CI)
no_color=True,
# Disable the automatic session summary at process exit
auto_report_on_exit=False,
# Turn off all analysis entirely (e.g. in production)
enabled=False,
),
)
```
### 配置参考
| 字段 | 默认值 | 描述 |
|---|---|---|
| `enabled` | `True` | 主开关 —— 在生产环境中设置为 `False` |
| `no_color` | `False` | 在输出中禁用 ANSI 颜色 |
| `auto_report_on_exit` | `True` | 进程退出时输出会话摘要 |
| `static_prompt_repeat_threshold` | `3` | 触发 TL001 前的调用次数 |
| `history_message_threshold` | `20` | 触发 TL002 前的非系统消息数 |
| `high_tier_models` | (见下文) | 被视为“高级别”以触发 TL004 的模型名称 |
| `simple_task_completion_token_threshold` | `80` | 触发 TL004 的补全 token 数下限 |
| `system_prompt_token_threshold` | `500` | 触发 TL005 的 token 数上限 |
| `redundant_context_jaccard_threshold` | `0.70` | 触发 TL007 的重叠率比例 |
| `redundant_context_min_words` | `50` | 应用 TL007 的最小消息长度 |
| `pii_check_roles` | `{"user","system","tool"}` | 将扫描以检测 PII (TL008) 的消息角色 |
| `tool_result_token_threshold` | `500` | 触发 TL011 的 token 数量阈值 |
| `max_tools_per_call` | `15` | 触发 TL012 的工具数量阈值 |
| `spike_min_calls` | `5` | 激活 TL014 前所需的最小基线调用次数 |
| `spike_multiplier` | `3.0` | 触发 TL014 所需的会话平均值倍数 |
| `disabled_rules` | `set()` | 要跳过的规则 ID 集合(例如 `{"TL004", "TL007"}`) |
### 环境变量
| 变量 | 效果 |
|---|---|
| `NO_COLOR` | 禁用 ANSI 颜色(社区标准) |
| `TOKEN_LENS_NO_COLOR` | 同上,token-lens 专用 |
## 检测规则
| ID | 名称 | 阶段 | 触发条件 | 建议 |
|---|---|---|---|---|
| **TL001** | 静态系统提示词 | post | 连续调用使用相同系统提示词 ≥ N 次 | 使用提示词缓存(OpenAI 自动缓存 >1024 个 token;Anthropic:`cache_control`) |
| **TL002** | 无限制的历史记录 | post | 非系统消息超过 20 条且持续增加 | 使用 `trim_messages()` 或添加摘要节点 |
| **TL003** | 重复的提示词 | post | 发送完全相同的消息列表 ≥ 2 次 | 添加以提示词哈希为键的结果缓存 |
| **TL004** | 模型过剩 | post | 高级别模型,补全 token < 80,单条消息 | 对于简单任务,切换到更小/更便宜的模型 |
| **TL005** | 冗长的系统提示词 | post | 系统提示词超过 500 个 token | 将静态事实移至检索;精简提示词 |
| **TL006** | 缺失 max_tokens | pre | 既没有设置 `max_tokens` 也没有设置 `max_completion_tokens` | 添加 `max_tokens=<预期的上限>` |
| **TL007** | 冗余的上下文 | post | 两条消息的词汇重叠度 >70%(各 >50 个词) | 对其进行去重并整合到系统提示词中 |
| **TL008** | 提示词中包含 PII | pre | 在 user/system/tool 消息中检测到电子邮件、电话号码、信用卡、社会安全代码或 IP 地址 | 在发送给提供商之前移除或匿名化 PII (GDPR/CCPA) |
| **TL009** | 提示词中包含密钥 | pre | 在任何消息中检测到 API key、Bearer token 或 PEM 密钥 | 立即移除凭据;轮换任何已暴露的密钥 |
| **TL010** | 未固定版本的模型 | post | 使用了未固定版本的模型别名(例如 `gpt-4o`、`claude-3-5-sonnet`) | 固定到带有日期的版本,以防止行为发生隐蔽变更 |
| **TL011** | 工具结果臃肿 | post | 工具/函数返回结果超过 token 阈值 | 在将结果返回给模型之前进行摘要或过滤 |
| **TL012** | 过多的工具 | pre | 单次调用中注册了超过 N 个工具 | 过滤工具,仅保留与当前上下文相关的工具 |
| **TL013** | 缺失 response_format | pre | 系统提示词要求返回 JSON,但未设置 `response_format` | 添加 `response_format={"type": "json_object"}` 以保证输出有效的 JSON |
| **TL014** | 成本激增 | post | 调用 token 数 ≥ 会话平均值的 3 倍(在至少 5 次基线调用后) | 排查庞大的工具结果、无限制的历史记录或失控的提示词 |
| **TL015** | n>1 次补全 | pre | 在 kwargs 中传入了 `n > 1` | 使用 `n=1`;通过改变 temperature 或提示词来获取多样性 |
所有规则仅输出到 **stderr** —— 你的 `stdout` 管道永远不会被中断。
## 隐私与数据处理
**token-lens 绝不会向任何地方发送任何数据。**
- 所有分析均在进程内和内存中运行。
- token-lens 本身不会发起任何网络连接。
- 不会向磁盘写入任何日志。
- 没有遥测、没有分析、没有向外部服务的回调。
- 你的提示词、补全内容和 API key 永远不会受到 token-lens 的干涉 —— 它们会原封不动地直接传递给你的 SDK。
这使得 token-lens 可以安全地部署在企业服务器和物理隔离环境中。
## 企业级 / 服务器部署
适用于希望在所有服务中推行 token 效率标准的团队:
1. 将 `token-lens` 添加到你共享的 `requirements.txt` 或 `pyproject.toml` 中
2. 使用 `TokenLens` 封装你共享的 LLM 客户端工厂
3. 通过 `Config(enabled=os.getenv("TOKEN_LENS_ENABLED", "true") == "true")` 进行配置,以便在生产环境中禁用,同时在预发布/开发环境中保持开启状态
```
# shared_llm.py — 你的团队的核心 LLM 客户端模块
import os
from openai import OpenAI
from token_lens import TokenLens, Config
_base_client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
client = TokenLens(
_base_client,
Config(enabled=os.getenv("ENV", "dev") != "prod"),
)
```
从该模块导入 `client` 的每个服务都会自动获得 token-lens 的监控 —— 无需针对单个服务进行修改。
## 运行测试
```
# 安装 dev 依赖
pip install -e ".[accurate-counting,dev]"
# 运行完整的测试套件
pytest
# 包含覆盖率
pytest --cov=token_lens --cov-report=term-missing
```
## 项目结构
```
token_lens/
├── __init__.py # Public API: TokenLens, AnthropicTokenLens, Config, TokenLensCallbackHandler
├── _wrapper.py # TokenLens — OpenAI-compatible SDK wrapper
├── _anthropic_wrapper.py # AnthropicTokenLens — native Anthropic SDK wrapper
├── _chat.py # Intercepts chat.completions.create()
├── _anthropic_messages.py # Intercepts messages.create()
├── _session.py # Session state, CallRecord, NormalizedUsage
├── _analyzer.py # Runs detection rules pre- and post-call
├── _reporter.py # ANSI terminal output and session summary
├── _tokenizer.py # Token counting (tiktoken or 4-char fallback)
├── _config.py # Config dataclass
├── langgraph_integration.py # TokenLensCallbackHandler for LangGraph/LangChain
└── rules/
├── _base.py # Rule ABC, Finding dataclass, Severity enum
├── static_system_prompt.py # TL001
├── unbounded_history.py # TL002
├── duplicate_prompt.py # TL003
├── model_overkill.py # TL004
├── long_system_prompt.py # TL005
├── missing_max_tokens.py # TL006
├── redundant_context.py # TL007
├── pii_in_prompt.py # TL008
├── secret_in_prompt.py # TL009
├── unversioned_model.py # TL010
├── tool_result_bloat.py # TL011
├── excessive_tools.py # TL012
├── missing_response_format.py # TL013
├── cost_spike.py # TL014
└── n_completions.py # TL015
```
## 许可证
MIT —— 详情见 [LICENSE](LICENSE)。
标签:DLL 劫持, SOC Prime, 中间件, 人工智能, 大语言模型, 开发工具, 成本优化, 本地部署, 用户模式Hook绕过, 逆向工具