基于 LLamaSharp + 通义千问本地 GGUF 模型的离线 AI 控制台智能代理(Agent)程序,核心实现对话交互 + 技能插件 + C# 代码自动生成 / 编译 / 无限纠错运行能力,整体架构分层清晰、插件化设计。

一、整体架构

  1. 技能抽象层BaseSkill 抽象基类,统一定义技能名称、描述、触发词、执行入口,所有功能均继承此类,实现插件式扩展。
  2. 具体技能模块(三大内置技能)
    • 文件读取技能:识别 “读取文件” 等关键词,解析路径、读取本地文本文件并返回内容,做异常捕获。
    • 清屏技能:匹配 “清屏 /cls”,调用控制台清屏方法。
    • 代码运行 & 自动修复技能(核心)
      • 基于csc.exe编译 C# 代码、运行生成的 exe 程序;
      • 循环重试机制:编译错误 / 运行错误都会自动调用大模型重新生成 / 修复代码,无限迭代直至运行成功;
      • 内置临时目录管理,自动生成、清理代码与可执行文件;
      • 构造专属提示词,要求模型输出完整可编译的 C# 控制台代码。
  3. AI 代理核心LlamaAgent
    • 统一管理所有技能,根据用户输入关键词匹配对应技能并执行;
    • 无匹配技能时,走通用 LLM 对话逻辑,流式输出回复;
    • 封装 LLama 推理执行器与推理参数。
  4. 主程序入口
    • 加载本地 Qwen2.5 GGUF 大模型,配置上下文、GPU 加速、线程等参数;
    • 隐藏模型加载日志,启动交互式控制台循环;
    • 监听用户输入,支持exit退出,全程 UTF8 编码适配中文。

二、核心亮点

  1. 插件化技能体系:新增功能只需继承BaseSkill并注册,拓展性强。
  2. 代码全自动闭环:AI 生成代码 → CSC 编译 → 运行 → 出错自动回传给模型修复,无限重试,实现代码自纠错。
  3. 本地离线运行:依托 LLamaSharp 加载 GGUF 模型,无需联网。
  4. 完善容错:文件读写、进程调用、模型推理全流程异常捕获,临时文件自动清理。

三、关键依赖与配置

  • 依赖:LLamaLLama.CommonLLama.Sampling(LLamaSharp 库)
  • 模型:qwen2.5-7b-instruct 量化 GGUF 模型
  • 编译依赖:系统自带 csc.exe(C# 编译器)
  • 推理参数:上下文 8192、温度 0.3、最大生成长度 1500,GPU 分层加速。

四、使用流程

  1. 程序启动加载本地大模型;
  2. 控制台交互输入指令:
    • 普通文字:进入 AI 闲聊;
    • 含「读取文件 + 路径」:读取本地文件;
    • 含「清屏 /cls」:清空控制台;
    • 含「写代码 / 运行代码」:AI 生成代码→编译→运行→出错自动修复;
  3. 输入exit退出程序,自动释放模型资源。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
using LLama;
using LLama.Common;
using LLama.Sampling;

namespace QwenCodeAgentConsole
{
    #region 技能抽象基类
    public abstract class BaseSkill
    {
        public abstract string Name { get; }
        public abstract string Description { get; }
        public abstract List<string> TriggerWords { get; }
        public abstract Task<string> ExecuteAsync(string userInput);
    }

    #region 基础技能
    /// <summary>文件读取技能</summary>
    public class FileReadSkill : BaseSkill
    {
        public override string Name => "FileReadSkill";
        public override string Description => "读取本地文本文件内容";
        public override List<string> TriggerWords => new() { "读取文件", "读文件", "获取文件内容" };

        public override async Task<string> ExecuteAsync(string userInput)
        {
            await Task.CompletedTask;
            // 简单提取路径(可根据需求增强路径解析)
            string path = ExtractFilePath(userInput);
            if (string.IsNullOrWhiteSpace(path))
                return "未识别到有效文件路径,请输入:读取文件 + 文件完整路径";

            try
            {
                if (!File.Exists(path))
                    return $"错误:文件不存在 -> {path}";

                string content = await File.ReadAllTextAsync(path, Encoding.UTF8);
                return $"✅ 文件读取成功\n路径:{path}\n内容:\n{content}";
            }
            catch (Exception ex)
            {
                return $"❌ 读取文件失败:{ex.Message}";
            }
        }

        // 简易提取路径(取最后一段路径字符串,适配常规输入)
        private string ExtractFilePath(string input)
        {
            char[] splitChars = { ' ', ':', ':', ',', ',' };
            var parts = input.Split(splitChars, StringSplitOptions.RemoveEmptyEntries);
            foreach (var part in parts)
            {
                if (File.Exists(part) || part.Contains("\\") || part.Contains("/"))
                    return part;
            }
            return string.Empty;
        }
    }

    public class ClearScreenSkill : BaseSkill
    {
        public override string Name => "ClearScreenSkill";
        public override string Description => "清空控制台界面";
        public override List<string> TriggerWords => new() { "清屏", "清空屏幕", "cls" };

        public override async Task<string> ExecuteAsync(string userInput)
        {
            await Task.CompletedTask;
            Console.Clear();
            return "✅ 已清空屏幕";
        }
    }
    #endregion

    #region 代码运行技能(无限自动迭代修复)
    public class CodeRunSkill : BaseSkill
    {
        private readonly InteractiveExecutor _llamaExecutor;
        private readonly InferenceParams _inferParams;

        private readonly string _tempDir;
        private readonly string _codeFile;
        private readonly string _exeFile;

        public CodeRunSkill(InteractiveExecutor executor, InferenceParams inferParams)
        {
            _llamaExecutor = executor;
            _inferParams = inferParams;

            _tempDir = Path.Combine(Environment.CurrentDirectory, "CodeTemp");
            Directory.CreateDirectory(_tempDir);
            _codeFile = Path.Combine(_tempDir, "RunCode.cs");
            _exeFile = Path.Combine(_tempDir, "RunCode.exe");
        }

        public override string Name => "CodeRunSkill";
        public override string Description => "编写C#代码,自动编译、运行,编译/运行异常时无限自动修复重试";
        public override List<string> TriggerWords => new()
        {
            "写代码", "编写代码", "写c#", "运行代码", "编译代码", "帮我写程序"
        };

        public override async Task<string> ExecuteAsync(string userInput)
        {
            try
            {
                int retryTimes = 0;
                string lastError = string.Empty;
                string lastOutput = string.Empty;

                // 无限循环重试
                while (true)
                {
                    retryTimes++;
                    Console.WriteLine($"\n[系统] 第 {retryTimes} 次尝试");

                    Console.WriteLine("[系统] 正在生成/修复代码...");
                    string code = await GenerateOrFixCode(userInput, lastError, lastOutput);
                    if (string.IsNullOrWhiteSpace(code))
                    {
                        lastError = "代码生成为空";
                        continue;
                    }

                    await File.WriteAllTextAsync(_codeFile, code, Encoding.UTF8);
                    Console.WriteLine("[系统] 开始编译...");

                    var compileResult = await CompileCodeAsync();
                    if (!compileResult.Success)
                    {
                        lastError = $"编译错误:{compileResult.Message}";
                        Console.WriteLine($"[系统] {lastError},准备重新修复代码");
                        continue;
                    }
                    Console.WriteLine("[系统] 编译成功,开始运行...");

                    var runOutput = await RunExeAsync();
                    lastOutput = runOutput;

                    if (!runOutput.Contains("运行错误:"))
                    {
                        CleanTempFiles();
                        return $"✅ 执行成功(共尝试 {retryTimes} 次)\n--- 程序输出 ---\n{runOutput}";
                    }

                    lastError = $"运行错误:{runOutput}";
                    Console.WriteLine($"[系统] {lastError},准备重新修复代码");
                }
            }
            catch (Exception ex)
            {
                CleanTempFiles();
                return $"❌ 全局异常:{ex.Message}";
            }
        }

        private async Task<string> GenerateOrFixCode(string originRequest, string errorInfo, string runOutput)
        {
            StringBuilder promptBuilder = new StringBuilder();
            promptBuilder.Append(@"<|im_start|>system
你是专业C#程序员。根据需求编写**完整可直接用csc.exe编译运行**的控制台代码。
必须包含 using、namespace、Program、Main 方法。
不要额外解释、不要markdown标记、不要多余文字,只输出代码。
如果存在编译错误、运行错误,根据错误日志精准修正代码。<|im_end|>
");

            promptBuilder.AppendLine("<|im_start|>user");
            promptBuilder.AppendLine($"用户需求:{originRequest}");

            if (!string.IsNullOrWhiteSpace(errorInfo))
                promptBuilder.AppendLine($"历史错误信息:{errorInfo}");
            if (!string.IsNullOrWhiteSpace(runOutput) && !runOutput.Contains("运行错误:"))
                promptBuilder.AppendLine($"上一次运行输出:{runOutput}");

            promptBuilder.AppendLine("请修正代码并重新输出完整代码<|im_end|>");
            promptBuilder.AppendLine("<|im_start|>assistant");

            StringBuilder codeSb = new StringBuilder();
            await foreach (var token in _llamaExecutor.InferAsync(promptBuilder.ToString(), _inferParams))
            {
                if (token.Contains("<|im_end|>")) break;
                codeSb.Append(token);
            }
            return codeSb.ToString().Trim();
        }

        #region 编译、运行、清理工具方法
        private class CompileOutput
        {
            public bool Success { get; set; }
            public string Message { get; set; } = string.Empty;
        }

        private async Task<CompileOutput> CompileCodeAsync()
        {
            var process = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = "csc.exe",
                    Arguments = $"\"{_codeFile}\" /out:\"{_exeFile}\"",
                    UseShellExecute = false,
                    RedirectStandardError = true,
                    RedirectStandardOutput = true,
                    CreateNoWindow = true
                }
            };

            process.Start();
            string err = await process.StandardError.ReadToEndAsync();
            string outStr = await process.StandardOutput.ReadToEndAsync();
            process.WaitForExit();

            return process.ExitCode == 0
                ? new CompileOutput { Success = true, Message = outStr }
                : new CompileOutput { Success = false, Message = $"退出码:{process.ExitCode}\n{err}" };
        }

        private async Task<string> RunExeAsync()
        {
            if (!File.Exists(_exeFile)) return "可执行文件不存在";

            var process = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = _exeFile,
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    CreateNoWindow = true
                }
            };

            process.Start();
            string output = await process.StandardOutput.ReadToEndAsync();
            string error = await process.StandardError.ReadToEndAsync();
            process.WaitForExit();

            StringBuilder sb = new StringBuilder();
            if (!string.IsNullOrEmpty(output)) sb.AppendLine(output);
            if (!string.IsNullOrEmpty(error)) sb.AppendLine($"运行错误:{error}");
            return sb.ToString();
        }

        private void CleanTempFiles()
        {
            try
            {
                if (File.Exists(_codeFile)) File.Delete(_codeFile);
                if (File.Exists(_exeFile)) File.Delete(_exeFile);
            }
            catch { }
        }
        #endregion
    }
    #endregion
    #endregion

    #region AI Agent 核心
    public class LlamaAgent
    {
        private readonly List<BaseSkill> _skills = new();
        private readonly InteractiveExecutor _executor;
        private readonly InferenceParams _inferParams;

        public LlamaAgent(InteractiveExecutor executor, InferenceParams inferParams)
        {
            _executor = executor;
            _inferParams = inferParams;
            RegisterAllSkills();
        }

        public void RegisterSkill(BaseSkill skill)
        {
            if (!_skills.Any(s => s.Name == skill.Name))
                _skills.Add(skill);
        }

        private void RegisterAllSkills()
        {
            // 移除 TimeSkill,新增文件读取技能
            RegisterSkill(new FileReadSkill());
            RegisterSkill(new ClearScreenSkill());
            RegisterSkill(new CodeRunSkill(_executor, _inferParams));
        }

        public BaseSkill? MatchSkill(string userInput)
        {
            var input = userInput.ToLower();
            return _skills.FirstOrDefault(skill =>
                skill.TriggerWords.Any(k => input.Contains(k.ToLower())));
        }

        public async Task ChatAsync(string userInput)
        {
            var hitSkill = MatchSkill(userInput);
            if (hitSkill != null)
            {
                string result = await hitSkill.ExecuteAsync(userInput);
                Console.WriteLine($"AI:{result}");
                return;
            }
            await NormalChatAsync(userInput);
        }

        private async Task NormalChatAsync(string question)
        {
            string prompt = $@"<|im_start|>system
你是一个有用的AI助手<|im_end|>
<|im_start|>user
{question}<|im_end|>
<|im_start|>assistant
";
            Console.Write("AI:");
            await foreach (var token in _executor.InferAsync(prompt, _inferParams))
            {
                if (token.Contains("<|im_end|>")) break;
                Console.Write(token);
            }
        }
    }
    #endregion

    #region 主程序
    class Program
    {
        private static LLamaWeights? _model;
        private static LLamaContext? _context;
        private static LlamaAgent? _aiAgent;

        private static readonly InferenceParams _infParams = new InferenceParams
        {
            MaxTokens = 1500,
            AntiPrompts = new[] { "<|im_end|>" },
            SamplingPipeline = new DefaultSamplingPipeline { Temperature = 0.3f }
        };

        private static string _modelPath = "qwen2.5-7b-instruct-1m-q4_k_m.gguf";

        static async Task Main(string[] args)
        {
            Console.OutputEncoding = Encoding.UTF8;
            Console.WriteLine("正在加载模型...");

            using (var nullWriter = new StringWriter())
            {
                Console.SetOut(nullWriter);
                var parameters = new ModelParams(_modelPath)
                {
                    ContextSize = 8192,
                    GpuLayerCount = 99,
                    Threads = Environment.ProcessorCount,
                };

                _model = LLamaWeights.LoadFromFile(parameters);
                _context = new LLamaContext(_model, parameters);
                var executor = new InteractiveExecutor(_context);

                _aiAgent = new LlamaAgent(executor, _infParams);
            }

            Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true });

            Console.WriteLine("================================================================================================");
            Console.WriteLine("     本地离线 AI 代码迭代助手 | 无限自动修复编译/运行错误");
            Console.WriteLine($"模型:qwen2.5-7b-instruct-1m-q4_k_m.gguf");
            Console.WriteLine("功能:对话 / 读取文件 / 清屏 / 代码自动编写+编译+无限修复重试");
            Console.WriteLine("输入 exit 退出");
            Console.WriteLine("================================================================================================");
            Console.WriteLine();

            while (true)
            {
                Console.Write("你:");
                string input = Console.ReadLine()?.Trim() ?? string.Empty;
                if (string.IsNullOrEmpty(input)) continue;
                if (input.ToLower() == "exit") break;

                if (_aiAgent != null)
                    await _aiAgent.ChatAsync(input);
                Console.WriteLine("\n");
            }

            _context?.Dispose();
            _model?.Dispose();
        }
    }
    #endregion
}

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐