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
}
相关推荐
曲幽2 小时前
不止于JWT:用FastAPI的Depends实现细粒度权限控制
python·fastapi·web·jwt·rbac·permission·depends·abac
怕浪猫2 小时前
第21章:微服务与分布式架构中的Go应用
后端·go·编程语言
武子康2 小时前
大数据-239 离线数仓 - 广告业务实战:Flume 导入日志到 HDFS,并完成 Hive ODS/DWD 分层加载
大数据·后端·apache hive
摸鱼的春哥2 小时前
Agent教程15:认识LangChain(中),状态机思维
前端·javascript·后端
风象南9 小时前
我把大脑开源给了AI
人工智能·后端
橙序员小站14 小时前
Agent Skill 是什么?一文讲透 Agent Skill 的设计与实现
前端·后端
怒放吧德德14 小时前
Netty 4.2 入门指南:从概念到第一个程序
java·后端·netty
雨中飘荡的记忆16 小时前
大流量下库存扣减的数据库瓶颈:Redis分片缓存解决方案
java·redis·后端
开心就好202517 小时前
UniApp开发应用多平台上架全流程:H5小程序iOS和Android
后端·ios
悟空码字17 小时前
告别“屎山代码”:AI 代码整洁器让老项目重获新生
后端·aigc·ai编程