AutoGen .NET 实战:基于 Ollama + LiteLLM 的本地模型 Function Call 完整指南
【免费下载链接】autogenA programming framework for agentic AI项目地址: https://gitcode.com/GitHub_Trending/au/autogen
本文围绕 AutoGen .NET 官方文档《Function call with Ollama and LiteLLM》展开,讲解如何使用本地部署的 Ollama 模型(以支持函数调用的dolphincoder:latest为例)配合 LiteLLM 代理服务器,让 AutoGen 的OpenAIChatAgent在本地完成类型安全的 Function Call。读完本篇,你可以独立搭建一套"本地模型 + OpenAI 兼容接口 + AutoGen 工具调用"的完整链路,并理解FunctionCallMiddleware在消息流中的底层处理机制。
一、整体架构:为什么需要 LiteLLM 做中间层
AutoGen .NET 中与 OpenAI 生态对接的入口是OpenAIChatAgent,它依赖 OpenAI SDK 的OpenAIClient。本地 Ollama 服务本身提供的是 Ollama 私有接口,而 LiteLLM 可以将任意模型(含 Ollama)包装成OpenAI API 兼容的服务端点,于是整条链路为:
OpenAIChatAgent (AutoGen) -> OpenAI SDK (指向 http://localhost:4000) -> LiteLLM 代理 (OpenAI 兼容接口) -> Ollama (本地模型 dolphincoder:latest)正因为 LiteLLM 提供的是 OpenAI 兼容接口,AutoGen 侧无需任何 Ollama 专属适配代码,只需把OpenAIClient的 Endpoint 指向本地代理即可。
二、前置条件与环境准备
运行该示例需要以下三项前置条件(引自官方文档 Function-call-with-ollama-and-litellm.md):
- 在本地安装 Ollama 与 LiteLLM;
- 一个支持函数调用的本地模型,示例使用
dolphincoder:latest; - 已启用 .NET SDK 的 AutoGen 项目。
2.1 安装 Ollama 并拉取模型
按照 Ollama 官方说明完成安装后,执行以下命令拉取模型:
ollama pull dolphincoder:latestOllama 默认监听http://localhost:11434,这一点在示例代码的注释中也有明确说明(见 Tool_Call_With_Ollama_And_LiteLLM.cs):
// Before running this code, make sure you have // - Ollama: // - Install dolphincoder:latest in Ollama // - Ollama running on http://localhost:11434 // - LiteLLM // - Install LiteLLM // - Start LiteLLM with the following command: // - litellm --model ollama_chat/dolphincoder --port 40002.2 安装 LiteLLM 并启动代理服务器
通过 pip 安装带代理功能的 LiteLLM:
pip install 'litellm[proxy]'然后启动指向 Ollama 模型的 OpenAI 兼容代理:
litellm --model ollama_chat/dolphincoder --port 4000该命令会在http://localhost:4000启动 OpenAI API 兼容的代理服务。终端出现如下输出即表示启动成功:
#------------------------------------------------------------# # # # 'The worst thing about this product is...' # # https://github.com/BerriAI/litellm/issues/new # # # #------------------------------------------------------------# INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:4000 (Press CTRL+C to quit)注意--model参数的格式约定:ollama_chat/dolphincoder中前缀ollama_chat告诉 LiteLLM 底层是 Ollama 的 chat 端点,dolphincoder是模型名。
三、安装 AutoGen 与 AutoGen.SourceGenerator
在项目目录中执行:
dotnet add package AutoGen dotnet add package AutoGen.SourceGeneratorAutoGen.SourceGenerator是一个 Roslyn 源生成器包,用于把标记了[Function]特性的方法自动编译为类型安全的FunctionContract(以及对应的XXXWrapper委托),免去手工编写 JSON Schema 与参数解析代码。关于该机制的详细说明可参考 Create-type-safe-function-call.md。
为了让源生成器读取方法的 XML 文档注释(用于生成工具描述,直接影响 LLM 对工具的识别效果),需要在项目文件中启用结构化 XML 文档:
<PropertyGroup> <!-- This enables structural xml document support --> <GenerateDocumentationFile>true</GenerateDocumentationFile> </PropertyGroup>四、定义 WeatherReport 函数并创建 FunctionCallMiddleware
4.1 定义工具函数
约定:承载方法的类必须是public partial,方法必须是public实例方法,返回类型必须是Task<string>,并用[Function]特性标记。示例完整代码如下(摘自 Tool_Call_With_Ollama_And_LiteLLM.cs):
#region Function public partial class Function { [Function] public async Task<string> GetWeatherAsync(string city) { return await Task.FromResult("The weather in " + city + " is 72 degrees and sunny."); } } #endregion Function编译后,源生成器会为GetWeatherAsync生成GetWeatherAsyncFunctionContract(工具契约)与GetWeatherAsyncWrapper(可被中间件调用的包装委托)。
4.2 创建 FunctionCallMiddleware
#region Create_tools var functions = new Function(); var functionMiddleware = new FunctionCallMiddleware( functions: [functions.GetWeatherAsyncFunctionContract], functionMap: new Dictionary<string, Func<string, Task<string>>> { { functions.GetWeatherAsyncFunctionContract.Name!, functions.GetWeatherAsyncWrapper }, }); #endregion Create_tools构造参数含义:
| 参数 | 类型 | 作用 |
|---|---|---|
functions | IEnumerable<FunctionContract>? | 工具契约列表,会被透传给 Agent,模型据此生成工具调用 |
functionMap | IDictionary<string, Func<string, Task<string>>>? | 工具名 → 实际执行委托的映射,用于真正执行工具并拿到结果 |
name | string? | 中间件名称,默认为FunctionCallMiddleware |
源码级原理:FunctionCallMiddleware的完整实现位于 FunctionCallMiddleware.cs,它实现了IStreamingMiddleware,即同时支持流式与非流式。其非流式入口InvokeAsync(第 63-86 行)的处理逻辑值得细看,它决定了工具调用的完整闭环:
- 入站短路:如果消息队列中最后一条消息是
ToolCallMessage(通常是上一轮模型发起的调用结果回传),中间件会直接在InvokeToolCallMessagesBeforeInvokingAgentAsync中执行functionMap里对应的函数,返回ToolCallResultMessage,内层 Agent 被短路、不会再次调用模型(第 138-164 行)。若某个工具名不在functionMap中,会返回Function {name} is not available...的报错信息作为工具结果,让模型自行纠正;若functionMap为 null 则直接抛出InvalidOperationException。 - 工具契约合并:调用 Agent 前,中间件会把自身持有的
functions与GenerateReplyOptions.Functions中的工具合并后一并下发(第 71-76 行),因此可以在不同中间件中注册多组工具而不互相覆盖。 - 出站拦截:如果 Agent 的回复是
ToolCallMessage且工具名在functionMap中,中间件会立即执行工具,并把"工具调用 + 工具结果"打包为ToolCallAggregateMessage返回(第 166-190 行);若回复不是工具调用或工具不可执行,则原样返回模型回复。
正是这个"出站拦截 + 结果聚合"的行为,解释了下文运行结果中为什么一次SendAsync就能看到工具调用和工具结果两条消息。
五、创建 OpenAIChatAgent 并发起对话
由于 LiteLLM 代理是 OpenAI API 兼容的,直接用OpenAIChatAgent作为第三方 OpenAI 兼容后端接入即可。本地服务不需要真实密钥,ApiKeyCredential传任意字符串即可:
#region Create_Agent // api-key is not required for local server // so you can use any string here var openAIClient = new OpenAIClient(new ApiKeyCredential("api-key"), new OpenAIClientOptions { Endpoint = new Uri("http://localhost:4000"), }); var agent = new OpenAIChatAgent( chatClient: openAIClient.GetChatClient("dolphincoder:latest"), name: "assistant", systemMessage: "You are a helpful AI assistant") .RegisterMessageConnector() .RegisterMiddleware(functionMiddleware) .RegisterPrintMessage(); await agent.SendAsync("what's the weather in new york"); #endregion Create_Agent关键点说明:
- Endpoint 指向代理:
http://localhost:4000是 LiteLLM 端口,而非 Ollama 的 11434; - 模型名保持 Ollama 侧命名:
GetChatClient("dolphincoder:latest")中的模型名会透传到 LiteLLM,再映射到 Ollama 的dolphincoder:latest; RegisterMessageConnector():来自 OpenAIAgentExtension.cs,负责把 AutoGen 消息与 OpenAI SDK 的ChatMessage互转;RegisterMiddleware(functionMiddleware):注册工具调用中间件;RegisterPrintMessage():控制台打印消息,便于观察。
六、运行结果与消息流解读
运行上述对话后,控制台输出形如(引自官方文档):
AggregateMessage from assistant -------------------- ToolCallMessage: ToolCallMessage from assistant -------------------- - GetWeatherAsync: {"city": "new york"} -------------------- ToolCallResultMessage: ToolCallResultMessage from assistant -------------------- - GetWeatherAsync: The weather in new york is 72 degrees and sunny. --------------------对照第五节的源码分析,这条输出正是ToolCallAggregateMessage的打印形态:外层是聚合消息(AggregateMessage),内部依次包含模型的ToolCallMessage(参数{"city": "new york"})和中间件本地执行后生成的ToolCallResultMessage("72 degrees and sunny")。模型识别出意图 → 生成工具调用 → 中间件执行本地函数 → 结果聚合回消息流,整个闭环在本地完成,没有任何请求离开局域网。
七、小结与适用边界
本方案的价值在于用最少代码打通"本地模型 + 工具调用":AutoGen 侧只写了工具类、中间件和 Agent 三段代码,所有协议适配都交给 LiteLLM 完成。需要注意的适用前提与限制:
- 所选本地模型必须真正支持 function calling,否则模型无法生成结构化工具调用,示例选用
dolphincoder:latest正是基于该能力; - 端口与模型名需三方对齐:Ollama(11434)← LiteLLM(4000,
ollama_chat/模型名)← OpenAI SDK(Endpoint 4000,模型名原样); FunctionCallMiddleware依赖functionMap执行工具,未注册的工具名会得到"不可用"错误信息而非静默失败,便于调试;- 若希望进一步理解类型安全工具契约的生成机制,可继续阅读 Create-type-safe-function-call.md 与 FunctionCallGenerator.cs。
完整可运行示例见 Tool_Call_With_Ollama_And_LiteLLM.cs,其Program.cs入口位于同目录,可按需加入自己的解决方案运行验证。
【免费下载链接】autogenA programming framework for agentic AI项目地址: https://gitcode.com/GitHub_Trending/au/autogen
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考