당뇨병환자 식단 추천 서비스 개발 사례 공유

1. 들어가는 말

저는 3년차 웹 백엔드 개발자 서영수라고 합니다.

ChatGPT MS애저톤에 참가할 때 제출했던 서비스를 소개해보고자 합니다.
NutriGuard 라는 이름도 ChatGPT를 통해 만들었습니다.

2. 서비스 소개

배경

당뇨병 환자들의 건강한 식생활은 그들의 전반적인 건강 상태에 큰 영향을 미칩니다. 많은 당뇨병 환자들이 식사 선택에 어려움을 겪고 있으며, 이를 돕기 위해 제공되는 정보가 제한적이거나 신뢰할 수 없는 경우가 많습니다. 이러한 배경 속에서 본 서비스는 다음과 같은 목적과 대상을 가집니다.

목적

당뇨병 환자들에게 개인화된 식단 추천을 제공하고, 음식 섭취 여부를 논문 기반의 신뢰할 수 있는 정보로 안내하는 것입니다.

대상

당뇨병 환자와 그들의 가족, 의료진, 영양사 등 관련 전문가들

3. 서비스 동작 방식

4. 기대효과

  1. 당뇨병 환자들의 올바른 식단 선택을 돕는 것으로, 건강 상태 향상과 당뇨병 합병증 예방에 도움을 줄 것입니다.
  2. 논문 기반의 정보 제공으로, 신뢰성 있는 음식 섭취 여부를 안내하여 사용자의 건강에 긍정적인 영향을 미칠 것입니다.
  3. 개인화된 식단 추천으로, 각 사용자의 개별적인 영양 및 건강 상태를 고려한 효과적인 식단 관리가 가능해질 것입니다.
  4. 의료진, 영양사 등 전문가들이 본 서비스를 활용하여 환자들의 건강 관리에 보다 효율적으로 참여할 수 있게 됩니다.

5. 서비스 화면

5. 서비스 링크

6. 서비스 프롬프트

식단 검색

You are an expert providing information on dietary management for individuals with diabetes. When answering a user's question, please include relevant research paper sources. If the user asks about whether they can eat a certain food, provide information on whether it's good to eat, any precautions, and alternatives if available.
Answer in Korean

(당신은 당뇨병 환자들을 위한 식단 관리에 대한 정보를 제공하는 전문가입니다. 사용자의 질문에 답변할 때 관련 연구 논문 출처를 포함하십시오. 사용자가 특정 음식을 먹을 수 있는지 여부를 묻는 경우, 먹는 것이 좋은지에 대한 정보, 주의사항 및 사용 가능한 경우의 대안을 제공합니다.)

식단 추천

You are an expert in providing specific food lists for breakfast, lunch, and dinner for people with diabetes. Please suggest a Korean diet to help them eat healthy and manage their diabetes, and today is ${todayDateTime}. Also, please provide the number of servings (g) and calorie (kcal) values. Based on the text in ${chatMessage}, please organize a good diet for diabetics for today and tomorrow. For example, please provide it in JSON format as below. We don't need the rest of the sentence, just the JSON part.
"[{\"meal\": \"morning\", \"title\": \"Oatmeal (50g, 150kcal), Banana (30g, 130kcal), Yogurt (25g, 200kcal)\", \"start\": \"2023-04-21T07:30:00\", \"end\": \"2023-04-21T08:00:00\"}, {\"meal\": \"lunch\", \"title\": \"Bean paste soup (400g, 550kcal), Radish wraps (30g, 70kcal), Grilled fish (70g, 250kcal)\", \"start\": \"2023-04-21T12:00:00\", \"end\": \"2023-04-21T13:30:00\"}, {\"meal\": \"dinner\", \"title\": \"Bean paste soup (400g, 550kcal), Radish wraps (30g, 70kcal), Grilled fish (70g, 250kcal)\", \"start\": \"2023-04-21T18:00:00\", \"end\": \"2023-04-21T19:00:00\"}]"
Answer in Korean

(당신은 당뇨병이 있는 사람들을 위해 아침, 점심, 저녁의 특정 음식 목록을 제공하는 전문가입니다. 건강한 식사와 당뇨병 관리에 도움이 될 수 있는 한국식 식단을 제안해 주십시오, 오늘은 ${todayDateTime}입니다. 또한, 1인분의 수(g)와 칼로리(kcal) 값을 제공해주시기 바랍니다. ${chatMessage}에 있는 글을 바탕으로 오늘과 내일 당뇨병 환자들을 위한 좋은 식단을 짜주세요. 예를 들어 아래와 같이 JSON 형식으로 제공해주시기 바랍니다. 나머지 문장은 필요 없고 JSON 부분만 필요합니다.)

7. 소스코드

const express = require('express');
const serverless = require('serverless-http');
const app = express();
const cors = require('cors');
const { Configuration, OpenAIApi } = require("openai");

const configuration = new Configuration({
    apiKey: API_KEY,
});
const openai = new OpenAIApi(configuration);

const corsOptions = {
    origin: 'https://diabetes-meals.pages.dev/',
    optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
    credentials: true
}
app.use(cors(corsOptions));
app.use(express.json());
app.use(express.urlencoded({extended: true}));

app.get('/', function (req, res) {
    res.send('Hello World')
});

// 질문 API
app.post('/foods', async function (req, res) {
    let { userMessages, assistantMessages } = req.body

    let messages = [
        {
            role: "system",
            content: "QUESTION_FOOD_PROMPT"
        }
    ];

    while (!!userMessages && userMessages.length !== 0 || !!assistantMessages && assistantMessages.length !== 0) {
        if (!!userMessages &&userMessages.length !== 0) {
            messages.push(
                JSON.parse('{"role": "user", "content": "' + String(userMessages.shift()).replace(/\n/g,"") + '"}')
            )
        }
        if (!!assistantMessages && assistantMessages.length !== 0) {
            messages.push(
                JSON.parse('{"role": "assistant", "content": "' + String(assistantMessages.shift()).replace(/\n/g,"") + '"}')
            )
        }
    }

    const maxRetries = 3;
    let retries = 0;
    let completion
    while (retries < maxRetries) {
        try {
            completion = await openai.createChatCompletion({
                model: "gpt-3.5-turbo",
                messages: messages,
            });
            break;
        } catch (e) {
            retries++;
            console.log(e);
            console.log(`Retrying... ${retries} / ${maxRetries}`);
        }
    }

    let content = completion.data.choices[0].message['content'];
    res.json(content);
});

// 식단 추천 api
app.post('/meals', async function (req, res) {
    let { chatMessage } = req.body;
    console.log(`chatMessage: ${chatMessage}`);

    let todayDateTime = new Date().toLocaleString('ko-KR', { timeZone: 'Asia/Seoul' });

    const completion = await openai.createChatCompletion({
        model: "gpt-4.0",
        messages: [
            {role: "system", content: "RECOMMEND_MEAL_PROMPT"}
        ],
    });
    let content = completion.data.choices[0].message.content;
    console.log(content);
    res.json(content);
});

module.exports.handler = serverless(app);

8. 더 생각해볼 내용

  1. 논문 정보에 대해 할루시네이션이 발생할 수 있기 때문에, 논문들을 직접 학습시켜 서비스를 제공.
  2. GPT 3.5 버전에서는 JSON 형식으로 답변하는 것이 정확하지 않는 경우가 있고, 4.0 버전에서는 잘 대답하는 것을 확인할 수 있었음.
  3. langchain을 도입하고, 프롬프트를 잘 작성하여 더 좋은 결과를 도출해 낼 수 있도록 한다.
7
3개의 답글

👉 이 게시글도 읽어보세요