小王盯着屏幕,300 页的《电动汽车电池技术路线报告》已经翻了快半个小时。他只是想找到一句话:“三元锂电池和磷酸铁锂电池在 -20°C 时,各自的放电容量保持率是多少?” 逐页查找像大海捞针。如果有个工具,让他直接对着文档提问,几秒内就能得到带引用的答案,该多好。
这种工具真的可以自己搭,而且用不了几行代码。背后的核心思路叫 RAG——检索增强生成(Retrieval-Augmented Generation)。你可以把它想象成:一个图书馆里有个聪明的研究助理,你问一个问题,它先冲进书库精准抱回几本相关的书(检索),然后根据这些书上的内容,给你一个简明的回答(生成),而不是凭空乱编。长文档问答正是 RAG 的典型战场,而 Kimi K2.5 这类擅长处理超长上下文的模型,则让答案的完整度又上了一个台阶。
RAG 为什么需要长上下文模型?
传统的 RAG 流程大概是这样:把文档切成一个个小段落(chunk),用嵌入模型把每个段落变成向量,存进向量数据库。提问时,先把问题也向量化,从库里找回最相似的几个段落,再把这些段落连同问题一起扔给大语言模型,让它“参考素材作答”。
但问题在于,普通模型处理不了太多素材。假设你一次只能塞进 4K 字(大约 1000 个 token),那顶多能放 3~5 个段落。如果答案需要的证据分散在 8 个不同的地方,你只能硬着头皮挑最重要的几个,剩下的信息就丢了。更糟的是,有些段落之间逻辑是连贯的——比如一个结论要靠前面多次实验数据共同支撑——断章取义式的检索会让模型得出片面甚至错误的判断。
Kimi K2.5 的厉害之处,就是它可以吃下非常长的上下文。这意味着我们能一次性把 8 个、12 个甚至更多的候选段落全扔进去,让模型自己从中找出真正有用的几条,再综合生成答案。检索阶段宁可多找回一些,把精筛的工作交给更强的生成模型。这对合同审阅、学术论文综述、技术文档答疑等需要跨章节理解的任务,提升很明显。
动手搭一个最简系统
硬件门槛比你想的低:一台内存 32GB 以上、带一块 12GB 显存以上 GPU 的电脑(或者使用云端 GPU 实例),就足够跑起 Kimi K2.5 的量化版。我们用 Ollama 做本地模型管理,LangChain 搭工作流,ChromaDB 做向量存储。以下是完整过程,你可以边看边跑。
第一步:安装 Ollama 并拉取模型
根据你的系统从 ollama.com 下载安装 Ollama 0.24.0,然后启动服务并拉取 Kimi K2.5 以及一个免费的嵌入模型。
ollama pull kimi-k2.5
ollama pull nomic-embed-text
# 确认模型都在
ollama list
Ollama 会自动处理模型下载和量化版本的选择,不用操心。
第二步:用 Python 搭建 RAG 流水线
安装必要的库(建议在虚拟环境中操作):
pip install langchain-core==1.4.0 langchain-ollama chromadb==1.5.9 pdfplumber langchain-community
然后写一个脚本 rag_ask.py:
from langchain_ollama import OllamaEmbeddings, ChatOllama
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PDFPlumberLoader
from langchain_community.vectorstores import Chroma
# 1. 加载 PDF 并分块
loader = PDFPlumberLoader("battery_report.pdf")
docs = loader.load()
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # 每块最多1000字
chunk_overlap=200, # 重叠200字,防止句子被拦腰切断
separators=["\n\n", "\n", "。", ",", " ", ""]
)
chunks = text_splitter.split_documents(docs)
# 2. 向量化并存入 ChromaDB
embeddings = OllamaEmbeddings(model="nomic-embed-text", base_url="http://localhost:11434")
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)
# 3. 初始化 Kimi K2.5 模型(低温度,追求精准)
chat = ChatOllama(model="kimi-k2.5", base_url="http://localhost:11434", temperature=0.1)
# 4. 定义问答函数
def ask(question: str) -> str:
retriever = vectorstore.as_retriever(search_kwargs={"k": 8})
relevant = retriever.get_relevant_documents(question)
context = "\n\n---\n\n".join([d.page_content for d in relevant])
prompt = (
"请根据以下资料片段回答问题。如果资料中没有明确答案,就说不知道,不要编造。\n\n"
f"资料:\n{context}\n\n"
f"问题:{question}\n\n"
"答案:"
)
response = chat.invoke(prompt)
return response.content
# 试一下
q = "三元锂电池和磷酸铁锂电池在-20°C时的放电容量保持率分别是多少?"
print(ask(q))
跑起来。如果报显存不足,可以在 Ollama 中尝试更低量化的版本,或者把 k 调小到 5,也能正常工作,只是覆盖度稍降。
长文档问答,到底意味着什么?
对个人用户来说,这意味着你所有躺在硬盘里的 PDF——技术手册、法律合同、课程讲义、论文——都能变成一个“可对话的知识库”。不需要联网,不需要上传到任何平台,数据完全留在本地。隐私敏感的行业(医疗、法务、金融)尤其受益。
对开发者来说,这套方案是把 Kimi K2.5 的长上下文优势嵌入到了传统 RAG 框架里。以前你必须绞尽脑汁设计精巧的分块和路由策略来弥补生成模型的“短视”,现在可以把更多精力放在检索质量上,因为生成端兜底能力更强了。比如你可以在 search_kwargs 里把 k 设到 15,让模型面对一长串可能掺杂了噪音的段落,依然能抓出关键信息。这在旧模型上根本没法做——放不下。
但也要诚实:分块永远是信息折损的一环。如果一份关键数据就落在两个块的交界处,哪怕 overlap 设得再大,也可能被切断。另外,ChromaDB 自带的嵌入模型对中文专业术语的召回有时不够理想,你可以后续换成更对症的嵌入模型(BGE、stella 等),不需改动其他代码。模型的本地推理速度也取决于你的硬件,在 CPU-only 机器上生成一个复杂答案可能要等十几秒,你得接受这个现实。
这个周末,不如拿你手头那本翻了无数遍都没读完的行业报告试试。把它变成能直接回答你问题的东西,或许比重新啃一遍更划算。
