做这个之前需要先安装一下LangSmith,方便查看提示词和检索的知识库document,因为这里我们需要用到rag的优化策略
LangSmith的功能

  • 看每一步 prompt / chain 在干嘛
  • 记录调用日志(trace)
  • 找 bug(为什么模型答错)
  • 做评测(哪个 prompt 更好)安装步骤

1.安装

1
pip install langsmith

2.注册 + 拿 API Key

去官网:
https://smith.langchain.com

拿到 API key 后,设置环境变量:写在.env就好

1
2
3
4
5
# langsmith 配置
LANGSMITH_TRACING=true
LANGSMITH_ENDPOINT=https://api.smith.langchain.com
LANGSMITH_API_KEY=lsv2...............................................7c
LANGSMITH_PROJECT="nangua"

这样就好了,记得重启环境和load_dotenv()
image.png


这边说说什么是 ,RAG融合吧(rag_fusion) ,核心的话就是两点

  • 把单一问题变成多角度的(用LLM在检索前改写)
  • 对得到的chunk进行打分重新排序ps:注意哦,这个在你切分得到chunks,准备初始化Chroma数据库的时候,先给chunks的meta加一个id属性,方便后面辨认chunk是不是同一个
1
2
3
4
5
6
7
#......在这一步
# 对文档进行切割
chunks=text_splitter.split_documents(doc)

#给chunks打上id
for i, doc in enumerate(chunks):
    doc.metadata["id"] = f"doc_{i}"

具体而言分以下步骤

1 写一个专门改写生成多个问题的链条

1.1 定义一个prompt

1
2
3
4
5
6
7
8
#定义多问题的prompt

prompt_perspectives=ChatPromptTemplate.from_template(
    """You are an AI language model assistant.
Your task is to generate five different versions of the given user question
to retrieve relevant documents from a vector database.
Provide these alternative questions separated by newlines.
Original question: {question}"""

1.2 定义一个链条

1
2
3
4
5
6
7
8
generate_queries = (

    prompt_perspectives
    | ChatOpenAI(temperature=0)
    | StrOutputParser()
    | (lambda x: x.split("\n"))

)

肉眼可以直接看出吧?这里得到的肯定是一个list[document]老朋友了。然后我们要用这五个问题去检索。生成结果类似这种。
image.png

2 对检索的Chunk排序打分

有五个问题嘛,假设我们一个问题取3个相似最高的chunk嘛,那不就是一个二维数组,
5×3吗~注意哦他 检索给我们返回的顺序也是很重要的,越靠前的语义相似度最高 。那我们现在要做的就是对这个二维列表里的chunk再排序,这次我们排序的依据是 重复次数 ,

这个是打分的函数
其中;

  • rank:文档在当前列表中的位置(0-based)
  • k:平滑常数(代码中k=60),防止分母为0,调节排名影响
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
def rec(result: list[list], k=60):

    # 咱们用字典来存,Key是id,Value是一个小列表 [doc, score]
    # 这样既能去重,又能随时修改分数
    fused_scores = {}

    for docs in result:
        for rank, doc in enumerate(docs):
            # 拿到文档的身份证号
            doc_id = doc.metadata.get('id')
            # 如果是第一次见到这个文档,就先给它“建个档”
            # 多次见到的就是重复的,给她加分
            if doc_id not in fused_scores:
                # 初始分数设为 0
                fused_scores[doc_id] = [doc, 0]
            # 根据 RRF 公式累加分数
            # 这里的 rank 是从 0 开始的,逻辑完全正确!
            fused_scores[doc_id][1] += 1 / (rank + k)
    # 重排序:把字典里的 [doc, score] 拿出来排排坐

    reranked_result = sorted(
        fused_scores.values(), # 只要值的部分
        key=lambda x: x[1],    # 按分数(索引1)排
        reverse=True           # 分高的排前面
    )

    return reranked_result