AutoGen.Net 代码执行实战:用 dotnet-interactive 内核运行 Agent 返回的代码片段
AutoGen.Net 内置了“从 Agent 响应中运行代码片段”的能力,当前支持 dotnet(含 C#、F#、PowerShell)以及通过 Jupyter 协议接入的 Python 内核。本文基于文档 Run-dotnet-code,结合 AutoGen.DotnetInteractive 源码实现,讲解如何构建 dotnet-interactive 组合内核(Composite Kernel)、注册中间件拦截代码块并执行,以及如何在 agentic 工作流中编排“写代码—执行代码”的闭环。
什么是代码片段(code snippet)?
在 Agent 响应中,代码片段是指带有语言标识符的代码块。例如一段包含 C# 和 Python 两个代码块的文本:
var content = @"
```csharp
// This is csharp code snippet
```
```python
// This is python code snippet
```
";
该示例完整可见于 RunCodeSnippetCodeSnippet.cs。
为什么运行代码片段有价值?
代码执行能力可以显著扩展 Agent 的能力边界:Agent 不再是只能返回纯文本回答,而是能够“先写代码、再运行代码”来解决任务,其能力远比纯文本响应更强。
以数据分析场景为例,面对“最近 7 天的销售额平均值是多少?”这类问题,Agent 可以先写一段查询最近 7 天销售额并计算平均值的代码片段,然后执行该片段拿到真实结果,而不是凭空给出一个数字。
安全警告:运行来自 Agent 响应的任意代码片段可能给系统带来风险(执行不受控的进程、文件、网络操作等),在生产环境使用此功能时需格外谨慎,例如限制内核可访问的资源、在隔离环境中运行。
使用 dotnet interactive 内核执行代码片段
AutoGen.Net 中执行 dotnet 代码片片的内置能力由 dotnet-interactive 提供。使用前需要向项目添加 AutoGen.DotnetInteractive 包引用,它封装了 AutoGen 与 dotnet-interactive 的集成:
<PackageReference Include="AutoGen.DotnetInteractive" />
步骤一:创建 in-process 组合内核
通过 DotnetInteractiveKernelBuilder.CreateDefaultInProcessKernelBuilder() 可以创建一个包含 C# 与 F# 内核的进程内(in-process)dotnet-interactive 组合内核:
using AutoGen.Core;
using AutoGen.DotnetInteractive;
using AutoGen.DotnetInteractive.Extension;
var kernel = DotnetInteractiveKernelBuilder
.CreateDefaultInProcessKernelBuilder() // add C# and F# kernels
.Build();
对应示例位于 RunCodeSnippetCodeSnippet.cs。
从源码结构看,DotnetInteractiveKernelBuilder 提供两类构建入口:
CreateDefaultInProcessKernelBuilder():返回一个预置了 C# 与 F# 内核的 InProccessDotnetInteractiveKernelBuilder,注意它带有#if NET8_0_OR_GREATER条件编译,即进程内内核仅在 .NET 8 及以上目标框架可用;CreateEmptyInProcessKernelBuilder():返回不含任何内核的空构建器,供按需添加内核;CreateKernelBuilder(workingDirectory, kernelName):返回基于外部进程(stdio)的DotnetInteractiveStdioKernelConnector,适合需要隔离进程的场景。
InProccessDotnetInteractiveKernelBuilder 的构造器内部创建了一个 CompositeKernel,并注册了 Jupyter 连接器指令(ConnectJupyterKernelDirective,同时支持 HTTP 与本地两种连接方式),这正是后续可以挂接 Python 内核的基础。它还提供了以下扩展方法,均可通过别名调用对应内核:
| 方法 | 默认别名 | 说明 |
|---|---|---|
AddCSharpKernel(aliases) |
c#、C#、csharp |
创建 C# 内核,启用 NuGet 指令、KernelHelpers、%who 与值共享 |
AddFSharpKernel(aliases) |
f#、F#、fsharp |
创建 F# 内核,启用默认格式化、KernelHelpers、%who 与值共享 |
AddPowershellKernel(aliases) |
pwsh、powershell |
创建 PowerShell 内核,启用配置文件与值共享 |
AddPythonKernel(venv, kernelName) |
— | 通过 Jupyter 协议挂接 Python 内核(见下文) |
Build() 最终返回启用默认魔法命令与 #!import 命令的 CompositeKernel。
步骤二:注册中间件执行代码块并取回结果
拿到内核后,通过 IAgent.RegisterMiddleware 注册中间件,在 Agent 收到消息时提取代码块并用 RunSubmitCodeCommandAsync 扩展方法执行。该方法会返回代码片段的执行结果:
// register middleware to execute code block
var dotnetCodeAgent = agent
.RegisterMiddleware(async (msgs, option, innerAgent, ct) =>
{
var lastMessage = msgs.LastOrDefault();
if (lastMessage == null || lastMessage.GetContent() is null)
{
return await innerAgent.GenerateReplyAsync(msgs, option, ct);
}
if (lastMessage.ExtractCodeBlock("```csharp", "```") is string codeSnippet)
{
// execute code snippet
var result = await kernel.RunSubmitCodeCommandAsync(codeSnippet, "csharp");
return new TextMessage(Role.Assistant, result, from: agent.Name);
}
else
{
// no code block found, invoke next agent
return await innerAgent.GenerateReply(msgs, option, ct);
}
});
var codeSnippet = @"
```csharp
Console.WriteLine(""Hello World"");
```";
await dotnetCodeAgent.SendAsync(codeSnippet);
// output: Hello World
完整可运行版本见 RunCodeSnippetCodeSnippet.cs。这里的关键 API 在源码中的实现值得注意:
ExtractCodeBlock/ExtractCodeBlocks:定义于 MessageExtension.cs,基于正则codeBlockPrefix([\s\S]*?)codeBlockSuffix从消息内容中提取代码块。单数版本只返回第一个匹配的代码块,复数版本以惰性迭代返回所有匹配块,并对提取内容做Trim()。因此调用ExtractCodeBlock("```csharp", "```")时,前缀需精确匹配语言标识(如csharp)。RunSubmitCodeCommandAsync:定义于 KernelExtension.cs。它将代码块包装为SubmitCode命令并发送到指定名称的目标内核,然后从命令结果事件中筛选StandardOutputValueProduced、StandardErrorValueProduced、ReturnValueProduced、DisplayedValueProduced四类展示事件,把格式化后的输出值用换行拼接为字符串返回。两个边界行为需要注意:若没有任何展示输出则返回null;若命令执行失败则捕获异常并返回形如Error: {message}的字符串,而不是抛出异常。
运行 Python 代码片段
要运行 Python 代码,需要先在机器上安装 Python,并在环境中配置好 ipykernel 与 jupyter:
pip install ipykernel
pip install jupyter
安装完成后,可以运行以下命令确认 ipykernel 安装正确:
jupyter kernelspec list
输出应包含所有可用内核,其中需要能看到 python3:
Available kernels:
python3 /usr/local/share/jupyter/kernels/python3
...
确认环境就绪后,调用 AddPythonKernel 方法把 Python 内核挂接到 dotnet-interactive 组合内核上:
var pythonKernel = DotnetInteractiveKernelBuilder
.CreateDefaultInProcessKernelBuilder()
.AddPythonKernel(venv: "python3")
.Build();
var pythonCode = """
print('Hello from Python!')
""";
var result = await pythonKernel.RunSubmitCodeCommandAsync(pythonCode, "python3");
对应示例见 RunCodeSnippetCodeSnippet.cs。
从源码看,AddPythonKernel 并不直接创建 Python 进程,而是向组合内核提交一条 Jupyter 连接魔法命令:
#!connect jupyter --kernel-name {kernelName} --kernel-spec {venv}
其中 venv 参数指定 Jupyter 内核规范(kernelspec,示例中为 python3),kernelName 参数(默认 python)是连接后内核在组合内核中的名称;命令失败时会通过 ThrowOnCommandFailed 抛出异常。这也解释了为什么 Python 路径依赖 jupyter kernelspec list 能列出对应内核——本质上是 dotnet-interactive 的 Jupyter 连接器在拉起并连接外部 Python 内核进程,与前文构造器中预置的 ConnectJupyterKernelDirective 相呼应。
在 agentic 工作流中运行代码片段
上面的中间件方式适合单 Agent 场景。文档“Further reading”部分给出的两个动态群聊示例展示了更完整的编排形态(均位于 AutoGen.Basic.Sample):
- Example04_Dynamic_GroupChat_Coding_Task.cs:动态群聊解决编程任务的完整工作流。示例中用
CreateDefaultInProcessKernelBuilder().AddPythonKernel("python3").Build()构建同时具备 C#、F# 与 Python 能力的内核,然后由 admin Agent 把用户问题拆解为小任务,分派给 coder(写 Python 代码)与 runner(执行代码)等 Agent,循环推进直到问题解决; - Example07_Dynamic_GroupChat_Calculate_Fibonacci.cs:用动态群聊计算斐波那契数列,演示“写代码 Agent + 执行代码 Agent”协作的更简洁形态。
小结
- AutoGen.Net 通过
AutoGen.DotnetInteractive包提供“运行 Agent 响应中代码片段”的内置能力,底层是 dotnet-interactive 的CompositeKernel; CreateDefaultInProcessKernelBuilder().Build()得到含 C#、F# 内核的进程内组合内核(仅 .NET 8+),可再经AddPowershellKernel、AddPythonKernel扩展语言支持;ExtractCodeBlock负责从消息中正则提取带语言标识的代码块,RunSubmitCodeCommandAsync负责提交执行并聚合 stdout/stderr/返回值/展示值,无输出返回null、失败返回Error: ...字符串;- Python 支持依赖本机
ipykernel+jupyter环境,通过 Jupyter 魔法命令连接外部内核; - 该能力让 Agent 从“只回答”进化到“写代码并运行代码”,但执行任意代码存在安全风险,应按文档中的警告谨慎启用。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00