Qwen Cloud에서 DiversiFi 메모리 에이전트 구축하기
요약
Qwen Cloud를 활용하여 사용자의 금융 철학과 성향을 추출하는 메모리 에이전트 'DiversiFi' 구축 사례를 소개합니다. 단순 채팅 기록을 넘어 원시 데이터를 내구성이 있는 프로필로 요약하고 관리하는 메모리 계층 설계 과정을 다룹니다.
핵심 포인트
- 단순 채팅 기록이 아닌 사용자 프로필을 추출하는 메모리 에이전트 설계
- DashScope를 활용한 OpenAI 호환 엔드포인트 통합 및 모델 활용법
- Tablestore를 이용한 에이전트의 장기 메모리 계층 구축
- Qwen 모델(plus, long, max)의 용도별 최적 활용 전략
저는 1인 개발자입니다. 이는 새벽 2시에 무언가 고장 나더라도 Slack으로 연락할 팀원이 없다는 뜻입니다. 오직 저와 터미널, 그리고 제가 분명히 무언가 뻔한 설정을 잘못했을 것이라는 커지는 의구심만이 있을 뿐입니다. 이것은 Qwen Cloud 글로벌 AI 해커톤(Global AI Hackathon)의 트랙 1(Track 1) 제출물인 DiversiFi(MemoryAgent)를 구축한 이야기이자, 실제로 작동한 부분보다 더 많은 것을 가르쳐준 Alibaba Cloud의 베타 액세스 연옥(beta-access purgatory)으로의 3시간짜리 우회로에 대한 이야기입니다.
아이디어
DiversiFi는 자금 관리 에이전트(treasury management agent)입니다. 하지만 제가 실제로 구축하고 싶었던 부분은 자금 관리 로직이 아니었습니다. 바로 메모리(memory)였습니다.
대부분의 "에이전트 메모리(agent memory)"는 그저 더 화려한 이름을 붙인 채팅 기록(chat history)에 불과합니다. 저는 다른 것을 원했습니다. 소음 속에서 당신의 실제 프로필을 추출해내는 에이전트 말입니다. 당신의 위험 감수 성향(risk tolerance). 당신의 금융 철학 — 그것이 아프리카 자본주의(Africapitalism)일 수도 있고, 이슬람 금융 원칙(Islamic finance principles)일 수도 있으며, 부엔 비비르(Buen Vivir)일 수도 있습니다. 통화 가치 하락에 대한 당신의 반복적인 불안감. 시간이 흐름에 따라 나타나는 당신의 스왑 패턴(swap patterns). "지금까지 나에게 말한 모든 것"이 아니라, "내가 파악할 수 있는 한, 당신은 이런 사람입니다"라고 말하는 것입니다.
이는 두 가지 일이 지속적으로 일어나야 함을 의미합니다. 즉, 가공되지 않은 상호작용(raw interactions)이 내구성이 있는 진술(durable statements)로 통합되어야 하며, 오래되고 무관한 신호(signal)는 잊혀져야 합니다. 이것이 트랙 1(Track 1)의 핵심이며, 단순해 보이지만 실제로 매우 어려운 문제입니다.
세 가지 서비스 연결하기
결국 저는 세 가지 Alibaba Cloud 구성 요소를 활용하게 되었습니다:
DashScope (Model Studio / Bailian)는 두뇌 역할을 합니다. 저는 OpenAI 호환 엔드포인트 (OpenAI-compatible endpoint)를 사용했는데, 이는 단 하나의 베이스 URL (base URL)만 변경함으로써 기존의 OpenAI 클라이언트 형태의 프로바이더 (provider)를 Qwen으로 지정할 수 있음을 의미했습니다. 코드 재작성이 전혀 필요 없었습니다. 통합 파이프라인 (consolidation pipeline)은 최대 40개의 원시 메모리 (raw memories)를 Qwen에 전달하고, 이를 3~7개의 내구적인 프로필 문장 (durable profile statements)으로 요약하도록 요청합니다. 기본적으로는 qwen-plus를 사용하고, 더 큰 메모리 풀을 위해 1M 토큰 컨텍스트 (1M-token context)가 필요할 때는 qwen-long을, 속도보다 품질이 더 중요할 때는 qwen-max를 사용합니다. 심지어 싱가포르에 전용 MaaS 워크스페이스 엔드포인트 (MaaS workspace endpoint)를 구축했으며, 실제 완료 응답 (completions)을 반환하는 라이브 curl 명령어를 실행 중입니다. 이 부분은 정말 만족스러웠습니다.
Tablestore의 에이전트 메모리 스토어 (Agent Memory Store)는 이 모든 것의 밑단에서 장기 메모리 계층 (long-term memory layer) 역할을 수행하도록 설계되었습니다. 공식 SDK는 createMemoryStore, addMemories, searchMemories 등 모든 기능을 제공하며, 제가 예상치 못하게 매우 좋아하게 된 기능도 포함되어 있습니다. 바로 원시 메시지 (raw messages)에 대해 자체적인 백그라운드 추출 패스 (background extraction pass)를 실행한다는 점입니다. 덕분에 제가 Qwen 기반으로 수행한 통합 작업 위에, 무엇이 중요한지에 대한 두 번째의 독립적인 해석이 레이어 형태로 쌓이게 됩니다. appId / tenantId / agentId / runId로 이어지는 4단계 스코프 (four-level scope)는 재무 에이전트 (treasury agent)에 꼭 필요한 멀티 유저 격리 모델 (multi-user isolation model)이며, 제가 직접 고안할 필요도 없었습니다.
Function Compute는 접착제 역할을 합니다. cn-beijing 지역의 작은 Node.js 18 핸들러 (handler)가 스토리지에서 원시 메모리를 가져와 Qwen으로 보내 통합하고, 추출된 프로필을 우선순위가 높은 메모리로 다시 기록하며, 방금 흡수된 데이터를 삭제(evict)합니다. 제 Hetzner 서버의 크론 잡 (cron job)이 활성 사용자당 6시간마다 이 작업을 수행하여 모든 프로필을 조용히 최신 상태로 유지합니다.
이론상으로는 이것이 전체 아키텍처 (architecture)입니다. 하지만 실제로는 세 개의 다리 중 하나가 결코 서지 못했습니다.
"사용자가 비활성화되었습니다 (The user is disabled)"
여기서부터 해커톤이 아닌 미스터리가 시작되었습니다.
저는 Tablestore 인스턴스를 생성하고, OTS 및 FC에 대한 전체 액세스 권한을 가진 RAM 사용자를 설정했으며, AccessKey가 활성 상태인지 이중으로 확인했습니다. 하지만 메모리 스토리지 API (Memory Storage API)를 호출할 때마다 매번 동일한 에러가 반환되었습니다:
OTSAuthFailed: The user is disabled.
그 문구는 엄청난 오해를 불러일으킵니다. 저의 첫 번째 직감, 그리고 아마 여러분의 직감도 마찬가지겠지만, "좋아, 내 IAM 정책에 뭔가 문제가 있구나"라고 생각하게 됩니다. 그래서 문서를 다시 읽었습니다. RAM 사용자를 다시 만들었습니다. 권한 문제를 완전히 배제하기 위해 루트 계정의 키로도 시도해 보았습니다. 매번 똑같은 에러가 발생했습니다.
알고 보니 createMemoryStore 및 관련 기능들의 실제 API 접점인 Tablestore Memory Storage 하위 서비스가 '邀测(초청 기반 베타)' 상태였습니다. 이 서비스는 cn-beijing 리전에 제한되어 있으며, 접근 권한은 제가 직접 작성할 수 있는 어떤 정책에 의해서가 아니라 계정 수준에서 수동 화이트리스트(allowlist)를 통해 제어됩니다. "The user is disabled"는 권한 관련 메시지가 아니었습니다. 그것은 베타 게이트(beta gate) 자체를 나타내는 메시지였으며, 사용자가 몇 시간 동안 IAM 유령을 쫓게 만드는 방식으로 표현되어 있었습니다. 제가 공식 가이드를 찾아냈을 때, 가이드는 접근 권한을 요청하기 위해 DingTalk 그룹에 가입하라고 안내하고 있었습니다. 결제 관련 문서에서도 이 서비스가 아직 GA(General Availability, 정식 출시) 상태가 아님을 조용히 확인해 주었습니다. 가격 책정은 7월 말까지 적용되지 않습니다.
이 과정에서 제가 얼마나 많은 시간을 허비했는지 솔직하게 말씀드리고 싶습니다. 만약 여러분이 마감 기한이 있는 해커톤을 위해 Alibaba Cloud의 베타 서비스를 사용하여 구축하고 있다면, 코드 한 줄을 쓰기 전에 먼저 접근 권한을 요청하십시오. 저는 그러지 않았고, 그 대가로 여유 없는 시간 몇 시간을 날렸습니다.
의존성(dependency)이 협조하지 않을 때 1인 개발자가 하는 일
당연히 멈추지는 않습니다. 하지만 — 속이지도 않습니다.
Tablestore 어댑터는 완료되었습니다. 문서에 명시된 대로 SDK를 사용하여 완전히 작성되었고 테스트도 마쳤습니다. 단지 계정이 화이트리스트에 등록되지 않았기 때문에 아직 실행할 수 없을 뿐입니다. 그래서 다른 사람의 승인 대기열 때문에 전체 제출을 막는 대신, 동일한 remember/recall/sweep 인터페이스를 구현하는 로컬 폴백(fallback)인 Cognee를 연결했습니다. 통합 서비스(consolidation service)는 엔드포인트가 설정되어 있으면 Tablestore를 먼저 시도하고, 설정되어 있지 않으면 매끄럽게 폴백됩니다. 사용자 입장에서는 아무것도 변하지 않습니다. 제 입장에서는 앱이 오늘 바로 완벽하게 작동하며, DingTalk 요청이 승인되는 즉시 코드 변경 없이 Tablestore가 온라인 상태가 됩니다. 환경 변수는 이미 그곳에서 대기하고 있으니까요.
그 폴백 (fallback) 패턴은 이 전체 빌드 과정에서 그 어떤 단일 통합보다도 제가 가장 자랑스럽게 여기는 부분이 되었습니다. 이 앱에 사용된 모든 Alibaba Cloud 서비스는 환경 변수 뒤에 배치되어 있으며, 앱이 작동하는 데 있어 그 어떤 것도 필수적인 하중을 견디는 요소(load-bearing)가 아닙니다.
ALIBABA_CLOUD_FC_ENDPOINT가 없나요? Guardian cron이 로컬에서 통합을 수행합니다.
TABLESTORE_ENDPOINT가 없나요? 메모리는 Cognee로 폴백(fallback)됩니다.
DASHSCOPE_API_KEY가 없나요? LLM 체인은 Gemini를 거쳐 Venice, 그리고 리스트의 다음 순서로 넘어갑니다.
Alibaba Cloud는 이 시스템을 더 낫게 만듭니다. 이 시스템은 단일 장애점 (Single Point of Failure)이 되는 것이 결코 허용되지 않았으며, 이는 그 자체의 서비스 중 하나가 저를 차단했을 때 제가 예상했던 것보다 훨씬 더 중요하게 작용했습니다.
의도적인 망각
또 다른 요구 사항인 '적시의 망각 (timely forgetting)'은 메모리를 단순히 계속해서 커지기만 하는 무언가로 취급하지 않게 되자 실제로 생각하기가 더 쉬워졌습니다. 여기에는 두 가지 계층이 있습니다. 소프트 감쇠 함수 (soft decay function)는 30일이 지난 모든 항목의 회상 점수 (recall score)를 조용히 감점하며, 그 두 배의 시간이 지나면 0에 도달하게 하여, 오래된 기억들이 삭제되기 전에 관련성에서 서서히 사라지게 합니다. 그 후 더 강력한 스윕 (sweep)이 해당 임계값 미만의 항목을 실제로 축출(evict)하며, 통합 (consolidation) 과정 자체도 방금 흡수한 원시 메모리 (raw memories)를 정제된 문장으로 변환하여 축출합니다. 그렇지 않으면 단순히 노이즈를 영원히 축적하면서 그것을 "메모리"라고 부르는 것에 불과하기 때문입니다.
세 번째 요구 사항인 '제한된 컨텍스트 윈도우 (context window) 내에서 중요한 기억을 회상하는 것'은 다소 직관에 반하는 해답을 가지고 있었습니다. Qwen의 qwen-long은 실제로 100만 토큰의 컨텍스트를 제공합니다. 하지만 진정한 승리는 그 모든 것을 사용하는 것이 아니었습니다. 그것은 40개의 원시 메모리를 3~7개의 내구성 있는 문장으로 통합하고, 회상 시 이를 우선순위에 두는 것이었습니다. 이를 통해 어드바이저의 컨텍스트가 단순한 스크롤백 (scrollback)이 아닌, 당신의 철학이나 리스크 프로필과 같은 유의미한 신호 (signal)로 가득 차게 유지될 수 있었습니다.
현재 상황
DashScope: 라이브 상태이며, 검증되었고, 오늘 실제로 요청에 응답하고 있습니다. Function Compute (함수 컴퓨팅): 완전히 구성되었으며, 배포(deploy) 한 번이면 바로 실행 가능한 상태입니다. Tablestore 인스턴스 자체도 존재하며, 인터넷 접속이 활성화되어 있고, 적절한 정책(policies)이 연결되어 있습니다. 단지 제가 열 수 없는 베타 게이트(beta gate)가 열리기를 기다리고 있을 뿐입니다. 그리고 그동안 Cognee는 880개의 테스트를 통과하며 조용히 제 역할을 수행하고 있습니다.
클라우드 해커톤을 노리는 다른 1인 개발자에게 줄 교훈이 있다면 이것입니다: 새로운 기술이 제대로 작동하는지 확인하기도 전에, 그 기술이 실패할 상황을 대비하여 구축하십시오. 이는 비관론 때문이 아니라, 베타 서비스는 정의상 첫 시도에 접속을 허용하지 않을 수도 있다는 사실을 존중하기 때문입니다. 결국 흥미로운 엔지니어링 요소는 Qwen 통합이 아니었습니다. 전체 시스템이 그 통합에만 의존하지 않도록 만드는 것이었습니다.
코드: github.com/thisyearnofear/diversify
FC 핸들러 (세 가지 서비스가 한 곳에 있음): alibaba-cloud/fc-memory-consolidation/index.js
Tablestore 어댑터: packages/shared/src/services/tablestore-memory-service.ts
DashScope 프로바이더: packages/shared/src/services/ai/providers/dashscope-provider.ts
배포 문서: docs/alibaba-cloud-deployment.md
Qwen Cloud Global AI Hackathon, Track 1: MemoryAgent를 위해 구축되었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기