关于Langchain的个人心得

# Langchain
## **一、Langchain概述**
### 1.1 Langchain概述
#### 1.1.1 概述

LangChain提供了一套工具、组件和接口,可简化创建由大型语言模型 (LLM) 和聊天模型提供支持的应用程序的过程。

LangChain 可以轻松管理与语言模型的交互,将多个组件链接在一起,并集成额外的资源。

LangChain是一个基于语言模型开发应用程序的框架。

- 数据感知:将语言模型连接到其他数据源
- 自主性:允许语言模型与其环境进行交互

主要价值在于:

- 组件化:为使用语言模型提供抽象层,以及每个抽象层的一组实现。组件是模块化且易于使用的,无论您是否使用LangChain框架的其余部分。
- 现成的链:结构化的组件集合,用于完成特定的高级任务

LangChain 为特定用例提供了多种组件,例如个人助理、文档问答、聊天机器人、查询表格数据、与 API 交互、提取、评估和汇总。

LangChain 中的模型分类:

- LLM(大型语言模型):这些模型将文本字符串作为输入并返回文本字符串作为输出。它们是许多语言模型应用程序的支柱。

- 聊天模型( Chat Model):聊天模型由语言模型支持,但具有更结构化的 API。

  ​    他们将聊天消息列表作为输入并返回聊天消息。

  ​    这使得管理对话历史记录和维护上下文变得容易。

- 文本嵌入模型(Text Embedding Models):这些模型将文本作为输入并返回表示文本嵌入的浮点列表。

  这些嵌入可用于文档检索、聚类和相似性比较等任务。

LangChain 的特点:

- LLM 和提示:LangChain 使管理提示、优化它们以及为所有 LLM 创建通用界面变得容易。此外,它还包括一些用于处理 LLM 的便捷实用程序。
- 链(Chain):这些是对 LLM 或其他实用程序的调用序列。LangChain 为链提供标准接口,与各种工具集成,为流行应用提供端到端的链。

- 数据增强生成:LangChain 使链能够与外部数据源交互以收集生成步骤的数据。例如,它可以帮助总结长文本或使用特定数据源回答问题。
- Agents:Agents 让 LLM 做出有关行动的决定,采取这些行动,检查结果,并继续前进直到工作完成。LangChain 提供了代理的标准接口,多种代理可供选择,以及端到端的代理示例。

- 内存:LangChain 有一个标准的内存接口,有助于维护链或代理调用之间的状态。它还提供了一系列内存实现和使用内存的链或代理的示例。
- 评估:很难用传统指标评估生成模型。这就是为什么 LangChain 提供提示和链来帮助开发者自己使用 LLM 评估他们的模型。

LangChain提供了以下主要组件:

\- 模型 ( Models )

\- 提示词 ( Prompt )

\- 代理( Agents )

\- 链( Chains )

\- 索引 ( Indexes )

\- 内存(Memory)

\- 模式 ( Schema )

#### 1.1.2应用场景


1. **信息检索**
   - LangChain可以根据用户的查询意图,通过记忆链中的语义信息,提供准确、全面的搜索结果。
   - 无论是文本、图片还是视频等多媒体数据,LangChain都能进行深度理解和检索。
2. **问答系统**
   - LangChain可以根据用户的问题,从记忆链中抽取相关信息,并给出准确的答案。
   - 无论是常见问题还是专业领域的知识,LangChain都能提供高质量的回答。
3. 个性化推荐
   - LangChain可以根据用户的兴趣和偏好,从记忆链中推荐相关的内容。
   - 无论是新闻、音乐还是电影等,LangChain都能根据用户的历史行为和喜好进行精准推荐。
4. 机器翻译
   - LangChain可以利用记忆链中的语义信息,进行更加准确、自然的机器翻译。
   - 通过对源语言和目标语言的语义关联进行建模,LangChain可以提供更加流畅、准确的翻译结果。
5. 聊天机器人
   - LangChain可以用于构建聊天机器人,使其具备更丰富的交互能力和更准确的回答能力。
   - 通过与大型语言模型的结合,聊天机器人可以更加智能地理解用户意图,并提供相应的回复。
6. **生成式问答(GQA)和摘要**
   - LangChain允许将语言模型与其他数据源连接在一起,实现数据感知,从而支持生成式问答和摘要等任务。
   - 这些任务通常需要处理大量的文本数据,LangChain的模块化设计和对LLM的通用接口实现可以极大地简化开发过程。
7. 结合大型语言模型、知识库和计算逻辑快速开发AI应用
   - LangChain通过其强大的框架和灵活的表达语言(LCEL),支持并行化、回退、批处理、流式传输和异步操作等功能。
   - 这使得开发人员能够结合大型语言模型、知识库和计算逻辑快速开发强大的AI应用。

#### 1.1.3案例
安装
~~~
pip install langchain==0.1.6
~~~
# 1、导入大模型的类
from langchain_community.llms import Tongyi
# 实例化
llm = Tongyi()
# 调用通义千问
ret =  llm.invoke("你是谁?")
print(ret)
### 1.2 Prompt
#### 1.2.1Prompt介绍
LangChain 中的 "prompt" 是一个关键概念,它指的是输入给大型语言模型(LLM)的文本指令或提示,用于引导模型生成特定的输出或执行特定的任务。在 LangChain 的框架中,prompt 的设计和使用对于构建高效、准确的链式应用至关重要。

以下是一些 LangChain 中 prompt 的应用场景和重要性:

1. **任务定义**:通过精心设计的 prompt,可以明确告诉 LLM 要执行什么任务。例如,对于问答系统,prompt 可能包含问题文本和指示模型生成答案的指令。
2. **上下文提供**:Prompt 可以包含必要的上下文信息,以帮助 LLM 理解当前任务的背景和上下文。这对于处理具有复杂依赖关系或需要跨多个步骤推理的任务尤为重要。
3. **示例引导**:通过提供示例 prompt(即少样本学习或零次学习中的例子),可以指导 LLM 如何生成符合要求的输出。这种方法特别适用于那些难以用明确规则定义的任务。
4. **链式推理**:在 LangChain 中,prompt 可以用于构建链式推理流程。通过设计一系列相互关联的 prompt,可以引导 LLM 逐步完成复杂的推理任务,如多步骤问题解答或对话生成。
5. **安全和合规性**:通过适当的 prompt 设计,可以确保 LLM 的输出符合特定的安全和合规性要求。例如,可以通过在 prompt 中包含适当的过滤器或指导原则来避免生成不适当或冒犯性的内容。
6. **性能优化**:Prompt 的设计也可以影响 LLM 的性能和效率。通过优化 prompt 的长度、结构和内容,可以提高 LLM 的响应速度和输出质量。
#### 1.2.2案例实现流程

实现流程:

1.导入prompt的类

2.导入通义大模型

3.定义一个模板

4.实例化模板类

5.提醒用户输入

6.生成prompt

7.实例化通义大模型

8.调用invoke问

# 1导入prompt的类
from langchain.prompts import PromptTemplate
# 导入通义大模型
from langchain_community.llms import Tongyi
# 定义一个模板
pp = "{county}的首都是哪里?"
# 实例化模板类
promptTemplate = PromptTemplate.from_template(pp)
# 输入
ins = input("请输入国家名:")
# 生成prompt
prompt = promptTemplate.format(county=ins)
print(prompt)
# 实例化通义大模型
tongyi = Tongyi()
ret = tongyi.invoke(prompt)
print(ret)
**格式化输出**
from langchain.schema import BaseOutputParser
#自定义class,继承了BaseOutputParser
class CommaSeparatedListOutputParser(BaseOutputParser):
    """Parse the output of an LLM call to a comma-separated list."""


    def parse(self, text: str):
        """Parse the output of an LLM call."""
        return text.strip().split(", ")

CommaSeparatedListOutputParser().parse("hi, bye")
#### 1.2.3 ChatPromptTemplate

ChatPromptTemplate是一种用于帮助人们更好地进行对话和交流的工具,特别是在现代社会中,随着信息爆炸和人们日益依赖文字和数字进行沟通的背景下,其重要性愈发凸显。以下是关于ChatPromptTemplate的详细解释:

1. 概述
   - ChatPromptTemplate是一个模板化的对话工具,它允许用户创建、设定对话内容和格式,并与他人进行分享和互动。
   - 通过使用ChatPromptTemplate,用户能够以一种简洁、清晰的方式组织和展示对话内容,从而提高沟通效率,减少信息混乱和误解的可能性。
2. 功能
   - **上下文理解**:ChatPromptTemplate的上下文功能有助于用户更好地理解对话的背景和情境,从而更好地参与和回应对话内容。
   - **模板创建**:用户可以轻松创建对话模板,这些模板可以包含各种元素,如文字、图片、信息等,以适应不同的沟通需求。
   - **角色和参数**:在ChatPromptTemplate中,聊天消息可以与内容和一个称为角色的额外参数相关联。例如,在OpenAI Chat Completions API中,聊天消息可以与AI助手、人类或系统角色相关联。
3. 使用示例
   - ChatPromptTemplate的使用可以基于不同的库和框架,如LangChain等。通过调用相关的类和函数,用户可以构建和处理提示模板。
   - 示例代码可能包括从特定库中导入ChatPromptTemplate,并通过格式化或提供参数来生成具体的聊天提示。
4. 应用场景
   - ChatPromptTemplate广泛应用于各种聊天软件和社交平台,特别是在需要高效、清晰沟通的场景中,如客服系统、在线教育、社交媒体等。
5. 优势
   - 提高沟通效率:通过预定义的模板和格式,用户可以更快地创建和发送对话内容。
   - 减少误解:清晰的模板和上下文功能有助于减少信息混乱和误解的可能性。
   - 可定制性:ChatPromptTemplate允许用户根据自己的需求创建和修改模板,以满足不同的沟通场景。

**区别**

- PromptTemplate:这是一种通用形式的模板,用于生成系统提示或信息模板。它可以应用于多种场景,如用户输入错误时的提示、系统操作成功时的提示、数据加载提示、信息确认提示以及活动推广提示等。它并不特定于对话或聊天场景。
- ChatPromptTemplate:这是PromptTemplate在聊天领域的一个特殊表达。它专注于对话和聊天场景,帮助用户更好地组织和展示对话内容。ChatPromptTemplate的上下文功能使得用户能够更好地理解对话的背景和情境,从而更好地参与和回应对话内容。

聊天模型是语言模型的一个变体,聊天模型以语言模型为基础,其内部使用语言模型,不再以文本字符串为输入和输出,而是将聊天信息列表为输入和输出,他们提供更加结构化的 API。通过聊天模型可以传递一个或多个消息。LangChain 目前支持四类消息类型:分别是 AIMessage、HumanMessage、SystemMessage 和 ChatMessage 。

- SystemMessage:系统消息是用来设定模型的一种工具,可以用于指定模型具体所处的环境和背景,如角色扮演等;
- HumanMessage:人类消息就是用户信息,由人给出的信息,如提问;使用 Chat Model 模型就得把系统消息和人类消息放在一个列表里,然后作为 Chat Model 模型的输入
- AIMessage:就是 AI 输出的消息,可以是针对问题的回答
- ChatMessage:Chat 消息可以接受任意角色的参数

大多数情况下,我们只需要处理 HumanMessage、AIMessage 和 SystemMessage 消息类型。此外聊天模型支持多个消息作为输入,如下系统消息和用户消息的示例

#### 1.2.4 ChatPromptTemplate案例
# 导入通义大模型
from langchain_community.llms import Tongyi
# 导入模板类
from langchain.prompts import ChatPromptTemplate
# 定义模板
message = [   
        ("system","假设你是起名字的大师,"),
        ("human", "我家是{sex}宝,姓{firstName},请起3个好养的名字?"),
]
# 实例化模板类
chartmp =  ChatPromptTemplate.from_messages(message);
prompt = chartmp.format_messages(sex="男",firstName="李")
print(prompt)
# 实例化通义大模型
tongyi = Tongyi()
ret = llm.invoke(prompt)
print(ret)
### 1.3 ChatMessagePromptTemplate 
#### 1.3.1介绍
ChatMessagePromptTemplate是LangChain框架中用于生成特定角色或自定义角色对话提示的一个模板。这个模板允许用户指定角色的名称,并基于提供的模板字符串生成相应的聊天消息提示。以下是关于ChatMessagePromptTemplate的详细解释:

1. **作用**:
   - ChatMessagePromptTemplate主要用于在对话模型(chat model)中,封装并生成具有特定角色或自定义角色的聊天消息提示。
2. **使用方式**:
   - 用户首先需要从`langchain.prompts`模块中导入ChatMessagePromptTemplate类。
   - 然后,用户可以定义一个包含占位符(如`{subject}`)的模板字符串。
   - 使用`ChatMessagePromptTemplate.from_template()`方法,用户可以指定角色的名称和模板字符串来创建一个ChatMessagePromptTemplate对象。
   - 接着,用户可以通过调用该对象的`format()`方法,并传入相应的参数来替换模板字符串中的占位符,从而生成具体的聊天消息提示。

#### 1.3.2案例
from langchain.prompts import ChatMessagePromptTemplate
from langchain_community.llms import Tongyi

message = "请帮我写一篇关于{type}的文章"
# 实例化
promptTemplate = ChatMessagePromptTemplate.from_template(role = "全科医生",template=message)
prompt = promptTemplate.format(type="心脏病")
print(prompt.content)
# 实例化通义大模型
tongyi = Tongyi()
ret = tongyi.invoke(prompt.content)
print(ret)
### 1.4 **StringPromptTemplate**自定义模板
#### 1.4.1介绍
在LangChain中,`StringPromptTemplate`(尽管在提供的参考文章中并未直接提及这个名字,但我们可以基于上下文和一般性的理解来讨论)可能是一个用于生成字符串形式Prompt的模板类。以下是对`StringPromptTemplate`(或类似概念)在LangChain中的可能用途和特性的描述:

1. 定义与用途

- **定义**:`StringPromptTemplate`是一个类,它允许用户通过提供模板字符串和参数来生成自定义的Prompt。
- **用途**:在LangChain中,Prompt是引导大语言模型(LLM)生成响应的关键输入。`StringPromptTemplate`提供了一种灵活且可重复的方式来创建这些Prompt。

2. 特性

- **参数化**:模板字符串可以包含占位符,这些占位符在生成Prompt时会被实际参数替换。
- **灵活性**:用户可以根据需要自定义模板字符串,以生成适应不同任务和场景的Prompt。
- **易于使用**:通过简单的API调用,用户可以快速地根据模板和参数生成所需的Prompt。
#### 1.4.2案例
# 引入自定义的Prompt模板
from langchain.prompts import StringPromptTemplate
from langchain_community.llms import Tongyi
# 代码解析器
import inspect

Prompt = "假设你是一个非常擅长编程的AI,现在给你如下函数名称,你会按照如下格式,输出这段代码的名称、源代码、中文解释。\n函数名称: {function_name}\n返回类型:{function_type}\n源代码:\n{source_code}\n代码解释:\n"
# 自定义的函数
def HelloWorld(abc):
    print("Hello World"+ abc)

class CustmPrompt(StringPromptTemplate):
    def format(self, **kwargs) -> str:
        # 获得源代码
        code = inspect.getsource(kwargs["function_name"])
        # 生成提示词模板
        prompt = Prompt.format(
            function_name=kwargs["function_name"].__name__, function_type = kwargs["function_type"] , source_code=code
        )
        return prompt
  
pp = CustmPrompt(input_variables=["function_name","function_type"])
prop = pp.format(function_name=HelloWorld,function_type="int")
tongyi = Tongyi()
ret = tongyi.invoke(prop)
print(ret)
### 1.5**使用jinja2与f-string来实现提示词模板格式化**
#### 1.5.1安装
pip install jinja2
#### 15.2模板格式化
from langchain.prompts import PromptTemplate
from langchain_community.llms import Tongyi
pp = "{{county}}的首都是哪里?"
# 实例化模板类
promptTemplate = PromptTemplate.from_template(pp,template_format="jinja2")
promt1 = promptTemplate.format(county="中国")
print(promt1)

pp1 = "{county}的首都是哪里?"
# 实例化模板类
promptTemplate = PromptTemplate.from_template(pp1,template_format="f-string")
promt = promptTemplate.format(county="中国")
print(promt)
### **1.6.使用PipelinePromptTemplate实现多步提示词**
#### 1.6.1多步提示词

\- Final prompt: 最终返回的提示词模板

\- Pipeline prompts:组成提示词管道的模板

\```

\#角色

你是一个擅长成语接龙的游戏玩家。

\## 技能

\### 技能1:进行成语接龙游戏

1.当对方说出一个成语后,你首先判断它是不是一个成语。如果不是则提示用户,要求用户重新输入;如果是成语,则需要根据该成语的最后一个字,说出一个新的成语。

2.可以使用同音字接龙。

3.你说的必须是“成语”,不能是一般的词语

4.当用户或你无法找到下一个成语时,此游戏结束。用户可以输入一个新的成

语,重新开始接龙

\## 限制

只进行成语接龙游戏,拒绝回答其他话题
# 管道模板
from langchain.prompts.pipeline import PipelinePromptTemplate
from langchain.prompts.prompt import PromptTemplate


# Final Prompt由一系列变量构成
full_template = "{name}{skill}{limit}"
full_prompt = PromptTemplate.from_template(full_template)

# 第一层基本设计
name = """#角色
你是一个擅长{name}的游戏玩家。"""
name_prompt = PromptTemplate.from_template(name)

# 第二基本设计
skill = """## 技能
{skill}"""
skill_prompt = PromptTemplate.from_template(skill)

# 第三基本设计
limit = """## 限制
{limit}"""
limit_prompt = PromptTemplate.from_template(limit)

input_prompts = [
    ("name", name_prompt),
    ("skill", skill_prompt),
    ("limit", limit_prompt)
]
pipeline_prompt = PipelinePromptTemplate(final_prompt=full_prompt, pipeline_prompts=input_prompts)

ret1 = pipeline_prompt.format(
    name="石头剪刀布", 
    skill="""### 技能1:进行石头剪刀布游戏
1.当对方说出一个石头剪刀布后,你首先判断它是不是一个石头剪刀布。如果不是则提示用户,要求用户重新输入;如果是剪刀,则需要根据该剪刀的最后一个字,说出一个新的剪刀。
2.你可以使用石头剪刀布接龙。
3.你说的必须是“剪刀”,不能是一般的词语""", 
limit="只进行石头剪刀布游戏,拒绝回答其他话题")
print(ret1)


ret2 = pipeline_prompt.format(
    name="成语接龙", 
    skill="""### 技能1:进行成语接龙游戏
1.当对方说出一个成语后,你首先判断它是不是一个成语。如果不是则提示用户,要求用户重新输入;如果是成语,则需要根据该成语的最后一个字,说出一个新的成语。
2.可以使用同音字接龙。
3.你说的必须是“成语”,不能是一般的词语
4.当用户或你无法找到下一个成语时,此游戏结束。用户可以输入一个新的成语,重新开始接龙""", 
limit="只进行成语接龙游戏,拒绝回答其他话题")
print(ret2)


#### **1.6.2.使用文件来管理提示词模板**
\- 便于共享

\- 便于版本管理

\- 便于存储

\- 支持常见格式(json/yaml/txt)
from langchain.prompts import load_prompt
#加载yaml格式的prompt模版
# prompt = load_prompt("simple_prompt.yaml")
# print(prompt.format(name="小黑",what="恐怖的"))

# 加载json格式的prompt模版
prompt1 = load_prompt("simple_prompt.json")
print(prompt1.format(name="小红",what="搞笑的"))
~~~

~~~python
from langchain.prompts import load_prompt
#支持加载文件格式的模版,并且对prompt的最终解析结果进行自定义格式化
prompt = load_prompt("simple_output_parser.json")
prompt.output_parser.parse(
    "chartGPT在哪一年发布的.\n年: 2022年"
)
### 5.4Prompt加载支持多种方式
LangChain的Prompt加载支持多种方式,以满足不同场景下的需求。以下是几种主要的加载方式:
#### 5.4.1 从YAML/JSON文件加载
LangChain支持从YAML文件中加载Prompt模板。YAML文件通常包含模板的类型、输入变量和模板内容等信息。加载时,可以通过LangChain提供的API读取YAML文件,并将其内容转换为Prompt对象。

与YAML类似,LangChain也支持从JSON文件中加载Prompt模板。JSON文件的结构与YAML文件相似,但使用不同的语法。加载JSON文件的代码与加载YAML文件的代码类似,只是文件扩展名和可能的API调用细节可能有所不同。


假设有一个名为`simple_prompt.yaml`的文件,例如其内容如下:
_type: prompt  
input_variables: ["adjective", "content"]  
template: Tell me a {adjective} joke about {content}.
假设有一个json文件,例如其内容如下:
{
    "_type": "prompt",
    "template": "The {adjective} {content} make me laugh!",
    "input_variables": ["adjective", "content"]
}
加载这个 YAML文件/json文件 的代码可能类似于:
from langchain.prompts import load_prompt

#加载yaml格式的prompt模版
prompt = load_prompt("simple_prompt.yaml")
print(prompt.format(name="小黑",what="恐怖的"))
# 加载json格式的prompt模版
prompt1 = load_prompt("simple_prompt.json")
print(prompt1.format(name="小红",what="搞笑的"))
#支持加载文件格式的模版,并且对prompt的最终解析结果进行自定义格式化
prompt = load_prompt("simple_output_parser.json")
prompt.output_parser.parse(
    "chartGPT在哪一年发布的.\n年: 2022年"
)
# 加载prompt <==> yaml
# prompt1 = load_prompt("name.yaml")
# 加载prompt <==> json
# prompt1 = load_prompt("name.json")
# 格式化
print(prompt1.format(sex="男",name="翟"))
#### 5.4.3 直接使用字符串定义Prompt
除了从文件加载外,LangChain还允许直接使用字符串定义Prompt模板。这在某些情况下非常方便,尤其是当Prompt模板比较简单或者需要动态构建Prompt模板时。
## **二、Prompts高级**
### 5.1 示例选择器 Example selectors
#### 5.1.1 介绍及应用

如果您拥有大量的示例,您可能需要选择在提示中包含哪些示例。ExampleSelector 是负责执行此操作的类。 基本接口定义如下所示:

```text
class BaseExampleSelector(ABC):
    """Interface for selecting examples to include in prompts."""

    @abstractmethod
    def select_examples(self, input_variables: Dict[str, str]) -> List[dict]:
        """Select which examples to use based on the inputs."""
```

它只需要暴露一个 `select_examples` 方法。该方法接收输入变量并返回一个示例列表。具体如何选择这些示例取决于每个具体实现。

LangChain的示例选择器是一个用于从一组示例中动态选择部分示例以构建提示(prompt)的重要组件。在LangChain中,有多种不同类型的示例选择器,包括自定义样例选择器、长度样例选择器、MMR样例选择器、n-gram重叠度样例选择器和相似度样例选择器。以下是这些示例选择器的简要介绍和格式清晰的回答:

1. 自定义样例选择器
   - 允许用户根据自己的业务逻辑和需求来定义样例的选择方式。
   - 至少需要实现两个方法:`add_example`(用于添加新示例)和`select_examples`(基于输入变量返回一个样例列表)。
   - 用户可以通过继承`BaseExampleSelector`类并实现所需的方法来创建自定义样例选择器。
2. 长度样例选择器
   - 根据输入的长度来选择要使用的示例。对于较长的输入,它会选择较少的示例,而对于较短的输入,则会选择更多示例。
   - 这在构建提示时需要控制上下文窗口长度时特别有用。
   - 使用时,可以导入`LengthBasedExampleSelector`类,并传入示例列表来创建长度样例选择器。
3. MMR样例选择器
   - MMR(Maximum Marginal Relevance)是一种常用于信息检索和推荐系统的排序算法,也适用于样例选择。
   - MMR样例选择器通过计算示例与输入之间的相关性以及示例之间的不相似性来选择样例。
   - 具体的实现细节可能因库或框架而异,但通常用户需要指定计算相关性和不相似性的方法。
4. n-gram重叠度样例选择器
   - 基于输入和示例之间的n-gram重叠度来选择样例。
   - n-gram是一种用于表示文本中连续n个词或字符的序列的模型。
   - 通过计算输入和示例之间的n-gram重叠度,可以选择与输入内容最相关的样例。
5. 相似度样例选择器
   - 基于输入和示例之间的相似度来选择样例。
   - 相似度可以使用各种方法来计算,如余弦相似度、Jaccard相似度等。
   - 选择与输入内容最相似的样例可以帮助语言模型更好地理解prompt,并给出更准确的回答。

**归纳**:

LangChain的示例选择器为构建有效的提示提供了灵活的工具。用户可以根据具体的应用场景和需求选择合适的示例选择器。无论是根据长度、MMR算法、n-gram重叠度还是相似度来选择样例,都可以帮助提高语言模型的理解能力和回答准确性。同时,用户还可以根据自己的业务逻辑自定义样例选择器,以实现更精细的样例选择。
#### 5.1.2自定义示例选择器

我们将创建一个自定义示例选择器,该选择器从给定的示例列表中选择每个交替示例。

`ExampleSelector` 必须实现两个方法:

1. `add_example` 方法,接受一个示例并将其添加到 ExampleSelector 中
2. `select_examples` 方法,接受输入变量(用于用户输入)并返回要在 few shot prompt 中使用的示例列表。

代码实现

1.新建一张表

标题    简单描述

流感       发烧、咳嗽、喉咙痛、身体酸痛  

2.写一个vue页面,搜索框,输入标题

3.写一个接口,在接口中获取到输入信息,用自定义选择器实现,查询数据库中的数据构造examples,

4.生成prompt,调用大模型,返回结果

5.vue展示,sse流式显示

from typing import List, Dict, Any
from langchain.prompts.example_selector.base import BaseExampleSelector
from langchain.prompts.few_shot import FewShotPromptTemplate
from langchain import FewShotPromptTemplate

class MedicalExampleSelector(BaseExampleSelector):
    def __init__(self, examples: List[Dict[str, str]]):
        """
        初始化医疗示例选择器
        :param examples: 医疗相关的示例列表,每个示例是一个字典,包含输入和输出
        """
        self.examples = examples

    def add_example(self, example: Dict[str, str]) -> None:
        """将新示例添加到存储中的键。"""
        self.examples.append(example)

    def select_examples(self, input_variables: Dict[str, str]) -> List[Dict[str, str]]:  
        """  
        根据输入变量选择医疗示例  
        :param input_variables: 包含选择条件(如疾病名称、症状等)的字典
        :return: 符合条件的示例列表
        """
        # 假设我们根据疾病名称来选择示例
        disease_name = input_variables.get("disease_name", None)
        if disease_name is None:
            return []  # 如果没有提供疾病名称,则返回空列表
        selected_examples = [
            example for example in self.examples
            if disease_name.lower() in example["input"].lower()
        ]
        return selected_examples
# 示例数据
examples = [
{"input":"流感症状","output":"发烧、咳嗽、喉咙痛、身体酸痛。"},
{"input":"糖尿病治疗","output":"饮食控制、运动、药物"},
{"input":"新冠肺炎症状","output":"发烧、咳嗽、疲劳、味觉或嗅觉丧失。"},
{"input":"糖尿病症状","output":"口渴、尿频、体重减轻。"},
]
# 创建示例选择器实例
example_prompt = PromptTemplate(
    input_variables=["input", "output"],
    template="Input: {input}\nOutput: {output}",
)
# 创建医疗示例选择器实例
medical_example_selector = MedicalExampleSelector(examples)
dynamic_prompt = FewShotPromptTemplate(
    # 我们提供一个ExampleSelector而不是示例。
    example_selector=medical_example_selector,
    example_prompt=example_prompt,
    prefix="根据描述确定病情",
    suffix="输入: {disease_name}\n输出:",
    input_variables=["disease_name"],
)
# 一个输入较小的示例,因此它选择所有示例。
dynamic_prompt.format(disease_name="糖尿病")
print(prompt)
tongyi = Tongyi()
ret = tongyi.invoke(prompt)
print(ret)
#### 5.1.3根据长度选择
这个示例选择器根据长度选择要使用的示例。当您担心构建的提示会超过上下文窗口的长度时,这是非常有用的。对于较长的输入,它会选择较少的示例进行包含,而对于较短的输入,它会选择更多的示例
from langchain.prompts import PromptTemplate  
from langchain.prompts import FewShotPromptTemplate  
from langchain.prompts.example_selector import LengthBasedExampleSelector 
# 导入通义大模型
from langchain_community.llms import Tongyi


examples = [  
    {"input": "患者如何在家测量血压?", "output": "患者可以在家使用电子血压计测量血压,遵循说明书上的步骤,通常在早上和晚上各测量一次。"},  
    {"input": "糖尿病患者的饮食应该注意什么?", "output": "糖尿病患者应该注意饮食中的糖分和碳水化合物摄入,多食用蔬菜、全谷物和瘦肉,避免高糖和高脂肪食物。"},  
    {"input": "儿童发烧时应该如何处理?", "output": "儿童发烧时,应首先测量体温,如果超过38.5°C,可以使用退烧药,并给孩子多喝水,保持通风,适当减少衣物。"}  
]  

example_prompt = PromptTemplate(
    input_variables=["input", "output"],
    template="Input: {input}\nOutput: {output}",
)
# 定义计算长度的函数  
def calculate_text_length(text):  
    return len(re.split("\n| ", text))  
    
example_selector = LengthBasedExampleSelector(
    # 这些是可供选择的示例。
    examples=examples,
    # 这是用于格式化示例的PromptTemplate。
    example_prompt=example_prompt,
    # 这是格式化示例的最大长度。
    # 长度由下面的get_text_length函数测量。
    max_length=10,
    
)
dynamic_prompt = FewShotPromptTemplate(
    # 我们提供一个ExampleSelector而不是示例。
    example_selector=example_selector,
    example_prompt=example_prompt,
    prefix="根据描述确定病情",
    suffix="输入: {adjective}\n输出:",
    input_variables=["adjective"],
)

# 一个输入较小的示例,因此它选择所有示例。
prompt = dynamic_prompt.format(adjective="糖尿病患者的饮食")

# 一个输入较长的示例,因此它只选择一个示例。
# long_string = "big and huge and massive and large and gigantic and tall and much much much much much bigger than everything else"
# print(dynamic_prompt.format(adjective=long_string))

# 您还可以将示例添加到示例选择器中。
# new_example = {"input": "big", "output": "small"}
# dynamic_prompt.example_selector.add_example(new_example)
# print(dynamic_prompt.format(adjective="enthusiastic"))

tongyi = Tongyi()
print(prompt)
ret = tongyi.invoke(prompt)
print(ret)
#### 5.1.4最大边际相关性(MMR)选择

``根据示例与输入之间的相似性以及多样性进行选择。它通过找到与输入具有最大余弦相似度的嵌入示例,并在迭代中添加它们,同时对已选择示例的接近程度进行惩罚来实现这一目标。

\- MMR是一种在信息检索中常用的方法,它的目标是在相关性和多样性之间找到一个平衡

\- MMR会首先找出与输入最相似(即余弦相似度最大)的样本

\- 然后在迭代添加样本的过程中,对于与已选择样本过于接近(即相似度过高)的样本进行惩罚

\- MMR既能确保选出的样本与输入高度相关,又能保证选出的样本之间有足够的多样性

\- 关注如何在相关性和多样性之间找到一个平衡
from langchain.prompts import PromptTemplate
from langchain.prompts import FewShotPromptTemplate
from langchain.prompts.example_selector import MaxMarginalRelevanceExampleSelector
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.llms import Tongyi

#假设已经有这么多的提示词示例组:
examples =  [  
    {"id": "1", "features": ', '.join(["时尚", "运动鞋", "跑步"])},  
    {"id": "2", "features": ', '.join(["休闲", "运动鞋", "篮球"])}, 
    {"id": "3", "features": ', '.join(["商务", "皮鞋", "正装"])}, 
    {"id": "4", "features": ', '.join(["户外", "徒步鞋", "探险"])}, 
    {"id": "5", "features": ', '.join(["时尚", "板鞋", "滑板"])}, 
]  

#构造提示词模板
example_prompt = PromptTemplate(
    input_variables=["id","features"],
    template="id:{id}\n描述:{features}"
)

#调用MMR
example_selector = MaxMarginalRelevanceExampleSelector.from_examples(
    #传入示例组
    examples,
    #使用阿里云的dashscope的嵌入来做相似性搜索
    DashScopeEmbeddings(),
    #设置使用的向量数据库是什么
    FAISS,
    #结果条数
    k=3,
)

#使用小样本提示词模版来实现动态示例的调用
dynamic_prompt = FewShotPromptTemplate(
    example_selector=example_selector,
    example_prompt=example_prompt,
    prefix="查询和features相似度最高的id",
    suffix="关键词为:{word}",
    input_variables=["word"]
)

print(dynamic_prompt.format(word="时尚"))

# qq = dynamic_prompt.format(word="时尚")
# llm = Tongyi()
# llm.invoke(qq)
#### 5.1.5**最大余弦相似度**
余弦相似度(Cosine Similarity)是一种常用的度量两个非零向量之间相似度的方法,广泛应用于文本挖掘、推荐系统、信息检索等领域。其原理及公式如下:

余弦相似度通过计算两个向量之间的夹角的余弦值来评估它们的相似度。在向量空间中,两个向量的夹角越小,说明它们的方向越接近,从而相似度越高。余弦相似度与向量的长度无关,仅与向量的方向有关。

- 一种常见的相似度计算方法

- 它通过计算两个向量(在这里,向量可以代表文本、句子或词语)之间的余弦值来衡量它们的相似度

- 余弦值越接近1,表示两个向量越相似

- 主要关注的是如何准确衡量两个向量的相似度
from langchain.prompts import PromptTemplate
from langchain.prompts import FewShotPromptTemplate
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.llms import Tongyi

#假设已经有这么多的提示词示例组:
examples =  [  
    {"id": "1", "features": ', '.join(["时尚", "运动鞋", "跑步"])},  
    {"id": "2", "features": ', '.join(["休闲", "运动鞋", "篮球"])}, 
    {"id": "3", "features": ', '.join(["商务", "皮鞋", "正装"])}, 
    {"id": "4", "features": ', '.join(["户外", "徒步鞋", "探险"])}, 
    {"id": "5", "features": ', '.join(["时尚", "板鞋", "滑板"])}, 
]  

#构造提示词模板
example_prompt = PromptTemplate(
    input_variables=["id","features"],
    template="id:{id}\n描述:{features}"
)

#调用MMR
example_selector = SemanticSimilarityExampleSelector.from_examples(
    #传入示例组
    examples,
    #使用阿里云的dashscope的嵌入来做相似性搜索
    DashScopeEmbeddings(),
    #设置使用的向量数据库是什么
    FAISS,
    #结果条数
    k=3,
)

#使用小样本提示词模版来实现动态示例的调用
dynamic_prompt = FewShotPromptTemplate(
    example_selector=example_selector,
    example_prompt=example_prompt,
    prefix="查询和features相似度最高的id",
    suffix="关键词为:{word}",
    input_variables=["word"]
)

print(dynamic_prompt.format(word="时尚"))

# qq = dynamic_prompt.format(word="时尚")
# llm = Tongyi()
# llm.invoke(qq)
### 5.3输出解析器
#### 5.3.1介绍
LangChain输出解析器是LangChain框架中的一个重要组成部分,它负责将自然语言模型(如ChatGPT、GPT系列等)的输出从文本形式转换为结构化的数据格式,以便进行进一步的分析或操作。以下是对LangChain输出解析器的详细解析:

一、定义与功能

LangChain输出解析器是一种特殊的自然语言处理(NLP)技术,其核心功能是将非结构化的自然语言文本转换为结构化的数据,如JSON对象、列表等。这种转换使得输出更加易于程序理解和处理,提高了数据处理的效率和准确性。

二、工作原理

LangChain输出解析器的工作原理通常包括以下几个步骤:

1. **文本预处理**:对输入的自然语言文本进行预处理,包括去除停用词、标点符号等无意义字符,以及对文本进行分词、词性标注等操作。
2. **特征提取**:将预处理后的文本转换为向量表示,以便进行后续处理。这可以通过词袋模型、TF-IDF、Word2Vec等方法实现。
3. **模型解析**:将处理后的文本输入到训练好的输出解析器模型中,模型根据预设的规则或算法将文本解析为结构化的数据格式。
4. **输出结果**:输出解析器将解析后的结构化数据以特定的格式(如JSON、列表等)返回给调用者。

三、类型与实现

LangChain提供了多种类型的输出解析器,以满足不同的需求。这些解析器包括但不限于:

1. **结构化输出解析器**:将文本解析为JSON对象等结构化数据格式。
2. **CSV解析器**:将文本解析为CSV格式的数据,便于进行表格化处理。
3. **日期时间解析器**:专门用于处理日期和时间相关的输出,确保输出的日期和时间格式正确。
4. **枚举解析器**:将文本中的特定词汇映射为枚举类型的值,适用于处理有限集合的数据。
5. **Pydantic解析器**:利用Pydantic库的功能,将文本解析为符合特定数据模式的Python对象。

四、应用场景

LangChain输出解析器在多个领域都有广泛的应用,包括但不限于:

1. **问答系统**:从用户的问题中抽取关键信息,并将其作为查询条件在知识库中进行搜索。
2. **机器翻译**:将源语言文本解析为目标语言文本中的实体、关系等信息,以提高翻译的准确性和流畅性。
3. **摘要生成**:从原始文本中抽取关键信息,并将其组合成一个简洁的摘要。
4. **数据分析**:将自然语言形式的数据转换为结构化数据,以便进行进一步的数据分析和挖掘。

五、总结

LangChain输出解析器是LangChain框架中一项强大的功能,它能够将自然语言模型的输出转换为结构化的数据格式,为后续的处理和分析提供了极大的便利。通过不同的解析器类型,LangChain能够满足不同领域和场景下的需求,为构建高效的自然语言处理应用程序提供了有力的支持。

#### 5.3.2列表解析器
当您想要返回逗号分隔的项目列表时,可以使用此输出解析器。也可以作为CVS解析器。

from langchain_core.output_parsers import CommaSeparatedListOutputParser,NumberedListOutputParser
from langchain.prompts import PromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate

# 定义列表解析器
output_parser = CommaSeparatedListOutputParser()
# 获取格式说明
format_instructions = output_parser.get_format_instructions()
# 创建一个提示模板
promptTemplate = PromptTemplate(
    template="列出五种{subject}。\n{format_instructions}",
    input_variables=["subject"],
    partial_variables={"format_instructions": format_instructions},
)
# 测试
prompt=promptTemplate.format(subject="水果")
ret = llm.invoke(prompt)
output_parser.parse(ret)
#### 5.3.3日期时间解析器
这个OutputParser展示了如何将LLM的输出解析为日期时间格式。
from langchain_community.llms import Tongyi
from langchain.prompts import PromptTemplate
from langchain.output_parsers.datetime import DatetimeOutputParser

# 1\实例化
output_parser = DatetimeOutputParser()
template = """请按下面要求回答问题:
{question}
{format_instructions}"""
# 2、定义模板
promptTemplate = PromptTemplate.from_template(
    template,
    partial_variables={"format_instructions": output_parser.get_format_instructions()},
)
# 3、生成prompt
prompt = promptTemplate.format(question="新中国成立的日期是什么?")
# 4、提交大模型
output = llm.invoke(prompt)
# 5、解析
res = output_parser.parse(output)
#### 5.3.4 枚举解析器

**枚举解析器在 LangChain 中的可能实现和功能**:

1. 定义
   - 枚举解析器可能是 LangChain 输出解析器的一个特定实现,专门用于处理枚举类型的数据或输出。
2. 功能
   - **识别枚举值**:从模型输出中识别并提取枚举值。
   - **格式化输出**:将提取的枚举值转换为指定的数据结构或格式。
   - **验证**:可能还包括对提取的枚举值进行验证,以确保其符合预期的枚举类型。
3. 核心方法
   - `get_format_instructions`:返回指导如何格式化枚举类型输出的字符串。
   - `parse_enum`(假设的方法名):接受一个字符串(模型输出),并尝试从中解析出枚举值。
   - `validate_enum`(假设的方法名):验证解析出的枚举值是否有效。
4. 实现
   - 枚举解析器可能需要预定义或动态加载一个枚举类型的定义(如 Python 中的 `Enum` 类),以便能够正确解析和验证枚举值。
   - 它可能会使用正则表达式、字符串匹配或其他 NLP 技术来从模型输出中提取枚举值。
5. 使用场景
   - 当模型输出中包含枚举类型的数据时,枚举解析器可以确保这些数据被正确地提取、格式化和验证。
   - 这在处理具有固定选项集(如颜色、状态、类别等)的数据时特别有用。

from langchain.output_parsers.enum import EnumOutputParser
from enum import Enum

class Colors(Enum):
    RED = "red"
    GREEN = "green"
    BLUE = "blue"
parser = EnumOutputParser(enum=Colors)
print(parser.parse("red"))
# -> <Colors.RED: 'red'>
# 包括空格也可以
print(parser.parse(" green"))
# -> <Colors.GREEN: 'green'>
# 包括\n\t也可以
print(parser.parse("blue\n\t"))
# -> <Colors.BLUE: 'blue'>
# 其他字符不可以
print(parser.parse("yellow"))
# -> <Colors.BLUE: 'blue'>
#### 5.3.5 Pydantic 解析器

要使用 Pydantic 解析器,必须保证大模型的输出是可靠的Json格式。

这个解析器将大模型输出的Json字符串解析为Pydantic模型。在信息提取的场景中很有用。

from typing import List
from langchain.output_parsers import PydanticOutputParser
from langchain.prompts import PromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field, validator
from langchain_community.llms.tongyi import Tongyi

# 定义您期望的数据结构。
class Joke(BaseModel):
    joke: str = Field(description="设置笑话的问题部分",title="笑话")
    answer: str = Field(description="解答笑话的答案部分")
# 设置一个解析器 + 将指令注入到提示模板中。
parser = PydanticOutputParser(pydantic_object=Joke)
promptTemplate = PromptTemplate(
    template="回答用户的问题:\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": parser.get_format_instructions()},
)
prompt = promptTemplate.format(query="讲一个中文的笑话。")
ret = llm.invoke(prompt)
parser.parse(ret)
#### 5.3.6 JSON 解析器
from langchain.output_parsers.json import JsonOutputParser
from langchain.prompts import PromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field, validator

# 定义您期望的数据结构。
class Joke(BaseModel):
    joke: str = Field(description="设置笑话的问题部分")
    answer: str = Field(description="解答笑话的答案部分")
# 还有一个用于提示语言模型填充数据结构的查询意图。
joke_query="告诉我一个笑话"
# 设置一个解析器 + 将指令注入到提示模板中。
parser = JsonOutputParser(pydantic_object=Joke)
promptTemplate = PromptTemplate(
    template="回答用户的问题:\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": parser.get_format_instructions()},
)
prompt = promptTemplate.format(query=joke_query)
ret = llm.invoke(prompt)
parser.parse(ret)
from typing import List, Dict, Any
from langchain.prompts.example_selector.base import BaseExampleSelector
from langchain.prompts import PromptTemplate
from langchain.prompts.few_shot import FewShotPromptTemplate

class MedicalExampleSelector(BaseExampleSelector):
    def __init__(self, examples: List[Dict[str, str]]):
        """ 初始化菜品示例选择器 :param examples: 菜品相关的示例列表,每个示例是一个字典,包含输入和输出 """
        self.examples = examples
    def add_example(self, example: Dict[str, str]) -> None:
        """ 将新示例添加到存储中的键。"""
        self.examples.append(example)
    def select_examples(self, input_variables: Dict[str, str]) -> List[Dict[str, str]]:
        """ 根据输入变量选择菜品示例 :param input_variables: 包含选择条件(如菜品名称、菜品种类等)的字典 :return: 符合条件的示例列表 """
        # 假设我们根据疾病名称来选择示例
        disease_name = input_variables.get("disease_name", None)
        if disease_name is None:
            return []  # 如果没有提供疾病名称,则返回空列表
        selected_examples = [
            example for example in self.examples
            if disease_name.lower() in example["input"].lower()
        ]
        return selected_examples
# 示例数据
examples = [
{"input":"菜品名称","output":" 宫保鸡丁,糖醋排骨,红烧肉,酸辣粉,酸辣土豆丝,酸辣鱼片,酸辣虾,酸辣蟹"},
{"input":"菜品种类","output":"热菜,凉菜,主食 ,饮品 ,小吃"},
{"input":"菜品菜系","output":" 川菜,粤菜,鲁菜,浙菜,湘菜,苏菜,徽菜,闽菜,鲁菜,湘菜,苏菜,徽菜,闽菜,鲁菜,湘菜,苏菜,徽菜,闽菜,鲁菜,湘菜,苏菜,徽菜,闽菜,鲁菜,湘菜,苏菜,徽菜,闽菜,鲁菜,湘菜,苏菜"},
]
example_prompt = PromptTemplate(
    input_variables=["input", "output"],
    template="Input: {input}\nOutput: {output}",
)
# 创建医疗示例选择器实例
medical_example_selector = MedicalExampleSelector(examples)
dynamic_prompt = FewShotPromptTemplate(
    # 我们提供一个ExampleSelector而不是示例
    example_selector=medical_example_selector,
    example_prompt=example_prompt,
    prefix="根据描述说出菜品",
    suffix="输入: {disease_name}\n输出:",
    input_variables=["disease_name"],
)
# 一个输入较小的示例,因此它选择所有示例
prompt=dynamic_prompt.format(disease_name="菜品")
ret = llm.invoke(prompt)
output_parser.parse(ret)
parser.parse(ret)
## **三、RAG增强**
### 5.1 RAG增强检索
#### 5.1.1 RAG增强检索概念
任务重新训练模型。
- **优化模型输出效果**:用户可以额外附加外部知识库,丰富输入,从而优化模型的输出效果。
- **减少模型幻觉**:通过结合外部知识库,RAG能够生成更准确、更符合上下文的答案,减少模型生成的错误或不正确内容。

5. 应用场景

RAG技术在知识密集型NLP任务中表现出色,如问答系统、文本摘要、对话系统等。在这些任务中,RAG能够提供更准确、更全面的文本处理能力,满足用户的不同需求。

RAG的流程如下:

\- 1文档加载

\- 2文档分割

\- 3文档向量化

-4构建知识库

\- 5基于知识库的问答
#### 5.1.3 文档加载的几种方式
##### 5.4.1 加载markdown
from langchain_community.document_loaders import TextLoader

loader = TextLoader("./NBA/demo.md", encoding='utf-8')
docs = loader.load()
##### 5.4.2 加载csv
from langchain_community.document_loaders.csv_loader import CSVLoader

loader = CSVLoader(file_path="./NBA/demo.csv", encoding="utf-8")
data = loader.load()
##### 5.4.3 加载html
#使用loader来加载cvs文件
from langchain_community.document_loaders import BSHTMLLoader

loader = BSHTMLLoader("./NBA/demo.html", open_encoding="utf-8")
data = loader.load()
##### 5.4.4 加载JSON
#使用loader来加载json文件
from langchain_community.document_loaders import JSONLoader

loader = JSONLoader(
    file_path = "simple_prompt.json",
    jq_schema=".",
    text_content=True
)
data = loader.load()
##### 5.4.5 加载PDF
#使用loader来加载pdf文件
from langchain_community.document_loaders import PyPDFLoader
loader = PyPDFLoader("loader.pdf")
pages = loader.load_and_split()
print(pages[0])
##### 5.4.6 加载doc/docx/wps
from langchain_community.document_loaders import Docx2txtLoader

loader = Docx2txtLoader("doc/demo.docx")
data = loader.load()
print(data)
##### 5.4.6 加载xlsx
from langchain_community.document_loaders import UnstructuredExcelLoader

loader = UnstructuredExcelLoader("doc/demo.xlsx",mode="elements")
docs = loader.load()
print(docs)
inputmes = "请写一首关于春天的诗"  
  
stream = llm.stream(inputmes)
# 注意,我假设 prompt 是正确的参数名,而不是 promt  
# for chunk in stream:  
#     print(chunk)
# 假设 chunk 对象有一个 text 属性,这取决于 Tongyi 的返回类型
### 5.2流式调用
#### 5.2.1流式调用概念

大模型的流式输出方法主要涉及到在模型生成答案的过程中,以流的形式将结果逐步输出给用户,而不是等待整个答案生成完成后再一次性输出。这种流式输出的方法有助于提高用户体验,尤其是在处理大规模数据和生成长文本时
#### 5.2.2案例实现
from langchain.text_splitter import CharacterTextSplitter

# 初始化文本分割器
text_splitter = CharacterTextSplitter.from_huggingface_tokenizer(
    chunk_size=200,# 切分的文本块大小,一般通过长度函数来计算
    chunk_overlap=0# 切分的文本块重叠大小,一般通过长度函数来计算
    )
loader = TextLoader('../data/example.txt', encoding='utf-8')
text_splitter.split_documents(loader.load())
### 5.3token跟踪
### 5.3token跟踪

在LLM(大型语言模型)中,Token追踪的概念并不直接等同于传统的资产追踪或位置追踪。但在LLM的语境下,Token是模型进行语言处理的基本信息单元,它代表了模型可以理解和生成的最小意义单位。关于LLM的Token追踪,我们可以从以下几个方面进行理解:

1. Token的定义
   - Token是大语言模型(LLM)进行语言处理的最小单元,它可以是一个字、一个词,甚至是一个短语或句子的一部分。
   - Token在不同的上下文中可能会有不同的划分粒度,这取决于所使用的特定标记化(Tokenization)方案。
2. Token的追踪意义
   - 在LLM中,Token的追踪并不是指追踪某个具体的Token的物理位置或来源,而是指模型如何处理、理解和生成这些Token。
   - 当用户与LLM进行交互时,输入的文本会被转换为一系列的Token,这些Token随后被模型用于生成预测或回答。
3. Token的数量与关系
   - 一个Token的大致长度可以用字符或单词来估算。例如,在英语中,一个Token大约等于4个字符,而在中文中,一个Token可能对应于¾个单词(这是一个粗略的估算)。
   - 模型的性能通常与其能够处理的Token数量有关。一些模型具有MAX TOKENS的参数,这限制了模型在一次会话中能够基于整个上下文记忆的Token数量。
4. Token与向量
   - Token在LLM中通常与向量相关联,这些向量代表了Token的特征或语义信息。
   - 相同的Token在不同的上下文中可能会有不同的特征向量,这取决于其在整个文本中的角色和含义。
5. Token在LLM中的作用
   - Token作为原始文本数据和LLM可以使用的数字表示之间的桥梁,确保了文本的连贯性和一致性。
   - LLM使用Token来理解和生成文本,有效地处理各种任务,如写作、翻译和回答查询。

综上所述,LLM的Token追踪更多地是指模型如何处理、理解和生成这些基本的语言单元,而不是物理上追踪某个Token的位置或来源。

#### 5.3.2案例


### 5.4文档分割

* 原理

\1. 将文档分成小的、有意义的块(句子).

\2. 将小的块组合成为一个更大的块,直到达到一定的大小.

\3. 一旦达到一定的大小,接着开始创建与下一个块重叠的部分.

分类

\- 按字符切割

\- 代码文档切割

\- 按token来切割

#### 5.4.1字符串分割
使用langchain的text_splitter模块,导入CharacterTextSplitter类

\- separator 分隔符

\- chunk_size 每块的最大长度

\- chunk_overlap 重复字符的长度

关于 `chunk_overlap` 参数,它指的是在分割文本时,相邻的两个文本块之间将有多少字符是重叠的。这个参数在某些情况下非常有用,比如当你想要确保某些关键信息(如句子的一部分)不会恰好被分割在两个不同的块中时。通过设置重叠,你可以增加相邻块之间的上下文联系,这有助于后续处理(如文本摘要、情感分析等)时保持更好的连贯性。

from langchain_community.document_loaders import TextLoader

# 实例化TextLoader对象
loader = TextLoader("doc/NBA新闻.txt",encoding="utf-8")
# 加载文档
docs = loader.load()
print(docs)

# 导入分割类
from langchain.text_splitter import CharacterTextSplitter

# 实例化
text_splitter = CharacterTextSplitter(separator="\n",chunk_size=150, chunk_overlap=0)

text_splitter.split_documents(docs)
#### 5.4.2 按token来切割文档
tiktoken怎么计算分块的长度
from langchain.text_splitter import CharacterTextSplitter

#初始化切分器
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
    chunk_size=100, #切分的文本块大小,一般通过长度函数计算
    chunk_overlap=0, #切分的文本块重叠大小,一般通过长度函数计算
)
loader = TextLoader("doc/NBA新闻.txt",encoding="utf-8")
# 加载文档
loader.load_and_split(text_splitter=text_splitter)

text_splitter = CharacterTextSplitter(separator='\n',chunk_size=200,chunk_overlap=0)
texts = text_splitter.split_documents(docs)
#### 5.4.3 代码文档切割
from langchain.text_splitter import (
    RecursiveCharacterTextSplitter,
    Language,
)

#支持解析的编程语言
#[e.value for e in Language]
#要切割的代码文档
PYTHON_CODE = """
def hello_world():
    print("Hello, World!")
#调用函数
hello_world()
"""
py_spliter = RecursiveCharacterTextSplitter.from_language(
    language=Language.PYTHON,
    chunk_size=10,
    chunk_overlap=0,
)
py_spliter.create_documents([PYTHON_CODE])
### 5.5文档向量化
#### 5.5.1向量模型

DashScopeEmbeddings

DashScopeEmbeddings 是一种服务,主要用于生成和处理文本嵌入向量。以下是关于 DashScopeEmbeddings 的详细介绍:

1. 服务概述
   - DashScopeEmbeddings 是一种基于阿里云百炼大模型服务平台的文本嵌入服务。
   - 它通过标准的API提供多种模型服务,支持文本Embedding的模型,其中文名为通用文本向量,英文名为text-embedding-v1。
   - 该服务允许用户方便地通过DashScope API调用来获得输入文本的embedding向量。
2. 前提条件
   - 需要开通阿里云百炼大模型服务产品。
   - 需要创建API_KEY以获取API-KEY。
3. 安装环境
   - 需要安装Python,版本要求在3.8到3.12之间。
   - 需要通过pip安装相关的库,如`pip install llama-index-core` 和 `pip install llama-index-embeddings-dashscope`。
4. 使用示例
   - 示例代码展示了如何在Llama-Index中调用DashScopeEmbedding服务。
   - 需要使用API-KEY替换示例中的占位符`YOUR_DASHSCOPE_API_KEY`,以便代码能够正常运行。
   - 代码会创建一个`DashScopeEmbedding`对象,并调用其`get_text_embedding_batch`方法来获取一批文本的embedding向量。
5. 应用场景
   - DashScopeEmbeddings 可用于各种需要文本向量表示的场景,如语义搜索、文本分类、信息检索等。
   - 通过在向量空间中表示文本,可以进行高效的语义相似度计算,从而找到在语义上最相似的文本片段。
6. 注意事项
   - 在使用DashScopeEmbeddings服务时,需要确保遵循相关的服务条款和使用限制。
   - 对于大规模的文本处理任务,可能需要考虑性能优化和资源管理。

总结来说,DashScopeEmbeddings 是一种功能强大的文本嵌入服务,通过它可以轻松地生成和处理文本的embedding向量,为各种自然语言处理任务提供有力的支持。

两个方法:(通过父类查看)

\- `embed_documents`: 输入一个文档列表,返回一个二维数组,每个元素是一个文档的向量。

\- `embed_query`: 输入一个查询,返回一个向量。
from langchain_community.embeddings.dashscope import DashScopeEmbeddings

embeddings = DashScopeEmbeddings()
embeddings.embed_documents(["你好"])
ret = embeddings.embed_query("你是谁?")
#### 5.5.2 BGE\BCE\M3E
### 5.6向量数据库
#### 5.6.1 Chroma

Chroma向量数据库(也称为ChromaDB)是一个开源的向量数据库,主要用于AI和机器学习场景。以下是关于Chroma的详细介绍:

1. 主要功能
   - ChromaDB主要用于存储和查询向量数据,这些数据通常是通过嵌入(embedding)算法从文本、图像等数据转换而来的。
   - 它的设计目标是简化大模型应用的构建过程,允许开发者轻松地将知识、事实和技能等文档整合进大型语言模型(LLM)中。
2. 特点
   - **轻量级**:ChromaDB是一个基于向量检索库实现的轻量级向量数据库。
   - **易用性**:提供简单的API,易于集成和使用。
   - **功能丰富**:支持存储嵌入及其元数据、嵌入文档和查询、搜索嵌入等功能。
   - **集成**:可以直接插入LangChain、LlamaIndex、OpenAI等。
   - **多语言支持**:包括Python和JavaScript客户端SDK。
   - **开源**:采用Apache 2.0开源许可。
3. 性能
   - Chroma使用高效的索引结构,如倒排索引、KD-树或基于图的索引,以加快向量搜索速度。
   - 它支持多种向量相似度度量标准,包括欧氏距离、余弦相似度等,使其可以广泛应用于各种场景。
4. 使用方式
   - Chroma提供了多种使用方式,包括内存模式、client模式和Server模式,以及支持数据持久化的功能。
   - 开发者可以通过简单的API调用,创建数据集、写入数据、查询数据等。
5. 限制
   - 目前只支持CPU计算,不支持GPU加速。
   - 功能相对简单,但计划未来推出托管产品,提供无服务器存储和检索功能,支持向上和向下扩展。

总的来说,Chroma向量数据库为研究人员和开发者提供了一个有用的工具,使他们能够利用词向量来处理自然语言数据并改善各种NLP任务的性能。同时,其轻量级、易用性和功能丰富的特点也使得它成为许多项目的首选。

# 引入向量化的类
from langchain_community.vectorstores import Chroma
from langchain.embeddings.dashscope import DashScopeEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain_community.document_loaders import TextLoader

doc = TextLoader("./doc/NBA新闻.txt",encoding='utf-8').load()
spliter = CharacterTextSplitter("\n",chunk_size=1000, chunk_overlap=0)
chunks = spliter.split_documents(doc)
# 实例化
embeddings = DashScopeEmbeddings()
# 创建向量数据库,向量化文档
db = Chroma.from_documents(chunks,embeddings, persist_directory="./chroma")
db.persist()
# 添加文档
doc = TextLoader("./doc/NBA新闻.txt",encoding='utf-8').load()
spliter = CharacterTextSplitter("\n",chunk_size=200, chunk_overlap=0)
chunks = spliter.split_documents(doc)
# 添加文档的方法
db.add_documents(chunks)
db.__len__()
# 对数据进行加载
db1 = Chroma(persist_directory="./chroma/zhisk1", embedding_function=embeddings)
db1.__len__()
# 召回相似的数据块
rets = db.similarity_search("2024冠军球队是谁",k=2)
# 直接拼接prompt
prompt = ""
for ret in rets:
    prompt+=ret.page_content +  "\n"
prompt += "请根据上面内容回答:"+"2024冠军球队是谁"
print(prompt)

from langchain_community.llms import Tongyi

llm = Tongyi()
llm.invoke(prompt)

from langchain_community.llms import Tongyi
# 检索问答
from langchain.chains import RetrievalQA

llm = Tongyi()
# 实例化
qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=db.as_retriever())
print(qa.invoke("2024年NBA冠军是谁"))
#### 5.6.2 Faiss
Faiss向量数据库,实际上是Facebook AI Research团队开源的一个高性能的向量相似性搜索库,主要用于处理大规模向量数据集并快速搜索最相似的向量。以下是关于Faiss的详细概述:

1. 基本信息

- **开发单位**:Facebook AI Research
- **主要功能**:在大规模向量数据集中进行快速相似性搜索
- **应用领域**:广泛应用于图像搜索、文本搜索、推荐系统等

2. 主要特性

- **高性能**:Faiss通过优化索引结构和并行计算能力,在大规模数据上实现快速搜索。
- **多平台支持**:支持CPU和GPU两种模式,GPU模式可利用CUDA进行加速。
- **多种索引类型**:支持Flat、IVF、PQ等多种索引类型,以适应不同场景下的需求。
- **灵活性**:可以根据数据集大小和维度选择合适的算法,以及实现代价最高的计算步骤在GPU上解决线性计算问题。

3. 工作原理

- **核心思想**:将向量空间嵌入到更紧致、更容易处理的空间,同时保持原有向量间的相对位置关系。
- **过程**:一般涉及两个步骤:量化和编码。量化是通过聚类或其他技术将向量量化为索引中的桶,编码则是将量化后的向量进行编码以节省存储空间。

4. 索引类型

- **暴力搜索索引(精确)**:在CPU或GPU上使用基于余弦相似度或欧式距离的全量计算,寻找最相近的向量。
- **逼近索引(近似)**:借助聚类或其他技术做相似度近似计算,结果通常不精确但计算上更高效。

5. 安装和使用

- **安装**:可以通过pip或conda进行安装,支持CPU和GPU两种模式。
- **使用**:包括创建索引、添加向量到索引、进行相似性搜索等基本操作。Faiss还支持其他高级功能,如聚类、PQ索引等。

6. 性能分析

- Faiss在相似性搜索方面表现出色,主要得益于其优化的索引结构和并行计算能力。

7. 应用场景

- Faiss广泛应用于推荐系统、信息检索、语义搜索、计算机视觉等现实任务中。

综上所述,Faiss向量数据库是一个功能强大、性能卓越的向量相似性搜索库,适用于处理大规模向量数据集并进行快速相似性搜索。
# 导入所需的模块和类
from langchain.embeddings import CacheBackedEmbeddings
from langchain.storage import LocalFileStore
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS
from langchain.embeddings.dashscope import DashScopeEmbeddings
from langchain_text_splitters import CharacterTextSplitter

# 实例化向量嵌入器
embeddings = OpenAIEmbeddings()
# 初始化缓存存储器
store = LocalFileStore("./faiss/")
# 创建缓存支持的嵌入器
cached_embeddings = CacheBackedEmbeddings.from_bates_store(
    embeddings,
    store,
    namespace=embeddings.model,
)
# 加载文档并将其踹分成片段
doc = TextLoader("../data/day06.md", encoding='utf-8').load()
spliter = CharacterTextSplitter("\n", chunk_size=200, chunk_overlap=0)
chunks = spliter.split_documents(doc)
# 创建向量存储
db = FAISS.from_documents(chunks, cached_embeddings)
db.similarity_search("NBA冠军球队是哪个?", k=2)
#### 5.6.3 Milvus

Milvus是一个开源的向量数据库,由Zilliz公司发起并维护。它专为处理非结构化数据而设计,能够存储、检索和分析大量的向量数据。以下是关于Milvus的详细介绍:

一、基本概述

- **名称由来**:Milvus的名字来源于拉丁语,意为“一万”,象征着其处理大规模数据集的能力。
- **创建时间**:Milvus创建于2019年,其唯一目标是存储、索引和管理由深度神经网络和其他机器学习(ML)模型生成的大规模嵌入向量。
- **数据处理能力**:作为一个专门设计用于处理输入向量查询的数据库,Milvus能够处理万亿级别的向量索引。

二、核心特性

1. **高性能**:Milvus提供了高效的向量搜索能力,支持毫秒级的最近邻搜索,即使在亿级向量规模下也能保持高性能。
2. **高可用、高可靠**:Milvus支持在云上扩展,其容灾能力能够保证服务高可用。同时,它采用共享存储架构,存储计算完全分离,计算节点支持横向扩展。
3. **灵活性与兼容性**:Milvus与多种机器学习框架兼容,如TensorFlow、PyTorch和PaddlePaddle,可以轻松集成到现有的机器学习工作流程中。此外,它还提供了简单易用的API,支持多种编程语言,如Python、Java和Go。
4. **多索引类型**:Milvus支持多种索引类型,如FLAT、IVF、HNSW等,以适应不同的搜索性能和存储效率需求。
5. **混合查询**:Milvus支持在向量相似度检索过程中进行标量字段过滤,实现混合查询,进一步提高了召回率和搜索的灵活性。

三、系统架构

Milvus的系统架构分为四个层次:

- **接入层(Access Layer)**:由一组无状态proxy组成,对外提供用户连接的endpoint,负责验证客户端请求并合并返回结果。
- **协调服务(Coordinator Service)**:充当系统的大脑,负责分配任务给执行节点。协调服务共有四种角色,分别为root coord、data coord、query coord和index coord。
- **执行节点(Worker Node)**:负责完成协调服务下发的指令和proxy发起的数据操作语言(DML)命令。执行节点分为三种角色,分别为data node、query node和index node。
- **存储层(Storage)**:负责Milvus数据的持久化,分为元数据存储(meta store)、消息存储(log broker)和对象存储(object storage)三个部分。

四、应用场景

Milvus适用于需要处理大规模向量数据的场景,特别是在以下领域有广泛应用:

- **机器学习**:在机器学习模型训练后,Milvus可以用来存储和搜索模型生成的向量。
- **计算机视觉**:用于图像和视频分析中的向量搜索,如图像匹配、相似图像搜索等。
- **语音识别**:在语音识别系统中,Milvus可以用来检索与查询语音最相似的向量。
- **推荐系统**:在推荐系统中,Milvus可以用来找到用户可能感兴趣的商品或内容。
- **自然语言处理**:Milvus可以用来检索与查询文本最相关的文档或句子。

此外,Milvus还可以应用于音频相似性搜索、分子相似性搜索等多个领域,为这些领域的数据处理和分析提供强大的支持。

综上所述,Milvus作为一款开源的向量数据库,以其高性能、高可用、高可靠和灵活性等特性,在机器学习、计算机视觉、语音识别等多个领域有着广泛的应用前景。

安装使用

docker安装

CentOS 7 安装 Docker 的步骤如下:

1. 卸载旧版本的 Docker(如果有):

```
sudo yum remove docker \                  docker-client \                  docker-client-latest \                  docker-common \                  docker-latest \                  docker-latest-logrotate \                  docker-logrotate \                  docker-engine
```

1. 安装 Docker 依赖的软件包:

```
sudo yum install -y yum-utils
```

1. 设置 Docker 仓库:

```
sudo yum-config-manager \    --add-repo \    https://download.docker.com/linux/centos/docker-ce.repo
```

1. 安装 Docker Engine-Community:

```
sudo yum install docker-ce docker-ce-cli containerd.io
```

1. 更新镜像

   ~~~
   sudo mkdir -p /etc/docker
   sudo tee /etc/docker/daemon.json <<-'EOF'
   {
     "registry-mirrors": ["https://gt8iqili.mirror.aliyuncs.com"]
   }
   EOF
   sudo systemctl daemon-reload
   sudo systemctl restart docker
   ~~~

   

2. 启动 Docker 服务:

```
sudo systemctl start docker
```

1. 验证 Docker 是否正确安装:

```
sudo docker run hello-world
```

这些命令应以 root 用户或使用 sudo 执行。每一步都需要网络连接以从 Docker 仓库下载所需的包。

docker pull milvusdb/milvus:latest

docker run -d --name milvus_server -p 19530:19530 -p 9091:9091 milvusdb/milvus:latest

docker ps查看正在运行的镜像

测试

pip3 install pymilvus

from pymilvus import Collection, connections, DataType  
  
# 连接到Milvus服务器  
connections.connect("default", host="124.71.227.70", port="19530")  
  
# 创建集合  
field_schemas = [  
    {"name": "id", "dtype": DataType.INT64, "is_primary": True, "auto_id": True},  
    {"name": "embedding", "dtype": DataType.FLOAT_VECTOR, "dim": 128}  
]  
collection_schema = CollectionSchema(fields=field_schemas, description="test collection")  
collection = Collection("test_collection", schema=collection_schema)  
  
# 插入向量  
vectors = [[random.random() for _ in range(128)] for _ in range(10)]  
collection.insert(vectors)  
  
# 执行搜索  
query_vector = [random.random() for _ in range(128)]  
search_params = {"metric_type": "L2", "params": {"nprobe": 10}}  
results = collection.search(query_vector, anns_field="embedding", params=search_params, limit=5)  
  
# 打印搜索结果  
print(results)
## **四、Chains and Memory**
### 8.1.1 **什么是链**
在简单应用中,单独使用LLM是可以的,但更复杂的应用需要将LLM进行链接。例如,我们可以创建一个链,该链接收用户输入,使用PromptTemplate对其进行格式化,然后将格式化后的响应传递给LLM。链允许我们将多个组件组合在一起创建一个单一的、连贯的应用。

四个常用的链:

\- LLMChain:一个链,将一个LLM和一个PromptTemplate组合在一起。

\- SimpleSequentialChain:一个简单的链,将一个链的输出作为下一个链的输入。

\- SequentialChain:一个更复杂的链,允许我们定义多个链,并将它们链接在一起。

\- ConversationChain:一个链,将一个LLM和一个ConversationPromptTemplate组合在一起。
# Chain

### 8.2 LLMChain
#### 8.2.1 LLMChain介绍
LLMChain是一个在语言模型周围添加功能的简单链,它被广泛地应用于LangChain中,包括其他链和代理。以下是关于LLMChain的详细解释:

1. 定义与结构
   - LLMChain由一个PromptTemplate和一个语言模型(LLM或聊天模型)组成。
   - 它接受用户输入,使用PromptTemplate进行格式化,然后将格式化后的响应传递给LLM。
2. 应用背景
   - 在LangChain这样的框架中,LLMChain被用作构建基于大语言模型(LLM)应用程序的重要组件。
   - LangChain旨在简化LLM应用程序的开发过程,并提供了一系列模块和工具来支持常见的用例。
3. 使用场景
   - LLMChain的使用场景广泛,包括但不限于聊天机器人、智能问答工具、文档分析和摘要、代码分析等。
   - 开发人员可以使用LLMChain来构建上下文感知、推理应用程序,实现从原型到生产环境的快速转化。
4. 优势与特点
   - 模块化设计:LLMChain作为LangChain框架的一部分,采用模块化设计,使得开发者能够更加便捷地进行模块化的开发和维护。
   - 易用性强:LangChain框架提供了丰富的API和文档,让开发者可以更加便捷地进行LLM应用的开发。
   - 高性能:LangChain框架在底层进行了优化,保证了LLM应用的高性能,使得开发者可以在保证性能的前提下,更加专注于业务逻辑的实现。
5. 工作原理
   - 当用户输入一个提示时,LLMChain首先使用PromptTemplate对输入进行格式化。
   - 然后,它将格式化后的提示传递给LLM(如ChatGPT),由LLM生成相应的回答或响应。
   - 最后,LLMChain将LLM生成的响应返回给用户。
6. 集成与合作
   - LLMChain可以与其他LangChain组件(如Agents、Chains、Memory等)进行集成,以构建更复杂、更强大的LLM应用程序。
   - 它还可以与各种LLM和聊天模型进行配合,如OpenAI的GPT系列模型,以实现更丰富的功能和更高的性能。
7. 发展趋势
   - 随着人工智能技术的不断发展,LLMChain和LangChain等框架将继续在LLM应用开发领域发挥重要作用。
   - 未来,这些框架可能会进一步优化性能、提升易用性,并扩展更多的应用场景和功能。
#### 8.2.2 LLMChain案例
from langchain.chains.llm import LLMChain
from langchain_community.llms import Tongyi
from langchain_community.llms.tongyi import Tongyi

llm = Tongyi()
promptTemplate = PromptTemplate.from_template("你是起名大师,我家是{sex}宝,姓{firstName},请起3个好养的名字?")

# 多个参数
chain = LLMChain(llm=llm, prompt=promptTemplate, verbose=True)
ret = chain.invoke({'sex': "男",'firstname': "翟"})
### 8.3 SimpleSequentialChain
#### 8.3.1 SimpleSequentialChain介绍
只支持固定的链路

SimpleSequentialChain是Langchain框架中的一种顺序链类型,它的主要特点是每个步骤都具有单一输入/输出,并且一个步骤的输出是下一个步骤的输入。以下是关于SimpleSequentialChain的详细解释:

1. 定义与结构
   - SimpleSequentialChain是顺序链的最简单形式,它允许用户将多个单输入/单输出链连接成一个链。
   - 在这种链中,每个步骤的输出都会直接传递给下一个步骤作为输入。
2. 特点
   - **单一输入输出**:每个步骤都接受一个输入并产生一个输出,这个输出将作为下一个步骤的输入。
   - **顺序执行**:步骤按照定义的顺序依次执行,确保数据按照预期的方式流动。
   - **易于理解**:由于其简单性,SimpleSequentialChain易于理解和实现,特别适用于简单的场景。
3. 使用场景
   - SimpleSequentialChain适用于那些需要按顺序执行多个单输入/单输出步骤的场景。
   - 例如,在一个文本处理任务中,可能需要先对文本进行分词,然后对每个词进行词性标注,最后进行命名实体识别。这些步骤可以作为一个SimpleSequentialChain来执行。
4. 与SequentialChain的区别
   - SequentialChain是更通用的顺序链形式,它允许多个输入/输出,并且步骤之间可以有更复杂的依赖关系。
   - 相比之下,SimpleSequentialChain更加简单和直接,适用于那些不需要复杂依赖关系的场景。
5. 示例
   - 假设我们有一个简单的文本处理任务,需要先将文本转换为小写,然后去除标点符号,最后进行分词。这三个步骤可以作为一个SimpleSequentialChain来执行。
   - 在这个示例中,第一个步骤的输入是原始文本,输出是转换为小写的文本;第二个步骤的输入是转换为小写的文本,输出是去除标点符号的文本;第三个步骤的输入是去除标点符号的文本,输出是分词后的结果。
#### 8.3.2案例
from langchain.chains.llm import LLMChain
from langchain_community.llms import Tongyi

llm = Tongyi()
from langchain.prompts import ChatPromptTemplate
from langchain.chains.sequential import SimpleSequentialChain

#chain 1
first_prompt = ChatPromptTemplate.from_template("帮我给{product}的公司起一个响亮容易记忆的名字?")
chain_one = LLMChain(
    llm=llm,
    prompt=first_prompt
)

#chain 2
second_prompt = ChatPromptTemplate.from_template("用5个词来描述一下这个公司名字:{company_name}")
chain_two = LLMChain(
    llm=llm,
    prompt=second_prompt
)
# 实例化
simple_chain = SimpleSequentialChain(
    chains=[chain_one, chain_two],
    verbose=True,#打开日志
)
simple_chain.invoke("动漫制作")
### 8.4 SequentialChain

#### 8.4.1介绍
支持多个链路的顺序执行

SequentialChain是LangChain库中的一个重要概念,它允许用户将多个链(Chain)按照特定的顺序连接起来,形成一个处理流程。以下是关于SequentialChain的详细解释:

定义与结构

- **SequentialChain**:它是一种链式结构,可以将多个LLMChain(或其他类型的Chain)按照特定的顺序连接起来。这种结构使得一个大任务可以被分解为多个小任务,并依次执行。
- **结构特点**:SequentialChain允许用户定义任务的执行顺序,并且每个任务的输出可以作为下一个任务的输入。这种机制非常适合处理具有依赖关系的任务序列。

特点

1. **顺序执行**:SequentialChain中的任务按照用户定义的顺序依次执行,确保数据按照预期的方式流动。
2. **灵活组合**:用户可以根据需要自由组合不同的Chain,形成复杂的处理流程。
3. **可扩展性**:SequentialChain的设计允许用户轻松地添加新的任务或修改现有任务的顺序。
4. **数据传递**:SequentialChain支持任务之间的数据传递,即一个任务的输出可以作为下一个任务的输入。

类型

- **SimpleSequentialChain**:这是SequentialChain的最简单形式,其中每个步骤都具有单一输入/输出,并且一个步骤的输出是下一个步骤的输入。
- **SequentialChain**:更通用形式的顺序链,允许多个输入/输出,可以处理更复杂的场景。

使用场景

- **文本处理**:例如,将一个长文本首先进行文本清洗,然后进行情感分析,最后生成摘要。这三个任务可以按照顺序连接成一个SequentialChain。
- **问答系统**:在处理复杂问题时,可以将问题解析、信息检索和答案生成等任务连接成一个SequentialChain,实现自动化的问答功能。

示例

假设我们有一个任务是将用户输入的文本转换为小写,然后统计其中的单词数。这个任务可以分解为两个子任务:文本转小写和单词计数。这两个子任务可以连接成一个SequentialChain:

1. **文本转小写Chain**:接收用户输入的文本作为输入,输出转换为小写后的文本。
2. **单词计数Chain**:接收小写文本作为输入,输出文本中的单词数。

通过将这两个Chain按照顺序连接起来,我们就可以形成一个完整的SequentialChain来处理用户的输入文本了。

总之,SequentialChain是LangChain库中一个非常强大的工具,它允许用户将多个任务连接成一个处理流程,并按照特定的顺序执行这些任务。这种机制可以大大提高任务的执行效率和准确性。
#### 8.4.2案例
from langchain.prompts import ChatPromptTemplate
from langchain.chains.sequential import SequentialChain

#chain 1 任务:翻译成中文
first_prompt = ChatPromptTemplate.from_template("把下面内容翻译成中文:\n\n{content}")
chain_one = LLMChain(
    llm=llm,
    prompt=first_prompt,
    # verbose=True,
    output_key="Chinese_Rview",
)
#chain 2 任务:对翻译后的中文进行总结摘要 input_key是上一个chain的output_key
second_prompt = ChatPromptTemplate.from_template("用一句话总结下面内容:\n\n{Chinese_Rview}")
chain_two = LLMChain(
    llm=llm,
    prompt=second_prompt,
    # verbose=True,
    output_key="Chinese_Summary",
)
#chain 3 任务:智能识别语言 input_key是上一个chain的output_key
third_prompt = ChatPromptTemplate.from_template("根据下面内容写5条评价信息:\n\n{Chinese_Summary}")
chain_three = LLMChain(
    llm=llm,
    prompt=third_prompt,
    # verbose=True,
    output_key="Language",
)
#chain 4 任务:针对摘要使用指定语言进行评论 input_key是上一个chain的output_key   
fourth_prompt = ChatPromptTemplate.from_template("请使用指定的语言对以下内容进行回复:\n\n内容:{Chinese_Summary}\n\n语言:{Language}")
chain_four = LLMChain(
    llm=llm,
    prompt=fourth_prompt,
    verbose=True,
    output_key="Reply",
)
#overall 任务:翻译成中文->对翻译后的中文进行总结摘要->智能识别语言->针对摘要使用指定语言进行评论
overall_chain = SequentialChain(
    chains=[chain_one, chain_two, chain_three, chain_four],
    verbose=True,
    input_variables=["content"],
    output_variables=["Chinese_Rview", "Chinese_Summary", "Language"],
)
content = "I am a student of Cumulus Education, my course is artificial intelligence, I like this course, because I can get a high salary after graduation"
ret = overall_chain.invoke(content)
### 8.5 RouterChain

#### 8.5.1介绍

RouterChain 是一个特殊的 Chain,它可以根据输入动态选择下一个 Chain。

RouterChain 由两个组件组成:

RouterChain (负责选择下一个要调用的链条)

destination_chains: 路由器链条可以路由到的链条

RouterChain 包括不同类型的路由链条。下面的例子是 MultiPromptChain 中的使用方式,以创建一个问答链条,该链条根据给定的问题选择最相关的提示,并使用该提示回答问题。

RouterChain在LangChain库中扮演着重要的角色,它主要用于实现基于条件判断的路由功能,使得智能体或应用能够根据输入内容动态地选择不同的处理流程。以下是关于RouterChain的详细解释:

定义

RouterChain,也称为分支链,它能够根据输入内容动态地选择并调用下一个链。在智能体的可视化编排流程中,RouterChain扮演着决策中心的角色,通过灵活的意图配置和链连接点,为智能体提供了强大的决策能力。

特点

1. **条件判断**:RouterChain的核心功能是根据输入的提示词或指令进行条件判断,以确定下一步应该调用哪个链。
2. **动态选择**:基于判断结果,RouterChain能够动态地选择一个或多个目标链进行调用,从而实现不同的处理流程。
3. **意图配置**:RouterChain允许用户定制决策逻辑,包括传入对话历史记录、输出关键词等配置项,以满足不同的应用场景需求。
4. **链连接点**:RouterChain具有链输入连接点和链输出连接点,这些连接点是其决策功能的实现基础。链输入连接点允许RouterChain与其他任意链相连,接收输入信息;链输出连接点需要配置意图,每个意图对应一个链输出连接点。

工作原理

1. **输入接收**:RouterChain接收来自用户的输入,该输入可以是文本、语音或其他形式的数据。
2. **条件判断**:根据用户输入的内容,RouterChain会调用内置的AI模型进行条件判断。这些判断可以基于关键词匹配、语义理解等多种算法实现。
3. **目标链选择**:基于判断结果,RouterChain会选择一个或多个目标链进行调用。这些目标链可以是预先定义好的处理流程,也可以是用户自定义的链。
4. **输出传递**:RouterChain将用户输入传递给选定的目标链,并等待目标链的处理结果。一旦目标链处理完成,RouterChain会将结果返回给用户或传递给下一个处理环节。

应用场景

RouterChain在多个领域都有广泛的应用,包括但不限于:

- **智能客服**:在智能客服系统中,RouterChain可以根据用户的问题类型选择不同的回复模板或处理流程,提高客服效率和用户体验。
- **推荐系统**:在推荐系统中,RouterChain可以根据用户的兴趣和行为数据选择不同的推荐算法或模型,实现更精准的个性化推荐。
- **自然语言处理**:在自然语言处理领域,RouterChain可以根据文本内容选择不同的分析器或处理流程,实现更复杂的文本理解和处理任务。

总结

RouterChain是LangChain库中一个重要的组件,它通过条件判断和动态选择机制实现了智能体的决策功能。在实际应用中,RouterChain可以根据不同的输入内容和应用场景灵活配置决策逻辑和链连接点,为智能体提供了强大的决策能力和适应性。

#### 8.5.2案例
from langchain.chains.llm import LLMChain
from langchain.prompts import PromptTemplate
from langchain.chains.conversation.base import ConversationChain
from langchain.chains.router.llm_router import LLMRouterChain,RouterOutputParser
from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE
from langchain.chains.router import MultiPromptChain

#物理链
physics_template = """您是一位非常聪明的物理教授.\n
您擅长以简洁易懂的方式回答物理问题.\n
当您不知道问题答案的时候,您会坦率承认不知道.\n
下面是一个问题:
{input}"""
physics_prompt = PromptTemplate.from_template(physics_template)
# 物理的任务
physicschain = LLMChain( llm=llm,prompt=physics_prompt,verbose=True)
#数学链
math_template = """您是一位非常优秀的数学教授.\n
您擅长回答数学问题.\n
您之所以如此优秀,是因为您能够将困难问题分解成组成的部分,回答这些部分,然后将它们组合起来,回答更广泛的问题.\n
下面是一个问题:
{input}"""
math_prompt = PromptTemplate.from_template(math_template)
###数学的任务
mathschain = LLMChain(llm=llm, prompt=math_prompt, verbose=True)
# 默认任务
default_chain = ConversationChain(
    llm = llm,
    output_key="text"
)
# 组合路由任务
destination_chains = {}  
destination_chains["physics"] = physicschain
destination_chains["math"] = mathschain
# 定义路由Chain
router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(destinations="physics:擅长回答物理问题\n math:擅长回答数学问题")
router_prompt = PromptTemplate(
    template=router_template,
    input_variables=["input"],
    output_parser=RouterOutputParser()
)
router_chain = LLMRouterChain.from_llm(
    llm,
    router_prompt
)
chain = MultiPromptChain(
    router_chain=router_chain,
    destination_chains=destination_chains,
    default_chain=default_chain,
    verbose=True
)
# question = "什么是牛顿第一定律?"
# print(router_chain.invoke(question))
# chain.run(question)
# question = "2+2等于几?"
# print(router_chain.invoke(question))
# print(chain.run("2+2等于几?"))
question = "你是谁?"
# print(router_chain.invoke(question))
chain.run(question)
### 8.6 TransformChain
#### 8.6.1介绍

通用的转换链。下面的例子是创建一个虚拟转换,它接收一个超长的文本,将文本过滤为仅保留前三个段落,然后将其传递给 LLMChain 进行摘要生成。

TransformChain在LangChain库中是一个重要的组件,主要用于处理chains之间的输入和输出数据,以便于chains之间的数据传输。以下是关于TransformChain的详细解释:

定义

TransformChain是一个框架或工具,它允许用户定义自定义的转换函数,这些函数可以应用于chains之间的数据。这意味着,当数据从一个chain传递到另一个chain时,TransformChain可以提供一个或多个转换步骤,以修改或格式化数据,使其符合下一个chain的输入要求。

特点

1. **自定义转换**:TransformChain支持用户定义自己的转换函数,这些函数可以根据具体需求对输入数据进行处理。
2. **数据处理**:TransformChain的主要作用是对chains之间的数据进行处理,确保数据在传递过程中满足下一个chain的输入要求。
3. **灵活性**:由于支持自定义转换函数,TransformChain具有很高的灵活性,可以适应各种复杂的数据处理场景。

工作原理

- **数据接收**:TransformChain接收来自上一个chain的输出数据。
- **数据转换**:TransformChain应用用户定义的转换函数对接收到的数据进行处理。这些转换函数可以是简单的数据清洗、格式化,也可以是复杂的逻辑处理。
- **数据输出**:经过转换的数据被传递给下一个chain作为输入。
#### 8.6.2案例
from langchain.chains.llm import LLMChain
from langchain.prompts import PromptTemplate
from langchain.chains.transform import TransformChain
from langchain.chains.sequential import SimpleSequentialChain

# 第一个任务
def transform_func(inputs:dict) -> dict:
    text = inputs["text"]
    shortened_text = "\n".join(text.split("\n")[:3])
    return {"output_text":shortened_text}
#文档转换链
transform_chain = TransformChain(
    input_variables=["text"],
    output_variables=["output_text"],
    transform=transform_func
)
# 第二个任务
template = """对下面的文字进行总结:
{output_text}
总结:"""
prompt = PromptTemplate(
    input_variables=["output_text"],
    template=template
)
llm_chain = LLMChain(
    llm = llm,
    prompt=prompt
)
#使用顺序链连接起来
squential_chain = SimpleSequentialChain(
    chains=[transform_chain,llm_chain],
    verbose=True
)
with open("NBA/NBA新闻.txt",encoding='utf-8') as f:
    letters = f.read()
squential_chain.invoke(letters)
### 8.7 四种文档处理链

####  8.7.1 StuffDocumentsChain 整合


这种链最简单直接,是将所有获取到的文档作为 context 放入到 Prompt 中,传递到 LLM 获取答案。这种方式可以完整的保留上下文,调用 LLM 的次数也比较少,建议能使用 stuff 的就使用这种方式。其适合文档拆分的比较小,一次获取文档比较少的场景,不然容易超过 token 的限制。

StuffDocumentsChain在LangChain框架中是一个用于处理文档的关键组件,它主要用于将多个文档组装成一个提示(prompt),并将这个提示传递给大模型(LLM)。以下是关于StuffDocumentsChain的详细解释:

1. 作用
   - StuffDocumentsChain的主要作用是将多个文档的内容整合到一个单一的提示中,然后将其传递给大模型。这样做可以确保需要的信息都被传递,进而从大模型中获取所需的上下文和响应。
2. 使用场景
   - 当需要处理多个文档,并且这些文档的内容需要一起作为输入传递给大模型时,可以使用StuffDocumentsChain。例如,在处理一个包含多篇相关文章的文档集合,并希望将这些文章的内容作为一个整体输入给大模型进行摘要、问答等任务时。
3. 优点
   - 简单易用:StuffDocumentsChain提供了一种简单的方式,将多个文档的内容合并成一个提示,从而简化了与大模型的交互。
   - 保持上下文完整性:通过将多个文档的内容整合到一个提示中,可以确保大模型在处理时能够考虑到这些文档之间的上下文关系。
4. 注意事项
   - 当文档的数量或长度非常大时,可能会超出大模型的上下文窗口限制,导致信息丢失或模型性能下降。因此,在使用StuffDocumentsChain时,需要注意文档的数量和长度,并考虑是否需要进行适当的分割或简化。
5. 与其他组件的协同
   - StuffDocumentsChain通常与其他LangChain组件一起使用,如LLM(大模型)加载器、文档加载器等,以构建一个完整的文档处理流程。例如,可以使用文档加载器从外部数据源加载文档,然后使用StuffDocumentsChain将文档整合成提示,最后传递给大模型进行处理。

综上所述,StuffDocumentsChain是LangChain框架中一个重要的文档处理组件,它通过将多个文档的内容整合到一个提示中,并传递给大模型,从而简化了与大模型的交互,并保持了上下文的完整性。

from langchain.chains.combine_documents.stuff import StuffDocumentsChain
from langchain.chains.llm import LLMChain
from langchain.prompts import PromptTemplate
from langchain_community.document_loaders import PyPDFLoader,TextLoader
from langchain.text_splitter import CharacterTextSplitter

# 1 定义prompt模板
prompt_template = """对以下文字做简洁的总结:
{text}
简洁的总结:"""
prompt = PromptTemplate.from_template(prompt_template)

# 2 定义任务
llm_chain = LLMChain(llm=llm, prompt=prompt)

# 3、定义chain
stuff_chain = StuffDocumentsChain(
    llm_chain=llm_chain,
    document_variable_name="text",
)

# loader = PyPDFLoader("doc/demo.pdf")
loader = TextLoader("doc/NBA新闻.txt",encoding='utf-8')
docs = loader.load()

# split = CharacterTextSplitter("\n",chunk_size = 200)
# text = split.split_documents(docs)

print(stuff_chain.run(docs))
#### 8.7.2 RefineDocumentsChain 递归


通过迭代更新的方式获取答案。先处理第一个文档,作为 context 传递给 llm,获取中间结果 intermediate answer。然后将第一个文档的中间结果以及第二个文档发给 llm 进行处理,后续的文档类似处理。Refine 这种方式能部分保留上下文,以及 token 的使用能控制在一定范围。

RefineDocumentsChain是LangChain框架中的一个重要组件,它用于顺序地处理多个文档,并通过迭代的方式改进生成的答案。以下是关于RefineDocumentsChain的详细解释:

1. 基本概念

- **作用**:RefineDocumentsChain的主要作用是基于第一个文档生成初始答案,然后循环处理其余文档以改进该答案。它鼓励在多个文档之间进行信息传递,以产生更准确和完整的回答。
- **使用场景**:当需要处理多个文档,并且希望生成的答案能够综合所有文档的信息时,可以使用RefineDocumentsChain。例如,在问答系统中,当用户的问题涉及多个文档时,可以使用RefineDocumentsChain来生成一个综合所有文档信息的答案。

2. 工作原理

- **顺序处理**:RefineDocumentsChain按照顺序处理文档,首先基于第一个文档生成初始答案。
- **迭代改进**:然后,它循环处理其余文档,每次使用当前文档的内容和之前生成的答案作为上下文,生成一个新的答案。这个过程不断迭代,直到处理完所有文档。
- **信息传递**:RefineDocumentsChain鼓励在多个文档之间进行信息传递。这意味着,在处理一个文档时,它会考虑之前文档的内容和处理结果,以确保生成的答案能够综合所有文档的信息。

3. 优点

- **准确性**:由于RefineDocumentsChain能够综合多个文档的信息,因此生成的答案通常更准确和全面。
- **可解释性**:由于RefineDocumentsChain的处理过程是顺序和可迭代的,因此可以更容易地理解和解释生成的答案是如何从多个文档中得出的。

4. 注意事项

- **文档数量**:虽然RefineDocumentsChain可以处理多个文档,但当文档数量非常大时,处理时间可能会增加。因此,在实际应用中需要根据具体情况考虑是否需要对文档进行筛选或合并。
- **上下文窗口限制**:与所有基于大模型的方法一样,RefineDocumentsChain也受到大模型上下文窗口的限制。如果文档的总长度超过了模型的上下文窗口大小,可能需要考虑对文档进行分割或选择更合适的方法来处理。

5. 示例

假设有一个问答系统,用户的问题涉及三个文档A、B和C。使用RefineDocumentsChain处理这些文档时,首先基于文档A生成初始答案。然后,在处理文档B时,会将文档B的内容和之前生成的答案作为上下文,生成一个新的答案。最后,在处理文档C时,同样会将文档C的内容和之前生成的答案作为上下文,生成最终的答案。这样,最终的答案就能够综合文档A、B和C的信息。
from langchain.chains.combine_documents.refine import RefineDocumentsChain
from langchain.chains.llm import LLMChain
from langchain.prompts import PromptTemplate
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.llms.tongyi import Tongyi
from langchain.text_splitter import CharacterTextSplitter
from langchain.chains.summarize import load_summarize_chain

# 加载问答
# loader = PyPDFLoader("doc/demo.pdf")
loader = TextLoader("doc/NBA新闻.txt",encoding='utf-8')
docs = loader.load()

#split
text_splitter = CharacterTextSplitter(separator="\n",chunk_size=400, chunk_overlap=0)
split_docs = text_splitter.split_documents(docs)

prompt_template = """对以下文字做简洁的总结:
{text}
简洁的总结:"""

prompt = PromptTemplate.from_template(prompt_template)

refine_template = (
    "你的任务是产生最终摘要\n"
    "我们已经提供了一个到某个特定点的现有回答:{existing_answer}\n"
    "我们有机会通过下面的一些更多上下文来完善现有的回答(仅在需要时使用).\n"
    "------------\n"
    "{text}\n"
    "------------\n"
    "根据新的上下文,用中文完善原始回答.\n"
    "如果上下文没有用处,返回原始回答。"
)

refine_prompt = PromptTemplate.from_template(refine_template)

chain = load_summarize_chain(
    llm=llm,
    chain_type="refine",
    question_prompt=prompt,
    refine_prompt = refine_prompt,
    return_intermediate_steps=True,
    input_key = "documents",
    output_key = "output_text",
)
result = chain.invoke({"documents":split_docs})
print(result)
print(result["output_text"])
print("\n\n".join(result["intermediate_steps"][:3]))
#### 8.7.3 MapReduceDocumentsChain 汇总

先通过 LLM 对每个 document 进行处理,然后将所有文档的答案在通过 LLM 进行合并处理,得到最终的结果。

MapReduce 的方式将每个 document 单独处理,可以并发进行调用。但是每个文档之间缺少上下文。

MapReduceDocumentsChain是LangChain框架中的一个组件,它结合了MapReduce的思想来处理文档数据。以下是关于MapReduceDocumentsChain的详细解释:

1. 基本概念

- **作用**:MapReduceDocumentsChain主要用于处理大规模的文档数据。它首先通过Map操作将每个文档传递给大语言模型(LLM),然后利用Reduce操作对处理后的文档进行简化和聚合。
- **使用场景**:当需要处理大量文档,并且文档之间可以并行处理时,MapReduceDocumentsChain是一个有效的选择。它适用于文档数量众多,且可以分割成多个独立部分进行并行计算的情况。

2. 工作原理

- Map阶段

  :

  - 将输入的文档集合分割成多个块(blocks),每个块可以并行处理。
  - 将每个文档块传递给LLM进行处理,提取关键信息或生成初步结果。

- Reduce阶段

  :

  - 收集Map阶段生成的所有初步结果。
  - 使用ReduceDocumentsChain或其他组件对初步结果进行简化、聚合或进一步处理。
  - 最终生成一个综合了所有文档信息的输出。

3. 优点

- **并行处理**:MapReduceDocumentsChain利用MapReduce的并行处理特性,可以显著提高处理大规模文档数据的效率。
- **简化文档**:通过ReduceDocumentsChain的简化操作,可以将复杂的文档数据简化为更易于理解和处理的格式。
- **灵活性**:MapReduceDocumentsChain可以与其他LangChain组件结合使用,构建出更复杂、更强大的文档处理流程。

4. 注意事项

- **文档数量**:虽然MapReduceDocumentsChain适用于处理大量文档,但当文档数量过于庞大时,可能需要考虑更高效的分割和并行处理策略。
- **上下文窗口限制**:由于LLM的上下文窗口限制,对于过长的文档或文档集合,可能需要采取分块处理或其他策略来避免信息丢失。

5. 与其他组件的协同

- **LLM**:MapReduceDocumentsChain依赖于LLM进行文档处理。LLM的选择和配置将直接影响MapReduceDocumentsChain的性能和效果。
- **ReduceDocumentsChain**:MapReduceDocumentsChain的Reduce阶段通常使用ReduceDocumentsChain或其他组件来简化和聚合初步结果。这些组件的选择和配置将影响最终输出的质量和准确性。

总之,MapReduceDocumentsChain是一个强大的文档处理组件,它结合了MapReduce的并行处理特性和LangChain的文档处理能力,适用于处理大规模、复杂的文档数据。
from langchain.chains.combine_documents.refine import RefineDocumentsChain
from langchain.chains.llm import LLMChain
from langchain.prompts import PromptTemplate
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.llms.tongyi import Tongyi
from langchain.text_splitter import CharacterTextSplitter
from langchain.chains.summarize import load_summarize_chain

# 加载问答
# loader = PyPDFLoader("doc/demo.pdf")
loader = TextLoader("NBA/NBA新闻.txt",encoding='utf-8')
docs = loader.load()
#split
text_splitter = CharacterTextSplitter(separator="\n",chunk_size=400, chunk_overlap=0)
split_docs = text_splitter.split_documents(docs)
prompt_template = """对以下文字做简洁的总结:
{text}
简洁的总结:"""
prompt = PromptTemplate.from_template(prompt_template)
refine_template = (
    "你的任务是产生最终摘要\n"
    "我们已经提供了一个到某个特定点的现有回答:{existing_answer}\n"
    "我们有机会通过下面的一些更多上下文来完善现有的回答(仅在需要时使用).\n"
    "------------\n"
    "{text}\n"
    "------------\n"
    "根据新的上下文,用中文完善原始回答.\n"
    "如果上下文没有用处,返回原始回答。"
)
refine_prompt = PromptTemplate.from_template(refine_template)
chain = load_summarize_chain(
    llm=llm,
    chain_type="refine",
    question_prompt=prompt,
    refine_prompt = refine_prompt,
    # return_intermediate_steps=True,
    input_key = "documents",
    output_key = "output_text",
    verbose=True
)
result = chain.invoke({"documents":split_docs})
print(result)
# print(result["output_text"])
# print("\n\n".join(result["intermediate_steps"][:3]))
#### 8.7.4 MapRerankDocumentsChain 选分
MapRerankDocumentsChain 和 MapReduceDocumentsChain 类似,先通过 LLM 对每个 document 进行处理,每个答案都会返回一个 score,最后选择 score 最高的答案。MapRerank 和 MapReduce 类似,会大批量的调用 LLM,每个 document 之间是独立处理。
from langchain.chains.combine_documents.map_reduce import MapReduceDocumentsChain
from langchain.chains.combine_documents.reduce import ReduceDocumentsChain
from langchain.chains.combine_documents.stuff import StuffDocumentsChain
from langchain.prompts import PromptTemplate
from langchain_community.llms.tongyi import Tongyi
from langchain.chains.llm import LLMChain
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain.chains.qa_with_sources import load_qa_with_sources_chain

#load
# loader = PyPDFLoader("doc/demo.pdf")

loader = TextLoader("doc/NBA新闻.txt",encoding='utf-8')
docs = loader.load()
#split
text_splitter = CharacterTextSplitter(separator="\n",chunk_size=200, chunk_overlap=0)
split_docs = text_splitter.split_documents(docs)

chain = load_qa_with_sources_chain(
    llm, 
    chain_type="map_rerank", 
    metadata_keys=['source'], 
    return_intermediate_steps=True
)
print(chain)
query = "中文回答这篇文章的主要内容是什么?"
result = chain.invoke({"input_documents":split_docs,"question":query})
#### 8.7.5 自定义链
当通用链不满足的时候,可以自行构建来实现特定的目的。


from typing import List, Dict, Any, Optional
from langchain.callbacks.manager import CallbackManagerForChainRun
from langchain.chains.base import  Chain
from langchain.prompts.base import BasePromptTemplate
from langchain.base_language import  BaseLanguageModel

class wiki_article_chain(Chain):
    """开发一个wiki文章生成器"""
    prompt:BasePromptTemplate
    llm:BaseLanguageModel
    out_key:str="text"

    @property
    def input_keys(self) -> List[str]:
        """将返回Prompt所需的所有键"""
        return self.prompt.input_variables
    
    @property
    def output_keys(self) -> List[str]:
        """将始终返回text键"""
        return [self.out_key]
    
    def _call(
        self,
        inputs:Dict[str,Any],
        run_manager:Optional[CallbackManagerForChainRun]=None,
    ) -> Dict[str,Any]:
        """运行链"""
        prompt_value = self.prompt.format_prompt(**inputs)
        #print("prompt_value:",prompt_value)
        response = self.llm.generate_prompt(
            [prompt_value],callbacks=run_manager.get_child() if run_manager else None
        )
        #print("response:",response)
        if run_manager:
            run_manager.on_text("wiki article is written")
        return {self.out_key:response.generations[0][0].text}
    
    @property
    def _chain_type(self) -> str:
        """链类型"""
        return "wiki_article_chain"
        
        
from langchain_community.llms.tongyi import Tongyi
from langchain.prompts import  PromptTemplate

prompt=PromptTemplate(
    template="写一篇关于{topic}的维基百科形式的文章",
    input_variables=["topic"]
)

chain = wiki_article_chain(
    llm = llm,
    prompt = prompt
)
### 8.8 Memory

#### 8.8.1 memory介绍

默认情况下,Chains(链)和Agents(代理)是无状态的,这意味着它们将每个传入的查询视为独立的(底层的LLM和聊天模型也是如此)。在某些应用程序中(聊天机器人就是一个很好的例子),记住先前的交互非常重要,无论是短期还是长期。Memory(记忆)正是为此而设计的。 LangChain提供两种形式的记忆组件。首先,LangChain提供了用于管理和操作先前聊天消息的辅助工具。无论如何使用,这些工具都被设计为模块化和有用。其次,LangChain提供了将这些工具轻松整合到链中的方法。

通常情况下,对于每种类型的记忆,有两种理解使用记忆的方法。一种是独立的函数,从一系列消息中提取信息,另一种是在链中使用这种类型的记忆的方法。

记忆可以返回多个信息(例如,最近的 N 条消息和所有先前消息的摘要)。返回的信息可以是字符串或消息列表。

ChatMessageHistory

在大多数(如果不是全部)记忆模块的核心实用类之一是 `ChatMessageHistory` 类。这是一个超轻量级的包装器,提供了保存人类消息、AI 消息以及获取所有消息的便捷方法。

如果您在链外管理记忆,则可以直接使用此类。

from langchain.memory import ChatMessageHistory

history = ChatMessageHistory()
history.add_user_message("你好")
history.add_ai_message("你好?")
history.add_user_message("请问丹麦的首都是哪里?")
history.add_ai_message("哥本哈根")
ConversationBufferMemory

现在我们展示如何在链中使用这个简单的概念。首先展示 `ConversationBufferMemory`,它只是一个对 ChatMessageHistory 的包装器,用于提取消息到一个变量中。

我们可以首先将其提取为一个字符串。
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory()
memory.chat_memory.add_user_message("hi!")
memory.chat_memory.add_ai_message("whats up?")
memory.load_memory_variables({})

我们还可以将历史记录作为消息列表获取。
memory = ConversationBufferMemory(return_messages=True)
memory.chat_memory.add_user_message("hi!")
memory.chat_memory.add_ai_message("whats up?")
memory.load_memory_variables({})
在链中使用

最后,让我们看看如何在链中使用这个模块(设置 `verbose=True` 以便查看提示)。

from langchain_community.llms import Tongyi
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory

memory = ConversationBufferMemory()
memory.chat_memory.add_user_message("你好,我是小红")
memory.chat_memory.add_ai_message("小红您好,我是一个医生,可以帮助你吗")
memory.chat_memory.add_user_message("我感觉头晕,眼花,不想吃东西怎么办")
# memory.chat_memory.add_ai_message("小红您好,我是一个医生,可以帮助你吗")
memory.load_memory_variables({})


llm = Tongyi(temperature=0)
conversation = ConversationChain(
    llm=llm, 
    # verbose=True, 
    memory=memory
)
conversation.predict(input="我感觉头晕,眼花,不想吃东西怎么办")

保存消息记录

您可能经常需要保存消息,并在以后使用时加载它们。可以通过将消息首先转换为普通的 Python 字典来轻松实现此操作,然后将其保存(例如,保存为 JSON 格式),然后再加载。以下是一个示例:
import json

from langchain.memory import ChatMessageHistory
from langchain.schema import messages_from_dict, messages_to_dict

history = ChatMessageHistory()

history.add_user_message("hi!")

history.add_ai_message("whats up?")
dicts = messages_to_dict(history.messages)

new_messages = messages_from_dict(dicts)
new_messages
长时记忆
保存到向量数据库(向量数据库存储Memory)
from langchain.memory import ConversationBufferMemory
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings.dashscope import DashScopeEmbeddings

memory = ConversationBufferMemory()
memory.save_context(
    {"input":"帮我找一下tomie"},
    {"output":"对不起请问什么是tomie?"}
)
memory.save_context(
    {"input":"tomie是一个培训讲师"},
    {"output":"好的,我知道了。"}
)
memory.save_context(
    {"input":"今天他要讲一门关于RAG的课程"},
    {"output":"好的,我知道了。需要RAG的资料吗?"}
)
memory.save_context(
    {"input":"不需要资料了,谢谢"},
    {"output":"好的,那我就不打扰你了。"}
)
vectorstore = FAISS.from_texts(
    memory.buffer.split("\n"),
    DashScopeEmbeddings()
)
FAISS.save_local(vectorstore,"test_faiss")
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings.dashscope import DashScopeEmbeddings
faiss = FAISS.load_local("test_faiss", DashScopeEmbeddings(), allow_dangerous_deserialization=True)
faiss.similarity_search("tomie是什么职业")
## **五、agents**
### 9.1 agents

#### 9.1.1 agents介绍
Agents 是一个具有智能功能的智能体,它使用 LLM 和工具来执行任务。

Agents 核心思想是使用LLM来选择要采取的一系列动作。在链式结构中,一系列动作是硬编码的(在代码中)。 在 Agents 中,使用语言模型作为推理引擎来确定要采取的动作及其顺序。

Agents 包括几个关键组件:

\- ***\*Agent\****: 用于生成指令和执行动作的代理。

\- ***\*Tool\****: 用于执行动作的函数。

\- ***\*Memory\****: 用于存储历史对话和生成的指令。

\- ***\*LLM\****: 用于生成指令和执行动作的 LLM。

有些应用程序不仅需要预先确定的LLM(语言模型)/其他工具的调用链,还可能需要依赖用户输入的未知链条。在这些类型的链条中,有一个代理程序可以访问一套工具。根据用户的输入,代理程序可以决定是否调用这些工具中的任何一个。

目前,有两种主要类型的代理程序:

动作代理:这些代理程序决定要采取的动作,并逐个执行这些动作。

计划和执行代理:这些代理程序首先制定一套要采取的行动计划,然后逐个执行这些行动。

何时使用每种类型的代理程序?动作代理更为传统,适用于小型任务。对于更复杂或长时间运行的任务,初始的规划步骤有助于保持长期目标和专注。然而,这样做通常会增加调用次数和延迟。这两种代理程序也不是互斥的 - 实际上,通常最好由动作代理负责计划和执行代理的执行。

动作代理的高级伪代码如下:

1. 接收用户输入。
2. 代理程序决定是否使用某个工具,以及工具的输入应该是什么。
3. 使用该工具以工具输入进行调用,并记录观察结果(调用的输出)。
4. 将工具、工具输入和观察结果的历史传递回代理程序,并由代理程序决定下一步操作。
5. 重复上述步骤,直到代理程序决定不再需要使用工具,然后直接向用户做出响应。

代理程序涉及的不同抽象概念如下:

- 代理程序(Agent):这是应用程序的逻辑所在。代理程序提供一个接口,接受用户输入以及代理程序已经执行的步骤列表,并返回代理动作(AgentAction)或代理结束(AgentFinish)。
  - 代理动作(AgentAction):对应要使用的工具以及该工具的输入。
  - 代理结束(AgentFinish):表示代理程序已经完成,并包含向用户返回的信息。
- 工具(Tools):代理程序可以执行的操作。您向代理程序提供哪些工具高度取决于您希望代理程序执行的任务。
- 工具包(Toolkits):这些是为特定用例设计的工具组合。例如,为了使代理程序能够以最佳方式与SQL数据库交互,它可能需要访问一个工具来执行查询和另一个工具来检查表格。
- 代理执行器(Agent Executor):它包装了一个代理程序和一组工具。它负责循环地迭代运行代理程序,直到满足停止条件为止。

LangChain Agent 是 LangChain 框架中的一个重要组成部分,它代表了智能合约的实例或具有特定功能的软件程序,旨在与现实世界进行交互并执行任务。以下是对 LangChain Agent 的详细解析:

一、LangChain Agent 的基本概念

LangChain 是一个开源的语言模型集成框架,旨在简化使用大型语言模型(LLM)创建应用程序的过程。Agent 在 LangChain 中扮演着核心角色,它利用语言模型(LLM)作为推理引擎,根据实时情况决定如何调用工具并执行任务。

#### 9.1.2 LangChain Agent 的工作原理

1. **接收任务**:用户给出一个任务(Prompt),这是 Agent 工作的起点。
2. **思考(Thought)**:Agent 使用语言模型进行推理,制定解决问题的计划,并确定下一步需要采取的行动。
3. **行动(Action)**:Agent 根据计划调用相应的工具(如搜索引擎、计算器、API等),并执行必要的操作。
4. **观察(Observation)**:Agent 观察操作的结果,并将其作为新的上下文信息,用于后续的思考和行动。

这个过程会循环进行,直到语言模型认为已经找到最终答案或达到预设的迭代次数。

LangChain Agent 的类型

LangChain 提供了多种类型的 Agent,以适应不同的应用场景和需求。主要包括以下几种类型:

1. **动作代理人(Action Agents)**:在每个时间步上,使用所有先前动作的输出决定下一个动作。这类 Agent 适用于小任务或需要实时响应的场景。
2. **计划执行代理人(Plan-and-execute Agents)**:预先决定所有动作的完整顺序,然后按照计划执行,而不更新计划。这类 Agent 适用于复杂或长时间运行的任务,需要保持长期目标和重点。

LangChain Agent 的应用示例

LangChain Agent 可以应用于各种任务,如文本生成、文档问答、聊天机器人、调用特定的 SaaS 服务等。以下是一个简单的应用示例:
from langchain.agents import initialize_agent, load_tools
from langchain_community.llms.tongyi import Tongyi
from langchain.memory import ConversationBufferMemory

# 初始化 OpenAI 语言模型  
llm = Tongyi()
# 加载工具,例如数学计算工具
tools = load_tools(["llm-math"], llm=llm)
# 创建会话缓冲内存,用于保存对话历史
memory = ConversationBufferMemory(memory_key="chat_history")
# 初始化 Agent,指定代理类型和工具
agent = initialize_agent(
    agent="conversational-react-description",
    tools=tools,
    llm=llm,
    verbose=True,
    max_iterations=3,
    memory=memory,
)
# 与 Agent 交互
output_1 = agent.invoke("when you add 4 and 5 the result comes 10.")
output_2 = agent.invoke("4 + 5 is ")

LangChain Agent 是一种强大的工具,它利用语言模型作为推理引擎,通过调用各种工具来执行复杂任务。不同类型的 Agent 适用于不同的应用场景,可以根据具体需求进行选择和配置。随着 LangChain 的不断发展和完善,Agent 的功能和性能也将不断提升,为开发者提供更加便捷和高效的解决方案。

#### 9.1.3 搭建工具


\- serpai是一个聚合搜索引擎,需要安装谷歌搜索包以及申请账号 https://serpapi.com/manage-api-key

\- llm-math是一个封装好的数学计算链

pip install google-search-results

import os
os.environ["SERPAPI_API_KEY"] = 'db166b810c6b85674b6ceab3bd4e10d5048e1ba837db1c0d962ad91b34558805'
from langchain_community.llms.tongyi import Tongyi
llm = Tongyi()
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType

tools = load_tools(["serpapi","llm-math"], llm=llm)
agent = initialize_agent(
    tools,
    llm,
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,#这里有不同的类型
    verbose=True,#是否打印日志
)
res = llm.invoke("请问2024年的美国总统是谁?他的年龄的除以2是多少?")
#### 9.1.4 memory和agents配合使用
import os 
os.environ["SERPAPI_API_KEY"] = 'db166b810c6b85674b6ceab3bd4e10d5048e1ba837db1c0d962ad91b34558805'
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
from langchain.memory import ConversationBufferMemory

#记忆组件
memory = ConversationBufferMemory(
    memory_key="chat_history",
)
# 定义tool
tools = load_tools(["serpapi","llm-math"],llm=llm)
# 定义agent
agent = initialize_agent(
    tools,
    llm,
    agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION,
    memory=memory,#记忆组件
    verbose=True,
)
print(agent.agent.llm_chain.prompt.template)
agent.invoke("我是张三,今年18岁,性别女,现在在深圳工作,工作年限1年,月薪5000元")
print(agent.invoke("我的名字是什么?"))
### tools工具类
import langchain
langchain.debug = True
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType

def buy_xlb(days: int):
    return "感冒发烧了"
def buy_jz(input: str):
    #调用天气预报接口
    # res = requests.get("?city="+input)
    # data = json.loads(res.text)
    return "北京今天35度"
xlb = Tool.from_function(
    func=buy_xlb,
    name="buy_xlb",
    description="当用户咨询的是关于医疗问题的时候,使用这个工具,返回值为这个函数返回的结果"
)
jz = Tool.from_function(
    func=buy_jz,
    name="buy_jz",
    description="当用户咨询的是关于天气问题的时候,使用这个工具,返回值为这个函数返回的结果"
)
tools = [xlb,jz]
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION)
res3 = agent.run("今天多少度")
### 9.2Agents 的类型

\- ZERO_SHOT_REACT_DESCRIPTION                   零样本反应描述,prompt没有用json格式化

\- CHAT_ZERO_SHOT_REACT_DESCRIPTION              聊天零样本反应描述,promp用json格式化

\- CONVERSATIONAL_REACT_DESCRIPTION              会话反应描述,需要memory

\- CHAT_CONVERSATIONAL_REACT_DESCRIPTION         聊天会话反应描述,需要memory

\- STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION   聊天结构化零样本反应描述,输出结果也是json格式化的
import os 
os.environ["SERPAPI_API_KEY"] = 'db166b810c6b85674b6ceab3bd4e10d5048e1ba837db1c0d962ad91b34558805'
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType
from langchain.agents import load_tools
#### 9.2.1 ZERO_SHOT_REACT_DESCRIPTION


即在没有示例的情况下可以自主的进行对话的类型。

应用场景:

主要用于即时响应描述性任务,这些任务可能不需要复杂的对话上下文,而是直接针对某个问题或请求给出描述性回答。

适用于那些需要快速生成准确描述的场景,如产品特性说明、数据报告解读等。

交互方式:

通常以单轮问答的形式进行,用户输入一个问题或请求,系统直接返回相应的描述性回答。

不涉及复杂的对话历史或会话管理。

生成内容特点:

生成的回答侧重于直接、准确的描述,可能不包含过多的对话性语言或上下文依赖。
from langchain.agents import load_tools
# 定义tools
tools = load_tools(["serpapi","llm-math"],llm=llm)
# 定义agent--(tools、agent、llm、memory)
agent = initialize_agent(
    tools,
    llm,
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True,
)
# print(agent.agent.llm_chain.prompt.messages[0].prompt.template)
agent.invoke("现在美国总统是谁?他的年龄除以2是多少?")
#### 9.2CHAT_ZERO_SHOT_REACT_DESCRIPTION 
零样本增强式生成,即在没有示例的情况下可以自主的进行对话的类型。

应用场景:

专为聊天和对话场景设计,能够处理多轮对话,并在对话过程中生成连贯、自然的回答。

适用于需要与用户进行交互、理解用户意图并给出相应反应的场景,如聊天机器人、客服系统等。

交互方式:

支持多轮对话,能够处理用户的连续输入,并根据对话历史生成更加准确的回答。

可能需要管理对话状态、跟踪用户意图和上下文信息。

生成内容特点:

生成的回答更加自然、流畅,能够适应用户的语言风格和对话习惯。

可能包含对话性语言、问候语、确认信息等,以增强用户体验。
tools = load_tools(["serpapi","llm-math"],llm=llm)
agent = initialize_agent(
    tools,
    llm,
    agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True,
)
print(agent.agent.llm_chain.prompt.messages[0].prompt.template)
# agent.invoke("现在美国总统是谁?他的年龄除以2是多少?")
#### 9.3 CONVERSATIONAL_REACT_DESCRIPTION

一个对话型的agent,这个agent要求与memory一起使用
import os 
os.environ["SERPAPI_API_KEY"] = 'db166b810c6b85674b6ceab3bd4e10d5048e1ba837db1c0d962ad91b34558805'
from langchain_community.llms.tongyi import Tongyi
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
from langchain.memory import ConversationBufferMemory

#记忆组件
memory = ConversationBufferMemory(
    memory_key="chat_history",
)
# 定义tool
tools = load_tools(["serpapi","llm-math"],llm=llm)
# 定义agent
agent = initialize_agent(
    tools,
    llm,
    agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION,
    memory=memory,#记忆组件
    verbose=True,
)
print(agent)
print(agent.agent.llm_chain.prompt.template)
agent.run("我是张三,今年18岁,性别女,现在在深圳工作,工作年限1年,月薪5000元")
agent.run("我的名字是什么?")
# agent.run("有什么好吃的泰国菜可以推荐给我吗?")
# agent.run("这些我都没吃过!我名字的最后一个字母是什么?1998年的世界杯谁夺冠了?")
# agent.run("中国陕西西安现在的气温多少?截止目前我们聊了什么?")
#### 9.4 CHAT_CONVERSATIONAL_REACT_DESCRIPTION 使用了chatmodel


代理是 LangChain 框架中针对聊天和会话场景设计的一种高级代理类型。它利用 React 框架(在 LangChain 的上下文中,React 框架指的是一种用于决定何时调用哪个工具的逻辑框架,而非前端开发中的 React 库)来动态地选择和执行工具,同时利用会话记忆(如对话历史)来增强交互的连贯性和上下文理解能力。

from langchain.memory import ConversationBufferMemory

#记忆组件
memory = ConversationBufferMemory(
    memory_key="chat_history",
    return_messages=True,
)
# 定义tool
tools = load_tools(["serpapi","llm-math"],llm=llm)
agent = initialize_agent(
    tools,
    Tongyi(),
    agent=AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION,
    memory=memory,#记忆组件
    agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
    # agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, #agent类型 
    # memory=memory,#记忆组件
    # handle_parsing_errors=True,
    verbose=True,
)
# print("1 ------------------------")
# print(len(agent.agent.llm_chain.prompt.messages))
print("2 ------------------------")
print(agent.agent.llm_chain.prompt.messages[0].prompt.template)
# print("3 ------------------------")
# print(agent.agent.llm_chain.prompt.messages[1])
# print("4 ------------------------")
# print(agent.agent.llm_chain.prompt.messages[2].prompt.template)
# print("5 ------------------------")
# print(agent.agent.llm_chain.prompt.messages[3])
# agent.run("有什么好吃的泰国菜可以推荐给我吗?用中文回答")
#### 9.2.5 STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION

import os 
os.environ["SERPAPI_API_KEY"] = 'db166b810c6b85674b6ceab3bd4e10d5048e1ba837db1c0d962ad91b34558805'
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType
from langchain.agents import load_tools
from langchain.memory import ConversationBufferMemory

#记忆组件
memory = ConversationBufferMemory(
    memory_key="chat_history",
    return_messages=True,
)
# 定义tool
tools = load_tools(["serpapi","llm-math"],llm=llm)
# 定义agent
agent = initialize_agent(
    tools,
    Tongyi(),
    agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, #agent类型 
    memory=memory,#记忆组件
    handle_parsing_errors=True,
    verbose=True,   
)
print(agent.agent.llm_chain.prompt.messages[0].prompt.template)
# print(agent.agent.llm_chain.prompt.messages[1].prompt.template)
agent.run("有什么好吃的泰国菜可以推荐给我吗?用中文回答")
### 9.3 内置的Tools
langchain预制了大量的tools,基本这些工具能满足大部分需求。 https://python.langchain.com.cn/docs/modules/agents/tools/

\- 加载预制tool的方法

\- 几种tool的使用方式

对输出做了结构化处理
#添加预制工具的方法很简单
from langchain.agents import load_tools
tool_names = [...]
tools = load_tools(tool_names) #使用load方法

#有些tool需要单独设置llm
from langchain.agents import load_tools
tool_names = [...]
llm = ...
tools = load_tools(tool_names, llm=llm) #在load的时候指定llm

#### 9.3.1 SerpAPI

最常见的聚合搜索引擎 https://serper.dev/dashboard,支持google\bing

from langchain.utilities.serpapi import SerpAPIWrapper

# 实例化
search = SerpAPIWrapper()
search.run("美国现在的总统是谁?")
# 支持自定义参数,比如将引擎切换到bing,设置搜索语言等
params = {
    "engine": "bing",
    "gl": "us",
    "hl": "en",
}
search = SerpAPIWrapper(params=params)
search.run("美国现在的总统是谁?")
tools = load_tools(["serpapi"],llm=llm)
#### 9.3.2 Dall-E
Dall-E是openai出品的文到图AI大模型

pip install opencv-python scikit-image
from langchain.agents import initialize_agent, load_tools

tools = load_tools(["dalle-image-generator"])
agent = initialize_agent(
    tools, 
    llm, 
    agent="zero-shot-react-description",
    verbose=True
)
agent.run("Create an image of a halloween night at a haunted museum")
#### 9.3.4 Eleven Labs Text2Speech
ElevenLabs 是非常优秀的TTS合成API
import os
os.environ["ELEVEN_API_KEY"] = "23261e4a3b79697822252a505a169863"
# os.environ["SERPAPI_API_KEY"] = 'db166b810c6b85674b6ceab3bd4e10d5048e1ba837db1c0d962ad91b34558805'
from langchain.tools.eleven_labs import ElevenLabsText2SpeechTool

text_to_speak = "Hello! 你好! Hola! नमस्ते! Bonjour! こんにちは! مرحبا! 안녕하세요! Ciao! Cześć! Привіт! வணக்கம்!"
tts = ElevenLabsText2SpeechTool(
    voice="Bella",
    text_to_speak=text_to_speak,
    verbose=True
)
speech_file = tts.run(text_to_speak)
speech_file = tts.run(text_to_speak)
tts.stream_speech(text_to_speak)
from langchain.agents import initialize_agent, load_tools

tools = load_tools(["eleven_labs_text2speech"])
agent = initialize_agent(
    tools, 
    llm, 
    agent="zero-shot-react-description",
    verbose=True
)
agent.run("Create an image of a halloween night at a haunted museum")
#### 9.3.5 GraphQL
一种api查询语言,类似sql,我们用它来查询奈飞的数据库,查找一下和星球大战相关的电影,API地址
https://swapi-graphql.netlify.app/.netlify/functions/index

### from langchain.chat_models import ChatOpenAI
from langchain.agents import load_tools, initialize_agent, AgentType
from langchain.utilities import GraphQLAPIWrapper

tools = load_tools(
    ["graphql"],
    graphql_endpoint="https://swapi-graphql.netlify.app/.netlify/functions/index",
)
agent = initialize_agent(
    tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True
)
#### 9.3.6 Tookit
tookit是langchain已经封装好的一系列工具,一个工具包是一组工具来组合完成特定的任务

一个python代码机器人

from langchain_experimental.agents.agent_toolkits import create_python_agent
from langchain_experimental.tools import PythonREPLTool
from langchain_experimental.utilities import PythonREPL
from langchain.agents.agent_types import AgentType

agent_executor = create_python_agent(
    llm=llm,
    tool=PythonREPLTool(),
    verbose=True,
    agent_type=AgentType.OPENAI_FUNCTIONS,
    agent_executor_kwargs={"handle_parsing_errors": True},
)
agent_executor.run("生成10个斐波那契数列?")
def generate_fibonacci_sequence(n):
    sequence = []
    a, b = 0, 1
    for _ in range(n):
        sequence.append(a)
        a, b = b, a + b
    return sequence
fibonacci_sequence = generate_fibonacci_sequence(10)
fibonacci_sequence
#### 9.3.7 SQL Database

使用SQLDatabaseChain构建的agent,用来根据数据库回答一般行动饿问题

from langchain.agents import create_sql_agent
from langchain.agents.agent_toolkits import SQLDatabaseToolkit
from langchain.sql_database import SQLDatabase
from langchain.agents import AgentExecutor
from langchain.agents.agent_types import AgentType
from langchain_community.chat_models import ChatTongyi

db = SQLDatabase.from_uri("sqlite:///db/Chinook.db")
toolkit = SQLDatabaseToolkit(db=db, llm=llm)
agent_executor = create_sql_agent(
    llm=llm,
    toolkit=toolkit,
    verbose=True,
    agent_type=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
)
# agent_executor.run("创建用户表,包括用户名和密码两个列")
agent_executor.run("""用户表结构如下:
                   CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(50) NOT NULL
);生成向用户表中插入3个数据的sql""")
### 9.4自定义Agent

\- 定义一个class

\- 工具:默认搜索

\- 提示词:定义agent要做什么任务

\- outparse:约束LLM的行为和输出

\- 不同的LLM不同的质量

自定义tools
from langchain.agents import Tool, AgentExecutor, LLMSingleActionAgent, AgentOutputParser
from langchain.prompts import StringPromptTemplate
from langchain import SerpAPIWrapper, LLMChain
from typing import List, Union
from langchain.schema import AgentAction, AgentFinish, OutputParserException
from langchain_community.llms.tongyi import Tongyi
import re
import os

class MyAgentTool:
    def __init__(self) -> None:    
        os.environ["SERPAPI_API_KEY"] = 'db166b810c6b85674b6ceab3bd4e10d5048e1ba837db1c0d962ad91b34558805'    
        self.serpapi = SerpAPIWrapper()
    def tools(self):
        return [
            Tool(
                name="search",
                description="适用于当你需要回答关于当前事件的问题时",
                func=self.serpapi.run,
            )
        ]
s = MyAgentTool()
s.serpapi.run("python")

  • 19
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值