Files
FastGPT/docSite/content/zh-cn/docs/use-cases/app-cases/lab_appointment.md
Archer e9d52ada73 4.8.13 feature (#3118)
* chore(ui): login page & workflow page (#3046)

* login page & number input & multirow select & llm select

* workflow

* adjust nodes

* New file upload (#3058)

* feat: toolNode aiNode readFileNode adapt new version

* update docker-compose

* update tip

* feat: adapt new file version

* perf: file input

* fix: ts

* feat: add chat history time label (#3024)

* feat:add chat and logs time

* feat: add chat history time label

* code perf

* code perf

---------

Co-authored-by: 勤劳上班的卑微小张 <jiazhan.zhang@ggimage.com>

* add chatType (#3060)

* pref: slow query of full text search (#3044)

* Adapt findLast api;perf: markdown zh format. (#3066)

* perf: context code

* fix: adapt findLast api

* perf: commercial plugin run error

* perf: markdown zh format

* perf: dockerfile proxy (#3067)

* fix ui (#3065)

* fix ui

* fix

* feat: support array reference multi-select (#3041)

* feat: support array reference multi-select

* fix build

* fix

* fix loop multi-select

* adjust condition

* fix get value

* array and non-array conversion

* fix plugin input

* merge func

* feat: iframe code block;perf: workflow selector type (#3076)

* feat: iframe code block

* perf: workflow selector type

* node pluginoutput check (#3074)

* feat: View will move when workflow check error;fix: ui refresh error when continuous file upload (#3077)

* fix: plugin output check

* fix: ui refresh error when continuous file upload

* feat: View will move when workflow check error

* add dispatch try catch (#3075)

* perf: workflow context split (#3083)

* perf: workflow context split

* perf: context

* 4.8.13 test (#3085)

* perf: workflow node ui

* chat iframe url

* feat: support sub route config (#3071)

* feat: support sub route config

* dockerfile

* fix upload

* delete unused code

* 4.8.13 test (#3087)

* fix: image expired

* fix: datacard navbar ui

* perf: build action

* fix: workflow file upload refresh (#3088)

* fix: http tool response (#3097)

* loop node dynamic height (#3092)

* loop node dynamic height

* fix

* fix

* feat: support push chat log (#3093)

* feat: custom uid/metadata

* to: custom info

* fix: chat push latest

* feat: add chat log envs

* refactor: move timer to pushChatLog

* fix: using precise log

---------

Co-authored-by: Finley Ge <m13203533462@163.com>

* 4.8.13 test (#3098)

* perf: loop node refresh

* rename context

* comment

* fix: ts

* perf: push chat log

* array reference check & node ui (#3100)

* feat: loop start add index (#3101)

* feat: loop start add index

* update doc

* 4.8.13 test (#3102)

* fix: loop index;edge parent check

* perf: reference invalid check

* fix: ts

* fix: plugin select files and ai response check (#3104)

* fix: plugin select files and ai response check

* perf: text editor selector;tool call tip;remove invalid image url;

* perf: select file

* perf: drop files

* feat: source id prefix env (#3103)

* 4.8.13 test (#3106)

* perf: select file

* perf: drop files

* perf: env template

* 4.8.13 test (#3107)

* perf: select file

* perf: drop files

* fix: imple mode adapt files

* perf: push chat log (#3109)

* fix: share page load title error (#3111)

* 4.8.13 perf (#3112)

* fix: share page load title error

* update file input doc

* perf: auto add file urls

* perf: auto ser loop node offset height

* 4.8.13 test (#3117)

* perf: plugin

* updat eaction

* feat: add more share config (#3120)

* feat: add more share config

* add i18n en

* fix: missing subroute (#3121)

* perf: outlink config (#3128)

* update action

* perf: outlink config

* fix: ts (#3129)

* 更新 docSite 文档内容 (#3131)

* fix: null pointer (#3130)

* fix: null pointer

* perf: not input text

* update doc url

* perf: outlink default value (#3134)

* update doc (#3136)

* 4.8.13 test (#3137)

* update doc

* perf: completions chat api

* Restore docSite content based on upstream/4.8.13-dev (#3138)

* Restore docSite content based on upstream/4.8.13-dev

* 4813.md缺少更正

* update doc (#3141)

---------

Co-authored-by: heheer <heheer@sealos.io>
Co-authored-by: papapatrick <109422393+Patrickill@users.noreply.github.com>
Co-authored-by: 勤劳上班的卑微小张 <jiazhan.zhang@ggimage.com>
Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com>
Co-authored-by: a.e. <49438478+I-Info@users.noreply.github.com>
Co-authored-by: Finley Ge <m13203533462@163.com>
Co-authored-by: Jiangween <145003935+Jiangween@users.noreply.github.com>
2024-11-13 11:29:53 +08:00

22 KiB
Raw Blame History

title, description, icon, draft, toc, weight
title description icon draft toc weight
实验室预约 展示高级编排操作数据库的能力 database false true 612

本示例演示了利用工具调用,自动选择调用知识库搜索实验室相关内容,或调用 HTTP 模块实现数据库的 CRUD 操作。

以一个实验室预约为例,用户可以通过对话系统预约、取消、修改预约和查询预约记录。

1. 全局变量使用

通过设计一个全局变量,让用户输入姓名,模拟用户身份信息。实际使用过程中,通常是直接通过嵌入 Token 来标记用户身份。

2. 工具调用

背景知识中,引导模型调用工具去执行不通的操作。

{{% alert icon="🤗" context="warning" %}} Tips: 这里需要增加适当的上下文,方便模型结合历史纪录进行判断和决策~ {{% /alert %}}

3. HTTP 模块

HTTP模块中需要设置 3 个工具参数:

  • 预约行为:可取 get, put, post, delete 四个值分别对应查询、修改、新增、删除操作。当然你也可以写4个HTTP模块来分别处理。
  • labname: 实验室名。非必填,因为查询和删除时候,不需要。
  • time: 预约时间。

总结

  1. 工具调用模块是非常强大的功能,可以在一定程度上替代问题分类和内容提取。
  2. 通过工具模块,动态的调用不同的工具,可以将复杂业务解耦。

附件

编排配置

可直接复制,导入到 FastGPT 中。

{{% details title="编排配置" closed="true" %}}

{
  "nodes": [
    {
      "nodeId": "userChatInput",
      "name": "流程开始",
      "intro": "当用户发送一个内容后,流程将会从这个模块开始执行。",
      "avatar": "/imgs/workflow/userChatInput.svg",
      "flowNodeType": "workflowStart",
      "position": {
        "x": 309.7143912167367,
        "y": 1501.2761754220846
      },
      "inputs": [
        {
          "key": "userChatInput",
          "renderTypeList": [
            "reference",
            "textarea"
          ],
          "valueType": "string",
          "label": "问题输入",
          "required": true,
          "toolDescription": "用户问题",
          "type": "systemInput",
          "showTargetInApp": false,
          "showTargetInPlugin": false,
          "connected": false,
          "selectedTypeIndex": 0,
          "value": [
            "userChatInput",
            "userChatInput"
          ]
        }
      ],
      "outputs": [
        {
          "id": "userChatInput",
          "type": "static",
          "key": "userChatInput",
          "valueType": "string",
          "label": "core.module.input.label.user question"
        }
      ]
    },
    {
      "nodeId": "eg5upi",
      "name": "指定回复",
      "intro": "该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。",
      "avatar": "/imgs/workflow/reply.png",
      "flowNodeType": "answerNode",
      "position": {
        "x": 1962.729630445213,
        "y": 2295.9791334948304
      },
      "inputs": [
        {
          "key": "text",
          "renderTypeList": [
            "textarea",
            "reference"
          ],
          "valueType": "any",
          "label": "core.module.input.label.Response content",
          "description": "core.module.input.description.Response content",
          "placeholder": "core.module.input.description.Response content",
          "type": "textarea",
          "showTargetInApp": true,
          "showTargetInPlugin": true,
          "connected": true,
          "selectedTypeIndex": 1,
          "value": [
            "40clf3",
            "result"
          ]
        }
      ],
      "outputs": []
    },
    {
      "nodeId": "kge59i",
      "name": "用户引导",
      "intro": "可以配置应用的系统参数。",
      "avatar": "/imgs/workflow/userGuide.png",
      "flowNodeType": "userGuide",
      "position": {
        "x": -327.218389965887,
        "y": 1504.8056414948464
      },
      "inputs": [
        {
          "key": "welcomeText",
          "renderTypeList": [
            "hidden"
          ],
          "valueType": "string",
          "label": "core.app.Welcome Text",
          "type": "hidden",
          "showTargetInApp": false,
          "showTargetInPlugin": false,
          "value": "你好,我是实验室助手,请问有什么可以帮助你的么?如需预约或修改预约实验室,请提供姓名、时间和实验室名称。\n[实验室介绍]\n[开放时间]\n[预约]",
          "connected": false,
          "selectedTypeIndex": 0
        },
        {
          "key": "variables",
          "renderTypeList": [
            "hidden"
          ],
          "valueType": "any",
          "label": "core.module.Variable",
          "value": [
            {
              "id": "gt9b23",
              "key": "name",
              "label": "name",
              "type": "input",
              "required": true,
              "maxLen": 50,
              "enums": [
                {
                  "value": ""
                }
              ]
            }
          ],
          "type": "hidden",
          "showTargetInApp": false,
          "showTargetInPlugin": false,
          "connected": false,
          "selectedTypeIndex": 0
        },
        {
          "key": "questionGuide",
          "valueType": "boolean",
          "renderTypeList": [
            "hidden"
          ],
          "label": "",
          "type": "switch",
          "showTargetInApp": false,
          "showTargetInPlugin": false,
          "value": false,
          "connected": false,
          "selectedTypeIndex": 0
        },
        {
          "key": "tts",
          "renderTypeList": [
            "hidden"
          ],
          "valueType": "any",
          "label": "",
          "type": "hidden",
          "showTargetInApp": false,
          "showTargetInPlugin": false,
          "value": {
            "type": "model",
            "model": "tts-1",
            "voice": "alloy"
          },
          "connected": false,
          "selectedTypeIndex": 0
        },
        {
          "key": "whisper",
          "renderTypeList": [
            "hidden"
          ],
          "valueType": "any",
          "label": "",
          "type": "hidden",
          "showTargetInApp": false,
          "showTargetInPlugin": false,
          "connected": false,
          "selectedTypeIndex": 0
        },
        {
          "key": "scheduleTrigger",
          "renderTypeList": [
            "hidden"
          ],
          "valueType": "any",
          "label": "",
          "value": null
        }
      ],
      "outputs": []
    },
    {
      "nodeId": "40clf3",
      "name": "HTTP请求",
      "intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
      "avatar": "/imgs/workflow/http.png",
      "flowNodeType": "httpRequest468",
      "showStatus": true,
      "position": {
        "x": 1118.6532653446993,
        "y": 1955.886106913907
      },
      "inputs": [
        {
          "key": "system_httpMethod",
          "renderTypeList": [
            "custom"
          ],
          "valueType": "string",
          "label": "",
          "value": "POST",
          "required": true,
          "type": "custom",
          "showTargetInApp": false,
          "showTargetInPlugin": false,
          "connected": false,
          "selectedTypeIndex": 0
        },
        {
          "valueType": "string",
          "renderTypeList": [
            "reference"
          ],
          "key": "action",
          "label": "action",
          "toolDescription": "预约行为,一共四种:\nget - 查询预约情况\nput - 更新预约\npost - 新增预约\ndelete - 删除预约",
          "required": true,
          "canEdit": true,
          "editField": {
            "key": true,
            "description": true
          }
        },
        {
          "valueType": "string",
          "renderTypeList": [
            "reference"
          ],
          "key": "labname",
          "label": "labname",
          "toolDescription": "实验室名称",
          "required": false,
          "canEdit": true,
          "editField": {
            "key": true,
            "description": true
          }
        },
        {
          "valueType": "string",
          "renderTypeList": [
            "reference"
          ],
          "key": "time",
          "label": "time",
          "toolDescription": "预约时间,按 YYYY/MM/DD HH:mm 格式返回",
          "required": false,
          "canEdit": true,
          "editField": {
            "key": true,
            "description": true
          }
        },
        {
          "key": "system_httpReqUrl",
          "renderTypeList": [
            "hidden"
          ],
          "valueType": "string",
          "label": "",
          "description": "core.module.input.description.Http Request Url",
          "placeholder": "https://api.ai.com/getInventory",
          "required": false,
          "type": "hidden",
          "showTargetInApp": false,
          "showTargetInPlugin": false,
          "value": "https://d8dns0.laf.dev/appointment-lab",
          "connected": false,
          "selectedTypeIndex": 0
        },
        {
          "key": "system_httpHeader",
          "renderTypeList": [
            "custom"
          ],
          "valueType": "any",
          "value": [],
          "label": "",
          "description": "core.module.input.description.Http Request Header",
          "placeholder": "core.module.input.description.Http Request Header",
          "required": false,
          "type": "custom",
          "showTargetInApp": false,
          "showTargetInPlugin": false,
          "connected": false,
          "selectedTypeIndex": 0
        },
        {
          "key": "system_httpParams",
          "renderTypeList": [
            "hidden"
          ],
          "valueType": "any",
          "value": [],
          "label": "",
          "required": false,
          "type": "hidden",
          "showTargetInApp": false,
          "showTargetInPlugin": false,
          "connected": false,
          "selectedTypeIndex": 0
        },
        {
          "key": "system_httpJsonBody",
          "renderTypeList": [
            "hidden"
          ],
          "valueType": "any",
          "value": "{\r\n  \"name\": \"{{name}}\",\r\n  \"time\": \"{{time}}\",\r\n  \"labname\": \"{{labname}}\",\r\n  \"action\": \"{{action}}\"\r\n}",
          "label": "",
          "required": false,
          "type": "hidden",
          "showTargetInApp": false,
          "showTargetInPlugin": false,
          "connected": false,
          "selectedTypeIndex": 0
        },
        {
          "key": "system_addInputParam",
          "renderTypeList": [
            "addInputParam"
          ],
          "valueType": "dynamic",
          "label": "",
          "required": false,
          "description": "core.module.input.description.HTTP Dynamic Input",
          "editField": {
            "key": true,
            "valueType": true
          }
        }
      ],
      "outputs": [
        {
          "id": "system_addOutputParam",
          "type": "dynamic",
          "key": "system_addOutputParam",
          "valueType": "dynamic",
          "label": "",
          "editField": {
            "key": true,
            "valueType": true
          }
        },
        {
          "id": "result",
          "type": "static",
          "key": "result",
          "valueType": "string",
          "label": "result",
          "description": "result",
          "canEdit": true,
          "editField": {
            "key": true,
            "name": true,
            "description": true,
            "dataType": true
          }
        },
        {
          "id": "httpRawResponse",
          "type": "static",
          "key": "httpRawResponse",
          "valueType": "any",
          "label": "原始响应",
          "description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。"
        }
      ]
    },
    {
      "nodeId": "fYxwWym8flYL",
      "name": "工具调用",
      "intro": "通过AI模型自动选择一个或多个功能块进行调用也可以对插件进行调用。",
      "avatar": "/imgs/workflow/tool.svg",
      "flowNodeType": "tools",
      "showStatus": true,
      "position": {
        "x": 933.9342354248961,
        "y": 1229.3563445150553
      },
      "inputs": [
        {
          "key": "model",
          "renderTypeList": [
            "settingLLMModel",
            "reference"
          ],
          "label": "core.module.input.label.aiModel",
          "valueType": "string",
          "llmModelType": "all",
          "value": "gpt-3.5-turbo"
        },
        {
          "key": "temperature",
          "renderTypeList": [
            "hidden"
          ],
          "label": "",
          "value": 0,
          "valueType": "number",
          "min": 0,
          "max": 10,
          "step": 1
        },
        {
          "key": "maxToken",
          "renderTypeList": [
            "hidden"
          ],
          "label": "",
          "value": 2000,
          "valueType": "number",
          "min": 100,
          "max": 4000,
          "step": 50
        },
        {
          "key": "systemPrompt",
          "renderTypeList": [
            "textarea",
            "reference"
          ],
          "max": 3000,
          "valueType": "string",
          "label": "core.ai.Prompt",
          "description": "core.app.tip.chatNodeSystemPromptTip",
          "placeholder": "core.app.tip.chatNodeSystemPromptTip",
          "value": "当前时间为: {{cTime}}\n你是实验室助手用户可能会询问实验室相关介绍或预约实验室。\n请选择合适的工具去帮助他们。"
        },
        {
          "key": "history",
          "renderTypeList": [
            "numberInput",
            "reference"
          ],
          "valueType": "chatHistory",
          "label": "core.module.input.label.chat history",
          "required": true,
          "min": 0,
          "max": 30,
          "value": 6
        },
        {
          "key": "userChatInput",
          "renderTypeList": [
            "reference",
            "textarea"
          ],
          "valueType": "string",
          "label": "用户问题",
          "required": true,
          "value": [
            "userChatInput",
            "userChatInput"
          ]
        }
      ],
      "outputs": []
    },
    {
      "nodeId": "JSSQtDgwmmbE",
      "name": "知识库搜索",
      "intro": "调用“语义检索”和“全文检索”能力,从“知识库”中查找实验室介绍和使用规则等信息。",
      "avatar": "/imgs/workflow/db.png",
      "flowNodeType": "datasetSearchNode",
      "showStatus": true,
      "position": {
        "x": 447.0795498711184,
        "y": 1971.5311041711186
      },
      "inputs": [
        {
          "key": "datasets",
          "renderTypeList": [
            "selectDataset",
            "reference"
          ],
          "label": "core.module.input.label.Select dataset",
          "value": [],
          "valueType": "selectDataset",
          "list": [],
          "required": true
        },
        {
          "key": "similarity",
          "renderTypeList": [
            "selectDatasetParamsModal"
          ],
          "label": "",
          "value": 0.4,
          "valueType": "number"
        },
        {
          "key": "limit",
          "renderTypeList": [
            "hidden"
          ],
          "label": "",
          "value": 1500,
          "valueType": "number"
        },
        {
          "key": "searchMode",
          "renderTypeList": [
            "hidden"
          ],
          "label": "",
          "valueType": "string",
          "value": "embedding"
        },
        {
          "key": "usingReRank",
          "renderTypeList": [
            "hidden"
          ],
          "label": "",
          "valueType": "boolean",
          "value": false
        },
        {
          "key": "datasetSearchUsingExtensionQuery",
          "renderTypeList": [
            "hidden"
          ],
          "label": "",
          "valueType": "boolean",
          "value": false
        },
        {
          "key": "datasetSearchExtensionModel",
          "renderTypeList": [
            "hidden"
          ],
          "label": "",
          "valueType": "string",
          "value": "gpt-3.5-turbo"
        },
        {
          "key": "datasetSearchExtensionBg",
          "renderTypeList": [
            "hidden"
          ],
          "label": "",
          "valueType": "string",
          "value": ""
        },
        {
          "key": "userChatInput",
          "renderTypeList": [
            "reference",
            "textarea"
          ],
          "valueType": "string",
          "label": "用户问题",
          "required": true,
          "toolDescription": "需要检索的内容"
        }
      ],
      "outputs": [
        {
          "id": "quoteQA",
          "key": "quoteQA",
          "label": "core.module.Dataset quote.label",
          "description": "特殊数组格式,搜索结果为空时,返回空数组。",
          "type": "static",
          "valueType": "datasetQuote"
        }
      ]
    },
    {
      "nodeId": "IdntVQiTopHT",
      "name": "工具调用终止",
      "intro": "该模块需配置工具调用使用。当该模块被执行时本次工具调用将会强制结束并且不再调用AI针对工具调用结果回答问题。",
      "avatar": "/imgs/workflow/toolStop.svg",
      "flowNodeType": "stopTool",
      "position": {
        "x": 1969.73331750207,
        "y": 2650.0258908119413
      },
      "inputs": [],
      "outputs": []
    }
  ],
  "edges": [
    {
      "source": "40clf3",
      "target": "eg5upi",
      "sourceHandle": "40clf3-source-right",
      "targetHandle": "eg5upi-target-left"
    },
    {
      "source": "userChatInput",
      "target": "fYxwWym8flYL",
      "sourceHandle": "userChatInput-source-right",
      "targetHandle": "fYxwWym8flYL-target-left"
    },
    {
      "source": "fYxwWym8flYL",
      "target": "40clf3",
      "sourceHandle": "selectedTools",
      "targetHandle": "selectedTools"
    },
    {
      "source": "fYxwWym8flYL",
      "target": "JSSQtDgwmmbE",
      "sourceHandle": "selectedTools",
      "targetHandle": "selectedTools"
    },
    {
      "source": "40clf3",
      "target": "IdntVQiTopHT",
      "sourceHandle": "40clf3-source-right",
      "targetHandle": "IdntVQiTopHT-target-left"
    }
  ]
}

{{% /details %}}

Laf 云函数代码

可以在 Laf 中快速构建 HTTP 接口。

{{% details title="函数代码" closed="true" %}}

import cloud from '@lafjs/cloud'
const db = cloud.database()

type RequestType = {
    name: string;
    time?: string;
    labname?: string;
    action: 'post' | 'delete' | 'put' | 'get'
}

export default async function (ctx: FunctionContext) {
  try {
    const {   action,...body  } = ctx.body as RequestType

    if (action === 'get') {
      return await getRecord(ctx.body)
    }
    if (action === 'post') {
      return await createRecord(ctx.body)
    }
    if (action === 'put') {
      return await putRecord(ctx.body)
    }
    if (action === 'delete') {
      return await removeRecord(ctx.body)
    }


    return {
      result: "异常"
    }
  } catch (err) {
    return {
      result: "异常"
    }
  }
}

async function putRecord({ name, time, labname }: RequestType) {
  const missData = []
  if (!name) missData.push("你的姓名")

  if (missData.length > 0) {
    return {
      result: `请提供: ${missData.join("、")}`
    }
  }

  const { data: record } = await db.collection("LabAppointment").where({
    name, status: "unStart"
  }).getOne()

  if (!record) {
    return {
      result: `${name} 还没有预约记录`
    }
  }

  const updateWhere = {
    name,
    time: time || record.time,
    labname: labname || record.labname
  }

  await db.collection("LabAppointment").where({
    name, status: "unStart"
  }).update(updateWhere)

  return {
    result: `修改预约成功。
  姓名:${name}·
  时间: ${updateWhere.time}
  实验室名: ${updateWhere.labname}
  ` }
}


async function getRecord({ name }: RequestType) {
  if (!name) {
    return {
      result: "请提供你的姓名"
    }
  }
  const { data } = await db.collection('LabAppointment').where({ name, status: "unStart" }).getOne()

  if (!data) {
    return {
      result: `${name} 没有预约中的记录`
    }
  }
  return {
    result: `${name} 有一条预约记录:
姓名:${data.name}
时间: ${data.time}
实验室名: ${data.labname}
    `
  }
}

async function removeRecord({ name }: RequestType) {
  if (!name) {
    return {
      result: "请提供你的姓名"
    }
  }
  const { deleted } = await db.collection('LabAppointment').where({ name, status: "unStart" }).remove()

  if (deleted > 0) {
    return {
      result: `取消预约记录成功: ${name}`
    }
  }
  return {
    result: ` ${name} 没有预约中的记录`
  }
}

async function createRecord({ name, time, labname }: RequestType) {
  const missData = []
  if (!name) missData.push("你的姓名")
  if (!time) missData.push("需要预约的时间")
  if (!labname) missData.push("实验室名名称")

  if (missData.length > 0) {
    return {
      result: `请提供: ${missData.join("、")}`
    }
  }

  const { data: record } = await db.collection("LabAppointment").where({
    name, status: "unStart"
  }).getOne()

  if (record) {
    return {
      result: `您已经有一个预约记录了:
姓名:${record.name}
时间: ${record.time}
实验室名: ${record.labname}

每人仅能同时预约一个实验室名。
      `
    }
  }

  await db.collection("LabAppointment").add({
    name, time, labname, status: "unStart"
  })

  return {
    result: `预约成功。
  姓名:${name}
  时间: ${time}
  实验室名: ${labname}
  ` }
}

{{% /details %}}