对于C#开发者来说,Google AI Studio 虽然是快速原型设计生成式AI应用的强大平台,但其“获取代码”功能目前并不直接支持C#,这无疑是个小小的遗憾。不过,不用担心!我们可以利用 Gemini大模型 的强大能力,巧妙地绕过这个限制,生成所需的C#代码,最终实现 Gemini集成 。本文将详细介绍这一“曲线救国”的方案,并提供实际案例和操作步骤,帮助C#开发者在 AI Studio 中充分发挥 Gemini大模型 的潜力。

1. AI Studio:GenAI原型设计的理想平台

AI Studio 凭借其低门槛、高效率的特性,已成为GenAI原型设计的首选平台。它允许开发者快速迭代prompt,测试不同的模型,并立即看到结果,极大地加速了GenAI应用的探索过程。正如原文作者所说,AI Studio 是一个“真正的 GenAI 游乐场”。然而,对于C#开发者来说,缺少直接的C#代码生成功能,就像在游乐场里少了一件心仪的玩具,略有遗憾。

举个例子,假设你想开发一个基于 Gemini大模型 的智能问答系统。在 AI Studio 中,你可以轻松地构建和测试不同的对话流程,优化模型的回答质量。但是,当你想要将这个原型移植到你的C#项目中时,却发现无法直接获取C#代码,这无疑增加了开发的难度。幸运的是,我们可以借助 Gemini大模型 本身来解决这个问题。

2. Gemini大模型:自给自足的C#代码生成器

既然 AI Studio 不提供C#代码,那么为何不直接让 Gemini大模型 来编写呢?这正是本文的核心思想:利用 Gemini大模型AI Studio 生成的curl/REST代码转换为C#代码。这种“以子之矛,攻子之盾”的方法,充分体现了开发者们的创造力和解决问题的能力。

具体来说,我们需要以下几个步骤:

  • 在AI Studio中原型设计: 首先,在 AI Studio 中完成你的GenAI应用原型设计,例如,一个电影推荐系统,如原文所示。
  • 获取curl/REST代码: 通过 AI Studio 的“获取代码”按钮,获取对应的curl/REST代码。这些代码包含了与 Gemini大模型 交互的所有必要信息。
  • 编写转换Prompt:AI Studio 中创建一个新的prompt,指示 Gemini大模型 将curl/REST代码转换为C#代码。务必提供清晰、明确的指令,并指定所需的C#代码结构和功能。
  • 运行转换Prompt: 运行转换prompt,Gemini大模型 将生成相应的C#代码。
  • 复制并适配: 将生成的C#代码复制到你的Visual Studio项目中,并进行必要的清理和适配。需要注意的是,AI生成的代码可能需要进行一些修改才能完美地融入你的项目中。

3. 实战案例:电影推荐系统的C#集成

为了更好地理解上述流程,我们以原文中的电影推荐系统为例,详细演示如何使用 Gemini大模型 生成C#代码,并将其集成到C#项目中。

步骤1:AI Studio原型设计

AI Studio 中,创建一个新的Chat prompt,并配置以下内容:

  • 系统指令: “你是一位电影推荐专家。我会提供我喜欢的电影列表,你将推荐3部我可能喜欢的电影,并简要解释原因。”
  • 示例对话:
    • 用户: “我喜欢的电影有《黑客帝国》、《银翼杀手》、《星际穿越》。”
    • 模型: “基于您对《黑客帝国》、《银翼杀手》和《星际穿越》的喜爱,我为您推荐以下3部电影:1.《盗梦空间》:一部关于梦境入侵的惊悚片。2.《机械姬》:一部视觉上令人惊叹的电影,探索了人工智能和意识。3.《沙丘(2021)》:一部史诗级的太空歌剧,拥有丰富的世界观。”
  • 用户输入占位符: “我喜欢的电影是[INSERT_USER_MOVIES_HERE]”

测试你的原型,确保 Gemini大模型 能够根据用户提供的电影列表,给出合理的推荐。

步骤2:获取curl代码

点击 AI Studio 中的“获取代码”按钮,选择“REST”选项卡,复制生成的curl命令。

例如:

#!/bin/bash
set -e -E

GEMINI_API_KEY="$GEMINI_API_KEY"
MODEL_ID="gemini-2.0-flash"
GENERATE_CONTENT_API="streamGenerateContent"

cat << EOF > request.json
{
    "contents": [
      {
        "role": "user",
        "parts": [
          {
            "text": "My favorite movies are The Matrix, Blade Runner, and Interstellar."
          },
        ]
      },
      {
        "role": "model",
        "parts": [
          {
            "text": "Okay, based on your love for The Matrix, Blade Runner, and Interstellar, it's clear you enjoy thought-provoking sci-fi with strong themes of existentialism, technology, and the nature of reality. Here are 3 recommendations for you:\n\n1.  **Arrival (2016):** This film shares Interstellar's focus on communicating across vast divides, but does so with alien languages rather than space travel. It delves into the impact of language on thought and perception, offering a deeply moving and philosophical exploration of time and choice, which I think you will appreciate.\n\n2.  **Dark City (1998):** Before The Matrix, there was Dark City. This neo-noir sci-fi explores similar themes of constructed reality and the search for identity. It has a visually striking, gothic aesthetic and a mystery that will keep you guessing, with a philosophical core that resonates with your other favorites.\n\n3.  **Gattaca (1997):** This movie explores a future society driven by eugenics, where genetic perfection determines your fate. Like Blade Runner, it raises questions about humanity, destiny, and the value of striving against seemingly insurmountable odds. The film has a cool, elegant style and a focus on character that makes it very compelling.\n"
          },
        ]
      },
      {
        "role": "user",
        "parts": [
          {
            "text": "My favorite movies are [INSERT_USER_MOVIES_HERE]"
          }
        ]
      },
    ],
    "generationConfig": {
      "responseMimeType": "text/plain",
    }
}
EOF

curl \
  -X POST \
  -H "Content-Type: application/json" \
  "https://generativelanguage.googleapis.com/v1beta/models/${MODEL_ID}:${GENERATE_CONTENT_API}?key=${GEMINI_API_KEY}" \
  -d '@request.json'

注意GENERATE_CONTENT_API变量,如果它是streamGenerateContent,为了简化,我们可以要求 Gemini大模型 生成使用generateContent endpoint 的C#代码。

步骤3:编写转换Prompt

创建一个新的Chat prompt,并输入以下内容:

创建一个C#函数,使用System.Net.Http.HttpClient与Gemini API交互。

**函数目标:** 该函数应接收一个API密钥(string)和一个用户喜欢的电影列表(string)作为参数。它应将此信息发送到Gemini API,并以单个字符串的形式返回模型的电影推荐。

提供此函数的完整C#代码,包括必要的using语句,在一个C#代码块中。该函数应处理请求的基本JSON序列化和响应的反序列化,以提取文本内容。

CURL请求:
[将你从步骤2复制的CURL请求体粘贴到这里,确保 "INSERT_INPUT_HERE" 占位符位于最后一个用户消息的文本中]

步骤4:运行转换Prompt

运行转换prompt,Gemini大模型 将生成C#代码。代码可能类似于:

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Text.Json;
using System.Text.Json.Nodes; // For easily modifying the JSON
using System.Linq;

public class GeminiApiClient
{
    private readonly HttpClient _httpClient = new HttpClient();
    private const string ApiBaseUrl = "https://generativelanguage.googleapis.com/v1beta/models/";

    public async Task<string> GetMovieSuggestionsAsync(string apiKey, string modelId, string userMoviesList)
    {
        string requestUri = $"{ApiBaseUrl}{modelId}/generateContent?key={apiKey}";

        // Base JSON structure (from your Step 3 prompt)
        // We'll parse it and modify the dynamic part.
        string baseJsonPayload = @"
        {
            ""contents"": [
              {
                ""role"": ""user"",
                ""parts"": [ { ""text"": ""My favorite movies are The Matrix, Blade Runner, and Interstellar."" } ]
              },
              {
                ""role"": ""model"",
                ""parts"": [ { ""text"": ""Okay, based on your love for The Matrix, Blade Runner, and Interstellar, it's clear you enjoy thought-provoking sci-fi... (full model response here)"" } ]
              },
              {
                ""role"": ""user"",
                ""parts"": [ { ""text"": ""INSERT_INPUT_HERE"" } ]
              }
            ],
            ""generationConfig"": {
              ""responseMimeType"": ""text/plain""
            }
            // Potentially ""safetySettings"": [ ... ] here too
        }";

        // Parse the JSON string into a JsonNode
        JsonNode requestNode = JsonNode.Parse(baseJsonPayload);

        // Navigate to the part to be replaced and update it
        // This assumes the structure matches exactly.
        var contentsArray = requestNode["contents"].AsArray();
        var lastUserContent = contentsArray.LastOrDefault(c => c["role"].GetValue<string>() == "user");
        if (lastUserContent != null)
        {
            var partsArray = lastUserContent["parts"].AsArray();
            if (partsArray.Any())
            {
                // Assuming the text to replace is in the first part of the last user message
                partsArray[0]["text"] = userMoviesList;
            }
        }

        string finalJsonBody = requestNode.ToJsonString();

        var content = new StringContent(finalJsonBody, Encoding.UTF8, "application/json");
        HttpResponseMessage response = await _httpClient.PostAsync(requestUri, content);
        response.EnsureSuccessStatusCode();
        string responseBody = await response.Content.ReadAsStringAsync();

        // Basic parsing for the text response
        try
        {
            using (JsonDocument doc = JsonDocument.Parse(responseBody))
            {
                JsonElement candidatesElement = doc.RootElement.GetProperty("candidates");
                if (candidatesElement.EnumerateArray().Any())
                {
                    JsonElement firstCandidate = candidatesElement.EnumerateArray().First();
                    JsonElement contentElement = firstCandidate.GetProperty("content");
                    JsonElement partsElement = contentElement.GetProperty("parts");
                    if (partsElement.EnumerateArray().Any())
                    {
                        return partsElement.EnumerateArray().First().GetProperty("text").GetString();
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error parsing response: {ex.Message}. Raw response: {responseBody}");
        }

        return $"Could not parse response. Raw: {responseBody}"; // Fallback
    }
}

步骤5:在Visual Studio中使用C#代码

创建一个新的.NET控制台应用程序,并将生成的GeminiApiClient类添加到项目中。确保包含必要的using语句。

Program.cs中调用该类:

using System;
using System.Threading.Tasks;
using System.Net.Http;

class Program
{
    static async Task Main(string[] args)
    {
        string apiKey = Environment.GetEnvironmentVariable("GEMINI_API_KEY");
        string modelId = "gemini-2.0-flash"; // Or from your script

        if (string.IsNullOrEmpty(apiKey))
        {
            Console.WriteLine("API key not found. Set GEMINI_API_KEY environment variable.");
            return;
        }

        Console.WriteLine("Please list your favorite movies (comma-separated):");
        string userMovies = Console.ReadLine();

        if (string.IsNullOrWhiteSpace(userMovies))
        {
            Console.WriteLine("No movies entered.");
            return;
        }

        try
        {
            GeminiApiClient client = new GeminiApiClient();
            Console.WriteLine("\nGetting suggestions...\n");
            string suggestions = await client.GetMovieSuggestionsAsync(apiKey, modelId, userMovies);
            Console.WriteLine($"Gemini's Movie Suggestions:\n{suggestions}");
        }
        catch (HttpRequestException e)
        {
            Console.WriteLine($"API request error: {e.Message}");
        }
        catch (Exception e)
        {
            Console.WriteLine($"An unexpected error occurred: {e.Message}");
        }

        Console.ReadKey();
    }
}

运行程序,输入你的API密钥和喜欢的电影列表,即可获得 Gemini大模型 的电影推荐。

4. 重要注意事项

  • 参数化: 示例C#代码现在将modelId作为一个参数。如果需要,你仍然希望使基本prompt历史记录(第一个用户/模型回合)动态化。
  • 错误处理: 对异常进行适当的处理,例如API请求错误和JSON解析错误。
  • JSON解析: Gemini大模型 生成的代码可能使用JsonNode或类结构进行序列化。选择适合你需求的方案。JsonNode适合动态修改已知模板。
  • 配置管理: 将API密钥等敏感信息存储在配置文件或环境变量中,而不是硬编码在代码中。
  • 异步编程: 使用asyncawait关键字进行异步编程,以提高应用程序的响应能力。
  • HttpClient生命周期管理: 确保 HttpClient 实例被正确地创建和销毁,避免资源泄漏。推荐使用 IHttpClientFactory
  • 流式 vs. 非流式: 本例针对 generateContent 进行了简化。如果你的原型严重依赖流式输出并且你需要 C# 中的流式输出,C# 客户端代码和解析逻辑会更加复杂(处理 streamGenerateContent 涉及逐块读取响应流)。

5. 优化代码:简化输出和结构化数据

正如原文作者之前所说,Gemini大模型 可能生成过于复杂的代码。例如,在本例中,我们可以使用简单的字符串替换来替换请求中的用户电影列表,而不是使用JsonNode进行复杂的JSON操作。

此外,为了提高代码的健壮性和可维护性,建议使用结构化输出。可以通过Prompt Engineering指示 Gemini大模型 返回结构化的JSON数据,然后使用C#的JSON序列化库进行解析。

例如,修改转换Prompt,要求 Gemini大模型 返回以下格式的JSON数据:

{
  "suggestions": [
    {
      "title": "盗梦空间",
      "reason": "一部关于梦境入侵的惊悚片。"
    },
    {
      "title": "机械姬",
      "reason": "一部视觉上令人惊叹的电影,探索了人工智能和意识。"
    },
    {
      "title": "沙丘(2021)",
      "reason": "一部史诗级的太空歌剧,拥有丰富的世界观。"
    }
  ]
}

然后,在C#代码中使用对应的类结构进行反序列化:

public class MovieSuggestion
{
    public string Title { get; set; }
    public string Reason { get; set; }
}

public class MovieSuggestionsResponse
{
    public List<MovieSuggestion> Suggestions { get; set; }
}

这样做可以使代码更加清晰、易于理解和维护。

6. 结语:充分利用Gemini大模型的潜力

尽管 AI Studio 的“获取代码”功能尚未完全覆盖所有语言,但我们可以通过巧妙地利用 Gemini大模型 本身,生成所需的代码,从而克服这一限制。本文提供的方法不仅适用于C#,也适用于其他未被 AI Studio 直接支持的语言。

即使 AI Studio 的“获取代码”功能不断发展,提取核心API调用(端点、方法、标头、正文)并使用 Gemini大模型 本身来搭建C#客户端代码的基本原则仍然非常有用。它现在需要从脚本中进行更多手动提取,但仍然胜过从头开始编写所有内容,尤其是在处理嵌套JSON请求正文时。

所以,继续在 AI Studio 中进行实验吧!不要因为“获取代码”输出看起来有点不同而感到害怕。解构它,将基本要素输入到 Gemini大模型 中,并保持 C# 集成向前发展。未来,随着越来越多的语言被添加到支持列表中,相信我们的首选语言也会很快出现在其中。希望本文能帮助C#开发者更有效地利用 AI StudioGemini大模型,开发出更强大的GenAI应用。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注