首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >AI 编程的工程化落地:从补全到可验证的智能体

AI 编程的工程化落地:从补全到可验证的智能体

原创
作者头像
用户12339161
发布于 2026-10-22 14:34:39
发布于 2026-10-22 14:34:39
320
举报

AI 编程的争论常常停在"能不能替代程序员",但真问题在工程侧:模型生成的代码如何被约束、被检索、被验证、被回滚。一段"看起来对"的代码如果进不了 CI,就没有价值。本文从上下文构建、约束解码、自验证循环、评测四个环节,给出可落地的实现思路与代码。

一、上下文构建:检索优于堆砌

模型答不好,多半是上下文给错了。把整个仓库塞进 prompt 既超窗又稀释信号。正确做法是"检索—排序—裁剪":按符号名与调用关系建索引,取与当前编辑点最近的 K 个片段。

代码语言:javascript
复制
# rag/context_builder.py
import ast, re
from dataclasses import dataclass

@dataclass
class Chunk:
    path: str
    symbol: str
    code: str
    score: float = 0.0

def index_repo(root):
    chunks = []
    for p in Path(root).rglob("*.py"):
        tree = ast.parse(p.read_text(encoding="utf-8"))
        lines = p.read_text().splitlines()
        for node in ast.walk(tree):
            if isinstance(node, (ast.FunctionDef, ast.ClassDef)):
                code = "\n".join(lines[node.lineno - 1: node.end_lineno])
                chunks.append(Chunk(str(p), node.name, code))
    return chunks

def build_context(chunks, query, k=8):
    q = set(re.findall(r"\w+", query.lower()))
    for c in chunks:
        # 符号名精确命中权重最高,其次词重叠
        name_hit = 1.0 if c.symbol.lower() in query.lower() else 0.0
        overlap = len(q & set(re.findall(r"\w+", c.code.lower()))) / (len(q) or 1)
        c.score = name_hit * 2 + overlap
    top = sorted(chunks, key=lambda c: c.score, reverse=True)[:k]
    # 按字符预算裁剪,防止撑爆上下文窗口
    budget, out = 6000, []
    for c in top:
        if budget - len(c.code) < 0: break
        out.append(f"# {c.path}::{c.symbol}\n{c.code}")
        budget -= len(c.code)
    return "\n\n".join(out)

检索的关键不是向量库本身,而是符号级切分:按函数/类切块,让召回单元与"要改的东西"对齐,比整文件向量化精准得多。

二、约束解码:让输出结构可解析

自由文本输出后靠正则抠 JSON,是脆弱工程。更稳的是约束解码:把模型每步的 token 候选限制在语法允许的集合内。无需自己实现,可用 GBNF / JSON Schema 语法。

代码语言:javascript
复制
# 用 grammar 约束输出为严格的补丁对象
GRAMMAR = r'''
root   ::= object
object ::= "{" pair ("," pair)* "}"
pair   ::= string ":" value
value  ::= string | number | object | array | "true" | "false" | "null"
string ::= "\"" [^"]* "\""
number ::= "-"? [0-9]+ ("." [0-9]+)?
array  ::= "[" (value ("," value)*)? "]"
'''

def request_patch(llm, prompt):
    raw = llm.generate(
        prompt,
        grammar=GRAMMAR,
        max_tokens=2048,
        temperature=0.1,          # 代码任务压低随机性
    )
    return json.loads(raw)        # 语法保证下,解析几乎不会失败

输出结构固定为 {"file": ..., "old": ..., "new": ...} 的补丁列表,后续就能自动化应用、测试、回滚,而不是人工复制粘贴。

三、自验证循环:生成—测试—修正

真正把 AI 编程从"玩具"拉到"工具"的,是闭环验证。核心是:跑测试,把失败信息喂回去,最多迭代 N 轮;任何一轮不通过就不合入。

代码语言:javascript
复制
# agent/verify_loop.py
def run_patch(patch, repo):
    for edit in patch["edits"]:
        path = repo / edit["file"]
        src = path.read_text(encoding="utf-8")
        if edit["old"] not in src:
            return False, f"anchor not found in {edit['file']}"
        path.write_text(src.replace(edit["old"], edit["new"], 1), encoding="utf-8")
    return True, ""

def verify_loop(llm, task, repo, max_rounds=3):
    context = build_context(index_repo(repo), task)
    history = []

    for r in range(max_rounds):
        prompt = f"任务:{task}\n上下文:\n{context}\n历史:\n{history}\n输出补丁 JSON。"
        patch = request_patch(llm, prompt)

        ok, err = run_patch(patch, repo)
        if not ok:
            history.append(f"应用失败:{err}")
            continue

        test = subprocess.run(["pytest", "-q"], cwd=repo,
                              capture_output=True, text=True)
        if test.returncode == 0:
            return {"status": "pass", "rounds": r + 1, "patch": patch}

        history.append("测试失败:\n" + test.stdout[-2000:])  # 只回传尾部
        git_checkout(repo)                                     # 失败即回滚

    return {"status": "fail", "history": history}

三处细节决定成败:失败即回滚(git checkout)、只回传日志尾部(防止上下文膨胀)、锚点存在性校验(old 找不到就拒应用,避免模型幻觉补丁污染代码)。

四、评测:没有基线就没有进步

凭感觉判断"AI 变强了"不可靠。需要固定任务集、固定环境、可重复指标。

代码语言:javascript
复制
# eval/bench.py
def evaluate(agent, cases):
    res = {"pass@1": 0, "avg_rounds": 0, "regression": 0}
    for c in cases:
        repo = clone(c["repo"], c["commit"])
        out = agent.verify_loop(c["task"], repo)
        res["pass@1"] += out["status"] == "pass"
        res["avg_rounds"] += out.get("rounds", 0)
        # 回归:原有测试是否被破坏
        res["regression"] += subprocess.run(
            ["pytest", c["legacy"]], cwd=repo).returncode != 0
    n = len(cases)
    return {k: v / n for k, v in res.items()}

pass@1 看一次通过率,regression 看有没有为修 bug 制造新 bug——后者在生产环境比前者更要命。

五、工程化结论

AI 编程的有效性不取决于模型多大,而取决于把不确定性关进确定性的笼子里:检索让上下文相关,约束解码让输出可解析,验证循环让错误可回滚,评测让改进可度量。这四件事做扎实,AI 才是一个能进 CI、能上生产的协作对象;缺了任何一环,它就只是会写代码的聊天框。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、上下文构建:检索优于堆砌
  • 二、约束解码:让输出结构可解析
  • 三、自验证循环:生成—测试—修正
  • 四、评测:没有基线就没有进步
  • 五、工程化结论
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档