赞
踩
开源大模型通常不具备最新语料的问答能力。因此需要外部插件的拓展,目前主流的langChain框架已经集成了网络搜索的能力。但是作为一个倔强的Java程序员,还是想要用Java去实现。
Serpapi 提供了多种搜索引擎的搜索API接口。
访问 Serpapi 官网上注册一个用户:
https://serpapi.com/
可以选择Free Plan,提供每月100次的免费使用。接下来就是使用自己的邮箱和手机号进行注册。
注册成功登录:
public class SerpApiHttp { private int httpConnectionTimeout; private int httpReadTimeout; /** * 后端服务地址 */ private static final String BACK_END = "https://serpapi.com"; /** * 初始化Gson对象 */ private static Gson gson = new Gson(); /** * 当前后端HTTP路径 */ public String path; /*** * 构造函数 * @param path HTTP url路径 */ public SerpApiHttp(String path) { this.path = path; } /*** * 建立Socket连接 * * @param path URL端点 * @param parameter 客户端参数,如: { "q": "coffee", "location": "Austin, TX"} * @return HttpURLConnection 连接对象 * @throws SerpApiException 包装错误信息 */ protected HttpURLConnection connect(String path, Map<String, String> parameter) throws SerpApiException { HttpURLConnection con; try { //allowHTTPS(); // 允许HTTPS支持 String query = ParameterStringBuilder.getParamsString(parameter); URL url = new URL(BACK_END + path + "?" + query); con = (HttpURLConnection) url.openConnection(); con.setRequestMethod("GET"); } catch (IOException e) { throw new SerpApiException(e); } catch (Exception e) { e.printStackTrace(); throw new SerpApiException(e); } String outputFormat = parameter.get("output"); if (outputFormat == null) { throw new SerpApiException("output format must be defined: " + path); } else if (outputFormat.startsWith("json")) { con.setRequestProperty("Content-Type", "application/json"); } con.setConnectTimeout(getHttpConnectionTimeout()); con.setReadTimeout(getHttpReadTimeout()); con.setDoOutput(true); return con; } /*** * 返回HTTP响应内容的原始字符串 * * @param parameter 用户客户端参数 * @return HTTP响应体 * @throws SerpApiException 包装错误信息 */ public String get(Map<String, String> parameter) throws SerpApiException { HttpURLConnection con = connect(this.path, parameter); // 获取HTTP状态码 int statusCode = -1; // 保存响应流 InputStream is = null; // 读取缓冲区 BufferedReader in = null; try { statusCode = con.getResponseCode(); if (statusCode == 200) { is = con.getInputStream(); } else { is = con.getErrorStream(); } Reader reader = new InputStreamReader(is); in = new BufferedReader(reader); } catch (IOException e) { throw new SerpApiException(e); } String inputLine; StringBuilder content = new StringBuilder(); try { while ((inputLine = in.readLine()) != null) { content.append(inputLine); } in.close(); } catch (IOException e) { throw new SerpApiException(e); } // 断开连接 con.disconnect(); if (statusCode != 200) { triggerSerpApiException(content.toString()); } return content.toString(); } /** * 在错误情况下触发异常 * * @param content 从serpapi.com返回的原始JSON响应 * @throws SerpApiException 包装错误信息 */ protected void triggerSerpApiException(String content) throws SerpApiException { String errorMessage; try { JsonObject element = gson.fromJson(content, JsonObject.class); errorMessage = element.get("error").getAsString(); } catch (Exception e) { throw new AssertionError("invalid response format: " + content); } throw new SerpApiException(errorMessage); } /** * @return 当前HTTP连接超时时间 */ public int getHttpConnectionTimeout() { return httpConnectionTimeout; } /** * @param httpConnectionTimeout 设置HTTP连接超时时间 */ public void setHttpConnectionTimeout(int httpConnectionTimeout) { this.httpConnectionTimeout = httpConnectionTimeout; } /** * @return 当前HTTP读取超时时间 */ public int getHttpReadTimeout() { return httpReadTimeout; } /** * @param httpReadTimeout 设置HTTP读取超时时间 */ public void setHttpReadTimeout(int httpReadTimeout) { this.httpReadTimeout = httpReadTimeout; } }
public class SerpApi extends Exception { /** * 客户端参数 */ private final Map<String, String> parameter; /** * 初始化 gson */ private static final Gson gson = new Gson(); /** * Java 7+ 的 https 客户端实现 */ private final SerpApiHttp client; /** * 默认 HTTP 客户端超时时间 */ private static final Integer TIME_OUT = 60000; /** * 搜索路径 */ private static final String SEARCH_PATH = "/search"; /*** * 构造函数 * * @param parameter 默认搜索参数,应包括 {"api_key": "secret_api_key", "engine": "google" } */ public SerpApi(Map<String, String> parameter) { this.parameter = parameter; this.client = new SerpApiHttp(SEARCH_PATH); this.client.setHttpConnectionTimeout(TIME_OUT); } /*** * 返回原始HTML搜索结果 * * @param parameter HTML搜索参数 * @return 从客户端引擎获取的原始HTML响应,用于自定义解析 * @throws SerpApiException 封装后端错误消息 */ public String html(Map<String, String> parameter) throws SerpApiException { return get("/client", "html", parameter); } /*** * 返回JSON格式的搜索结果 * * @param parameter 自定义搜索参数,可覆盖构造函数中提供的默认参数 * @return JSON对象,包含搜索结果的顶层节点 * @throws SerpApiException 封装后端错误消息 */ public JsonObject search(Map<String, String> parameter) throws SerpApiException { return json(SEARCH_PATH, parameter); } /*** * 使用Location API返回位置信息 * * @param parameter 必须包括 {q: "city", limit: 3} * @return JSON数组,使用Location API返回的位置信息 * @throws SerpApiException 封装后端错误消息 */ public JsonArray location(Map<String, String> parameter) throws SerpApiException { String content = get("/locations.json", "json", parameter); JsonElement element = gson.fromJson(content, JsonElement.class); return element.getAsJsonArray(); } /*** * 通过Search Archive API检索搜索结果 * * @param id 搜索的唯一标识符 * @return 客户端结果的JSON对象 * @throws SerpApiException 封装后端错误消息 */ public JsonObject searchArchive(String id) throws SerpApiException { return json("/searches/" + id + ".json", null); } /*** * 使用Account API获取账户信息 * * @param parameter 包含api_key的Map,如果未在默认客户端参数中设置 * @return JSON对象,账户信息 * @throws SerpApiException 封装后端错误消息 */ public JsonObject account(Map<String, String> parameter) throws SerpApiException { return json("/account.json", parameter); } /*** * 使用Account API获取账户信息 * * @return JSON对象,账户信息 * @throws SerpApiException 封装后端错误消息 */ public JsonObject account() throws SerpApiException { return json("/account.json", null); } /*** * 将HTTP内容转换为JsonValue * * @param endpoint 原始JSON HTTP响应 * @return 通过gson解析器创建的JSON对象 */ private JsonObject json(String endpoint, Map<String, String> parameter) throws SerpApiException { String content = get(endpoint, "json", parameter); JsonElement element = gson.fromJson(content, JsonElement.class); return element.getAsJsonObject(); } /*** * 获取HTTP客户端 * * @return 客户端实例 */ public SerpApiHttp getClient() { return this.client; } /*** * 扩展现有参数构建Serp API查询 * * @param path 后端HTTP路径 * @param output 输出类型(json, html, json_with_images) * @param parameter 自定义搜索参数,可覆盖默认参数 * @return 格式化参数HashMap * @throws SerpApiException 封装后端错误消息 */ public String get(String path, String output, Map<String, String> parameter) throws SerpApiException { // 更新客户端路径 this.client.path = path; // 创建HTTP查询 Map<String, String> query = new HashMap(16); if (path.startsWith("/searches")) { // 仅保留API_KEY query.put("api_key", this.parameter.get("api_key")); } else { // 合并默认参数 query.putAll(this.parameter); } // 用自定义参数覆盖默认参数 if (parameter != null) { query.putAll(parameter); } // 设置当前编程语言 query.put("source", "java"); // 设置输出格式 query.put("output", output); return this.client.get(query); } }
public class WebSearchChain { /** * apiKey */ private String apiKey; /** * 构造函数 * @param apiKey */ public WebSearchChain(String apiKey){ this.apiKey = apiKey; } /** * 初始化 * @param apiKey * @return */ public static WebSearchChain fromLlm(String apiKey){ return new WebSearchChain(apiKey); } /** * 搜索 * @param question * @return */ public String search(String question){ Map<String, String> parameter = new HashMap<>(); parameter.put("api_key", apiKey); parameter.put("q", question); parameter.put("hl", "zh-cn"); parameter.put("gl", "cn"); parameter.put("google_domain", "google.com"); parameter.put("safe", "active"); parameter.put("start", "10"); parameter.put("num", "10"); parameter.put("device", "desktop"); SerpApi serpapi = new SerpApi(parameter); JsonObject results = null; StringBuilder stringBuilder = new StringBuilder(); try { results = serpapi.search(parameter); results.getAsJsonArray("organic_results").forEach(organicResult->{ JsonObject result = organicResult.getAsJsonObject(); String title = result.getAsJsonPrimitive("title").getAsString(); String snippet = result.getAsJsonPrimitive("snippet").getAsString(); stringBuilder.append(title).append("。").append(snippet).append("。"); }); } catch (SerpApiException e) { e.printStackTrace(); } return stringBuilder.toString(); } }
博主之前借鉴langChain的思路封装一个Java版的框架,可参考:https://blog.csdn.net/weixin_44455388/article/details/137098743?spm=1001.2014.3001.5501
因此,直接调用即可:
public static void test7() {
String prompt = "吴亦凡犯了什么事";
OpenAIChat openAIChat = OpenAIChat.builder().endpointUrl("http://192.168.0.84:9997/v1").model("Qwen1.5-14B-Chat").build().init();
WebSearchChain webSearchChain = WebSearchChain.fromLlm("48d1bd8f7419xxxxxxxxxxxxxxxxxxxxxxxxxxxx");
String searchResult = webSearchChain.search(prompt);
Flux<String> stringFlux = openAIChat.streamChatWithChain("112233", "你是一个AI助手", searchResult, prompt);
stringFlux.subscribe();
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。