 中基于 Gemini 实现函数调用:AutoGen.Gemini 工具调用(Function Calling)实战指南)
AutoGen(.NET) 中基于 Gemini 实现函数调用AutoGen.Gemini 工具调用Function Calling实战指南【免费下载链接】autogenA programming framework for agentic AI项目地址: https://gitcode.com/GitHub_Trending/au/autogen本文围绕 AutoGen.NET 版的AutoGen.Gemini包讲解如何让GeminiChatAgent完成函数调用Function Calling从 NuGet 依赖安装、使用AutoGen.SourceGenerator生成类型安全的函数契约、通过 Vertex AI 创建带ToolConfig的 Gemini Agent到单轮与多轮工具调用的完整代码流程并结合GeminiMessageConnector、FunctionContractExtension等源码剖析消息角色映射与工具声明转换的底层原理。读完本文你可以在 .NET 项目中复现一个能够响应查电影/查影院/查场次等自然语言请求、自动触发 C# 函数并返回最终答案的 Gemini 工具调用 Agent。一、前置条件与运行环境本示例基于 Google Vertex AI 提供的 Gemini 模型运行函数调用Function Calling示例逻辑改编自 Google 官方 Gemini API 的 function calling 教程。运行前需要满足拥有一个 Google Cloud 项目并开通了 Vertex AI API 访问权限在运行环境设置环境变量GCP_VERTEX_PROJECT_ID示例代码会读取该变量若未设置则直接退出并提示export GCP_VERTEX_PROJECT_IDyour-gcp-project-id # Linux/macOS # Windows PowerShell: # $env:GCP_VERTEX_PROJECT_ID your-gcp-project-id示例的完整可运行代码见 Function_Call_With_Gemini.cs下文各步骤代码均取自该文件对应的#region片段。二、Step 1安装 AutoGen.Gemini 与 AutoGen.SourceGenerator使用以下命令安装两个 NuGet 包dotnet add package AutoGen.Gemini dotnet add package AutoGen.SourceGeneratorAutoGen.Gemini提供GeminiChatAgent、GoogleGeminiClient、VertexGeminiClient及消息转换中间件AutoGen.SourceGenerator用于自动生成AutoGen.Core.FunctionContract函数契约。它是一个 Roslyn 源生成器只要给方法打上Function特性就会基于方法签名和 XML 文档注释生成函数定义与类型安全的调用包装器。其使用细节参见同仓库文档 Create-type-safe-function-call 及 AutoGen.SourceGenerator 说明。建议配置为了让源生成器读取方法的 XML 文档注释函数描述、参数说明会进入函数契约在 csproj 中开启结构化文档生成PropertyGroup !-- This enables structural xml document support -- GenerateDocumentationFiletrue/GenerateDocumentationFile /PropertyGroup三、Step 2添加 using 语句using AutoGen.Core; using Google.Cloud.AIPlatform.V1;AutoGen.Core提供TextMessage、Role、FunctionCallMiddleware等核心消息与中间件类型Google.Cloud.AIPlatform.V1提供 Vertex AI 的 protobuf 类型例如ToolConfig、FunctionCallingConfig示例中创建 Agent 时会用到。四、Step 3创建MovieFunction函数集示例定义了一个MovieFunction类包含三个模拟电影查询业务的函数模拟 Google 官方教程中的电影查询场景public partial class MovieFunction { /// summary /// find movie titles currently playing in theaters based on any description, genre, title words, etc. /// /summary /// param namelocationThe city and state, e.g. San Francisco, CA or a zip code e.g. 95616/param /// param namedescriptionAny kind of description including category or genre, title words, attributes, etc./param /// returns/returns [Function] public async Taskstring FindMovies(string location, string description) { // dummy implementation var movies new Liststring { Barbie, Spiderman, Batman }; var result $Movies playing in {location} based on {description} are: {string.Join(, , movies)}; return result; } /// summary /// find theaters based on location and optionally movie title which is currently playing in theaters /// /summary /// param namelocationThe city and state, e.g. San Francisco, CA or a zip code e.g. 95616/param /// param namemovieAny movie title/param [Function] public async Taskstring FindTheaters(string location, string movie) { // dummy implementation var theaters new Liststring { AMC, Regal, Cinemark }; var result $Theaters playing {movie} in {location} are: {string.Join(, , theaters)}; return result; } /// summary /// Find the start times for movies playing in a specific theater /// /summary /// param namelocationThe city and state, e.g. San Francisco, CA or a zip code e.g. 95616/param /// param namemovieAny movie title/param /// param nametheaterName of the theater/param /// param namedateDate for requested showtime/param /// returns/returns [Function] public async Taskstring GetShowtimes(string location, string movie, string theater, string date) { // dummy implementation var showtimes new Liststring { 10:00 AM, 12:00 PM, 2:00 PM, 4:00 PM, 6:00 PM, 8:00 PM }; var result $Showtimes for {movie} at {theater} in {location} are: {string.Join(, , showtimes)}; return result; } }对应源码位置Function_Call_With_Gemini.cs#L13-L64。编写这三个函数时需要注意源生成器的约束约束说明类必须是public partial源生成器需要 partial 类来注入生成的代码方法必须是public实例方法返回Taskstring函数调用包装器以字符串结果回传给模型参数建议使用基本类型从源生成器文档看这是出于性能与 JSON 序列化稳定性的考虑必须提供 XML 文档注释方法summary与参数param注释会被写入函数契约直接影响模型选择函数与填参的准确性编译后源生成器会为每个方法生成两个成员以FindMovies为例FindMoviesFunctionContractAutoGen.Core.FunctionContract包含函数名、描述、参数元数据是与具体 LLM 无关的中间表示FindMoviesWrapper(string arguments)类型安全包装器内部先把模型返回的 JSON 参数反序列化为参数对象再调用真正的FindMovies方法。这与 AutoGen.SourceGenerator README 中描述的生成模式一致生成XxxFunction定义与XxxWrapper包装器本文示例使用的是AutoGen.Core的FunctionContract变体可无缝接入FunctionCallMiddleware。五、Step 4创建带工具配置的 Gemini Agentvar projectID Environment.GetEnvironmentVariable(GCP_VERTEX_PROJECT_ID); if (projectID is null) { Console.WriteLine(Please set GCP_VERTEX_PROJECT_ID environment variable.); return; } var movieFunction new MovieFunction(); var functionMiddleware new FunctionCallMiddleware( functions: [ movieFunction.FindMoviesFunctionContract, movieFunction.FindTheatersFunctionContract, movieFunction.GetShowtimesFunctionContract ], functionMap: new Dictionarystring, Funcstring, Taskstring { { movieFunction.FindMoviesFunctionContract.Name!, movieFunction.FindMoviesWrapper }, { movieFunction.FindTheatersFunctionContract.Name!, movieFunction.FindTheatersWrapper }, { movieFunction.GetShowtimesFunctionContract.Name!, movieFunction.GetShowtimesWrapper }, }); var geminiAgent new GeminiChatAgent( name: gemini, model: gemini-1.5-flash-001, location: us-central1, project: projectID, systemMessage: You are a helpful AI assistant, toolConfig: new ToolConfig() { FunctionCallingConfig new FunctionCallingConfig() { Mode FunctionCallingConfig.Types.Mode.Auto, } }) .RegisterMessageConnector() .RegisterPrintMessage() .RegisterStreamingMiddleware(functionMiddleware);对应源码位置Function_Call_With_Gemini.cs#L73-L112。5.1 关键参数说明这里使用的是面向 Vertex AI 的GeminiChatAgent构造函数见 GeminiChatAgent.cs#L113-L134参数含义如下参数取值/说明nameAgent 名称示例为gemini消息连接器会用它区分自己发出的与用户侧消息modelGemini 模型名如gemini-1.5-flash-001构造函数内部会拼接为projects/{project}/locations/{location}/publishers/{provider}/models/{model}的完整资源路径provider默认googlelocation模型服务位置示例为us-central1projectGCP 项目 ID来自环境变量systemMessage系统指令示例为You are a helpful AI assistant源码中它会被放入请求的SystemInstruction字段而非普通对话轮次toolConfig工具调用配置核心是FunctionCallingConfig.Mode关于FunctionCallingConfig.Types.ModeMode.Auto示例所用模型自行判断是否需要调用函数Mode.Any强制模型至少调用一个函数Mode.None禁用函数调用。5.2 三个注册方法各自的作用RegisterMessageConnector()注册GeminiMessageConnector负责把 AutoGen 的TextMessage/ToolCallMessage/ToolCallResultMessage等消息双向翻译成 Gemini 的Contentuser/model/function角色。它是函数调用消息闭环的关键后文展开RegisterPrintMessage()打印消息中间件便于在控制台观察对话过程RegisterStreamingMiddleware(functionMiddleware)注册FunctionCallMiddleware。当模型返回函数调用请求时中间件按functionMap中注册的委托实际执行对应 C# 方法并把结果封装为工具调用结果消息回灌给 Agent从而让模型基于真实返回继续作答。六、Step 5单轮函数调用Single-turnvar question new TextMessage(Role.User, What movies are showing in North Seattle tonight?); var functionCallReply await geminiAgent.SendAsync(question);// 断言第一轮回复应当是工具调用聚合消息 functionCallReply.Should().BeOfTypeToolCallAggregateMessage();流程说明用户消息What movies are showing in North Seattle tonight?进入 Agent由于Mode.AutoGemini 判定需要查询正在上映的电影返回一个对FindMovies的FunctionCall参数为location与descriptionFunctionCallMiddleware拦截该调用通过functionMap找到FindMoviesWrapper执行 C# 函数并拿到结果最终SendAsync返回的functionCallReply是ToolCallAggregateMessage——它聚合了模型发起的函数调用与函数执行结果两段信息示例用 FluentAssertions 断言了这一类型证明工具链路确实被触发。源码视角一轮调用中消息如何流转RegisterMessageConnector()注册的GeminiMessageConnectorGeminiMessageConnector.cs在这条链路中承担了 Gemini 角色体系的映射出站方向用户TextMessage被转为Role user的Content模型产生的ToolCallMessage被转为Role model且携带FunctionCallPart 的Content见 ProcessToolCallMessage#L312-L341函数执行结果ToolCallResultMessage被转为Role function且携带FunctionResponse的Content若结果本身不是 JSON 对象连接器会将其包装为{result: ...}后再序列化见 ProcessToolCallResultMessage#L269-L310入站方向GenerateContentResponse中的FunctionCallPart 会被收集并转换为 AutoGen 的ToolCallMessage文本 Part 则转换为TextMessage见 PostProcessMessage#L165-L200。因此模型看到的对话历史始终是 Gemini 规范要求的user / model / function交替角色序列GeminiChatAgent.BuildChatRequest还会校验首条消息必须来自 user 或 function、末条消息同样如此并把连续同角色消息合并为一条见 GeminiChatAgent.cs#L157-L267。另一个值得注意的实现细节从BuildChatRequest源码看所有FunctionContract会经ToFunctionDeclaration()转成 Gemini 的FunctionDeclarationFunctionContractExtension.cs#L20-L53其中参数的IsRequired映射到 OpenAPI 的Required列表、参数类型映射到 OpenAPI 类型随后被合并进单个Tool——源码注释指出这是当前 Gemini 尚不支持多个Tool条目的规避方案多函数场景应像本示例一样通过FunctionCallMiddleware传入多个函数契约而不是传多个Tool。七、Step 6多轮函数调用Multi-turnvar finalReply await geminiAgent.SendAsync(chatHistory: [question, functionCallReply]);// 断言携带工具结果后再问一次应当得到最终的文本回复 finalReply.Should().BeOfTypeTextMessage();多轮调用的要点把上一轮的question与functionCallReply含函数调用与执行结果一起作为聊天历史再次发送GeminiMessageConnector会将ToolCallAggregateMessage拆分为modelFunctionCallPart与functionFunctionResponsePart两条Content回灌给模型见 ProcessToolCallAggregateMessage#L227-L250Gemini 基于真实的函数返回结果如Movies playing in North Seattle based on ... are: Barbie, Spiderman, Batman生成自然语言答案此时SendAsync返回的finalReply是TextMessage即完成了用户提问 → 工具调用 → 执行 → 文本作答的完整闭环。对于查询某影院某电影某日场次这类问题模型可能连续触发FindMovies、FindTheaters、GetShowtimes多个函数FunctionCallMiddleware会循环执行直到模型认为信息充分、输出最终文本示例中的两个BeOfType断言见 Function_Call_With_Gemini.cs#L119-L129正是在验证这一第一轮工具消息、末轮文本消息的行为契约。八、常见问题与注意事项模型资源路径使用 Vertex AI 构造函数时model参数只需传模型短名如gemini-1.5-flash-001完整路径由 GeminiChatAgent 构造函数 自动拼接若改用IGeminiClient构造函数则需自行传入完整资源路径。系统消息的处理systemMessage不会作为普通Content参与user/model交替序列而是放入SystemInstruction见 GeminiChatAgent.cs#L197-L220GeminiMessageConnector对显式Role.System的TextMessage在非严格模式下会降级为user消息处理Gemini 对话轮次中不存在 system 角色。多 Tool 限制如第六节所述多个函数应通过FunctionCallMiddleware的functions集合声明由BuildChatRequest统一聚合到单个Tool中下发。运行验证仓库为AutoGen.Gemini提供了测试工程 dotnet/test/AutoGen.Gemini.Tests其中包含对GeminiChatAgent行为如消息转换的测试可作为行为对照参考本文示例本身则通过FluentAssertions断言在运行时自验证工具调用类型。依赖包版本文中dotnet add package AutoGen.Gemini安装的是 NuGet 上的发布版本示例位于 AutoGen.Gemini.Sample 工程若在本仓库内直接运行建议以仓库的 Directory.Packages.props 中的集中版本为准。九、小结本文以 AutoGen(.NET) 官方文档《Function-call-with-gemini》为主线完整复现了基于AutoGen.Gemini的函数调用实现路径依赖层AutoGen.GeminiAutoGen.SourceGenerator后者通过Function特性从 C# 方法签名与 XML 注释自动派生FunctionContract与类型安全包装器Agent 层GeminiChatAgentVertex AI 构造重载ToolConfig/FunctionCallingConfig(Auto)声明工具调用策略中间件层GeminiMessageConnector完成 AutoGen 消息体系与 Geminiuser/model/function角色体系的互转FunctionCallMiddleware负责按函数名路由执行并回填结果交互层单轮SendAsync得到ToolCallAggregateMessage调用结果聚合多轮回灌历史后得到TextMessage最终答案。掌握以上链路后你可以将该模式直接迁移到任何需要 Gemini 工具调用的 .NET 场景——只需替换MovieFunction中的业务实现与functionMap注册即可接入真实 API 或数据源。【免费下载链接】autogenA programming framework for agentic AI项目地址: https://gitcode.com/GitHub_Trending/au/autogen创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考