Spring?AI?+?混元帶你實現(xiàn)企業(yè)級穩(wěn)定可部署的AI業(yè)務(wù)智能體
前言
在之前的內(nèi)容中,我們詳細(xì)講解了Spring AI的基礎(chǔ)用法及其底層原理。如果還有小伙伴對此感到困惑,歡迎參考下面這篇文章,深入學(xué)習(xí)并進一步掌握相關(guān)知識:http://www.dbjr.com.cn/program/3306627kt.htm
今天,我們將重點關(guān)注AI在實際應(yīng)用中的落地表現(xiàn),特別是Spring AI如何能夠幫助企業(yè)實現(xiàn)功能優(yōu)化以及推動AI與業(yè)務(wù)的深度融合。我們將以當(dāng)前大廠廣泛追逐的智能體賽道為切入點,探討其在實際場景中的應(yīng)用??紤]到許多同學(xué)可能已經(jīng)接觸過智能體,以這一主題作為討論的基礎(chǔ),能夠更有效地幫助大家理解相關(guān)概念和技術(shù)的實際操作與效果。
因此,在本章節(jié)中,我們將以智能體為出發(fā)點,帶領(lǐng)大家輕松實現(xiàn)一個本地穩(wěn)定且可部署的智能體解決方案。在這一過程中,我將詳細(xì)介紹每一個步驟,確保大家能夠順利跟上。此外,在章節(jié)的最后,我會根據(jù)我的理解,分析這一方案與現(xiàn)有智能體的優(yōu)缺點,以幫助大家全面了解不同選擇的利弊。
準(zhǔn)備工作
當(dāng)然,Spring AI集成了許多知名公司的接口實現(xiàn)。如果你真的想使用OpenAI的接口,可以考慮國內(nèi)的混元API。混元API兼容OpenAI的接口規(guī)范,這意味著你可以直接使用OpenAI官方提供的SDK來調(diào)用混元的大模型。這一設(shè)計大大簡化了遷移過程,你只需將base_url和api_key替換為混元相關(guān)的配置,而無需對現(xiàn)有應(yīng)用進行額外修改。這樣,你就能夠無縫地將您的應(yīng)用切換到混元大模型,享受到強大的AI功能和支持。
申請API KEY
大家完全不必?fù)?dān)心,經(jīng)過我親自測試,目前所有接口都能夠正常兼容,并且沒有發(fā)現(xiàn)任何異?;騿栴}??梢酝ㄟ^以下鏈接申請:混元API申請地址
請確保在您個人的賬戶下申請相關(guān)的API KEY。
請務(wù)必妥善保存您的API KEY信息,因為在后續(xù)使用過程中,這一信息將會變得非常重要。
對接文檔
在這里,了解一些注意事項并不是強制性的,因為我們并不需要直接對接混元(Hunyuan)的接口。實際上,我們可以在Spring AI中直接使用兼容OpenAI的接口,這樣能夠大大簡化我們的操作流程。如果您有興趣深入了解相關(guān)的API文檔,可以自行查找接口文檔地址,里面有詳盡的說明和指導(dǎo):API接口文檔
請大家特別注意,由于智能體在運行時需要調(diào)用相關(guān)的插件或工作流,因此支持函數(shù)回調(diào)的模型僅限于以下三個。這意味著,除了這三個模型之外,其他模型都不具備這一支持功能。請確保在選擇模型時考慮這一點。
請大家留意,目前混元尚未推出預(yù)付費的大模型資源包,用戶只能進行并發(fā)包的預(yù)購。有關(guān)計費詳情,請參見下方圖示。
項目配置
接下來,我們將繼續(xù)使用之前的 Spring AI 演示項目,并對其進行必要的修改。具體需要調(diào)整的 Maven POM 依賴項如下所示:
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> </dependency>
如圖所示,在我第一次配置文件時選擇了使用 functioncall 模型,因為它的成本相對較低。然而,后來我發(fā)現(xiàn)該模型在對系統(tǒng)提示詞的識別上表現(xiàn)并不理想,后面我都換成了pro模型,大家可以根據(jù)自己的具體需求和預(yù)算做出相應(yīng)的選擇。
functioncall對提示詞不敏感但是對函數(shù)回調(diào)的結(jié)果可以很好的解析,pro對提示詞敏感但是函數(shù)回調(diào)的結(jié)果他不直接回答,一直輸出planner內(nèi)容但就是不回復(fù)用戶。后面會有詳細(xì)說明。
application.properties
文件用于全局配置,所有的 ChatClient 都會遵循這一設(shè)置。這樣做的一個顯著好處是,開發(fā)人員在代碼層面無需進行任何修改,只需在 Maven 的 POM 文件中更改相應(yīng)的依賴項,即可輕松切換到不同的 AI 大模型廠商。這種靈活性不僅提高了項目的可維護性,還方便了模型的替換與升級。
Spring AI 智能體構(gòu)建
現(xiàn)在,假設(shè)你已經(jīng)完成了所有的準(zhǔn)備工作,我們可以開始構(gòu)建屬于自己的智能體。首先,我們將專注于單獨定制配置參數(shù)。之前提到過,application.properties 文件是全局設(shè)置,適用于所有的 ChatClient,但每個模型實際上都有自己特定的領(lǐng)域和應(yīng)用場景。因此,我們首先需要配置如何為每個接口進行個性化定制,以確保模型的表現(xiàn)更加貼合實際的業(yè)務(wù)需求。
個性化配置模型
普通調(diào)用
首先,讓我們來觀察在正常情況下代碼應(yīng)該如何編寫:
@PostMapping("/ai-function") ChatDataPO functionGenerationByText(@RequestParam("userInput") String userInput) { String content = this.myChatClientWithSystem .prompt() .system("你是努力的小雨,一名 Java 服務(wù)端碼農(nóng),潛心研究著 AI 技術(shù)的奧秘。熱愛技術(shù)交流與分享,對開源社區(qū)充滿熱情。") .user(userInput) .advisors(messageChatMemoryAdvisor) .functions("CurrentWeather") .call() .content(); log.info("content: {}", content); ChatDataPO chatDataPO = ChatDataPO.builder().code("text").data(ChildData.builder().text(content).build()).build();; return chatDataPO; }
如圖所示,在我們發(fā)起請求之前,如果提前設(shè)置一個斷點,我們就能夠在這一時刻查看到 chatOptions
參數(shù),這個參數(shù)代表了我們默認(rèn)的配置設(shè)置。因此,我們的主要目標(biāo)就是在發(fā)送請求之前,探討如何對 chatOptions
參數(shù)進行有效的修改。
在對提示詞進行測試的過程中,我們發(fā)現(xiàn) functioncall
模型對于 system
提示詞的響應(yīng)效果并不顯著,似乎沒有發(fā)揮出預(yù)期的作用。然而,這個模型的一個顯著優(yōu)點是它支持函數(shù)回調(diào)功能(在前面的章節(jié)中已經(jīng)詳細(xì)講解過),此外,與 pro
模型相比,functioncall
模型的使用費用也相對較低,這使得它在某些情況下成為一個更具成本效益的選擇。
特殊調(diào)用
為了使模型的回復(fù)更加貼合提示詞的要求,我們可以對模型進行單獨配置。如果你希望對某一個特定方法進行調(diào)整,而不是采用像 application.properties 中的全局設(shè)置,那么可以通過自行修改相應(yīng)的參數(shù)來實現(xiàn)。具體的配置方法如下所示:
//省略重復(fù)代碼 OpenAiChatOptions openAiChatOptions = OpenAiChatOptions.builder() .withModel("hunyuan-pro").withTemperature(0.5f).build(); String content = this.myChatClientWithSystem .prompt() .system("你是努力的小雨,一名 Java 服務(wù)端碼農(nóng),潛心研究著 AI 技術(shù)的奧秘。熱愛技術(shù)交流與分享,對開源社區(qū)充滿熱情。") .user(userInput) .options(openAiChatOptions) .advisors(messageChatMemoryAdvisor) //省略重復(fù)代碼 }
在此,我們只需簡單地配置相關(guān)的選項即可完成設(shè)置。接下來,我們可以在斷點的部分檢查相關(guān)的配置,以確保這些設(shè)置已經(jīng)生效并正常運行。
同樣的寫法,例如,我們之前設(shè)置的 pro 模型相比于 function-call 模型在處理系統(tǒng)提示詞時顯得更加友好。
思考路徑
實際上,在絕大多數(shù)智能體中,這些思考路徑并不會被顯示出來,只有百度那邊的智能體系統(tǒng)會將其呈現(xiàn)給用戶。這些思考路徑都是由大模型生成并返回的,因此我并沒有在這里進行額外的配置。實際上,我們也可以選擇返回這些路徑,相關(guān)的源代碼也在此處:
private void writeWithMessageConverters(Object body, Type bodyType, ClientHttpRequest clientRequest) throws IOException { //省略代碼 for (HttpMessageConverter messageConverter : DefaultRestClient.this.messageConverters) { if (messageConverter instanceof GenericHttpMessageConverter genericMessageConverter) { if (genericMessageConverter.canWrite(bodyType, bodyClass, contentType)) { logBody(body, contentType, genericMessageConverter); genericMessageConverter.write(body, bodyType, contentType, clientRequest); return; } } if (messageConverter.canWrite(bodyClass, contentType)) { logBody(body, contentType, messageConverter); messageConverter.write(body, contentType, clientRequest); return; } } //省略代碼 }
如圖所示,目前我們僅僅進行了簡單的打印操作,并未實現(xiàn)消息轉(zhuǎn)換器(message converter)??紤]到我們的業(yè)務(wù)系統(tǒng)并不需要將這些信息展示給客戶,因此我們認(rèn)為當(dāng)前的實現(xiàn)方式已足夠滿足需求。
大家可以看下思考路徑的信息打印結(jié)果如下所示:
org.springframework.web.client.DefaultRestClient [453] -| Writing [ChatCompletionRequest[messages=[ChatCompletionMessage[
省略其他, 關(guān)鍵代碼如下:
role=SYSTEM, name=null, toolCallId=null, toolCalls=null, refusal=null], ChatCompletionMessage[rawContent=長春的天氣咋樣?, role=USER, name=null, toolCallId=null, toolCalls=null, refusal=null], ChatCompletionMessage[rawContent=使用'CurrentWeather'功能來獲取長春的天氣情況。用戶想要知道長春當(dāng)前的天氣情況。用戶的請求是關(guān)于獲取特定地點的天氣信息,這與工具提供的'CurrentWeather'功能相匹配。
,##省略其他
配置插件
我之前在視頻中詳細(xì)講解了智能體如何創(chuàng)建自定義插件。在這次的實踐中,我們將繼續(xù)利用百度天氣插件來獲取實時的天氣信息。不過,與之前不同的是,這一次我們將把這一功能集成到Spring AI項目中。
數(shù)據(jù)庫配置
每個業(yè)務(wù)系統(tǒng)通常都會配備自有數(shù)據(jù)庫,以便更好地服務(wù)用戶。為了演示這一點,我們將創(chuàng)建一個MySQL示例,具體內(nèi)容是獲取地區(qū)編碼值,并將其傳遞給API進行調(diào)用。在這個過程中,你可以通過插件對數(shù)據(jù)庫進行各種操作,但在此我們主要專注于查詢的演示。
本次示例中,我將繼續(xù)使用騰訊云輕量應(yīng)用服務(wù)器來搭建一個MySQL單機環(huán)境。在成功搭建環(huán)境后,我們將繼續(xù)進行后續(xù)操作。請確保在開始之前,所有必要的配置和設(shè)置都已完成,以便順利進行數(shù)據(jù)庫的查詢和API的調(diào)用。
以下是與相關(guān)配置有關(guān)的POM文件依賴項:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-spring-boot3-starter</artifactId> <version>3.5.7</version> </dependency>
數(shù)據(jù)庫連接配置信息如下:
spring.datasource.url=jdbc:mysql://ip:3306/agent?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 spring.datasource.username=agent spring.datasource.password=password spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
導(dǎo)入數(shù)據(jù)
我已經(jīng)成功完成了百度地圖提供的數(shù)據(jù)導(dǎo)入工作,具體情況請參見下圖所示:
操作數(shù)據(jù)庫
接下來,您只需在插件類內(nèi)部直接進行數(shù)據(jù)庫操作即可。關(guān)于 SearchHttpAK
實體類,您可以直接從百度地圖提供的 Java SDK 中復(fù)制,無需額外說明。同時,請注意,areaInfoPOMapper
需要您在配置類中自行進行 Bean 注入,以確保其正常使用。
public class BaiDuWeatherService implements Function<Request, Response> { AreaInfoPOMapper areaInfoPOMapper; public BaiDuWeatherService(AreaInfoPOMapper areaInfoPOMapper) { this.areaInfoPOMapper = areaInfoPOMapper; } @JsonClassDescription("location:城市地址,例如:長春市") public record Request(String location) {} public record Response(String weather) {} public Response apply(Request request) { SearchHttpAK snCal = new SearchHttpAK(); Map params = new LinkedHashMap<String, String>(); QueryWrapper<AreaInfoPO> queryWrapper = new QueryWrapper<>(); queryWrapper.like("city", request.location()); List<AreaInfoPO> areaInfoPOS = areaInfoPOMapper.selectList(queryWrapper); String reslut = ""; try { params.put("district_id", areaInfoPOS.get(0).getCityGeocode()); reslut = "天氣信息以獲取完畢,請你整理信息,以清晰易懂的方式回復(fù)用戶:" + snCal.requestGetAKForPlugins(params); log.info("reslut:{}", reslut); } catch (Exception e) { //此返回慎用,會導(dǎo)致無線調(diào)用工具鏈,所以請自行設(shè)置好次數(shù)或者直接返回錯誤即可。 //reslut = "本次調(diào)用失敗,請重新調(diào)用CurrentWeather!"; reslut = "本次調(diào)用失敗了!"; } return new Response(reslut); }
無論此次操作是否成功,都請務(wù)必避免讓大模型自行再次發(fā)起調(diào)用。這樣做可能會導(dǎo)致程序陷入死循環(huán),從而影響系統(tǒng)的穩(wěn)定性和可靠性。務(wù)必要確保在操作結(jié)束后進行適當(dāng)?shù)目刂坪凸芾?,以防止這種情況發(fā)生。
插件調(diào)用
通過這種方式,當(dāng)我們再次詢問關(guān)于長春的天氣時,大模型將能夠有效地利用插件返回的數(shù)據(jù),以準(zhǔn)確且及時地回答我們的問題。
在之前的討論中,我們提到過Pro模型對系統(tǒng)提示詞非常敏感。然而,需要注意的是,它并不會直接優(yōu)化返回的回調(diào)結(jié)果。
為了確保系統(tǒng)的響應(yīng)符合預(yù)期,這里建議再次使用系統(tǒng)提示詞進行限制和指導(dǎo)。通過明確的提示詞,我們可以更好地控制模型的輸出。
請將工具返回的數(shù)據(jù)格式化后以友好的方式回復(fù)用戶的問題。
優(yōu)化后,返回結(jié)果正常:
工作流配置
在這里,我將不再演示Spring AI中的工作流,實際上,我們的某些插件所編寫的業(yè)務(wù)邏輯本質(zhì)上就構(gòu)成了一個工作流的邏輯框架。接下來,我想重點講解如何利用第三方工作流工具來快速滿足業(yè)務(wù)需求。
集成第三方工作流
在考慮使用Spring AI實現(xiàn)智能體功能時,我們不應(yīng)輕易拋棄第三方可視化平臺。集成這些第三方工作流可以幫助我們快速實現(xiàn)所需的功能,尤其是在開發(fā)過程中,編寫Java代碼的要求往往繁瑣且復(fù)雜,一個簡單的需求可能需要涉及多個實體類的創(chuàng)建與維護。相較之下,某些簡單的業(yè)務(wù)邏輯通過第三方工作流來實現(xiàn),無疑能提升我們的開發(fā)效率,減少不必要的工作量。
以Coze智能體平臺為例,我們可以首先專注于編寫一個高效的工作流。這個工作流的主要目標(biāo)是為用戶提供全面的查詢服務(wù),包括旅游航班、火車時刻、酒店預(yù)訂等信息。
我們需要在申請到API密鑰后,進行后續(xù)的對接工作,并仔細(xì)研究開發(fā)文檔,以確保順利整合和實現(xiàn)所需的功能。
工作流插件
根據(jù)以上信息,我們可以將工作流調(diào)用封裝成插件。實際上,對于智能體平臺而言,工作流與插件本質(zhì)上都是以函數(shù)調(diào)用的形式存在,因此將工作流轉(zhuǎn)換為插件的過程是相對簡單且直接的。
public class TravelPlanningService implements Function<RequestParamer, ResponseParamer> { @JsonClassDescription("dep_city:出發(fā)城市地址,例如長春市;arr_city:到達城市,例如北京市") public record RequestParamer(String dep_city, String arr_city) {} public record ResponseParamer(String weather) {} public ResponseParamer apply(RequestParamer request) { CozeWorkFlow cozeWorkFlow = new CozeWorkFlow<RequestParamer>(); Map params = new LinkedHashMap<String, String>(); String reslut = ""; try { //這里我已經(jīng)封裝好了http調(diào)用 reslut = cozeWorkFlow.getCoze("7423018070586064915",request);; log.info("reslut:{}", reslut); } catch (Exception e) { reslut = "本次調(diào)用失敗了!"; } return new ResponseParamer(reslut); } }
由于我們的RequestParamer中使用了Java 14引入的record記錄特性,而舊版本的Fastjson無法支持將其轉(zhuǎn)換為JSON格式,因此在項目中必須使用最新版本的Fastjson依賴。如果使用不兼容的舊版本,將會導(dǎo)致功能無法正常執(zhí)行或發(fā)生失敗。
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.31</version> </dependency>
經(jīng)過配置后,如果Coze插件能夠正常運行,那么我們就可以開始為混元大模型提供相應(yīng)的回答。
工作流調(diào)用
我們已成功將該插件集成到請求處理流程中,具體實現(xiàn)的代碼如下所示:
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.31</version> </dependency>
由于返回的信息較為冗長,因此混元大模型的響應(yīng)時間通常會顯著延長。在這種情況下,我們的普通API調(diào)用可能會超時,導(dǎo)致無法成功獲取預(yù)期的結(jié)果。具體的錯誤信息如下所示:
I/O error on POST request for "https://api.hunyuan.cloud.tencent.com/v1/chat/completions": timeout
retryTemplate超時修復(fù)
我們需要對當(dāng)前的配置進行重新調(diào)整。起初,我認(rèn)為問題出在retryTemplate的配置上,因為我們在之前的討論中提到過這一點。然而,經(jīng)過仔細(xì)檢查后,我發(fā)現(xiàn)retryTemplate僅負(fù)責(zé)重試相關(guān)的信息配置,并沒有涉及到超時設(shè)置。為了進一步排查問題,我深入查看了后面的源碼,最終發(fā)現(xiàn)需要對RestClientAutoConfiguration類進行相應(yīng)的修改。
值得一提的是,RestClientAutoConfiguration類提供了定制化配置的選項,允許我們對請求的行為進行更細(xì)致的控制。以下是該類的源碼示例,展示了我們可以進行哪些具體調(diào)整:
@Bean @ConditionalOnMissingBean RestClientBuilderConfigurer restClientBuilderConfigurer(ObjectProvider<RestClientCustomizer> customizerProvider) { RestClientBuilderConfigurer configurer = new RestClientBuilderConfigurer(); configurer.setRestClientCustomizers(customizerProvider.orderedStream().toList()); return configurer; } @Bean @Scope("prototype") @ConditionalOnMissingBean RestClient.Builder restClientBuilder(RestClientBuilderConfigurer restClientBuilderConfigurer) { RestClient.Builder builder = RestClient.builder() .requestFactory(ClientHttpRequestFactories.get(ClientHttpRequestFactorySettings.DEFAULTS)); return restClientBuilderConfigurer.configure(builder); }
因此,我們需要對restClientBuilder進行必要的修改。目前,restClientBuilder中的DEFAULTS配置全部為null,這意味著它正在使用默認(rèn)的配置。而在我們調(diào)用coze工作流時,由于使用了okhttp類,內(nèi)部實際上集成了okhttp,因此也遵循了okhttp的配置方式。
為了解決這一問題,我們可以直接調(diào)整ClientHttpRequestFactorySettings的配置,以設(shè)置我們所需的超時時間。具體的配置調(diào)整如下所示:
@Bean RestClient.Builder restClientBuilder(RestClientBuilderConfigurer restClientBuilderConfigurer) { ClientHttpRequestFactorySettings defaultConfigurer = ClientHttpRequestFactorySettings.DEFAULTS .withReadTimeout(Duration.ofMinutes(5)) .withConnectTimeout(Duration.ofSeconds(30)); RestClient.Builder builder = RestClient.builder() .requestFactory(ClientHttpRequestFactories.get(defaultConfigurer)); return restClientBuilderConfigurer.configure(builder); }
請注意,在剛才提到的思考路徑中,messageConverter也是在此處進行配置的。如果有特定的需求,您完全可以進行個性化的定制。關(guān)鍵的代碼部分如下,這段代碼將調(diào)用我們自定義的方法,以便實現(xiàn)定制化的邏輯。
如果您希望設(shè)置其他的個性化配置或信息,可以參考以下示例進行調(diào)整。
public RestClient.Builder configure(RestClient.Builder builder) { applyCustomizers(builder); return builder; } private void applyCustomizers(Builder builder) { if (this.customizers != null) { for (RestClientCustomizer customizer : this.customizers) { customizer.customize(builder); } } }
至此,經(jīng)過一系列的調(diào)整和配置,我們成功解決了超時問題。這意味著在調(diào)用hunyuan模型時,我們現(xiàn)在可以順利獲取到返回的結(jié)果。
私有知識庫
由于智能體具備知識庫這一常見且重要的功能,我們也將實現(xiàn)這一部分。值得注意的是,hunyuan的API兼容向量功能,這意味著我們可以直接利用知識庫來增強智能體的能力。通過這一實現(xiàn),我們不僅能夠享受到無限制的訪問權(quán)限,還能夠進行高度的定制化,以滿足特定的業(yè)務(wù)需求。
更重要的是,這種設(shè)計使得我們在使用知識庫時具有完全的自主可控性,你無需擔(dān)心數(shù)據(jù)泄露的問題。
向量數(shù)據(jù)庫配置
接下來,我們將繼續(xù)集成Milvus,這是一個我們之前使用過的向量數(shù)據(jù)庫功能。雖然騰訊云也提供了自己的向量數(shù)據(jù)庫解決方案,但目前尚未將其集成到Spring AI中。為了便于演示和開發(fā),我們決定首先使用Milvus作為我們的向量數(shù)據(jù)庫。
為了順利完成這一集成,我們需要配置相應(yīng)的依賴項,具體如下:
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-milvus-store-spring-boot-starter</artifactId> </dependency>
需要的配置文件如下:
# 配置Milvus客戶端主機地址 spring.ai.vectorstore.milvus.client.host= # 配置Milvus客戶端端口號 spring.ai.vectorstore.milvus.client.port=19530 # 配置Milvus數(shù)據(jù)庫名稱 spring.ai.vectorstore.milvus.databaseName= # 配置Milvus集合名稱 spring.ai.vectorstore.milvus.collectionName= # 如果沒有集合會默認(rèn)創(chuàng)建一個,默認(rèn)值為false spring.ai.vectorstore.milvus.initialize-schema=true # 配置向量嵌入維度 spring.ai.vectorstore.milvus.embeddingDimension=1024 # 配置索引類型 spring.ai.vectorstore.milvus.indexType=IVF_FLAT # 配置距離度量類型 spring.ai.vectorstore.milvus.metricType=COSINE
騰訊混元的embedding 接口目前僅支持 input 和 model 參數(shù),model 當(dāng)前固定為 hunyuan-embedding,dimensions 固定為 1024。
spring.ai.openai.embedding.base-url=https://api.hunyuan.cloud.tencent.com spring.ai.openai.embedding.options.model=hunyuan-embedding spring.ai.openai.embedding.options.dimensions=1024
在這里,我們依然使用申請的混元大模型的API-key,因此無需再次進行配置。值得強調(diào)的是,這些參數(shù)的正確配置至關(guān)重要。如果未能妥善設(shè)置,將會導(dǎo)致系統(tǒng)在調(diào)用時出現(xiàn)錯誤。
基本操作
大多數(shù)智能體平臺都將對知識庫進行全面開放,以便用戶能夠自由地進行查看、修改、刪除和新增等操作。接下來,我們將演示如何進行這些操作:
@GetMapping("/ai/embedding") public Map embed(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) { EmbeddingResponse embeddingResponse = this.embeddingModel.embedForResponse(List.of(message)); return Map.of("embedding", embeddingResponse); } @GetMapping("/ai/addKnowledage") public boolean addKnowledage(@RequestParam(value = "meta-message") String message,@RequestParam(value = "vector-content") String content) { String uuid = UUID.randomUUID().toString(); DocumentInfoPO documentInfoPO = new DocumentInfoPO(); documentInfoPO.setVectorId(uuid); documentInfoPO.setMetaMessage(message); documentInfoPO.setVectorContent(content); documentInfoPOMapper.insert(documentInfoPO); List<Document> documents = List.of( new Document(uuid,content, Map.of("text", message))); vectorStore.add(documents); return true; } @GetMapping("/ai/selectKnowledage") public List<Document> selectKnowledage(@RequestParam(value = "vector-content") String content) { List<Document> result = vectorStore.similaritySearch(SearchRequest.query(content).withTopK(5).withSimilarityThreshold(0.9)); return result; } @GetMapping("/ai/deleteKnowledage") public Boolean deleteKnowledage(@RequestParam(value = "vector-id") String id) { Optional<Boolean> delete = vectorStore.delete(List.of(id)); return delete.get(); }
以下是我個人的觀點:增刪查操作的基本實現(xiàn)已經(jīng)完成。第三方智能體平臺提供修改操作的原因在于,后續(xù)的流程中,都是在刪除數(shù)據(jù)后重新插入,這一操作是不可避免的,因為大家都有修改的需求。此外,值得注意的是,默認(rèn)的向量數(shù)據(jù)庫并不支持顯示所有數(shù)據(jù),這一限制促使我們需要引入相應(yīng)的數(shù)據(jù)庫操作,以彌補這一缺陷,確保數(shù)據(jù)的完整性和可操作性。
為了更好地驗證這一過程的有效性,我提前調(diào)用了接口,上傳了一些知識庫的數(shù)據(jù)。接下來,我將展示這些數(shù)據(jù)的查詢效果。
這是我剛剛上傳的知識庫信息。為了提高效率,接下來我將直接展示知識庫的RAG(Retrieval-Augmented Generation)檢索功能在我們的智能體中的應(yīng)用。
自動調(diào)用
根據(jù)我目前的觀察,所有智能體平臺主要可以分為兩種實現(xiàn)方式:自動調(diào)用和按需調(diào)用。大部分平臺的實現(xiàn)還是以自動調(diào)用為主,除非寫在了工作流中也是就我們的函數(shù)里,那就和上面的插件一樣了,我就不講解了。今天,我將重點討論自動調(diào)用是如何實現(xiàn)的。
自動調(diào)用知識庫的實現(xiàn)依賴于Advisor接口,具體方法是在每次請求前構(gòu)造一個額外的提示詞。目前,Spring AI已經(jīng)實現(xiàn)了長期記憶的功能,其具體類為VectorStoreChatMemoryAdvisor。因此,我們可以直接參考該類的實現(xiàn)方式,以便構(gòu)建一個符合我們需求的知識庫自動調(diào)用系統(tǒng)。
我們可以進行一次實現(xiàn)。由于我們的主要目標(biāo)是在將參考信息提供給大模型時,使其能夠更好地理解上下文,因此對于響應(yīng)的增強部分可以直接忽略。這意味著我們不需要在此過程中對響應(yīng)的內(nèi)容進行額外的處理或優(yōu)化,以下是具體的代碼示例:
public class PromptChatKnowledageAdvisor implements RequestResponseAdvisor { private VectorStore vectorStore; private static final String userTextAdvise = """ 請使用以下參考信息回答問題.如果沒有參考信息,那么請直接回答即可。 --------------------- 參考信息如下: {memory} --------------------- """; public PromptChatKnowledageAdvisor(VectorStore vectorStore) { this.vectorStore = vectorStore; } @Override public AdvisedRequest adviseRequest(AdvisedRequest request, Map<String, Object> context) { // 1. 添加一段知識庫提示 String advisedSystemText = request.userText() + System.lineSeparator() + this.userTextAdvise; List<Document> documents = vectorStore.similaritySearch(request.userText()); // 2. 拼接知識庫數(shù)據(jù) String data = documents.stream().map(d -> d.getMetadata().get("text").toString()).collect(Collectors.joining(",")); Map<String, Object> advisedParams = new HashMap<>(request.userParams()); advisedParams.put("memory", data); // 3. 賦值提示詞參數(shù) AdvisedRequest advisedRequest = AdvisedRequest.from(request) .withSystemText(advisedSystemText) .withSystemParams(advisedParams) //知識庫RAG檢索數(shù)據(jù) .build(); return advisedRequest; } @Override public ChatResponse adviseResponse(ChatResponse chatResponse, Map<String, Object> context) { //不需要修改任何東西 return chatResponse; } @Override public Flux<ChatResponse> adviseResponse(Flux<ChatResponse> fluxChatResponse, Map<String, Object> context) { //不需要修改任何東西 return fluxChatResponse; } }
需要在配置類中通過構(gòu)造器注入來傳遞相同的 VectorStore
實例。
@Bean PromptChatKnowledageAdvisor promptChatKnowledageAdvisor(VectorStore vectorStore) { return new PromptChatKnowledageAdvisor(vectorStore); }
接下來,我們只需在請求方式中添加相應(yīng)的代碼或配置,以便整合新功能。
//省略重復(fù)代碼 .advisors(messageChatMemoryAdvisor,promptChatKnowledageAdvisor) .functions("CurrentWeather","TravelPlanning") .call() .content(); //省略重復(fù)代碼
這正是自動調(diào)用所帶來的顯著效果,所有操作都得到了完全的封裝,清晰明了且易于理解。
接下來,我們來看下第二種按需調(diào)用的方式,這種方法是通過使用插件(即函數(shù)回調(diào))來實現(xiàn)的。在這種模式下,系統(tǒng)可以根據(jù)實際需要動態(tài)調(diào)用相應(yīng)的插件,以提供靈活而高效的功能支持。我們之前已經(jīng)演示過兩個相關(guān)的插件,因此在這里就不再詳細(xì)展示。
線上部署
我決定不再單獨將其部署到服務(wù)器上,而是采用本地啟動的方式來暴露接口。此外,我還特別制作了一個獨立的頁面,考慮到這部分內(nèi)容并不是本章的重點,因此我將不對前端知識進行詳細(xì)講解。
為了更好地展示這些內(nèi)容,我提供了相關(guān)的演示視頻,供大家參考:
權(quán)衡利弊
首先,我想談?wù)勀壳案鞔笾悄荏w平臺的一些顯著優(yōu)勢:
可視化操作:這些平臺提供了直觀的可視化界面,使得即使是初學(xué)者也能快速開發(fā)出適合自己的業(yè)務(wù)智能體,從而更好地滿足自身的業(yè)務(wù)需求。多樣的發(fā)布渠道:許多平臺支持多種發(fā)布渠道,如公眾號等,這對于新手來說非常友好。相比之下,單純配置服務(wù)器后臺往往需要專業(yè)知識,而這些平臺則大大降低了入門門檻。豐富的插件商店:無論是哪家智能體平臺,插件的多樣性都至關(guān)重要。這些平臺通常提供官方和開發(fā)者創(chuàng)建的各種插件,幫助用戶擴展功能,滿足不同的需求。多元的工作流:工作流功能實際上與插件的作用類似,只是名稱有所不同。對外部系統(tǒng)而言,這些工作流都通過API接口實現(xiàn)集成,提升了系統(tǒng)間的互操作性與靈活性。
世間萬物都有缺陷,智能體也不例外。即使像Coze這樣的強大平臺,同樣存在一些不足之處。以下幾點尤為明顯:
功能異常處理:當(dāng)智能體出現(xiàn)功能異常時,即使你提交了工單,客服和技術(shù)人員解決問題的速度往往很慢。這種情況下,你只能無奈地等待,無法確定問題出在哪里。如果只是個人用戶的問題,可能連排期都不會給予反饋。而如果是自己開發(fā)的智能體,遇到錯誤時,你可以迅速定位問題,無論需求如何,都能隨時進行修復(fù)并發(fā)布新版本。知識庫存儲限制:由于這些智能體是面向廣大用戶的,因此知識庫的存儲額度往往受到限制,而且未來可能會開始收費。Coze已經(jīng)逐步引入了不同的收費標(biāo)準(zhǔn),各種收費標(biāo)準(zhǔn)讓你看都看不懂。在這種情況下,自己維護一個服務(wù)器無疑更加劃算。此外,當(dāng)前各大云服務(wù)商和國產(chǎn)數(shù)據(jù)庫均有向量數(shù)據(jù)庫的推薦,且通常會提供優(yōu)惠政策,極具吸引力。知識庫資料優(yōu)化:各大智能體平臺的知識庫管理方式各異,用戶需要花時間適應(yīng)其操作方式。而自己維護向量數(shù)據(jù)庫的好處在于,所有的額外元數(shù)據(jù)信息都可以自由配置,能夠根據(jù)具體業(yè)務(wù)需求進行信息過濾,從而更好地符合自身的業(yè)務(wù)標(biāo)準(zhǔn)。這是其他智能體平臺所無法提供的靈活性。費用不可控:對于企業(yè)而言,管理各種費用的可控性至關(guān)重要。然而,智能體平臺的收費往往隨著流量的增加而不受控制,可能會出現(xiàn)亂收費的情況,使企業(yè)陷入被動局面。相比之下,自行開發(fā)智能體時,可以自由更換模型,費用也在自己的掌控之中,無論是服務(wù)器費用還是大模型費用,都能有效管理。選擇性弱:智能體平臺通常與自身企業(yè)綁定,限制了用戶的選擇自由。某一天,平臺可能會決定不再支持某個大模型,這樣一來,相關(guān)的工作流也需要全部更換,因為不同的大模型在回復(fù)能力上存在顯著差異,導(dǎo)致用戶不得不重新適應(yīng)。等等.....
說了這么多,并不是說Spring AI未來會完全取代智能體平臺。畢竟,對于小眾客戶而言,通常缺乏開發(fā)和維護人員去管理代碼。因此,未來的趨勢很可能是這兩者相輔相成。智能體平臺的開發(fā)速度和能力能夠基本滿足業(yè)務(wù)中80%的需求,這一原則與大廠所踐行的二八法則不謀而合。而剩下的20%則可能需要公司內(nèi)部自行開發(fā)智能體平臺來彌補,這一比例甚至有可能更高。
因此,掌握相關(guān)技術(shù)才是企業(yè)在這一變革中最為關(guān)鍵的因素。擁有技術(shù)能力將使企業(yè)在選擇和使用智能體平臺時更加靈活,能夠根據(jù)自身的具體需求進行定制和優(yōu)化。同時,我也希望混元大模型能夠盡快兼容OpenAI的接口,或者融入Spring AI的大家庭,這樣將為用戶提供更多的選擇與靈活性。
總結(jié)
今天,我們深入探討了Spring AI在智能體構(gòu)建中的實際應(yīng)用,特別是在企業(yè)環(huán)境中的價值與效能。通過逐步實現(xiàn)一個本地部署的智能體解決方案,我們不僅展示了Spring AI的靈活性與易用性,還強調(diào)了它在推動AI技術(shù)與業(yè)務(wù)深度融合方面的潛力。
智能體的核心在于其能夠高效處理復(fù)雜的業(yè)務(wù)需求,而這一切的實現(xiàn)離不開合理的架構(gòu)設(shè)計與技術(shù)選型。通過Spring AI的集成,我們可以靈活地調(diào)用不同的API,不論是使用國內(nèi)的混元API還是其他主流的AI接口,開發(fā)者都能在項目中快速切換,確保系統(tǒng)的可維護性與擴展性。這一特性不僅提升了開發(fā)效率,還使得企業(yè)在面對市場需求變化時能夠快速反應(yīng),靈活調(diào)整技術(shù)路線。
我們在過程中涉及到的個性化配置和插件調(diào)用,充分展示了如何將傳統(tǒng)的開發(fā)模式與現(xiàn)代AI技術(shù)相結(jié)合。通過自定義插件與工作流,企業(yè)可以根據(jù)具體的業(yè)務(wù)需求,設(shè)計出更具針對性的智能體,從而提高服務(wù)質(zhì)量和客戶滿意度。例如,在天氣查詢的場景中,智能體不僅能夠通過API獲取實時數(shù)據(jù),還能將其與數(shù)據(jù)庫中的信息相結(jié)合,實現(xiàn)精準(zhǔn)而個性化的服務(wù)。這種深度的功能整合,不僅簡化了用戶的操作流程,也提高了系統(tǒng)的響應(yīng)速度。
此外,我們還提到私有知識庫的集成,強調(diào)了數(shù)據(jù)安全與自主可控的重要性。利用向量數(shù)據(jù)庫如Milvus,企業(yè)不僅能夠高效管理海量數(shù)據(jù),還能通過嵌入技術(shù)提升智能體的智能水平。這為企業(yè)在信息安全與知識產(chǎn)權(quán)保護方面提供了更為堅實的保障,尤其是在當(dāng)前信息化快速發(fā)展的背景下,這一點顯得尤為重要。
總之,本文不僅僅是對Spring AI智能體構(gòu)建過程的闡述,更是對企業(yè)如何有效利用這一技術(shù)實現(xiàn)業(yè)務(wù)升級與轉(zhuǎn)型的深入思考。希望通過我們的探討,能為您在智能體開發(fā)與應(yīng)用中提供新的視角與啟示,助力您在未來的AI之路上走得更加穩(wěn)健。
到此這篇關(guān)于Spring AI + 混元 手把手帶你實現(xiàn)企業(yè)級穩(wěn)定可部署的AI業(yè)務(wù)智能體 的文章就介紹到這了,更多相關(guān)Spring AI 混元AI業(yè)務(wù)智能體 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring Boot中利用JavaMailSender發(fā)送郵件的方法示例(附源碼)
- 深度解析Spring AI請求與響應(yīng)機制的核心邏輯
- Spring AI實現(xiàn)智能聊天模型
- 深入解析Spring?AI框架如何在Java應(yīng)用中實現(xiàn)智能化交互的關(guān)鍵
- Spring AI 入門學(xué)習(xí)指南
- Spring?AI?+?ollama?本地搭建聊天?AI?功能
- Spring?AI借助全局參數(shù)實現(xiàn)智能數(shù)據(jù)庫操作與個性化待辦管理
- Spring AI 文檔的提取、轉(zhuǎn)換、加載功能實現(xiàn)
- 如何使用spring-ws發(fā)布webservice服務(wù)
- 使用?Spring?AI?+?Ollama?構(gòu)建生成式?AI?應(yīng)用的方法
- Spring AI源碼分析流式回答(最新推薦)
相關(guān)文章
Java同步鎖synchronized用法的最全總結(jié)
這篇文章主要介紹了Java同步鎖synchronized用法的最全總結(jié),需要的朋友可以參考下,文章詳細(xì)講解了Java同步鎖Synchronized的使用方法和需要注意的點,希望對你有所幫助2023-03-03SpringBoot?整合?Elasticsearch?實現(xiàn)海量級數(shù)據(jù)搜索功能
這篇文章主要介紹了SpringBoot?整合?Elasticsearch?實現(xiàn)海量級數(shù)據(jù)搜索,本文主要圍繞?SpringBoot?整合?ElasticSearch?接受數(shù)據(jù)的插入和搜索使用技巧,在實際的使用過程中,版本號尤其的重要,不同版本的?es,對應(yīng)的?api?是不一樣,需要的朋友可以參考下2022-07-07理解Java注解及Spring的@Autowired是如何實現(xiàn)的
今天通過本文帶領(lǐng)大家學(xué)習(xí)注解的基礎(chǔ)知識,學(xué)習(xí)Spring的@Autowired是怎么實現(xiàn)的,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2021-07-07Java中反射的"暴破"機制(SetAccessible方法)詳解
這篇文章主要為大家詳細(xì)介紹了Java中反射的"暴破"機制,以及如何利用這一機制實現(xiàn)訪問非公有屬性,方法,和構(gòu)造器,文中示例代碼講解詳細(xì),感興趣的可以了解一下2022-08-08SpringMVC框架和SpringBoot項目中控制器的響應(yīng)結(jié)果深入分析
這篇文章主要介紹了SpringMVC框架和SpringBoot項目中控制器的響應(yīng)結(jié)果,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-12-12