項目一系列-第9章 集成AI千帆大模型

第9章 集成AI千帆大模型

學習目標

  • 能夠說清楚健康評估模塊在項目中的作用
  • 能夠掌握千帆大模型的開通和對接
  • 能夠掌握健康評估模塊中的prompt提示詞編寫
  • 能夠自主完成健康評估模塊的接口開發

分析設計

需求說明

健康評估是指老人辦理入住前需上傳體檢報告,由AI自動進行分析,并對報告進行總結,同時給出合理的建議;

下圖是健康評估列表頁,展示了經過健康評估的老人列表。

在這里插入圖片描述

當點擊了上傳體檢報告按鈕之后會彈窗,效果如下,需要輸入信息,需要提前準備好老人的體檢報告(PDF格式)

點擊確定按鈕之后,會使用AI對老人的健康報告進行評估

在這里插入圖片描述

下圖是健康評估的詳情頁面,是使用AI分析后的結果頁,給了很多的數據,可以讓護理員或銷售人員來查看老人的健康狀況,進一步更好的服務老人或者給老人推薦一些護理服務。

在這里插入圖片描述

表結構說明

基于需求原型,咱們可以確定健康評估表(health_assessment)如下建表語句:

CREATE TABLE "health_assessment" ("id" bigint NOT NULL AUTO_INCREMENT COMMENT '主鍵',"elder_name" varchar(255) DEFAULT NULL COMMENT '老人姓名',"id_card" varchar(255) DEFAULT NULL COMMENT '身份證號',"birth_date" datetime DEFAULT NULL COMMENT '出生日期',"age" int DEFAULT NULL COMMENT '年齡',"gender" int DEFAULT NULL COMMENT '性別(0:男,1:女)',"health_score" varchar(255) DEFAULT NULL COMMENT '健康評分',"risk_level" varchar(255) DEFAULT NULL COMMENT '危險等級(健康, 提示, 風險, 危險, 嚴重危險)',"suggestion_for_admission" int DEFAULT NULL COMMENT '是否建議入住(0:建議,1:不建議)',"nursing_level_name" varchar(255) DEFAULT NULL COMMENT '推薦護理等級',"admission_status" int DEFAULT NULL COMMENT '入住情況(0:已入住,1:未入住)',"total_check_date" varchar(64) DEFAULT NULL COMMENT '總檢日期',"physical_exam_institution" varchar(255) DEFAULT NULL COMMENT '體檢機構',"physical_report_url" varchar(255) DEFAULT NULL COMMENT '體檢報告URL鏈接',"assessment_time" datetime DEFAULT NULL COMMENT '評估時間',"report_summary" text COMMENT '報告總結',"disease_risk" text COMMENT '疾病風險',"abnormal_analysis" text COMMENT '異常分析',"system_score" varchar(255) DEFAULT NULL COMMENT '健康系統分值',"create_by" varchar(255) DEFAULT NULL COMMENT '創建者',"create_time" datetime DEFAULT NULL COMMENT '創建時間',"update_by" varchar(255) DEFAULT NULL COMMENT '更新者',"update_time" datetime DEFAULT NULL COMMENT '更新時間',"remark" text COMMENT '備注',PRIMARY KEY ("id")
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='健康評估表';

特別字段說明:

  • abnormal_analysis(異常分析) 這個字段是字符串類型,會把異常數據轉換為json進行存儲

接口說明

基于需求說明,在健康評估這個模塊中,共有4個接口需要開發,分別是

  • 列表查詢-模板生成
  • 上傳體檢報告-手動編寫
  • 智能評測-手動編寫
  • 查看詳情-模板生成

實現方案

整體實現流程如下:

在這里插入圖片描述

讀取PDF文件內容

市面有很多工具類,咱們可以直接使用,目前選擇的是Apache PDFBox

Apache PDFBox庫是一個用于處理PDF文檔的開源Java工具。該項目允許創建新的PDF文檔,編輯現有的文檔,以及從文檔中提取內容。

導入對應的依賴,在zzyl-common模塊中導入以下依賴:

<!--pdf工具包-->
<dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.24</version>
</dependency>

創建工具類,通過pdf文件來讀取內容,工具類路徑:zzyl-common模塊下的com.zzyl.common.utils.PDFUtil

package com.zzyl.common.utils;import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;import java.io.File;
import java.io.IOException;
import java.io.InputStream;public class PDFUtil {public static String pdfToString(InputStream inputStream) {PDDocument document = null;try {// 加載PDF文檔document = PDDocument.load(inputStream);// 創建一個PDFTextStripper實例來提取文本PDFTextStripper pdfStripper = new PDFTextStripper();// 從PDF文檔中提取文本String text = pdfStripper.getText(document);return text;} catch (IOException e) {e.printStackTrace();} finally {// 關閉PDF文檔if (document != null) {try {document.close();inputStream.close();} catch (IOException e) {e.printStackTrace();}}}return null;}
}

測試:找一個pdf文檔來讀取里面的內容為字符串

package com.nursing.test;import com.zzyl.common.utils.PDFUtil;import java.io.FileInputStream;
import java.io.FileNotFoundException;public class PDFUtilTest {public static void main(String[] args) throws FileNotFoundException {FileInputStream fileInputStream = new FileInputStream("C:\\tmp\\體檢報告-劉愛國-男-69歲.pdf");String result = PDFUtil.pdfToString(fileInputStream);System.out.println(result);}
}

百度智能云AI集成

選擇合適的AI

咱們的需求中,prompt是比較多的,也就是說需要更多的token,對話Chat V2支持的模型列表如下:

https://cloud.baidu.com/doc/WENXINWORKSHOP/s/wm7ltcvgc

經過綜合評估,咱們本次采用的是ERNIE-4.0-8K

官方地址:https://cloud.baidu.com/doc/WENXINWORKSHOP/s/clntwmv7t

ERNIE 4.0是百度自研的旗艦級超大規模?語?模型,相較ERNIE 3.5實現了模型能力全面升級,廣泛適用于各領域復雜任務場景;支持自動對接百度搜索插件,保障問答信息時效,支持5K tokens輸入+2K tokens輸出。

  • 對話模型,一次請求就是一次對話
  • 提供了java的sdk調用,地址:https://cloud.baidu.com/doc/qianfan-docs/s/nm9l6oc8e
  • 支持單輪對話、多輪對話、流式
  • 部分關鍵的參數:https://cloud.baidu.com/doc/qianfan-api/s/3m7of64lb
  • 返回參數:result可以獲取對話返回的結果

集成準備步驟

1)注冊和實名認證百度智能云

2)創建應用

2.1)訪問千帆大模型,地址:https://qianfan.cloud.baidu.com/

2.2)點擊大模型服務與開發平臺ModelBuilder**,**可以進入到管理平臺

在這里插入圖片描述

2.3)進入管理平臺后,找到應用接入,點擊創建應用按鈕。

2.4)創建一個新的API Key

3)項目集成

3.1)在父工程pom.xml文件中統一管理依賴的版本:

<properties><openai-java.version>2.8.1</openai-java.version><kotlin.version>1.9.23</kotlin.version><jackson.version>2.16.1</jackson.version><okhttp.version>4.12.0</okhttp.version>
</properties><dependencies><!-- 千帆AI --><dependency><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-bom</artifactId><version>${kotlin.version}</version><scope>import</scope><type>pom</type></dependency><!-- Kotlin Standard Library --><dependency><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib</artifactId><version>${kotlin.version}</version></dependency><dependency><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-reflect</artifactId><version>${kotlin.version}</version></dependency><dependency><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib-jdk8</artifactId><version>${kotlin.version}</version></dependency><dependency><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib-jdk7</artifactId><version>${kotlin.version}</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>${jackson.version}</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>${jackson.version}</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId><version>${jackson.version}</version></dependency><dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jdk8</artifactId><version>${jackson.version}</version></dependency><dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId><version>${jackson.version}</version></dependency><dependency><groupId>com.fasterxml.jackson.module</groupId><artifactId>jackson-module-kotlin</artifactId><version>${jackson.version}</version></dependency><dependency><groupId>com.openai</groupId><artifactId>openai-java</artifactId><version>${openai-java.version}</version></dependency><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>${okhttp.version}</version></dependency>
</dependencies>

3.2)在common模塊的pom.xml文件中引入openai-java的依賴,并排除有版本沖突的依賴然后重新引入

<!-- 千帆AI -->
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId>
</dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId>
</dependency><dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jdk8</artifactId>
</dependency><dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId>
</dependency><dependency><groupId>com.fasterxml.jackson.module</groupId><artifactId>jackson-module-kotlin</artifactId><exclusions><exclusion><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-reflect</artifactId></exclusion><exclusion><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib</artifactId></exclusion><exclusion><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib-jdk7</artifactId></exclusion></exclusions>
</dependency><!--MyBatisPlus再AI集成中要排除有版本沖突的依賴-->
<!--MyBatisPlus-->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><exclusions><exclusion><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib</artifactId></exclusion><exclusion><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-reflect</artifactId></exclusion><exclusion><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib-jdk7</artifactId></exclusion><exclusion><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib-jdk8</artifactId></exclusion></exclusions>
</dependency><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId>
</dependency><dependency><groupId>com.openai</groupId><artifactId>openai-java</artifactId><exclusions><exclusion><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId></exclusion><exclusion><groupId>com.squareup.okio</groupId><artifactId>okio</artifactId></exclusion><exclusion><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib</artifactId></exclusion><exclusion><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib-jdk7</artifactId></exclusion><exclusion><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib-jdk8</artifactId></exclusion><exclusion><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-reflect</artifactId></exclusion></exclusions>
</dependency><dependency><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib-jdk7</artifactId>
</dependency><!-- Kotlin Standard Library -->
<dependency><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib</artifactId>
</dependency><dependency><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-reflect</artifactId>
</dependency><dependency><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib-jdk8</artifactId><exclusions><exclusion><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib-jdk7</artifactId></exclusion></exclusions>
</dependency>

3.3)參考官方示例(https://cloud.baidu.com/doc/qianfan-docs/s/nm9l6oc8e)編寫測試方法來學習

package com.zzyl;import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.ResponseFormatJsonObject;
import com.openai.models.chat.completions.ChatCompletion;
import com.openai.models.chat.completions.ChatCompletionCreateParams;public class QianfanAIModelTest {public static void main(String[] args) {OpenAIClient client = OpenAIOkHttpClient.builder().apiKey("bce-v3/ALTAK-6TGebWXMraNYDUsJdvwoh/61483266642b4a18108057ce85a900850d4b7715") //將your_APIKey替換為真實值,如何獲取API Key請查看https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Um2wxbaps#步驟二-獲取api-key.baseUrl("https://qianfan.baidubce.com/v2/") //千帆ModelBuilder平臺地址.build();ChatCompletionCreateParams params = ChatCompletionCreateParams.builder().addUserMessage("你是誰?") // 對話messages信息.model("ernie-4.0-8k") // 模型對應的model值,請查看支持的模型列表:https://cloud.baidu.com/doc/WENXINWORKSHOP/s/wm7ltcvgc
//                .responseFormat(ChatCompletionCreateParams.ResponseFormat.ofJsonObject(ResponseFormatJsonObject.builder().build())).build();ChatCompletion chatCompletion = client.chat().completions().create(params);System.out.println(chatCompletion.choices().get(0).message().content().orElseGet(() -> ""));}
}

代碼的apiKey要換成自己的

后續再接口開發中就要將AI集成到項目中。

接口開發

先用若依跟據表結構生成基礎代碼,便于后續代碼的改造開發。

該項目主要有兩個接口需要開發,上傳體檢報告保存智能評測(AI生成評測數據)

上傳體檢報告

為什么要有單獨的文件上傳接口,因為這個接口還有將數據添加到緩存的步驟。

可以由原先的通用上傳接口改造。

在功能模塊的pom.xml文件下:

<!-- 阿里云OSS -->
<dependency><groupId>com.zzyl</groupId><artifactId>zzyl-oss</artifactId>
</dependency>

工具類要存在common模塊的utils包下:

package com.zzyl.common.utils;import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;import java.io.File;
import java.io.IOException;
import java.io.InputStream;public class PDFUtil {public static String pdfToString(InputStream inputStream) {PDDocument document = null;try {// 加載PDF文檔document = PDDocument.load(inputStream);// 創建一個PDFTextStripper實例來提取文本PDFTextStripper pdfStripper = new PDFTextStripper();// 從PDF文檔中提取文本String text = pdfStripper.getText(document);return text;} catch (IOException e) {e.printStackTrace();} finally {// 關閉PDF文檔if (document != null) {try {document.close();inputStream.close();} catch (IOException e) {e.printStackTrace();}}}return null;}
}

該工具類要生效確保pdf工具包依賴已經引入。

在控制層:

@Autowired
private AliyunOSSOperator aliyunOSSOperator;@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 通用上傳請求(單個)
*/             
@ApiOperation("上傳體檢報告")
@PostMapping("/upload")
public AjaxResult uploadFile(MultipartFile file, String idCardNo) throws Exception
{try {// 上傳到OSSString url = aliyunOSSOperator.upload(file.getBytes(), file.getOriginalFilename());AjaxResult ajax = AjaxResult.success();ajax.put("url", url);ajax.put("fileName", url);ajax.put("newFileName", url.substring(url.lastIndexOf("/")));ajax.put("originalFilename", file.getOriginalFilename());// PDF文件內容讀取為字符串String content = PDFUtil.pdfToString(file.getInputStream());// 臨時存儲到redis中redisTemplate.opsForHash().put(CacheConstants.HEALTH_REPORT_ALL_KEY, idCardNo, content);return ajax;} catch (Exception e) {return AjaxResult.error(e.getMessage());}
}
保存智能評測(AI生成評測數據)
設計Prompt

Prompt的構成部分:

  • 角色:給 AI 定義一個最匹配任務的角色,比如:「你是一位軟件工程師」「你是一位小學老師」

  • 指示:對任務進行描述

  • 上下文:給出與任務相關的其它背景信息(尤其在多輪交互中)

  • 例子:必要時給出舉例,[實踐證明例子對輸出正確性有幫助]

  • 輸入:任務的輸入信息;在提示詞中明確的標識出輸入

  • 輸出:輸出的格式描述,以便后繼模塊自動解析模型的輸出結果,比如(JSON、Java)

    設計Prompt提示詞,因為涉及了大量的專業名詞和健康指標,實際開發中需要找產品經理協助

    請以一個專業醫生的視角來分析這份體檢報告,報告中包含了一些異常數據,我需要您對這些數據進行解讀,并給出相應的健康建議。
    體檢內容如下:內容略....要求:
    1. 提取體檢報告中的“總檢日期”;
    2. 通過臨床醫學、疾病風險評估模型和數據智能分析,給該用戶的風險等級和健康指數給出結果。風險等級分為:健康、提示、風險、危險、嚴重危險。健康指數范圍為0100分;
    3. 根據用戶身體各項指標數據,詳細說明該用戶各項風險等級的占比是多少,最多保留兩位小數。結論格式:該用戶健康占比20.00%,提示占比20.00%,風險占比20%,危險占比20%,嚴重危險占比20%4. 對于體檢報告中的異常數據,請列出(異常數據的結論、體檢項目名稱、檢查結果、參考值、單位、異常解讀、建議)這7字段。解讀異常數據,解決這些數據可能代表的健康問題或風險。分析可能的原因,包括但不限于生活習慣、飲食習慣、遺傳因素等。基于這些異常數據和可能的原因,請給出具體的健康建議,包括飲食調整、運動建議、生活方式改變以及是否需要進一步檢查或治療等。
    結論格式:異常數據的結論:肥胖,體檢項目名稱:體重指數BMI,檢查結果:29.2,參考值>24,單位:-。異常解讀:體重超標包括超重與肥胖。體重指數(BMI=體重(kg)/身?(m)的平?,BMI24為超重,BMI28為肥胖;男性腰圍≥90cm和?性腰圍≥85cm為腹型肥胖。體重超標是?種由多因素(如遺傳、進?油脂較多、運動少、疾病等)引起的慢性代謝性疾病,尤其是肥胖,已經被世界衛?組織列為導致疾病負擔的??危險因素之?。AI建議:采取綜合措施預防和控制體重,積極改變?活?式,宜低脂、低糖、?纖維素膳?,多?果蔬及菌藻類?物,增加有氧運動。若有相關疾病(如?脂異常、??壓、糖尿病等)應積極治療。
    5. 根據這個體檢報告的內容,分別給人體的8大系統打分,每項滿分為100分,8大系統分別為:呼吸系統、消化系統、內分泌系統、免疫系統、循環系統、泌尿系統、運動系統、感官系統
    6. 給體檢報告做一個總結,總結格式:體檢報告中尿蛋?、癌胚抗原、?沉、空腹?糖、總膽固醇、?油三酯、低密度脂蛋?膽固醇、?清載脂蛋?B、動脈硬化指數、?細胞、平均紅細胞體積、平均?紅蛋?共12項指標提示異常,尿液常規共1項指標處于臨界值,?脂、?液常規、尿液常規、糖類抗原、?清酶類等共43項指標提示正常,綜合這些臨床指標和數據分析:腎臟、肝膽、?腦?管存在隱患,其中?腦?管有“?危”?險;腎臟部位有“中危”?險;肝膽部位有“低危”?險。輸出要求:
    最后,將以上結果輸出為純JSON格式,不要包含其他的文字說明,也不要出現Markdown語法相關的文字,所有的返回結果都是json,詳細格式如下:{"totalCheckDate": "YYYY-MM-DD","healthAssessment": {"riskLevel": "healthy/caution/risk/danger/severeDanger","healthIndex": XX.XX},"riskDistribution": {"healthy": XX.XX,"caution": XX.XX,"risk": XX.XX,"danger": XX.XX,"severeDanger": XX.XX},"abnormalData": [{"conclusion": "異常數據的結論","examinationItem": "體檢項目名稱","result": "檢查結果","referenceValue": "參考值","unit": "單位","interpret":"對于異常的結論進一步詳細的說明","advice":"針對于這一項的異常,給出一些健康的建議"}],"systemScore": {"breathingSystem": XX,"digestiveSystem": XX,"endocrineSystem": XX,"immuneSystem": XX,"circulatorySystem": XX,"urinarySystem": XX,"motionSystem": XX,"senseSystem": XX},"summarize": "體檢報告的總結"
    }
    
功能編寫步驟

當用戶上傳體檢報告之后,點擊確定,就會觸發智能評測的調用。

1)一些可變的參數,在application.yml文件中定義:

# 百度千帆大模型配置
baidu:qianfan:apiKey: bce-v3/ALTAK-6H6k7kuLyTmZPBmOJ2ajC/2520d4ad36847715153ca819932f1d854bb9e71f(換成自己的apiKey)baseUrl: https://qianfan.baidubce.com/v2/model: ernie-4.0-8k

2)在zzyl-common模塊下定義工具類:

2.2)在zzyl-common模塊下定義BaiduAIProperties配置類和AIModelInvoker工具類:

com.zzyl.common.ai.BaiduAIProperties配置類:

package com.zzyl.common.ai;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;@Data
@Configuration
@ConfigurationProperties(prefix = "baidu.qianfan")
public class BaiduAIProperties {private String apiKey;private String baseUrl;private String model;
}

com.zzyl.common.ai.AIModelInvoker工具類:

package com.zzyl.common.ai;import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.ResponseFormatJsonObject;
import com.openai.models.chat.completions.ChatCompletion;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
@Slf4j
public class AIModelInvoker {@Autowiredprivate BaiduAIProperties baiduAIProperties;public String qianfanInvoker(String prompt) {OpenAIClient client = OpenAIOkHttpClient.builder().apiKey(baiduAIProperties.getApiKey()).baseUrl(baiduAIProperties.getBaseUrl()).build();ChatCompletionCreateParams params = ChatCompletionCreateParams.builder().addUserMessage(prompt).model(baiduAIProperties.getModel()).responseFormat(ChatCompletionCreateParams.ResponseFormat.ofJsonObject(ResponseFormatJsonObject.builder().build())).build();ChatCompletion chatCompletion = client.chat().completions().create(params);return chatCompletion.choices().get(0).message().content().orElseGet(() -> "");}
}

2.2)跟據身份證提取個人信息的IDCardUtils工具類

package com.zzyl.common.utils;import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Period;
import java.time.format.DateTimeFormatter;public final class IDCardUtils {// 私有構造器,防止實例化private IDCardUtils() {throw new AssertionError("Cannot instantiate static class IDCardUtils");}/*** 根據身份證號提取個人年齡。** @param idCard 身份證號碼* @return 個人年齡,數值類型*/public static int getAgeByIdCard(String idCard) {LocalDate birthDate = extractBirthDate(idCard);if (birthDate != null) {LocalDate currentDate = LocalDate.now();return Period.between(birthDate, currentDate).getYears();}throw new IllegalArgumentException("Invalid ID card number.");}/*** 根據身份證號提取出生日期。** @param idCard 身份證號碼* @return 出生日期,LocalDateTime類型*/public static LocalDateTime getBirthDateByIdCard(String idCard) {String birthDateString = idCard.substring(6, 14); // 身份證號碼中的出生日期部分DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");LocalDate birthDate = LocalDate.parse(birthDateString, formatter);return LocalDateTime.of(birthDate, LocalTime.MIDNIGHT);}/*** 根據身份證號提取性別。** @param idCard 身份證號碼* @return 性別,0表示女性,1表示男性*/public static int getGenderFromIdCard(String idCard) {if (idCard == null || idCard.length() != 18) {throw new IllegalArgumentException("Invalid ID card number");}String genderCode = idCard.substring(16, 17);int gender = Integer.parseInt(genderCode);return gender % 2 == 0 ? 0 : 1;}/*** 內部輔助方法,從身份證號中提取出生日期的LocalDate對象。** @param idCard 身份證號碼* @return 出生日期的LocalDate對象,或null(如果身份證號碼無效)*/private static LocalDate extractBirthDate(String idCard) {if (idCard == null || idCard.length() != 18) {return null;}try {String birthStr = idCard.substring(6, 14);DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");return LocalDate.parse(birthStr, formatter);} catch (Exception e) {return null;}}public static void main(String[] args) {System.out.println(getAgeByIdCard("330103199001011234"));System.out.println(getBirthDateByIdCard("330103199001011234"));}
}

3)改造MVC代碼

3.1)修改IHealthAssessmentService中的insertHealthAssessment方法,把返回值類型替換為Long

/*** 新增健康評估* * @param healthAssessment 健康評估* @return 結果*/
public Long insertHealthAssessment(HealthAssessment healthAssessment);

3.2)實現類中的返回值類型也要改為Long,并且按照思路來編寫業務代碼

最終代碼如下:

@Autowired
private RedisTemplate<String, String> redisTemplate;@Autowired
private AIModelInvoker aiModelInvoker;/*** 新增健康評估* * @param healthAssessment 健康評估* @return 結果*/
@Override
public Long insertHealthAssessment(HealthAssessment healthAssessment)
{// 1.設計Prompt提示詞(需要從Redis中讀取當前身份證號對應的體檢報告)String prompt = getPrompt(healthAssessment.getIdCard());// 2.調用百度千帆大模型,分析體檢報告,獲取分析結果String qianfanResult = aiModelInvoker.qianfanInvoker(prompt);// 3.將分析結果保存到數據庫中,并返回保存的這條記錄的id// 將大模型返回的字符串解析為對象,方便取數據HealthReportVo healthReportVo = JSON.parseObject(qianfanResult, HealthReportVo.class);return saveHealthAssessment(healthReportVo, healthAssessment);
}/*** 保存大模型返回的結果和前端傳遞的老人信息到數據庫* @param healthReportVo        千帆大模型返回的結果* @param healthAssessment      老人基本信息* @return  記錄的id*/
private Long saveHealthAssessment(HealthReportVo healthReportVo, HealthAssessment healthAssessment) {// 老人身份證號String idCard = healthAssessment.getIdCard();healthAssessment.setBirthDate(IDCardUtils.getBirthDateByIdCard(idCard));healthAssessment.setAge(IDCardUtils.getAgeByIdCard(idCard));healthAssessment.setGender(IDCardUtils.getGenderFromIdCard(idCard));// 健康評分double healthScore = healthReportVo.getHealthAssessment().getHealthIndex();healthAssessment.setHealthScore(String.valueOf(healthScore));// 風險等級healthAssessment.setRiskLevel(healthReportVo.getHealthAssessment().getRiskLevel());// 通過健康評分判斷是否建議入住healthAssessment.setSuggestionForAdmission(healthScore >= 60 ? 0 : 1);// 通過健康評分計算一個推薦的護理等級String nursingLevelName = getLevelNameByHealthScore(healthScore);healthAssessment.setNursingLevelName(nursingLevelName);// 統一先設置未入住healthAssessment.setAdmissionStatus(1);// 總檢日期healthAssessment.setTotalCheckDate(healthReportVo.getTotalCheckDate());healthAssessment.setAssessmentTime(LocalDateTime.now());// 報告總結healthAssessment.setReportSummary(healthReportVo.getSummarize());// 疾病風險分布healthAssessment.setDiseaseRisk(JSON.toJSONString(healthReportVo.getRiskDistribution()));// 異常分析healthAssessment.setAbnormalAnalysis(JSON.toJSONString(healthReportVo.getAbnormalData()));// 八大系統評分healthAssessment.setSystemScore(JSON.toJSONString(healthReportVo.getSystemScore()));healthAssessmentMapper.insert(healthAssessment);return healthAssessment.getId();
}private String getLevelNameByHealthScore(double healthScore) {if (healthScore > 100 || healthScore < 0) {throw new BaseException("健康評分值不合法");}if (healthScore >= 90) {return "四級護理等級";} else if (healthScore >= 80) {return "三級護理等級";} else if (healthScore >= 70) {return "二級護理等級";} else if (healthScore >= 60) {return "一級護理等級";} else {return "特級護理等級";}
}/*** 獲取Prompt提示詞* @param idCard    身份證號* @return  提示詞*/
private String getPrompt(String idCard) {// 獲取文件中的內容String content = (String) redisTemplate.opsForHash().get("healthReport", idCard);// 判斷是否為空if (StringUtils.isEmpty(content)) {throw new BaseException("文件提取內容失敗,請重新上傳提交報告");}String prompt = "請以一個專業醫生的視角來分析這份體檢報告,報告中包含了一些異常數據,我需要您對這些數據進行解讀,并給出相應的健康建議。\n" +"體檢內容如下:\n" +content + "    \n" +"\n" +"要求:\n" +"1. 提取體檢報告中的“總檢日期”;\n" +"2. 通過臨床醫學、疾病風險評估模型和數據智能分析,給該用戶的風險等級和健康指數給出結果。風險等級分為:健康、提示、風險、危險、嚴重危險。健康指數范圍為0至100分;\n" +"3. 根據用戶身體各項指標數據,詳細說明該用戶各項風險等級的占比是多少,最多保留兩位小數。結論格式:該用戶健康占比20.00%,提示占比20.00%,風險占比20%,危險占比20%,嚴重危險占比20%;\n" +"4. 對于體檢報告中的異常數據,請列出(異常數據的結論、體檢項目名稱、檢查結果、參考值、單位、異常解讀、建議)這7字段。解讀異常數據,解決這些數據可能代表的健康問題或風險。分析可能的原因,包括但不限于生活習慣、飲食習慣、遺傳因素等。基于這些異常數據和可能的原因,請給出具體的健康建議,包括飲食調整、運動建議、生活方式改變以及是否需要進一步檢查或治療等。\n" +"結論格式:異常數據的結論:肥胖,體檢項目名稱:體重指數BMI,檢查結果:29.2,參考值>24,單位:-。異常解讀:體重超標包括超重與肥胖。體重指數(BMI)=體重(kg)/身?(m)的平?,BMI≥24為超重,BMI≥28為肥胖;男性腰圍≥90cm和?性腰圍≥85cm為腹型肥胖。體重超標是?種由多因素(如遺傳、進?油脂較多、運動少、疾病等)引起的慢性代謝性疾病,尤其是肥胖,已經被世界衛?組織列為導致疾病負擔的??危險因素之?。AI建議:采取綜合措施預防和控制體重,積極改變?活?式,宜低脂、低糖、?纖維素膳?,多?果蔬及菌藻類?物,增加有氧運動。若有相關疾病(如?脂異常、??壓、糖尿病等)應積極治療。\n" +"5. 根據這個體檢報告的內容,分別給人體的8大系統打分,每項滿分為100分,8大系統分別為:呼吸系統、消化系統、內分泌系統、免疫系統、循環系統、泌尿系統、運動系統、感官系統\n" +"6. 給體檢報告做一個總結,總結格式:體檢報告中尿蛋?、癌胚抗原、?沉、空腹?糖、總膽固醇、?油三酯、低密度脂蛋?膽固醇、?清載脂蛋?B、動脈硬化指數、?細胞、平均紅細胞體積、平均?紅蛋?共12項指標提示異常,尿液常規共1項指標處于臨界值,?脂、?液常規、尿液常規、糖類抗原、?清酶類等共43項指標提示正常,綜合這些臨床指標和數據分析:腎臟、肝膽、?腦?管存在隱患,其中?腦?管有“?危”?險;腎臟部位有“中危”?險;肝膽部位有“低危”?險。\n" +"\n" +"輸出要求:\n" +"最后,將以上結果輸出為純JSON格式,不要包含其他的文字說明,也不要出現Markdown語法相關的文字,所有的返回結果都是json,詳細格式如下:\n" +"\n" +"{\n" +"  \"totalCheckDate\": \"YYYY-MM-DD\",\n" +"  \"healthAssessment\": {\n" +"    \"riskLevel\": \"healthy/caution/risk/danger/severeDanger\",\n" +"    \"healthIndex\": XX.XX\n" +"  },\n" +"  \"riskDistribution\": {\n" +"    \"healthy\": XX.XX,\n" +"    \"caution\": XX.XX,\n" +"    \"risk\": XX.XX,\n" +"    \"danger\": XX.XX,\n" +"    \"severeDanger\": XX.XX\n" +"  },\n" +"  \"abnormalData\": [\n" +"    {\n" +"      \"conclusion\": \"異常數據的結論\",\n" +"      \"examinationItem\": \"體檢項目名稱\",\n" +"      \"result\": \"檢查結果\",\n" +"      \"referenceValue\": \"參考值\",\n" +"      \"unit\": \"單位\",\n" +"      \"interpret\":\"對于異常的結論進一步詳細的說明\",\n" +"      \"advice\":\"針對于這一項的異常,給出一些健康的建議\"\n" +"    }\n" +"  ],\n" +"  \"systemScore\": {\n" +"    \"breathingSystem\": XX,\n" +"    \"digestiveSystem\": XX,\n" +"    \"endocrineSystem\": XX,\n" +"    \"immuneSystem\": XX,\n" +"    \"circulatorySystem\": XX,\n" +"    \"urinarySystem\": XX,\n" +"    \"motionSystem\": XX,\n" +"    \"senseSystem\": XX\n" +"  },\n" +"  \"summarize\": \"體檢報告的總結\"\n" +"}";return prompt;
}

Vo可跟據Json格式生成并編寫嚴格的提示詞生成。

HealthReportVo值對象:

package com.zzyl.nursing.vo;import lombok.Data;import java.util.List;/*** 體檢報告* @author itheima*/
@Data
public class HealthReportVo {/*** 體檢日期*/private String totalCheckDate;/*** 健康評估*/private HealthAssessmentVo healthAssessment;/*** 風險分布*/private RiskDistributionVo riskDistribution;/*** 異常數據列表*/private List<AbnormalDataVo> abnormalData;/*** 健康系統分值*/private SystemScore systemScore;/*** 綜合總結*/private String summarize;
}

HealthAssessmentVo值對象:

package com.zzyl.nursing.vo;import lombok.Data;/***  健康評估類* @author itheima*/
@Data
public class HealthAssessmentVo {/*** 健康風險等級*/private String riskLevel;/*** 健康指數*/private double healthIndex;}

AbnormalDataVo值對象:

package com.zzyl.nursing.vo;import lombok.Data;/*** 異常數據類* @author itheima*/
@Data
public class AbnormalDataVo {/*** 結論*/private String conclusion;/*** 檢查項目*/private String examinationItem;/*** 結果*/private String result;/*** 參考值*/private String referenceValue;/*** 單位*/private String unit;/*** 結果解釋*/private String interpret;/*** 建議*/private String advice;}

RiskDistributionVo值對象:

package com.zzyl.nursing.vo;import lombok.Data;/*** 風險分布類* @author itheima*/
@Data
public class RiskDistributionVo {/*** 健康*/private double healthy;/*** 警告*/private double caution;/*** 風險*/private double risk;/*** 危險*/private double danger;/*** 嚴重危險*/private double severeDanger;}

SystemScore值對象:

package com.zzyl.nursing.vo;import lombok.Data;@Data
public class SystemScore {/*** 呼吸系統*/private int breathingSystem;/*** 消化系統*/private int digestiveSystem;/*** 內分泌系統*/private int endocrineSystem;/*** 免疫系統*/private int immuneSystem;/*** 循環系統*/private int circulatorySystem;/*** 泌尿系統*/private int urinarySystem;/*** 感覺系統*/private int motionSystem;/*** 感官系統*/private int senseSystem;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/94981.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/94981.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/94981.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vben admin5組件文檔(豆包版)---VbenTree

VbenTree 用法說明 VbenTree 是 Vben5 中基于 radix-vue 實現的樹形組件&#xff0c;支持單選、多選、展開/折疊、權限控制等功能。以下是其核心用法說明&#xff1a; 1. 基礎引入 import { VbenTree } from vben-core/shadcn-ui;2. 核心屬性&#xff08;Props&#xff09;屬性…

postman常用快捷鍵

作為一名IT程序猿&#xff0c;不懂一些工具的快捷方式&#xff0c;應該會被鄙視的吧。收集了一些Postman的快捷方式&#xff0c;大家一起動手操作~ 1小時postman接口測試從入門到精通教程簡單操作 操作mac系統windows系統 打開新標簽 ?TCtrl T關閉標簽?WCtrl W強制關閉標簽…

【物聯網】什么是 DHT11(數字溫濕度傳感器)?

正面照片&#xff08;藍色傳感器朝上&#xff0c;針腳朝下&#xff09; 絲印標注非常清晰&#xff1a; 左邊 → S &#x1f449; 信號 (DATA) 中間 → &#x1f449; VCC (電源&#xff0c;3.3V 或 5V) 右邊 → - &#x1f449; GND (地) ? 正確接法&#xff08;Arduino Nano…

光譜相機在霧霾監測中有何優勢?

光譜相機在霧霾監測中的優勢主要體現在多維度數據采集和環境適應性方面&#xff0c;結合最新技術進展分析如下&#xff1a;一、核心優勢?穿透性監測能力? 短波紅外&#xff08;SWIR&#xff09;波段可穿透霧霾顆粒&#xff0c;結合可見光成像實現霧霾濃度與能見度的同步監測&…

【c++】超好玩游戲

#include <iostream> #include <vector> #include <conio.h> #include <windows.h> #include <time.h>using namespace std;// 游戲常量 const int WIDTH 40; const int HEIGHT 20; const int PADDLE_WIDTH 5;// 方向枚舉 enum Direction { S…

GitHub 熱榜項目 - 日榜(2025-08-27)

GitHub 熱榜項目 - 日榜(2025-08-27) 生成于&#xff1a;2025-08-27 統計摘要 共發現熱門項目&#xff1a;15 個 榜單類型&#xff1a;日榜 本期熱點趨勢總結 本期GitHub熱榜呈現出三大技術趨勢&#xff1a;1. AI生產力工具持續升溫&#xff1a;系統提示詞泄露庫、DeepCode…

基于Springboot + vue3實現的學校學報出版發行管理系統

項目描述本系統包含管理員和用戶兩個角色。管理員角色&#xff1a;用戶管理&#xff1a;管理系統中所有用戶的信息&#xff0c;包括添加、刪除和修改用戶。稿件分類管理&#xff1a;管理稿件分類信息&#xff0c;包括新增、查看、修改和刪除稿件分類。新聞資訊管理&#xff1a;…

【Keil5教程及技巧】耗時一周精心整理萬字全網最全Keil5(MDK-ARM)功能詳細介紹【建議收藏-細細品嘗】

&#x1f48c; 所屬專欄&#xff1a;【單片機開發軟件技巧】 &#x1f600; 作??者&#xff1a; 于曉超 &#x1f680; 個人簡介&#xff1a;嵌入式工程師&#xff0c;專注嵌入式領域基礎和實戰分享 &#xff0c;歡迎咨詢&#xff01; &#x1f496; 歡迎大家&#xff1…

國家育兒補貼政策遭利用,黑產組織借機竊取敏感數據

組織概況與作案手法近期網絡安全領域出現了一個高度組織化的犯罪集團UTG-Q-1000&#xff0c;該組織通過利用中國國家育兒補貼政策實施大規模金融詐騙和數據竊取活動。這個結構嚴密的犯罪網絡下設多個專業部門&#xff0c;包括財務組、新聞與色情組、設計與制造組以及黑市交易組…

Python Imaging Library (PIL) 全面指南:PIL高級圖像處理-分割與顏色空間轉換

高級圖像處理&#xff1a;PIL中的圖像分割與顏色空間轉換 學習目標 本課程將深入探討PIL&#xff08;Python Imaging Library&#xff09;中的一些高級功能&#xff0c;包括圖像分割和顏色空間轉換。通過本課程的學習&#xff0c;學員將能夠掌握如何使用PIL進行更復雜的圖像處理…

圖解 OAuth,為什么這樣設計?

OAuth 于 2007 年首次推出。它最初由 Twitter 創建&#xff0c;因為 Twitter 希望能夠允許第三方應用代表用戶發布推文。想象一下&#xff0c;如果今天設計類似的應用&#xff0c;你會怎么做&#xff1f;一種方法是直接要求用戶輸入用戶名和密碼。因此&#xff0c;你創建一個非…

WeakAuras Lua Script ICC (BarneyICC) Simplified Chinese [Mini]

WeakAuras Lua Script ICC &#xff08;BarneyICC&#xff09; Simplified Chinese [Mini] ICC 迷你版本會打了只需要團隊框體高亮提示即可&#xff0c;因為有DBM&#xff0c;就不需要那么多了 !WA:2!S3xc4XrXzI6wkSjzcVSyb4aoKWGaC04ijMdPrsoit0OdRXwxmsYgmWoNTup4rZ0UNr2sKL…

mcp學習

mcp學習 預算&#xff1a;5塊(半頓拼好飯嗚嗚嗚) 出問題試著開啟或者關閉代理。 文章目錄mcp學習1. 基本原理2. 環境配置1. cherryStudiodeepseekpython2. Clinedeepseek3. 常用mcp服務1. mcp-server-fetch2. mcp-playwright3. baidu-map4. filesystem5. mcp-mysql-server參考…

Rust:所有權

Rust&#xff1a;所有權拷貝 & 移動堆棧拷貝移動克隆所有權變量的初始權限指針的雙重權限權限的動態變化引用賦值重新借用函數調用時的權限移動拷貝借用不可變借用可變借用復合類型的權限結構體元組數組傳統語言的內存管理要么依賴程序員手動管理&#xff08;C/C&#xff0…

Elasticsearch數據遷移快照方案初探(二):快照創建與多節點存儲問題解決

快照倉庫創建成功 經過前面的配置修改&#xff0c;我們成功創建了快照倉庫&#xff1a; curl -X PUT "https://[ES_HOST]:9200/_snapshot/backup_repo" \-H "Content-Type: application/json" \-u "[USERNAME]:[PASSWORD]" \-k \-d {"type&…

DeepSeek大模型風靡云平臺,百度智能云、阿里云、騰訊云等多個平臺宣布上線DeepSeek模型

近日&#xff0c;百度智能云、華為云、阿里云、騰訊云、360數字安全、云軸科技等多個平臺紛紛宣布上線DeepSeek大模型&#xff0c;這一消息無疑為AI開發者和企業用戶帶來了全新的機遇和選擇。本文將探討DeepSeek大模型上線的背景、意義以及未來的發展趨勢。 首先&#xff0c;我…

position屬性

文章目錄Position屬性&#x1f9ed; 一、position 屬性的取值&#x1f4dd; 二、各屬性值詳解與示例1. static&#xff08;靜態定位&#xff09;2. relative&#xff08;相對定位&#xff09;3. absolute&#xff08;絕對定位&#xff09;4. fixed&#xff08;固定定位&#xf…

通信中間件 Fast DDS(二) :詳細介紹

目錄 1.引言 2.DDS的基本原理 3.FastDDS 的核心特性 4.FastDDS 的核心架構 5.典型應用場景 6.FastDDS 的安裝與快速上手 7.學習資源與社區 1.引言 FastDDS&#xff08;原稱 Fast RTPS&#xff09;是由西班牙公司 eProsima 開發的一款開源、高性能、實時性強的數據分發服…

【69頁PPT】智慧方案智慧校園解決方案(附下載方式)

篇幅所限&#xff0c;本文只提供部分資料內容&#xff0c;完整資料請看下面鏈接 https://download.csdn.net/download/2501_92808811/91776074 資料解讀&#xff1a;【69頁PPT】智慧方案智慧校園解決方案 詳細資料請看本解讀文章的最后內容 智慧校園的概念與背景 智慧校園是…

FPGA的工作原理

FPGA&#xff08;現場可編程門陣列&#xff09;的核心工作原理是通過可配置的硬件架構&#xff0c;讓用戶在芯片出廠后自主定義電路邏輯&#xff0c;實現從“通用硬件”到“專用硬件”的靈活轉換&#xff0c;本質是用可編程資源搭建出符合特定需求的數字電路。一、核心架構&…