|
作者:kevine一图胜千言,LangChain已经成为当前LLM应用框架的事实标准,这篇文章就来对LangChain基本概念以及其具体使用场景做一个整理。LangChain是什么LangChain是一个基于大语言模型的应用开发框架,它主要通过两种方式规范和简化了使用LLM的方式:集成:集成外部数据(如文件、其他应用、API数据等)到LLM中;Agent:允许LLM通过决策与特定的环境交互,并由LLM协助决定下一步的操作。LangChain的优点包括:高度抽象的组件:规范和简化与语言模型交互所需的各种抽象和组件;高度可自定义的Chains:提供了大量预置Chains的同时,支持自行继承BaseChain并实现相关逻辑以及各个阶段的callbackhandler等;活跃的社区与生态:Langchain团队迭代速度非常快,能快速使用最新的语言模型特性,该团队也有langsmith,auto-evaluator等其它优秀项目,并且开源社区也有相当多的支持。LangChain的主要组件这是一张LangChain的组件与架构图(langchainpython和langchainJS/TS的架构基本一致,本文中以langchainpython来完成相关介绍),基本完整描述了LangChain的组件与抽象层(callback不在这张图中,在下方我们会另外介绍),以及它们之间的相关联系。ModelI/O首先我们从最基本面的部分讲起,ModelI/O指的是和LLM直接进行交互的过程。在ModelI/O这一流程中,LangChain抽象的组件主要有三个:LanguagemodelsPromptsOutputparsers下面我们展开介绍一下⚠️注:下面涉及的所有代码示例中的OPENAI_API_KEY和OPENAI_BASE_URL需要提前配置好,OPENAI_API_KEY指OpenAI/OpenAI代理服务的APIKey,OPENAI_BASE_URL指OpenAI代理服务的BaseUrl。LanguageModelLanguageModel是真正与LLM/ChatModel进行交互的组件,它可以直接被当作普通的openaiclient来使用,在LangChain中,主要使用到的是LLM,ChatModel和Embedding三类LanguageModel。LLM:最基础的通过“textin➡️ textout”模式来使用的LanguageModel,另一方面,LangChain也收录了大量的第三方LLM。from langchain.llms import OpenAIllm = OpenAI(model_name="text-ada-001", openai_api_key=OPENAI_API_KEY, openai_api_base=OPENAI_BASE_URL)llm("What day comes after Friday?")# '\n\nSaturday.'ChatModelLM的变体,抽象了Chat这一场景下的使用模式,由“textin➡️ textout”变成了“chatmessagesin➡️ chatmessageout”,chatmessage是指**text+messagetype(System,Human,AI)**。from langchain.chat_models import ChatOpenAIfrom langchain.schema import HumanMessage, SystemMessagechat = ChatOpenAI(model_name="gpt-4-0613", temperature=1, openai_api_key=OPENAI_API_KEY, openai_api_base=OPENAI_BASE_URL)chat( [ SystemMessage(content="You are an expert on large language models and can answer any questions related to large language models."), HumanMessage(content="What’s the difference between Generic Language Models, Instruction Tuned Models and Dialog Tuned Models") ])# AIMessage(content='Generic Language Models, Instruction-Tuned Models, and Dialog-Tuned Models are all various types of language models that have been trained according to different datasets and methodologies. They are used in different contexts according to their specific strengths. \n\n1. **Generic Language Models**: These models are trained on a broad range of internet text. They are typically not tuned to specific tasks and thus can be used across a wide variety of applications. GPT-3, used by OpenAI, is an example of a general language model.\n\n2. **Instruction-Tuned Models**: These are language models that are fine-tuned specifically to follow instructions given in a prompt. They are trained using a procedure called Reinforcement Learning from Human Feedback (RLHF), following an initial supervised fine-tuning which consists of human AI trainers providing conversations where they play both the user and an AI assistant. This model often includes comparison data - two or more model responses are ranked by quality.\n\n3. **Dialog-Tuned Models**: Like Instruction-Tuned Models, these models are also trained with reinforcement learning from human feedback, and especially shine in multi-turn conversations. However, these are specifically designed for dialog scenarios, resulting in an ability to maintain more coherent and context-aware conversations. \n\nIn essence, the difference among the three revolves around the breadth of their training and their specific use-cases. Generic Language Models are broad but may lack specificity; Instruction-Tuned Models are better at following specific instructions given in prompts; and Dialog-Tuned Models excel in carrying out more coherent and elongated dialogues.', additional_kwargs={}, example=False)另一方面,LangChain也收录了大量的第三方ChatModelSystem-告诉AI要做什么的背景信息上下文;Human-标识用户传入的消息类型;AI-标识AI返回的消息类型。以下是一个简单的ChatModel使用示例:Embedding:Embedding将一段文字向量化为一个定长的向量,有了文本的向量化表示我们就可以做一些像语义搜索,聚类选择等来选择需要的文本片段,如下是将一个embed任意字符串的示例:from langchain.embeddings import OpenAIEmbeddingsembeddings = OpenAIEmbeddings(openai_api_key=OPENAI_API_KEY, openai_api_base=OPENAI_BASE_URL)text_embedding = embeddings.embed_query("To embed text(it can have any length)")print (f"Your embedding's length: {len(text_embedding)}")print (f"Here's a sample: {text_embedding[:5]}...")'''Your embedding's length: 1536Here's a sample: [-0.03194352, 0.009228715, 0.00807182, 0.0077545005, 0.008256923]...'''PromptsPrompt指用户的一系列指令和输入,是决定LanguageModel输出内容的唯一输入,主要用于帮助模型理解上下文并生成相关和连贯的输出,如回答问题、拓写句子和总结问题。在LangChain中的相关组件主要有PromptTemplate和Exampleselectors,以及后面会提到的辅助/补充Prompt的一些其它组件。PromptTemplate:预定义的一系列指令和输入参数的prompt模版,支持更加灵活的输入,如支持**outputinstruction(输出格式指令),partialinput(提前指定部分输入参数),examples(输入输出示例)**等;LangChain提供了大量方法来创建PromptTemplate,有了这一层组件就可以在不同LanguageModel和不同Chain下大量复用PromptTemplate了,PromptTemplate中也会有下面将提到的Exampleselectors,OutputParser的参与。Exampleselectors:在很多场景下,单纯的instruction+input的prompt不足以让LLM完成高质量的推理回答,这时候我们就还需要为prompt补充一些针对具体问题的示例,LangChain将这一功能抽象为了Exampleselectors这一组件,我们可以基于关键字,相似度(通常使用MMR/cosinesimilarity/ngram来计算相似度,在后面的向量数据库章节中会提到)。为了让最终的prompt不超过LanguageModel的token上限(各个模型的token上限见下表),LangChain还提供了LengthBasedExampleSelector,根据长度来限制example数量,对于较长的输入,它会选择包含较少示例的提示,而对于较短的输入,它会选择包含更多示例。OutputParser通常我们希望LanguageModel的输出是固定的格式,以支持我们解析其输出为结构化数据,LangChain将这一诉求所需的功能抽象成了OutputParser这一组件,并提供了一系列的预定义OutputParser,如最常用的Structuredoutputparser,Listparser,以及在LLM输出无法解析时发挥作用的Auto-fixingparser和Retryparser。OutputParser需要和PromptTemplate,Chain组合使用:PromptTemplate:在PromptTemplate中通过指定partial_variables为OutputParser的format,即可在prompt中补充让模型输出所需格式内容的指令;Chain:在Chain中指定OutputParser,并使用Chain的predict_and_parse/apply_and_parse方法启动Chain,即可直接输出解析后的数据。使用示例以下是一个完整的组合PromptTemplate,OutputParser和Chain的具体用例:from langchain.chat_models import ChatOpenAIllm = ChatOpenAI(temperature=0.5, model_name="gpt-3.5-turbo-16k-0613", openai_api_key=OPENAI_API_KEY, openai_api_base=OPENAI_BASE_URL)template = """## Input{text}## InstructionPlease summarize the piece of text in the input part above.Respond in a manner that a 5 year old would understand.{format_instructions}YOUR RESPONSE:"""# 创建一个Output arser,包含两个输出字段,并指定类型和说明output_parser = StructuredOutputParser.from_response_schemas( [ ResponseSchema(name="keywords", type="list", description="keywords of the text"), ResponseSchema(name="summary", type="string", description="summary of the text"), ])# 创建Prompt Template,并将format_instructions通过partial_variables直接指定为Output arser的formatprompt = romptTemplate( input_variables=["text"], template=template, partial_variables={"format_instructions": output_parser.get_format_instructions()},)# 创建Chain并绑定Prompt Template和Output arser(它将自动使用Output arser解析llm输出)summarize_chain = LLMChain(llm=llm, verbose=True, prompt=prompt, output_parser=output_parser)to_summarize_text = 'Abstract. Text-to-SQL aims at generating SQL queries for the given natural language questions and thus helping users to query databases. rompt learning with large language models (LLMs) has emerged as a recent approach, which designs prompts to lead LLMs to understand the input question and generate the corresponding SQL. However, it faces challenges with strict SQL syntax requirements. Existing work prompts the LLMs with a list of demonstration examples (i.e. question-SQL pairs) to generate SQL, but the fixed prompts can hardly handle the scenario where the semantic gap between the retrieved demonstration and the input question is large.'output = summarize_chain.predict(text=to_summarize_text)import jsonprint (json.dumps(output, indent=4))输出如下:{ "keywords": [ "Text-to-SQL", "SQL queries", "natural language questions", "databases", "prompt learning", "large language models", "LLMs", "SQL syntax requirements", "demonstration examples", "semantic gap" ], "summary": "Text-to-SQL is a method that helps users generate SQL queries for their questions about databases. One approach is to use large language models to understand the question and generate the SQL. However, this approach faces challenges with strict SQL syntax rules. Existing methods use examples to teach the language models, but they struggle when the examples are very different from the question."}Dataconnection正如我在文章开头的LangChain是什么一节中提到的,集成外部数据到LanguageModel中是LangChain提供的核心能力之一,也是市面上很多优秀的大语言模型应用成功的核心之一(GithubCopilotChat,网页聊天助手,论文总结助手,youtube视频总结助手…),在LangChain中,Dataconnection这一层主要包含以下四个抽象组件:DocumentloadersDocumenttransformersVectorstoresRetrievers下面我们展开介绍一下Documentloaders为了补全LLM的上下文信息,给予其足够的提示,我们需要从各类数据源获取各类数据,这也就是LangChain抽象的Documentloaders这一组件的功能。使用Documentloaders可以将源中的数据加载为Document。Document由一段文本和相关元数据组成。例如,有用于加载简单.txt文件的,用于加载相对结构化的markdown文件的,用于加载任何网页文本内容,甚至用于加载解析YouTube视频的脚本。同时LangChain还收录了海量的第三方Documentloaders,以下是一个使用NotionDBLoader来加载notiondatabase中的page为Document的示例:from langchain.document_loaders import NotionDBLoaderfrom getpass import getpass# getpass()指引用户输入密钥NOTION_TOKEN = getpass()DATABASE_ID = getpass()loader = NotionDBLoader( integration_token=NOTION_TOKEN, database_id=DATABASE_ID, request_timeout_sec=30, # optional, defaults to 10)# 请求详情见 https://developers.notion.com/reference/post-database-querydocs = loader.load()docs[0].page_content[:100]Documenttransformers当我们加载Document到内存后,我们通常还会希望将他们尽可能的结构化/分块,以进行更加灵活的操作。最简单的例子是,我们很多时候都需要将一个长文档拆分成更小的块,以便放入模型的上下文窗口中;LangChain有许多内置的Documenttransformers(大部分都是TextSpliter),可以轻松地拆分、合并、筛选和以其他方式操作文档,一些常用的Documenttransformers如下:同时LangChain也收录了很多第三方的Documenttransformers(如基于爬虫中常见的beautifulsoup,基于OpenAI打metadatatag的等等)。Vectorstores在前面的Prompt一节中我们提到了Exampleselectors,那么我们要如何找到相关示例呢?通常这个答案就是向量数据库。存储和搜索非结构化数据的最常见方式之一是将其向量化(embedding)并存储所得到的嵌入向量,然后在查询时向量化非结构化查询并检索与嵌入的查询最相似的向量。Vectorstores负责存储向量化数据并提供向量搜索的功能,常见的向量数据库包括FAISS,Milvus,Pinecone,Weaviate,Chroma等,日常使用更常用的是FAISS。同时LangChain也收录了很多第三方的VectorStores,提供更加强大的向量搜索等功能。RetrieversRetrievers是LangChain提供的将Document与LanguageModel相结合的组件。LangChain中有许多不同类型的Retrievers,但最广泛使用的就是VectoreStoreRetriever,我们可以直接把它当做连接向量数据库和LanguageModel的中间层,并且VectoreStoreRetriever的使用也很简单,直接retriever=db.as_retriever()即可。当然我们还有很多其它的Retrievers如WebsearchRetrievers等,LangChain也收录了很多第三方的Retrievers使用示例5以下就是一个使用向量数据库+VectoreStoreRetriever+QAChain的QA应用示例:from langchain.chat_models import ChatOpenAIfrom langchain.vectorstores import FAISSfrom langchain.chains import RetrievalQAfrom langchain.document_loaders import TextLoaderfrom langchain.embeddings.openai import OpenAIEmbeddingsfrom langchain.text_splitter import RecursiveCharacterTextSplitter# 初始化LLMllm = ChatOpenAI(temperature=0.5, model_name="gpt-3.5-turbo-16k-0613", openai_api_key=OPENAI_API_KEY, openai_api_base=OPENAI_PROXY_URL)# 加载文档loader = TextLoader('path/to/related/document')doc = loader.load()print (f"You have {len(doc)} document")print (f"You have {len(doc[0].page_content)} characters in that document")# 分割字符串text_splitter = RecursiveCharacterTextSplitter(chunk_size=3000, chunk_overlap=400)docs = text_splitter.split_documents(doc)num_total_characters = sum([len(x.page_content) for x in docs])print (f"Now you have {len(docs)} documents that have an average of {num_total_characters / len(docs):,.0f} characters (smaller pieces)")# 初始化向量化模型embeddings = OpenAIEmbeddings(openai_api_key=OPENAI_API_KEY, openai_api_base=OPENAI_PROXY_URL)# 向量化Document并存入向量数据库(绑定向量和对应Document元数据),这里我们选择本地最常用的FAISS数据库# 注意: 这会向OpenAI产生请求并产生费用doc_search = FAISS.from_documents(docs, embeddings)qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=doc_search.as_retriever(), verbose=True)query = "Specific questions to be asked"qa.run(query)执行后verbose输出日志如下:Youhave1documentYouhave74663charactersinthatdocumentNowyouhave29documentsthathaveanaverageof2,930characters(smallerpieces)>Enteringnewchain...Promptafterformatting:System:Usethefollowingpiecesofcontexttoanswertheusersquestion.Ifyoudon'tknowtheanswer,justsaythatyoudon'tknow,don'ttrytomakeupananswer.---------------Human:Whatdoestheauthordescribeasgoodwork?Theauthordescribesworkingonthingsthataren'tprestigiousasasignofgoodwork.Theybelievethatworkingonunprestigioustypesofworkcanleadtothediscoveryofsomethingrealandthatitindicateshavingtherightkindofmotives.Theauthoralsomentionsthatworkingonthingsthatlast,suchaspaintings,isconsideredgoodwork.Chains接下来就是LangChain中的主角——Chains了,Chains是LangChain中为连接多次与LanguageModel的交互过程而抽象的重要组件,它可以将多个组件组合在一起以创建一个单一的、连贯的任务,也可以嵌套多个Chain组合在一起,或者将Chain与其他组件组合来构建更复杂的Chain。除了单一Chain外,常用的几个Chains如下:RouterChain在一些场景下,我们需要根据输入/上下文决定使用哪一条Chain,甚至哪一个Prompt,RouterChain就提供了诸如MultiPromptChain,LLMRouterChain等一系列用于决策的Chain(这与后面我们会提到的Agent有类似的地方)。RouterChain由两部分组成:RouterChain本身:负责决定下一个目标Chain;目标Chains:RouterChain可以路由到的目标Chain。SequentialChain顾名思义,顺序执行的串行Chain,其中最简单的SimpleSequentialChain非常简单粗暴,SimpleSequentialChain的每个子Chain都有一个单一的输入/输出,并且一个步骤的输出是下一步的输入。而高阶一些的SequentialChain则允许多输入输出,并且我们可以通过添加后面会提到的Memory等来提高其推理表现。Map-reduceChainMap-reduceChain主要用于summary的场景,针对那些超长的文档,首先我们通过前面提到过的TextSpliter按一定规则分割文档为更小的Chunks(通常使用RecursiveCharacterTextSplitter,如果Document是结构化的可以考虑使用指定的TextSpliter),然后对每个分割的部分执行”map-chain”,收集全部”map-chain”的输出后,再执行”reduce-chain”,获得最终的summary输出。使用示例下面就是一个RouterChain中的MultiPromptChain的具体示例:from langchain.chains.router import MultiPromptChainfrom langchain.llms import OpenAIfrom langchain.chains import ConversationChainfrom langchain.chains.llm import LLMChainfrom langchain.prompts import romptTemplatefrom langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParserfrom langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE# 定义要路由的promptsphysics_template = """You are a very smart physics professor. \You are great at answering questions about physics in a concise and easy to understand manner. \When you don't know the answer to a question you admit that you don't know.Here is a question:{input}"""math_template = """You are a very good mathematician. You are great at answering math questions. \You are so good because you are able to break down hard problems into their component parts, \answer the component parts, and then put them together to answer the broader question.Here is a question:{input}"""# 整理prompt和相关信息prompt_infos = [ { "name": "physics", "description": "Good for answering questions about physics", "prompt_template": physics_template, }, { "name": "math", "description": "Good for answering math questions", "prompt_template": math_template, },]llm = OpenAI(temperature=0.5, openai_api_key=OPENAI_API_KEY, openai_api_base=OPENAI_PROXY_URL+"/v1")destination_chains = {}for p_info in prompt_infos: # 以每个prompt为基础创建一个destination_chain(开启verbose) name = p_info["name"] prompt_template = p_info["prompt_template"] prompt = romptTemplate(template=prompt_template, input_variables=["input"]) chain = LLMChain(llm=llm, prompt=prompt) destination_chains[name] = chain# 创建一个缺省chain,如果没有其他chain满足路由条件,则使用该chaindefault_chain = ConversationChain(llm=llm, output_key="text")destinations = [f"{p['name']}: {p['description']}" for p in prompt_infos]destinations_str = "\n".join(destinations)# 根据prompt_infos中的映射关系创建router_promptrouter_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(destinations=destinations_str)router_prompt = romptTemplate( template=router_template, input_variables=["input"], output_parser=RouterOutputParser(),)# 创建router_chain(开启verbose)router_chain = LLMRouterChain(llm_chain=LLMChain(llm=llm, prompt=router_prompt, verbose=True), verbose=True)# 将router_chain和destination_chains以及default_chain组合成MultiPromptChain(开启verbose)chain = MultiPromptChain( router_chain=router_chain, destination_chains=destination_chains, default_chain=default_chain, verbose=True,)# runchain.run("What is black body radiation?")执行后verbose输出日志如下:Enteringnewchain...Promptafterformatting:Givenarawtextinputtoalanguagemodelselectthemodelpromptbestsuitedfortheinput.Youwillbegiventhenamesoftheavailablepromptsandadescriptionofwhatthepromptisbestsuitedfor.Youmayalsorevisetheoriginalinputifyouthinkthatrevisingitwillultimatelyleadtoabetterresponsefromthelanguagemodel.<< FORMATTING >>ReturnamarkdowncodesnippetwithaJSONobjectformattedtolooklike:{ "destination": string \ name of the prompt to use or "DEFAULT" "next_inputs": string \ a potentially modified version of the original input}REMEMBER:"destination"MUSTbeoneofthecandidatepromptnamesspecifiedbelowORitcanbe"DEFAULT"iftheinputisnotwellsuitedforanyofthecandidateprompts.REMEMBER:"next_inputs"canjustbetheoriginalinputifyoudon'tthinkanymodificationsareneeded.<< CANDIDATE PROMPTS >>physics:Goodforansweringquestionsaboutphysicsmath:Goodforansweringmathquestions<< INPUT >>Whatisblackbodyradiation?<< OUTPUT >> Finishedchain.physics:{'input':'Whatisblackbodyradiation?'}>Enteringnewchain...Promptafterformatting:Youareaverysmartphysicsprofessor.Youaregreatatansweringquestionsaboutphysicsinaconciseandeasytounderstandmanner.Whenyoudon'tknowtheanswertoaquestionyouadmitthatyoudon'tknow.Hereisaquestion: Whatisblackbodyradiation?>Finishedchain.MemoryMemory可以帮助LanguageModel补充历史信息的上下文,LangChain中的Memory是一个有点模糊的术语,它可以像记住你过去聊天过的信息一样简单,也可以结合向量数据库做更加复杂的历史信息检索,甚至维护相关实体及其关系的具体信息,这取决于具体的应用。通常Memory用于较长的Chain,能一定程度上提高模型的推理表现。常用的Memory类型如下:ChatMessages最简单Memory,将历史Chat记录作为补充信息放入prompt中.Vectorstore-backedmemory基于向量数据库的Memory,将memory存储在向量数据库中,并在每次调用时查询TopK的最“重要”的文档。这与大多数其他内存类的不同之处在于,它不显式跟踪交互的顺序,最多基于部分元数据筛选一下向量数据库的查询范围Conversationbuffer(window)memory保存一段时间内的历史Chat记录,它只使用最后K个记录(仅保持最近交互的滑动窗口),这样buffer也就不会变得太大,避免超过token上限。Conversationsummarymemory这种类型的Memory会随着Chat的进行创建对话的摘要,并将当前摘要存储在Memory中,用于后续对话的history提示;这种memory方案对长会话非常有用,但频繁的总结摘要会耗费大量的token.ConversationSummaryBufferMemory结合了buffermemory和summarymemory的策略,依旧会在内存中保留最后的一些Chat记录作为buffer,并在buffer的总token数达到预置的上限后,对所有Chat记录总结摘要作为SystemMessage并清理其它历史Messages;这种memory方案结合了buffermemory和summarymemory的优点,既不会频繁地总结摘要消耗token,也不会让buffer缺失过多信息。使用示例下面是一个ConversationSummaryBufferMemory在ConversationChain中的使用示例,包含了切换会话时恢复现场memory的方法以及自定义summaryprompt的方法:from langchain.chains import ConversationChainfrom langchain.memory import ConversationSummaryBufferMemoryfrom langchain.llms import OpenAIfrom langchain.schema import SystemMessage, AIMessage, HumanMessagefrom langchain.memory.prompt import SUMMARY_PROMPTfrom langchain.prompts import romptTemplatellm = OpenAI(temperature=0.7, openai_api_key=OPENAI_API_KEY, openai_api_base=OPENAI_PROXY_URL+"/v1")# ConversationSummaryBufferMemory默认使用langchain.memory.prompt.SUMMARY_PROMPT作为summary的PromptTemplate# 如果对它summary的格式/内容有特殊要求,可以自定义PromptTemplate(实测默认的summary有些流水账)prompt_template_str = """## InstructionProgressively summarize the lines of conversation provided, adding onto the previous summary returning a new concise and detailed summary.Don't repeat the conversation directly in the summary, extract key information instead.## EXAMPLECurrent summary:The human asks what the AI thinks of artificial intelligence. The AI thinks artificial intelligence is a force for good.New lines of conversation:Human: Why do you think artificial intelligence is a force for good?AI: Because artificial intelligence will help humans reach their full potential.New summary:The human inquires about the AI's opinion on artificial intelligence. The AI believes that it is a force for good as it can help humans reach their full potential.## Current summary{summary}## New lines of conversation{new_lines}## New summary"""prompt = PromptTemplate( input_variables=SUMMARY_PROMPT.input_variables, # input_variables为SUMMARY_PROMPT中的input_variables不变 template=prompt_template_str, # template替换为上面重新编写的prompt_template_str)memory = ConversationSummaryBufferMemory(llm=llm, prompt=prompt, max_token_limit=60)# 添加历史memory,其中第一条SystemMessage为历史对话中Summary的内容,第二条HumanMessage和第三条AIMessage为历史对话中最后的对话内容memory.chat_memory.add_message(SystemMessage(content="The human asks what the AI thinks of artificial intelligence. The AI thinks artificial intelligence is a force for good because it will help humans reach their full potential. The human then asks the difference between python and golang in short. The AI responds that python is a high-level interpreted language with an emphasis on readability and code readability, while golang is a statically typed compiled language with a focus on concurrency and performance. Python is typically used for general-purpose programming, while golang is often used for building distributed systems."))memory.chat_memory.add_user_message("Then if I want to build a distributed system, which language should I choose?")memory.chat_memory.add_ai_message("If you want to build a distributed system, I would recommend golang as it is a statically typed compiled language that is designed to facilitate concurrency and performance.")# 调用memory.prune()确保chat_memory中的对话内容不超过max_token_limitmemory.prune()conversation_with_summary = ConversationChain( llm=llm, # We set a very low max_token_limit for the purposes of testing. memory=memory, verbose=True,)# memory.prune()会在每次调用predict()后自动执行conversation_with_summary.predict(input="Is there any well-known distributed system built with golang?")conversation_with_summary.predict(input="Is there a substitutes for Kubernetes in python?")执行后verbose输出日志如下:>Enteringnewchain...Promptafterformatting:ThefollowingisafriendlyconversationbetweenahumanandanAI.TheAIistalkativeandprovideslotsofspecificdetailsfromitscontext.IftheAIdoesnotknowtheanswertoaquestion,ittruthfullysaysitdoesnotknow.Currentconversation:System:ThehumanaskstheAIaboutitsopiniononartificialintelligenceandistoldthatitisaforceforgoodthatcanhelphumansreachtheirfullpotential.Thehumantheninquiresaboutthedifferencesbetweenpythonandgolang,withtheAIexplainingthatpythonisahigh-levelinterpretedlanguageforgeneral-purposeprogramming,whilegolangisastaticallytypedcompiledlanguageoftenusedforbuildingdistributedsystems.Human:ThenifIwanttobuildadistributedsystem,whichlanguageshouldIchoose?AI:Ifyouwanttobuildadistributedsystem,Iwouldrecommendgolangasitisastaticallytypedcompiledlanguagethatisdesignedtofacilitateconcurrencyandperformance.Human:Isthereanywell-knowndistributedsystembuiltwithgolang?AI:>Finishedchain.>Enteringnewchain...Promptafterformatting:ThefollowingisafriendlyconversationbetweenahumanandanAI.TheAIistalkativeandprovideslotsofspecificdetailsfromitscontext.IftheAIdoesnotknowtheanswertoaquestion,ittruthfullysaysitdoesnotknow.Currentconversation:System:ThehumanaskstheAIaboutitsopiniononartificialintelligenceandistoldthatitisaforceforgoodthatcanhelphumansreachtheirfullpotential.Thehumantheninquiresaboutthedifferencesbetweenpythonandgolang,withtheAIexplainingthatpythonisahigh-levelinterpretedlanguageforgeneral-purposeprogramming,whilegolangisastaticallytypedcompiledlanguagedesignedtofacilitateconcurrencyandperformance,thusbettersuitedfordistributedsystems.TheAIrecommendsgolangforbuildingdistributedsystems.Human:Isthereanywell-knowndistributedsystembuiltwithgolang?AI:Yes,thereareseveralwell-knowndistributedsystemsbuiltwithgolang.TheseincludeKubernetes,Docker,andConsul.Human:IsthereasubstitutesforKubernetesinpython?AI:>Finishedchain.'Yes,thereareseveralsubstitutesforKubernetesinpython.TheseincludeDask,ApacheMesosandMarathon,andApacheAurora.’Agent在一些场景下,我们需要根据用户输入灵活地调用LLM和其它工具(LangChain将工具抽象为Tools这一组件),Agent为这样的应用程序提供了相关的支持。Agent可以访问一套工具,并根据用户输入确定要使用Chain或是Function,我们可以简单的理解为他可以动态的帮我们选择和调用Chain或者已有的工具。常用的Agent类型如下:ConversationalAgent这类Agent可以根据LanguageModel的输出决定是否使用指定的Tool,以及使用什么Tool(这里的Tool也可以是一个Chain),以及时的为ModelI/O的过程补充信息。OpenAIfunctionsAgent类似ConversationalAgent,但它能够让Agent更进一步地帮忙提取指定Tool的参数等,甚至使用多个Tools。PlanandexecuteAgent抽象Agent“决定做什么”的过程为“planningwhattodo”和“executingthesubtasks”(这种方法来自"Plan-and-Solve"这一篇论文),其中“planningwhattodo”这一步通常完全由LLM完成,而“executingthesubtasks”这一任务则通常由更多的Tools来完成。ReActAgent结合对LLM输出的归因和执行,类似OpenAIfunctionsAgent,提供了一个更加明确的框架以及由论文支撑的方法。Selfaskwithsearch这类Agent会基于LLM的输出,自行调用Tools以及LLM来进行额外的搜索和自查,以达到拓展和优化输出的目的。总结LangChain中的DataConnection将LLM与万物互联,给LangChain构建的应用带来了无限可能,而Agent又为应用开发中非常常见的”事件驱动“这一开发框架提供了偷懒的途径,将分支决策的工作交给LLM,这又进一步简化了应用开发的工作;结合基于高度抽象的ModelI/O及Memory等组件,LangChain让开发者能够更快,更好更灵活地实现LLM助手、对话机器人等应用,极大地降低了LLM的使用门槛。
|
|