教你如何入门AI Agent,让AI自动创建项目构建项目检查代码

目录

实际运行效果

最终成果

总结

进阶建议


 

在AI很火爆的时候,到现在为止还有很多人很迷茫,如何入门AI?了解AI?下面我将带你进入AI Agent高级AI编程的领域。

下面我们将基于Microsoft.SemanticKernel去实现Agent的功能,创建一个CodeAgent,用户提出需求,然后由Agent进行实现。

创建一个控制台项目CodeAgent,然后安装以下依赖

    <ItemGroup>
      <PackageReference Include="Microsoft.SemanticKernel" Version="1.33.0" />
      <PackageReference Include="Microsoft.SemanticKernel.Agents.Core" Version="1.33.0-alpha" />
      <PackageReference Include="Microsoft.SemanticKernel.Agents.OpenAI" Version="1.33.0-alpha" />
    </ItemGroup>

打开Program.cs

AI Agent是一种能够感知环境并作出相应决策和行动的智能系统。在本文中,我们将创建三个协同工作的AI Agent:

  • CodeReviewAgent: 负责代码审查和需求确认

  • ProgrammerAgent: 负责代码实现和文件操作

  • CodeErrorCheckAgent: 负责代码错误检查和反馈

using CodeAgent.Function;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.Chat;
using Microsoft.SemanticKernel.Agents.OpenAI;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;

#pragma warning disable OPENAI001

#pragma warning disable SKEXP0001

#pragma warning disable SKEXP0110

#pragma warning disable SKEXP0010

internalclassProgram
{
    public static async Task Main(string[] args)
    {
        var model = "gpt-4o";

        var kernelBuilder = Kernel.CreateBuilder();

        kernelBuilder.Plugins.AddFromType<UserFunction>();
        kernelBuilder.Plugins.AddFromType<CodeFunction>();

        kernelBuilder.AddOpenAIChatCompletion(model, new Uri("https://api.token-ai.cn/v1"),
            await File.ReadAllTextAsync("token"));

        var kernel = kernelBuilder.Build();

        var codeReviewAgent = CreateCodeReviewAgent(kernel);
        var programmerAgent = CreateProgrammerAgent(kernel);
        var codeErrorCheckAgent = CreateCodeErrorCheckAgent(kernel);


        var chat = new AgentGroupChat(codeReviewAgent, programmerAgent, codeErrorCheckAgent)
        {
            ExecutionSettings = new AgentGroupChatSettings()
            {
                TerminationStrategy = new CodeTerminationStrategy()
                {
                    Agents = [codeReviewAgent],
                    MaximumIterations = 10,
                }
            }
        };

        var chatHistory = new ChatMessageContent(AuthorRole.User, "使用Avalonia实现一个简单的计算器,工作目录:D:\\agent");

        chat.AddChatMessage(chatHistory);

        WriteAgentChatMessage(chatHistory);

        awaitforeach (var item in chat.InvokeAsync())
        {
            WriteAgentChatMessage(item);
        }
    }

    publicsealedclassCodeTerminationStrategy : TerminationStrategy
    {
        protected override Task<bool> ShouldAgentTerminateAsync(Agent agent, IReadOnlyList<ChatMessageContent> history,
            CancellationToken cancellationToken)
        {
            // 如果最后一条消息包含[done]则终止

            return history[^1].Content?.Contains("[done]") == true ? Task.FromResult(true) : Task.FromResult(false);
        }
    }

    public static void WriteAgentChatMessage(ChatMessageContent messages)
    {
        var authorExpression = messages.Role == AuthorRole.User ? string.Empty : $"- {messages.AuthorName ?? "*"}";

        var contentExpression = string.IsNullOrWhiteSpace(messages.Content) ? string.Empty : messages.Content;
        var isCode = messages.Metadata?.ContainsKey(OpenAIAssistantAgent.CodeInterpreterMetadataKey) ?? false;

        var codeMarker = isCode ? "\n [CODE]\n" : string.Empty;

        Console.ForegroundColor = messages.Role == AuthorRole.User ? ConsoleColor.Green : ConsoleColor.Blue;
        Console.Write($"\n [{messages.Role}]:");
        Console.ResetColor();
        Console.WriteLine($"{authorExpression} {codeMarker} {contentExpression}");

        foreach (var item in messages.Items)
        {
            switch (item)
            {
                case AnnotationContent annotation:
                    Console.WriteLine($"  [{item.GetType().Name}] {annotation.Quote}: File #{annotation.FileId}");
                    break;
                case FileReferenceContent fileReference:
                    Console.WriteLine($"  [{item.GetType().Name}] File #{fileReference.FileId}");
                    break;
                case ImageContent image:
                    Console.WriteLine(
                        $"  [{item.GetType().Name}] {image.Uri?.ToString() ?? image.DataUri ?? $"{image.Data?.Length} bytes"}");
                    break;
                case FunctionCallContent functionCall:
                    Console.WriteLine($"  [{item.GetType().Name}] {functionCall.Id}");
                    break;
                case FunctionResultContent functionResult:
                    Console.WriteLine(
                        $"  [{item.GetType().Name}] {functionResult.CallId} - {functionResult.Result ?? "*"}");
                    break;
            }
        }
    }

    public static ChatCompletionAgent CreateCodeReviewAgent(Kernel kernel)
    {
        conststring reviewAgentName = "CodeReviewAgent";
        conststring reviewInstruction =
            """
            你是CodeReviewAgent,
            - 你的职责是检查代码问题,只需要检查代码的质量,不需要编写代码。
            - 如果给出的代码完全符合预期,那么只回复:[done]。
            - 如果不符合用户的需求,你需要提出建议,并且给出解决方案的思路,提供思维链的一系列解决思路,但是不要直接给出答案。
            - 你还需要将用户的问题提供一些思维链的计划,然后告知ProgrammerAgent,但是你不能直接[done]。
            """;
        var codeReviewAgent = new ChatCompletionAgent()
        {
            Name = reviewAgentName,
            Instructions = reviewInstruction,
            Kernel = kernel,
            Description = "代码审查机器人",
            Arguments = new KernelArguments(new OpenAIPromptExecutionSettings()
                { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }),
        };

        return codeReviewAgent;
    }

    /// <summary>
    /// 检查代码是否存在报错
    /// </summary>
    /// <returns></returns>
    public static ChatCompletionAgent CreateCodeErrorCheckAgent(Kernel kernel)
    {
        conststring errorCheckAgentName = "CodeErrorCheckAgent";
        conststring errorCheckInstruction =
            """
            你是CodeErrorCheckAgent,
            - 你的职责是检查代码是否存在报错,只需要检查代码的质量,不需要编写代码。
            - 如果代码构建没问题,那么只回复:[yes-code]。
            - 如果代码构建出现异常,那么你需要告知ProgrammerAgent,然后提供解决方案的思路。
            """;
        var codeErrorCheckAgent = new ChatCompletionAgent()
        {
            Name = errorCheckAgentName,
            Instructions = errorCheckInstruction,
            Kernel = kernel,
            Description = "代码错误检查机器人",
            Arguments = new KernelArguments(new OpenAIPromptExecutionSettings()
                { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }),
        };

        return codeErrorCheckAgent;
    }

    /// <summary>
    /// 创建一个程序员Agent
    /// </summary>
    /// <returns></returns>
    public static ChatCompletionAgent CreateProgrammerAgent(Kernel kernel)
    {
        conststring programmerAgentName = "ProgrammerAgent";
        conststring programmerInstruction =
            """
            你是ProgrammerAgent,
            - 你的职责是编写代码,
            - 请根据用户的需求编写代码,
            - 尽可能考虑到最佳实践,并且保持代码的可读性。
            - 您还可以帮助用户直接创建项目,修改代码文件,如果需要构建项目,通知CodeErrorCheckAgent 检查代码是否存在报错。
            """;

        var programmerAgent = new ChatCompletionAgent()
        {
            Name = programmerAgentName,
            Instructions = programmerInstruction,
            Kernel = kernel,
            Description = "程序员机器人",
            Arguments = new KernelArguments(new OpenAIPromptExecutionSettings()
                { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }),
        };

        return programmerAgent;
    }
}

然后我们提供一些Agent需要的能力,创建俩个文件CodeFunction,UserFunction

CodeFunction.cs 这个Function由于提供操作文件的功能给AI

using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using Microsoft.SemanticKernel;

namespaceCodeAgent.Function;

publicclassCodeFunction
{
    /// <summary>
    /// 执行CMD命令
    /// </summary>
    [KernelFunction, Description("执行CMD命令, 并且等待命令执行完成")]
    public async Task<string> ExecuteCommand(string command)
    {
        Console.WriteLine(command);

        var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "cmd.exe",
                Arguments = $"/c {command}",
                // 显示窗口,并且将控制台内容输出
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = false,
                // 设置编码
                StandardOutputEncoding = Encoding.UTF8,
                StandardErrorEncoding = Encoding.UTF8
            }
        };

        process.Start();

        await process.WaitForExitAsync();

        var result = await process.StandardOutput.ReadToEndAsync();

        var error = await process.StandardError.ReadToEndAsync();

        process.Close();

        if (string.IsNullOrWhiteSpace(error))
        {
            Console.WriteLine(result);
            return result;
        }

        await Console.Error.WriteLineAsync(error);
        return error;
    }

    /// <summary>
    /// DotNet构建指定目录下的项目
    /// </summary>
    /// <returns></returns>
    public Task<string> BuildDotNetProject(
        [Description("项目路径")] string projectPath)
    {
        return ExecuteCommand($"dotnet build {projectPath}");
    }

    /// <summary>
    /// 获取当前系统基本信息
    /// </summary>
    [KernelFunction, Description("获取当前系统基本信息")]
    public string GetSystemInfo()
    {
        var os = Environment.OSVersion;
        var platform = Environment.OSVersion.Platform;
        var version = Environment.OSVersion.VersionString;
        var processorCount = Environment.ProcessorCount;
        var systemDirectory = Environment.SystemDirectory;
        var userName = Environment.UserName;
        var machineName = Environment.MachineName;
        var userDomainName = Environment.UserDomainName;
        var currentDirectory = Environment.CurrentDirectory;
        var is64BitOperatingSystem = Environment.Is64BitOperatingSystem;
        var is64BitProcess = Environment.Is64BitProcess;

        return @$"
            操作系统:{os}
            平台:{platform}
            版本:{version}
            处理器数量:{processorCount}
            系统目录:{systemDirectory}
            用户名:{userName}
            机器名:{machineName}
            用户域名:{userDomainName}
            当前目录:{currentDirectory}
            是否为64位操作系统:{is64BitOperatingSystem}
            是否为64位进程:{is64BitProcess}
        ";
    }

    /// <summary>
    /// 修改指定的文件的内容
    /// </summary>
    [KernelFunction, Description("修改指定的文件的内容")]
    public async Task ModifyFileContent(string filePath, string content)
    {
        await File.WriteAllTextAsync(filePath, content);
    }

    /// <summary>
    /// 获取指定目录下所有文件
    /// </summary>
    /// <returns></returns>
    [KernelFunction, Description("获取指定目录下所有文件")]
    public string[] GetFilesInDirectory(
        [Description("目录路径")] string directoryPath)
    {
        return Directory.GetFiles(directoryPath);
    }

    /// <summary>
    /// 文件重命名
    /// </summary>
    /// <returns></returns>
    [KernelFunction, Description("文件重命名")]
    public string RenameFile(
        [Description("文件路径")] string filePath,
        [Description("新文件名")] string newFileName)
    {
        File.Move(filePath, Path.Combine(Path.GetDirectoryName(filePath), newFileName));

        return"文件重命名成功";
    }

    /// <summary>
    /// 修改指定的文件 指定行的内容,如果行不存在则添加
    /// </summary>
    [KernelFunction, Description("修改指定的文件 指定行的内容,如果行不存在则添加")]
    public async Task ModifyFileLineContent(string filePath, int lineNumber, string content)
    {
        Console.WriteLine("修改指定的文件 指定行的内容,如果行不存在则添加:{0} {1} {2}", filePath, lineNumber, content);
        var lines = await File.ReadAllLinesAsync(filePath);

        if (lineNumber > lines.Length)
        {
            await File.AppendAllTextAsync(filePath, content);
        }
        else
        {
            lines[lineNumber - 1] = content;
            await File.WriteAllLinesAsync(filePath, lines);
        }
    }

    /// <summary>
    /// 读取指定文件的内容
    /// </summary>
    [KernelFunction, Description("读取指定文件的内容")]
    public async Task<string> ReadFileContent(string filePath)
    {
        Console.WriteLine("读取指定文件的内容:{0}", filePath);
        returnawait File.ReadAllTextAsync(filePath);
    }

    /// <summary>
    /// 读取指定文件的指定行的内容
    /// </summary>
    [KernelFunction, Description("读取指定文件的指定行的内容")]
    public async Task<string> ReadFileLineContent(string filePath, int lineNumber)
    {
        Console.WriteLine("读取指定文件的指定行的内容:{0} {1}", filePath, lineNumber);
        var lines = await File.ReadAllLinesAsync(filePath);

        if (lineNumber > lines.Length)
        {
            return"行数超出文件行数";
        }

        return lines[lineNumber - 1];
    }
}

UserFunction 如果需要在确认一些功能需求的时候需要让用户输入,所以需要提供读取用户输入的Function。

using System.ComponentModel;
using Microsoft.SemanticKernel;

namespaceCodeAgent.Function;

publicclassUserFunction
{
    /// <summary>
    /// 找用户确认需求
    /// </summary>
    /// <returns></returns>
    [KernelFunction, Description("找用户确认需求,并且等待用户提供确认")]
    public string ConfirmRequirement(
        [Description("需要确认的需求")] string requirement
    )
    {
        Console.WriteLine(requirement);

        Console.WriteLine("请确认需求,输入yes/no");

        var result = Console.ReadLine();

        return result;
    }

    /// <summary>
    /// 确认用户是否有额外的需要
    /// </summary>
    /// <returns></returns>
    [KernelFunction, Description("确认用户是否有额外的需要")]
    public string ConfirmAdditionalRequirement(
        [Description("提醒用户是否有额外的需求")] string reminder
    )
    {
        Console.WriteLine(reminder);

        Console.Write("用户输入:");
        var result = Console.ReadLine();

        return result;
    }

    /// <summary>
    /// 当任务完成时,通知用户,让用户确认是否满足需求,如果满足则返回[done]
    /// </summary>
    /// <returns></returns>
    [KernelFunction, Description("当任务完成时,通知用户,让用户确认是否满足需求,如果满足则返回[done]")]
    public string NotifyUserTaskDone(
        [Description("任务完成的消息")] string message
    )
    {
        Console.WriteLine(message);

        Console.Write("用户输入:");
        var result = Console.ReadLine();

        return result;
    }
}

创建token文件,然后将你的OpenAI Key放到文件即可。

然后我们执行项目,下面是执行的流程,在执行过程中,AI会先找我们确认详细的需求,然后确认以后他会生成一个计划告知ProgrammerAgent然后ProgrammerAgent进行代码实现,ProgrammerAgent会创建项目和修改代码文件,在日志中存在读取文件内容,和修改文件内容,然后CodeErrorCheckAgent会进行代码检查,代码检查是通过dotnet build去查看代码异常信息,然后再给ProgrammerAgent修复异常内容。

 [user]:  使用Avalonia实现一个简单的计算器,工作目录:D:\agent
您提到使用Avalonia实现一个简单的计算器,是否有具体的功能需求或界面设计偏好?例如,加减乘除基础运算,或是需要历史记录等功能。
请确认需求,输入yes/no
基本操作、历史记录、主题切换

 [Assistant]:- CodeReviewAgent  为了实现一个符合您要求的计算器应用程序,可以按照以下思路来构建项目:

1. **基础操作**:
    - 实现基本的四则运算:加、减、乘、除。
    - 在UI设计上,布局可以采用简单的网格布局来放置按钮,确保按键的响应速度和准确性。

2. **历史记录**:
    - 在计算器界面的侧边或底部增加一个区域,显示历史计算的结果。
    - 考虑使用一个列表控件(如`ListBox`或`DataGrid`)来存放历史记录。
    - 可以将历史记录保存在内存中,并在用户需要时进行清空。

3. **主题切换**:
    - 提供一个下拉菜单或者按钮,供用户切换主题配色。
    - 定义几个主题风格,并实现动态资源的切换,以便用户在运行时可以改变主题。

4. **项目组织**:
    - 创建一个新的Avalonia应用程序项目,用于开发此计算器。
    - 确保代码结构清晰,使用MVVM架构,使得界面与逻辑分离。

请确认这些思路符合您的期望,然后我们可以继续进行如何具体实现代码的探讨。
dotnet new avalonia.app -o D:\agent\AvaloniaCalculator
已成功创建模板“Avalonia .NET App”。

正在处理创建后操作...
正在还原 D:\agent\AvaloniaCalculator\AvaloniaCalculator.csproj:
  正在确定要还原的项目…
  已还原 D:\agent\AvaloniaCalculator\AvaloniaCalculator.csproj (用时 201 毫秒)。
已成功还原。



读取指定文件的内容:D:\agent\AvaloniaCalculator\MainWindow.axaml
读取指定文件的内容:D:\agent\AvaloniaCalculator\MainWindow.axaml.cs
cd D:\agent\AvaloniaCalculator && dotnet build
  正在确定要还原的项目…
  所有项目均是最新的,无法还原。
C:\Program Files\dotnet\sdk\9.0.200-preview.0.24575.35\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.RuntimeIdentifierInference.targets(326,5): message NETSDK1057: 你正在用 .NET 的预览版。请参阅 https://aka.ms/dotnet-support-policy [D:\agent\AvaloniaCalculator\AvaloniaCalculator.csproj]
D:\agent\AvaloniaCalculator\MainWindow.axaml.cs(15,9): error CS0200: 无法为属性或索引器“ItemsControl.Items”赋值 - 它是只读的 [D:\agent\AvaloniaCalculator\AvaloniaCalculator.csproj]
D:\agent\AvaloniaCalculator\MainWindow.axaml.cs(22,30): warning CS8602: 解引用可能出现空引用。 [D:\agent\AvaloniaCalculator\AvaloniaCalculator.csproj]
D:\agent\AvaloniaCalculator\MainWindow.axaml.cs(31,36): warning CS8602: 解引用可能出现空引用。 [D:\agent\AvaloniaCalculator\AvaloniaCalculator.csproj]
D:\agent\AvaloniaCalculator\MainWindow.axaml.cs(62,29): warning CS8604: “double double.Parse(string s)”中的形参“s”可能传入 null 引用实参。 [D:\agent\AvaloniaCalculator\AvaloniaCalculat

实际运行效果

让我们通过一个实际案例来看看这个系统是如何工作的。以下是创建一个Avalonia计算器应用的过程:

  1. 需求确认阶段

    • Agent询问具体需求

    • 用户确认功能要求

  2. 开发实现阶段

    • 自动创建项目结构

    • 生成必要的代码文件

    • 实现基本功能

  3. 检查优化阶段

    • 代码质量审查

    • 错误检测和修复

    • 功能完善

最终成果

通过AI Agent的协作,我们成功创建了一个具有以下功能的计算器应用:

  • 基础计算功能

  • 历史记录

  • 主题切换

图片

总结

通过本文的实践,我们了解了:

  1. 如何使用Semantic Kernel框架构建AI Agent

  2. 如何实现Agent之间的协作

  3. 如何将AI能力应用到实际开发中

这个案例展示了AI Agent在软件开发中的潜力,为我们提供了一个全新的开发范式。

进阶建议

  1. 尝试扩展Agent的功能

  2. 优化Agent之间的交互

  3. 探索更多实际应用场景

希望这个教程能帮助你更好地理解和使用AI Agent技术!

引入地址 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值