윤정민 d3271fa1e8
All checks were successful
Build and Deploy Teams Planner Bot / build-and-run (push) Successful in 32s
feat: hybrid conversation memory (working memory + last-2 raw turns)
Classifier now receives a ConversationContext: a compact LLM-maintained
WorkingMemory (topic/focusPlan/lastTaskTitle/openLoops/notes), the last 2
raw turns, and a pendingDigest derived each turn from the pending action.
The LLM emits an optional memoryUpdate patch alongside its action in the
same tool call (no extra API hop). Volatile fields decay after 10 min idle,
notes truncate at 500 chars, raw turns ring-buffer at 2, openLoops cap at 5.
Logout wipes everything.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 15:30:38 +09:00

Teams Planner Bot

MS Teams 봇. 사용자의 자연어 작업 보고를 LLM으로 분류해서 Microsoft Planner에 자동으로 task를 생성하거나 업데이트한다.

동작 개요

사용자 (Teams) → 봇 → OAuthPrompt 로그인 (최초 1회)
              → Graph로 본인 Plans/Buckets/최근 Tasks 조회
              → LLM(Claude 또는 Azure OpenAI)에게 발화 + 컨텍스트 전달
              → tool/function call로 {action, planId, ...} JSON 강제 추출
              → Planner Graph API로 create / patch
              → Adaptive Card로 결과 응답

구성

src/
  index.ts                       # restify 서버, CloudAdapter
  config/index.ts                # 환경변수 로딩/검증
  bot/PlannerBot.ts              # 메시지 핸들러 + OAuth 다이얼로그 + 오케스트레이션
  graph/
    graphClientFactory.ts        # 사용자 토큰을 받아 Graph Client 생성
    plannerClient.ts             # Plans/Buckets/Tasks 조회·생성·업데이트 (ETag 처리 포함)
  llm/
    types.ts                     # ClassifiedAction 등 공용 타입
    prompt.ts                    # 시스템 프롬프트 + tool/function 스키마
    coerce.ts                    # LLM JSON → ClassifiedAction 검증
    claudeClassifier.ts          # Anthropic Claude 구현 (tool_use 강제)
    azureOpenAiClassifier.ts     # Azure OpenAI 구현 (function calling 강제)
    factory.ts                   # LLM_PROVIDER 기반 분기
  cards/confirmationCard.ts      # 결과 Adaptive Card
appPackage/manifest.json         # Teams 앱 매니페스트 (개발자 포털에 업로드)

사전 준비 (Azure / Microsoft 365)

  1. Azure AD 앱 등록 (Graph 위임 권한)

    • Azure Portal → App registrations → New
    • API permissions → Microsoft Graph → Delegated:
      • Tasks.ReadWrite
      • Group.Read.All
      • User.Read
      • offline_access
    • Grant admin consent
    • Certificates & secrets → Client secret 발급
    • 노트: tenantId, clientId, clientSecret
  2. Azure Bot 리소스

    • Azure Portal → Create resource → Azure Bot (Multi Tenant)
    • Microsoft App ID/Password 발급 → 노트
    • Configuration → OAuth Connection Settings → Add
      • Name: GraphConnection (이 이름이 .envOAUTH_CONNECTION_NAME)
      • Service Provider: Azure Active Directory v2
      • Client id / secret: 1번에서 만든 값
      • Tenant ID: 1번의 tenantId
      • Token Exchange URL: 비워두기 (SSO 안 쓸 경우)
      • Scopes: Tasks.ReadWrite Group.Read.All User.Read offline_access
    • "Test Connection" 으로 토큰이 떨어지는지 확인
  3. Teams 앱 매니페스트

    • appPackage/manifest.jsonREPLACE_WITH_YOUR_BOT_APP_ID 를 Azure Bot의 App ID로 치환
    • color.png (192×192), outline.png (32×32) 아이콘을 appPackage/에 추가
    • 폴더를 zip 으로 압축 → Teams 관리자 센터(또는 개발자 포털)에서 사이드로딩

로컬 실행

npm install
cp .env.example .env
# .env 채우기
npm run dev

다른 터미널에서 ngrok 등으로 외부 노출:

ngrok http 3978

ngrok URL을 Azure Bot의 Messaging endpointhttps://<ngrok>/api/messages 형태로 설정.

LLM 교체

.envLLM_PROVIDER 만 바꾸면 됨:

  • claudeANTHROPIC_API_KEY, CLAUDE_MODEL 사용
  • azure-openaiAZURE_OPENAI_* 사용

두 구현 모두 동일한 LlmClassifier 인터페이스를 구현하므로 호출부 변경 없음.

알아두면 좋을 점 / 함정

  • Planner write는 ETag 필수. plannerClient.ts 가 PATCH 직전 항상 GET으로 ETag를 받아 If-Match에 넣는다. 만약 동시 편집이 잦은 환경이면 412 에러를 retry 로 감싸야 한다.
  • /me/planner/plans 는 사용자가 속한 그룹의 Plan 만 반환한다. 봇이 "보이지 않는다" 면 Teams 채널에 그 사용자가 멤버인지부터 확인.
  • OAuthPrompt 의 토큰은 Bot Framework 의 토큰 서비스(token.botframework.com)에 캐시된다. logout 명령으로 강제 갱신 가능.
  • MemoryStorage 는 개발용. 프로덕션은 CosmosDB/Blob 백엔드로 교체.
  • 확장 아이디어:
    • 다중 후보일 때 카드의 Action.Submit 으로 사용자가 직접 plan 선택
    • Plans/Buckets 캐시 (현재는 매 발화마다 다시 조회)
    • 일/주간 요약 발송 (proactive messaging)

다음 단계

  1. npm install → 의존성 받기
  2. Azure AD 앱 등록 + Azure Bot 리소스 + OAuth Connection 설정
  3. .env 채우기
  4. npm run dev 로 띄우고 ngrok 으로 노출
  5. Teams 매니페스트 사이드로딩 → 개인 채팅으로 봇과 대화
  6. "오늘 견적서 초안 작성 시작했어" 같은 첫 발화로 테스트
Description
No description provided
Readme 242 KiB
Languages
TypeScript 99.6%
Dockerfile 0.4%