跳至主要内容

如何返回来源

先决条件

本指南假定您熟悉以下内容

在问答应用程序中,通常需要向用户展示用于生成答案的来源。最简单的方法是让链返回在每次生成中检索到的文档。

我们将使用 Lilian Weng 编写的 LLM 驱动的自主代理 博客文章作为本笔记本检索内容。

设置

依赖项

在本演练中,我们将使用 OpenAI 聊天模型和嵌入以及内存向量存储,但此处显示的所有内容都适用于任何 ChatModelLLM嵌入 以及 VectorStore检索器

我们将使用以下软件包

npm install --save langchain @langchain/openai cheerio

我们需要设置环境变量 OPENAI_API_KEY

export OPENAI_API_KEY=YOUR_KEY

LangSmith

您使用 LangChain 构建的许多应用程序将包含多个步骤,这些步骤将包含对 LLM 调用的多次调用。随着这些应用程序变得越来越复杂,能够检查链或代理内部究竟发生了什么变得至关重要。最好的方法是使用 LangSmith

请注意,LangSmith 不是必需的,但它很有用。如果您确实想使用 LangSmith,在您从上面的链接注册后,请确保设置您的环境变量以开始记录跟踪

export LANGCHAIN_TRACING_V2=true
export LANGCHAIN_API_KEY=YOUR_KEY

# Reduce tracing latency if you are not in a serverless environment
# 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 的其他一些指南,例如 如何流式传输响应


此页面是否有帮助?


您也可以在 GitHub 上留下详细的反馈 GitHub.