當(dāng)前最熱門的ai話題估計都是圍繞著deepseek展開的。我也趁著這波熱度來學(xué)習(xí)并記錄一下。
參考資料:
文檔地址
視頻學(xué)習(xí)地址
課程倉庫
代碼倉庫
目前主要有無代碼平臺:Dify 和Coze
javaAi的編碼平臺有:SpringAi 和 LangChain4j
當(dāng)前模型可選擇:OpenAi、阿里百煉、DeepSeek、智譜清言、硅基流動、Ollma(本地ai容器)
開源庫:LangChain4j 。 使用它可接入各大模型,便于java開發(fā)者對大模型的api調(diào)用與集成。
使用阿里百煉平臺進行測試 鏈接
使用硅基流動鏈接
新建一個sprintboot項目,采用maven管理依賴
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
</dependency>
<!-- 加載bom 后,所有l(wèi)angchain4j引用不需要加版本號 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-bom</artifactId>
<version>0.36.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
yaml 配置文件:
langchain4j:
open-ai:
chat-model:
# 課程測試 KEY,需要更換為實際可用 KEY
api-key: sk-xx
model-name: qwen-turbo
# 百煉兼容OpenAI接口規(guī)范,base_url為https://dashscope.aliyuncs.com/compatible-mode/v1
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
本人進行復(fù)制操作時遇到的問題:在拷貝yaml配置文件的時候漏了 langchain4j: 節(jié)點,導(dǎo)致spring注入依賴沒有辦法匹配到配置文件,因此報錯:
@Bean
@ConditionalOnProperty({"langchain4j.open-ai.chat-model.api-key"})
OpenAiChatModel openAiChatModel(Properties properties) {
ChatModelProperties chatModelProperties = properties.getChatModel();
return OpenAiChatModel.builder().baseUrl(chatModelProperties.getBaseUrl()).apiKey(chatModelProperties.getApiKey()).organizationId(chatModelProperties.getOrganizationId()).modelName(chatModelProperties.getModelName()).temperature(chatModelProperties.getTemperature()).topP(chatModelProperties.getTopP()).stop(chatModelProperties.getStop()).maxTokens(chatModelProperties.getMaxTokens()).maxCompletionTokens(chatModelProperties.getMaxCompletionTokens()).presencePenalty(chatModelProperties.getPresencePenalty()).frequencyPenalty(chatModelProperties.getFrequencyPenalty()).logitBias(chatModelProperties.getLogitBias()).responseFormat(chatModelProperties.getResponseFormat()).strictJsonSchema(chatModelProperties.getStrictJsonSchema()).seed(chatModelProperties.getSeed()).user(chatModelProperties.getUser()).strictTools(chatModelProperties.getStrictTools()).parallelToolCalls(chatModelProperties.getParallelToolCalls()).timeout(chatModelProperties.getTimeout()).maxRetries(chatModelProperties.getMaxRetries()).proxy(ProxyProperties.convert(chatModelProperties.getProxy())).logRequests(chatModelProperties.getLogRequests()).logResponses(chatModelProperties.getLogResponses()).customHeaders(chatModelProperties.getCustomHeaders()).build();
}
chatAi :LLM API 調(diào)用
@Bean
public ChatLanguageModel chatLanguageModel() {
return OpenAiChatModel.builder()
.apiKey(apiKey)
.modelName("qwen-turbo")
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.build();
}
@Bean
public ChatAssistant chatAssistant(ChatLanguageModel chatLanguageModel){
return AiServices.create(ChatAssistant.class,chatLanguageModel);
}
chat流式輸出
使用spring controller進行web端的調(diào)用
/**
* 測試controller
* lp
* 2025年02月19日15:54:39
* */
@RestController
@RequiredArgsConstructor
@RequestMapping("/test01")
public class ChatTestController01 {
private final StreamChatAssistant streamChatAssistant;
/**
* 前端流式調(diào)用
* @param question
* http://127.0.0.1:8080/test01/chat/stream?question="hello"
* */
@GetMapping("/chat/stream")
public Flux<String> chat(@RequestParam("question") String question){
return streamChatAssistant.chat(question);
}
}
chat 的記憶持久化:ChatMemory、實現(xiàn)ChatMemoryStore接口進行自定義持久存儲。
在默認(rèn)情況下持久化內(nèi)容存儲存在內(nèi)存中
問題解決:有兩個 TestBean 類型的 bean注入問題時候。
為了明確指定在創(chuàng)建 時使用哪一個 TestBean,可以在參數(shù)上使用 @Qualifier 注解。
以下是具體的修改步驟:
在需要注入的地方使用 @Qualifier 注解:
參數(shù)中添加 @Qualifier("TestBean01"),以明確指定使用 TestBean01 這個 bean。
如果你需要在其他地方使用 TestBean02,也可以類似地使用 @Qualifier("TestBean02")。
提示詞工程:系統(tǒng)級提示詞、用戶提示詞模板
SystemMessage具有高優(yōu)先級,能有效地指導(dǎo)模型的整體行為
1. 使用 @UserMessage 和 @V 注解:
public interface AiAssistant {
@SystemMessage("你是一位專業(yè)的中國法律顧問,只回答與中國法律相關(guān)的問題。輸出限制:對于其他領(lǐng)域的問題禁止回答,直接返回'抱歉,我只能回答中國法律相關(guān)的問題。'")
@UserMessage("請回答以下法律問題:{{question}}")
String answerLegalQuestion(@V("question") String question);
}
2. 使用 @StructuredPrompt 定義結(jié)構(gòu)化提示:
@Data
@StructuredPrompt("根據(jù)中國{{legal}}法律,解答以下問題:{{question}}")
class LegalPrompt {
private String legal;
private String question;
}
3. 使用 PromptTemplate 渲染:
// 默認(rèn) form 構(gòu)造使用 it 屬性作為默認(rèn)占位符
PromptTemplate template = PromptTemplate.from("請解釋中國法律中的'{{it}}'概念。");
Prompt prompt = template.apply("知識產(chǎn)權(quán)");
System.out.println(prompt.text()); // 輸出: 請解釋中國法律中的'知識產(chǎn)權(quán)'概念。
// apply 方法接受 Map 作為參數(shù)
PromptTemplate template2 = PromptTemplate.from("請解釋中國法律中的'{{legal}}'概念。");
Prompt prompt2 = template2.apply(Map.of("legal", "知識產(chǎn)權(quán)"));
System.out.println(prompt2.text());
Json格式化輸出數(shù)據(jù)
讓大模型輸出我們想要的格式的數(shù)據(jù)方式有幾種:指定類型、根據(jù)枚舉、自定義pojo類
原理為:使用提示詞限定結(jié)果的輸出
/**
* 返回一個pojo自定義數(shù)據(jù)類型
* 可以使用@Description 注解幫助大模型理解字段含義
* */
@UserMessage("從數(shù)據(jù)中提取 person 信息 {{it}}")
Person extractPerson(String text);
@Data
class Person {
@Description("姓名") // 增加字段描述,讓大模型更理解字段含義
private String name;
private LocalDate birthDate;
}
-------
請求:
method: POST
- url: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
- headers: [Authorization: Bearer sk-cf...b4], [User-Agent: langchain4j-openai]
- body: {
"model" : "qwen-turbo",
"messages" : [ {
"role" : "user",
"content" : "從數(shù)據(jù)中提取 person 信息 我叫小明,今年18歲,我是一個學(xué)生,我的生日是2005年8月10日\nYou must answer strictly in the following JSON format: {\n\"name\": (姓名; type: string),\n\"birthDate\": (type: date string (2023-12-31))\n}"
} ],
"temperature" : 0.7
}
結(jié)果:
{"choices":[{"message":{"role":"assistant","content":"```json\n{\n \"name\": \"小明\",\n \"birthDate\": \"2005-08-10\"\n}\n```"}
注意點:如果沒有提取到對應(yīng)的數(shù)據(jù),可能會導(dǎo)致返回的內(nèi)容錯誤或者出現(xiàn)異常,需要對大模型額外針對錯誤數(shù)據(jù)進行提示處理:
@UserMessage("從數(shù)據(jù)中提取 person 信息 {{it}}。如果沒有提取到,則直接回復(fù):“(name=null, birthDate=1900-01-01)")
Person extractPerson(String text);
函數(shù)調(diào)用: 觸發(fā)外部操作:如發(fā)送郵件、控制智能家居設(shè)備等
方式:
編碼注入函數(shù)、注解注入函數(shù)、動態(tài)工具配置
@Bean
public FunctionAssistant functionAssistant(ChatLanguageModel chatLanguageModel) {
// 工具說明 ToolSpecification
ToolSpecification toolSpecification = ToolSpecification.builder()
.name("invoice_assistant")
.description("根據(jù)用戶提交的開票信息,開具發(fā)票")
.addParameter("companyName", type("string"), description("公司名稱"))
.addParameter("dutyNumber", type("string"), description("稅號"))
.addParameter("amount", type("number"), description("金額"))
.build();
// 業(yè)務(wù)邏輯 ToolExecutor
ToolExecutor toolExecutor = (toolExecutionRequest, memoryId) -> {
String arguments1 = toolExecutionRequest.arguments();
System.out.println("arguments1 =>>>> " + arguments1);
return "開具成功";
};
return AiServices.builder(FunctionAssistant.class)
.chatLanguageModel(chatLanguageModel)
.tools(Map.of(toolSpecification, toolExecutor))
.build();
}
動態(tài)函數(shù)調(diào)用:
接入GraalVM,實現(xiàn)動態(tài)函數(shù)調(diào)用,直接執(zhí)行代碼給出結(jié)果,而不是通過推理
CodeExecutionEngine engine = new GraalVmJavaScriptExecutionEngine();
String code = """
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
fibonacci(10)
""";
String result = engine.execute(code);
ChatAssistant assistant = AiServices.builder(ChatAssistant.class)
.chatLanguageModel(chatLanguageModel)
.tools(new GraalVmJavaScriptExecutionTool())
.build();
String chat = chatAssistant.chat("What is the square root of 485906798473894056 in scientific notation?");
System.out.println(chat);
引入的 graalVm 依賴,通過GraalVmJavaScriptExecutionTool類實現(xiàn)了JavaScript代碼的動態(tài)執(zhí)行
函數(shù)增強搜索:
接入SearchApi 實現(xiàn)大模型直接訪問web進行查詢數(shù)據(jù)
public interface ChatAssistant {
@SystemMessage("""
1. 搜索支持:你的職責(zé)是為用戶提供基于網(wǎng)絡(luò)搜索的支持。
2. 事件驗證:如果用戶提到的事件尚未發(fā)生或信息不明確,你需要通過網(wǎng)絡(luò)搜索確認(rèn)或查找相關(guān)信息。
3. 網(wǎng)絡(luò)搜索請求:使用用戶的查詢創(chuàng)建網(wǎng)絡(luò)搜索請求,并通過網(wǎng)絡(luò)搜索工具進行實際查詢。
4. 引用來源:在最終回應(yīng)中,必須包括搜索到的來源鏈接,以確保信息的準(zhǔn)確性和可驗證性。
""")
String chat(String message);
}
@Bean
public ChatAssistant chatAssistant(ChatLanguageModel chatLanguageModel) {
SearchApiWebSearchEngine searchEngine = SearchApiWebSearchEngine.builder()
.apiKey("p8SZVNAweqTtoZBBTVnXttcj")// 測試使用
.engine("google")
.build();
WebSearchTool webSearchTool = WebSearchTool.from(searchEngine);
return AiServices.builder(ChatAssistant.class).chatLanguageModel(chatLanguageModel).tools(webSearchTool).build();
}
String chat = chatAssistant.chat("20241008 上證指數(shù)是多少");
System.out.println(chat);
截至2024年10月8日,A股市場迎來了國慶節(jié)后的首個交易日,主要指數(shù)表現(xiàn)強勁。具體到上證指數(shù),開盤時漲幅達(dá)到了10.13%,但之后的走勢有所調(diào)整,最終收盤時上證指數(shù)報收于3489.78點,漲幅為4.59%。這一天,滬深兩市的成交額均非常活躍,接近或超過3.5萬億元人民幣。
以上信息來源于多個新聞源,包括財新網(wǎng)、觀察者網(wǎng)、中國新聞網(wǎng)、新浪財經(jīng)等,您可以點擊提供的鏈接查看更詳細(xì)的信息和報道背景。請注意,這些數(shù)據(jù)和分析僅供參考,市場情況可能會隨時變化。
向量化及存儲:
Embedding模型是將文本數(shù)據(jù)(如詞匯、短語或句子)轉(zhuǎn)換為數(shù)值向量的工具,將文本映射到高維空間中的點,使語義相似的文本在這個空間中距離較近.
@Value("${dashscope.api.key}")
private String apiKey;
private String qdrantHost="127.0.0.1";
private int qdrantPort=6334;
public static String collectionName="testEmbedding01";
/**
* 創(chuàng)建Qdrant客戶端
* */
@Bean
public QdrantClient qdrantClient() {
QdrantGrpcClient.Builder grpcClientBuilder = QdrantGrpcClient.newBuilder(qdrantHost, qdrantPort, false);
return new QdrantClient(grpcClientBuilder.build());
}
/**
*使用OpenAI的Embedding模型進行文本向量化
* 這里使用的是阿里百煉平臺 text-embedding-v3 向量模型
* */
@Bean
public EmbeddingModel embeddingModel() {
return OpenAiEmbeddingModel.builder()
.apiKey(apiKey)
.modelName("text-embedding-v3")
.logResponses(true)
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.build();
}
/**
* 配置Qdrant Embedding Store
* */
@Bean
public EmbeddingStore<TextSegment> embeddingStore() {
return QdrantEmbeddingStore.builder()
.host(qdrantHost)
.port(qdrantPort)
.collectionName(collectionName)
.build();
}
文本向量化分類:
過TextClassifier接口,結(jié)合向量模型,可以完成文本信息的分類,可以應(yīng)用到性格,心理等測試之中
/**
* 簡單的例子:
* 通過TextClassifier,以及文本向量化分類(只支持枚舉類型的分類)
* */
@Bean
public EmbeddingModelTextClassifier<PersonalityTrait> textClassifier(EmbeddingModel embeddingModel) {
return new EmbeddingModelTextClassifier(embeddingModel, PersonalityTraitExamples.examples);
}
/**
* 文本向量化分類 測試
* 使用textClassifer,通過EmbeddingModel 向量大模型,最后根據(jù)枚舉類型得到分類結(jié)果
* 可以應(yīng)用在性格判斷,比如說心理方面的評估
* */
@Test
void textClassifier() {
List<PersonalityTrait> personalityTraitList= textClassifier.classify("我最喜歡和別人一起工作了,讓我可以學(xué)到東西,也可以幫助到其他人,收獲很多,通過幫助團隊的人,我們可以一起進步和學(xué)習(xí)");
System.out.println("返回最匹配的性格分類");
System.out.println(personalityTraitList);
}
RAG:檢索增強生成(Retrieval-Augmented Generation,簡稱RAG)是一種結(jié)合大型語言模型(LLM)和外部知識庫的技術(shù)
兩個階段:索引(預(yù)處理和存儲文檔數(shù)據(jù)為向量數(shù)據(jù))和檢索(將用戶搜索轉(zhuǎn)換為向量進行搜索查詢)
檢索增強器(Retrieval Augmentor)
@Bean
public ChatAssistant assistant(ChatLanguageModel chatLanguageModel, EmbeddingStore<TextSegment> embeddingStore) {
DefaultRetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
.queryTransformer() // 查詢增強
.contentRetriever() // 內(nèi)容源 單個直接配置
.queryRouter(new DefaultQueryRouter())// 多個內(nèi)容源,路由
.contentAggregator() // 匹配結(jié)果聚合
.contentInjector() // 結(jié)果提示詞注入
.executor() // 并行化
.build();
return AiServices.builder(ChatAssistant.class)
.chatLanguageModel(chatLanguageModel)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.retrievalAugmentor(retrievalAugmentor)
.build();
}
調(diào)用本地部署的deepseek模型
參考鏈接1
參考鏈接2
@SpringBootTest
public class OllamaTest {
//首先在裝有ollama的電腦上啟動 : ollama serve, 然后啟動模型:ollama run deepseek-r1:7b
String url = "http://192.168.110.77:11434";
String modelName = "deepseek-r1:7b" ;
String embedingModel="bge-m3:latest";
@Test
void test(){
LanguageModel model = OllamaLanguageModel.builder()
.baseUrl(url)
.modelName(modelName)
.build();
String result = model.generate("你是誰").content();
System.out.println(result);
}
}