
OLLAMA - exaone3.5:2.4b모델 사용
from googleapiclient.discovery import build # Google API 클라이언트 생성 도구
from google_auth_oauthlib.flow import InstalledAppFlow # OAuth 인증 흐름을 다루는 도구
from google.auth.transport.requests import Request # 토큰 갱신 시 필요한 요청 객체
from datetime import datetime #날짜 다룰 때 사용
# 파일 저장 및 불러오기용 (토큰 저장)
import os
import pickle
import requests
import json
import re
# 사용할 모델 이름
model_name = "exaone3.5:2.4b"
# JSON 블록 추출 함수
def extract_json(text):
# ```json 블록 내부만 추출
pattern = r"```json\s*([\s\S]*?)\s*```"
match = re.search(pattern, text)
if not match:
print("❌ JSON 블록을 찾지 못했습니다.")
return None
json_str = match.group(1)
try:
data = json.loads(json_str)
return data
except json.JSONDecodeError as e:
print("❌ JSON 파싱 실패:", e)
return None
################################################ 구글 켈린더 엑세스
# 인증 범위 지정
SCOPES = ['https://www.googleapis.com/auth/calendar']
# 사용자 인증 + access_token 관리
def get_credentials():
creds = None
if os.path.exists('token.pickle'):
with open('token.pickle','rb') as token :
creds = pickle.load(token)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'client_secret.json', SCOPES )
creds = flow.run_local_server(port=8080)
# 새 토큰 저장
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
return creds # ✅ 함수 끝에서 항상 반환
################################################ 구글 켈린더 엑세스
################################################ user input 분류
classify_set = [
# ✅ ADD
{"input": "오늘 오후에 영화 보자", "output": "add"},
{"input": "5월 3일에 생일 파티 일정 추가해줘", "output": "add"},
{"input": "이번 주 금요일에 미용실 예약 좀 넣어줘", "output": "add"},
{"input": "4월 20일에 친구랑 저녁 약속 있어", "output": "add"},
{"input": "모레 점심 먹자", "output": "add"},
{"input": "내일 오전 9시에 회의 있어", "output": "add"},
{"input": "주말에 등산 일정 잡아줘", "output": "add"},
{"input": "다음주 화요일에 프로젝트 발표 있어", "output": "add"},
{"input": "오늘 밤에 헬스장 갈 거야", "output": "add"},
{"input": "7시에 엄마랑 전화하기 일정 넣어줘", "output": "add"},
# ✅ DELETE
{"input": "오늘 저녁 약속 취소해줘", "output": "delete"},
{"input": "5시 회의 일정 없애줘", "output": "delete"},
{"input": "내일 생일 파티 취소됐어", "output": "delete"},
{"input": "친구 만나는 일정 지워줘", "output": "delete"},
{"input": "방금 넣은 일정 삭제해줘", "output": "delete"},
{"input": "이번 주말 일정 취소할래", "output": "delete"},
{"input": "3시에 예약한 거 없애줘", "output": "delete"},
{"input": "다음주 월요일 약속 취소해줘", "output": "delete"},
{"input": "쇼핑 일정 삭제해줘", "output": "delete"},
{"input": "헬스장 안 가기로 했어", "output": "delete"},
# ✅ EDIT
{"input": "내일 회의 시간 바꿔줘", "output": "edit"},
{"input": "오늘 약속 3시로 변경해줘", "output": "edit"},
{"input": "저녁 6시 약속 7시로 옮겨줘", "output": "edit"},
{"input": "생일 파티 장소 바뀌었어", "output": "edit"},
{"input": "오후 회의 Zoom 링크로 수정해줘", "output": "edit"},
{"input": "영화 시간 2시로 바꿔줘", "output": "edit"},
{"input": "점심 시간 다시 조정해줘", "output": "edit"},
{"input": "오늘 일정 제목 바꿔줘", "output": "edit"},
{"input": "내일 약속 위치 바뀜", "output": "edit"},
{"input": "저녁 약속 시간 변경해줘", "output": "edit"}
]
def Input_analysis(user_input):
user_prompt=user_input
system_prompt=f'''
1. Please analyze the sentence : {user_input}
2. Please classify the sentences into 3 categories. [ Add schedule, delete schedule, edit schedule ]
3. If you are adding a schedule, print Add.
4. If the schedule is deleted, output Delete.
5. If the schedule is modified, print Edit.
default. Please print it out as in the example : {classify_set}
'''
final_prompt=f"{system_prompt} \n\n User:{user_prompt}"
# AI에게 일정 폼으로 변환 요청
response = requests.post(
"http://localhost:11434/api/generate",
json={
"model": "exaone3.5:2.4b", # 사용 중인 모델 이름 (예: ollama 로컬 서버)
"prompt": final_prompt
,
"stream": False
}
)
# 응답 파싱
result = response.json()
return result["response"]
def extract_output(text):
# JSON 배열 추출 시 ```json ``` 포함 여부와 관계없이 처리
pattern = r"\[.*?\]"
match = re.search(pattern, text, re.DOTALL)
if not match:
print("❌ JSON 배열을 찾을 수 없습니다.")
return "re"
json_str = match.group(0)
try:
# 작은따옴표 → 큰따옴표로 변경 후 파싱
data = json.loads(json_str.replace("'", '"'))
for item in data:
print("item['output']",item['output'])
return item['output']
except json.JSONDecodeError as e:
print("❌ JSON 파싱 실패:", e)
return "re"
################################################ user input 분류
################################################ 일정 추가
# 현재 날짜와 시간
now = datetime.now()
def MakeSchedule(user_input):
# 사용자로부터 자연어 일정 입력 받기
user_prompt = user_input #input("🗓️ 일정을 입력하세요: ")
event_data=None
while event_data==None :
system_prompt = f'''
0. Always remember the date : {now}
1. Please analyze the next schedule : {user_prompt}
2. Please write as in the example:
{{
"summary": "event_data['summary']",
"location": "event_data['location']",
"description": "event_data['description']",
"start": {{
"dateTime": "event_data['start'] + '+09:00'",
"timeZone": "Asia/Seoul"
}},
"end": {{
"dateTime": "event_data['end'] + '+09:00'",
"timeZone": "Asia/Seoul"
}},
"reminders": {{
"useDefault": false,
"overrides": [
{{"method": "popup", "minutes": 10}}
]
}}
}}
"3. If there is no data, write it randomly.\n"
"4. Please enter the date and time exactly in the format '2023-10-28T13:00:00+09:00'. Do not use values such as 'XX:XX'. \n"
"default : Return in JSON format"
)
'''
final_prompt=f"{system_prompt}\n\n User: {user_prompt}"
# AI에게 일정 폼으로 변환 요청
response = requests.post(
"http://localhost:11434/api/generate",
json={
"model": "exaone3.5:2.4b", # 사용 중인 모델 이름 (예: ollama 로컬 서버)
"prompt": final_prompt
,
"stream": False
}
)
# 응답 파싱
result = response.json()
# AI 응답 문자열에서 유효한 JSON만 추출해 dict로 반환
event_data = extract_json(result["response"])
if event_data is None:
print("❌ 응답을 JSON으로 해석할 수 없습니다.")
return None
# Google Calendar API에 맞는 폼으로 변환
event = {
"summary": event_data["summary"],
"location": event_data["location"],
"description": event_data["description"],
"start": {
"dateTime": event_data["start"]["dateTime"],
"timeZone": event_data["start"]["timeZone"]
},
"end": {
"dateTime": event_data["end"]["dateTime"],
"timeZone": event_data["end"]["timeZone"]
},
"reminders": {
"useDefault": event_data.get("reminders", {}).get("useDefault", False),
"overrides": event_data.get("reminders", {}).get("overrides", [])
}
}
# 디버깅용 출력
print("\n📋 생성된 일정:")
print(json.dumps(event, indent=4, ensure_ascii=False))
return event
# 실제 이벤트 등록 함수
# 위에서 얻은 인증 정보로 API를 사용할 수 있는 service 객체 생성
def add_event(make_event):
try:
creds = get_credentials()
service = build('calendar', 'v3', credentials=creds)
event = make_event
print("\n📤 보내는 이벤트 JSON:")
print(json.dumps(event, indent=4, ensure_ascii=False))
event_result = service.events().insert(
calendarId='primary',
body=event
).execute()
print(f"\n✅ 일정이 추가되었습니다: {event_result.get('htmlLink')}")
except Exception as e:
print("❌ 일정 추가 중 오류 발생:", e)
################################################ 일정 추가
################################################ 일정 삭제
delete_set = [
{
"list": '''[
{"id": "1", "summary": "회의", "start": "2025-04-10T10:00:00+09:00", "end": "2025-04-10T11:00:00+09:00"},
{"id": "2", "summary": "치과 예약", "start": "2025-04-11T15:00:00+09:00", "end": "2025-04-11T15:30:00+09:00"},
{"id": "3", "summary": "친구와 저녁", "start": "2025-04-12T19:00:00+09:00", "end": "2025-04-12T21:00:00+09:00"}
]''',
"input": "치과 일정 취소해줘",
"output": '{"id": "2", "summary": "치과 예약", "start": "2025-04-11T15:00:00+09:00", "end": "2025-04-11T15:30:00+09:00"}'
},
{
"list": '''[
{"id": "4", "summary": "프로젝트 미팅", "start": "2025-04-14T14:00:00+09:00", "end": "2025-04-14T15:30:00+09:00"},
{"id": "5", "summary": "헬스장", "start": "2025-04-14T18:00:00+09:00", "end": "2025-04-14T19:00:00+09:00"}
]''',
"input": "오늘 헬스장 삭제해줘",
"output": '{"id": "5", "summary": "헬스장", "start": "2025-04-14T18:00:00+09:00", "end": "2025-04-14T19:00:00+09:00"}'
},
{
"list": '''[
{"id": "6", "summary": "엄마 생신 파티", "start": "2025-04-16T17:00:00+09:00", "end": "2025-04-16T20:00:00+09:00"},
{"id": "7", "summary": "개발자 밋업", "start": "2025-04-17T13:00:00+09:00", "end": "2025-04-17T15:00:00+09:00"}
]''',
"input": "개발자 모임 일정 지워줘",
"output": '{"id": "7", "summary": "개발자 밋업", "start": "2025-04-17T13:00:00+09:00", "end": "2025-04-17T15:00:00+09:00"}'
},
{
"list": '''[
{"id": "8", "summary": "출장 - 부산", "start": "2025-04-20T07:00:00+09:00", "end": "2025-04-20T20:00:00+09:00"},
{"id": "9", "summary": "가족 여행", "start": "2025-04-21T08:00:00+09:00", "end": "2025-04-23T20:00:00+09:00"}
]''',
"input": "부산 출장 취소해줘",
"output": '{"id": "8", "summary": "출장 - 부산", "start": "2025-04-20T07:00:00+09:00", "end": "2025-04-20T20:00:00+09:00"}'
},
{
"list": '''[
{"id": "10", "summary": "스터디 모임", "start": "2025-04-22T10:00:00+09:00", "end": "2025-04-22T12:00:00+09:00"},
{"id": "11", "summary": "점심약속", "start": "2025-04-22T12:30:00+09:00", "end": "2025-04-22T14:00:00+09:00"}
]''',
"input": "22일 점심약속 빼줘",
"output": '{"id": "11", "summary": "점심약속", "start": "2025-04-22T12:30:00+09:00", "end": "2025-04-22T14:00:00+09:00"}'
}
]
def delete_event(user_input):
creds = get_credentials()
service = build('calendar', 'v3', credentials=creds)
# 일정 목록 가져오기
events_result = service.events().list(
calendarId='primary',
maxResults=2500,
singleEvents=True,
orderBy='startTime'
).execute()
events = events_result.get('items', [])
if not events:
print("📭 등록된 일정이 없습니다.")
return
# 요약용 이벤트 리스트 (AI가 분석할 수 있도록 최소 정보만 포함)
simplified_events = [
{
"id": event["id"],
"summary": event.get("summary", "(제목 없음)"),
"start": event['start'].get('dateTime', event['start'].get('date')),
"end": event['end'].get('dateTime', event['end'].get('date'))
}
for event in events
]
print("simplified_events : \n\n\n",simplified_events)
# AI에게 삭제할 일정 찾도록 요청
system_prompt = f"""
1. I'll give you a list
2. I will provide input
3. Please answer as in the following example : {delete_set}
4. only You just need to create that "output": '...'
list : {simplified_events}
User input: {user_input}
"""
response = requests.post(
"http://localhost:11434/api/generate",
json={
"model": "exaone3.5:2.4b",
"prompt": system_prompt,
"stream": False
}
)
result = response.json()
print("result!!!!!",result["response"])
# # 응답에서 JSON 추출
# try:
# selected_event = json.loads(result["response"])
# print("selected_eventselected_eventselected_event",selected_event)
# if not selected_event or "id" not in selected_event:
# print("❌ 삭제할 일정을 정확히 찾을 수 없습니다.")
# return
# # 일정 삭제
# service.events().delete(calendarId='primary', eventId=selected_event["id"]).execute()
# print(f"🗑️ 삭제 완료: {selected_event['summary']}")
# except Exception as e:
# print("❌ 응답 처리 중 오류:", e)
# print("응답 내용:", result["response"])
################################################ 일정 삭제
################################################ 일정 수정
def edit_event(user_input):
print("일정 수정")
system_prompt = ""
user_prompt = ""
final_prompt = f"{system_prompt}\n\n User : {user_prompt}"
response = requests.post(
"http://localhost:11434/api/generate",
json={
"model": "exaone3.5:2.4b",
"prompt": system_prompt,
"stream": False
}
)
return
################################################ 일정 수정
# 실행 진입전
if __name__ == '__main__':
user_input = input("🗓️ 일정을 입력하세요: ")
classification = "re"
while classification == "re" :
classification = Input_analysis(user_input)
print("classification",classification)
classification = extract_output(classification)
if classification == "add" :
print("일정 추가")
## 이벤트 생성
make_event = MakeSchedule(user_input)
print("make_event!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",make_event)
add_event( make_event )
elif classification == "edit" :
print("일정 수정")
edit_event(user_input)
elif classification == "delete" :
print("일정 삭제")
## 모델 이슈
delete_event(user_input)
else :
print('알 수 없는 명령입니다.')