고객별 메모리
대화는 어떻게 고객의 지속적 사실로 전환되는지 — 유도, 중복 제거, 중요성, 그리고 어떤 정보가 회상되는지
문서에서 정보를 검색합니다. 기억의 나머지 절반은 에이전트가 이 특정 고객에 대해 알고 있는 것으로, 세션, 채널 및 월을 걸쳐 유지됩니다.
기억은 기록된 것이 아니라 유도된 것입니다
대화 턴이 완료됩니다. 아직 아무것도 저장되지 않습니다 — 여기서는 트랜스크립트가 기억의 단위가 아닙니다.
모델은 에이전트의 응답이 아닌 고객이 말한 내용에서 정규화된 항목을 추출합니다. 이는 사실("4대의 차량으로 운송대를 운영함")과 주제로, 각 항목에는 중요도 점수가 부여됩니다. 이를 통해 가벼운 언급과 강력한 제약 조건이 동등하게 평가되지 않습니다.
턴의 항목들은 개별이 아닌 한 번의 배치로 임베딩됩니다. 임베딩 엔드포인트는 동시성 1로 실행되므로, 단일 호출을 순차적으로 수행하면 다른 사용자의 쿼리가 그 뒤에 대기하게 됩니다.
각 항목은 이미 존재하는 내용과 비교됩니다 — 주제는 이름으로, 사실은 임계값 내 벡터 근접도로 비교합니다. 이것이 없으면 다섯 번의 대화에 걸쳐 언급된 동일한 사실이 다섯 개의 기억으로 축적되어 다른 모든 내용을 밀어냅니다.
일치하는 경우 새 항목을 추가하는 대신 기존 행을 업데이트합니다. 콘텐츠와 임베딩을 새로 고치고, 중요도는 두 값 중 더 높은 것으로 올립니다. 또한 가장 최근 세션으로 다시 연결하여 최근성이 실시간 관계를 추적하도록 합니다. 일치하는 항목이 없으면 새 항목이 생성되어 암호화되고 공간에 스코프가 지정됩니다.
다음 턴에서 기억은 문서가 사용하는 것보다 더 엄격한 임계값 하에서 유사도에 의해 검색됩니다. 약간 관련 있는 문서 구절은 단순히 도움이 되지 않지만, 누군가에 대한 약간 관련 있는 "사실"은 오히려 잘못된 것입니다.
턴이 끝난 후 실행됩니다. 중복 제거 단계는 관계가 길어질수록 검색 성능이 저하되는 것을 방지합니다.
전체 트랜스크립트를 저장하고 다시 검색하는 것은 명백한 접근 방식이지만 나쁜 접근 방식입니다. 트랜스크립트는 대부분 여백이며, 동일한 사실이 스무 가지 다른 표현으로 나타나고, 관계가 길어질수록 검색 성능이 저하됩니다 — 이는 정확히 역방향입니다.
대신, 턴이 끝난 후 플랫폼은 모델에게 고객이 말한 내용에서 정규화된 항목을 추출하도록 요청합니다. 두 가지 종류가 있습니다:
- 사실 — 고객에 대한 영구적인 진술. "4대의 차량으로 운송대를 운영함." "갱신은 5월입니다." "WhatsApp을 선호함."
- 주제 — 관계가 계속 돌아오는 반복되는 주제들, 각 항목에는 주제 이름이 있습니다.
각 항목에는 중요도 점수가 포함되어 있으므로, 공간이 제한될 때 가벼운 언급과 강력한 제약 조건이 동등하게 처리되지 않습니다.
대화의 고객 측만
추출은 고객이 말한 내용을 읽습니다. 에이전트 자신의 응답은 고객에 대한 사실이 될 수 없으며, 이는 들리는 것보다 더 중요합니다.
한 번 추측하는 에이전트 — 이 고객의 회사가 무엇을 만드는지에 대한 문장, 하나의 응답에서 추측으로 제공됨 — 는 그렇지 않으면 그 추측이 영구적인 사실로 추출되어 이후 모든 턴에서 전제로 다시 읽힙니다. 그 순간부터 그것은 더 이상 추측이 아닙니다; 그것은 에이전트가 아는 것입니다. 고객은 원인을 보지 못한 채 결과를 봅니다: 잘못된 비즈니스에 대한 답변이 계속되며, 왜 그런지 설명하는 화면상의 내용은 없습니다.
두 가지 방어 기제가 있습니다. 첫 번째는 요청이고 두 번째는 확인입니다. 추출은 고객 측만 표시됩니다. 그런 다음, 아무것도 쓰기 전에 소프트웨어가 "사실"의 문구가 고객이 실제로 말한 내용에 근거가 있는지 확인합니다 — 이 확인은 모델 외부에서, 모든 항목, 모든 턴에서 실행됩니다.
중복 제거가 전체 게임입니다
고객이 다섯 번의 대화에 걸쳐 동일한 것을 언급합니다. 중복 제거가 없으면 거의 동일한 기억이 다섯 개 축적되고, 검색 시 다른 모든 내용을 밀어내며, 에이전트가 자신을 반복하기 시작합니다.
항목이 작성되기 전에 플랫폼은 대신 병합해야 할 항목을 찾습니다:
- 주제는 주제 이름으로 일치합니다 — 주제는 이미 정규화된 라벨이므로 정확히 일치해야 합니다.
- 사실은 벡터 근접도로 일치합니다 — 같은 유형의 기존 기억 중 가장 가까운 것이, 코사인 거리가 구성된 임계값 내에 있을 때만 중복으로 간주됩니다.
일치하는 경우 기존 행은 복제되지 않고 업데이트됩니다 — 콘텐츠와 임베딩이 새로 고쳐지고, 중요도는 두 값 중 더 높은 것으로 올립니다. 나중에 더 중요한 사실로 판명되면 다른 가중치로 두 번 저장되는 대신 승격됩니다. 또한 항목은 이를 트리거한 가장 최근 세션과 메시지로 다시 연결되므로, 최근성이 실시간 관계를 반영합니다.
배치 임베딩
단일 턴은 종종 여러 항목을 생성합니다. 이를 하나씩 임베딩하면 임베딩 엔드포인트에 여러 순차적인 왕복이 발생합니다 — 그리고 해당 엔드포인트는 동시성 1로 실행되므로, 이 순차적 처리가 다른 사용자의 쿼리 임베딩을 차단합니다.
따라서 턴의 항목들은 한 번의 배치로 임베딩되고 벡터가 쓰기 경로로 전달됩니다. 세 개의 항목이 있는 턴에서 측정한 결과: 순차적 117ms 대 배치 46ms — 고객과 답변 사이에 위치한 경로에서 2.4배 차이입니다.
검색
턴 시작 시, 기억은 해당 고객의 벡터 유사도에 따라 자체 거리 임계값 하에서 검색됩니다 — 문서 검색에 사용되는 것보다 더 엄격한 임계값입니다. 왜냐하면 약간 관련 있는 문서 구절은 단순히 도움이 되지 않지만, 누군가에 대한 약간 관련 있는 "사실"은 오히려 잘못된 것이기 때문입니다.
검색된 기억은 검색된 문서 청크와 함께 프롬프트에 추가됩니다. 에이전트는 당신의 문서에서 답변하며, 이미 알고 있는 사람에게 addressing합니다.
망각
위에서 설명한 모든 것이 축적됩니다. 항목은 병합되고, 중요도는 상승하며, 아무것도 제거되지 않습니다 — 이는 기억에 대한 올바른 기본값이지만 잘못된 기억에 대해서는 잘못된 것입니다.
잘못된 기억은 고객 측에서 거의 보이지 않습니다. 그들은 저장소를 볼 수 없으므로, 그 안에 잘못된 사실이 있는지 볼 수 없습니다. 그들이 보는 것은 어떤 것을 당연시하는 에이전트입니다. 대화에서 잘못된 항목을 식별할 수 있는 유일한 사람은 기억 실수를 당한 사람이며, 이를 수행할 유일한 방법은 그렇게 말하는 것입니다.
따라서 그렇게 말하는 것이 작동합니다. "우리는 그것을 만들지 않습니다." "그것은 제 회사가 아닙니다." "X에 대해 알고 있는 것을 잊으세요." 에이전트는 이를 저장된 내용과 일치시켜 삭제합니다. 설정 페이지를 열지 않고도 모든 언어로 가능합니다.
문장이 기억된 것을 부정하는지 판단하는 것은 진정으로 언어 문제입니다 — *"우리는 그것을 하지 않습니다"*는 수정일 수도 있고 비즈니스에 대한 단순한 진술일 수도 있습니다 — 따라서 모델이 그 판단을 내립니다. 모델이 결정할 수 없는 것은 허용된 피해 규모이며, 그 한계는 코드에 있습니다:
- 요청당 최대 몇 개 항목 — 하나의 오독이 제한되도록 합니다.
- 실제로 일치할 만큼 충분히 가까운 항목만. 유사도 검색은 항상 가장 가까운 결과를 반환합니다. 관련 없는 계정을 보유하고 있는 계정에서도 마찬가지입니다 — 거리 하한이 없으면, 관련 없는 계정에서 "X에 대해 알고 있는 것을 잊으세요"는 우연히 1위를 차지한whatever을 삭제합니다.
- 삭제된 내용은 고객에게 다시 읽힙니다. 조용히 삭제하는 것은 잘못 기억하는 것과 마찬가지로 나쁘며, 이번에는 그것이 옳았는지 확인할 수 있는 유일한 사람이 이미 대화에 있습니다.
삭제는 영구적입니다 — 고객이 잊혀지기를 요청한 무엇인가의 사본을 보관하는 아카이브는 없으며, 이는 해당 요청의 유일한 의미 있는 해석입니다.
스코핑 및 암호화
기억은 최종 사용자의 공간에 스코프가 지정되며, 모든 다른 테넌트 소유 레코드처럼 데이터베이스 수준에서 강제되고, 공간별로 암호화되어 저장됩니다. 한 고객의 내력이 다른 사람의 대화에 나타나지 않으며, 저장된 텍스트는 데이터베이스만으로는 읽을 수 없습니다.
나중에 붙여넣기 어려운 이유
고객별 기억은 데이터 모델의 형태를 변경합니다: 각 최종 사용자의 ID, 이를 스코프 지정할 공간, 해당 스코프에 키가 지정된 암호화, 그리고 턴 루프 내의 유도 단계. 단일 공유 어시스턴트로 시작하는 시스템은 어딘가에 CRM 필드를 두고 그것을 기억이라고 부르는 경향이 있습니다 — 이것이 기능 목록이 아닌 대화에서 차이가 나타나는 이유입니다.
이것에 대한 비즈니스 논리는 메커니즘이 아닌 Per-customer memory에 있습니다.