Series · Aliyun Bailian · Chapter 6

Aliyun Bailian (6): Qwen3-Coder and Coding Agents

Calling Qwen3-Coder-Plus on Model Studio, driving the qwen-code CLI, and wiring it into Claude Code with three env vars.

When this series started, “code” was a footnote in the Qwen-Plus article. It is now its own product surface on Bailian. The coder models are no longer just LLMs that happen to write Python well — they are trained for the agentic loop: read a repo, edit files, run tests, read the failures, edit again. This article is how I actually use them, both through the raw API and through the qwen-code CLI.


What qwen3-coder-plus actually is#

The flagship is Qwen3-Coder-480B-A35B-Instruct — a 480B-parameter Mixture-of-Experts model with 35B active parameters per token (8 of 160 experts fire on each forward pass). On Model Studio you don’t touch that name; you call the served alias qwen3-coder-plus. There are two siblings worth knowing:

  • qwen3-coder-plus — the workhorse. Best quality, the one I default to for any non-trivial code task.
  • qwen3-coder-flash — cheaper and faster, for autocomplete-style or high-volume tasks where you can tolerate a quality dip.
  • qwen3-coder-next — the newer hybrid-attention small model, built for local-style agentic loops at much lower inference cost.

Two numbers that matter for production planning:

  • Context: 256K tokens natively, up to 1M with extrapolation (YaRN). This is the headline feature. A 256K window means a mid-sized repository fits in one session — you stop fighting the “which files do I retrieve” problem that dominates RAG-over-code setups.
  • SWE-bench Verified: roughly 67% at release, climbing toward ~70% with multi-turn (500-turn) agentic runs. SWE-bench Verified is real GitHub issues with human-verified solutions, so the multi-turn number is the one to read: it reflects the read-edit-test-repeat loop, which is how the model is meant to be used.

Note: the coder models do not support thinking mode. Don’t pass enable_thinking=True to qwen3-coder-plus — it’s a no-op at best. The agentic training already bakes in the planning behavior; you drive it with tool calls, not with a reasoning toggle.

The raw API call#

Qwen3-Coder is just another model behind the OpenAI-compatible endpoint. Nothing special:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["DASHSCOPE_API_KEY"],
    # China mainland:
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    # Outside China, use the intl endpoint instead:
    # base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)

resp = client.chat.completions.create(
    model="qwen3-coder-plus",
    messages=[
        {"role": "system", "content": "You are a senior backend engineer. Output only code unless asked otherwise."},
        {"role": "user", "content": "Write a FastAPI endpoint that streams an LLM response as SSE."},
    ],
    temperature=0.0,
)
print(resp.choices[0].message.content)

I keep temperature=0.0 for code. Code wants determinism; the same prompt should give the same diff. The interesting part isn’t this call — it’s function calling, which is how you turn the model into an agent that edits your files.

Building an agent: the file-edit tool loop#

The agentic file-edit loop: read, edit, run until done
The agentic loop: qwen3-coder-plus emits a tool call, the host executes it on the repo, and the observation feeds back into the model until it stops calling tools.

The agentic value comes from giving the model tools and looping. The tool-calling protocol is identical to what article 2 covered — tools, tool_calls, append the assistant message, append a tool message per call. For coding, the tools are filesystem operations:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import json, os, subprocess
from openai import OpenAI

client = OpenAI(api_key=os.environ["DASHSCOPE_API_KEY"],
                base_url="https://dashscope.aliyuncs.com/compatible-mode/v1")

tools = [
    {"type": "function", "function": {
        "name": "read_file",
        "description": "Read a file's full contents",
        "parameters": {"type": "object",
                       "properties": {"path": {"type": "string"}},
                       "required": ["path"]}}},
    {"type": "function", "function": {
        "name": "write_file",
        "description": "Overwrite a file with new contents",
        "parameters": {"type": "object",
                       "properties": {"path": {"type": "string"},
                                      "content": {"type": "string"}},
                       "required": ["path", "content"]}}},
    {"type": "function", "function": {
        "name": "run_tests",
        "description": "Run the test suite and return stdout+stderr",
        "parameters": {"type": "object", "properties": {}}}},
]

def dispatch(name, args):
    if name == "read_file":
        return {"content": open(args["path"]).read()}
    if name == "write_file":
        open(args["path"], "w").write(args["content"])
        return {"ok": True}
    if name == "run_tests":
        p = subprocess.run(["pytest", "-q"], capture_output=True, text=True)
        return {"exit_code": p.returncode, "output": p.stdout + p.stderr}

def agent(task, max_rounds=12):
    messages = [{"role": "user", "content": task}]
    for _ in range(max_rounds):
        resp = client.chat.completions.create(
            model="qwen3-coder-plus", messages=messages,
            tools=tools, parallel_tool_calls=True, temperature=0.0)
        msg = resp.choices[0].message
        messages.append(msg)
        if not msg.tool_calls:
            return msg.content
        for call in msg.tool_calls:
            result = dispatch(call.function.name, json.loads(call.function.arguments))
            messages.append({"role": "tool", "tool_call_id": call.id,
                             "content": json.dumps(result, ensure_ascii=False)})
    raise RuntimeError("agent did not converge")

print(agent("The failing test in tests/test_parser.py — read it, find the bug in src/parser.py, fix it, re-run tests until green."))

That run_tests tool is the whole game. Without it the model is guessing; with it, the loop becomes “edit, observe the failure, edit again” — exactly the workflow SWE-bench measures. In practice I cap max_rounds at 12 and, like in article 2, I never let the loop run unbounded — a coder model with a write_file tool and no cap is a way to lose a Friday.

qwen-code: the CLI you actually live in#

Most days I don’t hand-roll the loop above — qwen-code is the official open-source CLI (Apache 2.0) that ships the read/write/run/grep tools, sub-agents, and a Claude-Code-like terminal UX, all tuned for the Qwen coder models. Install it with Node 22+:

1
2
npm install -g @qwen-code/qwen-code
qwen --version   # expect >= 0.11.x

Authenticate by pointing it at a Model Studio key. Create ~/.qwen/settings.json:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "env": { "BAILIAN_API_KEY": "YOUR_MODEL_STUDIO_KEY" },
  "modelProviders": {
    "openai": [
      {
        "id": "qwen3-coder-plus",
        "name": "[Bailian] qwen3-coder-plus",
        "baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1",
        "envKey": "BAILIAN_API_KEY"
      }
    ]
  },
  "security": { "auth": { "selectedType": "openai" } },
  "model": { "name": "qwen3-coder-plus" },
  "$version": 3
}

Then cd into a repo and run qwen. Inside the session, /auth switches auth methods and /model switches between any models you configured. Two practical notes from running it daily:

  • Use the intl baseUrl (dashscope-intl...) if your key is a Singapore-region key. Mixing region and key is the most common “401 invalid api key” cause.
  • The free Qwen OAuth tier is gone (discontinued April 2026). For real work, bring a Model Studio API key or subscribe to the Coding Plan, which gives a flat monthly fee with higher quotas instead of per-token billing.

Wiring Qwen3-Coder into Claude Code#

If you already live in Claude Code, you don’t have to switch CLIs — Bailian exposes an Anthropic-compatible endpoint, so Qwen3-Coder drops in with three environment variables:

1
2
3
4
export ANTHROPIC_BASE_URL="https://dashscope.aliyuncs.com/apps/anthropic"
export ANTHROPIC_AUTH_TOKEN="YOUR_MODEL_STUDIO_KEY"
export ANTHROPIC_MODEL="qwen3-coder-plus"
claude

Now Claude Code’s tool loop drives the Qwen model. I use this to A/B the same task across models without changing my workflow: flip ANTHROPIC_MODEL between a frontier model and qwen3-coder-plus, run the same prompt, compare the diffs. For a large, cost-sensitive refactor where the 256K context lets the whole module fit, the coder model is frequently the better trade.

Cost and context discipline#

The same rules from article 2 apply, with one coder-specific twist: pricing tiers by input length. Once a request crosses 128K input tokens you move into the higher price band. So “stuff the whole 256K repo into context” is real but not free — I keep the working set under 128K when I can, and only blow past it for genuinely repo-wide reasoning. Measure it: usage.prompt_tokens is in every response, and a coding agent that re-reads the same five files every round bloats fast. Cache the file contents in your tool layer and only re-read on a write.

What’s next#

Article 7 is the built-in toolsweb_search, web_extractor, and code_interpreter — that Model Studio runs server-side through the Responses API. They turn a plain LLM call into a mini-agent without you writing the tool loop at all, which pairs naturally with everything in this article.

In this series

Aliyun Bailian 7 parts

  1. 01 Aliyun Bailian (1): Platform Overview and First Request
  2. 02 Aliyun Bailian (2): The Qwen LLM API in Production
  3. 03 Aliyun Bailian (3): Qwen-Omni for Video, Audio, and Image Understanding
  4. 04 Aliyun Bailian (4): Wanxiang Video Generation End-to-End
  5. 05 Aliyun Bailian (5): Qwen-TTS for Multilingual Voice
  6. 06 Aliyun Bailian (6): Qwen3-Coder and Coding Agents you are here
  7. 07 Aliyun Bailian (7): Built-in Web Search and Code Interpreter

Liked this piece?

Follow on GitHub for the next one — usually one a week.

GitHub