系列 · 阿里云百炼 · 第 6 篇

阿里云百炼(六):Qwen3-Coder 与编码 Agent

在 Model Studio 上调用 Qwen3-Coder-Plus、驱动 qwen-code CLI,再用三个环境变量把它接进 Claude Code。

这个系列刚开始时,「代码」只是 Qwen-Plus 那篇里的一个脚注。如今它在百炼上已经是一条独立的产品线。coder 系列模型不再是「碰巧 Python 写得不错的 LLM」,而是按 agentic 循环训练出来的:读仓库、改文件、跑测试、看报错、再改一遍。本文讲我实际怎么用它们,既包括裸 API,也包括 qwen-code CLI。


qwen3-coder-plus 到底是什么#

旗舰是 Qwen3-Coder-480B-A35B-Instruct,一个 480B 参数的 Mixture-of-Experts 模型,每个 token 激活 35B 参数(160 个 expert 里每次前向激活 8 个)。在 Model Studio 上你不用碰这个全名,直接调用服务端别名 qwen3-coder-plus 即可。还有两个值得知道的兄弟型号:

  • qwen3-coder-plus,主力。质量最好,任何不平凡的代码任务我都默认用它。
  • qwen3-coder-flash,更便宜更快,适合补全式或高频调用、能容忍一点质量下降的场景。
  • qwen3-coder-next,更新的混合注意力小模型,为本地化 agentic 循环而生,推理成本低很多。

两个对生产规划要紧的数字:

  • 上下文:原生 256K token,靠外推(YaRN)可达 1M。 这是核心卖点。256K 窗口意味着一个中等规模的仓库能一次性塞进会话,你不用再纠结「该检索哪些文件」这个困扰所有 RAG-over-code 方案的问题。
  • SWE-bench Verified:发布时约 67%,配合多轮(500 轮)agentic 跑能爬到约 70%。 SWE-bench Verified 是带人工验证答案的真实 GitHub issue,所以该看的是多轮那个数字:它反映的是读-改-测-重复这条循环,而这正是模型被设计来用的方式。

注意: coder 系列模型不支持 thinking mode。别给 qwen3-coder-plusenable_thinking=True,好的情况下也只是个空操作。规划行为已经在 agentic 训练里内化了;你靠 tool call 驱动它,而不是靠一个推理开关。

裸 API 调用#

Qwen3-Coder 在 OpenAI 兼容端点后面就是另一个模型,没什么特殊:

 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"],
    # 中国大陆:
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    # 海外改用国际站端点:
    # 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)

代码任务我把 temperature 锁在 0.0。代码要的是确定性;同一个 prompt 应该给出同一份 diff。真正有意思的不是这次调用,而是 function calling,把模型变成能改文件的 agent,靠的就是它。

搭一个 agent:文件编辑工具循环#

Agent 文件编辑循环:读取、编辑、运行直到完成
Agent 循环:qwen3-coder-plus 发出工具调用,宿主在仓库上执行,观测结果回灌给模型,直到它不再调用工具。

agentic 的价值来自给模型工具再循环。tool-calling 协议跟第二篇讲的完全一致,toolstool_calls、append assistant message、每个调用 append 一条 tool message。对代码来说,这些工具就是文件系统操作:

 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."))

那个 run_tests 工具是全部精髓所在。没有它,模型在瞎猜;有了它,循环就变成「改、看报错、再改」,正是 SWE-bench 测的那套工作流。实践中我把 max_rounds 封在 12,并且像第二篇说的,绝不让循环无限跑,一个带 write_file 工具又没上限的 coder 模型,是搭进去一个周五的好办法。

qwen-code:你真正泡在里面的 CLI#

大多数日子我不会手搓上面那个循环,qwen-code 是官方开源 CLI(Apache 2.0),自带 read/write/run/grep 工具、sub-agent 以及一套类 Claude Code 的终端体验,整套都针对 Qwen coder 系列调过。用 Node 22+ 装:

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

把它指向一个 Model Studio key 来鉴权。创建 ~/.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
}

然后 cd 进仓库跑 qwen。会话里 /auth 切换鉴权方式,/model 在你配过的模型间切换。每天用下来有两条实用经验:

  • 国际站 key 就用国际站 baseUrldashscope-intl...)。region 和 key 对不上,是「401 invalid api key」最常见的原因。
  • 免费的 Qwen OAuth 档已经没了(2026 年 4 月下线)。正经活儿要么带 Model Studio API key,要么订阅 Coding Plan,按月固定费用、配额更高,替代按 token 计费。

把 Qwen3-Coder 接进 Claude Code#

如果你已经泡在 Claude Code 里,不必换 CLI,百炼暴露了一个 Anthropic 兼容端点,所以 Qwen3-Coder 靠三个环境变量就能接进去:

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

现在 Claude Code 的工具循环驱动的就是 Qwen 模型。我用这招在不改工作流的前提下对同一个任务做 A/B:把 ANTHROPIC_MODEL 在前沿模型和 qwen3-coder-plus 之间切换,跑同一个 prompt,对比 diff。对一次成本敏感的大型重构,256K 上下文刚好能把整个模块塞下,coder 模型常常是更划算的选择。

成本与上下文纪律#

第二篇那套规则照样适用,外加一条 coder 特有的:按输入长度分档计价。请求一旦越过 128K 输入 token,就进入更高的价格档。所以「把整个 256K 仓库塞进上下文」确实可行但不免费,能压在 128K 以内我就压住,只有真正需要全仓推理时才越过。要度量它:每个响应里都有 usage.prompt_tokens,一个每轮都重读同样五个文件的编码 agent 膨胀得很快。在工具层缓存文件内容,只在写入后才重读。

接下来#

第七篇讲内置工具web_searchweb_extractorcode_interpreter,Model Studio 通过 Responses API 在服务端跑它们。它们能把一次普通 LLM 调用变成迷你 agent,而你完全不用自己写工具循环,跟本文讲的一切天然契合。

本系列

阿里云百炼 7 篇

  1. 01 阿里云百炼(一):平台概览与第一个请求
  2. 02 阿里云百炼(二):Qwen API 生产接入
  3. 03 阿里云百炼(三):Qwen-Omni 多模态理解
  4. 04 阿里云百炼(四):万相视频生成端到端
  5. 05 阿里云百炼(五):Qwen-TTS 语音合成
  6. 06 阿里云百炼(六):Qwen3-Coder 与编码 Agent 当前
  7. 07 阿里云百炼(七):内置 Web Search 与 Code Interpreter

读有所得?

在 GitHub 关注我 → 新文大约每周一篇

GitHub