Plaid Labs AI 에이전트 Part. 2: 필요한 Context만 사용하는 방법

플래드랩스가 필요한 context를 뽑을 수 있었던 방법
이상민's avatar
Feb 09, 2026
Plaid Labs AI 에이전트 Part. 2: 
필요한 Context만 사용하는 방법

Plaiddy를 더 똑똑하게

챗봇을 만들면서 “필요한 컨텍스트만” 수집하게 된 기술 설계

안녕하세요. 플래드랩스 AX팀 엔지니어 이상민입니다.
이전 파트의 플래디는 정해진 시간에 브리핑을 전달하는 스케줄 기반 자동화 시스템이었습니다.
이번 파트의 본질은 챗봇 기능을 추가하는것, 더 나아가 질문마다 필요한 컨텍스트를 정확히 뽑는 기술을 설계한 과정입니다.

왜 컨텍스트 수집이 핵심이었는가

챗봇을 붙이기 전에는 정해진 시점에 미리 만든 브리핑을 전달하면 됐습니다.
하지만 실시간 질의응답에서는 질문마다 필요한 데이터 범위가 달라집니다.
  • “이번 주 DSY 프로젝트 진행 상황 알려줘”는 프로젝트+이슈 상태가 필요합니다.
  • “어제 내가 한 일 요약해줘”는 사용자 컨텍스트+EOD가 필요합니다.
  • “JIRA-1234 상태만 알려줘”는 특정 이슈 필터 결과만 필요합니다.
즉, 챗봇 문제의 핵심은 생성 모델이 아니라 컨텍스트 라우팅과 수집 전략이었습니다.

왜 필요 컨텍스트만 수집했는가

이론적으로는 모든 컨텍스트를 한 번에 넣어서 구현할 수도 있었습니다.
하지만 실제 운영 관점에서는 다음 한계가 분명했습니다.
  • 입력 토큰이 빠르게 커져 핵심 정보가 잘리거나 요약 왜곡이 발생할 수 있습니다.
  • 질문마다 불필요한 데이터까지 조회해 응답 지연과 비용이 함께 증가합니다.
  • 현재 질문과 무관한 데이터가 섞이면 모델이 초점을 잃고 답변 정확도가 떨어집니다.
  • 오래된 컨텍스트와 최신 데이터가 함께 들어가면 충돌 해석이 발생할 수 있습니다.
  • 사용자/프로젝트 범위를 과하게 주입하면 권한 경계와 정보 노출 리스크가 커집니다.
  • 문제 발생 시 어떤 컨텍스트가 답변에 영향을 줬는지 추적이 어려워집니다.
그래서 우리는 많이 넣는 구조보다 필요한 것만 선별하는 구조를 먼저 설계했고, 이 결정이 이후 챗봇 품질의 기준점이 됐습니다.

우리가 쓴 컨텍스트 기술 스택

  • Slack Bolt + Slack Events API: 실시간 이벤트 수신/ack
  • Redis: assistant thread 문맥 복원
  • MongoDB: 컨텍스트 소스(유저/프로젝트/이슈/브리핑/EOD)
  • LangSmith Prompt Router: 질의 의도와 슬롯 추출
  • Langfuse tracing(invoke_with_langfuse): 체인 실행 추적 통일
  • APScheduler + Celery: 배치성 컨텍스트(유저/프로젝트 context) 사전 업서트

핵심 설계: LLM 단독이 아니라 Hybrid Router

우리는 LLM에게 다 맡기는 방식을 쓰지 않았습니다.
대신 LLM + 결정적 규칙(Deterministic Rules) 하이브리드로 설계했습니다.

1) 이벤트에서 기본 요청(ContextRequest) 생성

# app/chat/context/request_builder.py # Slack event에서 user/channel/thread 식별자를 추출하고 # 사용자 문서와 매핑해 base request를 만든다. return ContextRequest( user_slack_id=user_slack_id, user_id=user_id, atlassian_id=atlassian_id, query=query, channel_id=channel_id, thread_ts=thread_ts, limit=limit, )
여기까지는 완전 결정적 로직입니다. LLM 없이 안정적으로 기본 슬롯을 채웁니다.

2) Router LLM으로 의도/슬롯 보강

# app/chat/context/context_resolver.py llm_payload = await _call_context_router_llm(conversation, text) llm_request = _request_from_llm_payload(llm_payload) merged_request = merge_context_requests(base_request, llm_request)
LLM은 다음만 담당합니다. - target(project/user) - action_intent(summary/briefing/lookup 등) - issue/project/date/assignee 필터 슬롯
그 후 merge 단계에서 기존 식별자와 결합합니다.

3) selector로 “필요한 타입만” 고정

# app/chat/context/selector.py if target == ContextTarget.PROJECT: if _has_issue_filters(request): return [ContextType.JIRA_ISSUE_FILTERED] return [ ContextType.PROJECT_CONTEXT, ContextType.JIRA_ISSUE_ACTIVE, ContextType.JIRA_ISSUE_DONE_TODAY, ]
이 단계가 핵심입니다.
질문마다 모든 데이터를 가져오지 않고, 필요한 컨텍스트 타입 집합을 먼저 결정합니다.

4) registry/fetcher 패턴으로 타입별 수집 분리

# app/chat/context/collector.py for context_type in select_context_types(...): fetcher = self.registry.get(context_type) item = await fetcher(request) if item and item.text: items.append(item)
컨텍스트 추가가 필요하면 fetcher만 추가하면 됩니다.
기존 파이프라인을 깨지 않고 확장 가능합니다.

컨텍스트를 실제로 어떻게 뽑았는가

프로젝트/유저 컨텍스트

  • Mongo의 jira_projects.context, users.context에서 조회
  • 사전 배치 업서트로 최신성 확보

Jira 이슈 컨텍스트

  • active / done_today / filtered를 분리
  • 상태 문자열 동의어를 normalize (done, 완료, to do 등)
  • 날짜 필터를 YYYY-MM-DD, ISO datetime 모두 수용
# app/chat/context/fetchers/jira_context.py statuses = _normalize_issue_statuses(request.issue_statuses) date_filter = _build_date_filter(...) issues_cursor = db.jira_issues.find(query).sort(sort_field, -1).limit(limit)

브리핑/EOD 컨텍스트

  • 사용자의 최신 briefing/EOD를 별도 fetcher에서 조회
  • EOD는 AI 입력 전 정규화(cleaning)

ChatFlow에서 수집 결과를 어떻게 사용했는가

# app/chat/orchestrators/chat_flow.py context_result = await run_context_request_flow(...) if context_result.resolution.action.confidence < confidence_threshold: await send_followup_question(event, say=say) return info = format_context_info(context_result.resolution.bundle) response_text = generate_chat_response(...)
여기서 중요한 정책은 하나입니다.
확신이 낮으면 답하지 않고 되묻는다.
이 정책 덕분에 “그럴듯하지만 틀린 답”을 줄이고, 필요한 슬롯을 재수집할 수 있었습니다.

구현 과정에서 얻은 실전 노하우

  • 수신(ack)과 처리(chat)를 분리하면 Slack 타임아웃 리스크가 줄어듭니다.
  • LLM 출력은 바로 쓰지 말고 merge/normalize 단계를 반드시 둬야 합니다.
  • 컨텍스트 수집은 “많이”보다 “정확히”가 중요합니다.
  • low-confidence는 에러가 아니라 정상 플로우로 설계해야 합니다.
  • tracing 경로(invoke_with_langfuse)를 통일해야 디버깅이 빨라집니다.
  • push(정기 브리핑)와 pull(실시간 질의응답)을 병행하면 전환 비용이 작습니다.

다음은 Slack 명령어 기반

다음 단계는 채팅 자유도 확대보다 Slack 명령어 기반 인터페이스 강화로 잡았습니다.
이유는 다음과 같습니다
  • 팀의 AI CLI 도구와 책임 분리
  • 기능 증가 시 고려 액션, 타겟, 필터 증가 및 경계의 모호
  • 입력 스키마 고정으로 운영 편차 축소
이러한 과정을 통해 플래드랩스는 더더욱 불필요한 과정은 줄이고 더 중요한 일에 집중할 수 있는 환경을 만드는 데에 초점을 맞추고 있습니다.

마무리

이번 개선의 본질은 챗봇 기능을 붙인 것이 아니라, 질문마다 필요한 컨텍스트를 선별·수집·검증하는 실행 규칙을 만든 데에 있었습니다.
단순히 챗봇을 구현하고 모든 컨텍스트를 입력해 답변을 받는 방식이 아닌 컨텍스트 경계와 입력 계약을 명확히 해 적재적소에 컨텍스트를 입력해 자체 모델의 성능을 최대한 끌어올리는 것으로 실제 운영 품질을 더더욱 향상시킬 수 있습니다.
 
Share article

플래드