如何返回来源
预备知识
本指南假定您已熟悉以下内容:
在问答应用中,向用户展示生成答案所使用的来源信息通常非常重要。实现这一点的最简单方法是让链结构返回每次生成中检索到的文档(Documents)。
我们将使用 Lilian Weng 撰写的LLM 驱动的自主代理博客文章作为本笔记本的检索内容。
环境配置
依赖项
在本次演示中,我们将使用 OpenAI 的聊天模型和嵌入模型,以及一个用于记忆的向量存储。但这里展示的所有功能都适用于任何 聊天模型 或 LLM、嵌入模型、向量存储 或 检索器。
我们将使用以下软件包:
npm install --save langchain @langchain/openai cheerio
我们需要设置环境变量 OPENAI_API_KEY:
export OPENAI_API_KEY=你的密钥
LangSmith
使用 LangChain 构建的许多应用程序将包含多个步骤,并多次调用 LLM。随着这些应用程序变得越来越复杂,能够检查链或代理内部确切发生的情况变得至关重要。要做到这一点,最佳方式是使用LangSmith。
请注意,LangSmith 并非必需,但它非常有帮助。如果确实想要使用 LangSmith,请在上方链接注册后,确保设置环境变量以开始记录追踪信息:
export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY=YOUR_KEY
# 如果你不在无服务器环境中,可启用此选项以减少追踪延迟
# export LANGCHAIN_CALLBACKS_BACKGROUND=true
无来源的链
这是我们在 Lilian Weng 的LLM 驱动的自主代理博客文章中,在快速入门部分构建的问答应用程序。
import "cheerio";
import { CheerioWebBaseLoader } from "@langchain/community/document_loaders/web/cheerio";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { OpenAIEmbeddings, ChatOpenAI } from "@langchain/openai";
import { pull } from "langchain/hub";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { formatDocumentsAsString } from "langchain/util/document";
import {
RunnableSequence,
RunnablePassthrough,
} from "@langchain/core/runnables";
import { StringOutputParser } from "@langchain/core/output_parsers";
const loader = new CheerioWebBaseLoader(
"https://lilianweng.github.io/posts/2023-06-23-agent/"
);
const docs = await loader.load();
const textSplitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
});
const splits = await textSplitter.splitDocuments(docs);
const vectorStore = await MemoryVectorStore.fromDocuments(
splits,
new OpenAIEmbeddings()
);
// Retrieve and generate using the relevant snippets of the blog.
const retriever = vectorStore.asRetriever();
const prompt = await pull<ChatPromptTemplate>("rlm/rag-prompt");
const llm = new ChatOpenAI({ model: "gpt-3.5-turbo", temperature: 0 });
const ragChain = RunnableSequence.from([
{
context: retriever.pipe(formatDocumentsAsString),
question: new RunnablePassthrough(),
},
prompt,
llm,
new StringOutputParser(),
]);
让我们看看这个提示实际是什么样子:
console.log(prompt.promptMessages.map((msg) => msg.prompt.template).join("\n"));
You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.
Question: {question}
Context: {context}
Answer:
await ragChain.invoke("What is task decomposition?");
"Task decomposition is a technique used to break down complex tasks into smaller and simpler steps. T"... 254 more characters
添加来源
使用 LCEL,我们可以轻松地将检索到的文档传递到整个链中,并在最终响应中返回它们:
import {
RunnableMap,
RunnablePassthrough,
RunnableSequence,
} from "@langchain/core/runnables";
import { formatDocumentsAsString } from "langchain/util/document";
const ragChainWithSources = RunnableMap.from({
// Return raw documents here for now since we want to return them at
// the end - we'll format in the next step of the chain
context: retriever,
question: new RunnablePassthrough(),
}).assign({
answer: RunnableSequence.from([
(input) => {
return {
// Now we format the documents as strings for the prompt
context: formatDocumentsAsString(input.context),
question: input.question,
};
},
prompt,
llm,
new StringOutputParser(),
]),
});
await ragChainWithSources.invoke("What is Task Decomposition");
{
question: "What is Task Decomposition",
context: [
Document {
pageContent: "Fig. 1. Overview of a LLM-powered autonomous agent system.\n" +
"Component One: Planning#\n" +
"A complicated ta"... 898 more characters,
metadata: {
source: "https://lilianweng.github.io/posts/2023-06-23-agent/",
loc: { lines: [Object] }
}
},
Document {
pageContent: 'Task decomposition can be done (1) by LLM with simple prompting like "Steps for XYZ.\\n1.", "What are'... 887 more characters,
metadata: {
source: "https://lilianweng.github.io/posts/2023-06-23-agent/",
loc: { lines: [Object] }
}
},
Document {
pageContent: "Agent System Overview\n" +
" \n" +
" Component One: Planning\n" +
" "... 850 more characters,
metadata: {
source: "https://lilianweng.github.io/posts/2023-06-23-agent/",
loc: { lines: [Object] }
}
},
Document {
pageContent: "Resources:\n" +
"1. Internet access for searches and information gathering.\n" +
"2. Long Term memory management"... 456 more characters,
metadata: {
source: "https://lilianweng.github.io/posts/2023-06-23-agent/",
loc: { lines: [Object] }
}
}
],
answer: "Task decomposition is a technique used to break down complex tasks into smaller and simpler steps fo"... 230 more characters
}
点击此处查看 LangSmith 追踪,了解链的内部机制。
下一步
你现在已了解如何从 QA 链中返回来源。
接下来,请查看有关 RAG 的其他指南,例如如何流式传输响应。