A2A Memory DB and Sync Contract
이 문서는 A2A 메모리 기능을 실제 구현으로 옮기기 위한 DB schema, sync cursor, privacy/delete 정책을 정의한다.
현재 구현 상태는 세션/voice_context 중심이다. 이 문서는 그 위에 장기 메모리를 얹기 위한 설계 기준이며, 아직 제품 DB 구현 완료를 의미하지 않는다.
1. 설계 목표와 제약
목표:
- 최소 단위는 user/assistant pair로 관리한다.
- session은 최대 8 pair를 묶지만, 항상 8 pair가 찬다고 가정하지 않는다.
- 하루 단기, 일주일 중기, 장기 profile memory를 분리한다.
- Planner는 memory를 참고하지만 현재 발화를 우선한다.
- 장기 memory는 candidate와 confirmed state를 분리한다.
제약:
- 온디바이스는 로컬 세션과 최근 turn cache를 관리한다.
- Cloud는 요약, 장기 후보 추출, profile 확정을 관리한다.
- 민감 정보는 저장 전 분류와 삭제 정책이 필요하다.
- 네트워크 단절 시 on-device local cursor가 재동기화 기준이 된다.
2. 아키텍처 개요
On-device local store
conversation_pairs
conversation_sessions
active_workflow_cache
sync_cursor
Cloud Memory DB
short_term_summaries
mid_term_summaries
long_term_profile_candidates
long_term_profile_items
memory_audit_log
Planner Input
recognized_text
voice_context.recent_turns
memory_snapshot.session_summary
memory_snapshot.short_term
memory_snapshot.mid_term
memory_snapshot.profile_hints
device_context
3. 컴포넌트 경계와 책임
| 컴포넌트 | 책임 | 하지 말아야 할 것 |
|---|---|---|
| On-device | 최근 pair/session 보관, sync cursor 관리, Cloud 요청 시 compact snapshot 전달 | 장기 profile을 독단적으로 확정 |
| Cloud Runtime | memory snapshot을 Planner 입력으로 조립, memory update를 응답에 포함 | 현재 발화보다 memory를 우선 적용 |
| Memory Summarizer | session/day/week summary 생성 | 원문 전체를 무기한 보존 |
| Profile Extractor | 장기 후보 생성, confidence와 source 근거 보존 | 단발성 발화를 즉시 confirmed profile로 승격 |
| Privacy/Retention Worker | 삭제, 만료, 민감정보 마스킹 | audit 없이 silent delete |
4. 데이터 모델/저장소 전략
4.1 conversation_pairs
최소 대화 원자다.
| 필드 | 타입 | 설명 |
|---|---|---|
pair_id |
string pk | user/assistant pair id |
user_id |
string | 사용자 또는 household scope |
device_id |
string | 기기 id |
session_id |
string | session id |
turn_index |
integer | session 내 순서 |
user_text |
text | STT normalized user utterance |
assistant_text |
text | assistant response summary 또는 full text |
route_family |
string | DEF/ODL/FRG/SCH 등 |
planner_summary |
json | selected route, steps, confidence |
created_at |
timestamp | 생성 시각 |
retention_until |
timestamp | 원문 보존 만료 시각 |
sensitive_flags |
json | 민감정보 분류 결과 |
4.2 conversation_sessions
최대 8 pair를 묶는 단기 흐름 단위다.
| 필드 | 타입 | 설명 |
|---|---|---|
session_id |
string pk | session id |
user_id |
string | 사용자 scope |
device_id |
string | 기기 scope |
started_at |
timestamp | 시작 시각 |
ended_at |
timestamp nullable | 종료 시각 |
pair_count |
integer | 포함 pair 수 |
active_workflow_id |
string nullable | 진행 중 workflow |
session_summary |
text | compact summary |
last_route |
string | 마지막 route family |
status |
string | active/completed/expired |
4.3 short_term_summaries
하루 단위 최근 맥락이다.
| 필드 | 타입 | 설명 |
|---|---|---|
summary_id |
string pk | summary id |
user_id |
string | 사용자 scope |
device_id |
string | 기기 scope |
date_key |
string | YYYY-MM-DD |
summary_text |
text | 하루 단기 summary |
open_tasks |
json | unresolved tasks |
recent_preferences |
json | 당일 선호/반복 표현 |
source_session_ids |
json | 근거 session id 목록 |
created_at |
timestamp | 생성 시각 |
expires_at |
timestamp | 만료 시각 |
4.4 mid_term_summaries
일주일 단위 반복 주제와 최근 패턴이다.
| 필드 | 타입 | 설명 |
|---|---|---|
summary_id |
string pk | summary id |
user_id |
string | 사용자 scope |
week_key |
string | ISO week |
summary_text |
text | 주간 summary |
repeated_topics |
json | 반복 주제 |
device_usage_patterns |
json | 기기 사용 패턴 |
unresolved_items |
json | 미해결 항목 |
source_summary_ids |
json | short-term 근거 |
expires_at |
timestamp | 만료 시각 |
4.5 long_term_profile_candidates
장기 profile 후보이며, 바로 확정하지 않는다.
| 필드 | 타입 | 설명 |
|---|---|---|
candidate_id |
string pk | 후보 id |
user_id |
string | 사용자 scope |
category |
string | preference/constraint/habit/device_pattern |
claim |
text | 후보 내용 |
confidence |
float | 신뢰도 |
source_refs |
json | pair/session/summary 근거 |
status |
string | pending/confirmed/rejected/expired |
created_at |
timestamp | 생성 시각 |
reviewed_at |
timestamp nullable | 검토 시각 |
4.6 long_term_profile_items
확정된 장기 memory다.
| 필드 | 타입 | 설명 |
|---|---|---|
profile_item_id |
string pk | profile item id |
user_id |
string | 사용자 scope |
category |
string | preference/constraint/habit/device_pattern |
value |
json | 확정 memory 값 |
confidence |
float | 확정 confidence |
source_candidate_ids |
json | 후보 근거 |
last_confirmed_at |
timestamp | 마지막 확인 시각 |
expires_at |
timestamp nullable | 만료 시각 |
delete_policy |
string | user_delete/retention_expire/manual_review |
4.7 memory_sync_cursors
온디바이스와 Cloud 동기화 위치다.
| 필드 | 타입 | 설명 |
|---|---|---|
cursor_id |
string pk | cursor id |
user_id |
string | 사용자 scope |
device_id |
string | 기기 id |
last_pair_seq |
integer | 마지막 업로드 pair sequence |
last_cloud_update_seq |
integer | 마지막 다운로드 memory update sequence |
last_sync_at |
timestamp | 마지막 sync 시각 |
sync_status |
string | ok/pending/conflict/error |
conflict_state |
json nullable | 충돌 상세 |
4.8 memory_audit_log
memory 변경 이력이다.
| 필드 | 타입 | 설명 |
|---|---|---|
audit_id |
string pk | audit id |
user_id |
string | 사용자 scope |
event_type |
string | create/update/delete/export/mask |
target_table |
string | 대상 table |
target_id |
string | 대상 row id |
actor |
string | device/cloud/user/admin |
reason |
string | 변경 사유 |
created_at |
timestamp | 발생 시각 |
5. API 계약 및 시퀀스
5.1 On-device -> Cloud request
{
"recognized_text": "안방으로 가서 청정해줘",
"voice_context": {
"session_id": "session-123",
"recent_turns": [],
"history_hint_strength": "weak",
"active_workflow_id": ""
},
"memory_snapshot": {
"session_summary": "오늘 사용자는 청정 관련 요청을 여러 번 했다.",
"short_term": [],
"mid_term": [],
"profile_hints": []
},
"memory_sync": {
"device_cursor": "pair_seq_42",
"last_cloud_update_seq": 18
}
}
5.2 Cloud -> On-device response
{
"session_state": {
"session_id": "session-123",
"workflow_context": {}
},
"memory_update": {
"cloud_update_seq": 19,
"session_summary_delta": "안방 청정 요청이 진행 중이다.",
"profile_candidates": [
{
"candidate_id": "cand-001",
"category": "device_pattern",
"claim": "사용자는 안방 청정을 자주 요청한다.",
"confidence": 0.62,
"status": "pending"
}
]
}
}
6. Planner 사용 원칙
Planner는 memory를 아래 우선순위로 사용한다.
- 현재
recognized_text. - active workflow / pending follow-up.
recent_turns.session_summary.- short-term / mid-term summary.
- confirmed long-term profile hints.
금지:
- memory preference가 현재 발화의 명확한 route family를 덮어쓰면 안 된다.
- long-term candidate를 confirmed profile처럼 쓰면 안 된다.
- 민감 정보나 삭제 요청된 memory를 prompt에 넣으면 안 된다.
7. 비기능 요구 대응
| 요구 | 정책 |
|---|---|
| 개인정보 최소화 | pair 원문은 짧은 retention, summary/profile은 목적 제한 |
| 삭제권 | user/device 단위 delete request가 memory_audit_log에 남아야 함 |
| 네트워크 단절 | on-device cursor 기준으로 pending upload 유지 |
| 충돌 처리 | Cloud update seq와 device cursor가 불일치하면 sync_status=conflict |
| 관측성 | memory update, deletion, masking은 audit event로 기록 |
8. 리스크와 의사결정 로그
| 리스크 | 대응 |
|---|---|
| memory가 router 판단을 오염 | 현재 발화 우선 원칙과 prompt guard 유지 |
| 장기 profile 오검출 | candidate/confirmed 분리와 confidence threshold |
| 민감 정보 저장 | sensitive flag, masking, retention policy |
| 온디바이스/Cloud 불일치 | sync cursor, cloud update sequence, conflict state |
| 과도한 DB 복잡도 | v1은 pair/session/summary/profile/cursor/audit 최소 테이블로 시작 |
구현 전 결정 필요
| 질문 | 제안 |
|---|---|
| user scope는 개인인가 household인가 | v1은 user_id와 device_id 둘 다 저장하고 권한 정책은 제품 정책에서 결정 |
| long-term confirmation은 자동인가 수동인가 | v1은 confidence 기반 pending 생성, confirmed는 명시 policy 후 승격 |
| pair 원문 retention은 얼마인가 | v1은 7~30일 후보, 제품 개인정보 정책에 맞춰 확정 |
| LangGraph/LangMem을 쓸 경우 schema가 바뀌는가 | 내부 orchestration은 바뀔 수 있지만 제품 DB schema는 유지 |