반응형
aws에서 제공한 lex, lambda, bedrock을 이용한 RAG chatbot 만들기 중 Lambda 코드에 대해서 포스팅을 하려고 합니다. 시작에 앞서서 각각의 서비스들을 설명해 보자면,
1. AWS Lex
- 챗봇 서비스: 사용자의 입력(텍스트/음성)을 이해하고 대화를 관리합니다.
- 구성 요소:
- Intent: 사용자의 의도를 정의. 예) 병원 찾기.
- Slots: 추가로 입력받아야 할 정보. 예) 병원 이름, 위치.
2. AWS Lambda
- 서버리스 함수: Lex가 요청한 작업(데이터 처리, 외부 API 호출 등)을 처리합니다.
- 특징:
- Lex에서 받은 데이터를 처리하고, Bedrock이나 DB와 연동.
3. AWS Bedrock
- 생성형 AI 서비스: AI 모델(GPT, Claude 등)을 사용해 자연어 답변 생성.
- Knowledge Base:
- 데이터 검색 후 답변 생성.
- 예) 병원 위치 데이터 검색.
그럼 셋의 연동은 어떤 식으로 하면 될까요?
Lex, Lambda, Bedrock의 연동
- Lex: 사용자의 요청(Intent, Slots)을 분석.
- Lambda: Lex에서 받은 데이터를 처리하고, Bedrock 호출.
- Bedrock: 데이터 검색 또는 AI 답변 생성.
- 결과: Lambda → Lex → 사용자.
라고 볼 수 있을 것 같습니다. 비유를 하자면, Lex가 frontend, bedrock이 데이터베이스, 그리고 lambda가 그 사이를 이어주는 backend 라고 볼 수 있을 것 같습니다.
그럼 code를 하나하나 보도록 하겠습니다.
1. 주요 라이브러리와 클라이언트
import json
import boto3
from decimal import Decimal
- json: JSON 데이터 처리에 사용됩니다.
- boto3: AWS 서비스와 상호작용하기 위한 라이브러리입니다. 여기서는 Bedrock 관련 클라이언트 bedrock-runtime과 bedrock-agent-runtime을 사용합니다.
- Decimal: AWS DynamoDB처럼 소수점 데이터를 처리할 때 주로 사용되며, 이를 JSON 직렬화하기 위한 유틸리티 함수도 있습니다.
2. Slot 관련 함수
elicit_slot
- 특정 슬롯에 대한 값을 요청할 때 사용합니다.
confirm_intent
- 사용자의 입력을 바탕으로 의도를 확인합니다.
close
- 대화가 끝났을 때 호출하며 Fulfillment 상태를 반환합니다.
3. Bedrock 관련 함수
get_bedrock_knowledge_base_response
- Bedrock의 Knowledge Base에서 데이터를 검색합니다.
- 주어진 쿼리를 Knowledge Base에 전달하고, 최대 5개의 결과를 반환합니다.
- 결과는 텍스트 기반의 간단한 구조로 변환됩니다.
get_bedrock_response
- 사용자의 질문과 검색된 결과를 기반으로 Bedrock에서 답변을 생성합니다.
- Claude 모델을 호출하여 결과를 생성하며, 결과는 JSON으로 반환됩니다.
- Prompt 구성: 대화 문맥과 검색된 정보를 포함한 세부적인 프롬프트가 작성됩니다.
4. 주요 비즈니스 로직
Bedrock_intent
- AWS Lex가 호출한 의도(Intent)에 대한 처리를 수행합니다.
- 주요 흐름:
- 슬롯 값 확인: 특정 슬롯(HospitalInquiry)이 비어 있으면 값을 요청합니다.
- 사용자 입력 확인: 사용자가 의도를 확정(Confirmed)하거나 거부(Denied)했는지 확인합니다.
- Knowledge Base 검색: 사용자의 질문을 기반으로 데이터를 검색하고 답변을 생성합니다.
- 응답 반환: 생성된 답변을 사용자에게 전달하고, 필요한 경우 대화를 종료합니다.
lambda_handler
- AWS Lambda가 실행될 때 진입점으로 호출되는 함수입니다.
- Lex로부터 전달된 이벤트를 처리하며, Intent에 따라 Bedrock_intent를 호출합니다.
5. 실행 흐름
- 사용자가 Lex를 통해 메시지를 전송합니다.
- Lex는 이를 Lambda에 전달하고, Lambda는 이벤트를 기반으로 적절한 Intent를 호출합니다.
- Intent에서:
- 사용자의 입력을 처리하고,
- 필요한 데이터를 검색(Bedrock)하거나,
- 대화 상태를 업데이트하고,
- 최종적으로 응답을 반환합니다.
실제 코드:
import json
import boto3
from decimal import Decimal
bedrock_runtime_client = boto3.client('bedrock-runtime')
bedrock_agent_runtime_client = boto3.client('bedrock-agent-runtime')
def decimal_to_int(obj):
if isinstance(obj, Decimal):
return int(obj)
def elicit_slot(slot_to_elicit, intent_name, slots, session_attributes):
return {
'sessionState': {
'dialogAction': {
'type': 'ElicitSlot',
'slotToElicit': slot_to_elicit,
},
'intent': {
'name': intent_name,
'slots': slots,
'state': 'InProgress'
},
'sessionAttributes': session_attributes
}
}
def confirm_intent(message_content, intent_name, slots, session_attributes):
return {
'messages': [{'contentType': 'PlainText', 'content': message_content}],
'sessionState': {
'dialogAction': {
'type': 'ConfirmIntent',
},
'intent': {
'name': intent_name,
'slots': slots,
'state': 'Fulfilled'
},
'sessionAttributes': session_attributes
}
}
def close(fulfillment_state, message_content, intent_name, slots, session_attributes):
return {
'messages': [{'contentType': 'PlainText', 'content': message_content}],
"sessionState": {
'dialogAction': {
'type': 'Close',
},
'intent': {
'name': intent_name,
'slots': slots,
'state': fulfillment_state
},
'sessionAttributes': session_attributes
}
}
def get_bedrock_knowledge_base_response(query_text, knowledgeBaseId):
print("GET BEDROCK KNOWLEDGE BASE RESPONSE")
try:
response = bedrock_agent_runtime_client.retrieve(
knowledgeBaseId=knowledgeBaseId,
retrievalQuery={
'text': query_text
},
retrievalConfiguration={
'vectorSearchConfiguration': {
'numberOfResults': 5
}
}
)
results = response['retrievalResults'][:5] if response['retrievalResults'] else []
retrieved_results = []
for item in results:
content = item.get('content', {}).get('text', '')
retrieved_results.append({
'Content': content
})
return retrieved_results
except Exception as e:
print(f"Error retrieving from Knowledge Base: {str(e)}")
return []
def get_bedrock_response(user_prompt, retrieved_results, conversation_context):
prompt = f"""\n\nHuman: 아래 [참고] 정보를 바탕으로 [질문]에 적절히 답해주세요.
200자 이내로 대답해주세요. 대답할 때 200자 이내라고 하지 않으셔도 됩니다. 완전한 문장으로 답변해주세요.
이전 대화 내용:
{json.dumps(conversation_context, ensure_ascii=False)}
[질문]
{user_prompt}
[참고]
{json.dumps(retrieved_results, ensure_ascii=False)}
Assistant: """
modelId = 'anthropic.claude-3-sonnet-20240229-v1:0'
accept = 'application/json'
contentType = 'application/json'
body = json.dumps({
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 300,
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.0,
})
response = bedrock_runtime_client.invoke_model(
modelId=modelId,
accept=accept,
contentType=contentType,
body=body
)
response_body = json.loads(response.get('body').read())
return response_body.get('content')[0]['text'].strip()
def Bedrock_intent(event):
intent_name = event['sessionState']['intent']['name']
slots = event['sessionState']['intent']['slots']
session_attributes = event['sessionState'].get('sessionAttributes', {})
user_prompt = event['inputTranscript']
if slots['HospitalInquiry'] is None:
return elicit_slot('HospitalInquiry', intent_name, slots, session_attributes)
confirmation_status = event['sessionState']['intent']['confirmationState']
if confirmation_status == "Confirmed":
return close("Fulfilled", '그럼 통화를 종료하겠습니다.', intent_name, slots, session_attributes)
elif confirmation_status == "Denied":
return close("Failed", '올바른 정보를 못 찾아드려서 죄송합니다.', intent_name, slots, session_attributes)
knowledgeBaseId = '생성한 Bedrock의 Knowledge Bases 입력'
conversation_context = {
"intent_name": intent_name,
"slots": slots,
"session_attributes": session_attributes
}
retrieved_results = get_bedrock_knowledge_base_response(user_prompt, knowledgeBaseId)
response_text = get_bedrock_response(user_prompt, retrieved_results, conversation_context)
# Update session attributes with the latest response
session_attributes['last_response'] = response_text
return confirm_intent(
f'{response_text}',
intent_name, slots, session_attributes)
def lambda_handler(event, context):
print("Received event:" + json.dumps(event, default=decimal_to_int, ensure_ascii=False))
intent_name = event['sessionState']['intent']['name']
if intent_name == 'selfAction':
return Bedrock_intent(event)
반응형
'Cloud' 카테고리의 다른 글
gcp cloudsql 과 mysqlbench 연결하기 (1) | 2025.01.02 |
---|