LangChain의 Deep Agents에서 FilesystemBackend 이해하기
요약
LangChain의 Deep Agents에서 FilesystemBackend를 사용하여 에이전트가 로컬 파일 시스템에 직접 파일을 읽고 쓸 수 있는 방법을 설명합니다. StateBackend와 달리 파일이 디스크에 영구적으로 남으므로 로컬 코딩 어시스턴트 구축에 유용합니다.
핵심 포인트
- FilesystemBackend는 에이전트가 실제 로컬 파일을 생성하고 수정할 수 있게 함
- 생성된 파일은 프로그램 종료 후에도 디스크에 유지됨
- StateBackend와 달리 thread_id에 의한 파일 격리가 이루어지지 않음
- CompositeBackend를 통해 임시 아티팩트와 실제 프로젝트 파일을 분리 가능
이전 예제에서는 파일이 특정 LangGraph 대화 스레드(conversation thread)에 속하는 StateBackend를 사용했습니다.
이번에는 다른 것을 사용합니다:
FilesystemBackend를 사용하면 Deep Agent가 사용자의 컴퓨터에 있는 실제 파일을 읽고 쓸 수 있습니다.
즉, 에이전트가 Markdown 파일, Python 파일 또는 프로젝트 폴더를 생성하면 스크립트가 종료된 후에도 디스크에 그대로 남아 있게 됩니다.
이는 로컬 코딩 어시스턴트(coding assistants), 문서 생성 도구 및 개발 워크플로(workflows)에 유용합니다. 하지만 에이전트가 실제 파일을 수정할 수 있으므로 주의해서 사용해야 합니다.
이 예제가 보여주는 것
코드는 네 가지 중요한 동작을 보여줍니다:
- 에이전트가 디스크의 실제 폴더에 파일을 작성합니다.
- 해당 파일들은 Python 프로그램이 중단된 후에도 남아 있습니다.
- 로컬 디스크 저장소를 사용할 때는
thread_id가 파일을 격리(isolate)하지 않습니다. CompositeBackend를 사용하여 임시 에이전트 아티팩트(artifacts)를 실제 프로젝트 파일과 분리하여 유지할 수 있습니다.
다음은 상위 수준의 비교입니다:
StateBackend
────────────
Thread A → Thread A를 위한 임시 파일들
...
FilesystemBackend를 사용하면 파일이 LangGraph 상태(state) 내부에 저장되지 않습니다. 파일은 구성된 디렉토리 아래에 있는 일반적인 운영체제(operating-system) 파일입니다. (docs.langchain.com)
전체 예제
"""
Deep Agents의 FilesystemBackend(디스크의 실제 파일)를 시연합니다.
...
코드 실행 전 주의사항
필요한 패키지를 설치하세요:
pip install deepagents langchain langgraph python-dotenv
또한 모델 제공자(model provider)의 자격 증명이 구성되어 있어야 합니다. 이 예제는 OpenRouter를 사용하므로, .env 파일에 API 키를 넣으세요:
OPENROUTER_API_KEY=your_api_key_here
load_dotenv() 호출은 해당 파일에서 환경 변수(environment variables)를 로드합니다:
from dotenv import load_dotenv
load_dotenv()
실제 API 키가 포함된 .env 파일을 GitHub에 커밋하지 마세요.
중요한 차이점: 실제 디스크 vs 에이전트 상태
코드를 공부하기 전에, 이 차이점을 이해해야 합니다.
| 기능 | StateBackend | FilesystemBackend |
|---|---|---|
| 파일 저장 위치 | LangGraph 상태 (state) | 사용자의 컴퓨터 파일 시스템 (filesystem) |
| ... |
FilesystemBackend는 로컬 파일 시스템 (filesystem)에 직접 읽고 씁니다. 이는 통제된 로컬 개발 또는 신중하게 구성된 CI/CD 워크플로우를 위한 것이며, 직접 노출된 웹 애플리케이션이나 다중 사용자 API를 위한 것이 아닙니다. (reference.langchain.com)
1단계: 모델 생성하기 (Creating the Model)
model = init_chat_model(
"openrouter:nvidia/nemotron-3-super-120b-a12b",
max_tokens=4096,
...
이 코드는 Deep Agent에서 사용될 언어 모델 (language model)을 생성합니다.
max_tokens=4096 인자는 생성된 응답의 크기를 제한합니다. 이는 유료 API를 사용할 때 토큰 사용량과 비용을 제어하는 데 도움이 되므로 실용적입니다.
나중에 다른 지원되는 모델을 사용할 수 있습니다. 이 글에서 설명하는 저장 동작은 모델 자체가 아니라 백엔드 (backend) 구성에서 비롯됩니다.
2단계: FilesystemBackend 생성하기
첫 번째 에이전트의 가장 중요한 부분은 다음과 같습니다:
agent = create_deep_agent(
model=model,
backend=FilesystemBackend(
...
이 코드는 에이전트에게 다음과 같이 지시합니다:
agent_workspace폴더를 실제 파일 작업의 위치로 사용하십시오.
에이전트가 다음과 같은 가상 경로 (virtual path)를 받으면:
/notes/todo.md
이는 다음과 유사한 실제 경로로 매핑됩니다:
your-project-folder/
└── agent_workspace/
└── notes/
...
정확한 전체 경로는 Python 스크립트를 실행하는 위치에 따라 달라집니다.
예를 들어, 프로젝트가 다음 위치에 있다면:
C:\Users\Talha\deep-agents-demo
최종 파일은 다음과 같을 수 있습니다:
C:\Users\Talha\deep-agents-demo\agent_workspace\notes\todo.md
macOS 또는 Linux에서는 다음과 같이 보일 수 있습니다:
/home/talha/deep-agents-demo/agent_workspace/notes/todo.md
왜 virtual_mode=True를 사용하나요?
virtual_mode=True
이것은 중요한 안전 설정입니다.
이를 통해 에이전트가 다음과 같이 깔끔한 가상 경로 (virtual paths)를 사용할 수 있게 합니다:
/notes/todo.md
를 설정된 root_dir 하위로 매핑하는 동시에 사용합니다.
또한 다음과 같은 일반적인 경로 탈출 (path-escape) 시도를 차단합니다:
../../some-other-folder/file.txt
또는:
~/secret-file.txt
하지만, 이것은 완전한 샌드박싱 (sandboxing)은 아닙니다. 경로 기반의 제한을 제공하지만, 컨테이너나 원격 샌드박스처럼 Python 프로세스를 격리하지는 않습니다. 공식 Deep Agents 문서에서는 설정된 루트 디렉토리와 함께 virtual_mode=True를 사용할 것을 권장합니다. (reference.langchain.com)
작은 개선 사항: 절대 경로 root_dir 사용하기
초보자용 프로젝트에서는 다음과 같이 작동합니다:
root_dir="./agent_workspace"
하지만 절대 경로 (absolute path)를 사용하면, 특히 서로 다른 디렉토리에서 스크립트를 실행할 경우 코드를 더 예측 가능하게 만들 수 있습니다.
다음은 약간 더 안전한 버전입니다:
from pathlib import Path
workspace_dir = Path("./agent_workspace").resolve()
...
이제 workspace_dir는 자동으로 완전한 OS 경로가 됩니다.
예를 들어:
C:\Users\Talha\deep-agents-demo\agent_workspace
또는:
/home/talha/deep-agents-demo/agent_workspace
3단계: run() 헬퍼 함수
헬퍼 함수는 에이전트를 호출합니다:
def run(thread_id: str, prompt: str):
result = agent.invoke(
{"messages": [{"role": "user", "content": prompt}]},
...
thread_id는 여전히 LangGraph로 전달됩니다:
config={"configurable": {"thread_id": thread_id}}
하지만 FilesystemBackend를 사용할 경우, 파일 자체는 스레드 (thread)에 속하지 않습니다.
이것은 매우 중요한 차이점입니다:
thread_id는 대화/체크포인트 상태 (checkpoint state)를 제어합니다
root_dir는 실제 파일 위치를 제어합니다
스레드는 대화 메모리에 영향을 줄 수 있지만, 디스크에 기록된 파일은 해당 디스크 위치에 접근할 권한이 있는 모든 것에 노출됩니다.
턴 1: 실제 마크다운 (Markdown) 파일 쓰기
첫 번째 프롬프트는 에이전트에게 다음을 생성하도록 요청합니다:
/notes/todo.md
다음 내용을 포함하여:
# To-do
- Buy groceries
- Finish the project
...
백엔드 루트 디렉토리 (backend root directory)가 다음과 같기 때문에:
./agent_workspace
실제 파일은 다음과 같이 생성됩니다:
./agent_workspace/notes/todo.md
그 후 에이전트는 파일이 존재하는지 확인하기 위해 ls를 호출해야 합니다.
개념적으로 파일 시스템 (filesystem)은 현재 다음과 같은 구조를 가집니다:
agent_workspace/
└── notes/
└── todo.md
StateBackend와 달리, 이 파일은 임시 에이전트 상태 (temporary agent state)가 아닙니다. 이는 사용자의 컴퓨터에 생성된 실제 파일입니다.
턴 2: 파일 다시 읽기
두 번째 호출은 동일한 스레드 (thread)를 사용합니다:
run(
"demo-thread-1",
...
...
에이전트는 다음을 읽습니다:
/notes/todo.md
그리고 다음과 같이 보고해야 합니다:
- Buy groceries
- Finish the project
- Review pull requests
이 시점에서는 StateBackend 예제와 유사해 보일 수 있습니다. 하지만 파일이 사용 가능한 이유는 다릅니다.
StateBackend의 경우, 파일이 스레드 상태 (thread state)에 저장되기 때문에 동일한 스레드 ID가 중요합니다.
FilesystemBackend의 경우, 파일이 다음 위치에 물리적으로 존재하기 때문에 사용 가능합니다:
./agent_workspace/notes/todo.md
파일이 여전히 디스크에 남아 있는 한, 에이전트는 Python 스크립트를 재시작한 후에도 파일을 읽을 수 있습니다.
턴 3: 다른 스레드도 동일한 파일을 읽을 수 있음
이제 다음 호출에 주목하세요:
run(
"demo-thread-2",
...
...
코드는 다른 스레드 ID를 사용합니다:
demo-thread-2
하지만 에이전트는 여전히 다음 파일을 찾아 읽을 수 있습니다:
/notes/todo.md
왜 그럴까요?
두 스레드 모두 동일한 실제 디렉토리를 가리키고 있기 때문입니다:
./agent_workspace
흐름은 다음과 같습니다:
Thread: demo-thread-1
│
│ writes
...
이는 FilesystemBackend를 사용할 때 thread_id가 파일 시스템 보안 경계 (filesystem security boundary)가 아님을 의미합니다.
만약 두 에이전트가 동일한
FilesystemBackend루트 디렉토리를 사용한다면, 운영 체제 권한 (operating-system permissions) 및 백엔드 설정에 따라 동일한 파일에 접근할 수 있습니다.
직접 파일 확인하기
작성하신 코드는 다음과 같은 Windows 명령어를 출력합니다:
dir agent_workspace\notes\
type agent_workspace\notes\todo.md
명령 프롬프트(Command Prompt)에서 실행하세요:
dir agent_workspace\notes\
type agent_workspace\notes\todo.md
예상 출력:
# To-do
- Buy groceries
- Finish the project
...
PowerShell의 경우, 다음을 사용할 수 있습니다:
Get-ChildItem .\agent_workspace\notes\
Get-Content .\agent_workspace\notes\todo.md
macOS 또는 Linux의 경우:
ls agent_workspace/notes/
cat agent_workspace/notes/todo.md
이는 파일이 에이전트(agent) 및 Python 프로세스와 독립적으로 존재함을 증명하므로 유용한 테스트입니다.
실제 프로젝트에서 CompositeBackend가 더 나은 이유
코드의 두 번째 부분에서는 다음을 소개합니다:
CompositeBackend(
default=StateBackend(),
routes={
...
이는 하이브리드(hybrid) 접근 방식입니다.
내용은 다음과 같습니다:
- 대부분의 에이전트 파일에는
StateBackend를 사용합니다. /workspace/로 시작하는 경로에 대해서만 실제 디스크를 사용합니다.
라우팅(routing) 개념은 다음과 같습니다:
에이전트 경로 사용되는 백엔드 (Backend)
────────────────────────────────────────────────────
/workspace/src/hello.py FilesystemBackend
...
시각적으로는 다음과 같습니다:
CompositeBackend
│
┌──────────────┴──────────────┐
...
CompositeBackend는 경로 접두사(path prefix)에 따라 파일 작업을 라우팅합니다. 일치하는 경로가 있으면 해당 백엔드로 작업을 보내고, 경로가 일치하지 않으면 기본 백엔드(default backend)를 사용합니다. (reference.langchain.com)
/workspace/ 경로 이해하기
다음 설정은:
routes={
"/workspace/": FilesystemBackend(
root_dir="./my_project",
...
이 에이전트 경로가:
/workspace/src/hello.py
다음의 실제 파일이 된다는 것을 의미합니다:
./my_project/src/hello.py
에이전트 프롬프트(agent prompt)는 다음과 같이 말합니다:
" /workspace/src/hello.py 경로에 파일을 작성하세요. "
" 내용은 print('hello from FilesystemBackend') 입니다. "
" 그런 다음 ls를 통해 파일이 존재하는지 확인하세요. "
에이전트가 작업을 마치면, 프로젝트 폴더에는 다음과 같은 내용이 포함되어 있어야 합니다:
my_project/
└── src/
└── hello.py
hello.py의 내용은 다음과 같아야 합니다:
print("hello from FilesystemBackend")
왜 내부 파일들을 StateBackend에 유지해야 할까요?
Deep Agents는 작업하는 동안 다음과 같은 임시 아티팩트 (artifacts)를 생성할 수 있습니다:
- 대규모 도구 결과 (tool results)
- 중간 연구 노트
- 계획 파일 (planning files)
- 대화 관련 아티팩트 (conversation-related artifacts)
- 임시 생성 콘텐츠
이러한 파일들이 실제 소스 코드 프로젝트에 나타나는 것을 원하지 않을 수 있습니다.
그렇기 때문에 다음과 같은 패턴이 유용합니다:
default=StateBackend()
명시적인 프로젝트 출력물만 디스크에 저장됩니다:
/workspace/...
그 외의 모든 것은 에이전트 상태 (agent state) 내에서 임시적이고 격리된 상태로 유지됩니다.
이를 통해 프로젝트 디렉토리를 더 깔끔하게 유지할 수 있으며, 에이전트의 내부 아티팩트가 애플리케이션 파일과 섞일 가능성을 줄여줍니다.
FilesystemBackend vs CompositeBackend
| 질문 | 단독 FilesystemBackend | CompositeBackend |
|---|---|---|
| 파일이 어디로 가나요? | 하나의 실제 디스크 폴더 | 경로에 따라 서로 다른 위치 |
| ... | ... | ... |
빠른 실험을 위해서는 FilesystemBackend를 사용하세요.
에이전트가 실제 소스 파일을 생성하되 내부 작업은 임시로 유지하기를 원하는 프로젝트라면 CompositeBackend를 사용하세요.
중요한 안전 경고
FilesystemBackend는 AI 에이전트에게 실제 파일에 대한 접근 권한을 부여하기 때문에 강력합니다.
하지만 이는 동시에 위험 요소가 되기도 합니다.
공식 문서에서는 이 백엔드 (backend)를 사용하는 에이전트가 .env 파일, API 키 또는 자격 증명 (credentials)과 같이 접근 가능한 비밀 정보 (secrets)를 읽을 수 있으며, 파일 변경 사항은 영구적이라는 점을 경고합니다. 이는 통제된 로컬 개발 환경 및 신중하게 관리되는 CI/CD 환경을 위해 설계된 것이며, 공개 웹 서버, 멀티 테넌트 (multi-tenant) 앱 또는 신뢰할 수 없는 사용자 입력에는 적합하지 않습니다. (reference.langchain.com)
이러한 실수를 피하세요
1. 에이전트가 컴퓨터 전체를 가리키게 하지 마세요
다음과 같은 방식은 피해야 합니다:
FilesystemBackend(
root_dir="C:/",
virtual_mode=True,
...
또는 macOS/Linux의 경우:
FilesystemBackend(
root_dir="/",
virtual_mode=True,
...
대신, 전용 워크스페이스 (workspace)를 생성하세요:
FilesystemBackend(
root_dir="./agent_workspace",
virtual_mode=True,
...
2. 워크스페이스 내부에 비밀 정보를 노출하지 마세요
에이전트가 접근 가능한 폴더 안에 다음과 같은 파일들을 배치하는 것을 피하세요:
.env
credentials.json
private_key.pem
...
더 안전한 프로젝트 구조는 다음과 같을 수 있습니다:
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기