Qwen-Agent 构建 RAG:从 “找文档” 到 “解难题” 的三级智能进阶
Qwen-Agent是基于通义千问开发的RAG框架,通过三级智能体架构解决检索增强生成的核心痛点。Lv1基础检索实现"关键词+BM25"精准定位;Lv2分块检索通过语义评估和二次检索提升准确率;Lv3逐步推理能拆解多跳问题并分步解决。评测显示其在百万字文档中"大海捞针"的成功率显著优于传统方案,并通过银行考核案例验证了落地效果。该框架支持从简单问答到复杂推理
目录
Level 1:基础检索(Lv1)—— 像 “按关键词查词典”,快速定位长文档
一句话总结:Lv1 是 “精准定位员”,用关键词快速从长文档里 “揪出” 相关片段,解决 “找得快” 的问题。
Level 2:分块检索(Lv2)—— 像 “先粗筛再精挑”,避免漏检关键内容
具体步骤:以用户查询 “如何提高深度学习模型的训练效率?” 为例:
一句话总结:Lv2 是 “精细筛选员”,比 Lv1 多了 “语义评估” 和 “二次检索”,解决 “找得全、找得准” 的问题。
Level 3:逐步推理(Lv3)—— 像 “拆难题分步解”,搞定多跳问题
具体步骤:以用户查询 “与贝多芬第五交响曲创作于同一世纪的交通工具是什么?” 为例:
一句话总结:Lv3 是 “问题拆解员”,通过 “拆子问题→解子问题→记结果→整合” 的循环,解决 “多跳推理” 的复杂问题。
三、Qwen-Agent RAG 的 “硬实力”:评测结果说话
四、实操案例:用 Qwen-Agent 做 “银行客户经理考核 RAG”
Qwen-Agent 是基于通义千问(Qwen)打造的 RAG 开发框架,核心能力是把 “检索” 和 “推理” 结合,通过三级智能体层层递进,解决从 “短文档快速查” 到 “长文档深理解” 再到 “复杂问题多跳解” 的全场景需求。它就像给 RAG 装上了 “智能导航”—— 不仅能找到相关资料,还能判断资料是否有用,甚至拆解复杂问题分步解决。
一、先搞懂:Qwen-Agent 为什么适合做 RAG?
在讲具体实现前,先明确 Qwen-Agent 的 “先天优势”:
- 深度适配通义千问:能充分利用 Qwen 的指令理解、工具调用能力,比如精准拆分用户查询中的 “需求” 和 “要求”;
- 支持两种模型形态:既能用 DashScope 上的 Qwen 云服务(不用自己部署),也能接入开源 Qwen 模型(如 Qwen-7B-Chat),灵活适配不同场景;
- 三级智能体设计:从简单到复杂覆盖 RAG 全需求,不用从零造轮子 —— 小需求用基础检索,大需求用多跳推理。
二、核心能力:三级智能体,层层递进解决 RAG 痛点
Qwen-Agent 的 RAG 能力分三个 Level,每一级都解决前一级的不足,最终实现 “长文档能 hold 住、复杂题能解开”。
Level 1:基础检索(Lv1)—— 像 “按关键词查词典”,快速定位长文档
痛点:处理 100 万字长文档时,直接喂给 LLM 会超上下文限制,且找不准关键片段(比如用户问 “自行车发明时间”,文档里藏在 “19 世纪交通工具” 章节)。
解决思路:用 “关键词 + BM25” 精准切出相关块,只把有用的部分给 LLM。
具体步骤:
以用户查询 “回答时请用 2000 字详尽阐述,我的问题是,自行车是什么时候发明的?请用英文回复” 为例:
-
拆分查询:分清 “要问什么” 和 “要怎么答”
Qwen 会自动把查询拆成两部分:- 「信息部分」:自行车是什么时候发明的(核心问题,要检索的内容);
- 「指令部分」:2000 字、详尽、英文回复(格式要求,不影响检索)
目的:避免被 “2000 字”“英文” 这些指令干扰检索,只聚焦核心问题。
-
提取多语言关键词:扩大检索范围
针对「信息部分」,Qwen 生成中英文关键词(适配多语言文档):- 中文:自行车、发明、时间;
- 英文:bicycles、invented、when。
目的:比如文档里有 “bicycle was invented in 1817”,英文关键词能精准匹配。
-
BM25 检索:找出最相关的文档块
用 BM25(传统关键词检索算法,比单纯向量检索更准)在拆分好的 “512 字小文档块” 中找匹配 —— 比如从 100 万字文档里,找出包含 “自行车 + 发明” 或 “bicycles+invented” 的 10 个块,再筛选出最相关的 3 个(比如提到 “德莱斯 1817 年发明第一辆自行车” 的块)。
一句话总结:Lv1 是 “精准定位员”,用关键词快速从长文档里 “揪出” 相关片段,解决 “找得快” 的问题。
Level 2:分块检索(Lv2)—— 像 “先粗筛再精挑”,避免漏检关键内容
痛点:Lv1 靠关键词匹配,一旦文档块里没有完全一致的关键词(比如用户查 “深度学习训练效率”,文档块里写的是 “优化深度学习训练速度”),就会漏检,导致 LLM 找不到有用信息。
解决思路:让 Qwen 先评估每个文档块的 “语义相关性”,再用相关内容二次检索,比 Lv1 更 “懂” 语义。
具体步骤:以用户查询 “如何提高深度学习模型的训练效率?” 为例:
-
并行评估所有块的相关性
Qwen 会逐个 “读” 拆分好的 512 字块,判断每个块是否和 “训练效率” 相关:- 块 1:讲 “AdamW 优化器”→ 相关,输出 “AdamW 优化器可提升训练效率”;
- 块 2:讲 “神经网络结构”→ 不相关,输出 “无”;
- 块 3:讲 “混合精度训练减少计算时间”→ 相关,输出 “混合精度训练加快训练速度”;
关键:并行处理所有块,不用等一个评完再评下一个,速度快。
-
用 “相关句子” 二次检索
收集所有 “非无” 的相关句子(比如块 1 和块 3 的输出),把这些句子作为 “新查询词”,再用 BM25 检索一次 —— 这次能找到更多语义相关但关键词不重叠的块(比如之前漏的 “分布式训练提升并行效率” 的块)。 -
生成答案
把二次检索到的块喂给 Qwen,结合用户指令生成答案(比如 “提高训练效率可从优化器、混合精度、分布式训练三方面入手……”)。
一句话总结:Lv2 是 “精细筛选员”,比 Lv1 多了 “语义评估” 和 “二次检索”,解决 “找得全、找得准” 的问题。
Level 3:逐步推理(Lv3)—— 像 “拆难题分步解”,搞定多跳问题
痛点:很多问题需要 “多跳推理”—— 比如用户问 “与第五交响曲创作于同一世纪的交通工具是什么?”,直接检索找不到答案,因为需要先知道 “第五交响曲的创作世纪”,再找 “那个世纪的交通工具”,这是 Lv1 和 Lv2 搞不定的。
解决思路:把复杂问题拆成 “子问题”,用 Lv2 逐个解决,再整合答案,就像人解题时 “先算第一步,再算第二步”。
具体步骤:以用户查询 “与贝多芬第五交响曲创作于同一世纪的交通工具是什么?” 为例:
-
判断能否直接回答
Lv3 智能体先检查自己的 “记忆”:不知道第五交响曲的创作世纪,也不知道对应世纪的交通工具→ 需要拆子问题。 -
生成第一个子问题,用 Lv2 解答
子问题 1:“贝多芬的第五交响曲是在哪个世纪创作的?”
→ 调用 Lv2 检索相关文档块(比如 “第五交响曲创作于 1808 年”),Lv2 返回 “19 世纪”,Lv3 把这个结果存入 “记忆”。 -
生成第二个子问题,用 Lv2 解答
子问题 2:“19 世纪期间发明了哪些交通工具?”
→ 调用 Lv2 检索,返回 “自行车(1817 年)、蒸汽火车(1814 年)”,Lv3 把这个结果也存入 “记忆”。 -
整合记忆,生成最终答案
Lv3 从记忆中提取 “第五交响曲→19 世纪”“19 世纪交通工具→自行车、蒸汽火车”,整合后生成答案:“贝多芬第五交响曲创作于 19 世纪,同一世纪发明的交通工具包括 1817 年的自行车和 1814 年的蒸汽火车。”
一句话总结:Lv3 是 “问题拆解员”,通过 “拆子问题→解子问题→记结果→整合” 的循环,解决 “多跳推理” 的复杂问题。
三、Qwen-Agent RAG 的 “硬实力”:评测结果说话
为了验证 Qwen-Agent 的能力,官方做了两个严苛测试(NeedleBench “大海捞针”、LV-Eval 多证据理解),对比了三种方案:
- 32k - 模型:直接用支持 32k 上下文的模型,不做 RAG;
- 4k-RAG:用 Lv1 的检索策略,只处理 4k 相关上下文;
- 4k - 智能体:用 Lv3 的逐步推理策略。
关键结论:
- 短上下文(比如 1 万字符):32k - 模型和 4k - 智能体差不多,因为内容短,不用复杂检索;
- 长上下文(比如 100 万字):4k - 智能体远超另外两种 ——32k - 模型会 “记不住” 前面的内容,4k-RAG 会漏检,而 4k - 智能体能精准找到 “针”(关键信息);
- 多跳问题:只有 4k - 智能体能搞定,比如 “第五交响曲同世纪交通工具” 这类需要拆步骤的问题;
- 极限测试:100 万字文档 “大海捞针”(找一个关键句子),4k - 智能体能正常找到,且响应时间在可接受范围。
四、实操案例:用 Qwen-Agent 做 “银行客户经理考核 RAG”
Qwen-Agent 落地:
import logging
import io
import os
from qwen_agent.agents import Assistant
# 创建一个自定义的日志处理器来捕获日志输出
class LogCapture:
def __init__(self):
self.log_capture_string = io.StringIO()
self.log_handler = logging.StreamHandler(self.log_capture_string)
self.log_handler.setLevel(logging.INFO)
self.log_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
self.log_handler.setFormatter(self.log_formatter)
# 获取 qwen_agent 的日志记录器
self.logger = logging.getLogger('qwen_agent_logger')
self.logger.setLevel(logging.INFO)
self.logger.addHandler(self.log_handler)
# 也可以捕获根日志记录器的输出
self.root_logger = logging.getLogger()
self.root_logger.setLevel(logging.INFO)
self.root_logger.addHandler(self.log_handler)
def get_log(self):
return self.log_capture_string.getvalue()
def clear_log(self):
self.log_capture_string.truncate(0)
self.log_capture_string.seek(0)
# 初始化日志捕获器
log_capture = LogCapture()
# 步骤 1:配置您所使用的 LLM。
llm_cfg = {
# 使用 DashScope 提供的模型服务:
'model': 'qwen-max',
'model_server': 'dashscope',
'api_key': "***********",
'generate_cfg': {
'top_p': 0.85
}
}
# 步骤 2:创建一个智能体。这里我们以 `Assistant` 智能体为例,它能够使用工具并读取文件。
system_instruction = ''
tools = []
files = ['./客户经理考核办法.pdf'] # 给智能体一个 PDF 文件阅读。
# 清除之前的日志
log_capture.clear_log()
# 创建智能体
bot = Assistant(llm=llm_cfg,
system_message=system_instruction,
function_list=tools,
files=files)
# 步骤 3:作为聊天机器人运行智能体。
messages = [] # 这里储存聊天历史。
query = "客户经理被客户投诉一次,扣多少分?"
# 将用户请求添加到聊天历史。
messages.append({'role': 'user', 'content': query})
response = []
current_index = 0
# 运行智能体
for response in bot.run(messages=messages):
# 在第一次响应时,分析日志以查找召回的文档内容
if current_index == 0:
# 获取日志内容
log_content = log_capture.get_log()
print("\n===== 从日志中提取的检索信息 =====")
# 查找与检索相关的日志行
retrieval_logs = [line for line in log_content.split('\n')
if any(keyword in line.lower() for keyword in
['retriev', 'search', 'chunk', 'document', 'ref', 'token'])]
# 打印检索相关的日志
for log_line in retrieval_logs:
print(log_line)
# 尝试从日志中提取文档内容
# 通常在日志中会有类似 "retrieved document: ..." 或 "content: ..." 的行
content_logs = [line for line in log_content.split('\n')
if any(keyword in line.lower() for keyword in
['content', 'text', 'document', 'chunk'])]
print("\n===== 可能包含文档内容的日志 =====")
for log_line in content_logs:
print(log_line)
print("===========================\n")
current_response = response[0]['content'][current_index:]
current_index = len(response[0]['content'])
print(current_response, end='')
# 将机器人的回应添加到聊天历史。
messages.extend(response)
# 运行结束后,分析完整的日志
print("\n\n===== 运行结束后的完整日志分析 =====")
log_content = log_capture.get_log()
# 尝试从日志中提取更多信息
print("\n1. 关键词提取:")
keyword_logs = [line for line in log_content.split('\n') if 'keywords' in line.lower()]
for log_line in keyword_logs:
print(log_line)
print("\n2. 文档处理:")
doc_logs = [line for line in log_content.split('\n') if 'doc' in line.lower() or 'chunk' in line.lower()]
for log_line in doc_logs:
print(log_line)
print("\n3. 检索相关:")
retrieval_logs = [line for line in log_content.split('\n') if 'retriev' in line.lower() or 'search' in line.lower() or 'ref' in line.lower()]
for log_line in retrieval_logs:
print(log_line)
print("\n4. 可能包含文档内容的日志:")
content_logs = [line for line in log_content.split('\n') if 'content:' in line.lower() or 'text:' in line.lower()]
for log_line in content_logs:
print(log_line)
print("===========================\n")
2025-09-17 18:11:44,026 - memory.py
- 130 - INFO - {"keywords_zh": ["客户经理", "投诉", "扣分"], "keywords_en": ["account manager", "complaint", "deduct points", "penalty"], "text": "客户经理被客户投诉一次,扣多少分?"}
2025-09-17 18:11:45,858 - INFO - Start parsing ./金客户经理考核办法.pdf...
2025-09-17 18:11:46,615 - INFO - Finished parsing ./金客户经理考核办法.pdf.
2025-09-17 18:11:46,618 - INFO - Start chunking ./金客户经理考核办法.pdf
2025-09-17 18:11:46,619 - INFO - Finished chunking ./金客户经理考核办法.pdf
2025-09-17 18:11:46,621 - INFO - all tokens: 2960
2025-09-17 18:11:46,622 - INFO - use full ref
===== 从日志中提取的检索信息 =====
2025-09-17 18:11:46,618 - INFO - Start chunking ./金客户经理考核办法.pdf
2025-09-17 18:11:46,618 - INFO - Start chunking ./金客户经理考核办法.pdf
2025-09-17 18:11:46,619 - INFO - Finished chunking ./金客户经理考核办法.pdf
2025-09-17 18:11:46,619 - INFO - Finished chunking ./金客户经理考核办法.pdf
2025-09-17 18:11:46,621 - INFO - all tokens: 2960
2025-09-17 18:11:46,622 - INFO - use full ref
===== 可能包含文档内容的日志 =====
2025-09-17 18:11:44,026 - INFO - {"keywords_zh": ["客户经理", "投诉", "扣分"], "keywords_en": ["account manager", "complaint", "deduct points", "penalty"], "text": "客户经理被客户投诉一次,扣多少分?"}
2025-09-17 18:11:44,026 - INFO - {"keywords_zh": ["客户经理", "投诉", "扣分"], "keywords_en": ["account manager", "complaint", "deduct points", "penalty"], "text": "客户经理被客户投诉一次,扣多少分?"}
2025-09-17 18:11:46,618 - INFO - Start chunking ./金客户经理考核办法.pdf
2025-09-17 18:11:46,618 - INFO - Start chunking ./金客户经理考核办法.pdf
2025-09-17 18:11:46,619 - INFO - Finished chunking ./金客户经理考核办法.pdf
2025-09-17 18:11:46,619 - INFO - Finished chunking ./金客户经理考核办法.pdf
===========================
客户经理被客户投诉一次,扣2分。
===== 运行结束后的完整日志分析 =====
1. 关键词提取:
2025-09-17 18:11:44,026 - INFO - {"keywords_zh": ["客户经理", "投诉", "扣分"], "keywords_en": ["account manager", "complaint", "deduct points", "penalty"], "text": "客户经理被客户投诉一次,扣多少分?"}
2025-09-17 18:11:44,026 - INFO - {"keywords_zh": ["客户经理", "投诉", "扣分"], "keywords_en": ["account manager", "complaint", "deduct points", "penalty"], "text": "客户经理被客户投诉一次,扣多少分?"}
2. 文档处理:
2025-09-17 18:11:46,618 - INFO - Start chunking ./金客户经理考核办法.pdf
2025-09-17 18:11:46,618 - INFO - Start chunking ./金客户经理考核办法.pdf
2025-09-17 18:11:46,619 - INFO - Finished chunking ./金客户经理考核办法.pdf
2025-09-17 18:11:46,619 - INFO - Finished chunking ./金客户经理考核办法.pdf
3. 检索相关:
2025-09-17 18:11:46,622 - INFO - use full ref
2025-09-17 18:11:46,622 - INFO - use full ref
4. 可能包含文档内容的日志:
===========================
五、总结:Qwen-Agent 构建 RAG 的核心价值
Qwen-Agent 的三级智能体,本质是 “从简单到复杂” 的 RAG 能力进化:
- Lv1:解决 “长文档找得快”,适合简单问答;
- Lv2:解决 “找得全、找得准”,适合需要语义理解的问答;
- Lv3:解决 “多跳推理”,适合复杂问题。
无论是快速做一个 “文档问答机器人”,还是落地 “企业知识库多跳查询”,Qwen-Agent 都能通过灵活选择智能体级别,平衡 “效率” 和 “效果”—— 不用为简单需求搭复杂架构,也不用为复杂需求凑合用基础检索。
更多推荐
所有评论(0)