基础RAG实现,最佳入门选择(十一)
RAG中的反馈循环
实现一个带有反馈循环机制的RAG系统,该机制随着时间的推移不断改进。通过收集和整合用户反馈,系统学会了在每次交互中提供更相关和更高质量的响应。传统的RAG系统是静态的——它们仅基于嵌入相似性来检索信息。通过反馈循环,创建了一个动态系统:
-记住什么有效(什么无效)
-随着时间的推移调整文档相关性分数
-将成功的问答配对纳入其知识库
-通过每次用户交互变得更智能
具体代码实现
PDF文本提取
从PDF文件中提取全部文本
def extract_text_from_pdf(pdf_path):
"""
从PDF文件中提取全部文本
:param pdf_path: PDF文件路径
:return: 提取的文本内容(str)
"""
print(f"[步骤] 正在从PDF提取文本: {pdf_path}")
with open(pdf_path, 'rb') as f:
reader = PdfReader(f)
text = ""
for i, page in enumerate(reader.pages):
page_text = page.extract_text()
if page_text:
text += page_text
print(f" - 已提取第{i+1}页")
print(f"[完成] PDF文本提取完成,总长度: {len(text)} 字符\n")
return text
文本分块
文本分割为带重叠的块
def chunk_text(text, n=1000, overlap=200):
"""
将文本分割为带重叠的块
:param text: 原始文本
:param n: 每块字符数
:param overlap: 块间重叠字符数
:return: 文本块列表
"""
print(f"[分块] 每块{n}字符,重叠{overlap}字符")
chunks = []
for i in range(0, len(text), n - overlap):
chunks.append(text[i:i + n])
print(f"[分块] 完成,共{len(chunks)}块\n")
return chunks
向量生成
阿里embedding模型批量生成文本向量
def create_embeddings(texts, model=EMBEDDING_MODEL):
"""
用阿里embedding模型批量生成文本向量
:param texts: 文本列表
:param model: 嵌入模型名
:return: 向量列表
"""
if isinstance(texts, str):
texts = [texts]
print(f"[嵌入生成] 正在生成{len(texts)}条文本的向量...")
try:
response = TextEmbedding.call(
model=model,
input=texts,
api_key=ALI_API_KEY
)
if response.status_code == 200:
embeddings = [np.array(item['embedding']) for item in response.output['embeddings']]
print(f"[嵌入生成] 成功,返回{len(embeddings)}条向量\n")
return embeddings if len(embeddings) > 1 else embeddings[0]
else:
print(f"[嵌入生成] 失败: {response.message}")
return [np.zeros(1536)] * len(texts)
except Exception as e:
print(f"[嵌入生成] 异常: {e}")
return [np.zeros(1536)] * len(texts)
简单向量库
简单的向量存储与检索类,支持相似度检索和元数据管理
class SimpleVectorStore:
"""
简单的向量存储与检索类,支持相似度检索和元数据管理
"""
def __init__(self):
self.vectors = []
self.texts = []
self.metadata = []
def add_item(self, text, embedding, metadata=None):
self.vectors.append(np.array(embedding))
self.texts.append(text)
self.metadata.append(metadata or {})
def similarity_search(self, query_embedding, k=5, filter_func=None):
if not self.vectors:
return []
query_vector = np.array(query_embedding)
similarities = []
for i, vector in enumerate(self.vectors):
if filter_func and not filter_func(self.metadata[i]):
continue
sim = np.dot(query_vector, vector) / (np.linalg.norm(query_vector) * np.linalg.norm(vector))
similarities.append((i, sim))
similarities.sort(key=lambda x: x[1], reverse=True)
results = []
for i in range(min(k, len(similarities))):
idx, score = similarities[i]
results.append({
"text": self.texts[idx],
"metadata": self.metadata[idx],
"similarity": score,
"relevance_score": self.metadata[idx].get("relevance_score", score)
})
return results
反馈数据处理
格式化用户反馈为字典
def get_user_feedback(query, response, relevance, quality, comments=""):
"""
格式化用户反馈为字典
"""
return {
"query": query,
"response": response,
"relevance": int(relevance),
"quality": int(quality),
"comments": comments,
"timestamp": datetime.now().isoformat()
}
存储反馈到JSON文件
def store_feedback(feedback, feedback_file="feedback_data.json"):
"""
存储反馈到JSON文件
"""
with open(feedback_file, "a", encoding="utf-8") as f:
json.dump(feedback, f, ensure_ascii=False)
f.write("\n")
加载历史反馈数据
def load_feedback_data(feedback_file="feedback_data.json"):
"""
加载历史反馈数据
"""
feedback_data = []
try:
with open(feedback_file, "r", encoding="utf-8") as f:
for line in f:
if line.strip():
feedback_data.append(json.loads(line.strip()))
except FileNotFoundError:
print("[提示] 未找到反馈数据文件,初始为空。")
return feedback_data
文档处理主流程
处理PDF文档,提取文本、分块、生成向量并构建向量库
def process_document(pdf_path, chunk_size=1000, chunk_overlap=200):
"""
处理PDF文档,提取文本、分块、生成向量并构建向量库。
"""
print("[主流程] 开始处理文档...")
extracted_text = extract_text_from_pdf(pdf_path)
text_chunks = chunk_text(extracted_text, chunk_size, chunk_overlap)
print("[主流程] 初始化向量库...")
vector_store = SimpleVectorStore()
print("[主流程] 为每个块生成向量...")
chunk_embeddings = create_embeddings(text_chunks)
for i, (chunk, embedding) in enumerate(zip(text_chunks, chunk_embeddings)):
print(f"[块{i+1}/{len(text_chunks)}] 已生成向量,长度: {len(chunk)} 字符")
vector_store.add_item(chunk, embedding, {
"type": "chunk",
"index": i,
"source": pdf_path,
"relevance_score": 1.0,
"feedback_count": 0
})
print("[主流程] 文档处理完毕,向量库构建完成\n")
return text_chunks, vector_store
反馈相关辅助
用LLM判断历史反馈是否与当前query和文档相关
def assess_feedback_relevance(query, doc_text, feedback):
"""
用LLM判断历史反馈是否与当前query和文档相关
"""
system_prompt = "你是相关性判断专家,只需判断历史反馈是否对当前问题和文档有用,只回答yes或no。"
user_prompt = f"""
当前问题: {query}
历史反馈问题: {feedback['query']}
文档内容: {doc_text[:500]}... [截断]
历史反馈回答: {feedback['response'][:500]}... [截断]
该反馈对当前问题和文档是否相关? (yes/no)
"""
try:
response = Generation.call(
model=LLM_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
api_key=ALI_API_KEY,
result_format='message'
)
if response.status_code == 200:
answer = response.output.choices[0].message.content.strip().lower()
return 'yes' in answer
else:
print(f"[反馈相关性] LLM调用失败: {response.message}")
return False
except Exception as e:
print(f"[反馈相关性] LLM调用异常: {e}")
return False
根据历史反馈动态调整检索结果的相关性分数
def adjust_relevance_scores(query, results, feedback_data):
"""
根据历史反馈动态调整检索结果的相关性分数
"""
if not feedback_data:
return results
print("[反馈增强] 正在根据历史反馈调整相关性分数...")
for i, result in enumerate(results):
document_text = result["text"]
relevant_feedback = []
for feedback in feedback_data:
is_relevant = assess_feedback_relevance(query, document_text, feedback)
if is_relevant:
relevant_feedback.append(feedback)
if relevant_feedback:
avg_relevance = sum(f['relevance'] for f in relevant_feedback) / len(relevant_feedback)
modifier = 0.5 + (avg_relevance / 5.0)
original_score = result["similarity"]
adjusted_score = original_score * modifier
result["original_similarity"] = original_score
result["similarity"] = adjusted_score
result["relevance_score"] = adjusted_score
result["feedback_applied"] = True
result["feedback_count"] = len(relevant_feedback)
print(f" 文档{i+1}: 分数由{original_score:.4f}调整为{adjusted_score:.4f},基于{len(relevant_feedback)}条反馈")
results.sort(key=lambda x: x["similarity"], reverse=True)
return results
反馈增强索引
用高质量反馈增强向量库
def fine_tune_index(current_store, chunks, feedback_data):
"""
用高质量反馈增强向量库
"""
print("[增强索引] 正在用高质量反馈增强向量库...")
good_feedback = [f for f in feedback_data if f['relevance'] >= 4 and f['quality'] >= 4]
if not good_feedback:
print("[增强索引] 未找到高质量反馈,跳过增强。")
return current_store
new_store = SimpleVectorStore()
for i in range(len(current_store.texts)):
new_store.add_item(
text=current_store.texts[i],
embedding=current_store.vectors[i],
metadata=current_store.metadata[i].copy()
)
for feedback in good_feedback:
enhanced_text = f"问题: {feedback['query']}\n回答: {feedback['response']}"
embedding = create_embeddings(enhanced_text)
new_store.add_item(
text=enhanced_text,
embedding=embedding,
metadata={
"type": "feedback_enhanced",
"query": feedback["query"],
"relevance_score": 1.2,
"feedback_count": 1,
"original_feedback": feedback
}
)
print(f" 已添加增强内容: {feedback['query'][:30]}...")
print(f"[增强索引] 增强后总条数: {len(new_store.texts)} (原始: {len(chunks)})\n")
return new_store
LLM生成回答
用大模型基于上下文生成回答
def generate_response(query, context, model=LLM_MODEL):
"""
用大模型基于上下文生成回答
"""
print("[流程] 正在调用大模型生成最终回答...")
system_prompt = "你是一个AI助手,只能基于给定上下文回答问题。如果上下文无法直接回答,请回复:'信息不足,无法回答。'"
user_prompt = f"""
上下文:\n{context}\n\n问题:{query}\n\n请只基于上述上下文简明准确作答。"""
try:
response = Generation.call(
model=model,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
api_key=ALI_API_KEY,
result_format='message'
)
if response.status_code == 200:
print("[流程] 回答生成成功\n")
return response.output.choices[0].message.content.strip()
else:
print(f"[流程] 回答生成失败: {response.message}")
return ""
except Exception as e:
print(f"[流程] 回答生成异常: {e}")
return ""
Feedback Loop RAG主流程
完整RAG流程,集成反馈闭环
def rag_with_feedback_loop(query, vector_store, feedback_data, k=5, model=LLM_MODEL):
"""
完整RAG流程,集成反馈闭环
"""
print(f"\n=== [RAG流程] 处理问题: {query} ===")
query_embedding = create_embeddings(query)
results = vector_store.similarity_search(query_embedding, k=k)
adjusted_results = adjust_relevance_scores(query, results, feedback_data)
retrieved_texts = [result["text"] for result in adjusted_results]
context = "\n\n---\n\n".join(retrieved_texts)
print("[流程] 正在生成回答...")
response = generate_response(query, context, model)
print("\n=== [RAG流程] 最终回答 ===")
print(response)
return {
"query": query,
"retrieved_documents": adjusted_results,
"response": response
}
完整工作流
执行完整RAG+反馈闭环流程
def full_rag_workflow(pdf_path, query, feedback_data=None, feedback_file="feedback_data.json", fine_tune=False):
"""
执行完整RAG+反馈闭环流程
"""
if feedback_data is None:
feedback_data = load_feedback_data(feedback_file)
print(f"[反馈] 已加载{len(feedback_data)}条历史反馈\n")
chunks, vector_store = process_document(pdf_path)
if fine_tune and feedback_data:
vector_store = fine_tune_index(vector_store, chunks, feedback_data)
result = rag_with_feedback_loop(query, vector_store, feedback_data)
print("\n=== [反馈收集] 请对本次回答进行评价 ===")
relevance = input("相关性评分(1-5): ")
quality = input("质量评分(1-5): ")
comments = input("其他建议(可选): ")
feedback = get_user_feedback(
query=query,
response=result["response"],
relevance=int(relevance),
quality=int(quality),
comments=comments
)
store_feedback(feedback, feedback_file)
print("[反馈] 已记录,感谢您的参与!\n")
return result
附录
执行效果
========== Feedback Loop RAG主流程演示 ==========
[配置] 使用API密钥: sk-fc6ad...2f23
[配置] PDF路径: data/2888年Java程序员找工作最新场景题.pdf
[配置] 问题: Java程序员面试中常见的技术问题有哪些?
[配置] 反馈增强: True
[反馈] 已加载1条历史反馈
[主流程] 开始处理文档...
[步骤] 正在从PDF提取文本: data/2888年Java程序员找工作最新场景题.pdf
- 已提取第1页
- 已提取第2页
- 已提取第3页
- 已提取第4页
- 已提取第5页
- 已提取第6页
- 已提取第7页
- 已提取第8页
- 已提取第9页
- 已提取第10页
[完成] PDF文本提取完成,总长度: 6984 字符
[分块] 每块1000字符,重叠200字符
[分块] 完成,共9块
[主流程] 初始化向量库...
[主流程] 为每个块生成向量...
[嵌入生成] 正在生成9条文本的向量...
[嵌入生成] 成功,返回9条向量
[块1/9] 已生成向量,长度: 1000 字符
[块2/9] 已生成向量,长度: 1000 字符
[块3/9] 已生成向量,长度: 1000 字符
[块4/9] 已生成向量,长度: 1000 字符
[块5/9] 已生成向量,长度: 1000 字符
[块6/9] 已生成向量,长度: 1000 字符
[块7/9] 已生成向量,长度: 1000 字符
[块8/9] 已生成向量,长度: 1000 字符
[块9/9] 已生成向量,长度: 584 字符
[主流程] 文档处理完毕,向量库构建完成
[增强索引] 正在用高质量反馈增强向量库...
[增强索引] 未找到高质量反馈,跳过增强。
=== [RAG流程] 处理问题: Java程序员面试中常见的技术问题有哪些? ===
[嵌入生成] 正在生成1条文本的向量...
[嵌入生成] 成功,返回1条向量
[反馈增强] 正在根据历史反馈调整相关性分数...
文档1: 分数由0.6383调整为0.5745,基于1条反馈
文档2: 分数由0.5401调整为0.4861,基于1条反馈
文档3: 分数由0.4255调整为0.3829,基于1条反馈
文档4: 分数由0.3958调整为0.3562,基于1条反馈
文档5: 分数由0.3343调整为0.3009,基于1条反馈
[流程] 正在生成回答...
[流程] 正在调用大模型生成最终回答...
[流程] 回答生成成功
=== [RAG流程] 最终回答 ===
根据上下文,Java程序员面试中常见的技术问题包括但不限于:
- 并发编程、NIO、JVM等进阶知识。
- Spring、Netty等流行框架的基本原理。
- 对CAP原则、微服务架构、弹性设计以及Spring Cloud、CloudNative等相关技术框架的理解。
- TCP/IP协议的三次握手、四次挥手过程,Socket编程基础,以及select、poll、epoll等I/O多路复用技术的知识点。
=== [反馈收集] 请对本次回答进行评价 ===
相关性评分(1-5): 2
质量评分(1-5): 2
其他建议(可选): 一定要加上AI的知识
[反馈] 已记录,感谢您的参与!
========== 演示结束 ==========
进程已结束,退出代码为 0
feedback_data.json 历史问答记录展示
{"query": "Java程序员面试中常见的技术问题有哪些?", "response": "根据上下文,Java程序员面试中常见的技术问题涉及以下几个方面:\n\n- 并发编程、NIO、JVM等进阶知识。\n- Spring、Netty等流行框架的基本原理。\n- 分布式架构相关的CAP原则、微服务架构、弹性设计以及Spring Cloud、Cloud Native等相关技术框架。\n- 网络编程技能,包括TCP/IP协议的三次握手、四次挥手过程,Socket编程基础,以及select、poll、epoll等I/O多路复用技术。", "relevance": 2, "quality": 2, "comments": "回答需要结合AI方面的知识", "timestamp": "2025-06-25T17:45:46.310644"}
{"query": "Java程序员面试中常见的技术问题有哪些?", "response": "根据上下文,Java程序员面试中常见的技术问题包括但不限于:\n\n- 并发编程、NIO、JVM等进阶知识。\n- Spring、Netty等流行框架的基本原理。\n- 对CAP原则、微服务架构、弹性设计以及Spring Cloud、CloudNative等相关技术框架的理解。\n- TCP/IP协议的三次握手、四次挥手过程,Socket编程基础,以及select、poll、epoll等I/O多路复用技术的知识点。", "relevance": 2, "quality": 2, "comments": "一定要加上AI的知识", "timestamp": "2025-06-25T17:48:17.234474"}
完整代码示例
# -*- coding: utf-8 -*-
"""
基于阿里大模型的反馈闭环RAG主流程(详细中文注释+详细控制台输出)
"""
import os
import sys
import numpy as np
import json
from PyPDF2 import PdfReader
from dashscope import Generation, TextEmbedding
from datetime import datetime
# ========== 密钥配置:优先从test/api_keys.py读取,否则用环境变量 ==========
# try:
# sys.path.append('test')
# from api_keys import ALI_API_KEY
# except Exception:
# ALI_API_KEY = os.getenv("ALI_API_KEY", "")
# if not ALI_API_KEY:
# print("[错误] 未找到API密钥,请在test/api_keys.py或环境变量中配置ALI_API_KEY!")
# sys.exit(1)
ALI_API_KEY="sk-fc6ad8ecccc5225372f23"
# ==============================================
LLM_MODEL = "qwen-max" # 通义千问主力模型
EMBEDDING_MODEL = "text-embedding-v2" # 阿里云嵌入模型
# ========== PDF文本提取 ==========
def extract_text_from_pdf(pdf_path):
"""
从PDF文件中提取全部文本
:param pdf_path: PDF文件路径
:return: 提取的文本内容(str)
"""
print(f"[步骤] 正在从PDF提取文本: {pdf_path}")
with open(pdf_path, 'rb') as f:
reader = PdfReader(f)
text = ""
for i, page in enumerate(reader.pages):
page_text = page.extract_text()
if page_text:
text += page_text
print(f" - 已提取第{i+1}页")
print(f"[完成] PDF文本提取完成,总长度: {len(text)} 字符\n")
return text
# ========== 文本分块 ==========
def chunk_text(text, n=1000, overlap=200):
"""
将文本分割为带重叠的块
:param text: 原始文本
:param n: 每块字符数
:param overlap: 块间重叠字符数
:return: 文本块列表
"""
print(f"[分块] 每块{n}字符,重叠{overlap}字符")
chunks = []
for i in range(0, len(text), n - overlap):
chunks.append(text[i:i + n])
print(f"[分块] 完成,共{len(chunks)}块\n")
return chunks
# ========== 向量生成 ==========
def create_embeddings(texts, model=EMBEDDING_MODEL):
"""
用阿里embedding模型批量生成文本向量
:param texts: 文本列表
:param model: 嵌入模型名
:return: 向量列表
"""
if isinstance(texts, str):
texts = [texts]
print(f"[嵌入生成] 正在生成{len(texts)}条文本的向量...")
try:
response = TextEmbedding.call(
model=model,
input=texts,
api_key=ALI_API_KEY
)
if response.status_code == 200:
embeddings = [np.array(item['embedding']) for item in response.output['embeddings']]
print(f"[嵌入生成] 成功,返回{len(embeddings)}条向量\n")
return embeddings if len(embeddings) > 1 else embeddings[0]
else:
print(f"[嵌入生成] 失败: {response.message}")
return [np.zeros(1536)] * len(texts)
except Exception as e:
print(f"[嵌入生成] 异常: {e}")
return [np.zeros(1536)] * len(texts)
# ========== 简单向量库 ==========
class SimpleVectorStore:
"""
简单的向量存储与检索类,支持相似度检索和元数据管理
"""
def __init__(self):
self.vectors = []
self.texts = []
self.metadata = []
def add_item(self, text, embedding, metadata=None):
self.vectors.append(np.array(embedding))
self.texts.append(text)
self.metadata.append(metadata or {})
def similarity_search(self, query_embedding, k=5, filter_func=None):
if not self.vectors:
return []
query_vector = np.array(query_embedding)
similarities = []
for i, vector in enumerate(self.vectors):
if filter_func and not filter_func(self.metadata[i]):
continue
sim = np.dot(query_vector, vector) / (np.linalg.norm(query_vector) * np.linalg.norm(vector))
similarities.append((i, sim))
similarities.sort(key=lambda x: x[1], reverse=True)
results = []
for i in range(min(k, len(similarities))):
idx, score = similarities[i]
results.append({
"text": self.texts[idx],
"metadata": self.metadata[idx],
"similarity": score,
"relevance_score": self.metadata[idx].get("relevance_score", score)
})
return results
# ========== 反馈数据处理 ==========
def get_user_feedback(query, response, relevance, quality, comments=""):
"""
格式化用户反馈为字典
"""
return {
"query": query,
"response": response,
"relevance": int(relevance),
"quality": int(quality),
"comments": comments,
"timestamp": datetime.now().isoformat()
}
def store_feedback(feedback, feedback_file="feedback_data.json"):
"""
存储反馈到JSON文件
"""
with open(feedback_file, "a", encoding="utf-8") as f:
json.dump(feedback, f, ensure_ascii=False)
f.write("\n")
def load_feedback_data(feedback_file="feedback_data.json"):
"""
加载历史反馈数据
"""
feedback_data = []
try:
with open(feedback_file, "r", encoding="utf-8") as f:
for line in f:
if line.strip():
feedback_data.append(json.loads(line.strip()))
except FileNotFoundError:
print("[提示] 未找到反馈数据文件,初始为空。")
return feedback_data
# ========== 文档处理主流程 ==========
def process_document(pdf_path, chunk_size=1000, chunk_overlap=200):
"""
处理PDF文档,提取文本、分块、生成向量并构建向量库。
"""
print("[主流程] 开始处理文档...")
extracted_text = extract_text_from_pdf(pdf_path)
text_chunks = chunk_text(extracted_text, chunk_size, chunk_overlap)
print("[主流程] 初始化向量库...")
vector_store = SimpleVectorStore()
print("[主流程] 为每个块生成向量...")
chunk_embeddings = create_embeddings(text_chunks)
for i, (chunk, embedding) in enumerate(zip(text_chunks, chunk_embeddings)):
print(f"[块{i+1}/{len(text_chunks)}] 已生成向量,长度: {len(chunk)} 字符")
vector_store.add_item(chunk, embedding, {
"type": "chunk",
"index": i,
"source": pdf_path,
"relevance_score": 1.0,
"feedback_count": 0
})
print("[主流程] 文档处理完毕,向量库构建完成\n")
return text_chunks, vector_store
# ========== 反馈相关辅助 ==========
def assess_feedback_relevance(query, doc_text, feedback):
"""
用LLM判断历史反馈是否与当前query和文档相关
"""
system_prompt = "你是相关性判断专家,只需判断历史反馈是否对当前问题和文档有用,只回答yes或no。"
user_prompt = f"""
当前问题: {query}
历史反馈问题: {feedback['query']}
文档内容: {doc_text[:500]}... [截断]
历史反馈回答: {feedback['response'][:500]}... [截断]
该反馈对当前问题和文档是否相关? (yes/no)
"""
try:
response = Generation.call(
model=LLM_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
api_key=ALI_API_KEY,
result_format='message'
)
if response.status_code == 200:
answer = response.output.choices[0].message.content.strip().lower()
return 'yes' in answer
else:
print(f"[反馈相关性] LLM调用失败: {response.message}")
return False
except Exception as e:
print(f"[反馈相关性] LLM调用异常: {e}")
return False
def adjust_relevance_scores(query, results, feedback_data):
"""
根据历史反馈动态调整检索结果的相关性分数
"""
if not feedback_data:
return results
print("[反馈增强] 正在根据历史反馈调整相关性分数...")
for i, result in enumerate(results):
document_text = result["text"]
relevant_feedback = []
for feedback in feedback_data:
is_relevant = assess_feedback_relevance(query, document_text, feedback)
if is_relevant:
relevant_feedback.append(feedback)
if relevant_feedback:
avg_relevance = sum(f['relevance'] for f in relevant_feedback) / len(relevant_feedback)
modifier = 0.5 + (avg_relevance / 5.0)
original_score = result["similarity"]
adjusted_score = original_score * modifier
result["original_similarity"] = original_score
result["similarity"] = adjusted_score
result["relevance_score"] = adjusted_score
result["feedback_applied"] = True
result["feedback_count"] = len(relevant_feedback)
print(f" 文档{i+1}: 分数由{original_score:.4f}调整为{adjusted_score:.4f},基于{len(relevant_feedback)}条反馈")
results.sort(key=lambda x: x["similarity"], reverse=True)
return results
# ========== 反馈增强索引 ==========
def fine_tune_index(current_store, chunks, feedback_data):
"""
用高质量反馈增强向量库
"""
print("[增强索引] 正在用高质量反馈增强向量库...")
good_feedback = [f for f in feedback_data if f['relevance'] >= 4 and f['quality'] >= 4]
if not good_feedback:
print("[增强索引] 未找到高质量反馈,跳过增强。")
return current_store
new_store = SimpleVectorStore()
for i in range(len(current_store.texts)):
new_store.add_item(
text=current_store.texts[i],
embedding=current_store.vectors[i],
metadata=current_store.metadata[i].copy()
)
for feedback in good_feedback:
enhanced_text = f"问题: {feedback['query']}\n回答: {feedback['response']}"
embedding = create_embeddings(enhanced_text)
new_store.add_item(
text=enhanced_text,
embedding=embedding,
metadata={
"type": "feedback_enhanced",
"query": feedback["query"],
"relevance_score": 1.2,
"feedback_count": 1,
"original_feedback": feedback
}
)
print(f" 已添加增强内容: {feedback['query'][:30]}...")
print(f"[增强索引] 增强后总条数: {len(new_store.texts)} (原始: {len(chunks)})\n")
return new_store
# ========== LLM生成回答 ==========
def generate_response(query, context, model=LLM_MODEL):
"""
用大模型基于上下文生成回答
"""
print("[流程] 正在调用大模型生成最终回答...")
system_prompt = "你是一个AI助手,只能基于给定上下文回答问题。如果上下文无法直接回答,请回复:'信息不足,无法回答。'"
user_prompt = f"""
上下文:\n{context}\n\n问题:{query}\n\n请只基于上述上下文简明准确作答。"""
try:
response = Generation.call(
model=model,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
api_key=ALI_API_KEY,
result_format='message'
)
if response.status_code == 200:
print("[流程] 回答生成成功\n")
return response.output.choices[0].message.content.strip()
else:
print(f"[流程] 回答生成失败: {response.message}")
return ""
except Exception as e:
print(f"[流程] 回答生成异常: {e}")
return ""
# ========== Feedback Loop RAG主流程 ==========
def rag_with_feedback_loop(query, vector_store, feedback_data, k=5, model=LLM_MODEL):
"""
完整RAG流程,集成反馈闭环
"""
print(f"\n=== [RAG流程] 处理问题: {query} ===")
query_embedding = create_embeddings(query)
results = vector_store.similarity_search(query_embedding, k=k)
adjusted_results = adjust_relevance_scores(query, results, feedback_data)
retrieved_texts = [result["text"] for result in adjusted_results]
context = "\n\n---\n\n".join(retrieved_texts)
print("[流程] 正在生成回答...")
response = generate_response(query, context, model)
print("\n=== [RAG流程] 最终回答 ===")
print(response)
return {
"query": query,
"retrieved_documents": adjusted_results,
"response": response
}
# ========== 完整工作流 ==========
def full_rag_workflow(pdf_path, query, feedback_data=None, feedback_file="feedback_data.json", fine_tune=False):
"""
执行完整RAG+反馈闭环流程
"""
if feedback_data is None:
feedback_data = load_feedback_data(feedback_file)
print(f"[反馈] 已加载{len(feedback_data)}条历史反馈\n")
chunks, vector_store = process_document(pdf_path)
if fine_tune and feedback_data:
vector_store = fine_tune_index(vector_store, chunks, feedback_data)
result = rag_with_feedback_loop(query, vector_store, feedback_data)
print("\n=== [反馈收集] 请对本次回答进行评价 ===")
relevance = input("相关性评分(1-5): ")
quality = input("质量评分(1-5): ")
comments = input("其他建议(可选): ")
feedback = get_user_feedback(
query=query,
response=result["response"],
relevance=int(relevance),
quality=int(quality),
comments=comments
)
store_feedback(feedback, feedback_file)
print("[反馈] 已记录,感谢您的参与!\n")
return result
# ========== main方法演示 ==========
def main():
"""
主方法:体验Feedback Loop RAG
"""
pdf_path = "data/2888年Java程序员找工作最新场景题.pdf" # 可自定义
query = "Java程序员面试中常见的技术问题有哪些?" # 可自定义
feedback_file = "feedback_data.json"
fine_tune = True # 是否用历史反馈增强索引
print("\n========== Feedback Loop RAG主流程演示 ==========")
print(f"[配置] 使用API密钥: {ALI_API_KEY[:8]}...{ALI_API_KEY[-4:]}")
print(f"[配置] PDF路径: {pdf_path}")
print(f"[配置] 问题: {query}")
print(f"[配置] 反馈增强: {fine_tune}\n")
full_rag_workflow(pdf_path, query, feedback_file=feedback_file, fine_tune=fine_tune)
print("========== 演示结束 ==========")
if __name__ == "__main__":
main()

浙公网安备 33010602011771号