1 大语言模型LLM 1.1 基本用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from langchain_ollama import OllamaLLMmodel = OllamaLLM( model='deepseek-r1:14b' , streaming=True ) for chunk in model.stream('你好啊' ): print (chunk, end='' , flush=True )
1.2 提示词 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from langchain_ollama import OllamaLLMfrom langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplatefrom langchain_core.output_parsers import StrOutputParsermodel = OllamaLLM( model='deepseek-r1:14b' , streaming=True , temperature=0.7 ) prompt_template = ChatPromptTemplate.from_messages([ ("system" , "你是一个专业的{role},请用{style}风格回答用户问题。" ), ("human" , "{question}" ) ]) chain = prompt_template | model | StrOutputParser() for chunk in chain.stream({ "role" : "技术顾问" , "style" : "简洁专业" , "question" : "解释一下深度学习的基本概念" }): print (chunk, end='' , flush=True )
1.2.1 历史对话提示词 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 from langchain_core.messages import HumanMessage, AIMessagefrom langchain_ollama import OllamaLLMfrom langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholderfrom langchain_core.output_parsers import StrOutputParsermodel = OllamaLLM( model='deepseek-r1:14b' , streaming=True , temperature=0.7 ) prompt_template = ChatPromptTemplate.from_messages([ ("system" , "你是一个专业的{role},请用{style}风格,并根据对话历史回答用户问题。" ), MessagesPlaceholder(variable_name='chat_history' ), ("human" , "{question}" ) ]) chain = prompt_template | model | StrOutputParser() chat_history = [ HumanMessage(content="你好,我是小明" ), AIMessage(content="你好小明,很高兴认识你!有什么我可以帮助你的吗?" ) ] for chunk in chain.stream({ "role" : "技术顾问" , "style" : "简洁专业" , 'chat_history' : chat_history, "question" : "解释一下深度学习的基本概念" }): print (chunk, end='' , flush=True )
大语言模型处理输入时,会先将文本转化为 token。在多轮对话中,每次交互的内容都会累积到上下文中,随着对话进行,历史内容会占用更多 token 空间,会挤占当前输入和输出的可用空间,导致模型能够生成的回答内容长度受限,所以大模型所能回答的内容就会越少。
2 嵌入模型Embeddings与向量数据库 2.1 基本用法 LLM 用于“生成” :它的核心任务是理解和生成人类语言 。你给它一段输入(提示词),它给你一段创造性的、连贯的文本输出。
Embeddings 用于“理解”和“比较” :它的核心任务是将文本转换成数学向量(一组数字) 。这些向量代表了文本的“含义”,用于机器计算、比较相似度和搜索。
向量数据库 用于“存储”与“索引” :它的核心任务是将嵌入模型输出的高维向量与原始文本作为一个键值对,保存到数据库中,而用户的问题也会被嵌入模型转化为高维向量,由向量数据库检索相似文本块。
它们通常协同工作,尤其是在构建检索增强生成(RAG)应用,具体流程如下:
由嵌入模型 将非结构化文本编码成结构化、可计算的形式
再由向量数据库 根据所提供的向量根据用户问题 找到最相似的文本块
最终由大语言模型 根据问题+检索到的文本,总结出答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from langchain_ollama.embeddings import OllamaEmbeddingsfrom langchain_chroma import Chromamodel_name='deepseek-r1:14b' embeddings = OllamaEmbeddings(model=model_name) vectorstore = Chroma.from_texts( texts=["小明是个画家" ], embedding=embeddings, persist_directory='./test_db' ) retriever = vectorstore.as_retriever()
2.2 构建本地知识库 我们可以构建本地知识库,让大语言模型在我们所提供的知识库中寻找答案。
2.2.1 向量数据库初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 vectorstore = Chroma.from_texts( texts=["小明是个画家" ], embedding=embeddings, persist_directory='./test_db' ) vectorstore = Chroma( embedding=embeddings, persist_directory='./test_db' )
2.2.2 添加文本块 1 2 3 4 5 6 data = ['文本知识块....' , '文本知识块....' , '文本知识块....' , ....] vectorstore.add_texts(data)
3 LangChain
链构造 在 LCEL 的 a | b | c
语法中,**a
, b
, c
都必须是实现了 Runnable
接口的对象**,这些对象主要来自:
LangChain
的内置抽象类 :BasePromptTemplate
, BaseLanguageModel
, BaseRetriever
, BaseOutputParser
, BaseTool
。因为它们都实现了 Runnable
接口。
组合器 :RunnableParallel
, RunnableSequence
。
适配器 :RunnableLambda
(将普通函数适配为 Runnable
),RunnablePassthrough
。
其他注意事项:
链的起点 :可以是任何数据类型,但通常是一个字典(包含多个输入参数)或一个字符串(单一输入)
类型转换流程 :LCEL 链的本质是一个类型安全的管道 ,前一个组件的输出类型必须与后一个组件的输入类型兼容,如下代码示例:
1 2 3 4 5 6 7 chain = ( {"context" : retriever, "question" : RunnablePassthrough()} | prompt_template | model | StrOutputParser() )
故我们可以通过LCEL链将 大语言模型 、检索器 、提示词 组合到一起:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def prepare_inputs (input_dict ): """准备所有输入参数""" return { "context" : retriever.invoke(input_dict.get("question" )), "question" : input_dict.get("question" ), "chat_history" : input_dict.get("chat_history" ), "role" : input_dict.get("role" , "技术顾问" ), "style" : input_dict.get("style" , "简洁专业" ) } rag_chain = prepare_inputs | prompt_template | model | StrOutputParser() for chunk in rag_chain.stream({ "question" : "小明的职业是什么?" , "chat_history" : chat_history, "role" : "技术顾问" , "style" : "简洁专业" , "context" : retriever }): print (chunk, end='' , flush=True )
检索器检索出来的文本块,也会占用token