問題引出
在我們日常使用大模型時,有一類典型的應用場景,就是將文件發送給大模型,然后由大模型進行解析,提煉總結等,這一類功能在官方app中較為常見,但是在很多模型的api中都不支持,那如何使用api實現該功能呢?
在阿里云官方文檔的模型列表中(模型列表與價格_大模型服務平臺百煉(Model Studio)-阿里云幫助中心),我們可以看到,qwen-vl系列大模型支持圖片類文檔的解析(如掃描件或圖片pdf),其底層即使用OCR進行圖片識別來解析
那如果是文字版pdf呢?先轉成圖片,再傳給qwen-vl系列大模型?
有一個方案是自己將pdf解析為純文本,然后發送給大模型,例如使用tika對pdf進行解析
public String summarize(@RequestParam("file") MultipartFile file) {List<Document> documents = new TikaDocumentReader(file.getResource()).get();String documentText = documents.stream().map(Document::getFormattedContent).collect(Collectors.joining("\n\n"));return chatClient.prompt().user(DEFAULT_SUMMARY_PROMPT).system(systemSpec ->systemSpec.text(summarizeTemplate).param("document", documentText)).call().content();}
不過這個方案略微繁瑣了點,需要自行對文件進行解析,那有沒有可以直接上傳文件的方案呢?
答案就是qwen-long系列大模型
qwen-long簡介
通義千問系列上下文窗口最長,能力均衡且成本較低的模型,適合長文本分析、信息抽取、總結摘要和分類打標等任務。參考文檔:Qwen-Long_大模型服務平臺百煉(Model Studio)-阿里云幫助中心
使用方式
先將文件上傳,然后在與大模型的對話中攜帶fileid
代碼調用
spring-ai-alibaba中為了方便用戶使用,最新提供了DashScopeDocumentAnalysisAdvisor,省略了用戶需要自行實現文件上傳及附帶fileid的步驟
@PostMapping(path = "/analyze", produces = "text/plain")public String analyze(@RequestParam("file") MultipartFile file) {ApiKey apiKey = new SimpleApiKey("your key");return chatClient.prompt().advisors(new DashScopeDocumentAnalysisAdvisor(apiKey)).advisors(a -> a.param(DashScopeDocumentAnalysisAdvisor.RESOURCE, file.getResource())).user("總結文檔內容") //或根據文檔內容提問.options(DashScopeChatOptions.builder().withModel("qwen-long").build()).call().content();}
若模型默認不是qwen-long,需指定模型為qwen-long
使用時需創建一個DashScopeDocumentAnalysisAdvisor,并傳入一個Resource
Resource可以是本地文件FileSystemResource,也可以是網址UrlResource,或者上傳的文件,如上述樣例中的代碼
ps:相關功能目前未上傳到中央倉庫,需自行編譯打包,或等待新版本發布(>1.0.0.2)
ps:雖然該實現由文字版pdf引出,實際支持文本文件( TXT、DOCX、PDF、XLSX、EPUB、MOBI、MD、CSV、JSON),圖片文件(BMP、PNG、JPG/JPEG、GIF 以及PDF掃描件)等
代碼解析
DashScopeDocumentAnalysisAdvisor內部代碼邏輯較為簡單
當resource參數不為空時,獲取Resource并將其上傳,得到文件信息,將其加入上下文
然后提取對話的SystemMessage,在其中加入文件信息,然后大模型即可識別用戶上傳的文檔
public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {var context = chatClientRequest.context();Resource resource = (Resource) context.get(RESOURCE);if (resource != null) {ResponseEntity<UploadResponse> uploadResponse = upload(resource);context.put(UPLOAD_RESPONSE, uploadResponse);Assert.notNull(uploadResponse.getBody(), "upload response body is null");String augmentSystemMessage = DEFAULT_PROMPT_TEMPLATE.render(Map.of("id", uploadResponse.getBody().id,"originSystemMessage", chatClientRequest.prompt().getSystemMessage().getText()));return chatClientRequest.mutate().prompt(chatClientRequest.prompt().augmentSystemMessage(augmentSystemMessage)).build();}return chatClientRequest;}