Files
planet/docs/technical/en/agents-aiprovider.md
linkong fbca381512
Some checks failed
ci / backend (push) Has been cancelled
ci / frontend (push) Has been cancelled
ci / delivery (push) Has been cancelled
release / images (push) Has been cancelled
release: bump version to 0.62.0
2026-05-21 01:37:32 +08:00

16 KiB

AI Provider Guide

Overview

aiprovider is the model-adapter service for Planet.

It isolates model-vendor details from the main backend so the rest of the system can call a stable business API:

  • Caller service -> planet backend
  • planet backend -> aiprovider
  • aiprovider -> concrete model provider

The recommended default is:

  • External and cross-service callers use planet backend
  • Only infrastructure-grade internal jobs call aiprovider directly

Responsibilities

backend is responsible for:

  • authentication and authorization
  • business-level request shaping
  • stable /api/v1/ai/... endpoints
  • internal service-to-service authentication toward aiprovider
  • reading the default provider, model, and per-provider keys saved in Settings, then overriding aiprovider .env defaults through internal headers

aiprovider is responsible for:

  • model protocol adaptation
  • provider selection by .env when no backend override headers are present
  • timeout and lightweight retry
  • request tracing via X-Request-ID

This now follows an OpenClaw-like seam:

  • AI_PROVIDER identifies the vendor or logical provider
  • AI_PROVIDER_API identifies the wire adapter

That split makes MiniMax, Claude-compatible gateways, and self-hosted OpenAI-compatible services easier to model without overloading one config field.

Supported Providers

aiprovider currently supports these provider identities:

  • openai
  • anthropic
  • minimax
  • ollama

Supported request adapters:

  • openai-completions
  • anthropic-messages
  • ollama-generate

Backward-compatible aliases still accepted:

  • openai_compatible
  • anthropic_compatible
  • claude_compatible

Provider mapping:

  • vLLM, LM Studio, One API: AI_PROVIDER=openai, AI_PROVIDER_API=openai-completions
  • MiniMax: AI_PROVIDER=minimax, AI_PROVIDER_API=anthropic-messages
  • Claude-compatible gateways: AI_PROVIDER=anthropic, AI_PROVIDER_API=anthropic-messages
  • Ollama: AI_PROVIDER=ollama, AI_PROVIDER_API=ollama-generate

API Surfaces

Main backend API

Preferred stable entrypoints:

  • GET /api/v1/ai/provider/status
  • POST /api/v1/ai/situational-awareness/analyze

Authentication:

  • Authorization: Bearer <jwt>

Optional tracing header:

  • X-Request-ID: <caller-generated-id>

The backend will propagate X-Request-ID to aiprovider and return the same header in the response.

Settings API

The AI settings page uses:

  • GET /api/v1/settings/integrations
  • PUT /api/v1/settings/integrations
  • POST /api/v1/settings/integrations/ai-provider/connect
  • GET /api/v1/settings/integrations/ai-provider/secrets
  • GET /api/v1/settings/integrations/ai-provider/presets
  • GET /api/v1/settings/ai-prompts
  • PUT /api/v1/settings/ai-prompts/{task_key}
  • POST /api/v1/settings/ai-prompts/{task_key}/reset

These endpoints require an authenticated user. The secrets endpoint is only used when the settings page reveals a key or token; hiding the field restores the masked preview.

Admin Next keeps the AI page aligned with the legacy information architecture:

  • Model Providers
    • Manages provider, wire adapter, default model, LLM API key, proxy URL, proxy token, model refresh, set-as-default, and lightweight connectivity testing.
  • Tool Calling
    • Manages tools such as WebSearch and OCR. Each tool first selects a provider, then edits that provider's API, key, and advanced parameters.
  • Prompts
    • Edits system/user prompts by prompt group and task key. Save and reset only affect the current task.
  • Playground
    • Runs real conversations with the active provider and prompt configuration. AI responses are rendered as Markdown.

Save, set-as-default, and connectivity testing are separate responsibilities: save only persists the form, set-as-default only changes the active provider/tool, and connectivity testing only validates the current draft. It must not implicitly save or switch defaults.

The ai-prompts endpoints back the Prompts tab in AI settings. Shipped defaults come from versioned backend resources, while business code references stable task keys. The API stores only operator overrides. Resetting a prompt removes the override and falls back to the current shipped default.

Prompt Boundary

aiprovider is a pure model adapter and does not inject a global business system prompt. News localization, alert briefing, BGP briefing, location factcheck, datasource mapping, and credential guide generation each resolve their own effective prompt by task key. Alert-analysis system prompts are only sent by alert-related tasks and do not leak into other LLM calls.

Agent And Tool Boundary

Agent workflows belong in the backend, not in aiprovider. Future Earth LLM commands, situational awareness, multi-role simulation, WebSearch, database queries, evidence storage, and configuration proposal application should be orchestrated by the backend Agent Runtime. aiprovider should receive model-ready requests from the backend and return normalized model responses.

If a provider supports native tool calling, aiprovider may pass through protocol fields and normalize response blocks, but tool whitelists, argument validation, permission policy, run records, and write approvals must stay in the backend. When a provider does not support native tools, the backend uses JSON tool-call fallback; business tools should not move into aiprovider for a provider-specific shortcut.

AI provider internal API

Internal-only endpoints:

  • GET /v1/provider/status
  • POST /v1/analyze

Authentication:

  • X-Provider-Token: <shared-secret>

Optional tracing header:

  • X-Request-ID: <caller-generated-id>

Request Example

Call through backend

curl -X POST http://localhost:8000/api/v1/ai/situational-awareness/analyze \
  -H "Authorization: Bearer <access_token>" \
  -H "X-Request-ID: bgp-incident-20260407-001" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "BGP异常研判",
    "objective": "总结当前风险并给出处置建议",
    "observations": [
      "collector A 在 5 分钟内出现多次 origin 变更",
      "异常集中在同一地区前缀"
    ],
    "constraints": [
      "不要编造不存在的数据",
      "区分事实和推断"
    ],
    "context": {
      "source": "bgp-monitor",
      "severity": "high"
    }
  }'

Call aiprovider directly

curl -X POST http://localhost:8010/v1/analyze \
  -H "X-Provider-Token: change_me" \
  -H "X-Request-ID: ai-batch-job-001" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "链路波动分析",
    "objective": "给出简要态势摘要和下一步建议",
    "observations": [
      "多个节点出现延迟上升"
    ],
    "constraints": [
      "不要假设根因已经确认"
    ],
    "context": {
      "region": "APAC"
    }
  }'

Response Shape

Both backend and aiprovider return the same payload shape:

{
  "provider": "minimax",
  "api": "anthropic-messages",
  "model": "MiniMax-M2.7",
  "content": "1) 态势摘要 ...",
  "content_blocks": [],
  "text_blocks": [],
  "thinking_blocks": [],
  "raw_response": {}
}

Both services also return:

  • X-Request-ID: <id>

Configuration

Runtime Configuration Flow

The backend Settings system owns the global LLM default. The runtime flow is:

  1. Frontend or application code calls a backend /api/v1/ai/... endpoint.
  2. backend reads category = external_integrations from the PostgreSQL system_settings table.
  3. payload.ai_provider.default_provider selects the active provider.
  4. payload.ai_provider.providers[provider] supplies that provider's api_key, provider_api, base_url, model, max_tokens, and anthropic_version.
  5. backend converts those values to internal headers such as X-AI-Provider, X-AI-Provider-API, X-AI-Base-URL, X-AI-API-Key, and X-AI-Model.
  6. aiprovider uses those headers to override its .env defaults before calling the real model vendor.

After the AI settings page saves a new default provider/model/key, Playground, alert briefs, datasource mapping generation, and other backend AI calls all use that same default.

Persistence Shape

AI settings are persisted in PostgreSQL, not a JSON file. The core payload shape is:

{
  "ai_provider": {
    "service_url": "http://localhost:8010",
    "service_token": "",
    "default_provider": "openai",
    "providers": {
      "openai": {
        "provider_api": "openai-completions",
        "base_url": "https://api.openai.com/v1",
        "model": "gpt-5.1",
        "api_key": "<saved secret>",
        "max_tokens": 4096,
        "anthropic_version": "2023-06-01"
      },
      "minimax": {
        "provider_api": "anthropic-messages",
        "base_url": "https://api.minimaxi.com/anthropic",
        "model": "MiniMax-M2.7",
        "api_key": "<saved secret>",
        "max_tokens": 1200,
        "anthropic_version": "2023-06-01"
      }
    },
    "timeout_seconds": 60,
    "retry_attempts": 2
  }
}

Legacy single-slot settings are mapped to providers[provider] on read and are written back in the new shape on save.

Key Fallback

Each provider has its own key slot. Resolution order is:

  1. providers[provider].api_key in PostgreSQL
  2. the provider-specific variable in aiprovider/.env, such as OPENAI_API_KEY, MINIMAX_API_KEY, or ANTHROPIC_API_KEY
  3. the generic AI_API_KEY in aiprovider/.env

.env is only a fallback. After the settings page saves successfully, or after the connection test succeeds, PostgreSQL becomes the global default source.

Admin Next must compute key status per provider or tool:

  • If the database has a key for the current provider/tool, show configured.
  • If the database has no key but the fallback provider, model, or tool matches the current item, show the fallback masked preview.
  • If neither database nor matching fallback exists, show not configured; a generic .env key for another provider must not make this item appear configured.
  • Masking keeps the prefix before the first -, for example sk-********. Plaintext reveal is only available inside the authorized configuration page.

Tool keys follow the same rule. WebSearch and OCR must match the current tool and provider before they can use fallback credentials.

Lightweight Connectivity Testing

The Admin Next plug button performs a lightweight connectivity check and does not save configuration. Common API-platform practice is two-tiered:

  • Check a provider catalog or low-cost endpoint to validate base URL, authentication, and model reachability.
  • Send full model requests only when the user explicitly runs Playground or a business task.

Connectivity results should be explicit:

  • ok: authentication, route, and model catalog are usable.
  • warning: service is reachable, but the current model is missing from the catalog or capability metadata is incomplete.
  • error: authentication, network, protocol, or model lookup failed.

Toast titles must match the result; failures must not be titled as a successful connection.

OpenCode Go Routing Model

Subscription channels such as OpenCode Go should not be handled by hard-coded frontend model sets. Prefer provider catalog or backend capability discovery that records per-model capabilities such as chat_completions, anthropic_messages, models_endpoint, and whether a subscription key is required. The frontend should display capabilities; the backend should map provider, base URL, model, and adapter into the real request.

Settings Page Behavior

  • The Provider select controls the global default provider.
  • The model select saves the default model for the selected provider.
  • The LLM API Key field shows a masked preview while hidden; keys with a - prefix keep the prefix, for example sk-********, and keys without a prefix are fully masked.
  • Clicking the eye icon fetches and displays the full plaintext value; hiding restores the masked preview.
  • Save AI Configuration saves the current form as the global default.
  • Test Connection uses the current form for a real model-chain test, then saves it as the global default only when the test succeeds.
  • Leaving a key field empty keeps the old key; it does not delete it.

Backend

Recommended backend .env:

AI_PROVIDER_SERVICE_URL=http://localhost:8010
AI_PROVIDER_SERVICE_TOKEN=change_me
AI_PROVIDER_TIMEOUT_SECONDS=60
AI_PROVIDER_RETRY_ATTEMPTS=2

Reference file:

AI Provider

Reference file:

Frontend local reference:

Common settings:

SERVICE_NAME=planet-ai-provider
SERVICE_VERSION=0.1.0
AI_PROVIDER_SERVICE_TOKEN=change_me
AI_TIMEOUT_SECONDS=60
AI_HTTP_RETRY_ATTEMPTS=2

Optional provider-specific keys:

MINIMAX_API_KEY=sk-cp-xxxxx
OPENAI_API_KEY=sk-xxxxx
ANTHROPIC_API_KEY=sk-ant-xxxxx
DEEPSEEK_API_KEY=sk-xxxxx
DASHSCOPE_API_KEY=sk-xxxxx
MOONSHOT_API_KEY=sk-xxxxx
OPENROUTER_API_KEY=sk-or-xxxxx

OpenAI-compatible example

AI_PROVIDER=openai
AI_PROVIDER_API=openai-completions
AI_BASE_URL=http://127.0.0.1:8001/v1
AI_API_KEY=local-key
AI_MODEL=your-local-model

MiniMax CN example

AI_PROVIDER=minimax
AI_PROVIDER_API=anthropic-messages
AI_BASE_URL=https://api.minimaxi.com/anthropic
AI_API_KEY=sk-cp-xxxxx
AI_MODEL=MiniMax-M2.7
AI_MAX_TOKENS=1200
AI_ANTHROPIC_VERSION=2023-06-01

MiniMax note:

  • This follows the same Anthropic Messages request shape as the official MiniMax examples.
  • For MiniMax, aiprovider now disables thinking by default unless the caller explicitly passes a thinking object.
  • This mirrors OpenClaw's caution around MiniMax Anthropic-compatible behavior.

Anthropic-compatible example

AI_PROVIDER=anthropic
AI_PROVIDER_API=anthropic-messages
AI_BASE_URL=https://your-claude-compatible-endpoint.example.com/anthropic
AI_API_KEY=your_api_key
AI_MODEL=your-model
AI_MAX_TOKENS=1200
AI_ANTHROPIC_VERSION=2023-06-01

Ollama example

AI_PROVIDER=ollama
AI_PROVIDER_API=ollama-generate
AI_BASE_URL=http://127.0.0.1:11434
AI_API_KEY=
AI_MODEL=qwen2.5:7b

Deployment Modes

Single machine

Recommended local flow:

  • backend on localhost:8000
  • aiprovider on localhost:8010
  • local model gateway on localhost:11434 or another local port

Helpers already included:

Multi-machine

Example topology:

  • app machine: backend
  • AI gateway machine: aiprovider
  • model machine: local model service or cloud proxy

In that case, this becomes service-to-service HTTP RPC:

  • caller -> backend
  • backend -> http://10.0.0.12:8010
  • aiprovider -> model endpoint

Recommended cross-machine backend config:

AI_PROVIDER_SERVICE_URL=http://10.0.0.12:8010
AI_PROVIDER_SERVICE_TOKEN=change_me
AI_PROVIDER_TIMEOUT_SECONDS=60
AI_PROVIDER_RETRY_ATTEMPTS=2

Recommended operating rules:

  • keep aiprovider on a private network
  • protect it with X-Provider-Token at minimum
  • always send X-Request-ID
  • keep callers on the backend API unless they are infrastructure jobs

Retry And Failure Behavior

backend -> aiprovider:

  • retries lightweight network / 5xx failures
  • returns 502 when the provider service is unavailable

aiprovider -> model provider:

  • retries lightweight network / 5xx failures
  • returns 502 when the model provider is unavailable

This is intentionally conservative. It avoids masking persistent errors while still absorbing short hiccups.

Operational Notes

  • ./planet.sh start now starts aiprovider automatically
  • ./planet.sh restart -a restarts only aiprovider
  • ./planet.sh log -a tails aiprovider logs
  • ./planet.sh health reports aiprovider health
  • Frontend and application services: call backend
  • Scheduled infra jobs and diagnostics: optionally call aiprovider
  • Do not let multiple business services integrate model vendors independently

That keeps provider switching centralized and avoids model-specific drift across the system.