Skip to content

Commit

Permalink
Merge pull request #70 from studio-recoding/dev
Browse files Browse the repository at this point in the history
add: 그로쓰 최종 제출
  • Loading branch information
uommou authored Jun 20, 2024
2 parents 43f950c + 3908597 commit a0a4764
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 14 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ chroma.log
/app/database/data/

# all __pycache__
**/__pycache__/
**/__pycache__/

.git
/venv/
76 changes: 75 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,77 @@
# NESS_ML

LLM 기반 인공지능 비서 서비스 NESS ML 레포지토리
NESS 프로젝트의 ML 레포지토리입니다.

## 🪧 About source code

NESS_ML의 프로젝트 구조는 다음과 같습니다.

```
NESS_ML
├─ app
│ ├─ database
│ │ ├─ chroma_db.py
│ │ └─ connect_rds.py
│ ├─ dto
│ │ ├─ db_dto.py
│ │ └─ openai_dto.py
│ ├─ main.py
│ ├─ prompt
│ │ ├─ email_prompt.py
│ │ ├─ openai_config.ini
│ │ ├─ openai_prompt.py
│ │ ├─ persona_prompt.py
│ │ └─ report_prompt.py
│ ├─ routers
│ │ ├─ chat.py
│ │ ├─ chromadb.py
│ │ ├─ email.py
│ │ ├─ recommendation.py
│ │ ├─ report.py
│ │ └─ __init__.py
│ └─ __init__.py
├─ Dockerfile
├─ README.md
└─ requirements.txt
```

`database`: vector db인 `chroma`와의 연결을 관리합니다. 또한 필요할 경우 rds에서 정보를 가져옵니다.

`dto`: request 및 response dto를 정의합니다.

`main`: 서버의 main 실행 파일입니다. api 엔드포인트는 router를 통해 관리됩니다.

`prompt`: open ai api 호출 시 사용하는 prompt를 관리합니다.

`routers`: api 엔드포인트입니다.

## 👩‍💻 Prerequisites
NESS의 백엔드 서버는 `FASTAPI` 애플리케이션으로, `requirements.txt`에 적힌 라이브러리 설치가 사전에 이루어져야 합니다.
```bash
pip install -r requirements.txt
```

## 🔧 How to build
이 레포지토리는 해당 명령어로 Clone 가능합니다.
```bash
https://github.com/studio-recoding/NESS_ML.git
```
별도의 빌드는 필요하지 않습니다.

## 🚀 How to run
다음 명령어로 `8080` 포트에서 실행 가능합니다.
```bash
uvicorn main:app -reload
```
## ✅ How to test
NESS의 ML 서버는 다음과 같은 프로그램을 통해서 API를 테스트할 수 있습니다.
`http://localhost:8080/API엔드포인트`로 API를 호출하면 동작을 확인할 수 있습니다.
- POSTMAN

## 📌 Description of used open source
NESS의 ML 서버는 다음과 같은 오픈 소스를 사용하고 있습니다.
<div>
<img alt="FastAPI" src ="https://img.shields.io/badge/spring boot-009688.svg?&style=for-the-badge&logo=fastapi&logoColor=white"/>
</div>

21 changes: 21 additions & 0 deletions app/dto/openai_dto.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,24 @@ class RecommendationResponse(BaseModel):
ness: str
activityList: List[ActivityDescription]

class CategoryInfo(BaseModel):
categoryName: str
categoryId: int
categoryColor: str

class RecommendationSchedule(BaseModel):
id: int
startTime: str
category: CategoryInfo
person: str
location: str
info: str
class ListRecommendationRequest(BaseModel):
persona: str
todoList: List[RecommendationSchedule]

class ListRecommendationPair(BaseModel):
todo: RecommendationSchedule
nessComment: str
class ListRecommendationResponse(BaseModel):
recommendationList: List[ListRecommendationPair]
14 changes: 7 additions & 7 deletions app/prompt/openai_config.ini
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
[NESS_NORMAL]
TEMPERATURE = 0.5
MAX_TOKENS = 2048
MODEL_NAME = gpt-4o
MAX_TOKENS = 1500
MODEL_NAME = gpt-4

[NESS_CASE]
TEMPERATURE = 0
MAX_TOKENS = 2048
MODEL_NAME = gpt-4o
MAX_TOKENS = 1000
MODEL_NAME = gpt-4

[NESS_RECOMMENDATION]
TEMPERATURE = 1
MAX_TOKENS = 2048
MODEL_NAME = gpt-4o
MODEL_NAME = gpt-4

[NESS_TAGS]
TEMPERATURE = 0.5
MAX_TOKENS = 2048
MODEL_NAME = gpt-4o
MODEL_NAME = gpt-4

[NESS_EMAIL]
TEMPERATURE = 0.5
MAX_TOKENS = 2048
MODEL_NAME = gpt-4o
MODEL_NAME = gpt-4
23 changes: 23 additions & 0 deletions app/prompt/openai_prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class Template:
2. Each of the three recommended activities should be creative yet somewhat related to the user's schedule, realistic, and specific.
3. Recommendations should be in noun form because there is limited space to write these activities.
4. Return the activities in a list format, without any additional commentary.
5. The user's schedule may not exist, and if so, do not randomly create the user's schedule and only recommend activities that helps the user.
Example:
User schedule: [Practice guitar, Calculate accuracy, Study backend development, Run AI models in the lab, Study NEXT.JS]
Expand All @@ -36,6 +37,28 @@ class Template:
"""

recommendation_list_template = """
Task: Generate comments for each schedule
{persona}
You will receive a list of the user's schedule information. Your task is to understand each schedule and generate a comment for each. There are a few rules you must follow in your comments:
1. YOU MUST USE {output_language} TO RESPOND TO THE USER INPUT.
2. Each comment should be concise, relevant to the schedule details, and realistic.
3. Comments should be in sentence form, suitable for displaying alongside the schedule.
4. Return the comments in a list format, without any additional commentary.
Example:
User schedule:
Start Time: 2024-05-25T18:00:00, Category: 공부 (ID: 1, Color: #0000FF), Person: 민주, Location: (Location not specified), Info: NEST.JS 공부하기
Start Time: 2024-05-25T20:00:00, Category: 약속 (ID: 2, Color: #008000), Person: (Person not specified), Location: (Location not specified), Info: 개발 공모전 미팅하기
Start Time: 2024-05-26T18:00:00, Category: 여가 (ID: 3, Color: #FF0000), Person: 채원, Location: 한강, Info: 친구랑 한강 놀러가기
AI Comments: ["민주님과 어디에서 만나지는 정하셨나요? 장소가 정해지지 않았어요.", "오늘의 날씨는 확인하셨나요? 친구와 즐겁게 피크닉을 즐기세요!", "오늘의 날씨는 확인하셨나요? 친구와 즐겁게 피크닉을 즐기세요!"]
User schedule: {schedule_list}
AI Comments:
"""

# case 분류 잘 안됨 - 수정 필요
case_classify_template = """
Task: User Chat Classification
Expand Down
8 changes: 7 additions & 1 deletion app/prompt/persona_prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ class Template:
You are an assertive, no-nonsense assistant whose role is to instill discipline and drive in users, pushing them to maximize their potential and productivity. Your interactions should be direct and results-focused, emphasizing the importance of hard work, dedication, and continuous improvement.
In every interaction, challenge the users to set ambitious goals and to prioritize their commitments. Remind them that success is earned through persistence and resilience. Encourage them to eliminate distractions and focus on what truly matters for achieving their objectives.
Your guidance should be firm and sometimes stern, reflecting a commitment to helping users achieve their very best. Provide clear, actionable advice that leads to efficient action and tangible results. You are here not to coddle, but to catalyze significant personal and professional growth.
"""
""",
"calm": """
You are a serene, supportive assistant whose role is to encourage tranquility and mindfulness in users. Your interactions should be gentle and reassuring, promoting a balanced approach to both personal and professional life.
In every conversation, remind users to take a moment to breathe and reflect on their well-being. Encourage them to prioritize self-care and mental health just as they would their physical health or career goals.
Your guidance should inspire peace and present strategies for managing stress effectively. Offer suggestions for mindfulness practices, such as meditation or yoga, and encourage breaks when needed to maintain a healthy mind and body.
While your approach is relaxed, it is purposefully structured to help users find harmony and satisfaction in their daily routines without feeling overwhelmed or rushed.
"""
}

@classmethod
Expand Down
72 changes: 68 additions & 4 deletions app/routers/recommendation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import configparser
import os
import json
import ast

from dotenv import load_dotenv
from fastapi import APIRouter, Depends, status, HTTPException
Expand All @@ -9,6 +10,7 @@

from app.dto.db_dto import RecommendationMainRequestDTO
from app.dto.openai_dto import RecommendationResponse, ActivityDescription
from app.dto.openai_dto import CategoryInfo, RecommendationSchedule, ListRecommendationPair, ListRecommendationRequest, ListRecommendationResponse
from app.prompt import openai_prompt, persona_prompt
import app.database.chroma_db as vectordb

Expand Down Expand Up @@ -59,6 +61,10 @@ async def get_recommendation(user_data: RecommendationMainRequestDTO) -> Recomme
# 한 줄 추천 기반 활동 추천하기
month_schedule = await vectordb.activity_recommendation_schedule(user_data, ness)
print(month_schedule)
# month_schedule이 비어있거나 [[]]인 경우 "No schedule"로 설정
if not month_schedule or month_schedule == [[]]:
month_schedule = "No schedule"
print(month_schedule)

activity_template = openai_prompt.Template.activity_template
activity_prompt = PromptTemplate.from_template(activity_template)
Expand All @@ -68,10 +74,17 @@ async def get_recommendation(user_data: RecommendationMainRequestDTO) -> Recomme
))
print(activity_response)

# AI 응답 파싱
try:
activities = json.loads(activity_response)
except json.JSONDecodeError:
print("Error parsing the JSON response")
# activity_response에서 "AI Recommendation:" 부분을 제거하고, [] 사이의 텍스트만 남김
if activity_response.startswith("AI Recommendation:"):
activity_response = activity_response[len("AI Recommendation:"):]
activity_response = activity_response.strip()
if activity_response.startswith("[") and activity_response.endswith("]"):
activity_response = activity_response[1:-1]
activities = [act.strip().strip('"') for act in activity_response.split(',')]
except (SyntaxError, ValueError):
print("Error parsing the response")
activities = []

# Generate ActivityDescription objects
Expand All @@ -84,4 +97,55 @@ async def get_recommendation(user_data: RecommendationMainRequestDTO) -> Recomme
return response

except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
raise HTTPException(status_code=500, detail=str(e))

@router.post("/list", status_code=status.HTTP_200_OK)
async def get_recommendation_list(user_data: ListRecommendationRequest) -> ListRecommendationResponse:
try:
# 모델 설정
config_recommendation = config['NESS_RECOMMENDATION']

chat_model = ChatOpenAI(
temperature=config_recommendation['TEMPERATURE'], # 창의성 (0.0 ~ 2.0)
max_tokens=config_recommendation['MAX_TOKENS'], # 최대 토큰수
model_name=config_recommendation['MODEL_NAME'], # 모델명
openai_api_key=OPENAI_API_KEY # API 키
)

# 프롬프트 템플릿 설정
recommendation_list_prompt = PromptTemplate.from_template(openai_prompt.Template.recommendation_list_template)
# 일정 리스트를 포맷팅
schedule_list = "\n".join([
f"Start Time: {schedule.startTime if schedule.startTime else 'not specified'}\n"
f"Category: {schedule.category.categoryName if schedule.category.categoryName else 'not specified'} (ID: {schedule.category.categoryId if schedule.category.categoryId else 'not specified'}, Color: {schedule.category.categoryColor if schedule.category.categoryColor else 'not specified'})\n"
f"Person: {schedule.person if schedule.person else 'not specified'}\n"
f"Location: {schedule.location if schedule.location else 'not specified'}\n"
f"Info: {schedule.info if schedule.info else 'not specified'}\n"
for schedule in user_data.todoList
])

# 하나의 프롬프트로 일정 리스트를 전달
recommendation_response = chat_model.predict(recommendation_list_prompt.format(
persona=user_data.persona,
output_language="Korean",
schedule_list=schedule_list
))

# 응답을 리스트 형식으로 파싱
comments = eval(recommendation_response.strip())

# 응답을 포맷팅하여 리스트로 반환
recommendations = [
ListRecommendationPair(
todo=schedule,
nessComment=comment
)
for schedule, comment in zip(user_data.todoList, comments)
]

return ListRecommendationResponse(recommendationList=recommendations)

return ListRecommendationResponse(recommendationList=recommendations)

except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

0 comments on commit a0a4764

Please sign in to comment.