把 Agent 每次运行当一条 Trajectory 来测:Qwen Code 进代码库的可评估性观察

简介: 阿里通义开源终端编程Agent「Qwen Code」(GitHub:QwenLM/qwen-code),可读代码、改代码、执行命令。本文从测试视角提出三大关键实践:1)为AI PR设三重自动门禁(diff阈值/全量回归/静态扫描);2)将每次运行记录为可复现、可断言的结构化轨迹;3)用沙箱+快照验证最小权限与副作用边界。测试重心转向“行为可控性”。

阿里通义把一款终端编程 Agent「Qwen Code」开源了,仓库就挂在 GitHub 上(QwenLM/qwen-code),自述一句话:An open-source AI coding agent。这类工具的能力边界是能在你本地终端里读代码、改代码、跑命令——也就是说,它迟早会被某个团队接进真实代码库,被允许提 PR、进 CI。

问题不在于它跑分多高。跑分是排行榜的事,跟你的代码库质量没有直接关系。真正的问题是:当这样一个能自己改代码、自己执行命令的 Agent 进了你的仓库,测试该盯住什么?过去我们判断一段代码「能不能跑」,现在得判断它的行为「可不可评估」。这篇从测试工程师立场,提炼三件事。

一、一手事实:通义开源了终端编程 Agent『Qwen Code』

先把事实钉死,避免后面讨论悬空。据 GitHub 仓库(核实于 2026-09-17),阿里通义已开源终端编程 Agent「Qwen Code」,仓库地址 QwenLM/qwen-code,仓库自述为 An open-source AI coding agent。本篇只引用这两条定性事实——它已开源、它是一个 AI 编程 Agent;不引用任何跑分、参数或指标。

社区口径称其在 SWE-bench 上有较强表现(此为社区说法、非本篇核实,本篇不展开、也不拿分数当论据)。我们要谈的是另一件事:一个能读写文件、执行命令、自主提交改动的 Agent,一旦被接进团队代码库,测试的责任边界就从「验一段人写的代码」扩展到「验一个会自己产生改动的执行体」。

image.png

二、第一件事:它改的代码谁兜底——给 AI 生成改动挂回归门禁

人类提 PR 和 Agent 提 PR,风险面根本不是一回事。人写代码有意图、有上下文、改动通常克制;Agent 生成改动可能一次动几十个文件、可能改到你没让它改的地方、可能引入它自己都没意识到的副作用。评审的重点必须跟着变。

维度 传统人类 PR Agent 生成 PR
评审重点 设计是否合理、命名与风格、边界处理 改动范围是否越界、是否动了不该动的文件、是否引入隐性副作用
回归策略 增量回归,跑受影响模块的用例 全量回归 + diff 体积阈值,改动面大时必须整体重跑
风险面 逻辑错误、遗漏边界,通常可追溯到某次思考 大范围机械改动、跨模块连锁、无法追溯「它为什么这么改」
门禁方式 Code Review 通过即可合并 Review + 回归 + 静态扫描 + diff 阈值多道闸,任一不过即拦

结论是:Agent PR 不能享受和人类 PR 一样的「Review 通过即合并」待遇。它至少要过三道自动门禁——回归测试全绿、静态扫描无新增高危、diff 体积不超过阈值。第一段代码就是一份给 Agent PR 挂这三道检查的 GitHub Actions。

# .github/workflows/agent-pr-gate.yml
# 给「Agent 提交的 PR」挂 CI 门禁:回归 + 静态扫描 + diff 体积阈值
# 触发条件:PR 打了 agent-generated 标签(由机器人账号提交时自动打)
name: agent-pr-gate
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  gate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0          # 需要完整历史来算 diff 体积

      - name: Only guard agent-generated PRs
        id: is_agent
        run: |
          if echo '${
   { toJson(github.event.pull_request.labels.*.name) }}' | grep -q 'agent-generated'; then
            echo "agent=true" >> "$GITHUB_OUTPUT"
          else
            echo "agent=false" >> "$GITHUB_OUTPUT"
          fi

      # 门禁一:diff 体积阈值。Agent 一次动太多文件,直接拦下要求拆分
      - name: Diff size threshold
        if: steps.is_agent.outputs.agent == 'true'
        run: |
          CHANGED=$(git diff --numstat origin/${
   { github.base_ref }}...HEAD | wc -l)
          echo "changed files: $CHANGED"
          if [ "$CHANGED" -gt 20 ]; then
            echo "::error::Agent PR touched $CHANGED files (> 20). Split it."
            exit 1
          fi

      # 门禁二:全量回归。Agent 改动面不可控,不能只跑增量
      - name: Full regression
        if: steps.is_agent.outputs.agent == 'true'
        run: |
          pip install -r requirements.txt
          pytest -q --maxfail=1

      # 门禁三:静态扫描。只看新增高危,避免存量问题淹没信号
      - name: Static scan (new high-severity only)
        if: steps.is_agent.outputs.agent == 'true'
        run: |
          pip install bandit
          bandit -r src -ll -q -f json -o bandit.json || true
          HIGH=$(python -c "import json;d=json.load(open('bandit.json'));print(len([r for r in d['results'] if r['issue_severity']=='HIGH']))")
          echo "new high-severity issues: $HIGH"
          if [ "$HIGH" -gt 0 ]; then exit 1; fi

为什么这么写:三道闸的顺序是「先便宜后昂贵」。diff 体积阈值几乎零成本,却能在第一时间拦下 Agent 最典型的失控——一次改一大片。先卡体积再跑全量回归,避免为一个明显该被打回的 PR 白烧十几分钟 CI。回归用 --maxfail=1 是因为 Agent PR 我们要的是「有没有破」这个布尔结论,快速失败比收集全部失败更省时间。静态扫描只报新增高危(-ll),是因为存量代码的老问题不该由这一次 Agent PR 背锅,否则门禁天天红、最后被人一键跳过。踩过的坑:fetch-depth: 0 不能省,默认浅克隆拿不到 base 分支的完整历史,diff 体积会算错成「改了全部文件」,门禁直接把每个 PR 都拦死。

三、第二件事:它的行为可不可复现、可不可评估——把每次运行当一条 Trajectory

回归门禁解决的是「这次改动有没有破东西」,但没解决更深的问题:Agent 这次为什么这么改?下次同样输入它还会这么改吗?一个行为不可复现、不可归因的执行体,你没法对它做回归——因为「回归」的前提是「同样的输入应该给出可预期的行为」。

所以第二件事是把 Agent 的每次运行当成一条待测的 Trajectory(轨迹):把它读了哪些文件、调了哪些命令、每步的输入输出,按顺序落成结构化记录。有了这条 jsonl,你才能对它断言——比如「它不该读 .env」「它改代码前必须先跑过测试」。

维度 能跑就行 行为可评估
可复现 同一输入多次运行结果可能不同,无法重放 轨迹落盘,可重放、可比对两次运行的差异
可归因 只知道最终成功/失败,不知道中间发生了什么 每一步的输入输出留痕,能定位是哪一步走偏
可回归 无法回归——没有稳定行为可对照 把轨迹当断言对象,行为漂移能被自动检出

第二段代码是把 Agent 运行轨迹落成 jsonl 的骨架,供后续断言。

"""
trajectory_log.py —— 把 Agent 每次运行落成一条 jsonl 轨迹,供后续断言
真实场景:包装 Agent 的「读文件 / 执行命令 / 写文件」三类动作,
每发生一步就追加一行 JSON,运行结束得到一条可重放、可断言的 Trajectory。
运行自测:python trajectory_log.py
"""
import json
import time


class TrajectoryLogger:
    def __init__(self, run_id, path="trajectory.jsonl"):
        self.run_id = run_id
        self.path = path
        self._seq = 0

    def log(self, action, target, detail=None):
        """记录 Agent 的一步动作。action 如 read_file / exec_cmd / write_file。"""
        self._seq += 1
        record = {
   
            "run_id": self.run_id,
            "seq": self._seq,
            "ts": round(time.time(), 3),
            "action": action,
            "target": target,
            "detail": detail or {
   },
        }
        with open(self.path, "a", encoding="utf-8") as f:
            f.write(json.dumps(record, ensure_ascii=False) + "\n")
        return record


def assert_no_secret_read(path):
    """一条示例断言:轨迹里不允许出现读 .env 的步骤。"""
    with open(path, encoding="utf-8") as f:
        for line in f:
            r = json.loads(line)
            if r["action"] == "read_file" and r["target"].endswith(".env"):
                raise AssertionError(f"Agent 越权读取敏感文件: {r['target']}")
    return True


if __name__ == "__main__":
    import os
    if os.path.exists("trajectory.jsonl"):
        os.remove("trajectory.jsonl")
    log = TrajectoryLogger(run_id="run-demo-1", path="trajectory.jsonl")
    log.log("read_file", "src/pay.py")
    log.log("exec_cmd", "pytest -q", detail={
   "exit_code": 0})
    log.log("write_file", "src/pay.py", detail={
   "diff_lines": 6})
    print("轨迹条数:", sum(1 for _ in open("trajectory.jsonl", encoding="utf-8")))
    print("敏感文件断言:", assert_no_secret_read("trajectory.jsonl"))
    os.remove("trajectory.jsonl")

为什么这么写:轨迹用 jsonl 而不是一个大 JSON 数组,是因为 Agent 运行是流式的、可能中途崩,一行一条能保证崩之前记录的步骤不丢,也方便 grep/逐行断言。每条都带 run_idseq,是为了能把多次运行的同一 seq 对齐比对——这正是「行为漂移检测」的基础:同一输入跑两次,逐 seq 比对 action 与 target,不一致就说明行为不稳定。示例断言 assert_no_secret_read 演示的是「轨迹不只是日志,而是可测对象」:你把安全边界写成对轨迹的断言,Agent 一旦越界,回归里就会红。踩过的坑:轨迹里千万别只记「成功/失败」这一个终态,那等于没记——真正能归因的是中间每一步的 target 和 detail,终态是果、轨迹才是因。

四、第三件事:权限与副作用边界——最小权限与沙箱怎么测

终端编程 Agent 的危险不在它「会不会写错代码」,而在它「能做什么」。它能读写文件、能执行命令,就意味着一次幻觉、一次被污染的输入,可能让它删掉不该删的、跑一条不该跑的命令。这类副作用,回归测试是兜不住的——因为副作用发生在测试之外的真实文件系统里。

所以第三件事是把权限当测试对象:给 Agent 划定最小权限边界(能读哪些目录、能写哪些目录、能执行哪类命令),放进沙箱,然后专门测「越界会不会被拦住」。测法有三层。

第一层是目录边界:把 Agent 关进一个工作区,然后专门造一个「写工作区外路径」的动作,断言它被拒。比如让它去改 /etc/hosts 或工作区上一级的文件,正确行为是拒绝并报错,而不是真的落盘。第二层是命令白名单:终端 Agent 能执行命令,就等于能执行任何命令,除非你显式收窄。rm -rfcurl xxx | shgit push --force 这类高危命令必须被拦在沙箱里,测法是把它们逐条喂给 Agent,断言要么被策略拒绝、要么只在沙箱内生效。第三层是副作用回滚:一次运行结束后,比对宿主文件系统的快照,断言工作区之外零改动——这一层是前两层的兜底,前两层漏掉的越界,最终都会在这层的快照比对里现形。

这三层各写一条断言,挂在 Agent 每次运行之后,就是它的权限回归网。注意它和第二件事的轨迹是配合的:轨迹告诉你 Agent「想做什么」,沙箱断言告诉你它「实际被允许做了什么」,两者对不上,就是权限边界没兜住。

这件事和第一件事、第二件事是连着的:门禁拦的是「改动质量」,轨迹评的是「行为可复现」,沙箱守的是「副作用边界」。三者缺一,你就不该让一个开源编程 Agent 真的接进生产代码库——因为它的风险不是「写错一段代码」,而是「在你没盯着的时候,对整个仓库和终端做了不可追溯的事」。

五、给测试负责人的三条落地建议

第一,先给 Agent 账号打标签、单独走一条更严的 CI,别让它和人类 PR 共用门禁——它的风险面本来就更宽。第二,从第一天就把轨迹落盘,哪怕暂时不写断言;没有轨迹,将来想做行为回归时无从下手。第三,把权限边界写成可执行断言而不是文档约定,沙箱拦不住的边界等于没有边界。

开源编程 Agent 进了代码库,测试要回答的就不再是『它能不能跑』,而是『它的每一次行为,我能不能复现、能不能归因、能不能拦住副作用』。

你团队要接的第一个编程 Agent,回归门禁和轨迹落盘,准备先上哪一个?评论区聊聊。

相关文章
|
Linux 网络安全 Android开发
|
3天前
|
人工智能 供应链 JavaScript
别再手写用例了!DeepSeek Harness + Workbuddy 10分钟生成可评审用例
本文介绍如何用DeepSeek Harness(DSH)与腾讯Workbuddy协同,10分钟自动生成高质量测试用例:DSH提供执行能力,Workbuddy提供模型与规范封装;支持PRD/接口文档输入,覆盖正常流、异常场景与边界值。手写低效,AI初稿+人工复核才是提效关键。(239字)
|
28天前
|
人工智能 NoSQL 测试技术
AI岗位渗透率升至37.56%:2026届秋招,测试开发应届生的准备方式也该变了
2026秋招AI岗位激增47.3%,渗透率达37.56%,但门槛同步升高:简历堆砌AI术语难过关,真能力看项目深度。应届生需夯实测试开发基础,再以RAG、Agent等真实AI测试项目体现工程力——会用AI不值钱,能测AI才稀缺。
|
7天前
|
人工智能 供应链 测试技术
DeepSeek Harness火了,但你知道怎么用它生成测试用例吗?
DeepSeek Harness是开源AI测试助手,一行命令即可启动。它能自动解析API文档,10分钟生成50+覆盖等价类、边界值与异常场景的测试用例,准确率高但需人工复核8条左右。专为测试工程师设计,大幅提升用例设计效率,降低重复劳动。
|
1月前
|
人工智能 自然语言处理 Java
RAG系统测试实战:如何验证企业知识库AI助手是否可靠?
近两年,企业纷纷构建基于RAG的AI应用:不训大模型,而是将产品文档、制度流程等内部知识接入,通过检索增强生成实现智能问答。但其质量保障远超传统测试——需覆盖知识库完整性、检索准确性、生成忠实度与答案相关性等多层验证,是AI时代测试工程师的核心新能力。
RAG系统测试实战:如何验证企业知识库AI助手是否可靠?
|
28天前
|
SQL 人工智能 算法
AI岗位渗透率升到37.56%:测试岗正在分成“新旧两种人”
2026秋招AI岗位渗透率达37.56%,测试岗正加速分层:传统执行岗溢价消失,懂AI应用、Agent工程、LLM评估的全栈测开人才需求暴增340%,薪资高30%-50%。转型,刻不容缓。
|
2月前
|
设计模式 人工智能 安全
从代码生成到需求交付:一个开发 Skill 的工程化实践
腾讯团队提出AI编程新范式:将需求交付拆解为8阶段工程流程,融合项目知识库、自动化工具与质量门禁。虽代码生成率达94%,但核心突破在于把研发经验转化为可执行规则——AI不再仅写代码,而是在严格约束下完成端到端交付。
|
2月前
|
关系型数据库 MySQL 数据安全/隐私保护
Docker 部署 Redmine:老牌开源项目管理部署实测记录
团队用 Jira、禅道、飞书项目,数据不在自己手里?Redmine 是一款开源、可自托管的项目管理与问题跟踪系统——支持 工单、甘特图、Wiki、版本库、时间跟踪,Docker Compose 双容器(Redmine + MySQL)即可跑通。浏览器打开就能建项目、派任务、看进度,数据全在你自己的服务器上。 本文带你完成一次 Redmine Docker Compose 私有化部署:镜像加速拉取、编写 docker-compose.yml、处理 3000 端口冲突、读懂启动日志,到浏览器 admin 登录、强制改密、切换中文、载入默认配置、新建第一个项目——全程零基础可跟做。
539 0
Docker 部署 Redmine:老牌开源项目管理部署实测记录
|
2月前
|
数据采集 存储 监控
选品比价API:电商运营的“数据雷达”
选品比价API是电商运营的“数据雷达”,可实时抓取全网商品价格、销量、评价等核心数据,支持智能选品、动态定价、竞品监控与趋势分析,助力运营从经验驱动升级为数据驱动,提升决策效率与市场竞争力。(239字)
|
3月前
|
存储 缓存 安全
Ios云手机数据安全存储技术解析 2026 云端账号镜像加密指南
2026年云端账号托管安全风险凸显:低价服务频现缓存泄露、镜像复用、明文存储等问题。本文详解临时缓存、明文快照、私有加密对象存储三大架构差异,指出AES256端到端加密+设备级密钥隔离的方案,方能兼顾iOS合规性、环境稳定与隐私安全。(239字)

热门文章

最新文章