OpenAI with guardrail frontend

วันนี่เราจะมาพูดถึงเรื่อง OpenAI with Guardrail กัน ในปัจจุบันนี้ก็มี Chatbot เกิดขึ้นมากในปัจจุบัน ซึ่งเราสามารถถามอะไรก็ได้หรือคุยอะไรก็ได้กับ Chatbot นั้นใช่ไหมละคะ?  แล้วถ้าสมมุติมีคนเข้าไปถามอะไรที่ไม่ดีเข้ามาละ 🤔 ซึ่งเราต้องป้องกันไม่ให้ Chatbot ตอบ ซึ่ง Guardrail จะมาจัดการในส่วนนี้นั้นเอง

ถ้าลองเปรียบเทียให้เห็นภาพ  ลองนึกถึงตอนเรากดเข้าไปใน Discord  ที่มีกฏต่างๆ ถ้าไม่ได้ตั้งกฏไว้ก็จะมีคนมาป่วน Discord  เช่น Discord ที่เราสร้างเป็นชุมชนเกี่ยวกับเกม เราก็ไม่อยากให้มีใครมาพิมพ์หรือพูดถึงเรื่องอย่างอื่นที่ไม่เกี่ยวกับเกม หรือ การซื้อขายไอเทมในเกม เป็นต้น

ตัว Guardrail ก็จะทำหน้าที่ควบคุมเนื้อหา สำหรับ user ที่เข้ามา แชท ถามกับ OpenAI นั้นเอง ซึ่งจะช่วยในการควบคุมเนื้อที่ user เข้ามาถาม ถ้าเนื้อหานั้นไม่มีความเกี่ยวข้องกับกฏที่ตั้งไว้นั้นเอง

Admin Interface for Rule Management

ในการออกแบบ UI ในส่วนของ Admin ที่สำหรับตั้งกฏ โดย Admin สามารถ สร้าง Rule ได้ และลบ Rule ทั้งหมดได้ และ สามารถ View ดู Rule ที่ตั้งไว้

ในการ Develop ตัว admin interface เราก็ใช้ React ในการออกแบบจัดการ state ต่างๆ ส่วนในการจัดการ Form ก็จะใช้ lib ของ react-hook-form ,@hookform/resolvers/zodและ zod ในการจัดการ validate input และในส่วนของการเชื่อมต่อ API จะใช้  @tanstack/react-query ซึ่งในการเชื่อมต่อกับ API  ตัวของ @tanstack/react-query ก็จะมีของให้ใช้หลายอย่างเลย เช่น

การใช้ useQueries ในการ GET  ค่าจากหลาย อันพร้อมกัน

  const [{ data: session }, { data: rule, refetch: refetchRule }] = useQueries({
    queries: [
      {
        queryKey: ['session', endPoint],
        queryFn: async () => {
          const { data } = await axios.get(`/session`)
          return data
        },
        enabled: typeof window !== 'undefined' && !localStorage?.session,
      },
      {
        queryKey: ['rule', endPoint],
        queryFn: async () => {
          const { data } = await axios.get(`/rule`)
          return data
        },
      },
    ],
  })

และในส่วนของการเพิ่ม rule และ ลบ rule ทั้งหมด เราก็จะใช้ useMutation ซึ่งใน useMutaion เราสามารถจัดการ state เพื่อ ทำ action สำเร็จได้ เช่น การให้ refetch ค่าตาราง เมื่อทำการ เพิ่ม หรือลบเเล้วเป็นต้น

  const { mutateAsync: createRule } = useMutation({
    mutationFn: (rule: string) => {
      return axios.post('/create_rule', { rule })
    },
    onSuccess: () => {
      refetchRule()
    },
  })

  const { mutateAsync: clearRule } = useMutation({
    mutationFn: () => {
      return axios.get('/clear_collection')
    },
    onSuccess: () => {
      refetchRule()
    },
  })

ซึ่งข้อดีของ lib นี้เลยคือ เราไม่ต้องจัดการ state ต่างๆ เอง นอกจากนี้ยังมีอะไรให้เล่นอีกหลายอย่างมาก ลองไปใช้กันนะเเล้วจะติดใจ

User Interface for Question Submission

ในส่วนของการของ Implement interface ของ Chatbot ก็จะมีในส่วนของ Input ที่ไว้หรับพิมพ์แชทกับ Ai โดย เมื่อเราได้ทำการพิมพ์ สิ่งที่ไม่เกี่ยวกับ กฏที่เราได้ตั้งไว้ ทาง API ก็จะ response กับมา เเล้ว ตัว Chatbot  ก็จะตอบว่า “bad prompt please ask another one“

ทีนี้เราลองมาดูว่า ถ้าเราถามคำถามเกี่ยวกับ แมวละจะเป็นยังไง

เราก็จะเห็นว่าตัว AI นั้นจะตอบเรากลับมา 😆 ว่าแมวที่เราถามไปนั้นคืออะไร

ซึ่งในการถามคำกับ Chatbot เราใช้ในรูปแบบของ stream  นั้นเอง

  useEffect(() => {
    const message = { query: watch('query') }
    const getData = async () => {
      try {
        setValue('query', '')
        const response = await fetch(
          `${endPoint ?? API_URL}/query?uuid=${localStorage?.session}&message=${
            message.query
          }`,
          {
            method: 'GET',
            headers: {
              Accept: 'text/event-stream',
              'x-api-key': localStorage?.apiKey,
            },
          }
        )

        if (response.status === 200) {
          const reader = response.body!.getReader()
          let result = ''
          while (true) {
            const { done, value } = await reader?.read()
            if (done) {
              setStreamText('')
              setAnswer((prevState) => [
                ...prevState,
                {
                  id: (prevState.length + 1).toString(),
                  role: 'ai',
                  message: result,
                },
              ])
              break
            }
            result += new TextDecoder().decode(value)
            setStreamText(result)
          }
        } else {
          if (response.status === 404) {
            localStorage.removeItem('session')
            refreshSession()
            setError('bot', {
              message: 'Something went wrong, please try again',
            })
            return
          }

          setError('bot', {
            message: 'Something went wrong',
          })
        }
      } catch (error: any) {
        console.error(error)
        setError('bot', {
          message: error?.response?.data?.message ?? 'Something went wrong',
        })
      }
    }
    if (isSubmitSuccessful && localStorage) {
      getData()
    }
  }, [
    submitCount,
    isSubmitSuccessful,
    setValue,
    watch,
    setError,
    endPoint,
    refreshSession,
  ])

Guardrail Interaction

หลักการทำงานของ  Guardrail เมื่อ user ถามคำถาม ตัว Chatbot จะจำกัดให้สามารถตอบได้ตาม Rule ที่ Admin กำหนดเท่านั้น ซึ่ง RAG ก็จะไปดู ว่า มีความคล้ายคลึงตรงกับ Rule มากน้อยแค่ไหน จึงจะส่งไปให้ OpenAI ต่อนั้นเอง เเต่ว่าถ้าเกิดมีความคล้ายคลึงน้อยกว่า 99% ทางAPI ก็จะส่ง response message กลับมาว่า “bad prompt please ask another one“  เพราะข้อมูลที่ถามไปนั้นอยู่นอกเหนือขอบเขตที่ตั้งกฏไว้นั้นเอง

Error Handling and User Feedback

การจัดการข้อผิดพลาดต่างบนหน้าเว็บในส่วนของการเชื่อมต่อกับเซิร์ฟเวอร์ หรือ Input จากผู้ใช้

การจัดการในส่วนของ Errors ที่เกิดจากเชื่อมต่อ Backend เช่นในส่วนของ session

ในส่วนของ session เราจะเก็บ session user ไปไว้ใน localStorage ซึ่งหาก session เกิดปัญหาเราต้องเคลียของเก่าทิ้งเเละให้ดึงsession ใหม่มา

 if (response.status === 404) {
            localStorage.removeItem('session')
            refreshSession()
            setError('bot', {
              message: 'Something went wrong, please try again',
            })
            return
          }

การดัก validation Error input ต่างๆ

ซึ่งในส่วนนี้เราก็ได้นำ  react-hook-form ,@hookform/resolvers/zodและ zod มาใช้ในการจัดการ ซึ่งเรามี schema ดังต่อไปนี้

export const askScheme = z.object({
  apiKey: z.string().optional(),
  query: z.string().trim().min(1, { message: 'Please enter your message' }),
  bot: z.string({}).optional(),
})

export const ruleScheme = z.object({
  rule: z.string().trim().min(1, { message: 'Please enter your rule' }),
})

export const endpointScheme = z.object({
  endpoint: z.string().url().optional(),
})
const methodRule = useForm<IRuleForm>({
    resolver: zodResolver(ruleScheme),
    mode: 'onChange',
    shouldFocusError: true,
  })

  const methodsEndpoint = useForm<IEndPointForm>({
    resolver: zodResolver(endpointScheme),
    mode: 'onChange',
    shouldFocusError: true,
  })

  const methods = useForm<IOpenAIForm>({
    resolver: zodResolver(askScheme),
    mode: 'onChange',
    shouldFocusError: true,
    defaultValues: {
      apiKey: '',
      query: '',
    },
  })

Aa

© 2023, All Rights Reserved, VulturePrime co., ltd.