基于LLM的企业知识库AI助手实现

在企业内部通常有各类知识库,例如IT手册、产品文档、制度规范、培训材料等。在AI技术火热的今天,将知识库与LLM大语言模型结合,可以实现“类ChatGPT”的问答体验,AI可以识别用户意图,从知识库中检索相关信息,并生成通俗易懂的回答,显著提升知识获取效率和用户体验。本文介绍博主为公司内部搭建的企业AI知识库实现过程。

效果展示

一、架构设计

1.1 架构设计图

1.2 组件说明:

  • FastGPT:基于 LLM 大语言模型的知识库问答平台

  • OneAPI:聚合各类 AI API,支持多模型调用

  • MongoDB\PostgreSQL:Fastgpt依赖组件,用于存储核心配置数据和向量数据

  • OpenAI API:LLM模型接口,支持各种公共模型(如ChatGPT、Deepseek、Claude等)或自建模型OpenAI接口

1.3 流程描述

在整体设计中,我们主要依赖FastGPT、OneAPI组件实现,大致流程如下:首先在OneAPI中配置接入LLM模型接口,然后FastGPT通过OneAPI调用语言模型用于用户对话,调用向量模型用于拆解知识库文档,最后再将FastGPT生成的应用窗口,通过iFrame形式嵌入到我们自有的服务台中,实现用户交互。

二、部署实施

2.1 FastGPT部署

这里采用官网推荐的Docker Compose方式部署,官方文档:https://doc.fastgpt.cn/docs/development/docker/,示例Docker-Compose文件如下:

# 数据库的默认账号和密码仅首次运行时设置有效
# 如果修改了账号密码,记得改数据库和项目连接参数,别只改一处~
# 该配置文件只是给快速启动,测试使用。正式使用,记得务必修改账号密码,以及调整合适的知识库参数,共享内存等。
# 如何无法访问 dockerhub 和 git,可以用阿里云(阿里云没有arm包)

version: '3.3'
services:
  pg:
    image: pgvector/pgvector:0.7.0-pg15 # docker hub
    # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.7.0 # 阿里云
    container_name: pg
    restart: always
    ports: # 生产环境建议不要暴露
      - 5432
    networks:
      - fastgpt
    environment:
      # 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
      - POSTGRES_USER=username
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=postgres
    volumes:
      - ./pg/data:/var/lib/postgresql/data
  mongo:
    # 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
    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
    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')" > /dev/null 2>&1; 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 $$!
  fastgpt:
    container_name: fastgpt
    image: ghcr.io/labring/fastgpt:v4.8 # git
    # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8 # 阿里云
    ports:
      - 3000:3000
    networks:
      - fastgpt
    depends_on:
      - mongo
      - pg
    restart: always
    environment:
      # root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
      - DEFAULT_ROOT_PSW=Password@12345
      # AI模型的API地址哦。务必加 /v1。这里默认填写了OneApi的访问地址。
      - OPENAI_BASE_URL=http://oneapi:3000/v1
      # AI模型的API Key。(这里默认填写了OneAPI的快速默认key,测试通后,务必及时修改)
      - 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
      # pg 连接参数
      - PG_URL=postgresql://username:password@pg:5432/postgres
    volumes:
      - ./config.json:/app/data/config.json
      - ./fastgpt/tmp:/app/tmp
  mysql:
    image: mysql:8.0.36
    container_name: mysql
    restart: always
    ports:
      - 3306
    networks:
      - fastgpt
    command: --default-authentication-plugin=mysql_native_password
    environment:
      # 默认root密码,仅首次运行有效
      MYSQL_ROOT_PASSWORD: oneapimmysql
      MYSQL_DATABASE: oneapi
    volumes:
      - ./mysql:/var/lib/mysql
  oneapi:
    container_name: oneapi
    image: ghcr.io/songquanpeng/one-api:latest
    ports:
      - 3001:3000
    depends_on:
      - mysql
    networks:
      - fastgpt
    restart: always
    environment:
      # mysql 连接参数
      - SQL_DSN=root:oneapimmysql@tcp(mysql:3306)/oneapi
      # 登录凭证加密密钥
      - SESSION_SECRET=oneapikey
      # 内存缓存
      - MEMORY_CACHE_ENABLED=true
      # 启动聚合更新,减少数据交互频率
      - BATCH_UPDATE_ENABLED=true
      # 聚合更新时长
      - BATCH_UPDATE_INTERVAL=10
      # 初始化的 root 密钥(建议部署完后更改,否则容易泄露)
      - INITIAL_ROOT_TOKEN=fastgpt
    volumes:
      - ./oneapi:/data
networks:
  fastgpt:

根据官网描述,我们还需要创建一个config.json文件,用于配置模型相关,配置如下:

{
  "feConfigs": {
    "lafEnv": "https://laf.dev"
  },
  "systemEnv": {
    "openapiPrefix": "fastgpt",
    "vectorMaxProcess": 15,
    "qaMaxProcess": 15,
    "pgHNSWEfSearch": 100
  },
  "llmModels": [
    {
      "model": "gpt-3.5-turbo",
      "name": "gpt-3.5-turbo",
      "maxContext": 16000,
      "avatar": "/imgs/model/openai.svg",
      "maxResponse": 4000,
      "quoteMaxToken": 13000,
      "maxTemperature": 1.2,
      "charsPointsPrice": 0,
      "censor": false,
      "vision": false,
      "datasetProcess": true,
      "usedInClassify": true,
      "usedInExtractFields": true,
      "usedInToolCall": true,
      "usedInQueryExtension": true,
      "toolChoice": true,
      "functionCall": true,
      "customCQPrompt": "",
      "customExtractPrompt": "",
      "defaultSystemChatPrompt": "",
      "defaultConfig": {}
    },
    {
      "model": "gpt-4-0125-preview",
      "name": "gpt-4-turbo",
      "avatar": "/imgs/model/openai.svg",
      "maxContext": 125000,
      "maxResponse": 4000,
      "quoteMaxToken": 100000,
      "maxTemperature": 1.2,
      "charsPointsPrice": 0,
      "censor": false,
      "vision": false,
      "datasetProcess": false,
      "usedInClassify": true,
      "usedInExtractFields": true,
      "usedInToolCall": true,
      "usedInQueryExtension": true,
      "toolChoice": true,
      "functionCall": false,
      "customCQPrompt": "",
      "customExtractPrompt": "",
      "defaultSystemChatPrompt": "",
      "defaultConfig": {}
    },
    {
      "model": "gpt-4-vision-preview",
      "name": "gpt-4-vision",
      "avatar": "/imgs/model/openai.svg",
      "maxContext": 128000,
      "maxResponse": 4000,
      "quoteMaxToken": 100000,
      "maxTemperature": 1.2,
      "charsPointsPrice": 0,
      "censor": false,
      "vision": true,
      "datasetProcess": false,
      "usedInClassify": false,
      "usedInExtractFields": false,
      "usedInToolCall": false,
      "usedInQueryExtension": false,
      "toolChoice": true,
      "functionCall": false,
      "customCQPrompt": "",
      "customExtractPrompt": "",
      "defaultSystemChatPrompt": "",
      "defaultConfig": {}
    }
  ],
  "vectorModels": [
    {
      "model": "text-embedding-3-large",
      "name": "Embedding-2",
      "avatar": "/imgs/model/openai.svg",
      "charsPointsPrice": 0,
      "defaultToken": 512,
      "maxToken": 3000,
      "weight": 100,
      "dbConfig": {},
      "queryConfig": {},
      "defaultConfig": {
        "dimensions": 1024
      }
    },
    {
      "model": "text-embedding-3-small",
      "name": "Embedding-2",
      "avatar": "/imgs/model/openai.svg",
      "charsPointsPrice": 0,
      "defaultToken": 512,
      "maxToken": 3000,
      "weight": 100,
      "dbConfig": {},
      "queryConfig": {}
    },
    {
      "model": "text-embedding-ada-002",
      "name": "Embedding-2",
      "avatar": "/imgs/model/openai.svg",
      "charsPointsPrice": 0,
      "defaultToken": 512,
      "maxToken": 3000,
      "weight": 100,
      "dbConfig": {},
      "queryConfig": {}
    }
  ],
  "reRankModels": [],
  "audioSpeechModels": [
    {
      "model": "tts-1",
      "name": "OpenAI TTS1",
      "charsPointsPrice": 0,
      "voices": [
        {
          "label": "Alloy",
          "value": "alloy",
          "bufferId": "openai-Alloy"
        },
        {
          "label": "Echo",
          "value": "echo",
          "bufferId": "openai-Echo"
        },
        {
          "label": "Fable",
          "value": "fable",
          "bufferId": "openai-Fable"
        },
        {
          "label": "Onyx",
          "value": "onyx",
          "bufferId": "openai-Onyx"
        },
        {
          "label": "Nova",
          "value": "nova",
          "bufferId": "openai-Nova"
        },
        {
          "label": "Shimmer",
          "value": "shimmer",
          "bufferId": "openai-Shimmer"
        }
      ]
    }
  ],
  "whisperModel": {
    "model": "whisper-1",
    "name": "Whisper1",
    "charsPointsPrice": 0
  }
}

配置文件准备完成后,docker-compose up启动容器

2.2 配置OneAPI

这里博主使用了公司自研的大模型运维平台LLMOPS作为示例,其他公有模型接口参考FastGPT或OneAPI官方文档即可

首先进入LLMOPS平台,我们选择Qwen3-30B-A3B-128k-nothink(无CoT)模型,在服务列表-服务详情-查看调用示例中,创建API-Key,然后从调用示例中获取API baseURL和model名称

调用示例

curl -k -X POST '{API_BASEURL}' \
-H "Content-type: application/json" \
-H "Authorization: Bearer {API_KEY}" \
-d '{
  "messages": [
    {
      "role": "system",
      "content": "你是一个很有用助手"
    },
    {
      "role": "user",
      "content": "请简单介绍一下自己的作用"
    }
  ],
  "model": "atom", //MODEL_NAME
  "stream": true
}'

进入OneAPI后台配置页面,配置渠道-新增渠道,类型选择自定义渠道,将LLMOPS调用示例中获取的API_BASEURL、API_KEY、MODEL_NAME分别填写到OneAPI渠道中的BaseURL、密钥、模型字段,然后保存并测试渠道

若测试有报错,可以在LLMOPS后台调用记录中查看接口调用详情

2.3 配置FastGPT

我们的自有模型添加完成后,还需要集成到FastGPT中供其调用,编辑config.json文件,在llmModels和vectorModels列表中添加我们自定义的模型,其中参数配置可参考官方配置手册

  "llmModels": [
    {
      "model": "atom", // 模型名(对应OneAPI中渠道的模型名)
      "name": "llmops_atom", // 模型别名
      "maxContext": 128000, // 最大上下文
      "maxResponse": 16000, // 最大回复
      "quoteMaxToken": 120000, // 最大引用内容
      "maxTemperature": 1.2, // 最大温度
      "charsPointsPrice": 0, // n积分/1k token(商业版)
      "censor": false, // 是否开启敏感校验(商业版)
      "vision": true, // 是否支持图片输入
      "datasetProcess": true, // 是否设置为文本理解模型(QA),务必保证至少有一个为true,否则知识库会报错
      "usedInClassify": true, // 是否用于问题分类(务必保证至少有一个为true)
      "usedInExtractFields": true, // 是否用于内容提取(务必保证至少有一个为true)
      "usedInToolCall": true, // 是否用于工具调用(务必保证至少有一个为true)
      "toolChoice": true, // 是否支持工具选择(分类,内容提取,工具调用会用到。)
      "functionCall": false, // 是否支持函数调用(分类,内容提取,工具调用会用到。会优先使用 toolChoice,如果为false,则使用 functionCall,如果仍为 false,则使用提示词模式)
      "customCQPrompt": "", // 自定义文本分类提示词(不支持工具和函数调用的模型
      "customExtractPrompt": "", // 自定义内容提取提示词
      "defaultSystemChatPrompt": "", // 对话默认携带的系统提示词
      "defaultConfig": {}, // 请求API时,挟带一些默认配置(比如 GLM4 的 top_p)
      "fieldMap": {} // 字段映射(o1 模型需要把 max_tokens 映射为 max_completion_tokens)
    }
  ],
  "vectorModels": [
    {
      "model": "atom", // 模型名(与OneAPI对应)
      "name": "atom", // 模型展示名
      "charsPointsPrice": 0, // n积分/1k token
      "defaultToken": 700, // 默认文本分割时候的 token
      "maxToken": 3000, // 最大 token
      "weight": 100, // 优先训练权重
      "defaultConfig": {}, // 自定义额外参数。例如,如果希望使用 embedding3-large 的话,可以传入 dimensions:1024,来返回1024维度的向量。(目前必须小于1536维度)
      "dbConfig": {}, // 存储时的额外参数(非对称向量模型时候需要用到)
      "queryConfig": {} // 参训时的额外参数
    },
  ],

配置文件准备完毕后,docker compose restart重启FastGPT服务,启动完成后进入FastGPT后台

2.3.1 创建知识库

左侧菜单栏-新建知识库,索引模型和文件处理模型都选择为我们自定义的模型,创建知识库

进入知识库,选择导入数据集,这里选择通过网页+选择器方式来导入数据集,确认数据导入后,知识库就准备完毕了

2.3.2 创建AI应用

点击应用-新建应用,场景选择知识库+对话引导,AI模型选择我们自定义的llmops_atom模型,提示词和对话开场白可自定义,以下是我的提示词和对话模板,可供参考:

你是一个基于星环科技内部IT知识库和星环LLMOPS平台训练的面向内部员工的AI助手
回答要求:
- 请使用简洁且专业的语言来回答用户的问题。
- 如果你不知道答案,请回答“没有在知识库中查找到相关信息,建议咨询相关技术支持或参考[IT知识库](http://it-kb.transwarp.io/)文档进行操作”。
- 避免提及你是从已知信息中获得的知识。
- 请保证答案与已知信息中描述的一致。
- 请使用 Markdown 语法优化答案的格式。
- 已知信息中的图片、链接地址和脚本语言请直接返回。
- 请使用与问题相同的语言来回答。
🤯你好,我是IT-AI小助手,帮助你解答公司内IT相关问题。
你可以向我提问:
[介绍一下你自己]
[如何配置远程办公VPN]
[如何配置代理服务器]

***
🧠知识库未覆盖到的问题会根据记录逐步标注完善
🤖GPT 也可能会犯错,重要信息请务必核查

在关联知识库中,选择我们刚刚创建完成的知识库,搜索模式选择语义检索并开启问题优化,优化模型同样选择我们创建的atom模型

配置完成后,我们在调试预览中与AI对话测试,不出意外的话AI已经可以正常回答知识库问题啦!

三、发布应用

我们最终的使用场景是在已有的IT服务台中进行AI对话,这里我选择最简单的实现方式——iFrame窗口嵌入

3.1 导出应用

在FastGPT应用窗口中,选择发布应用,选择免登录窗口,创建新链接,使用方式选择iFrame,然后复制出iframe代码块。

3.2 嵌入窗口

在IT服务台前端代码中,新建Vue视图,调整iFrame窗口宽高和边框样式,即可完成嵌入,将视图发布出来即可实现最终效果啦!

<template>
  <iframe
      src="http://172.16.20.41:3000/chat/share?shareId=tg8ci9osgnf2zvzwz1lpp12k"
      style="width: 100%; height: calc(100vh - 126px);"
      frameborder="0"
      allow="*"
  />
</template>