당신의 CGM이 점심을 주문해야 합니다: LangGraph와 함께 자율 건강 에이전트 구축 🥗🤖
Source: Dev.to
Introduction
우리는 모두 그런 경험을 해봤습니다: 연속 혈당 모니터(CGM)가 “고혈당” 알림을 보내면 뇌가 즉시 공황 모드에 들어갑니다. 먹어야 할까요? 무엇을 먹어야 할까요? 혈당이 급격히 오를 때, 합리적이고 저혈당 지수(GI)의 결정을 내리기란 쉽지 않습니다.
만약 건강 데이터가 단순히 알림만 주는 것이 아니라 대신 행동을 취한다면 어떨까요? 이번 튜토리얼에서는 LangGraph, OpenAI 함수 호출, 그리고 Dexcom API를 활용해 Health Butler를 구축합니다. AI 에이전트가 실시간 혈당 수치를 모니터링하고, 급증이나 급락을 감지하면 자동으로 음식 배달 API(예: Meituan 또는 Ele.me)와 상호작용하여 저GI 식사 주문을 제안하고 진행합니다.
아키텍처 다이어그램
graph TD
A[Start: Dexcom Monitor] --> B{Glucose Stable?}
B -- Yes --> C[Wait 5 Mins]
C --> A
B -- No: Spike/Crash --> D[Analyze Nutritional Needs]
D --> E[OpenAI Function Calling: Search Low‑GI Meals]
E --> F[Fetch Meituan/Ele.me Options]
F --> G[Propose Order to User]
G -- Approved --> H[Execute Delivery API]
G -- Denied --> A
H --> I[Log Event & Monitor Recovery]
I --> A
Prerequisites
- Node.js (v18+)
- LangChain & LangGraph SDKs (
@langchain/langgraph,@langchain/openai) - Dexcom Developer Account (sandbox API access)
- OpenAI API Key (using GPT‑4o for best reasoning)
에이전트 상태 정의
import { StateGraph, Annotation } from "@langchain/langgraph";
// Define our state schema
const AgentState = Annotation.Root({
glucoseLevel: Annotation(),
trend: Annotation(), // 'rising', 'falling', 'stable'
recommendations: Annotation(),
orderPlaced: Annotation(),
userAlerted: Annotation(),
});
상태는 진실의 원천이며, 현재 혈당 값, 그 추세, 그리고 권장되는 식품 항목들을 추적합니다.
Dexcom Fetch 노드
async function checkGlucoseNode(state) {
console.log("Checking CGM data... 🩸");
// Real-world: const response = await fetch('https://api.dexcom.com/v3/users/self/egvs/...');
// Mocking a "Spike" scenario:
const mockGlucose = 185;
const mockTrend = "risingFast";
return {
glucoseLevel: mockGlucose,
trend: mockTrend,
};
}
이 노드는 OAuth 흐름을 시뮬레이션하고 혈당 수치와 추세를 반환합니다.
Food‑Search Tool (OpenAI Function Calling)
import { ChatOpenAI } from "@langchain/openai";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
const foodSearchTool = tool(
async ({ query, maxCalories }) => {
// Logic to call Meituan/Ele.me/UberEats API
return `Found: Grilled Chicken Salad, Quinoa Bowl (Max ${maxCalories} kcal)`;
},
{
name: "search_delivery_food",
description: "Searches for healthy food options based on nutritional needs.",
schema: z.object({
query: z.string(),
maxCalories: z.number(),
}),
}
);
const model = new ChatOpenAI({ modelName: "gpt-4o" }).bindTools([foodSearchTool]);
이 도구는 LLM이 배달 서비스에서 저‑GI 식사 옵션을 요청할 수 있게 합니다.
워크플로우 구축
const workflow = new StateGraph(AgentState)
.addNode("monitor", checkGlucoseNode)
.addNode("analyzer", async (state) => {
const response = await model.invoke([
["system", "You are a medical nutrition assistant. Suggest a low-GI meal."],
["user", `My glucose is ${state.glucoseLevel} and ${state.trend}. Find me a meal.`],
]);
return { recommendations: [response] };
})
.addEdge("__start__", "monitor")
.addConditionalEdges("monitor", (state) => {
if (state.glucoseLevel > 160 || state.glucoseLevel < 70) return "analyzer";
return "monitor";
})
.addEdge("analyzer", "monitor"); // Simplified loop
const app = workflow.compile();
그래프는 monitor 노드에서 시작하여 혈당이 범위를 벗어났을 때 조건부로 analyzer로 라우팅하고, 그 후 다시 monitor로 루프합니다.
생산 고려사항
이 개념 증명은 핵심 아이디어를 보여주지만, 프로덕션 수준의 헬스 에이전트는 다음을 해결해야 합니다:
- 견고한 오류 처리 및 재시도
- HIPAA 준수 데이터 처리 및 저장
- 개인 맞춤 영양 조언을 위한 검색 강화 생성(RAG)
- 인간이 개입하는(HITL) 패턴을 포함한 보안 툴 호출
- 실시간 응답성을 위한 저지연 엣지 배포
이러한 주제에 대한 심층 논의를 원한다면 WellAlly Tech Blog의 엔지니어링 가이드를 참고하세요(검색어: “stateful AI agents in healthcare”).
Next Steps
- Add a Human‑in‑the‑Loop node that requires biometric confirmation before invoking the payment API.
- Implement a feedback loop where the agent learns which meals most effectively stabilize glucose levels.
- Expand the data sources to include multi‑modal health inputs (e.g., vision + CGM).
Happy coding, and stay healthy! 🥑💻