使用说明
对练
让你的法学硕士不同意。
一个MCP服务器,负责协调LLM之间的配对会话。查询多个模型,让它们相互挑战,通过摩擦来强化你的想法。
特征
- 多供应商:OpenAI、Anthropic、谷歌、OpenRouter、Moonshot(Kimi)、z.ai(GLM)、Ollama、自定义端点
- 推理模型感知:管理的统一解析器
reasoning_content,reasoning(OpenRouter),<think>内联(phi-4、QwQ),max_completion_tokens(GPT-5/o系列),finish_reason暴露于呼叫者 - 预算控制:会话、每日、每次请求限制+仅附加日志
session_id用于事后整合(按模型、按拳击) - 断路器:在N个连续错误(内容空、HTTP 5xx、超时)后自动禁用提供程序
- 简单图元:
ask_model,ask_all,challenge,get_models,get_lenses,get_usage,estimate_cost - 挑战镜片:10个观点(devil_advocate、cynical_dev、安全性、成本、用户、规模、简单性、天真、实用主义、钢铁侠)
- 诊断工具:
scripts/probe_providers.py(原始响应转储),scripts/consolidate_usage.py(JSONL聚合) - 本地优先:与Ollama合作进行自由局部推理
安装
快速设置
./install.sh
这将创建配置目录、中央API密钥存储,并安装依赖项。然后在克劳德代码中注册(步骤4)。
手动设置
1.克隆
git clone https://github.com/you/llm-sparring ~/path/to/llm-sparring
cd ~/path/to/llm-sparring
2.依赖关系
uv sync
uv 如果需要,安装Python 3.11+,创建 .venv/ 并从以下位置安装deps uv.lock. 安装uv 如果缺席。
3.设置API密钥
API密钥从 中央商店 跨项目共享:
mkdir -p ~/.config/llm-provider-keys
cp providers.env.example ~/.config/llm-provider-keys/providers.env
chmod 600 ~/.config/llm-provider-keys/providers.env
$EDITOR ~/.config/llm-provider-keys/providers.env
服务器加载 ~/.config/llm-provider-keys/providers.env 首先,然后 .env 在项目根目录中作为覆盖(仅适用于项目特定的值)。只填写您实际使用的提供者——服务器会跳过键为空的模型。
如果你已经有了 ~/.config/llm-provider-keys/providers.env 从另一个项目(jobset&match-v2、claude-code-x)中,您就完成了——不需要额外的密钥设置。
3b。配置模型
mkdir -p ~/.config/mcp/llm-sparring
cp config.yaml ~/.config/mcp/llm-sparring/config.yaml
$EDITOR ~/.config/mcp/llm-sparring/config.yaml
4.添加到克劳德代码
适用于克劳德桌面 (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"sparring": {
"command": "uv",
"args": [
"--directory",
"/Users/YOU/.claude/mcp/llm-sparring",
"run",
"server.py"
],
"env": {
"OPENAI_API_KEY": "sk-...",
"GOOGLE_API_KEY": "...",
"OPENROUTER_API_KEY": "sk-or-..."
}
}
}
}
适用于Claude Code CLI:
claude mcp add sparring -- uv --directory ~/.claude/mcp/llm-sparring run server.py
工具
ask_model
查询特定模型。
ask_model(
model: "gpt-4o",
question: "What are the pros/cons of microservices?",
context: "Building a SaaS product", # optional
max_tokens: 1000, # optional — voir cascade ci-dessous
session_id: "20260423-143012" # optional — pour regrouper la facturation
)
max_tokens (级联,从强到弱):显式arg> model.default_max_tokens (配置)> settings.default_max_tokens > 2000.提出倒推推理模型(Gemini 3、Qwen、GLM、o系列、phi-4局部)。
ask_all
并行查询所有启用的模型。每个模型都应用自己的级联 max_tokens.
ask_all(
question: "What database should I use for time-series data?",
context: "IoT project with 10M events/day", # optional
max_tokens: 1000, # optional — override uniforme
session_id: "20260423-143012" # optional
)
返回一个 meta 按模型 finish_reason, reasoning_len, inline_thinking_len 相关时+a models_with_errors 在管弦乐队的最高级别。
challenge
让一个模型评论一个响应(来自另一个模型、文件、片段——任何文本)。 这是拳击的核心。
challenge(
challenger_model: "gemini-flash",
original_question: "Best database for time-series?",
target_response: "PostgreSQL with TimescaleDB because...",
target_source: "gpt-4o", # optional: model name, file name, etc.
lens: "devil_advocate", # optional: see get_lenses. null = natural critique
language: "fr", # optional: "fr" (default) or "en"
max_tokens: 2000, # optional — même cascade que ask_model
session_id: "20260423-143012" # optional
)
get_models
列出所有已配置的型号及其状态和定价。
get_models()
{
"models": [
{
"name": "gpt-4o",
"provider": "openai",
"model_id": "gpt-4o",
"status": "available",
"enabled": true,
"pricing": {"input": 2.50, "output": 10.00},
"breaker": {"errors": 0, "disabled": false}
},
{
"name": "or-zai-glm",
"provider": "openrouter",
"model_id": "z-ai/glm-4.6",
"status": "available",
"enabled": true,
"pricing": {"input": 0.5, "output": 2.0},
"breaker": {
"errors": 3,
"disabled": true,
"disabled_until": 1745421012.5,
"last_error": "truncated during reasoning (finish=length, reasoning_len=693)"
}
}
]
}
冠军 breaker 反映断路器电路的状态: disabled: true 表示模型失败≥ threshold 连续两次,并放入冰箱,直到 disabled_until.
get_lenses
列出可用的挑战镜片及其说明。
get_lenses()
返回10个内置镜头(devil_advocate, steelman, pragmatist, cynical_dev, security, cost, user, scale, simplicity, naive)加上默认值和传递说明 lens: null 一种没有个性的自然批判。
get_usage
获取当前预算使用情况。无参数:totaux会话(内存中)/每日/每月。和 session_id :通过JSONL日志中的聚合模型崩溃。
get_usage()
get_usage(session_id: "20260423-143012")
{
"session": {"cost": 0.05, "requests": 3, "limit": 1.00, "remaining": 0.95},
"daily": {"cost": 0.25, "requests": 12, "limit": 5.00, "remaining": 4.75},
"monthly": {"cost": 3.50, "requests": 156},
"sparring_session": {
"session_id": "20260423-143012",
"total_cost": 0.0172,
"requests": 3,
"errors": 1,
"by_model": {
"gpt-4o": {"cost": 0.017, "requests": 2, "errors": 0, "input_tokens": 400, "output_tokens": 600},
"or-zai-glm": {"cost": 0.0002, "requests": 1, "errors": 1, "input_tokens": 100, "output_tokens": 0}
}
}
}
estimate_cost
在提出请求之前估算成本。
estimate_cost(
models: ["gpt-4o", "gemini-flash", "llama-local"],
input_tokens: 500,
output_tokens: 1000
)
配置
模型
models:
- name: "gpt-4o"
provider: "openai"
model_id: "gpt-4o"
enabled: true
- name: "or-zai-glm"
provider: "openrouter"
model_id: "z-ai/glm-4.6"
default_max_tokens: 4000 # reasoning model — besoin de marge pour thinking
enabled: true
- name: "llama-local"
provider: "ollama"
model_id: "llama3.2"
base_url: "http://localhost:11434"
enabled: true
default_max_tokens (可选)-此模板的默认代币预算。对于在生成文本之前在思考中消耗100-600个不可见令牌的推理模型:
|建议的模型|为什么|
|--------|---------|----------|
|Gemini 2.5/3 Flash/Pro|6000|~190个即使在琐碎提示上也看不见的思考令牌|
|GPT-5/O系列
|GLM-4.6/Qwen思考|4000|200-600响应令牌|
|phi-4/QwQ/DeepSeek-R1本地|8000| <think> 内联显示在内容中|
|标准模型(GPT-4O、Mistral、Claude)|-(回溯设置)|无隐形思维|
设置
settings:
default_timeout: 30 # secondes
max_parallel: 3 # concurrence ask_all
default_max_tokens: 2000 # fallback global — voir cascade plus haut
circuit_breaker: # optionnel, défauts indiqués
threshold: 3 # erreurs consécutives avant désactivation
cooldown_seconds: 300 # durée de mise au frigo
提供商
| 提供者 | 环境变量 | 类型 | 注释 |
|---|---|---|---|
openai |
OPENAI_API_KEY |
OpenAI兼容 | GPT-4、GPT-4o |
anthropic |
ANTHROPIC_API_KEY |
OpenAI compat | Claude(终点beta, 无提示缓存) |
google |
GOOGLE_API_KEY |
OpenAI compat | Gemini(终点测试版) |
openrouter |
OPENROUTER_API_KEY |
OpenAI兼容性 | 推荐:400多种型号 |
mistral |
MISTRAL_API_KEY |
OpenAI compat | Mistral大、小 |
deepseek |
DEEPSEEK_API_KEY |
OpenAI compat | DeepSeek聊天,推理 |
groq |
GROQ_API_KEY |
OpenAI compat | 非常快速的推理 |
together |
TOGETHER_API_KEY |
OpenAI compat | 开源模型 |
xai |
XAI_API_KEY |
OpenAI同胞 | Grok |
moonshot |
MOONSHOT_API_KEY |
OpenAI同胞 | Kimi |
zai |
ZAI_API_KEY |
OpenAI兼容性 | GLM(z.ai) |
custom |
自定义 | OpenAI compat | 任何兼容的端点 |
ollama |
-- | Ollama | 本地型号,免费(专用处理器) |
在中添加与OpenAI兼容的提供程序 providers.py:
"newprovider": {
"type": "openai_compatible",
"base_url": "https://api.newprovider.com/v1",
"api_key_env": "NEWPROVIDER_API_KEY",
},
预算
budget:
confirm_threshold: 0.10 # Confirm if request > $0.10
session_limit: 1.00 # Max per session
daily_limit: 5.00 # Max per day
tracking_file: "~/.config/mcp/llm-sparring/usage.json"
journal_file: "~/.config/mcp/llm-sparring/usage.jsonl" # optionnel, défaut à côté de tracking_file
两个文件并行维护:
usage.json-每个会话(内存)、日期和月份的汇总总数。实时预算检查的来源。usage.jsonl-仅附加日志,每个请求一行:ts,session_id,tool,model,provider,input_tokens,output_tokens,cost,error,finish_reason,reasoning_len,inline_thinking_len,duration_ms.反向审计和会话整合的真实来源。
定价
定价数据来自 pricing.json (~2600种型号,已售出 文学硕士).
级联分辨率:覆盖 config.yaml → 查找精确→ {provider}/{model_id} →本地规则(ollama/localhost→0)→带警告的保守回退。
建议季度刷新:
uv run scripts/refresh_pricing.py
覆盖JSON中缺少的模板或强制定价,在 config.yaml :
pricing:
my-custom-model:
input: 0.50 # per 1M tokens
output: 1.50
监控和计费
按会话聚合
管弦乐手(/sparring)生成 session_id 通过Sparring独特,并在每次工具调用时传播。巩固的两种方法:
来自Claude代码(在线) :
get_usage(session_id: "20260423-143012")
→返回 sparring_session.by_model 每个模型有成本、错误和令牌。
离线(脚本) :
# Détail d'une session
uv run scripts/consolidate_usage.py --session 20260423-143012
# Top sessions de la journée par coût
uv run scripts/consolidate_usage.py --day 2026-04-23 --by-session
# Breakdown par outil (ask_model / ask_all / challenge)
uv run scripts/consolidate_usage.py --by-tool
# Dump JSON pour traitement externe
uv run scripts/consolidate_usage.py --json | jq '.by_model'
默认情况下,床 ~/.config/mcp/llm-sparring/usage.jsonl ; --file <path> 另一条路。
脚本
三个CLI实用程序 scripts/所有通过发射 uv run :
| 脚本 | 角色 | 何时使用 |
|---|---|---|
probe_providers.py |
向一个或所有模板发送一个普通提示,转储返回空、截断、错误或 circuit breaker open |
|
consolidate_usage.py |
添加 usage.jsonl 每个会话/天/模型/工具 |
回溯会话审核,按成本划分的顶级会话 |
refresh_pricing.py |
重新下载 pricing.json 自Litellm |
季度或JSON中缺少的模板以来 |
诊断提供者
系统返回空、截断或“电路断路器打开”的模型? scripts/probe_providers.py 发送一个简单的提示并转储提供程序的原始HTTP响应,而不经过内部解析器,因此不隐藏内容消失的位置。
旗帜
| 标志 | 缺陷 | 效果 |
|---|---|---|
--model <name> |
所有 enabled |
仅测试一个模型(逻辑名称 config.yaml) |
--prompt "..." |
"Dis bonjour en une phrase." |
提示自定义(用于测试在prod中失败的情况) |
--max-tokens <N> |
200 |
预算代币为您带来回报 |
--sweep "500,1000,2000,4000" |
- | 多个循环 max_tokens 以识别有用的阈值。需要 --model. |
--json |
用于外部分析的原始JSON转储(每个模型的原始) |
工作流
1.全球分类 -所有型号均已启用,人为格式:
uv run scripts/probe_providers.py
2.测试特定模型 -例如,在deepseek错误之后:
uv run scripts/probe_providers.py --model deepseek-v4-flash
uv run scripts/probe_providers.py --model deepseek-v4-flash --max-tokens 4000
uv run scripts/probe_providers.py --model deepseek-v4-flash --prompt "<le prompt qui a foiré>"
读这行 Diagnostic 然后是块 Détails : content_len, reasoning_len, finish_reason, message_keys 指示文本经过的位置。一个 HTTP 401 对于所有值,表示未加载提供程序的API密钥-检查 ~/.config/llm-provider-keys/providers.env (ou .env 本地si覆盖)。
3.找到 max_tokens 最低有用 -例如,对于返回的Kimi null :
uv run scripts/probe_providers.py --model or-kimi-k2 --sweep 500,1000,2000,4000,8000
分拣类型:
max_tokens content reasoning finish diagnostic
500 0 487 length ⚠️ content vide MAIS reasoning_content présent (487 chars) — thinking model
1000 0 923 length ⚠️ tronqué avant tout content (finish=length) — augmenter max_tokens
2000 42 1450 stop OK — 42 chars (finish=stop)
4000 180 1502 stop OK — 180 chars (finish=stop)
8000 180 1502 stop OK — 180 chars (finish=stop)
→ Seuil utile détecté : 2000 (premier max_tokens avec content non-vide et non tronqué)
然后将阈值传播到 ~/.config/mcp/llm-sparring/config.yaml :
- name: "or-kimi-k2"
provider: "openrouter"
model_id: "moonshotai/kimi-k2"
default_max_tokens: 2000 # ou 4000 pour marge de sécurité
enabled: true
4.原始JSON浇注分析精细 -格式未知时有用:
uv run scripts/probe_providers.py --model <name> --json | jq '.[0].raw.choices[0].message | keys'
信号映射
| 症状 | 检查列 | 操作 |
|---|---|---|
content_len=0 + reasoning_len>0 |
只讲道理的回应 | 蒙特尔 default_max_tokens 对于该模型(建议扫描) |
finish_reason=length + content_len<50 |
思考代币耗尽预算 | 同上-扫描 2000,4000,8000 |
finish_reason=content_filter |
审核提供程序 | 重新格式化提示符 |
HTTP 400 max_tokens unsupported |
GPT-5/O系列124已自动管理(助手 _max_tokens_param) |
|
<think> 在 content_preview |
推理模型本地 | 自动条带,检查是否存在 </think> |
HTTP 401/403 |
API密钥丢失或无效 ~/.config/llm-provider-keys/providers.env |
|
ERREUR: TimeoutException |
端点速度慢/过载 settings.default_timeout |
使用/陪练
此MCP旨在与 /sparring 克劳德代码中的命令:
/sparring Should I use microservices or monolith for my startup?
→ Claude orchestrates:
1. Framing with you
2. ask_all() to gather perspectives
3. challenge() to have models critique each other
4. Synthesis and recommendation
测试
1.验证服务器是否启动
cd ~/.claude/mcp/llm-sparring
uv run server.py
# Ctrl+C to stop
在调试模式下了解更多详细信息:
LOG_LEVEL=DEBUG uv run server.py
2.检查型号配置
在Claude Code中,运行:
get_models()
验证您启用的模型是否显示为 status: "available".
3.测试单个模型
ask_model(model: "gpt-4o", question: "Say hello in one word")
从便宜/快速的型号开始(例如。 gpt-4o-mini, gemini-flash或当地Ollama模型)。
4.测试并行查询
ask_all(question: "What is 2+2?")
所有启用的模型都应该响应。
5.测试挑战
challenge(
challenger_model: "gemini-flash",
original_question: "What is the best programming language?",
target_response: "Python because it's simple",
target_source: "gpt-4o",
lens: "devil_advocate"
)
6.检查预算跟踪
get_usage()
验证会话和每日费用是否反映了您的测试查询。
7.测试/陪练
/sparring Should I use SQLite or PostgreSQL for a side project?
这将运行完整的编排流程(框架、ask_all、挑战、合成)。
故障排除
“没有可用型号”/ status: "unavailable"
- 检查中央存储中是否存在密钥:
cat ~/.config/llm-provider-keys/providers.env - Si覆盖本地:
cat .env在repo echo $OPENAI_API_KEY在终端中证明不了什么:MCP在 由Claude Code启动的子进程,而不是在shell中。- 检查配置:
cat ~/.config/mcp/llm-sparring/config.yaml - 必须至少有一个模型
enabled: true - 更改键或配置后,重新启动Claude代码。
“超时查询模型”
- 增加
default_timeout在配置中 - 对于Ollama:
ollama serve - 本地模型需要时间来加载第一个查询
“超出预算”
- 核对
get_usage - 调整配置中的限制
- 等待每日重置(午夜)
答案为空、截断或 circuit breaker open
推理模型的三个常见原因(双子座3、GPT-5、O系列、GLM、Qwen思维、Phi-4本地):
max_tokens托普·巴斯 -无形思维令牌在模型生成文本之前消耗预算。要查找有用的阈值:uv run scripts/probe_providers.py --model <name> --sweep 500,1000,2000,4000,8000(见“诊断提供者”一节)。然后提高default_max_tokens对于该模型config.yaml.- 冠军推理失误 -志浦把答案放在
reasoning_content,OpenRouter inreasoning,phi-4本地丹<think>内联。解析器自动处理这三个;如果它坏了,启动scripts/probe_providers.py --model <name> --json浇注确认格式。 - 断路器开关 -连续3次错误后,将模型放入冰箱5分钟。检查
get_models()→ champbreaker计数器在服务器首次成功或重新启动时重置为零。
发展
uv run server.py
LOG_LEVEL=DEBUG uv run server.py
许可证
麻省理工学院