基于AutoGen的代码生成Agent并不是简单地让大模型输出一段代码,而是通过多个智能体之间的消息交换、代码执行和错误反馈,形成一条自动化的编码与修正流水线。本文将带你完整走一遍构建过程:先从AutoGen的核心概念入手,然后定义代码生成所用的角色,配置本地执行环境,最后展示如何发起多轮任务并观察Agent自我纠错。整个过程会给出可以直接运行的Python代码。

一、AutoGen核心概念:多智能体协作的底层模型
AutoGen是微软开源的一个多智能体对话框架,它把每个智能体抽象为ConversableAgent,所有具体的智能体类都继承自它。框架里最常用的两个子类是AssistantAgent和UserProxyAgent。前者通常由大语言模型驱动,负责生成文本、代码或调用工具;后者代表人类用户或者系统执行者,可以配置代码执行能力,把真实运行结果返回给其他智能体。两者之间通过统一的消息格式进行交互,每一次消息都包含发送者、接收者和内容。
AutoGen特别适合用来做代码生成Agent,一个重要原因就是它原生支持函数调用和代码执行闭环。你不需要自己写复杂的解析逻辑去提取代码、运行、再拼接错误信息。只要把UserProxyAgent的代码执行配置打开,它收到包含Python代码的消息后,就会自动把代码写入临时文件、调用解释器执行、收集stdout和stderr,然后把完整的执行结果(包括异常堆栈)包装成新消息发回给AssistantAgent。正是这个自动反馈机制,让Agent能够根据错误信息反复修改代码,直到成功运行为止。
下面先用一个最小示例直观感受一下AutoGen的多智能体对话流程。这个例子中,一个用户代理向助手代理发送一个简单问题,助手回复一段代码,用户代理执行后把结果返回。注意代码块中的小于号、大于号已经做了HTML转义,确保在页面中正确显示。
import autogen
# 配置大模型端点
config_list = [
{
"model": "gpt-4",
"api_key": "your_api_key_here"
}
]
llm_config = {"config_list": config_list, "temperature": 0}
# 创建用户代理,启用本地代码执行
user_proxy = autogen.UserProxyAgent(
name="user_proxy",
human_input_mode="NEVER",
max_consecutive_auto_reply=3,
code_execution_config={
"work_dir": "coding",
"use_docker": False
}
)
# 创建助手代理
assistant = autogen.AssistantAgent(
name="assistant",
llm_config=llm_config,
system_message="你是一个Python编程助手,回答时需要给出可运行的代码。"
)
# 发起一次对话
user_proxy.initiate_chat(
assistant,
message="写一个函数,计算两个数的最大公约数。"
)
运行上面这段代码后,你会看到控制台里出现多个角色的消息:助手先输出包含GCD函数的代码,用户代理接着执行这段代码,如果代码正确,执行结果会显示为空或预期输出;如果代码有误,错误信息会再次进入对话,助手会尝试修复。这就是AutoGen实现代码生成Agent的基本骨架。
二、从零构建代码生成Agent:角色设计与代码实现
纯靠一个助手代理和一个用户代理虽然能跑通基本流程,但在真实场景中往往还需要一个代码审查角色,用来在代码执行前做一次静态检查,减少无效的执行轮次。我们可以定义三个角色:UserProxyAgent负责发送需求并执行代码;CoderAgent继承自AssistantAgent,专门负责生成代码;ReviewerAgent同样继承自AssistantAgent,负责审查代码逻辑、风格和潜在错误。三个角色通过GroupChat组织起来,由GroupChatManager统一调度消息顺序。
创建用户代理时,最关键的是设置code_execution_config。这里有两个重要参数:work_dir指定代码文件存放的目录,use_docker决定是否在Docker容器中执行代码。为了避免环境依赖问题,我们先把use_docker设为False,这样代码会直接在本机Python解释器中运行。实际生产环境建议启用Docker隔离,但本地调试阶段可以先关掉。另外human_input_mode设为NEVER表示全自动,不需要人工介入;如果希望在某些关键步骤等待人工确认,可以改为TERMINATE或ALWAYS。
下面是完整的角色定义和GroupChat配置代码。这段代码已经可以直接运行,前提是你的环境中安装了pyautogen库,并且配置了有效的OpenAI兼容API密钥。
import autogen
# 大模型配置,可替换为你自己的端点
llm_config = {
"config_list": [
{"model": "gpt-4", "api_key": "sk-xxx"}
],
"temperature": 0,
"cache_seed": 42
}
# 用户代理:负责执行代码并发送任务
user_proxy = autogen.UserProxyAgent(
name="UserProxy",
human_input_mode="NEVER",
max_consecutive_auto_reply=5,
is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"),
code_execution_config={
"work_dir": "agent_workspace",
"use_docker": False
},
system_message="你代表用户提出编程需求,并执行收到的Python代码。执行完毕后返回结果。"
)
# 代码生成代理
coder = autogen.AssistantAgent(
name="Coder",
llm_config=llm_config,
system_message="""你是一个资深Python工程师。请根据用户需求编写完整、可运行的Python代码。
代码必须包含必要的import语句,处理可能的异常,并添加简要注释。
输出代码时直接给出代码块,不要有多余的解释。"""
)
# 代码审查代理
reviewer = autogen.AssistantAgent(
name="Reviewer",
llm_config=llm_config,
system_message="""你负责审查Coder生成的代码。检查逻辑错误、语法问题、安全风险和性能瓶颈。
如果代码没有问题,回复'TERMINATE'。如果有问题,明确指出错误并要求Coder修改。"""
)
# 设置群聊
groupchat = autogen.GroupChat(
agents=[user_proxy, coder, reviewer],
messages=[],
max_round=10
)
manager = autogen.GroupChatManager(
groupchat=groupchat,
llm_config=llm_config
)
# 启动任务
user_proxy.initiate_chat(
manager,
message="请编写一个函数,从CSV文件中读取两列数据,并输出这两列的皮尔逊相关系数。"
)
提示词的设计直接影响生成代码的质量。对于CoderAgent,系统提示词里明确要求包含必要的import、异常处理和注释,可以显著减少第一次生成代码时因为缺少依赖或语法错误导致的失败。对于ReviewerAgent,让它以“找出问题并给出修改意见”为准则,并且只有在代码完全通过时才输出TERMINATE。这样整个群聊不会陷入无限循环,因为is_termination_msg会检查消息是否以TERMINATE结尾,一旦触达就会终止对话。
另外注意cache_seed参数的设置。AutoGen会缓存相同的请求和配置结果,如果你在调试时修改了提示词但没改cache_seed,可能会发现Agent仍然返回旧的回答。每次修改系统消息或LLM配置后,建议换一个新的cache_seed值,或者直接删除缓存目录。
三、运行与调试:让Agent真正产出可运行代码
按照上面的配置启动任务后,GroupChat会先在三个角色之间传递消息。通常第一轮是用户代理向管理者发送需求,管理者把消息转给Coder,Coder生成代码,Reviewer把代码发给用户代理执行。如果代码运行出错,错误信息会重新进入对话,Coder根据错误信息修改代码后再次提交。这种多轮修正机制是代码生成Agent的核心价值所在。
以“从CSV文件读取两列数据并计算皮尔逊相关系数”为例,第一轮Coder可能会生成一段代码,但忘记导入pandas或scipy。用户代理执行后返回ModuleNotFoundError,Reviewer在审查时可能也会提示需要补充依赖。第二轮Coder会添加导入语句,并给出完整代码。如果CSV文件不存在,执行时还会抛出FileNotFoundError,这时Coder可以进一步修改代码,添加文件存在性检查或使用一个默认路径。整个过程会持续到某一轮中Reviewer输出TERMINATE,或者达到max_round设定的最大轮数。
调试过程中最常见的问题是对话过早终止或者无限循环。过早终止往往是因为max_consecutive_auto_reply设置得太小,用户代理在没有收到有效回复时就停止了对话。无限循环则是因为is_termination_msg没有正确匹配到终止条件,或者max_round设得过大。建议开始时把max_round设为8到10轮,然后观察日志,逐步调优。
查看完整消息历史有助于理解Agent之间的交互。GroupChat对象内部维护了一个messages列表,你可以通过以下代码遍历并打印每个消息的角色和内容:
for msg in groupchat.messages:
role = msg.get("role", "unknown")
content = msg.get("content", "")
# 只打印前80个字符避免刷屏
print(f"{role}: {content[:80]}")
另一个实用技巧是开启AutoGen的日志功能。在代码开头设置autogen.runtime_logging.start(),可以把所有LLM请求和响应记录到SQLite数据库中。调试完毕后用autogen.runtime_logging.stop()关闭,再用autogen.runtime_logging.get_logged_history()查看记录。这样当某个回复看起来不对劲时,你可以直接定位到具体的prompt和API响应,从而知道是提示词的问题还是模型本身的偏差。
四、优化与扩展:将代码生成Agent推向生产
目前构建的Agent已经具备了基本的代码生成和自我修正能力,但距离真正的生产应用还有一段距离。最直接的优化方向是引入单元测试。让UserProxyAgent执行代码的同时运行一组预先定义好的测试用例,如果测试失败,错误信息会作为新增反馈进入对话。你可以通过register_reply方法给用户代理添加自定义回复函数,在执行完代码后自动运行测试并返回结果。这样可以进一步降低生成代码中出现逻辑错误的概率。
另一个重要的优化点是使用检索增强生成(RAG)把项目上下文注入到CoderAgent的提示词中。比如你的项目已经有一个现有的代码库,你可以把常用的函数签名、类定义和工具函数通过向量检索找到最相关的片段,拼接到系统消息里。这样Coder在生成新代码时能更好地复用已有模块,而不是重新造轮子。AutoGen允许你在创建Agent时传入动态的系统提示词生成函数,这样一来每次对话前都可以根据任务内容实时构造上下文。
安全性是所有自动执行代码的系统都必须严肃对待的问题。当前示例中我们把use_docker设为False,这仅仅适合本地开发和可信环境。如果要将Agent部署到服务器或面向多用户提供服务,必须启用Docker隔离。配置方法是在code_execution_config中设置use_docker=True,并指定一个Docker镜像,例如docker_image="python:3.11-slim"。这样每次执行代码都会在一个全新的容器中进行,文件系统、网络和进程都与宿主机隔离,即使生成的代码有恶意行为,也不会影响到系统安全。
此外,你还可以尝试更复杂的群聊拓扑。例如让UserProxyAgent作为入口,内部再嵌套一个子GroupChat用于代码生成和审查,外层再有一个部署Agent负责把最终代码提交到Git仓库。AutoGen支持自定义代理之间的消息路由策略,你可以根据业务需求设计出适合自己团队的协作模式。代码生成Agent的最终形态并不是一个固定不变的模板,而是一套可以灵活组合的积木。
把以上内容串起来,你就得到了一个可复用的代码生成Agent工作流:用户提出需求,Coder生成代码,Reviewer审查静态质量,UserProxy执行代码并返回错误,多轮迭代直到代码通过验证。这个流程可以扩展到API开发、数据处理脚本、自动化运维工具等众多场景。实践过程中多观察消息历史,逐步调整提示词和轮数限制,很快你就能训练出一套专门为自己业务服务的自动编码助手。

