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 长任务稳定性的其他实战方案。