최근 CrewAI를 이용하여 사용자 여행 일정 기반 맛집 추천 시스템을 개발했다.
구현 과정에서 몇 가지 비효율적인 데이터 요청과 구조적인 문제를 발견했고, 이를 해결하기 위해 코드를 수정했다.
이 글에서는 개선 전 코드에서 어떤 문제점이 있었고, 어떻게 개선했는지를 정리해본다.
1. 개선 전 코드의 문제점
① 문제: 모든 세부 정보를 한 번에 가져오는 비효율적인 구조
- 기존에는 RestaurantBasicSearchTool에서 식당 정보를 가져올 때, 모든 세부 정보를 포함해서 요청했다.
- 하지만 필터링 기준(평점 4.0 이상 & 리뷰 수 500개 이상)에 맞지 않는 데이터까지 세부 정보를 가져와 불필요한 API 요청이 발생했다.
개선 전 코드
- 필터링 전 모든 세부 정보를 가져옴 → 필터링 후 버려지는 데이터에도 API 요청을 소비함
- 불필요한 API 요청이 증가하면서 성능 저하 & 비용 증가
class RestaurantBasicSearchTool(BaseTool):
def _run(self, location: str, coordinates: str) -> List[Dict]:
url = "https://serpapi.com/search"
params = {
"engine": "google_maps",
"q": f"{location} 맛집",
"ll": f"@{coordinates},14z",
"api_key": SERPAPI_API_KEY,
}
response = requests.get(url, params=params)
data = response.json()
return [
{
"title": r["title"],
"rating": r.get("rating", 0),
"reviews": r.get("reviews", 0),
"place_id": r.get("place_id", ""),
"address": r.get("address", ""),
"phone_number": r.get("phone_number", ""),
}
for r in data.get("local_results", [])
]
② 개선 방향
해결책: 기본 검색 단계에서는 최소한의 정보만 가져오고, 필터링 후 세부 정보를 요청하도록 개선
- 1단계: 식당 검색 시 이름, 평점, 리뷰 수, place_id만 가져오기
- 2단계: 필터링 후 place_id를 이용해 필요한 세부 정보만 가져오기
- 효과: 불필요한 API 요청 감소 & 성능 최적화
③ 개선된 코드
(1) 기본 검색 시 최소한의 정보만 가져오기
- 불필요한 정보 제거 (예: 웹사이트 url, 전화번호 등)
- place_id만 남겨서 이후 필요한 세부 정보만 요청 가능
- 필터링 이후 세부 정보를 가져오도록 구조 변경
class RestaurantBasicSearchTool(BaseTool):
def _run(self, location: str, coordinates: str) -> List[Dict]:
url = "https://serpapi.com/search"
all_candidates = []
for start in [0, 20]: # 최대 40개까지 조회
params = {
"engine": "google_maps",
"q": f"{location} 맛집",
"ll": f"@{coordinates},14z",
"api_key": SERPAPI_API_KEY,
"start": start,
}
response = requests.get(url, params=params)
data = response.json()
candidates = [
{
"title": r["title"],
"rating": r.get("rating", 0),
"reviews": r.get("reviews", 0),
"place_id": r.get("place_id", ""),
}
for r in data.get("local_results", [])
]
all_candidates.extend(candidates)
return all_candidates
(2) 필터링 후 place_id를 이용해 필요한 세부 정보 요청
- 필터링된 식당들만 세부 정보를 요청
- 최소한의 API 요청으로 비용 절감
- 성능 최적화 및 불필요한 데이터 전송 감소
class RestaurantDetailTool(BaseTool):
def _run(self, place_id: str) -> Dict:
url = "https://maps.googleapis.com/maps/api/place/details/json"
params = {"placeid": place_id, "key": GOOGLE_MAP_API_KEY}
response = requests.get(url, params=params)
data = response.json()
result = data.get("result", {})
return {
"address": result.get("formatted_address", ""),
"opening_hours": result.get("opening_hours", {}).get("weekday_text", []),
"website": result.get("website", ""),
"phone_number": result.get("formatted_phone_number", ""),
}
개선 효과
1. API 요청 감소
- 기존에는 40개 맛집 모두의 세부 정보를 요청했지만,
→ 필터링 후 place_id를 이용해 필요한 곳만 요청하도록 개선
→ API 요청 수 40회 → 평균 10~15회로 감소
→ Google Places API 비용 절감
2. 성능 향상
- 불필요한 데이터를 가져오지 않으니 응답 속도가 빨라짐
- API 요청을 줄여서 최종 결과 반환 시간 단축
데이터 최적화 vs 정확도
필요 없는 식당에 대한 불필요한 데이터 요청을 줄여서 처리 속도를 개선한 점은 긍정적이지만, 여전히 API 요청을 두 번 수행해야 하는 문제가 남아 있다. 세부 정보 없이 LLM에게 맛집 추천을 요청하면 기준이 모호해 객관적인 리스트를 얻기 어렵고, 반대로 모든 세부 정보를 받아서 추천을 요청하면 글자 수 제한에 걸릴 가능성이 높으며, 불필요한 데이터로 인해 요청 시간이 길어지는 문제가 발생한다. 하지만 정확한 맛집 리스트를 추천받으려면 결국 어느 정도의 세부 정보 요청은 불가피하다는 점도 고려해야 한다. 최적의 방식에 대해 여전히 고민 중이다.
'AI' 카테고리의 다른 글
[AI] 네이버 웹 검색 API × CrewAI 식당 검색, 동기 방식에서 비동기로 업그레이드 (0) | 2025.02.11 |
---|---|
[AI] CrewAI 태스크 결과가 서비스에서 라우터로 정상 전달되지 않는 문제 해결과정 (0) | 2025.02.06 |
[AI] CrewAI에서의 Tool, Agent, Task 개념 (0) | 2025.02.02 |
[AI] CrewAI 설치 시 Microsoft Visual C++ 관련 오류 해결 (0) | 2025.02.01 |
[AI] LangChain 사용해보기3 (LLM과 네이버 검색 API를 활용한 맛집 데이터 수집 테스트) (0) | 2025.01.23 |