从零开始搭建RAG系统系列(六):集成LLM⽣成答案 (Generator Integration)

115 阅读4分钟

步骤四:集成LLM⽣成答案 (Generator Integration)

⽬标: 将检索模块获取到的相关上下⽂信息 (retrieved contexts) 与⽤⼾的原始查询整合起来,通过⼀个⼤型语⾔模型(LLM)来⽣成⼀个连贯且准确的答案。

具体操作:

1. 构建Prompt (Prompt Engineering)

Prompt是指导LLM如何⾏动的关键。⼀个好的Prompt模板应该清晰地告诉LLM它的⻆⾊、任务、可⽤的上下⽂信息,以及在特定情况(如信息不⾜)下应该如何回应。下⾯是⼀个针对RAG场景的Prompt模板⽰例:

你是⼀个智能问答助⼿。你的任务是根据下⾯提供的【上下⽂信息】来回答【⽤⼾问题】。

请严格依据【上下⽂信息】进⾏回答,不要依赖任何外部知识或进⾏猜测。

如果【上下⽂信息】中没有⾜够的内容来回答【⽤⼾问题】,请直接回复:“抱歉,根据我⽬前掌握的信息,

确保你的回答简洁、相关,并且直接针对【⽤⼾问题】。

【上下⽂信息】:

---

{context_str}

---

【⽤⼾问题】: {user_query}【你的回答】:

我们可以使⽤LangChain的 ChatPromptTemplate 来创建这个模板:

from langchain_core.prompts import ChatPromptTemplate
prompt_template_str = """你是⼀个智能问答助⼿。你的任务是根据下⾯提供的【上下⽂信息】来
请严格依据【上下⽂信息】进⾏回答,不要依赖任何外部知识或进⾏猜测。
如果【上下⽂信息】中没有⾜够的内容来回答【⽤⼾问题】,请直接回复:“抱歉,根据我⽬前掌握的信息,
确保你的回答简洁、相关,并且直接针对【⽤⼾问题】。
【上下⽂信息】:
---
{context_str}
---
【⽤⼾问题】: {user_query}
【你的回答】:"""
prompt_template = ChatPromptTemplate.from_template(prompt_template_str)
# 模拟:假设已经有了检索到的⽂档列表 relevant_docs 和⽤⼾查询 user_query
# (确保 user_query 和 relevant_docs 在此作⽤域内有效,通常它们来⾃上⼀步)
if relevant_docs and 'user_query' in locals() and user_query:
# 将检索到的多个⽂档块内容合并为⼀个字符串
context_str = "\n\n---\n\n".join([doc.page_content for doc in relevant_d
# 使⽤模板格式化Prompt,准备输⼊给LLM
formatted_prompt_messages = prompt_template.format_messages(
context_str=context_str,
user_query=user_query
)
print("\n--- 为LLM准备的格式化Prompt ---")
for msg in formatted_prompt_messages:
print(f"类型: {msg.type}, 内容:\n{msg.content}")
print("------------------------------")
else:
print("\n相关⽂档列表为空或⽤⼾查询未定义,⽆法格式化Prompt。")
formatted_prompt_messages = None

2. 调⽤LLM模型获取答案

接下来,我们初始化选定的LLM模型,并将格式化后的Prompt传递给它以⽣成答案。我们将展⽰如何使⽤OpenAI API和本地Ollama服务两种⽅式。

from langchain_core.output_parsers import StrOutputParser
import os # ⽤于读取环境变量
# 初始化LLM和输出解析器
llm = None
output_parser = StrOutputParser()
# --- ⽅案1: 使⽤OpenAI API (gpt-3.5-turbo) ---
# 需要设置环境变量 OPENAI_API_KEY
# 同时可能需要设置 OPENAI_API_BASE 如果使⽤代理
use_openai = True # 改为False以使⽤Ollama,或根据条件判断
if use_openai:
from langchain_openai import ChatOpenAI
try:
# openai_api_key = os.getenv("OPENAI_API_KEY")
# openai_api_base = os.getenv("OPENAI_API_BASE") # 例如 "https://api
# if not openai_api_key:
# raise ValueError("请设置 OPENAI_API_KEY 环境变量。")
llm = ChatOpenAI(
# openai_api_key=openai_api_key,
# openai_api_base=openai_api_base,
model_name="gpt-3.5-turbo",
temperature=0.1, # 低temperature使回答更确定、更基于事实
max_tokens=500
)
print("\n已配置使⽤ OpenAI GPT-3.5-Turbo。")
except Exception as e:
print(f"配置OpenAI失败: {e}. 将尝试Ollama。")
use_openai = False # 配置失败则尝试Ollama
# --- ⽅案2: 使⽤本地Ollama服务 (假设已运⾏并拉取了qwen:1.8b模型) ---
if not use_openai: # 如果不使⽤OpenAI或OpenAI配置失败
from langchain_community.llms import Ollama
try:
llm = Ollama(
model="qwen:1.8b", # 确保Ollama服务中有名为qwen:1.8b的模型
# 可以通过 `ollama list` 查看可⽤模型
# 或通过 `ollama pull qwen:1.8b` 拉取
temperature=0.1
)
print("\n已配置使⽤本地Ollama (qwen:1.8b)。请确保Ollama服务正在运⾏。")except Exception as e:
print(f"配置Ollama失败: {e}. LLM未能初始化。")
llm = None # 标记LLM初始化失败
# 调⽤LLM (如果Prompt和LLM都已准备好)
if formatted_prompt_messages and llm:
# 构建链: prompt -> llm -> output_parser
# (这⾥简化,后⾯会⽤LCEL正式构建链)
# messages_for_llm = prompt_template.format_prompt(context_str=context_s
try:
# response = llm.invoke(formatted_prompt_messages) # LCEL⻛格 invoke
# final_answer_from_llm = output_parser.invoke(response)
# # 为了直接展⽰,这⾥⼿动组合⼀个简单的调⽤,完整链在下⼀步
# # 此处仅为演⽰LLM调⽤,⾮最终RAG链
temp_chain = prompt_template | llm | output_parser
final_answer_from_llm = temp_chain.invoke({"context_str": context_st
print("\n--- LLM⽣成的初步回答 ---")
print(final_answer_from_llm)
print("--------------------------")
except Exception as e:
print(f"LLM调⽤失败: {e}")
if "OPENAI_API_KEY" in str(e).upper():
print("提⽰: 如果使⽤OpenAI,请确保正确设置了OPENAI_API_KEY环境变量,并
elif "CONNECTION REFUSED" in str(e).upper() and "OLLAMA" in str(llm
print("提⽰: 如果使⽤Ollama,请确保Ollama服务已在本地启动并运⾏。")
elif not llm:
print("\nLLM模型未能成功初始化,⽆法⽣成答案。")
else:
print("\n格式化Prompt未准备好,⽆法调⽤LLM。")

说明: 上述代码块提供了连接OpenAI和本地Ollama的选项。 temperature=0.1 设置使得LLM的输出更具确定性,这对于基于事实的问答通常是期望的。 StrOutputParser ⽤于从LLM的复杂输出对象中提取纯⽂本回答。请确保你已正确配置所选LLM的环境(如API密钥或本地服务)