Skip to content

八、生成

8.1 生成原理

查找到相关数据之后,就可以将所有的数据放到和LLM交互的上下文当中,让LLM基于完整的上下文信息来进行生成。

8.2 基础生成

python
def rag_demo(client: MilvusClient, query):
    from langchain_openai import ChatOpenAI

    llm = ChatOpenAI(model="gpt-4o-mini")

    # 检索相关数据
    retrieval_res = hybrid_vector_search_example_rrf(client=client, query=query)

    # 构建上下文
    context = "\n".join([data["entity"]["text"] for data in retrieval_res])
    message_list = [
        {
            "role": "system",
            "content": "你是一个专业的法律问答机器人,请根据上下文回答问题,"
                       "当上下文无法回答问题时,请回答'根据上下文无法回答该问题'"
        },
        {
            "role": "user",
            "content": f"根据以下上下文回答问题:{context}\n问题:{query}"
        }
    ]

    res = llm.invoke(message_list)
    print(res.content)

8.3 LCEL构建RAG链

python
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

rag_prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个有帮助的AI助手。请根据以下上下文回答问题。\n\n上下文:{context}"),
    ("human", "{question}")
])

def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

rag_chain = (
    {
        "context": retriever | format_docs,
        "question": RunnablePassthrough()
    }
    | rag_prompt
    | llm
    | StrOutputParser()
)

result = rag_chain.invoke("什么是RAG?")

8.4 来源引用

python
from langchain_core.runnables import RunnableParallel

def format_docs_with_sources(docs):
    formatted = []
    for i, doc in enumerate(docs):
        source = doc.metadata.get("source", "未知来源")
        formatted.append(f"[来源{i+1}: {source}]\n{doc.page_content}")
    return "\n\n---\n\n".join(formatted)

rag_chain_with_sources = (
    RunnableParallel(
        context=retriever | format_docs_with_sources,
        question=RunnablePassthrough()
    )
    | rag_prompt | llm | StrOutputParser()
)

到这里,我们已经逐步走完了RAG的六个环节。接下来把所有环节串在一起,跑通一个完整的端到端案例。


九、完整实战

9.1 端到端RAG

python
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 1. 加载文档
documents = TextLoader("./documents/sample.txt").load()

# 2. 切分文档
splits = RecursiveCharacterTextSplitter(
    chunk_size=500, chunk_overlap=50,
    separators=["\n\n", "\n", "。", "!", "?", ";", ",", " ", ""]
).split_documents(documents)

# 3. 创建向量存储
vectorstore = Chroma.from_documents(
    documents=splits, embedding=OpenAIEmbeddings(), collection_name="rag_demo"
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

# 4. 创建RAG链
rag_prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个有帮助的AI助手。请根据以下上下文回答问题。\n上下文:\n{context}"),
    ("human", "{question}")
])
llm = ChatOpenAI(model="gpt-4o-mini")

def format_docs(docs):
    return "\n\n---\n\n".join([
        f"[来源: {doc.metadata.get('source', '未知')}]\n{doc.page_content}"
        for doc in docs
    ])

rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | rag_prompt | llm | StrOutputParser()
)

# 5. 查询
response = rag_chain.invoke("什么是LangChain?")
print(response)

跑通了基础版RAG之后,你会发现实际效果不一定令人满意——未来还需要系统地解决这些问题。