
Cloudflare Agents로 간이 기억(Memory) 구현하기
요약
Cloudflare Agents의 Think와 Session API를 활용하여 에이전트에게 간이 기억(Memory) 기능을 구현하는 방법을 다룹니다. Context 설정을 통해 데이터의 읽기/쓰기 권한을 제어하고 시스템 프롬프트에 반영하는 과정을 설명합니다.
핵심 포인트
- Think와 Session API를 조합하여 에이전트의 메모리 구현 가능
- Context 설정을 통해 데이터 취득처, 쓰기 가능 여부, 검색 기능 부여
- set_context 호출 후 시스템 프롬프트 반영을 위한 refreshSystemPrompt 필요
- withCachedPrompt를 사용하여 Durable Object 슬립 시에도 프롬프트 영속화
Cloudflare Agents의 Think와 Session API를 사용하여, 에이전트에게 간이적인 기억을 부여하기 위한 조사를 수행했습니다. OpenClaw에서 말하는 Soul이나, ChatGPT의 메모리(Memory)에 해당하는 것입니다.
이번에는 Think를 전제로, Think와 Context를 조합하여 간이적인 기억을 구현합니다.
초기 설정
Think 에이전트 초기화 시, Session에 기억용 Context를 추가합니다. 다음 코드는 기억용 Context를 하나 갖춘 에이전트의 예시입니다.
export class ThinkAgent extends Think<Env> {
getModel() {
...
...
configureSession()
에서는 Session API를 사용하여 Context를 설정하고 있습니다. 인자로 받은 session에 대해 withContext()를 체이닝하여 Context를 추가합니다. 여기서 memory라는 Context가 기억을 유지하는 부분입니다. Context에는 데이터의 취득처, 쓰기 가능 여부, 검색, 지연 로딩(Lazy loading) 등의 특성을 부여할 수 있습니다.
각 Context는 시스템 프롬프트(System Prompt)에 포함됩니다. 다음 코드는 Context와 Think가 생성하는 시스템 프롬프트의 예시입니다.
[system prompt] ══════════════════════════════════════════════
SOUL [readonly]
══════════════════════════════════════════════
...
Session은 Provider의 기능에 따라 도구(Tool)를 생성합니다. writable에서는 set_context, searchable에서는 search_context 등이 생성됩니다. 반면, readonly에는 도구가 생성되지 않습니다.
set_context로 Context를 업데이트해도 시스템 프롬프트는 자동으로 재구축되지 않습니다. 업데이트 내용을 다음 턴부터 반영하려면 refreshSystemPrompt()를 호출해야 합니다.
withCachedPrompt()는 시스템 프롬프트를 영속화(Persistence)하기 위한 설정입니다. Durable Object는 기본적으로 상태(State)를 유지합니다. 하지만 Durable Object가 슬립(Sleep) 상태가 되는 등 상태가 파기되는 경우가 있습니다. 이를 활성화하면 Durable Object가 슬립한 후에도 영속화된 시스템 프롬프트를 재사용할 수 있습니다.
시스템 프롬프트에는 Context마다 readonly나 writable 등의 태그가 부여됩니다. 이는 각 Context의 특성을 나타냅니다.
Context는 Session에 종속됩니다. 서브 에이전트(Sub-agent)와 기억을 공유하려면 공유 방법을 별도로 설계해야 합니다. 또한, 사용자마다 기억을 분리하는 경우에는 사용자와 Session의 대응 관계도 설계가 필요합니다.
기억 업데이트 예시
다음과 같은 메시지를 에이전트에게 보냈다고 가정합니다.
저는 엔지니어입니다. 기억해 주세요.
다음은 모델이 set_context를 호출했을 경우의 개념적 예시입니다. 입력에 대해 반드시 이 도구가 호출되는 것은 아닙니다.
set_context가 호출되면 다음과 같은 Context가 저장됩니다.
══════════════════════════════════════════════
MEMORY (Important durable facts about the user's preferences, projects, and ongoing context.) [0% — 7/2000 tokens] [writable]
══════════════════════════════════════════════
...
Context의 종류
Context에는 다음 4가지 종류가 있습니다.
readonly: 읽기 전용(Read-only) Contextwritable: 에이전트가 읽고 쓸 수 있는(Read-write) Contextsearchable: 검색 가능한(Searchable) Contextloadable: Skill용 Context
이하 각 특징을 설명합니다.
readonly
readonly
는 Provider에 get()만을 구현했을 경우의 Context입니다. 이 Context에 대해서는 조작용 도구(tool)가 생성되지 않습니다.
session
.withContext("soul", {
provider: {
...
Provider의 get()이 반환하는 문자열이 Context의 내용이 됩니다. 가져올 대상을 R2나 D1로 설정하는 것도 가능합니다.
writable
writable은 에이전트의 ToDo 리스트나, 기억해 두어야 할 사용자 프로필 등에 사용할 수 있습니다.
session
.withContext("memory", {
description:
...
위의 코드에서는 readonly 예시와 비교하여 provider를 생략했습니다. provider를 생략하면, Session이 SQLite 기반의 쓰기용 Provider를 자동으로 설정합니다. 직접 Provider를 구현하는 경우에는 get()과 set()이 모두 필요합니다.
writable에서는 set_context라는 도구가 LLM에 공개됩니다. 이 도구를 사용하여 maxTokens를 상한으로 Context를 편집할 수 있습니다.
searchable
searchable은 지식 베이스(knowledge base)나 로그 등, 하나의 Context에 다 담을 수 없는 정보를 검색하기 위한 Context입니다. 시스템 프롬프트에는 검색 대상의 개요만 기재됩니다.
searchable의 Provider에는 get()과 search()를 구현합니다. set()은 선택 사항이며, 구현할 경우 set_context를 통한 쓰기도 가능해집니다. search()의 구현 방법은 Session에서 지정되지 않으므로, 전문 검색(full-text search)이나 벡터 검색(vector search) 등을 자유롭게 구축할 수 있습니다.
간편하게 테스트하려면, Agent의 Durable Object SQLite에서 FTS5[1]를 사용하는 내장 Provider인 AgentSearchProvider를 이용할 수 있습니다.
session
.withContext("knowledge", {
description: "Searchable knowledge base, search for relevant information before answering",
...
다음은 시스템 프롬프트의 개념 예시입니다. description이나 항목 수 등의 표시 내용은 Provider의 구현이나 저장된 데이터에 따라 달라집니다.
══════════════════════════════════════════════
KNOWLEDGE (Searchable long-term knowledge for this shared workspace. Store durable project facts, design decisions, and stable notes as separate keyed entries. Search this block before answering project-specific questions. Do not store secrets, credentials, or temporary conversation details.) [searchable]
══════════════════════════════════════════════
...
10 entries indexed.와 같은 개요는 Provider의 get()이 반환하는 내용에 의존합니다. Context를 업데이트하더라도, 동결된(frozen) 시스템 프롬프트가 업데이트될 때까지 표시가 바뀌지 않을 수 있습니다.
You are running ... 이후는 Think가 자체적으로 삽입하는 프롬프트입니다.
이 Context를 설정하면 Provider의 기능에 따라 set_context나 search_context가 LLM에 공개되어, Context의 변경 및 검색이 가능해집니다.
loadable (Skill)
Skill은 일련의 절차나 참조 자료를 모아놓은 것입니다. loadable에서는 Skill의 목록만을 시스템 프롬프트에 올리고, 필요할 때 내용을 불러옵니다.
Context를 loadable
Context를 loadable로 취급하려면, Provider에 get()과 load()를 구현합니다. set()은 선택 사항이며, 이를 구현하면 에이전트가 Skill의 내용을 업데이트할 수 있습니다. set()을 구현할 경우, 이 Context는 **특정 작업을 기억하기 위한 기억 (Memory)**으로 정의될 수 있습니다.
이번에는 실제로 코드를 실행하여 검증하지 않았으므로, 아래 내용은 공식 문서에서 인용한 예시입니다.
초기화 코드:
.withContext("skills", {
provider: new R2SkillProvider(env.SKILLS_BUCKET, { prefix: "skills/" }),
})
R2SkillProvider는 내장된 (built-in) Provider입니다. 이름에서 알 수 있듯이, R2에 저장된 Skill을 불러옵니다.
시스템 프롬프트:
══════════════════════════════════════════════
SKILLS [loadable]
══════════════════════════════════════════════
...
요약
Cloudflare Agents의 Context 기능을 사용하면, 짧은 사실이나 사용자 프로필을 유지하는 간이 기억 (Memory)을 구현할 수 있습니다. 이번 조사에서 구현한 코드를 공개하고 있습니다. 이번 구현에서는 Vercel AI SDK를 일부 사용하고 있으므로, Vercel의 AI Elements로 UI를 구축하고 있습니다.
참고 문헌
-
Conversation state and memory · Cloudflare Agents docs
-
Sessions · Cloudflare Agents docs
-
Configuration · Cloudflare Agents docs
-
Lifecycle hooks · Cloudflare Agents docs
SQLite의 전문 검색 (Full-text search) 확장이라고 합니다. ↩︎
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기