# 使用 Python f 字符串模板:from langchain.prompts import PromptTemplate
fstring_template = """Tell me a {adjective} joke about {content}"""
prompt = PromptTemplate.from_template(fstring_template)
print(prompt.format(adjective="funny", content="chickens"))
# Output: Tell me a funny joke about chickens.
# ChatPromptTemplate.from_messages 接受各种消息表示形式。
template = ChatPromptTemplate.from_messages([
("system", "You are a helpful AI bot. Your name is {name}."),
("human", "Hello, how are you doing?"),
("ai", "I'm doing well, thanks!"),
("human", "{user_input}"),
])
messages = template.format_messages(
name="Bob",
user_input="What is your name?"
)
print(messages)
2️⃣部分提示词模版:
在生成 prompt 前就已经提前初始化部分的提示词,实际进一步导入模版的时候只导入除已初始化的变量即可。通常部分提示词模版会被用在全局设置上,如下示例,在正式 format 前设定 foo 值为 foo,这样在生成最终 prompt 的时候只需要指定 bar 的值即可。有两种方法去指定部分提示词:
from datetime import datetime
def_get_datetime():
now = datetime.now()
return now.strftime("%m/%d/%Y, %H:%M:%S")
prompt = PromptTemplate(
template="Tell me a {adjective} joke about the day {date}",
input_variables=["adjective", "date"]
)
partial_prompt = prompt.partial(date=_get_datetime)
print(partial_prompt.format(adjective="funny"))
# 除上述方法,部分函数声明和普通的 prompt 一样,也可以直接用 partial_variables 去声明
prompt = PromptTemplate(
template="Tell me a {adjective} joke about the day {date}",
input_variables=["adjective"],
partial_variables={"date": _get_datetime})
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.prompts import FewShotPromptTemplate, PromptTemplate
examples = [
{"question": "Who lived longer, Muhammad Ali or Alan Turing?",
"answer":
"""
Are follow up questions needed here: Yes.
Follow up: How old was Muhammad Ali when he died?
Intermediate answer: Muhammad Ali was 74 years old when he died.
Follow up: How old was Alan Turing when he died?
Intermediate answer: Alan Turing was 41 years old when he died.
So the final answer is: Muhammad Ali
"""},
{"question": "When was the founder of craigslist born?",
"answer":
"""
Are follow up questions needed here: Yes.
Follow up: Who was the founder of craigslist?
Intermediate answer: Craigslist was founded by Craig Newmark.
Follow up: When was Craig Newmark born?
Intermediate answer: Craig Newmark was born on December 6, 1952.
So the final answer is: December 6, 1952
"""},
{"question": "Who was the maternal grandfather of George Washington?",
"answer":
"""
Are follow up questions needed here: Yes.
Follow up: Who was the mother of George Washington?
Intermediate answer: The mother of George Washington was Mary Ball Washington.
Follow up: Who was the father of Mary Ball Washington?
Intermediate answer: The father of Mary Ball Washington was Joseph Ball.
So the final answer is: Joseph Ball
"""},
{"question": "Are both the directors of Jaws and Casino Royale from the same country?",
"answer":
"""
Are follow up questions needed here: Yes.
Follow up: Who is the director of Jaws?
Intermediate Answer: The director of Jaws is Steven Spielberg.
Follow up: Where is Steven Spielberg from?
Intermediate Answer: The United States.
Follow up: Who is the director of Casino Royale?
Intermediate Answer: The director of Casino Royale is Martin Campbell.
Follow up: Where is Martin Campbell from?
Intermediate Answer: New Zealand.
So the final answer is: No
"""}
]
# 配置一个格式化程序,该格式化程序将 prompt 格式化为字符串。此格式化程序应该是一个 PromptTemplate 对象。
example_prompt = PromptTemplate(input_variables=["question", "answer"], template="Question: {question}\n{answer}")
print(example_prompt.format(**examples[0]))
# 创建一个选择器来选择最相似的例子
example_selector = SemanticSimilarityExampleSelector(
examples=examples,
vector_store=Chroma(),
embeddings_model=OpenAIEmbeddings(),
example_prompt=example_prompt
)
# 最后用 FewShotPromptTemplate 来创建一个提示词模板,该模板将输入变量作为输入,并将其格式化为包含示例的提示词。
prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
suffix="Question: {input}",
input_variables=["input"]
)
print(prompt)
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
example_selector = SemanticSimilarityExampleSelector.from_examples(
# 可选的示例列表。
examples,
# 用于生成嵌入的嵌入类,这些嵌入用于测量语义相似性。
OpenAIEmbeddings(),
# 用于存储嵌入并进行相似性搜索的 VectorStore 类。
Chroma,
# 要生成的示例数。
k=1)
然后我们去输入一条想要构建的 prompt,遍历整个示例列表,找到最为合适的 example。
# 选择与输入最相似的示例。
question = "Who was the father of Mary Ball Washington?"
selected_examples = example_selector.select_examples({"question": question})
print(f"Examples most similar to the input: {question}")
for example in selected_examples:
print("\n")
for k, v in example.items():
print(f"{k}: {v}")
此时就可以返回一个最相似的例子。接下来我们可以重新重复 few shot 的步骤,利用 FewShotPromptTemplate 去创建一个提示词模版。
对于聊天类型的 few shot 的 prompt 我们也可以采用例子选择器进行格式化:
examples = [
{"input": "2+2", "output": "4"},
{"input": "2+3", "output": "5"},
{"input": "2+4", "output": "6"},
{"input": "What did the cow say to the moon?", "output": "nothing at all"},
{
"input": "Write me a poem about the moon",
"output": "One for the moon, and one for me, who are we to talk about the moon?",
},
]
# 由于我们使用向量存储来根据语义相似性选择示例,因此我们需要首先填充存储。
to_vectorize = [" ".join(example.values()) for example in examples]
# 这里就单纯理解为将 value 对应的值提取出来进行格式化即可。# 创建向量库后,可以创建 example_selector 以表示返回的相似向量的个数# 注意:您需要先创建一个向量存储库(例如:vectorstore = ...)并填充它,然后将其传递给 SemanticSimilarityExampleSelector。
example_selector = SemanticSimilarityExampleSelector(vectorstore=vectorstore, k=2)
# 提示词模板将通过将输入传递给 `select_examples` 方法来加载示例
example_selector.select_examples({"input": "horse"})
此时就可以返回两个个最相似的例子。接下来我们可以重复 few shot 的步骤 利用 FewShotChatPromptTemplate 去创建一个提示词模版。
# 从 langchain.llms.human 模块导入 HumanInputLLM 类,此类可能允许人类输入或交互来模拟 LLM 的行为from langchain.llms.human import HumanInputLLM
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
# 调用 load_tools 函数,加载名为"wikipedia"的工具
tools = load_tools(["wikipedia"])
# 初始化一个 HumanInputLLM 对象,其中 prompt_func 是一个函数,用于打印提示信息
llm = HumanInputLLM(
prompt_func=lambda prompt: print(f"\n===PROMPT====\n{prompt}\n=====END OF PROMPT======"))
# 调用 initialize_agent 函数,使用上面的 tools 和 llm,以及指定的代理类型和 verbose 参数来初始化一个代理
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
# 调用代理的 run 方法,传递字符串"What is 'Bocchi the Rock!'?"作为输入,询问代理关于'Bocchi the Rock!'的信息
agent.run("What is 'Bocchi the Rock!'?")
缓存大语言模型:和测试大语言模型具有一样效果的是缓存大语言模型,通过缓存层可以尽可能的减少 API 的调用次数,从而节省费用。在 LangChain 中设置缓存分为两种情况:一是在内存中设置缓存,二是在数据中设置缓存。存储在内存中加载速度较快,但是占用资源并且在关机之后将不再被缓存,在内存中设置缓存示例如下:
from langchain.cache import SQLiteCache
import langchain
from langchain.llms import OpenAI
import time
langchain.llm_cache = SQLiteCache(database_path=".langchain.db")
llm = OpenAI(model_name="text-davinci-002", n=2, best_of=2)
start_time = time.time() # 记录开始时间print(llm.predict("用中文讲个笑话"))
end_time = time.time() # 记录结束时间
elapsed_time = end_time - start_time # 计算总时间print(f"Predict method took {elapsed_time:.4f} seconds to execute.")
from langchain.cache import SQLiteCache
import langchain
from langchain.llms import OpenAI
import time
langchain.llm_cache = SQLiteCache(database_path=".langchain.db")
llm = OpenAI(model_name="text-davinci-002", n=2, best_of=2)
start_time = time.time() # 记录开始时间print(llm.predict("用中文讲个笑话"))
end_time = time.time() # 记录结束时间
elapsed_time = end_time - start_time # 计算总时间print(f"Predict method took {elapsed_time:.4f} seconds to execute.")
跟踪 token 使用情况(仅限 model 为 openAI):
from langchain.llms import OpenAI
from langchain.callbacks import get_openai_callback
llm = OpenAI(model_name="text-davinci-002", n=2, best_of=2, cache=None)
with get_openai_callback() as cb:
result = llm("讲个笑话")
print(cb)
流式处理大语言模型的响应:流式处理意味着,在接收到第一个数据块后就立即开始处理,而不需要等待整个数据包传输完毕。这种概念应用在 LLM 中则可达到生成响应时就立刻向用户展示此下的响应,或者在生成响应时处理响应,也就是我们现在看到的和 ai 对话时逐字输出的效果:可以看到实现还是较为方便的只需要直接调用 StreamingStdOutCallbackHandler 作为 callback 即可。
from langchain.llms import OpenAI
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
llm = OpenAI(streaming=True, callbacks=[StreamingStdOutCallbackHandler()], temperature=0)
resp = llm("Write me a song about sparkling water.")
Model 返回的内容通常都是字符串的模式,但在实际开发过程中,往往希望 model 可以返回更直观的内容,LangChain 提供的输出解析器则将派上用场。在实现一个输出解析器的过程中,需要实现两种方法:1️⃣获取格式指令:返回一个字符串的方法,其中包含有关如何格式化语言模型输出的说明。2️⃣Parse:一种接收字符串(假设是来自语言模型的响应)并将其解析为某种结构的方法。
列表解析器:利用此解析器可以输出一个用逗号分割的列表。
from langchain.output_parsers import CommaSeparatedListOutputParser
from langchain.prompts import PromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate
from langchain.llms import OpenAI
from langchain.chat_models import ChatOpenAI
output_parser = CommaSeparatedListOutputParser()
format_instructions = output_parser.get_format_instructions()
prompt = PromptTemplate(
template="List five {subject}.\n{format_instructions}",
input_variables=["subject"],
partial_variables={"format_instructions": format_instructions}
)
model = OpenAI(temperature=0)
_input = prompt.format(subject="冰淇淋口味")
output = model(_input)
output_parser.parse(output)
日期解析器:利用此解析器可以直接将 LLM 输出解析为日期时间格式。
from langchain.prompts import PromptTemplate
from langchain.output_parsers import DatetimeOutputParser
from langchain.chains import LLMChain
from langchain.llms import OpenAI
output_parser = DatetimeOutputParser()
template = """回答用户的问题:
{question}
{format_instructions}"""
prompt = PromptTemplate.from_template(
template,
partial_variables={"format_instructions": output_parser.get_format_instructions()},
)
chain = LLMChain(prompt=prompt, llm=OpenAI())
output = chain.run("bitcoin 是什么时候成立的?用英文格式输出时间")
枚举解析器
from langchain.output_parsers.enum import EnumOutputParser
from enum import Enum
classColors(Enum):
RED = "red"
GREEN = "green"
BLUE = "blue"
parser = EnumOutputParser(enum=Colors)
乘积量化:该思想将高维向量分解为多个子向量。例如,将一个 D 维向量分解为 m 个子向量,每个子向量的维度为 D/m。然后对每个子向量进行量化。对于每个子向量空间,使用聚类算法将子向量分为 K 个簇,并将簇中心作为量化值。然后,用子向量在簇中的索引来表示原始子向量。这样,每个子向量可以用一个整数(量化索引)来表示。最后将量化索引组合起来表示原始高维向量。对于一个 D 维向量,可以用 m 个整数来表示,其中每个整数对应一个子向量的量化索引。此外这类方法不仅可以用于优化存储向量也可以用于优化检索。
前文中大概介绍了向量数据库是什么以及向量数据库所依赖的一些实现技术,接下来我们来谈论一下向量数据库与大模型之间的关系。为什么说想要用好大模型往往离不开向量数据库呢?对于大模型来讲,处理的数据格式一般都是非结构化数据,如音频、文本、图像…我们以大语言模型为例,在喂一份数据给大模型的时候,数据首先会被转为向量,在上述内容中我们知道如果向量较近那么就表示这两个向量含有的信息更为相似,当大量数据不断被喂到大模型中的时候,语言模型就会逐渐发现词汇间的语义和语法。当用户进行问答的时候,问题输入 Model 后会基于 Transformer 架构从每个词出发去找到它与其他词的关系权重,找到权重最重的一组搭配,这一组就为此次问答的答案了。最后再将这组向量返回回来,也就完成了一次问答。当我们把向量数据库接入到 AI 中,我们就可以通过更新向量数据库的数据,使得大模型能够不断获取并学习到业界最新的知识,而不是将能力局限于预训练的数据中。这种方式要比微调/重新训练大模型的方式节约更多成本。
这种类型的 Chain 应用起来很简单也可以说是后续要介绍的 Chain 的基础,但其功能是足够强大的。通过 LLMChain 可以直接将数据、prompt、以及想要应用的 Model 串到一起,以一个简单的例子来感受 LLMChain。
from langchain import PromptTemplate, OpenAI, LLMChain
prompt_template = "What is a good name for a company that makes {product}?"
llm = OpenAI(temperature=0)
chain = LLMChain(
llm=llm,
prompt=PromptTemplate.from_template(prompt_template)
)
print(chain("colorful socks"))
# 输出结果'Socktastic!'
# This is an LLMChain to write a synopsis given a title of a play.from langchain import PromptTemplate, OpenAI, LLMChain
llm = OpenAI(temperature=.7)
template = """You are a playwright. Given the title of play, it is your job to write a synopsis for that title.
Title: {title}
Playwright: This is a synopsis for the above play:"""
prompt_template = PromptTemplate(input_variables=["title"], template=template)
synopsis_chain = LLMChain(llm=llm, prompt=prompt_template)
第二条 chain:
# This is an LLMChain to write a review of a play given a synopsis.from langchain import PromptTemplate, OpenAI, LLMChain
llm = OpenAI(temperature=.7)
template = """You are a play critic from the New York Times. Given the synopsis of play, it is your job to write a review for that play.
Play Synopsis:
{synopsis}
Review from a New York Times play critic of the above play:"""
prompt_template = PromptTemplate(input_variables=["synopsis"], template=template)
review_chain = LLMChain(llm=llm, prompt=prompt_template)
最后利用 SimpleSequentialChain 即可将两个 chain 直接串联起来:
from langchain.chains import SimpleSequentialChain
overall_chain = SimpleSequentialChain(chains=[synopsis_chain, review_chain], verbose=True)
print(review = overall_chain.run("Tragedy at sunset on the beach"))
from langchain import PromptTemplate, OpenAI, LLMChain
llm = OpenAI(temperature=.7)
template = """You are a playwright. Given the title of play and the era it is set in, it is your job to write a synopsis for that title.
Title: {title}
Era: {era}
Playwright: This is a synopsis for the above play:"""
prompt_template = PromptTemplate(input_variables=["title", 'era'], template=template)
synopsis_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="synopsis")
#第一条 chain
from langchain import PromptTemplate, OpenAI, LLMChain
llm = OpenAI(temperature=.7)
template = """You are a play critic from the New York Times. Given the synopsis of play, it is your job to write a review for that play.
Play Synopsis:
{synopsis}
Review from a New York Times play critic of the above play:"""
prompt_template = PromptTemplate(input_variables=["synopsis"], template=template)
review_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="review")
#第二条 chain
from langchain.chains import SequentialChain
overall_chain = SequentialChain(
chains=[synopsis_chain, review_chain],
input_variables=["era", "title"],
# Here we return multiple variables
output_variables=["synopsis", "review"],
verbose=True)
#第三条 chain
overall_chain({"title": "Tragedy at sunset on the beach", "era": "Victorian England"})
physics_template = """You are a very smart physics professor. \
You are great at answering questions about physics in a concise and easy to understand manner. \
When you don't know the answer to a question you admit that you don't know.
Here is a question:
{input}"""
math_template = """You are a very good mathematician. You are great at answering math questions. \
You are so good because you are able to break down hard problems into their component parts, \
answer the component parts, and then put them together to answer the broader question.
Here is a question:
{input}"""
如上有一个物理学和数学的 prompt:
prompt_infos = [
{
"name": "physics",
"description": "Good for answering questions about physics",
"prompt_template": physics_template
},
{
"name": "math",
"description": "Good for answering math questions",
"prompt_template": math_template
}
]
然后,需要声明这两个 prompt 的基本信息。
from langchain import ConversationChain, LLMChain, PromptTemplate, OpenAI
llm = OpenAI()
destination_chains = {}
for p_info in prompt_infos:
name = p_info["name"]
prompt_template = p_info["prompt_template"]
prompt = PromptTemplate(template=prompt_template, input_variables=["input"])
chain = LLMChain(llm=llm, prompt=prompt)
destination_chains[name] = chain
default_chain = ConversationChain(llm=llm, output_key="text")
from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser
from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE
# Create a list of destinations
destinations = [f"{p['name']}: {p['description']}"for p in prompt_infos]
destinations_str = "\n".join(destinations)
# Create a router template
router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(destinations=destinations_str)
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,
)
print(chain.run('什么是黑体辐射'))
七、Agents
Agents 这一模块在 langchain 的使用过程中也是十分重要的,官方文档是这样定义它的'The core idea of agents is to use a language model to choose a sequence of actions to take. In chains, a sequence of actions is hardcoded (in code). In agents, a language model is used as a reasoning engine to determine which actions to take and in which order.'也就是说,在使用 Agents 时,其行为以及行为的顺序是由 LLM 的推理机制决定的,并不是像传统的程序一样,由核心代码预定义好去运行的。
from langchain.agents import initialize_agent, Tool
tools = [
Tool(
name="Search",
func=search,
description="useful for when you need to answer questions about current events, data. You should ask targeted questions"
),
ScrapeWebsiteTool(),
]
先看第一个工具:在配置工具时,需要声明工具依赖的函数,由于该示例实现的功能为依赖网络收集相应的信息,然后汇总成一篇论文,所以创建了一个 search 函数,这个函数用于调用 Google 搜索。它接受一个查询参数,然后将查询发送给 Serper API。API 的响应会被打印出来并返回。
再来看一下所依赖的第二个工具函数,这里用了另一种声明工具的方式 Class 声明——ScrapeWebsiteTool(),它有以下几个属性和方法:
classScrapeWebsiteTool(BaseTool):
name = "scrape_website"
description = "useful when you need to get data from a website url, passing both url and objective to the function; DO NOT make up any url, the url should only be from the search results"
args_schema: Type[BaseModel] = ScrapeWebsiteInput
def_run(self, target: str, url: str):
return scrape_website(target, url)
def_arun(self, url: str):
raise NotImplementedError("error here")
classScrapeWebsiteInput(BaseModel):
"""Inputs for scrape_website"""
target: str = Field(
description="The objective & task that users give to the agent")
url: str = Field(description="The url of the website to be scraped")
import os
from dotenv import load_dotenv
from langchain import PromptTemplate
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType
from langchain.chat_models import ChatOpenAI
from langchain.prompts import MessagesPlaceholder
from langchain.memory import ConversationSummaryBufferMemory
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chains.summarize import load_summarize_chain
from langchain.tools import BaseTool
from pydantic import BaseModel, Field
from langchain.schema import SystemMessage
from typing importTypefrom bs4 import BeautifulSoup
import requests
import json
import streamlit as st
# 加载必要的参数
load_dotenv()
serper_api_key=os.getenv("SERPER_API_KEY")
browserless_api_key=os.getenv("BROWSERLESS_API_KEY")
openai_api_key=os.getenv("OPENAI_API_KEY")
main 函数:这是 streamlit 应用的主函数。它首先设置了页面的标题和图标,然后创建了一些 header,并提供一个文本输入框让用户输入查询。当用户输入查询后,它会调用 agent 来处理这个查询,并将结果显示在页面上。
timerHandler = TimerHandler()
llm = OpenAI()
response = llm.predict("What is the HEX code of color BLACK?", callbacks=[timerHandler])
print(response)
这段代码首先创建一个 TimerHandler 实例并将其赋值给变量 timerHandler。然后创建一个 OpenAI 实例并将其赋值给变量 llm。调用 llm.predict() 方法,传入问题' What is the HEX code of color BLACK?',并通过 callbacks 参数设置回调处理器 timerHandler。