사람과 에이전트를 위한 공유 워크스페이스 구축

Perplexity 프로젝트가 장기 작업의 영속성, 동시성, 추적 가능성을 유지하는 방법.

작성자Perplexity Engineering

최근 Perplexity Computer에서 사람과 에이전트가 함께 장기 작업을 수행하는 프로젝트 기능을 출시했습니다. 각 프로젝트는 세션, 에이전트, 사용자 전반에 걸쳐 컨텍스트를 축적하는 파일 시스템을 갖춘 공유 워크스페이스를 기반으로 합니다. 몇 달에 걸친 멀티플레이어 협업에는 영속성, 동시성, 추적 가능성이 필요합니다:

  • 영속성: 작업은 개별 에이전트 세션이나 샌드박스보다 오래 유지되어야 합니다. 그래야 같은 사용자나 에이전트가 이후에 작업을 이어가고, 다른 협업자도 이를 기반으로 작업할 수 있습니다.
  • 동시성: 같은 프로젝트의 여러 사용자가 각각 여러 에이전트를 두고 병렬로 편집할 수 있으며, 이러한 편집은 서로를 덮어쓰지 않고 공유 상태를 함께 진전시킵니다.
  • 추적 가능성: 사용자와 에이전트 모두 기록을 볼 수 있어야 합니다. 그래야 프로젝트 전체를 다시 읽지 않고도 지난 세션 이후 누가 무엇을 변경했는지 확인하고, 필요할 때 편집을 롤백할 수 있습니다.

이러한 과제는 소프트웨어 엔지니어에게 익숙합니다. Git 포지는 수십 년 전 이러한 요구 사항을 충족하기 위해 등장했습니다. 포지는 버전 관리에 협업 기능을 더해 많은 기여자가 하나의 신뢰할 수 있는 정보 소스로 수렴할 수 있게 해 주는 호스팅 서비스입니다. 프로젝트를 구동하는 파일 시스템도 이러한 Git 포지의 하나로, 사람과 에이전트, 에이전트와 에이전트 간의 협업을 위해 구축되었습니다.

하나의 프로젝트: 대화형 에이전트, 예약된 에이전트, 다른 사용자의 에이전트가 모두 같은 리포지토리로 모입니다.

아키텍처

Git은 이미 영속성, 동시성, 추적 가능성을 제공합니다. Git은 각 파일을 내용으로 식별하므로 변경되지 않은 파일은 한 번만 저장되고, 저장된 사본이 오래된 상태가 되는 일이 없습니다. 각 커밋은 업데이트된 모든 파일을 한 번에 저장하므로 협업자가 절반만 완료된 상태를 보는 일이 없습니다. 두 작성자가 충돌하면 밀려난 쪽이 새 버전 위에 변경 사항을 다시 적용하고 재시도할 수 있습니다. 커밋 체인은 누가 무엇을 변경했는지 보여 주며, 이전 버전을 쉽게 복원할 수 있게 해 줍니다. 에이전트에게 필요 없는 것은 리뷰 화면, 브랜치 관리, 알림처럼 사람을 위해 만들어진 포지의 나머지 기능입니다.

저희 아키텍처는 전체 프로세스를 클라우드에서 실행하며, 코딩을 넘어 모든 형태의 지식 업무를 지원합니다. 모든 Computer 프로젝트는 다음 5가지 구성 요소가 제공하는 Git 리포지토리를 기반으로 합니다:

  • CLI는 각 샌드박스에서 로컬 체크아웃을 유지하고, 업데이트를 동기화하며, 에이전트의 변경 사항을 제출합니다.
  • 컨트롤 플레인은 프로젝트 리포지토리의 위치를 찾고, 해당 리포지토리에 접근하기 위한 단기 자격 증명을 발급합니다.
  • Git 서비스는 샌드박스에 리포지토리 데이터를 보내고, 샌드박스의 제출을 받아, 어떤 제출이 가장 최근에 수락된 커밋이 될지 결정합니다.
  • AWS S3 기반 스토리지는 리포지토리 데이터를 영속적으로 저장합니다.
  • 다운스트림 처리는 저장된 문서에서 텍스트와 기타 유용한 정보를 추출하고, 에이전트가 프로젝트를 로드하거나 새로 고칠 때 이를 사용할 수 있게 합니다.

각 에이전트는 격리된 자체 샌드박스를 갖습니다. 에이전트는 이 샌드박스 안에서 작은 CLI가 관리하는 리포지토리의 로컬 사본으로 작업합니다. CLI는 컨트롤 플레인에 리포지토리 위치를 요청한 다음, Git 서비스를 통해 변경 사항을 가져오고 제출합니다. 에이전트 프록시는 샌드박스 요청이 Git 서비스에 도달하기 전에 액세스 권한을 확인합니다. Git 서비스는 공유 파일 시스템에서 리포지토리를 읽고 쓰며, 각 제출이 가장 최근에 수락된 커밋이 될지를 결정합니다. 이 파일 시스템은 AWS S3Files가 제공하며, S3 기반 스토리지를 일반 파일과 디렉터리처럼 사용할 수 있게 해 줍니다. 스토리지는 격리된 셀로 나뉘며, 각 셀에는 자체 파일 시스템과 Git 서비스 복제본이 있습니다. 커밋이 수락되면 백그라운드 워커가 변경된 문서에서 텍스트와 기타 유용한 정보를 추출하고, 그 결과를 리포지토리 기록과 별도로 저장해 에이전트가 프로젝트를 로드하거나 새로 고칠 때 사용할 수 있게 합니다.

이제 각 구성 요소를 하나씩 살펴보겠습니다.

CLI는 컨트롤 플레인에 연결해 리포지토리 위치를 찾은 다음, 에이전트 프록시를 거쳐 Git 서비스를 통해 가져오기와 제출을 수행합니다. Git 서비스는 제출을 검사하고 DynamoDB의 수락된 헤드와 아웃박스를 업데이트합니다. 셀 로컬 복제본은 S3Files 스토리지를 사용하고, 다운스트림 워커는 체크아웃과 동기화를 위해 텍스트를 추출합니다.

CLI: 샌드박스에서 로컬 파일로 작업하기

에이전트는 자체 샌드박스 플랫폼인 SPACE에서 실행됩니다. 각 샌드박스에는 리포지토리를 구체화하고 동기화 상태를 유지하는 작은 CLI가 있습니다. 에이전트가 의미 있는 작업 단위를 완료할 때마다 CLI는 변경 사항을 커밋으로 제출합니다.

에이전트는 일반적으로 프로젝트 파일의 극히 일부만 다루므로, 처음부터 리포지토리 전체를 구체화할 필요가 없습니다. 그래서 CLI는 지연 방식으로 구체화합니다. Git LFS(Large File Storage)를 통해 저장된 대용량 파일과 바이너리 파일은 객체의 실제 크기를 담은 작은 포인터 파일 형태로 도착하므로, 에이전트는 이름과 크기를 보고 다운로드할 가치가 있는지 판단할 수 있습니다. PDF 같은 문서의 경우 에이전트는 파싱된 텍스트를 먼저 읽고, 필요할 때만 원본 파일을 다운로드합니다.

가상 파일 시스템을 사용하는 것도 자연스러운 선택지가 될 수 있습니다. FUSE 기반 구현을 테스트해 보았지만, 모든 요청에 네트워크 왕복이 필요하기 때문에 파일 시스템 작업이 많은 워크로드에서는 로컬 파일보다 약 400배 느렸습니다. 또한 일반적으로 호스트의 특권(privileged) 기능이 필요하므로, 모든 에이전트를 특권 샌드박스에서 실행해야 합니다.

포인터 크기와 파싱 매니페스트에 따라 파싱된 텍스트를 읽을지, 원본을 다운로드할지, 건너뛸지가 결정됩니다.

컨트롤 플레인: 리포지토리 위치 확인 및 액세스 발급

컨트롤 플레인은 각 리포지토리가 저장된 위치를 찾고 해당 리포지토리에 대한 액세스 권한을 발급합니다. 리포지토리가 생성되면 컨트롤 플레인은 이를 스토리지 셀에 할당합니다. 스토리지 셀은 자체 Git 서비스 복제본을 갖춘 격리된 스토리지 단위입니다. 에이전트가 프로젝트 작업을 시작하면 CLI는 컨트롤 플레인에 리포지토리가 저장된 위치를 묻습니다. 컨트롤 플레인은 위치와 Git 서비스용 임시 자격 증명을 반환합니다. 컨트롤 플레인은 리포지토리 데이터를 읽거나 쓰지 않습니다. 리포지토리에 무엇이 들어갈지는 Git 서비스가 결정합니다.

Git 서비스: 데이터 이동 및 업데이트 조율

Git 서비스는 Git 작업을 실행하고 샌드박스와 스토리지 사이에서 리포지토리 콘텐츠와 버전 기록을 전송합니다. Git 자체의 서빙 명령을 얇게 감싼 래퍼로, 클라이언트에 리포지토리 헤드 위치를 알려 주는 ref 탐색, 클론과 가져오기를 위한 upload-pack, 새 객체와 리포지토리 헤드 이동 요청을 받는 receive-pack을 지원합니다. 또한 Git LFS API도 제공합니다. 대용량 파일과 바이너리 파일의 경우 단기 링크를 발급해 샌드박스가 S3에서 직접 해당 파일을 업로드하고 다운로드할 수 있게 합니다.

처음에는 기성 포지 서버로 시작했습니다. 덕분에 필요한 Git 작업에만 집중한 자체 경량 포지를 구축하는 동시에 에이전트 및 사용자 인터페이스의 프로토타입을 만들 수 있었습니다.

이 서비스는 같은 서버의 실행 인스턴스인 여러 복제본으로 구성됩니다. 셀은 공유 스토리지와 복제본 집합, 그리고 이를 지원하는 서비스를 하나로 묶습니다. 따라서 셀 안의 복제본은 모든 리포지토리의 독립적인 사본을 각각 유지하는 대신 같은 공유 파일 시스템에 접근할 수 있습니다.

이 공유 스토리지 덕분에 복제본은 상태 비저장(stateless)으로 유지될 수 있습니다. 셀 안의 어느 복제본이든 해당 셀의 모든 리포지토리를 제공할 수 있으므로, 복제본을 교체하거나 추가할 때 서버 간에 리포지토리 데이터를 옮길 필요가 없습니다. 캐시를 유용하게 유지하기 위해 리포지토리 요청은 보통 같은 복제본으로 전달되지만, 리포지토리가 특정 인스턴스의 가용성에 의존하지는 않습니다.

수락 단계에서는 리포지토리 불변 조건을 검사하여 수락된 커밋이 유효한 리포지토리 상태인지 확인합니다. 모든 리포지토리에 명시적인 계약을 정의하므로, 기계적인 검사를 통해 리포지토리가 지나치게 커지지 않도록 막을 수 있습니다. PDF나 이미지 같은 대용량 파일과 바이너리 파일은 원시 Git blob이 아니라 Git LFS를 통해 저장됩니다. 컴팩션은 최근 기록을 유지하면서 리포지토리 크기를 제한합니다. 제출이 성공하면 CLI는 Git 서비스에 리포지토리 컴팩션이 필요한지 묻습니다. 보존된 커밋 수가 임계값을 초과하면 Git 서비스는 가장 최근 커밋 중 일부만 남깁니다. 이전 기록은 하나의 시작 스냅샷으로 대체되고, 최근 커밋을 다시 적용해 커밋 식별자를 갱신합니다.

컴팩션은 가장 최근 커밋을 유지하고, 이전 기록을 하나의 스냅샷 루트로 접어 넣은 뒤, 최근 커밋을 새 식별자로 다시 적용합니다. 새 헤드의 파일은 그대로 유지됩니다.

간단한 동기화 단계가 동시 작성자를 제어합니다. 이 단계는 가장 최근에 수락된 커밋, 즉 헤드를 추적합니다. 제출을 받으면 해당 제출이 그 헤드를 기반으로 만들어진 경우에만 수락합니다. 그렇지 않으면 구조화된 오래된 기반(stale-base) 응답을 보내고, 경합에서 진 쪽은 새 헤드 위에 변경 사항을 다시 적용한 뒤 다시 제출해야 합니다. 이 설계는 큰 속도 저하 없이, 동시에 작업하는 에이전트가 서로의 작업을 모르는 사이에 덮어쓰지 않도록 보장합니다. 프로덕션 워크플로우에서 오래된 기반으로 거부되는 제출은 2% 미만입니다. 전체적으로 제출 지연 시간의 중앙값은 1.7초 미만이고, p99 지연 시간은 8초 미만입니다.

두 에이전트가 동시에 제출합니다. 하나는 수락되고, 다른 하나는 오래된 기반(stale-base) 응답을 받아 새 헤드 위에 다시 적용한 뒤 다시 제출합니다.

Git 서비스는 수락된 모든 변경 사항도 기록합니다. 수락된 각 쓰기에 대해 아웃박스에 항목을 추가합니다. 아웃박스는 리포지토리, 새 커밋, 변경한 사용자, 변경된 경로를 나열하는 로그입니다. 파서 같은 다운스트림 서비스는 이 로그를 읽고 새 작업을 찾습니다.

이러한 요소가 모여, 에이전트에게 필요 없는 부분은 덜어낸 리포지토리 관리 계층을 이룹니다. 사람이 각 변경 사항을 검토하지 않으므로 pull request는 필요하지 않습니다. 대신 Git 서비스가 제출 시점에 계약을 검사하고, 경합에서 진 에이전트는 자신의 변경 사항을 다시 적용하며, 커밋 기록에는 누가 무엇을 변경했는지가 남습니다. 사람 운영자가 리포지토리 증가를 관리하는 대신, 결정론적 검사가 각 리포지토리를 계약 범위 안에 유지합니다.

스토리지: 작업을 영속적으로 보존하기

스토리지 계층은 샌드박스 및 Git 서비스 복제본과 독립적으로 리포지토리 데이터를 영속적으로 저장합니다. 리포지토리는 AWS S3Files에 저장됩니다. S3Files는 S3 기반 스토리지를 파일 시스템 인터페이스로 제공하므로, 표준 Git을 수정 없이 그대로 사용할 수 있습니다. 각 리포지토리는 공유 파일 시스템에 있는 bare Git 리포지토리로, 작업용 체크아웃이 아니라 리포지토리의 객체와 참조를 담고 있습니다.

스토리지와 컴퓨팅을 분리하면 샌드박스를 일시적인 상태로 유지할 수 있습니다. 각 에이전트는 자체 샌드박스를 받으며, 여러 에이전트가 머신을 공유하지 않고도 같은 프로젝트에서 작업할 수 있습니다. 샌드박스가 비정상 종료되더라도 위험에 처하는 것은 아직 제출되지 않은 작업뿐입니다.

대신 각 프로젝트에 모든 에이전트가 공유하는 장기 실행 샌드박스를 제공해, 프로젝트 파일이 해당 머신의 디스크에 저장되도록 할 수도 있습니다. 하지만 이렇게 하면 에이전트의 컴퓨팅이 해당 머신이 실행되는 위치에 묶이고, 가장 바쁜 시기에 맞춰 머신 사양을 정해야 합니다. 또한 머신이 몇 달 동안 정상 상태를 유지해야 하며, 백업과 복구도 프로젝트 스토리지 시스템의 일부로 관리해야 합니다.

프로젝트의 에이전트들이 공유하는 하나의 장기 실행 샌드박스와, 상태 비저장 Git 서비스 복제본을 통해 영속적인 공유 스토리지에 접근하는 단기 에이전트별 샌드박스 비교.

다운스트림 처리: 에이전트가 읽을 수 있는 문서 만들기

주요 다운스트림 구성 요소는 새로 추가되거나 업데이트된 문서에서 텍스트와 메타데이터를 추출해 에이전트가 프로젝트 파일을 쉽게 읽을 수 있게 하는 파서 파이프라인입니다. 파서 워커는 변경 스트림 큐에서 업데이트를 가져와 커밋 메타데이터로 변경된 파일을 파악하고, 콘텐츠 해시를 사용해 원시 바이트를 가져옵니다. 그런 다음 문서를 파싱해 추출된 텍스트와 문서 메타데이터를 생성합니다.

파싱된 결과물은 리포지토리 외부에, 원본 문서의 해시를 키로 하는 스토어에 보관됩니다. 이러한 아티팩트는 리포지토리에 다시 커밋되지 않습니다. 그렇게 하면 기록이 불필요하게 오염되고 동시 쓰기 횟수가 늘어나기 때문입니다. 대신 파싱된 결과물은 체크아웃과 동기화를 통해 샌드박스에 전달됩니다. 각 체크아웃에는 CLI가 아티팩트 구체화에 사용하는 예약된 디렉터리가 있습니다. 파싱은 비동기로 이루어지므로 백그라운드 파서 워커가 체크아웃을 차단하지 않습니다.

에이전트가 워크스페이스를 사용하는 방식

에이전트는 표준 파일 시스템 도구를 사용해 샌드박스에 로드된 리포지토리 버전을 탐색합니다. 고정된 '검색 후 읽기' 파이프라인을 따르는 대신, 폴더 목록을 확인하고, grep으로 파일 내용을 검색하고, 관련 구절을 읽은 뒤, 찾은 내용을 바탕으로 다음에 살펴볼 곳을 결정합니다.

Computer와 마찬가지로 Search도 표준 파일 도구를 통해 같은 접근 방식을 사용합니다. 별도의 탐색 에이전트가 더 제한된 예산 안에서 프로젝트를 검색하고 관련 파일 경로와 근거가 되는 발췌문을 반환합니다. 그런 다음 답변 모델이 선택된 파일을 읽고 답변을 생성합니다. 기존 프로젝트 샌드박스를 재사용하면 설정 시간이 줄어듭니다. 웜 체크아웃에 연결하는 데는 약 2.9초가 걸리는 반면, 콜드 스타트에는 약 8초가 걸립니다.

Computer 샌드박스, Ask의 탐색 서브에이전트, 백그라운드 지식 작성자, 파서 워커가 모두 같은 리포지토리와 아웃박스에서 읽고 씁니다.

파싱된 텍스트를 활용하면 PDF나 Office 파일 같은 큰 문서도 같은 도구로 검색할 수 있습니다. 에이전트는 먼저 추출된 텍스트가 있는 문서를 보여 주는 목록인 매니페스트를 확인한 다음, 그 텍스트를 일반 파일처럼 검색하고 읽습니다. 텍스트만으로 충분하지 않을 때, 예를 들어 그림, 레이아웃, 포함된 데이터를 살펴봐야 할 때만 원본 문서를 다운로드합니다.

같은 코퍼스에 대한 단일 문서 금융 질문 150개를 대상으로 이 접근 방식을 인덱스 기반 시맨틱 검색과 비교 벤치마크했습니다. 탐색 에이전트는 올바른 문서 검색에서 0.867점, 정확한 답변에서 0.627점을 기록했으며, 시맨틱 검색은 각각 약 0.39점과 0.37점이었습니다. 답변은 약 1.7배, 검색은 약 2배 더 우수했고, 지연 시간은 약 25% 더 짧았습니다. 탐색 에이전트는 주로 추출된 텍스트로 작업하며 원본 PDF는 거의 다운로드하지 않습니다.

내부 벤치마크에서의 탐색 방식과 시맨틱 파일 검색 비교.

하나의 공유 인프라

Computer의 영구 메모리도 동일한 파일 시스템 인프라를 사용합니다. Computer의 지식 위키인 Perplexity Brain은 자체 기록과 정책을 갖춘 지식 리포지토리에 메모리를 저장합니다. 지식을 프로젝트 파일과 분리해 두면 백그라운드 메모리 업데이트가 해당 파일의 편집과 경합하지 않습니다. 둘 다 필요한 세션은 두 리포지토리를 모두 로드합니다.

백그라운드 에이전트는 기존 지식을 읽고, 새 정보를 반영한 뒤, 업데이트된 페이지를 커밋으로 저장합니다. 다른 에이전트는 일반 파일 도구로 이 페이지를 읽으며, 사람도 이를 편집할 수 있습니다. 작성자는 각 독자와 직접 조율하는 대신 리포지토리를 통해 작업을 공유합니다. 커밋 기록에는 누가 무엇을 변경했는지가 남고, 아웃박스는 수락된 업데이트를 다운스트림 서비스에 알립니다. 사람과 에이전트는 지식이 어떻게 발전해 왔는지에 대한 기록을 바탕으로 서로의 작업을 이어갈 수 있습니다.

결론

사람과 에이전트가 프로젝트를 공유하면 워크스페이스는 단순히 파일을 저장하는 것 이상의 역할을 합니다. 기여를 통합하고 무엇이 바뀌었는지 추적하는 공통의 장소가 됩니다. Git은 이미 그 기반의 상당 부분을 제공합니다. 여기에 조율 기능과 자동 검사를 더해, 사람이 각 업데이트를 승인하지 않아도 에이전트가 작업을 제출할 수 있도록 했습니다. 그 결과 핵심만 남긴 포지가 탄생했습니다. 에이전트 간 협업과 여러 기여자가 참여하는 소프트웨어 개발은 근본적으로 같은 문제이기 때문입니다.

에이전트는 독립적인 임시 샌드박스에서 작업하면서 영속적인 리포지토리에 기여합니다. 리포지토리 한도는 증가를 관리 가능한 수준으로 유지하고, 명시적 다운로드를 통해 에이전트는 가져올 대상을 직접 선택하며, 일반 파일 덕분에 익숙한 도구로 탐색할 수 있습니다. 백그라운드 업데이트, 대화형 편집, 예약된 작업은 직접 조율하지 않고도 서로의 작업을 이어받아 발전시킬 수 있습니다.

프로젝트 안에서 자율적으로 작업하는 에이전트가 늘어날수록, 이들이 공유하는 워크스페이스는 안정적이어야 하며 사람이 이해할 수 있어야 합니다. 목표는 간단합니다. 각 변경 사항은 그것을 만든 에이전트보다 오래 유지되고, 다음 협업자가 이용할 수 있어야 하며, 무엇이 바뀌었는지 명확한 기록을 남겨야 합니다.

인용

BibTeX
@online{perplexity2026BuildingSharedWorkspacesForPeopleAndAgents,
  author = {{Perplexity} and Se Won Jang and Peter Hart and Kartik Kapur and Kevin Lau and Sidhant Rajadnya and Ishbir Singh and Thomas Wang and Jarrod Bryan and Claire Beamer and Jeremy Yang},
  title = {{Building shared workspaces for people and agents}},
  date = {2026-10-12},
  url = {https://www.perplexity.ai/hub/blog/building-shared-workspaces-for-people-and-agents}
}