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 '[email protected];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
}
相关推荐
fish_study_csdn5 小时前
pytest 技术总结
开发语言·python·pytest
柏油5 小时前
MySQL InnoDB 行锁
数据库·后端·mysql
咖啡调调。5 小时前
使用Django框架表单
后端·python·django
BO_S__5 小时前
python调用ffmpeg对截取视频片段,可批量处理
python·ffmpeg·音视频
白泽talk5 小时前
2个小时1w字| React & Golang 全栈微服务实战
前端·后端·微服务
摆烂工程师5 小时前
全网最详细的5分钟快速申请一个国际 “edu教育邮箱” 的保姆级教程!
前端·后端·程序员
一只叫煤球的猫5 小时前
你真的会用 return 吗?—— 11个值得借鉴的 return 写法
java·后端·代码规范
就叫飞六吧5 小时前
如何判断你的PyTorch是GPU版还是CPU版?
人工智能·pytorch·python
Asthenia04126 小时前
HTTP调用超时与重试问题分析
后端
颇有几分姿色6 小时前
Spring Boot 读取配置文件的几种方式
java·spring boot·后端