JavaAi+DeepSeek 部署筆記

當(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);

   }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容