---
title: Lab Appointment System
description: Demonstrates advanced workflow capabilities for database operations
---

import { Alert } from '@/components/docs/Alert';

|                       |                       |
| --------------------- | --------------------- |
| ![](/imgs/demo-appointment1.webp) | ![](/imgs/demo-appointment2.webp) |
| ![](/imgs/demo-appointment3.webp) | ![](/imgs/demo-appointment4.webp) |



This example demonstrates using tool calls to automatically choose between searching the knowledge base for lab information or calling HTTP modules to perform database CRUD operations.

Using a lab appointment system as an example, users can book, cancel, modify, and query appointments through a conversational interface.

## 1. Using Global Variables

Design a global variable for users to input their name, simulating user identity. In production, you'd typically use embedded tokens to identify users.

## 2. Tool Calling

![](/imgs/demo-appointment5.png)

In the background knowledge, guide the model to call tools for different operations.

<Alert icon="🤗" context="success">
**Tips:** Add appropriate context here so the model can make decisions based on conversation history.
</Alert>

## 3. HTTP Module

![](/imgs/demo-appointment6.jpg)

The HTTP module requires 3 tool parameters:

- Appointment action: Can be get, put, post, or delete, corresponding to query, modify, create, and delete operations. You could also create 4 separate HTTP modules to handle each action.
- labname: Lab name. Optional, since it's not needed for queries and deletions.
- time: Appointment time.


# Summary

1. The tool calling module is incredibly powerful and can largely replace question classification and content extraction.
2. By dynamically calling different tools through the tool module, you can decouple complex business logic.


# Attachments

## Workflow Configuration

Copy and import directly into FastGPT.

<details>
<summary>Workflow Configuration</summary>

```json
{
  "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 Cloud Function Code

You can quickly build HTTP endpoints in [Laf](https://laf.dev/).
<details>
<summary>Function Code</summary>


```ts
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>
