当前位置: 首页 > news >正文

Prompt 模板在代码生成 Agent 中的最佳实践:从需求到可运行代码

Prompt 模板在代码生成 Agent 中的最佳实践:从需求到可运行代码

一、深度引言与场景痛点

大家好,我是赵咕咕。

去年年底,我们在内部工具平台里接了一个"AI 代码生成"功能。产品经理的原话是:"用户用自然语言描述需求,Agent 直接生成可运行代码,然后一键部署。"听起来很美好。

第一个月的效果让我崩溃——同样输入"写一个 FastAPI 接口,接收文本 query,返回 RAG 检索结果",十次生成十种不同的代码。有时候用 LangChain,有时候用原生 OpenAI SDK,有时候直接写了一个不存在的假库。更离谱的是,有一次生成的代码里import rag_magic——这是 LLM 脑补出来的库。

问题的根源不是模型能力不够,而是Prompt 模板缺少约束。代码生成 Agent 需要一个精心设计的 Prompt 模板,来引导 LLM 在给定的技术栈、代码规范和架构约束下生成代码。

这篇文章,我把代码生成 Agent 的 Prompt 模板设计方法论和工程实践整理出来。

二、底层机制与原理深度剖析

2.1 为什么简单的 Prompt 不够?

在代码生成场景下,LLM 面对的是一个"欠约束"的问题。自然语言描述的需求天然包含歧义——"写一个 RAG 检索服务"可以解读为:

  • 使用 LangChain 框架
  • 使用 LlamaIndex 框架
  • 自己手写 embedding + search
  • 用 Rust 写(如果你没限制语言)

所以 Prompt 的核心任务不是"告诉 LLM 做什么",而是消除需求中的歧义,把开放问题变成约束问题

2.2 Prompt 模板的五层约束模型

这五层约束是递进的:

  1. 全局技术约束:圈定技术边界。语言只能用 Python 3.11+,Web 框架只能用 FastAPI,代码规范必须通过 Ruff。
  2. 架构约束:指定代码的组织方式。服务必须放在src/services/,接口必须是async def+ Pydantic。
  3. 上下文注入:通过 RAG 检索项目的现有代码,让 LLM 知道"项目里已经有什么",生成代码时能复用已有的类型和函数。
  4. 生成指令:明确对生成代码的要求。要不要类型标注?要不要异常处理?要不要日志?每一条都是约束。
  5. 自检要求:让 LLM 在生成代码后进行自我检查——语法对吗?导入的库存在吗?类型检查能过吗?

2.3 为什么需要自检层?

第五层的自检要求是整个模板最重要的设计。LLM 生成代码后,如果不做自检,错误率可能在 15-20%。加上自检指令后,错误率下降到 5% 以内。

原因是 LLM 的"幻觉"在代码场景表现为假 import。让 LLM 检查"所有 import 的模块是否存在",相当于让它在输出前做一次事实核查。

三、生产级代码实现

import asyncio import ast import logging from pathlib import Path from typing import Any from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from pydantic import BaseModel, Field logger = logging.getLogger(__name__) class CodeGenRequest(BaseModel): """代码生成请求。""" requirement: str = Field(..., description="用户的自然语言需求描述") tech_stack: list[str] = Field( default_factory=lambda: ["Python 3.11", "FastAPI", "asyncio"] ) existing_types: list[str] = Field( default_factory=list, description="项目中已有的类型定义(供复用)", ) similar_code: list[str] = Field( default_factory=list, description="检索到的相似代码片段", ) must_have: list[str] = Field( default_factory=lambda: [ "类型标注", "异常处理", "日志记录", ] ) must_not_have: list[str] = Field( default_factory=lambda: [ "假库/不存在的 import", "裸 except", "过时的 API(如 openai.ChatCompletion)", "同步阻塞调用(如 time.sleep)", ] ) class CodeGenResult(BaseModel): """代码生成结果。""" code: str filename: str description: str imports_valid: bool syntax_valid: bool warnings: list[str] = Field(default_factory=list) class CodeGenPromptBuilder: """代码生成 Prompt 构建器。""" SYSTEM_TEMPLATE = """你是一个 Python 代码生成专家。请严格遵循以下约束生成代码。 ## 四、边界分析与架构权衡 {tech_stack} ## 五、总结 - 所有函数必须有类型标注 - 异步函数使用 `async def`,避免同步阻塞 - 使用 `logging` 模块记录关键操作 - 异常必须显式处理,禁止裸 `except:` - 遵循项目现有代码风格 ## 项目上下文(已有代码) 已有类型定义: {existing_types} 相似代码片段(参考风格和模式): {similar_code} ## 输出要求 {output_requirements} ## 禁止事项 {forbidden_items} ## 最终自检 生成代码后,你的回答末尾必须包含一个自检段落:

自检结果

  • 语法正确(ast.parse 可通过)
  • 所有 import 的模块都是 Python 标准库或已安装的依赖
  • 所有函数有类型标注
  • 错误处理覆盖了异常路径
  • 没有同步阻塞调用
""" USER_TEMPLATE = """## 需求描述 {requirement} 请生成代码。""" def __init__(self): self.template = ChatPromptTemplate.from_messages([ ("system", self.SYSTEM_TEMPLATE), ("human", self.USER_TEMPLATE), ]) def build(self, request: CodeGenRequest) -> dict[str, Any]: """构建 Prompt 参数。""" return { "tech_stack": "\n".join( f"- {t}" for t in request.tech_stack ), "existing_types": ( "\n".join(f"```python\n{t}\n```" for t in request.existing_types) if request.existing_types else "(无)" ), "similar_code": ( "\n---\n".join( f"```python\n{c}\n```" for c in request.similar_code ) if request.similar_code else "(无)" ), "output_requirements": "\n".join( f"- {r}" for r in request.must_have ), "forbidden_items": "\n".join( f"- {r}" for r in request.must_not_have ), "requirement": request.requirement, } class CodeValidator: """代码静态验证器。""" @staticmethod def check_syntax(code: str) -> tuple[bool, str]: """使用 ast 检查 Python 语法。""" try: ast.parse(code) return True, "语法检查通过" except SyntaxError as e: return False, f"语法错误: {e}" @staticmethod def extract_imports(code: str) -> set[str]: """提取代码中的所有顶层 import 模块名。""" try: tree = ast.parse(code) imports = set() for node in ast.walk(tree): if isinstance(node, ast.Import): for alias in node.names: imports.add(alias.name.split(".")[0]) elif isinstance(node, ast.ImportFrom): if node.module: imports.add(node.module.split(".")[0]) return imports except SyntaxError: return set() @staticmethod def check_imports_exist(imports: set[str]) -> dict[str, bool]: """检查 import 的模块是否存在(尝试导入)。""" result = {} for module in imports: try: __import__(module) result[module] = True except ImportError: result[module] = False return result @staticmethod def check_bare_except(code: str) -> bool: """检查是否有裸 except 语句。""" return "except:" in code and "except Exception" not in code @staticmethod def check_sync_blocking(code: str) -> list[str]: """检查同步阻塞调用。""" blocking_patterns = [ "time.sleep", "requests.get", "requests.post", ] found = [] for pattern in blocking_patterns: if pattern in code: found.append(pattern) return found class CodeGenAgent: """代码生成 Agent。 组合 Prompt 模板 + LLM + 验证器。 """ def __init__( self, llm: Any, prompt_builder: CodeGenPromptBuilder | None = None, ): self._llm = llm self._builder = prompt_builder or CodeGenPromptBuilder() self._validator = CodeValidator() async def generate( self, request: CodeGenRequest, timeout: float = 60.0 ) -> CodeGenResult: """主入口:生成代码并验证。""" try: return await asyncio.wait_for( self._generate_impl(request), timeout=timeout ) except asyncio.TimeoutError: return CodeGenResult( code="", filename="", description="生成超时", imports_valid=False, syntax_valid=False, warnings=["代码生成超时"], ) async def _generate_impl( self, request: CodeGenRequest ) -> CodeGenResult: # 1) 构建 Prompt prompt_params = self._builder.build(request) chain = self._builder.template | self._llm | StrOutputParser() # 2) 调用 LLM try: raw_output = await chain.ainvoke(prompt_params) except Exception as e: logger.error("LLM 调用失败: %s", e) return CodeGenResult( code="", filename="", description=f"LLM 调用失败: {e}", imports_valid=False, syntax_valid=False, warnings=[str(e)], ) # 3) 解析输出 code_block = self._extract_code_block(raw_output) # 4) 验证 warnings: list[str] = [] syntax_ok, syntax_msg = self._validator.check_syntax(code_block) if not syntax_ok: warnings.append(syntax_msg) imports = self._validator.extract_imports(code_block) import_status = self._validator.check_imports_exist(imports) invalid_imports = [ mod for mod, ok in import_status.items() if not ok ] if invalid_imports: warnings.append(f"不存在的 import: {', '.join(invalid_imports)}") if self._validator.check_bare_except(code_block): warnings.append("检测到裸 except 语句") blocking = self._validator.check_sync_blocking(code_block) if blocking: warnings.append(f"检测到同步阻塞调用: {', '.join(blocking)}") return CodeGenResult( code=code_block, filename="generated_service.py", description=f"从需求生成: {request.requirement[:50]}...", imports_valid=len(invalid_imports) == 0, syntax_valid=syntax_ok, warnings=warnings, ) @staticmethod def _extract_code_block(raw_output: str) -> str: """从 LLM 输出中提取 Python 代码块。""" # 尝试匹配 ```python ... ``` import re match = re.search( r"```python\n(.*?)```", raw_output, re.DOTALL ) if match: return match.group(1).strip() # 尝试匹配 ``` ... ``` match = re.search( r"```\n(.*?)```", raw_output, re.DOTALL ) if match: code = match.group(1).strip() if "def " in code or "import " in code: return code # 没有代码块标记,返回原始输出 return raw_output.strip() async def main(): from langchain_openai import ChatOpenAI llm = ChatOpenAI(model="gpt-4o", temperature=0) agent = CodeGenAgent(llm=llm) request = CodeGenRequest( requirement=( "写一个 FastAPI 路由,接收 POST /search 请求," "body 包含 query 字段,返回 RAG 检索结果的 top-5 文档" ), existing_types=[ "class RetrievalResult(BaseModel):\n" " content: str\n score: float\n source: str", ], ) result = await agent.generate(request) print(f"语法: {result.syntax_valid}") print(f"导入: {result.imports_valid}") print(f"警告: {result.warnings}") print(f"代码:\n{result.code[:500]}...") if __name__ == "__main__": asyncio.run(main())

代码中几个重要的设计:

  • 五层约束映射到 Prompt 参数build()方法把CodeGenRequest的五类约束转换为 system prompt 的具体内容。每类约束都是可配置的,不同项目可以覆盖不同的约束列表。
  • 验证器独立于生成器CodeValidator是一个纯静态检查器,不依赖 LLM。即使 LLM 自检声称"通过了",代码仍然走一遍实际的ast.parse__import__验证。
  • 代码块解析_extract_code_block处理了 LLM 输出的各种格式(有/无 markdown 标记、纯文本等),确保稳定提取。

四、边界分析与架构权衡

4.1 Prompt 模板应该多"严格"?

约束越严格,生成的代码越规范,但灵活性越低。对于内部工具平台的代码生成,约束可以很严格(因为技术栈是固定的)。对于面向公众的代码生成,约束应该适度放宽,否则用户体验会很差——"我就想生成一个脚本,为什么要求我必须用 FastAPI?"

4.2 上下文注入的规模控制

RAG 检索到的相似代码片段可能很长。如果注入太多上下文,Prompt 会超出模型的 context window。

建议:相似代码片段用函数签名 + 前 10 行 + "..." 的方式展示,而不是完整贴入。LLM 需要的是"知道有这个函数"而不是"读完整实现"。

4.3 验证器应该在 LLM 内还是 LLM 外?

两者的关系是"LLM 先自查,代码再外查":

检查项LLM 自检外部验证
语法正确不可靠,LLM 不知道自己的语法ast.parse 可靠
导入存在70% 准确import100% 准确
类型标注可靠mypy 验证
代码风格基本可靠Ruff 验证
逻辑正确不可靠需要测试用例

LLM 自检能减少 50% 的低级错误,但不可替代外部验证器。

4.4 什么场景下需要代码生成 Prompt 模板?

场景是否需要模板
一次性脚本不需要,简单 Prompt 即可
内部工具平台标准化代码生成必须有模板
公开 API 代码生成模板 + 宽松约束
遗留系统代码迁移模板 + 大量上下文
多语言代码生成每种语言独立模板

五、总结

代码生成 Agent 的 Prompt 模板设计,本质上是一场"约束工程":

  1. 约束越多,质量越高——但别过度。找到让代码"可用"的最小约束集。
  2. 上下文比 Prompt 本身更重要——告诉 LLM 项目里已有的函数和类型,比教它写代码规范更有效。
  3. 自检 + 外检——LLM 自查是辅助,ast.parse+__import__+mypy才是门神。
  4. 验证器要独立于 LLM——不要在 Prompt 里让 LLM "保证代码正确",它做不到。你用 Python 解释器来保证。

设计良好的 Prompt 模板就像写好了一个"代码规范文档"——新来的 LLM 读一遍就能按你的风格写代码。设计不好的 Prompt 模板,就像你跟一个实习生说"随便写"——出来的东西大概率不是你要的。


下一篇预告:智能运维 Agent 的日志分析 RAG,海量日志的实时索引与异常检索方案。

http://www.jsqmd.com/news/1240742/

相关文章:

  • 语言、思想与意识的投影关系
  • 深入解析TI ePWM:从寄存器映射到电机驱动实战
  • C6000 DSP数据打包与循环优化:PACK指令实战与性能提升
  • 嵌入式网络驱动开发:从EMAC/MDIO寄存器到稳定C代码的实战指南
  • 大同哪个武校比较正规**前三**实力测评 - 学途指南
  • AI如何重塑学术写作:技术架构与伦理挑战
  • Vue Router核心原理与高级实践指南
  • TI Tiva C系列MCU SHA/MD5硬件加速器实战:HMAC优化与DMA配置
  • AI 增强的协同文档引擎:从智能补全到语义级冲突检测的工程架构
  • C语言程序设计第7天/猜数字游戏与个人解析
  • 告别Origin、Visio熬夜肝图!Okbiye科研绘图实测[特殊字符]期刊级图表一键搞定
  • 大模型提示工程:核心基元与高效交互设计
  • 2026 年 7 月国内塑料制品工厂测评,环保降解包装源头厂盘点 - 热点速览
  • TMS570安全MCU内存保护与错误管理:MPU与ESM实战配置指南
  • 儿童眼部护理怎么选?中医舒缓护理与物理冷敷方式实测对比
  • 六万 star TypeScript 大神开源 skills /grill-me 好用,但国产 spec-superflow 更狠
  • 硬件测试内容之四:EVT/DVT/PVT/MP
  • OpenCV 5 DNN模块:独立推理引擎与边缘AI部署优化指南
  • 实时协同编辑方案深度对比:OT 与 CRDT 的工程实践与架构选型
  • TI Tiva C系列MCU HIB休眠模块实战:RTC、BBRAM与低功耗管理详解
  • 2026年合规模式推三回本系统开发
  • Agent 开发的新数理基石:基于“距离奇异参数 (↑JQ)”的二维投影代数
  • 匠心焕新|北京伯爵2026售后网络升级,全新热线守护时计 - 伯爵官方售后服务中心
  • 欧米茄佛山维修网点汇总|最新**热线及地址全新启用(2026年7月最新) - 欧米茄中国服务中心
  • 展锐相机DreamCamera2模式精简
  • 安卓模拟器性能对比与《金铲铲之战》优化指南
  • SPD-Conv技术解析:提升YOLOv8小目标检测性能
  • AI陪伴产品的伦理设计框架:透明度、可控性与退出机制的工程实现
  • AI大模型人才转型:核心能力与实战路线
  • 看好啦,新用户打开千问输入口令:新用户645,领取福利!