Commit 602e4306 by Archer Committed by GitHub

perf: node type adapt (#7054)

* fix: node type

* fix: node type

* doc

* fix: modal style

* test: isolate top agent resource mocks

* document

* rename agents folder
parent ee979434
...@@ -280,7 +280,7 @@ ChatItem ...@@ -280,7 +280,7 @@ ChatItem
设计文档: 设计文档:
- `.codex/design/bug/stream-resume-form-input-file-list.md` - `.agent/design/bug/stream-resume-form-input-file-list.md`
- 新增本次问题的设计说明、根因、方案、测试和 TODO。 - 新增本次问题的设计说明、根因、方案、测试和 TODO。
### 数据合并与恢复工具 ### 数据合并与恢复工具
......
# CLAUDE.md
本文件为 Claude Code (claude.ai/code) 在本仓库中工作时提供指导说明。
## 项目概述
FastGPT 是一个 AI Agent 构建平台,通过 Flow 提供开箱即用的数据处理、模型调用能力和可视化工作流编排。这是一个基于 NextJS 构建的全栈 TypeScript 应用,后端使用 MongoDB/PostgreSQL。
**技术栈**: NextJS + TypeScript + ChakraUI + MongoDB + PostgreSQL (PG Vector)/Milvus
## 设计文档
你可以参考 [项目设计文档](./design/) 来了解 FastGPT 已有的设计方案。
## 架构
这是一个使用 pnpm workspaces 的 monorepo,主要结构如下:
### Packages (库代码)
- `packages/global/` - 所有项目共享的类型、常量、工具函数
- `packages/service/` - 后端服务、数据库模型、API 控制器、工作流引擎
- `packages/web/` - 共享的前端组件、hooks、样式、国际化
### Projects (应用程序)
- `projects/app/` - 主 NextJS Web 应用(前端 + API 路由)
- `projects/code-sandbox/` - Bun + Hono 代码执行沙箱服务
- `projects/mcp_server/` - Model Context Protocol 服务器实现
### 关键目录
- `document/` - 文档站点(NextJS 应用及内容)
- `plugins/` - 外部插件(模型、爬虫等)
- `deploy/` - Docker 和 Helm 部署配置
- `test/` - 集中的测试文件和工具
## 开发命令
### 项目专用命令
**主应用 (projects/app/)**:
- `cd projects/app && pnpm dev` - 启动 NextJS 开发服务器
- `cd projects/app && pnpm build` - 构建 NextJS 应用
- `cd projects/app && pnpm start` - 启动生产服务器
**代码沙箱 (projects/code-sandbox/)**:
- `cd projects/code-sandbox && pnpm dev` - 以监视模式启动(Bun)
- `cd projects/code-sandbox && pnpm build` - 构建沙箱服务
- `cd projects/code-sandbox && pnpm test` - 运行 Vitest 测试
**MCP 服务器 (projects/mcp_server/)**:
- `cd projects/mcp_server && bun dev` - 使用 Bun 以监视模式启动
- `cd projects/mcp_server && bun build` - 构建 MCP 服务器
- `cd projects/mcp_server && bun start` - 启动 MCP 服务器
### 工具命令
- `pnpm lint` - 对所有 TypeScript 文件运行 ESLint 并自动修复
- `pnpm initIcon` - 初始化图标资源
- `pnpm gen:theme-typings` - 生成 Chakra UI 主题类型定义
## 测试
项目使用 Vitest 进行测试并生成覆盖率报告。主要测试命令:
- `pnpm test` - 运行所有测试
- `pnpm test {file-path}` - 使用 Vitest 运行指定测试文件的指定测试
- 测试文件位于 `test/` 目录和 `projects/{{name}}/test/`,代表这`packages``单个 project`的测试文件目录。
- 覆盖率报告生成在 `coverage/` 目录
## 代码组织模式
### Monorepo 结构
- 共享代码存放在 `packages/` 中,通过 workspace 引用导入
- `projects/` 中的每个项目都是独立的应用程序
- 使用 `@fastgpt/global``@fastgpt/service``@fastgpt/web` 导入共享包
### API 结构
- NextJS API 路由在 `projects/app/src/pages/api/`
- API 路由合约定义在`packages/global/openapi/`, 对应的
- 通用服务端业务逻辑在 `packages/service/``projects/app/src/service`
- 数据库模型在 `packages/service/` 中,使用 MongoDB/Mongoose
### 前端架构
- React 组件在 `projects/app/src/components/``packages/web/components/`
- 使用 Chakra UI 进行样式设计,自定义主题在 `packages/web/styles/theme.ts`
- 国际化支持文件在 `packages/web/i18n/`
- 使用 React Context 和 Zustand 进行状态管理
## 开发注意事项
- **包管理器**: 使用 pnpm 及 workspace 配置
- **Node 版本**: 需要 Node.js >=20.x, pnpm >=9.x
- **数据库**: 支持 MongoDB、带 pgvector 的 PostgreSQL 或 Milvus 向量存储
- **AI 集成**: 通过统一接口支持多个 AI 提供商
- **国际化**: 完整支持中文、英文和日文
## 关键文件模式
- `.ts``.tsx` 文件全部使用 TypeScript
- 数据库模型使用 Mongoose 配合 TypeScript
- API 路由遵循 NextJS 约定
- 组件文件使用 React 函数式组件和 hooks
- 共享类型定义在 `packages/global/`
## 环境配置
- 配置文件在 `projects/app/data/config.json`
- 支持特定环境配置
- 模型配置在 `packages/service/core/ai/config/`
## 代码规范
[FastGPT 代码规范](./skills/system-pr_review/style/syntax.md)
## 运行要求
### 性格
1. 保持怀疑态度,要深入思考和分析现有代码,提出问题,并让用户确认。
2. 编写单个需求时,运行测试命令,中途不要运行全量测试,只需局部测试即可,只需最后运行全量测试,确保没有问题。
### 工作流程
对于简单任务,可以直接进行编写实现,对于复杂任务,遵循以下流程:
function agent_loop(用户需求){
// 1. 需求文档编写
while(需求文档编写未完成){
用户需求分析
编写需求分析文档;
提出问题,让用户提供答案;
调整需求文档;
}
// 2. 开发文档编写
while(开发文档编写未完成){
编写开发文档;
提出问题,让用户提供答案;
调整开发文档;
}
// 3. 列出 TODO
while(TODO 列表编写未完成){
编写 TODO 列表; // 包含写代码,运行测试等,需要与开发文档对应
提出问题,让用户提供答案;
调整 TODO 列表;
}
// 4. 执行 TODO List
while(TODO List 执行未完成){
执行 TODO List;
更新 TODO List 状态;
}
}
### 输出规范
1. 输出语言:中文
2. 输出文档位置:
2.1. 设计文档:.claude/design,todo 跟在设计文档后面。
2.2. 问题分析文档: .claude/issue
3. 相同需求文档,尽量写在一起(内容超过 300 行,可以分批写入),或者创建要给目录一起管理,不要随意平铺一堆不同版本的相同问题的文档。
4. 文件输出,使用正确的编码格式,例如UTF-8。
5. 除非用户指明,否则不要编写总结报告。
\ No newline at end of file
# 功能开发文档
# 功能开发文档
## 文档标识
- 任务前缀:`md-encoding-repair`
- 文档文件名:`md-encoding-repair-功能开发文档.md`
- 文档状态:已补全(实施完成版)
- 最后更新:2026-04-15
## 0. 开发目标与约束
- 功能目标:修复“Markdown 文件前部英文导致编码误判为 `ascii`,从而中文乱码”的问题。
- 核心策略:`UTF-8 严格校验优先(BOM + 字节级合法性校验)`,失败后再进入探测回退。
- 代码范围:
- `FastGPT/packages/global/common/file/tools.ts`
- `FastGPT/packages/service/worker/readFile/extension/rawText.ts`
- `FastGPT/test/cases/global/common/file/tool.test.ts`
- `FastGPT/test/cases/service/common/file/read/encoding.regression.test.ts`
- 非目标(明确不做):API 协议、DB schema、前端交互改造。
- 必须遵循规范:`/Users/xxyyh/.codex/skills/fastgpt-requirement-design/references/style-standards-entry.md`
- 适用维度(从需求分析继承):API[ ] DB[ ] Front[ ] Logger[ ] Package[x]
## 1. 实施任务拆解(执行结果)
| 任务ID | 任务名称 | 责任层 | 执行结果 | 完成定义(DoD) | 状态 |
|---|---|---|---|---|---|
| T1 | 重构编码检测策略 | Service(global utils) | `detectFileEncoding` 改为 BOM + UTF-8 严格校验优先;失败再 fallback 探测 | 不再依赖“仅前 200 字节” | ✅ 已完成 |
| T2 | 增加 `ascii` 误判兜底 | Service(global + worker) | 检测层与解码层都对 `ascii` + 非 ASCII 字节做兜底 | 不再按错误 `ascii` 解码中文 | ✅ 已完成 |
| T3 | 补充/修正单测 | Test | 增加 BOM、长英文前缀+中文正文等用例 | 新场景稳定通过 | ✅ 已完成 |
| T4 | 解码层轻量保护落地 | Service(worker) | `readFileRawText` 增加 `ascii` 误判兜底,并复用全局 `hasNonAsciiByte` | 上游误判时仍输出可读文本 | ✅ 已完成 |
| T5 | 编码回归矩阵补齐 | Test | 新增 `encoding.regression.test.ts` 覆盖 4 类编码回归场景 | 回归矩阵全通过 | ✅ 已完成 |
## 2. 文件级改动清单
| 文件路径 | 改动类型 | 变更摘要 | 关联任务ID |
|---|---|---|---|
| `FastGPT/packages/global/common/file/tools.ts` | 修改 | 新增 `hasUtf8Bom``isValidUtf8``getDetectSample`、导出 `hasNonAsciiByte``detectFileEncoding` 改为验证优先策略 | T1,T2 |
| `FastGPT/packages/service/worker/readFile/extension/rawText.ts` | 修改 | 删除本地重复 `hasNonAsciiByte`,改为复用全局工具;`ascii` + 非 ASCII 字节时改走 UTF-8 解码 | T2,T4 |
| `FastGPT/test/cases/global/common/file/tool.test.ts` | 修改 | 新增 UTF-8 BOM 测试;新增“长英文前缀 + 中文正文”测试;替换旧弱断言用例语义 | T3 |
| `FastGPT/test/cases/service/common/file/read/encoding.regression.test.ts` | 新增 | 新增编码回归矩阵(UTF-8 混排、ASCII、ascii 误传、非法 UTF-8) | T5 |
## 3. 后端实施说明
### 3.1 API 改动
| 路由 | 方法 | 请求参数 | 响应结构 | 鉴权 | 错误处理 |
|---|---|---|---|---|---|
| N/A | N/A | N/A | N/A | N/A | N/A |
说明:本需求仅涉及文件编码判定与解码策略,不改 API 合约。
### 3.2 Service/Core 改动
| 模块 | 函数/类型 | 具体改动 | 依赖关系 |
|---|---|---|---|
| `packages/global/common/file/tools.ts` | `detectFileEncoding` | 判定顺序改为:`BOM -> strict UTF-8 validate -> jschardet fallback`;对 `ascii` + 非 ASCII 字节做兜底 | 仅使用现有 `jschardet`,未新增依赖 |
| `packages/global/common/file/tools.ts` | `hasNonAsciiByte` | 由私有函数改为导出,供多处复用 | 复用方为 service worker 解码逻辑 |
| `packages/service/worker/readFile/extension/rawText.ts` | `readFileRawText` | 新增 `normalizedEncoding`,在 `encoding='ascii'` 且检测到非 ASCII 字节时强制按 UTF-8 解码 | 复用 `@fastgpt/global/common/file/tools` |
### 3.3 数据层改动
| 集合/表 | 字段 | 类型 | 必填 | 默认值 | 索引 | 迁移策略 |
|---|---|---|---|---|---|---|
| N/A | N/A | N/A | N/A | N/A | N/A | 无需迁移 |
## 4. 前端实施说明
| 页面/组件 | 文件路径 | 交互变化 | i18n 改动 | 状态覆盖 |
|---|---|---|---|---|
| N/A | N/A | 无 | 无 | N/A |
## 5. 日志与可观测性
| 触发点 | 日志级别 | category | 字段 | 备注 |
|---|---|---|---|---|
| 本期无新增日志点 | N/A | N/A | N/A | 为最小改动未加新日志,后续可按需要加 debug 观测 |
## 6. 测试与验证
### 6.1 自动化测试清单(已执行)
| 测试文件 | 覆盖重点 | 结果 |
|---|---|---|
| `test/cases/global/common/file/tool.test.ts` | 编码检测基础能力(UTF-8、ASCII、BOM、混排场景) | ✅ 通过 |
| `test/cases/service/common/file/read/utils.test.ts` | 文件读取主链路(readFileContentByBuffer) | ✅ 通过 |
| `test/cases/service/common/file/gridfs/utils.test.ts` | 预览流编码链路(stream2Encoding) | ✅ 通过 |
| `test/cases/service/common/file/read/encoding.regression.test.ts` | 编码回归矩阵(4 场景) | ✅ 通过 |
### 6.2 回归矩阵(新增)
| 场景 | 输入 | 预期 | 结果 |
|---|---|---|---|
| UTF-8 混排正文 | 长英文前缀 + 中文正文(UTF-8) | 检测 `utf-8`,中文可读 | ✅ |
| 纯 ASCII 文本 | `Hello ASCII 123` | 行为不变 | ✅ |
| `ascii` 误传 | UTF-8 中文 buffer + `encoding='ascii'` | 触发兜底,中文可读 | ✅ |
| 非法 UTF-8 序列 | 非 UTF-8 合法字节序列 | 不应判为 `utf-8` | ✅ |
### 6.3 执行命令与结果
执行命令:
```bash
pnpm -C FastGPT exec vitest run \
test/cases/global/common/file/tool.test.ts \
test/cases/service/common/file/read/utils.test.ts \
test/cases/service/common/file/gridfs/utils.test.ts \
test/cases/service/common/file/read/encoding.regression.test.ts
```
结果摘要:
- Test Files: `4 passed (4)`
- Tests: `45 passed (45)`
### 6.4 UTF-8 严格校验性能检测报告(新增)
#### 6.4.1 检测背景
- 目标:评估 `isValidUtf8(buffer)` 全量线性扫描在大文件场景下的 CPU 开销,确认是否需要阈值门控。
- 方法:在本地通过 Node.js 基准脚本,复用当前实现逻辑,分别对 ASCII 缓冲区与中英混排 UTF-8 缓冲区做多轮扫描取平均值。
#### 6.4.2 检测结果(平均单次扫描耗时)
| 文件大小 | ASCII only | UTF-8 中英混排 |
|---|---:|---:|
| 10MB | 16.20ms | 32.21ms |
| 50MB | 84.04ms | 87.52ms |
| 100MB | 160.06ms | 169.08ms |
| 200MB | 327.01ms | 345.51ms |
| 500MB | 894.27ms | 880.22ms |
补充:实测吞吐约 `560~620 MB/s`,整体符合线性增长特征(O(n))。
#### 6.4.3 结论与策略
- 20MB 以下:开销较小,通常无体感影响。
- 50~100MB:开始出现可感知延迟。
- 200MB 以上:单次扫描约 300ms+,并发场景下会放大 CPU 压力。
- 500MB:接近 1 秒/次,不建议默认全量严格校验。
建议落地策略:
1. 交互链路默认仅对 `<=32MB`(或 `<=64MB`)执行全量 UTF-8 严格校验。
2. 超过阈值时跳过全量校验,改走采样探测 fallback。
3. 后续可按线上机器规格和峰值并发再微调阈值。
## 7. 质量自检清单
- [x] 输入与权限流程未被破坏(未改 API/权限逻辑)
- [x] 无新增 `any` 滥用、无未处理 Promise
- [x] 包依赖方向符合 monorepo 约束(`service` 复用 `global`
- [x] 覆盖关键回归场景(UTF-8 混排、ascii 误判兜底)
- [x] 无新增敏感日志输出
## 8. 发布与回滚
### 8.1 发布步骤
1. 合并代码后在测试环境执行上述 4 组回归测试。
2. 手工上传 UTF-8 中英混排 Markdown,确认预览与入库内容正常。
3. 观察线上相关解析失败反馈。
### 8.2 回滚触发条件
- 发布后出现明显新增的非 UTF-8 文本解析失败反馈。
### 8.3 回滚步骤
1. 回滚 `FastGPT/packages/global/common/file/tools.ts``FastGPT/packages/service/worker/readFile/extension/rawText.ts`
2. 重新发布并复测上传链路。
## 9. AI 实施提示(给执行模型)
- 编码检测必须坚持“验证优先”,禁止回退到“仅前缀猜测优先”。
- 若后续扩展多候选编码评分,单独开需求,不在本任务内扩大范围。
- 每次改动编码策略后,必须至少跑本文件第 6.3 节的回归命令。
# 需求设计文档
# 需求设计文档
## 0. 文档标识
- 任务前缀:`md-encoding-repair`
- 文档文件名:`md-encoding-repair-需求设计文档.md`
- 文档状态:已补全(实施回填版)
- 最后更新:2026-04-15
## 1. 需求背景与目标
### 1.1 背景
- 问题现状:知识库上传 `.md` 文件时,若文件开头英文占比高,系统可能将编码误判为 `ascii`,随后按 `ascii` 解码整篇文本,导致中文出现乱码。
- 触发场景:`md/txt/csv/html` 等文本文件,文件前部主要为英文,中文内容出现在较后位置。
### 1.2 目标
- 业务目标:保证知识库文档上传后中文内容可被正确解析并进入分段/训练链路。
- 技术目标:避免“前部英文导致整篇 `ascii` 误判”的编码检测缺陷。
- 成功指标(可量化):
- 构造“前 500+ 字节英文、后续中文”的 UTF-8 Markdown 文件,解析结果中文不乱码。
- 纯英文 ASCII 文件解析结果保持不变。
- 编码相关回归测试全量通过。
## 2. 当前项目事实基线(基于代码)
| 能力项 | 现有实现位置(文件路径) | 现状说明 | 结论(复用/修改/新增) |
|---|---|---|---|
| API | `FastGPT/projects/app/src/pages/api/core/dataset/collection/create/fileId.ts` | 上传后创建文件型 collection 的入口;本身不处理编码,只触发后续解析流程 | 复用 |
| Core Service | `FastGPT/packages/service/core/dataset/read.ts` -> `FastGPT/packages/service/common/s3/sources/dataset/index.ts` | 读取 S3 文件后调用 `detectFileEncoding(buffer)`,再把 encoding 传给解析器 | 复用调用链,修改编码策略实现 |
| Worker Decode | `FastGPT/packages/service/worker/readFile/extension/rawText.ts` | 根据上游 encoding 对文本 buffer 解码 | 新增解码兜底保护 |
| DB Schema | `FastGPT/packages/service/core/dataset/collection/schema.ts` | 当前问题与 DB 结构无关 | 不改 |
| Frontend | `FastGPT/projects/app/src/pageComponents/dataset/detail/Import/diffSource/FileLocal.tsx` | 前端只负责上传到 S3,不决定后端解析编码 | 不改 |
| Logger | `FastGPT/packages/service/common/s3/sources/dataset/index.ts` | 当前已有下载日志;编码误判场景无专门日志 | 本期不改 |
关键代码锚点(实施后):
- 编码判定入口:`FastGPT/packages/global/common/file/tools.ts``detectFileEncoding`(已改为验证优先)
- 解码兜底:`FastGPT/packages/service/worker/readFile/extension/rawText.ts``readFileRawText`
## 3. 需求澄清记录
| 维度 | 已确认内容 | 待确认内容 | 备注 |
|---|---|---|---|
| 业务目标 | 修复 Markdown 上传中文乱码 | 是否覆盖更多小众本地编码(如 Shift_JIS) | 后续二期可评估 |
| 范围边界 | 聚焦知识库/读文件链路的编码判定与解码保护 | 是否新增线上编码判定日志点 | 本期不做 |
| 权限模型 | 无权限模型变化 | 无 | N/A |
| 数据模型 | 不新增字段 | 无 | N/A |
| API 行为 | 不改 API 入参/出参 | 无 | N/A |
| 前端交互 | 不改页面交互 | 无 | N/A |
## 3.1 影响域判定(先判定,再核对规范)
| 维度 | 是否命中 | 证据(需求/代码锚点) | 核对规范 | 结论 |
|---|---|---|---|---|
| API | No | 编码问题发生在服务端文件解析,现有路由仅透传 fileId | `style/api.md` | Not Applicable(不改接口) |
| DB | No | 不涉及 schema/索引/数据迁移 | `style/db.md` | Not Applicable |
| Front | No | 上传流程不参与后端解码决策 | `style/front.md` | Not Applicable |
| Logger | No(本期) | 本期目标为最小可控修复,未新增观测点 | `style/logger.md` | Not Applicable |
| Package | Yes | 修改位置在 `packages/global``packages/service`,涉及跨包复用 | `style/package.md` | 已遵守依赖方向(service 依赖 global) |
## 4. 范围定义
### 4.1 In Scope(本期必须)
-`detectFileEncoding` 从“猜测优先”调整为“验证优先”。
- 增加 `ascii` 误判保护(检测层+解码层双保险)。
- 补充编码回归测试矩阵,覆盖核心场景。
### 4.2 Out of Scope(本期不做)
- 不改知识库 API 协议。
- 不改数据库结构。
- 不做前端上传流程改造。
- 不引入多候选编码评分引擎(后续可独立需求)。
## 5. 方案对比
| 方案 | 核心思路 | 优点 | 风险 | 实施成本 | 结论 |
|---|---|---|---|---|---|
| 方案A(最小改动) | `UTF-8 验证优先(BOM + 严格字节校验)` + fallback 探测 + `ascii` 兜底 | 改动集中、确定性高、能直接修复现网主问题 | 对极端小众编码仍依赖 fallback | 低 | 推荐并已落地 |
| 方案B(可扩展) | 多候选编码试解 + 文本质量打分 | 覆盖更多边缘编码场景 | 复杂度与误判调参成本高 | 中-高 | 本期不选 |
推荐方案:方案A(已实施)。
## 6. 推荐方案详细设计(实施回填)
### 6.1 API 设计
| 路由 | 方法 | 鉴权 | 请求 | 响应 | 错误分支 | 相关文件 |
|---|---|---|---|---|---|---|
| N/A | N/A | N/A | N/A | N/A | N/A | 不涉及 |
### 6.2 数据设计
| 实体/集合 | 字段 | 类型 | 必填 | 默认值 | 索引/约束 | 兼容策略 |
|---|---|---|---|---|---|---|
| N/A | N/A | N/A | N/A | N/A | N/A | N/A |
### 6.3 核心代码设计
| 模块 | 关键函数/类型 | 实际变更 | 上下游影响 |
|---|---|---|---|
| `FastGPT/packages/global/common/file/tools.ts` | `detectFileEncoding(buffer)` | 判定顺序调整为:`hasUtf8Bom -> isValidUtf8 -> detect(getDetectSample)`;并增加 `ascii + 非 ASCII 字节` 兜底 | 所有调用方收益(知识库、工作流读文件、预览编码头) |
| `FastGPT/packages/global/common/file/tools.ts` | `hasNonAsciiByte(buffer)` | 从私有函数提升为导出工具函数,供多处复用 | 减少重复实现,维护单一事实源 |
| `FastGPT/packages/service/worker/readFile/extension/rawText.ts` | `readFileRawText` | 新增 `encoding='ascii'` 且存在非 ASCII 字节时强制 UTF-8 解码保护 | 防止上游误判导致中文乱码 |
| `FastGPT/test/cases/global/common/file/tool.test.ts` | `detectFileEncoding` tests | 新增 BOM 场景、长英文前缀+中文正文场景 | 防回归 |
| `FastGPT/test/cases/service/common/file/read/encoding.regression.test.ts` | 编码回归矩阵 | 新增 4 场景覆盖:UTF-8 混排、ASCII、ascii 误传、非法 UTF-8 | 回归覆盖补齐 |
### 6.4 前端设计
| 页面/组件 | 入口文件 | 交互状态(加载/空/错/成功) | i18n key | 变更说明 |
|---|---|---|---|---|
| N/A | N/A | N/A | N/A | 本期不涉及 |
### 6.5 日志与观测设计
| 场景 | 日志级别 | category | 结构化字段 | 脱敏策略 |
|---|---|---|---|---|
| 本期无新增日志 | N/A | N/A | N/A | N/A |
## 7. 风险、迁移与回滚
### 7.1 风险清单
- 风险1:严格 UTF-8 校验为 O(n) 线性扫描,超大文本文件会增加少量 CPU。
- 风险2:小众非 UTF-8 编码仍可能依赖 fallback 的稳定性。
### 7.2 迁移策略
- 无 DB 迁移。
- 通过新增回归矩阵 + 现有测试集进行功能验证。
### 7.3 回滚策略
- 回滚目标文件:
- `FastGPT/packages/global/common/file/tools.ts`
- `FastGPT/packages/service/worker/readFile/extension/rawText.ts`
- 回滚触发条件:发布后出现显著新增的“非 UTF-8 文本解析异常”反馈。
## 8. 验收标准(执行结果)
| 验收项 | 验收方式 | 通过标准 | 结果 |
|---|---|---|---|
| UTF-8 混合文档不乱码 | 自动化测试 + 手测路径定义 | 中文片段解析正常 | ✅ 通过 |
| 纯 ASCII 文档兼容 | 自动化测试 | 行为不回归 | ✅ 通过 |
| `ascii` 误传防护 | 自动化测试 | 解码结果中文可读 | ✅ 通过 |
| 回归安全 | 编码相关测试集合 | 全部通过 | ✅ 45/45 |
已执行测试命令:
```bash
pnpm -C FastGPT exec vitest run \
test/cases/global/common/file/tool.test.ts \
test/cases/service/common/file/read/utils.test.ts \
test/cases/service/common/file/gridfs/utils.test.ts \
test/cases/service/common/file/read/encoding.regression.test.ts
```
测试结果摘要:
- Test Files: `4 passed (4)`
- Tests: `45 passed (45)`
## 9. MECE 核查结论(实施后)
### 9.1 相互独立检查结果
- 发现问题:编码检测与解码保护存在重复判断风险。
- 影响范围:后续维护可能出现策略漂移。
- 修订动作:统一由 `tools.ts` 提供通用工具(`hasNonAsciiByte`),`rawText.ts` 仅做末端防护。
- 修订后结果:职责清晰,复用一致。
### 9.2 完全穷尽检查结果
- 发现问题:仅修检测层不足以防止历史调用链误传 `ascii`
- 影响范围:部分链路仍可能乱码。
- 修订动作:增加解码层二次兜底 + 回归矩阵覆盖误传场景。
- 修订后结果:正常/异常链路覆盖完整。
### 9.3 修订动作与最终边界
- 本期聚焦编码检测与解码兜底,不扩展 API/DB/前端。
- 后续若要支持更广泛本地编码智能识别,建议单开“多候选评分”二期需求。
# TODO — 变量更新节点类型操作扩展
> 设计见同目录 `design.md`
## Phase 1:类型 & 运行时(后端,先行)
- [x] 扩展 `TUpdateListItem` 类型,新增 `numberOperator / booleanMode / arrayMode`
- [x] `runUpdateVar.ts`:添加 oldValue 读取工具函数
- [x] `runUpdateVar.ts`:Number 公式分派(含除零保持旧值)
- [x] `runUpdateVar.ts`:Boolean `true/false/negate` 分派
- [x] `runUpdateVar.ts`:Array `append/clear/equal` 分派(append 使用元素类型做 `valueTypeFormat`
- [x] `runUpdateVar.ts`:所有新字段仅在 `renderType === input` 时生效的 guard
- [x] 写 vitest 测试 `runUpdateVar.test.ts`,运行通过(20 tests)
## Phase 2:前端组件拆分(重构,不改行为)
- [x] 建立目录 `NodeVariableUpdate/`
- [x] 把现有 `NodeVariableUpdate.tsx` 迁到 `NodeVariableUpdate/index.tsx`,拆出 `VariableSelector.tsx`
- [x] 新增 `ValueRenderer.tsx`(按 renderType / valueType 派发)
## Phase 3:前端渲染器(新功能)
- [x] `renderers/NumberFormula.tsx`:运算符下拉 + numberInput(图标化)
- [x] `renderers/BooleanSelect.tsx`:True/False/Negate 下拉
- [x] `renderers/ArrayValue.tsx`:模式下拉 + 按元素类型映射 `InputRender`(不递归)
- [x] `ValueRenderer.tsx`:按 valueType 派发到新 renderer
- [x] 切换模式时清空 `value: undefined`
## Phase 4:i18n
- [x] 补充 `workflow:var_update_boolean_*``workflow:var_update_array_*` 中 / 英 / 繁中
## Phase 5:联调
- [x] dev server 起:string / number / boolean / array 四类变量逐一验证手动输入 + 引用两种模式
- [x] 老数据打开(无新字段),表现与升级前一致
- [x] 运行 `pnpm lint` 全量通过(0 errors)
## Phase 6:Review 清理(2026-04-15)
- [x] 还原 `constants.ts`:剥离 107 条与 math icons 无关的图标注册,只保留 5 个 `math/*`
- [x] `any[]` 收紧为 `EditorVariablePickerType[]` / `EditorVariableLabelPickerType[]`(3 个 renderer + ValueRenderer)
- [x] 抽出 `getDefaultsForValueType()` 统一目标变量切换时的默认字段下发
- [x] `VariableSelector.tsx``.includes('array')``.startsWith('array')` 与仓内风格对齐
- [x] `workflow.json` 三语把 `var_update_*` 按字母序移到 `variable_*` 之前
{
"enabledPlugins": {
"skill-creator@claude-plugins-official": true
}
}
---
name: api-development
description: FastGPT API 开发规范。重点强调使用 zod schema 定义入参和出参,在 API 文档中声明路由信息,编写对应的 OpenAPI 文档,以及在 API 路由中使用 schema.parse 进行验证。
---
# FastGPT API 开发规范
> FastGPT 项目 API 路由开发的标准化指南,确保 API 的一致性、类型安全和文档完整性。
## 何时使用此技能
- 开发新的 Next.js API 路由
- 修改现有 API 的入参或出参
- 需要 API 类型定义和文档
- 审查 API 相关代码
## 说明文档
[API 设计规范](../../design/api/index.md)
\ No newline at end of file
---
name: doc-i18n
description: 将 FastGPT 文档从中文翻译为面向北美用户的英文。当用户提到翻译文档、i18n、国际化、translate docs、新增/修改了中文文档需要同步英文版时,使用此 skill。也适用于用户要求检查文档翻译缺失、批量翻译、或对比中英文文档差异的场景。
---
## 概述
FastGPT 文档采用双文件 i18n 方案,中文为源语言,英文为目标语言。你的任务是将中文文档翻译为自然流畅的北美英文,而非逐字直译。
## 文件结构
文档位于 `document/content/docs/` 目录下:
- 内容文件:`{name}.mdx`(中文) → `{name}.en.mdx`(英文)
- 导航文件:`meta.json`(中文) → `meta.en.json`(英文)
## 工作流程
### 1. 确定翻译范围
两种方式确定需要翻译的文件:
**自动检测**(用户未指定具体文件时):
- 运行 `git diff --name-only``git diff --cached --name-only` 检测 `document/` 下变更的中文文件
- 筛选出 `.mdx`(排除 `.en.mdx`)和 `meta.json`(排除 `meta.en.json`
- 检查对应的英文文件是否存在或是否需要更新
**手动指定**:用户直接给出文件路径或目录。
### 2. 翻译内容文件(.mdx → .en.mdx)
对每个中文 `.mdx` 文件,生成或更新对应的 `.en.mdx` 文件。
**保持不变的部分**
- MDX import 语句(如 `import { Alert } from '@/components/docs/Alert'`
- 图片路径(如 `![](/imgs/intro/image1.png)`
- 链接 URL(保持原始 URL 不变)
- HTML/JSX 组件结构和属性(如 `<Alert icon="🤖" context="success">`
- 表格的 markdown 结构
- 代码块内容(除非是中文注释)
- emoji 符号
**需要翻译的部分**
- frontmatter 的 `title``description`
- 所有正文文本内容
- 组件内的文本内容(如 Alert 内的文字)
- 表格中的文字内容
- 代码块中的中文注释
### 3. 翻译导航文件(meta.json → meta.en.json)
对每个中文 `meta.json`,生成或更新对应的 `meta.en.json`
**需要翻译的字段**`title``description`、分隔符字符串(如 `"---入门---"``"---Getting Started---"`
**保持不变的字段**`pages` 数组中的文件名引用、`icon``root``order`
### 4. 翻译完成后
- 列出所有已翻译的文件
- 如果发现中文文件有对应英文文件缺失的情况,提醒用户
## 翻译原则
这些原则的核心目标是让北美开发者读起来感觉像是原生英文文档,而不是翻译过来的。
### 语言风格
- 面向北美开发者,使用自然的美式英语技术写作风格
- 不要逐字翻译,要传达原文的意思和意图
- 技术文档倾向简洁直接,避免冗余修饰
- 中文文档常用的排比、铺陈手法,翻译时应精简为英文读者习惯的表达
**示例**
```
中文:可轻松导入各式各样的文档及数据,能自动对其开展知识结构化处理工作。
✗:You can easily import various documents and data, which will be automatically processed for knowledge structuring.
✓:Import documents and data with automatic knowledge structuring.
```
### 中国特有平台和服务的本地化
直接使用国际版名称,不保留中文原名:
| 中文 | 英文 |
|------|------|
| 飞书 | Lark |
| 企业微信 | WeCom |
| 钉钉 | DingTalk |
| 公众号 | WeChat Official Account |
| 文心一言 | ERNIE Bot |
| 中国大陆版 | China Mainland |
| 国际版 | International |
### 技术术语
保持业界通用的英文术语,不要生造翻译:
| 中文 | 英文 |
|------|------|
| 知识库 | Knowledge Base |
| 工作流 | Workflow |
| 大语言模型 | LLM / Large Language Model |
| 向量存储 | Vector Store |
| 可视化编排 | Visual Orchestration |
| 低代码 | Low-code |
| 节点 | Node |
| 插件 | Plugin |
### 语气
- 保持专业但友好的语气,和原文档的风格一致
- 不要过度正式,也不要过于随意
- 面向开发者和技术用户,假设读者有基本的技术背景
---
name: local-review
description: 当用户请求对本地未提交的代码(或本地分支特定 commit 等)进行代码审查时触发该 skill,对本地变更进行审查。
---
# When to Use This Skill
当用户请求本地审查时触发。本技能支持对以下三种变更范围进行环境友好的深度审查:
1. **未提交的变更 (Uncommitted)**: 审查工作区中所有尚未提交的修改(包括已暂存和未暂存)。
2. **特定的历史 Commit (Recent Commits)**: 选定检查最近几次 commit(如 `HEAD~N`)的改动情况。
3. **主干差异对比 (Main Branch Diff)**: 与 `main` 等主干分支对比,整体审查当前特性的增量细节。
# Local Review 本地代码审查技能
> 在本地开发环境中,利用全量代码库的上下文优势,全面审查代码变更的质量、安全性、性能和架构设计,并自动评估跨文件影响,提供专业的改进建议。
## 审查范围与启动方式 (Scope Definitions)
你需要根据用户的指令意图,判断并选择对应的比对策略来拉取代码。**务必使用 `-U15` 等参数增加局部上下文视野**
### 模式 1:未提交审查 (Uncommitted Changes)
```bash
# 获取工作区未暂存及已暂存的混合全量 diff
git diff HEAD -U15
```
### 模式 2:特定历史 Commit 审查 (Recent Commits)
```bash
# 审查最后一次 commit
git diff HEAD~1 HEAD -U15
# 审查最近 3 次的整体 commit 变更 (数字可根据用户指令替换)
git diff HEAD~3 HEAD -U15
```
### 模式 3:主干差异审查 (Main Branch Diff)
```bash
# 审查当前特性分支相较于 main 主干的所有的增量代码
git diff main...HEAD -U15
```
## 工具集成
### 使用 git CLI 与本地工具加速审查
在本地环境审查时,相较于 PR 审查的局限点,我们要极大发挥能“到处跑、到处看”的优势。
```bash
# 获取已修改的文件列表及状态
git status -s
# 将宽泛上下文的 diff 存入临时文件便于分析
git diff -U15 > /tmp/local_changes.diff
# 跨文件依赖与上下文搜索(寻找受修改影响的引用方)
git grep "修改的函数名或类型"
```
### 本地测试与验证代码
本地审查时可以直接执行验证流程,以结果作为审查依据:
```bash
# 运行单元测试
pnpm test
# 运行代码规范检查
pnpm lint
# 运行类型检查以捕获变更导致的隐藏/全局类型错误
pnpm tsc --noEmit
# 启动开发服务器通过 UI 进行直观验证
pnpm dev
```
### 常见命令参考
```bash
# 获取具体的 diff 内容
git diff --name-only
git diff <file_path>
# 追溯某段代码的演进历史
git blame <file_path>
# 获取最近的 commit 记录
git log -n 5 --oneline
```
## 审查流程
### 1. 信息与上下文收集阶段 (⭐本地环境核心增强)
**切勿仅凭零碎的补丁代码(Diff chunks)盲目审查,务必建立完整的上下文认知:**
自动执行/指导执行以下步骤:
```bash
# 1. 确认当前的修改状态及涉及的文件
git status
# 2. 获取具有充足上下文的变更细节(-U15 或更大)
git diff -U15
# 3. 阅读全量文件(上下文增强):
# 对于重点修改的文件,不要只看 diff,而是查看整个源文件,理解数据流向和全貌。
cat <file_path>
# 4. 分析跨文件影响(上下文追踪):
# 如果改动涉及到变量名变更、导出方法的增删改、Props 类型更改,
# 必须使用本地搜索查出其在项目中的所有调用点:
git grep "<Edited_Target>"
```
### 2. 多维度代码审查
基于完整的调用链和文件上下文,按照以下维度进行系统性审查:
#### 维度 1: 基本代码质量标准 📐
通用的代码质量标准,适用于所有项目:
- **安全性**: 输入验证、权限检查、注入防护、敏感信息保护
- **正确性**: 错误处理、边界条件、类型安全(**结合上下文,看传参方是否提供了正确数据**
- **性能**: 算法复杂度、数据库优化、内存管理(**结合上下文,审查循环逻辑或多次重渲染问题**
- **可测试性**: 测试覆盖、测试质量、Mock 使用
📖 **详细指南**: [code-quality-standards.md](./code-quality-standards.md)
#### 维度 2: FastGPT 风格规范 🎨
FastGPT 项目特定的代码规范和约定:
- **API 路由开发**: 路由定义、权限验证、错误处理: [API 路由开发规范](./style/api.md)
- **前端组件开发**: TypeScript + React、Chakra UI、状态管理: [前端组件开发规范](./style/front.md)
- **数据库操作**: Model 定义、查询优化、索引设计: [数据库操作规范](./style/db.md)
- **包结构与依赖**: 依赖方向、导入规范、类型导出: [包结构与依赖规范](./style/package.md)
- **日志与可观测性**: 统一日志分类、结构化字段、敏感信息、OTEL 导出等审查标准: [日志review 标准](./style/logger.md)
#### 维度 3: 常见问题检查清单 🔍
快速识别和修复常见问题模式:
- **TypeScript 问题**: any 类型滥用、类型定义不完整、不安全断言
- **异步错误处理**: 未处理 Promise、错误信息丢失、静默失败
- **React 性能**: 不必要的重渲染、渲染中创建对象、缺少 memoization
- **工作流节点**: isEntry 未重置、交互历史未清理、白名单遗漏
- **安全漏洞**: 注入攻击、XSS、文件上传漏洞
📖 **详细清单**: [common-issues-checklist.md](./common-issues-checklist.md)
### 3. 生成并输出综合审查报告
本地审查输出主要通过对话框返回整体审查报告和行级建议。
#### 步骤 1: 结合上下文深度分析代码并准备评论
在详细审查阶段,记录之前通过额外探索收集的上下文论据:
- **全局视野**:这个改动是否打破了使用该模块的其他文件的运行?
- **文件路径**: 如 `packages/service/core/workflow/dispatch.ts`
- **行号 / 变更块**: 如 `L142-L150`
- **问题类型**: 🔴严重 / 🟡改进 / 🟢优化
- **评论内容**: 具体的问题描述和基于整体上下文推导出的深层建议
#### 步骤 2: 汇总代码审查评论
将所有的行级代码和上下文关联分析结果整合成一份连贯的内容:
```markdown
### 📍 代码级深层意见
#### `packages/service/core/workflow/dispatch.ts`
- **L142**: 🔴 **严重跨文件隐患**: 这里更改了返回值格式,但通过 `git grep` 追踪发现调用方的接收逻辑(如 `file_B.ts L30`)未进行相应的适配,将导致生产环境崩溃。
**建议**: 修改本处的兼容逻辑,或一并更新相关的调用方代码:
```typescript
// 提供结合了调用关系的修复代码
```
- **L150**: 🟡 **逻辑重构优化**: 结合该文件的大环境,该段正则其实在多个地方共享,建议提取到外部公共目录中复用。
```
#### 步骤 3: 生成整体审查报告
在对话框中提供类似以下的整体审查报告格式:
```markdown
# Local Review 代码审查报告
## 📊 审查面与追踪范围
- **本地变更类型**: 未暂存 / 已暂存 / 与某分支差异
- **变更统计**: +{additions} -{deletions} 行
- **涉及直接修改文件**: {files.length} 个
- **延伸上下文排查文件**: {context_files.length} 个(评估了相关的外链影响)
## ✅ 亮点与优点
{列出做得好的地方}
## ⚠️ 问题汇总
### 🔴 严峻环境问题 ({count} 个,必须修复)
{简要列出每个严重问题,并指向下文的详细行级评论}
### 🟡 建议与重构项 ({count} 个)
{简要列出每个建议}
### 🟢 局部优化选项 ({count} 个)
{简要列出优化建议}
---
{在此处插入包含上下文增强分析内容的 "📍 代码级深层意见"}
---
## 🧪 本地测试验证策略
{基于本次上下文排查所发现的影响范围,给出建议:例如某个隐性关联页面的渲染测试等}
## 💬 总体评价
- **代码质量**: ⭐⭐⭐⭐☆ (4/5)
- **安全性**: ⭐⭐⭐⭐⭐ (5/5)
- **性能**: ⭐⭐⭐⭐☆ (4/5)
- **全栈协调性**: ⭐⭐⭐⭐☆ (4/5) (反映修改对全局代码影响程度的好坏)
## 🚀 审查结论
{建议: 可以提交 / 修改后提交 / 建议将调用方文件的修复一起囊括}
```
#### 步骤 4: 响应用户并提供报告
输出或保存完整的 Markdown 报告,必要时提出主动帮用户把发现的联调问题(调用方 bug 等)以命令形式进行顺手修复的建议。
#### 本地审查命令快速补救参考:
| 场景 | 命令 |
|------|------|
| 修改后需重新审查暂存区 | `git diff --cached` |
| 检测改动后是否出现死类型 | `pnpm tsc --noEmit` |
| 放弃某个被污染的本地改动 | `git checkout -- <file>` |
# API 路由开发规范
FastGPT 使用 Next.js API Routes,需要遵循特定的开发模式。
### 2.1 路由定义
**文件位置**: `projects/app/src/pages/api/`
**审查要点**:
- ✅ 路由文件使用命名导出,不支持默认导出
- ✅ 使用 `NextAPIRequest``NextAPIResponse` 类型
- ✅ 支持的 HTTP 方法明确 (`GET`, `POST`, `PUT`, `DELETE`)
- ✅ 返回统一的响应格式
**示例**:
```typescript
import type { NextAPIRequest, NextAPIResponse } from '@fastgpt/service/type/next';
import { APIError } from '@fastgpt/service/core/error/controller';
export default async function handler(req: NextAPIRequest, res: NextAPIResponse) {
try {
if (req.method !== 'POST') {
throw new Error('Method not allowed');
}
// 处理逻辑...
const result = await processData(req.body);
res.json(result);
} catch (error) {
APIError(error)(req, res);
}
}
```
### 2.2 类型合约
**文件位置**: `packages/global/openapi/`
**审查要点**:
- ✅ API 合约定义在 OpenAPI 规范文件中
- ✅ 请求参数有完整的类型定义
- ✅ 响应格式有完整的类型定义
- ✅ 错误响应有说明
### 2.3 业务逻辑
**文件位置**:
- 通用逻辑: `packages/service/`
- 项目特定逻辑: `projects/app/src/service/`
**审查要点**:
- ✅ 业务逻辑与 API 路由分离
- ✅ 服务函数有明确的类型定义
- ✅ 错误处理统一
### 2.4 权限验证
**审查要点**:
- ✅ 所有 API 路由都有权限验证 (除了公开端点)
- ✅ 使用 `parseHeaderCert` 解析认证头
- ✅ 验证用户对资源的所有权
- ✅ 敏感操作需要额外验证
**示例**:
```typescript
import { parseHeaderCert } from '@fastgpt/global/support/permission/controller';
export default async function handler(req: NextAPIRequest, res: NextAPIResponse) {
try {
// 解析认证头
const { userId, teamId } = await parseHeaderCert(req);
// 验证权限
const resource = await Resource.findById(resourceId);
if (!resource || resource.userId !== userId) {
throw new Error('Permission denied');
}
// 继续处理...
} catch (error) {
APIError(error)(req, res);
}
}
```
### 2.5 错误处理
**审查要点**:
- ✅ 使用 try-catch 包裹所有异步操作
- ✅ 使用 `APIError` 统一错误响应
- ✅ 错误信息不暴露敏感数据
- ✅ HTTP 状态码正确
---
# 数据库操作规范
FastGPT 使用 MongoDB (Mongoose) 和 PostgreSQL。
## 4.1 Model 定义
**文件位置**: `packages/service/common/mongo/schema/`
**审查要点**:
- ✅ Schema 定义使用 TypeScript 泛型
- ✅ 必要的字段添加索引
- ✅ 敏感字段加密存储
- ✅ 定义虚拟字段和实例方法
**示例**:
```typescript
import { mongoose, Schema } from '@fastgpt/service/common/mongo';
const UserSchema = new Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true, select: false }, // 默认不查询
email: { type: String, required: true },
createdAt: { type: Date, default: Date.now }
});
// 索引
UserSchema.index({ username: 1 });
UserSchema.index({ email: 1 });
// 虚拟字段
UserSchema.virtual('fullName').get(function() {
return `${this.firstName} ${this.lastName}`;
});
export const User = mongoose.model('User', UserSchema);
```
## 4.2 查询操作
**审查要点**:
- ✅ 使用参数化查询防止注入
- ✅ 避免 N+1 查询
- ✅ 使用 projection 只查询需要的字段
- ✅ 大结果集使用分页
- ✅ 异步操作有错误处理
**示例**:
```typescript
// ❌ 不好的实践
const users = await User.find({}).toArray(); // 可能返回大量数据
// ✅ 好的实践
const users = await User.find({})
.project({ username: 1, email: 1 }) // 只查询需要的字段
.limit(20) // 限制结果数量
.skip(page * 20)
.toArray();
```
## 4.3 错误处理
**审查要点**:
- ✅ 数据库操作使用 try-catch
- ✅ 处理重复键错误 (code 11000)
- ✅ 处理连接错误
- ✅ 错误日志包含上下文信息
---
# 前端组件开发规范
FastGPT 使用 React + TypeScript + Chakra UI。
## 3.1 组件结构
**审查要点**:
- ✅ 使用函数式组件和 Hooks
- ✅ 组件使用 `React.memo` 优化性能
- ✅ Props 有明确的类型定义
- ✅ 使用 TypeScript type 而不是 interface (项目约定)
**示例**:
```typescript
import React from 'react';
import { Box, Button } from '@chakra-ui/react';
type YourComponentProps = {
title: string;
onClick: () => void;
disabled?: boolean;
};
export const YourComponent = React.memo(function YourComponent({
title,
onClick,
disabled = false
}: YourComponentProps) {
return (
<Box>
<Button onClick={onClick} isDisabled={disabled}>
{title}
</Button>
</Box>
);
});
```
## 3.2 状态管理
**审查要点**:
- ✅ 本地状态使用 `useState`
- ✅ 全局状态使用 Zustand store
- ✅ 表单状态使用 `useForm` (react-hook-form)
- ✅ 复杂状态逻辑使用 `useReducer`
## 3.3 样式规范
**审查要点**:
- ✅ 优先使用 Chakra UI props
- ✅ 响应式设计使用 Chakra UI 的断点系统
- ✅ 自定义样式放在 `styles/theme.ts`
- ✅ 避免内联样式
**示例**:
```typescript
// ❌ 不好的实践
<Box style={{ backgroundColor: 'blue', padding: '16px' }}>
// ✅ 好的实践
<Box bg="blue.500" p={4}>
```
## 3.4 国际化
**审查要点**:
- ✅ 所有用户可见文本使用 `i18nT`
- ✅ 翻译 key 使用命名空间
- ✅ 动态文本使用插值
**示例**:
```typescript
import { i18nT } from '@fastgpt/web/i18n/utils';
const message = i18nT('user:welcome', { name: userName });
```
## 3.5 性能优化
**审查要点**:
- ✅ 列表渲染使用 key
- ✅ 大列表使用虚拟化
- ✅ 避免在渲染中创建新对象/函数
- ✅ 使用 `useMemo` 缓存计算结果
- ✅ 使用 `useCallback` 缓存函数
---
# 5. 包结构与依赖规范
FastGPT 是一个 monorepo,使用 pnpm workspaces。
## 5.1 包结构
```
packages/
├── global/ # 类型、常量、工具函数 (无运行时依赖)
├── service/ # 后端服务、数据库模型 (依赖 global)
└── web/ # 前端组件、样式、i18n (依赖 global)
projects/
├── app/ # NextJS 应用 (依赖所有 packages)
├── sandbox/ # NestJS 沙箱服务 (独立应用)
└── mcp_server/ # MCP 服务器 (独立应用)
```
## 5.2 依赖规则
**审查要点**:
-`packages/global/` 无任何运行时依赖
-`packages/service/` 只依赖 `packages/global/`
-`packages/web/` 只依赖 `packages/global/`
-`projects/app/` 可以依赖所有 packages
- ✅ 独立项目 (sandbox, mcp_server) 最小化依赖
## 5.3 导入规范
**审查要点**:
- ✅ 使用项目别名导入: `@fastgpt/global`, `@fastgpt/service`, `@fastgpt/web`
- ✅ 避免相对路径导入跨包的文件
- ✅ 导入路径使用 index 简化
**示例**:
```typescript
// ❌ 不好的导入
import { UserType } from ../../../../../packages/global/core/user/type.d.ts;
// ✅ 好的导入
import { UserType } from '@fastgpt/global/core/user/type';
```
## 5.4 类型导出
**审查要点**:
- ✅ 公共类型必须导出
- ✅ 类型文件使用 `.d.ts` 扩展名
- ✅ 复杂类型放在独立的类型文件
- ✅ 使用 `export type` 导出类型
---
---
name: pr-review
description: 当用户传入一个 review 的 pr 链接时候,触发该 skill,对 pr 进行代码审查。
---
# PR Review 代码审查技能
> 按阶段对 Pull Request 进行系统性审查,先验证需求理解与逻辑正确性,再并行进行多维度质量检测,最后提交审查报告。
---
## 步骤 0:拉取代码
使用以下命令**无需切换分支**,直接使用 PR 编号即可:
```bash
# 获取 PR 基本信息
gh pr view <number> --json number,title,body,author,state,headRefName,baseRefName,additions,deletions,files
# 获取完整 diff
gh pr diff <number>
# 查看 commit 历史
gh pr view <number> --json commits --jq '.commits[].messageHeadline'
# 检查 CI 状态
gh pr checks <number>
```
如需在本地运行 **tsc / 单元测试**,使用 `git worktree` 创建独立目录,**不影响当前分支**
```bash
# 1. 拉取 PR 代码到临时分支
git fetch upstream pull/<number>/head:pr/<number>
# 2. 在独立目录检出(与当前工作区完全隔离)
git worktree add ~/pr-worktrees/pr-<number> pr/<number>
# 3. 进入该目录安装依赖、运行测试
cd ~/pr-worktrees/pr-<number>
pnpm install
pnpm tsc --noEmit # 类型检查
pnpm test # 单元测试
# 4. 审查完毕后清理
cd -
git worktree remove ~/pr-worktrees/pr-<number>
git branch -D pr/<number>
```
---
## 第一阶段:需求理解与逻辑验证
**目标**:理解本次 PR 的意图,并通过阅读代码来推理测试用例是否能通过。
### 1.1 需求总结
阅读 PR 标题、描述和 diff,用自己的语言总结:
- 本次 PR 的核心目的是什么?
- 改动了哪些关键模块?
- 对外部接口或数据结构是否有变更?
### 1.2 测试推理
充当测试角色,针对 PR 的核心改动,**提出 3~5 个关键测例**,然后在代码中找到对应逻辑进行推理校验:
- 正常路径:主流程是否按预期运行?
- 边界条件:空值、极大值、并发等边界是否被处理?
- 异常路径:错误输入或依赖失败时行为是否正确?
**校验方式**:直接阅读相关代码,推理每个测例的执行路径,确认逻辑能通过。如果代码中存在对应单元测试,也一并检查。
### ⚠️ 阶段门控
如果第一阶段发现**需求理解存在严重歧义****核心逻辑存在明显错误**(如测例推理无法通过),**立即跳过后续阶段,直接进入"提交评论"步骤**,在报告中标明阻塞原因,请求作者澄清或修复后再继续审查。
---
## 第二到第六阶段:并行深度审查
第一阶段通过后,**以下五个阶段可以并行执行**,彼此独立,互不依赖。
---
### 第二阶段:后端代码质量 🔒
聚焦后端(`packages/service/``projects/app/src/pages/api/``projects/app/src/service/`)的质量问题,完成以下检查清单:
- [] [后端安全](./backend-quality/security.md)
- [] [后端错误处理](./backend-quality/error-handling.md)
- [] [后端性能](./backend-quality/performance.md)
---
### 第三阶段:前端代码质量 🎨
聚焦前端(`projects/app/src/``packages/web/`)的质量问题,完成以下检查清单:
- [] [React 性能](./frontend-quality/react-performance.md)
- [] [前端安全](./frontend-quality/security.md)
- [] [TypeScript 质量](./frontend-quality/typescript.md)
### 第四阶段:代码风格规范 📐
对照 FastGPT 各项规范逐一检查,完成以下检查清单:
- [] [API 路由开发规范](../../design/api/index.md)
- [] [前端组件规范](./style/front.md)
- [] [数据库规范](./style/db.md)
- [] [包结构规范](./style/package.md)
- [] [日志规范](./style/logger.md)
- [] [Service 解耦规范](./style/service-decoupling.md)
- [] [语法风格规范](./style/syntax.md)
---
### 第五阶段:测试覆盖 🧪
- 新增的核心业务逻辑是否有对应单元测试(`test/``projects/*/test/`)?
- 测试是否覆盖了正常路径、边界条件和错误路径?
- 如果没有测试,评估缺失测试的风险等级(高风险逻辑无测试应标记为 🔴)。
---
### 第六阶段:回归风险检测 🔄
- **接口兼容性**:对外 API 是否有 breaking change(字段删除、类型变更、行为变更)?
- **数据库兼容性**:schema 变更是否向后兼容?旧数据是否需要迁移?
- **依赖影响**:修改的公共模块(`packages/global/``packages/service/`)是否会影响其他调用方?
- **配置变更**:是否新增了必填配置项,且未提供默认值或迁移说明?
---
## 最终步骤:提交审查报告
### 收集所有阶段的问题
汇总各阶段发现的问题,按严重程度分类:
- 🔴 **严重**(必须修复才能合并)
- 🟡 **建议**(改进代码质量)
- 🟢 **可选**(优化建议)
### 提交行级代码评论
GitHub CLI 不支持行级评论,需通过 GitHub API 提交:
```bash
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
cat > /tmp/review-data.json << 'EOF'
{
"body": "## 📊 代码审查总结\n\n详细意见请查看下方行级评论。",
"event": "COMMENT",
"comments": [
{
"path": "文件路径",
"line": 行号,
"body": "🔴 **问题描述**\n\n**建议**:\n```typescript\n// 修复示例\n```"
}
]
}
EOF
gh api repos/$REPO/pulls/<number>/reviews \
--method POST \
--input /tmp/review-data.json
```
### 审查报告模板
```markdown
# PR Review: {PR Title}
## 📋 需求理解
{第一阶段总结:PR 的核心目的与改动范围}
## 🧪 逻辑验证
{列出提出的测例及推理结果,标明是否通过}
## ⚠️ 问题汇总
### 🔴 严重问题({count} 个,必须修复)
{问题列表,行级评论已标注}
### 🟡 建议改进({count} 个)
{问题列表}
### 🟢 可选优化({count} 个)
{问题列表}
## ✅ 做得好的地方
{列出值得肯定的实现}
## 🚀 审查结论
{通过 / 需修改 / 阻塞(说明原因)}
```
### 命令参考
| 场景 | 命令 |
|------|------|
| 请求修改 | `gh pr review <number> --request-changes --body-file /tmp/review.md` |
| 批准 PR | `gh pr review <number> --approve` |
| 仅评论 | `gh pr review <number> --comment --body-file /tmp/review.md` |
# 日志与可观测性审查标准
FastGPT 后端统一使用 `@fastgpt/service/common/logger`。日志需要可检索、可聚合、可回放,且避免敏感信息泄露。
## 1. 统一 Logger 接入
**审查要点**:
- ✅ 服务端统一使用 `@fastgpt/service/common/logger`,避免 `console.log`
- ✅ 启动入口只初始化一次 `configureLogger()`
- ✅ 使用 `getLogger(LogCategories.XXX)` 指定分类
- ✅ 未经讨论不要自定义 category 字符串数组
**示例**:
```ts
import { configureLogger, getLogger, LogCategories } from '@fastgpt/service/common/logger';
await configureLogger();
const logger = getLogger(LogCategories.SYSTEM);
logger.info('System initialized successfully');
```
## 2. 分类(Category)选择
**审查要点**:
-`SYSTEM` 用于系统级初始化与全局状态
-`INFRA.*` 用于数据库/缓存/队列/存储等
-`HTTP.*` 用于请求、响应、请求错误
-`MODULE.*` 用于业务模块(参考 `pages/api` 路径)
- ✅ 缺少分类时补充到 `packages/service/common/logger/categories.ts`
**问题示例**:
```ts
// ❌ 不规范:自定义字符串分类
const logger = getLogger(['custom', 'random']);
```
## 3. 结构化日志与消息规范
**审查要点**:
- ✅ 消息短、稳定、可检索
- ✅ 关键字段放在结构化对象中(id、状态、耗时、数量)
- ✅ 避免 `JSON.stringify` 拼接到消息里
- ✅ 避免在消息中包含用户输入或大段文本
**示例**:
```ts
// ✅ 推荐
logger.info('Vector queue task finished', { taskId, durationMs, count });
// ❌ 不推荐
logger.info(`Task finished: ${JSON.stringify({ taskId, durationMs, count })}`);
```
## 4. 错误日志标准
**审查要点**:
- ✅ 使用 `error` 字段记录 `Error` 对象,保留堆栈
- ✅ 错误日志包含关键上下文(teamId、datasetId、jobId 等)
- ✅ 捕获后必须记录或向上抛出,避免静默失败
**示例**:
```ts
try {
await doSomething();
} catch (error) {
logger.error('Do something failed', { error, teamId, datasetId });
throw error;
}
```
## 5. 日志等级规范
**审查要点**:
-`info` 用于阶段开始/完成
-`warn` 用于可恢复异常
-`error` 用于失败或影响流程的异常
- ✅ 高频循环日志必须使用 `debug/trace`
**示例**:
```ts
logger.info('Training started', { datasetId });
logger.debug('Training progress', { datasetId, step, total });
logger.warn('Retrying batch', { batchId, retryLeft });
logger.error('Training failed', { datasetId, error });
```
## 6. 请求/任务链路上下文
**审查要点**:
- ✅ HTTP 请求日志应带 `requestId`,优先使用 `withContext`
- ✅ 队列/定时任务日志应带 `jobId`/`queueName`
- ✅ 跨模块调用尽量保持同一上下文字段名
**示例**:
```ts
return withContext({ requestId }, async () => {
logger.info('Request received', { requestId, method, url });
});
```
## 7. 敏感信息与 OTEL
**审查要点**:
- ✅ 禁止输出 token、密钥、密码、完整对话内容
- ✅ 必须记录敏感信息时做脱敏或截断
- ✅ 需要阻止 OTEL 导出时,可添加 `fastgpt` 属性
**示例**:
```ts
logger.warn('Payload truncated for debug', {
fastgpt: true,
payloadPreview: payload.slice(0, 200)
});
```
# 代码规范
## 基础代码组织模式
采用 DDD 架构,按业务域 → 子功能 → 固定文件三层划分。
### 目录结构
```
packages/
├── global/core/ # 类型、常量(前后端共享)
│ ├── app/
│ │ ├── type.ts # 顶层聚合类型
│ │ ├── constants.ts
│ │ ├── workflow/
│ │ │ ├── type.ts
│ │ │ └── constants.ts
│ │ ├── version/
│ │ │ └── type.ts
│ │ └── evaluation/
│ │ └── type.ts
│ ├── chat/
│ ├── dataset/
│ └── plugin/
└── service/core/ # 后端业务逻辑(不可在前端引用)
├── app/
│ ├── schema.ts # App 主表 Mongoose Schema
│ ├── entity.ts # findById / create / updateById 等基础操作封装
│ ├── service.ts # 聚合业务逻辑(跨子功能协调),不允许互相引用,只允许单向依赖,跨 service 的协调需由上层通过 props 传入另一个 service 或者衍生方法
│ ├── auth.ts # 鉴权相关(如有)
│ ├── utils.ts # 纯函数工具,无副作用,可独立单测
│ ├── version/
│ │ ├── schema.ts
│ │ ├── entity.ts
│ │ ├── service.ts
│ │ └── utils.ts
│ ├── evaluation/
│ │ ├── schema.ts # 合并多个 schema 到单文件
│ │ ├── entity.ts
│ │ ├── service.ts
│ │ └── utils.ts
│ ├── logs/
│ └── tool/
│ ├── service.ts
│ └── utils.ts
├── chat/
├── dataset/
└── plugin/
```
### 叶子目录固定文件说明
| 文件 | 职责 |
|------|------|
| `schema.ts` | Mongoose Schema 定义,导出 Model 和 SchemaType |
| `entity.ts` | 数据访问封装:`findById``create``updateById` 等基础操作 |
| `service.ts` | 业务逻辑:调用 entity,跨模块协调,处理业务规则 |
| `utils.ts` | 纯函数工具,无副作用,可独立单测 |
```typescript
// entity.ts 示例 —— 只做数据访问,不含业务判断
export const findAppById = (id: string) =>
MongoApp.findById(id).lean();
export const createApp = (data: AppCreateParams, session?: ClientSession) =>
MongoApp.create([data], { session });
// service.ts 示例 —— 调用 entity,处理业务规则
export const createAppAndInitVersion = async (data: AppCreateParams, session?: ClientSession) => {
const app = await createApp(data, session);
await createVersion({ appId: app._id, ... }, session);
return app;
};
// service 需协同,通过 props 传入另一个 service 或者衍生方法。
const service1 = xxxx
const service2 = (props: {id:string; service1: typeof service1 }) => {
const data = findAppById(id)
return props.service1(data);
};
```
### 层级约束
- `global/core/` 只放类型和常量,**禁止**引入 mongoose、服务端 SDK
- `service/core/` 只在服务端使用,**禁止**`packages/web/` 或前端页面直接引用
- 子功能目录不超过 **3 层**嵌套
- 一个目录内无需拆子功能时,直接放 `schema.ts` + `entity.ts` + `service.ts` + `utils.ts`
- 多个 schema 文件(如 `evalSchema.ts` + `evalItemSchema.ts`**合并**到单个 `schema.ts`
## 代码风格
### 使用 `type` 进行类型声明,不使用 `interface`
```typescript
// ❌ 不好的实践
interface User {
id: string;
name: string;
}
// ✅ 好的实践
type User = {
id: string;
name: string;
}
```
---
### 使用 IIFE 写法来取代 if/else 进行变量条件赋值。
```typescript
// ❌ 不好的实践
if (condition) {
value = true;
} else {
value = false;
}
// ✅ 好的实践
const value = (() => {
if (condition) {
return true;
}
return false;
})();
```
---
### 类型推导:Zod schema 同时承担校验和类型
`z.infer` 从 schema 推导类型,不重复手写相同结构的 type。
```typescript
// ❌ 不好的实践
type MessageParam = { role: 'user' | 'assistant'; content: string };
const MessageParamSchema = z.object({ role: z.enum(['user', 'assistant']), content: z.string() });
// ✅ 好的实践
export const MessageParamSchema = z.discriminatedUnion('role', [...]);
export type MessageParam = z.infer<typeof MessageParamSchema>;
```
---
## 可选链调用回调
`?.()` 调用可选回调,取代 `if (fn) fn()` 的冗余写法。
```typescript
// ❌ 不好的实践
if (onProgress) {
onProgress({ phase: 'creatingContainer' });
}
// ✅ 好的实践
onProgress?.({ phase: 'creatingContainer' });
```
---
### 空值合并取默认值
`??` 取代 `||` 处理默认值,避免 `0``false``''` 被错误覆盖。
```typescript
// ❌ 不好的实践
const version = lastVersion?.version || 0; // version 为 0 时被误覆盖
const text = item?.value || '';
// ✅ 好的实践
const version = (lastVersion?.version ?? -1) + 1;
const text = item?.value ?? '';
```
---
### 解构重命名
同名变量来自多个来源时,解构时重命名,避免命名冲突。
```typescript
// ❌ 不好的实践
const r1 = await getSkillGuidance(...);
const r2 = await createLLMResponse(...);
const inputTokens = r1.usage.inputTokens + r2.usage.inputTokens;
// ✅ 好的实践
const { usage: guidanceUsage } = await getSkillGuidance(...);
const { usage: generateUsage } = await createLLMResponse(...);
const inputTokens = guidanceUsage.inputTokens + generateUsage.inputTokens;
```
---
### 类型守卫
`is` 关键字收窄 `unknown` / `any` 类型,替代强制断言。
```typescript
// ❌ 不好的实践
function process(value: unknown) {
const n = value as number; // 不安全
}
// ✅ 好的实践
const isValidNumber = (value: unknown): value is number =>
typeof value === 'number' && Number.isFinite(value);
if (isValidNumber(value)) {
// 此处 value 安全收窄为 number
}
```
---
### 非关键清理用 `.catch()` 链
次要的清理操作(不影响主流程)用 `.catch()` 吞掉错误,不污染主 try/catch。
```typescript
// ❌ 不好的实践
try {
await client.delete();
} catch {
// 清理失败,主流程中断
}
// ✅ 好的实践
await client.delete().catch(() => {});
```
---
### 函数参数不超过 2 个,多参数用对象传递
独立参数不超过 2 个,超过时改为对象参数,便于扩展且无需关心顺序。
```typescript
// ❌ 不好的实践
function createVersion(skillId: string, teamId: string, tmbId: string, version: number) {}
// ✅ 好的实践
function createVersion(data: { skillId: string; teamId: string; tmbId: string; version: number }) {}
```
---
### 数据写操作函数支持可选 session 参数
涉及数据库写操作的函数统一支持可选的 `session` 参数,便于上层组合事务。事务统一通过 `mongoSessionRun` 发起,内部自动处理 startTransaction / commit / abort / retry。
```typescript
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { type ClientSession } from '@fastgpt/service/common/mongo';
// entity.ts —— 基础操作透传 session
export const createVersion = (data: CreateVersionData, session?: ClientSession) =>
MongoAppVersion.create([data], { session });
// service.ts —— 需要事务时用 mongoSessionRun 包裹,外部已有 session 时直接传入
export const createAppAndInitVersion = async (
data: AppCreateParams,
session?: ClientSession
) => {
const create = async (session: ClientSession) => {
const app = await createApp(data, session);
await createVersion({ appId: app._id, version: 0 }, session);
return app;
};
if (session) {
return create(session);
} else {
return mongoSessionRun(create);
}
};
```
---
name: test-skill
description: 当用户需要编写一个单元测试时,触发该 skill,编写单元测试。
---
## When to Use This Skill
用户需要编写一个单元测试时,触发该 skill,编写单元测试。
## 测试文件位置
### packages 测试
packages 里的测试,写在 FastGPT/packages/xxx/test 目录下,子路径对应 packages 的目录结构。例如:
`packages/global/common/error/s3.ts`文件,对应的测例文件路径为 `packages/test/global/common/error/s3.test.ts`
并且,可以通过 @fastgpt 来导入 packages 里的文件。
例如:
```typescript
import { s3 } from '@fastgpt/global/common/error/s3';
```
### projects 测试
projects 里的测试,写在 FastGPT/projects/app/test 目录下,子路径对应 projects 的目录结构。
`projects/app/src/pages/api/core/dataset/collection/create.ts`文件,对应的测例文件路径为 `projects/app/test/api/core/dataset/collection/create.test.ts`
## 测试文件规则
### 通用规则
1. 每个文件,对应一个测试文件。每个函数对应不同的 describe 块。
2. 需覆盖 100% 行数和分支。
3. 测试文件尽可能不要引入第三方依赖库,使用较为原生的方式进行检查。如果需要引入第三方依赖库,则从对应文件里 export 依赖库给 test 使用。例如:
```ts
// FastGPT/packages/service/common/geo/index.ts
import type { NextApiRequest } from 'next';
// 同时导出一个依赖给 FastGPT/packages/service/test/common/geo/index.test.ts 使用
export type { NextApiRequest } from 'next';
```
4. 尽量减少函数 mock,如果是系统上原生可运行的函数,则无需 mock,只需要 mock 那些无法本地直接运行的依赖(比如需要远程服务,API 密钥之类的)
5. 对于 type.ts, constants.ts, schema.ts, *.schema.ts 文件,以及静态数据,直接跳过忽略。
6. 根据 [vitest.config.mts](../../../vitest.config.mts) 文件配置,跳过不需要测试的文件。
7. [Mock.ts](../../../test/mocks/index.ts) 文件里,包含了全局 mock 的内容,在编写测试时,请勿重复 mock。理论上,测试里可以 mock 运行各类 infra。
### 基础函数文件测试
尽量不要 mock,而是完整的运行其逻辑进行测试。
### 带 API 请求的函数
mock 对应的 API 请求进行测试。
## 编写流程
**一、任务准备**
1. 获取所需要编写的测试文件。
2. 创建任务清单,来逐个完成每个文件的测例编写。
**二、测例编写**
不同测例文件,可以并行进行编写。
1. 检查对应的 .test.ts 测试文件,如果没有则创建。
2. 思考和分析代码后编写测试样例。
3. 检查 TS 错误,确保无 ts 报错。
4. 完成所有测试文件编写
**三、结果验证**
1. 调用`pnpm test <file-path> <test-name>`来运行测试并检查覆盖率,确保每个文件的覆盖率达到 90% 以上。
2. 如果测试不通过,则根据错误信息检查代码逻辑或者测试用例。
3. 如需二次修改,则回到”二、测例编写“。
## 单测包含哪些场景
1. 基础场景
2. 复杂场景
3. 边界值
4. 安全边界情况(死循环、系统崩溃、超大数据等)
5. 异常场景
## 常用命令
```shell
# 运行所有测试
pnpm test
# 运行指定测试文件(file-path 填完整文件路径)
pnpm test <file-path>
# 运行指定测试文件的指定测试
pnpm test <file-path> <test-name>
```
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment