SpringAIAlibaba实现RAG+SearXNG双栈检索智能体
摘要:RAG与SearXNG双栈检索智能体开发指南 本文介绍了如何通过RAG(检索增强生成)与SearXNG结合构建智能检索系统。RAG负责本地知识库检索,SearXNG作为开源搜索引擎聚合器提供联网搜索能力。文章详细说明了SearXNG的Docker部署方法、配置修改要点(如禁用Google引擎、启用JSON格式返回),并提供了Java整合示例。最终实现双栈检索策略:优先查询本地知识库,未命中时
背景
对于 Agent 开发,我们都知道 RAG 可以增强检索,但是本地知识库毕竟知识很有限,只包括我们整理过的内容,对于一些超出范围的问题,比如用户搜索行业动态信息,新闻资讯,股票信息等,本地知识库天然的回答不了,这时候就需要让 Agnet 去联网搜索,主动的去外部世界找答案,而不是直接说不知道,这也刚好填补了本地知识库这一块的空缺。
对于联网搜索,我推荐使用的是 SearXNG ,开源免费,并且集成了市面上几乎所有主流的搜索引擎,还可以自由的配置启用/禁用指定的搜索引擎,返回我们想要的 JSON 格式数据。
基础概念
SearXNG 是一款开源的,可以实现联网搜索的工具,它打通了 baidu,bing,google,360 等几十种搜索引擎,并且会自动的将搜索到的信息合并在一起返回给调用者。
github 地址:https://github.com/searxng/searxng
官方地址:https://docs.searxng.org/admin/installation.html
部署方式
要使用 SearXNG ,就需要将他部署在服务器上,比如使用 docker 来部署
第一步拉镜像
docker pull docker.io/searxng/searxng:latest
拉镜像的时候要注意:如果镜像源地址配置的不合适会超时,阿里的镜像源也不行,需要下面的镜像源,亲测可用。
{
"registry-mirrors": [
"https://docker.1ms.run",
"https://docker.m.daocloud.io",
"https://hub-mirror.c.163.com"
]
}
第二步运行镜像为容器
docker run --name searxng -d \
-p 8888:8080 \
-v "./config/:/etc/searxng/" \
-v "./data/:/var/cache/searxng/" \
docker.io/searxng/searxng:latest
第三步使用服务器的 ip 加 8888 端口就可以直接访问了

注意事项
需要去修改 SearXNG 的 settings.yml 配置文件,禁用掉比如 google 这样的浏览器搜索,因为它很有可能搜索超时。启用 baidu,bing,360 这样的国内搜索引擎。
disabled:true 表示禁用
disabled:false 表示启用
自己根据实际情况进行更改即可,更改完重启 SearXNG 容器才能生效。

Java 整合 SearXNG 实现联网搜索
如果你想让 SearXNG 返回的是 JSON 格式的数据,那么需要去修改 SearXNG 的配置文件 settings.yml 中的formats,默认是返回 html 格式的数据。
修改保存之后需要重启 SearXNG 的容器才会生效
formats:
- html
- json
public static void main(String[] args) {
String response = HttpUtil.get("http://47.109.158.183:8888/search", Map.of(
"q", "你的问题"
, "format", "json"
));
}
RAG ➕ SearXNG 实现双栈检索智能体
RAG 增强检索通过向量库存储企业知识资产,在查询时进行语义检索,优先从知识库获取精准答案。但知识库覆盖范围有限,无法回答所有问题。因此,我们为智能体增加了联网搜索能力,当知识库无法提供答案时,自动切换到全网搜索,获取实时信息,实现与外部世界的实时连接,确保智能体能够回答更广泛的问题。
设计思路:
- 优先使用 RAG 在本地知识库进行检索
- 当 RAG 本地知识库检索不到想要的信息时,再使用 SearXNG 进行联网搜索
- 设计了两个 tool,一个是 RAG 搜索的 tool,一个是 SearXNG 联网搜索的 tool,使用 Prompt 让大模型动态的控制工具的调用。
代码实现
- SearchByRAGTool 知识库搜索
@Slf4j
@Component
public class SearchByRAGTool implements BiFunction<String, ToolContext,String> {
/**
* Redis向量库
*/
private final RedisVectorStore redisVectorStore;
public SearchByRAGTool(RedisVectorStore redisVectorStore){
this.redisVectorStore = redisVectorStore;
}
@Override
public String apply(String s, ToolContext toolContext) {
log.info("开始检索RAG知识库:{}",s);
// 根据用户的question 从RAG知识库中检索相关信息
List<Document> documents = redisVectorStore.similaritySearch(
SearchRequest.builder()
.query(s)
.similarityThreshold(0.75)
.build()
);
String context = "未检索到上下文信息";
StringBuilder stringBuilder = new StringBuilder();
for (Document document : documents) {
stringBuilder.append(document.getText());
}
if (!stringBuilder.isEmpty()){
context = stringBuilder.toString();
}
log.info("RAG知识库检索的内容:{}",context);
return context;
}
public static ToolCallback createToolCallback(RedisVectorStore redisVectorStore){
return FunctionToolCallback.builder("searchByRAGTool",new SearchByRAGTool(redisVectorStore))
.description("根据用户问题,从RAG知识库中检索相关信息")
.inputType(String.class)
.build();
}
}
- SearchBySearXNGTool 联网搜索
@Slf4j
public class SearchBySearXNGTool implements BiFunction<String, ToolContext,String> {
/**
* searXNG请求地址
*/
private final String SEAR_XNG_URL = "http://47.109.158.183:8888/search";
@Override
public String apply(String s, ToolContext toolContext) {
log.info("开始联网搜索");
String response = HttpUtil.get(SEAR_XNG_URL, Map.of(
"q", s
, "format", "json"
));
SearXNGResponse searXNGResponse = JSON.parseObject(response, SearXNGResponse.class);
log.info("联网搜索的结果:{}",searXNGResponse);
String context = "未检索到上下文信息";
StringBuilder stringBuilder = new StringBuilder();
for (SearchResult result : searXNGResponse.getResults()) {
stringBuilder.append(result.toString());
}
if (!stringBuilder.isEmpty()){
context = stringBuilder.toString();
}
return context;
}
public static ToolCallback createToolCallback(){
return FunctionToolCallback.builder("searchBySearXNGTool",new SearchBySearXNGTool())
.description("通过SearXNG实现联网搜索信息")
.inputType(String.class)
.build();
}
}
- 智能体实现
/**
* 测试RAG 和 SearXNG搜索
* 让Agent真正具备与世界对话的能力
*/
@GetMapping("/testRAGAndSearXNGSearch")
public String testRAGAndSearXNGSearch(String question) throws GraphRunnerException {
DashScopeApi dashScopeApi = DashScopeApi.builder()
.apiKey("你的key")
.build();
// 创建大模型
ChatModel chatModel = DashScopeChatModel.builder()
.dashScopeApi(dashScopeApi)
.build();
//初始化提示词,基于提示词去动态的选择tool,准确率很难达到100%
String prompt = """
你是一名智能助手,需要基于“上下文+用户问题”给出准确有条理的回答。
【规则】(必须严格遵守)
1. 你有两个工具可调用
- searchByRAGtool:从本地知识库检索与问题相关的上下文信息。
- searchBySearXNGTool:联网搜索与问题相关的上下文信息。
2. 始终优先调用searchByRAGtool进行信息的检索。
3. 当searchByRAGtool返回"未检索到上下文信息"或者返回空时,再调用searchBySearXNGTool工具进行联网搜索。
4. 严禁在searchByRAGtool工具已经返回有效上下文信息时还去调用searchBySearXNGTool进行联网搜索,这将视为回答失败。
上下文:{context}
问题: {question}
""";
// 通过RAG从本地知识库检索信息的tool
ToolCallback searchByRAGTool = SearchByRAGTool.createToolCallback(redisVectorStore);
// 通过SearXNG联网检索信息的tool
ToolCallback searchBySearXNGTool = SearchBySearXNGTool.createToolCallback();
// 创建ReactAgent
ReactAgent reactAgent =ReactAgent.builder()
.name("智能小助手")
.model(chatModel)
.systemPrompt(prompt)
.tools(List.of(searchByRAGTool,searchBySearXNGTool))
.build();
// 开始执行
AssistantMessage call = reactAgent.call(question);
return call.getText();
}
更多推荐


所有评论(0)