技术教程 · 阅读约 9 分钟

LLM Agent 写后端代码为什么总在关键时刻"忘规矩"?一个实战方案

LLM Agent 写后端代码为什么总在关键时刻"忘规矩"?一个实战方案

你有没有遇到过这种情况:

让 AI Agent 帮你生成一套 CRUD 接口,前几个文件写得有模有样,到第五个文件突然不做参数校验了;或者你明确说"所有接口必须鉴权",它前三个接口加了,后面悄悄省掉了。

这不是模型变笨了,这是 Constraint Decay(约束衰减) 问题——随着上下文变长,早期注入的约束在模型注意力中权重下降,Agent 开始"忘记"你定的规矩。

HN 上最近有篇论文专门研究这个现象,结论很直接:LLM Agent 在长任务后端代码生成中,约束遵守率随 token 数增长显著下降,尤其是安全类约束(鉴权、输入校验、SQL 注入防护)最容易被丢弃。

这篇文章给你一个可以直接跑的解决方案。


问题复现

先看一个典型失败场景。你给 Agent 一个系统提示:


你是一个后端工程师,所有接口必须:

1. 验证 JWT token

2. 对用户输入做 schema 校验

3. 使用参数化查询,禁止字符串拼接 SQL

然后让它生成 10 个接口。你会发现第 7、8 个接口开始出现问题——不是完全不遵守,而是"部分遵守",比如有鉴权但没有输入校验,或者 SQL 用了 f-string 拼接。

这就是约束衰减的典型表现。


解决思路:约束锚定 + 自动审计

核心思路是两步:

1. 约束锚定:每次生成前,把关键约束重新注入到当前 prompt 的显眼位置,而不是只放在系统提示里

2. 自动审计:每个文件生成后,立刻用另一个 LLM 调用做约束合规检查,不合规就打回重生成

下面是完整实现,Python,可以直接跑。


完整代码


import json

from openai import OpenAI



# 用无量Api,国内直连,比官方便宜 60%+

client = OpenAI(

    api_key="your_api_key",

    base_url="https://api2everything.xyz/v1"

)



# 定义必须遵守的约束(这是你的"法律")

CONSTRAINTS = [

    "所有接口必须验证 JWT token,未授权返回 401",

    "所有用户输入必须通过 Pydantic schema 校验",

    "数据库操作必须使用参数化查询,禁止任何形式的字符串拼接",

    "异常必须被捕获并返回统一格式的错误响应",

]



def build_generation_prompt(task: str, constraints: list[str]) -> str:

    """每次生成都把约束锚定在 prompt 最前面"""

    constraint_block = "\n".join(f"- {c}" for c in constraints)

    return f"""## 强制约束(每一条都必须体现在代码中)

{constraint_block}



## 任务

{task}



生成完整的 Python FastAPI 代码,不要省略任何约束的实现。"""



def audit_code(code: str, constraints: list[str]) -> dict:

    """用 LLM 审计生成的代码是否满足所有约束"""

    constraint_block = "\n".join(f"{i+1}. {c}" for i, c in enumerate(constraints))

    audit_prompt = f"""你是一个代码安全审计员。检查以下代码是否满足所有约束。



## 约束列表

{constraint_block}



## 待审计代码

{code}




返回 JSON 格式:

{{

  "passed": true/false,

  "violations": ["违反的约束描述1", "违反的约束描述2"],

  "score": 0-100

}}



只返回 JSON,不要其他内容。"""



    response = client.chat.completions.create(

        model="gpt-4o-mini",  # 审计用小模型,省钱

        messages=[{"role": "user", "content": audit_prompt}],

        temperature=0

    )

    

    try:

        return json.loads(response.choices[0].message.content)

    except json.JSONDecodeError:

        return {"passed": False, "violations": ["审计结果解析失败"], "score": 0}



def generate_with_audit(task: str, max_retries: int = 3) -> str:

    """生成代码,自动审计,不通过就重试"""

    for attempt in range(max_retries):

        print(f"\n[生成] 第 {attempt + 1} 次尝试...")

        

        # 约束锚定:每次都重新注入

        prompt = build_generation_prompt(task, CONSTRAINTS)

        

        response = client.chat.completions.create(

            model="gpt-4o",

            messages=[

                {

                    "role": "system", 

                    "content": "你是一个严格遵守安全规范的后端工程师。"

                },

                {"role": "user", "content": prompt}

            ],

            temperature=0.2

        )

        

        code = response.choices[0].message.content

        

        # 立刻审计

        print("[审计] 检查约束合规性...")

        audit_result = audit_code(code, CONSTRAINTS)

        

        print(f"[审计] 得分: {audit_result.get('score', 0)}/100")

        

        if audit_result.get("passed", False):

            print("[审计] ✅ 通过")

            return code

        else:

            violations = audit_result.get("violations", [])

            print(f"[审计] ❌ 发现 {len(violations)} 个违规:")

            for v in violations:

                print(f"  - {v}")

            

            if attempt < max_retries - 1:

                # 把违规信息加入下一次的 task,让模型知道上次哪里错了

                task = f"{task}\n\n## 上次生成的问题(必须修复)\n" + \

                       "\n".join(f"- {v}" for v in violations)

    

    print("[警告] 达到最大重试次数,返回最后一次生成结果")

    return code



# 示例:生成用户管理模块

if __name__ == "__main__":

    tasks = [

        "生成用户注册接口 POST /users,接收 email 和 password",

        "生成用户登录接口 POST /auth/login,返回 JWT token",

        "生成获取用户信息接口 GET /users/{user_id},需要鉴权",

        "生成更新用户信息接口 PUT /users/{user_id},需要鉴权",

    ]

    

    results = []

    for task in tasks:

        print(f"\n{'='*50}")

        print(f"任务: {task}")

        code = generate_with_audit(task)

        results.append({"task": task, "code": code})

    

    print(f"\n{'='*50}")

    print(f"✅ 完成 {len(results)} 个接口生成,全部通过约束审计")


关键点解释

为什么要在每次 prompt 里重新注入约束?

模型处理长上下文时,对"最近出现"的内容注意力更高。把约束放在系统提示里,随着对话轮次增加,它的权重会被稀释。每次生成前重新锚定,相当于把约束"拉回"到注意力焦点。

审计为什么用小模型?

审计任务是判断题,不需要创造力,gpt-4o-mini 完全够用,成本是 gpt-4o 的 1/15。生成用大模型,审计用小模型,这是控制成本的标准做法。

违规信息反馈到下一次 prompt 有多大用?

实测下来,把具体违规描述加入重试 prompt 后,第二次通过率超过 90%。模型不是"不知道规则",而是"没被提醒"。


跑起来需要什么

  • Python 3.10+
  • pip install openai pydantic
  • 一个 API Key

如果你还在用官方 API,可以考虑换到 无量Api,国内直连不需要代理,支持 GPT-4o、Claude、Gemini、DeepSeek 等 300+ 模型,OpenAI 格式兼容,把上面代码的 base_url 改一行就能用,价格比官方便宜 65%,注册还送 ¥1 余额。


延伸思路

这套"生成 + 审计"的模式不只适用于约束合规,还可以用来:

  • 检查生成代码的测试覆盖率是否达标
  • 验证 API 响应格式是否符合你的 OpenAPI spec
  • 确保生成的 SQL migration 不包含破坏性操作

本质上是给 Agent 加了一个"质检环节",把单次生成的不确定性,转化为可重试的确定性流程。


你在用 LLM 生成后端代码时遇到过哪些奇怪的问题?评论区聊聊,或者直接把你的约束列表贴出来,看看这套方案能不能解决。

觉得有用的话点个赞,后续还会写 Agent 长任务稳定性的其他实战方案。