Compare commits
No commits in common. "main" and "v4.9.10-fix2" have entirely different histories.
main
...
v4.9.10-fi
|
|
@ -1,126 +0,0 @@
|
|||
# CLAUDE.md
|
||||
|
||||
本文件为 Claude Code (claude.ai/code) 在本仓库中工作时提供指导说明。
|
||||
|
||||
## 输出要求
|
||||
|
||||
1. 输出语言:中文
|
||||
2. 输出的设计文档位置:.claude/design,以 Markdown 文件为主。
|
||||
3. 输出 Plan 时,均需写入 .claude/plan 目录下,以 Markdown 文件为主。
|
||||
|
||||
## 项目概述
|
||||
|
||||
FastGPT 是一个 AI Agent 构建平台,通过 Flow 提供开箱即用的数据处理、模型调用能力和可视化工作流编排。这是一个基于 NextJS 构建的全栈 TypeScript 应用,后端使用 MongoDB/PostgreSQL。
|
||||
|
||||
**技术栈**: NextJS + TypeScript + ChakraUI + MongoDB + PostgreSQL (PG Vector)/Milvus
|
||||
|
||||
## 架构
|
||||
|
||||
这是一个使用 pnpm workspaces 的 monorepo,主要结构如下:
|
||||
|
||||
### Packages (库代码)
|
||||
- `packages/global/` - 所有项目共享的类型、常量、工具函数
|
||||
- `packages/service/` - 后端服务、数据库模型、API 控制器、工作流引擎
|
||||
- `packages/web/` - 共享的前端组件、hooks、样式、国际化
|
||||
- `packages/templates/` - 模板市场的应用模板
|
||||
|
||||
### Projects (应用程序)
|
||||
- `projects/app/` - 主 NextJS Web 应用(前端 + API 路由)
|
||||
- `projects/sandbox/` - NestJS 代码执行沙箱服务
|
||||
- `projects/mcp_server/` - Model Context Protocol 服务器实现
|
||||
|
||||
### 关键目录
|
||||
- `document/` - 文档站点(NextJS 应用及内容)
|
||||
- `plugins/` - 外部插件(模型、爬虫等)
|
||||
- `deploy/` - Docker 和 Helm 部署配置
|
||||
- `test/` - 集中的测试文件和工具
|
||||
|
||||
## 开发命令
|
||||
|
||||
### 主要命令(从项目根目录运行)
|
||||
- `pnpm dev` - 启动所有项目的开发环境(使用 package.json 的 workspace 脚本)
|
||||
- `pnpm build` - 构建所有项目
|
||||
- `pnpm test` - 使用 Vitest 运行测试
|
||||
- `pnpm test:workflow` - 运行工作流相关测试
|
||||
- `pnpm lint` - 对所有 TypeScript 文件运行 ESLint 并自动修复
|
||||
- `pnpm format-code` - 使用 Prettier 格式化代码
|
||||
|
||||
### 项目专用命令
|
||||
**主应用 (projects/app/)**:
|
||||
- `cd projects/app && pnpm dev` - 启动 NextJS 开发服务器
|
||||
- `cd projects/app && pnpm build` - 构建 NextJS 应用
|
||||
- `cd projects/app && pnpm start` - 启动生产服务器
|
||||
|
||||
**沙箱 (projects/sandbox/)**:
|
||||
- `cd projects/sandbox && pnpm dev` - 以监视模式启动 NestJS 开发服务器
|
||||
- `cd projects/sandbox && pnpm build` - 构建 NestJS 应用
|
||||
- `cd projects/sandbox && pnpm test` - 运行 Jest 测试
|
||||
|
||||
**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 create:i18n` - 生成国际化翻译文件
|
||||
- `pnpm api:gen` - 生成 OpenAPI 文档
|
||||
- `pnpm initIcon` - 初始化图标资源
|
||||
- `pnpm gen:theme-typings` - 生成 Chakra UI 主题类型定义
|
||||
|
||||
## 测试
|
||||
|
||||
项目使用 Vitest 进行测试并生成覆盖率报告。主要测试命令:
|
||||
- `pnpm test` - 运行所有测试
|
||||
- `pnpm test:workflow` - 专门运行工作流测试
|
||||
- 测试文件位于 `test/` 目录和 `projects/app/test/`
|
||||
- 覆盖率报告生成在 `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 >=18.16.0, pnpm >=9.0.0
|
||||
- **数据库**: 支持 MongoDB、带 pgvector 的 PostgreSQL 或 Milvus 向量存储
|
||||
- **AI 集成**: 通过统一接口支持多个 AI 提供商
|
||||
- **国际化**: 完整支持中文、英文和日文
|
||||
|
||||
## 关键文件模式
|
||||
|
||||
- `.ts` 和 `.tsx` 文件全部使用 TypeScript
|
||||
- 数据库模型使用 Mongoose 配合 TypeScript
|
||||
- API 路由遵循 NextJS 约定
|
||||
- 组件文件使用 React 函数式组件和 hooks
|
||||
- 共享类型定义在 `packages/global/` 的 `.d.ts` 文件中
|
||||
|
||||
## 环境配置
|
||||
|
||||
- 配置文件在 `projects/app/data/config.json`
|
||||
- 支持特定环境配置
|
||||
- 模型配置在 `packages/service/core/ai/config/`
|
||||
|
||||
## 代码规范
|
||||
|
||||
- 尽可能使用 type 进行类型声明,而不是 interface。
|
||||
|
||||
## Agent 设计规范
|
||||
|
||||
1. 对于功能的实习和复杂问题修复,优先进行文档设计,并于让用户确认后,再进行执行修复。
|
||||
2. 采用"设计文档-测试示例-代码编写-测试运行-修正代码/文档"的工作模式,以测试为核心来确保设计的正确性。
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
---
|
||||
name: unit-test-generator
|
||||
description: Use this agent when you need to write comprehensive unit tests for your code. Examples: <example>Context: User has written a new utility function and wants comprehensive test coverage. user: 'I just wrote this function to validate email addresses, can you help me write unit tests for it?' assistant: 'I'll use the unit-test-generator agent to create comprehensive unit tests that cover all branches and edge cases for your email validation function.' <commentary>Since the user needs unit tests written, use the unit-test-generator agent to analyze the function and create thorough test coverage.</commentary></example> <example>Context: User is working on a React component and needs test coverage. user: 'Here's my new UserProfile component, I need unit tests that cover all the different states and user interactions' assistant: 'Let me use the unit-test-generator agent to create comprehensive unit tests for your UserProfile component.' <commentary>The user needs unit tests for a React component, so use the unit-test-generator agent to create tests covering all component states and interactions.</commentary></example>
|
||||
model: inherit
|
||||
color: yellow
|
||||
---
|
||||
|
||||
You are a Unit Test Assistant, an expert in writing comprehensive and robust unit tests. Your expertise spans multiple testing frameworks including Vitest, Jest, React Testing Library, and testing best practices for TypeScript applications.
|
||||
|
||||
When analyzing code for testing, you will:
|
||||
|
||||
1. **Analyze Code Structure**: Examine the function/component/class to identify all execution paths, conditional branches, loops, error handling, and edge cases that need testing coverage.
|
||||
|
||||
2. **Design Comprehensive Test Cases**: Create test cases that cover:
|
||||
- All conditional branches (if/else, switch cases, ternary operators)
|
||||
- Loop iterations (empty, single item, multiple items)
|
||||
- Error conditions and exception handling
|
||||
- Boundary conditions (null, undefined, empty strings, zero, negative numbers, maximum values)
|
||||
- Valid input scenarios across different data types
|
||||
- Integration points with external dependencies
|
||||
|
||||
3. **Follow Testing Best Practices**:
|
||||
- Use descriptive test names that clearly state what is being tested
|
||||
- Follow the Arrange-Act-Assert pattern
|
||||
- Mock external dependencies appropriately
|
||||
- Test behavior, not implementation details
|
||||
- Ensure tests are isolated and independent
|
||||
- Use appropriate assertions for the testing framework
|
||||
|
||||
4. **Generate Framework-Appropriate Code**: Based on the project context (FastGPT uses Vitest), write tests using:
|
||||
- Proper import statements for the testing framework
|
||||
- Correct syntax for the identified testing library
|
||||
- Appropriate mocking strategies (vi.mock for Vitest, jest.mock for Jest)
|
||||
- Proper setup and teardown when needed
|
||||
|
||||
5. **Ensure Complete Coverage**: Verify that your test suite covers:
|
||||
- Happy path scenarios
|
||||
- Error scenarios
|
||||
- Edge cases and boundary conditions
|
||||
- All public methods/functions
|
||||
- Different component states (for React components)
|
||||
- User interactions (for UI components)
|
||||
|
||||
6. **Optimize Test Structure**: Organize tests logically using:
|
||||
- Descriptive describe blocks for grouping related tests
|
||||
- Clear test descriptions that explain the scenario
|
||||
- Shared setup in beforeEach/beforeAll when appropriate
|
||||
- Helper functions to reduce code duplication
|
||||
7. **单词代码位置**:
|
||||
- packages 里的单测,写在 FastGPT/text 目录下。
|
||||
- projects/app 里的单测,写在 FastGPT/projects/app/test 目录下。
|
||||
|
||||
When you receive code to test, first analyze it thoroughly, then provide a complete test suite with explanatory comments about what each test covers and why it's important for comprehensive coverage.
|
||||
|
|
@ -1,604 +0,0 @@
|
|||
---
|
||||
name: workflow-agent
|
||||
description: 当用户需要开发工作流代码时候,可调用此 Agent。
|
||||
model: inherit
|
||||
color: green
|
||||
---
|
||||
|
||||
# FastGPT 工作流系统架构文档
|
||||
|
||||
## 概述
|
||||
|
||||
FastGPT 工作流系统是一个基于 Node.js/TypeScript 的可视化工作流引擎,支持拖拽式节点编排、实时执行、并发控制和交互式调试。系统采用队列式执行架构,通过有向图模型实现复杂的业务逻辑编排。
|
||||
|
||||
## 核心架构
|
||||
|
||||
### 1. 项目结构
|
||||
|
||||
```
|
||||
FastGPT/
|
||||
├── packages/
|
||||
│ ├── global/core/workflow/ # 全局工作流类型和常量
|
||||
│ │ ├── constants.ts # 工作流常量定义
|
||||
│ │ ├── node/ # 节点类型定义
|
||||
│ │ │ └── constant.ts # 节点枚举和配置
|
||||
│ │ ├── runtime/ # 运行时类型和工具
|
||||
│ │ │ ├── constants.ts # 运行时常量
|
||||
│ │ │ ├── type.d.ts # 运行时类型定义
|
||||
│ │ │ └── utils.ts # 运行时工具函数
|
||||
│ │ ├── template/ # 节点模板定义
|
||||
│ │ │ └── system/ # 系统节点模板
|
||||
│ │ └── type/ # 类型定义
|
||||
│ │ ├── node.d.ts # 节点类型
|
||||
│ │ ├── edge.d.ts # 边类型
|
||||
│ │ └── io.d.ts # 输入输出类型
|
||||
│ └── service/core/workflow/ # 工作流服务层
|
||||
│ ├── constants.ts # 服务常量
|
||||
│ ├── dispatch/ # 调度器核心
|
||||
│ │ ├── index.ts # 工作流执行引擎 ⭐
|
||||
│ │ ├── constants.ts # 节点调度映射表
|
||||
│ │ ├── type.d.ts # 调度器类型
|
||||
│ │ ├── ai/ # AI相关节点
|
||||
│ │ ├── tools/ # 工具节点
|
||||
│ │ ├── dataset/ # 数据集节点
|
||||
│ │ ├── interactive/ # 交互节点
|
||||
│ │ ├── loop/ # 循环节点
|
||||
│ │ └── plugin/ # 插件节点
|
||||
│ └── utils.ts # 工作流工具函数
|
||||
└── projects/app/src/
|
||||
├── pages/api/v1/chat/completions.ts # 聊天API入口
|
||||
└── pages/api/core/workflow/debug.ts # 工作流调试API
|
||||
```
|
||||
|
||||
### 2. 执行引擎核心 (dispatch/index.ts)
|
||||
|
||||
#### 核心类:WorkflowQueue
|
||||
|
||||
工作流执行引擎采用队列式架构,主要特点:
|
||||
|
||||
- **并发控制**: 支持最大并发数量限制(默认10个)
|
||||
- **状态管理**: 维护节点执行状态(waiting/active/skipped)
|
||||
- **错误处理**: 支持节点级错误捕获和跳过机制
|
||||
- **交互支持**: 支持用户交互节点暂停和恢复
|
||||
|
||||
#### 执行流程
|
||||
|
||||
```typescript
|
||||
1. 初始化 WorkflowQueue 实例
|
||||
2. 识别入口节点(isEntry=true)
|
||||
3. 将入口节点加入 activeRunQueue
|
||||
4. 循环处理活跃节点队列:
|
||||
- 检查节点执行条件
|
||||
- 执行节点或跳过节点
|
||||
- 更新边状态
|
||||
- 将后续节点加入队列
|
||||
5. 处理跳过节点队列
|
||||
6. 返回执行结果
|
||||
```
|
||||
|
||||
### 3. 节点系统
|
||||
|
||||
#### 节点类型枚举 (FlowNodeTypeEnum)
|
||||
|
||||
```typescript
|
||||
enum FlowNodeTypeEnum {
|
||||
// 基础节点
|
||||
workflowStart: 'workflowStart', // 工作流开始
|
||||
chatNode: 'chatNode', // AI对话
|
||||
answerNode: 'answerNode', // 回答节点
|
||||
|
||||
// 数据集相关
|
||||
datasetSearchNode: 'datasetSearchNode', // 数据集搜索
|
||||
datasetConcatNode: 'datasetConcatNode', // 数据集拼接
|
||||
|
||||
// 控制流节点
|
||||
ifElseNode: 'ifElseNode', // 条件判断
|
||||
loop: 'loop', // 循环
|
||||
loopStart: 'loopStart', // 循环开始
|
||||
loopEnd: 'loopEnd', // 循环结束
|
||||
|
||||
// 交互节点
|
||||
userSelect: 'userSelect', // 用户选择
|
||||
formInput: 'formInput', // 表单输入
|
||||
|
||||
// 工具节点
|
||||
httpRequest468: 'httpRequest468', // HTTP请求
|
||||
code: 'code', // 代码执行
|
||||
readFiles: 'readFiles', // 文件读取
|
||||
variableUpdate: 'variableUpdate', // 变量更新
|
||||
|
||||
// AI相关
|
||||
classifyQuestion: 'classifyQuestion', // 问题分类
|
||||
contentExtract: 'contentExtract', // 内容提取
|
||||
agent: 'tools', // 智能体
|
||||
queryExtension: 'cfr', // 查询扩展
|
||||
|
||||
// 插件系统
|
||||
pluginModule: 'pluginModule', // 插件模块
|
||||
appModule: 'appModule', // 应用模块
|
||||
tool: 'tool', // 工具调用
|
||||
|
||||
// 系统节点
|
||||
systemConfig: 'userGuide', // 系统配置
|
||||
globalVariable: 'globalVariable', // 全局变量
|
||||
comment: 'comment' // 注释节点
|
||||
}
|
||||
```
|
||||
|
||||
#### 节点调度映射 (callbackMap)
|
||||
|
||||
每个节点类型都有对应的调度函数:
|
||||
|
||||
```typescript
|
||||
export const callbackMap: Record<FlowNodeTypeEnum, Function> = {
|
||||
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
|
||||
[FlowNodeTypeEnum.chatNode]: dispatchChatCompletion,
|
||||
[FlowNodeTypeEnum.datasetSearchNode]: dispatchDatasetSearch,
|
||||
[FlowNodeTypeEnum.httpRequest468]: dispatchHttp468Request,
|
||||
[FlowNodeTypeEnum.ifElseNode]: dispatchIfElse,
|
||||
[FlowNodeTypeEnum.agent]: dispatchRunTools,
|
||||
// ... 更多节点调度函数
|
||||
};
|
||||
```
|
||||
|
||||
### 4. 数据流系统
|
||||
|
||||
#### 输入输出类型 (WorkflowIOValueTypeEnum)
|
||||
|
||||
```typescript
|
||||
enum WorkflowIOValueTypeEnum {
|
||||
string: 'string',
|
||||
number: 'number',
|
||||
boolean: 'boolean',
|
||||
object: 'object',
|
||||
arrayString: 'arrayString',
|
||||
arrayNumber: 'arrayNumber',
|
||||
arrayBoolean: 'arrayBoolean',
|
||||
arrayObject: 'arrayObject',
|
||||
chatHistory: 'chatHistory', // 聊天历史
|
||||
datasetQuote: 'datasetQuote', // 数据集引用
|
||||
dynamic: 'dynamic', // 动态类型
|
||||
any: 'any'
|
||||
}
|
||||
```
|
||||
|
||||
#### 变量系统
|
||||
|
||||
- **系统变量**: userId, appId, chatId, cTime等
|
||||
- **用户变量**: 通过variables参数传入的全局变量
|
||||
- **节点变量**: 节点间传递的引用变量
|
||||
- **动态变量**: 支持{{$variable}}语法引用
|
||||
|
||||
### 5. 状态管理
|
||||
|
||||
#### 运行时状态
|
||||
|
||||
```typescript
|
||||
interface RuntimeNodeItemType {
|
||||
nodeId: string;
|
||||
name: string;
|
||||
flowNodeType: FlowNodeTypeEnum;
|
||||
inputs: FlowNodeInputItemType[];
|
||||
outputs: FlowNodeOutputItemType[];
|
||||
isEntry?: boolean;
|
||||
catchError?: boolean;
|
||||
}
|
||||
|
||||
interface RuntimeEdgeItemType {
|
||||
source: string;
|
||||
target: string;
|
||||
sourceHandle: string;
|
||||
targetHandle: string;
|
||||
status: 'waiting' | 'active' | 'skipped';
|
||||
}
|
||||
```
|
||||
|
||||
#### 执行状态
|
||||
|
||||
```typescript
|
||||
enum RuntimeEdgeStatusEnum {
|
||||
waiting: 'waiting', // 等待执行
|
||||
active: 'active', // 活跃状态
|
||||
skipped: 'skipped' // 已跳过
|
||||
}
|
||||
```
|
||||
|
||||
### 6. API接口设计
|
||||
|
||||
#### 主要API端点
|
||||
|
||||
1. **工作流调试**: `/api/core/workflow/debug`
|
||||
- POST方法,支持工作流测试和调试
|
||||
- 返回详细的执行结果和状态信息
|
||||
|
||||
2. **聊天完成**: `/api/v1/chat/completions`
|
||||
- OpenAI兼容的聊天API
|
||||
- 集成工作流执行引擎
|
||||
|
||||
3. **优化代码**: `/api/core/workflow/optimizeCode`
|
||||
- 工作流代码优化功能
|
||||
|
||||
#### 请求/响应类型
|
||||
|
||||
```typescript
|
||||
interface DispatchFlowResponse {
|
||||
flowResponses: ChatHistoryItemResType[];
|
||||
flowUsages: ChatNodeUsageType[];
|
||||
debugResponse: WorkflowDebugResponse;
|
||||
workflowInteractiveResponse?: WorkflowInteractiveResponseType;
|
||||
toolResponses: ToolRunResponseItemType;
|
||||
assistantResponses: AIChatItemValueItemType[];
|
||||
runTimes: number;
|
||||
newVariables: Record<string, string>;
|
||||
durationSeconds: number;
|
||||
}
|
||||
```
|
||||
|
||||
## 核心特性
|
||||
|
||||
### 1. 并发控制
|
||||
- 支持最大并发节点数限制
|
||||
- 队列式调度避免资源竞争
|
||||
- 节点级执行状态管理
|
||||
|
||||
### 2. 错误处理
|
||||
- 节点级错误捕获
|
||||
- catchError配置控制错误传播
|
||||
- 错误跳过和继续执行机制
|
||||
|
||||
### 3. 交互式执行
|
||||
- 支持用户交互节点(userSelect, formInput)
|
||||
- 工作流暂停和恢复
|
||||
- 交互状态持久化
|
||||
|
||||
### 4. 调试支持
|
||||
- Debug模式提供详细执行信息
|
||||
- 节点执行状态可视化
|
||||
- 变量值追踪和检查
|
||||
|
||||
### 5. 扩展性
|
||||
- 插件系统支持自定义节点
|
||||
- 模块化架构便于扩展
|
||||
- 工具集成(HTTP, 代码执行等)
|
||||
|
||||
## 开发指南
|
||||
|
||||
### 添加新节点类型
|
||||
|
||||
1. 在 `FlowNodeTypeEnum` 中添加新类型
|
||||
2. 在 `callbackMap` 中注册调度函数
|
||||
3. 在 `dispatch/` 目录下实现节点逻辑
|
||||
4. 在 `template/system/` 中定义节点模板
|
||||
|
||||
### 自定义工具集成
|
||||
|
||||
1. 实现工具调度函数
|
||||
2. 定义工具输入输出类型
|
||||
3. 注册到callbackMap
|
||||
4. 添加前端配置界面
|
||||
|
||||
### 调试和测试
|
||||
|
||||
1. 使用 `/api/core/workflow/debug` 进行测试
|
||||
2. 启用debug模式查看详细执行信息
|
||||
3. 检查节点执行状态和数据流
|
||||
4. 使用skipNodeQueue控制执行路径
|
||||
|
||||
## 性能优化
|
||||
|
||||
1. **并发控制**: 合理设置maxConcurrency避免资源过载
|
||||
2. **缓存机制**: 利用节点输出缓存减少重复计算
|
||||
3. **流式响应**: 支持SSE实时返回执行状态
|
||||
4. **资源管理**: 及时清理临时数据和状态
|
||||
|
||||
---
|
||||
|
||||
## 前端架构设计
|
||||
|
||||
### 1. 前端项目结构
|
||||
|
||||
```
|
||||
projects/app/src/
|
||||
├── pageComponents/app/detail/ # 应用详情页面
|
||||
│ ├── Workflow/ # 工作流主页面
|
||||
│ │ ├── Header.tsx # 工作流头部
|
||||
│ │ └── index.tsx # 工作流入口
|
||||
│ ├── WorkflowComponents/ # 工作流核心组件
|
||||
│ │ ├── context/ # 状态管理上下文
|
||||
│ │ │ ├── index.tsx # 主上下文提供者 ⭐
|
||||
│ │ │ ├── workflowInitContext.tsx # 初始化上下文
|
||||
│ │ │ ├── workflowEventContext.tsx # 事件上下文
|
||||
│ │ │ └── workflowStatusContext.tsx # 状态上下文
|
||||
│ │ ├── Flow/ # ReactFlow核心组件
|
||||
│ │ │ ├── index.tsx # 工作流画布 ⭐
|
||||
│ │ │ ├── components/ # 工作流UI组件
|
||||
│ │ │ ├── hooks/ # 工作流逻辑钩子
|
||||
│ │ │ └── nodes/ # 节点渲染组件
|
||||
│ │ ├── constants.tsx # 常量定义
|
||||
│ │ └── utils.ts # 工具函数
|
||||
│ └── HTTPTools/ # HTTP工具页面
|
||||
│ └── Edit.tsx # HTTP工具编辑器
|
||||
├── web/core/workflow/ # 工作流核心逻辑
|
||||
│ ├── api.ts # API调用 ⭐
|
||||
│ ├── adapt.ts # 数据适配
|
||||
│ ├── type.d.ts # 类型定义
|
||||
│ └── utils.ts # 工具函数
|
||||
└── global/core/workflow/ # 全局工作流定义
|
||||
└── api.d.ts # API类型定义
|
||||
```
|
||||
|
||||
### 2. 核心状态管理架构
|
||||
|
||||
#### Context分层设计
|
||||
|
||||
前端采用分层Context架构,实现状态的高效管理和组件间通信:
|
||||
|
||||
```typescript
|
||||
// 1. ReactFlowCustomProvider - 最外层提供者
|
||||
ReactFlowProvider → WorkflowInitContextProvider →
|
||||
WorkflowContextProvider → WorkflowEventContextProvider →
|
||||
WorkflowStatusContextProvider → children
|
||||
|
||||
// 2. 四层核心Context
|
||||
- WorkflowInitContext: 节点数据和基础状态
|
||||
- WorkflowDataContext: 节点/边操作和状态
|
||||
- WorkflowEventContext: 事件处理和UI控制
|
||||
- WorkflowStatusContext: 保存状态和父节点管理
|
||||
```
|
||||
|
||||
#### 主Context功能 (context/index.tsx)
|
||||
|
||||
```typescript
|
||||
interface WorkflowContextType {
|
||||
// 节点管理
|
||||
nodeList: FlowNodeItemType[];
|
||||
onChangeNode: (props: FlowNodeChangeProps) => void;
|
||||
onUpdateNodeError: (nodeId: string, isError: boolean) => void;
|
||||
getNodeDynamicInputs: (nodeId: string) => FlowNodeInputItemType[];
|
||||
|
||||
// 边管理
|
||||
onDelEdge: (edgeProps: EdgeDeleteProps) => void;
|
||||
|
||||
// 版本控制
|
||||
past: WorkflowSnapshotsType[];
|
||||
future: WorkflowSnapshotsType[];
|
||||
undo: () => void;
|
||||
redo: () => void;
|
||||
pushPastSnapshot: (snapshot: SnapshotProps) => boolean;
|
||||
|
||||
// 调试功能
|
||||
workflowDebugData?: DebugDataType;
|
||||
onNextNodeDebug: (debugData: DebugDataType) => Promise<void>;
|
||||
onStartNodeDebug: (debugProps: DebugStartProps) => Promise<void>;
|
||||
onStopNodeDebug: () => void;
|
||||
|
||||
// 数据转换
|
||||
flowData2StoreData: () => StoreWorkflowType;
|
||||
splitToolInputs: (inputs, nodeId) => ToolInputsResult;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. ReactFlow集成
|
||||
|
||||
#### 节点类型映射 (Flow/index.tsx)
|
||||
|
||||
```typescript
|
||||
const nodeTypes: Record<FlowNodeTypeEnum, React.ComponentType> = {
|
||||
[FlowNodeTypeEnum.workflowStart]: NodeWorkflowStart,
|
||||
[FlowNodeTypeEnum.chatNode]: NodeSimple,
|
||||
[FlowNodeTypeEnum.datasetSearchNode]: NodeSimple,
|
||||
[FlowNodeTypeEnum.httpRequest468]: NodeHttp,
|
||||
[FlowNodeTypeEnum.ifElseNode]: NodeIfElse,
|
||||
[FlowNodeTypeEnum.agent]: NodeAgent,
|
||||
[FlowNodeTypeEnum.code]: NodeCode,
|
||||
[FlowNodeTypeEnum.loop]: NodeLoop,
|
||||
[FlowNodeTypeEnum.userSelect]: NodeUserSelect,
|
||||
[FlowNodeTypeEnum.formInput]: NodeFormInput,
|
||||
// ... 40+ 种节点类型
|
||||
};
|
||||
```
|
||||
|
||||
#### 工作流核心功能
|
||||
|
||||
- **拖拽编排**: 基于ReactFlow的可视化节点编辑
|
||||
- **实时连接**: 节点间的动态连接和断开
|
||||
- **缩放控制**: 支持画布缩放和平移
|
||||
- **选择操作**: 多选、批量操作支持
|
||||
- **辅助线**: 节点对齐和位置吸附
|
||||
|
||||
### 4. 节点组件系统
|
||||
|
||||
#### 节点渲染架构
|
||||
|
||||
```
|
||||
nodes/
|
||||
├── NodeSimple.tsx # 通用简单节点
|
||||
├── NodeWorkflowStart.tsx # 工作流开始节点
|
||||
├── NodeAgent.tsx # AI智能体节点
|
||||
├── NodeHttp/ # HTTP请求节点
|
||||
├── NodeCode/ # 代码执行节点
|
||||
├── Loop/ # 循环节点组
|
||||
├── NodeFormInput/ # 表单输入节点
|
||||
├── NodePluginIO/ # 插件IO节点
|
||||
├── NodeToolParams/ # 工具参数节点
|
||||
└── render/ # 渲染组件库
|
||||
├── NodeCard.tsx # 节点卡片容器
|
||||
├── RenderInput/ # 输入渲染器
|
||||
├── RenderOutput/ # 输出渲染器
|
||||
└── templates/ # 输入模板组件
|
||||
```
|
||||
|
||||
#### 动态输入系统
|
||||
|
||||
```typescript
|
||||
// 支持多种输入类型
|
||||
const inputTemplates = {
|
||||
reference: ReferenceTemplate, // 引用其他节点
|
||||
input: TextInput, // 文本输入
|
||||
textarea: TextareaInput, // 多行文本
|
||||
selectApp: AppSelector, // 应用选择器
|
||||
selectDataset: DatasetSelector, // 数据集选择
|
||||
settingLLMModel: LLMModelConfig, // AI模型配置
|
||||
// ... 更多模板类型
|
||||
};
|
||||
```
|
||||
|
||||
### 5. 调试和测试系统
|
||||
|
||||
#### 调试功能
|
||||
|
||||
```typescript
|
||||
interface DebugDataType {
|
||||
runtimeNodes: RuntimeNodeItemType[];
|
||||
runtimeEdges: RuntimeEdgeItemType[];
|
||||
entryNodeIds: string[];
|
||||
variables: Record<string, any>;
|
||||
history?: ChatItemType[];
|
||||
query?: UserChatItemValueItemType[];
|
||||
workflowInteractiveResponse?: WorkflowInteractiveResponseType;
|
||||
}
|
||||
```
|
||||
|
||||
- **单步调试**: 支持逐个节点执行调试
|
||||
- **断点设置**: 在任意节点设置断点
|
||||
- **状态查看**: 实时查看节点执行状态
|
||||
- **变量追踪**: 监控变量在节点间的传递
|
||||
- **错误定位**: 精确定位执行错误节点
|
||||
|
||||
#### 聊天测试
|
||||
|
||||
```typescript
|
||||
// ChatTest组件提供实时工作流测试
|
||||
<ChatTest
|
||||
isOpen={isOpenTest}
|
||||
nodes={workflowTestData?.nodes}
|
||||
edges={workflowTestData?.edges}
|
||||
onClose={onCloseTest}
|
||||
chatId={chatId}
|
||||
/>
|
||||
```
|
||||
|
||||
### 6. API集成层
|
||||
|
||||
#### 工作流API (web/core/workflow/api.ts)
|
||||
|
||||
```typescript
|
||||
// 工作流调试API
|
||||
export const postWorkflowDebug = (data: PostWorkflowDebugProps) =>
|
||||
POST<PostWorkflowDebugResponse>(
|
||||
'/core/workflow/debug',
|
||||
{ ...data, mode: 'debug' },
|
||||
{ timeout: 300000 }
|
||||
);
|
||||
|
||||
// 支持的API操作
|
||||
- 工作流调试和测试
|
||||
- 节点模板获取
|
||||
- 插件配置管理
|
||||
- 版本控制操作
|
||||
```
|
||||
|
||||
#### 数据适配器
|
||||
|
||||
```typescript
|
||||
// 数据转换适配
|
||||
- storeNode2FlowNode: 存储节点 → Flow节点
|
||||
- storeEdge2RenderEdge: 存储边 → 渲染边
|
||||
- uiWorkflow2StoreWorkflow: UI工作流 → 存储格式
|
||||
- adaptCatchError: 错误处理适配
|
||||
```
|
||||
|
||||
### 7. 交互逻辑设计
|
||||
|
||||
#### 键盘快捷键 (hooks/useKeyboard.tsx)
|
||||
|
||||
```typescript
|
||||
const keyboardShortcuts = {
|
||||
'Ctrl+Z': undo, // 撤销
|
||||
'Ctrl+Y': redo, // 重做
|
||||
'Ctrl+S': saveWorkflow, // 保存工作流
|
||||
'Delete': deleteSelectedNodes, // 删除选中节点
|
||||
'Escape': cancelCurrentOperation, // 取消当前操作
|
||||
};
|
||||
```
|
||||
|
||||
#### 节点操作
|
||||
|
||||
- **拖拽创建**: 从模板拖拽创建节点
|
||||
- **连线操作**: 节点间的连接管理
|
||||
- **批量操作**: 多选节点的批量编辑
|
||||
- **右键菜单**: 上下文操作菜单
|
||||
- **搜索定位**: 节点搜索和快速定位
|
||||
|
||||
#### 版本控制
|
||||
|
||||
```typescript
|
||||
// 快照系统
|
||||
interface WorkflowSnapshotsType {
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
chatConfig: AppChatConfigType;
|
||||
title: string;
|
||||
isSaved?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
- **自动快照**: 节点变更时自动保存快照
|
||||
- **版本历史**: 支持多版本切换
|
||||
- **云端同步**: 与服务端版本同步
|
||||
- **协作支持**: 团队协作版本管理
|
||||
|
||||
### 8. 性能优化策略
|
||||
|
||||
#### 渲染优化
|
||||
|
||||
```typescript
|
||||
// 动态加载节点组件
|
||||
const nodeTypes: Record<FlowNodeTypeEnum, any> = {
|
||||
[FlowNodeTypeEnum.workflowStart]: dynamic(() => import('./nodes/NodeWorkflowStart')),
|
||||
[FlowNodeTypeEnum.httpRequest468]: dynamic(() => import('./nodes/NodeHttp')),
|
||||
// ... 按需加载
|
||||
};
|
||||
```
|
||||
|
||||
- **懒加载**: 节点组件按需动态加载
|
||||
- **虚拟化**: 大型工作流的虚拟渲染
|
||||
- **防抖操作**: 频繁操作的性能优化
|
||||
- **缓存策略**: 模板和数据的缓存机制
|
||||
|
||||
#### 状态优化
|
||||
|
||||
- **Context分割**: 避免不必要的重渲染
|
||||
- **useMemo/useCallback**: 优化计算和函数创建
|
||||
- **选择器模式**: 精确订阅状态变化
|
||||
- **批量更新**: 合并多个状态更新
|
||||
|
||||
### 9. 扩展性设计
|
||||
|
||||
#### 插件系统
|
||||
|
||||
```typescript
|
||||
// 节点模板扩展
|
||||
interface NodeTemplateListItemType {
|
||||
id: string;
|
||||
flowNodeType: FlowNodeTypeEnum;
|
||||
templateType: string;
|
||||
avatar?: string;
|
||||
name: string;
|
||||
intro?: string;
|
||||
isTool?: boolean;
|
||||
pluginId?: string;
|
||||
}
|
||||
```
|
||||
|
||||
- **自定义节点**: 支持第三方节点开发
|
||||
- **模板市场**: 节点模板的共享和分发
|
||||
- **插件生态**: 丰富的节点插件生态
|
||||
- **开放API**: 标准化的节点开发接口
|
||||
|
||||
#### 主题定制
|
||||
|
||||
- **节点样式**: 可定制的节点外观
|
||||
- **连线样式**: 自定义连线类型和颜色
|
||||
- **布局配置**: 多种布局算法支持
|
||||
- **国际化**: 多语言界面支持
|
||||
|
|
@ -1,534 +0,0 @@
|
|||
---
|
||||
name: workflow-interactive-dev
|
||||
description: 用于开发 FastGPT 工作流中的交互响应。详细说明了交互节点的架构、开发流程和需要修改的文件。
|
||||
---
|
||||
|
||||
# 交互节点开发指南
|
||||
|
||||
## 概述
|
||||
|
||||
FastGPT 工作流支持多种交互节点类型,允许在工作流执行过程中暂停并等待用户输入。本指南详细说明了如何开发新的交互节点。
|
||||
|
||||
## 现有交互节点类型
|
||||
|
||||
当前系统支持以下交互节点类型:
|
||||
|
||||
1. **userSelect** - 用户选择节点(单选)
|
||||
2. **formInput** - 表单输入节点(多字段表单)
|
||||
3. **childrenInteractive** - 子工作流交互
|
||||
4. **loopInteractive** - 循环交互
|
||||
5. **paymentPause** - 欠费暂停交互
|
||||
|
||||
## 交互节点架构
|
||||
|
||||
### 核心类型定义
|
||||
|
||||
交互节点的类型定义位于 `packages/global/core/workflow/template/system/interactive/type.d.ts`
|
||||
|
||||
```typescript
|
||||
// 基础交互结构
|
||||
type InteractiveBasicType = {
|
||||
entryNodeIds: string[]; // 入口节点ID列表
|
||||
memoryEdges: RuntimeEdgeItemType[]; // 需要记忆的边
|
||||
nodeOutputs: NodeOutputItemType[]; // 节点输出
|
||||
skipNodeQueue?: Array; // 跳过的节点队列
|
||||
usageId?: string; // 用量记录ID
|
||||
};
|
||||
|
||||
// 具体交互节点类型
|
||||
type YourInteractiveNode = InteractiveNodeType & {
|
||||
type: 'yourNodeType';
|
||||
params: {
|
||||
// 节点特定参数
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### 工作流执行机制
|
||||
|
||||
交互节点在工作流执行中的特殊处理(位于 `packages/service/core/workflow/dispatch/index.ts:1012-1019`):
|
||||
|
||||
```typescript
|
||||
// 部分交互节点不会自动重置 isEntry 标志(因为需要根据 isEntry 字段来判断是首次进入还是流程进入)
|
||||
runtimeNodes.forEach((item) => {
|
||||
if (
|
||||
item.flowNodeType !== FlowNodeTypeEnum.userSelect &&
|
||||
item.flowNodeType !== FlowNodeTypeEnum.formInput &&
|
||||
item.flowNodeType !== FlowNodeTypeEnum.agent
|
||||
) {
|
||||
item.isEntry = false;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## 开发新交互响应的步骤
|
||||
|
||||
### 步骤 1: 定义节点类型
|
||||
|
||||
**文件**: `packages/global/core/workflow/template/system/interactive/type.d.ts`
|
||||
|
||||
```typescript
|
||||
export type YourInputItemType = {
|
||||
// 定义输入项的结构
|
||||
key: string;
|
||||
label: string;
|
||||
value: any;
|
||||
// ... 其他字段
|
||||
};
|
||||
|
||||
type YourInteractiveNode = InteractiveNodeType & {
|
||||
type: 'yourNodeType';
|
||||
params: {
|
||||
description: string;
|
||||
yourInputField: YourInputItemType[];
|
||||
submitted?: boolean; // 可选:是否已提交
|
||||
};
|
||||
};
|
||||
|
||||
// 添加到联合类型
|
||||
export type InteractiveNodeResponseType =
|
||||
| UserSelectInteractive
|
||||
| UserInputInteractive
|
||||
| YourInteractiveNode // 新增
|
||||
| ChildrenInteractive
|
||||
| LoopInteractive
|
||||
| PaymentPauseInteractive;
|
||||
```
|
||||
|
||||
### 步骤 2: 定义节点枚举(可选)
|
||||
|
||||
**文件**: `packages/global/core/workflow/node/constant.ts`
|
||||
|
||||
如果不需要添加新的节点类型,则不需要修改这个文件。
|
||||
|
||||
```typescript
|
||||
export enum FlowNodeTypeEnum {
|
||||
// ... 现有类型
|
||||
yourNodeType = 'yourNodeType', // 新增节点类型
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤 3: 创建节点模板(可选)
|
||||
|
||||
**文件**: `packages/global/core/workflow/template/system/interactive/yourNode.ts`
|
||||
|
||||
```typescript
|
||||
import { i18nT } from '../../../../../../web/i18n/utils';
|
||||
import {
|
||||
FlowNodeTemplateTypeEnum,
|
||||
NodeInputKeyEnum,
|
||||
NodeOutputKeyEnum,
|
||||
WorkflowIOValueTypeEnum
|
||||
} from '../../../constants';
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeOutputTypeEnum,
|
||||
FlowNodeTypeEnum
|
||||
} from '../../../node/constant';
|
||||
import { type FlowNodeTemplateType } from '../../../type/node';
|
||||
|
||||
export const YourNode: FlowNodeTemplateType = {
|
||||
id: FlowNodeTypeEnum.yourNodeType,
|
||||
templateType: FlowNodeTemplateTypeEnum.interactive,
|
||||
flowNodeType: FlowNodeTypeEnum.yourNodeType,
|
||||
showSourceHandle: true, // 是否显示源连接点
|
||||
showTargetHandle: true, // 是否显示目标连接点
|
||||
avatar: 'core/workflow/template/yourNode',
|
||||
name: i18nT('app:workflow.your_node'),
|
||||
intro: i18nT('app:workflow.your_node_tip'),
|
||||
isTool: true, // 标记为工具节点
|
||||
inputs: [
|
||||
{
|
||||
key: NodeInputKeyEnum.description,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.textarea],
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
label: i18nT('app:workflow.node_description'),
|
||||
placeholder: i18nT('app:workflow.your_node_placeholder')
|
||||
},
|
||||
{
|
||||
key: NodeInputKeyEnum.yourInputField,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.custom],
|
||||
valueType: WorkflowIOValueTypeEnum.any,
|
||||
label: '',
|
||||
value: [] // 默认值
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
id: NodeOutputKeyEnum.yourResult,
|
||||
key: NodeOutputKeyEnum.yourResult,
|
||||
required: true,
|
||||
label: i18nT('workflow:your_result'),
|
||||
valueType: WorkflowIOValueTypeEnum.object,
|
||||
type: FlowNodeOutputTypeEnum.static
|
||||
}
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
### 步骤 4: 创建节点执行逻辑或在需要处理交互逻辑的节点上增加新逻辑
|
||||
|
||||
**文件**: `packages/service/core/workflow/dispatch/interactive/yourNode.ts`
|
||||
|
||||
```typescript
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import type {
|
||||
DispatchNodeResultType,
|
||||
ModuleDispatchProps
|
||||
} from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import type { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import type { YourInputItemType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.description]: string;
|
||||
[NodeInputKeyEnum.yourInputField]: YourInputItemType[];
|
||||
}>;
|
||||
|
||||
type YourNodeResponse = DispatchNodeResultType<{
|
||||
[NodeOutputKeyEnum.yourResult]?: Record<string, any>;
|
||||
}>;
|
||||
|
||||
export const dispatchYourNode = async (props: Props): Promise<YourNodeResponse> => {
|
||||
const {
|
||||
histories,
|
||||
node,
|
||||
params: { description, yourInputField },
|
||||
query,
|
||||
lastInteractive
|
||||
} = props;
|
||||
const { isEntry } = node;
|
||||
|
||||
// 第一阶段:非入口节点或不是对应的交互类型,返回交互请求
|
||||
if (!isEntry || lastInteractive?.type !== 'yourNodeType') {
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.interactive]: {
|
||||
type: 'yourNodeType',
|
||||
params: {
|
||||
description,
|
||||
yourInputField
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 第二阶段:处理用户提交的数据
|
||||
node.isEntry = false; // 重要:重置入口标志
|
||||
|
||||
const { text } = chatValue2RuntimePrompt(query);
|
||||
const userInputVal = (() => {
|
||||
try {
|
||||
return JSON.parse(text); // 根据实际格式解析
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
})();
|
||||
|
||||
return {
|
||||
data: {
|
||||
[NodeOutputKeyEnum.yourResult]: userInputVal
|
||||
},
|
||||
// 移除当前交互的历史记录(最后2条)
|
||||
[DispatchNodeResponseKeyEnum.rewriteHistories]: histories.slice(0, -2),
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: userInputVal,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
yourResult: userInputVal
|
||||
}
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### 步骤 5: 注册节点回调
|
||||
|
||||
**文件**: `packages/service/core/workflow/dispatch/constants.ts`
|
||||
|
||||
```typescript
|
||||
import { dispatchYourNode } from './interactive/yourNode';
|
||||
|
||||
export const callbackMap: Record<FlowNodeTypeEnum, any> = {
|
||||
// ... 现有节点
|
||||
[FlowNodeTypeEnum.yourNodeType]: dispatchYourNode,
|
||||
};
|
||||
```
|
||||
|
||||
### 步骤 6: 创建前端渲染组件
|
||||
|
||||
#### 6.1 聊天界面交互组件
|
||||
|
||||
**文件**: `projects/app/src/components/core/chat/components/Interactive/InteractiveComponents.tsx`
|
||||
|
||||
```typescript
|
||||
export const YourNodeComponent = React.memo(function YourNodeComponent({
|
||||
interactiveParams: { description, yourInputField, submitted },
|
||||
defaultValues = {},
|
||||
SubmitButton
|
||||
}: {
|
||||
interactiveParams: YourInteractiveNode['params'];
|
||||
defaultValues?: Record<string, any>;
|
||||
SubmitButton: (e: { onSubmit: UseFormHandleSubmit<Record<string, any>> }) => React.JSX.Element;
|
||||
}) {
|
||||
const { handleSubmit, control } = useForm({
|
||||
defaultValues
|
||||
});
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<DescriptionBox description={description} />
|
||||
<Flex flexDirection={'column'} gap={3}>
|
||||
{yourInputField.map((input) => (
|
||||
<Box key={input.key}>
|
||||
{/* 渲染你的输入组件 */}
|
||||
<Controller
|
||||
control={control}
|
||||
name={input.key}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<YourInputComponent
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
isDisabled={submitted}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
</Flex>
|
||||
|
||||
{!submitted && (
|
||||
<Flex justifyContent={'flex-end'} mt={4}>
|
||||
<SubmitButton onSubmit={handleSubmit} />
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
#### 6.2 工作流编辑器节点组件
|
||||
|
||||
**文件**: `projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodeYourNode.tsx`
|
||||
|
||||
```typescript
|
||||
import React, { useMemo } from 'react';
|
||||
import { type NodeProps } from 'reactflow';
|
||||
import { Box, Button } from '@chakra-ui/react';
|
||||
import NodeCard from './render/NodeCard';
|
||||
import { type FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
|
||||
import Container from '../components/Container';
|
||||
import RenderInput from './render/RenderInput';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { type FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io.d';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import IOTitle from '../components/IOTitle';
|
||||
import RenderOutput from './render/RenderOutput';
|
||||
import { WorkflowActionsContext } from '../../context/workflowActionsContext';
|
||||
|
||||
const NodeYourNode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { t } = useTranslation();
|
||||
const { nodeId, inputs, outputs } = data;
|
||||
const onChangeNode = useContextSelector(WorkflowActionsContext, (v) => v.onChangeNode);
|
||||
|
||||
const CustomComponent = useMemo(
|
||||
() => ({
|
||||
[NodeInputKeyEnum.yourInputField]: (v: FlowNodeInputItemType) => {
|
||||
// 自定义渲染逻辑
|
||||
return (
|
||||
<Box>
|
||||
{/* 你的自定义UI */}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}),
|
||||
[nodeId, onChangeNode, t]
|
||||
);
|
||||
|
||||
return (
|
||||
<NodeCard minW={'400px'} selected={selected} {...data}>
|
||||
<Container>
|
||||
<RenderInput nodeId={nodeId} flowInputList={inputs} CustomComponent={CustomComponent} />
|
||||
</Container>
|
||||
<Container>
|
||||
<IOTitle text={t('common:Output')} />
|
||||
<RenderOutput nodeId={nodeId} flowOutputList={outputs} />
|
||||
</Container>
|
||||
</NodeCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(NodeYourNode);
|
||||
```
|
||||
|
||||
### 步骤 7: 注册节点组件
|
||||
|
||||
需要在节点注册表中添加你的节点组件(具体位置根据项目配置而定)。
|
||||
|
||||
### 步骤 8: 添加国际化
|
||||
|
||||
**文件**: `packages/web/i18n/zh-CN/app.json` 和其他语言文件
|
||||
|
||||
```json
|
||||
{
|
||||
"workflow": {
|
||||
"your_node": "你的节点名称",
|
||||
"your_node_tip": "节点功能说明",
|
||||
"your_node_placeholder": "提示文本"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤9 调整保存对话记录逻辑
|
||||
|
||||
**文件**: `FastGPT/packages/service/core/chat/saveChat.ts`
|
||||
|
||||
修改 `updateInteractiveChat` 方法,支持新的交互
|
||||
|
||||
### 步骤10 根据历史记录获取/设置交互状态
|
||||
|
||||
**文件**: `FastGPT/projects/app/src/components/core/chat/ChatContainer/ChatBox/utils.ts`
|
||||
**文件**: `FastGPT/packages/global/core/workflow/runtime/utils.ts`
|
||||
|
||||
调整`setInteractiveResultToHistories`, `getInteractiveByHistories` 和 `getLastInteractiveValue`方法。
|
||||
|
||||
## 关键注意事项
|
||||
|
||||
### 1. isEntry 标志管理
|
||||
|
||||
交互节点需要保持 `isEntry` 标志在工作流恢复时有效:
|
||||
|
||||
```typescript
|
||||
// 在 packages/service/core/workflow/dispatch/index.ts 中
|
||||
// 确保你的节点类型被添加到白名单
|
||||
if (
|
||||
item.flowNodeType !== FlowNodeTypeEnum.userSelect &&
|
||||
item.flowNodeType !== FlowNodeTypeEnum.formInput &&
|
||||
item.flowNodeType !== FlowNodeTypeEnum.yourNodeType // 新增
|
||||
) {
|
||||
item.isEntry = false;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 交互响应流程
|
||||
|
||||
交互节点有两个执行阶段:
|
||||
|
||||
1. **第一次执行**: 返回 `interactive` 响应,暂停工作流
|
||||
2. **第二次执行**: 接收用户输入,继续工作流
|
||||
|
||||
```typescript
|
||||
// 第一阶段
|
||||
if (!isEntry || lastInteractive?.type !== 'yourNodeType') {
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.interactive]: {
|
||||
type: 'yourNodeType',
|
||||
params: { /* ... */ }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 第二阶段
|
||||
node.isEntry = false; // 重要!重置标志
|
||||
// 处理用户输入...
|
||||
```
|
||||
|
||||
### 3. 历史记录管理
|
||||
|
||||
交互节点需要正确处理历史记录:
|
||||
|
||||
```typescript
|
||||
return {
|
||||
// 移除交互对话的历史记录(用户问题 + 系统响应)
|
||||
[DispatchNodeResponseKeyEnum.rewriteHistories]: histories.slice(0, -2),
|
||||
// ... 其他返回值
|
||||
};
|
||||
```
|
||||
|
||||
### 4. Skip 节点队列
|
||||
|
||||
交互节点触发时,系统会保存 `skipNodeQueue` 以便恢复时跳过已处理的节点。
|
||||
|
||||
### 5. 工具调用支持
|
||||
|
||||
如果节点需要在工具调用中使用,设置 `isTool: true`。
|
||||
|
||||
## 测试清单
|
||||
|
||||
开发完成后,请测试以下场景:
|
||||
|
||||
- [ ] 节点在工作流编辑器中正常显示
|
||||
- [ ] 节点配置保存和加载正确
|
||||
- [ ] 交互请求正确发送到前端
|
||||
- [ ] 前端组件正确渲染交互界面
|
||||
- [ ] 用户输入正确传回后端
|
||||
- [ ] 工作流正确恢复并继续执行
|
||||
- [ ] 历史记录正确更新
|
||||
- [ ] 节点输出正确连接到后续节点
|
||||
- [ ] 错误情况处理正确
|
||||
- [ ] 多语言支持完整
|
||||
|
||||
## 参考实现
|
||||
|
||||
可以参考以下现有实现:
|
||||
|
||||
1. **简单单选**: `userSelect` 节点
|
||||
- 类型定义: `packages/global/core/workflow/template/system/interactive/type.d.ts:48-55`
|
||||
- 执行逻辑: `packages/service/core/workflow/dispatch/interactive/userSelect.ts`
|
||||
- 前端组件: `projects/app/src/components/core/chat/components/Interactive/InteractiveComponents.tsx:29-63`
|
||||
|
||||
2. **复杂表单**: `formInput` 节点
|
||||
- 类型定义: `packages/global/core/workflow/template/system/interactive/type.d.ts:57-82`
|
||||
- 执行逻辑: `packages/service/core/workflow/dispatch/interactive/formInput.ts`
|
||||
- 前端组件: `projects/app/src/components/core/chat/components/Interactive/InteractiveComponents.tsx:65-126`
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 交互节点执行了两次?
|
||||
A: 这是正常的。第一次返回交互请求,第二次处理用户输入。确保在第二次执行时设置 `node.isEntry = false`。
|
||||
|
||||
### Q: 工作流恢复后没有继续执行?
|
||||
A: 检查你的节点类型是否在 `isEntry` 白名单中(dispatch/index.ts:1013-1018)。
|
||||
|
||||
### Q: 用户输入格式不对?
|
||||
A: 检查 `chatValue2RuntimePrompt` 的返回值,根据你的数据格式进行解析。
|
||||
|
||||
### Q: 如何支持多个交互节点串联?
|
||||
A: 每个交互节点都会暂停工作流,用户完成后会自动继续到下一个节点。
|
||||
|
||||
## 文件清单总结
|
||||
|
||||
开发新交互节点需要修改/创建以下文件:
|
||||
|
||||
### 后端核心文件
|
||||
1. `packages/global/core/workflow/template/system/interactive/type.d.ts` - 类型定义
|
||||
2. `packages/global/core/workflow/node/constant.ts` - 节点枚举
|
||||
3. `packages/global/core/workflow/template/system/interactive/yourNode.ts` - 节点模板
|
||||
4. `packages/service/core/workflow/dispatch/interactive/yourNode.ts` - 执行逻辑
|
||||
5. `packages/service/core/workflow/dispatch/constants.ts` - 回调注册
|
||||
6. `packages/service/core/workflow/dispatch/index.ts` - isEntry 白名单
|
||||
|
||||
### 前端组件文件
|
||||
7. `projects/app/src/components/core/chat/components/Interactive/InteractiveComponents.tsx` - 聊天交互组件
|
||||
8. `projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodeYourNode.tsx` - 工作流编辑器组件
|
||||
|
||||
### 国际化文件
|
||||
9. `packages/web/i18n/zh-CN/app.json` - 中文翻译
|
||||
10. `packages/web/i18n/en/app.json` - 英文翻译
|
||||
11. `packages/web/i18n/zh-Hant/app.json` - 繁体中文翻译
|
||||
|
||||
## 附录:关键输入输出键定义
|
||||
|
||||
如果需要新的输入输出键,在以下文件中定义:
|
||||
|
||||
**文件**: `packages/global/core/workflow/constants.ts`
|
||||
|
||||
```typescript
|
||||
export enum NodeInputKeyEnum {
|
||||
// ... 现有键
|
||||
yourInputKey = 'yourInputKey',
|
||||
}
|
||||
|
||||
export enum NodeOutputKeyEnum {
|
||||
// ... 现有键
|
||||
yourOutputKey = 'yourOutputKey',
|
||||
}
|
||||
```
|
||||
|
|
@ -1,672 +0,0 @@
|
|||
---
|
||||
name: workflow-stop-design
|
||||
description: 工作流暂停逻辑设计方案
|
||||
---
|
||||
|
||||
## 1. Redis 状态管理方案
|
||||
|
||||
### 1.1 状态键设计
|
||||
|
||||
**Redis Key 结构:**
|
||||
```typescript
|
||||
// Key 格式: agent_runtime_stopping:{appId}:{chatId}
|
||||
const WORKFLOW_STATUS_PREFIX = 'agent_runtime_stopping';
|
||||
|
||||
type WorkflowStatusKey = `${typeof WORKFLOW_STATUS_PREFIX}:${string}:${string}`;
|
||||
|
||||
// 示例: agent_runtime_stopping:app_123456:chat_789012
|
||||
```
|
||||
|
||||
**状态值设计:**
|
||||
- **存在键 (值为 1)**: 工作流应该停止
|
||||
- **不存在键**: 工作流正常运行
|
||||
- **设计简化**: 不使用状态枚举,仅通过键的存在与否判断
|
||||
|
||||
**参数类型定义:**
|
||||
```typescript
|
||||
type WorkflowStatusParams = {
|
||||
appId: string;
|
||||
chatId: string;
|
||||
};
|
||||
```
|
||||
|
||||
### 1.2 状态生命周期管理
|
||||
|
||||
**状态转换流程:**
|
||||
```
|
||||
正常运行(无键) → 停止中(键存在) → 完成(删除键)
|
||||
```
|
||||
|
||||
**TTL 设置:**
|
||||
- **停止标志 TTL**: 60 秒
|
||||
- 原因: 避免因意外情况导致的键泄漏
|
||||
- 正常情况下会在工作流完成时主动删除
|
||||
- **工作流完成后**: 直接删除 Redis 键
|
||||
- 原因: 不需要保留终态,减少 Redis 内存占用
|
||||
|
||||
### 1.3 核心函数说明
|
||||
|
||||
**1. setAgentRuntimeStop**
|
||||
- **功能**: 设置停止标志
|
||||
- **参数**: `{ appId, chatId }`
|
||||
- **实现**: 使用 `SETEX` 命令,设置键值为 1,TTL 60 秒
|
||||
|
||||
**2. shouldWorkflowStop**
|
||||
- **功能**: 检查工作流是否应该停止
|
||||
- **参数**: `{ appId, chatId }`
|
||||
- **返回**: `Promise<boolean>` - true=应该停止, false=继续运行
|
||||
- **实现**: GET 命令获取键值,存在则返回 true
|
||||
|
||||
**3. delAgentRuntimeStopSign**
|
||||
- **功能**: 删除停止标志
|
||||
- **参数**: `{ appId, chatId }`
|
||||
- **实现**: DEL 命令删除键
|
||||
|
||||
**4. waitForWorkflowComplete**
|
||||
- **功能**: 等待工作流完成(停止标志被删除)
|
||||
- **参数**: `{ appId, chatId, timeout?, pollInterval? }`
|
||||
- **实现**: 轮询检查停止标志是否被删除,超时返回
|
||||
|
||||
### 1.4 边界情况处理
|
||||
|
||||
**1. Redis 操作失败**
|
||||
- **错误处理**: 所有 Redis 操作都包含 `.catch()` 错误处理
|
||||
- **降级策略**:
|
||||
- `shouldWorkflowStop`: 出错时返回 `false` (认为不需要停止,继续运行)
|
||||
- `delAgentRuntimeStopSign`: 出错时记录错误日志,但不影响主流程
|
||||
- **设计原因**: Redis 异常不应阻塞工作流运行,降级到继续执行策略
|
||||
|
||||
**2. TTL 自动清理**
|
||||
- **TTL 设置**: 60 秒
|
||||
- **清理时机**: Redis 自动清理过期键
|
||||
- **设计原因**:
|
||||
- 避免因异常情况导致的 Redis 键泄漏
|
||||
- 自动清理减少手动维护成本
|
||||
- 60 秒足够大多数工作流完成停止操作
|
||||
|
||||
**3. stop 接口等待超时**
|
||||
- **超时时间**: 5 秒
|
||||
- **超时策略**: `waitForWorkflowComplete` 在 5 秒内轮询检查停止标志是否被删除
|
||||
- **超时处理**: 5 秒后直接返回,不影响工作流继续执行
|
||||
- **设计原因**:
|
||||
- 避免前端长时间等待
|
||||
- 5 秒足够大多数节点完成当前操作
|
||||
- 用户体验优先,超时后前端可选择重试或放弃
|
||||
|
||||
**4. 并发停止请求**
|
||||
- **处理方式**: 多次调用 `setAgentRuntimeStop` 是安全的,Redis SETEX 是幂等操作
|
||||
- **设计原因**: 避免用户多次点击停止按钮导致的问题
|
||||
|
||||
---
|
||||
|
||||
## 2. Redis 工具函数实现
|
||||
|
||||
**位置**: `packages/service/core/workflow/dispatch/workflowStatus.ts`
|
||||
|
||||
```typescript
|
||||
import { addLog } from '../../../common/system/log';
|
||||
import { getGlobalRedisConnection } from '../../../common/redis/index';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
|
||||
const WORKFLOW_STATUS_PREFIX = 'agent_runtime_stopping';
|
||||
const TTL = 60; // 60秒
|
||||
|
||||
export const StopStatus = 'STOPPING';
|
||||
|
||||
export type WorkflowStatusParams = {
|
||||
appId: string;
|
||||
chatId: string;
|
||||
};
|
||||
|
||||
// 获取工作流状态键
|
||||
export const getRuntimeStatusKey = (params: WorkflowStatusParams): string => {
|
||||
return `${WORKFLOW_STATUS_PREFIX}:${params.appId}:${params.chatId}`;
|
||||
};
|
||||
|
||||
// 设置停止标志
|
||||
export const setAgentRuntimeStop = async (params: WorkflowStatusParams): Promise<void> => {
|
||||
const redis = getGlobalRedisConnection();
|
||||
const key = getRuntimeStatusKey(params);
|
||||
await redis.setex(key, TTL, 1);
|
||||
};
|
||||
|
||||
// 删除停止标志
|
||||
export const delAgentRuntimeStopSign = async (params: WorkflowStatusParams): Promise<void> => {
|
||||
const redis = getGlobalRedisConnection();
|
||||
const key = getRuntimeStatusKey(params);
|
||||
await redis.del(key).catch((err) => {
|
||||
addLog.error(`[Agent Runtime Stop] Delete stop sign error`, err);
|
||||
});
|
||||
};
|
||||
|
||||
// 检查工作流是否应该停止
|
||||
export const shouldWorkflowStop = (params: WorkflowStatusParams): Promise<boolean> => {
|
||||
const redis = getGlobalRedisConnection();
|
||||
const key = getRuntimeStatusKey(params);
|
||||
return redis
|
||||
.get(key)
|
||||
.then((res) => !!res)
|
||||
.catch(() => false);
|
||||
};
|
||||
|
||||
/**
|
||||
* 等待工作流完成(停止标志被删除)
|
||||
* @param params 工作流参数
|
||||
* @param timeout 超时时间(毫秒),默认5秒
|
||||
* @param pollInterval 轮询间隔(毫秒),默认50毫秒
|
||||
*/
|
||||
export const waitForWorkflowComplete = async ({
|
||||
appId,
|
||||
chatId,
|
||||
timeout = 5000,
|
||||
pollInterval = 50
|
||||
}: {
|
||||
appId: string;
|
||||
chatId: string;
|
||||
timeout?: number;
|
||||
pollInterval?: number;
|
||||
}) => {
|
||||
const startTime = Date.now();
|
||||
|
||||
while (Date.now() - startTime < timeout) {
|
||||
const sign = await shouldWorkflowStop({ appId, chatId });
|
||||
|
||||
// 如果停止标志已被删除,说明工作流已完成
|
||||
if (!sign) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 等待下一次轮询
|
||||
await delay(pollInterval);
|
||||
}
|
||||
|
||||
// 超时后直接返回
|
||||
return;
|
||||
};
|
||||
```
|
||||
|
||||
**测试用例位置**: `test/cases/service/core/app/workflow/workflowStatus.test.ts`
|
||||
|
||||
```typescript
|
||||
import { describe, test, expect, beforeEach } from 'vitest';
|
||||
import {
|
||||
setAgentRuntimeStop,
|
||||
delAgentRuntimeStopSign,
|
||||
shouldWorkflowStop,
|
||||
waitForWorkflowComplete
|
||||
} from '@fastgpt/service/core/workflow/dispatch/workflowStatus';
|
||||
|
||||
describe('Workflow Status Redis Functions', () => {
|
||||
const testAppId = 'test_app_123';
|
||||
const testChatId = 'test_chat_456';
|
||||
|
||||
beforeEach(async () => {
|
||||
// 清理测试数据
|
||||
await delAgentRuntimeStopSign({ appId: testAppId, chatId: testChatId });
|
||||
});
|
||||
|
||||
test('should set stopping sign', async () => {
|
||||
await setAgentRuntimeStop({ appId: testAppId, chatId: testChatId });
|
||||
const shouldStop = await shouldWorkflowStop({ appId: testAppId, chatId: testChatId });
|
||||
expect(shouldStop).toBe(true);
|
||||
});
|
||||
|
||||
test('should return false for non-existent status', async () => {
|
||||
const shouldStop = await shouldWorkflowStop({ appId: testAppId, chatId: testChatId });
|
||||
expect(shouldStop).toBe(false);
|
||||
});
|
||||
|
||||
test('should return false after deleting stop sign', async () => {
|
||||
await setAgentRuntimeStop({ appId: testAppId, chatId: testChatId });
|
||||
await delAgentRuntimeStopSign({ appId: testAppId, chatId: testChatId });
|
||||
const shouldStop = await shouldWorkflowStop({ appId: testAppId, chatId: testChatId });
|
||||
expect(shouldStop).toBe(false);
|
||||
});
|
||||
|
||||
test('should wait for workflow completion', async () => {
|
||||
// 设置初始停止标志
|
||||
await setAgentRuntimeStop({ appId: testAppId, chatId: testChatId });
|
||||
|
||||
// 模拟异步完成(删除停止标志)
|
||||
setTimeout(async () => {
|
||||
await delAgentRuntimeStopSign({ appId: testAppId, chatId: testChatId });
|
||||
}, 500);
|
||||
|
||||
// 等待完成
|
||||
await waitForWorkflowComplete({
|
||||
appId: testAppId,
|
||||
chatId: testChatId,
|
||||
timeout: 2000
|
||||
});
|
||||
|
||||
// 验证停止标志已被删除
|
||||
const shouldStop = await shouldWorkflowStop({ appId: testAppId, chatId: testChatId });
|
||||
expect(shouldStop).toBe(false);
|
||||
});
|
||||
|
||||
test('should timeout when waiting too long', async () => {
|
||||
await setAgentRuntimeStop({ appId: testAppId, chatId: testChatId });
|
||||
|
||||
// 等待超时(不删除标志)
|
||||
await waitForWorkflowComplete({
|
||||
appId: testAppId,
|
||||
chatId: testChatId,
|
||||
timeout: 100
|
||||
});
|
||||
|
||||
// 验证停止标志仍然存在
|
||||
const shouldStop = await shouldWorkflowStop({ appId: testAppId, chatId: testChatId });
|
||||
expect(shouldStop).toBe(true);
|
||||
});
|
||||
|
||||
test('should handle concurrent stop sign operations', async () => {
|
||||
// 并发设置停止标志
|
||||
await Promise.all([
|
||||
setAgentRuntimeStop({ appId: testAppId, chatId: testChatId }),
|
||||
setAgentRuntimeStop({ appId: testAppId, chatId: testChatId })
|
||||
]);
|
||||
|
||||
// 停止标志应该存在
|
||||
const shouldStop = await shouldWorkflowStop({ appId: testAppId, chatId: testChatId });
|
||||
expect(shouldStop).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## 3. 工作流停止检测机制改造
|
||||
|
||||
### 3.1 修改位置
|
||||
|
||||
**文件**: `packages/service/core/workflow/dispatch/index.ts`
|
||||
|
||||
### 3.2 工作流启动时的停止检测机制
|
||||
|
||||
**改造点 1: 停止检测逻辑 (行 196-216)**
|
||||
|
||||
使用内存变量 + 定时轮询 Redis 的方式:
|
||||
|
||||
```typescript
|
||||
import { delAgentRuntimeStopSign, shouldWorkflowStop } from './workflowStatus';
|
||||
|
||||
// 初始化停止检测
|
||||
let stopping = false;
|
||||
const checkIsStopping = (): boolean => {
|
||||
if (apiVersion === 'v2') {
|
||||
return stopping;
|
||||
}
|
||||
if (apiVersion === 'v1') {
|
||||
if (!res) return false;
|
||||
return res.closed || !!res.errored;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// v2 版本: 启动定时器定期检查 Redis
|
||||
const checkStoppingTimer =
|
||||
apiVersion === 'v2'
|
||||
? setInterval(async () => {
|
||||
stopping = await shouldWorkflowStop({
|
||||
appId: runningAppInfo.id,
|
||||
chatId
|
||||
});
|
||||
}, 100)
|
||||
: undefined;
|
||||
```
|
||||
|
||||
**设计要点**:
|
||||
- v2 版本使用内存变量 `stopping` + 100ms 定时器轮询 Redis
|
||||
- v1 版本仍使用原有的 `res.closed/res.errored` 检测
|
||||
- 轮询频率 100ms,平衡性能和响应速度
|
||||
|
||||
**改造点 2: 工作流完成后清理 (行 232-249)**
|
||||
|
||||
```typescript
|
||||
return runWorkflow({
|
||||
...data,
|
||||
checkIsStopping, // 传递检测函数
|
||||
query,
|
||||
histories,
|
||||
// ... 其他参数
|
||||
}).finally(async () => {
|
||||
// 清理定时器
|
||||
if (streamCheckTimer) {
|
||||
clearInterval(streamCheckTimer);
|
||||
}
|
||||
if (checkStoppingTimer) {
|
||||
clearInterval(checkStoppingTimer);
|
||||
}
|
||||
|
||||
// Close mcpClient connections
|
||||
Object.values(mcpClientMemory).forEach((client) => {
|
||||
client.closeConnection();
|
||||
});
|
||||
|
||||
// 工作流完成后删除 Redis 记录
|
||||
await delAgentRuntimeStopSign({
|
||||
appId: runningAppInfo.id,
|
||||
chatId
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 3.3 节点执行前的停止检测
|
||||
|
||||
**位置**: `packages/service/core/workflow/dispatch/index.ts:861-868`
|
||||
|
||||
在 `checkNodeCanRun` 方法中,每个节点执行前检查:
|
||||
|
||||
```typescript
|
||||
private async checkNodeCanRun(
|
||||
node: RuntimeNodeItemType,
|
||||
skippedNodeIdList = new Set<string>()
|
||||
) {
|
||||
// ... 其他检查逻辑 ...
|
||||
|
||||
// Check queue status
|
||||
if (data.maxRunTimes <= 0) {
|
||||
addLog.error('Max run times is 0', {
|
||||
appId: data.runningAppInfo.id
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 停止检测
|
||||
if (checkIsStopping()) {
|
||||
addLog.warn('Workflow stopped', {
|
||||
appId: data.runningAppInfo.id,
|
||||
nodeId: node.nodeId,
|
||||
nodeName: node.name
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// ... 执行节点逻辑 ...
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 直接调用 `checkIsStopping()` 同步方法
|
||||
- 内部会检查内存变量 `stopping`
|
||||
- 定时器每 100ms 更新一次该变量
|
||||
- 检测到停止时记录日志并直接返回,不执行节点
|
||||
|
||||
## 4. v2/chat/stop 接口设计
|
||||
|
||||
### 4.1 接口规范
|
||||
|
||||
**接口路径**: `/api/v2/chat/stop`
|
||||
|
||||
**Schema 位置**: `packages/global/openapi/core/chat/api.ts`
|
||||
|
||||
**接口文档位置**: `packages/global/openapi/core/chat/index.ts`
|
||||
|
||||
**请求方法**: POST
|
||||
|
||||
**请求参数**:
|
||||
```typescript
|
||||
// packages/global/openapi/core/chat/api.ts
|
||||
export const StopV2ChatSchema = z
|
||||
.object({
|
||||
appId: ObjectIdSchema.describe('应用ID'),
|
||||
chatId: z.string().min(1).describe('对话ID'),
|
||||
outLinkAuthData: OutLinkChatAuthSchema.optional().describe('外链鉴权数据')
|
||||
});
|
||||
|
||||
export type StopV2ChatParams = z.infer<typeof StopV2ChatSchema>;
|
||||
```
|
||||
|
||||
**响应格式**:
|
||||
```typescript
|
||||
export const StopV2ChatResponseSchema = z
|
||||
.object({
|
||||
success: z.boolean().describe('是否成功停止')
|
||||
});
|
||||
|
||||
export type StopV2ChatResponse = z.infer<typeof StopV2ChatResponseSchema>;
|
||||
```
|
||||
|
||||
### 4.2 接口实现
|
||||
|
||||
**文件位置**: `projects/app/src/pages/api/v2/chat/stop.ts`
|
||||
|
||||
```typescript
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { authChatCrud } from '@/service/support/permission/auth/chat';
|
||||
import {
|
||||
setAgentRuntimeStop,
|
||||
waitForWorkflowComplete
|
||||
} from '@fastgpt/service/core/workflow/dispatch/workflowStatus';
|
||||
import { StopV2ChatSchema, type StopV2ChatResponse } from '@fastgpt/global/openapi/core/chat/controler/api';
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse): Promise<StopV2ChatResponse> {
|
||||
const { appId, chatId, outLinkAuthData } = StopV2ChatSchema.parse(req.body);
|
||||
|
||||
// 鉴权 (复用聊天 CRUD 鉴权)
|
||||
await authChatCrud({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
appId,
|
||||
chatId,
|
||||
...outLinkAuthData
|
||||
});
|
||||
|
||||
// 设置停止标志
|
||||
await setAgentRuntimeStop({
|
||||
appId,
|
||||
chatId
|
||||
});
|
||||
|
||||
// 等待工作流完成 (最多等待 5 秒)
|
||||
await waitForWorkflowComplete({ appId, chatId, timeout: 5000 });
|
||||
|
||||
return {
|
||||
success: true
|
||||
};
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
```
|
||||
|
||||
**接口文档** (`packages/global/openapi/core/chat/index.ts`):
|
||||
|
||||
```typescript
|
||||
export const ChatPath: OpenAPIPath = {
|
||||
// ... 其他路径
|
||||
|
||||
'/v2/chat/stop': {
|
||||
post: {
|
||||
summary: '停止 Agent 运行',
|
||||
description: `优雅停止正在运行的 Agent, 会尝试等待当前节点结束后返回,最长 5s,超过 5s 仍未结束,则会返回成功。
|
||||
LLM 节点,流输出时会同时被终止,但 HTTP 请求节点这种可能长时间运行的,不会被终止。`,
|
||||
tags: [TagsMap.chatPage],
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: StopV2ChatSchema
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功停止工作流',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: StopV2ChatResponseSchema
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 接口使用 `authChatCrud` 进行鉴权,支持 Token 和 API Key
|
||||
- 支持分享链接和团队空间的鉴权数据
|
||||
- 设置停止标志后等待最多 5 秒
|
||||
- 无论是否超时,都返回 `success: true`
|
||||
|
||||
## 5. 前端改造
|
||||
|
||||
由于当前代码已经能够正常工作,且 v2 版本的后端已经实现了基于 Redis 的停止机制,前端可以保持现有的简单实现:
|
||||
|
||||
**保持现有实现的原因**:
|
||||
1. 后端已经通过定时器轮询 Redis 实现了停止检测
|
||||
2. 前端调用 `abort()` 后,后端会在下个检测周期(100ms内)发现停止标志
|
||||
3. 简化前端逻辑,避免增加复杂性
|
||||
4. 用户体验上,立即中断连接响应更快
|
||||
|
||||
**可选的增强方案**:
|
||||
|
||||
如果需要在前端显示更详细的停止状态,可以添加 API 客户端函数:
|
||||
|
||||
**文件位置**: `projects/app/src/web/core/chat/api.ts`
|
||||
|
||||
```typescript
|
||||
import { POST } from '@/web/common/api/request';
|
||||
import type { StopV2ChatParams, StopV2ChatResponse } from '@fastgpt/global/openapi/core/chat/controler/api';
|
||||
|
||||
/**
|
||||
* 停止 v2 版本工作流运行
|
||||
*/
|
||||
export const stopV2Chat = (data: StopV2ChatParams) =>
|
||||
POST<StopV2ChatResponse>('/api/v2/chat/stop', data);
|
||||
```
|
||||
|
||||
**增强的 abortRequest 函数**:
|
||||
|
||||
```typescript
|
||||
/* Abort chat completions, questionGuide */
|
||||
const abortRequest = useMemoizedFn(async (reason: string = 'stop') => {
|
||||
// 先调用 abort 中断连接
|
||||
chatController.current?.abort(new Error(reason));
|
||||
questionGuideController.current?.abort(new Error(reason));
|
||||
pluginController.current?.abort(new Error(reason));
|
||||
|
||||
// v2 版本: 可选地通知后端优雅停止
|
||||
if (chatBoxData?.app?.version === 'v2' && appId && chatId) {
|
||||
try {
|
||||
await stopV2Chat({
|
||||
appId,
|
||||
chatId,
|
||||
outLinkAuthData
|
||||
});
|
||||
} catch (error) {
|
||||
// 静默失败,不影响用户体验
|
||||
console.warn('Failed to notify backend to stop workflow', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**建议**:
|
||||
- **推荐**: 保持当前简单实现,后端已经足够健壮
|
||||
- **可选**: 如果需要更精确的停止状态追踪,可以实现上述增强方案
|
||||
|
||||
## 6. 完整调用流程
|
||||
|
||||
### 6.1 正常停止流程
|
||||
|
||||
```
|
||||
用户点击停止按钮
|
||||
↓
|
||||
前端: abortRequest()
|
||||
↓
|
||||
前端: chatController.abort() [立即中断 HTTP 连接]
|
||||
↓
|
||||
[可选] 前端: POST /api/v2/chat/stop
|
||||
↓
|
||||
后端: setAgentRuntimeStop(appId, chatId) [设置停止标志]
|
||||
↓
|
||||
后端: 定时器检测到 Redis 停止标志,更新内存变量 stopping = true
|
||||
↓
|
||||
后端: 下个节点执行前 checkIsStopping() 返回 true
|
||||
↓
|
||||
后端: 停止处理新节点,记录日志
|
||||
↓
|
||||
后端: 工作流 finally 块删除 Redis 停止标志
|
||||
↓
|
||||
[可选] 后端: waitForWorkflowComplete() 检测到停止标志被删除
|
||||
↓
|
||||
[可选] 前端: 显示停止成功提示
|
||||
```
|
||||
|
||||
### 6.2 超时流程
|
||||
|
||||
```
|
||||
[可选] 前端: POST /api/v2/chat/stop
|
||||
↓
|
||||
后端: setAgentRuntimeStop(appId, chatId)
|
||||
↓
|
||||
后端: waitForWorkflowComplete(timeout=5s)
|
||||
↓
|
||||
后端: 5秒后停止标志仍存在
|
||||
↓
|
||||
后端: 返回成功响应 (不区分超时)
|
||||
↓
|
||||
[可选] 前端: 显示成功提示
|
||||
↓
|
||||
后端: 工作流继续运行,最终完成后删除停止标志
|
||||
```
|
||||
|
||||
### 6.3 工作流自然完成流程
|
||||
|
||||
```
|
||||
工作流运行中
|
||||
↓
|
||||
所有节点执行完成
|
||||
↓
|
||||
dispatchWorkFlow.finally()
|
||||
↓
|
||||
删除 Redis 停止标志
|
||||
↓
|
||||
清理定时器
|
||||
↓
|
||||
60秒 TTL 确保即使删除失败也会自动清理
|
||||
```
|
||||
|
||||
### 6.4 时序说明
|
||||
|
||||
**关键时间点**:
|
||||
- **100ms**: 后端定时器检查 Redis 停止标志的频率
|
||||
- **5s**: stop 接口等待工作流完成的超时时间
|
||||
- **60s**: Redis 键的 TTL,自动清理时间
|
||||
|
||||
**响应时间**:
|
||||
- 用户点击停止 → HTTP 连接中断: **立即** (前端 abort)
|
||||
- 停止标志写入 Redis: **< 50ms** (Redis SETEX 操作)
|
||||
- 后端检测到停止: **< 100ms** (定时器轮询周期)
|
||||
- 当前节点停止执行: **取决于节点类型**
|
||||
- LLM 流式输出: **立即**中断流
|
||||
- HTTP 请求节点: **等待请求完成**
|
||||
- 其他节点: **等待当前操作完成**
|
||||
|
||||
## 7. 测试策略
|
||||
|
||||
### 7.1 单元测试
|
||||
|
||||
**Redis 工具函数测试**:
|
||||
- `setAgentRuntimeStop` / `shouldWorkflowStop` 基本功能
|
||||
- `delAgentRuntimeStopSign` 删除功能
|
||||
- `waitForWorkflowComplete` 等待机制和超时
|
||||
- 并发操作安全性
|
||||
|
||||
**文件位置**: `test/cases/service/core/app/workflow/workflowStatus.test.ts`
|
||||
|
||||
**测试用例**:
|
||||
```typescript
|
||||
describe('Workflow Status Redis Functions', () => {
|
||||
test('should set stopping sign')
|
||||
test('should return false for non-existent status')
|
||||
test('should detect stopping status')
|
||||
test('should return false after deleting stop sign')
|
||||
test('should wait for workflow completion')
|
||||
test('should timeout when waiting too long')
|
||||
test('should delete workflow stop sign')
|
||||
test('should handle concurrent stop sign operations')
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -1,475 +0,0 @@
|
|||
---
|
||||
name: create-skill-file
|
||||
description: Guides Claude in creating well-structured SKILL.md files following best practices. Provides clear guidelines for naming, structure, and content organization to make skills easy to discover and execute.
|
||||
---
|
||||
|
||||
# Claude Agent Skill 编写规范
|
||||
|
||||
> 如何创建高质量的 SKILL.md 文件
|
||||
|
||||
## 目录
|
||||
|
||||
- [快速开始](#快速开始)
|
||||
- [核心原则](#核心原则)
|
||||
- [文件结构规范](#文件结构规范)
|
||||
- [命名和描述规范](#命名和描述规范)
|
||||
- [内容编写指南](#内容编写指南)
|
||||
- [质量检查清单](#质量检查清单)
|
||||
|
||||
---
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 3步创建 Skill
|
||||
|
||||
**第1步: 创建目录**
|
||||
|
||||
```bash
|
||||
mkdir -p .claude/skill/your-skill-name
|
||||
cd .claude/skill/your-skill-name
|
||||
```
|
||||
|
||||
**第2步: 创建 SKILL.md**
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: your-skill-name
|
||||
description: Brief description with trigger keywords and scenarios
|
||||
---
|
||||
|
||||
# Your Skill Title
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- User asks to [specific scenario]
|
||||
- User mentions "[keyword]"
|
||||
|
||||
## How It Works
|
||||
|
||||
1. Step 1: [Action]
|
||||
2. Step 2: [Action]
|
||||
|
||||
## Examples
|
||||
|
||||
**Input**: User request
|
||||
**Output**: Expected result
|
||||
```
|
||||
|
||||
**第3步: 测试**
|
||||
- 在对话中使用 description 中的关键词触发
|
||||
- 观察 Claude 是否正确执行
|
||||
- 根据效果调整
|
||||
|
||||
---
|
||||
|
||||
## 核心原则
|
||||
|
||||
### 1. 保持简洁
|
||||
|
||||
只添加 Claude **不知道**的新知识:
|
||||
- ✅ 项目特定的工作流程
|
||||
- ✅ 特殊的命名规范或格式要求
|
||||
- ✅ 自定义工具和脚本的使用方法
|
||||
- ❌ 通用编程知识
|
||||
- ❌ 显而易见的步骤
|
||||
|
||||
**示例对比**:
|
||||
|
||||
```markdown
|
||||
# ❌ 过度详细
|
||||
1. 创建 Python 文件
|
||||
2. 导入必要的库
|
||||
3. 定义函数
|
||||
4. 编写主程序逻辑
|
||||
|
||||
# ✅ 简洁有效
|
||||
使用 `scripts/api_client.py` 调用内部 API。
|
||||
请求头必须包含 `X-Internal-Token`(从环境变量 `INTERNAL_API_KEY` 获取)。
|
||||
```
|
||||
|
||||
### 2. 设定合适的自由度
|
||||
|
||||
| 自由度 | 适用场景 | 编写方式 |
|
||||
|--------|---------|---------|
|
||||
| **高** | 需要创造性、多种解决方案 | 提供指导原则,不限定具体步骤 |
|
||||
| **中** | 有推荐模式但允许变化 | 提供参数化示例和默认流程 |
|
||||
| **低** | 容易出错、需严格执行 | 提供详细的分步指令或脚本 |
|
||||
|
||||
**判断标准**:
|
||||
- 任务是否有明确的"正确答案"? → 低自由度
|
||||
- 是否需要适应不同场景? → 高自由度
|
||||
- 错误的代价有多大? → 代价高则用低自由度
|
||||
|
||||
### 3. 渐进式披露
|
||||
|
||||
将复杂内容分层组织:
|
||||
|
||||
```
|
||||
SKILL.md (主文档, 200-500行)
|
||||
├── reference.md (详细文档)
|
||||
├── examples.md (完整示例)
|
||||
└── scripts/ (可执行脚本)
|
||||
```
|
||||
|
||||
**规则**:
|
||||
- SKILL.md 超过 500行 → 拆分子文件
|
||||
- 子文件超过 100行 → 添加目录
|
||||
- 引用深度 ≤ 1层
|
||||
|
||||
---
|
||||
|
||||
## 文件结构规范
|
||||
|
||||
### YAML Frontmatter
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: skill-name-here
|
||||
description: Clear description of what this skill does and when to activate it
|
||||
---
|
||||
```
|
||||
|
||||
**字段规范**:
|
||||
|
||||
| 字段 | 要求 | 说明 |
|
||||
|------|------|------|
|
||||
| `name` | 小写字母、数字、短横线,≤64字符 | 必须与目录名一致 |
|
||||
| `description` | 纯文本,≤1024字符 | 用于检索和激活 |
|
||||
|
||||
**命名禁忌**:
|
||||
- ❌ XML 标签、保留字(`anthropic`, `claude`)
|
||||
- ❌ 模糊词汇(`helper`, `utility`, `manager`)
|
||||
- ❌ 空格或下划线(用短横线 `-`)
|
||||
|
||||
**Description 技巧**:
|
||||
|
||||
```yaml
|
||||
# ❌ 过于泛化
|
||||
description: Helps with code tasks
|
||||
|
||||
# ✅ 具体且包含关键词
|
||||
description: Processes CSV files and generates Excel reports with charts. Use when user asks to convert data formats or create visual reports.
|
||||
|
||||
# ✅ 说明触发场景
|
||||
description: Analyzes Python code for security vulnerabilities using bandit. Activates when user mentions "security audit" or "vulnerability scan".
|
||||
```
|
||||
|
||||
### 目录组织
|
||||
|
||||
**基础结构**(简单 Skill):
|
||||
```
|
||||
skill-name/
|
||||
└── SKILL.md
|
||||
```
|
||||
|
||||
**标准结构**(推荐):
|
||||
```
|
||||
skill-name/
|
||||
├── SKILL.md
|
||||
├── templates/
|
||||
│ └── template.md
|
||||
└── scripts/
|
||||
└── script.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 命名和描述规范
|
||||
|
||||
### Skill 命名
|
||||
|
||||
**推荐格式**: 动名词形式 (verb-ing + noun)
|
||||
|
||||
```
|
||||
✅ 好的命名:
|
||||
- processing-csv-files
|
||||
- generating-api-docs
|
||||
- managing-database-migrations
|
||||
|
||||
❌ 不好的命名:
|
||||
- csv (过于简短)
|
||||
- data_processor (使用下划线)
|
||||
- helper (过于模糊)
|
||||
```
|
||||
|
||||
### Description 编写
|
||||
|
||||
**必须使用第三人称**:
|
||||
|
||||
```yaml
|
||||
# ❌ 错误
|
||||
description: I help you process PDFs
|
||||
|
||||
# ✅ 正确
|
||||
description: Processes PDF documents and extracts structured data
|
||||
```
|
||||
|
||||
**4C 原则**:
|
||||
- **Clear** (清晰): 避免术语和模糊词汇
|
||||
- **Concise** (简洁): 1-2句话说明核心功能
|
||||
- **Contextual** (上下文): 说明适用场景
|
||||
- **Complete** (完整): 功能 + 触发条件
|
||||
|
||||
---
|
||||
|
||||
## 内容编写指南
|
||||
|
||||
### "When to Use" 章节
|
||||
|
||||
明确说明触发场景:
|
||||
|
||||
```markdown
|
||||
## When to Use This Skill
|
||||
|
||||
- User asks to analyze Python code for type errors
|
||||
- User mentions "mypy" or "type checking"
|
||||
- User is working in a Python project with type hints
|
||||
- User needs to add type annotations
|
||||
```
|
||||
|
||||
**模式**:
|
||||
- 直接请求: "User asks to X"
|
||||
- 关键词: "User mentions 'keyword'"
|
||||
- 上下文: "User is working with X"
|
||||
- 任务类型: "User needs to X"
|
||||
|
||||
### 工作流设计
|
||||
|
||||
**简单线性流程**:
|
||||
|
||||
```markdown
|
||||
## How It Works
|
||||
|
||||
1. Scan the project for all `.py` files
|
||||
2. Run `mypy --strict` on each file
|
||||
3. Parse error output and categorize by severity
|
||||
4. Generate summary report with fix suggestions
|
||||
```
|
||||
|
||||
**条件分支流程**:
|
||||
|
||||
```markdown
|
||||
## Workflow
|
||||
|
||||
1. **Check project type**
|
||||
- If Django → Use `django-stubs` config
|
||||
- If Flask → Use `flask-stubs` config
|
||||
- Otherwise → Use default mypy config
|
||||
|
||||
2. **Run type checking**
|
||||
- If errors found → Proceed to step 3
|
||||
- If no errors → Report success and exit
|
||||
```
|
||||
|
||||
**Checklist 模式**(验证型任务):
|
||||
|
||||
```markdown
|
||||
## Pre-deployment Checklist
|
||||
|
||||
Execute in order. Stop if any step fails.
|
||||
|
||||
- [ ] Run tests: `npm test` (must pass)
|
||||
- [ ] Build: `npm run build` (no errors)
|
||||
- [ ] Check deps: `npm audit` (no critical vulnerabilities)
|
||||
```
|
||||
|
||||
### 示例和模板
|
||||
|
||||
**输入-输出示例**:
|
||||
|
||||
```markdown
|
||||
## Examples
|
||||
|
||||
### Example 1: Basic Check
|
||||
|
||||
**User Request**: "Check my code for type errors"
|
||||
|
||||
**Action**:
|
||||
1. Scan for `.py` files
|
||||
2. Run `mypy` on all files
|
||||
|
||||
**Output**:
|
||||
|
||||
Found 3 type errors in 2 files:
|
||||
src/main.py:15: error: Missing return type
|
||||
src/utils.py:42: error: Incompatible types
|
||||
|
||||
```
|
||||
|
||||
### 脚本集成
|
||||
|
||||
**何时使用脚本**:
|
||||
- 简单命令 → 直接在 SKILL.md 中说明
|
||||
- 复杂流程 → 提供独立脚本
|
||||
|
||||
**脚本编写规范**:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Brief description of what this script does.
|
||||
|
||||
Usage:
|
||||
python script.py <arg> [--option value]
|
||||
"""
|
||||
|
||||
import argparse
|
||||
|
||||
DEFAULT_VALUE = 80 # Use constants, not magic numbers
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("directory", help="Directory to process")
|
||||
parser.add_argument("--threshold", type=int, default=DEFAULT_VALUE)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Validate inputs
|
||||
if not Path(args.directory).is_dir():
|
||||
print(f"Error: {args.directory} not found")
|
||||
return 1
|
||||
|
||||
# Execute
|
||||
result = process(args.directory, args.threshold)
|
||||
|
||||
# Report
|
||||
print(f"Processed {result['count']} files")
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
```
|
||||
|
||||
**关键规范**:
|
||||
- ✅ Shebang 行和 docstring
|
||||
- ✅ 类型注解和常量
|
||||
- ✅ 参数验证和错误处理
|
||||
- ✅ 清晰的返回值(0=成功, 1=失败)
|
||||
|
||||
### 最佳实践
|
||||
|
||||
**Do**:
|
||||
- ✅ 提供可执行的命令和脚本
|
||||
- ✅ 包含输入-输出示例
|
||||
- ✅ 说明验证标准和成功条件
|
||||
- ✅ 包含 Do/Don't 清单
|
||||
|
||||
**Don't**:
|
||||
- ❌ 包含 Claude 已知的通用知识
|
||||
- ❌ 使用抽象描述而非具体步骤
|
||||
- ❌ 遗漏错误处理指导
|
||||
- ❌ 示例使用伪代码而非真实代码
|
||||
|
||||
---
|
||||
|
||||
## 质量检查清单
|
||||
|
||||
### 核心质量
|
||||
|
||||
- [ ] `name` 符合命名规范(小写、短横线、≤64字符)
|
||||
- [ ] `description` 包含触发关键词和场景(≤1024字符)
|
||||
- [ ] 名称与目录名一致
|
||||
- [ ] 只包含 Claude 不知道的信息
|
||||
- [ ] 没有冗余或重复内容
|
||||
|
||||
### 功能完整性
|
||||
|
||||
- [ ] 有"When to Use"章节,列出 3-5 个触发场景
|
||||
- [ ] 有清晰的执行流程或步骤
|
||||
- [ ] 至少 2-3 个完整示例
|
||||
- [ ] 包含输入和预期输出
|
||||
- [ ] 错误处理有指导
|
||||
|
||||
### 结构规范
|
||||
|
||||
- [ ] 章节组织清晰
|
||||
- [ ] 超过 200行有目录导航
|
||||
- [ ] 引用层级 ≤ 1层
|
||||
- [ ] 所有路径使用正斜杠 `/`
|
||||
- [ ] 术语使用一致
|
||||
|
||||
### 脚本和模板
|
||||
|
||||
- [ ] 脚本包含使用说明和参数文档
|
||||
- [ ] 脚本有错误处理
|
||||
- [ ] 避免魔法数字,使用配置
|
||||
- [ ] 模板格式清晰易用
|
||||
|
||||
### 最终检查
|
||||
|
||||
- [ ] 通读全文,确保流畅易读
|
||||
- [ ] 使用实际场景测试触发
|
||||
- [ ] 长度适中(200-500行,或已拆分)
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
**Q: Skill 多长才合适?**
|
||||
- 最小: 50-100行
|
||||
- 理想: 200-500行
|
||||
- 最大: 500行(超过则拆分)
|
||||
|
||||
**Q: 如何让 Skill 更容易激活?**
|
||||
- 在 `description` 中使用用户会说的关键词
|
||||
- 说明具体场景("when user asks to X")
|
||||
- 提及相关工具名称
|
||||
|
||||
**Q: 多个 Skill 功能重叠怎么办?**
|
||||
- 使用更具体的 `description` 区分
|
||||
- 在"When to Use"中说明关系
|
||||
- 考虑合并为一个 Skill
|
||||
|
||||
**Q: Skill 需要维护吗?**
|
||||
- 每季度审查一次,更新过时信息
|
||||
- 根据使用反馈迭代
|
||||
- 工具或 API 变更时及时更新
|
||||
|
||||
---
|
||||
|
||||
## 快速参考
|
||||
|
||||
### Frontmatter 模板
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: skill-name
|
||||
description: Brief description with trigger keywords
|
||||
---
|
||||
```
|
||||
|
||||
### 基础结构模板
|
||||
|
||||
```markdown
|
||||
# Skill Title
|
||||
|
||||
## When to Use This Skill
|
||||
- Scenario 1
|
||||
- Scenario 2
|
||||
|
||||
## How It Works
|
||||
1. Step 1
|
||||
2. Step 2
|
||||
|
||||
## Examples
|
||||
### Example 1
|
||||
...
|
||||
|
||||
## References
|
||||
- [Link](url)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 相关资源
|
||||
|
||||
- [Claude Agent Skills 官方文档](https://docs.claude.com/en/docs/agents-and-tools/agent-skills)
|
||||
- [Best Practices Checklist](https://docs.claude.com/en/docs/agents-and-tools/agent-skills/best-practices)
|
||||
- [模板文件](templates/) - 开箱即用的模板
|
||||
- [基础 skill 的模板](templates/basic-skill-template.md)
|
||||
- [工作流 skill 的模板](templates/workflow-skill-template.md)
|
||||
- [示例库](examples/) - 完整的 Skill 示例
|
||||
- [优秀示例](examples/good-example.md)
|
||||
- [常见错误示例](examples/bad-example.md)
|
||||
|
||||
---
|
||||
|
|
@ -1,867 +0,0 @@
|
|||
# 不好的 Skill 示例与改进建议
|
||||
|
||||
本文档展示常见的 Skill 编写错误,并提供改进建议。
|
||||
|
||||
---
|
||||
|
||||
## 示例 1: 过于模糊的 Skill
|
||||
|
||||
### ❌ 不好的版本
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: helper
|
||||
description: Helps with code
|
||||
---
|
||||
|
||||
# Code Helper
|
||||
|
||||
This skill helps you with coding tasks.
|
||||
|
||||
## Usage
|
||||
|
||||
Use this when you need help with code.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. Analyzes your code
|
||||
2. Provides suggestions
|
||||
3. Helps improve it
|
||||
```
|
||||
|
||||
### 问题分析
|
||||
|
||||
| 问题 | 说明 | 影响 |
|
||||
|------|------|------|
|
||||
| **模糊的名称** | "helper" 太泛化,没有说明具体做什么 | Claude 不知道何时激活 |
|
||||
| **无关键词** | description 缺少具体触发词 | 用户很难激活这个 Skill |
|
||||
| **无具体场景** | 没说明适用什么类型的代码 | 适用范围不清 |
|
||||
| **抽象的步骤** | "Provides suggestions" 太模糊 | Claude 不知道具体做什么 |
|
||||
| **无示例** | 没有实际例子 | 用户和 Claude 都不清楚预期输出 |
|
||||
|
||||
### ✅ 改进版本
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: python-code-refactoring
|
||||
description: Refactors Python code to improve readability and maintainability using standard patterns. Activates when user asks to clean up code, improve structure, or mentions refactoring. Focuses on function extraction, variable naming, and removing code smells.
|
||||
---
|
||||
|
||||
# Python Code Refactoring Skill
|
||||
|
||||
Improves Python code quality through systematic refactoring.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- User asks to "refactor this code", "clean up this function", or "improve readability"
|
||||
- User mentions "code smell", "technical debt", or "maintainability"
|
||||
- User is working with Python code that has:
|
||||
- Long functions (>50 lines)
|
||||
- Nested conditionals (>3 levels)
|
||||
- Repeated code patterns
|
||||
- Unclear variable names
|
||||
|
||||
## How It Works
|
||||
|
||||
### 1. Identify Refactoring Opportunities
|
||||
|
||||
Scan code for common issues:
|
||||
- Functions longer than 50 lines
|
||||
- Repeated code blocks (DRY violations)
|
||||
- Magic numbers without constants
|
||||
- Unclear variable names (x, temp, data)
|
||||
- Deep nesting (>3 levels)
|
||||
|
||||
### 2. Prioritize Changes
|
||||
|
||||
Focus on high-impact refactorings:
|
||||
- **High**: Extract complex nested logic to functions
|
||||
- **Medium**: Rename unclear variables
|
||||
- **Low**: Minor style improvements
|
||||
|
||||
### 3. Apply Refactorings
|
||||
|
||||
**Extract Method**:
|
||||
```python
|
||||
# Before
|
||||
def process_order(order):
|
||||
# 50 lines of validation logic
|
||||
# 30 lines of payment processing
|
||||
# 20 lines of email notification
|
||||
|
||||
# After
|
||||
def process_order(order):
|
||||
validate_order(order)
|
||||
process_payment(order)
|
||||
send_confirmation_email(order)
|
||||
```
|
||||
|
||||
**Replace Magic Numbers**:
|
||||
```python
|
||||
# Before
|
||||
if user.age < 18:
|
||||
return False
|
||||
|
||||
# After
|
||||
MINIMUM_AGE = 18
|
||||
if user.age < MINIMUM_AGE:
|
||||
return False
|
||||
```
|
||||
|
||||
**Simplify Conditionals**:
|
||||
```python
|
||||
# Before
|
||||
if user.role == 'admin':
|
||||
return True
|
||||
elif user.role == 'moderator':
|
||||
return True
|
||||
elif user.role == 'editor':
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# After
|
||||
PRIVILEGED_ROLES = {'admin', 'moderator', 'editor'}
|
||||
return user.role in PRIVILEGED_ROLES
|
||||
```
|
||||
|
||||
### 4. Verify Improvements
|
||||
|
||||
After refactoring:
|
||||
- Run existing tests (all must pass)
|
||||
- Check code length reduced
|
||||
- Verify improved readability
|
||||
|
||||
## Example
|
||||
|
||||
**User Request**: "Refactor this function, it's too long"
|
||||
|
||||
```python
|
||||
def process_user_registration(data):
|
||||
if not data.get('email'):
|
||||
return {'error': 'Email required'}
|
||||
if '@' not in data['email']:
|
||||
return {'error': 'Invalid email'}
|
||||
if not data.get('password'):
|
||||
return {'error': 'Password required'}
|
||||
if len(data['password']) < 8:
|
||||
return {'error': 'Password too short'}
|
||||
if not any(c.isupper() for c in data['password']):
|
||||
return {'error': 'Password needs uppercase'}
|
||||
existing = db.query(User).filter_by(email=data['email']).first()
|
||||
if existing:
|
||||
return {'error': 'Email already registered'}
|
||||
salt = bcrypt.gensalt()
|
||||
hashed = bcrypt.hashpw(data['password'].encode(), salt)
|
||||
user = User(email=data['email'], password_hash=hashed)
|
||||
db.add(user)
|
||||
db.commit()
|
||||
token = jwt.encode({'user_id': user.id}, SECRET_KEY)
|
||||
send_email(data['email'], 'Welcome!', 'Thanks for registering')
|
||||
return {'success': True, 'token': token}
|
||||
```
|
||||
|
||||
**Refactored**:
|
||||
|
||||
```python
|
||||
def process_user_registration(data):
|
||||
"""Register new user with validation and email confirmation."""
|
||||
# Validation
|
||||
validation_error = validate_registration_data(data)
|
||||
if validation_error:
|
||||
return {'error': validation_error}
|
||||
|
||||
# Check uniqueness
|
||||
if user_exists(data['email']):
|
||||
return {'error': 'Email already registered'}
|
||||
|
||||
# Create user
|
||||
user = create_user(data['email'], data['password'])
|
||||
|
||||
# Generate token
|
||||
token = generate_auth_token(user.id)
|
||||
|
||||
# Send welcome email
|
||||
send_welcome_email(user.email)
|
||||
|
||||
return {'success': True, 'token': token}
|
||||
|
||||
|
||||
def validate_registration_data(data):
|
||||
"""Validate registration data, return error message or None."""
|
||||
if not data.get('email'):
|
||||
return 'Email required'
|
||||
if '@' not in data['email']:
|
||||
return 'Invalid email'
|
||||
if not data.get('password'):
|
||||
return 'Password required'
|
||||
return validate_password_strength(data['password'])
|
||||
|
||||
|
||||
def validate_password_strength(password):
|
||||
"""Check password meets security requirements."""
|
||||
MIN_PASSWORD_LENGTH = 8
|
||||
if len(password) < MIN_PASSWORD_LENGTH:
|
||||
return f'Password must be at least {MIN_PASSWORD_LENGTH} characters'
|
||||
if not any(c.isupper() for c in password):
|
||||
return 'Password must contain uppercase letter'
|
||||
return None
|
||||
|
||||
|
||||
def user_exists(email):
|
||||
"""Check if user with given email already exists."""
|
||||
return db.query(User).filter_by(email=email).first() is not None
|
||||
|
||||
|
||||
def create_user(email, password):
|
||||
"""Create and save new user with hashed password."""
|
||||
salt = bcrypt.gensalt()
|
||||
hashed = bcrypt.hashpw(password.encode(), salt)
|
||||
user = User(email=email, password_hash=hashed)
|
||||
db.add(user)
|
||||
db.commit()
|
||||
return user
|
||||
|
||||
|
||||
def generate_auth_token(user_id):
|
||||
"""Generate JWT authentication token."""
|
||||
return jwt.encode({'user_id': user_id}, SECRET_KEY)
|
||||
|
||||
|
||||
def send_welcome_email(email):
|
||||
"""Send welcome email to new user."""
|
||||
send_email(email, 'Welcome!', 'Thanks for registering')
|
||||
```
|
||||
|
||||
**Improvements**:
|
||||
- ✅ Main function reduced from 20 lines to 15 lines
|
||||
- ✅ Each function has single responsibility
|
||||
- ✅ Magic number (8) extracted to constant
|
||||
- ✅ All functions documented with docstrings
|
||||
- ✅ Easier to test individual functions
|
||||
- ✅ Easier to modify validation rules
|
||||
|
||||
## Best Practices
|
||||
|
||||
- ✅ Extract functions with clear names
|
||||
- ✅ Use constants instead of magic numbers
|
||||
- ✅ Keep functions under 30 lines
|
||||
- ✅ Maximum nesting depth of 2-3 levels
|
||||
- ✅ Write docstrings for extracted functions
|
||||
```
|
||||
|
||||
### 改进要点
|
||||
|
||||
1. ✅ 具体的名称: `python-code-refactoring` 而非 `helper`
|
||||
2. ✅ 详细的 description: 包含触发词和适用场景
|
||||
3. ✅ 明确的触发条件: 列出具体的使用场景
|
||||
4. ✅ 可执行的步骤: 每个步骤都有具体操作
|
||||
5. ✅ 实际代码示例: 展示完整的重构过程
|
||||
6. ✅ 具体的改进指标: 列出可验证的改进效果
|
||||
|
||||
---
|
||||
|
||||
## 示例 2: 过度冗长的 Skill
|
||||
|
||||
### ❌ 不好的版本
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: python-basics
|
||||
description: Teaches Python programming basics
|
||||
---
|
||||
|
||||
# Python Basics
|
||||
|
||||
This skill helps you learn Python programming.
|
||||
|
||||
## Variables
|
||||
|
||||
In Python, you can create variables like this:
|
||||
|
||||
```python
|
||||
x = 5
|
||||
y = "hello"
|
||||
z = 3.14
|
||||
```
|
||||
|
||||
Python supports different data types:
|
||||
- Integers (int): whole numbers like 1, 2, 3
|
||||
- Floats (float): decimal numbers like 3.14, 2.5
|
||||
- Strings (str): text like "hello", 'world'
|
||||
- Booleans (bool): True or False
|
||||
|
||||
## Conditional Statements
|
||||
|
||||
You can use if statements to make decisions:
|
||||
|
||||
```python
|
||||
if x > 0:
|
||||
print("Positive")
|
||||
elif x < 0:
|
||||
print("Negative")
|
||||
else:
|
||||
print("Zero")
|
||||
```
|
||||
|
||||
The if statement checks a condition. If True, it runs the indented code.
|
||||
The elif means "else if" and provides an alternative condition.
|
||||
The else runs if none of the above conditions are True.
|
||||
|
||||
## Loops
|
||||
|
||||
Python has two main types of loops:
|
||||
|
||||
### For Loops
|
||||
|
||||
For loops iterate over a sequence:
|
||||
|
||||
```python
|
||||
for i in range(5):
|
||||
print(i)
|
||||
```
|
||||
|
||||
This prints numbers 0 through 4. The range() function generates numbers.
|
||||
|
||||
### While Loops
|
||||
|
||||
While loops continue while a condition is True:
|
||||
|
||||
```python
|
||||
i = 0
|
||||
while i < 5:
|
||||
print(i)
|
||||
i += 1
|
||||
```
|
||||
|
||||
This does the same thing as the for loop above.
|
||||
|
||||
## Functions
|
||||
|
||||
Functions are reusable blocks of code:
|
||||
|
||||
```python
|
||||
def greet(name):
|
||||
return f"Hello, {name}!"
|
||||
```
|
||||
|
||||
The def keyword defines a function. The function name is greet.
|
||||
It takes one parameter called name. The return statement sends back a value.
|
||||
|
||||
## Lists
|
||||
|
||||
Lists store multiple items:
|
||||
|
||||
```python
|
||||
fruits = ["apple", "banana", "orange"]
|
||||
```
|
||||
|
||||
You can access items by index:
|
||||
|
||||
```python
|
||||
first_fruit = fruits[0] # "apple"
|
||||
```
|
||||
|
||||
... [continues for 50 more sections about Python basics]
|
||||
```
|
||||
|
||||
### 问题分析
|
||||
|
||||
| 问题 | 说明 | 影响 |
|
||||
|------|------|------|
|
||||
| **包含通用知识** | Python 基础知识 Claude 已经知道 | 浪费 token,增加检索成本 |
|
||||
| **教程式内容** | 像教程而非工作指南 | Claude 不需要学习,需要的是工作指导 |
|
||||
| **过度详细** | 解释显而易见的概念 | 信息过载,难以找到关键信息 |
|
||||
| **缺少项目特定信息** | 没有项目相关的规范或约定 | 无法提供项目特定价值 |
|
||||
|
||||
### ✅ 改进版本
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: project-python-conventions
|
||||
description: Enforces Python coding conventions specific to this project. Activates when user writes Python code or asks about code style. Covers naming, imports, error handling, and project-specific patterns.
|
||||
---
|
||||
|
||||
# Project Python Conventions
|
||||
|
||||
Project-specific Python coding standards and patterns.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- User is writing or reviewing Python code
|
||||
- User asks about code style or conventions
|
||||
- User needs guidance on project patterns
|
||||
|
||||
## Import Organization
|
||||
|
||||
Follow this order:
|
||||
|
||||
```python
|
||||
# 1. Standard library
|
||||
import os
|
||||
import sys
|
||||
from typing import Optional, List
|
||||
|
||||
# 2. Third-party packages
|
||||
import numpy as np
|
||||
from fastapi import FastAPI
|
||||
|
||||
# 3. Local application imports
|
||||
from core.models import User
|
||||
from utils.helpers import format_date
|
||||
```
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
### Project-Specific Rules
|
||||
|
||||
| Type | Pattern | Example |
|
||||
|------|---------|---------|
|
||||
| API endpoints | `/api/v1/{resource}` | `/api/v1/users` |
|
||||
| Database tables | `{resource}_table` | `users_table` |
|
||||
| Environment variables | `APP_{NAME}` | `APP_DATABASE_URL` |
|
||||
| Config files | `{env}.config.py` | `prod.config.py` |
|
||||
|
||||
### Forbidden Patterns
|
||||
|
||||
```python
|
||||
# ❌ Don't use single-letter variables (except i, j, k in loops)
|
||||
d = get_data()
|
||||
|
||||
# ✅ Do use descriptive names
|
||||
user_data = get_data()
|
||||
|
||||
# ❌ Don't use abbreviations
|
||||
usr_mgr = UserManager()
|
||||
|
||||
# ✅ Do use full words
|
||||
user_manager = UserManager()
|
||||
```
|
||||
|
||||
## Error Handling Pattern
|
||||
|
||||
Use project's custom exceptions:
|
||||
|
||||
```python
|
||||
from core.exceptions import UserNotFoundError, ValidationError
|
||||
|
||||
def get_user(user_id: int) -> User:
|
||||
"""
|
||||
Retrieve user by ID.
|
||||
|
||||
Raises:
|
||||
UserNotFoundError: If user doesn't exist
|
||||
ValidationError: If user_id is invalid
|
||||
"""
|
||||
if not isinstance(user_id, int) or user_id <= 0:
|
||||
raise ValidationError(f"Invalid user_id: {user_id}")
|
||||
|
||||
user = db.query(User).get(user_id)
|
||||
if user is None:
|
||||
raise UserNotFoundError(f"User {user_id} not found")
|
||||
|
||||
return user
|
||||
```
|
||||
|
||||
**Never** use bare `except:` - always catch specific exceptions.
|
||||
|
||||
## Database Queries
|
||||
|
||||
Always use the project's query helper:
|
||||
|
||||
```python
|
||||
# ❌ Don't use raw SQLAlchemy queries
|
||||
users = db.query(User).filter(User.age > 18).all()
|
||||
|
||||
# ✅ Do use query helper
|
||||
from core.database import QueryBuilder
|
||||
|
||||
users = QueryBuilder(User).where('age', '>', 18).get()
|
||||
```
|
||||
|
||||
## API Response Format
|
||||
|
||||
All API endpoints must return this format:
|
||||
|
||||
```python
|
||||
{
|
||||
"success": True,
|
||||
"data": {
|
||||
# ... response data
|
||||
},
|
||||
"error": None,
|
||||
"meta": {
|
||||
"timestamp": "2025-01-31T12:00:00Z",
|
||||
"version": "1.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use the response helper:
|
||||
|
||||
```python
|
||||
from core.responses import success_response, error_response
|
||||
|
||||
@app.get("/users/{id}")
|
||||
async def get_user(id: int):
|
||||
try:
|
||||
user = get_user_data(id)
|
||||
return success_response(user)
|
||||
except UserNotFoundError as e:
|
||||
return error_response(str(e), status_code=404)
|
||||
```
|
||||
|
||||
## Testing Patterns
|
||||
|
||||
### Test File Location
|
||||
|
||||
```
|
||||
project/
|
||||
├── src/
|
||||
│ └── services/
|
||||
│ └── user_service.py
|
||||
└── tests/
|
||||
└── services/
|
||||
└── test_user_service.py
|
||||
```
|
||||
|
||||
### Test Naming
|
||||
|
||||
```python
|
||||
# Format: test_{function_name}_{scenario}_{expected_result}
|
||||
|
||||
def test_get_user_valid_id_returns_user():
|
||||
"""Test getting user with valid ID returns User object."""
|
||||
pass
|
||||
|
||||
def test_get_user_invalid_id_raises_validation_error():
|
||||
"""Test getting user with invalid ID raises ValidationError."""
|
||||
pass
|
||||
|
||||
def test_get_user_nonexistent_id_raises_not_found_error():
|
||||
"""Test getting non-existent user raises UserNotFoundError."""
|
||||
pass
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [Full Style Guide](docs/STYLE_GUIDE.md)
|
||||
- [API Standards](docs/API_STANDARDS.md)
|
||||
- [Database Conventions](docs/DATABASE.md)
|
||||
```
|
||||
|
||||
### 改进要点
|
||||
|
||||
1. ✅ 只包含项目特定信息: 不教 Python 基础
|
||||
2. ✅ 简洁明了: 200 行 vs 原来的 500+ 行
|
||||
3. ✅ 实用的规则: 直接可应用的约定
|
||||
4. ✅ 清晰的示例: Do/Don't 对比
|
||||
5. ✅ 引用详细文档: 用链接而非全部内容
|
||||
|
||||
---
|
||||
|
||||
## 示例 3: 缺少上下文的 Skill
|
||||
|
||||
### ❌ 不好的版本
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: deployment
|
||||
description: Deploys code
|
||||
---
|
||||
|
||||
# Deployment
|
||||
|
||||
## Steps
|
||||
|
||||
1. Build the code
|
||||
2. Run tests
|
||||
3. Deploy to server
|
||||
4. Verify deployment
|
||||
```
|
||||
|
||||
### 问题分析
|
||||
|
||||
| 问题 | 说明 | 影响 |
|
||||
|------|------|------|
|
||||
| **无具体命令** | 没说明如何 build, test, deploy | Claude 无法执行 |
|
||||
| **无环境区分** | 开发、测试、生产部署可能不同 | 可能部署到错误环境 |
|
||||
| **无错误处理** | 没说明出错时怎么办 | 失败时不知如何恢复 |
|
||||
| **无验证标准** | "Verify" 太模糊 | 不知道检查什么 |
|
||||
|
||||
### ✅ 改进版本
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: deploy-to-production
|
||||
description: Deploys application to production environment on AWS. Activates when user asks to deploy to prod or mentions production deployment. Includes pre-flight checks, blue-green deployment, and rollback procedures.
|
||||
---
|
||||
|
||||
# Production Deployment
|
||||
|
||||
Safely deploy application to production with zero downtime.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- User asks to "deploy to production" or "push to prod"
|
||||
- User mentions "production deployment", "go live"
|
||||
- User needs to rollback a deployment
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before deployment, verify:
|
||||
|
||||
```bash
|
||||
# 1. On main branch
|
||||
git branch --show-current # Must be "main"
|
||||
|
||||
# 2. All tests pass
|
||||
npm test # Exit code must be 0
|
||||
|
||||
# 3. Build succeeds
|
||||
npm run build # Must complete without errors
|
||||
|
||||
# 4. No uncommitted changes
|
||||
git status # Must show "nothing to commit"
|
||||
|
||||
# 5. Latest code pulled
|
||||
git pull origin main # Must be up to date
|
||||
```
|
||||
|
||||
If any prerequisite fails, **stop** and fix the issue.
|
||||
|
||||
## Deployment Process
|
||||
|
||||
### Step 1: Pre-flight Checks
|
||||
|
||||
```bash
|
||||
# Run deployment readiness script
|
||||
./scripts/preflight-check.sh
|
||||
|
||||
# Expected output:
|
||||
# ✓ Tests passed
|
||||
# ✓ Build succeeded
|
||||
# ✓ Environment variables configured
|
||||
# ✓ Database migrations ready
|
||||
# ✓ Ready to deploy
|
||||
```
|
||||
|
||||
### Step 2: Database Migrations (if needed)
|
||||
|
||||
```bash
|
||||
# Connect to production database
|
||||
aws rds describe-db-instances --db-instance-identifier prod-db
|
||||
|
||||
# Backup before migration
|
||||
./scripts/backup-database.sh prod
|
||||
|
||||
# Run migrations
|
||||
NODE_ENV=production npm run migrate
|
||||
|
||||
# Verify migration succeeded
|
||||
npm run migrate:status
|
||||
```
|
||||
|
||||
### Step 3: Blue-Green Deployment
|
||||
|
||||
```bash
|
||||
# Deploy to green environment (inactive)
|
||||
aws elasticbeanstalk create-environment \
|
||||
--application-name myapp \
|
||||
--environment-name myapp-prod-green \
|
||||
--solution-stack-name "64bit Amazon Linux 2 v5.x.x running Node.js 18"
|
||||
|
||||
# Wait for green environment to be healthy
|
||||
aws elasticbeanstalk wait environment-updated \
|
||||
--environment-name myapp-prod-green
|
||||
|
||||
# Check green environment health
|
||||
curl https://myapp-prod-green.aws.com/health
|
||||
# Expected: {"status": "healthy"}
|
||||
```
|
||||
|
||||
### Step 4: Smoke Tests
|
||||
|
||||
```bash
|
||||
# Run smoke tests against green environment
|
||||
BASE_URL=https://myapp-prod-green.aws.com npm run test:smoke
|
||||
|
||||
# Tests must include:
|
||||
# - Health check endpoint
|
||||
# - Authentication flow
|
||||
# - Critical API endpoints
|
||||
# - Database connectivity
|
||||
```
|
||||
|
||||
### Step 5: Switch Traffic
|
||||
|
||||
```bash
|
||||
# Swap URLs (blue becomes green, green becomes blue)
|
||||
aws elasticbeanstalk swap-environment-cnames \
|
||||
--source-environment-name myapp-prod-blue \
|
||||
--destination-environment-name myapp-prod-green
|
||||
|
||||
# Wait 5 minutes for DNS propagation
|
||||
echo "Waiting for DNS propagation..."
|
||||
sleep 300
|
||||
|
||||
# Verify production URL serves new version
|
||||
curl https://myapp.com/version
|
||||
# Expected: {"version": "1.2.3"} (new version)
|
||||
```
|
||||
|
||||
### Step 6: Monitor
|
||||
|
||||
```bash
|
||||
# Monitor error rates for 15 minutes
|
||||
aws cloudwatch get-metric-statistics \
|
||||
--namespace AWS/ELB \
|
||||
--metric-name HTTPCode_Backend_5XX \
|
||||
--start-time $(date -u -d '15 minutes ago' +%Y-%m-%dT%H:%M:%S) \
|
||||
--end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
|
||||
--period 300 \
|
||||
--statistics Sum
|
||||
|
||||
# Error rate must be < 1%
|
||||
```
|
||||
|
||||
If error rate exceeds 1%:
|
||||
- **Rollback immediately** (see Rollback section)
|
||||
- Investigate issue
|
||||
- Fix and redeploy
|
||||
|
||||
### Step 7: Cleanup
|
||||
|
||||
```bash
|
||||
# After 24 hours, if no issues:
|
||||
# Terminate old blue environment
|
||||
aws elasticbeanstalk terminate-environment \
|
||||
--environment-name myapp-prod-blue
|
||||
```
|
||||
|
||||
## Rollback Procedure
|
||||
|
||||
If deployment fails:
|
||||
|
||||
```bash
|
||||
# 1. Swap back to previous version
|
||||
aws elasticbeanstalk swap-environment-cnames \
|
||||
--source-environment-name myapp-prod-green \
|
||||
--destination-environment-name myapp-prod-blue
|
||||
|
||||
# 2. Verify old version is serving
|
||||
curl https://myapp.com/version
|
||||
# Expected: {"version": "1.2.2"} (old version)
|
||||
|
||||
# 3. Rollback database migrations (if ran)
|
||||
NODE_ENV=production npm run migrate:rollback
|
||||
|
||||
# 4. Notify team
|
||||
./scripts/notify-rollback.sh "Deployment rolled back due to [reason]"
|
||||
```
|
||||
|
||||
## Example Deployment
|
||||
|
||||
**User Request**: "Deploy v1.2.3 to production"
|
||||
|
||||
**Execution Log**:
|
||||
|
||||
```
|
||||
[14:00:00] Starting deployment of v1.2.3 to production
|
||||
[14:00:05] ✓ Pre-flight checks passed
|
||||
[14:00:10] ✓ Database backup completed
|
||||
[14:00:30] ✓ Database migrations applied (3 migrations)
|
||||
[14:01:00] → Creating green environment
|
||||
[14:05:00] ✓ Green environment healthy
|
||||
[14:05:30] ✓ Smoke tests passed (12/12)
|
||||
[14:06:00] → Switching traffic to green environment
|
||||
[14:11:00] ✓ DNS propagated
|
||||
[14:11:05] ✓ Production serving v1.2.3
|
||||
[14:11:10] → Monitoring for 15 minutes
|
||||
[14:26:10] ✓ Error rate: 0.05% (within threshold)
|
||||
[14:26:15] ✓ Deployment successful
|
||||
[14:26:20] → Old environment will be terminated in 24h
|
||||
|
||||
Deployment completed successfully in 26 minutes
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [AWS Deployment Guide](docs/AWS_DEPLOYMENT.md)
|
||||
- [Runbook](docs/RUNBOOK.md)
|
||||
- [On-Call Procedures](docs/ONCALL.md)
|
||||
```
|
||||
|
||||
### 改进要点
|
||||
|
||||
1. ✅ 具体命令: 每个步骤都有可执行的命令
|
||||
2. ✅ 环境明确: 专注于生产环境部署
|
||||
3. ✅ 验证标准: 说明检查什么和预期结果
|
||||
4. ✅ 错误处理: 包含完整的回滚流程
|
||||
5. ✅ 实际输出: 展示命令的预期输出
|
||||
6. ✅ 监控指标: 定义具体的成功标准
|
||||
|
||||
---
|
||||
|
||||
## 常见错误总结
|
||||
|
||||
### 1. 命名和描述问题
|
||||
|
||||
| 错误 | 示例 | 改进 |
|
||||
|------|------|------|
|
||||
| 过于泛化 | `name: helper` | `name: python-type-hints` |
|
||||
| 缺少关键词 | `description: Helps with code` | `description: Adds type hints to Python using mypy` |
|
||||
| 使用第一人称 | `description: I help you...` | `description: Adds type hints...` |
|
||||
|
||||
### 2. 内容问题
|
||||
|
||||
| 错误 | 说明 | 改进 |
|
||||
|------|------|------|
|
||||
| 包含通用知识 | 教 Python 基础语法 | 只包含项目特定规范 |
|
||||
| 过于抽象 | "分析代码并提供建议" | "检查函数长度、变量命名、重复代码" |
|
||||
| 缺少示例 | 只有文字描述 | 包含输入-输出示例 |
|
||||
|
||||
### 3. 结构问题
|
||||
|
||||
| 错误 | 说明 | 改进 |
|
||||
|------|------|------|
|
||||
| 无层次结构 | 所有内容混在一起 | 使用标题、列表、代码块组织 |
|
||||
| 缺少"When to Use" | 不知道何时激活 | 列出 3-5 个触发场景 |
|
||||
| 无验证步骤 | 不知道如何确认成功 | 说明检查项和预期结果 |
|
||||
|
||||
### 4. 自由度问题
|
||||
|
||||
| 错误 | 说明 | 改进 |
|
||||
|------|------|------|
|
||||
| 创意任务低自由度 | 为架构设计提供分步指令 | 提供指导原则和考虑因素 |
|
||||
| 危险任务高自由度 | 生产部署没有具体步骤 | 提供详细的检查清单 |
|
||||
| 不匹配任务类型 | 代码生成用教程式内容 | 提供模板和实际示例 |
|
||||
|
||||
---
|
||||
|
||||
## 快速检查清单
|
||||
|
||||
在发布 Skill 之前,问自己:
|
||||
|
||||
### 基础检查
|
||||
|
||||
- [ ] Name 是否具体且描述性强?
|
||||
- [ ] Description 包含触发关键词和场景?
|
||||
- [ ] 有明确的"When to Use"章节?
|
||||
- [ ] 内容只包含 Claude 不知道的信息?
|
||||
|
||||
### 内容检查
|
||||
|
||||
- [ ] 是否有实际的代码示例?
|
||||
- [ ] 步骤是否具体可执行?
|
||||
- [ ] 是否说明了如何验证成功?
|
||||
- [ ] 是否包含错误处理指导?
|
||||
|
||||
### 结构检查
|
||||
|
||||
- [ ] 内容组织清晰(使用标题、列表)?
|
||||
- [ ] 自由度设定合适(匹配任务类型)?
|
||||
- [ ] 长度合适(200-500行,或拆分子文件)?
|
||||
- [ ] 包含 Do/Don't 最佳实践?
|
||||
|
||||
如果有任何一项答"否",参考本文档的改进建议进行修改。
|
||||
|
|
@ -1,908 +0,0 @@
|
|||
# 好的 Skill 示例
|
||||
|
||||
本文档展示几个编写良好的 SKILL.md 示例,说明最佳实践的实际应用。
|
||||
|
||||
---
|
||||
|
||||
## 示例 1: 数据库迁移 Skill (高质量基础 Skill)
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: database-migration
|
||||
description: Manages database schema migrations using Alembic for SQLAlchemy projects. Activates when user asks to create migrations, upgrade/downgrade database, or mentions Alembic. Handles both development and production scenarios with safety checks.
|
||||
---
|
||||
|
||||
# Database Migration Skill
|
||||
|
||||
Automates database schema migration management using Alembic.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- User asks to "create migration", "update database schema", or "rollback migration"
|
||||
- User mentions "Alembic", "database migration", or "schema change"
|
||||
- User is working in a Python project with SQLAlchemy models
|
||||
- User needs to apply or revert database changes
|
||||
|
||||
## Quick Start
|
||||
|
||||
Create a new migration:
|
||||
```bash
|
||||
alembic revision --autogenerate -m "Description of changes"
|
||||
```
|
||||
|
||||
Apply migrations:
|
||||
```bash
|
||||
alembic upgrade head
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### Creating Migrations
|
||||
|
||||
1. **Detect model changes**
|
||||
- Scan SQLAlchemy models in `models/` directory
|
||||
- Compare with current database schema
|
||||
- Identify additions, modifications, deletions
|
||||
|
||||
2. **Generate migration script**
|
||||
- Run `alembic revision --autogenerate`
|
||||
- Review generated script for accuracy
|
||||
- Edit if necessary (Alembic can't auto-detect everything)
|
||||
|
||||
3. **Verify migration**
|
||||
- Check upgrade() function is correct
|
||||
- Ensure downgrade() function reverses changes
|
||||
- Test on development database first
|
||||
|
||||
### Applying Migrations
|
||||
|
||||
1. **Safety checks**
|
||||
- Backup database (production only)
|
||||
- Verify no pending migrations
|
||||
- Check database connectivity
|
||||
|
||||
2. **Execute migration**
|
||||
- Run `alembic upgrade head`
|
||||
- Monitor for errors
|
||||
- Verify schema matches expected state
|
||||
|
||||
3. **Post-migration validation**
|
||||
- Run application tests
|
||||
- Check data integrity
|
||||
- Confirm application starts successfully
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Add New Column
|
||||
|
||||
**User Request**: "Add an email column to the users table"
|
||||
|
||||
**Step 1**: Update the model
|
||||
```python
|
||||
# models/user.py
|
||||
class User(Base):
|
||||
__tablename__ = 'users'
|
||||
id = Column(Integer, primary_key=True)
|
||||
username = Column(String(50), nullable=False)
|
||||
email = Column(String(120), nullable=True) # ← New field
|
||||
```
|
||||
|
||||
**Step 2**: Generate migration
|
||||
```bash
|
||||
alembic revision --autogenerate -m "Add email column to users table"
|
||||
```
|
||||
|
||||
**Generated migration** (alembic/versions/abc123_add_email.py):
|
||||
```python
|
||||
def upgrade():
|
||||
op.add_column('users', sa.Column('email', sa.String(120), nullable=True))
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('users', 'email')
|
||||
```
|
||||
|
||||
**Step 3**: Review and apply
|
||||
```bash
|
||||
# Review the migration file
|
||||
cat alembic/versions/abc123_add_email.py
|
||||
|
||||
# Apply migration
|
||||
alembic upgrade head
|
||||
```
|
||||
|
||||
**Output**:
|
||||
```
|
||||
INFO [alembic.runtime.migration] Running upgrade xyz789 -> abc123, Add email column to users table
|
||||
```
|
||||
|
||||
### Example 2: Complex Migration with Data Changes
|
||||
|
||||
**User Request**: "Split the 'name' column into 'first_name' and 'last_name'"
|
||||
|
||||
**Step 1**: Create empty migration (can't auto-generate data changes)
|
||||
```bash
|
||||
alembic revision -m "Split name into first_name and last_name"
|
||||
```
|
||||
|
||||
**Step 2**: Write custom migration
|
||||
```python
|
||||
def upgrade():
|
||||
# Add new columns
|
||||
op.add_column('users', sa.Column('first_name', sa.String(50)))
|
||||
op.add_column('users', sa.Column('last_name', sa.String(50)))
|
||||
|
||||
# Migrate existing data
|
||||
connection = op.get_bind()
|
||||
users = connection.execute("SELECT id, name FROM users")
|
||||
for user_id, name in users:
|
||||
parts = name.split(' ', 1)
|
||||
first = parts[0]
|
||||
last = parts[1] if len(parts) > 1 else ''
|
||||
connection.execute(
|
||||
"UPDATE users SET first_name = %s, last_name = %s WHERE id = %s",
|
||||
(first, last, user_id)
|
||||
)
|
||||
|
||||
# Make new columns non-nullable and drop old column
|
||||
op.alter_column('users', 'first_name', nullable=False)
|
||||
op.alter_column('users', 'last_name', nullable=False)
|
||||
op.drop_column('users', 'name')
|
||||
|
||||
def downgrade():
|
||||
# Add back name column
|
||||
op.add_column('users', sa.Column('name', sa.String(100)))
|
||||
|
||||
# Restore data
|
||||
connection = op.get_bind()
|
||||
users = connection.execute("SELECT id, first_name, last_name FROM users")
|
||||
for user_id, first, last in users:
|
||||
full_name = f"{first} {last}".strip()
|
||||
connection.execute(
|
||||
"UPDATE users SET name = %s WHERE id = %s",
|
||||
(full_name, user_id)
|
||||
)
|
||||
|
||||
op.alter_column('users', 'name', nullable=False)
|
||||
op.drop_column('users', 'first_name')
|
||||
op.drop_column('users', 'last_name')
|
||||
```
|
||||
|
||||
**Step 3**: Test thoroughly
|
||||
```bash
|
||||
# Apply migration
|
||||
alembic upgrade head
|
||||
|
||||
# Verify data
|
||||
python -c "from models import User; print(User.query.first().first_name)"
|
||||
|
||||
# Test rollback
|
||||
alembic downgrade -1
|
||||
python -c "from models import User; print(User.query.first().name)"
|
||||
|
||||
# Reapply
|
||||
alembic upgrade head
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Do
|
||||
|
||||
- ✅ Always review auto-generated migrations before applying
|
||||
- ✅ Test migrations on development database first
|
||||
- ✅ Write reversible downgrade() functions
|
||||
- ✅ Backup production databases before major migrations
|
||||
- ✅ Use meaningful migration messages
|
||||
|
||||
### Don't
|
||||
|
||||
- ❌ Trust auto-generated migrations blindly
|
||||
- ❌ Skip downgrade() implementation
|
||||
- ❌ Apply untested migrations to production
|
||||
- ❌ Modify existing migration files after they're committed
|
||||
- ❌ Use raw SQL without bind parameters
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Target database is not up to date"
|
||||
|
||||
**Problem**: Someone else applied migrations you don't have locally
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
git pull # Get latest migrations
|
||||
alembic upgrade head # Apply them locally
|
||||
```
|
||||
|
||||
### "Can't locate revision identified by 'xyz'"
|
||||
|
||||
**Problem**: Migration file deleted or branch conflict
|
||||
|
||||
**Solution**:
|
||||
1. Check if migration file exists in `alembic/versions/`
|
||||
2. If missing, restore from git history
|
||||
3. If branch conflict, merge migration branches:
|
||||
```bash
|
||||
alembic merge -m "Merge migration branches" head1 head2
|
||||
```
|
||||
|
||||
### Migration fails mid-execution
|
||||
|
||||
**Problem**: Error occurred during migration
|
||||
|
||||
**Solution**:
|
||||
1. Check error message for specifics
|
||||
2. Manually fix database to consistent state if needed
|
||||
3. Update migration script to fix the issue
|
||||
4. Mark migration as completed or retry:
|
||||
```bash
|
||||
# Mark as done without running
|
||||
alembic stamp head
|
||||
|
||||
# Or fix and retry
|
||||
alembic upgrade head
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Project Structure
|
||||
```
|
||||
project/
|
||||
├── alembic/
|
||||
│ ├── versions/ # Migration scripts
|
||||
│ ├── env.py # Alembic environment
|
||||
│ └── script.py.mako # Migration template
|
||||
├── alembic.ini # Alembic configuration
|
||||
└── models/ # SQLAlchemy models
|
||||
├── __init__.py
|
||||
├── user.py
|
||||
└── post.py
|
||||
```
|
||||
|
||||
### alembic.ini Configuration
|
||||
```ini
|
||||
[alembic]
|
||||
script_location = alembic
|
||||
sqlalchemy.url = driver://user:pass@localhost/dbname
|
||||
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers = console
|
||||
qualname = alembic
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [Alembic Documentation](https://alembic.sqlalchemy.org/)
|
||||
- [SQLAlchemy Documentation](https://docs.sqlalchemy.org/)
|
||||
- [Project Migration Guidelines](docs/database-migrations.md)
|
||||
```
|
||||
|
||||
### 为什么这是好的 Skill?
|
||||
|
||||
1. ✅ **清晰的 description**: 包含触发关键词 ("Alembic", "create migrations") 和场景 ("SQLAlchemy projects")
|
||||
2. ✅ **具体的触发条件**: "When to Use" 列出 4 个明确场景
|
||||
3. ✅ **分步工作流**: 每个操作都有清晰的 1-2-3 步骤
|
||||
4. ✅ **实际示例**: 包含简单和复杂两个示例,有完整代码
|
||||
5. ✅ **最佳实践**: Do/Don't 清单易于遵循
|
||||
6. ✅ **故障排除**: 覆盖 3 个常见问题及解决方案
|
||||
7. ✅ **项目特定信息**: 包含配置和目录结构
|
||||
|
||||
---
|
||||
|
||||
## 示例 2: API 文档生成 Skill (优秀的工作流 Skill)
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: api-documentation-generation
|
||||
description: Generates OpenAPI/Swagger documentation from FastAPI or Flask applications. Activates when user asks to create API docs, generate OpenAPI spec, or needs to document REST endpoints. Supports automatic extraction and custom annotations.
|
||||
---
|
||||
|
||||
# API Documentation Generation Skill
|
||||
|
||||
Automates creation of comprehensive API documentation from Python web applications.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- User asks to "generate API docs" or "create OpenAPI spec"
|
||||
- User mentions "Swagger", "OpenAPI", "API documentation"
|
||||
- User has a FastAPI or Flask application
|
||||
- User needs to document REST API endpoints
|
||||
|
||||
## Workflow
|
||||
|
||||
### Phase 1: Discovery
|
||||
|
||||
1. **Identify framework**
|
||||
- Check for FastAPI: `from fastapi import FastAPI` in codebase
|
||||
- Check for Flask: `from flask import Flask` in codebase
|
||||
- Check for Flask-RESTful: `from flask_restful import Resource`
|
||||
|
||||
2. **Locate API definitions**
|
||||
- Scan for route decorators: `@app.get()`, `@app.post()`, `@app.route()`
|
||||
- Find API routers and blueprints
|
||||
- Identify request/response models
|
||||
|
||||
3. **Extract metadata**
|
||||
- Endpoint paths and HTTP methods
|
||||
- Request parameters (path, query, body)
|
||||
- Response schemas and status codes
|
||||
- Authentication requirements
|
||||
|
||||
### Phase 2: Enhancement
|
||||
|
||||
1. **Review docstrings**
|
||||
- Check if endpoints have docstrings
|
||||
- Verify docstrings follow format (summary, description, params, returns)
|
||||
- Flag missing documentation
|
||||
|
||||
2. **Add missing docs** (if user approves)
|
||||
- Generate docstrings based on type hints
|
||||
- Infer descriptions from parameter names
|
||||
- Add example requests/responses
|
||||
|
||||
3. **Validate schemas**
|
||||
- Ensure Pydantic models are well-documented
|
||||
- Check for missing field descriptions
|
||||
- Verify example values are provided
|
||||
|
||||
### Phase 3: Generation
|
||||
|
||||
1. **Generate OpenAPI spec**
|
||||
```bash
|
||||
# For FastAPI
|
||||
python -c "from main import app; import json; print(json.dumps(app.openapi()))" > openapi.json
|
||||
|
||||
# For Flask with flasgger
|
||||
python scripts/generate_swagger.py > swagger.json
|
||||
```
|
||||
|
||||
2. **Create Swagger UI**
|
||||
- Copy Swagger UI static files to `docs/api/`
|
||||
- Configure to load generated spec
|
||||
- Test in browser: `http://localhost:8000/docs`
|
||||
|
||||
3. **Generate Markdown docs**
|
||||
- Use `openapi-to-md` to create human-readable docs
|
||||
- Organize by tags/resource groups
|
||||
- Add navigation and table of contents
|
||||
|
||||
### Phase 4: Validation
|
||||
|
||||
1. **Check completeness**
|
||||
- All endpoints documented?
|
||||
- All parameters described?
|
||||
- Example requests provided?
|
||||
|
||||
2. **Validate spec**
|
||||
```bash
|
||||
openapi-spec-validator openapi.json
|
||||
```
|
||||
|
||||
3. **Test interactive docs**
|
||||
- Try sample requests in Swagger UI
|
||||
- Verify authentication flows work
|
||||
- Check response schemas match actual responses
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: FastAPI Application
|
||||
|
||||
**User Request**: "Generate API documentation for my FastAPI app"
|
||||
|
||||
**Discovery**:
|
||||
```
|
||||
Found FastAPI application in: main.py
|
||||
Endpoints discovered:
|
||||
- GET /users (List users)
|
||||
- POST /users (Create user)
|
||||
- GET /users/{id} (Get user by ID)
|
||||
- PUT /users/{id} (Update user)
|
||||
- DELETE /users/{id} (Delete user)
|
||||
```
|
||||
|
||||
**Enhancement**:
|
||||
```
|
||||
Checking documentation coverage...
|
||||
✓ GET /users - Well documented
|
||||
✗ POST /users - Missing response examples
|
||||
✗ GET /users/{id} - No description
|
||||
✗ PUT /users/{id} - No description
|
||||
✗ DELETE /users/{id} - No description
|
||||
|
||||
Would you like me to add missing documentation?
|
||||
```
|
||||
|
||||
**User**: Yes
|
||||
|
||||
**Adding docs**:
|
||||
```python
|
||||
@app.get("/users/{id}", summary="Get user by ID")
|
||||
async def get_user(id: int):
|
||||
"""
|
||||
Retrieve a single user by their unique ID.
|
||||
|
||||
Args:
|
||||
id: The unique identifier of the user
|
||||
|
||||
Returns:
|
||||
User object with id, name, email, and created_at fields
|
||||
|
||||
Raises:
|
||||
404: User not found
|
||||
"""
|
||||
# ... implementation
|
||||
```
|
||||
|
||||
**Generation**:
|
||||
```bash
|
||||
Generating OpenAPI specification...
|
||||
✓ openapi.json created (15.2 KB)
|
||||
✓ Swagger UI deployed to docs/api/
|
||||
✓ Markdown docs created in docs/api/endpoints/
|
||||
|
||||
View documentation at: http://localhost:8000/docs
|
||||
```
|
||||
|
||||
### Example 2: Flask Application with Custom Schemas
|
||||
|
||||
**User Request**: "Document my Flask API and include request/response examples"
|
||||
|
||||
**Discovery**:
|
||||
```
|
||||
Found Flask application with Flask-RESTful
|
||||
Endpoints:
|
||||
- GET /api/products
|
||||
- POST /api/products
|
||||
- GET /api/products/<id>
|
||||
- PATCH /api/products/<id>
|
||||
- DELETE /api/products/<id>
|
||||
```
|
||||
|
||||
**Custom Examples Added**:
|
||||
```python
|
||||
class ProductList(Resource):
|
||||
def get(self):
|
||||
"""
|
||||
Get all products
|
||||
|
||||
Example Response:
|
||||
```json
|
||||
{
|
||||
"products": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Widget",
|
||||
"price": 29.99,
|
||||
"stock": 100
|
||||
}
|
||||
],
|
||||
"total": 1
|
||||
}
|
||||
```
|
||||
"""
|
||||
pass
|
||||
|
||||
def post(self):
|
||||
"""
|
||||
Create a new product
|
||||
|
||||
Example Request:
|
||||
```json
|
||||
{
|
||||
"name": "New Widget",
|
||||
"price": 39.99,
|
||||
"stock": 50
|
||||
}
|
||||
```
|
||||
|
||||
Example Response:
|
||||
```json
|
||||
{
|
||||
"id": 2,
|
||||
"name": "New Widget",
|
||||
"price": 39.99,
|
||||
"stock": 50,
|
||||
"created_at": "2025-01-31T12:00:00Z"
|
||||
}
|
||||
```
|
||||
"""
|
||||
pass
|
||||
```
|
||||
|
||||
**Result**:
|
||||
```
|
||||
Generated documentation:
|
||||
- openapi.json (with examples)
|
||||
- Swagger UI at /api/docs
|
||||
- Postman collection at docs/api/postman_collection.json
|
||||
- Markdown API reference at docs/api/README.md
|
||||
|
||||
All endpoints now include:
|
||||
✓ Request examples
|
||||
✓ Response examples
|
||||
✓ Error codes
|
||||
✓ Authentication requirements
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### FastAPI Projects
|
||||
|
||||
No additional configuration needed! FastAPI auto-generates OpenAPI docs.
|
||||
|
||||
Access at:
|
||||
- Swagger UI: `http://localhost:8000/docs`
|
||||
- ReDoc: `http://localhost:8000/redoc`
|
||||
- OpenAPI JSON: `http://localhost:8000/openapi.json`
|
||||
|
||||
### Flask Projects
|
||||
|
||||
Install flasgger:
|
||||
```bash
|
||||
pip install flasgger
|
||||
```
|
||||
|
||||
Configure in app:
|
||||
```python
|
||||
from flasgger import Swagger
|
||||
|
||||
app = Flask(__name__)
|
||||
swagger = Swagger(app, template={
|
||||
"info": {
|
||||
"title": "My API",
|
||||
"description": "API for managing resources",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- ✅ Use type hints - enables automatic schema generation
|
||||
- ✅ Write descriptive docstrings for all endpoints
|
||||
- ✅ Provide example requests and responses
|
||||
- ✅ Document error codes and edge cases
|
||||
- ✅ Keep docs in sync with code (auto-generate when possible)
|
||||
|
||||
## Tools Used
|
||||
|
||||
- **FastAPI**: Built-in OpenAPI support
|
||||
- **flasgger**: Swagger for Flask
|
||||
- **openapi-spec-validator**: Validates OpenAPI specs
|
||||
- **openapi-to-md**: Converts OpenAPI to Markdown
|
||||
|
||||
## References
|
||||
|
||||
- [OpenAPI Specification](https://spec.openapis.org/oas/latest.html)
|
||||
- [FastAPI Documentation](https://fastapi.tiangolo.com/)
|
||||
- [Swagger Documentation](https://swagger.io/docs/)
|
||||
```
|
||||
|
||||
### 为什么这是优秀的工作流 Skill?
|
||||
|
||||
1. ✅ **清晰的工作流阶段**: 4 个阶段 (Discovery, Enhancement, Generation, Validation)
|
||||
2. ✅ **决策点**: Phase 2 询问用户是否添加缺失文档
|
||||
3. ✅ **实际输出示例**: 展示了命令输出和生成的代码
|
||||
4. ✅ **多框架支持**: 处理 FastAPI 和 Flask 两种情况
|
||||
5. ✅ **工具集成**: 列出所需工具及其用途
|
||||
6. ✅ **可执行命令**: 提供完整的命令示例
|
||||
7. ✅ **验证步骤**: Phase 4 确保生成的文档质量
|
||||
|
||||
---
|
||||
|
||||
## 示例 3: 代码审查 Skill (高自由度 Skill)
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: code-review
|
||||
description: Performs comprehensive code reviews focusing on best practices, security, performance, and maintainability. Activates when user asks to review code, check pull request, or mentions code quality. Provides actionable feedback with severity ratings.
|
||||
---
|
||||
|
||||
# Code Review Skill
|
||||
|
||||
Conducts thorough code reviews with focus on quality, security, and best practices.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- User asks to "review my code" or "check this PR"
|
||||
- User mentions "code review", "code quality", or "best practices"
|
||||
- User wants feedback on specific code changes
|
||||
- User needs security or performance analysis
|
||||
|
||||
## Review Criteria
|
||||
|
||||
Code is evaluated across 5 dimensions:
|
||||
|
||||
### 1. Correctness
|
||||
- Logic errors and bugs
|
||||
- Edge case handling
|
||||
- Error handling and validation
|
||||
- Type safety
|
||||
|
||||
### 2. Security
|
||||
- SQL injection vulnerabilities
|
||||
- XSS vulnerabilities
|
||||
- Authentication/authorization issues
|
||||
- Sensitive data exposure
|
||||
- Dependency vulnerabilities
|
||||
|
||||
### 3. Performance
|
||||
- Algorithm efficiency
|
||||
- Database query optimization
|
||||
- Memory leaks
|
||||
- Unnecessary computations
|
||||
- Caching opportunities
|
||||
|
||||
### 4. Maintainability
|
||||
- Code clarity and readability
|
||||
- Function/class size
|
||||
- Code duplication
|
||||
- Naming conventions
|
||||
- Documentation
|
||||
|
||||
### 5. Best Practices
|
||||
- Language-specific idioms
|
||||
- Design patterns
|
||||
- SOLID principles
|
||||
- Testing coverage
|
||||
- Error handling patterns
|
||||
|
||||
## Review Process
|
||||
|
||||
1. **Understand context**
|
||||
- What does this code do?
|
||||
- What problem does it solve?
|
||||
- Are there any constraints or requirements?
|
||||
|
||||
2. **Identify issues**
|
||||
- Scan for common anti-patterns
|
||||
- Check against language best practices
|
||||
- Look for security vulnerabilities
|
||||
- Assess performance implications
|
||||
|
||||
3. **Prioritize feedback**
|
||||
- **Critical**: Security issues, data loss risks, crashes
|
||||
- **High**: Bugs, major performance issues
|
||||
- **Medium**: Code smells, maintainability concerns
|
||||
- **Low**: Style preferences, minor optimizations
|
||||
|
||||
4. **Provide suggestions**
|
||||
- Explain the issue clearly
|
||||
- Show better alternative (code example)
|
||||
- Explain why the alternative is better
|
||||
|
||||
## Example Review
|
||||
|
||||
### Code Submitted
|
||||
|
||||
```python
|
||||
def get_user_data(user_id):
|
||||
conn = sqlite3.connect('users.db')
|
||||
cursor = conn.cursor()
|
||||
query = "SELECT * FROM users WHERE id = " + str(user_id)
|
||||
cursor.execute(query)
|
||||
result = cursor.fetchone()
|
||||
return result
|
||||
```
|
||||
|
||||
### Review Feedback
|
||||
|
||||
**❌ CRITICAL: SQL Injection Vulnerability**
|
||||
|
||||
The code concatenates user input directly into SQL query, allowing SQL injection attacks.
|
||||
|
||||
**Bad**:
|
||||
```python
|
||||
query = "SELECT * FROM users WHERE id = " + str(user_id)
|
||||
```
|
||||
|
||||
**Good**:
|
||||
```python
|
||||
query = "SELECT * FROM users WHERE id = ?"
|
||||
cursor.execute(query, (user_id,))
|
||||
```
|
||||
|
||||
**Why**: Parameterized queries prevent SQL injection by properly escaping user input.
|
||||
|
||||
---
|
||||
|
||||
**⚠️ HIGH: Resource Leak**
|
||||
|
||||
Database connection is never closed, leading to resource exhaustion under load.
|
||||
|
||||
**Bad**:
|
||||
```python
|
||||
conn = sqlite3.connect('users.db')
|
||||
# ... use connection
|
||||
# connection never closed
|
||||
```
|
||||
|
||||
**Good**:
|
||||
```python
|
||||
with sqlite3.connect('users.db') as conn:
|
||||
cursor = conn.cursor()
|
||||
# ... use connection
|
||||
# automatically closed when exiting with block
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**ℹ️ MEDIUM: Overly Broad SELECT**
|
||||
|
||||
Using `SELECT *` fetches unnecessary data and is fragile to schema changes.
|
||||
|
||||
**Bad**:
|
||||
```python
|
||||
query = "SELECT * FROM users WHERE id = ?"
|
||||
```
|
||||
|
||||
**Good**:
|
||||
```python
|
||||
query = "SELECT id, name, email FROM users WHERE id = ?"
|
||||
```
|
||||
|
||||
**Why**: Explicit column selection is more efficient and makes intent clear.
|
||||
|
||||
---
|
||||
|
||||
**📝 LOW: Missing Error Handling**
|
||||
|
||||
Function doesn't handle case where user doesn't exist.
|
||||
|
||||
**Suggestion**:
|
||||
```python
|
||||
def get_user_data(user_id: int) -> Optional[dict]:
|
||||
"""
|
||||
Retrieve user data by ID.
|
||||
|
||||
Args:
|
||||
user_id: The unique identifier of the user
|
||||
|
||||
Returns:
|
||||
Dictionary with user data, or None if user not found
|
||||
"""
|
||||
with sqlite3.connect('users.db') as conn:
|
||||
cursor = conn.cursor()
|
||||
query = "SELECT id, name, email FROM users WHERE id = ?"
|
||||
cursor.execute(query, (user_id,))
|
||||
result = cursor.fetchone()
|
||||
|
||||
if result is None:
|
||||
return None
|
||||
|
||||
return {
|
||||
'id': result[0],
|
||||
'name': result[1],
|
||||
'email': result[2]
|
||||
}
|
||||
```
|
||||
|
||||
### Revised Code
|
||||
|
||||
After applying all suggestions:
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
import sqlite3
|
||||
|
||||
def get_user_data(user_id: int) -> Optional[dict]:
|
||||
"""
|
||||
Retrieve user data by ID.
|
||||
|
||||
Args:
|
||||
user_id: The unique identifier of the user
|
||||
|
||||
Returns:
|
||||
Dictionary with user data, or None if user not found
|
||||
|
||||
Raises:
|
||||
ValueError: If user_id is not a positive integer
|
||||
"""
|
||||
if not isinstance(user_id, int) or user_id <= 0:
|
||||
raise ValueError(f"Invalid user_id: {user_id}")
|
||||
|
||||
with sqlite3.connect('users.db') as conn:
|
||||
conn.row_factory = sqlite3.Row # Enable dict-like access
|
||||
cursor = conn.cursor()
|
||||
|
||||
query = "SELECT id, name, email, created_at FROM users WHERE id = ?"
|
||||
cursor.execute(query, (user_id,))
|
||||
result = cursor.fetchone()
|
||||
|
||||
if result is None:
|
||||
return None
|
||||
|
||||
return dict(result) # Convert Row to dict
|
||||
```
|
||||
|
||||
### Summary
|
||||
|
||||
**Issues Found**: 4
|
||||
- 1 Critical (SQL Injection)
|
||||
- 1 High (Resource Leak)
|
||||
- 1 Medium (Inefficient Query)
|
||||
- 1 Low (Missing Error Handling)
|
||||
|
||||
**All Issues Addressed**: ✓
|
||||
|
||||
## Best Practices
|
||||
|
||||
### When Reviewing
|
||||
|
||||
- 🎯 Focus on impact - prioritize critical issues
|
||||
- 📝 Be specific - provide code examples
|
||||
- 🎓 Be educational - explain why, not just what
|
||||
- 🤝 Be constructive - suggest improvements, don't just criticize
|
||||
- ⚖️ Be balanced - acknowledge good practices too
|
||||
|
||||
### What to Look For
|
||||
|
||||
**Python-specific**:
|
||||
- Use of `with` for resource management
|
||||
- Type hints on function signatures
|
||||
- Proper exception handling
|
||||
- List comprehensions vs loops
|
||||
- Dictionary vs if-elif chains
|
||||
|
||||
**General**:
|
||||
- DRY principle violations
|
||||
- Magic numbers
|
||||
- Long functions (>50 lines)
|
||||
- Deep nesting (>3 levels)
|
||||
- Missing tests for critical paths
|
||||
|
||||
## Automated Tools
|
||||
|
||||
Complement manual review with automated tools:
|
||||
|
||||
```bash
|
||||
# Linting
|
||||
pylint mycode.py
|
||||
flake8 mycode.py
|
||||
|
||||
# Type checking
|
||||
mypy mycode.py
|
||||
|
||||
# Security scanning
|
||||
bandit -r .
|
||||
safety check
|
||||
|
||||
# Code complexity
|
||||
radon cc mycode.py -a
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
|
||||
- [Python Best Practices](https://docs.python-guide.org/)
|
||||
- [Clean Code Principles](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882)
|
||||
```
|
||||
|
||||
### 为什么这是高自由度 Skill?
|
||||
|
||||
1. ✅ **指导原则而非严格步骤**: 提供评审维度,不限定具体流程
|
||||
2. ✅ **情境适应**: 根据代码类型和问题严重性调整重点
|
||||
3. ✅ **教育性**: 解释"为什么",帮助 Claude 做出判断
|
||||
4. ✅ **优先级框架**: 定义严重性级别,让 Claude 自行判断
|
||||
5. ✅ **完整示例**: 展示从问题识别到解决的完整流程
|
||||
6. ✅ **工具集成**: 提到自动化工具,但不强制使用
|
||||
|
||||
---
|
||||
|
||||
## 总结: 好 Skill 的共同特征
|
||||
|
||||
| 特征 | 说明 | 示例位置 |
|
||||
|------|------|---------|
|
||||
| **清晰触发** | description 包含关键词和场景 | 所有 frontmatter |
|
||||
| **结构化内容** | 使用标题、列表、代码块组织信息 | 所有示例 |
|
||||
| **实际示例** | 真实代码,不是伪代码 | Example sections |
|
||||
| **决策指导** | 告诉 Claude 何时做什么 | 工作流 Skill 的 Phase 2 |
|
||||
| **可执行命令** | 提供完整的命令,不是抽象描述 | 迁移 Skill 的命令 |
|
||||
| **错误处理** | 包含故障排除章节 | 所有 Troubleshooting |
|
||||
| **最佳实践** | Do/Don't 清单 | 所有 Best Practices |
|
||||
| **工具引用** | 说明使用哪些工具及如何使用 | API 文档 Skill |
|
||||
| **验证步骤** | 说明如何确认操作成功 | 迁移 Skill 的验证 |
|
||||
| **合适的自由度** | 根据任务特性选择指导程度 | 代码审查 Skill |
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
---
|
||||
name: your-skill-name
|
||||
description: Brief description of what this skill does and when to activate it. Include trigger keywords and scenarios where this skill should be used.
|
||||
---
|
||||
|
||||
# Your Skill Title
|
||||
|
||||
> Brief one-line summary of what this skill accomplishes
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- User asks to [specific action or task]
|
||||
- User mentions keywords like "[keyword1]", "[keyword2]", or "[keyword3]"
|
||||
- User is working with [specific technology/framework/tool]
|
||||
- User needs to [specific outcome or goal]
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Basic usage example
|
||||
command-to-run --option value
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **Step 1**: Brief description of first step
|
||||
- Detail about what happens
|
||||
- Any prerequisites or conditions
|
||||
|
||||
2. **Step 2**: Brief description of second step
|
||||
- Key actions taken
|
||||
- Expected outputs
|
||||
|
||||
3. **Step 3**: Brief description of final step
|
||||
- Validation or verification
|
||||
- Success criteria
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Basic Usage
|
||||
|
||||
**User Request**: "Example of what user might say"
|
||||
|
||||
**Action**: What Claude does in response
|
||||
|
||||
**Output**:
|
||||
```
|
||||
Expected output or result
|
||||
```
|
||||
|
||||
### Example 2: Advanced Usage
|
||||
|
||||
**User Request**: "More complex user request"
|
||||
|
||||
**Action**:
|
||||
1. First action taken
|
||||
2. Second action taken
|
||||
3. Final action
|
||||
|
||||
**Output**:
|
||||
```
|
||||
Expected output showing more complex results
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- ✅ Do this for best results
|
||||
- ✅ Follow this pattern
|
||||
- ❌ Avoid this common mistake
|
||||
- ❌ Don't do this
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issue 1
|
||||
|
||||
**Problem**: Description of the problem
|
||||
|
||||
**Solution**: How to fix it
|
||||
|
||||
### Common Issue 2
|
||||
|
||||
**Problem**: Description of another problem
|
||||
|
||||
**Solution**: Steps to resolve
|
||||
|
||||
## References
|
||||
|
||||
- [Related Documentation](link-to-docs)
|
||||
- [Official Guide](link-to-guide)
|
||||
- [Additional Resources](link-to-resources)
|
||||
|
||||
---
|
||||
|
||||
**Version**: 1.0
|
||||
**Last Updated**: YYYY-MM-DD
|
||||
|
|
@ -1,402 +0,0 @@
|
|||
---
|
||||
name: your-workflow-skill
|
||||
description: Guides Claude through a multi-step workflow for [specific task]. Activates when user needs to [trigger scenario] or mentions [key terms].
|
||||
---
|
||||
|
||||
# Your Workflow Skill Title
|
||||
|
||||
> Automates a complex multi-step process with decision points and validation
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- User needs to execute a multi-step workflow
|
||||
- User asks to "[workflow trigger phrase]"
|
||||
- User is working on [specific type of project or task]
|
||||
- Task requires validation and error handling at each step
|
||||
|
||||
## Workflow Overview
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ Start │
|
||||
└──────┬──────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Preparation │
|
||||
│ & Validation │
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌────▼────┐
|
||||
│ Step 1 │
|
||||
└────┬────┘
|
||||
│
|
||||
┌────▼────┐
|
||||
│ Step 2 │──┐
|
||||
└────┬────┘ │ (Loop if needed)
|
||||
│ │
|
||||
└───────┘
|
||||
│
|
||||
┌────▼────┐
|
||||
│ Step 3 │
|
||||
└────┬────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ Complete │
|
||||
│ & Report │
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
## Detailed Workflow
|
||||
|
||||
### Preparation Phase
|
||||
|
||||
Before starting the main workflow:
|
||||
|
||||
- [ ] Check prerequisite 1
|
||||
- [ ] Validate prerequisite 2
|
||||
- [ ] Ensure prerequisite 3 is met
|
||||
|
||||
If any prerequisite fails:
|
||||
- Stop execution
|
||||
- Report which prerequisite failed
|
||||
- Provide remediation steps
|
||||
|
||||
### Step 1: [Step Name]
|
||||
|
||||
**Purpose**: What this step accomplishes
|
||||
|
||||
**Actions**:
|
||||
1. Action 1
|
||||
2. Action 2
|
||||
3. Action 3
|
||||
|
||||
**Validation**:
|
||||
- Check condition 1
|
||||
- Verify condition 2
|
||||
|
||||
**On Success**: → Proceed to Step 2
|
||||
**On Failure**: → [Error handling procedure]
|
||||
|
||||
### Step 2: [Step Name]
|
||||
|
||||
**Purpose**: What this step accomplishes
|
||||
|
||||
**Actions**:
|
||||
1. Action 1
|
||||
2. Action 2
|
||||
|
||||
**Decision Point**:
|
||||
- If condition A: → Action X
|
||||
- If condition B: → Action Y
|
||||
- Otherwise: → Default action
|
||||
|
||||
**Validation**:
|
||||
- Verify expected output
|
||||
- Check for errors
|
||||
|
||||
**On Success**: → Proceed to Step 3
|
||||
**On Failure**: → [Error handling procedure]
|
||||
|
||||
### Step 3: [Step Name]
|
||||
|
||||
**Purpose**: Final actions and cleanup
|
||||
|
||||
**Actions**:
|
||||
1. Finalize changes
|
||||
2. Run validation tests
|
||||
3. Generate summary report
|
||||
|
||||
**Success Criteria**:
|
||||
- All tests pass
|
||||
- No errors in logs
|
||||
- Expected artifacts created
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Standard Workflow Execution
|
||||
|
||||
**User Request**: "Run the [workflow name]"
|
||||
|
||||
**Execution**:
|
||||
|
||||
**Preparation Phase** ✓
|
||||
```
|
||||
✓ Prerequisite 1 met
|
||||
✓ Prerequisite 2 validated
|
||||
✓ Ready to begin
|
||||
```
|
||||
|
||||
**Step 1: [Step Name]** ✓
|
||||
```
|
||||
→ Action 1 completed
|
||||
→ Action 2 completed
|
||||
→ Validation passed
|
||||
```
|
||||
|
||||
**Step 2: [Step Name]** ✓
|
||||
```
|
||||
→ Decision: Condition A detected
|
||||
→ Executing Action X
|
||||
→ Validation passed
|
||||
```
|
||||
|
||||
**Step 3: [Step Name]** ✓
|
||||
```
|
||||
→ Finalization complete
|
||||
→ All tests passed
|
||||
→ Summary generated
|
||||
```
|
||||
|
||||
**Result**: Workflow completed successfully
|
||||
|
||||
### Example 2: Workflow with Error Recovery
|
||||
|
||||
**User Request**: "Execute [workflow name]"
|
||||
|
||||
**Execution**:
|
||||
|
||||
**Step 1** ✓
|
||||
```
|
||||
→ Completed successfully
|
||||
```
|
||||
|
||||
**Step 2** ⚠️
|
||||
```
|
||||
→ Action 1 completed
|
||||
→ Action 2 failed: [Error message]
|
||||
```
|
||||
|
||||
**Error Recovery**:
|
||||
1. Identified root cause: [Explanation]
|
||||
2. Applied fix: [Fix description]
|
||||
3. Retrying Step 2...
|
||||
|
||||
**Step 2 (Retry)** ✓
|
||||
```
|
||||
→ Completed after fix
|
||||
```
|
||||
|
||||
**Step 3** ✓
|
||||
```
|
||||
→ Completed successfully
|
||||
```
|
||||
|
||||
**Result**: Workflow completed with 1 retry
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Error Categories
|
||||
|
||||
| Category | Action |
|
||||
|----------|--------|
|
||||
| **Recoverable** | Attempt automatic fix, retry up to 3 times |
|
||||
| **User Input Needed** | Pause workflow, ask user for guidance |
|
||||
| **Critical** | Stop workflow, rollback changes if possible |
|
||||
|
||||
### Common Errors
|
||||
|
||||
**Error 1: [Error Name]**
|
||||
- **Cause**: What causes this error
|
||||
- **Detection**: How to identify it
|
||||
- **Recovery**: Steps to fix
|
||||
1. Recovery action 1
|
||||
2. Recovery action 2
|
||||
3. Retry from failed step
|
||||
|
||||
**Error 2: [Error Name]**
|
||||
- **Cause**: What causes this error
|
||||
- **Detection**: How to identify it
|
||||
- **Recovery**: Manual intervention required
|
||||
- Ask user: "[Question to ask]"
|
||||
- Wait for user input
|
||||
- Apply user's guidance
|
||||
- Resume workflow
|
||||
|
||||
## Rollback Procedure
|
||||
|
||||
If the workflow fails critically:
|
||||
|
||||
1. **Identify last successful step**
|
||||
- Step 1: ✓ Completed
|
||||
- Step 2: ❌ Failed at action 3
|
||||
|
||||
2. **Undo changes from failed step**
|
||||
- Revert action 1
|
||||
- Revert action 2
|
||||
- Clean up partial state
|
||||
|
||||
3. **Verify system state**
|
||||
- Confirm rollback successful
|
||||
- Check for side effects
|
||||
|
||||
4. **Report to user**
|
||||
```
|
||||
Workflow failed at Step 2, action 3
|
||||
Reason: [Error message]
|
||||
All changes have been rolled back
|
||||
System is back to pre-workflow state
|
||||
```
|
||||
|
||||
## Workflow Variations
|
||||
|
||||
### Variation 1: Quick Mode
|
||||
|
||||
**When to use**: User needs faster execution, can accept lower validation
|
||||
|
||||
**Changes**:
|
||||
- Skip optional validations
|
||||
- Use cached data where available
|
||||
- Reduce logging verbosity
|
||||
|
||||
**Trade-offs**:
|
||||
- ⚡ 50% faster
|
||||
- ⚠️ Less detailed error messages
|
||||
|
||||
### Variation 2: Strict Mode
|
||||
|
||||
**When to use**: Production deployments, critical changes
|
||||
|
||||
**Changes**:
|
||||
- Enable all validations
|
||||
- Require explicit user confirmation at each step
|
||||
- Generate detailed audit logs
|
||||
|
||||
**Trade-offs**:
|
||||
- 🛡️ Maximum safety
|
||||
- 🐢 Slower execution
|
||||
|
||||
## Monitoring and Logging
|
||||
|
||||
Throughout the workflow:
|
||||
|
||||
```
|
||||
[TIMESTAMP] [STEP] [STATUS] Message
|
||||
|
||||
[2025-01-31 14:30:01] [PREP] [INFO] Starting preparation phase
|
||||
[2025-01-31 14:30:02] [PREP] [OK] All prerequisites met
|
||||
[2025-01-31 14:30:03] [STEP1] [INFO] Beginning Step 1
|
||||
[2025-01-31 14:30:05] [STEP1] [OK] Step 1 completed successfully
|
||||
[2025-01-31 14:30:06] [STEP2] [INFO] Beginning Step 2
|
||||
[2025-01-31 14:30:08] [STEP2] [WARN] Condition B detected, using fallback
|
||||
[2025-01-31 14:30:10] [STEP2] [OK] Step 2 completed with warnings
|
||||
[2025-01-31 14:30:11] [STEP3] [INFO] Beginning Step 3
|
||||
[2025-01-31 14:30:15] [STEP3] [OK] Step 3 completed successfully
|
||||
[2025-01-31 14:30:16] [COMPLETE] [OK] Workflow finished successfully
|
||||
```
|
||||
|
||||
## Post-Workflow Report
|
||||
|
||||
After completion, generate a summary:
|
||||
|
||||
```markdown
|
||||
# Workflow Execution Report
|
||||
|
||||
**Workflow**: [Workflow Name]
|
||||
**Started**: 2025-01-31 14:30:01
|
||||
**Completed**: 2025-01-31 14:30:16
|
||||
**Duration**: 15 seconds
|
||||
**Status**: ✓ Success
|
||||
|
||||
## Steps Executed
|
||||
|
||||
1. ✓ Preparation Phase (1s)
|
||||
2. ✓ Step 1: [Step Name] (2s)
|
||||
3. ✓ Step 2: [Step Name] (4s) - 1 warning
|
||||
4. ✓ Step 3: [Step Name] (4s)
|
||||
|
||||
## Warnings
|
||||
|
||||
- Step 2: Condition B detected, used fallback action
|
||||
|
||||
## Artifacts Generated
|
||||
|
||||
- `/path/to/output1.txt`
|
||||
- `/path/to/output2.json`
|
||||
- `/path/to/report.html`
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Review generated artifacts
|
||||
- Deploy to production (if applicable)
|
||||
- Archive logs to `/logs/workflow-20250131-143001.log`
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Do
|
||||
|
||||
- ✅ Validate inputs before starting workflow
|
||||
- ✅ Provide clear progress updates at each step
|
||||
- ✅ Log all decisions and actions
|
||||
- ✅ Handle errors gracefully with recovery options
|
||||
- ✅ Generate summary report at completion
|
||||
|
||||
### Don't
|
||||
|
||||
- ❌ Skip validation steps to save time
|
||||
- ❌ Continue after critical errors
|
||||
- ❌ Assume prerequisites are met without checking
|
||||
- ❌ Lose partial progress on failure
|
||||
- ❌ Leave system in inconsistent state
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Parallel Execution
|
||||
|
||||
Some steps can run in parallel:
|
||||
|
||||
```
|
||||
Step 1 ─┬─→ Step 2A ─┐
|
||||
│ ├─→ Step 3
|
||||
└─→ Step 2B ─┘
|
||||
```
|
||||
|
||||
**Requirements**:
|
||||
- Steps 2A and 2B must be independent
|
||||
- Both must complete before Step 3
|
||||
|
||||
**Implementation**:
|
||||
1. Start Step 2A in background
|
||||
2. Start Step 2B in background
|
||||
3. Wait for both to complete
|
||||
4. Verify both succeeded
|
||||
5. Proceed to Step 3
|
||||
|
||||
### Conditional Branching
|
||||
|
||||
```
|
||||
Step 1 → Decision
|
||||
├─→ [Condition A] → Path A → Step 3
|
||||
├─→ [Condition B] → Path B → Step 3
|
||||
└─→ [Default] → Path C → Step 3
|
||||
```
|
||||
|
||||
## Testing This Workflow
|
||||
|
||||
To test the workflow without side effects:
|
||||
|
||||
1. Use `--dry-run` flag to simulate execution
|
||||
2. Check that all steps are logged correctly
|
||||
3. Verify error handling with intentional failures
|
||||
4. Confirm rollback procedure works
|
||||
|
||||
Example:
|
||||
```bash
|
||||
workflow-runner --dry-run --inject-error step2
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```
|
||||
[DRY RUN] Step 1: Would execute [actions]
|
||||
[DRY RUN] Step 2: Injected error as requested
|
||||
[DRY RUN] Error Recovery: Would attempt fix
|
||||
[DRY RUN] Rollback: Would undo Step 1 changes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Version**: 1.0
|
||||
**Last Updated**: YYYY-MM-DD
|
||||
**Maintainer**: Team Name
|
||||
|
|
@ -9,5 +9,4 @@ README.md
|
|||
.yalc/
|
||||
yalc.lock
|
||||
testApi/
|
||||
*.local.*
|
||||
*.local
|
||||
*.local.*
|
||||
|
|
@ -3,7 +3,6 @@ dist/
|
|||
build/
|
||||
.next/
|
||||
out/
|
||||
local/
|
||||
|
||||
# 依赖目录
|
||||
node_modules/
|
||||
|
|
@ -23,6 +22,4 @@ vitest.config.mts
|
|||
bin/
|
||||
scripts/
|
||||
deploy/
|
||||
document/
|
||||
|
||||
projects/marketplace
|
||||
docSite/
|
||||
|
|
@ -11,7 +11,7 @@ assignees: ''
|
|||
[//]: # '方框内填 x 表示打钩'
|
||||
|
||||
- [ ] 我已确认目前没有类似 issue
|
||||
- [ ] 我已完整查看过项目 README,以及[项目文档](https://doc.fastgpt.io/docs/introduction/)
|
||||
- [ ] 我已完整查看过项目 README,以及[项目文档](https://doc.tryfastgpt.ai/docs/intro/)
|
||||
- [ ] 我使用了自己的 key,并确认我的 key 是可正常使用的
|
||||
- [ ] 我理解并愿意跟进此 issue,协助测试和提供反馈
|
||||
- [x] 我理解并认可上述内容,并理解项目维护者精力有限,**不遵循规则的 issue 可能会被无视或直接关闭**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
yangchuansheng/fastgpt-imgs:
|
||||
- source: docSite/assets/imgs/
|
||||
dest: imgs/
|
||||
deleteOrphaned: true
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
name: Deploy doc image to cf
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'docSite/**'
|
||||
branches:
|
||||
- 'main'
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains jobs "deploy-production"
|
||||
deploy-production:
|
||||
# The environment this job references
|
||||
environment:
|
||||
name: Production
|
||||
url: ${{ steps.vercel-action.outputs.preview-url }}
|
||||
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
# Job outputs
|
||||
outputs:
|
||||
docs: ${{ steps.filter.outputs.docs }}
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
# Step 1 - Checks-out your repository under $GITHUB_WORKSPACE
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive # Fetch submodules
|
||||
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
|
||||
|
||||
# Step 2 Detect changes to Docs Content
|
||||
- name: Detect changes in doc content
|
||||
uses: dorny/paths-filter@v2
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
docs:
|
||||
- 'docSite/content/docs/**'
|
||||
base: main
|
||||
|
||||
- name: Add cdn for images
|
||||
run: |
|
||||
sed -i "s#\](/imgs/#\](https://cdn.jsdelivr.net/gh/yangchuansheng/fastgpt-imgs@main/imgs/#g" $(grep -rl "\](/imgs/" docSite/content/zh-cn/docs)
|
||||
|
||||
# Step 3 - Install Hugo (specific version)
|
||||
- name: Install Hugo
|
||||
uses: peaceiris/actions-hugo@v2
|
||||
with:
|
||||
hugo-version: '0.117.0'
|
||||
extended: true
|
||||
|
||||
# Step 4 - Builds the site using Hugo
|
||||
- name: Build
|
||||
run: cd docSite && hugo mod get -u github.com/colinwilson/lotusdocs@6d0568e && hugo -v --minify
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
if: github.ref == 'refs/heads/main'
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: docSite/public
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
name: Deploy doc image by kubeconfig
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'docSite/**'
|
||||
branches:
|
||||
- 'main'
|
||||
|
||||
jobs:
|
||||
build-fastgpt-docs-images:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Change baseURL
|
||||
run: sed -i 's|^baseURL =.*|baseURL = "https://doc.fastgpt.cn"|g' ./docSite/hugo.toml
|
||||
|
||||
- name: Get current date and time
|
||||
id: datetime
|
||||
run: echo "datetime=$(date +'%Y%m%d%H%M%S')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
# list of Docker images to use as base name for tags
|
||||
images: |
|
||||
ghcr.io/${{ github.repository_owner }}/fastgpt-docs
|
||||
registry.cn-hangzhou.aliyuncs.com/${{ secrets.ALI_HUB_USERNAME }}/fastgpt-docs
|
||||
tags: |
|
||||
${{ steps.datetime.outputs.datetime }}
|
||||
flavor: latest=false
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to Aliyun
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: registry.cn-hangzhou.aliyuncs.com
|
||||
username: ${{ secrets.ALI_HUB_USERNAME }}
|
||||
password: ${{ secrets.ALI_HUB_PASSWORD }}
|
||||
|
||||
- name: Build and push Docker images to ghcr.io and DockerHub
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./docSite/Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
outputs:
|
||||
tags: ${{ steps.datetime.outputs.datetime }}
|
||||
|
||||
update-docs-image:
|
||||
needs: build-fastgpt-docs-images
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.repository == 'labring/FastGPT'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions-hub/kubectl@master
|
||||
env:
|
||||
KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}
|
||||
with:
|
||||
args: set image deployment/fastgpt-docs fastgpt-docs=registry.cn-hangzhou.aliyuncs.com/${{ secrets.ALI_HUB_USERNAME }}/fastgpt-docs:${{ needs.build-fastgpt-docs-images.outputs.tags }}
|
||||
- uses: actions-hub/kubectl@master
|
||||
env:
|
||||
KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}
|
||||
with:
|
||||
args: annotate deployment/fastgpt-docs originImageName="registry.cn-hangzhou.aliyuncs.com/${{ secrets.ALI_HUB_USERNAME }}/fastgpt-docs:${{ needs.build-fastgpt-docs-images.outputs.tags }}" --overwrite
|
||||
|
|
@ -1,169 +0,0 @@
|
|||
name: Document deploy
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'document/**'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
sync-images:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout current repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Checkout target repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: labring/fastgpt-img
|
||||
token: ${{ secrets.DOCS_IMGS_SYNC_TOKEN }}
|
||||
path: fastgpt-img
|
||||
|
||||
- name: Sync images
|
||||
run: |
|
||||
# Create imgs directory if it doesn't exist
|
||||
mkdir -p fastgpt-img
|
||||
|
||||
# Copy all images from document/public/imgs to the target repository
|
||||
cp -r document/public/imgs/* fastgpt-img
|
||||
|
||||
# Navigate to target repository
|
||||
cd fastgpt-img
|
||||
|
||||
# Configure git
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
# Add, commit and push changes
|
||||
git add .
|
||||
if ! git diff --cached --quiet; then
|
||||
git commit -m "Sync images from FastGPT document at $(date)"
|
||||
git push
|
||||
echo "Images synced successfully"
|
||||
else
|
||||
echo "No changes to sync"
|
||||
fi
|
||||
# Add a new job to generate unified timestamp
|
||||
generate-timestamp:
|
||||
needs: sync-images
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
datetime: ${{ steps.datetime.outputs.datetime }}
|
||||
steps:
|
||||
- name: Get current datetime
|
||||
id: datetime
|
||||
run: echo "datetime=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT
|
||||
|
||||
build-images:
|
||||
needs: generate-timestamp
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
domain_config:
|
||||
- domain: 'https://fastgpt.io'
|
||||
suffix: 'io'
|
||||
- domain: 'https://fastgpt.cn'
|
||||
suffix: 'cn'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Rewrite image paths
|
||||
if: matrix.domain_config.suffix == 'io'
|
||||
run: |
|
||||
find document/content/docs -name "*.mdx" -type f | while read file; do
|
||||
sed -i 's|](/imgs/|](https://cdn.jsdelivr.net/gh/labring/fastgpt-img@main/|g' "$file"
|
||||
done
|
||||
- name: Rewrite domain links for CN
|
||||
if: matrix.domain_config.suffix == 'cn'
|
||||
run: |
|
||||
find document/content/docs -name "*.mdx" -type f | while read file; do
|
||||
sed -i 's|doc\.fastgpt\.io|doc.fastgpt.cn|g' "$file"
|
||||
done
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
# list of Docker images to use as base name for tags
|
||||
images: |
|
||||
${{ secrets.ALI_IMAGE_NAME }}/fastgpt-docs
|
||||
tags: |
|
||||
${{ matrix.domain_config.suffix }}-${{ needs.generate-timestamp.outputs.datetime }}
|
||||
flavor: latest=false
|
||||
|
||||
- name: Login to Aliyun
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: registry.cn-hangzhou.aliyuncs.com
|
||||
username: ${{ secrets.ALI_HUB_USERNAME }}
|
||||
password: ${{ secrets.ALI_HUB_PASSWORD }}
|
||||
|
||||
- name: Build and push Docker images (CN)
|
||||
if: matrix.domain_config.suffix == 'cn'
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./document
|
||||
file: ./document/Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
platforms: linux/amd64
|
||||
build-args: |
|
||||
FASTGPT_HOME_DOMAIN=${{ matrix.domain_config.domain }}
|
||||
|
||||
- name: Build and push Docker images (IO)
|
||||
if: matrix.domain_config.suffix == 'io'
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./document
|
||||
file: ./document/Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
platforms: linux/amd64
|
||||
build-args: |
|
||||
FASTGPT_HOME_DOMAIN=${{ matrix.domain_config.domain }}
|
||||
|
||||
update-images:
|
||||
needs: [generate-timestamp, build-images]
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
domain_config:
|
||||
- domain: 'https://fastgpt.io'
|
||||
suffix: 'io'
|
||||
deployment: 'fastgpt-docs'
|
||||
kube_config: 'KUBE_CONFIG_IO'
|
||||
- domain: 'https://fastgpt.cn'
|
||||
suffix: 'cn'
|
||||
deployment: 'fastgpt-docs'
|
||||
kube_config: 'KUBE_CONFIG_CN'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Add kubeconfig setup step to handle encoding issues
|
||||
- name: Setup kubeconfig
|
||||
run: |
|
||||
mkdir -p $HOME/.kube
|
||||
echo "${{ secrets[matrix.domain_config.kube_config] }}" > $HOME/.kube/config
|
||||
chmod 600 $HOME/.kube/config
|
||||
|
||||
- name: Update deployment image
|
||||
run: |
|
||||
kubectl set image deployment/${{ matrix.domain_config.deployment }} ${{ matrix.domain_config.deployment }}=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-docs:${{ matrix.domain_config.suffix }}-${{ needs.generate-timestamp.outputs.datetime }}
|
||||
|
||||
- name: Annotate deployment
|
||||
run: |
|
||||
kubectl annotate deployment/${{ matrix.domain_config.deployment }} originImageName="${{ secrets.ALI_IMAGE_NAME }}/fastgpt-docs:${{ matrix.domain_config.suffix }}-${{ needs.generate-timestamp.outputs.datetime }}" --overwrite
|
||||
|
|
@ -1,96 +1,95 @@
|
|||
name: Preview documents
|
||||
name: Preview FastGPT docs
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- 'document/**'
|
||||
- 'docSite/**'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
pull-requests: write
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
build-images:
|
||||
runs-on: ubuntu-latest
|
||||
# This workflow contains jobs "deploy-production"
|
||||
deploy-preview:
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
pull-requests: write
|
||||
# The environment this job references
|
||||
environment:
|
||||
name: Preview
|
||||
url: ${{ steps.vercel-action.outputs.preview-url }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Get current datetime
|
||||
id: datetime
|
||||
run: echo "datetime=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
# list of Docker images to use as base name for tags
|
||||
images: |
|
||||
${{ secrets.ALI_IMAGE_NAME }}/fastgpt-docs
|
||||
tags: |
|
||||
${{ steps.datetime.outputs.datetime }}
|
||||
flavor: latest=false
|
||||
|
||||
- name: Login to Aliyun
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: registry.cn-hangzhou.aliyuncs.com
|
||||
username: ${{ secrets.ALI_HUB_USERNAME }}
|
||||
password: ${{ secrets.ALI_HUB_PASSWORD }}
|
||||
|
||||
- name: Build and push Docker images
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./document
|
||||
file: ./document/Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
FASTGPT_HOME_DOMAIN=https://fastgpt.io
|
||||
outputs:
|
||||
tags: ${{ steps.datetime.outputs.datetime }}
|
||||
|
||||
update-images:
|
||||
needs: build-images
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
# Job outputs
|
||||
outputs:
|
||||
url: ${{ steps.vercel-action.outputs.preview-url }}
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
# Step 1 - Checks-out your repository under $GITHUB_WORKSPACE
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
submodules: recursive # Fetch submodules
|
||||
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Add kubeconfig setup step to handle encoding issues
|
||||
- name: Setup kubeconfig
|
||||
run: |
|
||||
mkdir -p $HOME/.kube
|
||||
echo "${{ secrets.KUBE_CONFIG_CN }}" > $HOME/.kube/config
|
||||
chmod 600 $HOME/.kube/config
|
||||
# Step 2 Detect changes to Docs Content
|
||||
- name: Detect changes in doc content
|
||||
uses: dorny/paths-filter@v2
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
docs:
|
||||
- 'docSite/content/docs/**'
|
||||
base: main
|
||||
|
||||
- name: Update deployment image
|
||||
run: |
|
||||
kubectl set image deployment/fastgpt-docs-preview fastgpt-docs-preview=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-docs:${{ needs.build-images.outputs.tags }}
|
||||
# Step 3 - Install Hugo (specific version)
|
||||
- name: Install Hugo
|
||||
uses: peaceiris/actions-hugo@v2
|
||||
with:
|
||||
hugo-version: '0.117.0'
|
||||
extended: true
|
||||
|
||||
- name: Annotate deployment
|
||||
run: |
|
||||
kubectl annotate deployment/fastgpt-docs-preview originImageName="${{ secrets.ALI_IMAGE_NAME }}/fastgpt-docs:${{ needs.build-images.outputs.tags }}" --overwrite
|
||||
# Step 4 - Builds the site using Hugo
|
||||
- name: Build
|
||||
run: cd docSite && hugo mod get -u github.com/colinwilson/lotusdocs@6d0568e && hugo -v --minify
|
||||
|
||||
- name: '@finleyge/github-tools'
|
||||
uses: FinleyGe/github-tools@0.0.1
|
||||
id: print-image-label
|
||||
if: success()
|
||||
# Step 5 - Push our generated site to Cloudflare
|
||||
- name: Deploy to Cloudflare Pages
|
||||
id: deploy
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: pages deploy ./docSite/public --project-name=fastgpt-doc
|
||||
packageManager: npm
|
||||
|
||||
- name: Create deployment status comment
|
||||
if: always()
|
||||
env:
|
||||
JOB_STATUS: ${{ job.status }}
|
||||
PREVIEW_URL: ${{ steps.deploy.outputs.deployment-url }}
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tool: issue-comment
|
||||
title: 'Docs Preview:'
|
||||
body: |
|
||||
---
|
||||
🚀 **FastGPT Document Preview Ready!**
|
||||
script: |
|
||||
const success = process.env.JOB_STATUS === 'success';
|
||||
const deploymentUrl = `${process.env.PREVIEW_URL}`;
|
||||
const status = success ? '✅ Success' : '❌ Failed';
|
||||
console.log(process.env.JOB_STATUS);
|
||||
|
||||
🔗 [👀 Click here to visit preview](https://pueuoharpgcl.sealoshzh.site)
|
||||
const commentBody = `**Deployment Status: ${status}**
|
||||
${success ? `🔗 Preview URL: ${deploymentUrl}` : ''}`;
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
...context.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
body: commentBody
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
name: Sync images
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- docSite/assets/imgs/**
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- docSite/assets/imgs/**
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Run GitHub File Sync
|
||||
uses: BetaHuhn/repo-file-sync-action@v1.21.0
|
||||
with:
|
||||
GH_PAT: ${{ secrets.IMG_GH_PAT }}
|
||||
CONFIG_PATH: .github/doc-sync-image.yml
|
||||
ORIGINAL_MESSAGE: true
|
||||
SKIP_PR: true
|
||||
COMMIT_EACH_FILE: false
|
||||
|
|
@ -7,123 +7,51 @@ on:
|
|||
- 'packages/**'
|
||||
branches:
|
||||
- 'main'
|
||||
|
||||
jobs:
|
||||
get-vars:
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
docker_repo: ${{ steps.set_docker_repo.outputs.docker_repo }}
|
||||
docker_tag: ${{ steps.set_docker_repo.outputs.docker_tag }}
|
||||
steps:
|
||||
- name: Set docker repository and tag
|
||||
id: set_docker_repo
|
||||
run: |
|
||||
echo "docker_repo=ghcr.io/$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
|
||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
||||
echo "docker_tag=latest" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "docker_tag=${{ github.ref_name }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
build-fastgpt-images:
|
||||
needs: get-vars
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
strategy:
|
||||
matrix:
|
||||
archs:
|
||||
- arch: amd64
|
||||
runs-on: ubuntu-24.04
|
||||
- arch: arm64
|
||||
runs-on: ubuntu-24.04-arm
|
||||
runs-on: ${{ matrix.archs.runs-on || 'ubuntu-24.04' }}
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.repository != 'labring/FastGPT'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
driver-opts: network=host
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-${{ matrix.archs.arch }}-buildx-${{ github.sha }}
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.archs.arch }}-buildx-
|
||||
${{ runner.os }}-buildx-
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push for ${{ matrix.archs.arch }}
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: projects/app/Dockerfile
|
||||
platforms: linux/${{ matrix.archs.arch }}
|
||||
labels: |
|
||||
org.opencontainers.image.source=https://github.com/${{ github.repository }}
|
||||
org.opencontainers.image.description=fastgpt image
|
||||
outputs: type=image,"name=${{ needs.get-vars.outputs.docker_repo }}",push-by-digest=true,push=true
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
- name: Export digest
|
||||
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
||||
run: |
|
||||
mkdir -p ${{ runner.temp }}/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-${{ github.sha }}-${{ matrix.archs.arch }}
|
||||
path: ${{ runner.temp }}/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
release-fastgpt-images:
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
needs: [get-vars, build-fastgpt-images]
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ${{ runner.temp }}/digests
|
||||
pattern: digests-${{ github.sha }}-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Set image name and tag
|
||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt:latest" >> $GITHUB_ENV
|
||||
- name: Build and publish image for main branch or tag push event
|
||||
env:
|
||||
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
||||
run: |
|
||||
echo "Git_Tag=${{ needs.get-vars.outputs.docker_repo }}:${{ needs.get-vars.outputs.docker_tag }}" >> $GITHUB_ENV
|
||||
echo "Git_Latest=${{ needs.get-vars.outputs.docker_repo }}:latest" >> $GITHUB_ENV
|
||||
|
||||
- name: Create manifest list and push
|
||||
working-directory: ${{ runner.temp }}/digests
|
||||
run: |
|
||||
TAGS="$(echo -e "${Git_Tag}\n${Git_Latest}")"
|
||||
for TAG in $TAGS; do
|
||||
docker buildx imagetools create -t $TAG \
|
||||
$(printf '${{ needs.get-vars.outputs.docker_repo }}@sha256:%s ' *)
|
||||
sleep 5
|
||||
done
|
||||
docker buildx build \
|
||||
--build-arg name=app \
|
||||
--label "org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/FastGPT" \
|
||||
--label "org.opencontainers.image.description=fastgpt image" \
|
||||
--push \
|
||||
--cache-from=type=local,src=/tmp/.buildx-cache \
|
||||
--cache-to=type=local,dest=/tmp/.buildx-cache \
|
||||
-t ${DOCKER_REPO_TAGGED} \
|
||||
-f Dockerfile \
|
||||
.
|
||||
|
|
|
|||
|
|
@ -46,9 +46,9 @@ jobs:
|
|||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-${{ matrix.archs.arch }}-${{ matrix.sub_routes.repo }}-buildx-${{ github.sha }}
|
||||
key: ${{ runner.os }}-${{ matrix.sub_routes.repo }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.archs.arch }}-${{ matrix.sub_routes.repo }}-buildx-
|
||||
${{ runner.os }}-${{ matrix.sub_routes.repo }}-buildx-
|
||||
|
||||
# login docker
|
||||
- name: Login to GitHub Container Registry
|
||||
|
|
|
|||
|
|
@ -41,12 +41,12 @@ jobs:
|
|||
${{ runner.os }}-buildx-${{ github.sha }}-
|
||||
${{ runner.os }}-buildx-
|
||||
|
||||
- name: Login to Aliyun Container Registry
|
||||
uses: docker/login-action@v3
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: registry.cn-hangzhou.aliyuncs.com
|
||||
username: ${{ secrets.ALI_HUB_USERNAME }}
|
||||
password: ${{ secrets.ALI_HUB_PASSWORD }}
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set image config
|
||||
id: config
|
||||
|
|
@ -54,15 +54,15 @@ jobs:
|
|||
if [[ "${{ matrix.image }}" == "fastgpt" ]]; then
|
||||
echo "DOCKERFILE=projects/app/Dockerfile" >> $GITHUB_OUTPUT
|
||||
echo "DESCRIPTION=fastgpt-pr image" >> $GITHUB_OUTPUT
|
||||
echo "DOCKER_REPO_TAGGED=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-pr:fatsgpt_${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT
|
||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-pr:fatsgpt_${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ matrix.image }}" == "sandbox" ]]; then
|
||||
echo "DOCKERFILE=projects/sandbox/Dockerfile" >> $GITHUB_OUTPUT
|
||||
echo "DESCRIPTION=fastgpt-sandbox-pr image" >> $GITHUB_OUTPUT
|
||||
echo "DOCKER_REPO_TAGGED=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-pr:fatsgpt_sandbox_${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT
|
||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-pr:fatsgpt_sandbox_${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ matrix.image }}" == "mcp_server" ]]; then
|
||||
echo "DOCKERFILE=projects/mcp_server/Dockerfile" >> $GITHUB_OUTPUT
|
||||
echo "DESCRIPTION=fastgpt-mcp_server-pr image" >> $GITHUB_OUTPUT
|
||||
echo "DOCKER_REPO_TAGGED=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-pr:fatsgpt_mcp_server_${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT
|
||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-pr:fatsgpt_mcp_server_${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Build ${{ matrix.image }} image for PR
|
||||
|
|
@ -73,18 +73,17 @@ jobs:
|
|||
--label "org.opencontainers.image.description=${{ steps.config.outputs.DESCRIPTION }}" \
|
||||
--push \
|
||||
--cache-from=type=local,src=/tmp/.buildx-cache \
|
||||
--cache-to=type=local,dest=/tmp/.buildx-cache \
|
||||
-t ${{ steps.config.outputs.DOCKER_REPO_TAGGED }} \
|
||||
.
|
||||
|
||||
- name: '@finleyge/github-tools'
|
||||
uses: FinleyGe/github-tools@0.0.1
|
||||
id: print-image-label
|
||||
if: success()
|
||||
- uses: actions/github-script@v7
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tool: issue-comment
|
||||
title: 'Preview ${{ matrix.image }} Image:'
|
||||
body: |
|
||||
```
|
||||
${{ steps.config.outputs.DOCKER_REPO_TAGGED }}
|
||||
```
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: 'Preview ${{ matrix.image }} Image: `${{ steps.config.outputs.DOCKER_REPO_TAGGED }}`'
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,147 +0,0 @@
|
|||
name: Build fastgpt-marketplace images
|
||||
on:
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
build-fastgpt-marketplace-images:
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: amd64
|
||||
- arch: arm64
|
||||
runs-on: ubuntu-24.04-arm
|
||||
runs-on: ${{ matrix.runs-on || 'ubuntu-24.04' }}
|
||||
steps:
|
||||
# install env
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
driver-opts: network=host
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-marketplace-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-marketplace-buildx-
|
||||
|
||||
# login docker
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Login to Ali Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: registry.cn-hangzhou.aliyuncs.com
|
||||
username: ${{ secrets.ALI_HUB_USERNAME }}
|
||||
password: ${{ secrets.ALI_HUB_PASSWORD }}
|
||||
|
||||
- name: Build for ${{ matrix.arch }}
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: projects/marketplace/Dockerfile
|
||||
platforms: linux/${{ matrix.arch }}
|
||||
labels: |
|
||||
org.opencontainers.image.source=https://github.com/${{ github.repository }}
|
||||
org.opencontainers.image.description=fastgpt-marketplace image
|
||||
outputs: type=image,"name=ghcr.io/${{ github.repository_owner }}/fastgpt-marketplace,${{ secrets.ALI_IMAGE_NAME }}/fastgpt-marketplace",push-by-digest=true,push=true
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p ${{ runner.temp }}/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-fastgpt-marketplace-${{ github.sha }}-${{ matrix.arch }}
|
||||
path: ${{ runner.temp }}/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
release-fastgpt-marketplace-images:
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
needs: build-fastgpt-marketplace-images
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Login to Ali Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: registry.cn-hangzhou.aliyuncs.com
|
||||
username: ${{ secrets.ALI_HUB_USERNAME }}
|
||||
password: ${{ secrets.ALI_HUB_PASSWORD }}
|
||||
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ${{ runner.temp }}/digests
|
||||
pattern: digests-fastgpt-marketplace-${{ github.sha }}-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Generate random tag
|
||||
id: tag
|
||||
run: |
|
||||
# Generate random hash tag (8 characters)
|
||||
TAG=$(echo $RANDOM | md5sum | head -c 8)
|
||||
echo "RANDOM_TAG=$TAG" >> $GITHUB_ENV
|
||||
echo "Generated tag: $TAG"
|
||||
|
||||
- name: Set image name and tag
|
||||
run: |
|
||||
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt-marketplace:${{ env.RANDOM_TAG }}" >> $GITHUB_ENV
|
||||
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-marketplace:${{ env.RANDOM_TAG }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Create manifest list and push
|
||||
working-directory: ${{ runner.temp }}/digests
|
||||
run: |
|
||||
echo "Pushing image with tag: ${{ env.RANDOM_TAG }}"
|
||||
echo "Available digests:"
|
||||
ls -la
|
||||
echo ""
|
||||
|
||||
# Create manifest for GitHub Container Registry
|
||||
echo "Creating manifest for GitHub: ${Git_Tag}"
|
||||
docker buildx imagetools create -t ${Git_Tag} \
|
||||
$(printf 'ghcr.io/${{ github.repository_owner }}/fastgpt-marketplace@sha256:%s ' *)
|
||||
echo "✅ GitHub manifest created"
|
||||
sleep 5
|
||||
|
||||
# Create manifest for Ali Cloud
|
||||
echo "Creating manifest for Ali Cloud: ${Ali_Tag}"
|
||||
docker buildx imagetools create -t ${Ali_Tag} \
|
||||
$(printf '${{ secrets.ALI_IMAGE_NAME }}/fastgpt-marketplace@sha256:%s ' *)
|
||||
echo "✅ Ali Cloud manifest created"
|
||||
|
||||
echo ""
|
||||
echo "✅ All images pushed successfully:"
|
||||
echo " - ${{ env.Git_Tag }}"
|
||||
echo " - ${{ env.Ali_Tag }}"
|
||||
|
|
@ -31,12 +31,17 @@ testApi/
|
|||
local/
|
||||
dist/
|
||||
|
||||
# hugo
|
||||
**/.hugo_build.lock
|
||||
docSite/public/
|
||||
docSite/resources/_gen/
|
||||
docSite/.vercel
|
||||
*.local.*
|
||||
|
||||
|
||||
.idea/
|
||||
files/helm/fastgpt/fastgpt-0.1.0.tgz
|
||||
files/helm/fastgpt/charts/*.tgz
|
||||
|
||||
tmp/
|
||||
coverage
|
||||
document/.source
|
||||
|
||||
projects/app/worker/
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
if command -v pnpm >/dev/null 2>&1; then
|
||||
pnpm lint-staged
|
||||
elif command -v npx >/dev/null 2>&1; then
|
||||
if command -v npx >/dev/null 2>&1; then
|
||||
npx lint-staged
|
||||
fi
|
||||
|
|
@ -2,9 +2,8 @@ dist
|
|||
.vscode
|
||||
**/.DS_Store
|
||||
node_modules
|
||||
document/
|
||||
docSite/
|
||||
*.md
|
||||
*.mdx
|
||||
|
||||
pnpm-lock.yaml
|
||||
cl100l_base.ts
|
||||
|
|
|
|||
|
|
@ -1,33 +1,35 @@
|
|||
{
|
||||
// Place your FastGPT 工作区 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
|
||||
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
|
||||
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
|
||||
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
|
||||
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
|
||||
// Place your FastGPT 工作区 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
|
||||
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
|
||||
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
|
||||
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
|
||||
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
|
||||
// Placeholders with the same ids are connected.
|
||||
// Example:
|
||||
"Next api template": {
|
||||
"scope": "javascript,typescript",
|
||||
"prefix": "nextapi",
|
||||
"body": [
|
||||
"import { NextAPI } from '@/service/middleware/entry';",
|
||||
"import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';",
|
||||
"",
|
||||
"async function handler(",
|
||||
" req: ApiRequestProps,",
|
||||
" res: ApiResponseType<${1}ResponseType>",
|
||||
"): Promise<${1}ResponseType> {",
|
||||
" const body = ${1}BodySchema.parse(req.body);",
|
||||
" const query = ${1}QuerySchema.parse(req.query);",
|
||||
"",
|
||||
" ${2}",
|
||||
"",
|
||||
" return {};",
|
||||
"}",
|
||||
"",
|
||||
"export default NextAPI(handler);"
|
||||
"import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';",
|
||||
"import { NextAPI } from '@/service/middleware/entry';",
|
||||
"",
|
||||
"export type ${TM_FILENAME_BASE}Query = {};",
|
||||
"",
|
||||
"export type ${TM_FILENAME_BASE}Body = {};",
|
||||
"",
|
||||
"export type ${TM_FILENAME_BASE}Response = {};",
|
||||
"",
|
||||
"async function handler(",
|
||||
" req: ApiRequestProps<${TM_FILENAME_BASE}Body, ${TM_FILENAME_BASE}Query>,",
|
||||
" res: ApiResponseType<any>",
|
||||
"): Promise<${TM_FILENAME_BASE}Response> {",
|
||||
" $1",
|
||||
" return {}",
|
||||
"}",
|
||||
"",
|
||||
"export default NextAPI(handler);"
|
||||
],
|
||||
"description": "FastGPT Next API template with Zod validation"
|
||||
"description": "FastGPT Next API template"
|
||||
},
|
||||
"use context template": {
|
||||
"scope": "typescriptreact",
|
||||
|
|
@ -36,7 +38,7 @@
|
|||
"import React, { ReactNode } from 'react';",
|
||||
"import { createContext } from 'use-context-selector';",
|
||||
"",
|
||||
"type ContextType = {${1}};",
|
||||
"type ContextType = {$1};",
|
||||
"",
|
||||
"export const Context = createContext<ContextType>({});",
|
||||
"",
|
||||
|
|
@ -63,4 +65,4 @@
|
|||
"});"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
"editor.mouseWheelZoom": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"prettier.prettierPath": "node_modules/prettier",
|
||||
"typescript.preferences.includePackageJsonAutoImports": "on",
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"i18n-ally.localesPaths": [
|
||||
"packages/web/i18n",
|
||||
|
|
@ -26,12 +25,7 @@
|
|||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"mdx.server.enable": true,
|
||||
"markdown.copyFiles.overwriteBehavior": "nameIncrementally",
|
||||
"markdown.copyFiles.destination": {
|
||||
"/document/content/docs/**/*": "${documentWorkspaceFolder}/document/public/imgs/"
|
||||
},
|
||||
"files.associations": {
|
||||
"*.mdx": "markdown"
|
||||
"/docSite/content/**/*": "${documentWorkspaceFolder}/docSite/assets/imgs/"
|
||||
}
|
||||
}
|
||||
2
LICENSE
|
|
@ -7,7 +7,7 @@ The FastGPT is licensed under the Apache License 2.0, with the following additio
|
|||
a. Multi-tenant SaaS service: Unless explicitly authorized by FastGPT in writing, you may not use the FastGPT.AI source code to operate a multi-tenant SaaS service that is similar to the FastGPT.
|
||||
b. LOGO and copyright information: In the process of using FastGPT, you may not remove or modify the LOGO or copyright information in the FastGPT console.
|
||||
|
||||
Please contact dennis@sealos.io by email to inquire about licensing matters.
|
||||
Please contact yujinlong@sealos.io by email to inquire about licensing matters.
|
||||
|
||||
2. As a contributor, you should agree that your contributed code:
|
||||
|
||||
|
|
|
|||
2
Makefile
|
|
@ -21,5 +21,5 @@ ifeq ($(proxy), taobao)
|
|||
else ifeq ($(proxy), clash)
|
||||
docker build -f $(filePath) -t $(image) . --network host --build-arg HTTP_PROXY=http://127.0.0.1:7890 --build-arg HTTPS_PROXY=http://127.0.0.1:7890
|
||||
else
|
||||
docker build --progress=plain -f $(filePath) -t $(image) .
|
||||
docker build -f $(filePath) -t $(image) .
|
||||
endif
|
||||
70
README.md
|
|
@ -1,6 +1,6 @@
|
|||
<div align="center">
|
||||
|
||||
<a href="https://fastgpt.io/"><img src="/.github/imgs/logo.svg" width="120" height="120" alt="fastgpt logo"></a>
|
||||
<a href="https://tryfastgpt.ai/"><img src="/.github/imgs/logo.svg" width="120" height="120" alt="fastgpt logo"></a>
|
||||
|
||||
# FastGPT
|
||||
|
||||
|
|
@ -15,13 +15,13 @@ FastGPT 是一个 AI Agent 构建平台,提供开箱即用的数据处理、
|
|||
</div>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://fastgpt.io/">
|
||||
<a href="https://tryfastgpt.ai/">
|
||||
<img height="21" src="https://img.shields.io/badge/在线使用-d4eaf7?style=flat-square&logo=spoj&logoColor=7d09f1" alt="cloud">
|
||||
</a>
|
||||
<a href="https://doc.fastgpt.io/docs/introduction">
|
||||
<a href="https://doc.tryfastgpt.ai/docs/intro">
|
||||
<img height="21" src="https://img.shields.io/badge/相关文档-7d09f1?style=flat-square" alt="document">
|
||||
</a>
|
||||
<a href="https://doc.fastgpt.io/docs/introduction/development/intro">
|
||||
<a href="https://doc.tryfastgpt.ai/docs/development">
|
||||
<img height="21" src="https://img.shields.io/badge/本地开发-%23d4eaf7?style=flat-square&logo=xcode&logoColor=7d09f1" alt="development">
|
||||
</a>
|
||||
<a href="/#-%E7%9B%B8%E5%85%B3%E9%A1%B9%E7%9B%AE">
|
||||
|
|
@ -33,7 +33,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
|
|||
|
||||
## 🛸 在线使用
|
||||
|
||||
- 🌍 国际版:[fastgpt.io](https://fastgpt.io/)
|
||||
- 🌍 国际版:[tryfastgpt.ai](https://tryfastgpt.ai/)
|
||||
|
||||
| | |
|
||||
| ---------------------------------- | ---------------------------------- |
|
||||
|
|
@ -47,40 +47,40 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
|
|||
## 💡 RoadMap
|
||||
|
||||
`1` 应用编排能力
|
||||
- [x] 对话工作流、插件工作流,包含基础的 RPA 节点。
|
||||
- [x] 用户交互
|
||||
- [x] 双向 MCP
|
||||
- [ ] Agent 模式
|
||||
- [ ] AI 生成工作流
|
||||
- [x] 对话工作流、插件工作流
|
||||
- [x] 工具调用
|
||||
- [x] Code sandbox
|
||||
- [x] 循环调用
|
||||
- [x] 用户选择
|
||||
- [x] 表单输入
|
||||
|
||||
`2` 应用调试能力
|
||||
- [x] 知识库单点搜索测试
|
||||
- [x] 对话时反馈引用并可修改与删除
|
||||
- [x] 完整调用链路日志
|
||||
- [x] 应用评测
|
||||
- [ ] 高级编排 DeBug 调试模式
|
||||
- [ ] 应用节点日志
|
||||
|
||||
`3` 知识库能力
|
||||
`2` 知识库能力
|
||||
- [x] 多库复用,混用
|
||||
- [x] chunk 记录修改和删除
|
||||
- [x] 支持手动输入,直接分段,QA 拆分导入
|
||||
- [x] 支持 txt,md,html,pdf,docx,pptx,csv,xlsx (有需要更多可 PR file loader),支持 url 读取、CSV 批量导入
|
||||
- [x] 混合检索 & 重排
|
||||
- [x] API 知识库
|
||||
- [ ] RAG 模块热插拔
|
||||
- [ ] 自定义文件读取服务
|
||||
- [ ] 自定义分块服务
|
||||
|
||||
`3` 应用调试能力
|
||||
- [x] 知识库单点搜索测试
|
||||
- [x] 对话时反馈引用并可修改与删除
|
||||
- [x] 完整上下文呈现
|
||||
- [x] 完整模块中间值呈现
|
||||
- [ ] 高级编排 DeBug 模式
|
||||
|
||||
`4` OpenAPI 接口
|
||||
- [x] completions 接口 (chat 模式对齐 GPT 接口)
|
||||
- [x] 知识库 CRUD
|
||||
- [x] 对话 CRUD
|
||||
- [ ] 自动化 OpenAPI 接口
|
||||
|
||||
`5` 运营能力
|
||||
- [x] 免登录分享窗口
|
||||
- [x] Iframe 一键嵌入
|
||||
- [x] 聊天窗口嵌入支持自定义 Icon,默认打开,拖拽等功能
|
||||
- [x] 统一查阅对话记录,并对数据进行标注
|
||||
- [x] 应用运营日志
|
||||
|
||||
`6` 其他
|
||||
- [x] 可视化模型配置。
|
||||
|
|
@ -100,23 +100,26 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
|
|||
|
||||
> 使用 [Sealos](https://sealos.io) 服务,无需采购服务器、无需域名,支持高并发 & 动态伸缩,并且数据库应用采用 kubeblocks 的数据库,在 IO 性能方面,远超于简单的 Docker 容器部署。
|
||||
|
||||
[点击查看 Sealos 一键部署 FastGPT 教程](https://doc.fastgpt.io/docs/introduction/development/sealos/)
|
||||
[点击查看 Sealos 一键部署 FastGPT 教程](https://doc.tryfastgpt.ai/docs/development/sealos/)
|
||||
|
||||
* [快速开始本地开发](https://doc.fastgpt.io/docs/introduction/development/intro/)
|
||||
* [部署 FastGPT](https://doc.fastgpt.io/docs/introduction/development/sealos/)
|
||||
* [系统配置文件说明](https://doc.fastgpt.io/docs/introduction/development/configuration/)
|
||||
* [多模型配置方案](https://doc.fastgpt.io/docs/introduction/development/modelConfig/one-api/)
|
||||
* [版本更新/升级介绍](https://doc.fastgpt.io/docs/upgrading)
|
||||
* [OpenAPI API 文档](https://doc.fastgpt.io/docs/introduction/development/openapi/)
|
||||
* [知识库结构详解](https://doc.fastgpt.io/docs/introduction/guide/knowledge_base/RAG/)
|
||||
* [快速开始本地开发](https://doc.tryfastgpt.ai/docs/development/intro/)
|
||||
* [部署 FastGPT](https://doc.tryfastgpt.ai/docs/development/sealos/)
|
||||
* [系统配置文件说明](https://doc.tryfastgpt.ai/docs/development/configuration/)
|
||||
* [多模型配置方案](https://doc.tryfastgpt.ai/docs/development/modelconfig/one-api/)
|
||||
* [版本更新/升级介绍](https://doc.tryfastgpt.ai/docs/development/upgrading/)
|
||||
* [OpenAPI API 文档](https://doc.tryfastgpt.ai/docs/development/openapi/)
|
||||
* [知识库结构详解](https://doc.tryfastgpt.ai/docs/guide/knowledge_base/rag/)
|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 🏘️ 加入我们
|
||||
|
||||
我们正在寻找志同道合的小伙伴,加速 FastGPT 的发展。你可以通过 [FastGPT 2025 招聘](https://fael3z0zfze.feishu.cn/wiki/P7FOwEmPziVcaYkvVaacnVX1nvg)了解 FastGPT 的招聘信息。
|
||||
|
||||
## 💪 相关项目
|
||||
|
||||
- [FastGPT-plugin](https://github.com/labring/fastgpt-plugin)
|
||||
- [Laf:3 分钟快速接入三方应用](https://github.com/labring/laf)
|
||||
- [Sealos:快速部署集群应用](https://github.com/labring/sealos)
|
||||
- [One API:多模型管理,支持 Azure、文心一言等](https://github.com/songquanpeng/one-api)
|
||||
|
|
@ -129,6 +132,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
|
|||
- [PPIO 派欧云:一键调用高性价比的开源模型 API 和 GPU 容器](https://ppinfra.com/user/register?invited_by=VITYVU&utm_source=github_fastgpt)
|
||||
- [AI Proxy:国内模型聚合服务](https://sealos.run/aiproxy/?k=fastgpt-github/)
|
||||
- [SiliconCloud (硅基流动) —— 开源模型在线体验平台](https://cloud.siliconflow.cn/i/TR9Ym0c4)
|
||||
- [COW 个人微信/企微机器人](https://doc.tryfastgpt.ai/docs/use-cases/external-integration/onwechat/)
|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
|
||||
|
|
@ -138,7 +142,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
|
|||
|
||||
扫码加入飞书话题群:
|
||||
|
||||

|
||||

|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
|
||||
|
|
@ -211,4 +215,4 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
|
|||
1. 允许作为后台服务直接商用,但不允许提供 SaaS 服务。
|
||||
2. 未经商业授权,任何形式的商用服务均需保留相关版权信息。
|
||||
3. 完整请查看 [FastGPT Open Source License](./LICENSE)
|
||||
4. 联系方式:Dennis@sealos.io,[点击查看商业版定价策略](https://doc.fastgpt.io/docs/introduction/commercial/)
|
||||
4. 联系方式:Dennis@sealos.io,[点击查看商业版定价策略](https://doc.tryfastgpt.ai/docs/shopping_cart/intro/)
|
||||
|
|
|
|||
26
README_en.md
|
|
@ -1,6 +1,6 @@
|
|||
<div align="center">
|
||||
|
||||
<a href="https://fastgpt.io/"><img src="/.github/imgs/logo.svg" width="120" height="120" alt="fastgpt logo"></a>
|
||||
<a href="https://tryfastgpt.ai/"><img src="/.github/imgs/logo.svg" width="120" height="120" alt="fastgpt logo"></a>
|
||||
|
||||
# FastGPT
|
||||
|
||||
|
|
@ -18,9 +18,9 @@ FastGPT is a knowledge-based platform built on the LLMs, offers a comprehensive
|
|||
[](https://github.com/labring/FastGPT/pulls)
|
||||
[](https://github.com/labring/FastGPT/pulls)
|
||||
[](https://github.com/labring/FastGPT/blob/main/LICENSE)
|
||||
[](https://doc.fastgpt.io/docs/introduction)
|
||||
[](https://doc.fastgpt.io/docs/introduction/development/intro)
|
||||
[](https://fastgpt.io/)
|
||||
[](https://doc.tryfastgpt.ai/docs/intro)
|
||||
[](https://doc.tryfastgpt.ai/docs/intro)
|
||||
[](https://tryfastgpt.ai/)
|
||||
|
||||
[](https://discord.gg/mp68xkZn2Q)
|
||||
[](https://oss.laf.run/otnvvf-imgs/feishu3.png)
|
||||
|
|
@ -33,7 +33,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
|
|||
|
||||
## 🛸 Online Use
|
||||
|
||||
Website: [fastgpt.io](https://fastgpt.io/)
|
||||
Website: [tryfastgpt.ai](https://tryfastgpt.ai/)
|
||||
|
||||
| | |
|
||||
| ---------------------------------- | ---------------------------------- |
|
||||
|
|
@ -74,13 +74,13 @@ Project tech stack: NextJs + TS + ChakraUI + MongoDB + PostgreSQL (PG Vector plu
|
|||
|
||||
Give it a 2-4 minute wait after deployment as it sets up the database. Initially, it might be a too slow since we're using the basic settings.
|
||||
|
||||
[sealos one click deployment tutorial](https://doc.fastgpt.io/docs/introduction/development/sealos/)
|
||||
[sealos one click deployment tutorial](https://doc.tryfastgpt.ai/docs/development/sealos/)
|
||||
|
||||
- [Getting Started with Local Development](https://doc.fastgpt.io/docs/introduction/development/intro)
|
||||
- [Deploying FastGPT](https://doc.fastgpt.io/docs/introduction/development/docker)
|
||||
- [Guide on System Configs](https://doc.fastgpt.io/docs/introduction/development/configuration)
|
||||
- [Configuring Multiple Models](https://doc.fastgpt.io/docs//introduction/development/modelConfig/intro)
|
||||
- [Version Updates & Upgrades](https://doc.fastgpt.io/docs/introduction/development/upgrading/index)
|
||||
- [Getting Started with Local Development](https://doc.tryfastgpt.ai/docs/development)
|
||||
- [Deploying FastGPT](https://doc.tryfastgpt.ai/docs/installation)
|
||||
- [Guide on System Configs](https://doc.tryfastgpt.ai/docs/installation/reference)
|
||||
- [Configuring Multiple Models](https://doc.tryfastgpt.ai/docs/installation/reference/models)
|
||||
- [Version Updates & Upgrades](https://doc.tryfastgpt.ai/docs/installation/upgrading)
|
||||
|
||||
<a href="#FastGPT">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
|
|
@ -108,7 +108,7 @@ Project tech stack: NextJs + TS + ChakraUI + MongoDB + PostgreSQL (PG Vector plu
|
|||
|
||||
## 🏘️ Community & Support
|
||||
|
||||
+ 🌐 Visit the [FastGPT website](https://fastgpt.io/) for full documentation and useful links.
|
||||
+ 🌐 Visit the [FastGPT website](https://tryfastgpt.ai/) for full documentation and useful links.
|
||||
+ 💬 Join our [Discord server](https://discord.gg/mp68xkZn2Q) is to chat with FastGPT developers and other FastGPT users. This is a good place to learn about FastGPT, ask questions, and share your experiences.
|
||||
+ 🐞 Create [GitHub Issues](https://github.com/labring/FastGPT/issues/new/choose) for bug reports and feature requests.
|
||||
|
||||
|
|
@ -185,7 +185,7 @@ This repository complies with the [FastGPT Open Source License](./LICENSE) open
|
|||
1. Direct commercial use as a backend service is allowed, but provision of SaaS services is not allowed.
|
||||
2. Without commercial authorization, any form of commercial service must retain relevant copyright information.
|
||||
3. For full details, please see [FastGPT Open Source License](./LICENSE)
|
||||
4. Contact: Dennis@sealos.io , [click to view commercial version pricing strategy](https://doc.fastgpt.io/docs/introduction/commercial/)
|
||||
4. Contact: Dennis@sealos.io , [click to view commercial version pricing strategy](https://doc.tryfastgpt.ai/docs/commercial)
|
||||
|
||||
<a href="#FastGPT">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
|
|
|
|||
20
README_ja.md
|
|
@ -1,6 +1,6 @@
|
|||
<div align="center">
|
||||
|
||||
<a href="https://fastgpt.io/"><img src="/.github/imgs/logo.svg" width="120" height="120" alt="fastgpt logo"></a>
|
||||
<a href="https://tryfastgpt.ai/"><img src="/.github/imgs/logo.svg" width="120" height="120" alt="fastgpt logo"></a>
|
||||
|
||||
# FastGPT
|
||||
|
||||
|
|
@ -15,13 +15,13 @@ FastGPT は、LLM 上 に 構築 された 知識 ベースの Q&A システム
|
|||
</div>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://fastgpt.io/">
|
||||
<a href="https://tryfastgpt.ai/">
|
||||
<img height="21" src="https://img.shields.io/badge/在线使用-d4eaf7?style=flat-square&logo=spoj&logoColor=7d09f1" alt="cloud">
|
||||
</a>
|
||||
<a href="https://doc.fastgpt.io/docs/introduction">
|
||||
<a href="https://doc.tryfastgpt.ai/docs/intro">
|
||||
<img height="21" src="https://img.shields.io/badge/相关文档-7d09f1?style=flat-square" alt="document">
|
||||
</a>
|
||||
<a href="https://doc.fastgpt.io/docs/introduction/development/intro">
|
||||
<a href="https://doc.tryfastgpt.ai/docs/development">
|
||||
<img height="21" src="https://img.shields.io/badge/本地开发-%23d4eaf7?style=flat-square&logo=xcode&logoColor=7d09f1" alt="development">
|
||||
</a>
|
||||
<a href="/#-%E7%9B%B8%E5%85%B3%E9%A1%B9%E7%9B%AE">
|
||||
|
|
@ -36,7 +36,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
|
|||
|
||||
## 🛸 クラウドサービスの 利用
|
||||
|
||||
[fastgpt.io](https://fastgpt.io/)
|
||||
[tryfastgpt.ai](https://tryfastgpt.ai/)
|
||||
|
||||
| | |
|
||||
| ---------------------------------- | ---------------------------------- |
|
||||
|
|
@ -98,11 +98,11 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
|
|||
|
||||
デプロイ 後、データベースをセットアップするので、2~4分待 ってください。基本設定 を 使 っているので、最初 は 少 し 遅 いかもしれません。
|
||||
|
||||
- [ローカル 開発入門](https://doc.fastgpt.io/docs/introduction/development/intro)
|
||||
- [FastGPT のデプロイ](https://doc.fastgpt.io/docs/introduction/development/docker)
|
||||
- [システム 設定 ガイド](https://doc.fastgpt.io/docs/introduction/development/configuration)
|
||||
- [複数 モデルの 設定](https://doc.fastgpt.io/docs/introduction/development/modelConfig/ai-proxy)
|
||||
- [バージョン 更新 とアップグレード](https://doc.fastgpt.io/docs/introduction/development/upgrading/index)
|
||||
- [ローカル 開発入門](https://doc.tryfastgpt.ai/docs/development)
|
||||
- [FastGPT のデプロイ](https://doc.tryfastgpt.ai/docs/installation)
|
||||
- [システム 設定 ガイド](https://doc.tryfastgpt.ai/docs/installation/reference)
|
||||
- [複数 モデルの 設定](https://doc.tryfastgpt.ai/docs/installation/reference/models)
|
||||
- [バージョン 更新 とアップグレード](https://doc.tryfastgpt.ai/docs/installation/upgrading)
|
||||
|
||||
<!-- ## :point_right: ロードマップ
|
||||
- [FastGPT ロードマップ](https://kjqvjse66l.feishu.cn/docx/RVUxdqE2WolDYyxEKATcM0XXnte) -->
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
如果您发现了 FastGPT 的安全漏洞,请按照以下步骤进行报告:
|
||||
|
||||
1. **报告方式**
|
||||
发送邮件至:archer@fastgpt.io
|
||||
发送邮件至:yujinlong@sealos.io
|
||||
请备注版本以及您的 GitHub 账号
|
||||
|
||||
3. **响应时间**
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
## 更新 docker compose 脚本
|
||||
|
||||
### 正常更新(不动服务,只改版本)
|
||||
|
||||
1. 更新 `args.json` 中的版本号
|
||||
2. 在 `FastGPT` 目录执行 `pnpm run gen:deploy` 即可
|
||||
|
||||
### 加服务
|
||||
|
||||
比如要添加 `example` 服务:
|
||||
1. `init.mjs` 的 `Services Enum` 中添加 fastgptExample: fastgpt-example
|
||||
2. 在 `args.json` 中添加 image 和 tag, 注意 `args.json` 的 `key` 值,要和 `init.mjs` 的 `value` 值一致。
|
||||
3. 更新 templates/docker-compose.[dev|prod].yml 文件,把服务的相关配置加进去,并且:服务的 image 改为 ${{example.image}}:${{example.tag}}
|
||||
|
||||
### 加向量库
|
||||
|
||||
比如添加 `exampleDB` 向量库:
|
||||
1. 添加 vector service 配置在 `templates/vector` 下面,例如 `templates/vector/exampleDB.txt` 内容可以参考其他 txt,注意缩进,image 名字也要替换成 ${{exampleDB.image}}:${{exampleDB:tag}}, service name 必须是 `vectorDB`
|
||||
2. 在 `args.json` 中添加 `exampleDB` 的配置
|
||||
3. init.mjs vector enum 中添加 `vector`
|
||||
4. init.mjs 中添加 vector 的相关配置:
|
||||
|
||||
```ts
|
||||
const vector = {
|
||||
// pg, milvus, ob ...
|
||||
vector: {
|
||||
db: '', // 空即可
|
||||
config: `/
|
||||
VECTOR_URL:vectordb://xxxxx
|
||||
`, //注意 第一行反引号后面的 / 不能少(去除首个换行符); 左边的两个空格的缩进不能变,否则会语法错误
|
||||
extra: `` // 额外的配置,可以看 ob 的那个,需要一个 config 字段引入 init.sql
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
5. init.mjs 读入 vector 配置
|
||||
|
||||
```json
|
||||
{ // 这是个块作用域, 直接搜 read in Vectors
|
||||
// read in Vectors
|
||||
// pg, ob ....
|
||||
const vectordb = fs.readFileSync(path.join(process.cwd(), 'templates', 'vector', 'vector.txt'));
|
||||
vector.vector.db = String(vectordb);
|
||||
}
|
||||
```
|
||||
|
||||
6. init.mjs 最后生成的时候,需要添加
|
||||
```ts
|
||||
fs.promises.writeFile(
|
||||
path.join(process.cwd(), 'docker', 'cn', 'docker-compose.vector.yml'),
|
||||
replace(template, 'cn', VectorEnum.vector)
|
||||
),
|
||||
fs.promises.writeFile(
|
||||
path.join(process.cwd(), 'docker', 'global', 'docker-compose.ziliiz.yml'),
|
||||
replace(template, 'global', VectorEnum.vector)
|
||||
),
|
||||
```
|
||||
|
||||
## yaml 的锚点和引用
|
||||
|
||||
`&` 标志一个锚点
|
||||
|
||||
```yaml
|
||||
x-share-config: &x-share-config 'I am the config content'
|
||||
x-share-config-list: &x-share-config-list
|
||||
key1: value
|
||||
key2: value
|
||||
```
|
||||
|
||||
`*` 引用一个锚点
|
||||
```yaml
|
||||
some_other_example: *x-share-config-list
|
||||
```
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
{
|
||||
"tags": {
|
||||
"fastgpt": "v4.14.4",
|
||||
"fastgpt-sandbox": "v4.14.4",
|
||||
"fastgpt-mcp_server": "v4.14.4",
|
||||
"fastgpt-plugin": "v0.3.4",
|
||||
"aiproxy": "v0.3.2",
|
||||
"aiproxy-pg": "0.8.0-pg15",
|
||||
"mongo": "5.0.18",
|
||||
"redis": "7.2-alpine",
|
||||
"minio": "RELEASE.2025-09-07T16-13-09Z",
|
||||
"pg": "0.8.0-pg15",
|
||||
"milvus-minio": "RELEASE.2023-03-20T20-16-18Z",
|
||||
"milvus-etcd": "v3.5.5",
|
||||
"milvus-standalone": "v2.4.3",
|
||||
"oceanbase": "4.3.5-lts"
|
||||
},
|
||||
"images": {
|
||||
"cn": {
|
||||
"fastgpt": "registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt",
|
||||
"fastgpt-plugin": "registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin",
|
||||
"fastgpt-sandbox": "registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox",
|
||||
"fastgpt-mcp_server": "registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server",
|
||||
"aiproxy": "registry.cn-hangzhou.aliyuncs.com/labring/aiproxy",
|
||||
"aiproxy-pg": "registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector",
|
||||
"mongo": "registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo",
|
||||
"redis": "registry.cn-hangzhou.aliyuncs.com/fastgpt/redis",
|
||||
"minio": "registry.cn-hangzhou.aliyuncs.com/fastgpt/minio",
|
||||
"pg": "registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector",
|
||||
"milvus-minio": "minio/minio",
|
||||
"milvus-etcd": "quay.io/coreos/etcd",
|
||||
"milvus-standalone": "milvusdb/milvus",
|
||||
"oceanbase": "oceanbase/oceanbase-ce"
|
||||
},
|
||||
"global": {
|
||||
"fastgpt": "ghcr.io/labring/fastgpt",
|
||||
"fastgpt-plugin": "ghcr.io/labring/fastgpt-plugin",
|
||||
"fastgpt-sandbox": "ghcr.io/labring/fastgpt-sandbox",
|
||||
"fastgpt-mcp_server": "ghcr.io/labring/fastgpt-mcp_server",
|
||||
"aiproxy": "ghcr.io/labring/aiproxy",
|
||||
"aiproxy-pg": "pgvector/pgvector",
|
||||
"mongo": "mongo",
|
||||
"redis": "redis",
|
||||
"minio": "minio/minio",
|
||||
"pg": "pgvector/pgvector",
|
||||
"milvus-minio": "minio/minio",
|
||||
"milvus-etcd": "quay.io/coreos/etcd",
|
||||
"milvus-standalone": "milvusdb/milvus",
|
||||
"oceanbase": "oceanbase/oceanbase-ce"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
*
|
||||
!.gitignore
|
||||
!docker-compose.yml
|
||||
!docker-compose.cn.yml
|
||||
|
|
@ -1,229 +0,0 @@
|
|||
# 用于开发的 docker-compose 文件:
|
||||
# - 只包含 FastGPT 的最小化运行条件
|
||||
# - 没有 FastGPT 本体
|
||||
# - 所有端口都映射到外层
|
||||
# - pg: 5432
|
||||
# - mongo: 27017
|
||||
# - redis: 6379
|
||||
# - fastgpt-sandbox: 3002
|
||||
# - fastgpt-plugin: 3003
|
||||
# - aiproxy: 3010
|
||||
# - 使用 pgvector 作为默认的向量库
|
||||
|
||||
services:
|
||||
# Vector DB
|
||||
pg:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:0.8.0-pg15
|
||||
container_name: pg
|
||||
restart: always
|
||||
ports: # 生产环境建议不要暴露
|
||||
- 5432:5432
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
||||
- POSTGRES_USER=username
|
||||
- POSTGRES_PASSWORD=password
|
||||
- POSTGRES_DB=postgres
|
||||
volumes:
|
||||
- ./pg/data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'username', '-d', 'postgres']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
# DB
|
||||
mongo:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # cpu 不支持 AVX 时候使用 4.4.29
|
||||
container_name: mongo
|
||||
restart: always
|
||||
ports:
|
||||
- 27017:27017
|
||||
networks:
|
||||
- fastgpt
|
||||
command: mongod --keyFile /data/mongodb.key --replSet rs0
|
||||
environment:
|
||||
- MONGO_INITDB_ROOT_USERNAME=myusername
|
||||
- MONGO_INITDB_ROOT_PASSWORD=mypassword
|
||||
volumes:
|
||||
- ./mongo/data:/data/db
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
'CMD',
|
||||
'mongo',
|
||||
'-u',
|
||||
'myusername',
|
||||
'-p',
|
||||
'mypassword',
|
||||
'--authenticationDatabase',
|
||||
'admin',
|
||||
'--eval',
|
||||
"db.adminCommand('ping')"
|
||||
]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
openssl rand -base64 128 > /data/mongodb.key
|
||||
chmod 400 /data/mongodb.key
|
||||
chown 999:999 /data/mongodb.key
|
||||
echo 'const isInited = rs.status().ok === 1
|
||||
if(!isInited){
|
||||
rs.initiate({
|
||||
_id: "rs0",
|
||||
members: [
|
||||
{ _id: 0, host: "mongo:27017" }
|
||||
]
|
||||
})
|
||||
}' > /data/initReplicaSet.js
|
||||
# 启动MongoDB服务
|
||||
exec docker-entrypoint.sh "$$@" &
|
||||
|
||||
# 等待MongoDB服务启动
|
||||
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')"; do
|
||||
echo "Waiting for MongoDB to start..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# 执行初始化副本集的脚本
|
||||
mongo -u myusername -p mypassword --authenticationDatabase admin /data/initReplicaSet.js
|
||||
|
||||
# 等待docker-entrypoint.sh脚本执行的MongoDB服务进程
|
||||
wait $$!
|
||||
redis:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/redis:7.2-alpine
|
||||
container_name: redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
command: |
|
||||
redis-server --requirepass mypassword --loglevel warning --maxclients 10000 --appendonly yes --save 60 10 --maxmemory 4gb --maxmemory-policy noeviction
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', '-a', 'mypassword', 'ping']
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
fastgpt-minio:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/minio:RELEASE.2025-09-07T16-13-09Z
|
||||
container_name: fastgpt-minio
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
ports:
|
||||
- '9000:9000'
|
||||
- '9001:9001'
|
||||
environment:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
volumes:
|
||||
- ./fastgpt-minio:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.4
|
||||
ports:
|
||||
- 3002:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.4
|
||||
ports:
|
||||
- 3005:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt-plugin:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.4
|
||||
container_name: fastgpt-plugin
|
||||
restart: always
|
||||
ports:
|
||||
- 3003:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
- AUTH_TOKEN=token
|
||||
- S3_EXTERNAL_BASE_URL=http://127.0.0.1:9000 # TODO: 改为你 Minio 的实际的 ip 地址
|
||||
- S3_ENDPOINT=fastgpt-minio
|
||||
- S3_PORT=9000
|
||||
- S3_USE_SSL=false
|
||||
- S3_ACCESS_KEY=minioadmin
|
||||
- S3_SECRET_KEY=minioadmin
|
||||
- S3_PUBLIC_BUCKET=fastgpt-public # 系统工具,创建的临时文件,存储的桶,要求公开读私有写。
|
||||
- S3_PRIVATE_BUCKET=fastgpt-private # 系统插件热安装文件的桶,私有读写。
|
||||
- MONGODB_URI=mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin&directConnection=true
|
||||
- REDIS_URL=redis://default:mypassword@redis:6379
|
||||
depends_on:
|
||||
fastgpt-minio:
|
||||
condition: service_healthy
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.3.2
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 3010:3000
|
||||
depends_on:
|
||||
aiproxy_pg:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- fastgpt
|
||||
- aiproxy
|
||||
environment:
|
||||
# 对应 fastgpt 里的AIPROXY_API_TOKEN
|
||||
- ADMIN_KEY=aiproxy
|
||||
# 错误日志详情保存时间(小时)
|
||||
- LOG_DETAIL_STORAGE_HOURS=1
|
||||
# 数据库连接地址
|
||||
- SQL_DSN=postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
# 最大重试次数
|
||||
- RETRY_TIMES=3
|
||||
# 不需要计费
|
||||
- BILLING_ENABLED=false
|
||||
# 不需要严格检测模型
|
||||
- DISABLE_MODEL_CONFIG=true
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/status']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
aiproxy_pg:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:0.8.0-pg15 # docker hub
|
||||
restart: unless-stopped
|
||||
container_name: aiproxy_pg
|
||||
volumes:
|
||||
- ./aiproxy_pg:/var/lib/postgresql/data
|
||||
networks:
|
||||
- aiproxy
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: aiproxy
|
||||
POSTGRES_PASSWORD: aiproxy
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'postgres', '-d', 'aiproxy']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks:
|
||||
fastgpt:
|
||||
aiproxy:
|
||||
|
|
@ -1,229 +0,0 @@
|
|||
# 用于开发的 docker-compose 文件:
|
||||
# - 只包含 FastGPT 的最小化运行条件
|
||||
# - 没有 FastGPT 本体
|
||||
# - 所有端口都映射到外层
|
||||
# - pg: 5432
|
||||
# - mongo: 27017
|
||||
# - redis: 6379
|
||||
# - fastgpt-sandbox: 3002
|
||||
# - fastgpt-plugin: 3003
|
||||
# - aiproxy: 3010
|
||||
# - 使用 pgvector 作为默认的向量库
|
||||
|
||||
services:
|
||||
# Vector DB
|
||||
pg:
|
||||
image: pgvector/pgvector:0.8.0-pg15
|
||||
container_name: pg
|
||||
restart: always
|
||||
ports: # 生产环境建议不要暴露
|
||||
- 5432:5432
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
||||
- POSTGRES_USER=username
|
||||
- POSTGRES_PASSWORD=password
|
||||
- POSTGRES_DB=postgres
|
||||
volumes:
|
||||
- ./pg/data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'username', '-d', 'postgres']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
# DB
|
||||
mongo:
|
||||
image: mongo:5.0.18 # cpu 不支持 AVX 时候使用 4.4.29
|
||||
container_name: mongo
|
||||
restart: always
|
||||
ports:
|
||||
- 27017:27017
|
||||
networks:
|
||||
- fastgpt
|
||||
command: mongod --keyFile /data/mongodb.key --replSet rs0
|
||||
environment:
|
||||
- MONGO_INITDB_ROOT_USERNAME=myusername
|
||||
- MONGO_INITDB_ROOT_PASSWORD=mypassword
|
||||
volumes:
|
||||
- ./mongo/data:/data/db
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
'CMD',
|
||||
'mongo',
|
||||
'-u',
|
||||
'myusername',
|
||||
'-p',
|
||||
'mypassword',
|
||||
'--authenticationDatabase',
|
||||
'admin',
|
||||
'--eval',
|
||||
"db.adminCommand('ping')"
|
||||
]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
openssl rand -base64 128 > /data/mongodb.key
|
||||
chmod 400 /data/mongodb.key
|
||||
chown 999:999 /data/mongodb.key
|
||||
echo 'const isInited = rs.status().ok === 1
|
||||
if(!isInited){
|
||||
rs.initiate({
|
||||
_id: "rs0",
|
||||
members: [
|
||||
{ _id: 0, host: "mongo:27017" }
|
||||
]
|
||||
})
|
||||
}' > /data/initReplicaSet.js
|
||||
# 启动MongoDB服务
|
||||
exec docker-entrypoint.sh "$$@" &
|
||||
|
||||
# 等待MongoDB服务启动
|
||||
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')"; do
|
||||
echo "Waiting for MongoDB to start..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# 执行初始化副本集的脚本
|
||||
mongo -u myusername -p mypassword --authenticationDatabase admin /data/initReplicaSet.js
|
||||
|
||||
# 等待docker-entrypoint.sh脚本执行的MongoDB服务进程
|
||||
wait $$!
|
||||
redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
command: |
|
||||
redis-server --requirepass mypassword --loglevel warning --maxclients 10000 --appendonly yes --save 60 10 --maxmemory 4gb --maxmemory-policy noeviction
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', '-a', 'mypassword', 'ping']
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
fastgpt-minio:
|
||||
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
|
||||
container_name: fastgpt-minio
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
ports:
|
||||
- '9000:9000'
|
||||
- '9001:9001'
|
||||
environment:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
volumes:
|
||||
- ./fastgpt-minio:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.14.4
|
||||
ports:
|
||||
- 3002:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.4
|
||||
ports:
|
||||
- 3005:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt-plugin:
|
||||
image: ghcr.io/labring/fastgpt-plugin:v0.3.4
|
||||
container_name: fastgpt-plugin
|
||||
restart: always
|
||||
ports:
|
||||
- 3003:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
- AUTH_TOKEN=token
|
||||
- S3_EXTERNAL_BASE_URL=http://127.0.0.1:9000 # TODO: 改为你 Minio 的实际的 ip 地址
|
||||
- S3_ENDPOINT=fastgpt-minio
|
||||
- S3_PORT=9000
|
||||
- S3_USE_SSL=false
|
||||
- S3_ACCESS_KEY=minioadmin
|
||||
- S3_SECRET_KEY=minioadmin
|
||||
- S3_PUBLIC_BUCKET=fastgpt-public # 系统工具,创建的临时文件,存储的桶,要求公开读私有写。
|
||||
- S3_PRIVATE_BUCKET=fastgpt-private # 系统插件热安装文件的桶,私有读写。
|
||||
- MONGODB_URI=mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin&directConnection=true
|
||||
- REDIS_URL=redis://default:mypassword@redis:6379
|
||||
depends_on:
|
||||
fastgpt-minio:
|
||||
condition: service_healthy
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: ghcr.io/labring/aiproxy:v0.3.2
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 3010:3000
|
||||
depends_on:
|
||||
aiproxy_pg:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- fastgpt
|
||||
- aiproxy
|
||||
environment:
|
||||
# 对应 fastgpt 里的AIPROXY_API_TOKEN
|
||||
- ADMIN_KEY=aiproxy
|
||||
# 错误日志详情保存时间(小时)
|
||||
- LOG_DETAIL_STORAGE_HOURS=1
|
||||
# 数据库连接地址
|
||||
- SQL_DSN=postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
# 最大重试次数
|
||||
- RETRY_TIMES=3
|
||||
# 不需要计费
|
||||
- BILLING_ENABLED=false
|
||||
# 不需要严格检测模型
|
||||
- DISABLE_MODEL_CONFIG=true
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/status']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
aiproxy_pg:
|
||||
image: pgvector/pgvector:0.8.0-pg15 # docker hub
|
||||
restart: unless-stopped
|
||||
container_name: aiproxy_pg
|
||||
volumes:
|
||||
- ./aiproxy_pg:/var/lib/postgresql/data
|
||||
networks:
|
||||
- aiproxy
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: aiproxy
|
||||
POSTGRES_PASSWORD: aiproxy
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'postgres', '-d', 'aiproxy']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks:
|
||||
fastgpt:
|
||||
aiproxy:
|
||||
|
|
@ -1,311 +0,0 @@
|
|||
# 用于部署的 docker-compose 文件:
|
||||
# - FastGPT 端口映射为 3000:3000
|
||||
# - FastGPT-mcp-server 端口映射 3005:3000
|
||||
# - 建议修改账密后再运行
|
||||
|
||||
# plugin auth token
|
||||
x-plugin-auth-token: &x-plugin-auth-token 'token'
|
||||
# aiproxy token
|
||||
x-aiproxy-token: &x-aiproxy-token 'token'
|
||||
# 数据库连接相关配置
|
||||
x-share-db-config: &x-share-db-config
|
||||
MONGODB_URI: mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
DB_MAX_LINK: 100
|
||||
REDIS_URL: redis://default:mypassword@redis:6379
|
||||
S3_EXTERNAL_BASE_URL: https://minio.com # S3 的公网访问地址
|
||||
S3_ENDPOINT: fastgpt-minio
|
||||
S3_PORT: 9000
|
||||
S3_USE_SSL: false
|
||||
S3_ACCESS_KEY: minioadmin
|
||||
S3_SECRET_KEY: minioadmin
|
||||
S3_PUBLIC_BUCKET: fastgpt-public # 公开读私有写桶
|
||||
S3_PRIVATE_BUCKET: fastgpt-private # 私有读写桶
|
||||
|
||||
# 向量库相关配置
|
||||
x-vec-config: &x-vec-config
|
||||
MILVUS_ADDRESS: http://milvusStandalone:19530
|
||||
MILVUS_TOKEN: none
|
||||
|
||||
|
||||
version: '3.3'
|
||||
services:
|
||||
# Vector DB
|
||||
milvus-minio:
|
||||
container_name: milvus-minio
|
||||
image: minio/minio:RELEASE.2023-03-20T20-16-18Z
|
||||
environment:
|
||||
MINIO_ACCESS_KEY: minioadmin
|
||||
MINIO_SECRET_KEY: minioadmin
|
||||
networks:
|
||||
- vector
|
||||
volumes:
|
||||
- ./milvus-minio:/minio_data
|
||||
command: minio server /minio_data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
# milvus
|
||||
milvus-etcd:
|
||||
container_name: milvus-etcd
|
||||
image: quay.io/coreos/etcd:v3.5.5
|
||||
environment:
|
||||
- ETCD_AUTO_COMPACTION_MODE=revision
|
||||
- ETCD_AUTO_COMPACTION_RETENTION=1000
|
||||
- ETCD_QUOTA_BACKEND_BYTES=4294967296
|
||||
- ETCD_SNAPSHOT_COUNT=50000
|
||||
networks:
|
||||
- vector
|
||||
volumes:
|
||||
- ./milvus/etcd:/etcd
|
||||
command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
|
||||
healthcheck:
|
||||
test: ['CMD', 'etcdctl', 'endpoint', 'health']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
vectorDB:
|
||||
container_name: milvusStandalone
|
||||
image: milvusdb/milvus:v2.4.3
|
||||
command: ['milvus', 'run', 'standalone']
|
||||
security_opt:
|
||||
- seccomp:unconfined
|
||||
environment:
|
||||
ETCD_ENDPOINTS: milvus-etcd:2379
|
||||
MINIO_ADDRESS: milvus-minio:9000
|
||||
networks:
|
||||
- fastgpt
|
||||
- vector
|
||||
volumes:
|
||||
- ./milvus/data:/var/lib/milvus
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9091/healthz']
|
||||
interval: 30s
|
||||
start_period: 90s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
depends_on:
|
||||
- 'milvus-etcd'
|
||||
- 'milvus-minio'
|
||||
|
||||
|
||||
mongo:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # cpu 不支持 AVX 时候使用 4.4.29
|
||||
container_name: mongo
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
command: mongod --keyFile /data/mongodb.key --replSet rs0
|
||||
environment:
|
||||
- MONGO_INITDB_ROOT_USERNAME=myusername
|
||||
- MONGO_INITDB_ROOT_PASSWORD=mypassword
|
||||
volumes:
|
||||
- ./mongo/data:/data/db
|
||||
healthcheck:
|
||||
test: ['CMD', 'mongo', '-u', 'myusername', '-p', 'mypassword', '--authenticationDatabase', 'admin', '--eval', "db.adminCommand('ping')"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
openssl rand -base64 128 > /data/mongodb.key
|
||||
chmod 400 /data/mongodb.key
|
||||
chown 999:999 /data/mongodb.key
|
||||
echo 'const isInited = rs.status().ok === 1
|
||||
if(!isInited){
|
||||
rs.initiate({
|
||||
_id: "rs0",
|
||||
members: [
|
||||
{ _id: 0, host: "mongo:27017" }
|
||||
]
|
||||
})
|
||||
}' > /data/initReplicaSet.js
|
||||
# 启动MongoDB服务
|
||||
exec docker-entrypoint.sh "$$@" &
|
||||
|
||||
# 等待MongoDB服务启动
|
||||
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')"; do
|
||||
echo "Waiting for MongoDB to start..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# 执行初始化副本集的脚本
|
||||
mongo -u myusername -p mypassword --authenticationDatabase admin /data/initReplicaSet.js
|
||||
|
||||
# 等待docker-entrypoint.sh脚本执行的MongoDB服务进程
|
||||
wait $$!
|
||||
redis:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/redis:7.2-alpine
|
||||
container_name: redis
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
command: |
|
||||
redis-server --requirepass mypassword --loglevel warning --maxclients 10000 --appendonly yes --save 60 10 --maxmemory 4gb --maxmemory-policy noeviction
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', '-a', 'mypassword', 'ping']
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
fastgpt-minio:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/minio:RELEASE.2025-09-07T16-13-09Z
|
||||
container_name: fastgpt-minio
|
||||
restart: always
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 9001:9001
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
volumes:
|
||||
- ./fastgpt-minio:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.4 # git
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
depends_on:
|
||||
- mongo
|
||||
- sandbox
|
||||
- vectorDB
|
||||
restart: always
|
||||
environment:
|
||||
<<: [*x-share-db-config, *x-vec-config]
|
||||
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||
FE_DOMAIN:
|
||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||
DEFAULT_ROOT_PSW: 1234
|
||||
# 登录凭证密钥
|
||||
TOKEN_KEY: any
|
||||
# root的密钥,常用于升级时候的初始化请求
|
||||
ROOT_KEY: root_key
|
||||
# 文件阅读加密
|
||||
FILE_TOKEN_KEY: filetoken
|
||||
# 密钥加密key
|
||||
AES256_SECRET_KEY: fastgptkey
|
||||
|
||||
# plugin 地址
|
||||
PLUGIN_BASE_URL: http://fastgpt-plugin:3000
|
||||
PLUGIN_TOKEN: *x-plugin-auth-token
|
||||
# sandbox 地址
|
||||
SANDBOX_URL: http://sandbox:3000
|
||||
# AI Proxy 的地址,如果配了该地址,优先使用
|
||||
AIPROXY_API_ENDPOINT: http://aiproxy:3000
|
||||
# AI Proxy 的 Admin Token,与 AI Proxy 中的环境变量 ADMIN_KEY
|
||||
AIPROXY_API_TOKEN: *x-aiproxy-token
|
||||
|
||||
# 日志等级: debug, info, warn, error
|
||||
LOG_LEVEL: info
|
||||
STORE_LOG_LEVEL: warn
|
||||
# 工作流最大运行次数
|
||||
WORKFLOW_MAX_RUN_TIMES: 1000
|
||||
# 批量执行节点,最大输入长度
|
||||
WORKFLOW_MAX_LOOP_TIMES: 100
|
||||
# 对话文件过期天数
|
||||
CHAT_FILE_EXPIRE_TIME: 7
|
||||
# 服务器接收请求,最大大小,单位 MB
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
ports:
|
||||
- 3005:3000
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt-plugin:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.4
|
||||
container_name: fastgpt-plugin
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
<<: *x-share-db-config
|
||||
AUTH_TOKEN: *x-plugin-auth-token
|
||||
# 工具网络请求,最大请求和响应体
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
# 最大 API 请求体大小
|
||||
MAX_API_SIZE: 10
|
||||
depends_on:
|
||||
fastgpt-minio:
|
||||
condition: service_healthy
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.3.2
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
aiproxy_pg:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- fastgpt
|
||||
- aiproxy
|
||||
environment:
|
||||
# 对应 fastgpt 里的AIPROXY_API_TOKEN
|
||||
ADMIN_KEY: *x-aiproxy-token
|
||||
# 错误日志详情保存时间(小时)
|
||||
LOG_DETAIL_STORAGE_HOURS: 1
|
||||
# 数据库连接地址
|
||||
SQL_DSN: postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
# 最大重试次数
|
||||
RETRY_TIMES: 3
|
||||
# 不需要计费
|
||||
BILLING_ENABLED: false
|
||||
# 不需要严格检测模型
|
||||
DISABLE_MODEL_CONFIG: true
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/status']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
aiproxy_pg:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:0.8.0-pg15 # docker hub
|
||||
restart: unless-stopped
|
||||
container_name: aiproxy_pg
|
||||
volumes:
|
||||
- ./aiproxy_pg:/var/lib/postgresql/data
|
||||
networks:
|
||||
- aiproxy
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: aiproxy
|
||||
POSTGRES_PASSWORD: aiproxy
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'postgres', '-d', 'aiproxy']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks:
|
||||
fastgpt:
|
||||
aiproxy:
|
||||
vector:
|
||||
|
||||
|
|
@ -1,293 +0,0 @@
|
|||
# 用于部署的 docker-compose 文件:
|
||||
# - FastGPT 端口映射为 3000:3000
|
||||
# - FastGPT-mcp-server 端口映射 3005:3000
|
||||
# - 建议修改账密后再运行
|
||||
|
||||
# plugin auth token
|
||||
x-plugin-auth-token: &x-plugin-auth-token 'token'
|
||||
# aiproxy token
|
||||
x-aiproxy-token: &x-aiproxy-token 'token'
|
||||
# 数据库连接相关配置
|
||||
x-share-db-config: &x-share-db-config
|
||||
MONGODB_URI: mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
DB_MAX_LINK: 100
|
||||
REDIS_URL: redis://default:mypassword@redis:6379
|
||||
S3_EXTERNAL_BASE_URL: https://minio.com # S3 的公网访问地址
|
||||
S3_ENDPOINT: fastgpt-minio
|
||||
S3_PORT: 9000
|
||||
S3_USE_SSL: false
|
||||
S3_ACCESS_KEY: minioadmin
|
||||
S3_SECRET_KEY: minioadmin
|
||||
S3_PUBLIC_BUCKET: fastgpt-public # 公开读私有写桶
|
||||
S3_PRIVATE_BUCKET: fastgpt-private # 私有读写桶
|
||||
|
||||
# 向量库相关配置
|
||||
x-vec-config: &x-vec-config
|
||||
OCEANBASE_URL: mysql://root%40tenantname:tenantpassword@ob:2881/test
|
||||
|
||||
|
||||
version: '3.3'
|
||||
services:
|
||||
# Vector DB
|
||||
vectorDB:
|
||||
image: oceanbase/oceanbase-ce:4.3.5-lts
|
||||
container_name: ob
|
||||
restart: always
|
||||
# ports: # 生产环境建议不要暴露
|
||||
# - 2881:2881
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
||||
- OB_SYS_PASSWORD=obsyspassword
|
||||
# 不同于传统数据库,OceanBase 数据库的账号包含更多字段,包括用户名、租户名和集群名。经典格式为"用户名@租户名#集群名"
|
||||
# 比如用mysql客户端连接时,根据本文件的默认配置,应该指定 "-uroot@tenantname"
|
||||
- OB_TENANT_NAME=tenantname
|
||||
- OB_TENANT_PASSWORD=tenantpassword
|
||||
# MODE分为MINI和NORMAL, 后者会最大程度使用主机资源
|
||||
- MODE=MINI
|
||||
- OB_SERVER_IP=127.0.0.1
|
||||
# 更多环境变量配置见oceanbase官方文档: https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000002013494
|
||||
volumes:
|
||||
- ../ob/data:/root/ob
|
||||
- ../ob/config:/root/.obd/cluster
|
||||
configs:
|
||||
- source: init_sql
|
||||
target: /root/boot/init.d/init.sql
|
||||
healthcheck:
|
||||
# obclient -h127.0.0.1 -P2881 -uroot@tenantname -ptenantpassword -e "SELECT 1;"
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
'obclient -h$${OB_SERVER_IP} -P2881 -uroot@$${OB_TENANT_NAME} -p$${OB_TENANT_PASSWORD} -e "SELECT 1;"',
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 1000
|
||||
start_period: 10s
|
||||
|
||||
|
||||
mongo:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # cpu 不支持 AVX 时候使用 4.4.29
|
||||
container_name: mongo
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
command: mongod --keyFile /data/mongodb.key --replSet rs0
|
||||
environment:
|
||||
- MONGO_INITDB_ROOT_USERNAME=myusername
|
||||
- MONGO_INITDB_ROOT_PASSWORD=mypassword
|
||||
volumes:
|
||||
- ./mongo/data:/data/db
|
||||
healthcheck:
|
||||
test: ['CMD', 'mongo', '-u', 'myusername', '-p', 'mypassword', '--authenticationDatabase', 'admin', '--eval', "db.adminCommand('ping')"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
openssl rand -base64 128 > /data/mongodb.key
|
||||
chmod 400 /data/mongodb.key
|
||||
chown 999:999 /data/mongodb.key
|
||||
echo 'const isInited = rs.status().ok === 1
|
||||
if(!isInited){
|
||||
rs.initiate({
|
||||
_id: "rs0",
|
||||
members: [
|
||||
{ _id: 0, host: "mongo:27017" }
|
||||
]
|
||||
})
|
||||
}' > /data/initReplicaSet.js
|
||||
# 启动MongoDB服务
|
||||
exec docker-entrypoint.sh "$$@" &
|
||||
|
||||
# 等待MongoDB服务启动
|
||||
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')"; do
|
||||
echo "Waiting for MongoDB to start..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# 执行初始化副本集的脚本
|
||||
mongo -u myusername -p mypassword --authenticationDatabase admin /data/initReplicaSet.js
|
||||
|
||||
# 等待docker-entrypoint.sh脚本执行的MongoDB服务进程
|
||||
wait $$!
|
||||
redis:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/redis:7.2-alpine
|
||||
container_name: redis
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
command: |
|
||||
redis-server --requirepass mypassword --loglevel warning --maxclients 10000 --appendonly yes --save 60 10 --maxmemory 4gb --maxmemory-policy noeviction
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', '-a', 'mypassword', 'ping']
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
fastgpt-minio:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/minio:RELEASE.2025-09-07T16-13-09Z
|
||||
container_name: fastgpt-minio
|
||||
restart: always
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 9001:9001
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
volumes:
|
||||
- ./fastgpt-minio:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.4 # git
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
depends_on:
|
||||
- mongo
|
||||
- sandbox
|
||||
- vectorDB
|
||||
restart: always
|
||||
environment:
|
||||
<<: [*x-share-db-config, *x-vec-config]
|
||||
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||
FE_DOMAIN:
|
||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||
DEFAULT_ROOT_PSW: 1234
|
||||
# 登录凭证密钥
|
||||
TOKEN_KEY: any
|
||||
# root的密钥,常用于升级时候的初始化请求
|
||||
ROOT_KEY: root_key
|
||||
# 文件阅读加密
|
||||
FILE_TOKEN_KEY: filetoken
|
||||
# 密钥加密key
|
||||
AES256_SECRET_KEY: fastgptkey
|
||||
|
||||
# plugin 地址
|
||||
PLUGIN_BASE_URL: http://fastgpt-plugin:3000
|
||||
PLUGIN_TOKEN: *x-plugin-auth-token
|
||||
# sandbox 地址
|
||||
SANDBOX_URL: http://sandbox:3000
|
||||
# AI Proxy 的地址,如果配了该地址,优先使用
|
||||
AIPROXY_API_ENDPOINT: http://aiproxy:3000
|
||||
# AI Proxy 的 Admin Token,与 AI Proxy 中的环境变量 ADMIN_KEY
|
||||
AIPROXY_API_TOKEN: *x-aiproxy-token
|
||||
|
||||
# 日志等级: debug, info, warn, error
|
||||
LOG_LEVEL: info
|
||||
STORE_LOG_LEVEL: warn
|
||||
# 工作流最大运行次数
|
||||
WORKFLOW_MAX_RUN_TIMES: 1000
|
||||
# 批量执行节点,最大输入长度
|
||||
WORKFLOW_MAX_LOOP_TIMES: 100
|
||||
# 对话文件过期天数
|
||||
CHAT_FILE_EXPIRE_TIME: 7
|
||||
# 服务器接收请求,最大大小,单位 MB
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
ports:
|
||||
- 3005:3000
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt-plugin:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.4
|
||||
container_name: fastgpt-plugin
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
<<: *x-share-db-config
|
||||
AUTH_TOKEN: *x-plugin-auth-token
|
||||
# 工具网络请求,最大请求和响应体
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
# 最大 API 请求体大小
|
||||
MAX_API_SIZE: 10
|
||||
depends_on:
|
||||
fastgpt-minio:
|
||||
condition: service_healthy
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.3.2
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
aiproxy_pg:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- fastgpt
|
||||
- aiproxy
|
||||
environment:
|
||||
# 对应 fastgpt 里的AIPROXY_API_TOKEN
|
||||
ADMIN_KEY: *x-aiproxy-token
|
||||
# 错误日志详情保存时间(小时)
|
||||
LOG_DETAIL_STORAGE_HOURS: 1
|
||||
# 数据库连接地址
|
||||
SQL_DSN: postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
# 最大重试次数
|
||||
RETRY_TIMES: 3
|
||||
# 不需要计费
|
||||
BILLING_ENABLED: false
|
||||
# 不需要严格检测模型
|
||||
DISABLE_MODEL_CONFIG: true
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/status']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
aiproxy_pg:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:0.8.0-pg15 # docker hub
|
||||
restart: unless-stopped
|
||||
container_name: aiproxy_pg
|
||||
volumes:
|
||||
- ./aiproxy_pg:/var/lib/postgresql/data
|
||||
networks:
|
||||
- aiproxy
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: aiproxy
|
||||
POSTGRES_PASSWORD: aiproxy
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'postgres', '-d', 'aiproxy']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks:
|
||||
fastgpt:
|
||||
aiproxy:
|
||||
vector:
|
||||
configs:
|
||||
init_sql:
|
||||
name: init_sql
|
||||
content: |
|
||||
ALTER SYSTEM SET ob_vector_memory_limit_percentage = 30;
|
||||
|
||||
|
|
@ -1,269 +0,0 @@
|
|||
# 用于部署的 docker-compose 文件:
|
||||
# - FastGPT 端口映射为 3000:3000
|
||||
# - FastGPT-mcp-server 端口映射 3005:3000
|
||||
# - 建议修改账密后再运行
|
||||
|
||||
# plugin auth token
|
||||
x-plugin-auth-token: &x-plugin-auth-token 'token'
|
||||
# aiproxy token
|
||||
x-aiproxy-token: &x-aiproxy-token 'token'
|
||||
# 数据库连接相关配置
|
||||
x-share-db-config: &x-share-db-config
|
||||
MONGODB_URI: mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
DB_MAX_LINK: 100
|
||||
REDIS_URL: redis://default:mypassword@redis:6379
|
||||
S3_EXTERNAL_BASE_URL: https://minio.com # S3 的公网访问地址
|
||||
S3_ENDPOINT: fastgpt-minio
|
||||
S3_PORT: 9000
|
||||
S3_USE_SSL: false
|
||||
S3_ACCESS_KEY: minioadmin
|
||||
S3_SECRET_KEY: minioadmin
|
||||
S3_PUBLIC_BUCKET: fastgpt-public # 公开读私有写桶
|
||||
S3_PRIVATE_BUCKET: fastgpt-private # 私有读写桶
|
||||
|
||||
# 向量库相关配置
|
||||
x-vec-config: &x-vec-config
|
||||
PG_URL: postgresql://username:password@pg:5432/postgres
|
||||
|
||||
version: '3.3'
|
||||
services:
|
||||
# Vector DB
|
||||
vectorDB:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:0.8.0-pg15
|
||||
container_name: pg
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
||||
- POSTGRES_USER=username
|
||||
- POSTGRES_PASSWORD=password
|
||||
- POSTGRES_DB=postgres
|
||||
volumes:
|
||||
- ./pg/data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'username', '-d', 'postgres']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
|
||||
mongo:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # cpu 不支持 AVX 时候使用 4.4.29
|
||||
container_name: mongo
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
command: mongod --keyFile /data/mongodb.key --replSet rs0
|
||||
environment:
|
||||
- MONGO_INITDB_ROOT_USERNAME=myusername
|
||||
- MONGO_INITDB_ROOT_PASSWORD=mypassword
|
||||
volumes:
|
||||
- ./mongo/data:/data/db
|
||||
healthcheck:
|
||||
test: ['CMD', 'mongo', '-u', 'myusername', '-p', 'mypassword', '--authenticationDatabase', 'admin', '--eval', "db.adminCommand('ping')"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
openssl rand -base64 128 > /data/mongodb.key
|
||||
chmod 400 /data/mongodb.key
|
||||
chown 999:999 /data/mongodb.key
|
||||
echo 'const isInited = rs.status().ok === 1
|
||||
if(!isInited){
|
||||
rs.initiate({
|
||||
_id: "rs0",
|
||||
members: [
|
||||
{ _id: 0, host: "mongo:27017" }
|
||||
]
|
||||
})
|
||||
}' > /data/initReplicaSet.js
|
||||
# 启动MongoDB服务
|
||||
exec docker-entrypoint.sh "$$@" &
|
||||
|
||||
# 等待MongoDB服务启动
|
||||
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')"; do
|
||||
echo "Waiting for MongoDB to start..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# 执行初始化副本集的脚本
|
||||
mongo -u myusername -p mypassword --authenticationDatabase admin /data/initReplicaSet.js
|
||||
|
||||
# 等待docker-entrypoint.sh脚本执行的MongoDB服务进程
|
||||
wait $$!
|
||||
redis:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/redis:7.2-alpine
|
||||
container_name: redis
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
command: |
|
||||
redis-server --requirepass mypassword --loglevel warning --maxclients 10000 --appendonly yes --save 60 10 --maxmemory 4gb --maxmemory-policy noeviction
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', '-a', 'mypassword', 'ping']
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
fastgpt-minio:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/minio:RELEASE.2025-09-07T16-13-09Z
|
||||
container_name: fastgpt-minio
|
||||
restart: always
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 9001:9001
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
volumes:
|
||||
- ./fastgpt-minio:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.4 # git
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
depends_on:
|
||||
- mongo
|
||||
- sandbox
|
||||
- vectorDB
|
||||
restart: always
|
||||
environment:
|
||||
<<: [*x-share-db-config, *x-vec-config]
|
||||
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||
FE_DOMAIN:
|
||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||
DEFAULT_ROOT_PSW: 1234
|
||||
# 登录凭证密钥
|
||||
TOKEN_KEY: any
|
||||
# root的密钥,常用于升级时候的初始化请求
|
||||
ROOT_KEY: root_key
|
||||
# 文件阅读加密
|
||||
FILE_TOKEN_KEY: filetoken
|
||||
# 密钥加密key
|
||||
AES256_SECRET_KEY: fastgptkey
|
||||
|
||||
# plugin 地址
|
||||
PLUGIN_BASE_URL: http://fastgpt-plugin:3000
|
||||
PLUGIN_TOKEN: *x-plugin-auth-token
|
||||
# sandbox 地址
|
||||
SANDBOX_URL: http://sandbox:3000
|
||||
# AI Proxy 的地址,如果配了该地址,优先使用
|
||||
AIPROXY_API_ENDPOINT: http://aiproxy:3000
|
||||
# AI Proxy 的 Admin Token,与 AI Proxy 中的环境变量 ADMIN_KEY
|
||||
AIPROXY_API_TOKEN: *x-aiproxy-token
|
||||
|
||||
# 日志等级: debug, info, warn, error
|
||||
LOG_LEVEL: info
|
||||
STORE_LOG_LEVEL: warn
|
||||
# 工作流最大运行次数
|
||||
WORKFLOW_MAX_RUN_TIMES: 1000
|
||||
# 批量执行节点,最大输入长度
|
||||
WORKFLOW_MAX_LOOP_TIMES: 100
|
||||
# 对话文件过期天数
|
||||
CHAT_FILE_EXPIRE_TIME: 7
|
||||
# 服务器接收请求,最大大小,单位 MB
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
ports:
|
||||
- 3005:3000
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt-plugin:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.4
|
||||
container_name: fastgpt-plugin
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
<<: *x-share-db-config
|
||||
AUTH_TOKEN: *x-plugin-auth-token
|
||||
# 工具网络请求,最大请求和响应体
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
# 最大 API 请求体大小
|
||||
MAX_API_SIZE: 10
|
||||
depends_on:
|
||||
fastgpt-minio:
|
||||
condition: service_healthy
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.3.2
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
aiproxy_pg:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- fastgpt
|
||||
- aiproxy
|
||||
environment:
|
||||
# 对应 fastgpt 里的AIPROXY_API_TOKEN
|
||||
ADMIN_KEY: *x-aiproxy-token
|
||||
# 错误日志详情保存时间(小时)
|
||||
LOG_DETAIL_STORAGE_HOURS: 1
|
||||
# 数据库连接地址
|
||||
SQL_DSN: postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
# 最大重试次数
|
||||
RETRY_TIMES: 3
|
||||
# 不需要计费
|
||||
BILLING_ENABLED: false
|
||||
# 不需要严格检测模型
|
||||
DISABLE_MODEL_CONFIG: true
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/status']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
aiproxy_pg:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:0.8.0-pg15 # docker hub
|
||||
restart: unless-stopped
|
||||
container_name: aiproxy_pg
|
||||
volumes:
|
||||
- ./aiproxy_pg:/var/lib/postgresql/data
|
||||
networks:
|
||||
- aiproxy
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: aiproxy
|
||||
POSTGRES_PASSWORD: aiproxy
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'postgres', '-d', 'aiproxy']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks:
|
||||
fastgpt:
|
||||
aiproxy:
|
||||
vector:
|
||||
|
||||
|
|
@ -1,252 +0,0 @@
|
|||
# 用于部署的 docker-compose 文件:
|
||||
# - FastGPT 端口映射为 3000:3000
|
||||
# - FastGPT-mcp-server 端口映射 3005:3000
|
||||
# - 建议修改账密后再运行
|
||||
|
||||
# plugin auth token
|
||||
x-plugin-auth-token: &x-plugin-auth-token 'token'
|
||||
# aiproxy token
|
||||
x-aiproxy-token: &x-aiproxy-token 'token'
|
||||
# 数据库连接相关配置
|
||||
x-share-db-config: &x-share-db-config
|
||||
MONGODB_URI: mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
DB_MAX_LINK: 100
|
||||
REDIS_URL: redis://default:mypassword@redis:6379
|
||||
S3_EXTERNAL_BASE_URL: https://minio.com # S3 的公网访问地址
|
||||
S3_ENDPOINT: fastgpt-minio
|
||||
S3_PORT: 9000
|
||||
S3_USE_SSL: false
|
||||
S3_ACCESS_KEY: minioadmin
|
||||
S3_SECRET_KEY: minioadmin
|
||||
S3_PUBLIC_BUCKET: fastgpt-public # 公开读私有写桶
|
||||
S3_PRIVATE_BUCKET: fastgpt-private # 私有读写桶
|
||||
|
||||
# 向量库相关配置
|
||||
x-vec-config: &x-vec-config
|
||||
MILVUS_ADDRESS: zilliz_cloud_address
|
||||
MILVUS_TOKEN: zilliz_cloud_token
|
||||
|
||||
version: '3.3'
|
||||
services:
|
||||
# Vector DB
|
||||
|
||||
|
||||
mongo:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # cpu 不支持 AVX 时候使用 4.4.29
|
||||
container_name: mongo
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
command: mongod --keyFile /data/mongodb.key --replSet rs0
|
||||
environment:
|
||||
- MONGO_INITDB_ROOT_USERNAME=myusername
|
||||
- MONGO_INITDB_ROOT_PASSWORD=mypassword
|
||||
volumes:
|
||||
- ./mongo/data:/data/db
|
||||
healthcheck:
|
||||
test: ['CMD', 'mongo', '-u', 'myusername', '-p', 'mypassword', '--authenticationDatabase', 'admin', '--eval', "db.adminCommand('ping')"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
openssl rand -base64 128 > /data/mongodb.key
|
||||
chmod 400 /data/mongodb.key
|
||||
chown 999:999 /data/mongodb.key
|
||||
echo 'const isInited = rs.status().ok === 1
|
||||
if(!isInited){
|
||||
rs.initiate({
|
||||
_id: "rs0",
|
||||
members: [
|
||||
{ _id: 0, host: "mongo:27017" }
|
||||
]
|
||||
})
|
||||
}' > /data/initReplicaSet.js
|
||||
# 启动MongoDB服务
|
||||
exec docker-entrypoint.sh "$$@" &
|
||||
|
||||
# 等待MongoDB服务启动
|
||||
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')"; do
|
||||
echo "Waiting for MongoDB to start..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# 执行初始化副本集的脚本
|
||||
mongo -u myusername -p mypassword --authenticationDatabase admin /data/initReplicaSet.js
|
||||
|
||||
# 等待docker-entrypoint.sh脚本执行的MongoDB服务进程
|
||||
wait $$!
|
||||
redis:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/redis:7.2-alpine
|
||||
container_name: redis
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
command: |
|
||||
redis-server --requirepass mypassword --loglevel warning --maxclients 10000 --appendonly yes --save 60 10 --maxmemory 4gb --maxmemory-policy noeviction
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', '-a', 'mypassword', 'ping']
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
fastgpt-minio:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/minio:RELEASE.2025-09-07T16-13-09Z
|
||||
container_name: fastgpt-minio
|
||||
restart: always
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 9001:9001
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
volumes:
|
||||
- ./fastgpt-minio:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.4 # git
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
depends_on:
|
||||
- mongo
|
||||
- sandbox
|
||||
- vectorDB
|
||||
restart: always
|
||||
environment:
|
||||
<<: [*x-share-db-config, *x-vec-config]
|
||||
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||
FE_DOMAIN:
|
||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||
DEFAULT_ROOT_PSW: 1234
|
||||
# 登录凭证密钥
|
||||
TOKEN_KEY: any
|
||||
# root的密钥,常用于升级时候的初始化请求
|
||||
ROOT_KEY: root_key
|
||||
# 文件阅读加密
|
||||
FILE_TOKEN_KEY: filetoken
|
||||
# 密钥加密key
|
||||
AES256_SECRET_KEY: fastgptkey
|
||||
|
||||
# plugin 地址
|
||||
PLUGIN_BASE_URL: http://fastgpt-plugin:3000
|
||||
PLUGIN_TOKEN: *x-plugin-auth-token
|
||||
# sandbox 地址
|
||||
SANDBOX_URL: http://sandbox:3000
|
||||
# AI Proxy 的地址,如果配了该地址,优先使用
|
||||
AIPROXY_API_ENDPOINT: http://aiproxy:3000
|
||||
# AI Proxy 的 Admin Token,与 AI Proxy 中的环境变量 ADMIN_KEY
|
||||
AIPROXY_API_TOKEN: *x-aiproxy-token
|
||||
|
||||
# 日志等级: debug, info, warn, error
|
||||
LOG_LEVEL: info
|
||||
STORE_LOG_LEVEL: warn
|
||||
# 工作流最大运行次数
|
||||
WORKFLOW_MAX_RUN_TIMES: 1000
|
||||
# 批量执行节点,最大输入长度
|
||||
WORKFLOW_MAX_LOOP_TIMES: 100
|
||||
# 对话文件过期天数
|
||||
CHAT_FILE_EXPIRE_TIME: 7
|
||||
# 服务器接收请求,最大大小,单位 MB
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
ports:
|
||||
- 3005:3000
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt-plugin:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.4
|
||||
container_name: fastgpt-plugin
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
<<: *x-share-db-config
|
||||
AUTH_TOKEN: *x-plugin-auth-token
|
||||
# 工具网络请求,最大请求和响应体
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
# 最大 API 请求体大小
|
||||
MAX_API_SIZE: 10
|
||||
depends_on:
|
||||
fastgpt-minio:
|
||||
condition: service_healthy
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.3.2
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
aiproxy_pg:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- fastgpt
|
||||
- aiproxy
|
||||
environment:
|
||||
# 对应 fastgpt 里的AIPROXY_API_TOKEN
|
||||
ADMIN_KEY: *x-aiproxy-token
|
||||
# 错误日志详情保存时间(小时)
|
||||
LOG_DETAIL_STORAGE_HOURS: 1
|
||||
# 数据库连接地址
|
||||
SQL_DSN: postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
# 最大重试次数
|
||||
RETRY_TIMES: 3
|
||||
# 不需要计费
|
||||
BILLING_ENABLED: false
|
||||
# 不需要严格检测模型
|
||||
DISABLE_MODEL_CONFIG: true
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/status']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
aiproxy_pg:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:0.8.0-pg15 # docker hub
|
||||
restart: unless-stopped
|
||||
container_name: aiproxy_pg
|
||||
volumes:
|
||||
- ./aiproxy_pg:/var/lib/postgresql/data
|
||||
networks:
|
||||
- aiproxy
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: aiproxy
|
||||
POSTGRES_PASSWORD: aiproxy
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'postgres', '-d', 'aiproxy']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks:
|
||||
fastgpt:
|
||||
aiproxy:
|
||||
vector:
|
||||
|
||||
|
|
@ -1,45 +1,23 @@
|
|||
# 用于部署的 docker-compose 文件:
|
||||
# - FastGPT 端口映射为 3000:3000
|
||||
# - FastGPT-mcp-server 端口映射 3005:3000
|
||||
# - 建议修改账密后再运行
|
||||
|
||||
# plugin auth token
|
||||
x-plugin-auth-token: &x-plugin-auth-token 'token'
|
||||
# aiproxy token
|
||||
x-aiproxy-token: &x-aiproxy-token 'token'
|
||||
# 数据库连接相关配置
|
||||
x-share-db-config: &x-share-db-config
|
||||
MONGODB_URI: mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
DB_MAX_LINK: 100
|
||||
REDIS_URL: redis://default:mypassword@redis:6379
|
||||
S3_EXTERNAL_BASE_URL: https://minio.com # S3 的公网访问地址
|
||||
S3_ENDPOINT: fastgpt-minio
|
||||
S3_PORT: 9000
|
||||
S3_USE_SSL: false
|
||||
S3_ACCESS_KEY: minioadmin
|
||||
S3_SECRET_KEY: minioadmin
|
||||
S3_PUBLIC_BUCKET: fastgpt-public # 公开读私有写桶
|
||||
S3_PRIVATE_BUCKET: fastgpt-private # 私有读写桶
|
||||
|
||||
# 向量库相关配置
|
||||
x-vec-config: &x-vec-config
|
||||
MILVUS_ADDRESS: http://milvusStandalone:19530
|
||||
MILVUS_TOKEN: none
|
||||
|
||||
# 数据库的默认账号和密码仅首次运行时设置有效
|
||||
# 如果修改了账号密码,记得改数据库和项目连接参数,别只改一处~
|
||||
# 该配置文件只是给快速启动,测试使用。正式使用,记得务必修改账号密码,以及调整合适的知识库参数,共享内存等。
|
||||
# 如何无法访问 dockerhub 和 git,可以用阿里云(阿里云没有arm包)
|
||||
|
||||
version: '3.3'
|
||||
services:
|
||||
# Vector DB
|
||||
milvus-minio:
|
||||
container_name: milvus-minio
|
||||
minio:
|
||||
container_name: minio
|
||||
image: minio/minio:RELEASE.2023-03-20T20-16-18Z
|
||||
environment:
|
||||
MINIO_ACCESS_KEY: minioadmin
|
||||
MINIO_SECRET_KEY: minioadmin
|
||||
ports:
|
||||
- '9001:9001'
|
||||
- '9000:9000'
|
||||
networks:
|
||||
- vector
|
||||
- fastgpt
|
||||
volumes:
|
||||
- ./milvus-minio:/minio_data
|
||||
- ./minio:/minio_data
|
||||
command: minio server /minio_data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
|
|
@ -47,8 +25,8 @@ services:
|
|||
timeout: 20s
|
||||
retries: 3
|
||||
# milvus
|
||||
milvus-etcd:
|
||||
container_name: milvus-etcd
|
||||
milvusEtcd:
|
||||
container_name: milvusEtcd
|
||||
image: quay.io/coreos/etcd:v3.5.5
|
||||
environment:
|
||||
- ETCD_AUTO_COMPACTION_MODE=revision
|
||||
|
|
@ -56,7 +34,7 @@ services:
|
|||
- ETCD_QUOTA_BACKEND_BYTES=4294967296
|
||||
- ETCD_SNAPSHOT_COUNT=50000
|
||||
networks:
|
||||
- vector
|
||||
- fastgpt
|
||||
volumes:
|
||||
- ./milvus/etcd:/etcd
|
||||
command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
|
||||
|
|
@ -65,18 +43,17 @@ services:
|
|||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
vectorDB:
|
||||
milvusStandalone:
|
||||
container_name: milvusStandalone
|
||||
image: milvusdb/milvus:v2.4.3
|
||||
command: ['milvus', 'run', 'standalone']
|
||||
security_opt:
|
||||
- seccomp:unconfined
|
||||
environment:
|
||||
ETCD_ENDPOINTS: milvus-etcd:2379
|
||||
MINIO_ADDRESS: milvus-minio:9000
|
||||
ETCD_ENDPOINTS: milvusEtcd:2379
|
||||
MINIO_ADDRESS: minio:9000
|
||||
networks:
|
||||
- fastgpt
|
||||
- vector
|
||||
volumes:
|
||||
- ./milvus/data:/var/lib/milvus
|
||||
healthcheck:
|
||||
|
|
@ -86,14 +63,15 @@ services:
|
|||
timeout: 20s
|
||||
retries: 3
|
||||
depends_on:
|
||||
- 'milvus-etcd'
|
||||
- 'milvus-minio'
|
||||
|
||||
- 'milvusEtcd'
|
||||
- 'minio'
|
||||
|
||||
mongo:
|
||||
image: mongo:5.0.18 # cpu 不支持 AVX 时候使用 4.4.29
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # 阿里云
|
||||
container_name: mongo
|
||||
restart: always
|
||||
ports:
|
||||
- 27017:27017
|
||||
networks:
|
||||
- fastgpt
|
||||
command: mongod --keyFile /data/mongodb.key --replSet rs0
|
||||
|
|
@ -102,12 +80,6 @@ services:
|
|||
- MONGO_INITDB_ROOT_PASSWORD=mypassword
|
||||
volumes:
|
||||
- ./mongo/data:/data/db
|
||||
healthcheck:
|
||||
test: ['CMD', 'mongo', '-u', 'myusername', '-p', 'mypassword', '--authenticationDatabase', 'admin', '--eval', "db.adminCommand('ping')"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
|
|
@ -141,6 +113,8 @@ services:
|
|||
redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: redis
|
||||
# ports:
|
||||
# - 6379:6379
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
|
|
@ -154,112 +128,88 @@ services:
|
|||
start_period: 30s
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
fastgpt-minio:
|
||||
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
|
||||
container_name: fastgpt-minio
|
||||
restart: always
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 9001:9001
|
||||
|
||||
# fastgpt
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.10-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.10-fix2 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.10-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.10-fix2 # 阿里云
|
||||
ports:
|
||||
- 3005:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
environment:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
volumes:
|
||||
- ./fastgpt-minio:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.14.4 # git
|
||||
image: ghcr.io/labring/fastgpt:v4.9.10-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.10-fix2 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
depends_on:
|
||||
- mongo
|
||||
- milvusStandalone
|
||||
- sandbox
|
||||
- vectorDB
|
||||
restart: always
|
||||
environment:
|
||||
<<: [*x-share-db-config, *x-vec-config]
|
||||
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||
FE_DOMAIN:
|
||||
- FE_DOMAIN=
|
||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||
DEFAULT_ROOT_PSW: 1234
|
||||
# 登录凭证密钥
|
||||
TOKEN_KEY: any
|
||||
# root的密钥,常用于升级时候的初始化请求
|
||||
ROOT_KEY: root_key
|
||||
# 文件阅读加密
|
||||
FILE_TOKEN_KEY: filetoken
|
||||
# 密钥加密key
|
||||
AES256_SECRET_KEY: fastgptkey
|
||||
|
||||
# plugin 地址
|
||||
PLUGIN_BASE_URL: http://fastgpt-plugin:3000
|
||||
PLUGIN_TOKEN: *x-plugin-auth-token
|
||||
# sandbox 地址
|
||||
SANDBOX_URL: http://sandbox:3000
|
||||
- DEFAULT_ROOT_PSW=1234
|
||||
# AI Proxy 的地址,如果配了该地址,优先使用
|
||||
AIPROXY_API_ENDPOINT: http://aiproxy:3000
|
||||
- AIPROXY_API_ENDPOINT=http://aiproxy:3000
|
||||
# AI Proxy 的 Admin Token,与 AI Proxy 中的环境变量 ADMIN_KEY
|
||||
AIPROXY_API_TOKEN: *x-aiproxy-token
|
||||
|
||||
- AIPROXY_API_TOKEN=aiproxy
|
||||
# 模型中转地址(如果用了 AI Proxy,下面 2 个就不需要了,旧版 OneAPI 用户,使用下面的变量)
|
||||
# - OPENAI_BASE_URL=http://oneapi:3000/v1
|
||||
# - CHAT_API_KEY=sk-fastgpt
|
||||
# 数据库最大连接数
|
||||
- DB_MAX_LINK=30
|
||||
# 登录凭证密钥
|
||||
- TOKEN_KEY=any
|
||||
# root的密钥,常用于升级时候的初始化请求
|
||||
- ROOT_KEY=root_key
|
||||
# 文件阅读加密
|
||||
- FILE_TOKEN_KEY=filetoken
|
||||
# MongoDB 连接参数. 用户名myusername,密码mypassword。
|
||||
- MONGODB_URI=mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
# zilliz 连接参数
|
||||
- MILVUS_ADDRESS=http://milvusStandalone:19530
|
||||
- MILVUS_TOKEN=none
|
||||
# Redis 地址
|
||||
- REDIS_URL=redis://default:mypassword@redis:6379
|
||||
# sandbox 地址
|
||||
- SANDBOX_URL=http://sandbox:3000
|
||||
# 日志等级: debug, info, warn, error
|
||||
LOG_LEVEL: info
|
||||
STORE_LOG_LEVEL: warn
|
||||
- LOG_LEVEL=info
|
||||
- STORE_LOG_LEVEL=warn
|
||||
# 工作流最大运行次数
|
||||
WORKFLOW_MAX_RUN_TIMES: 1000
|
||||
- WORKFLOW_MAX_RUN_TIMES=1000
|
||||
# 批量执行节点,最大输入长度
|
||||
WORKFLOW_MAX_LOOP_TIMES: 100
|
||||
- WORKFLOW_MAX_LOOP_TIMES=100
|
||||
# 自定义跨域,不配置时,默认都允许跨域(多个域名通过逗号分割)
|
||||
- ALLOWED_ORIGINS=
|
||||
# 是否开启IP限制,默认不开启
|
||||
- USE_IP_LIMIT=false
|
||||
# 对话文件过期天数
|
||||
CHAT_FILE_EXPIRE_TIME: 7
|
||||
# 服务器接收请求,最大大小,单位 MB
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
- CHAT_FILE_EXPIRE_TIME=7
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
ports:
|
||||
- 3005:3000
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt-plugin:
|
||||
image: ghcr.io/labring/fastgpt-plugin:v0.3.4
|
||||
container_name: fastgpt-plugin
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
<<: *x-share-db-config
|
||||
AUTH_TOKEN: *x-plugin-auth-token
|
||||
# 工具网络请求,最大请求和响应体
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
# 最大 API 请求体大小
|
||||
MAX_API_SIZE: 10
|
||||
depends_on:
|
||||
fastgpt-minio:
|
||||
condition: service_healthy
|
||||
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: ghcr.io/labring/aiproxy:v0.3.2
|
||||
image: ghcr.io/labring/aiproxy:v0.1.7
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.7 # 阿里云
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
|
|
@ -267,20 +217,19 @@ services:
|
|||
condition: service_healthy
|
||||
networks:
|
||||
- fastgpt
|
||||
- aiproxy
|
||||
environment:
|
||||
# 对应 fastgpt 里的AIPROXY_API_TOKEN
|
||||
ADMIN_KEY: *x-aiproxy-token
|
||||
- ADMIN_KEY=aiproxy
|
||||
# 错误日志详情保存时间(小时)
|
||||
LOG_DETAIL_STORAGE_HOURS: 1
|
||||
- LOG_DETAIL_STORAGE_HOURS=1
|
||||
# 数据库连接地址
|
||||
SQL_DSN: postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
- SQL_DSN=postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
# 最大重试次数
|
||||
RETRY_TIMES: 3
|
||||
- RETRY_TIMES=3
|
||||
# 不需要计费
|
||||
BILLING_ENABLED: false
|
||||
- BILLING_ENABLED=false
|
||||
# 不需要严格检测模型
|
||||
DISABLE_MODEL_CONFIG: true
|
||||
- DISABLE_MODEL_CONFIG=true
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/status']
|
||||
interval: 5s
|
||||
|
|
@ -288,12 +237,13 @@ services:
|
|||
retries: 10
|
||||
aiproxy_pg:
|
||||
image: pgvector/pgvector:0.8.0-pg15 # docker hub
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.8.0-pg15 # 阿里云
|
||||
restart: unless-stopped
|
||||
container_name: aiproxy_pg
|
||||
volumes:
|
||||
- ./aiproxy_pg:/var/lib/postgresql/data
|
||||
networks:
|
||||
- aiproxy
|
||||
- fastgpt
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
POSTGRES_USER: postgres
|
||||
|
|
@ -306,6 +256,3 @@ services:
|
|||
retries: 10
|
||||
networks:
|
||||
fastgpt:
|
||||
aiproxy:
|
||||
vector:
|
||||
|
||||
|
|
@ -1,36 +1,14 @@
|
|||
# 用于部署的 docker-compose 文件:
|
||||
# - FastGPT 端口映射为 3000:3000
|
||||
# - FastGPT-mcp-server 端口映射 3005:3000
|
||||
# - 建议修改账密后再运行
|
||||
|
||||
# plugin auth token
|
||||
x-plugin-auth-token: &x-plugin-auth-token 'token'
|
||||
# aiproxy token
|
||||
x-aiproxy-token: &x-aiproxy-token 'token'
|
||||
# 数据库连接相关配置
|
||||
x-share-db-config: &x-share-db-config
|
||||
MONGODB_URI: mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
DB_MAX_LINK: 100
|
||||
REDIS_URL: redis://default:mypassword@redis:6379
|
||||
S3_EXTERNAL_BASE_URL: https://minio.com # S3 的公网访问地址
|
||||
S3_ENDPOINT: fastgpt-minio
|
||||
S3_PORT: 9000
|
||||
S3_USE_SSL: false
|
||||
S3_ACCESS_KEY: minioadmin
|
||||
S3_SECRET_KEY: minioadmin
|
||||
S3_PUBLIC_BUCKET: fastgpt-public # 公开读私有写桶
|
||||
S3_PRIVATE_BUCKET: fastgpt-private # 私有读写桶
|
||||
|
||||
# 向量库相关配置
|
||||
x-vec-config: &x-vec-config
|
||||
OCEANBASE_URL: mysql://root%40tenantname:tenantpassword@ob:2881/test
|
||||
|
||||
# 数据库的默认账号和密码仅首次运行时设置有效
|
||||
# 如果修改了账号密码,记得改数据库和项目连接参数,别只改一处~
|
||||
# 该配置文件只是给快速启动,测试使用。正式使用,记得务必修改账号密码,以及调整合适的知识库参数,共享内存等。
|
||||
# 如何无法访问 dockerhub 和 git,可以用阿里云(阿里云没有arm包)
|
||||
|
||||
version: '3.3'
|
||||
services:
|
||||
# Vector DB
|
||||
vectorDB:
|
||||
image: oceanbase/oceanbase-ce:4.3.5-lts
|
||||
# vector db
|
||||
ob:
|
||||
image: oceanbase/oceanbase-ce:4.3.5-lts # docker hub
|
||||
# image: quay.io/oceanbase/oceanbase-ce:4.3.5-lts # 镜像
|
||||
container_name: ob
|
||||
restart: always
|
||||
# ports: # 生产环境建议不要暴露
|
||||
|
|
@ -40,8 +18,8 @@ services:
|
|||
environment:
|
||||
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
||||
- OB_SYS_PASSWORD=obsyspassword
|
||||
# 不同于传统数据库,OceanBase 数据库的账号包含更多字段,包括用户名、租户名和集群名。经典格式为"用户名@租户名#集群名"
|
||||
# 比如用mysql客户端连接时,根据本文件的默认配置,应该指定 "-uroot@tenantname"
|
||||
# 不同于传统数据库,OceanBase 数据库的账号包含更多字段,包括用户名、租户名和集群名。经典格式为“用户名@租户名#集群名”
|
||||
# 比如用mysql客户端连接时,根据本文件的默认配置,应该指定 “-uroot@tenantname”
|
||||
- OB_TENANT_NAME=tenantname
|
||||
- OB_TENANT_PASSWORD=tenantpassword
|
||||
# MODE分为MINI和NORMAL, 后者会最大程度使用主机资源
|
||||
|
|
@ -49,28 +27,28 @@ services:
|
|||
- OB_SERVER_IP=127.0.0.1
|
||||
# 更多环境变量配置见oceanbase官方文档: https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000002013494
|
||||
volumes:
|
||||
- ../ob/data:/root/ob
|
||||
- ../ob/config:/root/.obd/cluster
|
||||
configs:
|
||||
- source: init_sql
|
||||
target: /root/boot/init.d/init.sql
|
||||
- ./ob/data:/root/ob
|
||||
- ./ob/config:/root/.obd/cluster
|
||||
- ./init.sql:/root/boot/init.d/init.sql
|
||||
healthcheck:
|
||||
# obclient -h127.0.0.1 -P2881 -uroot@tenantname -ptenantpassword -e "SELECT 1;"
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
'obclient -h$${OB_SERVER_IP} -P2881 -uroot@$${OB_TENANT_NAME} -p$${OB_TENANT_PASSWORD} -e "SELECT 1;"',
|
||||
'CMD-SHELL',
|
||||
'obclient -h$${OB_SERVER_IP} -P2881 -uroot@$${OB_TENANT_NAME} -p$${OB_TENANT_PASSWORD} -e "SELECT 1;"'
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 1000
|
||||
start_period: 10s
|
||||
|
||||
|
||||
mongo:
|
||||
image: mongo:5.0.18 # cpu 不支持 AVX 时候使用 4.4.29
|
||||
image: mongo:5.0.18 # dockerhub
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # 阿里云
|
||||
# image: mongo:4.4.29 # cpu不支持AVX时候使用
|
||||
container_name: mongo
|
||||
restart: always
|
||||
# ports:
|
||||
# - 27017:27017
|
||||
networks:
|
||||
- fastgpt
|
||||
command: mongod --keyFile /data/mongodb.key --replSet rs0
|
||||
|
|
@ -79,12 +57,6 @@ services:
|
|||
- MONGO_INITDB_ROOT_PASSWORD=mypassword
|
||||
volumes:
|
||||
- ./mongo/data:/data/db
|
||||
healthcheck:
|
||||
test: ['CMD', 'mongo', '-u', 'myusername', '-p', 'mypassword', '--authenticationDatabase', 'admin', '--eval', "db.adminCommand('ping')"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
|
|
@ -118,125 +90,102 @@ services:
|
|||
redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: redis
|
||||
# ports:
|
||||
# - 6379:6379
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
command: |
|
||||
redis-server --requirepass mypassword --loglevel warning --maxclients 10000 --appendonly yes --save 60 10 --maxmemory 4gb --maxmemory-policy noeviction
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', '-a', 'mypassword', 'ping']
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
fastgpt-minio:
|
||||
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
|
||||
container_name: fastgpt-minio
|
||||
restart: always
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 9001:9001
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
volumes:
|
||||
- ./fastgpt-minio:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.14.4 # git
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
depends_on:
|
||||
- mongo
|
||||
- sandbox
|
||||
- vectorDB
|
||||
restart: always
|
||||
environment:
|
||||
<<: [*x-share-db-config, *x-vec-config]
|
||||
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||
FE_DOMAIN:
|
||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||
DEFAULT_ROOT_PSW: 1234
|
||||
# 登录凭证密钥
|
||||
TOKEN_KEY: any
|
||||
# root的密钥,常用于升级时候的初始化请求
|
||||
ROOT_KEY: root_key
|
||||
# 文件阅读加密
|
||||
FILE_TOKEN_KEY: filetoken
|
||||
# 密钥加密key
|
||||
AES256_SECRET_KEY: fastgptkey
|
||||
|
||||
# plugin 地址
|
||||
PLUGIN_BASE_URL: http://fastgpt-plugin:3000
|
||||
PLUGIN_TOKEN: *x-plugin-auth-token
|
||||
# sandbox 地址
|
||||
SANDBOX_URL: http://sandbox:3000
|
||||
# AI Proxy 的地址,如果配了该地址,优先使用
|
||||
AIPROXY_API_ENDPOINT: http://aiproxy:3000
|
||||
# AI Proxy 的 Admin Token,与 AI Proxy 中的环境变量 ADMIN_KEY
|
||||
AIPROXY_API_TOKEN: *x-aiproxy-token
|
||||
|
||||
# 日志等级: debug, info, warn, error
|
||||
LOG_LEVEL: info
|
||||
STORE_LOG_LEVEL: warn
|
||||
# 工作流最大运行次数
|
||||
WORKFLOW_MAX_RUN_TIMES: 1000
|
||||
# 批量执行节点,最大输入长度
|
||||
WORKFLOW_MAX_LOOP_TIMES: 100
|
||||
# 对话文件过期天数
|
||||
CHAT_FILE_EXPIRE_TIME: 7
|
||||
# 服务器接收请求,最大大小,单位 MB
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
# fastgpt
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.14.4
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.10-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.10-fix2 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.10-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.10-fix2 # 阿里云
|
||||
ports:
|
||||
- 3005:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt-plugin:
|
||||
image: ghcr.io/labring/fastgpt-plugin:v0.3.4
|
||||
container_name: fastgpt-plugin
|
||||
restart: always
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.9.10-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.10-fix2 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
<<: *x-share-db-config
|
||||
AUTH_TOKEN: *x-plugin-auth-token
|
||||
# 工具网络请求,最大请求和响应体
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
# 最大 API 请求体大小
|
||||
MAX_API_SIZE: 10
|
||||
depends_on:
|
||||
fastgpt-minio:
|
||||
mongo:
|
||||
condition: service_started
|
||||
ob:
|
||||
condition: service_healthy
|
||||
sandbox:
|
||||
condition: service_started
|
||||
restart: always
|
||||
environment:
|
||||
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||
- FE_DOMAIN=
|
||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||
- DEFAULT_ROOT_PSW=1234
|
||||
# # AI Proxy 的地址,如果配了该地址,优先使用
|
||||
- AIPROXY_API_ENDPOINT=http://aiproxy:3000
|
||||
# # AI Proxy 的 Admin Token,与 AI Proxy 中的环境变量 ADMIN_KEY
|
||||
- AIPROXY_API_TOKEN=aiproxy
|
||||
# 数据库最大连接数
|
||||
- DB_MAX_LINK=30
|
||||
# 登录凭证密钥
|
||||
- TOKEN_KEY=any
|
||||
# root的密钥,常用于升级时候的初始化请求
|
||||
- ROOT_KEY=root_key
|
||||
# 文件阅读加密
|
||||
- FILE_TOKEN_KEY=filetoken
|
||||
# MongoDB 连接参数. 用户名myusername,密码mypassword。
|
||||
- MONGODB_URI=mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
# OceanBase 向量库连接参数
|
||||
- OCEANBASE_URL=mysql://root%40tenantname:tenantpassword@ob:2881/test
|
||||
# Redis 连接参数
|
||||
- REDIS_URL=redis://default:mypassword@redis:6379
|
||||
# sandbox 地址
|
||||
- SANDBOX_URL=http://sandbox:3000
|
||||
# 日志等级: debug, info, warn, error
|
||||
- LOG_LEVEL=info
|
||||
- STORE_LOG_LEVEL=warn
|
||||
# 工作流最大运行次数
|
||||
- WORKFLOW_MAX_RUN_TIMES=1000
|
||||
# 批量执行节点,最大输入长度
|
||||
- WORKFLOW_MAX_LOOP_TIMES=100
|
||||
# 自定义跨域,不配置时,默认都允许跨域(多个域名通过逗号分割)
|
||||
- ALLOWED_ORIGINS=
|
||||
# 是否开启IP限制,默认不开启
|
||||
- USE_IP_LIMIT=false
|
||||
# 对话文件过期天数
|
||||
- CHAT_FILE_EXPIRE_TIME=7
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: ghcr.io/labring/aiproxy:v0.3.2
|
||||
image: ghcr.io/labring/aiproxy:v0.1.7
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.7 # 阿里云
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
|
|
@ -244,20 +193,19 @@ services:
|
|||
condition: service_healthy
|
||||
networks:
|
||||
- fastgpt
|
||||
- aiproxy
|
||||
environment:
|
||||
# 对应 fastgpt 里的AIPROXY_API_TOKEN
|
||||
ADMIN_KEY: *x-aiproxy-token
|
||||
- ADMIN_KEY=aiproxy
|
||||
# 错误日志详情保存时间(小时)
|
||||
LOG_DETAIL_STORAGE_HOURS: 1
|
||||
- LOG_DETAIL_STORAGE_HOURS=1
|
||||
# 数据库连接地址
|
||||
SQL_DSN: postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
- SQL_DSN=postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
# 最大重试次数
|
||||
RETRY_TIMES: 3
|
||||
- RETRY_TIMES=3
|
||||
# 不需要计费
|
||||
BILLING_ENABLED: false
|
||||
- BILLING_ENABLED=false
|
||||
# 不需要严格检测模型
|
||||
DISABLE_MODEL_CONFIG: true
|
||||
- DISABLE_MODEL_CONFIG=true
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/status']
|
||||
interval: 5s
|
||||
|
|
@ -265,12 +213,13 @@ services:
|
|||
retries: 10
|
||||
aiproxy_pg:
|
||||
image: pgvector/pgvector:0.8.0-pg15 # docker hub
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.8.0-pg15 # 阿里云
|
||||
restart: unless-stopped
|
||||
container_name: aiproxy_pg
|
||||
volumes:
|
||||
- ./aiproxy_pg:/var/lib/postgresql/data
|
||||
networks:
|
||||
- aiproxy
|
||||
- fastgpt
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
POSTGRES_USER: postgres
|
||||
|
|
@ -283,11 +232,3 @@ services:
|
|||
retries: 10
|
||||
networks:
|
||||
fastgpt:
|
||||
aiproxy:
|
||||
vector:
|
||||
configs:
|
||||
init_sql:
|
||||
name: init_sql
|
||||
content: |
|
||||
ALTER SYSTEM SET ob_vector_memory_limit_percentage = 30;
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
ALTER SYSTEM SET ob_vector_memory_limit_percentage = 30;
|
||||
|
||||
|
|
@ -1,37 +1,18 @@
|
|||
# 用于部署的 docker-compose 文件:
|
||||
# - FastGPT 端口映射为 3000:3000
|
||||
# - FastGPT-mcp-server 端口映射 3005:3000
|
||||
# - 建议修改账密后再运行
|
||||
|
||||
# plugin auth token
|
||||
x-plugin-auth-token: &x-plugin-auth-token 'token'
|
||||
# aiproxy token
|
||||
x-aiproxy-token: &x-aiproxy-token 'token'
|
||||
# 数据库连接相关配置
|
||||
x-share-db-config: &x-share-db-config
|
||||
MONGODB_URI: mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
DB_MAX_LINK: 100
|
||||
REDIS_URL: redis://default:mypassword@redis:6379
|
||||
S3_EXTERNAL_BASE_URL: https://minio.com # S3 的公网访问地址
|
||||
S3_ENDPOINT: fastgpt-minio
|
||||
S3_PORT: 9000
|
||||
S3_USE_SSL: false
|
||||
S3_ACCESS_KEY: minioadmin
|
||||
S3_SECRET_KEY: minioadmin
|
||||
S3_PUBLIC_BUCKET: fastgpt-public # 公开读私有写桶
|
||||
S3_PRIVATE_BUCKET: fastgpt-private # 私有读写桶
|
||||
|
||||
# 向量库相关配置
|
||||
x-vec-config: &x-vec-config
|
||||
PG_URL: postgresql://username:password@pg:5432/postgres
|
||||
# 数据库的默认账号和密码仅首次运行时设置有效
|
||||
# 如果修改了账号密码,记得改数据库和项目连接参数,别只改一处~
|
||||
# 该配置文件只是给快速启动,测试使用。正式使用,记得务必修改账号密码,以及调整合适的知识库参数,共享内存等。
|
||||
# 如何无法访问 dockerhub 和 git,可以用阿里云(阿里云没有arm包)
|
||||
|
||||
version: '3.3'
|
||||
services:
|
||||
# Vector DB
|
||||
vectorDB:
|
||||
image: pgvector/pgvector:0.8.0-pg15
|
||||
# db
|
||||
pg:
|
||||
image: pgvector/pgvector:0.8.0-pg15 # docker hub
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.8.0-pg15 # 阿里云
|
||||
container_name: pg
|
||||
restart: always
|
||||
# ports: # 生产环境建议不要暴露
|
||||
# - 5432:5432
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
|
|
@ -42,16 +23,18 @@ services:
|
|||
volumes:
|
||||
- ./pg/data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'username', '-d', 'postgres']
|
||||
test: ['CMD', 'pg_isready', '-U', 'postgres', '-d', 'postgres']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
|
||||
mongo:
|
||||
image: mongo:5.0.18 # cpu 不支持 AVX 时候使用 4.4.29
|
||||
image: mongo:5.0.18 # dockerhub
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # 阿里云
|
||||
# image: mongo:4.4.29 # cpu不支持AVX时候使用
|
||||
container_name: mongo
|
||||
restart: always
|
||||
# ports:
|
||||
# - 27017:27017
|
||||
networks:
|
||||
- fastgpt
|
||||
command: mongod --keyFile /data/mongodb.key --replSet rs0
|
||||
|
|
@ -60,12 +43,6 @@ services:
|
|||
- MONGO_INITDB_ROOT_PASSWORD=mypassword
|
||||
volumes:
|
||||
- ./mongo/data:/data/db
|
||||
healthcheck:
|
||||
test: ['CMD', 'mongo', '-u', 'myusername', '-p', 'mypassword', '--authenticationDatabase', 'admin', '--eval', "db.adminCommand('ping')"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
|
|
@ -96,9 +73,12 @@ services:
|
|||
|
||||
# 等待docker-entrypoint.sh脚本执行的MongoDB服务进程
|
||||
wait $$!
|
||||
|
||||
redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: redis
|
||||
# ports:
|
||||
# - 6379:6379
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
|
|
@ -112,112 +92,84 @@ services:
|
|||
start_period: 30s
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
fastgpt-minio:
|
||||
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
|
||||
container_name: fastgpt-minio
|
||||
restart: always
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 9001:9001
|
||||
|
||||
# fastgpt
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.10-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.10-fix2 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.10-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.10-fix2 # 阿里云
|
||||
ports:
|
||||
- 3005:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
environment:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
volumes:
|
||||
- ./fastgpt-minio:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.14.4 # git
|
||||
image: ghcr.io/labring/fastgpt:v4.9.10-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.10-fix2 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
depends_on:
|
||||
- mongo
|
||||
- pg
|
||||
- sandbox
|
||||
- vectorDB
|
||||
restart: always
|
||||
environment:
|
||||
<<: [*x-share-db-config, *x-vec-config]
|
||||
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||
FE_DOMAIN:
|
||||
- FE_DOMAIN=
|
||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||
DEFAULT_ROOT_PSW: 1234
|
||||
# 登录凭证密钥
|
||||
TOKEN_KEY: any
|
||||
# root的密钥,常用于升级时候的初始化请求
|
||||
ROOT_KEY: root_key
|
||||
# 文件阅读加密
|
||||
FILE_TOKEN_KEY: filetoken
|
||||
# 密钥加密key
|
||||
AES256_SECRET_KEY: fastgptkey
|
||||
|
||||
# plugin 地址
|
||||
PLUGIN_BASE_URL: http://fastgpt-plugin:3000
|
||||
PLUGIN_TOKEN: *x-plugin-auth-token
|
||||
# sandbox 地址
|
||||
SANDBOX_URL: http://sandbox:3000
|
||||
- DEFAULT_ROOT_PSW=1234
|
||||
# AI Proxy 的地址,如果配了该地址,优先使用
|
||||
AIPROXY_API_ENDPOINT: http://aiproxy:3000
|
||||
- AIPROXY_API_ENDPOINT=http://aiproxy:3000
|
||||
# AI Proxy 的 Admin Token,与 AI Proxy 中的环境变量 ADMIN_KEY
|
||||
AIPROXY_API_TOKEN: *x-aiproxy-token
|
||||
|
||||
- AIPROXY_API_TOKEN=aiproxy
|
||||
# 数据库最大连接数
|
||||
- DB_MAX_LINK=30
|
||||
# 登录凭证密钥
|
||||
- TOKEN_KEY=any
|
||||
# root的密钥,常用于升级时候的初始化请求
|
||||
- ROOT_KEY=root_key
|
||||
# 文件阅读加密
|
||||
- FILE_TOKEN_KEY=filetoken
|
||||
# MongoDB 连接参数. 用户名myusername,密码mypassword。
|
||||
- MONGODB_URI=mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
# pg 连接参数
|
||||
- PG_URL=postgresql://username:password@pg:5432/postgres
|
||||
# Redis 连接参数
|
||||
- REDIS_URL=redis://default:mypassword@redis:6379
|
||||
# sandbox 地址
|
||||
- SANDBOX_URL=http://sandbox:3000
|
||||
# 日志等级: debug, info, warn, error
|
||||
LOG_LEVEL: info
|
||||
STORE_LOG_LEVEL: warn
|
||||
- LOG_LEVEL=info
|
||||
- STORE_LOG_LEVEL=warn
|
||||
# 工作流最大运行次数
|
||||
WORKFLOW_MAX_RUN_TIMES: 1000
|
||||
- WORKFLOW_MAX_RUN_TIMES=1000
|
||||
# 批量执行节点,最大输入长度
|
||||
WORKFLOW_MAX_LOOP_TIMES: 100
|
||||
- WORKFLOW_MAX_LOOP_TIMES=100
|
||||
# 自定义跨域,不配置时,默认都允许跨域(多个域名通过逗号分割)
|
||||
- ALLOWED_ORIGINS=
|
||||
# 是否开启IP限制,默认不开启
|
||||
- USE_IP_LIMIT=false
|
||||
# 对话文件过期天数
|
||||
CHAT_FILE_EXPIRE_TIME: 7
|
||||
# 服务器接收请求,最大大小,单位 MB
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
- CHAT_FILE_EXPIRE_TIME=7
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
ports:
|
||||
- 3005:3000
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt-plugin:
|
||||
image: ghcr.io/labring/fastgpt-plugin:v0.3.4
|
||||
container_name: fastgpt-plugin
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
<<: *x-share-db-config
|
||||
AUTH_TOKEN: *x-plugin-auth-token
|
||||
# 工具网络请求,最大请求和响应体
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
# 最大 API 请求体大小
|
||||
MAX_API_SIZE: 10
|
||||
depends_on:
|
||||
fastgpt-minio:
|
||||
condition: service_healthy
|
||||
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: ghcr.io/labring/aiproxy:v0.3.2
|
||||
image: ghcr.io/labring/aiproxy:v0.1.7
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.7 # 阿里云
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
|
|
@ -225,20 +177,19 @@ services:
|
|||
condition: service_healthy
|
||||
networks:
|
||||
- fastgpt
|
||||
- aiproxy
|
||||
environment:
|
||||
# 对应 fastgpt 里的AIPROXY_API_TOKEN
|
||||
ADMIN_KEY: *x-aiproxy-token
|
||||
- ADMIN_KEY=aiproxy
|
||||
# 错误日志详情保存时间(小时)
|
||||
LOG_DETAIL_STORAGE_HOURS: 1
|
||||
- LOG_DETAIL_STORAGE_HOURS=1
|
||||
# 数据库连接地址
|
||||
SQL_DSN: postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
- SQL_DSN=postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
# 最大重试次数
|
||||
RETRY_TIMES: 3
|
||||
- RETRY_TIMES=3
|
||||
# 不需要计费
|
||||
BILLING_ENABLED: false
|
||||
- BILLING_ENABLED=false
|
||||
# 不需要严格检测模型
|
||||
DISABLE_MODEL_CONFIG: true
|
||||
- DISABLE_MODEL_CONFIG=true
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/status']
|
||||
interval: 5s
|
||||
|
|
@ -246,12 +197,13 @@ services:
|
|||
retries: 10
|
||||
aiproxy_pg:
|
||||
image: pgvector/pgvector:0.8.0-pg15 # docker hub
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.8.0-pg15 # 阿里云
|
||||
restart: unless-stopped
|
||||
container_name: aiproxy_pg
|
||||
volumes:
|
||||
- ./aiproxy_pg:/var/lib/postgresql/data
|
||||
networks:
|
||||
- aiproxy
|
||||
- fastgpt
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
POSTGRES_USER: postgres
|
||||
|
|
@ -264,6 +216,3 @@ services:
|
|||
retries: 10
|
||||
networks:
|
||||
fastgpt:
|
||||
aiproxy:
|
||||
vector:
|
||||
|
||||
|
|
@ -1,40 +1,18 @@
|
|||
# 用于部署的 docker-compose 文件:
|
||||
# - FastGPT 端口映射为 3000:3000
|
||||
# - FastGPT-mcp-server 端口映射 3005:3000
|
||||
# - 建议修改账密后再运行
|
||||
|
||||
# plugin auth token
|
||||
x-plugin-auth-token: &x-plugin-auth-token 'token'
|
||||
# aiproxy token
|
||||
x-aiproxy-token: &x-aiproxy-token 'token'
|
||||
# 数据库连接相关配置
|
||||
x-share-db-config: &x-share-db-config
|
||||
MONGODB_URI: mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
DB_MAX_LINK: 100
|
||||
REDIS_URL: redis://default:mypassword@redis:6379
|
||||
S3_EXTERNAL_BASE_URL: https://minio.com # S3 的公网访问地址
|
||||
S3_ENDPOINT: fastgpt-minio
|
||||
S3_PORT: 9000
|
||||
S3_USE_SSL: false
|
||||
S3_ACCESS_KEY: minioadmin
|
||||
S3_SECRET_KEY: minioadmin
|
||||
S3_PUBLIC_BUCKET: fastgpt-public # 公开读私有写桶
|
||||
S3_PRIVATE_BUCKET: fastgpt-private # 私有读写桶
|
||||
|
||||
# 向量库相关配置
|
||||
x-vec-config: &x-vec-config
|
||||
MILVUS_ADDRESS: zilliz_cloud_address
|
||||
MILVUS_TOKEN: zilliz_cloud_token
|
||||
# 数据库的默认账号和密码仅首次运行时设置有效
|
||||
# 如果修改了账号密码,记得改数据库和项目连接参数,别只改一处~
|
||||
# 该配置文件只是给快速启动,测试使用。正式使用,记得务必修改账号密码,以及调整合适的知识库参数,共享内存等。
|
||||
# 如何无法访问 dockerhub 和 git,可以用阿里云(阿里云没有arm包)
|
||||
|
||||
version: '3.3'
|
||||
services:
|
||||
# Vector DB
|
||||
|
||||
|
||||
mongo:
|
||||
image: mongo:5.0.18 # cpu 不支持 AVX 时候使用 4.4.29
|
||||
image: mongo:5.0.18 # dockerhub
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # 阿里云
|
||||
# image: mongo:4.4.29 # cpu不支持AVX时候使用
|
||||
container_name: mongo
|
||||
restart: always
|
||||
ports:
|
||||
- 27017:27017
|
||||
networks:
|
||||
- fastgpt
|
||||
command: mongod --keyFile /data/mongodb.key --replSet rs0
|
||||
|
|
@ -43,12 +21,6 @@ services:
|
|||
- MONGO_INITDB_ROOT_PASSWORD=mypassword
|
||||
volumes:
|
||||
- ./mongo/data:/data/db
|
||||
healthcheck:
|
||||
test: ['CMD', 'mongo', '-u', 'myusername', '-p', 'mypassword', '--authenticationDatabase', 'admin', '--eval', "db.adminCommand('ping')"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
|
|
@ -82,6 +54,8 @@ services:
|
|||
redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: redis
|
||||
# ports:
|
||||
# - 6379:6379
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
|
|
@ -95,30 +69,29 @@ services:
|
|||
start_period: 30s
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
fastgpt-minio:
|
||||
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
|
||||
container_name: fastgpt-minio
|
||||
restart: always
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 9001:9001
|
||||
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.10-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.10-fix2 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.10-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.10-fix2 # 阿里云
|
||||
ports:
|
||||
- 3005:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
environment:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
volumes:
|
||||
- ./fastgpt-minio:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.14.4 # git
|
||||
image: ghcr.io/labring/fastgpt:v4.9.10-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.10-fix2 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
|
|
@ -126,81 +99,56 @@ services:
|
|||
depends_on:
|
||||
- mongo
|
||||
- sandbox
|
||||
- vectorDB
|
||||
restart: always
|
||||
environment:
|
||||
<<: [*x-share-db-config, *x-vec-config]
|
||||
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||
FE_DOMAIN:
|
||||
- FE_DOMAIN=
|
||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||
DEFAULT_ROOT_PSW: 1234
|
||||
# 登录凭证密钥
|
||||
TOKEN_KEY: any
|
||||
# root的密钥,常用于升级时候的初始化请求
|
||||
ROOT_KEY: root_key
|
||||
# 文件阅读加密
|
||||
FILE_TOKEN_KEY: filetoken
|
||||
# 密钥加密key
|
||||
AES256_SECRET_KEY: fastgptkey
|
||||
|
||||
# plugin 地址
|
||||
PLUGIN_BASE_URL: http://fastgpt-plugin:3000
|
||||
PLUGIN_TOKEN: *x-plugin-auth-token
|
||||
# sandbox 地址
|
||||
SANDBOX_URL: http://sandbox:3000
|
||||
- DEFAULT_ROOT_PSW=1234
|
||||
# AI Proxy 的地址,如果配了该地址,优先使用
|
||||
AIPROXY_API_ENDPOINT: http://aiproxy:3000
|
||||
- AIPROXY_API_ENDPOINT=http://aiproxy:3000
|
||||
# AI Proxy 的 Admin Token,与 AI Proxy 中的环境变量 ADMIN_KEY
|
||||
AIPROXY_API_TOKEN: *x-aiproxy-token
|
||||
|
||||
- AIPROXY_API_TOKEN=aiproxy
|
||||
# 模型中转地址(如果用了 AI Proxy,下面 2 个就不需要了,旧版 OneAPI 用户,使用下面的变量)
|
||||
# - OPENAI_BASE_URL=http://oneapi:3000/v1
|
||||
# - CHAT_API_KEY=sk-fastgpt
|
||||
# 数据库最大连接数
|
||||
- DB_MAX_LINK=30
|
||||
# 登录凭证密钥
|
||||
- TOKEN_KEY=any
|
||||
# root的密钥,常用于升级时候的初始化请求
|
||||
- ROOT_KEY=root_key
|
||||
# 文件阅读加密
|
||||
- FILE_TOKEN_KEY=filetoken
|
||||
# MongoDB 连接参数. 用户名myusername,密码mypassword。
|
||||
- MONGODB_URI=mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
# Redis 连接参数
|
||||
- REDIS_URI=redis://default:mypassword@redis:6379
|
||||
# zilliz 连接参数
|
||||
- MILVUS_ADDRESS=zilliz_cloud_address
|
||||
- MILVUS_TOKEN=zilliz_cloud_token
|
||||
# sandbox 地址
|
||||
- SANDBOX_URL=http://sandbox:3000
|
||||
# 日志等级: debug, info, warn, error
|
||||
LOG_LEVEL: info
|
||||
STORE_LOG_LEVEL: warn
|
||||
- LOG_LEVEL=info
|
||||
- STORE_LOG_LEVEL=warn
|
||||
# 工作流最大运行次数
|
||||
WORKFLOW_MAX_RUN_TIMES: 1000
|
||||
- WORKFLOW_MAX_RUN_TIMES=1000
|
||||
# 批量执行节点,最大输入长度
|
||||
WORKFLOW_MAX_LOOP_TIMES: 100
|
||||
- WORKFLOW_MAX_LOOP_TIMES=100
|
||||
# 自定义跨域,不配置时,默认都允许跨域(多个域名通过逗号分割)
|
||||
- ALLOWED_ORIGINS=
|
||||
# 是否开启IP限制,默认不开启
|
||||
- USE_IP_LIMIT=false
|
||||
# 对话文件过期天数
|
||||
CHAT_FILE_EXPIRE_TIME: 7
|
||||
# 服务器接收请求,最大大小,单位 MB
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
- CHAT_FILE_EXPIRE_TIME=7
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
ports:
|
||||
- 3005:3000
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt-plugin:
|
||||
image: ghcr.io/labring/fastgpt-plugin:v0.3.4
|
||||
container_name: fastgpt-plugin
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
<<: *x-share-db-config
|
||||
AUTH_TOKEN: *x-plugin-auth-token
|
||||
# 工具网络请求,最大请求和响应体
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
# 最大 API 请求体大小
|
||||
MAX_API_SIZE: 10
|
||||
depends_on:
|
||||
fastgpt-minio:
|
||||
condition: service_healthy
|
||||
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: ghcr.io/labring/aiproxy:v0.3.2
|
||||
image: ghcr.io/labring/aiproxy:v0.1.7
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.7 # 阿里云
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
|
|
@ -208,20 +156,19 @@ services:
|
|||
condition: service_healthy
|
||||
networks:
|
||||
- fastgpt
|
||||
- aiproxy
|
||||
environment:
|
||||
# 对应 fastgpt 里的AIPROXY_API_TOKEN
|
||||
ADMIN_KEY: *x-aiproxy-token
|
||||
- ADMIN_KEY=aiproxy
|
||||
# 错误日志详情保存时间(小时)
|
||||
LOG_DETAIL_STORAGE_HOURS: 1
|
||||
- LOG_DETAIL_STORAGE_HOURS=1
|
||||
# 数据库连接地址
|
||||
SQL_DSN: postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
- SQL_DSN=postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
# 最大重试次数
|
||||
RETRY_TIMES: 3
|
||||
- RETRY_TIMES=3
|
||||
# 不需要计费
|
||||
BILLING_ENABLED: false
|
||||
- BILLING_ENABLED=false
|
||||
# 不需要严格检测模型
|
||||
DISABLE_MODEL_CONFIG: true
|
||||
- DISABLE_MODEL_CONFIG=true
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/status']
|
||||
interval: 5s
|
||||
|
|
@ -229,12 +176,13 @@ services:
|
|||
retries: 10
|
||||
aiproxy_pg:
|
||||
image: pgvector/pgvector:0.8.0-pg15 # docker hub
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.8.0-pg15 # 阿里云
|
||||
restart: unless-stopped
|
||||
container_name: aiproxy_pg
|
||||
volumes:
|
||||
- ./aiproxy_pg:/var/lib/postgresql/data
|
||||
networks:
|
||||
- aiproxy
|
||||
- fastgpt
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
POSTGRES_USER: postgres
|
||||
|
|
@ -247,6 +195,3 @@ services:
|
|||
retries: 10
|
||||
networks:
|
||||
fastgpt:
|
||||
aiproxy:
|
||||
vector:
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
cp ./docker-compose /usr/local/bin/docker-compose
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
|
|
@ -1,311 +0,0 @@
|
|||
# 用于部署的 docker-compose 文件:
|
||||
# - FastGPT 端口映射为 3000:3000
|
||||
# - FastGPT-mcp-server 端口映射 3005:3000
|
||||
# - 建议修改账密后再运行
|
||||
|
||||
# plugin auth token
|
||||
x-plugin-auth-token: &x-plugin-auth-token 'token'
|
||||
# aiproxy token
|
||||
x-aiproxy-token: &x-aiproxy-token 'token'
|
||||
# 数据库连接相关配置
|
||||
x-share-db-config: &x-share-db-config
|
||||
MONGODB_URI: mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
DB_MAX_LINK: 100
|
||||
REDIS_URL: redis://default:mypassword@redis:6379
|
||||
S3_EXTERNAL_BASE_URL: https://minio.com # S3 的公网访问地址
|
||||
S3_ENDPOINT: fastgpt-minio
|
||||
S3_PORT: 9000
|
||||
S3_USE_SSL: false
|
||||
S3_ACCESS_KEY: minioadmin
|
||||
S3_SECRET_KEY: minioadmin
|
||||
S3_PUBLIC_BUCKET: fastgpt-public # 公开读私有写桶
|
||||
S3_PRIVATE_BUCKET: fastgpt-private # 私有读写桶
|
||||
|
||||
# 向量库相关配置
|
||||
x-vec-config: &x-vec-config
|
||||
MILVUS_ADDRESS: http://milvusStandalone:19530
|
||||
MILVUS_TOKEN: none
|
||||
|
||||
|
||||
version: '3.3'
|
||||
services:
|
||||
# Vector DB
|
||||
milvus-minio:
|
||||
container_name: milvus-minio
|
||||
image: minio/minio:RELEASE.2023-03-20T20-16-18Z
|
||||
environment:
|
||||
MINIO_ACCESS_KEY: minioadmin
|
||||
MINIO_SECRET_KEY: minioadmin
|
||||
networks:
|
||||
- vector
|
||||
volumes:
|
||||
- ./milvus-minio:/minio_data
|
||||
command: minio server /minio_data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
# milvus
|
||||
milvus-etcd:
|
||||
container_name: milvus-etcd
|
||||
image: quay.io/coreos/etcd:v3.5.5
|
||||
environment:
|
||||
- ETCD_AUTO_COMPACTION_MODE=revision
|
||||
- ETCD_AUTO_COMPACTION_RETENTION=1000
|
||||
- ETCD_QUOTA_BACKEND_BYTES=4294967296
|
||||
- ETCD_SNAPSHOT_COUNT=50000
|
||||
networks:
|
||||
- vector
|
||||
volumes:
|
||||
- ./milvus/etcd:/etcd
|
||||
command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
|
||||
healthcheck:
|
||||
test: ['CMD', 'etcdctl', 'endpoint', 'health']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
vectorDB:
|
||||
container_name: milvusStandalone
|
||||
image: milvusdb/milvus:v2.4.3
|
||||
command: ['milvus', 'run', 'standalone']
|
||||
security_opt:
|
||||
- seccomp:unconfined
|
||||
environment:
|
||||
ETCD_ENDPOINTS: milvus-etcd:2379
|
||||
MINIO_ADDRESS: milvus-minio:9000
|
||||
networks:
|
||||
- fastgpt
|
||||
- vector
|
||||
volumes:
|
||||
- ./milvus/data:/var/lib/milvus
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9091/healthz']
|
||||
interval: 30s
|
||||
start_period: 90s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
depends_on:
|
||||
- 'milvus-etcd'
|
||||
- 'milvus-minio'
|
||||
|
||||
|
||||
mongo:
|
||||
image: mongo:5.0.18 # cpu 不支持 AVX 时候使用 4.4.29
|
||||
container_name: mongo
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
command: mongod --keyFile /data/mongodb.key --replSet rs0
|
||||
environment:
|
||||
- MONGO_INITDB_ROOT_USERNAME=myusername
|
||||
- MONGO_INITDB_ROOT_PASSWORD=mypassword
|
||||
volumes:
|
||||
- ./mongo/data:/data/db
|
||||
healthcheck:
|
||||
test: ['CMD', 'mongo', '-u', 'myusername', '-p', 'mypassword', '--authenticationDatabase', 'admin', '--eval', "db.adminCommand('ping')"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
openssl rand -base64 128 > /data/mongodb.key
|
||||
chmod 400 /data/mongodb.key
|
||||
chown 999:999 /data/mongodb.key
|
||||
echo 'const isInited = rs.status().ok === 1
|
||||
if(!isInited){
|
||||
rs.initiate({
|
||||
_id: "rs0",
|
||||
members: [
|
||||
{ _id: 0, host: "mongo:27017" }
|
||||
]
|
||||
})
|
||||
}' > /data/initReplicaSet.js
|
||||
# 启动MongoDB服务
|
||||
exec docker-entrypoint.sh "$$@" &
|
||||
|
||||
# 等待MongoDB服务启动
|
||||
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')"; do
|
||||
echo "Waiting for MongoDB to start..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# 执行初始化副本集的脚本
|
||||
mongo -u myusername -p mypassword --authenticationDatabase admin /data/initReplicaSet.js
|
||||
|
||||
# 等待docker-entrypoint.sh脚本执行的MongoDB服务进程
|
||||
wait $$!
|
||||
redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: redis
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
command: |
|
||||
redis-server --requirepass mypassword --loglevel warning --maxclients 10000 --appendonly yes --save 60 10 --maxmemory 4gb --maxmemory-policy noeviction
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', '-a', 'mypassword', 'ping']
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
fastgpt-minio:
|
||||
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
|
||||
container_name: fastgpt-minio
|
||||
restart: always
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 9001:9001
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
volumes:
|
||||
- ./fastgpt-minio:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.14.4 # git
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
depends_on:
|
||||
- mongo
|
||||
- sandbox
|
||||
- vectorDB
|
||||
restart: always
|
||||
environment:
|
||||
<<: [*x-share-db-config, *x-vec-config]
|
||||
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||
FE_DOMAIN:
|
||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||
DEFAULT_ROOT_PSW: 1234
|
||||
# 登录凭证密钥
|
||||
TOKEN_KEY: any
|
||||
# root的密钥,常用于升级时候的初始化请求
|
||||
ROOT_KEY: root_key
|
||||
# 文件阅读加密
|
||||
FILE_TOKEN_KEY: filetoken
|
||||
# 密钥加密key
|
||||
AES256_SECRET_KEY: fastgptkey
|
||||
|
||||
# plugin 地址
|
||||
PLUGIN_BASE_URL: http://fastgpt-plugin:3000
|
||||
PLUGIN_TOKEN: *x-plugin-auth-token
|
||||
# sandbox 地址
|
||||
SANDBOX_URL: http://sandbox:3000
|
||||
# AI Proxy 的地址,如果配了该地址,优先使用
|
||||
AIPROXY_API_ENDPOINT: http://aiproxy:3000
|
||||
# AI Proxy 的 Admin Token,与 AI Proxy 中的环境变量 ADMIN_KEY
|
||||
AIPROXY_API_TOKEN: *x-aiproxy-token
|
||||
|
||||
# 日志等级: debug, info, warn, error
|
||||
LOG_LEVEL: info
|
||||
STORE_LOG_LEVEL: warn
|
||||
# 工作流最大运行次数
|
||||
WORKFLOW_MAX_RUN_TIMES: 1000
|
||||
# 批量执行节点,最大输入长度
|
||||
WORKFLOW_MAX_LOOP_TIMES: 100
|
||||
# 对话文件过期天数
|
||||
CHAT_FILE_EXPIRE_TIME: 7
|
||||
# 服务器接收请求,最大大小,单位 MB
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
ports:
|
||||
- 3005:3000
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt-plugin:
|
||||
image: ghcr.io/labring/fastgpt-plugin:v0.3.4
|
||||
container_name: fastgpt-plugin
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
<<: *x-share-db-config
|
||||
AUTH_TOKEN: *x-plugin-auth-token
|
||||
# 工具网络请求,最大请求和响应体
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
# 最大 API 请求体大小
|
||||
MAX_API_SIZE: 10
|
||||
depends_on:
|
||||
fastgpt-minio:
|
||||
condition: service_healthy
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: ghcr.io/labring/aiproxy:v0.3.2
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
aiproxy_pg:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- fastgpt
|
||||
- aiproxy
|
||||
environment:
|
||||
# 对应 fastgpt 里的AIPROXY_API_TOKEN
|
||||
ADMIN_KEY: *x-aiproxy-token
|
||||
# 错误日志详情保存时间(小时)
|
||||
LOG_DETAIL_STORAGE_HOURS: 1
|
||||
# 数据库连接地址
|
||||
SQL_DSN: postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
# 最大重试次数
|
||||
RETRY_TIMES: 3
|
||||
# 不需要计费
|
||||
BILLING_ENABLED: false
|
||||
# 不需要严格检测模型
|
||||
DISABLE_MODEL_CONFIG: true
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/status']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
aiproxy_pg:
|
||||
image: pgvector/pgvector:0.8.0-pg15 # docker hub
|
||||
restart: unless-stopped
|
||||
container_name: aiproxy_pg
|
||||
volumes:
|
||||
- ./aiproxy_pg:/var/lib/postgresql/data
|
||||
networks:
|
||||
- aiproxy
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: aiproxy
|
||||
POSTGRES_PASSWORD: aiproxy
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'postgres', '-d', 'aiproxy']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks:
|
||||
fastgpt:
|
||||
aiproxy:
|
||||
vector:
|
||||
|
||||
|
|
@ -1,293 +0,0 @@
|
|||
# 用于部署的 docker-compose 文件:
|
||||
# - FastGPT 端口映射为 3000:3000
|
||||
# - FastGPT-mcp-server 端口映射 3005:3000
|
||||
# - 建议修改账密后再运行
|
||||
|
||||
# plugin auth token
|
||||
x-plugin-auth-token: &x-plugin-auth-token 'token'
|
||||
# aiproxy token
|
||||
x-aiproxy-token: &x-aiproxy-token 'token'
|
||||
# 数据库连接相关配置
|
||||
x-share-db-config: &x-share-db-config
|
||||
MONGODB_URI: mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
DB_MAX_LINK: 100
|
||||
REDIS_URL: redis://default:mypassword@redis:6379
|
||||
S3_EXTERNAL_BASE_URL: https://minio.com # S3 的公网访问地址
|
||||
S3_ENDPOINT: fastgpt-minio
|
||||
S3_PORT: 9000
|
||||
S3_USE_SSL: false
|
||||
S3_ACCESS_KEY: minioadmin
|
||||
S3_SECRET_KEY: minioadmin
|
||||
S3_PUBLIC_BUCKET: fastgpt-public # 公开读私有写桶
|
||||
S3_PRIVATE_BUCKET: fastgpt-private # 私有读写桶
|
||||
|
||||
# 向量库相关配置
|
||||
x-vec-config: &x-vec-config
|
||||
OCEANBASE_URL: mysql://root%40tenantname:tenantpassword@ob:2881/test
|
||||
|
||||
|
||||
version: '3.3'
|
||||
services:
|
||||
# Vector DB
|
||||
vectorDB:
|
||||
image: oceanbase/oceanbase-ce:4.3.5-lts
|
||||
container_name: ob
|
||||
restart: always
|
||||
# ports: # 生产环境建议不要暴露
|
||||
# - 2881:2881
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
||||
- OB_SYS_PASSWORD=obsyspassword
|
||||
# 不同于传统数据库,OceanBase 数据库的账号包含更多字段,包括用户名、租户名和集群名。经典格式为"用户名@租户名#集群名"
|
||||
# 比如用mysql客户端连接时,根据本文件的默认配置,应该指定 "-uroot@tenantname"
|
||||
- OB_TENANT_NAME=tenantname
|
||||
- OB_TENANT_PASSWORD=tenantpassword
|
||||
# MODE分为MINI和NORMAL, 后者会最大程度使用主机资源
|
||||
- MODE=MINI
|
||||
- OB_SERVER_IP=127.0.0.1
|
||||
# 更多环境变量配置见oceanbase官方文档: https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000002013494
|
||||
volumes:
|
||||
- ../ob/data:/root/ob
|
||||
- ../ob/config:/root/.obd/cluster
|
||||
configs:
|
||||
- source: init_sql
|
||||
target: /root/boot/init.d/init.sql
|
||||
healthcheck:
|
||||
# obclient -h127.0.0.1 -P2881 -uroot@tenantname -ptenantpassword -e "SELECT 1;"
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
'obclient -h$${OB_SERVER_IP} -P2881 -uroot@$${OB_TENANT_NAME} -p$${OB_TENANT_PASSWORD} -e "SELECT 1;"',
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 1000
|
||||
start_period: 10s
|
||||
|
||||
|
||||
mongo:
|
||||
image: mongo:5.0.18 # cpu 不支持 AVX 时候使用 4.4.29
|
||||
container_name: mongo
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
command: mongod --keyFile /data/mongodb.key --replSet rs0
|
||||
environment:
|
||||
- MONGO_INITDB_ROOT_USERNAME=myusername
|
||||
- MONGO_INITDB_ROOT_PASSWORD=mypassword
|
||||
volumes:
|
||||
- ./mongo/data:/data/db
|
||||
healthcheck:
|
||||
test: ['CMD', 'mongo', '-u', 'myusername', '-p', 'mypassword', '--authenticationDatabase', 'admin', '--eval', "db.adminCommand('ping')"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
openssl rand -base64 128 > /data/mongodb.key
|
||||
chmod 400 /data/mongodb.key
|
||||
chown 999:999 /data/mongodb.key
|
||||
echo 'const isInited = rs.status().ok === 1
|
||||
if(!isInited){
|
||||
rs.initiate({
|
||||
_id: "rs0",
|
||||
members: [
|
||||
{ _id: 0, host: "mongo:27017" }
|
||||
]
|
||||
})
|
||||
}' > /data/initReplicaSet.js
|
||||
# 启动MongoDB服务
|
||||
exec docker-entrypoint.sh "$$@" &
|
||||
|
||||
# 等待MongoDB服务启动
|
||||
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')"; do
|
||||
echo "Waiting for MongoDB to start..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# 执行初始化副本集的脚本
|
||||
mongo -u myusername -p mypassword --authenticationDatabase admin /data/initReplicaSet.js
|
||||
|
||||
# 等待docker-entrypoint.sh脚本执行的MongoDB服务进程
|
||||
wait $$!
|
||||
redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: redis
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
command: |
|
||||
redis-server --requirepass mypassword --loglevel warning --maxclients 10000 --appendonly yes --save 60 10 --maxmemory 4gb --maxmemory-policy noeviction
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', '-a', 'mypassword', 'ping']
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
fastgpt-minio:
|
||||
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
|
||||
container_name: fastgpt-minio
|
||||
restart: always
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 9001:9001
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
volumes:
|
||||
- ./fastgpt-minio:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.14.4 # git
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
depends_on:
|
||||
- mongo
|
||||
- sandbox
|
||||
- vectorDB
|
||||
restart: always
|
||||
environment:
|
||||
<<: [*x-share-db-config, *x-vec-config]
|
||||
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||
FE_DOMAIN:
|
||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||
DEFAULT_ROOT_PSW: 1234
|
||||
# 登录凭证密钥
|
||||
TOKEN_KEY: any
|
||||
# root的密钥,常用于升级时候的初始化请求
|
||||
ROOT_KEY: root_key
|
||||
# 文件阅读加密
|
||||
FILE_TOKEN_KEY: filetoken
|
||||
# 密钥加密key
|
||||
AES256_SECRET_KEY: fastgptkey
|
||||
|
||||
# plugin 地址
|
||||
PLUGIN_BASE_URL: http://fastgpt-plugin:3000
|
||||
PLUGIN_TOKEN: *x-plugin-auth-token
|
||||
# sandbox 地址
|
||||
SANDBOX_URL: http://sandbox:3000
|
||||
# AI Proxy 的地址,如果配了该地址,优先使用
|
||||
AIPROXY_API_ENDPOINT: http://aiproxy:3000
|
||||
# AI Proxy 的 Admin Token,与 AI Proxy 中的环境变量 ADMIN_KEY
|
||||
AIPROXY_API_TOKEN: *x-aiproxy-token
|
||||
|
||||
# 日志等级: debug, info, warn, error
|
||||
LOG_LEVEL: info
|
||||
STORE_LOG_LEVEL: warn
|
||||
# 工作流最大运行次数
|
||||
WORKFLOW_MAX_RUN_TIMES: 1000
|
||||
# 批量执行节点,最大输入长度
|
||||
WORKFLOW_MAX_LOOP_TIMES: 100
|
||||
# 对话文件过期天数
|
||||
CHAT_FILE_EXPIRE_TIME: 7
|
||||
# 服务器接收请求,最大大小,单位 MB
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.4
|
||||
networks:
|
||||
- fastgpt
|
||||
ports:
|
||||
- 3005:3000
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt-plugin:
|
||||
image: ghcr.io/labring/fastgpt-plugin:v0.3.4
|
||||
container_name: fastgpt-plugin
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
<<: *x-share-db-config
|
||||
AUTH_TOKEN: *x-plugin-auth-token
|
||||
# 工具网络请求,最大请求和响应体
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
# 最大 API 请求体大小
|
||||
MAX_API_SIZE: 10
|
||||
depends_on:
|
||||
fastgpt-minio:
|
||||
condition: service_healthy
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: ghcr.io/labring/aiproxy:v0.3.2
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
aiproxy_pg:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- fastgpt
|
||||
- aiproxy
|
||||
environment:
|
||||
# 对应 fastgpt 里的AIPROXY_API_TOKEN
|
||||
ADMIN_KEY: *x-aiproxy-token
|
||||
# 错误日志详情保存时间(小时)
|
||||
LOG_DETAIL_STORAGE_HOURS: 1
|
||||
# 数据库连接地址
|
||||
SQL_DSN: postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
# 最大重试次数
|
||||
RETRY_TIMES: 3
|
||||
# 不需要计费
|
||||
BILLING_ENABLED: false
|
||||
# 不需要严格检测模型
|
||||
DISABLE_MODEL_CONFIG: true
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/status']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
aiproxy_pg:
|
||||
image: pgvector/pgvector:0.8.0-pg15 # docker hub
|
||||
restart: unless-stopped
|
||||
container_name: aiproxy_pg
|
||||
volumes:
|
||||
- ./aiproxy_pg:/var/lib/postgresql/data
|
||||
networks:
|
||||
- aiproxy
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: aiproxy
|
||||
POSTGRES_PASSWORD: aiproxy
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'postgres', '-d', 'aiproxy']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks:
|
||||
fastgpt:
|
||||
aiproxy:
|
||||
vector:
|
||||
configs:
|
||||
init_sql:
|
||||
name: init_sql
|
||||
content: |
|
||||
ALTER SYSTEM SET ob_vector_memory_limit_percentage = 30;
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/bash
|
||||
docker-compose pull
|
||||
docker-compose up -d
|
||||
|
||||
echo "Docker Compose 重新拉取镜像完成!"
|
||||
|
||||
# 删除本地旧镜像
|
||||
images=$(docker images --format "{{.ID}} {{.Repository}}" | grep fastgpt)
|
||||
|
||||
# 将镜像 ID 和名称放入数组中
|
||||
IFS=$'\n' read -rd '' -a image_array <<<"$images"
|
||||
|
||||
# 遍历数组并删除所有旧的镜像
|
||||
for ((i=1; i<${#image_array[@]}; i++))
|
||||
do
|
||||
image=${image_array[$i]}
|
||||
image_id=${image%% *}
|
||||
docker rmi $image_id
|
||||
done
|
||||
|
|
@ -26,6 +26,8 @@ data:
|
|||
"usedInToolCall": true,
|
||||
"toolChoice": true,
|
||||
"functionCall": false,
|
||||
"customCQPrompt": "",
|
||||
"customExtractPrompt": "",
|
||||
"defaultSystemChatPrompt": "",
|
||||
"defaultConfig": {}
|
||||
},
|
||||
|
|
@ -45,6 +47,8 @@ data:
|
|||
"usedInToolCall": true,
|
||||
"toolChoice": true,
|
||||
"functionCall": false,
|
||||
"customCQPrompt": "",
|
||||
"customExtractPrompt": "",
|
||||
"defaultSystemChatPrompt": "",
|
||||
"defaultConfig": {}
|
||||
},
|
||||
|
|
@ -64,6 +68,8 @@ data:
|
|||
"usedInToolCall": true,
|
||||
"toolChoice": true,
|
||||
"functionCall": false,
|
||||
"customCQPrompt": "",
|
||||
"customExtractPrompt": "",
|
||||
"defaultSystemChatPrompt": "",
|
||||
"defaultConfig": {}
|
||||
},
|
||||
|
|
@ -83,6 +89,8 @@ data:
|
|||
"usedInToolCall": false,
|
||||
"toolChoice": true,
|
||||
"functionCall": false,
|
||||
"customCQPrompt": "",
|
||||
"customExtractPrompt": "",
|
||||
"defaultSystemChatPrompt": "",
|
||||
"defaultConfig": {}
|
||||
}
|
||||
|
|
|
|||
228
deploy/init.mjs
|
|
@ -1,228 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
* @enum {String} RegionEnum
|
||||
*/
|
||||
const RegionEnum = {
|
||||
cn: 'cn',
|
||||
global: 'global'
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {String} VectorEnum
|
||||
*/
|
||||
const VectorEnum = {
|
||||
pg: 'pg',
|
||||
milvus: 'milvus',
|
||||
zilliz: 'zilliz',
|
||||
ob: 'ob'
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {string} Services
|
||||
*/
|
||||
const Services = {
|
||||
fastgpt: 'fastgpt',
|
||||
fastgptPlugin: 'fastgpt-plugin',
|
||||
fastgptSandbox: 'fastgpt-sandbox',
|
||||
fastgptMcpServer: 'fastgpt-mcp_server',
|
||||
minio: 'minio',
|
||||
mongo: 'mongo',
|
||||
redis: 'redis',
|
||||
aiproxy: 'aiproxy',
|
||||
aiproxyPg: 'aiproxy-pg',
|
||||
// vectors
|
||||
pg: 'pg',
|
||||
milvusMinio: 'milvus-minio',
|
||||
milvusEtcd: 'milvus-etcd',
|
||||
milvusStandalone: 'milvus-standalone',
|
||||
oceanbase: 'oceanbase'
|
||||
};
|
||||
|
||||
// make sure the cwd
|
||||
const basePath = process.cwd();
|
||||
if (!basePath.endsWith('deploy')) {
|
||||
process.chdir('deploy');
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {{ tag: String, image: {cn: String, global: String} }} ArgItemType
|
||||
*/
|
||||
/** format the args
|
||||
* @type {Record<Services, ArgItemType>}
|
||||
*/
|
||||
const args = (() => {
|
||||
/**
|
||||
* @type {{tags: Record<Services, string>, images: Record<Services, Record<string, string>>}}
|
||||
*/
|
||||
const obj = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'args.json')));
|
||||
const args = {};
|
||||
for (const key of Object.keys(obj.tags)) {
|
||||
args[key] = {
|
||||
tag: obj.tags[key],
|
||||
image: {
|
||||
cn: obj.images.cn[key],
|
||||
global: obj.images.global[key]
|
||||
}
|
||||
};
|
||||
}
|
||||
return args;
|
||||
})();
|
||||
|
||||
const vector = {
|
||||
pg: {
|
||||
db: '',
|
||||
config: `\
|
||||
PG_URL: postgresql://username:password@pg:5432/postgres`,
|
||||
extra: ''
|
||||
},
|
||||
milvus: {
|
||||
db: '',
|
||||
config: `\
|
||||
MILVUS_ADDRESS: http://milvusStandalone:19530
|
||||
MILVUS_TOKEN: none
|
||||
`,
|
||||
extra: ''
|
||||
},
|
||||
zilliz: {
|
||||
db: '',
|
||||
config: `\
|
||||
MILVUS_ADDRESS: zilliz_cloud_address
|
||||
MILVUS_TOKEN: zilliz_cloud_token`,
|
||||
extra: ''
|
||||
},
|
||||
ob: {
|
||||
db: '',
|
||||
config: `\
|
||||
OCEANBASE_URL: mysql://root%40tenantname:tenantpassword@ob:2881/test
|
||||
`,
|
||||
extra: `\
|
||||
configs:
|
||||
init_sql:
|
||||
name: init_sql
|
||||
content: |
|
||||
ALTER SYSTEM SET ob_vector_memory_limit_percentage = 30;
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* replace all ${{}}
|
||||
* @param {string} source
|
||||
* @param {RegionEnum} region
|
||||
* @param {VectorEnum} vec
|
||||
* @returns {string}
|
||||
*/
|
||||
const replace = (source, region, vec) => {
|
||||
// Match ${{expr}}, capture "expr" inside {{}}
|
||||
return source.replace(/\$\{\{([^}]*)\}\}/g, (_, expr) => {
|
||||
// expr: a.b
|
||||
/**
|
||||
* @type {String}
|
||||
*/
|
||||
const [a, b] = expr.split('.');
|
||||
if (a === 'vec') {
|
||||
if (b === 'db') {
|
||||
return replace(vector[vec].db, region, vec);
|
||||
} else {
|
||||
return vector[vec][b];
|
||||
}
|
||||
}
|
||||
|
||||
if (b === 'tag') {
|
||||
return args[a].tag;
|
||||
} else if (b === 'image') {
|
||||
return args[a].image[region];
|
||||
}
|
||||
});
|
||||
};
|
||||
{
|
||||
// read in Vectors
|
||||
const pg = fs.readFileSync(path.join(process.cwd(), 'templates', 'vector', 'pg.txt'));
|
||||
vector.pg.db = String(pg);
|
||||
|
||||
const milvus = fs.readFileSync(path.join(process.cwd(), 'templates', 'vector', 'milvus.txt'));
|
||||
vector.milvus.db = String(milvus);
|
||||
|
||||
const ob = fs.readFileSync(path.join(process.cwd(), 'templates', 'vector', 'ob.txt'));
|
||||
vector.ob.db = String(ob);
|
||||
}
|
||||
|
||||
const generateDevFile = async () => {
|
||||
console.log('generating dev/docker-compose.yml');
|
||||
// 1. read template
|
||||
const template = await fs.promises.readFile(
|
||||
path.join(process.cwd(), 'templates', 'docker-compose.dev.yml'),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
fs.promises.writeFile(
|
||||
path.join(process.cwd(), 'dev', 'docker-compose.cn.yml'),
|
||||
replace(template, 'cn')
|
||||
),
|
||||
fs.promises.writeFile(
|
||||
path.join(process.cwd(), 'dev', 'docker-compose.yml'),
|
||||
replace(template, 'global')
|
||||
)
|
||||
]);
|
||||
|
||||
console.log('success geenrate dev files');
|
||||
};
|
||||
|
||||
const generateProdFile = async () => {
|
||||
console.log('generating prod/docker-compose.yml');
|
||||
const template = await fs.promises.readFile(
|
||||
path.join(process.cwd(), 'templates', 'docker-compose.prod.yml'),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
fs.promises.writeFile(
|
||||
path.join(process.cwd(), 'docker', 'cn', 'docker-compose.pg.yml'),
|
||||
replace(template, 'cn', VectorEnum.pg)
|
||||
),
|
||||
fs.promises.writeFile(
|
||||
path.join(process.cwd(), 'docker', 'global', 'docker-compose.pg.yml'),
|
||||
replace(template, 'global', VectorEnum.pg)
|
||||
),
|
||||
fs.promises.writeFile(
|
||||
path.join(process.cwd(), 'docker', 'cn', 'docker-compose.milvus.yml'),
|
||||
replace(template, 'cn', VectorEnum.milvus)
|
||||
),
|
||||
fs.promises.writeFile(
|
||||
path.join(process.cwd(), 'docker', 'global', 'docker-compose.milvus.yml'),
|
||||
replace(template, 'global', VectorEnum.milvus)
|
||||
),
|
||||
fs.promises.writeFile(
|
||||
path.join(process.cwd(), 'docker', 'cn', 'docker-compose.zilliz.yml'),
|
||||
replace(template, 'cn', VectorEnum.zilliz)
|
||||
),
|
||||
fs.promises.writeFile(
|
||||
path.join(process.cwd(), 'docker', 'global', 'docker-compose.ziliiz.yml'),
|
||||
replace(template, 'global', VectorEnum.zilliz)
|
||||
),
|
||||
fs.promises.writeFile(
|
||||
path.join(process.cwd(), 'docker', 'cn', 'docker-compose.oceanbase.yml'),
|
||||
replace(template, 'cn', VectorEnum.ob)
|
||||
),
|
||||
fs.promises.writeFile(
|
||||
path.join(process.cwd(), 'docker', 'global', 'docker-compose.oceanbase.yml'),
|
||||
replace(template, 'global', VectorEnum.ob)
|
||||
)
|
||||
]);
|
||||
|
||||
console.log('success geenrate prod files');
|
||||
};
|
||||
|
||||
await Promise.all([generateDevFile(), generateProdFile()]);
|
||||
|
||||
console.log('copy the docker dir to ../document/public');
|
||||
|
||||
await fs.promises.cp(
|
||||
path.join(process.cwd(), 'docker'),
|
||||
path.join(process.cwd(), '..', 'document', 'public', 'deploy', 'docker'),
|
||||
{ recursive: true }
|
||||
);
|
||||
|
|
@ -1,229 +0,0 @@
|
|||
# 用于开发的 docker-compose 文件:
|
||||
# - 只包含 FastGPT 的最小化运行条件
|
||||
# - 没有 FastGPT 本体
|
||||
# - 所有端口都映射到外层
|
||||
# - pg: 5432
|
||||
# - mongo: 27017
|
||||
# - redis: 6379
|
||||
# - fastgpt-sandbox: 3002
|
||||
# - fastgpt-plugin: 3003
|
||||
# - aiproxy: 3010
|
||||
# - 使用 pgvector 作为默认的向量库
|
||||
|
||||
services:
|
||||
# Vector DB
|
||||
pg:
|
||||
image: ${{pg.image}}:${{pg.tag}}
|
||||
container_name: pg
|
||||
restart: always
|
||||
ports: # 生产环境建议不要暴露
|
||||
- 5432:5432
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
||||
- POSTGRES_USER=username
|
||||
- POSTGRES_PASSWORD=password
|
||||
- POSTGRES_DB=postgres
|
||||
volumes:
|
||||
- ./pg/data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'username', '-d', 'postgres']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
# DB
|
||||
mongo:
|
||||
image: ${{mongo.image}}:${{mongo.tag}} # cpu 不支持 AVX 时候使用 4.4.29
|
||||
container_name: mongo
|
||||
restart: always
|
||||
ports:
|
||||
- 27017:27017
|
||||
networks:
|
||||
- fastgpt
|
||||
command: mongod --keyFile /data/mongodb.key --replSet rs0
|
||||
environment:
|
||||
- MONGO_INITDB_ROOT_USERNAME=myusername
|
||||
- MONGO_INITDB_ROOT_PASSWORD=mypassword
|
||||
volumes:
|
||||
- ./mongo/data:/data/db
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
'CMD',
|
||||
'mongo',
|
||||
'-u',
|
||||
'myusername',
|
||||
'-p',
|
||||
'mypassword',
|
||||
'--authenticationDatabase',
|
||||
'admin',
|
||||
'--eval',
|
||||
"db.adminCommand('ping')"
|
||||
]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
openssl rand -base64 128 > /data/mongodb.key
|
||||
chmod 400 /data/mongodb.key
|
||||
chown 999:999 /data/mongodb.key
|
||||
echo 'const isInited = rs.status().ok === 1
|
||||
if(!isInited){
|
||||
rs.initiate({
|
||||
_id: "rs0",
|
||||
members: [
|
||||
{ _id: 0, host: "mongo:27017" }
|
||||
]
|
||||
})
|
||||
}' > /data/initReplicaSet.js
|
||||
# 启动MongoDB服务
|
||||
exec docker-entrypoint.sh "$$@" &
|
||||
|
||||
# 等待MongoDB服务启动
|
||||
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')"; do
|
||||
echo "Waiting for MongoDB to start..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# 执行初始化副本集的脚本
|
||||
mongo -u myusername -p mypassword --authenticationDatabase admin /data/initReplicaSet.js
|
||||
|
||||
# 等待docker-entrypoint.sh脚本执行的MongoDB服务进程
|
||||
wait $$!
|
||||
redis:
|
||||
image: ${{redis.image}}:${{redis.tag}}
|
||||
container_name: redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
command: |
|
||||
redis-server --requirepass mypassword --loglevel warning --maxclients 10000 --appendonly yes --save 60 10 --maxmemory 4gb --maxmemory-policy noeviction
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', '-a', 'mypassword', 'ping']
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
fastgpt-minio:
|
||||
image: ${{minio.image}}:${{minio.tag}}
|
||||
container_name: fastgpt-minio
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
ports:
|
||||
- '9000:9000'
|
||||
- '9001:9001'
|
||||
environment:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
volumes:
|
||||
- ./fastgpt-minio:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ${{fastgpt-sandbox.image}}:${{fastgpt-sandbox.tag}}
|
||||
ports:
|
||||
- 3002:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ${{fastgpt-mcp_server.image}}:${{fastgpt-mcp_server.tag}}
|
||||
ports:
|
||||
- 3005:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt-plugin:
|
||||
image: ${{fastgpt-plugin.image}}:${{fastgpt-plugin.tag}}
|
||||
container_name: fastgpt-plugin
|
||||
restart: always
|
||||
ports:
|
||||
- 3003:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
- AUTH_TOKEN=token
|
||||
- S3_EXTERNAL_BASE_URL=http://127.0.0.1:9000 # TODO: 改为你 Minio 的实际的 ip 地址
|
||||
- S3_ENDPOINT=fastgpt-minio
|
||||
- S3_PORT=9000
|
||||
- S3_USE_SSL=false
|
||||
- S3_ACCESS_KEY=minioadmin
|
||||
- S3_SECRET_KEY=minioadmin
|
||||
- S3_PUBLIC_BUCKET=fastgpt-public # 系统工具,创建的临时文件,存储的桶,要求公开读私有写。
|
||||
- S3_PRIVATE_BUCKET=fastgpt-private # 系统插件热安装文件的桶,私有读写。
|
||||
- MONGODB_URI=mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin&directConnection=true
|
||||
- REDIS_URL=redis://default:mypassword@redis:6379
|
||||
depends_on:
|
||||
fastgpt-minio:
|
||||
condition: service_healthy
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: ${{aiproxy.image}}:${{aiproxy.tag}}
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 3010:3000
|
||||
depends_on:
|
||||
aiproxy_pg:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- fastgpt
|
||||
- aiproxy
|
||||
environment:
|
||||
# 对应 fastgpt 里的AIPROXY_API_TOKEN
|
||||
- ADMIN_KEY=aiproxy
|
||||
# 错误日志详情保存时间(小时)
|
||||
- LOG_DETAIL_STORAGE_HOURS=1
|
||||
# 数据库连接地址
|
||||
- SQL_DSN=postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
# 最大重试次数
|
||||
- RETRY_TIMES=3
|
||||
# 不需要计费
|
||||
- BILLING_ENABLED=false
|
||||
# 不需要严格检测模型
|
||||
- DISABLE_MODEL_CONFIG=true
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/status']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
aiproxy_pg:
|
||||
image: ${{aiproxy-pg.image}}:${{aiproxy-pg.tag}} # docker hub
|
||||
restart: unless-stopped
|
||||
container_name: aiproxy_pg
|
||||
volumes:
|
||||
- ./aiproxy_pg:/var/lib/postgresql/data
|
||||
networks:
|
||||
- aiproxy
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: aiproxy
|
||||
POSTGRES_PASSWORD: aiproxy
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'postgres', '-d', 'aiproxy']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks:
|
||||
fastgpt:
|
||||
aiproxy:
|
||||
|
|
@ -1,251 +0,0 @@
|
|||
# 用于部署的 docker-compose 文件:
|
||||
# - FastGPT 端口映射为 3000:3000
|
||||
# - FastGPT-mcp-server 端口映射 3005:3000
|
||||
# - 建议修改账密后再运行
|
||||
|
||||
# plugin auth token
|
||||
x-plugin-auth-token: &x-plugin-auth-token 'token'
|
||||
# aiproxy token
|
||||
x-aiproxy-token: &x-aiproxy-token 'token'
|
||||
# 数据库连接相关配置
|
||||
x-share-db-config: &x-share-db-config
|
||||
MONGODB_URI: mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
DB_MAX_LINK: 100
|
||||
REDIS_URL: redis://default:mypassword@redis:6379
|
||||
S3_EXTERNAL_BASE_URL: https://minio.com # S3 的公网访问地址
|
||||
S3_ENDPOINT: fastgpt-minio
|
||||
S3_PORT: 9000
|
||||
S3_USE_SSL: false
|
||||
S3_ACCESS_KEY: minioadmin
|
||||
S3_SECRET_KEY: minioadmin
|
||||
S3_PUBLIC_BUCKET: fastgpt-public # 公开读私有写桶
|
||||
S3_PRIVATE_BUCKET: fastgpt-private # 私有读写桶
|
||||
|
||||
# 向量库相关配置
|
||||
x-vec-config: &x-vec-config
|
||||
${{vec.config}}
|
||||
|
||||
version: '3.3'
|
||||
services:
|
||||
# Vector DB
|
||||
${{vec.db}}
|
||||
|
||||
mongo:
|
||||
image: ${{mongo.image}}:${{mongo.tag}} # cpu 不支持 AVX 时候使用 4.4.29
|
||||
container_name: mongo
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
command: mongod --keyFile /data/mongodb.key --replSet rs0
|
||||
environment:
|
||||
- MONGO_INITDB_ROOT_USERNAME=myusername
|
||||
- MONGO_INITDB_ROOT_PASSWORD=mypassword
|
||||
volumes:
|
||||
- ./mongo/data:/data/db
|
||||
healthcheck:
|
||||
test: ['CMD', 'mongo', '-u', 'myusername', '-p', 'mypassword', '--authenticationDatabase', 'admin', '--eval', "db.adminCommand('ping')"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
openssl rand -base64 128 > /data/mongodb.key
|
||||
chmod 400 /data/mongodb.key
|
||||
chown 999:999 /data/mongodb.key
|
||||
echo 'const isInited = rs.status().ok === 1
|
||||
if(!isInited){
|
||||
rs.initiate({
|
||||
_id: "rs0",
|
||||
members: [
|
||||
{ _id: 0, host: "mongo:27017" }
|
||||
]
|
||||
})
|
||||
}' > /data/initReplicaSet.js
|
||||
# 启动MongoDB服务
|
||||
exec docker-entrypoint.sh "$$@" &
|
||||
|
||||
# 等待MongoDB服务启动
|
||||
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')"; do
|
||||
echo "Waiting for MongoDB to start..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# 执行初始化副本集的脚本
|
||||
mongo -u myusername -p mypassword --authenticationDatabase admin /data/initReplicaSet.js
|
||||
|
||||
# 等待docker-entrypoint.sh脚本执行的MongoDB服务进程
|
||||
wait $$!
|
||||
redis:
|
||||
image: ${{redis.image}}:${{redis.tag}}
|
||||
container_name: redis
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
command: |
|
||||
redis-server --requirepass mypassword --loglevel warning --maxclients 10000 --appendonly yes --save 60 10 --maxmemory 4gb --maxmemory-policy noeviction
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', '-a', 'mypassword', 'ping']
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
fastgpt-minio:
|
||||
image: ${{minio.image}}:${{minio.tag}}
|
||||
container_name: fastgpt-minio
|
||||
restart: always
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 9001:9001
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
volumes:
|
||||
- ./fastgpt-minio:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ${{fastgpt.image}}:${{fastgpt.tag}} # git
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
depends_on:
|
||||
- mongo
|
||||
- sandbox
|
||||
- vectorDB
|
||||
restart: always
|
||||
environment:
|
||||
<<: [*x-share-db-config, *x-vec-config]
|
||||
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。这个值可以不填,不填则发给模型的图片会是一个相对路径,而不是全路径,模型可能伪造Host。
|
||||
FE_DOMAIN:
|
||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||
DEFAULT_ROOT_PSW: 1234
|
||||
# 登录凭证密钥
|
||||
TOKEN_KEY: any
|
||||
# root的密钥,常用于升级时候的初始化请求
|
||||
ROOT_KEY: root_key
|
||||
# 文件阅读加密
|
||||
FILE_TOKEN_KEY: filetoken
|
||||
# 密钥加密key
|
||||
AES256_SECRET_KEY: fastgptkey
|
||||
|
||||
# plugin 地址
|
||||
PLUGIN_BASE_URL: http://fastgpt-plugin:3000
|
||||
PLUGIN_TOKEN: *x-plugin-auth-token
|
||||
# sandbox 地址
|
||||
SANDBOX_URL: http://sandbox:3000
|
||||
# AI Proxy 的地址,如果配了该地址,优先使用
|
||||
AIPROXY_API_ENDPOINT: http://aiproxy:3000
|
||||
# AI Proxy 的 Admin Token,与 AI Proxy 中的环境变量 ADMIN_KEY
|
||||
AIPROXY_API_TOKEN: *x-aiproxy-token
|
||||
|
||||
# 日志等级: debug, info, warn, error
|
||||
LOG_LEVEL: info
|
||||
STORE_LOG_LEVEL: warn
|
||||
# 工作流最大运行次数
|
||||
WORKFLOW_MAX_RUN_TIMES: 1000
|
||||
# 批量执行节点,最大输入长度
|
||||
WORKFLOW_MAX_LOOP_TIMES: 100
|
||||
# 对话文件过期天数
|
||||
CHAT_FILE_EXPIRE_TIME: 7
|
||||
# 服务器接收请求,最大大小,单位 MB
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ${{fastgpt-sandbox.image}}:${{fastgpt-sandbox.tag}}
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ${{fastgpt-mcp_server.image}}:${{fastgpt-mcp_server.tag}}
|
||||
networks:
|
||||
- fastgpt
|
||||
ports:
|
||||
- 3005:3000
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt-plugin:
|
||||
image: ${{fastgpt-plugin.image}}:${{fastgpt-plugin.tag}}
|
||||
container_name: fastgpt-plugin
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
<<: *x-share-db-config
|
||||
AUTH_TOKEN: *x-plugin-auth-token
|
||||
# 工具网络请求,最大请求和响应体
|
||||
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
|
||||
# 最大 API 请求体大小
|
||||
MAX_API_SIZE: 10
|
||||
depends_on:
|
||||
fastgpt-minio:
|
||||
condition: service_healthy
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: ${{aiproxy.image}}:${{aiproxy.tag}}
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
aiproxy_pg:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- fastgpt
|
||||
- aiproxy
|
||||
environment:
|
||||
# 对应 fastgpt 里的AIPROXY_API_TOKEN
|
||||
ADMIN_KEY: *x-aiproxy-token
|
||||
# 错误日志详情保存时间(小时)
|
||||
LOG_DETAIL_STORAGE_HOURS: 1
|
||||
# 数据库连接地址
|
||||
SQL_DSN: postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
|
||||
# 最大重试次数
|
||||
RETRY_TIMES: 3
|
||||
# 不需要计费
|
||||
BILLING_ENABLED: false
|
||||
# 不需要严格检测模型
|
||||
DISABLE_MODEL_CONFIG: true
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/status']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
aiproxy_pg:
|
||||
image: ${{aiproxy-pg.image}}:${{aiproxy-pg.tag}} # docker hub
|
||||
restart: unless-stopped
|
||||
container_name: aiproxy_pg
|
||||
volumes:
|
||||
- ./aiproxy_pg:/var/lib/postgresql/data
|
||||
networks:
|
||||
- aiproxy
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: aiproxy
|
||||
POSTGRES_PASSWORD: aiproxy
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'postgres', '-d', 'aiproxy']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks:
|
||||
fastgpt:
|
||||
aiproxy:
|
||||
vector:
|
||||
${{vec.extra}}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
milvus-minio:
|
||||
container_name: milvus-minio
|
||||
image: ${{milvus-minio.image}}:${{milvus-minio.tag}}
|
||||
environment:
|
||||
MINIO_ACCESS_KEY: minioadmin
|
||||
MINIO_SECRET_KEY: minioadmin
|
||||
networks:
|
||||
- vector
|
||||
volumes:
|
||||
- ./milvus-minio:/minio_data
|
||||
command: minio server /minio_data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
# milvus
|
||||
milvus-etcd:
|
||||
container_name: milvus-etcd
|
||||
image: ${{milvus-etcd.image}}:${{milvus-etcd.tag}}
|
||||
environment:
|
||||
- ETCD_AUTO_COMPACTION_MODE=revision
|
||||
- ETCD_AUTO_COMPACTION_RETENTION=1000
|
||||
- ETCD_QUOTA_BACKEND_BYTES=4294967296
|
||||
- ETCD_SNAPSHOT_COUNT=50000
|
||||
networks:
|
||||
- vector
|
||||
volumes:
|
||||
- ./milvus/etcd:/etcd
|
||||
command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
|
||||
healthcheck:
|
||||
test: ['CMD', 'etcdctl', 'endpoint', 'health']
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
vectorDB:
|
||||
container_name: milvusStandalone
|
||||
image: ${{milvus-standalone.image}}:${{milvus-standalone.tag}}
|
||||
command: ['milvus', 'run', 'standalone']
|
||||
security_opt:
|
||||
- seccomp:unconfined
|
||||
environment:
|
||||
ETCD_ENDPOINTS: milvus-etcd:2379
|
||||
MINIO_ADDRESS: milvus-minio:9000
|
||||
networks:
|
||||
- fastgpt
|
||||
- vector
|
||||
volumes:
|
||||
- ./milvus/data:/var/lib/milvus
|
||||
healthcheck:
|
||||
test: ['CMD', 'curl', '-f', 'http://localhost:9091/healthz']
|
||||
interval: 30s
|
||||
start_period: 90s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
depends_on:
|
||||
- 'milvus-etcd'
|
||||
- 'milvus-minio'
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
vectorDB:
|
||||
image: ${{oceanbase.image}}:${{oceanbase.tag}}
|
||||
container_name: ob
|
||||
restart: always
|
||||
# ports: # 生产环境建议不要暴露
|
||||
# - 2881:2881
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
||||
- OB_SYS_PASSWORD=obsyspassword
|
||||
# 不同于传统数据库,OceanBase 数据库的账号包含更多字段,包括用户名、租户名和集群名。经典格式为"用户名@租户名#集群名"
|
||||
# 比如用mysql客户端连接时,根据本文件的默认配置,应该指定 "-uroot@tenantname"
|
||||
- OB_TENANT_NAME=tenantname
|
||||
- OB_TENANT_PASSWORD=tenantpassword
|
||||
# MODE分为MINI和NORMAL, 后者会最大程度使用主机资源
|
||||
- MODE=MINI
|
||||
- OB_SERVER_IP=127.0.0.1
|
||||
# 更多环境变量配置见oceanbase官方文档: https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000002013494
|
||||
volumes:
|
||||
- ../ob/data:/root/ob
|
||||
- ../ob/config:/root/.obd/cluster
|
||||
configs:
|
||||
- source: init_sql
|
||||
target: /root/boot/init.d/init.sql
|
||||
healthcheck:
|
||||
# obclient -h127.0.0.1 -P2881 -uroot@tenantname -ptenantpassword -e "SELECT 1;"
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
'obclient -h$${OB_SERVER_IP} -P2881 -uroot@$${OB_TENANT_NAME} -p$${OB_TENANT_PASSWORD} -e "SELECT 1;"',
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 1000
|
||||
start_period: 10s
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
vectorDB:
|
||||
image: ${{pg.image}}:${{pg.tag}}
|
||||
container_name: pg
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
||||
- POSTGRES_USER=username
|
||||
- POSTGRES_PASSWORD=password
|
||||
- POSTGRES_DB=postgres
|
||||
volumes:
|
||||
- ./pg/data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ['CMD', 'pg_isready', '-U', 'username', '-d', 'postgres']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
232
dev.md
|
|
@ -1,118 +1,114 @@
|
|||
## Premise
|
||||
|
||||
Since FastGPT is managed in the same way as monorepo, it is recommended to install ‘make’ first during development.
|
||||
|
||||
monorepo Project Name:
|
||||
|
||||
- app: main project
|
||||
-......
|
||||
|
||||
## Dev
|
||||
|
||||
```sh
|
||||
# Give automatic script code execution permission (on non-Linux systems, you can manually execute the postinstall.sh file content)
|
||||
chmod -R +x ./scripts/
|
||||
# Executing under the code root directory installs all dependencies within the root package, projects, and packages
|
||||
pnpm i
|
||||
|
||||
# Not make cmd
|
||||
cd projects/app
|
||||
pnpm dev
|
||||
|
||||
# Make cmd
|
||||
make dev name=app
|
||||
```
|
||||
|
||||
Note: If the Node version is >= 20, you need to pass the `--no-node-snapshot` parameter to Node when running `pnpm i`
|
||||
|
||||
```sh
|
||||
NODE_OPTIONS=--no-node-snapshot pnpm i
|
||||
```
|
||||
|
||||
### Jest
|
||||
|
||||
https://fael3z0zfze.feishu.cn/docx/ZOI1dABpxoGhS7xzhkXcKPxZnDL
|
||||
|
||||
## I18N
|
||||
|
||||
### Install i18n-ally Plugin
|
||||
|
||||
1. Open the Extensions Marketplace in VSCode, search for and install the `i18n Ally` plugin.
|
||||
|
||||
### Code Optimization Examples
|
||||
|
||||
#### Fetch Specific Namespace Translations in `getServerSideProps`
|
||||
|
||||
```typescript
|
||||
// pages/yourPage.tsx
|
||||
export async function getServerSideProps(context: any) {
|
||||
return {
|
||||
props: {
|
||||
currentTab: context?.query?.currentTab || TabEnum.info,
|
||||
...(await serverSideTranslations(context.locale, ['publish', 'user']))
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### Use useTranslation Hook in Page
|
||||
|
||||
```typescript
|
||||
// pages/yourPage.tsx
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const YourComponent = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
mr={2}
|
||||
onClick={() => setShowSelected(false)}
|
||||
>
|
||||
{t('common:close')}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export default YourComponent;
|
||||
```
|
||||
|
||||
#### Handle Static File Translations
|
||||
|
||||
```typescript
|
||||
// utils/i18n.ts
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
|
||||
const staticContent = {
|
||||
id: 'simpleChat',
|
||||
avatar: 'core/workflow/template/aiChat',
|
||||
name: i18nT('app:template.simple_robot'),
|
||||
};
|
||||
|
||||
export default staticContent;
|
||||
```
|
||||
|
||||
### Standardize Translation Format
|
||||
|
||||
- Use the t(namespace:key) format to ensure consistent naming.
|
||||
- Translation keys should use lowercase letters and underscores, e.g., common.close.
|
||||
|
||||
## audit
|
||||
|
||||
Please fill the AuditEventEnum and audit function is added to the ts, and on the corresponding position to fill i18n, at the same time to add the location of the log using addOpearationLog function add function
|
||||
|
||||
## Build
|
||||
|
||||
```sh
|
||||
# Docker cmd: Build image, not proxy
|
||||
docker build -f ./projects/app/Dockerfile -t registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 . --build-arg name=app
|
||||
# Make cmd: Build image, not proxy
|
||||
make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1
|
||||
|
||||
# Docker cmd: Build image with proxy
|
||||
docker build -f ./projects/app/Dockerfile -t registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 . --build-arg name=app --build-arg proxy=taobao
|
||||
# Make cmd: Build image with proxy
|
||||
make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 proxy=taobao
|
||||
```
|
||||
## Premise
|
||||
|
||||
Since FastGPT is managed in the same way as monorepo, it is recommended to install ‘make’ first during development.
|
||||
|
||||
monorepo Project Name:
|
||||
|
||||
- app: main project
|
||||
-......
|
||||
|
||||
## Dev
|
||||
|
||||
```sh
|
||||
# Give automatic script code execution permission (on non-Linux systems, you can manually execute the postinstall.sh file content)
|
||||
chmod -R +x ./scripts/
|
||||
# Executing under the code root directory installs all dependencies within the root package, projects, and packages
|
||||
pnpm i
|
||||
|
||||
# Not make cmd
|
||||
cd projects/app
|
||||
pnpm dev
|
||||
|
||||
# Make cmd
|
||||
make dev name=app
|
||||
```
|
||||
|
||||
Note: If the Node version is >= 20, you need to pass the `--no-node-snapshot` parameter to Node when running `pnpm i`
|
||||
|
||||
```sh
|
||||
NODE_OPTIONS=--no-node-snapshot pnpm i
|
||||
```
|
||||
|
||||
### Jest
|
||||
|
||||
https://fael3z0zfze.feishu.cn/docx/ZOI1dABpxoGhS7xzhkXcKPxZnDL
|
||||
|
||||
## I18N
|
||||
|
||||
### Install i18n-ally Plugin
|
||||
|
||||
1. Open the Extensions Marketplace in VSCode, search for and install the `i18n Ally` plugin.
|
||||
|
||||
### Code Optimization Examples
|
||||
|
||||
#### Fetch Specific Namespace Translations in `getServerSideProps`
|
||||
|
||||
```typescript
|
||||
// pages/yourPage.tsx
|
||||
export async function getServerSideProps(context: any) {
|
||||
return {
|
||||
props: {
|
||||
currentTab: context?.query?.currentTab || TabEnum.info,
|
||||
...(await serverSideTranslations(context.locale, ['publish', 'user']))
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### Use useTranslation Hook in Page
|
||||
|
||||
```typescript
|
||||
// pages/yourPage.tsx
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const YourComponent = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
mr={2}
|
||||
onClick={() => setShowSelected(false)}
|
||||
>
|
||||
{t('common:close')}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export default YourComponent;
|
||||
```
|
||||
|
||||
#### Handle Static File Translations
|
||||
|
||||
```typescript
|
||||
// utils/i18n.ts
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
|
||||
const staticContent = {
|
||||
id: 'simpleChat',
|
||||
avatar: 'core/workflow/template/aiChat',
|
||||
name: i18nT('app:template.simple_robot'),
|
||||
};
|
||||
|
||||
export default staticContent;
|
||||
```
|
||||
|
||||
### Standardize Translation Format
|
||||
|
||||
- Use the t(namespace:key) format to ensure consistent naming.
|
||||
- Translation keys should use lowercase letters and underscores, e.g., common.close.
|
||||
|
||||
## Build
|
||||
|
||||
```sh
|
||||
# Docker cmd: Build image, not proxy
|
||||
docker build -f ./projects/app/Dockerfile -t registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 . --build-arg name=app
|
||||
# Make cmd: Build image, not proxy
|
||||
make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1
|
||||
|
||||
# Docker cmd: Build image with proxy
|
||||
docker build -f ./projects/app/Dockerfile -t registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 . --build-arg name=app --build-arg proxy=taobao
|
||||
# Make cmd: Build image with proxy
|
||||
make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 proxy=taobao
|
||||
```
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
FROM hugomods/hugo:0.117.0 AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ADD ./docSite hugo
|
||||
RUN cd /app/hugo && hugo mod get -u github.com/colinwilson/lotusdocs@6d0568e && hugo -v --minify
|
||||
|
||||
FROM fholzer/nginx-brotli:latest
|
||||
|
||||
LABEL org.opencontainers.image.source https://github.com/labring/FastGPT
|
||||
|
||||
COPY --from=builder /app/hugo/public /usr/share/nginx/html
|
||||
|
||||
COPY ./docSite/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# 文档
|
||||
|
||||
## 本地运行
|
||||
|
||||
1. 安装 go 语言环境。
|
||||
2. 安装 hugo。[二进制下载](https://github.com/gohugoio/hugo/releases/tag/v0.117.0),注意需要安装 extended 版本。
|
||||
3. cd docSite
|
||||
4. hugo serve
|
||||
5. 访问 http://localhost:1313
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: "{{ replace .Name "-" " " | title }}"
|
||||
date: {{ .Date }}
|
||||
draft: true
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
:root {
|
||||
--code-bg: rgba(0, 0, 0, 0.03);
|
||||
--code-color: rgba(14, 116, 144, 0.95);
|
||||
--inline-code-border: 0.5px solid var(--gray-400);
|
||||
|
||||
}
|
||||
|
||||
[data-dark-mode] {
|
||||
--code-bg: hsla(0, 2%, 14%, 1);
|
||||
--code-color: #f3f4f6ed;
|
||||
--inline-code-border: 0.5px solid var(--gray-600);
|
||||
}
|
||||
|
||||
#content {
|
||||
font-family: JetBrains Mono, LXGW WenKai Screen, -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", "Ubuntu";
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.docs-content h1 {
|
||||
font-size: 2.4rem;
|
||||
}
|
||||
|
||||
.docs-content .main-content h2 {
|
||||
margin-top: 2rem !important;
|
||||
margin-bottom: 1rem !important;
|
||||
font-size: 1.9rem;
|
||||
}
|
||||
|
||||
.docs-content .main-content h3 {
|
||||
margin-top: 1.6rem !important;
|
||||
margin-bottom: 0.6rem !important;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.docs-content .main-content h4 {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.docs-content .main-content ol, .docs-content .main-content ul {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.docs-content .main-content ol > li {
|
||||
margin-left: -1.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
.docs-content .main-content ol > li::before {
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.docs-content .main-content img, .docs-content .main-content svg:not(.gitinfo svg):not(a svg) {
|
||||
max-width: 80% !important;
|
||||
height: auto;
|
||||
display: block !important;
|
||||
margin: 0 auto !important;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
|
||||
div.code-toolbar {
|
||||
padding-top: 1.95rem !important;
|
||||
}
|
||||
|
||||
.docs-content .main-content pre code::before {
|
||||
background: #fc625d;
|
||||
border-radius: 50%;
|
||||
box-shadow: 20px 0 #fdbc40, 40px 0 #35cd4b;
|
||||
content: ' ';
|
||||
height: 12px;
|
||||
left: 12px;
|
||||
margin-top: -21px;
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.docs-content .main-content pre code {
|
||||
padding: 0 2.5rem 1.25rem .9rem;
|
||||
}
|
||||
|
||||
.docs-content .main-content code {
|
||||
font-size: .875em;
|
||||
padding: 1px 2px;
|
||||
background: var(--code-bg);
|
||||
border: var(--inline-code-border);
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
border-radius: .25rem;
|
||||
color: var(--code-color);
|
||||
}
|
||||
|
||||
li p {
|
||||
margin-top: 1rem !important;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.docs-content .main-content ul > li {
|
||||
margin-top: .3rem !important;
|
||||
margin-bottom: .3rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
height: 118px !important;
|
||||
}
|
||||
|
||||
/*
|
||||
footer a:hover {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
*/
|
||||
|
||||
.medium-zoom-overlay,
|
||||
.medium-zoom-image--opened {
|
||||
z-index: 1999;
|
||||
}
|
||||
|
||||
/* 徽章样式 */
|
||||
.github-badge {
|
||||
display: inline-block;
|
||||
border-radius: 4px;
|
||||
text-shadow: none;
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
line-height: 15px;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.github-badge .badge-subject {
|
||||
display: inline-block;
|
||||
background-color: #4D4D4D;
|
||||
padding: 4px 4px 4px 6px;
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
.github-badge .badge-value {
|
||||
display: inline-block;
|
||||
padding: 4px 6px 4px 4px;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
.github-badge .bg-brightgreen {
|
||||
background-color: #4DC820 !important;
|
||||
}
|
||||
.github-badge .bg-orange {
|
||||
background-color: #FFA500 !important;
|
||||
}
|
||||
.github-badge .bg-yellow {
|
||||
background-color: #D8B024 !important;
|
||||
}
|
||||
.github-badge .bg-blueviolet {
|
||||
background-color: #8833D7 !important;
|
||||
}
|
||||
.github-badge .bg-pink {
|
||||
background-color: #F26BAE !important;
|
||||
}
|
||||
.github-badge .bg-red {
|
||||
background-color: #e05d44 !important;
|
||||
}
|
||||
.github-badge .bg-blue {
|
||||
background-color: #007EC6 !important;
|
||||
}
|
||||
.github-badge .bg-lightgrey {
|
||||
background-color: #9F9F9F !important;
|
||||
}
|
||||
.github-badge .bg-grey, .github-badge .bg-gray {
|
||||
background-color: #555 !important;
|
||||
}
|
||||
.github-badge .bg-lightgrey, .github-badge .bg-lightgray {
|
||||
background-color: #9f9f9f !important;
|
||||
}
|
||||
|
||||
#fixed-box {
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
#fixed-box {
|
||||
display: none
|
||||
}
|
||||
}
|
||||
|
||||
.feedback-btn-wrapper {
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: 2rem;
|
||||
}
|
||||
#feedback-btn {
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 1.2rem 0.7rem;
|
||||
border-radius: 0.4rem;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
||||
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
user-select: none;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
color: #fff;
|
||||
background-color: #4d698e;
|
||||
transition: filter 0.4s ease;
|
||||
}
|
||||
|
||||
#feedback-btn svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
#feedback-btn span {
|
||||
font-weight: 700;
|
||||
font-size: 1rem;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
/**
|
||||
* Lotus Docs theme
|
||||
*
|
||||
* Adapted from a theme based on:
|
||||
* https://github.com/chriskempson/tomorrow-theme
|
||||
*
|
||||
* @author Colin Wilson <github.com/colinwilson>
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
:root {
|
||||
--prism-code-bg: #faf9f8;
|
||||
--prism-code-scrollbar-thumb-color: var(--gray-400);
|
||||
--prism-color: #333;
|
||||
--prism-bg: #f0f0f0;
|
||||
--prism-highlight-bg: var(--blue-200);
|
||||
--prism-copy-bg: var(--gray-500);
|
||||
--prism-copy-hover-bg: var(--gray-700);
|
||||
--prism-copy-success-bg: var(--emerald-500);
|
||||
--prism-token-punctuation: #666;
|
||||
--prism-token-deleted: #2b6cb0;
|
||||
--prism-token-function-name: #3182bd;
|
||||
--prism-token-function: #c53030;
|
||||
--prism-token-number: var(--cardinal-600);
|
||||
--prism-token-symbol: #333;
|
||||
--prism-token-builtin: #1a202c;
|
||||
--prism-token-regex: #2f855a;
|
||||
--prism-token-variable: var(--yellow-700);
|
||||
--prism-token-url: #4fd1c5;
|
||||
--prism-token-inserted: #38a169;
|
||||
}
|
||||
|
||||
[data-dark-mode] {
|
||||
--prism-code-bg: var(--gray-900);
|
||||
--prism-code-scrollbar-thumb-color: var(--gray-600);
|
||||
--prism-color: #f5fbff;
|
||||
--prism-bg: #32325d;
|
||||
--prism-highlight-bg: var(--blue-400);
|
||||
--prism-copy-bg: var(--gray-400);
|
||||
--prism-copy-hover-bg: var(--white);
|
||||
--prism-copy-success-bg: var(--emerald-200);
|
||||
--prism-token-punctuation: #ccc;
|
||||
--prism-token-deleted: #7fd3ed;
|
||||
--prism-token-function-name: #6196cc;
|
||||
--prism-token-function: #fda3f3;
|
||||
--prism-token-number: var(--cardinal-200);
|
||||
--prism-token-symbol: #ffffff;
|
||||
--prism-token-builtin: #a4cdfe;
|
||||
--prism-token-regex: #7ec699;
|
||||
--prism-token-variable: var(--yellow-100);
|
||||
--prism-token-url: #67cdcc;
|
||||
--prism-token-inserted: green;
|
||||
}
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: var(--prism-color) !important;
|
||||
background: var(--prism-code-bg) !important;
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
// padding: 1em;
|
||||
// margin: .5em 0;
|
||||
overflow: auto;
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: var(--prism-bg);
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre) > code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.line-highlight:before,
|
||||
.line-highlight[data-end]:after {
|
||||
background-color: var(--prism-highlight-bg);
|
||||
}
|
||||
|
||||
[data-copy-state="copy"] span:empty::before {
|
||||
background-color: var(--prism-copy-bg);
|
||||
}
|
||||
|
||||
[data-copy-state="copy"] span:empty:hover::before {
|
||||
background-color: var(--prism-copy-hover-bg);
|
||||
}
|
||||
|
||||
[data-copy-state="copy-success"] span:empty::before {
|
||||
background-color: var(--prism-copy-success-bg);
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.block-comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: var(--prism-token-punctuation);
|
||||
}
|
||||
|
||||
.token.tag,
|
||||
.token.attr-name,
|
||||
.token.namespace,
|
||||
.token.deleted {
|
||||
color: var(--prism-token-deleted);
|
||||
}
|
||||
|
||||
.token.function-name {
|
||||
color: var(--prism-token-function-name);
|
||||
}
|
||||
|
||||
.token.boolean,
|
||||
.token.function {
|
||||
color: var(--prism-token-function);
|
||||
}
|
||||
|
||||
.token.number {
|
||||
color: var(--prism-token-number);
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.class-name,
|
||||
.token.constant,
|
||||
.token.symbol {
|
||||
color: var(--prism-token-symbol);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.important,
|
||||
.token.atrule,
|
||||
.token.keyword,
|
||||
.token.builtin {
|
||||
color: var(--prism-token-builtin);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.attr-value,
|
||||
.token.regex {
|
||||
color: var(--prism-token-regex);
|
||||
}
|
||||
|
||||
.token.variable {
|
||||
color: var(--prism-token-variable);
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url {
|
||||
color: var(--prism-token-url);
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.token.inserted {
|
||||
color: var(--prism-token-inserted);
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/* Template Name: Lotus Docs
|
||||
Author: Colin Wilson
|
||||
E-mail: colin@aigis.uk
|
||||
Created: October 2022
|
||||
Version: 1.2.0
|
||||
File Description: Main CSS file for Lotus Docs
|
||||
*/
|
||||
|
||||
// Custom Font Variables
|
||||
$font-family-secondary: {{ .Site.Params.secondary_font | default "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', 'Ubuntu'" }};
|
||||
$font-family-sans-serif: {{ .Site.Params.sans_serif_font | default "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', 'Ubuntu'" }};
|
||||
$font-family-monospace: {{ .Site.Params.mono_font | default "SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace" }};
|
||||
|
||||
// Code Padding Variables
|
||||
$code-block-padding-top: {{ if eq .Site.Params.docs.prism true -}}0{{ else }}1.25rem 0 0 0{{ end }};
|
||||
|
||||
// Icon Fonts
|
||||
@import "custom/plugins/icons/google-material";
|
||||
|
||||
// Core files
|
||||
@import "../../scss/bootstrap/functions";
|
||||
@import "../../scss/bootstrap/variables";
|
||||
@import {{ printf "'%s%s'" "custom/colors/" (.Site.Params.docs.themeColor | default "blue") }}; // current theme color
|
||||
@import "../../scss/bootstrap/mixins";
|
||||
@import "../../scss/bootstrap/bootstrap";
|
||||
@import "variables";
|
||||
|
||||
{{ if and (.Site.Params.docsearch.appID) (.Site.Params.docsearch.apiKey) -}}
|
||||
@import "custom/plugins/docsearch/style";
|
||||
{{ end }}
|
||||
|
||||
// Structure
|
||||
@import "custom/structure/general";
|
||||
@import "custom/structure/content";
|
||||
@import "custom/structure/sidebar";
|
||||
@import "custom/structure/doc-nav";
|
||||
@import "custom/structure/toc";
|
||||
@import "custom/structure/footer";
|
||||
|
||||
// Components
|
||||
@import "custom/components/buttons";
|
||||
@import "custom/components/modal";
|
||||
@import "custom/components/breadcrumb";
|
||||
@import "custom/components/badge";
|
||||
@import "custom/components/backgrounds";
|
||||
@import "custom/components/alerts";
|
||||
@import "custom/components/card";
|
||||
@import "custom/components/forms";
|
||||
@import "custom/components/table";
|
||||
@import "custom/components/tabs";
|
||||
@import "custom/components/tooltip";
|
||||
|
||||
// Pages
|
||||
@import "custom/pages/features";
|
||||
@import "custom/pages/helper";
|
||||
|
||||
// Plugins
|
||||
|
||||
// Prism / Chroma
|
||||
{{- if eq .Site.Params.docs.prism true }}
|
||||
@import {{ printf "'%s%s'" "custom/plugins/prism/themes/" (.Site.Params.docs.prismTheme | default "lotusdocs") }}; // current prism theme
|
||||
@import "custom/plugins/prism/prism";
|
||||
{{- else }}
|
||||
@import "custom/plugins/chroma/default";
|
||||
{{- end -}}
|
||||
|
||||
// FlexSearch
|
||||
{{ if or (not (isset .Site.Params.flexsearch "enabled")) (eq .Site.Params.flexsearch.enabled true) -}}@import "custom/plugins/flexsearch/flexsearch";{{ end }}
|
||||
|
||||
// Feedback Widget
|
||||
{{ if .Site.Params.feedback.enabled | default false -}}@import "custom/plugins/feedback/feedback";{{ end}}
|
||||
|
||||
// Mermaid
|
||||
@import "custom/plugins/mermaid/mermaid";
|
||||
|
||||
// change
|
||||
@import "custom/pages/custom";
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<svg width="26" height="26" viewBox="0 0 1041 1348" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M340.837 0.33933L681.068 0.338989V0.455643C684.032 0.378397 686.999 0.339702 689.967 0.339702C735.961 0.3397 781.504 9.62899 823.997 27.6772C866.49 45.7254 905.099 72.1791 937.622 105.528C970.144 138.877 995.942 178.467 1013.54 222.04C1031.14 265.612 1040.2 312.312 1040.2 359.474L340.836 359.474L340.836 1347.84C296.157 1347.84 251.914 1338.55 210.636 1320.49C169.357 1302.43 131.85 1275.95 100.257 1242.58C68.6636 1209.21 43.6023 1169.59 26.5041 1125.99C11.3834 1087.43 2.75216 1046.42 0.957956 1004.81H0.605869L0.605897 368.098H0.70363C0.105752 341.831 2.23741 315.443 7.14306 289.411C20.2709 219.745 52.6748 155.754 100.257 105.528C147.839 55.3017 208.462 21.0975 274.461 7.24017C296.426 2.62833 318.657 0.339101 340.837 0.33933Z" fill="url(#paint0_linear_1172_228)"/>
|
||||
<path d="M633.639 904.645H513.029V576.37H635.422V576.377C678.161 576.607 720.454 585.093 759.951 601.37C799.997 617.874 836.384 642.064 867.033 672.559C897.683 703.054 921.996 739.257 938.583 779.101C955.171 818.944 963.709 861.648 963.709 904.775H633.639V904.645Z" fill="url(#paint1_linear_1172_228)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1172_228" x1="520.404" y1="0.338989" x2="520.404" y2="1347.84" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#326DFF"/>
|
||||
<stop offset="1" stop-color="#8EAEFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1172_228" x1="738.369" y1="576.37" x2="738.369" y2="904.775" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#326DFF"/>
|
||||
<stop offset="1" stop-color="#8EAEFF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
|
@ -0,0 +1,14 @@
|
|||
<svg width="26" height="26" viewBox="0 0 1041 1348" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M340.837 0.33933L681.068 0.338989V0.455643C684.032 0.378397 686.999 0.339702 689.967 0.339702C735.961 0.3397 781.504 9.62899 823.997 27.6772C866.49 45.7254 905.099 72.1791 937.622 105.528C970.144 138.877 995.942 178.467 1013.54 222.04C1031.14 265.612 1040.2 312.312 1040.2 359.474L340.836 359.474L340.836 1347.84C296.157 1347.84 251.914 1338.55 210.636 1320.49C169.357 1302.43 131.85 1275.95 100.257 1242.58C68.6636 1209.21 43.6023 1169.59 26.5041 1125.99C11.3834 1087.43 2.75216 1046.42 0.957956 1004.81H0.605869L0.605897 368.098H0.70363C0.105752 341.831 2.23741 315.443 7.14306 289.411C20.2709 219.745 52.6748 155.754 100.257 105.528C147.839 55.3017 208.462 21.0975 274.461 7.24017C296.426 2.62833 318.657 0.339101 340.837 0.33933Z" fill="url(#paint0_linear_1172_228)"/>
|
||||
<path d="M633.639 904.645H513.029V576.37H635.422V576.377C678.161 576.607 720.454 585.093 759.951 601.37C799.997 617.874 836.384 642.064 867.033 672.559C897.683 703.054 921.996 739.257 938.583 779.101C955.171 818.944 963.709 861.648 963.709 904.775H633.639V904.645Z" fill="url(#paint1_linear_1172_228)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1172_228" x1="520.404" y1="0.338989" x2="520.404" y2="1347.84" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#326DFF"/>
|
||||
<stop offset="1" stop-color="#8EAEFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1172_228" x1="738.369" y1="576.37" x2="738.369" y2="904.775" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#326DFF"/>
|
||||
<stop offset="1" stop-color="#8EAEFF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
|
@ -0,0 +1,5 @@
|
|||
<svg t="1728884315234" class="icon" viewBox="0 0 1224 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4847" width="28" height="28" fill="currentColor">
|
||||
|
||||
<path d="M1224.146926 401.768509a50.444385 50.444385 0 0 0-23.813954-38.631991c-6.095363-3.741292-61.752335-36.782364-141.475481-43.949671a317.253146 317.253146 0 0 0-135.884563 16.982943L746.964061 25.579507A50.444385 50.444385 0 0 0 703.077446 0h-418.268027A50.444385 50.444385 0 0 0 248.027055 84.97777c3.236848 3.447033 296.360763 315.739814 426.969683 459.653442-59.734559 55.762064-103.558119 83.800735-127.666331 96.832201l-200.894764-140.823909a50.045034 50.045034 0 0 0-6.97814-4.098606L79.416697 314.205464A50.444385 50.444385 0 0 0 0.744475 364.124387c0.210185 1.177036 20.619142 118.607361 42.036988 237.635091C86.815207 847.297523 91.775572 859.656397 95.054457 867.874628c5.065457 12.611096 14.334613 24.549601 44.895503 44.538188a595.916337 595.916337 0 0 0 69.361029 38.337733c49.519571 23.603769 128.212812 54.437899 221.59798 67.25918a623.009175 623.009175 0 0 0 85.061845 5.948234c131.491697 0 290.055215-44.138837 418.373119-211.404011 73.564728-96.054517 118.250046-163.944252 154.086578-218.592335 44.033745-67.070014 70.622139-107.551633 118.838564-150.177139a50.444385 50.444385 0 0 0 16.877851-42.015969zM673.693591 100.88877L834.443032 384.638437a413.097477 413.097477 0 0 0-63.055481 59.356226c-8.743693 10.04684-17.256183 19.568218-25.579507 28.711263C656.248242 373.961042 497.033151 203.332909 401.188819 100.88877zM305.491617 882.125167c-59.86067-22.594881-102.065806-47.85911-118.523287-59.692523-10.299062-45.610132-39.935138-209.638457-65.829922-355.780044l391.238243 274.270325a48.132351 48.132351 0 0 0 6.725918 3.951477l189.166445 132.689752a398.300458 398.300458 0 0 1-155.410744 44.138837c-97.336645 7.713787-188.262649-17.277202-247.366653-39.577824z m698.654734-343.442189c-34.932737 53.197808-78.398982 119.385045-149.819824 212.496972a503.371908 503.371908 0 0 1-58.641598 64.33761l-158.185184-110.830518c35.31107-23.813953 81.152405-60.070855 135.905581-114.803013a48.342536 48.342536 0 0 0 14.944149-15.154334c18.790533-19.379051 38.568936-40.859952 59.272153-64.694924 57.086229-65.745849 124.009113-96.243683 198.540692-90.673782a247.639894 247.639894 0 0 1 38.589955 6.011289c-28.290893 33.62959-51.936698 69.63427-80.605924 113.3107z" p-id="4848"></path>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<title>微信群</title>
|
||||
<path d="M21.6696 15.9962C21.9464 15.9925 22.2107 15.881 22.4064 15.6853C22.6021 15.4896 22.7137 15.2252 22.7173 14.9485C22.7173 14.3675 22.2518 13.9008 21.6696 13.9008C21.0875 13.9008 20.622 14.3675 20.622 14.9485C20.622 15.5318 21.0886 15.9962 21.6696 15.9962ZM16.5071 15.9962C16.7839 15.9925 17.0482 15.881 17.2439 15.6853C17.4396 15.4896 17.5512 15.2252 17.5548 14.9485C17.5548 14.3675 17.0881 13.9008 16.5071 13.9008C15.9238 13.9008 15.4595 14.3675 15.4595 14.9485C15.4595 15.5318 15.925 15.9962 16.5071 15.9962ZM24.1686 21.8762C24.0939 21.9177 24.0342 21.9818 23.9981 22.0594C23.9621 22.1369 23.9515 22.2239 23.968 22.3078C23.968 22.3638 23.968 22.421 23.9971 22.4793C24.1115 22.9658 24.3401 23.7405 24.3401 23.7697C24.3401 23.8548 24.3693 23.912 24.3693 23.9703C24.3693 24.0041 24.3626 24.0376 24.3496 24.0688C24.3367 24.1 24.3177 24.1284 24.2937 24.1522C24.2697 24.1761 24.2413 24.195 24.21 24.2078C24.1788 24.2206 24.1453 24.2272 24.1115 24.227C24.0531 24.227 24.0251 24.199 23.968 24.171L22.2775 23.1957C22.154 23.1277 22.0168 23.0886 21.8761 23.0813C21.791 23.0813 21.7046 23.0813 21.6475 23.1093C20.8448 23.3392 20.0141 23.4535 19.1263 23.4535C14.8563 23.4535 11.4181 20.587 11.4181 17.031C11.4181 13.4762 14.8563 10.6085 19.1263 10.6085C23.3951 10.6085 26.8333 13.4762 26.8333 17.031C26.8333 18.9525 25.802 20.7013 24.1686 21.8773V21.8762ZM19.4518 9.44883C19.3429 9.44521 19.234 9.44327 19.1251 9.443C14.2555 9.443 10.2515 12.7797 10.2515 17.0322C10.2515 17.6785 10.3448 18.3038 10.5175 18.8988H10.4136C9.39426 18.8885 8.38067 18.7442 7.39896 18.4695C7.31263 18.4403 7.22629 18.4403 7.13996 18.4403C6.96754 18.444 6.79921 18.4935 6.65229 18.5838L4.61296 19.7563C4.55463 19.7855 4.49746 19.8147 4.44029 19.8147C4.35703 19.8138 4.27744 19.7803 4.21856 19.7214C4.15968 19.6625 4.1262 19.5829 4.12529 19.4997C4.12529 19.4133 4.15329 19.3562 4.18246 19.2698C4.21046 19.2418 4.46946 18.2968 4.61296 17.7252C4.61296 17.6668 4.64096 17.5817 4.64096 17.5245C4.64065 17.4245 4.61721 17.3259 4.57248 17.2364C4.52775 17.147 4.46295 17.0691 4.38313 17.0088C2.40096 15.6053 1.16663 13.517 1.16663 11.1977C1.16663 6.93583 5.33163 3.5 10.4416 3.5C14.8341 3.5 18.5266 6.0305 19.4518 9.44767V9.44883ZM13.4388 9.9365C14.1073 9.9365 14.6358 9.38 14.6358 8.7395C14.6358 8.071 14.1073 7.5425 13.4388 7.5425C12.7703 7.5425 12.2418 8.071 12.2418 8.7395C12.2418 9.408 12.7703 9.9365 13.4388 9.9365ZM7.30213 9.9365C7.97063 9.9365 8.50029 9.38 8.50029 8.7395C8.50029 8.071 7.97063 7.5425 7.30213 7.5425C6.63479 7.5425 6.10513 8.071 6.10513 8.7395C6.10513 9.408 6.63479 9.9365 7.30213 9.9365Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
|
|
@ -0,0 +1,10 @@
|
|||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_280_54)">
|
||||
<path d="M21.6696 15.9962C21.9464 15.9925 22.2107 15.881 22.4064 15.6853C22.6021 15.4896 22.7137 15.2252 22.7173 14.9485C22.7173 14.3675 22.2518 13.9008 21.6696 13.9008C21.0875 13.9008 20.622 14.3675 20.622 14.9485C20.622 15.5318 21.0886 15.9962 21.6696 15.9962ZM16.5071 15.9962C16.7839 15.9925 17.0482 15.881 17.2439 15.6853C17.4396 15.4896 17.5512 15.2252 17.5548 14.9485C17.5548 14.3675 17.0881 13.9008 16.5071 13.9008C15.9238 13.9008 15.4595 14.3675 15.4595 14.9485C15.4595 15.5318 15.925 15.9962 16.5071 15.9962ZM24.1686 21.8762C24.0939 21.9177 24.0342 21.9818 23.9981 22.0594C23.9621 22.1369 23.9515 22.2239 23.968 22.3078C23.968 22.3638 23.968 22.421 23.9971 22.4793C24.1115 22.9658 24.3401 23.7405 24.3401 23.7697C24.3401 23.8548 24.3693 23.912 24.3693 23.9703C24.3693 24.0041 24.3626 24.0376 24.3496 24.0688C24.3367 24.1 24.3177 24.1284 24.2937 24.1522C24.2697 24.1761 24.2413 24.195 24.21 24.2078C24.1788 24.2206 24.1453 24.2272 24.1115 24.227C24.0531 24.227 24.0251 24.199 23.968 24.171L22.2775 23.1957C22.154 23.1277 22.0168 23.0886 21.8761 23.0813C21.791 23.0813 21.7046 23.0813 21.6475 23.1093C20.8448 23.3392 20.0141 23.4535 19.1263 23.4535C14.8563 23.4535 11.4181 20.587 11.4181 17.031C11.4181 13.4762 14.8563 10.6085 19.1263 10.6085C23.3951 10.6085 26.8333 13.4762 26.8333 17.031C26.8333 18.9525 25.802 20.7013 24.1686 21.8773V21.8762ZM19.4518 9.44883C19.3429 9.44521 19.234 9.44327 19.1251 9.443C14.2555 9.443 10.2515 12.7797 10.2515 17.0322C10.2515 17.6785 10.3448 18.3038 10.5175 18.8988H10.4136C9.39426 18.8885 8.38067 18.7442 7.39896 18.4695C7.31263 18.4403 7.22629 18.4403 7.13996 18.4403C6.96754 18.444 6.79921 18.4935 6.65229 18.5838L4.61296 19.7563C4.55463 19.7855 4.49746 19.8147 4.44029 19.8147C4.35703 19.8138 4.27744 19.7803 4.21856 19.7214C4.15968 19.6625 4.1262 19.5829 4.12529 19.4997C4.12529 19.4133 4.15329 19.3562 4.18246 19.2698C4.21046 19.2418 4.46946 18.2968 4.61296 17.7252C4.61296 17.6668 4.64096 17.5817 4.64096 17.5245C4.64065 17.4245 4.61721 17.3259 4.57248 17.2364C4.52775 17.147 4.46295 17.0691 4.38313 17.0088C2.40096 15.6053 1.16663 13.517 1.16663 11.1977C1.16663 6.93583 5.33163 3.5 10.4416 3.5C14.8341 3.5 18.5266 6.0305 19.4518 9.44767V9.44883ZM13.4388 9.9365C14.1073 9.9365 14.6358 9.38 14.6358 8.7395C14.6358 8.071 14.1073 7.5425 13.4388 7.5425C12.7703 7.5425 12.2418 8.071 12.2418 8.7395C12.2418 9.408 12.7703 9.9365 13.4388 9.9365ZM7.30213 9.9365C7.97063 9.9365 8.50029 9.38 8.50029 8.7395C8.50029 8.071 7.97063 7.5425 7.30213 7.5425C6.63479 7.5425 6.10513 8.071 6.10513 8.7395C6.10513 9.408 6.63479 9.9365 7.30213 9.9365Z" fill="#2CE25E"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_280_54">
|
||||
<rect width="28" height="28" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 127 KiB |
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |