用LLM打造专属智能助手:从零构建问答系统
随着大型语言模型(LLM)的普及,越来越多的开发者希望将通用模型转化为服务于特定业务场景的智能助手。一个典型的案例就是构建专属问答系统:用户提出自然语言问题,系统从企业知识库或文档中检索相关信息,再借助LLM生成精准回答。本文将从最基础的环境配置开始,逐步实现一个完整的问答系统,涵盖API封装、知识库索引、RAG流程以及Web部署等关键环节。

环境准备与依赖
在开始之前,确保你的开发环境具备以下条件:
- Python 3.9+(推荐使用虚拟环境)
- OpenAI API密钥(或其他兼容的LLM API,如Claude、本地模型)
- 必要的第三方库:
openai、langchain、chromadb、streamlit、pypdf、tiktoken
执行以下命令安装依赖:
pip install openai langchain chromadb streamlit pypdf tiktoken
基础封装:LLM调用模块
首先,我们创建一个简单的LLM调用函数,支持流式输出和错误处理。在实际开发中,建议将API密钥通过环境变量加载,避免硬编码。
import os
from openai import OpenAI
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def ask_llm(prompt, model="gpt-4o-mini", temperature=0.1):
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
temperature=temperature
)
return response.choices[0].message.content
测试一下:print(ask_llm("什么是RAG?")),如果一切正常,你将看到模型返回的解释。
构建知识库:文档加载与向量化
一个成熟的问答系统不能仅依赖模型的内置知识,还需要摄取特定领域的文档(如公司手册、产品文档、技术博客等)。这里我们以PDF文件为例,演示完整的知识索引流程。
文档加载与分片
使用PyPDFLoader加载PDF,然后通过RecursiveCharacterTextSplitter将文档分割成合理大小的块(chunk)。分片大小通常设为500-1000 token,重叠50-100 token以保证上下文连贯。
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
loader = PyPDFLoader("knowledge_base.pdf")
documents = loader.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(documents)
嵌入与向量存储
选择OpenAI的text-embedding-ada-002模型将文本转成向量,并存入本地向量数据库Chroma。
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
embeddings = OpenAIEmbeddings(model="text-embedding-ada-002")
vectorstore = Chroma.from_documents(documents=chunks, embedding=embeddings, persist_directory="./chroma_db")
vectorstore.persist()
之后每次启动系统只需加载已有的持久化数据库即可,无需重复嵌入。
实现RAG问答流程
RAG(Retrieval Augmented Generation)是目前最主流的LLM应用范式:先检索相关内容,再让LLM基于检索结果生成回答。这样可以有效缓解幻觉问题,并让回答基于私有知识。
检索模块
当用户提问时,先对问题进行向量化,然后从Chroma中检索最相似的top K个文档块。
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
def retrieve_context(query):
docs = retriever.get_relevant_documents(query)
return "nn".join([doc.page_content for doc in docs])
生成模块
将检索到的上下文与用户问题组合成一个精心设计的提示(prompt),然后调用LLM。
def generate_answer(query):
context = retrieve_context(query)
prompt = f"""你是一个专业的AI助手,请基于以下提供的资料回答问题。
如果资料中没有相关信息,请直接说明“知识库中未找到相关内容”。
不要添加无关的假设。
资料:
{context}
问题:{query}"""
return ask_llm(prompt)
测试一下:print(generate_answer("我们的产品支持哪些支付方式?")),如果PDF中包含该信息,你会得到精准的回答。
优化与部署
基础版本已经可以运行,但要投入生产还需要进行多项优化。
提示工程优化
- 增加系统指令(system prompt),约束回答格式和语气。
- 引入少样本示例(few-shot examples),让模型模仿特定风格的输出。
- 加入引用溯源要求,让模型在回答末尾标注信息来源。
性能与成本优化
- 对高频问题使用缓存(例如基于向量相似度缓存相同语义的查询结果)。
- 设置token限制,避免用户输入过长导致成本飙升。
- 考虑使用更便宜的模型(如gpt-4o-mini)处理常规问题,仅在复杂问题上降级到强模型。
部署为Web应用
使用Streamlit可以快速构建一个可交互的UI界面,让非技术用户也能使用。
import streamlit as st
st.title("💬 智能问答助手")
query = st.text_input("请输入你的问题:")
if query:
with st.spinner("思考中..."):
answer = generate_answer(query)
st.markdown(answer)
在终端运行streamlit run app.py,即可在浏览器中体验你的专属问答系统。
进阶方向
以上流程只是LLM开发的基础模板。在实际项目中,你还可以探索以下方向:
- 多轮对话记忆:将历史对话记录存入记忆模块,让助手具备上下文感知能力。
- 混合检索:结合关键词搜索(BM25)与向量搜索,提升召回率。
- 本地模型部署:使用Ollama或vLLM部署开源模型,完全私有化。
- Agent工具调用:让LLM具备调用外部API(如天气查询、数据库查询)的能力。
从零构建一个专属问答系统并不复杂,核心就是数据 + 检索 + LLM三个环节的有效配合。希望本文能为你打开AI应用开发的大门,让你快速拥有属于自己的智能助手。