문의 주신 내용에 맞는 전문 컨설턴트 배정 후 연락드리겠습니다.
테크니컬 라이팅을 위한 AI 어시스턴트 구축하기 (Feat. ReAct, Agents for Amazon Bedrock)
LLM이 발달해오면서 어떻게 LLM을 효율적으로 쓸 수 있을지에 대한 논의가 활발하게 이루어졌고, Prompt Engineering, RAG, Fine-Tuning 등 다양한 기법이 도입돼 왔습니다.
여러 기법 중에서도 가장 접근이 쉬우며, 구현을 위한 별다른 비용이 들지 않는 Prompt Engineering을 통해 많은 개발자와 연구자들은 복잡한 작업에 대한 LLM의 추론 능력을 향상시켜 왔습니다.
이번 포스팅에서는 Prompt Engineering 기법 중 하나인 ReAct 프레임워크에 대한 설명과, 해당 기법을 차용하여 AWS에서 관리형으로 쉽게 외부 API를 사용할 수 있도록 제공하고 있는 Agents for Amazon Bedrock에 대해 소개하겠습니다. 해당 기능을 통해 구현한 테크니컬 라이팅을 보조해 주는 어시스턴트 구축 과정 또한 함께 공유해 보겠습니다.
ReAct
ReAct(Reason + Act)를 소개하기 앞서, ReAct는 또 다른 추론 기법 중 하나인 CoT(Chain of Thought)에 기반을 두며 함께 사용하는 것을 권장하므로 CoT를 짧게 설명드리겠습니다.
CoT는 문제+답 형식의 프롬프트가 아니라,문제+풀이+답 형식의 프롬프트를 LLM에 전달함으로써, 큰 작업을 여러 개의 하위 작업으로 나누어 산술이나 상식 등의 질문에 대한 추론 능력을 극대화한 Prompt Engineering 기법입니다. 하지만 이러한 CoT 기법도 잘못된 답변을 생성하는 환각 현상이 여전히 심하거나 중간 추론 단계에서 오류를 전파하는 문제가 있었고, 이를 보완하기 위해서 ReAct라는 기법이 Google DeepMind 팀 주도로 개발되었습니다.
ReAct를 통해 모델이 외부 환경에서 얻은 지식을 내부 지식 기반과 함께 사용하여 사고할 수 있게 됩니다. 이해를 돕기 위한 간단한 예시로 논문에서 인용한 위 사례를 통해 설명드리겠습니다. 이미지에서 주어진 문제는 아래와 같으며, 모델 내부 지식 기반만으로는 답변하기가 어려워 보입니다.
논문에서는 위와 같은 문제 해결을 위해 ReAct 기법에 따라 구성된 질문-사고-행동-관찰 단계로 이루어진 프롬프트의 예시를 보여줍니다. 각 단계는 아래와 같은 일련의 작업들을 수행합니다.
질문 답변(HotPotQA)과 사실 확인(Fever) 데이터셋을 통해 각 프롬프트 기법을 평가했을 때, ReAct와 CoT를 함께 사용한 경우에 가장 높은 성능이 나왔음을 확인할 수 있습니다. 이렇게 효과적인 성능의 ReAct를 실제로 사용하기 위해서는 어떻게 해야 할까요?
Agents for Amazon Bedrock
대표적인 방법으로는 LangChain의 Agent를 사용하는 방법이 있겠습니다. 하지만 LangChain의 Agent는 프롬프트 및 파서 등이 OpenAI에 최적화되어 있는 경향이 있고, 기능 연동을 위한 개발 공수가 발생한다는 단점이 있습니다.
Agent는 여러 의도가 포함된 사용자 요청을 이해하여, 몇 가지 단계의 작업으로의 분할하고 작업 간 오케스트레이션을 자동화합니다. 이후 각 작업에서는 AWS Lambda를 통해 회사 및 외부의 API를 호출하거나, RAG를 통해 지식 기반에 접근하여 사용자에게 최종적인 자연어 응답을 반환하게 됩니다. ReAct의 4 단계는 아래와 같이 Agents for Amazon Bedrock에서 각각 매칭되며, 모든 ReAct 단계가 추상화되어 별다른 개발 공수가 필요 없기에 사용하기 간편합니다.
Agents for Amazon Bedrock — 사용 사례 고찰
LLM의 추론을 위해서 실시간 정보를 요하는 사용 사례에서 모두 사용할 수가 있습니다. LLM은 특정 시점까지의 정보를 학습하여, 학습된 정보를 기반으로 답변을 생성할 수 있으나 실시간 정보는 가지고 있지는 않습니다. 이를테면 아래와 같은 정보를 활용하는 경우가 적절한 사례가 될 수 있겠습니다.
특정 URL에 작성된 글이 테크니컬 라이팅 원칙에 의해 작성되었는지 리뷰를 진행하는 예시로 구성해 보았습니다.
Amazon Bedrock 콘솔로 접근하여 [Orchestration] — [Agents] 탭을 선택 후, Agent 생성을 진행합니다. 오케스트레이션 후 충분한 정보가 주어지지 않았을 경우 사용자가 정보를 보충하도록 [User input]을 Yes로 설정합니다. 이외의 설정은 모두 디폴트 값으로 설정해도 무방합니다. 2024년 1월 현재 Agent 기능은 버지니아 및 오리건 리전에서만 지원하고 있습니다.
작업을 분할, 할당하는 오케스트레이션 과정에서 약간의 지연시간이 발생합니다. 따라서 Claude 2 또는 2.1 모델을 사용할 수도 있으나, 여기서는 빠른 답변 생성을 위해 더 작고 저렴한 모델인 Claude Instant 모델을 사용하여 구현합니다. 지연시간은 입력 쿼리 및 출력 토큰 수, k 값 등 Agent 호출 시에 포함되는 다양한 parameter 들에 의해 달라질 수 있습니다.
당신은 IT에 능숙한 테크니컬 라이터입니다.
URL {input_url}을 열고 블로그 포스팅에 대해 평가해주세요. 피드백을 한국어로 작성해주세요.
다음 요소들을 테크니컬 라이팅의 요소로 참고해주세요.
1. 명확성
- 기술 용어의 정확한 사용: 전문 용어와 약어 사용 시 정의 및 설명 포함.
- 문장 구조: 간결하고 명료한 문장 사용.
- 흐름: 논리적이고 일관된 아이디어 흐름.
2. 정확성
- 기술적 정확성: 제공된 정보의 정확성 확인.
- 데이터 및 출처: 사용된 데이터와 출처의 신뢰성.
3. 목적 적합성
- 대상 독자: 목표 독자층에 맞는 내용과 스타일.
- 목적 명확성: 문서의 목적이 분명하고 독자에게 명확히 전달되어야 함.
4. 조직성
- 논리적 구조: 정보의 효과적인 구성 및 배열.
- 제목 및 소제목 사용: 정보를 구분하고 가독성을 높이기 위한 제목 사용.
5. 스타일
- 공식성: 비즈니스 또는 학술적 맥락에 적합한 어조.
- 일관성: 용어, 약어 및 서식의 일관된 사용.
6. 가독성
- 문단 분할: 정보의 청크화로 가독성 향상.
- 시각적 요소: 차트, 그래프, 표 등을 사용하여 정보 시각화.
7. 편집 및 검토
- 문법 및 맞춤법 오류: 오탈자 및 문법적 오류 최소화.
[Instructions for the Agent]에는 위와 같이 해당 Agent가 어떠한 작업을 수행할지에 대한 구체적인 지시를 작성합니다. URL의 글이 다음과 같은 테크니컬 라이팅 원칙을 지켰는지를 검증하는 지시를 작성하였습니다.
다음은 오케스트레이션 과정에서 작업을 수행할 AWS Lambda를 설정하는 절차입니다. 각 Action group은 Lambda 함수 1개와 연결되어 있고, 작업의 복잡도에 따라 여러 개의 Action group을 추가할 수 있습니다. 여기서 연결된 AWS Lambda는 BeautifulSoup 라이브러리를 통해 URL의 콘텐츠를 가져오는 코드를 아래와 같이 작성하였습니다.
import json
import requests
from urllib.request import Request, urlopen
import chardet
from bs4 import BeautifulSoup
def get_article_body(url: str) -> str:
req = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})
html_doc = req.text
soup = BeautifulSoup(html_doc, 'html.parser')
contents = soup.find('body')
texts = [c.get_text() for c in contents.find_all('p')]
texts = "nn".join(texts)
return texts[:5000]
def lambda_handler(event, context):
api_path = event['apiPath']
url = ''
if api_path == '/evaluate':
properties = event['requestBody']['content']['application/json']['properties']
for item in properties:
if item['name'] == 'url':
url = item['value']
article = {"article": get_article_body(url)}
response_body = {"application/json": {"body": json.dumps(article, ensure_ascii=False)}}
action_response = {
"actionGroup": event["actionGroup"],
"apiPath": event["apiPath"],
"httpMethod": event["httpMethod"],
"httpStatusCode": 200,
"responseBody": response_body,
}
api_response = {"messageVersion": "1.0", "response": action_response}
return api_response
API 스키마는 Lambda 함수를 오케스트레이션하고, 함수에 대한 요청 및 응답을 위한 정보가 포함되어 있으며, 아래와 같이 작성하였습니다. 오케스트레이션을 위해 description나 required field가 특히 중요하게 사용되기 때문에 원하는 작업에 적합하게 작성할 필요가 있습니다. 각 필드의 설명은 해당 링크에서 자세히 확인할 수 있습니다.
openapi: 3.0.0
info:
title: Evaluate an article API
version: 1.0.0
description: APIs for evaluating an article from URL.
paths:
/evaluate:
post:
summary: APIs for evaluation article from URL.
description: The URL should be determined based on the instructions. If the URL is unclear, please let the user decide.
operationId: evaulate
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
url:
type: string
description: url of article
required:
- url
responses:
'200':
description: Article evaluated.
content:
application/json:
schema:
type: object
properties:
article:
type: string
description: Evaluated article
Agent 생성 시에 Knowledge Base를 연동할 수도 있는데, 현재 사용 사례에서는 RAG를 사용하지 않기 때문에 Knowledge Base는 연결하지 않고 Agent를 생성 완료합니다.
마지막으로 생성한 Agent의 Action Group에서 AWS Lambda 함수를 호출할 수 있도록 위와 같이 Agent의 ARN을 명시하여 [리소스 기반 정책]을 생성합니다.
생성한 Agent를 통해 '서버리스에서의 멱등성 구현' 이라는 예시 블로그 포스팅에 대해서 테크니컬 라이팅 원칙을 준수하는지 리뷰를 진행하였습니다. URL만 전달했을 뿐인데 지시를 기반으로 잘 작동하는 것을 확인할 수 있습니다.
Conclusion
LLM이 발전하면서 다양한 기법들이 하루가 멀다 하고 우후죽순 생겨나고 있습니다. 다양한 기법을 직접 LLM과 통합하며 테스트, 비교하기에는 리소스의 낭비가 여러모로 큽니다. 이러한 상황에서 AWS, GCP, Azure와 같은 CSP가 나아가야 할 방향은 다양한 기법을 사용하기 간편하게 래핑하여 Agents for Amazon Bedrock과 같이 관리형으로 제공하는 것이라고 생각합니다.