你有没有过这样的情况?一个人写代码,写得贼嗨;另一个人审代码,挑得贼狠;最后项目经理坐在中间,不停地在两人之间调和。嗯,这场面有点熟悉吧?
这次咱们要聊的就是这么一个有趣的东西——让多个AI智能体相互配合,模拟这套既"互相制约"又"相互促进"的协作模式。说白了,就是教会AI怎么像真实开发团队一样工作。
先来摊开讲讲现状。你肯定遇过那种情况:问ChatGPT写个算法,它给你甩来一段代码。你一运行,嘿,还真能用!但要说这代码多完美、多严谨?呃……那就得打个大问号了。有时候它不考虑边界情况,有时候逻辑绕得跟麻绳似的,有时候写完就再也改不了——因为它已经"走"了。
反过来想一下,如果有两个AI,一个专门写代码,另一个专门挑毛病,它们之间反复打磨,是不是能出更靠谱的东西?这就是 多智能体协作 的核心价值。不是让AI变成一个人,而是让AI们像一个真实的团队那样相互牵制。
现在有个框架叫 AutoGen,专门就是为了这事儿而生的。再配上 Semantic Kernel(微软搞的提示词编排工具)和 Roslyn(C# 的动态编译执行器),咱们就能搭出一套完整的自动化编程助手。



让我先把框架拆开讲清楚。
在代码里,有个叫 AgentFactory 的东西,专门用来批量生产智能体。为啥要这样做?因为这些智能体虽然各自有各自的人设,但它们的"出生过程"其实是相似的:
代码这样写:
csharpprivate static OpenAIClient CreateQwenClient(string apiKey)
{
var endpoint = new Uri(QwenEndpoint);
var credential = new ApiKeyCredential(apiKey);
var options = new OpenAIClientOptions { Endpoint = endpoint };
return new OpenAIClient(credential, options);
}
看上去不起眼,但这就像一个模板。每次要生产一个新的智能体,咱们就基于这个模板,只需要改变它的"人设"(SystemMessage)就行了。
真实的开发工作,AI不能凭空想象。它需要工具。比如:
MathPluginTimePluginCodeExecutionPlugin这些插件就像给AI装上了"技能树"。它们通过 [KernelFunction] 这个标记暴露给AI,让AI能够在解决问题时动态地调用。
比如计算阶乘:
csharpusing Microsoft.SemanticKernel;
using System.ComponentModel;
namespace AutoGenDemo.Plugins;
/// <summary>
/// 数学工具 Plugin,演示 SK KernelFunction 与 AutoGen 的集成
/// </summary>
public class MathPlugin
{
[KernelFunction, Description("对两个数求和")]
public double Add(
[Description("第一个数")] double a,
[Description("第二个数")] double b) => a + b;
[KernelFunction, Description("对两个数求乘积")]
public double Multiply(
[Description("第一个数")] double a,
[Description("第二个数")] double b) => a * b;
[KernelFunction, Description("计算一个整数的阶乘")]
public long Factorial([Description("非负整数 n")] int n)
{
if (n < 0) throw new ArgumentException("n 必须 >= 0");
long result = 1;
for (int i = 2; i <= n; i++) result *= i;
return result;
}
}
AI 看到这个描述,就知道"哦,我遇到阶乘问题就可以调这个"。这叫 函数调用(Function Calling),是现代LLM的标配能力。
这里有个挺有意思的设计——CodeExecutionPlugin 用 Roslyn 来动态编译和执行 C# 代码:
csharpusing Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using Microsoft.SemanticKernel;
using System.ComponentModel;
using System.Text;
namespace AutoGenDemo.Plugins;
/// <summary>
/// 代码执行 Plugin:让 LLM 可以动态编写并执行 C# 代码片段
/// </summary>
public class CodeExecutionPlugin
{
private static readonly ScriptOptions _options = ScriptOptions.Default
.AddImports("System", "System.Linq", "System.Collections.Generic",
"System.Math", "System.Text", "System.IO")
.AddReferences(typeof(object).Assembly,
typeof(Enumerable).Assembly);
[KernelFunction, Description(
"执行一段 C# 代码并返回执行结果或错误信息。" +
"代码中用 return 语句返回最终结果(字符串或数字)。" +
"如果代码有错误,返回值以 'ERROR:' 开头。")]
public async Task<string> ExecuteCSharpAsync(
[Description("要执行的 C# 代码片段,使用 return 返回结果")] string code)
{
try
{
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine($"\n[CodeExecutionPlugin] 执行代码:\n{code}\n");
Console.ResetColor();
var result = await CSharpScript.EvaluateAsync<object>(code, _options);
var output = result?.ToString() ?? "(无返回值)";
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"[CodeExecutionPlugin] 执行结果: {output}");
Console.ResetColor();
return output;
}
catch (CompilationErrorException cex)
{
var errors = string.Join("\n", cex.Diagnostics.Select(d => d.ToString()));
return $"ERROR: 编译失败\n{errors}";
}
catch (Exception ex)
{
return $"ERROR: 运行时异常 - {ex.Message}";
}
}
}
换句人话说,就是 AI 写出来的代码可以被立即执行,然后把执行结果或者错误信息反馈给 AI。这是一个 即时反馈循环,让 AI 能够自我修正。
代码里给了四个不同的应用场景。咱们逐个掰扯。
这是最直白的用法。一个AI(SemanticKernelAgent),配上一堆工具(插件),你问它问题,它自己决定用哪个工具。
比如你问:"请计算 15 乘以 7 的结果,再加上 12 的阶乘"
这个AI会:
Multiply 插件Factorial 插件这场景适合那种一问一答、相对简单的任务。但问题是,如果AI算错了,就没人兜底。
这个就牛逼一些了。流程是这样的:
你的问题 ↓ AI 写代码 ↓ 执行引擎运行代码 ↓ 代码有ERROR?→ 反馈给AI重写 → 再执行 → 循环直到成功 代码成功?→ AI 解释结果 → 完成
比如这个需求:"用蒙特卡洛方法估算π的值"
第一次AI可能写得有点问题,执行失败了。代码直接把错误信息抛回去:"ERROR: 编译失败\n..."
AI 看到错误信息,不是傻傻地重复之前的代码,而是读懂错误,修正问题。这里面用到的关键技术是聊天历史维护。每一轮对话都被记录下来,形成一个连贯的上下文。
csharpvar history = new List<IMessage>
{
new TextMessage(Role.User, question)
};
// 每次交互都追加到历史
history.Add(reply);
history.Add(executionResult);
history.Add(nextAIResponse);
现在才是真正有趣的部分。我们把一个 AI 分裂成三个角色:
👨💻 Coder(代码编写者)
👀 Reviewer(代码审查者)
APPROVED 或具体的修改意见🔄 Executor(执行器,这里是 CodeExecutionPlugin)
一轮完整的协作流程长这样:
任务来了 ↓ Coder 写代码 → 执行结果 ↓ Reviewer 看代码和执行结果 → 提意见或通过 ↓ 意见通过? → 结束 不通过? → Coder 读意见 → 改代码 → 执行 → Reviewer再看
这里有个巧妙的地方:Reviewer 不会自己写代码。它只负责挑毛病。这样可以防止两个AI"唱双簧"糊弄你。
这个是 场景3 的加强版。有时候 Reviewer 可能会"仁慈",代码刚刚能用就通过了。但如果我们强制要求至少3轮迭代呢?
csharpif (isApproved && round < MinRoundsForTest)
{
history.Add(new TextMessage(Role.User,
$"为了多轮压测,请继续进行第 {round + 1} 轮优化:补充 1 组极端测试,并保持已有结果正确。"));
}
这样即使代码通过了审查,Coder 还得继续优化,补充边界测试、性能优化之类的。这个设计特别适合生产级别的代码把关。
用这套系统的时候,有些细节得特别留意。
坑1:SystemMessage 的魔力
这些智能体的"性格"全靠 SystemMessage。Coder 的 SystemMessage 要强调"只输出代码,不要解释";Reviewer 的则要强调"只反馈意见,不要改代码"。一旦这个搞混了,整个协作流程就乱套了。
我见过有人给 Reviewer 写的 SystemMessage 是"帮我改进代码",结果 Reviewer 不再给意见了,直接开始改代码。那就变成了两个 Coder 在争谁的方案更好,没有制约了。
坑2:代码块提取的脆弱性
代码里有个工具函数叫 ExtractCodeBlock,用来从 AI 的回复里提取 csharp... 这样的代码块:
csharpstatic string ExtractCodeBlock(string text)
{
const string marker = "```csharp";
var start = text.IndexOf(marker, StringComparison.OrdinalIgnoreCase);
if (start < 0) return string.Empty;
start += marker.Length;
var end = text.IndexOf("```", start);
if (end < 0) return string.Empty;
return text[start..end].Trim();
}
看起来简单,但有个问题:如果 AI 的回复里有多个代码块,这个函数只会提取第一个。而且万一 AI 忘记了 markdown 的语法标记,写成了 '''csharp 或者其他变体,就识别不了。
我的建议是,在 Coder 的 SystemMessage 里明确强调:"代码必须用 csharp ... 包裹,这是必须的,不能有其他格式"。
坑3:异步等待的陷阱
所有的 AI 调用都是异步的(async/await),这在多轮对话里尤其需要注意。如果某一步阻塞了,整个流程就卡住。代码里用了 await 来等待每一步的完成,这样才能保证顺序性。
坑4:环境变量和 API 密钥
代码最开头就要求设置环境变量:
csharpvar apiKey = Environment.GetEnvironmentVariable("ALIYUN_API_KEY")
?? throw new InvalidOperationException("请设置环境变量 ALIYUN_API_KEY");
千万别把密钥硬编码在代码里,这个安全常识就不赘述了。但还有个细节:如果你用 GitHub 或其他代码托管,要确保 .gitignore 里包含了环境变量文件。
假设你现在有一个 C# 项目,想要集成这套多智能体编程助手。步骤大致是这样的:
第一步:装依赖
bashdotnet add package AutoGen.Core dotnet add package AutoGen.OpenAI dotnet add package AutoGen.SemanticKernel dotnet add package Microsoft.SemanticKernel dotnet add package OpenAI dotnet add package Microsoft.CodeAnalysis.CSharp.Scripting
第二步:复制并改造 AgentFactory
把这个工厂类复制到你的项目里。如果你用的不是千问,而是 OpenAI 或其他厂商的API,只需要改这个地方:
csharpusing AutoGen.Core;
using AutoGen.OpenAI;
using AutoGen.OpenAI.Extension;
using AutoGen.SemanticKernel;
using AutoGen.SemanticKernel.Extension;
using AutoGenDemo.Plugins;
using Microsoft.SemanticKernel;
using OpenAI;
using System.ClientModel;
namespace AutoGenDemo.Agents;
public static class AgentFactory
{
private const string DefaultModel = "qwen-plus";
private const string CoderModel = "qwen-coder-plus";
private const string QwenEndpoint = "https://dashscope.aliyuncs.com/compatible-mode/v1";
// ----------------------------------------------------------------
// 私有工厂:创建指向千问的 OpenAIClient
// ----------------------------------------------------------------
private static OpenAIClient CreateQwenClient(string apiKey)
{
var endpoint = new Uri(QwenEndpoint);
var credential = new ApiKeyCredential(apiKey);
var options = new OpenAIClientOptions { Endpoint = endpoint };
return new OpenAIClient(credential, options);
}
// ----------------------------------------------------------------
// 私有工厂:创建指向千问的 Semantic Kernel
// ----------------------------------------------------------------
private static Kernel CreateQwenKernel(string apiKey, string model)
{
return Kernel.CreateBuilder()
.AddOpenAIChatCompletion(
modelId: model,
apiKey: apiKey,
endpoint: new Uri(QwenEndpoint))
.Build();
}
// ----------------------------------------------------------------
// 场景 1:SemanticKernelAgent + 全部 Plugins
// ----------------------------------------------------------------
public static IAgent CreateSkAgent(string apiKey, string model = DefaultModel)
{
var kernel = CreateQwenKernel(apiKey, model);
kernel.Plugins.AddFromObject(new MathPlugin(), "Math");
kernel.Plugins.AddFromObject(new TimePlugin(), "Time");
kernel.Plugins.AddFromObject(new CodeExecutionPlugin(), "CodeExec");
return new SemanticKernelAgent(
kernel: kernel,
name: "sk_agent",
systemMessage: """
你是一个能力强大的 AI 助手,拥有以下工具:
- Math Plugin:数学计算(加法、乘法、阶乘)
- Time Plugin:时间日期查询
- CodeExec Plugin:执行 C# 代码片段
使用规则:
1. 当问题涉及精确计算或逻辑处理时,优先调用工具,不要凭记忆猜测
2. 调用 CodeExec 时,代码中必须用 return 返回最终结果
3. 如果 CodeExec 返回以 "ERROR:" 开头的内容,分析原因并修正代码后重试
4. 最终用清晰的中文回答用户问题
""")
.RegisterMessageConnector()
.RegisterPrintMessage();
}
// ----------------------------------------------------------------
// 场景 2:自我修正循环专用的 Coder Agent(纯 OpenAI Agent)
// ----------------------------------------------------------------
public static IAgent CreateSelfCorrectingCoderAgent(
string apiKey, string model = CoderModel)
{
var client = CreateQwenClient(apiKey);
return new OpenAIChatAgent(
chatClient: client.GetChatClient(model),
name: "coder",
systemMessage: """
你是一个专业的 C# 编程专家,专注于解决计算和逻辑问题。
任务规则:
1. 收到编程任务后,编写能解决问题的 C# 代码片段
2. 代码要求:
- 不含 using 语句(运行环境已预导入常用命名空间)
- 必须用 return 语句返回最终结果(字符串或数字均可)
- 只输出代码本身,用 ```csharp ... ``` 包裹,不要有多余解释
3. 如果收到以 "ERROR:" 开头的错误信息:
- 仔细阅读错误原因
- 修正问题后重新输出完整代码
- 不要只输出修改部分,要输出完整可运行的代码块
4. 如果收到执行成功的结果,用自然语言给出最终答案,不再输出代码
""")
.RegisterMessageConnector()
.RegisterPrintMessage();
}
// ----------------------------------------------------------------
// 场景 3:Multi-Agent 中的 Coder Agent
// ----------------------------------------------------------------
public static IAgent CreateCoderAgent(
string apiKey, string model = CoderModel)
{
var client = CreateQwenClient(apiKey);
return new OpenAIChatAgent(
chatClient: client.GetChatClient(model),
name: "coder",
systemMessage: """
你是一个专业的 C# 程序员,负责编写代码实现需求。
输出规则:
5. 只输出代码片段,用 ```csharp ... ``` 包裹
6. 代码不含 using 语句,直接写逻辑
7. 必须用 return 语句返回最终计算结果,不能只定义方法不执行
8. 如果 Reviewer 指出了问题,认真修改后重新输出完整代码
9. 如果执行结果已正确,不再重复输出代码
""")
.RegisterMessageConnector()
.RegisterPrintMessage();
}
// ----------------------------------------------------------------
// 场景 3:Multi-Agent 中的 Reviewer Agent
// ----------------------------------------------------------------
public static IAgent CreateReviewerAgent(
string apiKey, string model = DefaultModel)
{
var client = CreateQwenClient(apiKey);
return new OpenAIChatAgent(
chatClient: client.GetChatClient(model),
name: "reviewer",
systemMessage: """
你是一名严格的 C# 代码审查员,负责验证 coder 提交的代码。
审查标准:
10. 逻辑正确性:算法是否能得出正确结果
11. 边界处理:是否考虑了空输入、负数、溢出等边界情况
12. 代码质量:是否简洁清晰,没有明显冗余
13. 执行结果校验:若执行结果为 ERROR 或无返回值,必须拒绝通过
输出规则:
- 如果代码通过所有审查,且执行结果也正确,只回复一行:APPROVED
- 如果存在问题,明确指出具体问题(不超过 3 条),要求 coder 修改
- 不要自己重写代码,只提出修改意见
""")
.RegisterMessageConnector()
.RegisterPrintMessage();
}
// ----------------------------------------------------------------
// 场景 3:Multi-Agent 中的 Summarizer Agent(最终汇总)
// ----------------------------------------------------------------
public static IAgent CreateSummarizerAgent(
string apiKey, string model = DefaultModel)
{
var client = CreateQwenClient(apiKey);
return new OpenAIChatAgent(
chatClient: client.GetChatClient(model),
name: "summarizer",
systemMessage: """
你是一个技术文档撰写员。
当 Reviewer 批准代码后,你负责整理本次对话,输出一份简洁的总结,包含:
1. 任务描述(一句话)
2. 最终代码(完整版)
3. 执行结果
4. 简要说明代码思路
输出格式清晰,适合直接保存为技术文档。
""")
.RegisterMessageConnector()
.RegisterPrintMessage();
}
}
第三步:定义自己的 Plugins
根据你的业务需求,创建自己的插件。比如如果你做金融系统,可能需要一个 FinancePlugin;如果做数据处理,可能需要 DataProcessingPlugin。
csharppublic class FinancePlugin
{
[KernelFunction, Description("计算复利收益")]
public double CompoundInterest(
[Description("本金")] double principal,
[Description("年利率")] double rate,
[Description("年数")] int years)
{
return principal * Math.Pow(1 + rate, years);
}
}
第四步:选择合适的场景
我在团队里试过这套系统。一个中等复杂度的算法题(比如"实现一个高效的排序+去重的混合函数"),手工写可能需要 10-15 分钟,包括测试和调试。
用了多智能体协作后:
数字上看省的时间不多,但关键的是代码质量和安心感。因为有 Reviewer 在,代码不会一开始就走歪路。而且如果有边界情况的 bug,往往能在第一轮迭代就被抓出来,而不是等到上线后再发现。
现在的架构还有很多扩展空间。
方向1:更复杂的协作模式
除了 Coder + Reviewer,还可以加入 Optimizer(性能优化员)、Documentor(文档编写员)、SecurityAuditor(安全审计员)。不同的角色对不同的方面负责,最后统一意见。这样就成了真正的"虚拟团队"。
方向2:与真实开发工具的集成
现在代码执行是本地的 Roslyn。未来可以考虑直接提交到 git、运行 CI/CD 管道、跑单元测试套件。这样 AI 的反馈循环就能包括真实的测试结果。
方向3:记忆和学习机制
现在每次都是"无状态"的对话。如果能让 AI 记住"之前这个问题咱们怎么解决的",下次遇到类似问题就能更快解决。这涉及到向量数据库和检索增强生成(RAG)的东西。
方向4:成本优化
现在每一轮对话都得调 API,成本可能比较高。可以考虑用小模型(比如 Qwen-Coder-Plus)做初步编写,只在关键的审查环节用大模型。这样能显著降低成本。
说实话,刚开始看这套代码的时候,我的第一反应是:"这得多复杂啊"。但深入理解后发现,本质就是几个 if-else 和一个聊天历史列表。AI 之间没有什么神秘的"智能对话",就是严格的角色定位 + 明确的输入输出规范 + 反馈循环的设计。
这其实挺有启发意义的。在团队协作中,人和人之间的高效配合,也不是靠某某人特别天才,而是靠清晰的职责分工、明确的沟通规范和及时的反馈机制。用 AI 来演这一出,某种程度上就是在验证这套理论。
如果你的项目涉及到重复性强、需要反复迭代、质量要求高的编程任务,这套架构值得一试。从小场景开始试水(比如先用场景2的自我修正循环),一旦摸清了门道,再逐步升级到多智能体协作。
下次再有人说"AI 写的代码不可靠",你就可以笑笑,说:"那是因为你没有让多个 AI 互相监督啊。" 🙂
关键技术栈速查表
| 组件 | 作用 | 核心类/方法 |
|---|---|---|
| AutoGen | 多智能体框架 | IAgent、GroupChat |
| SemanticKernel | 提示词编排 + 函数调用 | Kernel、KernelFunction |
| Roslyn | C# 动态编译执行 | CSharpScript.EvaluateAsync |
| OpenAI SDK | 与 LLM 通信 | OpenAIChatAgent、ChatClient |
| 自定义 Plugins | 工具集合 | MathPlugin、TimePlugin、CodeExecutionPlugin |
三个"一句话"的收获
多智能体协作 = 角色分工 + 反馈循环,本质上就是把真实的代码审查流程自动化了。
SystemMessage 是灵魂,决定了每个 AI 的性格和职责,写得好协作顺畅,写得差就是"自说自话"。
即时反馈是关键,AI 写完代码立即执行、立即看到结果、立即修改,这个闭环比"一次性输出"强太多。
如果你觉得这套思路还不错,欢迎在评论区分享你的看法。或者,如果你在实际应用中遇到了其他的坑或者创意用法,也很乐意听听。下一期,咱们可以聊聊怎样进一步优化这套系统的成本和性能。
推荐阅读
深入了解 Semantic Kernel 的函数调用机制 → [官方文档传送门]
AutoGen 的设计思想 → [研究论文链接]
Roslyn 在实际项目中的应用案例 → [案例分享文章]
相关信息
通过网盘分享的文件:AutoGenDemo.zip 链接: https://pan.baidu.com/s/1RMnLL-2hMbB2CPuvAc69Cg?pwd=h3cv 提取码: h3cv --来自百度网盘超级会员v9的分享
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!