Amazon Bedrock, LangChain과 사용하기
2. Amazon Bedrock API, LangChain 사용해보기
6. 간단한 검색증강(RAG : Retrieval Augmented Generation) 구현
7. 간단한 챗봇 구현 (Conversation Memory)
8. 챗봇 구현 (RAG + Conversation Memory)
스트리밍 API를 호출하는경우 전체 응답을 기다리지 않고 청크단위로 사용자에게 반환할 수 있습니다. 챗봇이나, 즉각적인 반응이 필요한경우 사용할 수 있습니다.
스트리밍 API 사용하기
이전에 사용되었던 코드와 다른점이 있다면 스트리밍 결과에 대한 콜백 핸들러를 지정해야 하고, LangChain에서 Streaming API를 지원하지 않기때문에 Bedrock API를 사용하겠습니다.
핸들러를 사용하게되면 청크단위로 반환되는 리스폰스에 대해 출력할 수 있습니다. 청크 핸들러를 작성합니다.
def chunk_handler(chunk):
print(chunk, end='')
API엔드포인트를 호출하지만, 이번에는 LangChain이 아닌 Bedrock API를 사용하여 호출합니다.
def get_streaming_response(prompt, streaming_callback):
bedrock_model_id = "ai21.j2-mid-v1"
body = json.dumps(
{
"prompt": prompt,
"maxTokens":1014,
"temperature":0.7,
"topP":0.4,
"stopSequences":[
],
"countPenalty":{
"scale":0.3
},
"presencePenalty":{
"scale":1.2
},
"frequencyPenalty":{
"scale":215.7
}
}
)
response = bedrock.invoke_model_with_response_stream(modelId=bedrock_model_id, body=body)
stream = response.get('body')
if stream:
for event in stream:
chunk = event.get('chunk')
if chunk:
chunk_json = json.loads(chunk.get('bytes').decode())
streaming_callback(chunk_json["completion"])
리스폰스를 출력합니다.
prompt = "\n\nHuman:Tell me a story about two puppies and two kittens who became best friends\n\nAssistant:"
get_streaming_response(prompt, chunk_handler)
해당 모델은 스트리밍을 지원하지 않네요 InvokeModelWithResponseStream 에러가 발생합니다. "anthropic.claude-v2"를 사용하면 가능하지만, 허가를 받아야 사용할 수 있기때문에, 이런게 있다 정도로 넘어가도록 하겠습니다. 정상적인 출력은 다음과 같습니다.
전체 코드는 다음과 같습니다.
import json
import boto3
session = boto3.Session()
bedrock = session.client(
service_name='bedrock-runtime',
region_name='us-east-1',
endpoint_url="https://bedrock-runtime.us-east-1.amazonaws.com"
)
def chunk_handler(chunk):
print(chunk, end='')
def get_streaming_response(prompt, streaming_callback):
bedrock_model_id = "ai21.j2-mid-v1" #set the foundation model
body = json.dumps(
{
"prompt": prompt,
"maxTokens":1014,
"temperature":0.7,
"topP":0.4,
"stopSequences":[
],
"countPenalty":{
"scale":0.3
},
"presencePenalty":{
"scale":1.2
},
"frequencyPenalty":{
"scale":215.7
}
}
)
response = bedrock.invoke_model_with_response_stream(modelId=bedrock_model_id, body=body) #invoke the streaming method
stream = response.get('body')
if stream:
for event in stream: #process each event returned by the stream
chunk = event.get('chunk')
if chunk:
chunk_json = json.loads(chunk.get('bytes').decode())
streaming_callback(chunk_json["completion"]) #pass the latest chunk's text to the callback method
prompt = "\n\nHuman:Tell me a story about two puppies and two kittens who became best friends\n\nAssistant:"
get_streaming_response(prompt, chunk_handler)
벡터임베딩
🔗Amazon Titan 임베딩을 사용하여 기본적인 임베딩을 진행해보겠습니다. 임베딩을 사용하면, 텍스트의 상대적 유사성을 찾는데 도움이 됩니다. 더 자세한 내용을 알고싶다면, 다음 블로그를 추천드립니다.
Amazon Bedrock Is Now Generally Available – Build and Scale Generative AI Applications with Foundation Models
Amazon Bedrock Is Now Generally Available – Build and Scale Generative AI Applications with Foundation Models | Amazon Web Ser
Update October 10, 2023 — Amazon Bedrock is now available in 3 regions globally: US East (N. Virginia), US West (Oregon), and Asia Pacific (Tokyo). This April, we announced Amazon Bedrock as part of a set of new tools for building with generative AI on A
aws.amazon.com
벡터단위의 숫자로 텍스트의 의미를 캡쳐하고, 이 벡터를 사용하여 텍스트 조각이 얼마나 유사한지를 확인 할 수 있습니다. 또한 벡터 데이터베이스를 사용하여 임베딩을 저장하고, 유사성 검색을 수행할 수 있습니다. 본 포스트에서는 벡터 임베딩 생성하고, 벡터 데이터베이스는 사용하지 않고 생성된 임베딩 정보를 Python의 내장 함수 (math.dist())를 사용하여 유사성을 확인해보겠습니다.
라이브러리를 import 하겠습니다. 임베딩 비교를 위한 math 또한 임포트합니다.
import json
import boto3
import math
Bedrock 클라이언트를 초기화합니다.
session = boto3.Session()
bedrock = session.client(
service_name='bedrock-runtime',
region_name='us-east-1',
endpoint_url="https://bedrock-runtime.us-east-1.amazonaws.com"
)
Bedrock의 invoke_model 메서드를 사용하여 Titan Embedding을 호출하겠습니다.
def get_embedding(text):
body = json.dumps({"inputText": text})
model_d = 'amazon.titan-embed-text-v1'
mime_type = 'application/json'
response = bedrock.invoke_model(body=body, modelId=model_d, accept=mime_type, contentType=mime_type)
response_body = json.loads(response.get('body').read())
embedding = response_body.get('embedding')
return embedding
임베딩 목록을 작성해보겠습니다.
embeddings = []
embeddings.append(get_embedding("Can you please tell me how to get to the bakery?"))
embeddings.append(get_embedding("I need directions to the bread shop"))
embeddings.append(get_embedding("Cats, dogs, and mice"))
embeddings.append(get_embedding("Felines, canines, and rodents"))
embeddings.append(get_embedding("Four score and seven years ago"))
임베딩들이 얼마나 유사한지에 대해 보여주는 테이블을 표시하겠습니다. 거리가 0이면 정확히 같다는것이고, 거리가 멀수록 임베딩이 다양해집니다.
#print the table of embeddings
i = 1
print("", end="\t")
for e2 in embeddings: #print the column headers
print(i, end="\t")
i = i + 1
print() #new line
i = 1
for e1 in embeddings:
#print the row
print(i, end="\t")
for e2 in embeddings:
dist = math.dist(e1, e2) #find the Euclidean distance between each embedding
dist_round = round(dist, 2)
print(dist_round, end="\t")
print() #new line
i = i + 1
1 2 3 4 5
1 0.0 12.27 25.01 24.7 21.0
2 12.27 0.0 25.95 25.9 21.39
3 25.01 25.95 0.0 9.99 24.92
4 24.7 25.9 9.99 0.0 24.64
5 21.0 21.39 24.92 24.64 0.0
결과를 살펴보겠습니다. 0에 가까울수록 상대적으로 유사함을 나타냅니다. 상대적으로 작은값인 1-2, 3-4 에대해 알아보겠습니다.
- 항목 1과 2("제과점에 가는 방법을 알려주시겠어요?", "빵집 가는 길 안내가 필요해요")는 1과 3보다 더 유사합니다
- 항목 3과 4("고양이, 개, 생쥐"와 "고양이, 개, 설치류")는 4와 5보다 더 유사합니다.