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
}
相关推荐
Q_19284999067 分钟前
基于Spring Boot的摄影器材租赁回收系统
java·spring boot·后端
良许Linux12 分钟前
0.96寸OLED显示屏详解
linux·服务器·后端·互联网
求知若饥24 分钟前
NestJS 项目实战-权限管理系统开发(六)
后端·node.js·nestjs
qq_5290252927 分钟前
Torch.gather
python·深度学习·机器学习
数据小爬虫@27 分钟前
如何高效利用Python爬虫按关键字搜索苏宁商品
开发语言·爬虫·python
Cachel wood1 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
終不似少年遊*1 小时前
pyecharts
python·信息可视化·数据分析·学习笔记·pyecharts·使用技巧
gb42152871 小时前
springboot中Jackson库和jsonpath库的区别和联系。
java·spring boot·后端
程序猿进阶1 小时前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
Python之栈1 小时前
【无标题】
数据库·python·mysql