让 AI Coding 真正进入交付链路:Harness 全链路研发智能体实践

2026-07-21 23 预计阅读时间: 1 分钟
来源: my.oschina.net AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:12 分钟

AI Coding 已经能快速补全函数、生成模块,甚至完成小型需求,但代码生成只占研发工时的一部分。来源复盘给出的比例是 10% 到 32%:其余时间仍消耗在需求澄清、接口对齐、代码审查、测试、部署和故障排查上。真正影响交付速度的,不是模型一次能写多少代码,而是它能否在一套可验证、可回滚、可追踪的流程中持续推进任务。

Harness 全链路研发智能体的核心,就是把大模型放进研发控制回路:每个阶段读取明确输入、产出结构化制品,由工具执行验证,再根据结果决定继续、重试、修复还是交给工程师处理。

从代码生成器升级为交付执行器

传统 AI Coding 往往采用一次性对话:开发者描述需求,模型输出代码,后续工作重新回到人工流程。这里有三个明显断点。

  • 模型不知道需求是否已经被正确理解,容易在错误假设上继续实现。
  • 生成代码与仓库的构建、测试、权限和部署规则脱节。
  • 测试失败后,错误日志没有自动回流,模型无法形成稳定的修复循环。

全链路智能体需要把研发任务拆成可检查的状态转换。例如,一个后端需求可以经过以下阶段:

  1. 将需求整理为验收条件、约束和待确认问题。
  2. 生成或更新 OpenAPI、数据库迁移等接口制品。
  3. 根据仓库规范修改代码。
  4. 执行静态检查、单元测试和安全扫描。
  5. 生成自动代码审查意见,并区分阻断项与建议项。
  6. 部署到临时环境,执行冒烟验证。
  7. 收集失败日志,定位问题并发起有限次数的修复。
  8. 达到质量门槛后提交人工审批。

这条链路的重点不是让模型掌握更多提示词,而是让每个阶段都有机器可判断的完成条件。比如,不能用“测试看起来没问题”作为结论,而应记录测试命令、退出码、失败用例和覆盖率报告位置。

用制品、状态和质量门禁构造闭环

一套可用的 Harness 通常需要三类工程对象。

制品是阶段之间传递的证据,包括需求说明、接口定义、代码补丁、审查结果、测试报告、部署地址和日志。模型应读取制品,而不是依赖越来越长的聊天记录。

状态描述当前任务进行到了哪里。推荐将状态持久化为 JSON 或数据库记录,至少包含任务 ID、仓库提交、阶段、执行次数、制品路径和人工审批结果。这样即使执行器中断,也可以从最近一次成功阶段继续。

质量门禁决定流程能否前进。门禁应尽量由确定性工具执行,例如:

  • OpenAPI 校验器检查接口定义;
  • 编译器和 linter 检查代码;
  • 测试框架验证行为;
  • 依赖与密钥扫描器检查安全风险;
  • kubectl rollout status 判断部署是否完成;
  • HTTP 探针验证关键路径。

模型可以解释失败、提出修改并生成补丁,但不应自己宣布验证成功。执行结果必须来自真实工具。

一个可改造的最小 Harness

下面是一个概念性最小项目。假设仓库已经提供 make lintmake testmake smoke,智能体负责按顺序调用它们。示例不绑定某个模型 API,重点展示阶段、证据和失败反馈如何落盘。

先创建 harness.yaml

task_id: add-order-query-api
max_fix_attempts: 2
stages:
  - name: lint
    command: make lint
    blocking: true
  - name: unit_test
    command: make test
    blocking: true
  - name: smoke_test
    command: make smoke
    blocking: true

再创建 harness.py。运行前安装 PyYAML,并把 YAML 中的命令改成项目真实命令。

from __future__ import annotations

import json
import subprocess
import sys
import time
from pathlib import Path

import yaml

CONFIG = Path("harness.yaml")
ARTIFACTS = Path("artifacts")


def run_stage(stage: dict) -> dict:
    started_at = time.time()
    process = subprocess.run(
        stage["command"],
        shell=True,
        text=True,
        capture_output=True,
    )
    return {
        "name": stage["name"],
        "command": stage["command"],
        "exit_code": process.returncode,
        "duration_seconds": round(time.time() - started_at, 2),
        "stdout": process.stdout[-8000:],
        "stderr": process.stderr[-8000:],
        "passed": process.returncode == 0,
    }


def main() -> int:
    config = yaml.safe_load(CONFIG.read_text(encoding="utf-8"))
    task_dir = ARTIFACTS / config["task_id"]
    task_dir.mkdir(parents=True, exist_ok=True)

    state = {
        "task_id": config["task_id"],
        "status": "running",
        "results": [],
    }

    for stage in config["stages"]:
        result = run_stage(stage)
        state["results"].append(result)
        (task_dir / f'{stage["name"]}.json').write_text(
            json.dumps(result, ensure_ascii=False, indent=2),
            encoding="utf-8",
        )

        if not result["passed"] and stage.get("blocking", True):
            state["status"] = "needs_fix"
            state["failed_stage"] = stage["name"]
            state["feedback_file"] = str(task_dir / f'{stage["name"]}.json')
            break
    else:
        state["status"] = "ready_for_review"

    (task_dir / "state.json").write_text(
        json.dumps(state, ensure_ascii=False, indent=2),
        encoding="utf-8",
    )
    print(json.dumps(state, ensure_ascii=False, indent=2))
    return 0 if state["status"] == "ready_for_review" else 1


if __name__ == "__main__":
    sys.exit(main())

执行方式如下:

python -m venv .venv
. .venv/bin/activate
pip install PyYAML
python harness.py

当单测失败时,artifacts/add-order-query-api/unit_test.json 会保留命令、退出码和日志。实际接入模型时,可以将这个 JSON、相关代码差异和仓库约束一起交给模型,要求它只返回补丁;应用补丁后再次执行同一阶段。修复次数必须受 max_fix_attempts 限制,超过阈值就转人工处理,避免智能体无限循环或不断扩大修改范围。

一个面向模型的修复提示可以这样实践:

你正在修复任务 add-order-query-api。

输入:
- 当前 git diff
- unit_test.json 中的失败输出
- CONTRIBUTING.md 中的仓库规则

要求:
1. 只处理导致当前测试失败的根因。
2. 不删除或跳过既有测试。
3. 不修改公开接口,除非验收条件明确要求。
4. 输出 unified diff,不要声称测试已经通过。
5. 如果证据不足,返回 NEEDS_HUMAN 和缺失信息列表。

这类约束能把模型的职责限制在分析和生成候选修改上,把是否通过的判断留给测试执行器。

自动 CR 和部署阶段需要更严格的边界

自动代码审查不应只输出一段自然语言评价。可以要求模型生成结构化结果,包含文件、行号、严重级别、问题类型、证据和建议。流水线只阻断明确的 correctness、security 或 compatibility 问题,风格建议则交给 linter 或作为非阻断评论。

部署阶段还要限制权限。智能体适合部署临时环境、查询只读日志和执行预定义回滚脚本,不适合持有长期生产管理员凭证。建议使用短期身份、命名空间隔离和命令白名单,并完整记录模型输入、工具调用、补丁和审批操作。

冒烟检查也应验证业务行为,而不只是检查进程存活。例如可以这样实践:

set -euo pipefail
BASE_URL="${BASE_URL:?set BASE_URL to the preview environment}"

curl --fail --silent --show-error \
  --max-time 10 \
  "$BASE_URL/healthz"

curl --fail --silent --show-error \
  --max-time 10 \
  -H 'Accept: application/json' \
  "$BASE_URL/api/orders/1001" \
  | jq -e '.id == 1001 and (.status | type == "string")'

这里需要把订单 ID、认证方式和响应字段替换为项目中的测试数据。探针失败后,应保存 HTTP 状态、响应体和对应服务日志,再进入诊断阶段。

落地时先收紧边界,再扩大自治范围

全链路智能体不是把 CI 脚本外面包一层大模型,也不是让模型直接接管生产环境。它的价值来自三点:流程有明确状态,阶段有真实证据,失败能回到受限的修复循环。

开始采用时,可以按以下顺序推进:

  • 先选择测试充分、依赖边界清晰的小型需求。
  • 固化仓库的构建、测试、部署和回滚命令。
  • 为每个阶段定义输入、输出、超时和通过条件。
  • 限制修改文件、工具权限、修复次数和令牌预算。
  • 在合并、数据迁移和生产发布前保留人工审批。
  • 统计一次通过率、人工接管率、回滚率和端到端交付时间,而不只统计生成代码量。

当团队能够回答“智能体当前在哪个阶段、依据什么继续、失败后修改了什么、谁批准了高风险操作”时,AI Coding 才从体感上的便利,变成可度量、可治理的工程能力。


相关推荐