版本升级/4.15.x
V4.15.0-beta4
FastGPT V4.15.0-beta4 更新说明
📦 升级指南
‼️重要更新,插件服务更新到 v1.0.0-beta1 版本,系统工具运行方式有较大调整。
1. 修改环境变量
- 修改
fastgpt-plugin的环境变量AUTH_TOKEN,要求 32 位以上。 - 同时修改
fastgpt的环境变量PLUGIN_TOKEN,与fastgpt-plugin的AUTH_TOKEN一致。 - 修改
fastgpt-plugin的环境变量MONGODB_URI中的数据库名,不与fastgpt的 Mongo 数据库名重名即可,例如:mongodb://myusername:mypassword@fastgpt-mongo:27017/fastgpt-plugin?authSource=admin
2. 镜像变更
- 更新 fastgpt-app(fastgpt 主服务) 镜像 tag: v4.15.0-beta4
- 更新 fastgpt-pro(fastgpt 商业版) 镜像 tag: v4.15.0-beta4
- 更新 fastgpt-plugin 镜像 tag: v1.0.0-beta2
- 更新 aiproxy 镜像 tag: v0.6.1
3. 重装系统工具
- 下载所有系统工具的 zip 包
- 打开
fastgpt网页 - 点击管理员navbar - 点击添加插件 - 点击导入/更新插件- 上传 zip - 确认。即可重装旧的所有系统工具。
也可以打开插件市场逐个下载,正式版之前,插件市场地址为: https://v2.marketplace.fastgpt.cn
🚀 新增内容
- 重写插件系统架构。
- 重写 chatbox ui。
- 应用/知识库增加虚拟列表渲染。
- 增加单独的 openapi 文档,区分 devapi 文档。
- 导出工作流模板,同时导出名字和介绍。
⚙️ 优化
- 系统工具运行迁移到 local-pool,支持进程池、队列、超时、重试退避和运行指标。
- 支持插件级 runtime config。
- 插件运行入口支持从对象存储拉取,并缓存到本地文件目录。
- 输入引导配置增加校验,避免错误配置了自定义词库地址。
- 工作流数组引用类型增强校验,避免刚好与二维数据冲突。
- 知识库被删除后,应用编排时优雅提示。
- PDF 解析,将 PDFJs 替换成
liteparse,速度提高 3 倍。 - 工作流运行,nodeResponse 扁平化存储优化,避免大的嵌套工作流保存失败。
- xlsx 解析,自动去除空行空列,补充合并单元格。
🐛 修复
- 模型获取多模态文件链接异常。
- 修复 training 接口存在的潜在越权风险。
- HTTP tool parse 的 SSRF 风险。
- 交互节点后的工具调用,展开 MCP 工具异常。
🛠️ 代码优化
- 插件服务从旧
runtime结构调整为 pnpm workspace monorepo,拆分为 HTTP 服务入口、领域模型、用例、API adapter、基础设施、SDK 和 CLI。 - 将 app API 接口全部用 zod schema 编写并生成文档。
- 及时处理 worker 内图片,不再存留 base64,降低内存消耗。
FAQ
如果出现 Mongo sync index error,并且是提示:
modelName: 'chat_item_responses',
error: MongoServerError: Index build failed: a45ca5d9-8429-4602-80bc-312a22b92687: Collection fastgpt.chat_item_responses ( f2a417fe-6e7c-4ec2-8947-d8003755462f ) :: caused by :: E11000 duplicate key error collection: fastgpt.chat_item_responses index: appId_1_chatId_1_chatItemDataId_1_data.id_1 dup key: { appId: ObjectId('69c9110790de745f57e93919'), chatId: "jzt1JCcaoy4kC67hdhQlzUob", chatItemDataId: "xa0f1AMFr9CWj9uVC9TqGtc0", data.id: "jTUTS9j67N4CyZ17" }可以进入 mongo shell,执行以下命令来清楚重复的数据。这里仅删除对话中每个节点的运行详情结果,无风险,可能是过去某次出现异常导致重复。
use fastgpt
const dryRun = true; // 确认后改成 false
const batchSize = 500;
const col = db.chat_item_responses;
let duplicateGroups = 0;
let duplicateDocs = 0;
let removableDocs = 0;
let deletedDocs = 0;
let pendingIds = [];
function flushDeletes() {
if (pendingIds.length === 0) return;
const ids = pendingIds;
pendingIds = [];
if (dryRun) {
print(`[dry-run] would delete ${ids.length} old docs`);
return;
}
const res = col.deleteMany({ _id: { $in: ids } });
deletedDocs += res.deletedCount;
print(`[delete] deleted ${res.deletedCount} old docs, total=${deletedDocs}`);
}
const cursor = col.aggregate(
[
{
$group: {
_id: {
appId: "$appId",
chatId: "$chatId",
chatItemDataId: "$chatItemDataId",
dataId: "$data.id"
},
count: { $sum: 1 }
}
},
{ $match: { count: { $gt: 1 } } },
{ $sort: { count: -1 } }
],
{ allowDiskUse: true }
);
while (cursor.hasNext()) {
const group = cursor.next();
duplicateGroups += 1;
duplicateDocs += group.count;
removableDocs += group.count - 1;
const key = group._id;
const filter = {
appId: key.appId,
chatId: key.chatId,
chatItemDataId: key.chatItemDataId,
"data.id": key.dataId
};
const docs = col
.find(filter, { _id: 1, time: 1 })
.sort({ _id: -1 })
.toArray();
const keepId = docs[0]?._id;
const removeIds = docs.slice(1).map((doc) => doc._id);
printjson({
group: duplicateGroups,
count: group.count,
keepId,
removeIds
});
pendingIds.push(...removeIds);
if (pendingIds.length >= batchSize) {
flushDeletes();
}
}
flushDeletes();
printjson({
dryRun,
duplicateGroups,
duplicateDocs,
removableDocs,
deletedDocs
});