AI‐6단계‐도구 통합 및 외부 API 활용 설계 - 100-hours-a-week/KTB4-5th-wiki GitHub Wiki
이 단계의 외부 API는 다른 LLM 모델을 호출하기 위한 API가 아닙니다. 저희 서비스에서 사용할 외부 API는 백엔드가 전달한 보유 재료를 바탕으로 레시피 후보를 찾기 위한 요리 데이터 API와 웹 검색 API를 뜻합니다.
외부 API의 역할은 레시피 후보와 출처를 제공하는 데 한정합니다. 외부 응답을 그대로 추천하거나 백엔드 MySQL에 저장하지 않고, AI 서버가 출처·필수 필드·이용 조건을 검증하고 공통 형식으로 정규화한 뒤 AI PostgreSQL에 레시피 원본을 저장합니다.
| API | 선택한 용도 | 비용·호출 조건 | 성능·제약과 저장 정책 |
|---|---|---|---|
식품안전나라 COOKRCP01
|
국내 조리식품 레시피 1차 원천 | 인증키가 필요합니다. 공개 문서에서 이 프로젝트에 적용할 호출 한도와 과금액은 확인하지 못했으므로 발급 화면에서 확인해야 합니다. | 메뉴명·재료명·변경일·요리종류 조건과 JSON·XML 응답을 지원합니다. 실제 지연시간은 운영 키로 측정합니다. 이용약관 확인 후 허용 필드를 저장합니다. |
| TheMealDB | 해외 레시피 보조 원천 | 무료 키 1은 개발·교육·개인 프로젝트용입니다. 공개 앱은 supporter 키가 필요하며 공식 안내의 현재 표시 가격은 £10 일회성입니다. 다중 재료 필터는 Premium입니다. |
이름 검색과 무료 V1의 단일 주재료 필터를 사용합니다. 공식 API 응답은 복사·수정할 수 있지만 사이트 스크래핑은 금지되며 출처를 표시해야 합니다. 실제 지연시간은 별도 측정합니다. |
| Tavily Search·Extract | 두 구조화 API 모두 사용 가능한 후보가 없을 때 추가 URL 탐색 | 월 1,000크레딧 무료, basic 검색은 요청당 1크레딧입니다. 초과 사용은 현재 공식 기준 크레딧당 $0.008이며 개발 키 100RPM, 운영 키 1,000RPM으로 안내됩니다. |
레시피 원천이 아니라 검색 보조 도구입니다. 응답시간 예시는 SLA가 아니므로 직접 측정합니다. 검색 결과는 대상 사이트의 저장·재사용 허가를 별도로 통과해야 합니다. |
확인 근거:
- 식품안전나라
COOKRCP01: https://www.foodsafetykorea.go.kr/api/newDatasetDetail.do?svc_no=COOKRCP01 - 식품안전나라 OpenAPI 신청 절차: https://www.foodsafetykorea.go.kr/api/howToUseApi.do
- TheMealDB API와 이용약관: https://themealdb.com/docs_api_guide.php, https://themealdb.com/terms_of_use.php
- Tavily API와 크레딧: https://docs.tavily.com/documentation/api-reference/introduction, https://docs.tavily.com/documentation/api-credits
API 기능·요금·약관은 바뀔 수 있으므로 배포 전 다시 확인합니다. 식품안전나라를 기본 원천, TheMealDB를 해외 보조 원천, Tavily를 앞선 두 API의 사용 가능한 후보가 0건일 때만 fallback으로 사용하되 운영 활성화는 샘플 품질과 이용 조건 검증을 통과한 공급자부터 진행합니다.
초기 버전에는 MCP를 도입하지 않습니다. 연결할 외부 서비스가 세 개로 한정되고 모두 일반 HTTPS API를 제공하며, LLM이 런타임에 임의 도구를 발견·선택할 필요가 없기 때문입니다. MCP 서버·클라이언트와 도구 권한 계층을 추가하면 현재 범위에서는 배포 대상과 장애 지점만 늘어납니다.
FastAPI 애플리케이션이 도메인 포트인 RecipeSourcePort만 호출하고, 공급자별 REST 어댑터가 인증·속도 제한·재시도·응답 변환을 담당합니다. 오케스트레이터에는 API 키, 공급자 URL, 공급자 응답 객체가 노출되지 않습니다.
flowchart TD
B[백엔드 서비스] -->|내부 HTTPS REST<br/>확정 재고 스냅샷| A[FastAPI 추천 API]
A --> O[Recipe Search Orchestrator]
O --> C{인메모리 추천 캐시 hit?}
C -- 예 --> R[추천 결과 반환]
C -- 아니오 --> PG{AI PostgreSQL에<br/>사용 가능한 레시피가 있는가}
PG -- 예 --> N[레시피 추출·필터·순위]
PG -- 아니오 --> P[RecipeSourcePort]
P --> F[FoodSafetyKoreaAdapter]
F -->|HTTPS REST·API key| FA[식품안전나라]
FA --> FQ{사용 가능한 후보?}
FQ -- 없음 --> M[TheMealDBAdapter]
M -->|HTTPS REST·supporter key| MA[TheMealDB]
MA --> MQ{사용 가능한 후보?}
MQ -- 없음 --> T[TavilyAdapter]
T -->|HTTPS REST·Bearer key| TA[Tavily]
TA --> TQ{저장·재사용 가능한 후보?}
FQ -- 있음 --> V[공통 정규화·검증]
MQ -- 있음 --> V
TQ -- 있음 --> V
TQ -- 없음 --> Z[빈 추천 결과 반환]
V --> S[(AI PostgreSQL<br/>승인된 레시피 원본 저장)]
S --> N
N --> CS[인메모리 추천 캐시 저장]
CS --> R
A -->|커서 기반 변경 REST| B
B --> MY[(백엔드 MySQL<br/>레시피 사본)]
RecipeSourcePort는 모든 공급자를 동시에 호출하는 인터페이스가 아닙니다. 오케스트레이터가 앞 단계에서 사용 가능한 후보를 얻지 못했을 때 다음 어댑터를 호출합니다. Tavily 어댑터는 식품안전나라와 TheMealDB가 모두 후보를 제공하지 못했을 때만 실행됩니다.
gRPC는 사용하지 않습니다. 공급자 자체가 REST이고 호출량이 낮은 초기 서비스에서는 별도의 IDL·프록시 운영 이점이 작습니다. 내부 백엔드와 AI 사이도 기존 HTTP API 계약을 유지합니다. 공급자 수가 크게 늘거나 LLM이 동적으로 승인 도구를 선택해야 하는 요구가 생기면 MCP를 다시 검토합니다.
원본 재료 구성은 정규화 과정에서 덮어쓰지 않습니다. 원본 이름·수량·소비기한·보관방식과 정규화된 검색 이름을 함께 사용해 inventory_fingerprint를 만듭니다. 정규화·중복 제거·정렬한 excludedIngredientNames로 exclusion_fingerprint를 만들고 두 fingerprint와 추천 모드를 cache key로 사용합니다. 따라서 재료 구성, 소비기한 우선순위 또는 제외 재료가 달라지면 이전 캐시를 재사용하지 않습니다.
캐시가 없을 때만 AI PostgreSQL에서 해당 재료 구성으로 만들 수 있는 기존 ACTIVE 레시피를 검색합니다. 기존 레시피가 있으면 외부 API를 호출하지 않고 이를 추출해 제외 재료 필터와 규칙 순위를 적용한 뒤 반환합니다.
AI PostgreSQL에도 사용할 레시피가 없을 때 새로운 레시피를 검색합니다. 검색 순서는 식품안전나라, TheMealDB, Tavily입니다. Tavily는 앞의 두 구조화 API에서 사용 가능한 후보를 모두 찾지 못한 경우에만 최후로 호출합니다. Tavily 검색 결과에 본문이 포함되어도 저장·재사용 권한이 자동으로 생기는 것은 아니므로 이용 조건을 통과한 후보만 정규화하여 AI PostgreSQL에 저장합니다.
각 공급자는 서로 다른 필드와 오류 형식을 사용하므로 FastAPI 오케스트레이터가 공급자 응답을 직접 해석하지 않습니다. 공급자별 어댑터가 다음 공통 결과를 반환합니다.
RecipeSourceCandidate
- source
- sourceRecipeId
- sourceRevision
- sourceUrl
- title
- baseServings
- ingredients[]
- originalName
- amountText
- measureType
- amountValue
- unit
- steps[]
- stepNo
- description
- imageUrl
- attribution
- fetchedAt
- usagePolicyVersion
sourceRecipeId는 공급자가 제공하는 안정 ID를 우선 사용합니다. 공급자 ID나 변경 버전이 없을 때 사용할 식별·revision 생성 방식은 샘플 데이터와 갱신 특성을 검증한 뒤 어댑터별로 정합니다. URL이나 제목만으로 같은 레시피라고 단정하지 않습니다.
필수 필드가 없거나 형식이 잘못된 후보는 저장하지 않습니다. 재료와 단계의 원문 순서는 유지하고, 정규화 값은 원문을 덮어쓰지 않는 별도 값으로 관리합니다.
논리 인터페이스는 공급자별 SDK 타입을 포함하지 않습니다.
RecipeSourcePort.search(query, pageToken) -> RecipeSourcePage
RecipeSourcePort.fetch(sourceRecipeId) -> RecipeSourceCandidate
RecipeSourcePort.health() -> ProviderHealth
RecipeSourcePage
- items[]
- nextPageToken
- providerRequestId
- observedRateLimit
모든 어댑터는 공통 ProviderError로 오류를 변환합니다. INVALID_REQUEST, AUTHENTICATION_FAILED, RATE_LIMITED, TIMEOUT, UPSTREAM_UNAVAILABLE, INVALID_RESPONSE, POLICY_BLOCKED를 구분하여 재시도 가능 여부를 오케스트레이터가 추측하지 않게 합니다.
공식 샘플 JSON을 직접 조회해 COOKRCP01 응답을 확인했습니다.
https://openapi.foodsafetykorea.go.kr/api/sample/COOKRCP01/json/1/3
실제 응답에는 RCP_SEQ, RCP_NM, RCP_PARTS_DTLS, MANUAL01부터 MANUAL20, 각 단계 이미지, 대표 이미지, 조리 방법, 요리 종류, 영양 정보가 포함됩니다. RCP_PARTS_DTLS는 구조화된 배열이 아니라 줄바꿈·쉼표·괄호가 섞인 자유 형식 문자열입니다. 샘플의 페이지 범위와 실제 row 개수가 직관적으로 일치하지 않는 사례도 확인했으므로, 요청 범위만 믿지 않고 응답의 RESULT, total_count, row와 RCP_SEQ를 검증합니다.
| MySQL 필드 | 식품안전나라 원천 | 변환 규칙 |
|---|---|---|
recipe_id |
없음 | 백엔드가 동기화할 때 대리키 부여 |
source |
고정값 | FOOD_SAFETY_KOREA |
source_recipe_id |
RCP_SEQ |
문자열로 보존. source와 묶어 유일성 보장 |
source_revision |
명시 버전 없음 | 저장 대상 원천 필드를 키 순서가 고정된 정규 JSON으로 만든 뒤 SHA-256 해시. 조회시각은 해시에 넣지 않음 |
name |
RCP_NM |
공백 정리 후 저장. 비어 있거나 255자를 넘으면 검증 실패 |
image_url |
ATT_FILE_NO_MAIN, 대체 ATT_FILE_NO_MK
|
빈 문자열은 null. 공급자 이미지 이용 조건 승인 전에는 복제하지 않고 원천 URL만 후보로 보관 |
base_servings |
RCP_PARTS_DTLS 안의 명시 표현 |
[1인분]처럼 명확한 경우만 양수로 파싱하고 그 외에는 null
|
status |
직접 필드 없음 | 정상 수집 항목은 ACTIVE. 비활성은 전체 목록 대조 정책을 통과한 경우에만 전환 |
synced_at |
없음 | 백엔드가 부모·재료·단계를 성공 반영한 시각 |
source_revision 해시에는 최소한 RCP_NM, RCP_PARTS_DTLS, MANUAL01~20, MANUAL_IMG01~20, ATT_FILE_NO_MAIN, ATT_FILE_NO_MK를 포함합니다. 향후 표시하거나 동기화할 필드를 추가하면 revision 계약 버전도 함께 올립니다. 해시는 최신·과거의 크기를 비교하는 버전 번호가 아니라 동일 내용 여부를 확인하는 불투명 토큰입니다.
RCP_PARTS_DTLS 원문은 AI PostgreSQL의 수집 원본에 그대로 보존하고 별도 파서가 재료 행을 만듭니다.
| 원문 예 | name |
amount_text |
measure_type |
amount_value |
unit |
|---|---|---|---|---|---|
연두부 75g(3/4모) |
연두부 | 75g(3/4모) |
WEIGHT |
75 | G |
물 300ml(1½컵) |
물 | 300ml(1½컵) |
VOLUME |
300 | ML |
방울토마토 150g(5개) |
방울토마토 | 150g(5개) |
WEIGHT |
150 | G |
달걀 1개 |
달걀 | 1개 |
COUNT |
1 | EA |
달걀 1/2개 |
달걀 | 1/2개 |
UNKNOWN |
null |
null |
참깨 약간 |
참깨 | 약간 |
UNKNOWN |
null |
null |
괄호 안의 컵·큰술·마리·조각 표현보다 원문 앞쪽의 명시적인 g·ml 값을 우선합니다. kg와 L은 각각 G, ML로 환산합니다. COUNT는 ERD 제약에 맞는 양의 정수일 때만 사용하고 분수·범위·비표준 단위는 UNKNOWN으로 둡니다. 값을 억지로 추정하지 않고 전체 표현은 amount_text에 보존합니다.
양념장, 고명, 새우두부계란찜처럼 수량 표현 없이 구획을 나누는 제목은 재료로 저장하지 않습니다. 파서가 이름과 분량의 경계를 확정하지 못한 조각은 원본에 남기고 동기화 재료 행에서는 제외하며 파싱 경고를 기록합니다. ingredient_no는 확정된 원천 등장 순서대로 1부터 부여합니다.
| MySQL 필드 | 식품안전나라 원천 | 변환 규칙 |
|---|---|---|
recipe_step_id |
없음 | 백엔드가 대리키 부여 |
instruction |
MANUAL01~MANUAL20
|
빈 문자열 제외. 원문 설명은 보존 |
image_url |
같은 번호의 MANUAL_IMG01~MANUAL_IMG20
|
빈 문자열은 null
|
step_no |
원천 필드 번호 |
MANUAL02라면 2. 중간 번호가 비어도 뒤 단계를 당겨 번호를 바꾸지 않음 |
recipe_id |
없음 | 동기화한 부모 recipes.recipe_id 참조 |
실제 샘플에는 MANUAL01이 비어 있고 MANUAL02부터 존재하는 레시피도 있습니다. 이 경우 원천 오류를 숨기기 위해 번호를 다시 매기지 않고 경고와 함께 원천 번호를 유지합니다. 조리 단계가 하나도 없으면 백엔드에 완전한 레시피로 동기화하지 않습니다.
MySQL 사본에 없는 다음 값은 향후 재파싱과 출처 검증을 위해 AI PostgreSQL 수집 원본에 보존합니다.
- 원천 JSON과 원천 스키마 버전
-
RCP_WAY2,RCP_PAT2,HASH_TAG -
INFO_ENG,INFO_CAR,INFO_PRO,INFO_FAT,INFO_NA,INFO_WGT RCP_NA_TIP- 최초 수집시각, 최근 확인시각, 파싱기 버전과 파싱 경고
- 이용약관 확인 버전과 출처 표시 정보
개별 레시피 응답에는 변경 버전과 삭제 표시가 없습니다. CHNG_DT는 변경일 이후 자료를 조회하는 요청 조건이므로 이것만으로 삭제를 탐지할 수 없습니다. 초기에는 전체 수집으로 기준 목록을 만들고, 이후 변경 조회와 정기 전체 대조를 병행합니다. 전체 대조가 중간에 실패했거나 페이지 응답이 비정상이라면 누락 항목을 INACTIVE로 바꾸지 않습니다. 성공한 전체 대조에서 반복해서 사라진 항목을 언제 비활성화할지는 실제 API 동작을 관찰한 뒤 횟수와 기간을 확정합니다.
검색 요청에는 냉장고 전체 내용을 무제한으로 넣지 않습니다. 백엔드가 권한을 확인해 전달한 재료 중 다음 기준으로 검색 재료를 고릅니다.
- 소비기한이 가까운 재료
- 사용자가 선택한 재료
- 레시피 검색에 구별력이 있는 주요 재료
API가 복수 재료 AND 검색을 지원하지 않으면 재료 조합을 제한된 횟수로 나누어 조회하고 결과를 합칩니다. 조합 개수와 공급자별 호출 상한은 실제 검색 결과의 중복률과 호출 제한을 측정한 뒤 정합니다.
excludedIngredientNames는 후보 정규화 직후, 순위 계산 전에 적용합니다. 빈 목록은 알레르기가 없다는 뜻으로 해석하지 않습니다.
AI PostgreSQL은 승인된 출처의 레시피 원본을 관리합니다. 백엔드 MySQL의 recipes, recipe_ingredients, recipe_steps는 동기화 사본입니다.
- AI 서버는 백엔드 MySQL에 직접 접속하거나 쓰지 않습니다.
- 외부 공급자 응답은 출처별 검증을 통과한 뒤에만 AI PostgreSQL에 저장합니다.
- 출처와 원천 ID는
source + sourceRecipeId로 관리합니다. - 같은 원천 버전 재수집은 중복 레시피를 만들지 않아야 합니다.
- 공급자에서 사라진 레시피를 즉시 삭제로 판단하지 않습니다. 비활성 판정 조건을 공급자별로 정한 뒤
INACTIVE변경으로 전달합니다. - 백엔드는 커서 기반 동기화 API를 읽어 자기 MySQL에 트랜잭션으로 반영합니다.
- 이미지 파일을 복제할지 원천 URL만 저장할지는 공급자별 이미지 이용 조건을 확인한 뒤 정합니다.
이 기능에서 외부 API의 대안은 GPU 모델이 아니라 직접 레시피 데이터를 만들고 검색 인덱스를 운영하는 것입니다. GPU나 LLM으로 레시피 문장을 생성할 수는 있지만, 검증된 원천·안정 ID·이용 허락·변경 이력을 제공하지 못하므로 레시피 원천 API를 대체하지 않습니다.
| 방식 | 장점 | 비용·성능·제약 |
|---|---|---|
| 외부 레시피 API | 초기 데이터 확보가 빠르고 원천 ID와 구조화 필드를 활용 가능 | 공급자 장애·호출 한도·약관 변경에 영향받고 공급자별 정규화 필요 |
| Tavily 검색 보조 | 구조화 API에 없는 후보 URL을 찾을 수 있음 | 크레딧 비용과 네트워크 지연이 추가되며 검색 결과에 저장 권한이 포함되지 않음 |
| 자체 레시피 데이터 구축 | 스키마·검색·갱신을 완전히 통제 | 데이터 작성·검수·저작권 확인·검색 인덱스 운영 비용이 큼 |
| GPU·LLM 생성 | 새로운 문장이나 조합 생성 가능 | 사실성·안전성·재현성과 출처 문제가 있으며 GPU 비용을 지불해도 합법적인 원천 데이터가 생기지 않음 |
따라서 초기에는 CPU FastAPI 서버에서 외부 REST API와 PostgreSQL 구조화 검색을 사용합니다. 레시피 검색을 위해 GPU 인스턴스를 추가하지 않습니다. 외부 API 비용은 Tavily 사용량과 TheMealDB supporter 키를 따로 기록하고, AI 서버 EC2 비용과 혼합하지 않습니다.
외부 API 키는 서버 비밀값으로 관리하며 PWA, 백엔드 응답, 로그에 노출하지 않습니다. 공급자마다 다음 값을 별도로 설정합니다.
- 연결·응답 타임아웃
- 요청당 최대 결과 수
- 초·일·월 호출 상한
- 재시도 가능한 상태 코드
- 재시도 횟수와 backoff
- 일시 차단 조건
정확한 숫자는 각 API의 실제 한도와 지연시간을 확인하기 전에는 정하지 않습니다. 429 또는 공급자 장애가 발생하면 무제한 재시도하거나 다른 사이트를 무차별 크롤링하지 않습니다. 확보한 기존 레시피에서 후보를 찾고, 그래도 없으면 후보 부족을 정상 결과로 반환합니다.
Tavily는 검색 깊이에 따라 크레딧이 달라지므로 초기 검증에서는 basic을 명시하고 자동으로 더 비싼 검색 방식이 선택되지 않게 합니다. 무료 크레딧과 가격은 변경될 수 있으므로 배포 시점의 공식 요금표를 다시 확인합니다.
- 원본 재료 구성과 정규화 결과로 만든
inventory_fingerprint, 정규화한 제외 재료의exclusion_fingerprint, 냉장고 ID와 추천 모드를 기준으로 추천 결과 캐시를 먼저 확인합니다. - 캐시 miss일 때 AI PostgreSQL의
ACTIVE레시피를 검색합니다. AI PostgreSQL은 응답 캐시가 아니라 승인된 레시피 원본 저장소입니다. - 추천 결과 캐시는 FastAPI 메모리에 두며 TTL과 최대 건수는 실제 호출량을 측정한 뒤 정합니다. 재시작 후 복구를 보장하지 않습니다.
- 공급자 약관이 캐싱을 제한하면 해당 응답은 메모리 캐시나 PostgreSQL에 저장하지 않습니다.
- 기본 순서는
추천 캐시 → AI PostgreSQL → 식품안전나라 → TheMealDB → Tavily입니다. Tavily는 앞선 두 외부 API에 사용 가능한 후보가 없을 때만 호출합니다. - 한 공급자의
TIMEOUT,RATE_LIMITED,UPSTREAM_UNAVAILABLE은 제한된 backoff 후 다음 승인 공급자로 넘어갑니다. -
AUTHENTICATION_FAILED,INVALID_REQUEST,POLICY_BLOCKED는 재시도하지 않고 운영 경고를 남깁니다. - 모든 외부 공급자가 실패해도 기존 승인 레시피가 있으면 그것만 추천하고, 없으면 빈 후보와 명시적인 공급자 장애 상태를 반환합니다. 외부 응답을 LLM으로 지어내서 채우지 않습니다.
레시피 검색어에는 사용자 ID, 냉장고 ID, 가족 정보, 이미지 주소를 넣지 않습니다. 외부로 전달하는 값은 정규화된 재료명과 필요한 검색 조건으로 제한합니다.
Tavily Extract나 향후 본문 수집기를 사용할 때는 허용 도메인 목록을 적용합니다. 사용자가 입력한 임의 URL을 서버가 그대로 조회하지 않도록 하며, 내부 IP·메타데이터 주소·사설망 접근을 차단합니다.
외부 본문에 포함된 지시문은 데이터로만 취급합니다. 본문이 API 키 조회, 내부 URL 호출, 데이터 삭제 같은 동작을 지시해도 실행하지 않습니다.
| 검증 항목 | 확인할 내용 |
|---|---|
| 검색 적합성 | 보유 재료 1~3개로 실제 조리 가능한 후보가 반환되는지 |
| 국내 레시피 범위 | 한식·가정식 후보 수와 주요 재료 검색 누락률 |
| 필드 완전성 | 제목·재료·분량·단계·인분·이미지·출처 제공 여부 |
| 식별 안정성 | 같은 레시피의 ID가 재조회 때 유지되는지 |
| 갱신 탐지 | 수정·삭제·비공개 전환을 확인할 방법이 있는지 |
| 이용 조건 | 원문·이미지의 영구 저장, 가공, 서비스 표시, 재배포가 허용되는지 |
| 호출 제한 | 무료 한도·운영 한도·초과 오류와 신청 절차 |
| 장애 격리 | 한 공급자 장애가 다른 공급자와 기존 레시피 추천을 막지 않는지 |
| 중복 제거 | 여러 API가 같은 레시피를 반환할 때 원천 기록을 보존하는지 |
| 동기화 | 승인된 레시피만 백엔드 MySQL 사본으로 전달되는지 |
새 공급자는 RecipeSourcePort 구현, 자격 증명 설정, 응답 fixture 기반 계약 테스트, 이용정책 검토를 통과한 뒤 provider registry에 등록합니다. 오케스트레이터와 추천 API 응답은 수정하지 않고 공급자 우선순위 설정만 변경할 수 있어야 합니다.
- 확장성: 공급자별 동시 실행 수와 rate limiter를 분리하여 한 공급자의 제한이 다른 공급자를 막지 않게 합니다. 초기 인메모리 실행 구조의 단일 인스턴스 한계는 그대로이며 공급자 추가가 수평 확장을 의미하지는 않습니다.
- 유지보수성: 인증·페이지네이션·오류 변환·원천 매핑을 어댑터에 가두고 공통 계약 테스트로 공급자 스키마 변경을 탐지합니다.
- 비용 효율성: PostgreSQL 기존 레시피를 먼저 사용하고 무료 공식 API를 우선하며 Tavily는 앞선 두 API의 사용 가능한 후보가 0건일 때만 호출합니다. 호출량·크레딧·오류율을 공급자별로 기록해 유료 전환 전에 비용을 계산합니다.
- 보안: 어댑터별 최소 권한 키와 허용 호스트 목록을 사용합니다. 공급자 하나의 키가 노출돼도 다른 공급자나 데이터베이스 권한으로 확장되지 않게 분리합니다.
- MCP 재검토 조건: 승인 도구가 다수로 증가하거나, 여러 AI 클라이언트가 같은 도구 카탈로그를 공유하거나, 런타임 도구 발견·권한 협상이 필요할 때 MCP 서버 도입안을 다시 작성합니다.
다음 값은 근거 없이 문서에서 확정하지 않고 샘플 검증과 부하 시험 후 결정합니다.
- 공급자별 검색 조합 수, timeout, retry, rate limit과 circuit breaker 임계값
- 추천 결과 캐시 TTL·최대 건수와 공급자 후보의 사용 가능 판정 기준
- AI PostgreSQL 물리 스키마·인덱스, 레시피 재수집 주기와 MySQL 동기화 주기
- 공급자별 원문·이미지 저장, 가공, 서비스 표시와 재배포 허용 범위
- 공급자 ID나 revision이 없는 경우의 안정 식별 규칙과 비활성 판정 기간
저장·재배포 범위를 확인하지 못한 공급자의 응답은 AI PostgreSQL에 영구 저장하지 않습니다. 저장 허가 여부와 검색 주기·비용 원장 등 다른 운영 결정을 서로 묶어서 추정하지 않습니다.