Skip to content

Commit de83fe2

Browse files
authored
Merge pull request #13 from hanjuhn/main
feat: 각 step 별로 필요한 정보가 없으면 추가 입력 요청
2 parents 0d868e9 + 27fb940 commit de83fe2

1 file changed

Lines changed: 63 additions & 45 deletions

File tree

core/tariff_prediction/agent/tariff_prediction_agent.py

Lines changed: 63 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
from core.tariff_prediction.tools.parse_hs_results import parse_hs6_result, generate_hs10_candidates
1111
from core.tariff_prediction.tools.parse_tariff_result import parse_tariff_result
1212
from core.tariff_prediction.constants import SUPPORTED_COUNTRIES, SCENARIOS, OFF_TOPIC_KEYWORDS, CORRECTION_KEYWORDS, SESSION_TERMINATION_KEYWORDS
13+
from core.tariff_prediction.agent.step_api import tariff_prediction_step_api
14+
from core.tariff_prediction.dto.tariff_request import TariffPredictionRequest
15+
from core.tariff_prediction.dto.tariff_response import TariffPredictionResponse
1316

1417
# 전역 워크플로우 매니저
1518
class WorkflowManager:
@@ -101,8 +104,9 @@ def is_off_topic(self, user_input: str) -> bool:
101104
return any(keyword in input_lower for keyword in OFF_TOPIC_KEYWORDS)
102105

103106
def process_user_input(self, user_input: str) -> str:
104-
"""사용자 입력을 처리하고 적절한 응답을 반환합니다."""
105-
107+
"""
108+
사용자 입력을 처리하고 적절한 응답을 반환합니다.
109+
"""
106110
# 세션 중단 요청 확인
107111
if any(word in user_input for word in SESSION_TERMINATION_KEYWORDS):
108112
self.reset_session()
@@ -126,8 +130,8 @@ def process_user_input(self, user_input: str) -> str:
126130
return self.handle_hs6_selection(user_input)
127131
elif self.state['current_step'] == 'hs10_selection':
128132
return self.handle_hs10_selection(user_input)
129-
elif self.state['current_step'] == 'calculation':
130-
return self.handle_calculation(user_input)
133+
# elif self.state['current_step'] == 'calculation':
134+
# return self.handle_calculation(user_input)
131135

132136
return "죄송합니다. 현재 상태를 인식할 수 없습니다. 처음부터 다시 시작하겠습니다."
133137

@@ -157,9 +161,7 @@ def handle_scenario_selection(self, user_input: str) -> str:
157161
번호를 입력하거나 상황을 설명해 주세요."""
158162

159163
def handle_input_collection(self, user_input: str) -> str:
160-
"""자연어 입력 수집을 처리합니다."""
161164
parsed = self.parse_user_input(user_input)
162-
163165
# 필수 정보 확인
164166
missing_info = []
165167
if 'product_name' not in parsed:
@@ -168,30 +170,29 @@ def handle_input_collection(self, user_input: str) -> str:
168170
missing_info.append("구매 국가")
169171
if 'price' not in parsed:
170172
missing_info.append("상품 가격")
171-
172173
if missing_info:
173174
missing_str = ", ".join(missing_info)
174175
return f"다음 정보가 누락되었습니다: {missing_str}\n\n💡 **상품 묘사의 정확도가 높을수록 정확한 관세 예측이 가능합니다!**\n\n예시:\n\"아랫창은 고무로 되어있고 하얀색 운동화를 80000원에 독일에서 샀어요\"\n\"인텔 i7 노트북을 150만원에 미국에서 구매했어요\"\n\"블루투스 이어폰 2개를 12만원에 일본에서 샀어요\""
175-
176-
# 국가 지원 여부 확인
177-
if not self.is_supported_country(parsed['country']):
178-
return f"죄송합니다. '{parsed['country']}'의 환율 정보는 현재 제공되지 않습니다. 지원되는 국가로 다시 입력해 주세요."
179-
180-
# 상품 설명 정제
181-
parsed['product_name'] = clean_product_description(parsed['product_name'])
182-
183176
# 상태 업데이트
184177
self.state.update(parsed)
178+
# step_api.py 활용
179+
req = TariffPredictionRequest(
180+
step="input",
181+
product_description=parsed['product_name'],
182+
origin_country=parsed['country'],
183+
price=parsed['price'],
184+
quantity=parsed.get('quantity', 1),
185+
shipping_cost=parsed.get('shipping_cost', 0),
186+
scenario=self.state.get('scenario')
187+
)
188+
resp: TariffPredictionResponse = tariff_prediction_step_api(req)
189+
if resp.message and (not resp.hs6_candidates):
190+
return resp.message
191+
self.state['hs6_candidates'] = resp.hs6_candidates
185192
self.state['current_step'] = 'hs6_selection'
186-
187-
# HS6 예측
188-
try:
189-
hs6_result = get_hs_classification(parsed['product_name'])
190-
self.state['hs6_candidates'] = self.parse_hs6_result(hs6_result)
191-
192-
return f"상품묘사: {parsed['product_name']}\n국가: {parsed['country']}\n가격: {parsed['price']:,}\n수량: {parsed.get('quantity', 1)}\n\nHS6 코드 후보를 찾았습니다. 번호를 선택해 주세요:\n{self.format_hs6_candidates()}"
193-
except Exception as e:
194-
return f"HS 코드 예측 중 오류가 발생했습니다: {str(e)}"
193+
return f"상품묘사: {parsed['product_name']}\n국가: {parsed['country']}\n가격: {parsed['price']:,}\n수량: {parsed.get('quantity', 1)}\n\nHS6 코드 후보를 찾았습니다. 번호를 선택해 주세요:\n" + '\n'.join([
194+
f"{i+1}. {c['code']} - {c['description']} (신뢰도: {c['confidence']})" for i, c in enumerate(resp.hs6_candidates or [])
195+
])
195196

196197
def parse_hs6_result(self, hs6_result: str) -> List[Dict]:
197198
"""HS6 결과를 파싱합니다."""
@@ -205,23 +206,28 @@ def format_hs6_candidates(self) -> str:
205206
return formatted
206207

207208
def handle_hs6_selection(self, user_input: str) -> str:
208-
"""HS6 선택을 자연어로 처리합니다."""
209-
# 숫자만 추출
210209
number_match = re.search(r'(\d+)', user_input)
211-
if number_match:
210+
if number_match and self.state.get('hs6_candidates'):
212211
selection = int(number_match.group(1))
213-
if 1 <= selection <= len(self.state['hs6_candidates']):
214-
selected = self.state['hs6_candidates'][selection - 1]
212+
candidates = self.state['hs6_candidates']
213+
if 1 <= selection <= len(candidates):
214+
selected = candidates[selection - 1]
215215
self.state['hs6_code'] = selected['code']
216+
# step_api.py 활용
217+
req = TariffPredictionRequest(
218+
step="hs6_select",
219+
hs6_code=selected['code']
220+
)
221+
resp: TariffPredictionResponse = tariff_prediction_step_api(req)
222+
if resp.message and (not resp.hs10_candidates):
223+
return resp.message
224+
self.state['hs10_candidates'] = resp.hs10_candidates
216225
self.state['current_step'] = 'hs10_selection'
217-
218-
# HS10 후보 생성
219-
hs10_candidates = self.generate_hs10_candidates(selected['code'])
220-
self.state['hs10_candidates'] = hs10_candidates
221-
222-
return f"선택하신 HS6 코드: {selected['code']}\n\nHS10 코드 후보를 선택해 주세요:\n{self.format_hs10_candidates()}"
226+
return f"선택하신 HS6 코드: {selected['code']}\n\nHS10 코드 후보를 선택해 주세요:\n" + '\n'.join([
227+
f"{i+1}. {c['code']} - {c['description']}" for i, c in enumerate(resp.hs10_candidates or [])
228+
])
223229
else:
224-
return f"1부터 {len(self.state['hs6_candidates'])} 사이의 번호를 입력해 주세요."
230+
return f"1부터 {len(candidates)} 사이의 번호를 입력해 주세요."
225231
else:
226232
return f"숫자를 입력해 주세요. (예: 1, 2, 3)"
227233

@@ -237,19 +243,31 @@ def format_hs10_candidates(self) -> str:
237243
return formatted
238244

239245
def handle_hs10_selection(self, user_input: str) -> str:
240-
"""HS10 선택을 자연어로 처리합니다."""
241-
# 숫자만 추출
242246
number_match = re.search(r'(\d+)', user_input)
243-
if number_match:
247+
if number_match and self.state.get('hs10_candidates'):
244248
selection = int(number_match.group(1))
245-
if 1 <= selection <= len(self.state['hs10_candidates']):
246-
selected = self.state['hs10_candidates'][selection - 1]
249+
candidates = self.state['hs10_candidates']
250+
if 1 <= selection <= len(candidates):
251+
selected = candidates[selection - 1]
247252
self.state['hs10_code'] = selected['code']
248-
self.state['current_step'] = 'calculation'
249-
250-
return self.perform_calculation()
253+
# step_api.py 활용
254+
req = TariffPredictionRequest(
255+
step="hs10_select",
256+
hs10_code=selected['code'],
257+
origin_country=self.state.get('country'),
258+
price=self.state.get('price'),
259+
quantity=self.state.get('quantity', 1),
260+
shipping_cost=self.state.get('shipping_cost', 0),
261+
scenario=self.state.get('scenario')
262+
)
263+
resp: TariffPredictionResponse = tariff_prediction_step_api(req)
264+
self.reset_session()
265+
if resp.calculation_result:
266+
return f"# 🎯 관세 계산 결과\n{resp.calculation_result}\n\n{resp.message or ''}"
267+
else:
268+
return resp.message or "계산 결과를 가져오지 못했습니다."
251269
else:
252-
return f"1부터 {len(self.state['hs10_candidates'])} 사이의 번호를 입력해 주세요."
270+
return f"1부터 {len(candidates)} 사이의 번호를 입력해 주세요."
253271
else:
254272
return f"숫자를 입력해 주세요. (예: 1, 2, 3)"
255273

0 commit comments

Comments
 (0)