当前位置: 首页 > news >正文

自己做的网页怎么上传到网站成都有什么好玩的娱乐场所

自己做的网页怎么上传到网站,成都有什么好玩的娱乐场所,做网站留言板需要什么条件,手机百度关键词优化本文为笔者学习LangChain时对官方文档以及一系列资料进行一些总结#xff5e;覆盖对Langchain的核心六大模块的理解与核心使用方法#xff0c;全文篇幅较长#xff0c;共计50000字#xff0c;可先码住辅助用于学习Langchain。 一、Langchain是什么#xff1f; 如今各类AI… 本文为笔者学习LangChain时对官方文档以及一系列资料进行一些总结覆盖对Langchain的核心六大模块的理解与核心使用方法全文篇幅较长共计50000字可先码住辅助用于学习Langchain。 一、Langchain是什么 如今各类AI模型层出不穷百花齐放大佬们开发的速度永远遥遥领先于学习者的学习速度。。为了解放生产力不让应用层开发人员受限于各语言模型的生产部署中…LangChain横空出世界。 Langchain可以说是现阶段十分值得学习的一个AI架构那么究竟它有什么魔法才会配享如此高的地位呢会不会学习成本很高不要担心Langchain虽然功能强大但其实它就是一个为了提升构建LLM相关应用效率的一个工具我们也可以将它理解成一个“说明书是的只是一个“说明书”它标准的定义了我们在构建一个LLM应用开发时可能会用到的东西。比如说在之前写过的AI文章中介绍的prompt就可以通过Langchain中的PromptTemplate进行格式化 代码语言javascript 复制 prompt Translate the text \ that is delimited by triple backticks \ into a style that is {style}. \ text: {text}当我们调用ChatPromptTemplate进行标准化时 代码语言javascript 复制 from langchain.prompts import ChatPromptTemplate prompt_templateChatPromptTemplate.from_template(prompt) print(prompt_template,ChatPromptTemplate)该prompt就会被格式化成 从上述例子可以直观的看到ChatPromptTemplate可以将prompt中声明的输入变量style和text准确提取出来使prompt更清晰。当然Langchain对于prompt的优化不止这一种方式它还提供了各类其他接口将prompt进一步优化这里只是举例一个较为基础且直观的方法让大家感受一下。 Langchain其实就是在定义多个通用类的规范去优化开发AI应用过程中可能用到的各类技术将它们抽象成多个小元素当我们构建应用时直接将这些元素堆积起来而无需在重复的去研究各元素实现的细枝末节。 二、官方文档Langchain这么长我怎么看 毋庸置疑想要学习Langchain最简单直接的方法就是阅读官方文档先贴一个链接Langchain官方文档 通过文档目录我们可以看到Langchain由6个module组成分别是Model IO、Retrieval、Chains、Memory、Agents和Callbacks。 Model IOAI应用的核心部分其中包括输入、Model和输出。 Retrieval“检索“——该功能与向量数据密切库相关是在向量数据库中搜索与问题相关的文档内容。 Memory为对话形式的模型存储历史对话记录在长对话过程中随时将这些历史对话记录重新加载以保证对话的准确度。 Chains虽然通过Model IO、Retrieval和Memory这三大模块可以初步完成应用搭建但是若想实现一个强大且复杂的应用还是需要将各模块组合起来这时就可以利用Chains将其连接起来从而丰富功能。 Agents它可以通过用户的输入理解用户的意图返回一个特定的动作类型和参数从而自主调用相关的工具来满足用户的需求将应用更加智能化。 Callbacks: 回调机制可以调用链路追踪记录日志帮助开发者更好的调试LLM模型。 六个module具体的关系如下图所示图片来源于网络 好了说到这我们只要一个一个module去攻破最后将他们融会贯通也就成为一名及格的Langchain学习者了。 三、Model IO 这一部分可以说是Langchain的核心部分引用一下之前介绍AI时用过的图介绍了Model IO内部的一些具体实现原理 由上图可以看出我们在利用Model IO的时候主要关注的就是输入、处理、输出这三个步骤。Langchain也是根据这一点去实现Model IO这一模块的在这一模块中Langchain针对此模块主要的实现手段为Prompt(输入)、Language model(处理、Output Pasers(输出)Langchain通过一系列的技术手法优化这三步使得其更加的标准化我们也无需再关注每一步骤中的具体实现可以直接通过Langchain提供的API堆积木式的完善我们应用构建贴张官方文档的图可以更清晰的了解。 既然我们无需再关注每一步骤的具体实现所以使用Langchain的Model IO应用时主要关注的就是prompt的构建了。下文将主要介绍Langchain中常用的一些prompt构建方法。 3.1prompt Langchain对于prompt的优化主要是致力于将其优化成为可移植性高的Prompt以便更好的支持各类LLM无需在切换Model时修改Prompt。 通过官方文档可以看到Prompt在Langchain被分成了两大类一类是Prompt template另一类则是Selectors。 Propmpt template:这个其实很好理解就是利用Langchain接口将prompt按照template进行一定格式化针对Prompt进行变量处理以及提示词的组合。 Selectors: 则是指可以根据不同的条件去选择不同的提示词或者在不同的情况下通过Selector选择不同的example去进一步提高Prompt支持能力。 3.1.1模版格式 在prompt中有两种类型的模版格式一是f-string这是十分常见的一类prompt二是jinja2。 f-string 是 Python 3.6 以后版本中引入的一种特性用于在字符串中插入表达式的值。语法简洁直接利用{}花括号包裹变量或者表达式即可执行简单的运算性能较好但是只限用在py中。 代码语言javascript 复制 #使用 Python f 字符串模板 from langchain.prompts import PromptTemplate fstring_template Tell me a {adjective} joke about {content} prompt PromptTemplate.from_template(fstring_template) print(prompt.format(adjectivefunny, contentchickens)) # Output: Tell me a funny joke about chickens.jinja2常被应用于网页开发与 Flask 和 Django 等框架结合使用。它不仅支持变量替换还支持其他的控制结构例如循环和条件语句以及自定义过滤器和宏等高级功能。此外它的可用性范围更广可在多种语境下使用。但与 f-string 不同使用 jinja2 需要安装相应的库。 代码语言javascript 复制 #使用 jinja2 模板 from langchain.prompts import PromptTemplate jinja2_template Tell me a {{ adjective }} joke about {{ content }} prompt PromptTemplate.from_template(jinja2_template, template_formatjinja2) print(prompt.format(adjectivefunny, contentchickens)) # Output: Tell me a funny joke about chickens.总结一下:如果只需要基本的字符串插值和格式化首选f-string 因为它的语法简洁且无需额外依赖。但如果需要更复杂的模板功能例如循环、条件、自定义过滤器等jinja2 更合适。 3.1.1.2Propmpt Template 在prompt template这一部分中需要掌握的几个概念 1️⃣基本提示模版 大多是字符串或者是由对话组成的数组对象。 对于创建字符串类型的prompt要了解两个概念一是input_variables 属性它表示的是prompt所需要输入的变量。二是format即通过input_variables将prompt格式化。比如利用PromptTemplate进行格式化。 代码语言javascript 复制 from langchain.prompts import PromptTemplate #用于 PromptTemplate 为字符串提示创建模板。 #默认情况下 PromptTemplate 使用 Python 的 str.format 语法进行模板化;但是可以使用其他模板语法例如 jinja2 prompt_template PromptTemplate.from_template(Tell me a {adjective} joke about {content}.) print(prompt_template.format(adjectivefunny, contentchickens))Output如下该例子就是将两个input_variables分别设置为funny和chickens然后利用format分别进行赋值。若在template中声明了input_variables利用format进行格式化时就一定要赋值否则会报错当在template中未设置input_variables则会自动忽略。 代码语言javascript 复制 Tell me a funny joke about chickens.当对对话类型的prompt进行格式化的时候可以利用ChatPromptTemplate进行 代码语言javascript 复制 #ChatPromptTemplate.from_messages 接受各种消息表示形式。 template ChatPromptTemplate.from_messages([ (system, You are a helpful AI bot. Your name is {name}.), (human, Hello, how are you doing?), (ai, Im doing well, thanks!), (human, {user_input}), ]) messages template.format_messages( nameBob, user_inputWhat is your name? ) print(messages)Output如下可以看到ChatPromptTemplate会根据role对每一句进行标准格式化。除了此类方法也可以直接指定身份模块如SystemMessage, HumanMessagePromptTemplate进行格式化这里不再赘述。 代码语言javascript 复制 [(system, You are a helpful AI bot. Your name is Bob.),(human, Hello, how are you doing?),(ai, Im doing well, thanks!),(human, What is your name?)]2️⃣部分提示词模版 在生成prompt前就已经提前初始化部分的提示词实际进一步导入模版的时候只导入除已初始化的变量即可。通常部分提示词模版会被用在全局设置上如下示例在正式format前设定foo值为foo这样在生成最终prompt的时候只需要指定bar的值即可。有两种方法去指定部分提示词 代码语言javascript 复制 from langchain.prompts import PromptTemplate prompt PromptTemplate(template{foo}{bar}, input_variables[foo, bar])# 可以使用 PromptTemplate.partial() 方法创建部分提示模板。 partial_prompt prompt.partial(foofoo) print(partial_prompt.format(barbaz))#也可以只使用分部变量初始化提示。 prompt PromptTemplate(template{foo}{bar}, input_variables[bar], partial_variables{foo: foo}) print(prompt.format(barbaz))Output如下: 代码语言javascript 复制 foobaz foobaz此外我们也可以将函数的最终值作为prompt的一部分进行返回如下例子如果想在prompt中实时展示当下时间我们可以直接声明一个函数用来返回当下时间并最终将该函数拼接到prompt中去 代码语言javascript 复制 from datetime import datetimedef _get_datetime():now datetime.now()return now.strftime(%m/%d/%Y, %H:%M:%S)prompt PromptTemplate(templateTell me a {adjective} joke about the day {date},input_variables[adjective, date] ) partial_prompt prompt.partial(date_get_datetime) print(partial_prompt.format(adjectivefunny))# 除上述方法部分函数声明和普通的prompt一样也可以直接用partial_variables去声明 prompt PromptTemplate( templateTell me a {adjective} joke about the day {date}, input_variables[adjective], partial_variables{date: _get_datetime})Output如下: 代码语言javascript 复制 Tell me a funny joke about the day 12/08/2022, 16:25:303️⃣组成提示词模版 可以通过PromptTemplate.compose()方法将多个提示词组合到一起。如下示例生成了full_prompt和introduction_prompt进行近一步组合。 代码语言javascript 复制 from langchain.prompts.pipeline import PipelinePromptTemplate from langchain.prompts.prompt import PromptTemplatefull_template {introduction} {example}full_prompt PromptTemplate.from_template(full_template)introduction_template You are impersonating Elon Musk. introduction_prompt PromptTemplate.from_template(introduction_template)example_template Heres an example of an interaction example_prompt PromptTemplate.from_template(example_template)input_prompts [(introduction, introduction_prompt), (example, example_prompt),]pipeline_prompt PipelinePromptTemplate(final_promptfull_prompt, pipeline_promptsinput_prompts)4️⃣自定义提示模版 在创建prompt时我们也可以按照自己的需求去创建自定义的提示模版。官方文档举了一个生成给定名称的函数的英语解释例子在这个例子中函数名称作为输入并设置提示格式以提供函数的源代码 代码语言javascript 复制 import inspect# 该函数将返回给定其名称的函数的源代码。 inspect作用就是获取源代码 def get_source_code(function_name):# Get the source code of the functionreturn inspect.getsource(function_name)# 测试函数 def test():return 1 1from langchain.prompts import StringPromptTemplate from pydantic import BaseModel, validator# 初始化字符串prompt PROMPT \ 提供一个函数名和源代码并给出函数的相应解释 函数名: {function_name} 源代码: {source_code} 解释: class FunctionExplainerPromptTemplate(StringPromptTemplate, BaseModel):一个自定义提示模板以函数名作为输入并格式化提示模板以提供函数的源代码。 validator(input_variables)def validate_input_variables(cls, v):验证输入变量是否正确。if len(v) ! 1 or function_name not in v:raise ValueError(函数名必须是唯一的输入变量。)return vdef format(self, **kwargs) - str:# 获取源代码source_code get_source_code(kwargs[function_name])# 源代码名字提供给promptprompt PROMPT.format(function_namekwargs[function_name].__name__, source_codesource_code)return promptdef _prompt_type(self):return function-explainerFunctionExplainerPromptTemplate接收两个变量一个是prompt另一个则是传入需要用到的model该class下面的validate_input_variables用来验证输入量format函数用来输出格式化后的prompt. 代码语言javascript 复制 #初始化prompt实例 fn_explainer FunctionExplainerPromptTemplate(input_variables[function_name])# 定义函数 test_add def test_add():return 1 1# Generate a prompt for the function test_add prompt_1 fn_explainer.format(function_nametest_add) print(prompt_1)Output如下 5️⃣少量提示模版 在构建prompt时可以通过构建一个少量示例列表去进一步格式化prompt每一个示例表都的结构都为字典其中键是输入变量值是输入变量的值。该过程通常先利用PromptTemplate将示例格式化成为字符串然后创建一个FewShotPromptTemplate对象用来接收few-shot的示例。官方文档中举例 代码语言javascript 复制 from langchain.prompts.example_selector import SemanticSimilarityExampleSelector from langchain.vectorstores import Chroma from langchain.embeddings import OpenAIEmbeddings from langchain.prompts import FewShotPromptTemplate, PromptTemplateexamples [{question: Who lived longer, Muhammad Ali or Alan Turing?,answer:Are follow up questions needed here: Yes.Follow up: How old was Muhammad Ali when he died?Intermediate answer: Muhammad Ali was 74 years old when he died.Follow up: How old was Alan Turing when he died?Intermediate answer: Alan Turing was 41 years old when he died.So the final answer is: Muhammad Ali},{question: When was the founder of craigslist born?,answer:Are follow up questions needed here: Yes.Follow up: Who was the founder of craigslist?Intermediate answer: Craigslist was founded by Craig Newmark.Follow up: When was Craig Newmark born?Intermediate answer: Craig Newmark was born on December 6, 1952.So the final answer is: December 6, 1952},{question: Who was the maternal grandfather of George Washington?,answer:Are follow up questions needed here: Yes.Follow up: Who was the mother of George Washington?Intermediate answer: The mother of George Washington was Mary Ball Washington.Follow up: Who was the father of Mary Ball Washington?Intermediate answer: The father of Mary Ball Washington was Joseph Ball.So the final answer is: Joseph Ball},{question: Are both the directors of Jaws and Casino Royale from the same country?,answer:Are follow up questions needed here: Yes.Follow up: Who is the director of Jaws?Intermediate Answer: The director of Jaws is Steven Spielberg.Follow up: Where is Steven Spielberg from?Intermediate Answer: The United States.Follow up: Who is the director of Casino Royale?Intermediate Answer: The director of Casino Royale is Martin Campbell.Follow up: Where is Martin Campbell from?Intermediate Answer: New Zealand.So the final answer is: No} ]# 配置一个格式化程序该格式化程序将prompt格式化为字符串。此格式化程序应该是一个 PromptTemplate 对象。 example_prompt PromptTemplate(input_variables[question, answer], templateQuestion: {question}\n{answer}) print(example_prompt.format(**examples[0]))# 创建一个选择器来选择最相似的例子 example_selector SemanticSimilarityExampleSelector(examplesexamples,vector_storeChroma(),embeddings_modelOpenAIEmbeddings(),example_promptexample_prompt )# 最后用FewShotPromptTemplate 来创建一个提示词模板该模板将输入变量作为输入并将其格式化为包含示例的提示词。 prompt FewShotPromptTemplate(example_selectorexample_selector,example_promptexample_prompt,suffixQuestion: {input},input_variables[input] ) print(prompt)除了上述普通的字符串模版聊天模版中也可以采用此类方式构建一个带例子的聊天提示词模版 代码语言javascript 复制 from langchain.prompts import ChatPromptTemplate, FewShotChatMessagePromptTemplate# 这是一个聊天提示词模板它将输入变量作为输入并将其格式化为包含示例的提示词。 examples [{input: 22, output: 4}, {input: 23, output: 5},]# 提示词模板用于格式化每个单独的示例。 example_prompt ChatPromptTemplate.from_messages([(human, {input}),(ai, {output}),])few_shot_prompt FewShotChatMessagePromptTemplate(example_promptexample_prompt,examplesexamples)print(few_shot_prompt.format())6️⃣独立化prompt 为了便于共享、存储和加强对prompt的版本控制可以将想要设定prompt所支持的格式保存为JSON或者YAML格式文件。也可以直接将待格式化的prompt单独存储于一个文件中通过格式化文件指定相应路径以更方便用户加载任何类型的提示信息。 创建json文件 代码语言javascript 复制 {_type: prompt,input_variables: [adjective, content],template: Tell me a {adjective} joke about {content}. }主文件代码 代码语言javascript 复制 from langchain.prompts import load_promptprompt load_prompt(./simple_prompt.json) print(prompt.format(adjectivefunny, contentchickens))Output如下 Tell me a funny joke about chickens. 这里是直接在json文件中指定template语句除此之外也可以将template单独抽离出来然后在json文件中指定template语句所在的文件路径以实现更好的区域化方便管理prompt。 创建json文件 代码语言javascript 复制 {_type: prompt,input_variables: [adjective, content],template_path: ./simple_template.txt }simple_template.txt 代码语言javascript 复制 Tell me a {adjective} joke about {content}.其余部分代码同第一部分介绍最后的输出结果也是一致的。 3.1.1.3Selector 在few shot模块当我们列举一系列示例值但不进一步指定返回值就会返回所有的prompt示例在实际开发中我们可以使用自定义选择器来选择例子。例如想要返回一个和新输入的内容最为近似的prompt这时候就可以去选择与输入最为相似的例子。这里的底层逻辑是利用了SemanticSimilarityExampleSelector这个例子选择器和向量相似度的计算(openAIEmbeddings)以及利用chroma进行数据存储代码如下 代码语言javascript 复制 from langchain.prompts.example_selector import SemanticSimilarityExampleSelector from langchain.vectorstores import Chroma from langchain.embeddings import OpenAIEmbeddingsexample_selector SemanticSimilarityExampleSelector.from_examples(# 可选的示例列表。examples,# 用于生成嵌入的嵌入类这些嵌入用于测量语义相似性。OpenAIEmbeddings(),# 用于存储嵌入并进行相似性搜索的 VectorStore 类。Chroma,# 要生成的示例数。k1)然后我们去输入一条想要构建的prompt遍历整个示例列表找到最为合适的example。 代码语言javascript 复制 # 选择与输入最相似的示例。 question Who was the father of Mary Ball Washington? selected_examples example_selector.select_examples({question: question}) print(fExamples most similar to the input: {question}) for example in selected_examples:print(\n)for k, v in example.items():print(f{k}: {v})此时就可以返回一个最相似的例子。接下来我们可以重新重复few shot的步骤利用FewShotPromptTemplate去创建一个提示词模版。 对于聊天类型的few shot的prompt我们也可以采用例子选择器进行格式化 代码语言javascript 复制 examples [{input: 22, output: 4},{input: 23, output: 5},{input: 24, output: 6},{input: What did the cow say to the moon?, output: nothing at all},{input: Write me a poem about the moon,output: One for the moon, and one for me, who are we to talk about the moon?,}, ]# 由于我们使用向量存储来根据语义相似性选择示例因此我们需要首先填充存储。 to_vectorize [ .join(example.values()) for example in examples]# 这里就单纯理解为将value对应的值提取出来进行格式化即可。# 创建向量库后可以创建 example_selector 以表示返回的相似向量的个数 # 注意您需要先创建一个向量存储库例如vectorstore ...并填充它然后将其传递给 SemanticSimilarityExampleSelector。 example_selector SemanticSimilarityExampleSelector(vectorstorevectorstore, k2)# 提示词模板将通过将输入传递给 select_examples 方法来加载示例 example_selector.select_examples({input: horse})此时就可以返回两个个最相似的例子。接下来我们可以重复few shot的步骤 利用FewShotChatPromptTemplate去创建一个提示词模版。 上文中介绍了在利用Langchain进行应用开发时所常用的构建prompt方式无论哪种方式其最终目的都是为了更方便的去构建prompt并尽可能的增加其复用性。Langchain提供的prompt相关工具远不止上文这些在了解了基础能力后可以进一步查阅官方文档找到最适合项目特点的工具进行prompt格式化。 3.1.2LLM 上除了上文中的promptLLM作为langchain中的核心内容也是我们需要花心思去了解学习的不过还是那句话应用层的开发实际上无需到模型底层原理了解的十分透彻我们更应该关注的是llm的调用形式Langchain作为一个“工具”它并没有提供自己的LLM而是提供了一个接口用于与许多不同类型的LLM进行交互比如耳熟能详的openai、huggingface或者是cohere等都可以通过langchain快速调用。 1.单个调用直接调用Model对象传入一串字符串然后直接返回输出值以openAI为例 代码语言javascript 复制 from langchain.llms import OpenAI llm OpenAI() print(llm(你是谁))2.批量调用通过generate可以对字符串列表进行批量应用Model使输出更加丰富且完整。 代码语言javascript 复制 llm_result llm.generate([给我背诵一首古诗, 给我讲个100字小故事]*10)这时的llm_result会生成一个键为generations的数组这个数组长度为20项第一项为古诗、第二项为故事、第三项又为古诗以此规则排列… 3.异步接口asyncio库为LLM提供异步支持目前支持的LLM为OpenAI、PromptLayerOpenAI、ChatOpenAI 、Anthropic 和 Cohere 受支持。 可以使用agenerate 异步调用 OpenAI LLM。 在代码编写中如果用了科学上网/魔法以openAI为例在异步调用之前则需要预先将openai的proxy设置成为本地代理这步很重要若不设置后续会有报错 代码语言javascript 复制 import os import openai import asyncio from langchain.llms import OpenAI# 设置代理 openai.proxy os.getenv(https_proxy)# 定义一个同步方式生成文本的函数 def generate_serially():llm OpenAI(temperature0.9) # 创建OpenAI对象并设置temperature参数为0.9for _ in range(10): # 循环10次resp llm.generate([Hello, how are you?]) # 调用generate方法生成文本print(resp.generations[0][0].text) # 打印生成的文本# 定义一个异步生成文本的函数 async def async_generate(llm):resp await llm.agenerate([Hello, how are you?]) # 异步调用agenerate方法生成文本print(resp.generations[0][0].text) # 打印生成的文本# 定义一个并发异步方式生成文本的函数 async def generate_concurrently():llm OpenAI(temperature0.9) # 创建OpenAI对象并设置temperature参数为0.9tasks [async_generate(llm) for _ in range(10)] # 创建10个异步任务await asyncio.gather(*tasks) # 使用asyncio.gather等待所有异步任务完成可以用time库去检查运行时间利用同步调用耗时大概为12s异步耗时仅有2s。通过这种方式可以大大提速任务执行。 4.自定义大语言模型在开发过程中如果遇到需要调用不同的LLM时可以通过自定义LLM实现效率的提高。自定义LLM时必须要实现的是_call方法通过这个方法接受一个字符串、一些可选的索引字并最终返回一个字符串。除了该方法之外还可以选择性生成一些方法用于以字典的模式返回该自定义LLM类的各属性。 代码语言javascript 复制 from langchain.callbacks.manager import CallbackManagerForLLMRun from langchain.llms.base import LLM from typing import Optional, List, Any, Mappingclass CustomLLM(LLM): # 这个类 CustomLLM 继承了 LLM 类并增加了一个新的类变量 n。n: int # 类变量表示一个整数propertydef _llm_type(self) - str:return customdef _call(self,prompt: str, # 输入的提示字符串stop: Optional[List[str]] None, # 可选的停止字符串列表默认为 Nonerun_manager: Optional[CallbackManagerForLLMRun] None, # 可选的回调管理器默认为 None**kwargs: Any,) - str:# 如果 stop 参数不为 None则抛出 ValueError 异常if stop is not None:raise ValueError(stop kwargs are not permitted.)return prompt[: self.n] # 返回 prompt 字符串的前 n 个字符property # 一个属性装饰器用于获取 _identifying_params 的值def _identifying_params(self) - Mapping[str, Any]:Get the identifying parameters. # 这个方法的文档字符串说明这个方法的功能是获取标识参数return {n: self.n} # 返回一个字典包含 n 的值5.测试大语言模型为了节省我们的成本当写好一串代码进行测试的时候通常情况下我们是不希望去真正调用LLM因为这会消耗token(打工人表示伤不起)贴心的Langchain则提供给我们一个“假的”大语言模型以方便我们进行测试。 代码语言javascript 复制 # 从langchain.llms.fake模块导入FakeListLLM类此类可能用于模拟或伪造某种行为 from langchain.llms.fake import FakeListLLM from langchain.agents import load_tools from langchain.agents import initialize_agent from langchain.agents import AgentType# 调用load_tools函数加载python_repl的工具 tools load_tools([python_repl]) # 定义一个响应列表这些响应可能是模拟LLM的预期响应 responses [Action: Python REPL\nAction Input: print(2 2), Final Answer: 4] # 使用上面定义的responses初始化一个FakeListLLM对象 llm FakeListLLM(responsesresponses) # 调用initialize_agent函数使用上面的tools和llm以及指定的代理类型和verbose参数来初始化一个代理 agent initialize_agent(tools, llm, agentAgentType.ZERO_SHOT_REACT_DESCRIPTION, verboseTrue ) # 调用代理的run方法传递字符串whats 2 2作为输入询问代理2加2的结果 agent.run(whats 2 2)与模拟llm同理langchain也提供了一个伪类去模拟人类回复该功能依赖于wikipedia所以模拟前需要install一下这个库并且需要设置proxy。这里同fakellm需要依赖agent的三个类此外它还依赖下面的库 代码语言javascript 复制 # 从langchain.llms.human模块导入HumanInputLLM类此类可能允许人类输入或交互来模拟LLM的行为 from langchain.llms.human import HumanInputLLM from langchain.agents import load_tools from langchain.agents import initialize_agent from langchain.agents import AgentType# 调用load_tools函数加载名为wikipedia的工具 tools load_tools([wikipedia])# 初始化一个HumanInputLLM对象其中prompt_func是一个函数用于打印提示信息 llm HumanInputLLM(prompt_funclambda prompt: print(f\nPROMPT\n{prompt}\nEND OF PROMPT)) # 调用initialize_agent函数使用上面的tools和llm以及指定的代理类型和verbose参数来初始化一个代理 agent initialize_agent(tools, llm, agentAgentType.ZERO_SHOT_REACT_DESCRIPTION, verboseTrue) # 调用代理的run方法传递字符串What is Bocchi the Rock!?作为输入询问代理关于Bocchi the Rock!的信息 agent.run(What is Bocchi the Rock!?)6.缓存大语言模型和测试大语言模型具有一样效果的是缓存大语言模型通过缓存层可以尽可能的减少API的调用次数从而节省费用。在Langchain中设置缓存分为两种情况一是在内存中设置缓存二是在数据中设置缓存。存储在内存中加载速度较快但是占用资源并且在关机之后将不再被缓存在内存中设置缓存示例如下 代码语言javascript 复制 from langchain.cache import SQLiteCache import langchain from langchain.llms import OpenAI import timelangchain.llm_cache SQLiteCache(database_path.langchain.db)llm OpenAI(model_nametext-davinci-002, n2, best_of2)start_time time.time() # 记录开始时间 print(llm.predict(用中文讲个笑话)) end_time time.time() # 记录结束时间 elapsed_time end_time - start_time # 计算总时间 print(fPredict method took {elapsed_time:.4f} seconds to execute.)这里的时间大概花费1s 因为被问题放在了内存里所以在下次调用时几乎不会再耗费时间。 除了存储在内存中进行缓存也可以存储在数据库中进行缓存当开发企业级应用的时候通常都会选择存储在数据库中不过这种方式的加载速度相较于将缓存存储在内存中更慢一些不过好处是不占电脑资源并且存储记录并不会随着关机消失。 代码语言javascript 复制 from langchain.cache import SQLiteCache import langchain from langchain.llms import OpenAI import timelangchain.llm_cache SQLiteCache(database_path.langchain.db)llm OpenAI(model_nametext-davinci-002, n2, best_of2)start_time time.time() # 记录开始时间 print(llm.predict(用中文讲个笑话)) end_time time.time() # 记录结束时间 elapsed_time end_time - start_time # 计算总时间 print(fPredict method took {elapsed_time:.4f} seconds to execute.)7.跟踪token使用情况仅限model为openAI: 代码语言javascript 复制 from langchain.llms import OpenAI from langchain.callbacks import get_openai_callbackllm OpenAI(model_nametext-davinci-002, n2, best_of2, cacheNone)with get_openai_callback() as cb:result llm(讲个笑话)print(cb)上述代码直接利用get_openai_callback即可完成对于单条的提问时token的记录此外对于有多个步骤的链或者agentlangchain也可以追踪到各步骤所耗费的token。 代码语言javascript 复制 from langchain.agents import load_tools from langchain.agents import initialize_agent from langchain.agents import AgentType from langchain.llms import OpenAI from langchain.callbacks import get_openai_callbackllm OpenAI(temperature0) tools load_tools([llm-math], llmllm) agent initialize_agent(tools, llm, agentAgentType.ZERO_SHOT_REACT_DESCRIPTION, verboseTrue )with get_openai_callback() as cb:response agent.run(王菲现在的年龄是多少)print(fTotal Tokens: {cb.total_tokens})print(fPrompt Tokens: {cb.prompt_tokens})print(fCompletion Tokens: {cb.completion_tokens})print(fTotal Cost (USD): ${cb.total_cost})8.序列化配置大语言模型Langchain也提供一种能力用来保存LLM在训练时使用的各类系数比如template、 model_name等。这类系数通常会被保存在json或者yaml文件中以json文件为例配置如下系数然后利用load_llm方法即可导入 代码语言javascript 复制 from langchain.llms.loading import load_llmllm load_llm(llm.json){model_name: text-davinci-003,temperature: 0.7,max_tokens: 256,top_p: 1.0,frequency_penalty: 0.0,presence_penalty: 0.0,n: 1,best_of: 1,request_timeout: None,_type: openai }亦或者在配置好大模型参数之后直接利用save方法即可直接保存配置到指定文件中。 llm.save(“llmsave.json”) 9.流式处理大语言模型的响应流式处理意味着在接收到第一个数据块后就立即开始处理而不需要等待整个数据包传输完毕。这种概念应用在LLM中则可达到生成响应时就立刻向用户展示此下的响应或者在生成响应时处理响应也就是我们现在看到的和ai对话时逐字输出的效果可以看到实现还是较为方便的只需要直接调用StreamingStdOutCallbackHandler作为callback即可。 代码语言javascript 复制 from langchain.llms import OpenAI from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandlerllm OpenAI(streamingTrue, callbacks[StreamingStdOutCallbackHandler()], temperature0) resp llm(Write me a song about sparkling water.)可以看到实现还是较为方便的只需要直接调用StreamingStdOutCallbackHandler作为callback即可。 3.1.3OutputParsers Model返回的内容通常都是字符串的模式但在实际开发过程中往往希望model可以返回更直观的内容Langchain提供的输出解析器则将派上用场。在实现一个输出解析器的过程中需要实现两种方法1️⃣获取格式指令返回一个字符串的方法其中包含有关如何格式化语言模型输出的说明。2️⃣Parse一种接收字符串假设是来自语言模型的响应并将其解析为某种结构的方法。 1.列表解析器利用此解析器可以输出一个用逗号分割的列表。 代码语言javascript 复制 from langchain.output_parsers import CommaSeparatedListOutputParser from langchain.prompts import PromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate from langchain.llms import OpenAI from langchain.chat_models import ChatOpenAIoutput_parser CommaSeparatedListOutputParser() format_instructions output_parser.get_format_instructions()prompt PromptTemplate(templateList five {subject}.\n{format_instructions},input_variables[subject],partial_variables{format_instructions: format_instructions} )model OpenAI(temperature0)_input prompt.format(subject冰淇淋口味) output model(_input)output_parser.parse(output)2.日期解析器利用此解析器可以直接将LLM输出解析为日期时间格式。 代码语言javascript 复制 from langchain.prompts import PromptTemplate from langchain.output_parsers import DatetimeOutputParser from langchain.chains import LLMChain from langchain.llms import OpenAIoutput_parser DatetimeOutputParser()template 回答用户的问题: {question} {format_instructions}prompt PromptTemplate.from_template(template,partial_variables{format_instructions: output_parser.get_format_instructions()}, )chain LLMChain(promptprompt, llmOpenAI())output chain.run(bitcoin是什么时候成立的用英文格式输出时间)3.枚举解析器 代码语言javascript 复制 from langchain.output_parsers.enum import EnumOutputParser from enum import Enumclass Colors(Enum):RED redGREEN greenBLUE blueparser EnumOutputParser(enumColors)4.自动修复解析器这类解析器是一种嵌套的形式如果第一个输出解析器出现错误就会直接调用另一个一修复错误 代码语言javascript 复制 # 导入所需的库和模块 from langchain.prompts import PromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate from langchain.llms import OpenAI from langchain.chat_models import ChatOpenAI from langchain.output_parsers import PydanticOutputParser from pydantic import BaseModel, Field, validator from typing import List# 定义一个表示演员的数据结构包括他们的名字和他们出演的电影列表 class Actor(BaseModel):name: str Field(descriptionname of an actor) # 演员的名字film_names: List[str] Field(descriptionlist of names of films they starred in) # 他们出演的电影列表# 定义一个查询用于提示生成随机演员的电影作品列表 actor_query Generate the filmography for a random actor.# 使用Actor模型初始化解析器 parser PydanticOutputParser(pydantic_objectActor)# 定义一个格式错误的字符串数据 misformatted {name: Tom Hanks, film_names: [Forrest Gump]}# 使用解析器尝试解析上述数据 try:parsed_data parser.parse(misformatted) except Exception as e:print(fError: {e})parser.parse(misformatted) 格式错误的原因是因为json文件需要双引号进行标记但是这里用了单引号此时利用该解析器进行解析就会出现报错但是此时可以利用RetryWithErrorOutputParser进行修复错误则会正常输出不报错。 代码语言javascript 复制 from langchain.output_parsers import RetryWithErrorOutputParser from langchain.llms import OpenAIretry_parser RetryWithErrorOutputParser.from_llm(parserparser, llmOpenAI(temperature0)) retry_parser.parse_with_prompt(bad_response, prompt_value)这里的“Parse_with_prompt”一种方法它接受一个字符串假设是来自语言模型的响应和一个提示假设是生成此类响应的提示并将其解析为某种结构。提示主要在 OutputParser 想要以某种方式重试或修复输出时提供并且需要来自提示的信息才能执行此操作。 四、Retrieval Retrieval直接汉译过来即”检索“。该功能经常被应用于构建一个“私人的知识库”构建过程更多的是将外部数据存储到知识库中。细化这一模块的主要职能有四部分其包括数据的获取、整理、存储和查询。如下图 首先在该过程中可以从本地/网站/文件等资源库去获取数据当数据量较小时我们可以直接进行存储但当数据量较大的时候则需要对其进行一定的切片切分时可以按照数据类型进行切片处理比如针对文本类数据可以直接按照字符、段落进行切片代码类数据则需要进一步细分以保证代码的功能性此外除了按照数据类型进行切片处理也可以直接根据token进行切片。而后利用Vector Stores进行向量存储其中Embedding完成的就是数据的向量化虽然这一能力往往被嵌套至大模型中但是我们也要清楚并不是所有的模型都能直接支持文本向量化这一能力。除此之外的memory、self-hosted以及baas则是指向量存储的三种载体形式可以选择直接存储于内存中也可以选择存储上云。最后则利用这些向量化数据进行检索检索形式可以是直接按照向量相似度去匹配相似内容也可以直接网络或者借用其他服务实现检索以及数据的返回。 4.1向量数据库 4.1.1基本概念 从上文中我们可以发现对于retrievers来说向量数据库发挥着很大的作用它不仅实现向量的存储也可以通过相似度实现向量的检索但是向量数据库到底是什么呢它和普通的数据库有着怎样的区别呢相信还是有很多同学和我一样有一点点疑惑所以在介绍langchain在此module方面的能力前先介绍一下向量数据库以及它在LLM中所发挥的作用。 我们在对一个事物进行描述的时候通常会根据事物的各方面特征进行表述。设想这样一个场景假设你是一名摄影师拍了大量的照片。为了方便管理和查找你决定将这些照片存储到一个数据库中。传统的关系型数据库如 MySQL、PostgreSQL 等可以帮助你存储照片的元数据比如拍摄时间、地点、相机型号等。但是当你想要根据照片的内容如颜色、纹理、物体等进行搜索时传统数据库可能无法满足你的需求因为它们通常以数据表的形式存储数据并使用查询语句进行精确搜索。但向量包含了大量信息使用查询语句很难精确地找到唯一的向量。 那么此时向量数据库就可以派上用场。我们可以构建一个多维的空间使得每张照片特征都存在于这个空间内并用已有的维度进行表示比如时间、地点、相机型号、颜色…此照片的信息将作为一个点存储于其中。以此类推即可在该空间中构建出无数的点而后我们将这些点与空间坐标轴的原点相连接就成为了一条条向量当这些点变为向量之后即可利用向量的计算进一步获取更多的信息。当要进行照片的检索时也会变得更容易更快捷。但在向量数据库中进行检索时检索并不是唯一的而是查询和目标向量最为相似的一些向量具有模糊性。 那么我们可以延伸思考一下只要对图片、视频、商品等素材进行向量化就可以实现以图搜图、视频相关推荐、相似宝贝推荐等功能那应用在LLM中小则可直接实现相关问题提示大则我们完全可以利用此特性去历史对话记录中找到一些最类似的对话然后重新喂给大模型这将极大的提高大模型的输出结果的准确性。 为更好的了解向量数据库接下来将继续介绍向量的几种检索方式以对向量数据库有一个更深度的了解。 4.1.2存储方式 因为每一个向量所记录的信息量都是比较多的所以自然而然其所占内存也是很大的举个例子如果我们的一个向量维度是256维的那么该向量所占用的内存大小就是256*32/81024字节若数据库中共计一千万个向量则所占内存为10240000000字节也就是9.54GB已经是一个很庞大的数目了而在实际开发中这个规模往往更大因此解决向量数据库的内存占用问题是重中之重的。我们往往会对每个向量进行压缩从而缩小其内存占用。常常利用乘积量化方法 乘积量化该思想将高维向量分解为多个子向量。例如将一个D维向量分解为m个子向量每个子向量的维度为D/m。然后对每个子向量进行量化。对于每个子向量空间使用聚类算法将子向量分为K个簇并将簇中心作为量化值。然后用子向量在簇中的索引来表示原始子向量。这样每个子向量可以用一个整数量化索引来表示。最后将量化索引组合起来表示原始高维向量。对于一个D维向量可以用m个整数来表示其中每个整数对应一个子向量的量化索引。此外这类方法不仅可以用于优化存储向量也可以用于优化检索。 4.1.3检索方式 通过上段文字的描述我们不难发现向量检索过程可以抽象化为“最近邻问题“对应的算法就是最近邻搜索算法具体有如下几种 1.暴力搜索依次比较向量数据库中所有的的向量与目标向量的相似度然后找出相似度最高一个或一些向量这样得到的结果质量是极高的但这对于数据量庞大的数据库来说无疑是十分耗时的。 2.聚类搜索这类算法首先初始化K个聚类中心将数据对象分组成若干个类别或簇cluster。其主要目的是根据数据的相似性或距离度量来对数据进行分组然后根据所选的聚类算法通过迭代计算来更新聚类结果。例如在K-means算法中需要不断更新簇中心并将数据对象分配给最近的簇中心在DBSCAN算法中需要根据密度可达性来扩展簇并合并相邻的簇。最后设置一个收敛条件用于判断聚类过程是否结束。收敛条件可以是迭代次数、簇中心变化幅度等。当满足收敛条件时聚类过程结束。这样的搜索效率大大提高但是不可避免会出现遗漏的情况。 3.位置敏感哈希此算法首先选择一组位置敏感哈希函数该函数需要满足一个特性对于相似的数据点它们的哈希值发生冲突的概率较高对于不相似的数据点它们的哈希值发生冲突的概率较低。而后利用该函数对数据集中的每个数据点进行哈希。将具有相同哈希值的数据点存储在相同的哈希桶中。在检索过程中对于给定的查询点首先使用LSH函数计算其哈希值然后在相应的哈希桶中搜索相似的数据点。最后根据需要可以在搜索到的候选数据点中进一步计算相似度以找到最近邻。 4.分层级的导航小世界算法这是一种基于图的近似最近邻搜索方法适用于大规模高维数据集。其核心思想是将数据点组织成一个分层结构的图使得在高层次上可以快速地找到距离查询点较近的候选点然后在低层次逐步细化搜索范围从而加速最近邻搜索过程。 该算法首先创建一个空的多层图结构。每一层都是一个图其中节点表示数据点边表示节点之间的连接关系。最底层包含所有数据点而上层图只包含部分数据点。每个数据点被分配一个随机的层数表示该点在哪些层次的图中出现。然后插入数据点对于每个新插入的数据点首先确定其层数然后从最高层开始将该点插入到相应的图中。插入过程中需要找到该点在每层的最近邻并将它们连接起来。同时还需要更新已有节点的连接关系以保持图的导航性能。其检索过程是首先在最高层的图中找到一个起始点然后逐层向下搜索直到达到底层。在每一层从当前点出发沿着边进行搜索直到找到一个局部最近邻。然后将局部最近邻作为下一层的起始点继续搜索。最后在底层找到的结果则为最终结果。 4.2向量数据库与AI 前文中大概介绍了向量数据库是什么以及向量数据库所依赖的一些实现技术接下来我们来谈论一下向量数据库与大模型之间的关系。为什么说想要用好大模型往往离不开向量数据库呢对于大模型来讲处理的数据格式一般都是非结构化数据如音频、文本、图像…我们以大语言模型为例在喂一份数据给大模型的时候数据首先会被转为向量在上述内容中我们知道如果向量较近那么就表示这两个向量含有的信息更为相似当大量数据不断被喂到大模型中的时候语言模型就会逐渐发现词汇间的语义和语法。当用户进行问答的时候问题输入Model后会基于Transformer架构从每个词出发去找到它与其他词的关系权重找到权重最重的一组搭配这一组就为此次问答的答案了。最后再将这组向量返回回来也就完成了一次问答。当我们把向量数据库接入到AI中我们就可以通过更新向量数据库的数据使得大模型能够不断获取并学习到业界最新的知识而不是将能力局限于预训练的数据中。这种方式要比微调/重新训练大模型的方式节约更多成本。 4.3DataLoaders 为了更好的理解retrieval的功能在上文中先介绍了一下它所依赖的核心概念——向量数据库接下来让我们看一下Langhcain中的retrieval是如何发挥作用的。我们已经知道一般在用户开发LLM应用程序往往会需要使用不在模型训练集中的特定数据去进一步增强大语言模型的能力这种方法被称为检索增强生成RAG。LangChain 提供了一整套工具来实现 RAG 应用程序首先第一步就是进行文档的相应加载即DocumentLoader LangChain提供了多种文档加载器支持从各种不同的来源加载文档例如私有的存储桶或公共网站支持的文档类型也十分丰富如 HTML、PDF 、MarkDown文件等… 1.加载 md文件 代码语言javascript 复制 from langchain.document_loaders import TextLoader # 创建一个TextLoader实例指定要加载的Markdown文件路径 loader TextLoader(./index.md) # 使用load方法加载文件内容并打印 print(loader.load())2.加载csv文件 代码语言javascript 复制 # 导入CSVLoader类 from langchain.document_loaders.csv_loader import CSVLoader# 创建CSVLoader实例指定要加载的CSV文件路径 loader CSVLoader(file_path./index.csv)# 使用load方法加载数据并将其存储在数据变量中 data loader.load()3.自定义 csv 解析和加载 指定csv文件的字段名fieldname即可 代码语言javascript 复制 from langchain.document_loaders.csv_loader import CSVLoader# 创建CSVLoader实例指定要加载的CSV文件路径和CSV参数 loader CSVLoader(file_path./index.csv, csv_args{delimiter: ,,quotechar: ,fieldnames: [title, content] })# 使用load方法加载数据并将其存储在数据变量中 data loader.load()4.可以使用该 source_column 参数指定文件加载的列。 代码语言javascript 复制 from langchain.document_loaders.csv_loader import CSVLoader# 创建CSVLoader实例指定要加载的CSV文件路径和源列名 loader CSVLoader(file_path./index.csv, source_columncontext)# 使用load方法加载数据并将其存储在数据变量中 data loader.load()除了上述的单个文件加载我们也可以批量加载一个文件夹内的所有文件该加载依赖unstructured所以开始前需要pip一下。如加载md文件就pip install “unstructured[md]” 代码语言javascript 复制 # 导入DirectoryLoader类 from langchain.document_loaders import DirectoryLoader# 创建DirectoryLoader实例指定要加载的文件夹路径、要加载的文件类型和是否使用多线程 loader DirectoryLoader(/Users/kyoku/Desktop/LLM/documentstore, glob**/*.md, use_multithreadingTrue)# 使用load方法加载所有文档并将其存储在docs变量中 docs loader.load()# 打印加载的文档数量 print(len(docs))# 导入UnstructuredHTMLLoader类 from langchain.document_loaders import UnstructuredHTMLLoader# 创建UnstructuredHTMLLoader实例指定要加载的HTML文件路径 loader UnstructuredHTMLLoader(./index.html)# 使用load方法加载HTML文件内容并将其存储在data变量中 data loader.load()# 导入BSHTMLLoader类 from langchain.document_loaders import BSHTMLLoader# 创建BSHTMLLoader实例指定要加载的HTML文件路径 loader BSHTMLLoader(./index.html)# 使用load方法加载HTML文件内容并将其存储在data变量中 data loader.load()4.4文本拆分DataTransformers 当文件内容成功加载之后通常会对数据集进行一系列处理以便更好地适应你的应用。比如说可能想把长文档分成小块这样就能更好地放入模型。LangChain 提供了很多现成的文档转换器可以轻松地拆分、组合、过滤文档还能进行其他操作。 虽然上述步骤听起来较为简单但实际上有很多潜在的复杂性。最好的情况是把相关的文本片段放在一起。这种“相关性”可能因文本的类型而有所不同。 Langchain提供了工具RecursiveCharacterTextSplitter用来进行文本的拆分其运行原理为首先尝试用第一个字符进行拆分创建小块。如果有些块太大它就会尝试下一个字符以此类推。默认情况下它会按照 [“\n\n”, “\n”, , “”] 的顺序尝试拆分字符。以下为示例代码 代码语言javascript 复制 # 打开一个文本文件并读取内容 with open(./test.txt) as f:state_of_the_union f.read()# 导入RecursiveCharacterTextSplitter类 from langchain.text_splitter import RecursiveCharacterTextSplitter# 创建RecursiveCharacterTextSplitter实例设置块大小、块重叠、长度函数和是否添加开始索引 text_splitter RecursiveCharacterTextSplitter(chunk_size100,chunk_overlap20,length_functionlen,add_start_indexTrue, )# 使用create_documents方法创建文档并将其存储在texts变量中 texts text_splitter.create_documents([state_of_the_union])从输出结果可以看到其是被拆分成了一个数组的形式。 除了上述的文本拆分代码拆分也经常被应用于llm应用的构建中 代码语言javascript 复制 # 导入所需的类和枚举 from langchain.text_splitter import RecursiveCharacterTextSplitter, Language# 定义一个包含Python代码的字符串 PYTHON_CODE def hello_world():print(Hello, World!)# Call the function hello_world() # 使用from_language方法创建一个针对Python语言的RecursiveCharacterTextSplitter实例 python_splitter RecursiveCharacterTextSplitter.from_language(languageLanguage.PYTHON, chunk_size50, chunk_overlap0 )# 使用create_documents方法创建文档并将其存储在python_docs变量中 python_docs python_splitter.create_documents([PYTHON_CODE])调用特定的拆分器可以保证拆分后的代码逻辑这里我们只要指定不同的Language就可以对不同的语言进行拆分。 4.5向量检索简单应用 在实际开发中我们可以将数据向量化细分为两步一是将数据向量化(向量化工具openai的embeding、huggingface的n3d…)二是将向量化后的数据存储到向量数据库中常见比较好用的免费向量数据库有Meta的faiss、chrome的chromad以及lance。 1.高性能利用 CPU 和 GPU 的并行计算能力实现了高效的向量索引和查询操作。 2.可扩展性支持大规模数据集可以处理数十亿个高维向量的相似性搜索和聚类任务。 3.灵活性提供了多种索引和搜索算法可以根据具体需求选择合适的算法。 4.开源是一个开源项目可以在 GitHub 上找到其源代码和详细文档。 安装相关库 pip install faiss-cpu (显卡好的同学也可以install gpu版本) 准备一个数据集这个数据集包含一段关于信用卡年费收取和提高信用卡额度的咨询对话。客户向客服提出了关于信用卡年费和额度的问题客服则详细解答了客户的疑问 代码语言javascript 复制 text 客户您好我想咨询一下信用卡的问题。\n客服您好欢迎咨询建行信用卡我是客服小李请问有什么问题我可以帮您解答吗\n客户我想了解一下信用卡的年费如何收取\n客服关于信用卡年费的收取我们会在每年的固定日期为您的信用卡收取年费。当然如果您在一年内的消费达到一定金额年费会自动免除。具体的免年费标准请您查看信用卡合同条款或登录我们的网站查询。\n客户好的谢谢。那我还想问一下如何提高信用卡的额度\n客服关于提高信用卡额度您可以通过以下途径操作1. 登录建行信用卡官方网站或手机APP提交在线提额申请2. 拨打我们的客服热线按语音提示进行提额申请3. 您还可以前往附近的建行网点提交提额申请。在您提交申请后我们会根据您的信用状况进行审核审核通过后您的信用卡额度将会相应提高。\n客户明白了非常感谢您的解答。\n客服您太客气了很高兴能够帮到您。如果您还有其他问题请随时联系我们。祝您生活愉快 list_text text.split(\n)from langchain.embeddings.openai import OpenAIEmbeddings from langchain.vectorstores import FAISSdb FAISS.from_texts(list_text, OpenAIEmbeddings())query 信用卡的额度可以提高吗 docs db.similarity_search(query) print(docs[0].page_content)embedding_vector OpenAIEmbeddings().embed_query(query) print(fembedding_vector{embedding_vector}) docs db.similarity_search_by_vector(embedding_vector) print(docs[0].page_content)除了上述直接输出效果最好的结果也可以按照相似度分数进行输出不过这里的规则是分数越低相似度越高。 代码语言javascript 复制 # 使用带分数的相似性搜索 docs_and_scores db.similarity_search_with_score(query)# 打印文档及其相似性分数 for doc, score in docs_and_scores:print(fDocument: {doc.page_content}\nScore: {score}\n)如果每次都要调用embedding无疑太浪费所以最后我们也可以直接将数据库保存起来避免重复调用。 代码语言javascript 复制 # 保存 db.save_local(faiss_index) # 加载 new_db FAISS.load_local(faiss_index, OpenAIEmbeddings())在官网中还介绍了另外两种向量数据库的使用方法这里不再赘述。 五、Memory Memory——存储历史对话信息。该功能主要会执行两步1.输入时从记忆组件中查询相关历史信息拼接历史信息和用户的输入到提示词中传给LLM。2.自动把LLM返回的内容存储到记忆组件用于下次查询。 5.1Memory的基本实现原理 Memory——存储历史对话信息。该功能主要会执行两步 1.输入时从记忆组件中查询相关历史信息拼接历史信息和用户的输入到提示词中传给LLM。 2.自动把LLM返回的内容存储到记忆组件用于下次查询。 不过GPT目前就有这个功能了它已经可以进行多轮对话了为何我们还要把这个功能拿出来细说呢在之前介绍prompt的文章中介绍过在进行多轮对话时我们会把历史对话内容不断的push到prompt数组中通俗来讲就是将所有的聊天记录都作为prompt了以存储的形式实现了大语言模型的“记忆”功能而大语言模型本身是无状态的这种方式无疑会较为浪费token所以开发者不得不将注意力聚焦于**如何在保证大语言模型功能的基础上尽可能的减少token的使用**Memory这个组件也就随之诞生。po一张Memory官网的图 从上图可以看到Memory实现思路还是蛮简单的就是存储查询存储的过程我们无需过度思考无非就是存到内存/数据库但是读取的过程还是值得我们探讨一番为什么这么说呢在上文中已经知道memory的目的其实就是要在保证大语言模型能力的前提下尽可能的减少token消耗所以我们不能把所有的数据一起丢给大语言模型这就失去了memory的意义了不是吗目前memory常利用以下几种查询策略 1.将会话直接作为prompt喂回给大模型背景可以称之为buffer。 2.将所有历史消息丢给模型生成一份摘要再将摘要作为prompt背景可以称之为summary。 3.利用之前提及的向量数据库查询相似历史信息作为prompt背景可以称之为vector。 5.2Memory的使用方式 Memory这一功能的使用方式还是较为简单的本节将会按照memory的三大分类依次介绍memory中会被高频使用到的一些工具函数。 5.2.1Buffer 1️⃣ConversationBufferMemory 先举例一个最简单的使用方法——直接将内容存储到buffer无论是单次或是多次存储其对话内容都会被存储到一个memory 代码语言javascript 复制 memory ConversationBufferMemory() memory.save_context({input: 你好我是人类}, {output: 你好我是AI助手})memory.save_context({input: 很开心认识你}, {output: 我也是})存储后可直接输出存储内容 代码语言javascript 复制 print(memory.load_memory_variables({})) # {history: Human: 你好我是人类\nAI: 你好我是AI助手\nHuman: 很开心认识你\nAI: 我也是}2️⃣ConversationBufferWindowMemory ConversationBufferMemory无疑是很简单方便的但是可以试想一下当我们与大语言模型进行多次对话时直接利用buffer存储的话所占内存量是十分大的并且消耗的token是十分多的这时通过ConversationBufferWindowMemory进行窗口缓存的方式就可以解决上述问题。其核心思想就是保留一个窗口大小的对话其内容只是最近的N次对话。在这个工具函数中可以利用k参数来声明保留的对话记忆比如k1时上述对话内容输出结果就会发生相应的改变 代码语言javascript 复制 memory ConversationBufferWindowMemory(k1) memory.save_context({input: 你好我是人类}, {output: 你好我是AI助手}) memory.save_context({input: 很开心认识你}, {output: 我也是})只保存了最近的k条记录 代码语言javascript 复制 print(memory.load_memory_variables({})) # {history: Human: 很开心认识你\nAI: 我也是}通过内置在Langchain中的缓存窗口(BufferWindow)可以将meomory记忆下来。 3️⃣ConversationTokenBufferMemory 除了通过设置对话数量控制memory也可以通过设置token来限制。如果字符数量超出指定数目它会切掉这个对话的早期部分 以保留与最近的交流相对应的字符数量 代码语言javascript 复制 from langchain.chat_models import ChatOpenAI from langchain.memory import ConversationTokenBufferMemory llm ChatOpenAI(temperature0.0) memory ConversationTokenBufferMemory(llmllm,) memory.save_context({input: 春眠不觉晓}, {output: 处处闻啼鸟}) memory.save_context({input: 夜来风雨声}, {output: 花落知多少}) print(memory.load_memory_variables({})) #{history: AI: 花落知多少。}5.2.2Summary 对于buffer方式我们不难发现如果全部保存下来太过浪费截断时无论是按照对话条数还是token都是无法保证即节省内存或token又保证对话质量的所以我们可以对其进行summary ConversationSummaryBufferMemory 在进行总结时最基础的就是ConversationSummaryBufferMemory这个工具函数利用该函数时通过设置token从而在清除历史对话时生成一份对话记录 代码语言javascript 复制 memory ConversationSummaryBufferMemory(llmllm, max_token_limit40, return_messagesTrue) memory.save_context({input: 嗨}, {output: 你好吗}) memory.save_context({input: 没什么特别的你呢}, {output: 我也是})messages memory.chat_memory.messages previous_summary print(memory.predict_new_summary(messages, previous_summary)) # 人类和AI都表示没有做什么特别的事该API通过 predict_new_summary成功的将对话进行了摘要总结。 5.2.3vector 最后来介绍一下vector在memory中的用法通过VectorStoreRetrieverMemory可以将memory存储到Vector数据库中每次调用时就会查找与该记忆关联最高的k个文档并且不会跟踪交互顺序。不过要注意的是在利用VectorStoreRetrieverMemory前我们需要先初始化一个VectorStore免费向量数据库有Meta的faiss、chrome的chromad以及lance以faiss为例 代码语言javascript 复制 import faissfrom langchain.docstore import InMemoryDocstore from langchain.vectorstores import FAISSembedding_size 1536 # Dimensions of the OpenAIEmbeddings index faiss.IndexFlatL2(embedding_size) embedding_fn OpenAIEmbeddings().embed_query vectorstore FAISS(embedding_fn, index, InMemoryDocstore({}), {})初始化好一个数据库之后我们就可以根据该数据库实例化出一个memory 代码语言javascript 复制 # 在实际使用中可以将k 设为更高的值这里使用 k1 来展示 # 向量查找仍然返回语义相关的信息 retriever vectorstore.as_retriever(search_kwargsdict(k1)) memory VectorStoreRetrieverMemory(retrieverretriever)# 当添加到一个代理时内存对象可以保存来自对话或使用的工具的相关信息 memory.save_context({input: 我最喜欢的食物是披萨}, {output: 好的我知道了}) memory.save_context({input: 我最喜欢的运动是足球}, {output: ...}) memory.save_context({input: 我不喜欢凯尔特人队}, {output: 好的}) print(memory.load_memory_variables({prompt: 我应该看什么运动?})[history])这时便会根据向量数据库检索后输出memory结果 代码语言javascript 复制 {history: [{input: 我最喜欢的运动是足球,output: ...}] }这表示在与用户的对话历史中语义上与 “我应该看什么运动?” 最相关的是 “我最喜欢的运动是足球” 这个对话。更复杂一点可以通过conversationchain进行多轮对话 代码语言javascript 复制 llm OpenAI(temperature0) # 可以是任何有效的LLM _DEFAULT_TEMPLATE 以下是一个人类与AI之间的友好对话。AI非常健谈并从其上下文中提供大量具体细节。如果AI不知道问题的答案它会诚实地说不知道。之前对话的相关部分 {history}如果不相关您不需要使用这些信息当前对话 人类{input} AI PROMPT PromptTemplate(input_variables[history, input], template_DEFAULT_TEMPLATE ) conversation_with_summary ConversationChain(llmllm, promptPROMPT,# 我们为测试目的设置了一个非常低的max_token_limit。memorymemory,verboseTrue ) conversation_with_summary.predict(input嗨我叫Perry你好吗) # 输出 Entering new ConversationChain chain... # Prompt after formatting: # ... # Finished chain. # 嗨Perry我很好。你呢# 这里与篮球相关的内容被提及 conversation_with_summary.predict(input我最喜欢的运动是什么) # 输出 Entering new ConversationChain chain... # ... # Finished chain. # 你之前告诉我你最喜欢的运动是足球。# 尽管语言模型是无状态的但由于获取到了相关的记忆它可以“推理”出时间。 # 为记忆和数据加上时间戳通常是有用的以便让代理确定时间相关性 conversation_with_summary.predict(input我的最喜欢的食物是什么) # 输出 Entering new ConversationChain chain... # ... # Finished chain. # 你说你最喜欢的食物是披萨。# 对话中的记忆被自动存储 # 由于这个查询与上面的介绍聊天最匹配 # 代理能够“记住”用户的名字。 conversation_with_summary.predict(input我的名字是什么) # 输出 Entering new ConversationChain chain... # ... # Finished chain. # 你的名字是Perry。conversation_with_summary这个实例使用了一个内存对象memory来存储与用户的对话历史。这使得AI可以在后续的对话中引用先前的上下文从而提供更准确和相关的回答。 在Langchain中memory属于较为简单的一模块小型开发中常常使用summary类型对于大一点的开发来说最常见的就是利用向量数据库进行数据的存储并在ai模型给出输出时到该数据库中检索出相似性最高的内容。 六、Chains 如果把用Langchain构建AI应用的过程比作“积木模型”的搭建与拼接那么Chain可以说是该模型搭建过程中的骨骼部分通过它将各模块快速组合在一起就可以快速搭建一个应用。Chain的使用方式也是通过接口的直接调用在本文中将Chain分为三种类型从简单到复杂依次介绍按照首先以一个简单的示例来直观的感受Chain的作用 6.1 LLMChains: 这种类型的Chain应用起来很简单也可以说是后续要介绍的Chain的基础但其功能是足够强大的。通过LLMChain可以直接将数据、prompt、以及想要应用的Model串到一起以一个简单的例子来感受LLMChain。 代码语言javascript 复制 from langchain import PromptTemplate, OpenAI, LLMChainprompt_template What is a good name for a company that makes {product}?llm OpenAI(temperature0) chain LLMChain(llmllm,promptPromptTemplate.from_template(prompt_template) ) print(chain(colorful socks)) # 输出结果Socktastic!在这个示例中我们首先初始化了一个prompt的字符串模版并初始化大语言模型然后利用Chain将模型运行起来。在「Chain将模型运行起来」这个过程中Chain将会格式化提示词然后将它传递给LLM。回忆一下在之前的ai入门篇中对于每个model的使用我们需要针对这个model去进行一系列初始化、实例化等操作。而用了chain之后我们无需再关注model本身。 6.2 Sequential Chains: 不同于基本的LLMChainSequential chain序列链是由一系列的链组合而成的序列链有两种类型一种是单个输入输出/另一个则是多个输入输出。先来看第一种单个输入输出的示例代码 1.单个输入输出 在这个示例中创建了两条chain并且让第一条chain接收一个虚构剧本的标题输出该剧本的概要作为第二条chain的输入然后生成一个虚构评论。通过sequential chains可以简单的实现这一需求。 第一条chain 代码语言javascript 复制 # This is an LLMChain to write a synopsis given a title of a play. from langchain import PromptTemplate, OpenAI, LLMChainllm OpenAI(temperature.7) template You are a playwright. Given the title of play, it is your job to write a synopsis for that title.Title: {title} Playwright: This is a synopsis for the above play: prompt_template PromptTemplate(input_variables[title], templatetemplate) synopsis_chain LLMChain(llmllm, promptprompt_template)第二条chain 代码语言javascript 复制 # This is an LLMChain to write a review of a play given a synopsis. from langchain import PromptTemplate, OpenAI, LLMChainllm OpenAI(temperature.7) template You are a play critic from the New York Times. Given the synopsis of play, it is your job to write a review for that play.Play Synopsis: {synopsis} Review from a New York Times play critic of the above play: prompt_template PromptTemplate(input_variables[synopsis], templatetemplate) review_chain LLMChain(llmllm, promptprompt_template)最后利用SimpleSequentialChain即可将两个chain直接串联起来 代码语言javascript 复制 from langchain.chains import SimpleSequentialChain overall_chain SimpleSequentialChain(chains[synopsis_chain, review_chain], verboseTrue) print(review overall_chain.run(Tragedy at sunset on the beach))可以看到对于单个输入输出的顺序链就是将两个chain作为参数传给simplesequentialchain即可无需复杂的声明。 2.多个输入输出 除了单个输入输出的模式顺序链还支持更为复杂的多个输入输出对于多输入输出模式来说最应该需要关注的就是输入关键字和输出关键字它们需要十分的精准才能够保证chain的识别与应用依旧以一个demo为例 代码语言javascript 复制 from langchain import PromptTemplate, OpenAI, LLMChainllm OpenAI(temperature.7) template You are a playwright. Given the title of play and the era it is set in, it is your job to write a synopsis for that title.Title: {title} Era: {era} Playwright: This is a synopsis for the above play: prompt_template PromptTemplate(input_variables[title, era], templatetemplate) synopsis_chain LLMChain(llmllm, promptprompt_template, output_keysynopsis) #第一条chain代码语言javascript 复制 from langchain import PromptTemplate, OpenAI, LLMChainllm OpenAI(temperature.7) template You are a play critic from the New York Times. Given the synopsis of play, it is your job to write a review for that play.Play Synopsis: {synopsis} Review from a New York Times play critic of the above play: prompt_template PromptTemplate(input_variables[synopsis], templatetemplate) review_chain LLMChain(llmllm, promptprompt_template, output_keyreview) #第二条chain代码语言javascript 复制 from langchain.chains import SequentialChainoverall_chain SequentialChain(chains[synopsis_chain, review_chain],input_variables[era, title],# Here we return multiple variablesoutput_variables[synopsis, review],verboseTrue) #第三条chain代码语言javascript 复制 overall_chain({title: Tragedy at sunset on the beach, era: Victorian England})对于每一个chain在定义的时候都需要关注其output_key 、和input_variables按照顺序将其指定清楚。最终在运行chain时我们只需要指定第一个chain中需要声明的变量。 6.3RouterChains: 最后介绍一个经常会用到的场景比如我们目前有三类chain分别对应三种学科的问题解答。我们的输入内容也是与这三种学科对应但是随机的比如第一次输入数学问题、第二次有可能是历史问题… 这时候期待的效果是可以根据输入的内容是什么自动将其应用到对应的子链中。Router Chain就为我们提供了这样一种能力它会首先决定将要传递下去的子链然后把输入传递给那个链。并且在设置的时候需要注意为其设置默认chain以兼容输入内容不满足任意一项时的情况。 代码语言javascript 复制 physics_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 dont know the answer to a question you admit that you dont 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 代码语言javascript 复制 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} ]然后需要声明这两个prompt的基本信息。 代码语言javascript 复制 from langchain import ConversationChain, LLMChain, PromptTemplate, OpenAI llm OpenAI() destination_chains {} for p_info in prompt_infos:name p_info[name]prompt_template p_info[prompt_template]prompt PromptTemplate(templateprompt_template, input_variables[input])chain LLMChain(llmllm, promptprompt)destination_chains[name] chaindefault_chain ConversationChain(llmllm, output_keytext)最后将其运行到routerchain中即可我们此时在输入的时候chain就会根据input的内容进行相应的选择最为合适的prompt。 代码语言javascript 复制 from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE# Create a list of destinations destinations [f{p[name]}: {p[description]} for p in prompt_infos] destinations_str \n.join(destinations)# Create a router template router_template MULTI_PROMPT_ROUTER_TEMPLATE.format(destinationsdestinations_str)router_prompt PromptTemplate(templaterouter_template,input_variables[input],output_parserRouterOutputParser(), )router_chain LLMRouterChain.from_llm(llm, router_prompt) chain MultiPromptChain(router_chainrouter_chain,destination_chainsdestination_chains,default_chaindefault_chain,verboseTrue, ) print(chain.run(什么是黑体辐射))七、Agents Agents这一模块在langchain的使用过程中也是十分重要的官方文档是这样定义它的“The core idea of agents is to use a language model to choose a sequence of actions to take. In chains, a sequence of actions is hardcoded (in code). In agents, a language model is used as a reasoning engine to determine which actions to take and in which order.”也就是说在使用Agents时其行为以及行为的顺序是由LLM的推理机制决定的并不是像传统的程序一样由核心代码预定义好去运行的。 举一个例子来对比一下对于传统的程序我们可以想象这样一个场景一个王子需要经历3个关卡才可以救到公主那么王子就必须按部就班的走一条确定的路线一步步去完成这三关才可以救到公主他不可以跳过或者修改关卡本身。但对于Agents来说我们可以将其想象成一个刚出生的原始人类随着大脑的日渐成熟和身体的不断发育该人类将会逐步拥有决策能力和记忆能力这时想象该人类处于一种饥饿状态那么他就需要吃饭。此时他刚好走到小河边通过“记忆”模块认知到河里的“鱼”是可以作为食物的那么他此时就会巧妙的利用自己身边的工具——鱼钩进行钓鱼然后再利用火将鱼烤熟。第二天他又饿了这时他在丛林里散步遇到了一头野猪通过“记忆”模块认知到“野猪”也是可以作为食物的由于野猪的体型较大于是他选取了更具杀伤力的长矛进行狩猎。从他这两次狩猎的经历我们可以发现他并不是按照预先设定好的流程使用固定的工具去捕固定的猎物而是根据环境的变化选择合适的猎物又根据猎物的种类去决策使用的狩猎工具。这一过程完美的利用了自己的决策、记忆系统并辅助利用工具从而做出一系列反应去解决问题。以一个数学公式来表示可以说AgentsLLM决策Memory记忆Tools执行。 通过上述的例子相信你已经清楚的认知到Agents与传统程序比起来其更加灵活通过不同的搭配往往会达到令人意想不到的效果现在就用代码来实操感受一下Agents的实际应用方式下文的示例代码主要实现的功能是——给予Agent一个题目让Agent生成一篇论文。 在该示例中我们肯定是要示例化Agents示例化一个Agents时需要关注上文中所描述的它的三要素LLM、Memory和tools其代码如下 代码语言javascript 复制 # 初始化 agent agent initialize_agent(tools, # 配置工具集llm, # 配置大语言模型 负责决策agentAgentType.OPENAI_FUNCTIONS, # 设置 agent 类型 agent_kwargsagent_kwargs, # 设定 agent 角色verboseTrue,memorymemory, # 配置记忆模式 )7.1tools相关的配置介绍 首先是配置工具集tools如下列代码可以看到这是一个二元数组也就意味着本示例中的Agents依赖两个工具。 代码语言javascript 复制 from langchain.agents import initialize_agent, Tool tools [Tool(nameSearch,funcsearch,descriptionuseful for when you need to answer questions about current events, data. You should ask targeted questions),ScrapeWebsiteTool(), ]先看第一个工具在配置工具时需要声明工具依赖的函数由于该示例实现的功能为依赖网络收集相应的信息然后汇总成一篇论文所以创建了一个search函数这个函数用于调用Google搜索。它接受一个查询参数然后将查询发送给Serper API。API的响应会被打印出来并返回。 代码语言javascript 复制 # 调用 Google search by Serper def search(query):serper_google_url os.getenv(SERPER_GOOGLE_URL)payload json.dumps({q: query})headers {X-API-KEY: serper_api_key,Content-Type: application/json}response requests.request(POST, serper_google_url, headersheaders, datapayload)print(fGoogle 搜索结果: \n {response.text})return response.text再来看一下所依赖的第二个工具函数这里用了另一种声明工具的方式Class声明—— ScrapeWebsiteTool()它有以下几个属性和方法 代码语言javascript 复制 class ScrapeWebsiteTool(BaseTool):name scrape_websitedescription useful when you need to get data from a website url, passing both url and objective to the function; DO NOT make up any url, the url should only be from the search resultsargs_schema: Type[BaseModel] ScrapeWebsiteInputdef _run(self, target: str, url: str):return scrape_website(target, url)def _arun(self, url: str):raise NotImplementedError(error here)1.name工具的名称这里是 “scrape_website”。 2.description工具的描述。 args_schema工具的参数模式这里是 ScrapeWebsiteInput 类表示这个工具需要的输入参数声明代码如下这是一个基于Pydantic的模型类用于定义 scrape_website 函数的输入参数。它有两个字段target 和 url分别表示用户给agent的目标和任务以及需要被爬取的网站的URL。 代码语言javascript 复制 class ScrapeWebsiteInput(BaseModel):Inputs for scrape_websitetarget: str Field(descriptionThe objective task that users give to the agent)url: str Field(descriptionThe url of the website to be scraped)_run 方法这是工具的主要执行函数它接收一个目标和一个URL作为参数然后调用 scrape_website 函数来爬取网站并返回结果。scrape_website 函数根据给定的目标和URL爬取网页内容。首先它发送一个HTTP请求来获取网页的内容。如果请求成功它会使用BeautifulSoup库来解析HTML内容并提取文本。如果文本长度超过5000个字符它会调用 summary 函数来对内容进行摘要。否则它将直接返回提取到的文本。其代码如下 代码语言javascript 复制 # 根据 url 爬取网页内容给出最终解答 # target 分配给 agent 的初始任务 # url Agent 在完成以上目标时所需要的URL完全由Agent自主决定并且选取其内容或是中间步骤需要或是最终解答需要 def scrape_website(target: str, url: str):print(f开始爬取 {url}...)headers {Cache-Control: no-cache,Content-Type: application/json,}payload json.dumps({url: url})post_url fhttps://chrome.browserless.io/content?token{browserless_api_key}response requests.post(post_url, headersheaders, datapayload)# 如果返回成功if response.status_code 200:soup BeautifulSoup(response.content, html.parser)text soup.get_text()print(爬取的具体内容:, text)# 控制返回内容长度如果内容太长就需要切片分别总结处理if len(text) 5000:# 总结爬取的返回内容output summary(target, text)return outputelse:return textelse:print(fHTTP请求错误错误码为{response.status_code})从上述代码中我们可以看到其还依赖一个summary 函数用此函数解决内容过长的问题这个函数使用Map-Reduce方法对长文本进行摘要。它首先初始化了一个大语言模型llm然后定义了一个大文本切割器text_splitter。接下来它创建了一个摘要链summary_chain并使用这个链对输入文档进行摘要。 代码语言javascript 复制 # 如果需要处理的内容过长先切片分别处理再综合总结 # 使用 Map-Reduce 方式 def summary(target, content):# model list https://platform.openai.com/docs/models# gpt-4-32k gpt-3.5-turbo-16k-0613llm ChatOpenAI(temperature0, modelgpt-3.5-turbo-16k-0613)# 定义大文本切割器# chunk_overlap 是一个在使用 OpenAI 的 GPT-3 或 GPT-4 API 时可能会遇到的参数特别是需要处理长文本时。# 该参数用于控制文本块chunks之间的重叠量。# 上下文维护重叠确保模型在处理后续块时有足够的上下文信息。# 连贯性它有助于生成更连贯和一致的输出因为模型可以“记住”前一个块的部分内容。text_splitter RecursiveCharacterTextSplitter(separators[\n\n, \n], chunk_size5000, chunk_overlap200)docs text_splitter.create_documents([content])map_prompt Write a summary of the following text for {target}:{text}SUMMARY:map_prompt_template PromptTemplate(templatemap_prompt, input_variables[text, target])summary_chain_arun 方法这是一个异步版本的 _run 方法这里没有实现如果调用会抛出一个 NotImplementedError 异常。 7.2LLM的配置介绍 代码语言javascript 复制 # 初始化大语言模型负责决策 llm ChatOpenAI(temperature0, modelgpt-3.5-turbo-16k-0613)这段代码初始化了一个名为 llm 的大语言模型对象它是 ChatOpenAI 类的实例。ChatOpenAI 类用于与大语言模型如GPT-3进行交互以生成决策和回答。在初始化 ChatOpenAI 对象时提供了以下参数 1.temperature一个浮点数表示生成文本时的温度。温度值越高生成的文本将越随机和多样温度值越低生成的文本将越确定和一致。在这里设置为 0因为本demo的目的为生成一个论文所以我们并不希望大模型有较多的可变性而是希望生成非常确定和一致的回答。 2.model一个字符串表示要使用的大语言模型的名称。在这里我们设置为 “gpt-3.5-turbo-16k-0613”表示使用 GPT-3.5 Turbo 模型。 7.3Agent类型及角色相关的配置介绍 首先来看一下AgentType这个变量的初始化这里是用来设置agent类型的一个参数具体可以参考官网AgentType 可以看到官网里列举了7中agent类型可以根据自己的需求进行选择在本示例中选用的是第一种类型OpenAi functions。此外还要设定agent角色以及记忆模式 代码语言javascript 复制 # 初始化agents的详细描述 system_message SystemMessage(content您是一位世界级的研究员可以对任何主题进行详细研究并产生基于事实的结果您不会凭空捏造事实您会尽最大努力收集事实和数据来支持研究。请确保按照以下规则完成上述目标1/ 您应该进行足够的研究尽可能收集关于目标的尽可能多的信息2/ 如果有相关链接和文章的网址您将抓取它以收集更多信息3/ 在抓取和搜索之后您应该思考“根据我收集到的数据是否有新的东西需要我搜索和抓取以提高研究质量”如果答案是肯定的继续但不要进行超过5次迭代4/ 您不应该捏造事实您只应该编写您收集到的事实和数据5/ 在最终输出中您应该包括所有参考数据和链接以支持您的研究您应该包括所有参考数据和链接以支持您的研究6/ 在最终输出中您应该包括所有参考数据和链接以支持您的研究您应该包括所有参考数据和链接以支持您的研究 ) # 初始化 agent 角色模板 agent_kwargs {extra_prompt_messages: [MessagesPlaceholder(variable_namememory)],system_message: system_message, }# 初始化记忆类型 memory ConversationSummaryBufferMemory(memory_keymemory, return_messagesTrue, llmllm, max_token_limit300)1️⃣在设置agent_kwargs时“extra_prompt_messages”这个键对应的值是一个包含 MessagesPlaceholder 对象的列表。这个对象的 variable_name 属性设置为 “memory”表示我们希望在构建 agent 的提示时将 memory 变量的内容插入到提示中。“system_message”这个键对应的值是一个 SystemMessage 对象它包含了 agent 的角色描述和任务要求。 7.4Memory的配置介绍 代码语言javascript 复制 # 初始化记忆类型 memory ConversationSummaryBufferMemory(memory_keymemory, return_messagesTrue, llmllm, max_token_limit300)在设置 memory 的记忆类型对象时利用了 ConversationSummaryBufferMemory 类的实例。该类用于在与AI助手的对话中缓存和管理信息。在初始化这个对象时提供了以下参数1.memory_key一个字符串表示这个记忆对象的键。在这里设置为 “memory”。2.return_messages一个布尔值表示是否在返回的消息中包含记忆内容。在这里设置为 True表示希望在返回的消息中包含记忆内容。3.llm对应的大语言模型对象这里是之前初始化的 llm 对象。这个参数用于指定在处理记忆内容时使用的大语言模型。4。max_token_limit一个整数表示记忆缓存的最大令牌限制。在这里设置为 300表示希望缓存的记忆内容最多包含 300 个token。 7.5依赖的环境包倒入以及启动主函数 这里导入所需库这段代码导入了一系列所需的库包括os、dotenv、langchain相关库、requests、BeautifulSoup、json和streamlit。 代码语言javascript 复制 import os from dotenv import load_dotenvfrom langchain import PromptTemplate from langchain.agents import initialize_agent, Tool from langchain.agents import AgentType from langchain.chat_models import ChatOpenAI from langchain.prompts import MessagesPlaceholder from langchain.memory import ConversationSummaryBufferMemory from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.chains.summarize import load_summarize_chain from langchain.tools import BaseTool from pydantic import BaseModel, Field from langchain.schema import SystemMessagefrom typing import Type from bs4 import BeautifulSoup import requests import jsonimport streamlit as st# 加载必要的参数 load_dotenv() serper_api_keyos.getenv(SERPER_API_KEY) browserless_api_keyos.getenv(BROWSERLESS_API_KEY) openai_api_keyos.getenv(OPENAI_API_KEY)main 函数这是streamlit应用的主函数。它首先设置了页面的标题和图标然后创建了一些header并提供一个文本输入框让用户输入查询。当用户输入查询后它会调用agent来处理这个查询并将结果显示在页面上。 代码语言javascript 复制 def main():st.set_page_config(page_titleAI Assistant Agent, page_icon:dolphin:)st.header(LangChain 实例讲解 3 -- Agent, dividerrainbow)st.header(AI Agent :blue[助理] :dolphin:)query st.text_input(请提问题和需求)if query:st.write(f开始收集和总结资料 【 {query}】 请稍等)result agent({input: query})st.info(result[output])至此Agent的使用示例代码就描述完毕了我们可以看到其实Agents的功能就是其会自主的去选择并利用最合适的工具从而解决问题我们提供的Tools越丰富则其功能越强大。 八、Callbacks Callbacks对于程序员们应该都不陌生就是一个回调函数这个函数允许我们在LLM的各个阶段使用各种各样的“钩子”从而达实现日志的记录、监控以及流式传输等功能。在Langchain中该回掉函数是通过继承 BaseCallbackHandler 来实现的该接口对于每一个订阅事件都声明了一个回掉函数。它的子类也就可以通过继承它实现事件的处理。如官网所示 代码语言javascript 复制 class BaseCallbackHandler:Base callback handler that can be used to handle callbacks from langchain.def on_llm_start(self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any) - Any:Run when LLM starts running.def on_chat_model_start(self, serialized: Dict[str, Any], messages: List[List[BaseMessage]], **kwargs: Any) - Any:Run when Chat Model starts running.def on_llm_new_token(self, token: str, **kwargs: Any) - Any:Run on new LLM token. Only available when streaming is enabled.def on_llm_end(self, response: LLMResult, **kwargs: Any) - Any:Run when LLM ends running.def on_llm_error(self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any) - Any:Run when LLM errors.def on_chain_start(self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs: Any) - Any:Run when chain starts running.def on_chain_end(self, outputs: Dict[str, Any], **kwargs: Any) - Any:Run when chain ends running.def on_chain_error(self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any) - Any:Run when chain errors.def on_tool_start(self, serialized: Dict[str, Any], input_str: str, **kwargs: Any) - Any:Run when tool starts running.def on_tool_end(self, output: str, **kwargs: Any) - Any:Run when tool ends running.def on_tool_error(self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any) - Any:Run when tool errors.def on_text(self, text: str, **kwargs: Any) - Any:Run on arbitrary text.def on_agent_action(self, action: AgentAction, **kwargs: Any) - Any:Run on agent action.def on_agent_finish(self, finish: AgentFinish, **kwargs: Any) - Any:Run on agent end.这个类包含了一系列方法这些方法在 langchain 的不同阶段被调用以便在处理过程中执行自定义操作。参考源码BaseCallbackHandler on_llm_start: 当大语言模型LLM开始运行时调用。 on_chat_model_start: 当聊天模型开始运行时调用。 on_llm_new_token: 当有新的LLM令牌时调用。仅在启用流式处理时可用。 on_llm_end: 当LLM运行结束时调用。 on_llm_error: 当LLM出现错误时调用。 on_chain_start: 当链开始运行时调用。 on_chain_end: 当链运行结束时调用。 on_chain_error: 当链出现错误时调用。 on_tool_start: 当工具开始运行时调用。 on_tool_end: 当工具运行结束时调用。 on_tool_error: 当工具出现错误时调用。 on_text: 当处理任意文本时调用。 on_agent_action: 当代理执行操作时调用。 on_agent_finish: 当代理结束时调用。 8.1基础使用方式StdOutCallbackHandler StdOutCallbackHandler 是 LangChain 支持的最基本的处理器它继承自 BaseCallbackHandler。这个处理器将所有回调信息打印到标准输出对于调试非常有用。以下是如何使用 StdOutCallbackHandler 的示例 代码语言javascript 复制 from langchain.callbacks import StdOutCallbackHandler from langchain.chains import LLMChain from langchain.llms import OpenAI from langchain.prompts import PromptTemplatehandler StdOutCallbackHandler() llm OpenAI() prompt PromptTemplate.from_template(Who is {name}?) chain LLMChain(llmllm, promptprompt, callbacks[handler]) chain.run(nameSuper Mario)在这个示例中我们首先从 langchain.callbacks 模块导入了 StdOutCallbackHandler 类。然后创建了一个 StdOutCallbackHandler 实例并将其赋值给变量 handler。接下来导入了 LLMChain、OpenAI 和 PromptTemplate 类并创建了相应的实例。在创建 LLMChain 实例时将 callbacks 参数设置为一个包含 handler 的列表。这样当链运行时所有的回调信息都会被打印到标准输出。最后使用 chain.run() 方法运行链并传入参数 name“Super Mario”。在链运行过程中所有的回调信息将被 StdOutCallbackHandler 处理并打印到标准输出。 8.2自定义回调处理器 代码语言javascript 复制 from langchain.callbacks.base import BaseCallbackHandler import timeclass TimerHandler(BaseCallbackHandler):def __init__(self) - None:super().__init__()self.previous_ms Noneself.durations []def current_ms(self):return int(time.time() * 1000 time.perf_counter() % 1 * 1000)def on_chain_start(self, serialized, inputs, **kwargs) - None:self.previous_ms self.current_ms()def on_chain_end(self, outputs, **kwargs) - None:if self.previous_ms:duration self.current_ms() - self.previous_msself.durations.append(duration)def on_llm_start(self, serialized, prompts, **kwargs) - None:self.previous_ms self.current_ms()def on_llm_end(self, response, **kwargs) - None:if self.previous_ms:duration self.current_ms() - self.previous_msself.durations.append(duration)llm OpenAI() timerHandler TimerHandler() prompt PromptTemplate.from_template(What is the HEX code of color {color_name}?) chain LLMChain(llmllm, promptprompt, callbacks[timerHandler]) response chain.run(color_nameblue) print(response) response chain.run(color_namepurple) print(response)这个示例展示了如何通过继承 BaseCallbackHandler 来实现自定义的回调处理器。在这个例子中创建了一个名为 TimerHandler 的自定义处理器它用于跟踪 Chain 或 LLM 交互的起止时间并统计每次交互的处理耗时。从 langchain.callbacks.base 模块导入 BaseCallbackHandler 类。导入 time 模块用于处理时间相关操作。 定义 TimerHandler 类继承自 BaseCallbackHandler。在 TimerHandler 类的 init 方法中初始化 previous_ms 和 durations 属性。定义 current_ms 方法用于返回当前时间的毫秒值。重写 on_chain_start、on_chain_end、on_llm_start 和 on_llm_end 方法在这些方法中记录开始和结束时间并计算处理耗时。接下来我们创建了一个 OpenAI 实例、一个 TimerHandler 实例以及一个 PromptTemplate 实例。然后我们创建了一个使用 timerHandler 作为回调处理器的 LLMChain 实例。最后我们运行了两次Chain分别查询蓝色和紫色的十六进制代码。在链运行过程中TimerHandler 将记录每次交互的处理耗时并将其添加到 durations 列表中。 输出如下 8.3callbacks使用场景总结 1️⃣通过构造函数参数 callbacks 设置。这种方式可以在创建对象时就设置好回调处理器。例如在创建 LLMChain 或 OpenAI 对象时可以通过 callbacks 参数设置回调处理器。 代码语言javascript 复制 timerHandler TimerHandler() llm OpenAI(callbacks[timerHandler]) response llm.predict(What is the HEX code of color BLACK?) print(response) 在这里构建llm的时候我们就直接指定了构造函数。 2️⃣通过运行时的函数调用。这种方式可以在运行时动态设置回调处理器如在Langchain的各module如ModelAgentTool以及 Chain的请求执行函数设置回调处理器。例如在调用 LLMChain 的 run 方法或 OpenAI 的 predict 方法时可以通过 callbacks 参数设置回调处理器。以OpenAI 的 predict 方法为例 代码语言javascript 复制 timerHandler TimerHandler() llm OpenAI() response llm.predict(What is the HEX code of color BLACK?, callbacks[timerHandler]) print(response)这段代码首先创建一个 TimerHandler 实例并将其赋值给变量 timerHandler。然后创建一个 OpenAI 实例并将其赋值给变量 llm。调用 llm.predict() 方法传入问题 “What is the HEX code of color BLACK?”并通过 callbacks 参数设置回调处理器 timerHandler。 两种方法的主要区别在于何时和如何设置回调处理器。 构造函数参数 callbacks 设置在创建对象如 OpenAI 或 LLMChain时就通过构造函数的 callbacks 参数设置回调处理器。这种方式的优点是你可以在对象创建时就确定回调处理器后续在使用该对象时无需再次设置。但如果在后续的使用过程中需要改变回调处理器可能需要重新创建对象。 通过运行时的函数调用在调用对象的某个方法如 OpenAI 的 predict 方法或 LLMChain 的 run 方法时通过该方法的 callbacks 参数设置回调处理器。这种方式的优点是你可以在每次调用方法时动态地设置回调处理器更加灵活。但每次调用方法时都需要设置如果忘记设置可能会导致回调处理器不生效。 在实际使用中可以根据需要选择合适的方式。如果回调处理器在对象的整个生命周期中都不会变可以选择在构造函数中设置如果回调处理器需要动态变化可以选择在运行时的函数调用中设置。 九、总结 至此Langchain的各个模块使用方法就已经介绍完毕啦相信你已经感受到Langchain的能力了不难发现LangChain 是一个功能十分强大的AI语言处理框架它将Model IO、Retrieval、Memory、Chains、Agents和Callbacks这六个模块组合在一起。Model IO负责处理AI模型的输入和输出Retrieval模块实现了与向量数据库相关的检索功能Memory模块则负责在对话过程中存储和重新加载历史对话记录。Chains模块充当了一个连接器的角色将前面提到的模块连接起来以实现更丰富的功能。Agents模块通过理解用户输入来自主调用相关工具使得应用更加智能化。而Callbacks模块则提供了回调机制方便开发者追踪调用链路和记录日志以便更好地调试LLM模型。总之LangChain是一个功能丰富、易于使用的AI语言处理框架它可以帮助开发者快速搭建和优化AI应用。本文只是列举了各模块的核心使用方法和一些示例demo建议结合本文认真阅读一遍官方文档会更加有所受益
http://www.dnsts.com.cn/news/46114.html

相关文章:

  • 创建购物网站多少钱wordpress花钱吗
  • 网站制作公司去哪找北京seo代理计费
  • C语言开发网站开发语言网页可视化设计
  • 网站建设私活东莞网络科技有限公司简介
  • 青岛网站推广关键词做兽药网站用什么图片好
  • 移动互联网站开发网站怎么做才有百度权重
  • 如何创建一个企业网站网站群建设工作培训会
  • 如何做网站教程wordpress参数
  • 互联网站安全管理服务平台宣城网站seo
  • 青岛公司的网站设计免费俄语网站制作
  • 中国最大房产网站排名织梦 做网站 教程
  • 做网站属于无形资产还是费用个人怎么做淘宝客网站
  • 企业网站建设解决方案window2008r2网站建设
  • 网站建设策划书范文商务信息网站怎么做
  • 建设工程网站168二手建筑铝模板哪里有卖
  • 加强企业网站建设给别人做金融网站 犯法吗
  • 网站建设费用有哪些wordpress注册页面404
  • 机房建设网站网站如何从后台进入
  • 深圳专业网站建设企wordpress类似娱乐网主题
  • 网站如何做地面推广山东通app官网下载二维码
  • 电子商务网站建设的要求网站名字大全有哪些
  • 江都建设总部网站快速排名服务平台
  • 提高网站建设水平吉林长春seo网站建设网站优化
  • 西安网站设计制作一般多少钱wordpress 固定导航菜单
  • 网站 内页品牌型网站建设
  • 个人可以做建站网站么wordpress固定连接无法访问
  • 建立网站目的wordpress一键关注
  • 两学一做教育考试网站淄博营销网站建设服务
  • 网站建设 招聘需求wordpress主题备份与恢复
  • 三亚兼职网站深圳建站网站网站公司