Semantic Kernel实战:用GPT-4构建智能文本摘要应用

发布时间:2026/5/31 11:59:24

Semantic Kernel实战:用GPT-4构建智能文本摘要应用 1. 项目概述与核心价值如果你最近也在关注AI应用开发尤其是想把手头的业务系统快速、低成本地“AI化”那你大概率绕不开一个选择是用LangChain还是用微软推出的Semantic Kernel今天我就结合自己实际用GPT-4和Semantic Kernel搭建一个文本摘要功能的经历来和你聊聊这个微软的“亲儿子”到底怎么用以及它和LangChain比到底有什么不一样的味道。这不仅仅是另一个“Hello World”式的教程我会重点拆解在实操中那些文档里不会写的配置细节、异步调用的坑以及如何根据你的项目规模来做出技术选型。简单说Semantic Kernel后文简称SK是一个开源的SDK它的核心目标是把大型语言模型LLM的能力像乐高积木一样“插拔”到你现有的应用程序里。无论是想给内部系统加个智能问答机器人还是构建一个能自动处理邮件、生成报告的智能体AgentSK都提供了一套统一的编程模型。我这次的目标很具体用GPT-4作为大脑通过SK将一个冗长的故事文本压缩成一条不超过140个字符的“推文体”摘要。这个过程中你会清晰地看到SK如何将“提示词工程”模块化、函数化这也是它最核心的设计哲学。2. 环境搭建与核心概念解析在开始写代码之前得先把场子搭好。和大多数Python项目一样先准备一个干净的虚拟环境是避免依赖冲突的好习惯。这里我直接用venv。# 创建并激活虚拟环境 python -m venv sk_env source sk_env/bin/activate # Linux/macOS # sk_env\Scripts\activate # Windows # 安装核心库 pip install semantic-kernel pip install python-dotenv pip install ipython # 如果你打算在Jupyter Notebook或类似环境中运行演示这里有个关键点semantic-kernel是主包而python-dotenv是为了方便管理你的API密钥等敏感信息避免硬编码在代码里。我强烈建议你从一开始就养成这个习惯。安装完成后我们来厘清两个贯穿SK始终的核心概念原生函数和语义函数。这是理解SK工作流的基础。原生函数就是你平时写的普通Python函数或者C#函数。它接收明确的参数执行确定的逻辑返回确定的结果。比如一个计算税费的函数calculate_tax(amount, rate)。语义函数则是SK的“灵魂”。它本质上是一个封装好的、可复用的提示词模板。你向这个模板“喂”输入它调用背后配置好的LLM比如GPT-4来生成输出。它的行为不是完全确定的会受模型、温度等参数影响。在SK里你把一个复杂的提示词比如“请用莎士比亚的风格总结以下文本”打包成一个语义函数shakespeare_summarizer(input_text)之后就可以像调用普通函数一样使用它。为什么要这么设计想象一下你的应用里有十几个地方需要做摘要每个地方的格式、长度要求还都不一样。如果没有语义函数你就得在每个地方都写一遍冗长的提示词维护起来是噩梦。而有了语义函数你只需要定义一次summarize_tweet(input)、summarize_report(input)然后在任何地方调用即可。SK的Kernel内核就是负责管理和调度这些原生函数与语义函数的“大脑”。3. 初始化Kernel与连接GPT-4环境好了概念清了现在让我们把SK的核心——Kernel启动起来并告诉它我们要用GPT-4。这里我使用OpenAI的官方API你也可以类似地配置Azure OpenAI服务。首先在项目根目录创建一个名为.env的文件用来存放你的密钥OPENAI_API_KEY你的OpenAI_API密钥接下来是初始化代码。我会逐段解释并指出几个容易踩坑的地方。import semantic_kernel as sk from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion import os from dotenv import load_dotenv, find_dotenv # 1. 加载环境变量 _ load_dotenv(find_dotenv()) api_key os.environ[OPENAI_API_KEY] # 2. 创建Kernel实例 kernel sk.Kernel() # 3. 添加文本补全服务 kernel.add_text_completion_service( openai-gpt4, OpenAIChatCompletion(gpt-4, api_key) ) print(Kernel初始化成功已挂载GPT-4服务。)关键点与避坑指南服务命名add_text_completion_service的第一个参数这里是openai-gpt4是一个自定义的服务名称。你可以注册多个服务比如一个用gpt-4做复杂推理另一个用gpt-3.5-turbo处理简单问答然后在不同的语义函数里按需调用。这个名字在后面创建语义函数时会被引用。模型名称OpenAIChatCompletion的第一个参数是模型名称。确保你写的gpt-4是你的API账户有权限访问的。如果你用的是gpt-4-turbo或者gpt-4o需要相应修改。错误处理在实际项目中这段初始化代码应该被try-except块包裹以捕获密钥无效、网络错误或模型不可用等异常。对于刚入门的情况至少要用print确认api_key已被成功加载。注意使用GPT-4 API会产生费用且其调用成本远高于GPT-3.5。在开发和测试阶段你可以先使用gpt-3.5-turbo来验证流程待逻辑无误后再切换至GPT-4进行高质量生成。只需将上面的gpt-4替换为gpt-3.5-turbo即可。4. 构建你的第一个语义函数现在Kernel已经就绪我们来实现核心功能创建一个能将长文本总结成推文的语义函数。这个过程就像是为LLM定制一个专属工具。# 定义提示词模板 sk_prompt {{$input}} 请将上面的内容总结成一条推特要求不超过140个字符。 # 使用Kernel创建语义函数 summary_function kernel.create_semantic_function( prompt_templatesk_prompt, function_namesummarize_to_tweet, description将输入文本总结为不超过140字符的推文。, max_tokens200, temperature0.2, top_p0.9, ) print(语义函数 summarize_to_tweet 已创建并注册到Kernel。)参数详解与调优心得prompt_template: 这是语义函数的“配方”。{{$input}}是一个占位符运行时会被你传入的实际文本替换。提示词的撰写质量直接决定输出效果。这里我用了中文指令因为我的目标输出是中文。如果你需要英文摘要提示词也应改为英文。function_name和description: 给函数起个好名字和清晰的描述这在后期管理大量语义函数时非常有用。SK的插件Plugin功能会利用这些信息。max_tokens: 限制模型输出的最大长度。设为200对于140字符的摘要来说绰绰有余同时预留了缓冲防止模型因意外生成过长内容而被截断。temperature(温度): 控制输出的随机性。范围0~1。我设为0.2是为了在保持一定创造性的同时确保摘要的稳定性和准确性。如果你需要非常确定、可重复的结果可以降到0.1甚至0。如果需要更多样化的表达可以提高到0.5以上。top_p(核采样): 另一种控制随机性的方法。通常与temperature配合使用。0.9是一个常用值意味着模型只从概率质量占前90%的词汇中采样。实操心得提示词模板的设计是门艺术。对于摘要任务除了简单的“总结一下”你还可以加入角色设定“你是一个专业的编辑”、格式要求“以‘摘要’开头”、或风格要求“语言活泼吸引读者”。多迭代几次提示词其效果提升可能比换模型还明显。5. 执行语义函数与异步调用实战函数定义好了我们来喂给它一段文本试试。我用的是Andrew Ng吴恩达经常讲的一个关于披萨店利用数据的小故事非常生动。# 准备输入文本 input_text 让我来举个例子。很多个周末我会开车从家走几分钟去一家本地披萨店从店主那里买一片夏威夷披萨。他的披萨很棒但他店里总是有很多冷掉的披萨放着而且每个周末总有不同的口味缺货。当我观察他经营店铺时我感到兴奋因为通过卖披萨他正在生成数据。如果他有机会利用AI这就是他可以加以利用的数据。AI系统在获得正确数据时擅长发现模式也许一个AI系统能发现地中海风味披萨在周五晚上卖得特别好从而建议他在周五下午多做些。 现在你可能会对我说“嘿Andrew这只是个小披萨店。有什么大不了的”而我想说对那位披萨店老板来说一个能帮他每年增加几千美元收入的东西对他来说就是天大的事。 # 方法一同步调用适用于简单脚本 try: summary_result summary_function(input_text) print(【同步调用结果】) print(summary_result) except Exception as e: print(f同步调用出错: {e}) # 方法二异步调用推荐用于生产环境 import asyncio async def async_summarize(): try: # 注意这里通过kernel.run_async来调用 result await kernel.run_async(summary_function, input_strinput_text) return result except Exception as e: print(f异步调用出错: {e}) return None # 运行异步函数 print(\n【异步调用结果】) final_result asyncio.run(async_summarize()) if final_result: print(final_result)执行结果分析运行上述代码你很可能会得到类似这样的输出AI可以帮助小披萨店分析销售数据优化库存从而显著提升年收入。成功我们得到了一条简洁、通顺且完全在140字符以内的中文摘要准确抓住了原文核心小披萨店、数据、AI分析、优化库存、增加收入。为什么强调异步调用在真实的AI应用里调用LLM API是一个网络I/O密集型操作可能需要几秒甚至更长时间。如果在Web服务器或GUI应用中使用同步调用会阻塞整个线程导致界面“卡死”或服务器无法处理其他请求。异步调用则允许程序在等待AI响应的同时去处理其他任务极大提升了应用的并发能力和响应速度。因此除非是极其简单的脚本否则我强烈建议你从一开始就习惯使用异步模式。6. 深入探索语义函数与原生函数的协作SK的强大之处在于它能将“思考”语义函数和“行动”原生函数无缝结合。让我们扩展一下场景假设我们不仅想生成摘要还想把摘要自动保存到一个本地文件里。首先我们创建一个原生函数来处理文件保存import datetime def save_summary_to_file(summary_text: str, filename_prefix: str summary): 将摘要文本保存到文件。这是一个原生函数。 if not summary_text: return 错误摘要内容为空。 # 生成带时间戳的文件名 timestamp datetime.datetime.now().strftime(%Y%m%d_%H%M%S) filename f{filename_prefix}_{timestamp}.txt try: with open(filename, w, encodingutf-8) as f: f.write(summary_text) return f摘要已成功保存至文件{filename} except IOError as e: return f文件保存失败{e} # 将这个原生函数注册到Kernel这样它就能被SK的编排器发现和调用 save_function kernel.import_skill_from_class(save_summary_to_file, class_nameFileSkill)[“save_summary_to_file”]现在我们如何将“生成摘要”和“保存文件”这两个步骤串联起来这就需要用到SK的管道Pipeline或顺序执行功能。我们可以手动编排但更SK的方式是定义一个组合函数通过插件形式或者在一个执行流程中依次调用。async def summarize_and_save_pipeline(long_text): 一个简单的自定义管道先摘要后保存。 # 步骤1: 调用语义函数生成摘要 summary await kernel.run_async(summary_function, input_strlong_text) # 步骤2: 调用原生函数保存摘要 save_result save_summary_to_file(str(summary), ai_summary) return { generated_summary: str(summary), save_operation_result: save_result } # 执行这个管道 pipeline_result asyncio.run(summarize_and_save_pipeline(input_text)) print(管道执行结果) print(f摘要{pipeline_result[generated_summary]}) print(f保存结果{pipeline_result[save_operation_result]})这个例子展示了SK的核心价值编排。在更复杂的智能体Agent场景中你可以让一个语义函数分析用户意图“用户想订机票”然后触发一系列原生函数查询航班API、计算价格、调用支付接口和语义函数生成确认邮件形成一个完整的自动化工作流。7. 常见问题、调试技巧与进阶方向在学习和使用Semantic Kernel的过程中你肯定会遇到一些坑。以下是我总结的几个典型问题及解决方案问题1调用语义函数时报错KeyError: ‘openai-gpt4’或类似的服务名错误。原因在create_semantic_function时如果没有显式指定service_idSK会尝试使用一个默认的服务。但你注册服务时可能用了自定义名称如“openai-gpt4”。解决创建语义函数时明确指定服务ID。summary_function kernel.create_semantic_function( prompt_templatesk_prompt, service_idopenai-gpt4, # 明确指定使用我们注册的服务 ... # 其他参数 )问题2异步调用时遇到事件循环Event Loop相关的错误。原因特别是在Jupyter Notebook或某些已有事件循环的环境中异步调用可能会冲突。解决确保使用asyncio.run(main_async_function())作为入口。在Jupyter中可以使用await直接调用或者使用nest_asyncio库不推荐在生产环境使用。最稳妥的方式是将异步逻辑封装在独立的脚本中运行。问题3生成的摘要不符合格式要求如超过140字符。排查检查提示词提示词指令是否清晰无歧义用英文写“less than 140 characters”模型有时会理解成“接近140”可以改为“strictly under 140 characters”或“within 120 characters”。调整参数降低max_tokens到150增加约束。同时可以稍微降低temperature让输出更“听话”。后处理在调用语义函数后添加一个原生函数进行校验和裁剪。if len(summary) 140: summary summary[:137] “...”。这是保证最终结果合规的可靠手段。问题4如何管理越来越多的提示词模板进阶技巧SK支持将语义函数定义为文件系统中的模板文件。你可以创建一个Prompts文件夹里面按功能建立子文件夹如Summarize、Translate在每个子文件夹里放两个文件skprompt.txt提示词模板和config.json配置参数。然后通过kernel.import_semantic_skill_from_directory函数批量导入。这使得版本管理和团队协作变得非常方便。关于Semantic Kernel vs. LangChain的选型思考经过这个项目的实践我对两者的区别有了更直观的感受。LangChain更像一个“AI应用的全家桶”提供了极其丰富的组件和集成从文档加载器到向量数据库生态庞大但学习曲线较陡有时感觉“重量级”。而Semantic Kernel的设计理念更偏向“轻量级编排框架”它强调与现有代码尤其是.NET生态的深度融合通过原生函数和语义函数的抽象让AI能力更自然地“长”在业务逻辑里。如果你是微软技术栈C# .NET的开发者或者你的应用主要基于Azure云服务那么Semantic Kernel的集成度和未来支持会更有优势。如果你需要快速集成各种第三方工具和数据库并且社区最新工具的探索对你很重要LangChain目前可能更活跃。这次用GPT-4和Semantic Kernel做文本摘要的实践让我深刻体会到构建AI应用的核心正在从“如何调API”转变为“如何设计和编排提示词与业务流程”。Semantic Kernel提供的这套抽象虽然入门时需要理解几个新概念但一旦掌握就能极大地提升开发效率和应用的可维护性。下一步我计划用它来尝试构建一个能自动处理会议纪要、提取任务并发送提醒的智能体那将是原生函数与语义函数更复杂的舞蹈。

相关新闻