Agentic AI 워크샵: AI 투자 어드바이저
openapi: 3.0.0
info:
title: Naver Book Search API
version: 1.0.0
description: 네이버 책 검색 API를 호출하여 아동 도서를 추천하는 Bedrock Agent 액션 그룹
paths:
/search/books:
get:
summary: Search books on Naver
description: 사용자가 제공한 검색어를 기반으로 네이버 책 검색 API를 호출하여 책 목록을 반환합니다.
operationId: searchBooks
parameters:
- name: query
in: query
description: 검색어
required: true
schema:
type: string
- name: display
in: query
description: 반환할 책의 개수
required: false
schema:
type: integer
default: 3
responses:
'200':
description: 성공적으로 책 목록을 반환
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Book'
'400':
description: 검색어가 누락된 경우
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: 서버 오류
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
Book:
type: object
properties:
title:
type: string
description: 책 제목
author:
type: string
description: 저자
publisher:
type: string
description: 출판사
pubdate:
type: string
description: 출판 날짜 (YYYYMMDD 형식)
description:
type: string
description: 책 설명
isbn:
type: string
description: ISBN 번호
price:
type: string
description: 가격
image:
type: string
description: 책 표지 이미지 URL
link:
type: string
description: 책 상세 페이지 URL
required:
- title
- author
Error:
type: object
properties:
error:
type: string
description: 오류 메시지
required:
- error
당신은 네이버 책 검색 API를 호출하여 사용자가 입력한 검색어를 기반으로 아이들을 위한 책을 추천합니다. 사용자 쿼리에서 검색어를 추출하고, 그 중에서 적절한 아동 도서 3권을 선별하여 제공합니다.
사용자는 책 검색어를 제공합니다. 예) "한국 전래 동화", "직업 동화", "세계 전래 동화", "판타지 동화", "동물 동화", "가족 동화"
당신의 작업:
1. 람다 함수에 정의된 naver 책 검색 api 를 통해 책 데이터를 가져옵니다.
2. 책 데이터 중에서 7세 미만의 아이들에게 적합한 최대 3개의 책을 골라 추천해줍니다.
3. 추천 이유를 간략히 설명하고, 각 책의 특징을 한 문장으로 소개하세요.
추천된 책은 다음 형식으로 사용자에게 제공하세요:
[
{
"title": "마루 밑 고양이 마루 (소중애 동물동화)",
"link": "<https://search.shopping.naver.com/book/catalog/34774842619>",
"image": "<https://shopping-phinf.pstatic.net/main_3477484/34774842619.20230313184029.jpg>",
"author": "소중애",
"publisher": "예림당",
},
{
"title": "어쩌다 쭈구리 (소중애 동물동화)",
"link": "<https://search.shopping.naver.com/book/catalog/32496263038>",
"image": "<https://shopping-phinf.pstatic.net/main_3249626/32496263038.20221019114301.jpg>",
"author": "소중애",
"publisher": "예림당",
},
{
"title": "동물들의 그림동화",
"link": "<https://search.shopping.naver.com/book/catalog/53143517737>",
"image": "<https://shopping-phinf.pstatic.net/main_5314351/53143517737.20250225082628.jpg>",
"author": "그림형제",
"publisher": "하늘퍼블리싱",
},
]
응답 시 다음 사항을 고려하세요:
- 최소 1개 이상의 책은 추천해야 합니다.
- 부정적인 설명을 담고 있는 책은 제외해야 합니다.
- 3개의 책들이 각각 유사하지 않도록 다양하게 책을 추천해야 합니다.
이 에이전트는 네이버 책 검색 API를 호출하여 사용자가 입력한 검색어를 기반으로 아이들을 위한 책을 추천합니다. 사용자 쿼리에서 검색어를 추출하고, 그 중에서 적절한 아동 도서 3권을 선별하여 제공합니다.
에이전트는 사용자의 질문을 이해하고, JSON 형식으로 응답하는 역할을 수행합니다. 제공된 규칙을 엄격히 준수해야 합니다.
사용자는 책 검색어를 제공하거나 특정 주제나 연령대에 맞는 책을 요청할 수 있습니다. 예) "한국 전래 동화", "직업 동화"
에이전트는 다음과 같은 요청을 처리해야 합니다:
일반 정보 질문: 사용자가 특정 책에 대한 추천을 요청하면, 관련 정보를 제공해야 합니다.
일반 대화 응답: AI가 제공할 수 없는 질문이 들어올 경우, 가이드 메시지를 출력해야 합니다.
추천된 책은 다음 형식으로 사용자에게 제공하세요:
{
"title": "판타지 동화 (33일동안 들려주는 안데르센의)",
"author": "계림 닷컴 편집부",
"publisher": "계림",
"pubdate": "20060220",
"description": "『판타지동화』는 1839년에 출판된 책입니다. 안데르센은 어린 시절의 친구 헨리에터 한크에게 보내는 편지에서 아라비안나이트를 쓰기 시작했다고 밝히고 있는데 그때 쓴 책이 바로 『판타지 동화』이지요. 안데르센이 들려주는 33가지 이야기는 안데르센이 직접 보고 들은 이야기라고 합니다. 신비하고 환상적인 이야기는 화려하고 화사한 그림을 보는 것 같은 느낌을 줍니다....",
"isbn": "9788953308947",
"price": "",
"image": "<https://shopping-phinf.pstatic.net/main_3250898/32508989761.20221019140216.jpg>",
"link": "<https://search.shopping.naver.com/book/catalog/32508989761>"
},
추천 이유를 간략히 설명하고, 각 책의 특징을 한 문장으로 소개하세요.
응답 형식 잘 맞춰줘야 함. 안맞으면 응답 안옴. → 제일 삽질한 부분.
import json
import urllib.request
import boto3
from botocore.exceptions import ClientError
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
try:
# 네이버 API 키
client_id = "571YeEEAPpAkp_lE_ZBM"
client_secret = "w5UbpSzB8G"
logger.info(f"Received event: {event}")
# Bedrock 에이전트 요청 구조 처리
action_group = event.get('actionGroup', '')
api_path = event.get('apiPath', '')
http_method = event.get('httpMethod', 'GET')
message_version = event.get('messageVersion', '1.0')
# 파라미터 리스트에서 값 추출
parameters = event.get('parameters', [])
query = ""
display = 3
# 리스트 형태의 파라미터 처리
for param in parameters:
if param.get('name') == 'query':
query = param.get('value', '')
elif param.get('name') == 'display':
try:
display = int(param.get('value', 3))
except ValueError:
display = 3
logger.info(f"Extracted parameters - query: {query}, display: {display}")
if not query:
return {
'messageVersion': message_version,
'response': {
'actionGroup': action_group,
'apiPath': api_path,
'httpMethod': http_method,
'httpStatusCode': 400,
'responseBody': {
'application/json': {
'body': json.dumps({'error': '검색어가 필요합니다.'}, ensure_ascii=False)
}
}
}
}
# 네이버 API 호출
encQuery = urllib.parse.quote(query)
url = f"<https://openapi.naver.com/v1/search/book.json?query={encQuery}&display={display}>"
request = urllib.request.Request(url)
request.add_header("X-Naver-Client-Id", client_id)
request.add_header("X-Naver-Client-Secret", client_secret)
response = urllib.request.urlopen(request)
response_body = response.read()
result = json.loads(response_body.decode('utf-8'))
# 결과 포맷팅
books = []
for item in result.get('items', []):
book = {
'title': item.get('title', '').replace('<b>', '').replace('</b>', ''),
'author': item.get('author', '').replace('<b>', '').replace('</b>', ''),
'publisher': item.get('publisher', ''),
'pubdate': item.get('pubdate', ''),
'description': item.get('description', ''),
'isbn': item.get('isbn', ''),
'price': item.get('price', ''),
'image': item.get('image', ''),
'link': item.get('link', '')
}
books.append(book)
logger.info(f"Found {len(books)} books")
response_json = {
'messageVersion': message_version,
'response': {
'actionGroup': action_group,
'apiPath': api_path,
'httpMethod': http_method,
'httpStatusCode': 200,
'responseBody': {
'application/json': {
'body': json.dumps(books, ensure_ascii=False)
}
}
}
}
logger.info(f"Returning response structure: {json.dumps(response_json, ensure_ascii=False)[:200]}...")
return response_json
except Exception as e:
logger.error(f"Unhandled error: {str(e)}")
# 오류 발생 시에도 응답 형식을 맞춤
return {
'messageVersion': event.get('messageVersion', '1.0'),
'response': {
'actionGroup': event.get('actionGroup', ''),
'apiPath': event.get('apiPath', ''),
'httpMethod': event.get('httpMethod', 'GET'),
'httpStatusCode': 500,
'responseBody': {
'application/json': {
'body': json.dumps({'error': f"오류가 발생했습니다: {str(e)}"}, ensure_ascii=False)
}
}
}
}