大模型RAG与Agent学习笔记

48 阅读11分钟

【一】大模型调用及相关准备

1.使用阿里云百炼的模型

image.png

image.png image.png
2.使用Ollama进行本地模型部署

简单说:在自己电脑上部署和运行大模型,由自己电脑的硬件提供算力支撑模型的运行,所以不需要像阿里云百炼那样担心自己Token不够用,消耗的只是自己的电力。
(可能会有点慢,要根据自己电脑的显卡性能选择合适的参数;不是标准的大模型【蒸馏模型】)

image.png

image.png

3.OpenAI大致使用流程
image.png
4.OpenAI的流式输出

目的:使用户获得更好的使用体验

image.png

image.png

5.OpenAI附带历史消息调用模型
image.png (这种方式太落后了,看看得了)

【二】提示词优化工程

image.png image.png image.png 提示:"ensure\_ascii=False" 避免中文错误\ 代码示例:采用Few-shot提示词优化:
from openai import OpenAI
import json
client = OpenAI(
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"

)
schema = ['日期', '股票名称', '开盘价', '收盘价', '成交量']
examples_data = [       # 示例数据
    {
        "content": "2023-01-10,股市震荡。股票强大科技A股今日开盘价100人民币,一度飙升至105人民币,随后回落至98人民币,最终以102人民币收盘,成交量达到520000。",
        "answers": {
            "日期": "2023-01-10",
            "股票名称": "强大科技A股",
            "开盘价": "100人民币",
            "收盘价": "102人民币",
            "成交量": "520000"
        }
    },
    {
        "content": "2024-05-16,股市利好。股票英伟达美股今日开盘价105美元,一度飙升至109美元,随后回落至100美元,最终以116美元收盘,成交量达到3560000。",
        "answers": {
            "日期": "2024-05-16",
            "股票名称": "英伟达美股",
            "开盘价": "105美元",
            "收盘价": "116美元",
            "成交量": "3560000"
        }
    }
]
questions = [       # 提问问题
    "2025-06-16,股市利好。股票传智教育A股今日开盘价66人民币,一度飙升至70人民币,随后回落至65人民币,最终以68人民币收盘,成交量达到123000。",
    "2025-06-06,股市利好。股票黑马程序员A股今日开盘价200人民币,一度飙升至211人民币,随后回落至201人民币,最终以206人民币收盘。"
]
messages = [
    {"role": "system",      "content": f"你帮我完成信息抽取,我给你句子,你抽取{schema}信息,按JSON字符串输出,如果某些信息不存在,用'原文未提及'表示,请参考如下示例:"}
]
for example in examples_data:
    messages.append(
        {"role": "user", "content": example["content"]}
    )
    messages.append(
        {"role": "assistant", "content": json.dumps(example["answers"], ensure_ascii=False)}
    )
for q in questions:
    response = client.chat.completions.create(
        model="deepseek-v3",
        messages=messages + [{"role": "user", "content": f"按照上述的示例,现在抽取这个句子的信息:{q}"}]
    )
    print(response.choices[0].message.content)

37行-43行是整理提示词数据,只输出整理结果就为:

[
    {"role": "system",      "content": f"你帮我完成信息抽取,我给你句子,你抽取{schema}信息,按JSON字符串输出,如果某些信息不存在,用'原文未提及'表示,请参考如下示例:"},

    {"role": "user",        "content": "2023-01-10,股市震荡。股票强大科技A股今日开盘价100人民币,一度飙升至105人民币,随后回落至98人民币,最终以102人民币收盘,成交量达到520000。"},
    {"role": "assistant",   "content": '{"日期":"2023-01-10","股票名称":"强大科技A股","开盘价":"100人民币","收盘价":"102人民币","成交量":"520000"}'},
    {"role": "user",        "content": "2024-05-16,股市利好。股票英伟达美股今日开盘价105美元,一度飙升至109美元,随后回落至100美元,最终以116美元收盘,成交量达到3560000。"},
    {"role": "assistant",   "content": '{"日期":"2024-05-16","股票名称":"英伟达美股","开盘价":"105美元","收盘价":"116美元","成交量":"3560000"}'},

    {"role": "user",        "content": f"按照上述示例,现在抽取这个句子的信息:{要抽取的句子文本}"}]}
]

【三】RAG开发-LangChain

1.LangChain
image.png image.png
2.RAG介绍

RAG核心工作的的两个流程: image.png

image.png

向量的基础概念:

image.png

扩展:余弦相似度计算:

image.png 代码:

import numpy as np
"""
计算两个向量的余弦相似度(衡量方向相似性,剔除长度影响)
参数:
    vec_a (np.array): 向量A
    vec_b (np.array): 向量B
返回:
    float: 余弦相似度结果(范围[-1,1],越接近1方向越一致)
公式:
    cos_sim = (vec_a · vec_b) / (||vec_a|| × ||vec_b||)
    拆解:
    1. 点积:vec_a · vec_b = vec_a[0]×vec_b[0] + vec_a[1]×vec_b[1] + ... + vec_a[n]×vec_b[n]
    2. 模长:||vec_a|| = √(vec_a[0]² + vec_a[1]² + ... + vec_a[n]²)
    3. 模长:||vec_b|| = √(vec_b[0]² + vec_b[1]² + ... + vec_b[n]²)
A: [0.5, 0.5]
B: [0.7, 0.7]
C: [0.7, 0.5]
D: [-0.6, -0.5]
"""
def get_dot(vec_a, vec_b):
    """计算2个向量的点积,2个向量同维度数字乘积之和"""
    if len(vec_a) != len(vec_b):
        raise ValueError("2个向量必须维度数量相同")
    dot_sum = 0
    for a, b in zip(vec_a, vec_b):
        dot_sum += a * b
    return dot_sum
def get_norm(vec):
    """计算单个向量的模长:对向量的每个数字求平方在求和在开根号"""
    sum_square = 0
    for v in vec:
        sum_square += v * v
    # numpy sqrt函数完成开根号
    return np.sqrt(sum_square)
def cosine_similarity(vec_a, vec_b):
    """余弦相似度:2个向量的点积 除以 2个向量模长的乘积"""
    result = get_dot(vec_a, vec_b) / (get_norm(vec_a) * get_norm(vec_b))
    return result
if __name__ == '__main__':
    vec_a = [0.5, 0.5]
    vec_b = [0.7, 0.7]
    vec_c = [0.7, 0.5]
    vec_d = [-0.6, -0.5]

    print("ab:", cosine_similarity(vec_a, vec_b))
    print("ac:", cosine_similarity(vec_a, vec_c))
    print("ad:", cosine_similarity(vec_a, vec_d))
3.LangChain调用大语言模型
# langchain_community
from langchain_community.llms.tongyi import Tongyi

# 不用qwen3-max,因为qwen3-max是聊天模型,qwen-max是大语言模型
model = Tongyi(model="qwen-max")

# 调用invoke向模型提问
res = model.invoke(input="你是谁呀能做什么?")

print(res)
4.LangChain模型流式输出(用Ollama版)
from langchain_ollama import OllamaLLM

model = OllamaLLM(model="qwen3:4b")

res = model.stream(input="你是谁呀能做什么?")

for chunk in res:
    print(chunk, end="", flush=True)
5.LangChain调用聊天模型

image.png

image.png

6.LangChain消息的简化升级

下面的内容被替换成了元组形式

# 准备消息列表
messages = [
    # (角色,内容)  角色:system/human/ai
    ("system", "你是一个边塞诗人。"),
    ("human", "写一首唐诗。"),
    ("ai", "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦。"),
    ("human", "按照你上一个回复的格式,在写一首唐诗。")
]

前一种调用方式是静态的,这一种方式是动态的
所以此方式有一个好处:支持内部填充{变量}占位

image.png
7.LangChain调用文本嵌入模型(文本向量化)
image.png
8.zero-shot提示词模板
from langchain_core.prompts import PromptTemplate
from langchain_community.llms.tongyi import Tongyi

prompt_template = PromptTemplate.from_template(
    "我的邻居姓{lastname}, 刚生了{gender}, 你帮我起个名字,简单回答。"
)
model = Tongyi(model="qwen-max")

chain = prompt_template | model
res = chain.invoke(input={"lastname": "张", "gender": "女儿"})
print(res)
image.png
9.FewShot提示词模板
image.png 代码示例:
from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate
from langchain_community.llms.tongyi import Tongyi

# 示例的模板
example_template = PromptTemplate.from_template("单词:{word}, 反义词:{antonym}")

# 示例的动态数据注入 要求是list内部套字典
examples_data = [
    {"word": "大", "antonym": "小"},
    {"word": "上", "antonym": "下"},
]

few_shot_template = FewShotPromptTemplate(
    example_prompt=example_template,    # 示例数据的模板
    examples=examples_data,             # 示例的数据(用来注入动态数据的),list内套字典
    prefix="告知我单词的反义词,我提供如下的示例:",                   # 示例之前的提示词
    suffix="基于前面的示例告知我,{input_word}的反义词是?",          # 示例之后的提示词
    input_variables=['input_word']      # 声明在前缀或后缀中所需要注入的变量名
)

prompt_text = few_shot_template.invoke(input={"input_word": "左"}).to_string()
print(prompt_text)

model = Tongyi(model="qwen-max")

print(model.invoke(input=prompt_text))
10.format和invoke区别

image.png 代码示例

template = PromptTemplate.from_template("我的邻居是:{lastname},最喜欢:{hobby}")

res = template.format(lastname="张大明", hobby="钓鱼")
print(res, type(res))


res2 = template.invoke({"lastname": "周杰轮", "hobby": "唱歌"})
print(res2, type(res2))

输出结果:

image.png

11.ChatPromptTemplate的使用
image.png image.png

代码示例: image.png

12.Chain的基础使用

image.png 代码实例:

model = ChatTongyi(model="qwen3-max")

# 组成链,要求每一个组件都是Runnable接口的子类
chain = chat_prompt_template | model

# 通过stream流式输出
for chunk in chain.stream({"history": history_data}):
    print(chunk.content, end="", flush=True)
image.png

image.png

13.StrOutputParse字符串输出解析器

image.png

image.png 第一个model的输出是AIMessage,是没法作为输入给第二个model的。所以得将AIMessage类型转化成基础字符串,具体表现为:加入parser这个组件。\ 代码实例:
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_community.chat_models.tongyi import ChatTongyi

parser = StrOutputParser()
model = ChatTongyi(model="qwen3-max")
prompt = PromptTemplate.from_template(
    "我邻居姓:{lastname},刚生了{gender},请起名,仅告知我名字无需其它内容。"
)

chain = prompt | model | parser | model

res: str = chain.invoke({"lastname": "张", "gender": "女儿"})
print(res)
14.JsonOutputParser和多模型执行链
image.png

image.png

15.自定义函数加入此多模型执行链

image.png

image.png

16.Memory临时会话记忆
image.png
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain_core.prompts import PromptTemplate, ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.chat_history import InMemoryChatMessageHistory
model = ChatTongyi(model="qwen3-max")
prompt = PromptTemplate.from_template(
     "你需要根据会话历史回应用户问题。对话历史:{chat_history},用户提问:{input},请回答"
)
str_parser = StrOutputParser()
base_chain = prompt | model | str_parser
store = {}      # key就是session,value就是InMemoryChatMessageHistory类对象
# 实现通过会话id获取InMemoryChatMessageHistory类对象
def get_history(session_id):
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]
# 创建一个新的链,对原有链增强功能:自动附加历史消息
conversation_chain = RunnableWithMessageHistory(
    base_chain,     # 被增强的原有chain
    get_history,    # 通过会话id获取InMemoryChatMessageHistory类对象
    input_messages_key="input",             # 表示用户输入在模板中的占位符
    history_messages_key="chat_history"     # 表示用户输入在模板中的占位符
)
if __name__ == '__main__':
    # 固定格式,添加LangChain的配置,为当前程序配置所属的session_id
    session_config = {
        "configurable": {
            "session_id": "user_001"
        }
    }
    res = conversation_chain.invoke({"input": "小明有2个猫"}, session_config)
    print("第1次执行:", res)
    #
    res = conversation_chain.invoke({"input": "小刚有1只狗"}, session_config)
    print("第2次执行:", res)

    res = conversation_chain.invoke({"input": "总共有几个宠物"}, session_config)
    print("第3次执行:", res)
17.CSVloader

image.png

image.png
18.JSONLoader
image.png
from langchain_community.document_loaders import JSONLoader

loader = JSONLoader(
    file_path="./data/stu_json_lines.json",
    jq_schema=".name",
    text_content=False,     # 告知JSONLoader 我抽取的内容不是字符串
    json_lines=True         # 告知JSONLoader 这是一个JSONLines文件(每一行都是一个独立的标准JSON)
)

document = loader.load()
print(document)
19.PyPDFLoader

image.png

from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader(
    file_path="./data/pdf2.pdf",
    mode="single",        # 默认是page模式,每个页面形成一个Document文档对象,
                        # single模式,不管有多少页,只返回1个Document对象
    password="itheima"
)

i = 0
for doc in loader.lazy_load():
    i += 1
    print(doc)
    print("="*20, i)
20.TextLoader和文档分割器

一:TextLoader:

image.png 二:文档分割器:

image.png

21.VectorStores向量存储

image.png

image.png

image.png

22.基于向量检索构建提示词
image.png
"""
提示词:用户的提问 + 向量库中检索到的参考资料
"""
from langchain_community.chat_models import ChatTongyi
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser


def print_prompt(prompt):
    print(prompt.to_string())
    print("=" * 20)
    return prompt


model = ChatTongyi(model="qwen3-max")
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "以我提供的已知参考资料为主,简洁和专业的回答用户问题。参考资料:{context}。"),
        ("user", "用户提问:{input}")
    ]
)

vector_store = InMemoryVectorStore(embedding=DashScopeEmbeddings(model="text-embedding-v4"))

# 准备一下资料(向量库的数据)
# add_texts 传入一个 list[str]
vector_store.add_texts(
    ["减肥就是要少吃多练", "在减脂期间吃东西很重要,清淡少油控制卡路里摄入并运动起来", "跑步是很好的运动哦"])

input_text = "怎么减肥?"

# 检索向量库
result = vector_store.similarity_search(input_text, 2)
reference_text = "["
for doc in result:
    reference_text += doc.page_content
reference_text += "]"

chain = prompt | print_prompt | model | StrOutputParser()

res = chain.invoke({"input": input_text, "context": reference_text})
print(res)
23.RunnablePassThrough的使用

为什么要用它:
(一)原因:
保留原始数据(“我要带着旧数据去下一站”)

假设你的链需要同时使用 用户原始输入 和 中间处理结果
如果不加它,数据经过一步处理后,原始输入就丢了。

例子:
你想做一个链:先让用户输入一个问题,然后让模型回答,最后把  “用户的问题”  和  “模型的回答”  一起打印出来。

❌ 错误写法(丢失了原始问题):

# 输入: {"question": "你好"}
# prompt | model  --> 输出变成了字符串 "你好!有什么可以帮你?"
# 此时原始的 {"question": "你好"} 已经丢了!
chain = prompt | model 

✅ 正确写法(使用 RunnablePassthrough 保留上下文):

from langchain_core.runnables import RunnablePassthrough

# 结构:{ "question": 原始问题, "answer": 模型回答 }
chain = (
    RunnablePassthrough.assign(answer=prompt | model)
)
# 输入: {"question": "你好"}
# 输出: {"question": "你好", "answer": "你好!有什么可以帮你?"}

(二)原因:
修复数据结构不匹配(“我要把字典原样传给下一个需要字典的步骤”)

  1. first_prompt | model 输出的是一个 字符串 (名字)。
  2. my_func 把它变成了 字典 {"name": "顾婉儿"}
  3. second_prompt 需要这个字典。

如果你的链中间某一步不需要处理,只是想把数据“搬运”到下一步,或者为了调试想看看中间数据长什么样,就可以插一个 RunnablePassthrough()

更典型的用法:并行处理
假设你要同时做两件事:

  1. 把用户输入翻译成英文。
  2. 把用户输入翻译成法文。
    最后把两个结果合并。
from langchain_core.runnables import RunnablePassthrough

# 输入: "Hello"
chain = (
    RunnablePassthrough() # 1. 原样传递 "Hello"
    | {
        "english": translate_en_prompt | model, # 2. 分支1:翻译英文
        "french": translate_fr_prompt | model,  # 3. 分支2:翻译法文
        # 注意:这里字典的值是其他的 Runnable,LangChain 会自动并行执行它们
        # 而键 "original" 需要原始输入,所以通常配合 assign 使用
      }
)

【四】RAG项目

1.项目总述

image.png 最终效果:

image.png
2.离线流程开发

image.png

image.png image.png
3.在线流程开发

image.png

【五】Agent智能体