跳到正文
DEV Community· mech.app·· 2 小時前AI 評分48

Qwen Code 的多協定 Agent Runtime:終端 Agent 如何在執行時切換 OpenAI、Anthropic 和本地模型

Qwen Code's Multi-Protocol Agent Runtime: How One Terminal Agent Switches Between OpenAI, Anthropic, and Local Models at Runtime

AI 導讀

Qwen Code 可在對話進行期間切換 OpenAI、Anthropic、Gemini、Qwen API 和本地模型,無須重寫工具定義或重新啟動工作階段。它透過統一介面轉換工具定義和回應格式,並在切換供應商時重新整理對話記錄,以延續狀態和上下文。本地模型以 JSON 格式處理工具呼叫;若回傳格式錯誤,系統會用澄清提示詞重試一次。

正文

Qwen Code is a terminal-based AI coding agent that lets you switch between OpenAI, Anthropic, Gemini, Qwen APIs, and local models (Ollama, vLLM) without rewriting tool definitions or restarting the session. It ships with Auto-Memory, Auto-Skills, SubAgents, Agent Teams, and MCP support. The project has 28.8K stars and is trending #9 on GitHub for TypeScript. The team uses the agent to file issues, submit PRs, and review code on its own repository.

The interesting plumbing is how it maintains state and context when you swap providers mid-conversation, and how the same tool definitions work across OpenAI function calling, Anthropic tool use, and local model APIs that may not support structured outputs.

Architecture: Provider Abstraction and Tool Normalization

Qwen Code uses a provider abstraction layer that normalizes tool definitions and response formats across different LLM APIs. The agent does not call OpenAI or Anthropic directly. Instead, it routes requests through a unified interface that translates tool schemas and response payloads.

Key components:

  • Provider registry: Maps provider names (openai, anthropic, gemini, qwen, ollama, vllm) to adapter classes.
  • Tool schema normalizer: Converts internal tool definitions to OpenAI function calling, Anthropic tool use, or plain JSON schemas for local models.
  • Response parser: Extracts tool calls from different response formats (OpenAI's tool_calls, Anthropic's content blocks, or regex-parsed JSON from local models).
  • State manager: Persists conversation history, tool results, and memory entries in a local SQLite database or JSON file.

When you switch providers, the agent re-serializes the conversation history into the new provider's format. OpenAI and Anthropic use different message structures for tool calls, so the adapter rewrites the history array before sending the next request.

Tool Call Flow Across Providers

Here's how a single tool definition flows through different providers:

Provider Tool Schema Format Tool Call Extraction Fallback Behavior
OpenAI functions or tools array Parse tool_calls from response Retry with plain JSON if model lacks tools
Anthropic tools array Parse content blocks with tool_use Retry with plain JSON if model lacks tools
Gemini function_declarations Parse functionCall from response Retry with plain JSON if model lacks tools
Qwen API tools array (OpenAI-like) Parse tool_calls from response Retry with plain JSON if model lacks tools
Ollama Plain JSON in system prompt Regex parse JSON from text response No fallback, fails if JSON is malformed
vLLM Plain JSON in system prompt Regex parse JSON from text response No fallback, fails if JSON is malformed

For local models, the agent injects a system prompt that describes available tools as JSON schemas and instructs the model to return tool calls in a specific format. The response parser uses regex to extract JSON blocks. If the model returns malformed JSON, the agent retries once with a clarification prompt.

Auto-Memory and Auto-Skills: Persistence and Skill Generation

Auto-Memory persists conversation history, tool results, and user preferences in a local SQLite database. Each session has a unique ID. When you switch providers, the agent loads the session history and re-serializes it for the new provider. Memory entries include:

  • Raw message arrays (user, assistant, tool)
  • Tool call metadata (function name, arguments, result)
  • User preferences (preferred provider, temperature, max tokens)

Auto-Skills generates new tool definitions at runtime based on repeated patterns. If the agent detects that you ask for the same multi-step workflow three times, it proposes a new skill. The skill is stored as a JSON file in ~/.qwen-code/skills/ and registered in the tool schema normalizer. The next time you invoke that workflow, the agent calls the skill directly instead of re-planning.

Skill generation uses a separate LLM call with a prompt that includes the conversation history and asks the model to extract a reusable function signature. The agent validates the generated schema and tests it once before persisting.

SubAgents and Agent Teams: Nested Orchestration

SubAgents are child agent instances that run in isolated contexts. The parent agent spawns a SubAgent by calling a special tool (spawn_subagent) with a task description and optional provider override. The SubAgent runs its own conversation loop and returns a final result to the parent.

Agent Teams are collections of SubAgents with different roles (planner, coder, reviewer). The parent agent routes tasks to the appropriate SubAgent based on a role classifier. The classifier is a small local model (Qwen2.5-0.5B) that runs inference in under 50ms. If the classifier is unavailable, the agent falls back to keyword matching.

SubAgents share the same tool registry as the parent but maintain separate conversation histories. If a SubAgent switches providers, it does not affect the parent's provider state.

MCP Support: External Tool Integration

Qwen Code supports the Model Context Protocol (MCP) for integrating external tools. MCP servers expose tools as JSON-RPC endpoints. The agent discovers available tools by calling the server's tools/list method and registers them in the tool schema normalizer.

When the agent invokes an MCP tool, it sends a JSON-RPC request to the server and waits for the response. The response is appended to the conversation history as a tool result. MCP tools can be stateful (the server maintains state between calls) or stateless.

The agent does not validate MCP tool schemas. If the server returns a malformed response, the agent logs the error and continues. This can cause the agent to hallucinate tool results.

Security Boundaries: Sandboxing and Credential Management

Qwen Code runs tool calls in a sandboxed subprocess. The sandbox uses Node.js's vm module to isolate tool execution. File system access is restricted to the current working directory. Network access is unrestricted.

Credential management:

  • API keys are stored in ~/.qwen-code/config.json or environment variables.
  • The agent does not send API keys to SubAgents or MCP servers.
  • If a tool requires credentials (e.g., GitHub API), the user must configure them manually.

The sandbox does not prevent tools from spawning child processes or making network requests. A malicious tool can exfiltrate data or execute arbitrary code. The agent logs all tool calls and results, but does not block suspicious behavior.

Observability: Logging and Debugging

The agent logs all LLM requests, tool calls, and state transitions to ~/.qwen-code/logs/. Logs are structured JSON with timestamps, session IDs, and provider names. You can tail the log file to watch the agent's decision-making in real time.

Debugging flags:

  • --debug: Enables verbose logging (includes full request/response payloads).
  • --trace: Enables trace-level logging (includes internal state changes).
  • --dry-run: Simulates tool calls without executing them.

The agent does not expose metrics or telemetry by default. You can integrate with observability platforms (Datadog, Prometheus) by parsing the log files.

Deployment Shape: CLI, Desktop, Web, and Chat Integrations

Qwen Code ships as:

  • CLI: Standalone binary installed via curl script.
  • Desktop app: Electron wrapper with system tray integration.
  • Web UI: React app that connects to a local agent server.
  • IDE plugins: VS Code, Zed, JetBrains extensions.
  • Chat integrations: Telegram, DingTalk, WeChat, Feishu bots.

All deployment shapes share the same agent core. The CLI and desktop app run the agent locally. The web UI and chat integrations connect to a remote agent server via WebSocket. The IDE plugins use language server protocol (LSP) to communicate with the agent.

The agent server is a Node.js HTTP server that exposes a REST API for starting sessions, sending messages, and retrieving history. WebSocket connections are used for streaming responses.

Failure Modes and Recovery

Common failure modes:

  • Provider rate limits: The agent retries with exponential backoff (max 3 retries). If all retries fail, it prompts the user to switch providers.
  • Malformed tool calls: The agent retries once with a clarification prompt. If the retry fails, it logs the error and continues without executing the tool.
  • SubAgent hangs: The parent agent sets a 60-second timeout for SubAgent tasks. If the timeout expires, the parent kills the SubAgent and returns an error.
  • MCP server unavailable: The agent logs the error and removes the MCP tools from the registry. It does not retry.

The agent does not checkpoint state during long-running tasks. If the process crashes, you lose the current session. You can resume from the last persisted message in the SQLite database, but tool results are not replayed.

Code Example: Provider Switching

Here's how the agent switches providers mid-conversation:

// Internal provider adapter interface
interface ProviderAdapter {
  name: string;
  sendMessage(history: Message[], tools: Tool[]): Promise<Response>;
  parseToolCalls(response: Response): ToolCall[];
}

// Switching providers
async function switchProvider(
  session: Session,
  newProvider: string
): Promise<void> {
  const adapter = providerRegistry.get(newProvider);
  if (!adapter) throw new Error(`Unknown provider: ${newProvider}`);

  // Re-serialize conversation history for new provider
  const history = session.history.map(msg => 
    adapter.normalizeMessage(msg)
  );

  // Update session state
  session.provider = newProvider;
  session.history = history;
  await session.save();
}

// Example: OpenAI to Anthropic
const session = await loadSession('abc123');
await switchProvider(session, 'anthropic');
const response = await session.sendMessage('Refactor this function');

The normalizeMessage method rewrites message objects to match the new provider's schema. OpenAI uses role and content, Anthropic uses role and content arrays with type fields.

Technical Verdict

Use Qwen Code when:

  • You need to switch between cloud and local models without rewriting tool definitions.
  • You want a terminal-based agent that integrates with your existing IDE and chat tools.
  • You are building agentic workflows that require SubAgents or Agent Teams.
  • You need MCP support for external tool integration.

Avoid Qwen Code when:

  • You require strong sandboxing or credential isolation (the sandbox is weak).
  • You need checkpointing or replay for long-running tasks (state is not checkpointed).
  • You are building a production system that requires telemetry and metrics (observability is limited to log files).
  • You need guaranteed tool call reliability (local models can return malformed JSON).

The multi-protocol abstraction is solid for prototyping and side projects. For production systems, you will need to add checkpointing, stronger sandboxing, and structured observability.

Source Links

來源:DEV Community · dev.to