AI Agent开发课程笔记记录 - 提升篇 About RAG

文章目录

AI Agent开发课程笔记记录 - 提升篇 About RAG

了解RAG

RAG(Retrieval-Augmented Generation,检索增强生成)是一种解决大模型"知识有限"和"幻觉"问题的技术架构。它的核心思路是:不让模型只依赖自己训练时记住的知识来回答,而是在回答前先去外部知识库(如公司文档、产品手册、数据库)中检索相关的片段,把这些片段作为"参考资料"和问题一起交给模型,让模型基于这些真实资料生成答案。 这样,模型既能回答私域或最新知识(无需重新训练),又能大幅减少胡编乱造,因为答案有据可循。典型的应用场景是:企业把自己的内部文档做成知识库,让AI客服能准确回答产品问题。

NativeRAG

GraphRAG






搭建RAG(Qwen-Agent)






RAFT(Retrieval Augmented Fine-Tuning)

RAFT 是一种专门为了让大模型在「开卷考试(RAG)」中表现更好而设计的微调方法。

它的核心是:

在微调阶段,同时训练模型两种能力:

当给的相关文档有用时 → 学会忽略干扰文档 + 引用原文回答

当没给相关文档时 → 完全依赖自身参数知识回答(避免检索失败就崩掉)

👉 可以理解为:先让模型经历一遍「糟糕的检索结果」并学会应对,再上考场(真实 RAG)自然更稳。

RAFT 不是改变 RAG 的检索或生成架构,而是通过「带干扰文档 + 强制引用」的微调数据,把一个大模型从「盲答选手」训练成「开卷考学霸」------给文档就会用,不给也能答。

完整的RAFT流程包含三个清晰的步骤:

======================================================================

1、Qwen-Agent速度还可以,效果不错,因为是通过Agent模式来进行推理,而且做了一些优化是已经做过一些优化的RAGAgent。

2、GraphRAG对知识全面的理解,通过社区、实体、对知识进行了总结。用户在提问时,可以获得更全面的知识。

3、RAFT(Retrieval Augmented Fine-Tuning),采用在微调中训练RAG的能力,再进行开卷考试的时候,回答的结果会更好。

具体案例

deepseek_faiss_搭建本地知识库检索

Jupyter运行情况:https://github.com/monkeyhlj/LLM_development_learning/blob/main/RAG_demo/deepseek_faiss_搭建本地知识库检索.ipynb

python 复制代码
# 基础文件处理
from PyPDF2 import PdfReader
# 文本分割
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 嵌入模型(阿里云DashScope)
from langchain_community.embeddings import DashScopeEmbeddings
# 向量数据库(FAISS)
from langchain_community.vectorstores import FAISS
# 问答链
from langchain.chains.question_answering import load_qa_chain
# 大模型(通义千问)
from langchain_community.llms import Tongyi
# 成本统计
from langchain_community.callbacks import get_openai_callback

# 配置阿里云灵积API Key(替换为你的有效Key)
DASHSCOPE_API_KEY = "sk-xxx"

# -------------------------- 步骤2:PDF文本提取 --------------------------
# 1. 读取PDF文件
pdf_path = r"C:\Users\houlj12\Desktop\work\test\LLM_development_learning\RAG_demo\docs\浦发上海浦东发展银行西安分行个金客户经理考核办法.pdf"
pdf_reader = PdfReader(pdf_path)
print(f"PDF文件总页数:{len(pdf_reader.pages)}")

# 2. 逐页提取文本,记录每页文本和对应页码
page_texts = []  # 存储每页的完整文本
page_numbers = []  # 存储每页对应的页码

for page_idx, page in enumerate(pdf_reader.pages, start=1):
    # 提取单页文本
    extracted_text = page.extract_text()
    if extracted_text:
        clean_text = extracted_text.strip()  # 清理首尾空格
        page_texts.append(clean_text)
        page_numbers.append(page_idx)
        print(f"第{page_idx}页提取完成,文本长度:{len(clean_text)}字符")
    else:
        print(f"第{page_idx}页无有效文本")

# 打印提取结果预览
print(f"\n共提取到 {len(page_texts)} 页有效文本")
print(f"第1页文本预览:{page_texts[0][:200]}...")


# -------------------------- 步骤3:文本分割 --------------------------
# 1. 初始化中文友好的文本分割器
text_splitter = RecursiveCharacterTextSplitter(
    separators=["\n\n", "\n", "。", "!", "?", ",", "、", "."],  # 优先按中文分句
    chunk_size=1000,        # 每个文本块最大字符数
    chunk_overlap=200,      # 文本块重叠字符(保证上下文连贯)
    length_function=len,    # 按字符长度分割
)

# 2. 分割文本并绑定每个文本块的页码
all_chunks = []               # 存储所有分割后的文本块
chunk_page_mapping = {}       # 关键:文本块 → 对应页码的映射字典

for text, page_num in zip(page_texts, page_numbers):
    # 分割单页文本为多个小块
    chunks = text_splitter.split_text(text)
    for chunk in chunks:
        chunk = chunk.strip()
        if chunk:  # 跳过空文本块
            all_chunks.append(chunk)
            chunk_page_mapping[chunk] = page_num  # 记录每个块的页码

# 打印分割结果
print(f"\n文本分割完成:")
print(f"- 原始有效页数:{len(page_texts)}")
print(f"- 分割后文本块数量:{len(all_chunks)}")
print(f"- 第1个文本块预览:{all_chunks[0][:100]}... 对应页码:{chunk_page_mapping[all_chunks[0]]}")


# -------------------------- 步骤4:Chroma向量库 --------------------------
# 替换FAISS为Chroma(需先安装:pip install chromadb)
from langchain_community.vectorstores import Chroma

# 初始化嵌入模型(保持不变)
embeddings = DashScopeEmbeddings(
    model="text-embedding-v1",
    dashscope_api_key=DASHSCOPE_API_KEY,
)

# 构建Chroma向量库(替代FAISS)
knowledge_base = Chroma.from_texts(
    texts=all_chunks,       # 分割后的文本块
    embedding=embeddings,   # 嵌入模型
    persist_directory="./local_rag_chroma_db"  # 本地保存路径
)

# 绑定页码映射(保持不变)
knowledge_base.chunk_page_mapping = chunk_page_mapping

# 持久化向量库(Chroma需手动执行persist)
knowledge_base.persist()
print(f"\n Chroma向量库已保存至本地:.RAG_case/local_rag_chroma_db_case")


# -------------------------- 步骤5:问答查询 --------------------------
# 1. 初始化通义千问大模型
llm = Tongyi(
    model_name='qwen-turbo',        # 通义千问轻量版(速度快、成本低)
    dashscope_api_key=DASHSCOPE_API_KEY,
    temperature=0.1,               # 降低随机性,保证回答准确性
)

# 2. 设置查询问题(可替换为任意问题)
query = "客户经理被投诉了,投诉一次扣多少分"
print(f"\n 开始查询:{query}")

# 3. 相似度检索(从向量库找最相关的文本块)
top_k = 3  # 返回最相关的3个文本块
related_docs = knowledge_base.similarity_search(query, k=top_k)
print(f"📄 检索到 {len(related_docs)} 个相关文本块")

# 4. 加载问答链(将检索结果拼接后传给LLM生成回答)
qa_chain = load_qa_chain(
    llm=llm,
    chain_type="stuff"  # 适合短文本:将所有相关文本块拼接成prompt
)

# 5. 执行问答并统计API成本
with get_openai_callback() as cost:
    # 执行问答
    response = qa_chain.invoke({
        "input_documents": related_docs,  # 检索到的相关文本
        "question": query                # 用户问题
    })

# 6. 打印结果
print("\n===== 问答结果 =====")
print(f"问题:{query}")
print(f"回答:{response['output_text']}")
print(f"\n API调用成本:{cost}")

# 7. 打印回答来源页码(去重)
print("\n===== 回答来源页码 =====")
unique_pages = set()
for doc in related_docs:
    chunk_content = doc.page_content.strip()
    # 从映射中获取文本块对应的页码
    source_page = knowledge_base.chunk_page_mapping.get(chunk_content, "未知")
    if source_page not in unique_pages:
        unique_pages.add(source_page)
        print(f"- 文本块来源页码:{source_page}")

if not unique_pages:
    print(" 未找到相关文本块的页码信息")

qwen-agent-multi-files

Jupyter运行情况:https://github.com/monkeyhlj/LLM_development_learning/blob/main/RAG_demo/qwen-agent-multi-files.ipynb

python 复制代码
import pprint
import urllib.parse
import json5
from qwen_agent.agents import Assistant
from qwen_agent.tools.base import BaseTool, register_tool
import os

# 步骤 1(可选):添加一个名为 `my_image_gen` 的自定义工具。
@register_tool('my_image_gen')
class MyImageGen(BaseTool):
    # `description` 用于告诉智能体该工具的功能。
    description = 'AI 绘画(图像生成)服务,输入文本描述,返回基于文本信息绘制的图像 URL。'
    # `parameters` 告诉智能体该工具有哪些输入参数。
    parameters = [{
        'name': 'prompt',
        'type': 'string',
        'description': '期望的图像内容的详细描述',
        'required': True
    }]

    def call(self, params: str, **kwargs) -> str:
        # `params` 是由 LLM 智能体生成的参数。
        prompt = json5.loads(params)['prompt']
        prompt = urllib.parse.quote(prompt)
        return json5.dumps(
            {'image_url': f'https://image.pollinations.ai/prompt/{prompt}'},
            ensure_ascii=False)

from dotenv import load_dotenv
load_dotenv()
# 步骤 2:配置您所使用的 LLM。
llm_cfg = {
    # 使用 DashScope 提供的模型服务:
    'model': 'qwen-max',
    'model_server': 'dashscope',
    'api_key': os.getenv('DASHSCOPE_API_KEY'),  # 从环境变量获取API Key
    'generate_cfg': {
        'top_p': 0.8
    }
}

llm_cfg = {
    # 使用 DashScope 提供的模型服务:
    'model': 'deepseek-v3',
    'model_server': 'https://dashscope.aliyuncs.com/compatible-mode/v1',
    'api_key': os.getenv('DASHSCOPE_API_KEY'),  # 从环境变量获取API Key
    'generate_cfg': {
        'top_p': 0.8
    }
}


# 步骤 3:创建一个智能体。这里我们以 `Assistant` 智能体为例,它能够使用工具并读取文件。
system_instruction = '''你是一个乐于助人的AI助手。
在收到用户的请求后,你应该:
- 首先绘制一幅图像,得到图像的url,
- 然后运行代码`request.get`以下载该图像的url,
- 最后从给定的文档中选择一个图像操作进行图像处理。
用 `plt.show()` 展示图像。
你总是用中文回复用户。'''
tools = ['my_image_gen', 'code_interpreter']  # `code_interpreter` 是框架自带的工具,用于执行代码。
import os
# 获取文件夹下所有文件
file_dir = os.path.join('./', 'docs')
files = []
if os.path.exists(file_dir):
    # 遍历目录下的所有文件
    for file in os.listdir(file_dir):
        file_path = os.path.join(file_dir, file)
        if os.path.isfile(file_path):  # 确保是文件而不是目录
            files.append(file_path)
print('files=', files)


bot = Assistant(llm=llm_cfg,
                system_message=system_instruction,
                function_list=tools,
                files=files)

# 步骤 4:作为聊天机器人运行智能体。
messages = []  # 这里储存聊天历史。
query = "介绍下雇主责任险"
# 将用户请求添加到聊天历史。
messages.append({'role': 'user', 'content': query})
response = []
current_index = 0
for response in bot.run(messages=messages):
    if current_index == 0:
        # 尝试获取并打印召回的文档内容
        if hasattr(bot, 'retriever') and bot.retriever:
            print("\n===== 召回的文档内容 =====")
            retrieved_docs = bot.retriever.retrieve(query)
            if retrieved_docs:
                for i, doc in enumerate(retrieved_docs):
                    print(f"\n文档片段 {i+1}:")
                    print(f"内容: {doc.page_content}")
                    print(f"元数据: {doc.metadata}")
            else:
                print("没有召回任何文档内容")
            print("===========================\n")

    current_response = response[0]['content'][current_index:]
    current_index = len(response[0]['content'])
    print(current_response, end='')


# 将机器人的回应添加到聊天历史。
messages.extend(response)
print(messages)

注:文档中带图片时,如何携带图片进行回答:多模态RAG (Multimodal RAG) 技术,后面再看下。

【代码仓库地址】https://github.com/monkeyhlj/LLM_development_learning

【参考】https://github.com/QwenLM/Qwen-Agent

【参考】https://www.bilibili.com/video/BV1k4w4ziEyu

相关推荐
qq_411262421 小时前
四博 AI 智能音箱 4G S3架构方案
人工智能·架构·智能音箱
苦 涩1 小时前
考研408笔记之操作系统(三)——内存管理
笔记·操作系统·考研408
skywalk81631 小时前
基于 Kotti CMS 的 AI 共创社区插件 —— 实现 AI 资源共享、协作交流和项目孵化(先放弃)
人工智能
qq_411262421 小时前
四博AI智能拍学机方案设计
人工智能·智能音箱
格林威1 小时前
面阵相机 vs 线阵相机:堡盟与Basler选型差异全解析 +C# 实战演示
开发语言·人工智能·数码相机·计算机视觉·c#·视觉检测·工业相机
爱上好庆祝2 小时前
学习js的第三天
前端·css·人工智能·学习·计算机外设·js
隔壁大炮2 小时前
10.PyTorch_元素类型转换
人工智能·pytorch·深度学习·算法
杨浦老苏2 小时前
AI使用追踪和代理网关GoModel
人工智能·docker·ai·api·群晖
企业架构师老王2 小时前
药品生产环节:用实在Agent自动生成批记录与打印领料单的合规设计与架构落地
大数据·人工智能·ai·架构