背景
WeKnora 是腾讯(Tencent)开发的一套企业级知识库管理系统,主要用于构建和管理基于大语言模型(LLM)的知识增强应用。从代码结构和功能实现来看,这是一个功能完备的知识管理服务平台,具有以下核心特点:
核心功能
-
多源知识导入
- 支持从文件上传(PDF、TXT、DOCX、Markdown等文档格式)
- 支持从URL导入网页内容
- 支持直接输入文本段落
- 支持图片等多模态内容处理
-
智能文档处理
- 文档自动分块(chunking)处理
- 内容摘要自动生成
- 多语言支持
- 高级分块策略配置(分块大小、重叠、分隔符等)
-
多模态处理能力
- 图片内容识别(OCR)
- 图像描述生成(通过VLM视觉语言模型)
- 支持多种图片格式(JPG、PNG、GIF等)
- 多模态内容与文本内容的关联处理
-
知识图谱构建
- 支持GraphRAG(知识图谱增强检索)
- 实体和关系自动提取
- 构建知识间的语义关联
- 支持实体、关系等知识图谱元素的存储和检索
-
向量检索系统
- 集成多种向量数据库引擎
- 支持批量索引和查询
- 基于嵌入模型的语义搜索
- 支持多租户的向量存储隔离
技术架构特点
-
微服务架构
- 模块化设计,如docreader服务专门处理文档解析
- 服务间通过gRPC通信(proto定义)
- 清晰的接口抽象和依赖管理
-
多租户支持
- 租户存储配额管理
- 租户级别的知识隔离
- 租户配置定制化(如检索引擎配置)
-
企业级特性
- 完整的错误处理和日志追踪
- OpenTelemetry支持的分布式追踪
- 请求级别的上下文管理
- 配额管理和资源控制
-
可扩展性设计
- 支持多种嵌入模型配置
- 可插拔的检索引擎架构
- 可配置的分块和处理策略
- 支持自定义摘要生成提示词
模块架构关系图

基础框架
- Web框架:Gin
- 消息队列:Asynq
数据库系统
- PostgreSQL 12+,带有用于向量操作的pgvector扩展
- MySQL 8.0+(替代主数据库)
- Elasticsearch 7.x或8.x用于向量搜索(可选)
AI和ML服务
- 用于本地LLM(大型语言模型)托管的Ollama
- 用于文档文本提取的PaddlePaddle与PaddleOCR
- 用于AI模型执行的Python 3.8+运行时
容器平台
- 用于容器化部署的Docker 20.10+和Docker Compose
- 支持GPU的容器运行时(用于GPU加速)
存储和缓存
- 用于流媒体和会话管理的Redis 6.0+
- MinIO(可选,用于S3兼容的对象存储)
链路跟踪
- Jaeger(Jaeger)是 Uber 开发的开源分布式追踪系统,用于监控和诊断微服务架构中的请求链路。
- OpenTelemetry
前端
- Vue.js,Typescript, TDesign,Pinia, Vite
聊天功能数据流

多模态文档处理

文档向量化处理流程
WeKnora 的文档向量化处理采用完整的异步机制,主要流程包括文档解析、分块处理、向量生成和索引存储四个阶段 。
异步处理机制
系统在创建知识条目时立即采用异步处理模式 knowledge.go:203-206 。无论是文件上传、URL内容还是文本段落,都会启动独立的 goroutine 进行后台处理 knowledge.go:330-332 。

核心处理流程
1. 文档解析阶段
文档首先通过 docReaderClient.ReadFromFile
进行解析和分块 knowledge.go:583-596 。解析过程支持多模态处理,包括 OCR 文字提取和图像描述生成。
2. 分块处理阶段
系统将解析后的内容传递给 processChunks
方法进行向量化处理 knowledge.go:608-610 。该方法负责创建文本块、图像块和实体关系块等不同类型的数据块。

3. 向量生成阶段
通过嵌入模型为每个文本块生成向量表示 knowledge.go:694-700 。系统支持批量向量生成以提高效率 keywords_vector_hybrid_indexer.go:74-85 。

4. 索引存储阶段
生成的向量通过复合检索引擎存储到多个后端系统 composite.go:190-208 。支持 PostgreSQL (pgvector) 和 Elasticsearch 等向量数据库。

并发优化机制
文档解析服务采用多层并发优化:
- 文档级并发:大文档按页面并行处理 docx_parser.py:696-730

- 图像处理并发:使用信号量控制图像处理的并发度 base_parser.py:1248-1267

- 向量化批处理:将内容分批进行向量生成,避免内存溢出 keywords_vector_hybrid_indexer.go:86-97
Notes
整个向量化流程通过 gRPC 协议在不同服务间协调 docreader.proto:8-13 ,确保了系统的模块化和可扩展性。处理状态会实时更新到数据库,用户可以通过 API 查询处理进度。

文档处理模块采用Python语言编写主要基于以下几个技术原因:
Python在文档处理领域的生态优势
文档处理模块需要处理多种格式的文档,包括PDF、Word、图片等,Python在这个领域有丰富的成熟库支持 init.py:4-10 。从代码中可以看到,该模块集成了多个专业的Python库:
- OCR处理:使用PaddleOCR进行图像文字识别 download_deps.py:7
- Word文档处理:使用python-docx库处理DOCX格式 docx_parser.py:9

- PDF处理:使用pypdf库进行PDF文本提取 pdf_parser.py:6
- 图像处理:使用PIL进行图像操作 docx_parser.py:8
多模态AI处理能力
文档处理模块需要支持多模态内容处理,包括文本提取、图像识别和OCR功能 README.md:70 。Python在AI/ML领域的生态系统非常成熟,特别是在深度学习框架和预训练模型的支持方面 Dockerfile.docreader:36-46 。
并发处理和性能优化
尽管Python在性能上有一定限制,但文档处理模块通过多进程并行处理来解决这个问题 docx_parser.py:839-851 。代码显示使用了ProcessPoolExecutor实现真正的多核并行化处理 docx_parser.py:1262-1287 。
微服务架构的适配性
文档处理模块作为独立的微服务运行,通过gRPC接口提供服务 docreader_pb2_grpc.py:8-11 。Python的gRPC支持和容器化部署能力使其很适合这种架构模式 server.py:15-17 。
Notes
文档处理模块是WeKnora系统中的一个独立服务,专门负责各种文档格式的解析和内容提取。选择Python主要是因为其在文档处理、OCR、图像处理等领域有丰富的第三方库支持,能够快速集成各种AI模型和处理工具。虽然Python在执行速度上不如编译型语言,但通过多进程并行处理和合理的架构设计,能够满足文档处理的性能需求。
消息队列的使用场景
1. 异步任务处理
WeKnora使用了Asynq作为异步任务队列系统 config.go:21 。这主要用于处理文档解析、向量化等耗时操作,避免阻塞用户请求。
2. 流式响应管理
系统实现了基于Redis的流式管理器来处理实时消息传输 config.go:24 。在知识问答场景中,系统会注册流式响应通道来处理LLM的实时输出 session.go:760-764 。
3. 服务器端事件流(SSE)
WeKnora采用Server-Sent Events机制来实现实时的问答交互 API.md:1203 。当用户提问时,系统会通过SSE推送知识检索结果和LLM生成的回答 session.go:767-777 。
架构中的消息流转
在RAG流水线中,消息队列主要支持以下场景:
- 文档处理流水线:文档上传后通过异步队列进行OCR、文本提取、向量化等处理
- 实时问答交互:用户查询通过流式管道实现增量式回答生成
- 多轮对话管理:会话状态和消息历史通过Redis缓存和流式管理器维护
Notes
WeKnora的消息队列设计主要围绕异步处理和实时交互两个核心需求。系统使用Asynq处理后台任务,Redis Stream管理实时消息流,SSE提供前端实时更新能力。这种设计确保了文档处理的高效性和用户交互的流畅性。
架构选择的核心考虑
-
轻量级部署需求
WeKnora设计为可以快速部署的文档理解框架,使用Docker Compose即可一键启动 docker-compose.yml:152-162 。引入Kafka会增加系统复杂度和资源消耗,不符合轻量化部署的目标。
-
实时性优先的场景特点
系统主要处理两类消息流:
异步任务队列:使用Asynq基于Redis实现 asyncq.go:14-26 ,支持文档处理等后台任务的优先级队列 asyncq.go:54-58 。
实时流式响应:采用Redis Stream + SSE机制 redis_manager.go:25-30 ,直接支持LLM流式输出的实时传输 session.go:224-247 。
- 简化的消息模式
WeKnora的消息场景相对简单:
文档处理任务的异步执行
问答会话的流式响应传输
会话状态的临时存储
这些场景不需要Kafka的复杂特性如分区、副本、持久化等。
- 统一的存储后端
系统统一使用Redis作为缓存、队列和流式管理的后端 factory.go:17-35 ,减少了基础设施的复杂性,便于运维管理。
技术实现对比
相比Kafka方案,当前架构的优势:
部署简单:只需Redis一个中间件
延迟更低:直接基于Redis的内存操作
资源占用小:适合中小规模部署
开发复杂度低:API更简洁直观
MinIO的业务场景
MinIO在WeKnora系统中主要用于文件存储后端,作为本地文件系统存储的替代方案 .env.example:21-22 。系统支持三种存储类型:本地存储(local)、MinIO对象存储(minio)和腾讯云COS(cos) container.go:201-237 。
具体功能实现
MinIO服务通过minioFileService
结构体实现,提供以下核心功能 minio.go:16-20 :
- 文件上传:将用户上传的文档保存到MinIO存储桶中 minio.go:53-78
- 文件下载:从MinIO获取存储的文档文件 minio.go:80-101
- 文件删除:删除不再需要的文档文件 minio.go:103-126
部署配置
当配置文件中STORAGE_TYPE=minio
时,系统会自动启动MinIO服务 start_all.sh:241-252 。需要配置以下环境变量 .env.example:80-91 :
MINIO_ENDPOINT
:MinIO服务器地址MINIO_ACCESS_KEY_ID
:访问密钥MINIO_SECRET_ACCESS_KEY
:密钥MINIO_BUCKET_NAME
:存储桶名称
在RAG系统中的作用
在WeKnora的RAG(检索增强生成)架构中,MinIO负责存储用户上传的原始文档文件,这些文件经过处理后会被分块、向量化并存储到向量数据库中用于检索 knowledge.go:405-408 。
Jaeger模块的业务场景和部署必要性
Jaeger模块在WeKnora中主要用于分布式链路追踪,帮助监控和调试微服务架构中的请求流转过程。
业务场景
Jaeger在WeKnora中的主要业务场景包括:
- 请求链路追踪:跟踪用户请求在各个微服务间的完整调用链路,包括文档处理、向量检索、LLM推理等服务 init.go:41-52
- 性能监控:监控各个服务的响应时间和性能瓶颈,特别是在RAG流水线中的文档解析、向量化处理、检索和生成等关键环节
- 故障排查:当系统出现问题时,通过链路追踪快速定位故障发生的具体服务和环节
是否可以省去部署
可以省去部署,但会失去重要的监控能力。
从配置来看,Jaeger的启用是可选的:
- 在
docker-compose.yml
中,Jaeger作为独立服务运行在端口16686 docker-compose.yml:107-126 - 应用通过环境变量
OTEL_EXPORTER_OTLP_ENDPOINT
连接到Jaeger服务 docker-compose.yml:23-28 - 如果没有设置OTLP端点,系统会降级到标准输出模式 init.go:53-59
省去部署的影响:
- ✅ 系统核心功能(文档理解、检索、问答)不受影响
- ❌ 失去分布式追踪能力,难以监控性能和排查问题
- ❌ 在生产环境中缺乏重要的可观测性工具
启动脚本显示Jaeger作为标准服务之一提供Web界面访问 start_all.sh:543-547 。
Notes
Jaeger是WeKnora架构中的可观测性组件,主要用于开发和运维阶段的监控调试。对于简单的测试或演示环境可以省去,但在生产环境中建议保留以确保系统的可维护性。
通过环境变量配置模型
在 .env
文件中,您可以配置不同类型的模型: .env.example:124-172
LLM 模型配置:
INIT_LLM_MODEL_NAME
- 模型名称INIT_LLM_MODEL_BASE_URL
- 模型访问地址(可选)INIT_LLM_MODEL_API_KEY
- API 密钥(可选)
Embedding 模型配置:
INIT_EMBEDDING_MODEL_NAME
- 嵌入模型名称INIT_EMBEDDING_MODEL_DIMENSION
- 向量维度INIT_EMBEDDING_MODEL_ID
- 模型标识符
Rerank 模型配置:
INIT_RERANK_MODEL_NAME
- 重排模型名称INIT_RERANK_MODEL_BASE_URL
- 访问地址INIT_RERANK_MODEL_API_KEY
- API 密钥
通过 API 接口创建模型
使用 REST API 创建自定义模型: API.md:664-676
支持的模型类型包括:
LLM
- 大语言模型EMBEDDING
- 嵌入模型RERANK
- 重排模型
模型来源支持:
OPENAI
- OpenAI APILOCAL
- 本地模型(如 Ollama)REMOTE
- 其他远程 API
通过 Go SDK 创建模型
使用 Go 客户端 SDK 创建模型: example.go:84-101
SDK 支持的模型类型常量: model.go:66-77
系统内部模型管理
系统内部通过 ModelService
接口管理模型: model.go:13-30
模型数据结构定义: model.go:48-74
系统启动时会自动初始化配置的模型: test_data.go:388-443
Notes
WeKnora 的模型配置系统非常灵活,支持本地模型(通过 Ollama)和远程 API 模型。您可以根据需要选择最适合的配置方式。对于生产环境,建议使用 API 接口或 SDK 进行动态模型管理,而环境变量配置更适合初始化默认模型。
代码走查
我们抽取一个业务逻辑serivice代码,knowledge.go这个文件1744行,让Qwen3帮我们走查下
代码质量一般:
Code Review: 知识库服务代码分析
硬编码(Hard Code)问题
1. 文件类型硬编码
代码中存在多处文件类型硬编码,这限制了系统的可扩展性:
go// 在isValidFileType函数中 switch strings.ToLower(getFileType(filename)) { case "pdf", "txt", "docx", "doc", "md", "markdown", "png", "jpg", "jpeg", "gif": return true default: return false } // 在IsImageType函数中 switch fileType { case "jpg", "jpeg", "png", "gif", "webp", "bmp", "svg", "tiff": return true default: return false }
问题:
- 文件类型列表直接写死在代码中,无法通过配置动态扩展
- 当需要支持新文件类型时,必须修改代码并重新部署
建议:
- 将支持的文件类型列表移到配置文件中
- 通过服务初始化时加载配置
2. 存储提供商硬编码
存储提供商的处理逻辑也是硬编码的:
goswitch kb.StorageConfig.Provider { case "cos": // COS配置检查 case "minio": // MinIO配置检查 }
问题:
- 仅支持"cos"和"minio"两种存储提供商
- 添加新的存储提供商需要修改核心业务逻辑
建议:
- 实现存储提供商接口,通过工厂模式动态创建
- 配置中指定存储提供商类型,而不是在代码中硬编码判断
3. 魔法数字问题
代码中存在多处"魔法数字",缺乏可读性:
go// 在processChunks函数中 chunk.ChunkIndex = maxSeq + i*100 + 1 // 100, 1是魔法数字 chunk.ChunkIndex = maxSeq + i*100 + 2 // 100, 2是魔法数字 sChunk.ChunkIndex = maxSeq + 3 // 3是魔法数字 // 其他地方 if chunk.EndAt > 4096 { break }
问题:
- 数字常量没有解释其含义
- 修改时容易出错,难以理解设计意图
建议:
- 定义有意义的常量替代魔法数字
- 例如:
const ChunkIndexOffset = 100
,const MaxContentLength = 4096
4. 环境变量直接检查
环境变量检查直接嵌入业务逻辑:
goenableGraphRAG := os.Getenv("ENABLE_GRAPH_RAG") == "true"
问题:
- 与配置系统耦合,不利于测试
- 环境变量检查分散在代码各处
建议:
- 在配置服务中统一处理环境变量
- 通过配置对象提供功能开关
其他代码质量问题
1. 函数过长且职责不单一
processChunks
函数超过200行,承担了过多职责:
- 处理文本和图片chunk
- 生成摘要
- 创建索引
- 更新知识状态
- 调整租户存储使用
建议 : 拆分为多个单一职责的函数,如
processTextChunks
,processImageChunks
,generateSummary
等2. 重复代码
DeleteKnowledge
和DeleteKnowledgeList
函数中有大量重复逻辑,特别是在错误处理和资源清理方面。建议 : 提取公共逻辑到辅助函数,如
deleteKnowledgeResources
3. 错误处理不一致
有些错误被记录但没有返回,有些则直接返回:
goif err := s.repo.UpdateKnowledge(ctx, knowledge); err != nil { span.RecordError(err) return } // vs if err != nil { logger.GetLogger(ctx).WithField("error", err).Errorf("...") span.RecordError(err) return }
建议: 统一错误处理策略,确保所有错误都被适当记录和处理
4. 代码风格不一致
- 有些注释是中文,有些是英文
- 错误日志记录方式不统一(
logger.ErrorWithFields
vslogger.GetLogger(ctx).WithField...Errorf
)建议: 统一日志记录方式和注释语言
5. 可测试性问题
- 直接使用全局配置和外部服务
- 缺乏接口抽象,难以进行单元测试
建议:
- 使用接口替代具体实现
- 通过依赖注入提供可测试的mock对象
6. 潜在性能问题
- 大文件处理时使用
io.ReadAll(f)
可能导致内存问题- 大量数据库操作未考虑批处理优化
建议:
- 对大文件使用流式处理
- 优化数据库批量操作
改进建议总结
消除硬编码 :
将文件类型、存储提供商等配置移到配置文件中
用有意义的常量替代魔法数字
重构代码结构 :
拆分长函数为单一职责的小函数
提取重复代码到公共函数
统一错误处理策略
提高可测试性 :
使用接口抽象外部依赖
通过依赖注入提供可测试性
优化性能 :
改进大文件处理方式
优化数据库批量操作
统一代码风格 :
统一日志记录方式
统一注释语言
这些改进将使代码更加灵活、可维护、可测试,同时减少未来扩展时的代码修改量。特别是消除硬编码问题,将使系统更容易适应未来的需求变化,无需修改核心业务逻辑即可支持新的文件类型或存储提供商。
安全
WeKnora 支持使用 AES 加密对租户数据进行加密。这点对信创不太好,需要改造支持国密算法。

配置安全管理
系统通过环境变量和配置文件管理敏感信息 config.go:14-25 ,包括数据库连接、租户配置等关键参数,避免硬编码敏感信息。

API密钥
WeKnora使用AES-GCM加密算法生成安全的API密钥 tenant.go:217-247 。密钥生成过程包括:
租户ID加密:将租户ID转换为字节数组,使用AES-GCM加密 tenant.go:219-239
随机nonce生成:每次生成12字节的随机nonce确保密钥唯一性 tenant.go:229-232
Base64编码:将nonce和密文组合后进行Base64编码 tenant.go:242-246
生成的API密钥格式为sk-{encrypted_part},其中encrypted_part包含了加密的租户ID信息。

请求拦截与验证流程
跳过无需认证的API:系统定义了无需认证的API列表,如创建租户接口 auth.go:17-30
提取API密钥:从HTTP请求头X-API-Key中获取API密钥 auth.go:47-53
解析租户ID:调用ExtractTenantIDFromAPIKey解析密钥中的租户ID auth.go:55-63
数据库验证:查询数据库验证API密钥的有效性 auth.go:65-82

前端集成
前端通过Axios拦截器自动添加API密钥到请求头 request.ts:77-80 ,支持从本地存储或测试数据中获取密钥 request.ts:54-59 。
认证机制缺陷
缺乏速率限制
认证中间件没有实现速率限制 auth.go:33-95 ,容易受到暴力破解攻击。攻击者可以无限制地尝试不同的API密钥。
无会话管理
系统采用无状态的API密钥认证,缺乏会话管理和令牌撤销机制。一旦API密钥被盗用,无法立即阻止未授权访问。
权限控制粗粒度
认证中间件只验证API密钥的有效性 auth.go:76-82 ,缺乏细粒度的权限控制。所有通过认证的请求都具有相同的访问权限。
数据隔离缺陷
上下文依赖风险
租户隔离严重依赖上下文中的租户ID chunk.go:110-111 。如果上下文被污染或篡改,可能导致跨租户数据访问。

缺乏数据访问审计
系统没有实现完整的数据访问审计机制,难以追踪和检测潜在的数据泄露或未授权访问。
输入验证缺陷
预处理不够全面
查询预处理插件虽然清理了一些常见的输入 preprocess.go:24-29 ,但缺乏对SQL注入、XSS等攻击的防护。

我们看看仅仅用正则匹配
缺乏输入长度限制
系统没有对输入数据的长度进行严格限制,可能导致DoS攻击或缓冲区溢出。
配置安全缺陷
敏感信息暴露
Docker配置文件中直接暴露了多个敏感的环境变量 docker-compose.yml:48-59 ,包括数据库密码、API密钥等,存在配置泄露风险。
缺乏配置加密
所有配置信息都以明文形式存储,没有实现配置文件加密或安全的密钥管理。
前端安全缺陷
客户端密钥存储
前端代码将API密钥存储在本地 request.ts:55-59 ,容易被恶意脚本或浏览器扩展获取。
缺乏CSRF保护
HTTP请求配置中没有实现CSRF令牌保护,可能受到跨站请求伪造攻击。
多租户设计
数据库层面的租户隔离
WeKnora在所有核心数据表中都包含tenant_id
字段来实现逻辑隔离 00-init-db.sql:9-22 。
每个租户的数据通过租户ID进行严格分离:
- 租户表:存储租户基本信息和API密钥 00-init-db.sql:9-22

- 知识库表 :通过
tenant_id
关联到特定租户 00-init-db.sql:49-61

- 知识表:包含租户ID确保知识内容隔离 00-init-db.sql:67-90
- 分块表:文档分块也按租户ID隔离 00-init-db.sql:124-148
同时我们看到数据库字段注释并不全面
租户数据结构设计
租户实体包含完整的隔离配置信息 tenant.go:11-37 :
- 存储配额管理:每个租户有独立的存储配额和使用量统计
- 检索引擎配置:支持租户级别的检索引擎参数定制 tenant.go:39-41
- API密钥认证:每个租户拥有唯一的加密API密钥
数据访问隔离实现
所有数据访问操作都通过租户ID进行过滤 knowledge.go:31-39 :
- 查询隔离 :所有数据库查询都包含
tenant_id
条件 - 分页查询:支持租户级别的分页数据访问 knowledge.go:54-82

- 批量操作:跨租户数据比较操作也严格按租户ID隔离 knowledge.go:170-187

还有SQL语句拼接,字段写死
上下文传递机制
系统通过上下文传递租户信息确保请求级别的隔离 message.go:46-52 :
- 租户ID注入:认证中间件将租户ID注入到请求上下文
- 服务层验证:所有服务层操作都从上下文获取租户ID进行验证
- 跨服务调用:评估服务等跨服务调用也严格检查租户ID匹配 evaluation.go:111-124
存储配额管理
系统实现了租户级别的存储配额控制 tenant.go:65-82 :
- 并发安全:使用悲观锁确保存储使用量更新的并发安全
- 配额检查:防止存储使用量出现负值的业务规则验证
- 动态调整:支持增量调整租户存储使用量
安全性缺陷
1. 单点失效风险
API Key 一旦泄露,攻击者即可获得完整的租户访问权限 auth.go:76-82 。系统没有实现细粒度的权限控制,所有操作都依赖同一个 API Key。
2. 缺乏会话管理
与传统的用户名/密码+会话机制不同,API Key 是长期有效的静态凭证 tenant.go:217-247 。没有会话过期、刷新令牌等安全机制。
3. 密钥轮换困难
虽然系统提供了 UpdateAPIKey
功能 tenant.go:184-215 ,但密钥更新需要手动操作,无法实现自动轮换,增加了管理负担。
数据库设计
E-R图

TENANTS: 租户实体,系统的顶层隔离单位 00-init-db.sql:9-22
MODELS: 模型配置实体,支持多种AI模型类型 00-init-db.sql:29-42
KNOWLEDGE_BASES: 知识库实体,知识管理的容器 00-init-db.sql:49-61
KNOWLEDGES: 知识实体,具体的文档或内容 00-init-db.sql:67-90
CHUNKS: 分块实体,文档的最小处理单位 00-init-db.sql:144-164
SESSIONS: 会话实体,用户交互的上下文 00-init-db.sql:99-120
MESSAGES: 消息实体,会话中的具体对话 00-init-db.sql:127-138
EMBEDDINGS: 向量嵌入实体,支持语义检索 00-init-db.sql:170-183
详细




总结
该项目属于微服务架构,涉及Golang与Python混合架构,也有Grpc通讯技术栈,模块划分还算清晰。但代码质量一般,大规模部署还有待性能测试。
支持多个不同类型LLM集成方式(本地与云端)。不支持信创体系,则政务类环境还需要改造。总体中规中矩,可以做为RAG项目进行学习与参考。