Commit 92e69588 by Archer Committed by GitHub

perf: workflow v1 abort check (#6945)

* perf: workflow v1 abort check

* fix: avoid direct llms type deps

* pro

* fix: cast remark include through unknown

* fix: handle client reset abort ordering
parent 82852fad
......@@ -12,7 +12,7 @@ import { SidebarScrollFix } from '@/components/sidebarScrollFix';
import { CategorySwitcher } from '@/components/docs/categorySwitcher';
import { LanguageSwitcher } from '@/components/docs/languageSwitcher';
import { normalizePageTreeSections } from '@/lib/page-tree';
import { BookOpen, Code, Lightbulb, CircleHelp, Server } from 'lucide-react';
import { BookOpen, Code, CircleHelp, Handshake, Server } from 'lucide-react';
export default async function Layout({
params,
......@@ -31,11 +31,6 @@ export default async function Layout({
url: getLocalizedPath('/guide', lang)
},
{
icon: <Lightbulb className={iconClass} />,
title: t('common:use-cases', lang),
url: getLocalizedPath('/use-cases', lang)
},
{
icon: <Server className={iconClass} />,
title: t('common:selfHost', lang),
url: getLocalizedPath('/self-host', lang)
......@@ -49,6 +44,11 @@ export default async function Layout({
icon: <CircleHelp className={iconClass} />,
title: t('common:faq', lang),
url: getLocalizedPath('/faq', lang)
},
{
icon: <Handshake className={iconClass} />,
title: t('common:businessConsultation', lang),
url: 'https://fael3z0zfze.feishu.cn/share/base/form/shrcnjJWtKqjOI9NbQTzhNyzljc'
}
];
......
......@@ -10,10 +10,12 @@ import { i18n } from '@/lib/i18n';
export const revalidate = false;
const includeMdxFiles = remarkInclude as unknown as typeof remarkMdx;
const processor = remark()
.use(remarkMdx)
// https://fumadocs.vercel.app/docs/mdx/include
.use(remarkInclude)
.use(includeMdxFiles)
// gfm styles
.use(remarkGfm)
// .use(your remark plugins)
......
......@@ -79,6 +79,23 @@ export function SwitcherDropdown({
);
if (item.href) {
if (/^https?:\/\//i.test(item.href)) {
return (
<a
key={item.key}
href={item.href}
target="_blank"
rel="noreferrer noopener"
onClick={() => selectItem(item)}
className={className}
role="option"
aria-selected={active}
>
{content}
</a>
);
}
return (
<Link
key={item.key}
......
......@@ -28,7 +28,3 @@ When the user asks "What is the second point?", the system searches the Knowledg
Calls an AI model to complete and refine the user's current question. It primarily resolves coreferences (pronouns and vague references), making search queries more complete and reliable. This improves Knowledge Base search accuracy in multi-turn conversations.
The main challenge is that the model may not have a clear understanding of "completion" and often struggles to determine how to properly refine queries with long context.
## Examples
- [Integrate Google Search](../../../../use-cases/app-cases/google_search.en.mdx)
......@@ -19,17 +19,12 @@ description: 问题优化模块介绍和使用
![](/imgs/coreferenceResolution2.webp)
用户在提问“第二点是什么”的时候,只会去知识库里查找“第二点是什么”,压根查不到内容。实际上需要查询的是“QA结构是什么”。因此我们需要引入一个【问题优化】模块,来对用户当前的问题进行补全,从而使得知识库搜索能够搜索到合适的内容。使用补全后效果如下:
用户在提问“第二点是什么”的时候,只会去知识库里查找“第二点是什么”,压根查不到内容。实际上需要查询的是“QA 结构是什么”。因此我们需要引入一个【问题优化】模块,来对用户当前的问题进行补全,从而使得知识库搜索能够搜索到合适的内容。使用补全后效果如下:
![](/imgs/coreferenceResolution3.webp)
## 功能
调用 AI 去对用户当前的问题进行补全。目前主要是补全“指代”词,使得检索词更加的完善可靠,从而增强上下文连续对话的知识库搜索能力。
遇到最大的难题在于:模型对于【补全】的概念可能不清晰,且对于长上下文往往无法准确的知道应该如何补全。
## 示例
- [接入谷歌搜索](../../../../use-cases/app-cases/google_search.mdx)
......@@ -46,7 +46,7 @@ Usage is the same as Postman and ApiFox.
Use `{{key}}` to reference variables. For example:
| key | value |
| --- | --- |
| ------------- | ------------------ |
| appId | `{{appId}}` |
| Authorization | Bearer `{{token}}` |
......@@ -59,7 +59,6 @@ Write a custom JSON body and use `{{key}}` to reference variables. For example:
<Tabs items={['Sample variables','Body declaration in HTTP node','Final parsed result']}>
<Tab value="Sample variables" >
```json
{
"string": "字符串",
......@@ -73,11 +72,9 @@ Write a custom JSON body and use `{{key}}` to reference variables. For example:
}
```
</Tab>
<Tab value="Body declaration in HTTP node" >
When referencing a `string` in the Body, wrap it in quotes: `"{{string}}"`.
```json
......@@ -92,11 +89,9 @@ When referencing a `string` in the Body, wrap it in quotes: `"{{string}}"`.
}
```
</Tab>
<Tab value="Final parsed result" >
```json
{
"string": "字符串",
......@@ -112,7 +107,6 @@ When referencing a `string` in the Body, wrap it in quotes: `"{{string}}"`.
}
```
</Tab>
</Tabs>
......@@ -125,11 +119,10 @@ Syntax reference: https://github.com/JSONPath-Plus/JSONPath?tab=readme-ov-file
<Tabs items={['API response example','Extraction example']}>
<Tab value="API response example" >
```json
{
"message": "测试",
"data":{
"data": {
"user": {
"name": "xxx",
"age": 12
......@@ -146,33 +139,29 @@ Syntax reference: https://github.com/JSONPath-Plus/JSONPath?tab=readme-ov-file
}
```
</Tab>
<Tab value="Extraction example" >
```json
{
"$.message": "测试",
"$.data.user": { "name": "xxx", "age": 12 },
"$.data.user.name": "xxx",
"$.data.user.age": 12,
"$.data.list": [ { "name": "xxx", "age": 50 }, [{ "test": 22 }] ],
"$.data.list": [{ "name": "xxx", "age": 50 }, [{ "test": 22 }]],
"$.data.list[0]": { "name": "xxx", "age": 50 },
"$.data.list[0].name": "xxx",
"$.data.list[0].age": 50,
"$.data.list[1]": [ { "test": 22 } ],
"$.data.list[1]": [{ "test": 22 }],
"$.data.list[1][0]": { "test": 22 },
"$.data.list[1][0].test": 22,
"$.data.psw": "xxx"
}
```
</Tab>
</Tabs>
Configure the `key` to extract values from FastGPT's parsed format, following standard JavaScript object access rules. For example:
1. To get the `message` content, set the `key` to `message`.
......@@ -183,55 +172,53 @@ Configure the `key` to extract values from FastGPT's parsed format, following st
Starting from FastGPT v4.6.8, output formatting was added, primarily converting `JSON` to `string`. If you select `string` as the output type, the HTTP node will convert the corresponding key's value to a JSON string. This lets you pipe HTTP output directly into a `Text Processing` node, append appropriate prompts, and feed the result into `AI Chat`.
<Alert context="warning">
The HTTP node is extremely versatile. You can integrate public APIs to extend your workflow capabilities.
If you don't want to deploy additional services, use [Laf](https://laf.dev/) to quickly develop and publish APIs -- write and ship instantly with no deployment needed.
</Alert>
## Laf HTTP Integration Example
Here is a POST request example written in Laf:
```ts
import cloud from '@lafjs/cloud'
const db = cloud.database()
import cloud from '@lafjs/cloud';
const db = cloud.database();
type RequestType = {
appId: string;
appointment: string;
action: 'post' | 'delete' | 'put' | 'get'
}
action: 'post' | 'delete' | 'put' | 'get';
};
export default async function (ctx: FunctionContext) {
try {
// Get parameters from body
const { appId, appointment, action } = ctx.body as RequestType
const { appId, appointment, action } = ctx.body as RequestType;
const parseBody = JSON.parse(appointment)
const parseBody = JSON.parse(appointment);
if (action === 'get') {
return await getRecord(parseBody)
return await getRecord(parseBody);
}
if (action === 'post') {
return await createRecord(parseBody)
return await createRecord(parseBody);
}
if (action === 'put') {
return await putRecord(parseBody)
return await putRecord(parseBody);
}
if (action === 'delete') {
return await removeRecord(parseBody)
return await removeRecord(parseBody);
}
return {
response: "Error"
}
response: 'Error'
};
} catch (err) {
return {
response: "Error"
}
response: 'Error'
};
}
}
```
......@@ -239,15 +226,9 @@ export default async function (ctx: FunctionContext) {
## Use Cases
The HTTP node enables unlimited extensibility, such as:
- Database operations
- External data source calls
- Web searches
- Sending emails
- ....
## Related Examples
- [Google Search](../../../../use-cases/app-cases/google_search.en.mdx)
- [Send Lark Webhook](../../../../use-cases/app-cases/feishu_webhook.en.mdx)
- [Lab Appointment (Database Operations)](../../../../use-cases/app-cases/lab_appointment.en.mdx)
......@@ -18,35 +18,35 @@ import { Alert } from '@/components/docs/Alert';
HTTP 模块会向对应的地址发送一个 `HTTP` 请求,实际操作与 Postman 和 ApiFox 这类直流工具使用差不多。
- Params 为路径请求参数,GET请求中用的居多。
- Body 为请求体,POST/PUT请求中用的居多。
- Params 为路径请求参数,GET 请求中用的居多。
- Body 为请求体,POST/PUT 请求中用的居多。
- Headers 为请求头,用于传递一些特殊的信息。
- 自定义变量中可以接收前方节点的输出作为变量
- 3 种数据中均可以通过 `{{}}` 来引用变量。
- url 也可以通过 `{{}}` 来引用变量。
- 变量来自于`全局变量`、`系统变量`、`前方节点输出`
- URL 也可以通过 `{{}}` 来引用变量。
- 变量来自于 `全局变量`、`系统变量`、`前方节点输出`
## 参数结构
### 系统变量说明
你可以将鼠标放置在`请求参数`旁边的问号中,里面会提示你可用的变量。
你可以将鼠标放置在 `请求参数` 旁边的问号中,里面会提示你可用的变量。
- appId: 应用的ID
- chatId: 当前对话的ID,测试模式下不存在。
- responseChatItemId: 当前对话中,响应的消息ID,测试模式下不存在。
- appId: 应用的 ID
- chatId: 当前对话的 ID,测试模式下不存在。
- responseChatItemId: 当前对话中,响应的消息 ID,测试模式下不存在。
- variables: 当前对话的全局变量。
- cTime: 当前时间。
- histories: 历史记录(默认最多取10条,无法修改长度)
- histories: 历史记录(默认最多取 10 条,无法修改长度)
### Params, Headers
不多描述,使用方法和Postman, ApiFox 基本一致。
不多描述,使用方法和 Postman, ApiFox 基本一致。
可通过 `{{key}}` 来引入变量。例如:
| key | value |
| --- | --- |
| ------------- | ------------------ |
| appId | `{{appId}}` |
| Authorization | Bearer `{{token}}` |
......@@ -54,12 +54,11 @@ HTTP 模块会向对应的地址发送一个 `HTTP` 请求,实际操作与 Pos
只有特定请求类型下会生效。
可以写一个`自定义的 Json`,并通过 `{{key}}` 来引入变量。例如:
可以写一个 `自定义的 Json`,并通过 `{{key}}` 来引入变量。例如:
<Tabs items={['假设有一组变量','Http 模块中的Body声明','最终得到的解析']}>
<Tab value="假设有一组变量" >
```json
{
"string": "字符串",
......@@ -73,12 +72,10 @@ HTTP 模块会向对应的地址发送一个 `HTTP` 请求,实际操作与 Pos
}
```
</Tab>
<Tab value="Http 模块中的Body声明" >
注意,在 Body 中,你如果引用`字符串`,则需要加上`""`,例如:`"{{string}}"`。
注意,在 Body 中,你如果引用 `字符串`,则需要加上 `""`,例如:`"{{string}}"`。
```json
{
......@@ -92,11 +89,9 @@ HTTP 模块会向对应的地址发送一个 `HTTP` 请求,实际操作与 Pos
}
```
</Tab>
<Tab value="最终得到的解析" >
```json
{
"string": "字符串",
......@@ -112,24 +107,22 @@ HTTP 模块会向对应的地址发送一个 `HTTP` 请求,实际操作与 Pos
}
```
</Tab>
</Tabs>
### 如何获取返回值
从图中可以看出,FastGPT可以添加多个返回值,这个返回值并不代表接口的返回值,而是代表`如何解析接口返回值`,可以通过 `JSON path` 的语法,来`提取`接口响应的值。
从图中可以看出,FastGPT 可以添加多个返回值,这个返回值并不代表接口的返回值,而是代表 `如何解析接口返回值`,可以通过 `JSON path` 的语法,来 `提取` 接口响应的值。
语法可以参考: https://github.com/JSONPath-Plus/JSONPath?tab=readme-ov-file
<Tabs items={['接口响应示例','提取示例']}>
<Tab value="接口响应示例" >
```json
{
"message": "测试",
"data":{
"data": {
"user": {
"name": "xxx",
"age": 12
......@@ -146,92 +139,86 @@ HTTP 模块会向对应的地址发送一个 `HTTP` 请求,实际操作与 Pos
}
```
</Tab>
<Tab value="提取示例" >
```json
{
"$.message": "测试",
"$.data.user": { "name": "xxx", "age": 12 },
"$.data.user.name": "xxx",
"$.data.user.age": 12,
"$.data.list": [ { "name": "xxx", "age": 50 }, [{ "test": 22 }] ],
"$.data.list": [{ "name": "xxx", "age": 50 }, [{ "test": 22 }]],
"$.data.list[0]": { "name": "xxx", "age": 50 },
"$.data.list[0].name": "xxx",
"$.data.list[0].age": 50,
"$.data.list[1]": [ { "test": 22 } ],
"$.data.list[1]": [{ "test": 22 }],
"$.data.list[1][0]": { "test": 22 },
"$.data.list[1][0].test": 22,
"$.data.psw": "xxx"
}
```
</Tab>
</Tabs>
你可以配置对应的 `key` 来从 `FastGPT 转化后的格式` 获取需要的值,该规则遵守 JS 的对象取值规则。例如:
你可以配置对应的`key`来从`FastGPT 转化后的格式`获取需要的值,该规则遵守 JS 的对象取值规则。例如:
1. 获取`message`的内容,那么你可以配置`message`的`key`为`message`,这样就可以获取到`message`的内容。
2. 获取`user的name`,则`key`可以为:`data.user.name`。
3. 获取list中第二个元素,则`key`可以为:`data.list[1]`,然后输出类型选择字符串,则获自动获取到`[ { "test": 22 } ]`的`json`字符串。
1. 获取 `message` 的内容,那么你可以配置 `message` 的 `key` 为 `message`,这样就可以获取到 `message` 的内容。
2. 获取 `user的name`,则 `key` 可以为:`data.user.name`。
3. 获取 list 中第二个元素,则 `key` 可以为:`data.list[1]`,然后输出类型选择字符串,则获自动获取到 `[ { "test": 22 } ]` 的 `json` 字符串。
### 自动格式化输出
FastGPT v4.6.8 后,加入了出参格式化功能,主要以`json`格式化成`字符串`为主。如果你的输出类型选择了`字符串`,则会将`HTTP`对应`key`的值,转成`json`字符串进行输出。因此,未来你可以直接从`HTTP`接口输出内容至`文本加工`中,然后拼接适当的提示词,最终输入给`AI对话`。
FastGPT v4.6.8 后,加入了出参格式化功能,主要以 `json` 格式化成 `字符串` 为主。如果你的输出类型选择了 `字符串`,则会将 `HTTP` 对应 `key` 的值,转成 `json` 字符串进行输出。因此,未来你可以直接从 `HTTP` 接口输出内容至 `文本加工` 中,然后拼接适当的提示词,最终输入给 `AI对话`。
<Alert context="warning">
HTTP模块非常强大,你可以对接一些公开的API,来提高编排的功能。
HTTP 模块非常强大,你可以对接一些公开的 API,来提高编排的功能。
如果你不想额外部署服务,可以使用 [Laf](https://laf.dev/) 来快速开发上线接口,即写即发,无需部署。
</Alert>
## laf 对接 HTTP 示例
下面是在 Laf 编写的 POST 请求示例:
```ts
import cloud from '@lafjs/cloud'
const db = cloud.database()
import cloud from '@lafjs/cloud';
const db = cloud.database();
type RequestType = {
appId: string;
appointment: string;
action: 'post' | 'delete' | 'put' | 'get'
}
action: 'post' | 'delete' | 'put' | 'get';
};
export default async function (ctx: FunctionContext) {
try {
// 从 body 中获取参数
const { appId, appointment, action } = ctx.body as RequestType
const { appId, appointment, action } = ctx.body as RequestType;
const parseBody = JSON.parse(appointment)
const parseBody = JSON.parse(appointment);
if (action === 'get') {
return await getRecord(parseBody)
return await getRecord(parseBody);
}
if (action === 'post') {
return await createRecord(parseBody)
return await createRecord(parseBody);
}
if (action === 'put') {
return await putRecord(parseBody)
return await putRecord(parseBody);
}
if (action === 'delete') {
return await removeRecord(parseBody)
return await removeRecord(parseBody);
}
return {
response: "异常"
}
response: '异常'
};
} catch (err) {
return {
response: "异常"
}
response: '异常'
};
}
}
```
......@@ -239,15 +226,9 @@ export default async function (ctx: FunctionContext) {
## 作用
通过 HTTP 模块你可以无限扩展,比如:
- 操作数据库
- 调用外部数据源
- 执行联网搜索
- 发送邮箱
- ....
## 相关示例
- [谷歌搜索](../../../../use-cases/app-cases/google_search.mdx)
- [发送飞书webhook](../../../../use-cases/app-cases/feishu_webhook.mdx)
- [实验室预约(操作数据库)](../../../../use-cases/app-cases/lab_appointment.mdx)
- ……
......@@ -21,7 +21,3 @@ In the example above, any input will have "The user's question is:" prepended to
## Use Cases
Provide custom-formatted text to any module, or preprocess system prompts for AI modules.
## Examples
- [Google Search Integration](../../../../use-cases/app-cases/google_search.en.mdx)
......@@ -12,17 +12,12 @@ description: FastGPT 文本加工模块介绍
![](/imgs/string.png)
## 功能
对输入文本进行固定加工处理,入参仅支持字符串和数字格式,入参以变量形式使用在文本编辑区域。
根据上方示例图的处理方式,对任何输入都会在前面拼接“用户的问题是:”。
## 作用
给任意模块输入自定格式文本,或处理 AI 模块系统提示词。
## 示例
- [接入谷歌搜索](../../../../use-cases/app-cases/google_search.mdx)
......@@ -39,7 +39,7 @@ Models that support `function calling` can invoke multiple tools in a single tur
### How to Use
| | |
| --- | --- |
| ------------------------- | ------------------------- |
| ![](/imgs/flow-tool3.png) | ![](/imgs/flow-tool4.png) |
In the advanced workflow editor, drag from the tool calling connection point. Eligible tools display a diamond icon at the top, which you can connect to the diamond at the bottom of the tool calling module.
......@@ -71,8 +71,3 @@ Tool Calling Termination ends the current call cycle. Place it after a tool node
Custom variables extend tool input capabilities. For nodes that aren't recognized as tool parameters or can't be directly tool-called, you can define custom tool variables with appropriate parameter descriptions. The tool calling node will then invoke this node and its downstream workflow accordingly.
![](/imgs/flow-tool7.png)
### Related Examples
- [Google Search](https://doc.fastgpt.in/docs/use-cases/app-cases/google_search/)
- [Send Lark Webhook](https://doc.fastgpt.in/docs/use-cases/app-cases/feishu_webhook/)
......@@ -3,57 +3,54 @@ title: 工具调用&终止
description: FastGPT 工具调用模块介绍
---
![](/imgs/flow-tool1.png)
### **什么是工具**
工具可以是一个系统模块,例如:AI对话、知识库搜索、HTTP模块等。也可以是一个插件。
工具可以是一个系统模块,例如:AI 对话、知识库搜索、HTTP 模块等。也可以是一个插件。
工具调用可以让 LLM 更动态的决策流程,而不都是固定的流程。(当然,缺点就是费tokens)
工具调用可以让 LLM 更动态的决策流程,而不都是固定的流程。(当然,缺点就是费 tokens)
### **工具的组成**
1. 工具介绍。通常是模块的介绍或插件的介绍,这个介绍会告诉LLM,这个工具的作用是什么。
1. 工具介绍。通常是模块的介绍或插件的介绍,这个介绍会告诉 LLM,这个工具的作用是什么。
2. 工具参数。对于系统模块来说,工具参数已经是固定的,无需额外配置。对于插件来说,工具参数是一个可配置项。
### **工具是如何运行的**
要了解工具如何运行的,首先需要知道它的运行条件。
1. 需要工具的介绍(或者叫描述)。这个介绍会告诉LLM,这个工具的作用是什么,LLM会根据上下文语义,决定是否需要调用这个工具。
2. 工具的参数。有些工具调用时,可能需要一些特殊的参数。参数中有2个关键的值:`参数介绍`和`是否必须`。
1. 需要工具的介绍(或者叫描述)。这个介绍会告诉 LLM,这个工具的作用是什么,LLM 会根据上下文语义,决定是否需要调用这个工具。
2. 工具的参数。有些工具调用时,可能需要一些特殊的参数。参数中有 2 个关键的值:`参数介绍` 和 `是否必须`。
结合工具的介绍、参数介绍和参数是否必须,LLM会决定是否调用这个工具。有以下几种情况:
结合工具的介绍、参数介绍和参数是否必须,LLM 会决定是否调用这个工具。有以下几种情况:
1. 无参数的工具:直接根据工具介绍,决定是否需要执行。例如:获取当前时间。
2. 有参数的工具:
1. 无必须的参数:尽管上下文中,没有适合的参数,也可以调用该工具。但有时候,LLM会自己伪造一个参数。
2. 有必须的参数:如果没有适合的参数,LLM可能不会调用该工具。可以通过提示词,引导用户提供参数。
1. 无必须的参数:尽管上下文中,没有适合的参数,也可以调用该工具。但有时候,LLM 会自己伪造一个参数。
2. 有必须的参数:如果没有适合的参数,LLM 可能不会调用该工具。可以通过提示词,引导用户提供参数。
#### **工具调用逻辑**
在支持`函数调用`的模型中,可以一次性调用多个工具,调用逻辑如下:
在支持 `函数调用` 的模型中,可以一次性调用多个工具,调用逻辑如下:
![](/imgs/flow-tool2.png)
### **怎么用**
| | |
| --- | --- |
| ------------------------- | ------------------------- |
| ![](/imgs/flow-tool3.png) | ![](/imgs/flow-tool4.png) |
高级编排中,拖动工具调用的连接点,可用的工具头部会出现一个菱形,可以将它与工具调用模块底部的菱形相连接。
被连接的工具,会自动分离工具输入与普通的输入,并且可以编辑`介绍`,可以通过调整介绍,使得该工具调用时机更加精确。
被连接的工具,会自动分离工具输入与普通的输入,并且可以编辑 `介绍`,可以通过调整介绍,使得该工具调用时机更加精确。
关于工具调用,如何调试仍然是一个玄学,所以建议,不要一次性增加太多工具,选择少量工具调优后再进一步尝试。
#### 用途
默认情况下,工具调用节点,在决定调用工具后,会将工具运行的结果,返回给AI,让 AI 对工具运行的结果进行总结输出。有时候,如果你不需要 AI 进行进一步的总结输出,可以使用该节点,将其接入对于工具流程的末尾。
默认情况下,工具调用节点,在决定调用工具后,会将工具运行的结果,返回给 AI,让 AI 对工具运行的结果进行总结输出。有时候,如果你不需要 AI 进行进一步的总结输出,可以使用该节点,将其接入对于工具流程的末尾。
如下图,在执行知识库搜索后,发送给了 HTTP 请求,搜索将不会返回搜索的结果给工具调用进行 AI 总结。
......@@ -74,8 +71,3 @@ description: FastGPT 工具调用模块介绍
自定义变量可以扩展工具的变量输入,即对于一些未被视作工具参数或无法工具调用的节点,可以自定义工具变量,填上对应的参数描述,那么工具调用便会相对应的调用这个节点,进而调用其之后的工作流。
![](/imgs/flow-tool7.png)
### **相关示例**
- [谷歌搜索](https://doc.fastgpt.in/docs/use-cases/app-cases/google_search/)
- [发送飞书webhook](https://doc.fastgpt.in/docs/use-cases/app-cases/feishu_webhook/)
......@@ -10,12 +10,11 @@ description: 'FastGPT V4.15.0-beta2 更新说明'
## ⚙️ 优化
1. 优化 OTEL 日志采集格式。
2. 增加 workflow zod 检查鲁棒性。
3. 禁用工作流无效连接模式。
4. 增加工作流节点,名字超长适配。
5. 知识库搜索测试交互。
6. 知识库数据编辑弹窗。
7. reason hide 开关完善,确保只是 UI 不显示,但是 request llm 时候依然可以保留。
2. 禁用工作流无效连接模式。
3. 增加工作流节点,名字超长适配。
4. 知识库搜索测试交互。
5. 知识库数据编辑弹窗。
6. reason hide 开关完善,确保只是 UI 不显示,但是 request llm 时候依然可以保留。
## 🐛 修复
......
......@@ -208,12 +208,3 @@ description: FastGPT Toc
- [/en/self-host/upgrading/outdated/498](/en/self-host/upgrading/outdated/498)
- [/en/self-host/upgrading/outdated/499](/en/self-host/upgrading/outdated/499)
- [/en/self-host/upgrading/upgrade-intruction](/en/self-host/upgrading/upgrade-intruction)
- [/en/use-cases/app-cases/dalle3](/en/use-cases/app-cases/dalle3)
- [/en/use-cases/app-cases/english_essay_correction_bot](/en/use-cases/app-cases/english_essay_correction_bot)
- [/en/use-cases/app-cases/feishu_webhook](/en/use-cases/app-cases/feishu_webhook)
- [/en/use-cases/app-cases/fixingEvidence](/en/use-cases/app-cases/fixingEvidence)
- [/en/use-cases/app-cases/google_search](/en/use-cases/app-cases/google_search)
- [/en/use-cases/app-cases/lab_appointment](/en/use-cases/app-cases/lab_appointment)
- [/en/use-cases/app-cases/multi_turn_translation_bot](/en/use-cases/app-cases/multi_turn_translation_bot)
- [/en/use-cases/app-cases/submit_application_template](/en/use-cases/app-cases/submit_application_template)
- [/en/use-cases/app-cases/translate-subtitle-using-gpt](/en/use-cases/app-cases/translate-subtitle-using-gpt)
......@@ -212,12 +212,3 @@ description: FastGPT 文档目录
- [/self-host/upgrading/outdated/498](/self-host/upgrading/outdated/498)
- [/self-host/upgrading/outdated/499](/self-host/upgrading/outdated/499)
- [/self-host/upgrading/upgrade-intruction](/self-host/upgrading/upgrade-intruction)
- [/use-cases/app-cases/dalle3](/use-cases/app-cases/dalle3)
- [/use-cases/app-cases/english_essay_correction_bot](/use-cases/app-cases/english_essay_correction_bot)
- [/use-cases/app-cases/feishu_webhook](/use-cases/app-cases/feishu_webhook)
- [/use-cases/app-cases/fixingEvidence](/use-cases/app-cases/fixingEvidence)
- [/use-cases/app-cases/google_search](/use-cases/app-cases/google_search)
- [/use-cases/app-cases/lab_appointment](/use-cases/app-cases/lab_appointment)
- [/use-cases/app-cases/multi_turn_translation_bot](/use-cases/app-cases/multi_turn_translation_bot)
- [/use-cases/app-cases/submit_application_template](/use-cases/app-cases/submit_application_template)
- [/use-cases/app-cases/translate-subtitle-using-gpt](/use-cases/app-cases/translate-subtitle-using-gpt)
---
title: English Essay Correction Bot
description: Build an English essay correction bot with FastGPT to detect and fix language errors
---
FastGPT provides a straightforward way to build applications powered by LLM models.
This article demonstrates how to use **Workflows** by building an English essay correction bot.
## Building Process
### 1. Create a Workflow
![](../../../public/imgs/spellcheck1.png)
You can start from the *Multi-turn Translation Bot* template.
> The Multi-turn Translation Bot was created by @米开朗基杨 — also a great workflow worth studying.
### 2. Get Input and Analyze with LLM
We want the LLM to process text and return structured data that we handle ourselves.
![](../../../public/imgs/spellcheck2.png)
The **System Prompt** is the most critical parameter. Here is a reference prompt:
~~~markdown
## Role
Senior English Writing Expert
## Task
Analyze the input text. Find all types of errors, including but not limited to spelling mistakes and grammar errors.
Note: Ignore spacing issues around punctuation.
Note: For sentences with errors, the suggestion should identify the specific part of the sentence and propose what to replace it with.
## Output Format
Do not use Markdown syntax. Output in JSON format.
Output the "reason" field in Chinese.
Directly output a list whose members are objects of the same type, defined as follows:
```
{
"raw": string; // the original text
"reason": string; // the reason
"suggestion": string; // suggested correction
}
```
~~~
You can disable AI reply in the model selection window.
This hides the raw JSON output from the user.
![](../../../public/imgs/spellcheck3.png)
### 3. Data Processing
The LLM outputs JSON, which needs further processing. Use the Code Execution module for this.
![](../../../public/imgs/spellcheck4.png)
```javascript
function main({data}){
const array = JSON.parse(data)
return {
content: array.map(
(item, index) => {
return `
## Analysis ${index+1}
- **Error**: ${item.raw}
- **Analysis**: ${item.reason}
- **Suggestion**: ${item.suggestion}
`
}
).join('')
}
}
```
The code parses the JSON into an object, then concatenates it into a Markdown-formatted string.
FastGPT's Specified Reply module renders Markdown as HTML in the response.
## Publishing
Publish through the available publishing channels.
![](../../../public/imgs/spellcheck5.png)
You can access it via URL or embed it directly into your webpage.
> [Click to try it](https://share.fastgpt.in/chat/share?shareId=b4r173wkcjae7wpnexcvmyc3)
---
title: 英语作文纠错机器人
description: 使用 FastGPT 创建一个用于英语作文纠错的机器人,帮助用户检测并纠正语言错误
---
FastGPT 提供了一种基于 LLM Model 搭建应用的简便方式。
本文通过搭建一个英语作文纠错机器人,介绍一下如何使用 **工作流**
## 搭建过程
### 1. 创建工作流
![](../../../public/imgs/spellcheck1.png)
可以从 *多轮翻译机器人* 开始创建。
> 多轮翻译机器人是 @米开朗基杨 同学创建的,同样也是一个值得学习的工作流。
### 2. 获取输入,使用大模型进行分析
我们期望让大模型处理文字,返回一个结构化的数据,由我们自己处理。
![](../../../public/imgs/spellcheck2.png)
**提示词** 是最重要的一个参数,这里提供的提示词仅供参考:
~~~markdown
## 角色
资深英语写作专家
## 任务
对输入的原文进行分析。 找出其中的各种错误, 包括但不限于单词拼写错误、 语法错误等。
注意: 忽略标点符号前后空格的问题。
注意: 对于存在错误的句子, 提出修改建议是指指出这个句子中的具体部分, 然后提出将这一个部分修改替换为什么。
## 输出格式
不要使用 Markdown 语法, 输入 JSON 格式的内容。
输出的"reason"的内容使用中文。
直接输出一个列表, 其成员为一个相同类型的对象, 定义如下
您正在找回 FastGPT 账号
```
{
“raw”: string; // 表示原文
“reason”: string; // 表示原因
“suggestion”: string; // 修改建议
}
```
~~~
可以在模型选择的窗口中设置禁用 AI 回复。
这样就看不到输出的 json 格式的内容了。
![](../../../public/imgs/spellcheck3.png)
### 3. 数据处理
上面的大模型输出了一个 json,这里要进行数据处理。数据处理可以使用代码执行组件。
![](../../../public/imgs/spellcheck4.png)
```javascript
function main({data}){
const array = JSON.parse(data)
return {
content: array.map(
(item, index) => {
return `
## 分析${index+1}
- **错误**: ${item.raw}
- **分析**: ${item.reason}
- **修改建议**: ${item.suggestion}
`
}
).join('')
}
}
```
上面的代码将 JSON 解析为 Object, 然后拼接成一串 Markdown 语法的字符串。
FastGPT 的指定回复组件可以将 Markdown 解析为 Html 返回。
## 发布
可以使用发布渠道进行发布。
![](../../../public/imgs/spellcheck5.png)
可以选择通过 URL 访问,或者是直接嵌入你的网页中。
> [点我使用](https://share.fastgpt.in/chat/share?shareId=b4r173wkcjae7wpnexcvmyc3)
{
"title": "Application Building Cases",
"description": "FastGPT application scenarios and feature implementation building cases",
"pages": [
"submit_application_template",
"translate-subtitle-using-gpt",
"multi_turn_translation_bot",
"english_essay_correction_bot",
"fixingEvidence",
"lab_appointment",
"dalle3",
"google_search",
"feishu_webhook"
]
}
{
"title": "应用搭建案例",
"description": "FastGPT 应用场景及功能实现的搭建案例",
"pages": ["submit_application_template","translate-subtitle-using-gpt","multi_turn_translation_bot","english_essay_correction_bot","fixingEvidence","lab_appointment","dalle3","google_search","feishu_webhook"]
}
\ No newline at end of file
---
title: How to Submit Application Templates
description: "Guide: How to submit application templates to FastGPT"
---
## Which Templates Can Be Merged
Currently, application templates merged into the repository are displayed to all users in the "Template Market".
To maintain template quality and avoid clutter from too many options, not all templates will be merged into the open-source repository. You can submit a PR in advance to discuss the template content with us.
We estimate the final total will be around 50 templates — half from the FastGPT Team and half from community contributors.
## How to Write an Application Template
### 1. Set Up FastGPT and FastGPT-plugin Development Environments
The following operations need to be performed in a dev environment.
Refer to these two guides to run the FastGPT and FastGPT-plugin development environments locally:
- [FastGPT | Quick Start Local Development](../../self-host/dev.en.mdx)
- [How to Develop System Plugins](../../guide/build/tools/system-plugins/dev_system_tool.en.mdx)
### 2. Create an Application Template
In the FastGPT-plugin project, application templates are located in the `modules/workflow/templates` directory.
1. Create a new `.json` file
2. Copy the following content and paste it into the newly created `.json` file
```json
{
"name": "Template Name",
"intro": "Template description, displayed on the template market page",
"author": "Your name",
"avatar": "Template avatar — place the image file in the same folder and fill in the corresponding path",
"tags": ["Template tags"], // writing (text creation), image-generation (image generation), web-search (web search),
// roleplay (role-playing), office-services (office services) — currently 5 categories, choose the appropriate tag
"type": "Template category", // simple (simple app), advanced (workflow), plugin (plugin)
"workflow": {
// Leave this object empty for now — we'll paste the exported workflow later
"nodes": [],
"edges": [],
"chatConfig": {}
}
}
```
### 3. Complete Template Orchestration and Testing
1. Build the workflow in FastGPT (either in the cloud service version or a locally deployed version).
### 4. Copy Configuration to template.json
Hover your mouse over the application avatar and name in the top-left corner. A dropdown menu will appear with an option to export the workflow configuration.
The exported configuration is automatically copied to the clipboard. Paste it directly into the template.json file, replacing the **workflow** value from step 2.
![](../../../public/imgs/template_submission3.png)
### 5. Verify the Template Loads Successfully
Refresh the page, open the Template Market, check if it loads successfully, and click "Use" to test its functionality.
![](../../../public/imgs/template_submission4.png)
### 6. Deploy and Use
After completing the above steps, you can package the fastgpt-plugin image for deployment. Currently, templates do not support hot updates — all templates are packaged within the image.
If you prefer not to repackage the fastgpt-plugin image every time you update templates, you can mount the `.json` file to the `/app/workflows/` directory inside the container using Docker volume mounting.
### 7. Submit a PR
If you believe your template should be included in the open-source repository, you can submit it via a PR.
- Clearly describe the template's purpose and functionality
- Include screenshots showing the template in action
- Explain template parameter configuration in the PR. For example, if the template requires applying for a key from a provider, include the corresponding URL and tutorial — we will add it to the documentation later.
---
title: 如何提交应用模板
description: 指南:如何向 FastGPT 提交应用模板
---
## 什么模板可以合并
目前合并进仓库的应用模板,会在「模板市场」中全部展示给用户。
为了控制模板的质量以及避免数量过多带来的繁琐,并不是所有的模板都会被合并到开源仓库中,你可以提前 PR 与我们沟通模板的内容。
预估最后总体的数量不会很多,控制在 50 个左右,一半来自 FastGPT Team,一半来自社区用户。
## 如何写一个应用模板
### 1. 跑通 FastGPT 开发环境和 FastGPT-plugin 开发环境
需要在 dev 环境下执行下面的操作。
可参照下面两篇文档在本地运行 FastGPT 开发环境和 FastGPT-plugin 开发环境
- [FastGPT|快速开始本地开发](../../self-host/dev.mdx)
- [如何开发系统插件](../../guide/build/tools/system-plugins/dev_system_tool.mdx)
### 2. 创建应用模版
在 FastGPT-plugin 项目中,应用模版都在 `modules/workflow/templates` 目录下。
1. 创建一个新的 `.json` 文件
2. 复制如下内容并粘贴在刚刚创建的 `.json` 文件中
```json
{
"name": "模板名",
"intro": "模板描述,会展示在模板市场的展示页",
"author": "填写你的名字",
"avatar": "模板头像,可以将图片文件放在同一个文件夹中,然后填写相应路径",
"tags": ["模板标签"], // writing(文本创作),image-generation(图片生成),web-search(联网搜索),
// roleplay(角色扮演), office-services(办公服务) 暂时分为 5 类,从中选择相应的标签
"type": "模板类别", // simple(简易应用), advanced(工作流), plugin(插件)
"workflow": {
// 这个对象先不管,待会直接粘贴导出的工作流即可
"nodes": [],
"edges": [],
"chatConfig": {}
}
}
```
### 3. 完成模版的编排和测试
1. 在 FastGPT 中(可以在云服务版或本地部署的版本中进行)编排工作流。
### 4. 复制配置到 template.json
鼠标放置在左上角应用的头像和名称上,会出现对于下拉框操作,可以导出工作流配置。
导出的配置,会自动复制到剪切板,可以直接到 template.json 文件中粘贴使用,替换步骤 2 中,**workflow** 的值。
![](../../../public/imgs/template_submission3.png)
### 5. 验证模板是否加载成功
刷新页面,打开模板市场,看其是否成功加载,并点击「使用」测试其功能。
![](../../../public/imgs/template_submission4.png)
### 6. 部署使用
完成上述操作后,可以打包 fastgpt-plugin 镜像使用 目前模版不支持热更新机制,所有模版都会打包在镜像内。
如果您不希望每次更新模版都要重新打包 fastgpt-plugin 镜像,可以选择通过 docker 存储卷挂载的方式将 .json 文件挂载到容器内的 `/app/workflows/` 目录下。
### 7. 提交 PR
如果你觉得你的模板需要提交到开源仓库,可以通过 PR 形式向我们提交。
- 写清楚模板的介绍和功能
- 配上模板运行的效果图
- 模板参数填写说明,需要在 PR 中写清楚。例如,有些模板需要去某个提供商申请 key,需要附上对应的地址和教程,后续我们会加入到文档中。
---
title: Use Cases
description: FastGPT Use Cases
---
import { Redirect } from '@/components/docs/Redirect';
<Redirect to="/use-cases/app-cases/submit_application_template" />
---
title: 使用案例
description: FastGPT 使用案例
---
import { Redirect } from '@/components/docs/Redirect';
<Redirect to="/use-cases/app-cases/submit_application_template" />
{
"title": "Use Cases",
"description": "More information about FastGPT practical use cases",
"root": true,
"pages": [
"...app-cases"
]
}
{
"title": "使用案例",
"description": "有关 FastGPT 其他实践案例的更多信息",
"root": true,
"pages": [
"...app-cases"
]
}
......@@ -274,7 +274,7 @@
"content/self-host/upgrading/4-14/4149.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/4-14/4149.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/4-15/4150.mdx": "2026-05-09T16:13:01+08:00",
"content/self-host/upgrading/4-15/41502.mdx": "2026-05-14T17:34:48+08:00",
"content/self-host/upgrading/4-15/41502.mdx": "2026-05-17T18:56:43+08:00",
"content/self-host/upgrading/outdated/40.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/outdated/40.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/outdated/41.en.mdx": "2026-04-26T21:08:47+08:00",
......@@ -416,25 +416,5 @@
"content/self-host/upgrading/upgrade-intruction.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/upgrade-intruction.mdx": "2026-04-26T21:08:47+08:00",
"content/toc.en.mdx": "2026-05-14T17:58:58+08:00",
"content/toc.mdx": "2026-05-14T17:58:58+08:00",
"content/use-cases/app-cases/dalle3.en.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/dalle3.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/english_essay_correction_bot.en.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/english_essay_correction_bot.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/feishu_webhook.en.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/feishu_webhook.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/fixingEvidence.en.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/fixingEvidence.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/google_search.en.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/google_search.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/lab_appointment.en.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/lab_appointment.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/multi_turn_translation_bot.en.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/multi_turn_translation_bot.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/submit_application_template.en.mdx": "2026-05-07T15:06:40+08:00",
"content/use-cases/app-cases/submit_application_template.mdx": "2026-05-07T15:06:40+08:00",
"content/use-cases/app-cases/translate-subtitle-using-gpt.en.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/app-cases/translate-subtitle-using-gpt.mdx": "2026-04-26T21:08:47+08:00",
"content/use-cases/index.en.mdx": "2026-05-07T15:06:40+08:00",
"content/use-cases/index.mdx": "2026-05-07T15:06:40+08:00"
"content/toc.mdx": "2026-05-14T17:58:58+08:00"
}
\ No newline at end of file
......@@ -2,7 +2,7 @@
"guide": "Guide",
"version": "Editions",
"api_reference": "API Reference",
"use-cases": "Use Cases",
"selfHost": "Self-Host",
"businessConsultation": "Business Inquiry",
"faq": "FAQ"
}
......@@ -2,7 +2,7 @@
"guide": "使用指南",
"version": "版本介绍",
"api_reference": "API 文档",
"use-cases": "使用场景",
"selfHost": "自部署",
"businessConsultation": "商业咨询",
"faq": "常见问题"
}
......@@ -54,7 +54,7 @@ const exactMap: Record<string, string> = {
// Navbar redirects.
'/guide': defaultHomePath,
'/use-cases': '/use-cases/app-cases/submit_application_template',
'/use-cases': defaultHomePath,
'/self-host': '/self-host/deploy/docker',
'/openapi': '/openapi/intro',
'/faq': '/faq/app'
......
......@@ -6,7 +6,6 @@ const fg = require('fast-glob');
// 黑名单路径(不带语言前缀)
const blacklist = [
'use-cases/index',
'protocol/index',
'api/index',
'faq/index',
......
import type { NextApiResponse } from 'next';
import type { IncomingMessage } from 'node:http';
type ResponseWithWritableAborted = NextApiResponse & {
writableAborted?: boolean;
const getErrorCode = (error: unknown) => {
if (typeof error !== 'object' || error === null || !('code' in error)) {
return '';
}
return String((error as { code?: unknown }).code);
};
const isClientResetSocketError = (error: unknown) => {
const code = getErrorCode(error);
return code === 'ECONNRESET' || code === 'EPIPE';
};
export const createClientAbortTracker = ({
......@@ -13,51 +22,62 @@ export const createClientAbortTracker = ({
res?: NextApiResponse;
}) => {
let clientAborted = false;
let requestAborted = !!req?.aborted;
let responseCompleted = !!(res?.writableEnded || res?.writableFinished);
let responseError = !!res?.errored;
let serverSocketError = false;
// For workflow stopping, "client aborted" means the current response can no longer be written.
// It is intentionally broader than a strict user-initiated cancel audit signal.
/**
* v1 工作流只在客户端主动断开当前响应时停止。
*
* `socket.destroyed`、`res.destroyed`、`writableAborted` 这类快照过宽,不能单独证明用户取消。
* `req.aborted` 是首选信号;但 Next API 运行时下 fetch abort 可能只稳定落到未 finish 的
* `res.close`,因此把 close 作为 fallback。服务端 response/socket error 会屏蔽该 fallback,
* 但客户端 reset 类 socket error 仍属于断开信号,不能抢先屏蔽后续 `req.aborted`/`res.close`。
*/
const responseFinished = () =>
responseCompleted || !!(res?.writableEnded || res?.writableFinished);
const responseWritableAborted = () =>
!!(res as ResponseWithWritableAborted | undefined)?.writableAborted;
const hasExplicitAbort = () => !!(req?.aborted || responseWritableAborted());
const hasBrokenConnection = () => !!(req?.socket?.destroyed || res?.destroyed || res?.errored);
const isAbortedSnapshot = () => {
// A normal completed response may also emit close later. Do not treat that as abort.
if (responseFinished()) return false;
return hasExplicitAbort() || hasBrokenConnection();
};
const responseErrored = () => responseError || serverSocketError || !!res?.errored;
const canAcceptRequestAbort = () => !responseFinished() && !responseErrored();
const isRequestAbortedSnapshot = () =>
(requestAborted || !!req?.aborted) && canAcceptRequestAbort();
const markResponseCompleted = () => {
responseCompleted = true;
};
const markResponseError = () => {
responseError = true;
};
const markSocketError = (error: unknown) => {
if (!isClientResetSocketError(error)) {
serverSocketError = true;
}
};
const markClientAborted = () => {
if (!responseFinished()) {
requestAborted = true;
if (canAcceptRequestAbort()) {
clientAborted = true;
}
};
const markClientAbortedIfConnectionBroken = () => {
if (!responseFinished() && (hasExplicitAbort() || hasBrokenConnection())) {
const markResponseClosed = () => {
if (canAcceptRequestAbort()) {
clientAborted = true;
}
};
req?.on('aborted', markClientAborted);
// close itself is too broad; only stop when paired with explicit abort or a broken connection.
req?.socket?.on('close', markClientAbortedIfConnectionBroken);
req?.socket?.on('error', markSocketError);
res?.on('finish', markResponseCompleted);
res?.on('close', markClientAbortedIfConnectionBroken);
res?.on('error', markClientAborted);
res?.on('error', markResponseError);
res?.on('close', markResponseClosed);
return {
isClientAborted: () => clientAborted || isAbortedSnapshot(),
isClientAborted: () => canAcceptRequestAbort() && (clientAborted || isRequestAbortedSnapshot()),
cleanup: () => {
req?.off('aborted', markClientAborted);
req?.socket?.off('close', markClientAbortedIfConnectionBroken);
req?.socket?.off('error', markSocketError);
res?.off('finish', markResponseCompleted);
res?.off('close', markClientAbortedIfConnectionBroken);
res?.off('error', markClientAborted);
res?.off('error', markResponseError);
res?.off('close', markResponseClosed);
}
};
};
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment