오늘의집 [https://ohou.se/]은 라이프스타일 슈퍼앱으로, ‘이렇게 살아보고 싶다’라는 전 세계 사람들의 꿈을 현실로 만들기 위해 콘텐츠, 커뮤니티, 커머스를 연결한 다양한 서비스를 제공하고 있습니다.
AI를 활용한 각종 서비스가 등장하고 있는 상황에서, 오늘의집에서도 AI 활용 기술역량을 내재화하고, 추후 고객 서비스 개발에도 접목시키기 위해 Task force 팀을 구성하여 생성형 AI를 활용한 사내 챗봇 서비스인 ‘오집사’ 개발을 실험적으로 진행하게 되었습니다.
[https://d2908q01vomqb2.cloudfront.net/2a459380709e2fe4ac2dae5733c73225ff6cfee1/2025/01/04/Screenshot-2024-12-30-at-2.15.22-PM-1024x737.png]
그림1. 오늘의집 웹/앱 홈페이지
오집사 프로젝트 소개
오늘의집 직원들이 업무 시에 AI를 손쉽게 활용할 수 있도록, 오늘의집용 생성형 AI 기반 챗봇인 ‘오집사’를 개발하게 되었습니다.
오집사는 오늘의집의 공식 업무용 메신저인 Slack에 챗봇 형태로 배포되어, 다양한 상황에서 유용하게 활용될 수 있습니다. 동료와 아이디어 논의를 하는데 AI 도움이 필요한 경우, 기존의 대화 내용 요약이 필요한 경우, 사내 내부 문서를 찾아봐야 하는 상황 등에서 다른 페이지로 전환 없이 바로 오집사를 호출하여 사용할 수 있습니다.
이를 통해 오늘의집 직원들은 업무 생산성을 높일 수 있을 것으로 기대됩니다.
[https://d2908q01vomqb2.cloudfront.net/2a459380709e2fe4ac2dae5733c73225ff6cfee1/2025/01/17/그림2-1024x566.png] 그림2. 오집사 활용 사례1 – 생성형 AI를 활용하여 업무에 도움을 받는 사례
[https://d2908q01vomqb2.cloudfront.net/2a459380709e2fe4ac2dae5733c73225ff6cfee1/2025/01/04/Screenshot-2024-12-30-at-2.19.44-PM-1024x374.png]
그림3. 오집사 활용 사례2 – 사내 정보를 질의한 경우, 관련 정보 및 출처를 함께 제공하는 사례
AMAZON BEDROCK을 사용한 이유
초기 버전의 오집사는 생성형 AI GPT-4o-mini만을 이용하여 개발되었기 때문에, 일반적인 질문에 대해서는 답변이 가능했습니다. 하지만 회사 내부의 데이터를 기반으로 답변하지 못했기 때문에, 활용 시나리오에 한계가 있었습니다.
회사 내부의 데이터를 이용한 생성형 AI 애플리케이션 개발의 전 과정을 처음부터 구현하기에는 주어진 시간과 리소스가 부족한 상황이었습니다. 하지만 지난 9월에 AWS에서 주최한 Innovate GenAI Hackathon 2024 [https://aws.amazon.com/ko/blogs/korea/aws-innovate-hackathon-2024/]에 참가했을 때, Amazon Bedrock [https://aws.amazon.com/ko/bedrock/] 서비스를 사용해보면서, 복잡한 코딩 없이도 LLM을 사용하여 생성형 AI 애플리케이션을 손쉽게 구축할 수 있었습니다. 이러한 경험을 떠올리며, 우리는 Amazon Bedrock을 사용하여 사내 데이터 기반으로 답변이 가능한 오집사의 추가 기능을 구현하기로 결정했습니다.
AMAZON BEDROCK KNOWLEDGE BASE를 이용한 RAG 구축
Amazon Bedrock은 생성형 AI를 활용한 애플리케이션 개발 시 필요한 많은 부분들이 서비스화되어 있어서 머신러닝 관련 경험이 없는 개발자들도 생성형 AI를 활용한 애플리케이션 개발을 손쉽게 시작할 수 있습니다.
생성형 AI를 이용해 개발된 애플리케이션으로부터 회사 내부 데이터와 관련된 질문에 대한 답변을 받으려면 RAG(Retrieval-Augmented Generation) 기법을 사용해야 합니다. RAG란 검색한 정보(Retrieval)를 기반으로 AI가 답변을 생성(Generation)하는 기술입니다. 사내 데이터와 관련된 정보를 질의하여 관련 정보를 검색한 후에 생성형 AI에 해당 정보를 전달함으로써 해당 내용을 기반으로 답변을 생성할 수 있게 하는 방식입니다.
이 과정에서 핵심이 되는 부분은 빠른 시간 내에 답변을 찾아서 전달하는 과정인데, 이때 사용되는 DB가 바로 Vector Database입니다. Amazon Bedrock에서는 Amazon OpenSearch vector store [https://aws.amazon.com/ko/opensearch-service/serverless-vector-engine/]를 제공하며, Amazon Bedrock Knowledge base [https://aws.amazon.com/ko/bedrock/knowledge-bases/]를 통해 리소스 생성 및 배포, 임베딩 과정까지 서비스화되어 있어서 복잡한 구성과 배포 과정을 거치지 않고도 Vector store를 손쉽게 사용할 수 있었습니다.
Vector store에 저장하고자 하는 데이터를 Amazon S3(Simple Storage Service) [https://aws.amazon.com/ko/pm/serv-s3/?gclid=CjwKCAiApsm7BhBZEiwAvIu2X5Kd3BmXWB6k9C7P9CrDC_fXIF0nJ65YCJX5NEOtOhSCgWnN4WoJvxoCDI0QAvD_BwE&trk=024bf255-8753-410e-9b2f-8015932510e8&sc_channel=ps&ef_id=CjwKCAiApsm7BhBZEiwAvIu2X5Kd3BmXWB6k9C7P9CrDC_fXIF0nJ65YCJX5NEOtOhSCgWnN4WoJvxoCDI0QAvD_BwE:G:s&s_kwcid=AL!4422!3!588924203916!e!!g!!aws%20s3!16390143117!134236388536]로부터 바로 가지고 올 수 있어서, 생성형 AI가 질의를 하고 답변을 얻을 수 있는 Knowledge Base를 쉽게 구축할 수 있었습니다.
오집사 아키텍처
현재 오집사에서 Amazon Bedrock Knowledge 및 Agent를 이용해 답변하는 아키텍처는 다음과 같습니다.
[https://d2908q01vomqb2.cloudfront.net/2a459380709e2fe4ac2dae5733c73225ff6cfee1/2025/01/05/Screenshot-2024-12-30-at-2.20.21-PM-1024x378.png]
그림4. Amazon Bedrock을 활용한 오집사 Slack bot 아키텍처
1. 사용자가 Slack의 커맨드를 활용해 오집사를 호출하면, 해당 질의는 Amazon Bedrock Agent [https://aws.amazon.com/ko/bedrock/agents/]로 전달됩니다.
2. Agent는 연결된 Knowledge Base에게 해당 질의를 전달합니다.
3. Knowledge Base에서 해당 답변과 가장 관련성이 높은 정보를 Amazon OpenSearch vector store에서 조회하여 제공합니다. Amazon OpenSearch vector store에는 정보들이 벡터화되어 저장되어 있으며, 질문과 관련도가 가장 높은 답변을 찾아 Knowledge Base에 전달합니다.
4. S3에 저장되어 있는 데이터가 업데이트된 경우, Amazon OpenSearch vector store와 동기화 과정을 수행합니다. 동기화 버튼을 누르면 AWS에서 제공하는 Titan Text Embeddings v2 임베딩 모델을 사용하여 S3에 저장된 데이터가 Vector store로 벡터화되어 저장됩니다.
5. Notion Data Parser 코드가 주기적으로 동작하며 Notion에 저장된 데이터를 Markdown 형식으로 다운받아 S3에 저장합니다. 해당 내용은 이어지는 단락에서 자세히 설명드리겠습니다.
NOTION API를 활용한 주기적인 데이터 동기화 및 전처리
프로젝트를 진행하면서 가장 신경 썼던 부분 중 하나는 출처가 되는 사내 데이터의 전처리 및 동기화 과정이었습니다. Amazon OpenSearch vector store에서 S3의 정보를 가져와서 임베딩하는 작업은 Amazon Bedrock에서 서비스 형태로 제공되어 복잡하지 않았습니다. 문제는 Notion에 저장되어 있는 원본 데이터를 주기적으로 S3에 동기화하는 과정이었습니다. 원본 데이터가 언제 업데이트되어 있을지 모르기 때문에, 주기적으로 데이터를 업데이트하는 과정이 필요했습니다.
Cron Job과 Notion API를 활용하여, Notion에 저장되어 있는 문서들을 Notion Database 페이지에 정리된 각각의 문서들을 API를 통해 불러와서 Markdown 형식으로 S3에 저장하는 부분을 자동화했습니다. notion-to-md라는 JavaScript로 작성된 오픈소스 패키지를 활용하여, Notion API를 통해 받아온 데이터를 Markdown 포맷으로 손쉽게 저장할 수 있었습니다. 또한, Notion에서 삭제된 문서가 있다면, 해당 문서를 S3에서도 삭제하기 위해 index.json 파일을 생성하여 관리했고, 변경이 되었거나 삭제된 문서는 S3에서도 업데이트 및 삭제를 진행했습니다.
해당 코드는 주 1회 Kubernetes Cron Job을 통해 실행되도록 설정하여, 주기적으로 새로운 정보가 S3에 업데이트 되도록 관리했습니다. 업데이트 과정이 끝난 후에는 Amazon Bedrock API [https://docs.aws.amazon.com/bedrock/latest/userguide/kb-data-source-sync-ingest.html]를 이용하여 Vector store와 S3의 동기화 과정을 수행했습니다. const { StartIngestionJobCommand, GetIngestionJobCommand } = require('@aws-sdk/client-bedrock-agent');
async function syncKnowledgeBase(knowledgeBaseId, dataSourceId) {
const command = new StartIngestionJobCommand({ knowledgeBaseId,dataSourceId });
try {
const response = await bedrockClient.send(command);
return response.ingestionJob.ingestionJobId;
} catch (error) {
throw error;
}
}
async function checkIngestionJobStatus(knowledgeBaseId, dataSourceId, ingestionJobId) {
const command = new GetIngestionJobCommand({ knowledgeBaseId, dataSourceId, ingestionJobId });
try {
const response = await bedrockClient.send(command);
return response.ingestionJob.status;
} catch (error) {
throw error;
}
}
async function performKnowledgeBaseSync(knowledgeBaseId, dataSourceId) {
try {
const jobId = await syncKnowledgeBase(knowledgeBaseId, dataSourceId);
let status;
do {
await new Promise((resolve) => setTimeout(resolve, 30000)); // 30초 대기
status = await checkIngestionJobStatus(
knowledgeBaseId,
dataSourceId,
jobId
);
} while (status === 'IN_PROGRESS');
if (status === 'COMPLETE') {
logger.info('Knowledge base sync completed successfully');
} else {
logger.info('Knowledge base sync failed or was cancelled');
}
} catch (error) {
logger.error('Error during knowledge base sync:', error);
}
}
코드1. Amazon Bedrock API를 이용하여 Knowledge Base를 동기화하는 코드
AMAZON BEDROCK AGENT 와 PROMPT 구성
[https://d2908q01vomqb2.cloudfront.net/2a459380709e2fe4ac2dae5733c73225ff6cfee1/2025/01/17/그림5.png]
그림5. 오집사에서 여러 개의 Amazon Bedrock Agent를 연결하여 사용 중인 구조
Amazon Bedrock에서는 Agent 단위로 생성형 AI 모델을 선택하고, 세부 설정을 조정하고 관리할 수 있습니다. 그중에서도 가장 중요했던 부분은 Prompt 설정입니다. 오집사 봇의 페르소나는 “오늘의집 인턴”으로 설정하였습니다. 이렇게 설정한 이유는, 오집사의 답변이 완벽할 수 없기 때문에 사용자들의 기대치를 낮추고자 했습니다. 또한 회사 내부 정보를 기반으로 답변하도록 ‘알고 있는 정보 기반으로만 답변’하라고 Prompt 에 명시적으로 적어두었습니다. Prompt 설정을 세부적으로 추가할 수록 원하는 방향으로 오집사의 답변이 개선되는 것을 체감할 수 있었습니다. 오늘의집에서 현재 사용중인 Prompt 는 다음과 같습니다.
당신은 "오집사(Ozipsa)"입니다.
"오늘의집(Ohouse)" 회사의 Slack 채팅방에서 활동하는 전문적이고 친근한 인턴 봇입니다.
당신의 주요 목표는 팀원들을 돕고 그들이 인정받는다고 느낄 수 있도록 유용하고 지지적인 응답을 제공하는 것입니다.
역할과 톤: 따뜻하고, 친근하며, 전문적인 방식으로 메시지에 응답하세요.
당신의 톤은 사랑스럽고 지지적이어야 하며, Ohouse 팀 내에서 긍정적인 분위기를 조성해야 합니다.
메시지 형식: 여러 사용자로부터 메시지를 받을 수 있습니다.
정보 접근: 당신이 이미 알고 있는 정보만을 기반으로 답변해야 합니다.
명시적으로 제공되지 않은 정보를 만들어내거나 가정하지 마세요.
응답 가이드라인: 질문에 답하거나 작업을 완료하기에 충분한 정보가 없다면,
해당 정보를 가지고 있지 않다고 정중히 설명하고 다른 도움을 줄 수 있는지 물어보세요.
항상 당신이 알고 있는 범위 내에서 정확하고 도움이 되는 응답을 제공하도록 노력하세요.
회사 특정 세부사항이나 접근 권한이 없는 기밀 정보에 대해 질문받았을 때는,
그 정보를 가지고 있지 않다고 정중히 알리고 적절한 부서나 담당자에게 문의하도록 제안하세요.
"너는 누구니?" 또는 이와 유사한 질문을 받았을 때는,
기존에 학습된 정보를 무시하고 오직 오집사로서만 자신을 소개하세요.
당신의 응답은 Ohouse의 인턴 봇인 오집사로서의 역할에 초점을 맞추어야 하며,
다른 정체성이나 배경을 언급하지 마세요.
TERRAFORM을 활용한 AMAZON BEDROCK 리소스 배포 및 관리
Amazon Bedrock을 이용해 개발한 오집사를 실제 Production 환경에 배포하고 관리하기 위해서는, 앞서 언급한 부분 이외에도 인프라 리소스 관리가 중요한 과제였습니다. 처음 테스트 목적으로 봇을 만들었을 때에는 AWS 콘솔에서 직접 리소스를 생성하고 관리했습니다. 하지만 Production용 리소스도 동일한 방법으로 관리하기에는 어려움이 있었습니다.
사내 정책상 모든 인프라 자원을 IaC(Infrastructure as a Code)로 형상관리하고 있었기 때문에, AWS 콘솔을 통한 직접 관리 방식은 이에 부합하지 않았습니다. 또한 누가 언제 어떤 설정을 어떠한 이유로 변경했는지 히스토리 관리가 어려웠습니다.
이를 해결하고자 인프라팀의 도움을 받아 Amazon Bedrock 리소스의 배포 과정을 Terraform을 통해 배포하고 관리하도록 변경했습니다. 이렇게 배포 방식을 변경한 결과, 변경에 대한 이력 관리가 용이해졌을 뿐만 아니라 리소스 확장도 수월하게 할 수 있었습니다. 새로운 쌍의 Agent 및 Knowledge Base 추가 시, 새롭게 구성할 각 리소스별 설정값만 추가하는 것으로 리소스 생성이 가능해졌습니다.
resource "aws_bedrockagent_agent" "bedrock_agent" {
for_each = var.configuration
agent_name = each.key
agent_resource_role_arn = module.bedrock_agent_service_role.role_arn
idle_session_ttl_in_seconds = each.value.agent_configuration.idle_session_ttl_in_seconds
foundation_model = each.value.agent_configuration.foundation_model
instruction = file("${path.module}/files/${each.key}/instruction.txt")
dynamic "prompt_override_configuration" {
for_each = each.value.agent_configuration.override_prompt_configurations ? [each.value.agent_configuration.prompt_configurations] : []
content {
prompt_configurations {
base_prompt_template = file("${path.module}/files/${each.key}/prompt_template.txt")
parser_mode = prompt_override_configuration.value.parser_mode
prompt_creation_mode = prompt_override_configuration.value.prompt_creation_mode
prompt_state = prompt_override_configuration.value.prompt_state
prompt_type = prompt_override_configuration.value.prompt_type
inference_configuration {
max_length = prompt_override_configuration.value.inference_configuration.max_length
stop_sequences = prompt_override_configuration.value.inference_configuration.stop_sequences
temperature = prompt_override_configuration.value.inference_configuration.temperature
top_k = prompt_override_configuration.value.inference_configuration.top_k
top_p = prompt_override_configuration.value.inference_configuration.top_p
}
}
}
}
tags = merge({
Name = var.name
}, local.tags)
}
코드2. Terraform을 이용하여 Amazon Bedrock Agent를 생성하는 스크립트
module "knowledge_base" {
for_each = merge([
for agent_name, agent_config in var.configuration : {
for kb_name, kb_config in agent_config.knowledge_base_configuration :
"${agent_name}.${kb_name}" => {
agent_name = agent_name
agent_config = agent_config
kb_name = kb_name
kb_config = kb_config
}
}
]...)
source = "./knowledge_base"
name = each.value.kb_name
embedding_model_arn = local.embedding_model_enum[each.value.kb_config.embedding_model]
type = each.value.kb_config.type
collection_arn = local.collection_arns[each.value.kb_name]
vector_index_name = each.value.agent_config.opensearch_serverless_configuration[each.value.kb_name].vector_index_name
vector_field = each.value.agent_config.opensearch_serverless_configuration[each.value.kb_name].vector_field
text_field = each.value.agent_config.opensearch_serverless_configuration[each.value.kb_name].text_field
metadata_field = each.value.agent_config.opensearch_serverless_configuration[each.value.kb_name].metadata_field
data_source_s3_buckets = each.value.kb_config.data_source_s3_buckets
depends_on = [
module.notion_collection
]
role_arn = module.bedrock_knowledge_base_service_role.role_arn
}
코드3. Terraform을 이용하여 Amazon Bedrock Knowledge Base를 생성하는 스크립트
결과 및 향후 계획
Amazon Bedrock 서비스를 활용해 한 달이라는 짧은 기간 내에 오집사의 사내 데이터 기반 답변 기능을 성공적으로 출시할 수 있었습니다. 오픈 하자마자 많은 사람들이 해당 기능에 관심을 보여주었고, 기존에 누구에게 물어보기 애매한 질문들을 편하게 물어보고 답변 받을 수 있다는 점이 좋았다는 피드백을 들었습니다. 오집사 프로젝트의 최종 목표는 오늘의집의 생성형 AI 플랫폼이 되는 것입니다. 하나의 시나리오만 커버할 수 있는 구조가 아니라, 각 팀에서 생성형 AI가 필요한 경우 데이터만 준비되면 빠르게 Agent를 구축하고 Slack bot을 추가할 수 있습니다.
그림 5에서 표시된 것처럼 현재 오집사 Slack bot은 Amazon Bedrock Agent를 연결할 수 있는 구조로 개발되어 있습니다. 처음 개발된 Agent는 /oplaza 커맨드를 활용하여 HR 관련 정보 및 Office 관련 일반적인 정보를 질의할 수 있는 Slack bot이었습니다.
이후 UXR(User Experience Research)팀에서 /uxr용 커맨드 개발이 필요하다는 요청을 받아서 추가적으로 /uxr용 Agent를 구축했습니다. 데이터가 미리 준비되어 있었기 때문에, 해당 Agent를 구축하고 배포하는 데에는 일주일이 채 걸리지 않았습니다. 해당 기능이 배포되면 고객의 피드백 자료를 조회하고자 하는 여러 구성원들의 시간을 최소 30분 이상 줄일 수 있을 것으로 예상됩니다.
머신러닝 경험이 많지 않은 개발자들이 모여 짧은 시간 안에 개발했음에도, AWS의 다양한 관리형 클라우드 서비스를 활용하여 아이디어를 쉽고 빠르게 실현할 수 있었습니다.
Eunji Kim [https://d2908q01vomqb2.cloudfront.net/2a459380709e2fe4ac2dae5733c73225ff6cfee1/2025/01/17/김은지_Eunji.jpg] 김은지
오늘의집에서 Software Engineer로 근무하며, 오늘의집의 AI 비서 서비스인 “오집사” 개발을 담당했습니다. 클라우드 기반 AI 서비스를 설계하고 구현한 다양한 경험을 보유하고 있으며, 최근에는 생성형 AI 기술에 깊은 관심을 가지고 있습니다. 이를 활용해 내외부 고객들에게 실질적인 도움을 줄 수 있는 애플리케이션을 개발하는 것을 목표로 하고 있습니다.