赞
踩
通过使用LangChain,使构建与外部数据源和计算过程无缝集成的应用成为可能。在接下来的内容中,将演示使用不同的方法来实现这种集成的过程。
LLM链是指语言模型(Language Model,通常缩写为LLM)与其他组件按照一定顺序和规则连接在一起的结构。在LangChain中,这些组件包括输入提示模板(ChatPromptTemplate)、语言模型(例如ChatOpenAI)、输出解析器(如StrOutputParser)等。这个链的目的是将多个处理步骤有序地连接在一起,形成一个处理流程。用户可以通过这个链提供输入,链中的组件逐步处理输入,最终生成输出。这种组件化的设计使得用户可以更灵活地构建自己的自然语言处理应用,根据需要定制不同的处理步骤和模型。
在LangChain中,LLM链通常用于构建能够连接外部数据源和计算资源到语言模型的应用程序。链的每个步骤都可以执行不同的任务,如处理提示、调用语言模型、解析输出等。在LangChain官网中提供了两种模型进行举例:OpenAI(通过API提供的流行模型)或使用本地的开源模型 Ollama。在本书的内容中,只展示使用OpenAI模型的用法。
(1)首先,通过如下命令安装LangChain x OpenAI集成包。
pip install langchain-openai
(2)在使用OpenAI API时需要提供API密钥,API密钥是一种用于验证您的身份并访问OpenAI服务的凭据。在获取到OpenAI密钥后,通过如下命令将其设置为环境变量。
export OPENAI_API_KEY="..."
(3)然后,我们可以初始化模型:
- from langchain_openai import ChatOpenAI
- llm = ChatOpenAI()
如果不想设置环境变量,可以在初始化OpenAI LLM类时直接通过openai_api_key参数传递密钥:
- from langchain_openai import ChatOpenAI
- llm = ChatOpenAI(openai_api_key="...")
(4)在安装并初始化所选的LLM后,可以尝试使用它,例如询问它“LangSmith是什么”,因为目前这是在训练数据中不存在的内容,所以它可能没有提供很好的响应。
llm.invoke("how can langsmith help with testing?")
(5)还可以使用提示模板引导响应,提示模板用于将原始用户输入转换为LLM的更好输入。
- from langchain_core.prompts import ChatPromptTemplate
- prompt = ChatPromptTemplate.from_messages([
- ("system", "You are a world-class technical documentation writer."),
- ("user", "{input}")
- ])
(6)现在,将前面提到的组件(包括ChatPromptTemplate、ChatOpenAI、和StrOutputParser)组合成一个简单的LLM链,以构建一个完整的处理流程。
chain = prompt | llm
(7)现在我们可以调用它并问同样的问题,虽然它可能不知道答案,但它应该以技术撰写人的更正式语气回应。
chain.invoke({"input": "how can langsmith help with testing?"})
在LangChain中,ChatModel是一种模型,其输出是一条消息(ChatMessage)。然而,在某些情况下,为了方便处理,用户可能更倾向于将输出转换为字符串形式。为了实现这一点,可以添加一个简单的输出解析器(output parser)。
- from langchain_core.output_parsers import StrOutputParser
- output_parser = StrOutputParser()
注意:输出解析器是一种组件,用于将一个类型的输出转换为另一种类型,通常是更易处理的类型。在这里,将输出消息(ChatMessage)转换为字符串(String)形式。这可以通过在LangChain流程中添加一个名为StrOutputParser的输出解析器来完成。
这个解析器的作用是将聊天消息转换为字符串,这使得后续的处理更加方便,因为字符串是一种常见的文本表示形式,易于处理和输出。这是为了满足用户在处理输出时的特定需求。然后将其添加到先前的链中:
chain = prompt | llm | output_parser
现在以调用它并问同样的问题,此时的答案将是一个字符串(而不是一个ChatMessage)。
chain.invoke({"input": "how can langsmith help with testing?"})
我们已经成功设置了一个基本的LLM链,其中涉及到了提示、模型和输出解析器的基础知识。
Retrieval Chain(检索链)是LangChain中的一个概念,用于处理对话系统中的信息检索。在对话系统中,当用户提出问题时,可能需要从大量的文档或数据中检索相关的信息,然后将这些信息传递给语言模型(LLM)进行进一步处理。
检索链的工作流程如下:
(1)Retriever(检索器):检索链的第一步是通过检索器获取相关的文档或信息。检索器可以基于各种方式工作,比如使用向量存储、数据库查询等。在LangChain中,你可以配置一个检索器,该检索器能够从一个向量存储中检索相关文档。
(2)Documents(文档):检索到的文档被传递到下一步。这些文档包含了对用户问题有用的信息。
(3)LLM处理:下一个步骤涉及将检索到的文档和用户的问题传递给语言模型(LLM)。这样,LLM可以基于上下文信息提供更准确的答案。
(4)生成答案:最终,语言模型处理输入并生成对用户问题的答案,这个答案被返回给用户。
检索链的功能使得在处理大量信息时更加高效,因为它允许系统首先从整个数据集中检索最相关的部分,然后仅将这些部分传递给语言模型。这提高了系统的性能和响应速度,特别是当数据集很庞大时。
为了正确回答原始问题("how can langsmith help with testing?"),我们需要向LLM提供额外的上下文信息,我们可以通过检索(Retrieval)来实现这一点。检索链在有太多数据需要传递给LLM时非常有用,我们可以使用检索仅提取最相关的信息并将其传递给LLM。在这个过程中,将从一个检索器中查找相关文档,然后将它们传递到提示中。检索器可以由任何东西支持 - SQL表、互联网等,但在这个实例中,我们将填充一个向量存储并将其用作检索器。
(1)首先,使用WebBaseLoader加载要进行索引的数据,这需要安装BeautifulSoup:
pip install beautifulsoup4
(2)接下来,可以导入并使用WebBaseLoader。
- from langchain_community.document_loaders import WebBaseLoader
- loader = WebBaseLoader("https://docs.smith.langchain.com/overview")
- docs = loader.load()
(3)接下来,要将其索引到一个向量存储中。这需要一些组件,即嵌入模型和向量存储。对于嵌入模型,再次提供通过OpenAI或通过本地模型访问的示例。
- from langchain_openai import OpenAIEmbeddings
- embeddings = OpenAIEmbeddings()
现在,可以使用这个嵌入模型将文档纳入向量存储中。出于简单起见,将使用一个简单的本地向量存储FAISS。先通过下面的命令安装所需的软件包。
pip install faiss-cpu
(4)然后可以构建我们的索引:
- from langchain_community.vectorstores import FAISS
- from langchain.text_splitter import RecursiveCharacterTextSplitter
- text_splitter = RecursiveCharacterTextSplitter()
- documents = text_splitter.split_documents(docs)
- vector = FAISS.from_documents(documents, embeddings)
现在已经将这些数据索引到一个向量存储中,我们将创建一个检索链。此链将接收一个传入的问题,查找相关文档,然后将这些文档与原始问题一起传递到LLM中,并要求它回答原始问题。
(5)设置一个链,该链将接收一个问题和检索到的文档,并生成一个答案。
- from langchain.chains.combine_documents import create_stuff_documents_chain
- from langchain_core.prompts import ChatPromptTemplate
-
- prompt = ChatPromptTemplate.from_template("""Answer the following question based only on the provided context:
- <context>
- {context}
- </context>
- Question: {input}""")
-
- document_chain = create_stuff_documents_chain(llm, prompt)
如果我们愿意,我们可以通过直接传递文档来运行这个链:
- from langchain_core.documents import Document
-
- document_chain.invoke({
- "input": "how can langsmith help with testing?",
- "context": [Document(page_content="langsmith can let you visualize test results")]
- })
(5)我们希望在检索链中首先使用一个检索器来获取文档,这个检索器的目的是从大量的可能相关的文档中筛选出最相关的那些。这么做的好处是,对于用户提出的特定问题,检索器可以根据问题的内容动态选择最相关的文档,然后将这些文档传递给语言模型(LLM)进行处理。
- from langchain.chains import create_retrieval_chain
- retriever = vector.as_retriever()
- retrieval_chain = create_retrieval_chain(retriever, document_chain)
(6)现在可以调用这个链,这将返回一个字典 - LLM的响应在answer键中:
- response = retrieval_chain.invoke({"input": "how can langsmith help with testing?"})
- print(response["answer"])
到现在为止,这个恢复的答案应该更准确了。
到目前为止,我们创建的链只能回答单一问题。在目前的应用中,构建的语言模型主要被应用在聊天系统中。那么,我们如何将这个链转变为能够回答跟进问题的聊天机器人呢?我们仍然可以使用create_retrieval_chain函数来实现,但需要更改两个方面。
(1)更新检索
为了更新检索,需要创建一个新的链。该链将接收最新的输入(input)和对话历史(chat_history),并使用语言模型(LLM)生成一个搜索查询。
- from langchain.chains import create_history_aware_retriever
- from langchain_core.prompts import MessagesPlaceholder
-
- # 首先,我们需要一个提示,可以传递给LLM以生成搜索查询
-
- prompt = ChatPromptTemplate.from_messages([
- MessagesPlaceholder(variable_name="chat_history"),
- ("user", "{input}"),
- ("user", "根据上面的对话,生成一个搜索查询以获取与对话相关的信息")
- ])
- retriever_chain = create_history_aware_retriever(llm, retriever, prompt)
通过使用这个新的检索链,可以根据对话历史和跟进问题生成相关文档。
(2)测试
通过传递用户询问跟进问题的实例,我们可以测试这个新的检索链。
- from langchain_core.messages import HumanMessage, AIMessage
-
- chat_history = [HumanMessage(content="LangSmith是否可以帮助测试我的LLM应用?"), AIMessage(content="是的!")]
- retriever_chain.invoke({
- "chat_history": chat_history,
- "input": "告诉我怎么做"
- })
现在应该会看到它返回了有关在LangSmith中进行测试的文档。,是因为LLM生成了一个新的查询,将对话历史与跟进问题结合在一起。在有了这个新的检索器,我们可以创建一个新的链,以在考虑这些检索到的文档的情况下继续对话。
- prompt = ChatPromptTemplate.from_messages([
- ("system", "根据以下上下文回答用户的问题:\n\n{context}"),
- MessagesPlaceholder(variable_name="chat_history"),
- ("user", "{input}"),
- ])
- document_chain = create_stuff_documents_chain(llm, prompt)
- retrieval_chain = create_retrieval_chain(retriever_chain, document_chain)
(3)现在可以进行端到端的测试,此时可以看到会给出一个连贯的答案,这表明成功地将我们的检索链转变为了一个聊天机器人。
- chat_history = [HumanMessage(content="LangSmith是否可以帮助测试我的LLM应用?"), AIMessage(content="是的!")]
- retrieval_chain.invoke({
- "chat_history": chat_history,
- "input": "告诉我怎么做"
- })
到目前为止,我们已经创建了使用链的例子,其中每个步骤都是预先知道的。接下来将要实现最后一个步骤:实现Agent代理。
(1)构建Agent代理的第一件事是决定它应该访问哪些工具,在本演示中,我们将让代理访问两个工具:
首先,设置一个用于刚刚创建的检索器的工具:
- from langchain.tools.retriever import create_retriever_tool
- retriever_tool = create_retriever_tool(
- retriever,
- "langsmith_search",
- "Search for information about LangSmith. For any questions about LangSmith, you must use this tool!",
- )
在本演示中将使用搜索工具Tavily,这需要用到一个API密钥。在其平台上创建后,需要先将其设置为环境变量:
export TAVILY_API_KEY=...
如果不想设置API密钥,可以跳过创建此工具的步骤。
- from langchain_community.tools.tavily_search import TavilySearchResults
- search = TavilySearchResults()
现在可以创建要使用的工具列表:
tools = [retriever_tool, search]
我们现在有了工具,可以创建一个Agent代理来使用它们。
(1)首先安装langchain hub,安装命令如下所示。
pip install langchainhub
(2)现在可以使用它来获取预定义的提示:
- from langchain_openai import ChatOpenAI
- from langchain import hub
- from langchain.agents import create_openai_functions_agent
- from langchain.agents import AgentExecutor
-
-
- # 获取要使用的提示 - 您可以修改这个!
- prompt = hub.pull("hwchase17/openai-functions-agent")
- llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
- agent = create_openai_functions_agent(llm, tools, prompt)
- agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
(3)现在可以调用代理并查看其响应,例如可以询问OpenAI关于LangSmith的问题:
agent_executor.invoke({"input": "how can langsmith help with testing?"})
也可以询问它关于天气的问题:
agent_executor.invoke({"input": "what is the weather in SF?"})
我们也可以与它进行对话:
- chat_history = [HumanMessage(content="Can LangSmith help test my LLM applications?"), AIMessage(content="Yes!")]
- agent_executor.invoke({
- "chat_history": chat_history,
- "input": "Tell me how"
- })
现在已经构建了一个应用程序,我们需要为其提供服务,这一功能需要LangServe实现。通使用LangServe可以将LangChain链部署为REST API。我们不需要使用LangServe来使用LangChain,但在本展示中,将演示使用LangServe部署应用程序的过程。
(1)在使用LangServe之前需要使用以下命令进行安装:
pip install "langserve[all]"
(2)为我们的应用程序创建一个Server(服务器),创建程序文件serve.py,在里面包含我们提供应用程序的逻辑,这由如下三个部分组成。
文件serve.py的具体实现代码如下所示。
- from typing import List
-
- from fastapi import FastAPI
- from langchain_core.prompts import ChatPromptTemplate
- from langchain_openai import ChatOpenAI
- from langchain_community.document_loaders import WebBaseLoader
- from langchain_openai import OpenAIEmbeddings
- from langchain_community.vectorstores import FAISS
- from langchain.text_splitter import RecursiveCharacterTextSplitter
- from langchain.tools.retriever import create_retriever_tool
- from langchain_community.tools.tavily_search import TavilySearchResults
- from langchain_openai import ChatOpenAI
- from langchain import hub
- from langchain.agents import create_openai_functions_agent
- from langchain.agents import AgentExecutor
- from langchain.pydantic_v1 import BaseModel, Field
- from langchain_core.messages import BaseMessage
- from langserve import add_routes
-
- # 1. 加载检索器
- loader = WebBaseLoader("https://docs.smith.langchain.com/overview")
- docs = loader.load()
- text_splitter = RecursiveCharacterTextSplitter()
- documents = text_splitter.split_documents(docs)
- embeddings = OpenAIEmbeddings()
- vector = FAISS.from_documents(documents, embeddings)
- retriever = vector.as_retriever()
-
- # 2. 创建工具
- retriever_tool = create_retriever_tool(
- retriever,
- "langsmith_search",
- "Search for information about LangSmith. For any questions about LangSmith, you must use this tool!",
- )
- search = TavilySearchResults()
- tools = [retriever_tool, search]
-
- # 3. 创建代理
- prompt = hub.pull("hwchase17/openai-functions-agent")
- llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
- agent = create_openai_functions_agent(llm, tools, prompt)
- agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
-
- # 4. 应用定义
- app = FastAPI(
- title="LangChain Server",
- version="1.0",
- description="A simple API server using LangChain's Runnable interfaces",
- )
-
- # 5. 添加链路由
-
- # 我们需要添加这些输入/输出模式,因为当前的 AgentExecutor
- # 在模式方面缺乏。
-
- class Input(BaseModel):
- input: str
- chat_history: List[BaseMessage] = Field(
- ...,
- extra={"widget": {"type": "chat", "input": "location"}},
- )
-
- class Output(BaseModel):
- output: str
-
- add_routes(
- app,
- agent_executor.with_types(input_type=Input, output_type=Output),
- path="/agent",
- )
-
- if __name__ == "__main__":
- import uvicorn
-
- uvicorn.run(app, host="localhost", port=8000)
执行文件serve.py后,可以在localhost:8000中看到我们的Langchin正在提供服务。
(3)Playground
每个LangServe服务都附带一个简单的内置UI,用于配置和调用具有流式输出和查看中间步骤,转到 http://localhost:8000/agent/playground/ 可以体验Playground的功能。
(4)Client(客户端)
现在设置一个客户端,用于以编程方式与我们的服务进行交互,我们可以使用 langserve.RemoteRunnable实现这一功能。通过使用langserve.RemoteRunnable,可以像在客户端运行一样与提供服务的链进行交互。
- from langserve import RemoteRunnable
- remote_chain = RemoteRunnable("http://localhost:8000/agent/")
- remote_chain.invoke({"input": "how can langsmith help with testing?"})
本项目是一个基于自然语言处理技术的问答系统,通过结合语言模型和文档检索器,能够根据用户提出的问题自动检索相关文档并给出相应答案,同时提供源文档以支持答案的可信度,并具备上下文压缩功能以提高检索效率,整体实现了自动化的问答过程,为用户提供高效准确的信息查询服务。
实例2-1:中文版的Q&A问答系统(源码路径:codes\2\chatbot_llama2-main\chatbot_demo.ipynb)
1. 准备环境
(1)通过如下命令检查和管理 NVIDIA GPU 设备的状态和性能。
LangChain
执行后会输出下面的信息,展示了NVIDIA GPU的各种信息,包括 GPU 的型号、驱动程序版本、GPU 的使用率、温度、内存使用情况等等。
- Thu Jan 24 16:25:43 2024
- +---------------------------------------------------------------------------------------+
- | NVIDIA-SMI 535.86.01 Driver Version: 536.67 CUDA Version: 12.2 |
- |-----------------------------------------+----------------------+----------------------+
- | GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
- | Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
- | | | MIG M. |
- |=========================================+======================+======================|
- | 0 NVIDIA GeForce GTX 1080 On | 00000000:01:00.0 Off | N/A |
- | 0% 38C P2 35W / 198W | 15MiB / 8192MiB | 0% Default |
- | | | N/A |
- +-----------------------------------------+----------------------+----------------------+
-
- +---------------------------------------------------------------------------------------+
- | Processes: |
- | GPU GI CI PID Type Process name GPU Memory |
- | ID ID Usage |
- |=======================================================================================|
- | 0 N/A N/A 23 G /Xwayland N/A |
- +---------------------------------------------------------------------------------------+
(2)通过如下命令安装库PyTorch、torchvision 和 torchaudio,这些库用于进行深度学习任务和音频处理。通过 --index-url 参数指定了 PyTorch 的安装源为 https://download.pytorch.org/whl/cu122,这是针对 CUDA 12.2 版本的 PyTorch 库的安装源。
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu122
(3)通过下面的代码导入多个 Python库和模块,用于构建自然语言处理任务的工具和模型。其中包括 PyTorch 深度学习框架、Hugging Face Transformers 库中的模型和管道、以及 LangChain 中的文本处理、嵌入和问答模块。这些工具和模型可用于文本分割、嵌入、语言模型训练和问答任务等自然语言处理任务。
- import torch
- from langchain.text_splitter import RecursiveCharacterTextSplitter
- from langchain.vectorstores import Chroma
- from langchain.embeddings import HuggingFaceInstructEmbeddings
-
- from langchain import HuggingFacePipeline
- from langchain import PromptTemplate, LLMChain
- from langchain.chains import RetrievalQA
- from transformers import AutoModelForCausalLM, AutoTokenizer, GenerationConfig
- from langchain.document_loaders import PyPDFLoader
- from transformers import pipeline
- import json
- import textwrap
2. 文件分割
下面代码的功能是从名为 "finance.pdf" 的 PDF 文件中加载文本,并将其分割成一系列小块。
- pdf_file_path = "finance.pdf"
- pdf_loader = PyPDFLoader(pdf_file_path)
- text_splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=0)
- splitted_docs = text_splitter.split_documents(pdf_loader.load())
- print(splitted_docs[0])
上述代码的实现流程如下所示:
page_content='中信证券研报表示,选取了14家具有代表性的海外CRO/CDMO企业以及生命科学上\n游服务商,对其中报业绩进行总结并作为对国内企业的参考。中信证券发现,全球临床\n阶段的新药研发依然活跃,早期研发的需求虽然暂时疲软,但随着海外投融资的回暖,\n也有望回归快速增长常态。生命科学上游服务商的收入,则受到了下游企业去库存周期\n的影响,但相关公司也普遍认为四季度将出现新增需求的拐点。综上,中信证券认为一\n体化服务于全球中后期临床研发阶段/商业化阶段外包需求,以及高校/科研院所客户收\n入占比高的企业业绩相对稳定,且海外收入占比较高的企业将有望率先受益于投融资回\n暖带来的需求增加。\n全文如下' metadata={'source': './finance.pdf', 'page': 0}
3. 为中文构建嵌入向量
(1)从 Hugging Face 模型库加载预训练的中文文本嵌入模型,然后使用该模型生成句子级别的嵌入向量。
- from langchain.embeddings.huggingface import HuggingFaceEmbeddings
- chinese_embedding_name = "/mnt/h/text2vec-base-chinese"
- embeddings = HuggingFaceEmbeddings(
- model_name=chinese_embedding_name,
- model_kwargs={"device": "cuda"},
- )
上述代码的具体功能如下所示:
(2)创建一个 Chroma 数据库对象,用于存储嵌入向量,并指定了数据库的名称、嵌入函数和持久化目录。
- collection_name = 'llama2_demo'
- db = Chroma(
- collection_name=collection_name,
- embedding_function=embeddings,
- persist_directory='./'
- )
对上述代码的具体说明如下所示:
(3)将之前分割得到的文档 splitted_docs 添加到 Chroma 数据库中进行存储,这些文档经过嵌入函数处理后生成了对应的嵌入向量,然后将这些嵌入向量与其原始文本一起存储在数据库中。这样,就可以在后续的检索和分析过程中使用这些嵌入向量来计算文本之间的相似度或执行其他相关任务。
db.add_documents(splitted_docs)
执行后会输出一组文档的唯一标识符,这代表成功将这些文档添加到 Chroma 数据库中的文档 ID,这些 ID以便在需要时进行检索或其他操作。
[Document(page_content='医疗健康|海外CXO生命科学上游公司中报盘点:投融资回暖趋势已现,需求拐\n点在望\n我们选取了14家具有代表性的海外CRO/CDMO企业以及生命科学上游服务商,\n对其中报业绩进行总结并作为对国内企业的参考。我们发现,全球临床阶段的新药研发\n依然活跃,早期研发的需求虽然暂时疲软,但随着海外投融资的回暖,也有望回归快速\n增长常态。生命科学上游服务商的收入,则受到了下游企业去库存周期的影响,但相关\n公司也普遍认为23Q4将出现新增需求的拐点。综上,我们认为一体化服务于全球中后\n期临床研发阶段/商业化阶段外包需求,以及高校/科研院所客户收入占比高的企业业绩', metadata={'page': 0, 'source': './finance.pdf'}), Document(page_content='增长常态。生命科学上游服务商的收入,则受到了下游企业去库存周期的影响,但企业\n普遍认为23Q4将出现新增需求的拐点。综上,我们认为一体化服务于全球中后期临床\n研发阶段/商业化阶段外包需求,以及高校/科研院所/大药企客户收入占比高的企业业\n绩相对稳定;且海外收入占比较高的企业将有望率先受益于投融资回暖带来的需求增加。', metadata={'page': 4, 'source': './finance.pdf'}), Document(page_content='了项目投资计划。来自资金相对更为充沛的高校、科研院所、大药企的需求则更为稳定。\n我们认为投融资的改善有望促进下游企业的资本开支恢复常态。\n3)中国区需求收入有一定波动。2023年H1,海外企业均表示中国区需求出现了\n下降,这一部分是因为国内同样正在经历投融资下滑+去库存的周期,一部分则是因为\n中国本土供应商的崛起:Sartorius认为中国本土供应商的市场份额已显著上升。\n▍风险因素:\n生物医药投融资恢复不及预期;去库存周期长于预期;宏观经济恢复不及预期;研\n发投入不及预期;研发管线推进不及预期。\n▍投资策略:\n我们选取了14家具有代表性的海外CRO/CDMO企业以及生命科学上游服务商,', metadata={'page': 3, 'source': './finance.pdf'}), Document(page_content='中信证券研报表示,选取了14家具有代表性的海外CRO/CDMO企业以及生命科学上\n游服务商,对其中报业绩进行总结并作为对国内企业的参考。中信证券发现,全球临床\n阶段的新药研发依然活跃,早期研发的需求虽然暂时疲软,但随着海外投融资的回暖,\n也有望回归快速增长常态。生命科学上游服务商的收入,则受到了下游企业去库存周期\n的影响,但相关公司也普遍认为四季度将出现新增需求的拐点。综上,中信证券认为一\n体化服务于全球中后期临床研发阶段/商业化阶段外包需求,以及高校/科研院所客户收\n入占比高的企业业绩相对稳定,且海外收入占比较高的企业将有望率先受益于投融资回\n暖带来的需求增加。\n全文如下', metadata={'page': 0, 'source': './finance.pdf'})]
4. 数据检索
下面的代码执行了一个与查询文本“医疗健康投资的风险因素有哪些?”相关的搜索操作,输出的结果是与查询文本相似的文档集合,这些文档包含了与查询文本语义相关的内容。
- test_query = '医疗健康投资的风险因素有哪些?'
- # test_query = 'what is dominant sequence transduction models?'
- search_docs = db.similarity_search(test_query)
- print(search_docs)
执行后会会发现输出的查询结果是文件finance.pdf中的内容:
[Document(page_content='医疗健康|海外CXO生命科学上游公司中报盘点:投融资回暖趋势已现,需求拐\n点在望\n我们选取了14家具有代表性的海外CRO/CDMO企业以及生命科学上游服务商,\n对其中报业绩进行总结并作为对国内企业的参考。我们发现,全球临床阶段的新药研发\n依然活跃,早期研发的需求虽然暂时疲软,但随着海外投融资的回暖,也有望回归快速\n增长常态。生命科学上游服务商的收入,则受到了下游企业去库存周期的影响,但相关\n公司也普遍认为23Q4将出现新增需求的拐点。综上,我们认为一体化服务于全球中后\n期临床研发阶段/商业化阶段外包需求,以及高校/科研院所客户收入占比高的企业业绩', metadata={'page': 0, 'source': './finance.pdf'}), Document(page_content='增长常态。生命科学上游服务商的收入,则受到了下游企业去库存周期的影响,但企业\n普遍认为23Q4将出现新增需求的拐点。综上,我们认为一体化服务于全球中后期临床\n研发阶段/商业化阶段外包需求,以及高校/科研院所/大药企客户收入占比高的企业业\n绩相对稳定;且海外收入占比较高的企业将有望率先受益于投融资回暖带来的需求增加。', metadata={'page': 4, 'source': './finance.pdf'}), Document(page_content='了项目投资计划。来自资金相对更为充沛的高校、科研院所、大药企的需求则更为稳定。\n我们认为投融资的改善有望促进下游企业的资本开支恢复常态。\n3)中国区需求收入有一定波动。2023年H1,海外企业均表示中国区需求出现了\n下降,这一部分是因为国内同样正在经历投融资下滑+去库存的周期,一部分则是因为\n中国本土供应商的崛起:Sartorius认为中国本土供应商的市场份额已显著上升。\n▍风险因素:\n生物医药投融资恢复不及预期;去库存周期长于预期;宏观经济恢复不及预期;研\n发投入不及预期;研发管线推进不及预期。\n▍投资策略:\n我们选取了14家具有代表性的海外CRO/CDMO企业以及生命科学上游服务商,', metadata={'page': 3, 'source': './finance.pdf'}), Document(page_content='中信证券研报表示,选取了14家具有代表性的海外CRO/CDMO企业以及生命科学上\n游服务商,对其中报业绩进行总结并作为对国内企业的参考。中信证券发现,全球临床\n阶段的新药研发依然活跃,早期研发的需求虽然暂时疲软,但随着海外投融资的回暖,\n也有望回归快速增长常态。生命科学上游服务商的收入,则受到了下游企业去库存周期\n的影响,但相关公司也普遍认为四季度将出现新增需求的拐点。综上,中信证券认为一\n体化服务于全球中后期临床研发阶段/商业化阶段外包需求,以及高校/科研院所客户收\n入占比高的企业业绩相对稳定,且海外收入占比较高的企业将有望率先受益于投融资回\n暖带来的需求增加。\n全文如下', metadata={'page': 0, 'source': './finance.pdf'})]
5. 构建模型和变换器管道
(1)加载预训练的中文语言模型,并为其设置相应的tokenizer和模型参数,以便后续在给定文本上进行模型推断或生成。
- model_path = '/mnt/h/Chinese-Llama-2-7b-4bit'
- tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=False)
- model = AutoModelForCausalLM.from_pretrained(
- model_path,
- load_in_4bit=True,
- torch_dtype=torch.float16,
- device_map='auto'
- )
对上述代码的具体说明如下所:
(2)加载预训练的中文语言模型和相应的tokenizer,然后创建一个文本生成管道,用于生成与给定模型和参数相关的文本。
- generation_config = GenerationConfig.from_pretrained(model_path)
- pipe = pipeline(
- "text-generation",
- model=model,
- torch_dtype=torch.bfloat16,
- device_map='auto',
- max_length=2048,
- temperature=0,
- top_p=0.95,
- repetition_penalty=1.15,
- tokenizer=tokenizer,
- generation_config=generation_config,
- )
6. 构建提示
在下面的代码中定义了一个名为 get_prompt 的函数,用于构建提示文本。函数get_prompt接受一个指令参数 instruction 和一个新的系统提示参数 new_system_prompt,并返回构建好的提示模板。
- B_INST, E_INST = "[INST]", "[/INST]"
- B_SYS, E_SYS = "<<SYS>>\n", "\n<</SYS>>\n\n"
- DEFAULT_SYSTEM_PROMPT = """\
- You are a helpful, respectful and honest assistant. Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature.
- If a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don't know the answer to a question, please don't share false information."""
-
- def get_prompt(instruction, new_system_prompt=DEFAULT_SYSTEM_PROMPT ):
- SYSTEM_PROMPT = B_SYS + new_system_prompt + E_SYS
- prompt_template = B_INST + SYSTEM_PROMPT + instruction + E_INST
- return prompt_template
-
- instruction = "What is the temperature in Melbourne?"
- get_prompt(instruction)
对上述代码的具体说明如下所示:
总的来说,这段代码的功能是根据给定的指令和系统提示,构建一个带有系统提示的完整提示模板。执行后会输出一个字符串,表示构建好的提示模板。这个提示模板包含了指令部分和系统提示部分,并且按照指定的格式进行了组合。具体的输出内容为:
[INST]<<SYS>>\nYou are a helpful, respectful and honest assistant. Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature.\n\nIf a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don't know the answer to a question, please don't share false information.\n<</SYS>>\n\nWhat is the temperature in Melbourne?[/INST]
7. 构建一个使用 Hugging Face 库的管道(pipeline)
创建一个 HuggingFacePipeline 对象,构建了一个使用 Hugging Face 库的管道(pipeline)。然后定义了一个解析文本的函数parse_text()。
- llm = HuggingFacePipeline(pipeline=pipe, model_kwargs={'temperature':0})
- def parse_text(text):
- wrapped_text = textwrap.fill(text, width=100)
- print(wrapped_text +'\n\n')
- from langchain.memory import ConversationBufferMemory
- from langchain.prompts import PromptTemplate
- template = """Use the following pieces of context to answer the question at the end. If you don't know the answer,\
- just say that you don't know, don't try to make up an answer. Must use Chinese to answer the question.
- {context}
- {history}
- Question: {question}
- Helpful Answer:"""
- prompt = PromptTemplate(input_variables=["history", "context", "question"], template=template)
- memory = ConversationBufferMemory(input_key='question', memory_key='history')
- from langchain.retrievers.multi_query import MultiQueryRetriever
-
- retriever_from_llm = MultiQueryRetriever.from_llm(retriever=db.as_retriever(), llm=llm)
对上述代码的具体说明如下所:
总的来说,这段代码的功能是设置了一个文本生成管道,定义了一个解析文本的函数,导入了其他必要的模块和类,并创建了一个检索器对象。
注意:在自然语言处理中,管道通常是指一系列数据处理步骤的组合,用于执行特定的任务。Hugging Face 提供了一系列强大的预训练模型和工具,可以用于文本生成、文本分类、命名实体识别等任务。因此,构建 HuggingFacePipeline 的过程可能涉及到了选择合适的模型和配置工作,以及定义数据处理和输出的方式,从而创建一个完成特定任务的管道。
8. 添加上下文压缩
上下文压缩是指对文本中的上下文信息进行压缩或简化,以便在给定的令牌数量限制下处理文档。在处理文档时需要进行上下文压缩工作,因为 Llama2 模型的最大令牌数量为 2048。
(1)请看下面的代码,功能是使用库 LangChain创建一个上下文压缩的检索器。
- from langchain.retrievers.document_compressors import LLMChainExtractor
- from langchain.retrievers import ContextualCompressionRetriever
-
- compressor = LLMChainExtractor.from_llm(llm)
- compression_retriever = ContextualCompressionRetriever(base_compressor=compressor, base_retriever=retriever_from_llm)
- import logging
- logging.getLogger("langchain.retrievers.multi_query").setLevel(logging.DEBUG)
- retri_docs = compression_retriever.get_relevant_documents('医疗健康投资的风险因素有哪些?')
- print(retri_docs)
上述代码的功能是利用 LangChain 库中的功能创建了一个上下文压缩的检索器,并执行了一次文档检索操作,返回了相关的文档。对上述代码的具体说明如下所示:
[Document(page_content='"全球临干阶段的新药研发依然活跃"', metadata={'page': 0, 'source': './finance.pdf'}), Document(page_content='"生命科学上游服务商的收入,则受到了下游企业去库存周期的影响"', metadata={'page': 0, 'source': './finance.pdf'}), Document(page_content='"生命科学上游服务商的收入,则受到了下游企业去库存周期的影响"', metadata={'page': 4, 'source': './finance.pdf'}), Document(page_content='"全球生物医药领域的投融资已出现回暖"', metadata={'page': 2, 'source': './finance.pdf'}), Document(page_content='"生物医药投融资恢复不及预期" and "去库存周期长于预期".', metadata={'page': 3, 'source': './finance.pdf'}), Document(page_content='Repligen, Waters, MerckKGaA, Sartorius are representatives of overseas life science upstream suppliers.', metadata={'page': 2, 'source': './finance.pdf'}), Document(page_content='IQVIA, Medpace, CRO, Q2, RFP, business growth, new contracts, revenue increase, client engagement, overall performance, industry outlook.', metadata={'page': 1, 'source': './finance.pdf'})]
(2)创建了一个用于检索问答的对象,该对象将使用预训练语言模型和上下文压缩的检索器来检索相关文档,并使用指定的提示模板和记忆对象来生成答案。
- qa = RetrievalQA.from_chain_type(
- llm = llm,
- chain_type = 'stuff',
- retriever = compression_retriever,
- return_source_documents = True,
- chain_type_kwargs = {"prompt": prompt, "memory": memory}
- )
方法RetrievalQA.from_chain_type用于根据给定的链类型创建 RetrievalQA 对象,在上述代码中,此方法指定了链类型为 'stuff',各个参数的具体说明如下所示。
9. Q&A问答系统
根据用户输入的查询问题,返回与之相关的答案和文档。
- query = input("\nEnter a query: ")
- res = qa(query)
- print(res)
上述代码的实现流程如下所示:
加入用户输入的问题是“医疗健康投资的风险因素有哪些?”,则执行后输出一个字典,其中包含了查询的问题、返回的结果和相关的源文档。
- {
- 'query': '医疗健康投资的风险因素有哪些?',
- 'result': '在医疗健康行业中进行投资时,可能会面临以下一些主要风险因素:1)政府法规和监管变化;2)技术创新与市场需求的失衡;3)公司治理问题;4)经济波动对消费者支付力度的影响;5)人才流失等。此外,还应注意其他特定行业或项目的风险,如基因工程、生物制品安全性等方面的风险。',
- 'source_documents': [
- Document(page_content='"全球临干阶段的新药研发依然活跃"', metadata={'page': 0, 'source': './finance.pdf'}),
- Document(page_content='"生命科学上游服务商的收入,则受到了下游企业去库存周期的影响"', metadata={'page': 0, 'source': './finance.pdf'}),
- Document(page_content='"生命科学上游服务商的收入,则受到了下游企业去库存周期的影响"', metadata={'page': 4, 'source': './finance.pdf'}),
- Document(page_content='"全球生物医药领域的投融资已出现回暖"', metadata={'page': 2, 'source': './finance.pdf'}),
- Document(page_content='"生物医药投融资恢复不及预期" and "去库存周期长于预期".', metadata={'page': 3, 'source': './finance.pdf'}),
- Document(page_content='Repligen, Waters, MerckKGaA, Sartorius are representatives of overseas life science upstream suppliers.', metadata={'page': 2, 'source': './finance.pdf'}),
- Document(page_content='IQVIA, Medpace, CRO, Q2, RFP, business growth, new contracts, revenue increase, client engagement, business outlook, industry trends.', metadata={'page': 1, 'source': './finance.pdf'})
- ]
- }
注意:本项目的参考资料是开源项目chatbot_llama2,此项目在Github开源。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。