Skip to content

Commit 9f7d0a1

Browse files
순화 과정 AI 모델을 Gemini로 변경 (#183) (#184)
* feat: FirebaseAI, FirebaseAppCheck SPM 추가 및 LLM 폴더 위치 수정 * feat: AppCheck 초기화 로직 추가 * feat: Gemini 순화 로직 추가 및 연결 * chore: 시스템 프롬프트 내용 보완 및 유저 인풋과 분리 * fix: 리뷰 내용 수정
1 parent 2b2851d commit 9f7d0a1

6 files changed

Lines changed: 100 additions & 2 deletions

File tree

GimiFeedback/GimiFeedback.xcodeproj/project.pbxproj

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
D306028E2E377EC500908D1D /* FeedbackWriteViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D306028C2E377EC500908D1D /* FeedbackWriteViewModel.swift */; };
1919
D30602962E3782A200908D1D /* KcELECTRA-base-v2022.mlpackage in Sources */ = {isa = PBXBuildFile; fileRef = D30602952E3782A200908D1D /* KcELECTRA-base-v2022.mlpackage */; };
2020
D36C43842E38A029006A027E /* TokenizerREADME.md in Resources */ = {isa = PBXBuildFile; fileRef = D36C43832E38A025006A027E /* TokenizerREADME.md */; };
21+
EC25CC332E5975880087EC50 /* FirebaseAppCheck in Frameworks */ = {isa = PBXBuildFile; productRef = EC25CC322E5975880087EC50 /* FirebaseAppCheck */; };
22+
EC371D842E55E9AA008E4AB7 /* GeminiManger.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC371D832E55E9AA008E4AB7 /* GeminiManger.swift */; };
23+
EC371DC92E560D76008E4AB7 /* FirebaseAI in Frameworks */ = {isa = PBXBuildFile; productRef = EC371DC82E560D76008E4AB7 /* FirebaseAI */; };
2124
EC6A4C842E31284F0087DB5C /* ChannelDetail+DescriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6A4C832E31284F0087DB5C /* ChannelDetail+DescriptionView.swift */; };
2225
EC6A4C862E3129300087DB5C /* ChannelDetail+EmptyFeedbackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6A4C852E3129300087DB5C /* ChannelDetail+EmptyFeedbackView.swift */; };
2326
EC6A4C882E312A3B0087DB5C /* ChannelDetail+FeedbackListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6A4C872E312A3B0087DB5C /* ChannelDetail+FeedbackListView.swift */; };
@@ -131,6 +134,7 @@
131134
D306028C2E377EC500908D1D /* FeedbackWriteViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackWriteViewModel.swift; sourceTree = "<group>"; };
132135
D30602952E3782A200908D1D /* KcELECTRA-base-v2022.mlpackage */ = {isa = PBXFileReference; lastKnownFileType = folder.mlpackage; path = "KcELECTRA-base-v2022.mlpackage"; sourceTree = "<group>"; };
133136
D36C43832E38A025006A027E /* TokenizerREADME.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = TokenizerREADME.md; sourceTree = "<group>"; };
137+
EC371D832E55E9AA008E4AB7 /* GeminiManger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeminiManger.swift; sourceTree = "<group>"; };
134138
EC6A4C832E31284F0087DB5C /* ChannelDetail+DescriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChannelDetail+DescriptionView.swift"; sourceTree = "<group>"; };
135139
EC6A4C852E3129300087DB5C /* ChannelDetail+EmptyFeedbackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChannelDetail+EmptyFeedbackView.swift"; sourceTree = "<group>"; };
136140
EC6A4C872E312A3B0087DB5C /* ChannelDetail+FeedbackListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChannelDetail+FeedbackListView.swift"; sourceTree = "<group>"; };
@@ -237,10 +241,12 @@
237241
EEDBB1BE2E2913CE00F16407 /* FirebaseFunctions in Frameworks */,
238242
D306027D2E377E8400908D1D /* Transformers in Frameworks */,
239243
EE41FEDE2E2250DF0002E595 /* KakaoSDKCommon in Frameworks */,
244+
EC25CC332E5975880087EC50 /* FirebaseAppCheck in Frameworks */,
240245
EE3755462E1E5B8D009AD262 /* FirebaseAuth in Frameworks */,
241246
ECC980B12E290DC4004A483B /* KakaoSDKTemplate in Frameworks */,
242247
EE3755482E1E5B8D009AD262 /* FirebaseFirestore in Frameworks */,
243248
EE41FEE02E2250DF0002E595 /* KakaoSDKUser in Frameworks */,
249+
EC371DC92E560D76008E4AB7 /* FirebaseAI in Frameworks */,
244250
EE41FEDC2E2250DF0002E595 /* KakaoSDKAuth in Frameworks */,
245251
ECC980AF2E290DC4004A483B /* KakaoSDKShare in Frameworks */,
246252
);
@@ -299,6 +305,15 @@
299305
path = MLWeights;
300306
sourceTree = "<group>";
301307
};
308+
EC371D822E55E99C008E4AB7 /* LLM */ = {
309+
isa = PBXGroup;
310+
children = (
311+
EC94C0F22E3746130020E59D /* GPTManger.swift */,
312+
EC371D832E55E9AA008E4AB7 /* GeminiManger.swift */,
313+
);
314+
path = LLM;
315+
sourceTree = "<group>";
316+
};
302317
EC6A4C822E31274B0087DB5C /* SubViews */ = {
303318
isa = PBXGroup;
304319
children = (
@@ -593,7 +608,6 @@
593608
EE856A4B2E1CD3AA006820B9 /* FirestoreManager.swift */,
594609
EE41FEEC2E225A1C0002E595 /* FirebaseAuthManager.swift */,
595610
EEDBB1BB2E28DEB900F16407 /* FCMManager.swift */,
596-
EC94C0F22E3746130020E59D /* GPTManger.swift */,
597611
ECC980AB2E28E91E004A483B /* DeepLinkManager.swift */,
598612
ECC980B42E294835004A483B /* KakaoShareManager.swift */,
599613
EE41FEEA2E2259980002E595 /* KakaoAuthManager.swift */,
@@ -666,6 +680,7 @@
666680
EEFEBF592E4B7EFB001D824D /* AI */ = {
667681
isa = PBXGroup;
668682
children = (
683+
EC371D822E55E99C008E4AB7 /* LLM */,
669684
D30602832E377E9700908D1D /* MLModel */,
670685
D30602892E377EA100908D1D /* MLWeights */,
671686
);
@@ -700,6 +715,8 @@
700715
EEDBB1B72E28D45700F16407 /* FirebaseMessaging */,
701716
EEDBB1BD2E2913CE00F16407 /* FirebaseFunctions */,
702717
D306027C2E377E8400908D1D /* Transformers */,
718+
EC371DC82E560D76008E4AB7 /* FirebaseAI */,
719+
EC25CC322E5975880087EC50 /* FirebaseAppCheck */,
703720
);
704721
productName = A10;
705722
productReference = EE856A262E1CD093006820B9 /* GimiFeedback.app */;
@@ -836,6 +853,7 @@
836853
EC6A4CA52E350B720087DB5C /* Spicy+Extension.swift in Sources */,
837854
EE37553D2E1E10EA009AD262 /* FeedbackContentType.swift in Sources */,
838855
EE37554A2E1E5BBD009AD262 /* AppDelegate.swift in Sources */,
856+
EC371D842E55E9AA008E4AB7 /* GeminiManger.swift in Sources */,
839857
D30602962E3782A200908D1D /* KcELECTRA-base-v2022.mlpackage in Sources */,
840858
EE3755412E1E1105009AD262 /* FeedbackChannel.swift in Sources */,
841859
EE37553F2E1E10F9009AD262 /* FeedbackContent.swift in Sources */,
@@ -1153,6 +1171,16 @@
11531171
package = D306027B2E377E8400908D1D /* XCRemoteSwiftPackageReference "swift-transformers" */;
11541172
productName = Transformers;
11551173
};
1174+
EC25CC322E5975880087EC50 /* FirebaseAppCheck */ = {
1175+
isa = XCSwiftPackageProductDependency;
1176+
package = EE3755442E1E5B8D009AD262 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
1177+
productName = FirebaseAppCheck;
1178+
};
1179+
EC371DC82E560D76008E4AB7 /* FirebaseAI */ = {
1180+
isa = XCSwiftPackageProductDependency;
1181+
package = EE3755442E1E5B8D009AD262 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
1182+
productName = FirebaseAI;
1183+
};
11561184
ECC980AE2E290DC4004A483B /* KakaoSDKShare */ = {
11571185
isa = XCSwiftPackageProductDependency;
11581186
package = EE41FEDA2E2250DF0002E595 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */;
File renamed without changes.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import Foundation
2+
import FirebaseAI
3+
4+
final class GeminiManger {
5+
static let shared = GeminiManger()
6+
7+
private let model: GenerativeModel?
8+
9+
private init() {
10+
let firebaseService = FirebaseAI.firebaseAI(backend: .googleAI())
11+
model = firebaseService.generativeModel(
12+
modelName: "gemini-2.5-flash",
13+
systemInstruction: ModelContent(role: "system", parts: systemPrompt))
14+
}
15+
16+
let systemPrompt = """
17+
너는 어떤 피드백이든 무조건 부드럽고 배려 있는 말로 순화하는 전문가야.
18+
너의 유일한 임무는 사용자 피드백을 공감적인 말투로 바꾸는 것이며, 이외의 추가적인 대화나 설명은 절대 하지 마.
19+
20+
핵심 메시지와 개선 방향은 그대로 유지하되, 상대방이 기분 나쁘지 않게 받아들일 수 있도록 마치 친한 동료가 진심을 담아 조언해주는 듯한 어조를 사용해.
21+
긍정적이거나 이미 부드러운 피드백이더라도, 더 따뜻하고 공감적인 표현으로 다듬어서 결과물만 출력해.
22+
23+
아래는 예시야:
24+
25+
<예시>
26+
<입력 예시>회의 시간에 집중 좀 해. 계속 딴짓해서 짜증나.</입력 예시>
27+
<출력 예시>회의에 더 집중해주면 좋겠어요. 가끔 딴짓하는 모습이 보여서 회의 흐름이 조금 끊기는 느낌이 있었어요.</출력 예시>
28+
</예시>
29+
30+
<예시>
31+
<입력 예시>일을 너무 느리게 해. 같이 일하기 힘들어.</입력 예시>
32+
<출력 예시>업무 속도가 조금 더 빨라지면 협업이 훨씬 원활할 것 같아요. 지금도 충분히 잘하고 있지만, 약간만 더 신경 써주면 좋겠어요.</출력 예시>
33+
</예시>
34+
35+
<예시>
36+
<입력 예시>말이 너무 많고 눈치가 없어서 분위기 망치고 있어.</입력 예시>
37+
<출력 예시>이야기를 적극적으로 해주는 건 좋지만, 상황에 맞게 조절해주면 더 좋은 분위기가 될 수 있을 것 같아요.</출력 예시>
38+
</예시>
39+
40+
위와 같이, 표현은 순화하지만 피드백의 본질은 흐리지 않도록 해줘.
41+
"""
42+
43+
}
44+
45+
extension GeminiManger {
46+
func generate(inputText: String) async throws -> String {
47+
guard let model else { return "" }
48+
49+
let response = try await model.generateContent(inputText)
50+
return response.text ?? ""
51+
}
52+
}

GimiFeedback/GimiFeedback/App/AppDelegate.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import SwiftUI
99
import FirebaseCore
1010
import UserNotifications
1111
import FirebaseMessaging
12+
import FirebaseAppCheck
1213

1314
final class AppDelegate: NSObject, UIApplicationDelegate {
1415
var saveUserInfo: [AnyHashable: Any]?
@@ -17,6 +18,10 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
1718
_ application: UIApplication,
1819
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
1920
) -> Bool {
21+
22+
let providerFactory = YourAppCheckProviderFactory()
23+
AppCheck.setAppCheckProviderFactory(providerFactory)
24+
2025
FirebaseApp.configure()
2126
setUIAppearance()
2227

@@ -134,3 +139,14 @@ extension AppDelegate {
134139
)
135140
}
136141
}
142+
143+
final class YourAppCheckProviderFactory: NSObject, AppCheckProviderFactory {
144+
func createProvider(with app: FirebaseApp) -> AppCheckProvider? {
145+
// 시뮬레이터 환경에서는 AppCheckDebugProvider를 사용하도록 설정합니다.
146+
#if targetEnvironment(simulator)
147+
return AppCheckDebugProvider(app: app)
148+
#else
149+
return AppAttestProvider(app: app)
150+
#endif
151+
}
152+
}

GimiFeedback/GimiFeedback/App/GimiFeedback.entitlements

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@
44
<dict>
55
<key>aps-environment</key>
66
<string>development</string>
7+
<key>com.apple.developer.devicecheck.appattest-environment</key>
8+
<string>production</string>
79
</dict>
810
</plist>

GimiFeedback/GimiFeedback/Presentation/FeedbackDetail/FeedbackDetailViewModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ extension FeedbackDetailViewModel {
7373
private func transFeedbackContent(content: FeedbackContent) {
7474
Task {
7575
isTransLoading = true
76-
let response = try await GPTManger.shared.sendChatCompletion(inputText: content.content)
76+
let response = try await GeminiManger.shared.generate(inputText: content.content)
7777

7878
guard let index = feedbackItem.content.firstIndex(where: { $0.id == content.id }) else { return }
7979

0 commit comments

Comments
 (0)