SpringBoot调用LangChain-ChatChat FastAPI上传文件

最近开发的系统的功能之一是要把前端上传的文件保存在MinIO的同时,还要用OCR识别存入ES和LangChain的知识库。LangChain在服务器上搭建好了,FastAPI也准备就绪,就差SpringBoot调用接口上传了,这个问题足足卡了我三四天,随着本地知识库的兴起,遇到这种问题的人应该会有很多,故写此文以供参考。

已知条件

使用Swagger调试FastAPI接口curl的传参结构为

rust 复制代码
curl -X 'POST' \
  'http://xxx/knowledge_base/upload_docs' \
  -H 'accept: application/json' \
  -H 'Content-Type: multipart/form-data' \
  -F 'to_vector_store=true' \
  -F 'override=true' \
  -F 'not_refresh_vs_cache=false' \
  -F 'chunk_size=1000' \
  -F 'chunk_overlap=50' \
  -F 'zh_title_enhance=true' \
  -F 'files=@XXXX.txt;type=text/plain' \
  -F 'knowledge_base_name=ganlan_guichengguifan' \
  -F 'docs='

参数释义:

  • to_vector_store指上传文件后是否进行向量化。
  • override指是否覆盖已有文件。
  • not_refresh_vs_cache指是否暂不保存向量库(用于FAISS)。
  • chunk_size指知识库中单段文本最大长度。
  • chunk_overlap指知识库中相邻文本重合长度。
  • zh_title_enhance指是否开启中文标题加强。
  • files指咱们要上传的文件。
  • docs指自定义的docs,可以为空。

因为传参结构是固定的,所以在SpringBoot中创建对象UploadKnowledgeEntity

java 复制代码
import lombok.Data;
import org.springframework.core.io.FileSystemResource;

@Data
public class UploadKnowledgeEntity {
    /**
     * 上传文件后是否进行向量化
     */
    private boolean to_vector_store;

    /**
     * 覆盖已有文件
     */
    private boolean override;

    /**
     * 暂不保存向量库(用于FAISS)
     */
    private boolean not_refresh_vs_cache;

    /**
     * 知识库中单段文本最大长度
     */
    private int  chunk_size;

    /**
     * 知识库中相邻文本重合长度
     */
    private int chunk_overlap;

    /**
     * 是否开启中文标题加强
     */
    private boolean zh_title_enhance;

    /**
     * 知识库名称
     */
    private String knowledge_base_name;

    /**
     * 文件
     */
    private FileSystemResource files;
}

注意看UploadKnowledgeEntity类中的文件的类型FileSystemResource,这个参数是SpringBoot发出的请求能否被FastAPI接受的关键,原因如下:

查看LangChain的upload_docs方法

python 复制代码
def upload_docs(
    files: List[UploadFile] = File(..., description="上传文件,支持多文件"),
    knowledge_base_name: str = Form(..., description="知识库名称", examples=["samples"]),
    override: bool = Form(False, description="覆盖已有文件"),
    to_vector_store: bool = Form(True, description="上传文件后是否进行向量化"),
    chunk_size: int = Form(CHUNK_SIZE, description="知识库中单段文本最大长度"),
    chunk_overlap: int = Form(OVERLAP_SIZE, description="知识库中相邻文本重合长度"),
    zh_title_enhance: bool = Form(ZH_TITLE_ENHANCE, description="是否开启中文标题加强"),
    docs: Json = Form({}, description="自定义的docs,需要转为json字符串",
                      examples=[{"test.txt": [Document(page_content="custom doc")]}]),
    not_refresh_vs_cache: bool = Form(False, description="暂不保存向量库(用于FAISS)"),

可以看到API的files类型是UploadFile,Python的UploadFile主要用于处理文件上传的场景。在使用requests库进行文件上传时,可以通过files参数来指定文件的位置和名称。

因为Java的FileSystemResource主要用于获取文件系统里面的资源。它既可以作为文件处理,也可以作为URL处理。

两者都可以用于处理文件上传和文件系统资源的获取。

这就是为什么UploadKnowledgeEntity实体里的files文件类型不是File,不是MultipartFile,不是File.getBytes()的原因,因为我他妈这几天都已经试过了,接口全都不认哈哈哈。

有了上述已知条件后,可求得核心代码如下:

java 复制代码
/**
 * 发送post请求
 * @param url
 * @param multipartFile
 * @param fileType
 * @return
 * @throws Exception
 */
public static String doPostJson(String url,MultipartFile multipartFile,String fileType) throws Exception {
    UploadKnowledgeEntity knowledgeEntity=new UploadKnowledgeEntity();
    knowledgeEntity.setOverride(false);
    knowledgeEntity.setTo_vector_store(true);
    knowledgeEntity.setNot_refresh_vs_cache(false);
    knowledgeEntity.setChunk_size(1000);
    knowledgeEntity.setChunk_overlap(50);
    knowledgeEntity.setZh_title_enhance(true);
    knowledgeEntity.setKnowledge_base_name("XXX");
    
    File file=multipartFileToFile(multipartFile);
    FileSystemResource fileSystemResource=new FileSystemResource(file);
    knowledgeEntity.setFiles(fileSystemResource);
    
    try {
        // 组装请求信息
        MultiValueMap<String,Object> param=new LinkedMultiValueMap<>();
        Map<String,Object> map=entityToMap(knowledgeEntity);
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            param.add(entry.getKey(), entry.getValue());
        }

        HttpEntity<MultiValueMap<String,Object> > httpEntity=new HttpEntity<>(param);
        RestTemplate restTemplate=new RestTemplate();
        KnowledgeAnswer response=restTemplate.postForObject(url,httpEntity,KnowledgeAnswer.class);
        return "文件上传成功";
    } catch (Exception e) {
        throw new RuntimeException("[发送POST请求错误:]" + e.getMessage());
    }
}

测试文件上传,响应内容:

json 复制代码
{
  "success": true,
  "message": "文件上传成功",
  "code": 200,
  "result": "文件上传成功",
  "timestamp": 1702541585135
}
相关推荐
丕羽1 小时前
【Pytorch】基本语法
人工智能·pytorch·python
2401_865854881 小时前
iOS应用想要下载到手机上只能苹果签名吗?
后端·ios·iphone
bryant_meng1 小时前
【python】Distribution
开发语言·python·分布函数·常用分布
AskHarries1 小时前
Spring Boot集成Access DB实现数据导入和解析
java·spring boot·后端
2401_857622662 小时前
SpringBoot健身房管理:敏捷与自动化
spring boot·后端·自动化
程序员阿龙2 小时前
基于SpringBoot的医疗陪护系统设计与实现(源码+定制+开发)
java·spring boot·后端·医疗陪护管理平台·患者护理服务平台·医疗信息管理系统·患者陪护服务平台
程思扬2 小时前
为什么Uptime+Kuma本地部署与远程使用是网站监控新选择?
linux·服务器·网络·经验分享·后端·网络协议·1024程序员节
阿华的代码王国2 小时前
【Spring】——SpringBoot项目创建
java·spring boot·后端·启动类·target文件
m0_594526302 小时前
Python批量合并多个PDF
java·python·pdf
九鼎科技-Leo3 小时前
什么是 ASP.NET Core?与 ASP.NET MVC 有什么区别?
windows·后端·c#·asp.net·mvc·.net