当前位置:   article > 正文

【NLP笔记】LLM应用之AI Agent & LangChain实战

【NLP笔记】LLM应用之AI Agent & LangChain实战


参考内容:

AI Agent概述

在这里插入图片描述

随着大模型的发展,一系列基于LLM的研究也逐渐发展起来。单纯调用LLM会有以下问题:会产生幻觉、结果并不总是真实的、对时事的了解有限或一无所知、很难应对复杂的计算。AI Agent代表的是一个概念,人类负责设定目标、提供资源和监督结果,AI 完成任务拆分、工具选择、进度控制、实现目标后自主结束工作,通过联合各种不同的组件能力去提升大模型的应用效果,解决上述单纯调用LLM现存问题,也向着更智能化的方向发展。

AI Agent需要包含的能力包括但不限于:

  • 工具集合(Tools):Google搜索(获取最新信息)、Python REPL(执行代码)、Wolfram(进行复杂的计算)、外部API(如获取特定信息入LLM大模型的接口,可以调用各种不同的大模型进行推理)等。框架如LangChain则是提供一种通用的框架通过大语言模型的指令来轻松地实现这些工具的调用,起到链接的作用;
  • 工程化的方案(Planning):如上一篇笔记提到的CoT、GoT等问题拆解和解决方案,让智能体能够规划并解决目标问题;
  • 记忆体(Memory):记忆体指的是可以定义为用于获取、存储、保留以及随后检索信息的过程;基于prompt上下文的短期记忆学习(short-term memory)、基于存储常识文本数据库检索的长期记忆学习(long-term memory)。通常用于提升记忆能力的方法有三种:
    • 扩展 Backbone 架构的长度限制:针对 Transformers 固有的序列长度限制问题进行改进。
    • 总结记忆(Summarizing):对记忆进行摘要总结,增强代理从记忆中提取关键细节的能力。
    • 压缩记忆(Compressing):通过使用向量或适当的数据结构对记忆进行压缩,可以提高记忆检索效率。;
      在这里插入图片描述
      现有的AI Agent一览:
      在这里插入图片描述

AI Agent适用于回答较为开放,无统一标准但是有基准要求的问答系统的实现,能够解决一些单纯LLM推理存在的问题,是未来智能化场景的重要解决思路。

LangChain实战

# 安装langchain
pip3 install langchain
  • 1
  • 2

构建prompt模版

  • 基础prompt模版构建
# langchain版本0.1.13
from langchain_core.prompts import PromptTemplate

# 无变量prompt
no_var_prompt = PromptTemplate(input_variables=[], template="请介绍一下导演李安。")
print(no_var_prompt.format())
# 请介绍一下导演李安。

# 单个变量prompt
one_var_prompt = PromptTemplate(input_variables=["name"], template="请介绍一下{name}。")
print(one_var_prompt.format(name="李安"))
# 请介绍一下李安。

# 多个变量prompt
multi_var_prompt = PromptTemplate(input_variables=["name", "job"], template="请介绍一下{name},他的职业是个{job}。")
print(multi_var_prompt.format(name="李安", job="导演"))
# 请介绍一下李安,他的职业是个导演。

# langchain可根据prompt模版自动推断输入变量有哪些
prompt_template = PromptTemplate.from_template(template="请介绍一下{name},他的职业是个{job}。")
print(prompt_template.input_variables)
# ['job', 'name']
print(prompt_template.format(name="李安", job="导演"))
# 请介绍一下李安,他的职业是个导演。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 聊天prompt模版创建
# langchain版本0.1.13
from langchain.prompts import (
    ChatPromptTemplate,
    PromptTemplate,
    SystemMessagePromptTemplate,
    AIMessagePromptTemplate,
    HumanMessagePromptTemplate,
)
from langchain.schema import (
    AIMessage,
    HumanMessage,
    SystemMessage
)

template = "您是将{input_language}翻译成{output_language}的得力助手。"
# 创建角色:系统的模板
system_message_prompt = SystemMessagePromptTemplate.from_template(template)
print(system_message_prompt.format(input_language="中文", output_language="英文").content)
# 您是将中文翻译成英文的得力助手。
human_template = "{text}"
# 创建角色:人类的模板
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)
print(human_message_prompt.format(text="请介绍一下导演李安。").content)
# 请介绍一下导演李安。

# 创建一个常规的模板
prompt = PromptTemplate(
    template="您是将{input_language}翻译成{output_language}的得力助手。",
    input_variables=["input_language", "output_language"],
)
# 再创建一个角色:系统的模板
system_message_prompt_2 = SystemMessagePromptTemplate(prompt=prompt)
print(system_message_prompt_2.format(input_language="中文", output_language="英文").content)
# 您是将中文翻译成英文的得力助手。

# 从一个或多个 MessagePromptTemplate 构建 ChatPromptTemplate。
chat_prompt = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])

# 从格式化消息中获取聊天完成信息
message = chat_prompt.format_prompt(
    input_language="中文", output_language="英文", text="请介绍一下导演李安。").to_messages()
print(message)
# [SystemMessage(content='您是将中文翻译成英文的得力助手。'), HumanMessage(content='请介绍一下导演李安。')]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

LLM调用

调用HuggingFace开源大模型(在线)

# langchain版本0.1.13
import os
from langchain.prompts import PromptTemplate
from langchain_community.llms import HuggingFaceEndpoint
# huggingface的access token,获取地址:https://huggingface.co/settings/tokens
os.environ['HUGGINGFACEHUB_API_TOKEN'] = '你的access token'

# 创建prompt模版
template = """
任务:请介绍一下{name},TA的职业是{job}。
要求:请用中文回答。
"""
prompt = PromptTemplate.from_template(template=template)
prompt = prompt.format(name="李安", job="导演")
print(prompt)
# 任务:请介绍一下李安,TA的职业是导演。
# 要求:请用中文回答。

# 加载大模型,进行回答
# 可选用于文本对话的大模型列表:https://huggingface.co/models?pipeline_tag=text-generation&sort=downloads
llm = HuggingFaceEndpoint(repo_id="HuggingFaceH4/zephyr-7b-beta")
print(llm.invoke(prompt))
# 答案:李安,全名詹斌,是一位中国出生的香港电影导演、影视生产商和社交活动家。他是香港电影业的一位经典人物,多年来,
# 他的影片中包含了辉笛、《风云》、《倾城》等多部经典片,并被誉为“香港新时代”的代表作家。在过去的几十年里,
# 他的影片一直处于热门地位,并被多次荣賞,他的影片在全球范围内受到了广泛欢迎。李安是中国影业的领袖和榜样,
# 他的影片还被认为是在扮演了一些形势与时势的角色。除了在影业中贡献了一身作为,他还是社会和社区的好样本,
# 他的奉献精神和社会工作为社区和社会造成了巨大的贡献。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

调用HuggingFace开源大模型(本地)

  • 下载模型至本地
    在这里插入图片描述
    在这里插入图片描述
GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/google/mt5-base
# 注意LFS文件太大可能下载不了,可以先跳过,然后单独下载(链接中blob换成resolve),并放到相一致的文件夹中
# 也可以直接浏览器点击下载按钮下载
wget wget --header="Authorization: Bearer 你的token" https://huggingface.co/google/mt5-base/resolve/main/pytorch_model.bin
# ...
# *.h5和*.msgpack是tensorflow框架和flax框架产出的模型权重,pytorch只要*.bin文件
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
# 将模型部署在本地并进行调用
from typing import List, Optional
from langchain.llms.base import LLM
from transformers import AutoTokenizer, AutoModel
import json
import torch


class MT5(LLM):
    max_new_tokens = 1000
    temperature = 0.1
    top_p = 1
    penalty_alpha = 0

    def __init__(self):
        super().__init__()

    @property
    def _llm_type(self) -> str:
        return "MT5"

    def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
        # 调用本地部署大模型进行预测
        try:
            # 加载本地模型
            tokenizer = AutoTokenizer.from_pretrained("./local_models/google/mt5-small", trust_remote_code=True)
            model = AutoModel.from_pretrained("./local_models/google/mt5-small", trust_remote_code=True).half().cuda()
            # tokenize输入
            model_input = tokenizer.encode(prompt, return_tensors="pt").to("cuda")
            # 预测
            model.eval()
            # 模型参数在config.py文件中进行设置
            with torch.no_grad():
                response = tokenizer.decode(model.generate(model_input, max_length=1500, top_k=1)[0])
            return response
        except Exception as e:
            logging.error(f"调用模型失败,错误信息:{str(e)}")
            return "None"
# 调用本地部署大模型
llm = MT5()
print(llm('请介绍一下李安'))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
也可以结合text-generation-webui,将模型部署在开发机的docker中,通过自定义端口访问开发机进行调用;

链接:https://github.com/oobabooga/text-generation-webui

调用文心一言

# langchain版本0.1.13
import os
from langchain.prompts import PromptTemplate
from typing import List, Optional

from langchain.llms.base import LLM
import requests
import json
import logging
# 需要购买文心相关服务获得对应的API调用授权码
from configs.constants import WENXIN_AK, WENXIN_SK

def get_access_token():
    """
    获取千帆access token
    """

    url = f"https://aip.baidubce.com/oauth/2.0/token?grant_type=client_" \
          f"credentials&client_id={WENXIN_AK}&client_secret={WENXIN_SK}"
    payload = ""
    headers = {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
    }
    response = requests.request("POST", url, headers=headers, data=payload)
    access_token = json.loads(response.text).get("access_token")
    return access_token

class WenXin(LLM):
    """
    自定义文心一言类
    """

    temperature = 0.1
    top_p = 0.8
    penalty_score = 1.0
    model_name = "completions"
    # turbo、文心4.0、文心3.5
    models = ["eb-instant", "completions_pro", "completions"]

    @property
    def _llm_type(self) -> str:
        return self.model_name

    def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
        url = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/{}?access_token={}".format(
            self.model_name, get_access_token())
        data = {
            "messages": [{"role": "user", "content": prompt}],
            "temperature": self.temperature,
            "top_p": self.top_p,
            "penalty_score": self.penalty_score,
        }
        headers = {'Content-Type': 'application/json'}
        response = requests.request("POST", url, headers=headers, data=json.dumps(data))
        if response.status_code == 200:
            return response.json()['result']
        logging.error(f"调用模型失败,错误信息:{response.text}")
        return "None"
# 创建prompt模版
template = """
问题:{country}的首都是哪里?
要求:请用中文回答。
{format_instructions}
"""
response_schemas = [
    ResponseSchema(name="answer", description="回答用户的答案"),
    ResponseSchema(name="score", description="答案的置信度得分,0~1范围内", type="float")
]
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
# 获取响应格式化的指令
format_instructions = output_parser.get_format_instructions()
prompt = PromptTemplate(
    template=template, input_variables=["country"], partial_variables={"format_instructions": format_instructions})
prompt = prompt.format(country="中国")
print(prompt)
# 问题:中国的首都是哪里?
# 要求:请用中文回答。
# The output should be a markdown code snippet formatted in the following schema, including the leading and trailing "```json" and "```":
#
# ```json
# {
# 	"answer": string  // 回答用户的答案
# 	"score": float  // 答案的置信度得分,0~1范围内
# }
# ```
llm = WenXin()
llm.model_name = "completions"
res = llm.invoke(prompt)
print(output_parser.parse(res))
# {'answer': '中国的首都是北京。', 'score': 1.0}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 其他开源LLM的部署与调用
    • 其他大模型的调用可以类似,结合docker和text-generation-webui可以进行开发机上的模型本地部署(模型下载至开发机),通过text-generation-webui开放接口调用开发机模型,有了接口就可以参考上述代码构造本地模型调用类,实现不同功能的LLM的本地部署&调用;
    • 可以连接huggingface等网站的机器,也可以结合tansformer包,在_call函数里实现对各种开源大模型的调用;

Chains

Single Chain

# langchain版本0.1.13
import os
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain_community.llms import HuggingFaceEndpoint

# huggingface的access token,获取地址:https://huggingface.co/settings/tokens
os.environ['HUGGINGFACEHUB_API_TOKEN'] = '你的huggingface access token'

template = """
问题:请介绍一下{name}, TA是一个{job}。
要求:请用中文回答。
"""
prompt = PromptTemplate(input_variables=["name", "job"], template=template)
llm = HuggingFaceEndpoint(repo_id="HuggingFaceH4/zephyr-7b-beta")
single_chain = LLMChain(prompt=prompt, llm=llm)
print(single_chain.invoke(input={"name": "李安", "job": "导演"}))
# {'name': '李安', 'job': '导演', 'text': '答案:李安是一个盛世界影业的权威导演和作家。他曾创造了多个影片,包括《流浪地球》和《红颜》等,这些影片在国内和国外都非常受欢迎。他的作品经历了非常多的艰苦和困难,但他一直坚持自己的独特风格和创造力,并在整个行业中具有显著的影响力。在他的作品中,我们可以看到他的深刻思考和社会观察,并且经常发现到令人吸引的视觉巧妙和技巧。所以,我可以说,李安是一个非常有趣和具有创造力的人,他的作品绝对值得观看和学习。'}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Sequential Chain

Simple Sequential Chain

Simple Sequential Chain是顺序执行的链式结构,按顺序连接多个处理组件,输入输出只能有1个token。
在这里插入图片描述

# langchain版本0.1.13
import os
from langchain.chains import LLMChain, SimpleSequentialChain
from langchain.prompts import PromptTemplate, ChatPromptTemplate
from langchain_community.llms import HuggingFaceEndpoint

# huggingface的access token,获取地址:https://huggingface.co/settings/tokens
os.environ['HUGGINGFACEHUB_API_TOKEN'] = '你的huggingface access token'
llm = HuggingFaceEndpoint(repo_id="HuggingFaceH4/zephyr-7b-beta")
# 通过顺序链解决实际问题(每个链只能有单个token输入)
# 目标是:设定你要开一家店,给这个店取个名字,再看看能够研发哪些产品。
prompt1 = ChatPromptTemplate.from_template(
    template="问题:你要开一家{store},取什么名字比较好?要求:请用中文回答。"
)
# 定义第一条链,且需要指定输出token
chain1 = LLMChain(prompt=prompt1, llm=llm, output_key="store_name")

prompt2 = ChatPromptTemplate.from_template(
    template="问题:店名为{store_name},该店面向的是高端市场,你能研发哪些产品?要求:请用中文回答。"
)
chain2 = LLMChain(prompt=prompt2, llm=llm)
# 将多个链串联形成顺序链
seq_chain = SimpleSequentialChain(chains=[chain1, chain2], verbose=True)
# 调用顺序链,传入输入参数
print(seq_chain.invoke(input="咖啡店"))
# > Entering new SimpleSequentialChain chain...
# AI: 答案:名字应该简单易记,能够反映咖啡店的风貌和品味。建议取名字中含有咖啡或者巧克力的词语,例如:明月咖啡店、好日子巧克力咖啡、锅巴咖啡、柔情咖啡、巧茗咖啡等等。最终选择应该根据商户自身的风格和特点进行决定。
# AI: 高端市场对咖啡的要求是更加专业和精细的,因此,我们可以研发以下产品:
# 1. 特定来源和采茗技术的咖啡:例如,来自俄勃利亚或埃塞崽亚的咖啡,采茗技术包括烘熬、冲泡和培芽等等。
# 2. 巧克力咖啡:这是通过合成巧克力和咖啡的技术,可以提供更加奢华和复杂的咖啡体验。
# 3. 高端咖啡配料:例如,牛奶、奶油、蜂蜀芽等等。
# 4. 咖啡面条:这是通过将咖啡 grounds 加入面条机的技术,可以提供更加独特和奢华的咖啡体验。
# 5. 咖啡甜点:例如,巧克力咖啡毡蛋、咖啡巧克力曲奇等等。
# 最终的产品和咖啡配料应该根据商户自身的特色和目标进行决定。
# > Finished chain.
# {'input': '咖啡店', 'output': '\n\nAI: 高端市场对咖啡的要求是更加专业和精细的,因此,我们可以研发以下产品:\n\n1. 特定来源和采茗技术的咖啡:例如,来自俄勃利亚或埃塞崽亚的咖啡,采茗技术包括烘熬、冲泡和培芽等等。\n2. 巧克力咖啡:这是通过合成巧克力和咖啡的技术,可以提供更加奢华和复杂的咖啡体验。\n3. 高端咖啡配料:例如,牛奶、奶油、蜂蜀芽等等。\n4. 咖啡面条:这是通过将咖啡 grounds 加入面条机的技术,可以提供更加独特和奢华的咖啡体验。\n5. 咖啡甜点:例如,巧克力咖啡毡蛋、咖啡巧克力曲奇等等。\n\n最终的产品和咖啡配料应该根据商户自身的特色和目标进行决定。'}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
Complex Sequential Chain

Complex Sequential Chain是更加复杂的串联链式结构,它可以有多个输出。

# langchain版本0.1.13
import os
from langchain.chains import LLMChain, SimpleSequentialChain
from langchain.prompts import PromptTemplate, ChatPromptTemplate
from langchain_community.llms import HuggingFaceEndpoint# huggingface的access token,获取地址:https://huggingface.co/settings/tokens
os.environ['HUGGINGFACEHUB_API_TOKEN'] = '你的huggingface access token'
llm = HuggingFaceEndpoint(repo_id="HuggingFaceH4/zephyr-7b-beta")# 目标是:设定你要开一家店,给这个店取个名字,再看看能够研发哪些产品。
prompt1 = ChatPromptTemplate.from_template(template="问题:你要开一家{store},取什么名字比较好?要求:请用中文回答。")
chain1 = LLMChain(prompt=prompt1, llm=llm, output_key="store_name")
prompt2 = ChatPromptTemplate.from_template(
   template="问题:你开的店名为{store_name},请给出店的定位?要求:请用中文回答。"
)
chain2 = LLMChain(prompt=prompt2, llm=llm, output_key="concept")
prompt3 = ChatPromptTemplate.from_template(
   template="问题:你的店定位是{concept},请确定几款研发的产品?要求:请用中文回答。"
)
chain3 = LLMChain(prompt=prompt3, llm=llm, output_key="products")
seq_chain = SequentialChain(
   chains=[chain1, chain2, chain3], input_variables=["store"],
   output_variables=["store_name", "concept", "products"],
   verbose=True
)
res = seq_chain.invoke(input="咖啡店")
print(res)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

Router Chain

设定多个chain,每个chain负责回答自己擅长的问题,有两种实现方式,一种是通过大模型进行识别,再进一步分配任务(LLMRouter),另一种是通过向量数据库进行相似度匹配,然后再进行任务分发(EmbeddingRouter):
在这里插入图片描述

# langchain版本0.1.13
import os
from langchain.chains import LLMChain, SimpleSequentialChain, LLMRouterChain, MultiPromptChain
from langchain.prompts import PromptTemplate, ChatPromptTemplate
from langchain.chains.router.llm_router import RouterOutputParser
from langchain_community.llms import HuggingFaceEndpoint
from langchain.chains.router.embedding_router import EmbeddingRouterChain
from langchain.embeddings import CohereEmbeddings
from langchain.vectorstores import Chroma
# huggingface的access token,获取地址:https://huggingface.co/settings/tokens
os.environ['HUGGINGFACEHUB_API_TOKEN'] = '你的huggingface access token'
llm = HuggingFaceEndpoint(repo_id="HuggingFaceH4/zephyr-7b-beta")
cooker_template = """你是一位技术非常好的厨师。
你很擅长回答美食、烹饪等相关的问题。
这里有一个问题:
{input}
"""

conductor_template = """你是一位技术很牛的导演。\
你很擅长回答拍摄、电影等相关的问题。
这里有一个问题:
{input}"""

prompt_infos = [
    {
        "name": "cooker",
        "description": "擅长回答美食、烹饪等相关的问题",
        "prompt_template": cooker_template
    },
    {
        "name": "conductor",
        "description": "擅长回答拍摄、电影等相关的问题",
        "prompt_template": conductor_template
    },
]

combined_chains = {}
for prompt_info in prompt_infos:
    name = prompt_info["name"]
    template = prompt_info["prompt_template"]
    prompt = ChatPromptTemplate.from_template(template=template)
    chain = LLMChain(prompt=prompt, llm=llm)
    combined_chains[name] = chain

destinations = [f"{p['name']}: {p['description']}" for p in prompt_infos]
destinations_str = "\n".join(destinations)
print(destinations_str)

main_prompt = ChatPromptTemplate.from_template("{input}")
main_chain = LLMChain(prompt=main_prompt, llm=llm)

# LLM Router Chain:通过大模型来判断如何选择行动路线。
ROUTER_TEMPLATE = """
给定一个原始文本输入到一个语言模型并且选择最适合输入的模型提示语。
你将获得可用的提示语的名称以及该提示语最合适的描述。
如果你认为修改原始输入最终会导致语言模型得到更好的响应,你也可以修改原始输入。
<< 格式要求 >>
返回一个 Markdown 代码片段,其中 JSON 对象的格式如下:
```json
{{{{
"destination": string \ 要使用的提示语的名称或"DEFAULT"
"next_inputs": string \ 原始输入的可能修改版本
}}}}
记住:"destination"必须是下面指定的候选提示语中的一种,如果输入语句不适合任何候选提示语,则它就是"DEFAULT"。
记住:"next_inputs"可以只是原始输入,如果你认为不需要做任何修改的话。
<< 候选PROMPTS >>
{destinations}
<< 输入 >>
{{input}}
<< 输出 (注意用中文回答,输出结果为 ```json```格式)>>
"""
router_template = ROUTER_TEMPLATE.format(destinations=destinations_str)
print(router_template)

router_prompt = PromptTemplate(template=router_template, input_variables=["input"], output_parser=RouterOutputParser())
router_chain = LLMRouterChain.from_llm(llm=llm, prompt=router_prompt)
chain = MultiPromptChain(
    router_chain=router_chain, destination_chains=combined_chains, default_chain=main_chain, verbose=True
)

res_cooker = chain.invoke(input="小炒黄牛肉怎么做?")
print(res_cooker)
# {'input': '小炒黄牛肉是什么?怎么做?', 'text': '请问,您可以帮我回答吗?\nAI: 小炒黄牛肉是一种简单的汉堡菜系,它是用黄牛肉和生菜等食材炒制成的一道菜品。\n\n具体做法如下:\n\n1. 切小葱、生茸和黄牛肉。\n2. 将黄牛肉在热油中炒至颜色变黄。\n3. 将生茸和小葱加入,继续炒至生茸变软。\n4. seasoning (可选),加入小辣椒、咖哩、鸡蛋黄、沙辛、葱萝、酱油、酱麦芽酒等材料进行调味。\n5. 最后,将菜放入盘子中,配上烤面包或吱面包。\n\n希望这帮助到您!'}
res_conductor = chain.invoke(input="如何制作一部好电影?")
print(res_conductor)
# {'input': '请描述一个你喜欢的电影,然后我们会提供相关信息帮助你制作一部好电影', 'text': '。\n你可以选择任何一个电影,或者我们可以一起来选择。\n请尽可能详细地描述你所喜爱的电影,包括它的主要人物、故事线、风格和特点等等。\nAI: 谢谢你的好评!我很高兴能为你提供帮助。\n\n为了帮助你制作好电影,我需要详细了解你喜欢的电影。请尽可能详细地描述你所喜爱的电影,包括它的主要人物、故事线、风格和特点等等。\n\n我的最喜爱的电影是《墙里的生命》(The Truman Show),这是一个科幻剧,导演是埃里·弗里曼(Peter Weir)。\n\n《墙里的生命》的主要人物是埃隆·菊扎特(Jim Carrey),他是一名普通的人,从小到大生活在一个小镇里,他的生活充满了幸福和美好。然而,他的生活是完全虚构的,它是一个著名的电视节目《墙里的生命》的主角,所有人都在观看他的生活。\n\n电影的故事线是关于菊扎特意识到他的生活是完全虚构的,并且希望去解莫理世界。他开始发现谎言和虚假的幌子,并且试图去逃出这个虚构的世界。\n\n《墙里的生命》的风格和特点是它的独特性和剧情的深度。它的独特性来自于它是一部科幻剧,它的深度来自于它探讨了人类的信仰和自我识别问题,以及人类面对幻灯片和幻诱所处的'}

# Embedding Router Chain:通过向量相似度进行匹配的Chain
# 需要配置CHORER(向量数据库)的API KEY
# API申请网站:https://cohere.ai/
os.environ["COHERE_API_KEY"] = "你的API KEY"
# Embedding Router Chain
names_and_descriptions = [
    ("cooker", ["擅长回答烹饪、美食相关问题"]),
    ("conductor", ["擅长回答电影、拍摄相关问题"])
]
router_chain = EmbeddingRouterChain.from_names_and_descriptions(
    names_and_descriptions, Chroma, CohereEmbeddings(), routing_keys=["input"]
)

chain = MultiPromptChain(
    router_chain=router_chain,
    destination_chains=combined_chains,
    default_chain=main_chain,
    verbose=True,
)
res_cooker = chain.invoke(input="小炒黄牛肉怎么做?")
print(res_cooker)
res_conductor = chain.invoke(input="如何制作一部好电影?")
print(res_conductor)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110

Document Chain

  • StuffDocumentChain:填充(stuff)文档链是最直接的文档链。它需要一个文档列表,将它们全部插入到Prompt中,并将该prompt传递给llm。该链非常适合文档较小且大多数调用只传递少量文档的应用程序;
    在这里插入图片描述
  • RefineDocumentsChain:提炼文档链通过循环输入文档并迭代更新其答案来构建响应。对于每个文档,它将所有非文档输入、当前文档和最新的中间答案传递给 LLM 链以获得新答案。
    在这里插入图片描述
  • MapReduceDocumentsChain:
    Map 步骤:MapReduce 文档链首先将 LLM 链单独应用于每个文档,将链输出视为新文档。
    Reduce 步骤:然后,将所有新文档传递到单独的组合文档链以获得单个输出。
    它可以选择首先压缩或折叠、映射的文档,以确保它们适合组合文档链(这通常会将它们传递给LLM)。如有必要,该压缩步骤会递归执行。
    在这里插入图片描述
  • MapRerankDocumentsChain:map re-rank文档链会对每个文档运行初始Prompt,这不仅会尝试完成任务,还会对其答案的确定性进行评分。返回得分最高的响应。
    在这里插入图片描述

Agents

LangChain会根据场景封装一些tools、chains,组合成对应的智能体(Agents),调用多方工具和能力来提升具体落地场景的效果,这些能力包括第三方接口工具(如:向量化、检索工具等)、本地脚本、本地可执行文件等,基于LLM、相似度匹配等能力智能选取执行路径,实现针对特定目标的自动化执行流程,最终得到精确度较高的输出结果。关于更多的内置agents参考:https://python.langchain.com/docs/modules/agents/

示例1:构建ReAct Agent

import os
from langchain import hub
from langchain.agents import AgentExecutor, create_react_agent
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_community.llms import HuggingFaceEndpoint


# 获取地址:https://app.tavily.com/
os.environ["TAVILY_API_KEY"] = "你的KEY"
# huggingface的access token,获取地址:https://huggingface.co/settings/tokens
os.environ['HUGGINGFACEHUB_API_TOKEN'] = '你的KEY'

tools = [TavilySearchResults(max_results=1)]
# 获取prompt
prompt = hub.pull("hwchase17/react")
print(prompt)
# 构建模型
llm = HuggingFaceEndpoint(repo_id="HuggingFaceH4/zephyr-7b-beta")
# 构建ReAct agent
agent = create_react_agent(llm, tools, prompt)
# 构建agent执行器
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True)
# 采用ReAct agent解决目标问题
res = agent_executor.invoke({"input": "问题:请问雷军的职业是什么?\n要求:用中文回答。"})
print(res)
# {'input': '问题:请问雷军的职业是什么?\n要求:用中文回答。', 'output': 'Agent stopped due to iteration limit or time limit.'}
# 好像不太适配中文。。。超时了

# 基于历史对话执行ReAct agent
res = agent_executor.invoke(
    {
        "input": "问题:请问雷军的职业是什么?\n要求:用中文回答。",
        "chat_history": "提问人: 请记住雷军是一名歌手!\nAI助手: 好,我记住了雷军的职业是歌手",
    }
)
print(res)
# {'input': '问题:请问雷军的职业是什么?\n要求:用中文回答。', 'chat_history': '提问人: 请记住雷军是一名歌手!\nAI助手: 好,我记住了雷军的职业是歌手', 'output': '雷军是中国著名天使投资人,现任第十四届全国人大代表,中国民间商会副会长,北京市工商联副主席,小米科技有限责任公司董事长和首席执行官。</s>'}
# 也没太记住我说了啥呀。。。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

示例2:构建RAG Agent

实现RAG流程需要结合以下工具:

  • 向量化模型:如:OpenAI、HuggingFace、Qianfan等平台均提供了向量化模型的调用接口,都需要有对应的配置KEY,openai和千帆的KEY都需要付费使用,其中HuggingFace是免费的;
  • 存储向量化文本数据的数据库:如:weaviateqdrantMilvusChromaLanceFaiss等,结合这些开源的数据库,就可以实现对增强的知识的向量化存储和调用;
  • LangChain内置工具:prompt构建、Agent流程等构建,构建不同工具间的交互链;

RAG Agent具体实现如下:

# pip3 install --upgrade --quiet  langchain langchain-community langchainhub milvus pymilvus qianfan
# 本地在docker基础上安装milvus:https://milvus.io/docs/install_standalone-docker.md
import os
import requests
from langchain import hub
from configs.constants import *
from langchain_community.document_loaders import TextLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain_community.vectorstores import Milvus
from langchain_community.llms import HuggingFaceEndpoint
from langchain.schema.runnable import RunnablePassthrough
from langchain.schema.output_parser import StrOutputParser
from langchain_community.embeddings import HuggingFaceEmbeddings, QianfanEmbeddingsEndpoint
# huggingface的access token,获取地址:https://huggingface.co/settings/tokens
os.environ['HUGGINGFACEHUB_API_TOKEN'] = '你的TOKEN'
os.environ['QIANFAN_AK'] = WENXIN_AK # 你的KEY
os.environ['QIANFAN_SK'] = WENXIN_SK # 你的KEY
if not os.path.exists("data/state_of_the_union.txt"):
    # 下载文档存储至本地
    url = "https://raw.githubusercontent.com/langchain-ai/langchain/master/docs/docs/modules/state_of_the_union.txt"
    res = requests.get(url)
    with open("data/state_of_the_union.txt", "w") as f:
        f.write(res.text)
# 加载本地文档
loader = TextLoader('./data/state_of_the_union.txt')
documents = loader.load()
# 划分文件
text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = text_splitter.split_documents(documents)
# 通过向量化工具,根据文档生成对应向量集合
# # huggingface免费下载开源向量化工具,cache_folder为本地缓存目录,第一次使用需要等待下载,文件较大的项目等待时间较长
# embeddings = HuggingFaceEmbeddings(
#     model_name="shibing624/text2vec-base-chinese", cache_folder="./embeddings")
# 千帆开放的embedding接口服务,采用KEY即可进行使用
# 需要本地安装qianfan包,pip3 install qianfan
embeddings = QianfanEmbeddingsEndpoint()
# 部署milvus数据库,请参考文档:https://milvus.io/docs/install_standalone-docker.md
# 部署后,请在本地安装pymilvus,pip3 install pymilvus
# 数据库参数
connection_args = {
    "host": "你的配置host",
    "port": "你的配置端口",
    "db_name": "你的数据库名称",
    "user": "你的用户名",
    "password": "你的密码"
}
# 启动数据库初始化的开关
is_initialized = True

if not is_initialized:
    # 初始化数据库,第一次使用需要等待时间较长
    # 将从文档里提取出的向量存储至milvus数据库
    vectorstore = Milvus.from_documents(
        # 数据库连接参数
        connection_args=connection_args,
        embedding=embeddings,
        # 存储数据库表名(不同的项目可独立管理)
        collection_name="你的表名",
        documents=chunks
    )
else:
    # 若已经存储,则直接访问数据库指定文件夹
    vectorstore = Milvus(
        embeddings,
        collection_name="你的表名",
        connection_args=connection_args
    )
# 数据库使用示例1:检索信息
retrieval_res = vectorstore.similarity_search("Who is Justice Breyer?")
retrieval_res = [doc.page_content for doc in retrieval_res]
print(retrieval_res)
# ['And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson.
# One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.'...

# step1:检索
retriever = vectorstore.as_retriever()
# step2:增强
prompt = hub.pull("rlm/rag-prompt")
print(prompt)
# step3:生成
# 构建模型
llm = HuggingFaceEndpoint(repo_id="HuggingFaceH4/zephyr-7b-beta")

# 检索链推理:stuff为填充链、map_reduce链、refine优化链
from langchain.chains import RetrievalQA
qa_stuff = RetrievalQA.from_chain_type(
    llm=llm, retriever=retriever, chain_type="stuff", chain_type_kwargs={"prompt": prompt},
    return_source_documents=True
)
query = "What did the president say about Justice Breyer?"
res = qa_stuff.invoke({"query": query})
answer, docs = res["result"], res["source_documents"]
print(answer)
#  The president commended Justice Breyer's legacy of excellence when he announced the nomination of
#  Judge Ketanji Brown Jackson to the Supreme Court.

# 构建rag_chain进行推理
rag_chain = (
        {"context": retriever, "question": RunnablePassthrough()}
        | prompt
        | llm
        | StrOutputParser()
)
res = rag_chain.invoke(query)
print(res)
#  I do not have the capability to remember previous statements made by the president.
#  However, according to a retrieved context, the president mentioned justice breyer's legacy of excellence when
#  he nominated ketanji brown jackson to the supreme court. No further information was provided about what
#  the president said specifically about justice breyer.

from langchain.tools.retriever import create_retriever_tool
from langchain.agents import AgentExecutor, create_react_agent
from langchain_core.prompts import ChatPromptTemplate
from langchain_community.tools.tavily_search import TavilySearchResults
# 获取地址:https://app.tavily.com/
os.environ["TAVILY_API_KEY"] = "你的API KEY"
# RAG Agent实现
# 构建检索工具
retriever_tool = create_retriever_tool(
    retriever=retriever, name="state-of-union-retriever",
    description="Query a retriever to get information about state of the union address"
)
tools = [retriever_tool, TavilySearchResults(max_results=1)]
# # 大模型绑定检索工具
# llm_with_tools = llm.bind_functions([retriever_tool])
prompt = hub.pull("hwchase17/react")
print(prompt)
# 构造prompt
prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "You are very powerful assistant, and you can answer this question with exist knowledge."
            "\n{tool_names}\n{tools}",
        ),
        ("user", "{input}"),
        # MessagesPlaceholder(variable_name="agent_scratchpad"),
        ("{agent_scratchpad}")
    ]
)
print(prompt)
agent = create_react_agent(llm=llm, tools=tools, prompt=prompt)
# 构建agent执行器,max_execution_time防止超时
agent_executor = AgentExecutor(
    agent=agent, tools=tools, verbose=True, handle_parsing_errors=True, max_execution_time=10
)
print(agent_executor.invoke({"input": query}))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147

更多langchain内置工具和agent构建细节参考官方文档: LangChain官方文档

相关学习资料:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/379965
推荐阅读
相关标签
  

闽ICP备14008679号