第30章 LMQL 与 LangChain 集成指南
LangChain 借助 LMQL 充分发挥你的 LangChain 技术栈
LMQL 也可以与 🦜🔗 LangChain Python 库配合使用。既支持在 LMQL 代码中调用 langchain 库,也支持把 LMQL 查询作为 Chain 的一部分。
在 LMQL 中使用 LangChain
先来看第一种情况:在 LMQL 程序中使用 LangChain 模块。这个例子利用 LangChain 的 Chroma 检索能力,实现针对文本文档的问答(这里用的是 LMQL 论文)。
首先导入所需的库。
lmql import lmql import asyncio from langchain.embeddings.openai import OpenAIEmbeddings from langchain.vectorstores import Chroma
接下来加载并嵌入相关文档的文本(这里是 lmql.txt)。
lmql # load text of LMQL paper with open("lmql.txt") as f: contents = f.read() texts = [] for i in range(0, len(contents), 120): texts.append(contents[i:i+120])
embeddings = OpenAIEmbeddings() docsearch = Chroma.from_texts(texts, embeddings, metadatas=[{"text": t} for t in texts], persist_directory="lmql-index")
然后构建一个聊天机器人函数,内部用一个简单的 LMQL 查询:先通过 await input(...) 获取用户问题,再用 LangChain 检索相关文本段落,最后用 openai/gpt-3.5-turbo(ChatGPT)生成回答。
lmql import termcolor
@lmql.query(model="openai/gpt-3.5-turbo") async def chatbot(): '''lmql # system instruction """{:system} You are a chatbot that helps users answer questions. You are first provided with the question and relevant information.""" # chat loop while True: # process user input q = await input("\nQuestion: ") if q == "exit": break # expose question to model "{:user} {q}\n" # insert retrieval results print(termcolor.colored("Reading relevant pages...", "green")) results = set([d.page_content for d in docsearch.similarity_search(q, 4)]) information = "\n\n".join(["..." + r + "..." for r in list(results)]) "{:system} \nRelevant Information: {information}\n" # generate model response "{:assistant} [RESPONSE]" '''
await chatbot(output_writer=lmql.stream(variable="RESPONSE"))
promptdown Chat Log
bubble:system You are a chatbot that helps users answer questions. You are first provided with the question and relevant information. bubble:user What is LMQL? bubble:system Relevant Information: (inserted by retriever) bubble:assistant LMQL is a high-level query language for LMs that allows for great expressiveness and supports scripted prompting. bubble:user How to write prompts? bubble:system Relevant Information: (inserted by retriever) bubble:assistant To write prompts, you can use a language model to expand the prompt and obtain the answer to a specific question.
如上面的查询所示,Python 脚本中的内联 LMQL 代码可以直接访问外层作用域中的变量(例如 docsearch),以及 LangChain 提供的各种工具函数和对象。
关于用 LMQL 构建聊天应用的更多细节,请参考 Chat API 文档。
在 LangChain 中使用 LMQL
除了在 LMQL 查询中使用 langchain 工具,LMQL 查询也可以无缝集成到 langchain 的 Chain 组件中。
以 langchain 文档中的顺序提示(sequential prompting)示例为例:先让语言模型为某个产品起一个公司名,再让它想一句宣传语。
首先导入所需的 langchain 组件和 LMQL。
lmql from langchain import LLMChain, PromptTemplate from langchain.chat_models import ChatOpenAI from langchain.prompts.chat import (ChatPromptTemplate,HumanMessagePromptTemplate) from langchain.llms import OpenAI
import lmql
我们的 Chain 包含两个阶段:(1) 让模型起公司名;(2) 让模型想宣传语。为了演示,我们用 langchain 提示实现 (1),用 LMQL 查询实现 (2)。
先定义获取公司名的 langchain 提示,并实例化对应的 LLMChain:
lmql # setup the LM to be used by langchain llm = OpenAI(temperature=0.9)
human_message_prompt = HumanMessagePromptTemplate( prompt=PromptTemplate( template="What is a good name for a company that makes {product}?", input_variables=["product"], ) ) chat_prompt_template = ChatPromptTemplate.from_messages([human_message_prompt]) chat = ChatOpenAI(temperature=0.9) chain = LLMChain(llm=chat, prompt=chat_prompt_template)
现在就可以单独运行它来生成公司名了:
lmql chain.run("colorful socks")
result 'VibrantSock Co.\nColorSplash Socks\nRainbowThreads\nChromaSock Co.\nKaleidosocks\nColorPop Socks\nPrismStep\nSockMosaic\nHueTrend Socks\nSpectrumStitch\nColorBurst Socks'
接着用 LMQL 定义提示 (2),即生成宣传语的 LMQL 查询:
lmql @lmql.query(model="chatgpt") async def write_catch_phrase(company_name: str): ''' "Write a catchphrase for the following company: {company_name}. [catchphrase]" '''
同样可以单独运行这一部分:
lmql (await write_catch_phrase("Socks Inc")).variables["catchphrase"]
result ' "Step up your style with Socks Inc. - where comfort meets fashion!"'
要把两个提示串联起来,可以使用 langchain 的 SimpleSequentialChain。只需在传入 SimpleSequentialChain 构造函数之前,对 LMQL 查询调用 .aschain(),它就能兼容 langchain。
lmql from langchain.chains import SimpleSequentialChain overall_chain = SimpleSequentialChain(chains=[chain, write_catch_phrase.aschain()], verbose=True)
现在运行整个 Chain,它同时结合了 LMQL 和 langchain 组件:
lmql # Run the chain specifying only the input variable for the first chain. catchphrase = overall_chain.run("colorful socks") print(catchphrase)
> Entering new SimpleSequentialChain chain... RainbowSocks Co. "Step into a world of color with RainbowSocks Co.!"
> Finished chain. "Step into a world of color with RainbowSocks Co.!"
最终我们得到了一个由 langchain 和 LMQL 组件共同组成、可作为整体使用的 Chain。
异步使用
LangChain 的同步 API 与 LMQL 的异步优先设计可能不匹配,从而引发问题。
为避免这类问题,可以安装 nest_asyncio 包并调用 nest_asyncio.apply() 来启用嵌套事件循环。之后 LMQL 会自动处理事件循环嵌套以及同步到异步的转换。