1 大语言模型LLM

1.1 基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from langchain_ollama import OllamaLLM

# 创建模型
model = OllamaLLM(
model='deepseek-r1:14b',
# 启用流式调用
streaming=True
)

# 流式调用
for chunk in model.stream('你好啊'):
print(chunk, end='', flush=True)

# 直接调用
# result = model.invoke('你好啊')
# print(result)

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 OllamaLLM
from langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
from langchain_core.output_parsers import StrOutputParser

# 创建模型
model = 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, AIMessage
from langchain_ollama import OllamaLLM
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser

# 创建模型
model = 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 OllamaEmbeddings
from langchain_chroma import Chroma


model_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
# demo1 若未创建 向量数据库,即本地无数据库文件
vectorstore = Chroma.from_texts(
texts=["小明是个画家"],
# 指定嵌入模型对象
embedding=embeddings,
# 设置数据库路径
persist_directory='./test_db'
)

# demo2 若本地创建了向量数据库,则可直接通过如下代码进行初始化
vectorstore = Chroma(
embedding=embeddings,
persist_directory='./test_db'
)

2.2.2 添加文本块

1
2
3
4
5
6
# 文本块 列表
data = ['文本知识块....', '文本知识块....', '文本知识块....', ....]
# 将文本块更新到 向量数据库中
vectorstore.add_texts(data)
# 检索器会自动更新,所以不需要重新创建检索器
# retriever = vectorstore.as_retriever()

3 LangChain链构造

在 LCEL 的 a | b | c 语法中,**a, b, c 都必须是实现了 Runnable 接口的对象**,这些对象主要来自:

  1. LangChain 的内置抽象类BasePromptTemplate, BaseLanguageModel, BaseRetriever, BaseOutputParser, BaseTool。因为它们都实现了 Runnable 接口。
  2. 组合器RunnableParallel, RunnableSequence
  3. 适配器RunnableLambda(将普通函数适配为 Runnable),RunnablePassthrough

其他注意事项:

  1. 链的起点:可以是任何数据类型,但通常是一个字典(包含多个输入参数)或一个字符串(单一输入)

  2. 类型转换流程:LCEL 链的本质是一个类型安全的管道,前一个组件的输出类型必须与后一个组件的输入类型兼容,如下代码示例:

    1
    2
    3
    4
    5
    6
    7
    # 输入: Dict -> 提示词模板: Dict -> PromptValue -> 模型: PromptValue -> LLMResult -> 输出解析器: LLMResult -> Str
    chain = (
    {"context": retriever, "question": RunnablePassthrough()} # 输入:Dict[str, Any]
    | prompt_template # 接收 Dict,输出 PromptValue
    | model # 接收 PromptValue,输出 LLMResult/ChatGeneration
    | StrOutputParser() # 接收 LLMResult,输出 Str
    )

故我们可以通过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