Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when wiring up or switching between China-domestic LLM providers (DeepSeek, Doubao/Volc Ark, Qwen/DashScope, MiniMax). Provides OpenAI-compatible adapter pattern, env-var contracts, fallback strategy, cost guardrails, and minimum verifications before declaring integration done.
.claude/skills/pcliangx-agf-wiring-multi-llm-sdk/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 44% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 16% | 0% |
| case-06 | ✗→✓ | ▲ Improved | -9% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 26% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 32% | 0% |
Use this skill when:
backend/app/agents/ or any backend moduleAll four providers expose OpenAI-compatible endpoints. Default to the openai Python SDK with a custom base_url rather than each vendor's bespoke SDK — fewer dependencies, easier to swap, less drift.
Bespoke SDK exceptions:
volcengine-python-sdk for Ark image APIminimax official SDKBefore wiring any SDK, pull its current docs via Context7 (resolve-library-id → query-docs) — all four vendors iterate fast and training-data memory of their APIs is likely stale. Context7 coverage of domestic SDKs varies; if a library isn't indexed, fall back to WebFetch on official docs.
All providers follow the same pattern. Never hardcode keys. Each is read from environment at module init; a missing key raises early.
| Provider | Endpoint env | Key env | Default model env | |---|---|---|---| | DeepSeek | DEEPSEEK_BASE_URL (default https://api.deepseek.com/v1) | DEEPSEEK_API_KEY | DEEPSEEK_MODEL (e.g. deepseek-chat) | | Doubao (Volc Ark) | ARK_BASE_URL (default https://ark.cn-beijing.volces.com/api/v3) | ARK_API_KEY | ARK_MODEL_ENDPOINT_ID (vendor-specific endpoint id, NOT model name) | | Qwen (DashScope) | DASHSCOPE_BASE_URL (default https://dashscope.aliyuncs.com/compatible-mode/v1) | DASHSCOPE_API_KEY | QWEN_MODEL (e.g. qwen-plus) | | MiniMax | MINIMAX_BASE_URL (default https://api.minimaxi.com/v1) | MINIMAX_API_KEY | MINIMAX_MODEL (e.g. abab6.5s-chat) |
> Doubao gotcha: the "model name" in OAI-compat call is actually the Ark endpoint id (ep-2024xxxx), not a public model id like doubao-pro-32k. Get the endpoint id from Volc Ark console.
python# backend/app/agents/llm_clients.py import os from openai import OpenAI def get_client(provider: str) -> tuple[OpenAI, str]: if provider == "deepseek": return OpenAI( api_key=os.environ["DEEPSEEK_API_KEY"], base_url=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com/v1"), ), os.getenv("DEEPSEEK_MODEL", "deepseek-chat") if provider == "doubao": return OpenAI( api_key=os.environ["ARK_API_KEY"], base_url=os.getenv("ARK_BASE_URL", "https://ark.cn-beijing.volces.com/api/v3"), ), os.environ["ARK_MODEL_ENDPOINT_ID"] if provider == "qwen": return OpenAI( api_key=os.environ["DASHSCOPE_API_KEY"], base_url=os.getenv("DASHSCOPE_BASE_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1"), ), os.getenv("QWEN_MODEL", "qwen-plus") if provider == "minimax": return OpenAI( api_key=os.environ["MINIMAX_API_KEY"], base_url=os.getenv("MINIMAX_BASE_URL", "https://api.minimaxi.com/v1"), ), os.getenv("MINIMAX_MODEL", "abab6.5s-chat") raise ValueError(f"unknown provider: {provider}")
Default order (tunable in CLAUDE.md per project): DeepSeek → Doubao → Qwen → MiniMax.
Implement with tenacity retry + a thin orchestrator that walks the list. Never silently swap models without telemetry — every failover emits a structured log line per observability.md.
Every call must record the LLM fields mandated by observability.md:17 plus provider. DeepSeek + Doubao support prompt caching — read cache_hit_ratio off the response usage object.
Before claiming the integration works, run this checklist explicitly. Verify outputs match expectations — do not assume.
localhost:1) and confirm auto-fallback + log linegit diff | grep -iE 'api[_-]?key|secret|token' before commit)base_url strings — always read from envusage shape — validate and normalize.env (gitignored) or secret manager| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 17,583 | 15,296 | -13% | 1 | 1 | 0% | 3,605 | 5,207 | +44% | 0 | 0 | — |
case-02 | fail→fail | 27,576 | 23,323 | -15% | 1 | 1 | 0% | 5,595 | 7,011 | +25% | 0 | 0 | — |
case-03 | fail→pass | 21,820 | 15,387 | -29% | 1 | 1 | 0% | 4,148 | 4,803 | +16% | 0 | 0 | — |
case-04 | pass→pass | 16,645 | 8,430 | -49% | 1 | 1 | 0% | 2,544 | 3,056 | +20% | 0 | 0 | — |
case-05 | pass→pass | 16,940 | 6,489 | -62% | 1 | 1 | 0% | 2,933 | 2,752 | -6% | 0 | 0 | — |
case-06 | fail→pass | 16,802 | 4,704 | -72% | 1 | 1 | 0% | 2,760 | 2,512 | -9% | 0 | 0 | — |
case-07 | pass→pass | 8,305 | 5,279 | -36% | 1 | 1 | 0% | 1,483 | 2,579 | +74% | 0 | 0 | — |
case-08 | fail→pass | 9,319 | 2,937 | -68% | 1 | 1 | 0% | 1,728 | 2,184 | +26% | 0 | 0 | — |
case-09 | fail→pass | 9,159 | 3,005 | -67% | 1 | 1 | 0% | 1,685 | 2,225 | +32% | 0 | 0 | — |
case-10 | fail→pass | 10,008 | 3,482 | -65% | 1 | 1 | 0% | 1,873 | 2,307 | +23% | 0 | 0 | — |
case-11 | fail→pass | 14,797 | 9,566 | -35% | 1 | 1 | 0% | 2,313 | 3,202 | +38% | 0 | 0 | — |
case-12 | fail→pass | 18,733 | 12,468 | -33% | 1 | 1 | 0% | 2,852 | 3,755 | +32% | 0 | 0 | — |
case-13 | pass→pass | 15,973 | 9,743 | -39% | 1 | 1 | 0% | 2,607 | 3,329 | +28% | 0 | 0 | — |
case-14 | pass→fail | 15,014 | 10,040 | -33% | 1 | 1 | 0% | 2,793 | 3,580 | +28% | 0 | 0 | — |
case-15 | fail→pass | 17,483 | 15,073 | -14% | 1 | 1 | 0% | 2,923 | 4,467 | +53% | 0 | 0 | — |
case-16 | pass→pass | 15,347 | 8,752 | -43% | 1 | 1 | 0% | 2,393 | 3,118 | +30% | 0 | 0 | — |
case-17 | pass→pass | 13,567 | 7,316 | -46% | 1 | 1 | 0% | 2,039 | 2,854 | +40% | 0 | 0 | — |
case-18 | pass→pass | 14,311 | 9,332 | -35% | 1 | 1 | 0% | 2,182 | 3,154 | +45% | 0 | 0 | — |
case-19 | pass→pass | 15,344 | 10,671 | -30% | 1 | 1 | 0% | 2,618 | 3,446 | +32% | 0 | 0 | — |
case-20 | pass→pass | 11,704 | 8,176 | -30% | 1 | 1 | 0% | 2,096 | 3,269 | +56% | 0 | 0 | — |
case-21 | pass→pass | 8,879 | 4,952 | -44% | 1 | 1 | 0% | 1,432 | 2,526 | +76% | 0 | 0 | — |
case-22 | fail→pass | 13,567 | 13,518 | -0% | 1 | 1 | 0% | 2,063 | 4,174 | +102% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +41 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.