使用C#构建一个论文总结AI Agent

前言

我觉得将日常生活中一些简单重复的任务交给AI Agent,是学习构建AI Agent应用一个很不错的开始。本次分享我以日常生活中一个总结论文的简单任务出发进行说明,希望对大家了解AI Agent有所帮助。任务可以是多种多样的,真的帮助自己提升了效率,那就是一个很不错的开始了!!

我的这个简单任务是这样的,有一篇文献,如下所示:

image-20250102100353781

我想要对该文献进行总结,然后将md格式笔记保存。

我之前的做法是使用Cherry Studio新建一个论文总结助手,如下所示:

image-20250102100813485

然后上传文献,进行总结,如下所示:

image-20250102101204179

然后新建一个笔记md文件,将这些内容复制进去,这样就完成了一个简单的任务,如下所示:

image-20250102101513430

虽然说已经比最开始的时候,只是将文献翻译一下,就直接开始读,然后尝试自己总结主要内容强太多了。但是还是有需要改进的地方,那就是选择文件,新建笔记文件,复制笔记内容这些简单重复的事,可以尝试一下把这些交给一个AI Agent!!

使用C#构建一个论文总结AI Agent相关实践

前几个月,当我刚开始尝试构建AI Agent应用的时候,经过测试,我发现在Semantic Kernel中,想要使用函数调用的话,只有OpenAI与Kimi的模型能用,而OpenAI模型的使用在国内是不太方便的,而构建一个AI Agent函数调用功能是必不可少的。经过一番探索,找到了一位大佬的方法,可以通过提示词来实现函数调用:

Semantic Kernel/C#:一种通用的Function Calling方法,文末附大模型清单

然后根据这个方法,做了一个简单的AI Agent项目进行介绍:

SimpleAIAgent:使用免费的glm-4-flash即可开始构建简单的AI Agent应用

GitHub地址:https://github.com/Ming-jiayou/SimpleAIAgent

经过几个月的发展,我发现现在在Semantic Kernel中使用国内具有函数调用能力的模型效果也还行了。现在开始构建我们自己的AI Agent应用吧!!

为了尽量保持简单,不增加无关的心智负担,便于感兴趣的朋友自己动手,新建一个C#控制台项目。

实现这个简单的Demo可以有五种不同的方式:

image-20250102103959346

第一种使用基本的Semantic Kernel中的Function calling with chat completion

相关文档:Function calling with chat completion | Microsoft Learn

第二种使用Semantic Kernel Chat Completion Agent

相关文档:Exploring the Semantic Kernel Chat Completion Agent (Experimental) | Microsoft Learn

第三种使用Microsoft.Extensions.AI

相关文档:extensions/src/Libraries/Microsoft.Extensions.AI.OpenAI at main · dotnet/extensions

第四种使用Semantic Kernel Open AI Assistant Agent

相关文档:Exploring the Semantic Kernel Open AI Assistant Agent (Experimental) | Microsoft Learn

第五种使用UniversalLLMFunctionCaller

相关文档:Jenscaasen/UniversalLLMFunctionCaller: A planner that integrates into Semantic Kernel to enable function calling on all Chat based LLMs (Mistral, Bard, Claude, LLama etc)

我先使用第二种方式进行说明。

先安装这三个库:

image-20250102105224219

实现这个AI Agent需要自己先写好一个总结论文的相关插件:

最初的插件:

 [KernelFunction("ExtractPDFContent")]
 [Description("读取指定路径的PDF文档内容")]
 [return: Description("PDF文档内容")]
 public string ExtractPDFContent(string filePath)
 {
     StringBuilder text = new StringBuilder();
     // 读取PDF内容
     using (PdfDocument document = PdfDocument.Open(filePath))
     {
         foreach (var page in document.GetPages())
         {
             text.Append(page.Text);
         }
     }
     return text.ToString();
 }

  [KernelFunction]
  [Description("根据文件路径与笔记内容创建一个md格式的文件")]
  public void SaveMDNotes([Description("保存笔记的路径")] string filePath, [Description("笔记的md格式内容")] string mdContent)
  {
      try
      {
          // 检查文件是否存在,如果不存在则创建
          if (!File.Exists(filePath))
          {
              // 创建文件并写入内容
              File.WriteAllText(filePath, mdContent);
          }
          else
          {
              // 如果文件已存在,覆盖写入内容
              File.WriteAllText(filePath, mdContent);
          }
      }
      catch (Exception ex)
      {
          // 处理异常
          Console.WriteLine($"An error occurred: {ex.Message}");
      }
  }

原本是想AI自己多次调用这些函数,比如先调用第一个获取pdf文献内容,然后生成一个md格式笔记,然后再调用第二个函数。但是在实际实践中,只有OpenAI的模型这样子效果还可以,其他的模型多次函数调用的效果并不好,因此最终选择内置一个Kernel的方法。

最终的插件:

    internal sealed class PaperAssistantPlugin
    {
        public PaperAssistantPlugin() 
        {
            IKernelBuilder builder = Kernel.CreateBuilder();
#pragma warning disable SKEXP0010 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。
            builder.AddOpenAIChatCompletion(
              modelId: "deepseek-ai/DeepSeek-V2.5",
              apiKey: "sk-xxx",
              endpoint: new Uri("https://api.siliconflow.cn")
            );         
#pragma warning restore SKEXP0010 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。
            InterKernel = builder.Build();
        }

        internal Kernel InterKernel { get; set; }

        [KernelFunction("ExtractPDFContent")]
        [Description("读取指定路径的PDF文档内容")]
        [return: Description("PDF文档内容")]
        public string ExtractPDFContent(string filePath)
        {
            StringBuilder text = new StringBuilder();
            // 读取PDF内容
            using (PdfDocument document = PdfDocument.Open(filePath))
            {
                foreach (var page in document.GetPages())
                {
                    text.Append(page.Text);
                }
            }
            return text.ToString();
        }

        [KernelFunction]
        [Description("根据文件路径与笔记内容创建一个md格式的文件")]
        public void SaveMDNotes([Description("保存笔记的路径")] string filePath, [Description("笔记的md格式内容")] string mdContent)
        {
            try
            {
                // 检查文件是否存在,如果不存在则创建
                if (!File.Exists(filePath))
                {
                    // 创建文件并写入内容
                    File.WriteAllText(filePath, mdContent);
                }
                else
                {
                    // 如果文件已存在,覆盖写入内容
                    File.WriteAllText(filePath, mdContent);
                }
            }
            catch (Exception ex)
            {
                // 处理异常
                Console.WriteLine($"An error occurred: {ex.Message}");
            }
        }

        [KernelFunction]
        [Description("总结论文内容生成一个md格式的笔记,并将笔记保存到指定路径")]      
        public async void GeneratePaperSummary(string filePath1,string filePath2)
        {
            StringBuilder text = new StringBuilder();
            // 读取PDF内容
            using (PdfDocument document = PdfDocument.Open(filePath1))
            {
                foreach (var page in document.GetPages())
                {
                    text.Append(page.Text);
                }
            }

            // 生成md格式的笔记
            string skPrompt = """
                                论文内容:

                                {{$input}}

                                请总结论文的摘要、前言、文献综述、主要论点、研究方法、结果和结论。
                                论文标题为《[论文标题]》,作者为[作者姓名],发表于[发表年份]。请确保总结包含以下内容:
                                论文摘要
                                论文前言
                                论文文献综诉
                                主要研究问题和背景
                                使用的研究方法和技术
                                主要结果和发现
                                论文的结论和未来研究方向
                                """;
            var result = await InterKernel.InvokePromptAsync(skPrompt, new() { ["input"] = text.ToString() });

            try
            {
                // 检查文件是否存在,如果不存在则创建
                if (!File.Exists(filePath2))
                {
                    // 创建文件并写入内容
                    File.WriteAllText(filePath2, result.ToString());
                    Console.WriteLine($"生成笔记成功,笔记路径:{filePath2}");
                }
                else
                {
                    // 如果文件已存在,覆盖写入内容
                    File.WriteAllText(filePath2, result.ToString());
                }
            }
            catch (Exception ex)
            {
                // 处理异常
                Console.WriteLine($"An error occurred: {ex.Message}");
            }
        }
    }
}

内置的一个Kernel用于生成md格式的论文笔记。

主函数如下所示:

    internal class Program
    {
        public static async Task Main()
        {
            Console.WriteLine("Initialize plugins...");

            PaperAssistantPlugin paperAssistantPugin = new PaperAssistantPlugin();

            Console.WriteLine("Creating kernel...");
            IKernelBuilder builder = Kernel.CreateBuilder();

            //builder.AddOpenAIChatCompletion(
            //    "gpt-4o-mini-2024-07-18",
            //    "xxx"
            //  );

#pragma warning disable SKEXP0010 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。
            builder.AddOpenAIChatCompletion(
              modelId: "Qwen/Qwen2.5-32B-Instruct",
              apiKey: "xxx",
              endpoint: new Uri("https://api.siliconflow.cn")
            );

            // builder.AddOpenAIChatCompletion(
            //  modelId: "glm-4-flash",
            //  apiKey: "xxx",
            //  endpoint: new Uri("https://open.bigmodel.cn/api/paas/v4")
            //);

            // builder.AddOpenAIChatCompletion(
            //  modelId: "yi-large-fc",
            //  apiKey: "xxx",
            //  endpoint: new Uri("https://api.lingyiwanwu.com/v1")
            //);
#pragma warning restore SKEXP0010 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。
            builder.Plugins.AddFromObject(paperAssistantPugin);

            Kernel kernel = builder.Build();

            Console.WriteLine("Defining agent...");
#pragma warning disable SKEXP0110 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。
            ChatCompletionAgent agent =
                new()
                {
                    Name = "PaperAssistantAgent",
                    Instructions =
                            """
                             你是一个用于读取pdf文献内容,并总结内容,生成一个md笔记的智能代理。
                             用户提供论文路径与创建笔记的路径
                             注意文件路径的格式应如下所示:
                             "D:\文献\表格识别相关\文献\xx.pdf"
                             "D:\文献\表格识别相关\笔记\xx.md"
                             请总结论文的摘要、前言、文献综述、主要论点、研究方法、结果和结论。
                             论文标题为《[论文标题]》,作者为[作者姓名],发表于[发表年份]。请确保总结包含以下内容:
                             论文摘要
                             论文前言
                             论文文献综诉
                             主要研究问题和背景
                             使用的研究方法和技术
                             主要结果和发现
                             论文的结论和未来研究方向
                             """,
                    Kernel = kernel,
                    Arguments =
                        new KernelArguments(new OpenAIPromptExecutionSettings() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(),MaxTokens = 16000})                     
                };
#pragma warning restore SKEXP0110 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。

            Console.WriteLine("Ready!");

            ChatHistory history = [];
            bool isComplete = false;
            do
            {
                Console.WriteLine();
                Console.Write("> ");
                string input = Console.ReadLine();
                if (string.IsNullOrWhiteSpace(input))
                {
                    continue;
                }
                if (input.Trim().Equals("EXIT", StringComparison.OrdinalIgnoreCase))
                {
                    isComplete = true;
                    break;
                }
                if (input.Trim().Equals("Clear", StringComparison.OrdinalIgnoreCase))
                {
                    history.Clear();
                    Console.WriteLine("已清除聊天记录");
                    continue;
                }

                history.Add(new ChatMessageContent(AuthorRole.User, input));

                Console.WriteLine();
              
#pragma warning disable SKEXP0110 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。
                await foreach (ChatMessageContent response in agent.InvokeAsync(history))
                {
                    // Display response.
                    Console.WriteLine($"{response.Content}");
                }
#pragma warning restore SKEXP0110 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。

            } while (!isComplete);
        }
    }
}

在主函数中定义的这个Kernel主要用于与用户交互与选择调用哪个函数。

开始查看效果:

image-20250102110835592

会发现在自动调用插件中的这个函数了。

image-20250102110931834

由于我使用的是异步方式,AI会先给出回答,实际上笔记还没有真的生成,当出现生成笔记成功,笔记路径:xxx的时候,笔记才真的生成成功,如下所示:

image-20250102111211988

image-20250102111135929

image-20250102111303999

就成功实现我们自己的简单的AI Agent应用了。

直接使用

可能很多人并不熟悉C#也不太懂得编程,但是对自己构建AI Agent应用还是很感兴趣的。接下来我将手把手地介绍该如何使用,希望完全的小白也能学会使用。

项目GitHub地址:https://github.com/Ming-jiayou/PaperAssistant

C#程序员git clone项目之后,将.env.example改为.env然后填入自己想要使用的模型与密钥以及Endpoint即可。

这里重点介绍一下非程序朋友的使用。

我已经发布了一个版本放到GitHub上了,如果上GitHub有问题,也可以联系我。但还是推荐从GitHub上下载,比较安全一点,随便打开别人给的文件不太好。

image-20250102163753555

image-20250102163814932

下载好了之后,解压如下所示:

image-20250102163832037

将.env.example改为.env然后填入自己想要使用的模型与密钥以及Endpoint即可。

image-20250102163925711

以下是手把手的尝试几个不同的平台。

SiliconCloud

现在注册有送2000万token的活动,最nice的一点是送的token没有时间期限。想试试的朋友可以点击链接:https://cloud.siliconflow.cn/i/Ia3zOSCU,注册使用。

image-20250102144700195

直接点击exe文件,即可使用:

image-20250102144800507

出现完成之后,并没有真的完成:

image-20250102144927790

需要继续等待,等到出现“生成笔记成功,笔记路径:xxx”的时候才真正生成完成:

image-20250102145051958

image-20250102145743037

智谱AI

glm-4-flash是免费使用的,配置如下所示:

image-20250102145436991

效果:

image-20250102145801666

image-20250102145841211

可以发现总结的内容比Qwen/Qwen2.5-72B-Instruct-128K要少很多。

DeepSeek

配置如下所示:

image-20250102150451237

效果:

image-20250102150712209

第一次没成功,就再来一次。

image-20250102150939008

总结的很好,可惜是英文的,再试一次:

image-20250102151237085

总结的也很不错,但是不好的一点是似乎陷入了死循环:

image-20250102151336922

过一会又把生成的覆盖掉了,变成英文的了,如下所示:

image-20250102151457836

一下就花掉了10万token,我这个应用看来不适合使用DeepSeek。

image-20250102151703421

零一万物

配置如下:

image-20250102152515824

需要改成yi-large-fc才行。

效果:

image-20250102152922071

image-20250102153014272

如果有朋友硅基流动赠送的额度都用完了,但是也想体验一下,可以联系我获取体验的api key,用的量多了随时关闭,不太可靠。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/947231.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[Qt] 常用控件 | QWidget | “表白程序2.0”

目录 一、控件概述 控件体系的发展阶段: 二、QWidget 核心属性 核心属性概览: 1、enabled 2、Geometry 实例 1: 控制按钮的位置 实例 2: 表白 程序 i、Window Frame 的影响 ii、API 设计理念 iii、Geometry 和 FrameGeometry 的区别 &#xf…

GAN对抗生成网络(二)——算法及Python实现

1 算法步骤 上一篇提到的GAN的最优化问题是,本文记录如何求解这一问题。 首先为了表示方便,记,这里让最大的可视作常量。 第一步,给定初始的,使用梯度上升找到 ,最大化。关于梯度下降,可以参考笔者另一篇…

JAVA(二)【未完】

数据类型与变量 数据类型:基本数据类型:整型:byte short int long 浮点型:float double char 布尔型:boolean 引用数据类型:数组 类 接口 枚举类型 long b 10l;System.out.println(b);System.out.printl…

C语言day5:shell脚本

一、练习题1 定义一个find函数,查找ubuntu和root的gid并使用变量接收结果 二、练习题2 定义一个数组,写一个函数完成对数组的冒泡排序 三、练习题3 使用break求1-100中的质数(质数:只能被1和它本身整除,如:…

R语言6种将字符转成数字的方法,写在新年来临之际

咱们临床研究中,拿到数据后首先要对数据进行清洗,把数据变成咱们想要的格式,才能进行下一步分析,其中数据中的字符转成数字是个重要的内容,因为字符中常含有特殊符号,不利于分析,转成数字后才能…

C语言面的向对象编程(OOP)

如果使用过C、C#、Java语言,一定知道面向对象编程,这些语言对面向对象编程的支持是语言级别的。C语言在语言级别不支持面向对象,那可以实现面向对象吗?其实面向对象是一种思想,而不是一种语言,很多初学者很…

C++ 基础思维导图(一)

目录 1、C基础 IO流 namespace 引用、const inline、函数参数 重载 2、类和对象 类举例 3、 内存管理 new/delete 对象内存分布 内存泄漏 4、继承 继承权限 继承中的构造与析构 菱形继承 1、C基础 IO流 #include <iostream> #include <iomanip> //…

聊聊前端框架中的process.env,env的来源及优先级(next.js、vue-cli、vite)

在平时开发中&#xff0c;常常使用vue、react相关脚手架创建项目&#xff0c;在项目根目录可以创建.env、.env.[mode]&#xff08;mode为development、production、test)、.env.local等文件&#xff0c;然后在项目中就可以通过process.env来访问相关的环境变量了。 下面针对如下…

基于云架构Web端的工业MES系统:赋能制造业数字化变革

基于云架构Web端的工业MES系统:赋能制造业数字化变革 在当今数字化浪潮席卷全球的背景下,制造业作为国家经济发展的重要支柱产业,正面临着前所未有的机遇与挑战。市场需求的快速变化、客户个性化定制要求的日益提高以及全球竞争的愈发激烈,都促使制造企业必须寻求更加高效、智…

LeetCode算法题——螺旋矩阵ll

题目描述 给你一个正整数n&#xff0c;生成一个包含1到n2所有元素&#xff0c;且元素按顺时针顺序螺旋排列的n x n正方形矩阵matrix 。 示例 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]]题解 思路&#xff1a; 将整个过程分解为逐圈填充的过程&#xf…

MySQL 01 02 章——数据库概述与MySQL安装篇

一、数据库概述 &#xff08;1&#xff09;为什么要使用数据库 数据库可以实现持久化&#xff0c;什么是持久化&#xff1a;数据持久化意味着将内存中的数据保存到硬盘上加以“固化”持久化的主要作用是&#xff1a;将内存中的数据存储在关系型数据库中&#xff0c;当然也可以…

GPU 进阶笔记(四):NVIDIA GH200 芯片、服务器及集群组网

大家读完觉得有意义记得关注和点赞&#xff01;&#xff01;&#xff01; 1 传统原厂 GPU 服务器&#xff1a;Intel/AMD x86 CPU NVIDIA GPU2 新一代原厂 GPU 服务器&#xff1a;NVIDIA CPU NVIDIA GPU 2.1 CPU 芯片&#xff1a;Grace (ARM)2.2 GPU 芯片&#xff1a;Hopper/B…

vite6+vue3+ts+prettier+eslint9配置前端项目(后台管理系统、移动端H5项目通用配置)

很多小伙伴苦于无法搭建一个规范的前端项目&#xff0c;导致后续开发不规范&#xff0c;今天给大家带来一个基于Vite6TypeScriptVue3ESlint9Prettier的搭建教程。 目录 一、基础配置1、初始化项目2、代码质量风格的统一2.1、配置prettier2.2、配置eslint2.3、配置typescript 3、…

ESLint+Prettier的配置

ESLintPrettier的配置 安装插件 ​​​​​​ 在settings.json中写下配置 {// tab自动转换标签"emmet.triggerExpansionOnTab": true,"workbench.colorTheme": "Default Dark","editor.tabSize": 2,"editor.fontSize": …

Cyber Security 101-Web Hacking-JavaScript Essentials(JavaScript 基础)

任务1&#xff1a;介绍 JavaScript &#xff08;JS&#xff09; 是一种流行的脚本语言&#xff0c;它允许 Web 开发人员向包含 HTML 和 CSS&#xff08;样式&#xff09;的网站添加交互式功能。创建 HTML 元素后&#xff0c;您可以通过 JS 添加交互性&#xff0c;例如验证、on…

《机器学习》从入门到实战——逻辑回归

目录 一、简介 二、逻辑回归的原理 1、线性回归部分 2、逻辑函数&#xff08;Sigmoid函数&#xff09; 3、分类决策 4、转换为概率的形式使用似然函数求解 5、对数似然函数 ​编辑 6、转换为梯度下降任务 三、逻辑回归拓展知识 1、数据标准化 &#xff08;1&#xf…

JDK8源码分析Jdk动态代理底层原理

本文侧重分析JDK8中jdk动态代理的源码&#xff0c;若是想看JDK17源码分析可以看我的这一篇文章 JDK17源码分析Jdk动态代理底层原理-CSDN博客 两者之间有着略微的差别&#xff0c;JDK17在JDK8上改进了不少 目录 源码分析 过程 生成的代理类大致结构 本文侧重分析JDK8中jdk…

ZYNQ初识6(zynq_7010)clock时钟IP核

基于板子的PL端无时钟晶振&#xff0c;需要从PS端借用clock1&#xff08;50M&#xff09;晶振 接下去是自定义clock的IP核封装&#xff0c;为后续的simulation可以正常仿真波形&#xff0c;需要注意顶层文件的设置&#xff0c;需要将自定义的IP核对应的.v文件设置为顶层文件&a…

深度学习模型格式转换:pytorch2onnx(包含自定义操作符)

将PyTorch模型转换为ONNX&#xff08;Open Neural Network Exchange&#xff09;格式是实现模型跨平台部署和优化推理性能的一种常见方法。PyTorch 提供了多种方式来完成这一转换&#xff0c;以下是几种主要的方法&#xff1a; 一、静态模型转换 使用 torch.onnx.export() t…

GPU 进阶笔记(一):高性能 GPU 服务器硬件拓扑与集群组网

记录一些平时接触到的 GPU 知识。由于是笔记而非教程&#xff0c;因此内容不求连贯&#xff0c;有基础的同学可作查漏补缺之用 1 术语与基础 1.1 PCIe 交换芯片1.2 NVLink 定义演进&#xff1a;1/2/3/4 代监控1.3 NVSwitch1.4 NVLink Switch1.5 HBM (High Bandwidth Memory) 由…