• LLMs 应用开发框架 Semantic Kernel 和 LangChain 比较


    本文首发于博客LLM 应用开发实践

    Semantic Kernel 和 LangChain 是当前比较受欢迎的两款 LLMs 应用开发框架,笔者通过实现一个支持联网功能的智能 AI 助手来比较分析下两个框架的差异(适合自己场景的工具才是最好滴 🧑🏻‍💻)

    关键概念对应(非严谨版)

    LangChainSemantic Kernel备注
    ChainsKernel构造调用序列
    AgentsPlanner自动规划任务以满足用户的需求
    ToolsPlugins (semantic functions + native function)可在不同应用之间重复使用的自定义组件
    MemoryMemory将上下文和嵌入存储在内存或其他存储中

    语言支持

    编程语言LangChainSemantic Kernel
    Python
    JavaScript
    C#
    Java

    基于Semantic Kernel 构建应用过程概览
    Semantic Kernel 组件关系(图源网络)
    Semantic Kernel 具体介绍可看之前发布的文章👉大模型时代软件开发:吴恩达提到的Semantic Kernel是什么?

    基于LangChain 构建应用过程概览

    LangChain 组件关系(图源网络)
    各组件深度解析具体可看前面的系列文章 👇

    从零开始学LangChain(1):入门介绍

    从零开始学LangChain(2):Model I/O 模块和 Data Connection 模块

    从零开始学LangChain(3):Memory 模块和 Chain 模块

    从零开始学LangChain(4):Agents 模块和 Callbacks 模块

    AI 助手的设计

    1. 能够结合上下文识别提问意图
    2. 使用向量数据库获取相关信息
    3. 回答内容参考自最新外部知识

    功能

    意图识别

    首先根据历史对话记录确定用户的询问意图

    LangChain 实现

    from langchain.chains import LLMChain
    from langchain.chat_models import ChatOpenAI
    from langchain.memory import ChatMessageHistory
    from langchain.prompts.chat import ChatPromptTemplate, SystemMessagePromptTemplate
    from langchain.retrievers.web_research import WebResearchRetriever
    from langchain.vectorstores.chroma import Chroma
    from langchain.embeddings import OpenAIEmbeddings
    from langchain.chat_models.openai import ChatOpenAI
    from langchain.utilities.google_search import GoogleSearchAPIWrapper
    
    def _summarize_user_intent(self, query: str) -> str:
            "根据历史对话,推断用户意图"
            
            chat_history_str = ""
            for item in self.chat_history.messages:
                chat_history_str += f"{item}\n"
            
            system_template = (
                "你是一个中文AI助手,正在阅读用户和助手之间对话的记录。根据聊天历史和用户的问题,推断用户的真实意图。"
                "聊天历史记录:```{chat_history_str}```\n 用户查询问题内容:```{query}```\n"
            )
            chat_template = ChatPromptTemplate(
                messages=[SystemMessagePromptTemplate.from_template(system_template)]
            )
            llm_chain = LLMChain(llm=self.llm, prompt=chat_template)
            uesr_intent = llm_chain({"chat_history_str": chat_history_str, "query": query})
            return uesr_intent.get("text")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    Semantic Kernel实现

    import os
    import chromadb
    import semantic_kernel as sk
    from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion, OpenAITextEmbedding
    from semantic_kernel.connectors.memory.chroma import ChromaMemoryStore
    from semantic_kernel.semantic_functions.chat_prompt_template import ChatPromptTemplate
    from semantic_kernel.semantic_functions.prompt_template_config import PromptTemplateConfig
    from semantic_kernel.semantic_functions.semantic_function_config import SemanticFunctionConfig
    
    async def _summarize_user_intent(self, query: str)->str:
            self.variables["query"] = query
            system_template = (
                "你是一个中文AI助手,正在阅读用户和助手之间对话的记录。根据聊天历史和用户的问题,推断用户的真实意图。"
                "聊天历史记录:```{{$chat_history_str}}```\n 用户查询问题内容:```{{$query}}```\n"
            )
            prompt_config_dict = {
                "type": "completion",
                "description": "一个能推断用户意图的AI助手",
                "completion": {
                    "temperature": 0.7,
                    "top_p": 0.5,
                    "max_tokens": 200,
                    "number_of_responses": 1,
                    "chat_system_prompt": system_template,
                    "presence_penalty": 0,
                    "frequence_penalty": 0
                },
                "inputs": {
                    "parameters":[
                        {
                            "name": "chat_history_str",
                            "description": "用户和助手所有消息历史记录",
                            "defaultValue": ""
                        },
                        {
                            "name": "query",
                            "description": "用户查询问题内容",
                            "defaultValue": "",
                        }
                    ]
                }
                
            }
            prompt_config = PromptTemplateConfig.from_dict(prompt_config_dict)
            prompt_template = ChatPromptTemplate(
                template=system_template,
                prompt_config=prompt_config,
                template_engine=self.kernel.prompt_template_engine
            )
            user_intent_function_config = SemanticFunctionConfig(
                prompt_config, prompt_template
            )
            user_intent_function = self.kernel.register_semantic_function(
                skill_name=PLUGIN,
                function_name="user_intent_function",
                function_config=user_intent_function_config
            )
            
            response = await self.kernel.run_async(
                user_intent_function, input_vars=self.variables
            )
            return str(response)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    信息检索功能

    使用向量数据库 Chroma 存储嵌入后的文本向量,获取相关上下文

    LangChain 实现

    def _get_web_research_retriever(self):
            vectorstore = Chroma(
                embedding_function=OpenAIEmbeddings(), persist_directory="./chroma_db"
            )
            search = GoogleSearchAPIWrapper()
            web_research_retriever = WebResearchRetriever.from_llm(
                vectorstore=vectorstore,
                llm=self.llm,
                search=search,
            )
            return web_research_retriever
    
    def _get_context(self, user_intent: str)-> list[str]:
        "从搜索引擎获取相关信息"
        retriever = self._get_web_research_retriever()
        docs = retriever.get_relevant_documents(user_intent)
        context = [doc.page_content for doc in docs]
        return context
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    Semantic Kernel实现

    async def _get_context(self, query: str) -> list[str]:
        kernel = sk.Kernel()
        kernel.add_text_embedding_generation_service(
          "openai-embedding",
          OpenAITextEmbedding(
            api_key=OPENAI_API_KEY,
            model_id="text-embedding-ada-002"
          )
        )
        kernel.register_memory_store(
          memory_store=ChromaMemoryStore(client_settings=chromadb.Settings(persist_directory="./chroma_db"))
        )
        docs = await kernel.memory.search_async(collection="chatbot_collection", query=query)
        context = [doc.text for doc in docs]
    
        return context
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    结合上下文(检索增强生成)

    结合历史对话记录,回答用户问题

    LangChain 实现

    def _rag(self, context: list[str], query: str)-> str:
            self.chat_history.add_message(query)
            context_str = "\n\n".join(context)
            system_template = (
                "你是一个中文AI助手,请使用您在上下文中可以找到的信息来回答用户的问题,如果用户的问题与上下文中的信息无关,请说你不知道。"
                "上下文内容:```{context_str}```\n"
            )
            
            user_template = ChatPromptTemplate(
                messages=[SystemMessagePromptTemplate.from_template(system_template)] + self.chat_history.messages
            )
            
            llm_chain = LLMChain(llm=self.llm, prompt=user_template)
            response = llm_chain({"context_str": context_str}).get("text")
            self.chat_history.add_ai_message(response)
            return response
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Semantic Kernel实现

    async def _rag(self, context: list[str], query: str)-> str:
        context_str = "\n\n".join(context)
        self.variables["context"] = context_str
        system_template = (
          "你是一个中文AI助手,请使用您在上下文中可以找到的信息来回答用户的问题,如果用户的问题与上下文中的信息无关,请说你不知道。"
          "上下文内容:```{{$context_str}}```\n"
        )
    
        self.variables["query"] = query
    
        user_template = "{{$chat_history}}" + f"{USER}" + "{{$query}}\n"
    
        prompt_config_dict = {
          "type": "completion",
          "description": "一个智能助手",
          "completion": {
            "temperature": 0.7,
            "top_p": 0.5,
            "max_tokens": 200,
            "number_of_responses": 1,
            "chat_system_prompt": system_template,
            "presence_penalty": 0,
            "frequency_penalty": 0,
          },
          "input": {
            "parameters": [
              {
                "name": "query",
                "description": "用户查询问题内容",
                "defaultValue": "",
              },
              {
                "name": "context",
                "description": "上下文内容",
                "defaultValue": "",
              },
              {
                "name": "chat_history",
                "description": "用户和助手所有消息历史记录",
                "defaultValue": "",
              },
            ]
          },
        }
        prompt_config = PromptTemplateConfig.from_dict(prompt_config_dict)
        prompt_template = ChatPromptTemplate(
          template=user_template,
          prompt_config=prompt_config,
          template_engine=self.kernel.prompt_template_engine
        )
    
        rag_function_config = SemanticFunctionConfig(prompt_config, prompt_template)
        rag_function = self.kernel.register_semantic_function(
          skill_name=PLUGIN,
          function_name="rag_function",
          function_config=rag_function_config
        )
    
        response = await self.kernel.run_async(rag_function, input_vars=self.variables)
        self.variables["chat_history"] = f"{USER}: {query}\n{ASSISTANT}:{response}\n"
        return str(response)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    问答功能实现

    async def ask(self, query: str) -> str:
        user_intent = await self._summarize_user_intent(query)
        context_list = await self._get_context(user_intent)
        response = await self._rag(context_list, query)
        print(
          "========\n"
          f"问题:{query}\n"
          f"用户意图:{user_intent}\n"
          f"回答:{response}\n"
          "========\n"
        )
        return response
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    结论

    Langchain更适合构建MVP,Semantic Kernel更适合生产级应用

    Langchain更适合独立开发,Semantic Kernel更适合企业应用

    LangChain 的优势在于丰富的组件支持,以WebResearchRetriever的封装实现为例,将向量数据库和搜索引擎结合起来,只需几行代码就可以完成复杂功能,加速MVP实现,需求验证。LangChain 的缺点主要是过度封装,Prompt 模板组件做这么抽象实在没必要,应用调试和生产环境可观测性方面都不太友好,软件工程方面的组织相对较差。

    Semantic Kernel生态比起 LangChain 还差点,但是背靠大厂“巨硬”,这点也能快速赶上,设计整体奔着构建生产级应用去的,把LangChain的一些缺点基本都规避了。Semantic Kernel主要面向C#开发者,也支持Python,C#开发者只能选择Semantic Kernel。

    更多内容在公号:
    在这里插入图片描述

    回复 联网 AI 助手获取 LangChain 和 SK 实现的AI助手完整源码。

  • 相关阅读:
    AP8851H 宽电压降压恒压DC-DC 电源管理芯片 5V 12V输出 零功耗 快充方案应用
    安装“react-dnd”和“react-dnd-html5-backend”无法找到“react/jsx-runtime”
    【C++音视频开发】初级篇 | 图像基础概念
    人工智能、深度学习、机器学习常见面试题321~324
    AI一点通: OpenAI whisper 在线怎么调用,怎么同时输出时间信息?
    【Numpy】给数组增加一个维度
    蓝桥杯2022初赛——扫雷
    leetcode268. Missing Number
    GO语言网络编程(并发编程)并发安全和锁
    【electron 4】electron配置打包环境
  • 原文地址:https://blog.csdn.net/weixin_43829633/article/details/134174360