一.LLM基础知识
1.1 大语言模型(Large Language Model,LLM)
- 他是模型,是 AI 能力的核心。
- 他是语言模型,其核心能力在于语言能力。
- 他是大语言模型,与传统模型相比,它最大的特点就是“大”。
1.2 AI应用
- AI应用,就是以 LLM 为核心的各种应用
- 从API的角度理解GPT模型,它最核心的参数就是输入一个或多个字符串,然后,大模型输出一个字符串
- 与传统的应用开发不同的是,这个 API 并非是传统应用开发中按照特定预期处理的结果。
- 使用传统的 API,我们需要关注的是接口文档,而想要发挥 LLM 的威力,我们需要对大模型有一定的了解
1.3 大模型原理
- 一次添加一个词
- 下一个词是什么
- token
- 温度(Temperature):表示随机性强弱的概念
这个参数越小,表示确定性越强,越大,表示随机性越强,简单理解就是,温度越高越活跃 - Embedding :在大模型内部处理的是向量,,Embedding 是一种将高维数据(如文本、图像、视频等)转换为低维向量表示的技术。这种技术在自然语言处理(NLP)、计算机视觉等领域有着广泛的应用。Embedding 的核心思想是将离散数据映射到连续的向量空间,使得相似的数据点在向量空间中的距离较近,而不相似的数据点则距离较远。
1.4 promot工程
- 提示词 = 定义角色 + 背景信息 + 任务目标 + 输出要求
- 要求:大模型处理复杂任务场景的能力
- 原因:Agent背后的技术能让大模型推断下一步行为,利用大模型的推理能力,依赖于promot工程
- 起源:Natural Language Processing(NLP):如果给予 AI 适当的引导,它能更准确地理解我们的意图,响应我们的指令
- 零样本提示(Zero-Shot Prompting):适合简单的任务。比如,一些简单查询就可以使用零样本提示。我们需要做的就是调整提示词
- 少样本提示(Few-Shot Prompting):适合复杂的任务。比如,我们需要让 AI 回答一个问题,我们需要提供一些例子,让 AI 学习这些例子,然后再回答问题。
- 思维链提示(Chain-of-Thought Prompting):思维链提示给出的答案则是带有完整的思考过程,是一个“慢下来”的答案,于是得到了一个正确的答案
- ReAct 框架(Reasoning + Acting) :推理 + 行动=大模型为了完成一个大目标,需要不断地做一些任务。每个任务都会经历思考(Thought)、行动(Action)、观察(Observation)三个阶段。
二.LLM-Code
2.1 Open AI API
- Text Generation:生成和处理文本
- Embeddings:文本转向量
- Speech to Text:语音转文本
- Image Generation:生成图像
- Vision:处理图像输入
2.2 SSE
- SSE 是服务器发送事件(Server-Sent Event),它是一种服务器推送技术,客户端通过 HTTP 连接接收来自服务器的自动更新
- 它描述了服务器如何在建立初始客户端连接后向客户端发起数据传输。
- 为啥不用WebSocket
- SSE 的技术特点契合流式应答的需求:客户端与大模型的交互是一次性的,每产生一个 token,服务端就可以给客户端推送一次,当生成内容结束时,断掉连接,无需考虑客户端的存活情况
- 如果采用 WebSocket 的话,服务端就需要维护连接,像 OpenAI 这样的服务体量,维护连接就会造成很大的服务器压力,而且,在生成内容场景下,也没有向服务端进一步发送内容,WebSocket 的双向通信在这里也是多余的
- SSE 这项技术而言,它存在已经很长时间了,2004 年就有人提出,大模型才流行起来
2.3 核心的三个抽象
- ChatModel:整个框架的核心,根据输入的内容生成输出
- PromptTemplate: 负责处理输入,有效拆分开发者提示词和用户提示词
- OutputParser:负责处理输出,许多输出解析器里包含了格式指令
2.4 编码实现
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnableWithMessageHistory
api_key = "你的密钥"
chat_model = ChatOpenAI(model="gpt-4o-mini", api_key=api_key)
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"你现在扮演程序员的角色,可以直接生成代码",
),
MessagesPlaceholder(variable_name="messages"),
]
)
with_message_history = RunnableWithMessageHistory(
prompt | chat_model,
get_session_history
)
config = {"configurable": {"session_id": "chatLLMCode"}}
while True:
user_input = input("You:> ")
if user_input.lower() == 'exit':
break
stream = with_message_history.stream(
{"messages": [HumanMessage(content=user_input)]},
config=config
)
for chunk in stream:
print(chunk.content, end='', flush=True)
print()
三.RAG
3.1 检索增强生成(Retrieval Augmented Generation,RAG)
- 检索增强生成(Retrieval Augmented Generation,RAG)是一种结合了检索和生成的技术,它可以在生成文本时,利用外部的知识库来增强生成的内容。
- 检索增强生成:在本地检索到相关的内容,把它增强到提示词里,然后再去做内容生成
产生背景:
* 让LLM知道自己的行业知识,有两种方式
- 模型微调:使用业务信息对已经训练好的模型进行微调
- RAG:在上下文中带有业务信息,让大模型据此进行整合
3.2 Embeddings和VectorStore
- Embeddings:Embeddings 是一种将高维数据(如文本、图像、视频等)转换为低维向量表示的技术。这种技术在自然语言处理(NLP)、计算机视觉等领域有着广泛的应用。Embeddings 的核心思想是将离散数据映射到连续的向量空间,使得相似的数据点在向量空间中的距离较近,而不相似的数据点则距离较远。
- VectorStore:VectorStore 是一种用于存储和检索向量数据的技术。它可以将高维向量数据存储在一个向量空间中,以便快速查找相似的向量。VectorStore 的核心思想是将高维数据映射到低维向量空间,以便进行高效的相似度搜索。
3.索引(Indexing):索引是一种用于快速查找数据的技术。它可以将数据存储在一个索引中,以便快速查找数据。索引的核心思想是将数据映射到一个索引空间中,以便进行高效的查找。 - 相似度搜索(Similarity Search):相似度搜索是一种用于查找与给定向量最相似的向量的技术。它可以将给定的向量与索引中的向量进行比较,以便找到最相似的向量。相似度搜索的核心思想是将给定的向量映射到索引空间中,以便进行高效的查找。
3.3 索引过程
- 首先,我们需要将文本数据转换为向量。这可以通过使用 Embeddings 技术来实现。
- 然后,我们需要将向量存储在一个向量空间中。这可以通过使用 VectorStore 技术来实现。
- 最后,索引把信息放到向量数据库中,而检索就是把信息提取出来,提取出来的信息与用户提示词合并起来,再到大模型去完成生成
RAG 是为了让大模型知道更多的东西。
3.4 RAG的实现
from operator import itemgetter
from typing import List
import tiktoken
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, ToolMessage, SystemMessage, trim_messages
from langchain_core.chat_history import BaseChatMessageHistory, InMemoryChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnablePassthrough
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import OpenAIEmbeddings
from langchain_openai.chat_models import ChatOpenAI
from langchain_chroma import Chroma
vectorstore = Chroma(
collection_name="rag",
embedding_function=OpenAIEmbeddings(),
persist_directory="vectordb"
)
retriever = vectorstore.as_retriever(search_type="similarity")
def str_token_counter(text: str) -> int:
enc = tiktoken.get_encoding("o200k_base")
return len(enc.encode(text))
def tiktoken_counter(messages: List[BaseMessage]) -> int:
num_tokens = 3
tokens_per_message = 3
tokens_per_name = 1
for msg in messages:
if isinstance(msg, HumanMessage):
role = "user"
elif isinstance(msg, AIMessage):
role = "assistant"
elif isinstance(msg, ToolMessage):
role = "tool"
elif isinstance(msg, SystemMessage):
role = "system"
else:
raise ValueError(f"Unsupported messages type {msg.__class__}")
num_tokens += (
tokens_per_message
+ str_token_counter(role)
+ str_token_counter(msg.content)
)
if msg.name:
num_tokens += tokens_per_name + str_token_counter(msg.name)
return num_tokens
trimmer = trim_messages(
max_tokens=4096,
strategy="last",
token_counter=tiktoken_counter,
include_system=True,
)
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]
model = ChatOpenAI()
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"""You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.
Context: {context}""",
),
MessagesPlaceholder(variable_name="history"),
("human", "{question}"),
]
)
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
context = itemgetter("question") | retriever | format_docs
first_step = RunnablePassthrough.assign(context=context)
chain = first_step | prompt | trimmer | model
with_message_history = RunnableWithMessageHistory(
chain,
get_session_history=get_session_history,
input_messages_key="question",
history_messages_key="history",
)
config = {"configurable": {"session_id": "dreamhead"}}
while True:
user_input = input("You:> ")
if user_input.lower() == 'exit':
break
if user_input.strip() == "":
continue
stream = with_message_history.stream({"question": user_input}, config=config)
for chunk in stream:
print(chunk.content, end='', flush=True)
print()