构建简单Agnet(3) 使用简单RAG

86 阅读14分钟

构建简单RAG

什么是RAG? RAG是一种 混合AI Agent架构 ,它将传统的信息检索系统与现代的生成式AI模型结合起来,以提高AI回答问题的准确性和可靠性。

工作原理

RAG的工作流程主要包括三个步骤:

  1. 检索(Retrieval) :当用户提出问题时,系统首先从知识库或文档集合中检索相关信息
  2. 增强(Augmentation) :将检索到的相关信息与用户的问题一起作为上下文
  3. 生成(Generation) :基于检索到的信息和用户问题,生成AI模型生成回答

主要优势

  • 减少幻觉 :通过基于真实文档生成回答,减少AI编造信息的情况
  • 知识更新 :可以轻松更新知识库,无需重新训练整个模型
  • 可解释性 :可以追溯回答的来源,提高透明度
  • 成本效益 :相比大规模预训练,维护知识库成本更低

上下文

上下文是RAG模型中的核心,它包含用户问题、检索到的文档和生成的答案。

# 导入 PromptTemplate:用于定义单条提示模板
# 导入 FewShotPromptTemplate:用于基于多个示例构建少样本提示模板
from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate

# Define examples for the few-shot prompt
examples = [
    {
            "question": "谁的寿命更长,史蒂夫·乔布斯还是爱因斯坦?",
            "answer": """这个问题是否需要追加问题:是。
    追加问题:史蒂夫·乔布斯享年多少岁?
    中间答案:史蒂夫·乔布斯享年56岁。
    追加问题:爱因斯坦享年多少岁?
    中间答案:爱因斯坦享年76岁。
    最终答案:爱因斯坦
""",
    },
    {
            "question": "Naver的创始人是什么时候出生的?",
            "answer": """这个问题是否需要追加问题:是。
    追加问题:谁是Naver的创始人?
    中间答案:Naver是由李海珍创立的。
    追加问题:李海珍是什么时候出生的?
    中间答案:李海珍出生于1967年6月22日。
    最终答案:1967年6月22日
""",
    },
    {
            "question": "栗谷李珥的母亲出生时,谁是在位的国王?",
            "answer": """这个问题是否需要追加问题:是。
    追加问题:谁是栗谷李珥的母亲?
    中间答案:栗谷李珥的母亲是申师任堂。
    追加问题:申师任堂是什么时候出生的?
    中间答案:申师任堂出生于1504年。
    追加问题:1504年朝鲜的国王是谁?
    中间答案:1504年朝鲜的国王是燕山君。
    最终答案:燕山君
""",
    },
    {
            "question": "《老男孩》和《寄生虫》的导演是来自同一个国家吗?",
            "answer": """这个问题是否需要追加问题:是。
    追加问题:谁是《老男孩》的导演?
    中间答案:《老男孩》的导演是朴赞郁。
    追加问题:朴赞郁来自哪个国家?
    中间答案:朴赞郁来自韩国。
    追加问题:谁是《寄生虫》的导演?
    中间答案:《寄生虫》的导演是奉俊昊。
    追加问题:奉俊昊来自哪个国家?
    中间答案:奉俊昊来自韩国。
    最终答案:是
""",
    },
]

观察

example_prompt = PromptTemplate.from_template(
    "Question:\n{question}\nAnswer:\n{answer}"
)

print(example_prompt.format(**examples[0]))
few_shot_prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
    suffix="Question:\n{question}\nAnswer:",
    input_variables=["question"],
)

# Example question
question = "当谷歌成立时,比尔盖茨多大年纪?"

# Generate the final prompt
final_prompt = few_shot_prompt.format(question=question)
print(final_prompt)

使用嵌入模型加载数据

什么是嵌入模型?

嵌入模型是一种 将文本转换为数值向量 的AI模型。它将人类可读的文本(如句子、段落、文档)转换为 高维空间中的数值表示 ,这些数值向量能够捕捉文本的语义信息。

嵌入模型在RAG系统中扮演关键角色:

  1. 文档索引 :将知识库文档转换为向量并建立索引
  2. 查询处理 :将用户问题转换为向量
  3. 相似度匹配 :通过向量相似度找到最相关的文档片段

工作原理

  1. 文本编码 :将输入文本(如"什么是人工智能?")转换为固定长度的向量
  2. 语义表示 :向量中的每个维度代表文本的某种语义特征
  3. 相似度计算 :通过计算向量之间的距离(如余弦相似度)来判断文本的语义相似性

测试简单RAG

from langchain_openai.embeddings.base import OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_core.prompts.few_shot import FewShotPromptTemplate
from langchain_core.example_selectors import SemanticSimilarityExampleSelector
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
import os

load_dotenv()
# 初始化嵌入模型与向量存储
embeddings = OpenAIEmbeddings(
    model="BAAI/bge-m3",  # 硅基流动支持的模型名称
    api_key=os.getenv("SILICONFLOW_API_KEY"),  # 从环境变量获取API密钥
    base_url="https://api.siliconflow.cn/v1"  # 硅基流动API的基础URL
)
# 创建Chroma向量数据库,用于持久化存储示例选择器所需的向量索引
chroma = Chroma(persist_directory="example_selector", embedding_function=embeddings)

# 构造查询输入并格式化提示词
query = {"question": "马斯克创建PayPal时多大年纪?"}
formatted_prompt = prompt.format(**query)

# 打印最终构建的提示词
print("构建的提示词:\n")
print(formatted_prompt)

# 创建ChatOpenAI对象,配置为使用硅基流动API
Qwen2_5_7B_Instruct_llm = ChatOpenAI(
    temperature=0.1,  # 控制输出的随机性和创造性,值越低输出越稳定可预测,值越高输出越有创意但可能偏离预期 (范围: 0.0 ~ 2.0)
    model_name="Qwen/Qwen2.5-7B-Instruct",  # 硅基流动支持的模型名称
    openai_api_key=os.getenv("SILICONFLOW_API_KEY"),  # 从环境变量获取API密钥
    openai_api_base="https://api.siliconflow.cn/v1"  # 硅基流动API的基础URL
)

# 使用构建好的提示词调用语言模型并获取回答
response = Qwen2_5_7B_Instruct_llm.predict(formatted_prompt)
print("\n回答:\n")
print(response)

解决示例选择器中的相似搜索问题

在计算相似度时,会同时使用指令(instruction)输入(input)。然而,仅基于输入(input)的搜索并不能得到准确的相似度结果。

为了解决这个问题,定义了一个用于相似性计算的自定义类。

下面是一个错误检索结果的示例。
from langchain_core.prompts.chat import ChatPromptTemplate
from langchain_openai.embeddings.base import OpenAIEmbeddings
from langchain_chroma import Chroma

# Initialize OpenAI embeddings
embeddings = OpenAIEmbeddings(
    model="BAAI/bge-m3",  # 硅基流动支持的模型名称
    api_key=os.getenv("SILICONFLOW_API_KEY"),  # 从环境变量获取API密钥
    base_url="https://api.siliconflow.cn/v1"  # 硅基流动API的基础URL
)

# Initialize the Vector DB
chroma = Chroma(persist_directory="fewshot_chat", embedding_function=embeddings)
examples = [
    {
        "instruction": "你是一位会议记录专家。请根据给定信息撰写会议记录",
        "input": "2023年12月25日下午3点,XYZ公司召开了营销策略会议。参会人员包括营销团队负责人金秀珍、数字营销经理朴智敏和社交媒体经理李俊浩。会议的主要目的是制定2024年上半年的营销策略,并讨论新的社交媒体营销方案。团队负责人金秀珍简要介绍了最近的市场趋势,随后各团队成员就各自领域的战略想法进行了汇报。",
        "answer": """
会议记录:XYZ公司营销策略会议
日期:2023年12月25日
地点:XYZ公司会议室
参会人员:金秀珍(营销团队负责人)、朴智敏(数字营销经理)、李俊浩(社交媒体经理)

1. 会议开场
   - 由团队负责人金秀珍致开场词
   - 会议目的:制定2024年上半年营销策略并讨论新的社交媒体营销方案

2. 市场趋势概述(金秀珍)
   - 团队负责人金秀珍分析了近期市场趋势
   - 分享了消费者行为变化和竞争对手策略的见解

3. 数字营销策略(朴智敏)
   - 朴智敏介绍了数字营销策略
   - 重点关注在线广告和SEO优化方法

4. 社交媒体营销方案(李俊浩)
   - 李俊浩提出了新的社交媒体营销方案
   - 说明了网红营销和内容策略计划

5. 综合讨论
   - 团队成员分享并讨论了想法
   - 讨论了各项策略的预算和资源分配

6. 会议结束
   - 确认下次会议日期和时间
   - 指派朴智敏整理并分发会议记录
""",
    },
    {
        "instruction": "你是一位总结专家。请根据给定信息进行内容总结",
        "input": "这是一份关于《可持续城市发展战略》的20页报告。该报告全面阐述了可持续城市发展的重要性、当前城市化问题以及实现可持续城市发展的各种策略。报告还介绍了多个国家成功的可持续城市发展案例,并总结了从这些案例中获得的经验教训。",
        "answer": """
文档总结:可持续城市发展战略报告

- 重要性:强调可持续城市发展的必要性及其社会、经济和环境效益。
- 当前问题:分析当前城市化进程中的主要问题,如环境污染、资源耗竭和不平等加剧等。
- 发展策略:提出实现可持续城市发展的各种策略。包括环保建设、公共交通改善、能源效率提升和加强社区参与等。
- 案例研究:介绍全球城市的成功可持续发展案例。例如通过丹麦哥本哈根和日本横滨等案例说明可实现的策略。
- 经验总结:总结从这些案例中获得的关键经验。强调了多方面方法的重要性、与当地社区合作以及长期规划的必要性。

本报告深入分析了如何以实际和有效的形式实现可持续城市发展。
""",
    },
    {
        "instruction": "你是一位句子修改专家。请修改以下句子",
        "input": "我们公司计划推出新的营销策略。通过这个策略,与客户的沟通将变得更加有效。",
        "answer": "本公司期望通过引入新的营销策略,更有效地改善与客户的沟通。",
    },
]

texts = []
for example in examples:
    texts.append(example["instruction"] + " " + example["input"])

metadatas = examples
chroma.add_texts(texts=texts, metadatas=metadatas)

def select_examples(query):
    # 合并指令和输入文本
    query_text = query["instruction"] + " " + query.get("input", "")
    # 使用Chroma进行相似度搜索
    results = chroma.similarity_search(query_text, k=1)
    print("\n[选择示例输出]")
    print(f"查询文本: {query_text}")
    for i, result in enumerate(results, start=1):
        print(f"结果 {i}:")
        print(f"页面内容: {result.page_content}")
        print(f"元数据: {result.metadata}")
    return results

# Example selector using Chroma
def custom_selector(query):
    # 获取相似示例
    results = select_examples(query)
    # 从结果中提取所需信息
    selected_examples = [
        {
            "instruction": result.metadata["instruction"],
            "input": result.metadata["input"],
            "answer": result.metadata["answer"],
        }
        for result in results
    ]
    print("\n[自定义选择器输出]")
    for i, example in enumerate(selected_examples, start=1):
        print(f"已选择的示例 {i}:")
        print(f"指令: {example['instruction']}")
        print(f"输入: {example['input']}")
        print(f"答案: {example['answer']}")
    return selected_examples

# Example query to test
query = {
    "instruction": "请编写会议记录",
    "input": "2023年12月26日,ABC科技公司产品开发团队就新移动应用项目举行了每周进度会议。参会人员包括项目经理崔铉洙、首席开发工程师黄智妍和UI/UX设计师金泰英。会议的主要目的是审查项目当前进度并制定即将到来的里程碑计划。每位团队成员都汇报了各自负责领域的最新进展,团队还为下周设定了目标。",
}

# Test the functions
custom_selector(query)

输出结果: [选择示例输出] 查询文本: 为以下讨论起草会议记录 2023年12月26日,ABC科技公司的产品开发团队就新移动应用项目举行了每周进度会议。出席会议的有项目经理John Davis、首席开发工程师Emily Chen和UI/UX设计师Michael Brown。团队回顾了当前项目里程碑并确定了下一步计划。每位团队成员都汇报了各自工作流程的最新进展,团队还为下周设定了交付目标。 结果 1: 页面内容: 你是一位会议纪要撰写专家。请根据给定信息撰写会议纪要 2023年12月25日下午3点,XYZ公司召开了营销策略会议。参会人员包括营销团队负责人约翰·史密斯、数字营销经理莎拉·约翰逊和社交媒体经理迈克·威尔逊。会议的主要目的是制定2024年上半年的营销策略,并讨论新的社交媒体营销活动方案。团队负责人约翰·史密斯简要介绍了近期市场趋势,随后各团队成员就各自领域的战略构想进行了汇报。 元数据: {'instruction': '你是一位会议纪要撰写专家。请根据给定信息撰写会议纪要', 'input': '2023年12月25日下午3点,XYZ公司召开了营销策略会议。参会人员包括营销团队负责人约翰·史密斯、数字营销经理莎拉·约翰逊和社交媒体经理迈克·威尔逊。会议的主要目的是制定2024年上半年的营销策略,并讨论新的社交媒体营销活动方案。团队负责人约翰·史密斯简要介绍了近期市场趋势,随后各团队成员就各自领域的战略构想进行了汇报。', 'answer': '\n会议纪要:XYZ公司营销策略会议\n日期:2023年12月25日\n地点:XYZ公司会议室\n参会人员:约翰·史密斯(营销团队负责人)、莎拉·约翰逊(数字营销经理)、迈克·威尔逊(社交媒体经理)\n\n1. 会议开场\n - 由团队负责人约翰·史密斯致开场词\n - 会议目的:制定2024年上半年营销策略并讨论新的社交媒体营销活动方案\n\n2. 市场趋势概述(约翰·史密斯)\n - 团队负责人约翰·史密斯分析了近期市场趋势\n - 分享了消费者行为变化和竞争对手策略的见解\n\n3. 数字营销策略(莎拉·约翰逊)\n - 莎拉·约翰逊介绍了数字营销策略\n - 重点关注在线广告和SEO优化方法\n\n4. 社交媒体营销活动(迈克·威尔逊)\n - 迈克·威尔逊提出了新的社交媒体营销活动方案\n - 说明了网红营销和内容策略计划\n\n5. 综合讨论\n - 团队成员共享并讨论了想法\n - 讨论了各项策略的预算和资源分配\n\n6. 会议结束\n - 确认下次会议日期和时间\n - 指派莎拉·约翰逊整理并分发会议纪要\n'}

[自定义选择器输出] 已选择的示例 1: 指令: 你是一位会议纪要撰写专家。请根据给定信息撰写会议纪要 输入: 2023年12月25日下午3点,XYZ公司召开了营销策略会议。参会人员包括营销团队负责人约翰·史密斯、数字营销经理莎拉·约翰逊和社交媒体经理迈克·威尔逊。会议的主要目的是制定2024年上半年的营销策略,并讨论新的社交媒体营销活动方案。团队负责人约翰·史密斯简要介绍了近期市场趋势,随后各团队成员就各自领域的战略构想进行了汇报。 答案: 会议纪要:XYZ公司营销策略会议 日期:2023年12月25日 地点:XYZ公司会议室 参会人员:约翰·史密斯(营销团队负责人)、莎拉·约翰逊(数字营销经理)、迈克·威尔逊(社交媒体经理)

  1. 会议开场

    • 由团队负责人约翰·史密斯致开场词
    • 会议目的:制定2024年上半年营销策略并讨论新的社交媒体营销活动方案
  2. 市场趋势概述(约翰·史密斯)

    • 团队负责人约翰·史密斯分析了近期市场趋势
    • 分享了消费者行为变化和竞争对手策略的见解 ... 公司预计本财年的收入将增长。新的战略举措已经显示出积极成果。