# 쓰는 법 — 사람은 뭐라고 말하고, 에이전트는 무엇을 하나 `https://api.affinitybubble.com/llms/scenarios.txt` 이 문서는 **유스케이스별 시나리오**다. 각 시나리오마다 "사람이 에이전트에게 하는 말" → "에이전트가 하는 일" → "나오는 것" 순으로 적었다. - 사람이라면: 자기 상황에 맞는 것을 찾아 **말 부분을 그대로 복사해서** 에이전트에게 주면 된다. - 에이전트라면: 사람 말이 어느 시나리오인지 맞춰 보고, 해당 호출 사슬을 따라가면 된다. 요청 형식의 정확한 계약은 [bubbles.txt](https://api.affinitybubble.com/llms/bubbles.txt) · [personas.txt](https://api.affinitybubble.com/llms/personas.txt) 에 있다. 결과를 리포트 문서로 낼 때의 규칙은 [design.md](https://api.affinitybubble.com/design.md). --- ## 0. 시작 — 키가 아직 없다면 **사람이 하는 말** > affinitybubble API 를 쓰고 싶어. 키부터 받아줘. **에이전트가 하는 일** `https://api.affinitybubble.com/llms.txt` 를 읽고 device flow(RFC 8628)로 키를 요청한다. 화면에 뜬 코드를 사람이 브라우저에서 승인하면 에이전트가 키를 직접 받는다. 사람이 키 문자열을 복사해 붙여 넣을 일은 없다. 키는 `~/.affinitybubble/credentials` 같은 **별도 파일**(`chmod 600`)에 두고, 이 문서들을 재사용 가능한 스킬로 저장해 둔다. 다음부터는 문서를 다시 읽지 않아도 된다. 신규 계정에는 크레딧 500이 자동으로 붙는다. 먼저 감을 보고 싶으면 키 없이 도는 `/v1/demo`(고정 샘플 전용)로 모양만 확인할 수 있다. --- ## 1. 설문 자유응답을 의견지도로 가장 흔한 경우. 주관식 답변 수백~수천 건이 있고, 무슨 얘기가 오가는지 알고 싶다. **사람이 하는 말** > `survey.csv` 의 '개선 의견' 열을 affinitybubble 로 분석해서 의견지도로 그려줘. > 라벨은 짧게, 리포트도 같이 뽑아줘. **에이전트가 하는 일** 1. CSV에서 해당 열을 문자열 배열로 뽑는다(빈 값·무응답 제거). 2. `POST /v1/bubbles/sync` — `texts`, `title`, `params.labelOption`, `report: true`. 3. 응답의 `data`(라벨·메타라벨·`embedding2D`·`sentimentScore`)를 voronoi-bubble 로 렌더. 4. 리포트 문서로 낼 거면 design.md 규칙대로 한 장을 만든다. **나오는 것** 의미가 가까운 답변끼리 뭉친 지도 + 군집 이름 + 군집별 비중 + 인사이트 리포트(markdown). **알아 둘 것** - `texts`는 1~5000개. 동기 호출은 1000건 기준 보통 5~15초. - 같은 답변이 여러 번 나오면 그 자체가 빈도다. 이미 집계된 데이터라면 `weights`로 넘긴다 (댓글 수 같은 치우친 값은 `1 + Math.sqrt(n)` 처럼 압축해서). - 리포트 톤은 `params.reportPreset` — `summary`·`executive`·`story`·`briefing`·`custom`. --- ## 2. 리뷰에서 "무엇을 따지나"와 "만족하나"를 갈라 보기 의견지도는 속성과 감정을 뭉쳐 "주제"를 준다. 그걸 **속성 × 그 속성에 대한 감성**으로 나눠 보는 게 속성분석(ABSA)이다. 경쟁 비교·개선 우선순위에 쓴다. **사람이 하는 말** > 앱스토어 리뷰 300건이야. 사람들이 뭘 따지는지, 각각에 만족하는지 불만인지 갈라서 보여줘. > 별점이랑 교차해서 어떤 항목이 낮은 별점을 끌고 가는지도. **에이전트가 하는 일** 1. `POST /v1/bubbles/sync` 로 버블을 먼저 만든다. 2. `POST /v1/bubbles/:id/aspects` — 속성 집합과 속성별 감성을 받는다. 3. 별점 같은 메타 열은 입력 순서 id로 **정확히** 맞춰 교차한다(라벨 집계 근사가 아니다). 4. 한 속성 안이 궁금하면 `POST /v1/bubbles/:id/aspects/:aspectId/drilldown`. **나오는 것** 속성별 언급률·순감성 막대, 속성 × 별점(또는 앱·기간·세그먼트) 교차표, 속성 안의 갈래. **알아 둘 것** 3번이 되려면 **원본 `texts[]` 를 입력 순서 그대로 본문에 동봉**해야 한다. 빠뜨리면 행 점수가 라벨 단위 집계(근사)라 교차분석에 쓸 수 없다. 계약은 [aspects.txt](https://api.affinitybubble.com/llms/aspects.txt) 에 있다. --- ## 3. 인터뷰·VOC 응답자를 퍼소나로 1인 1리뷰인 얕은 데이터로는 퍼소나가 착시다. **한 사람이 여러 번 말한** 데이터여야 한다 (사용자당 발화 5건 미만이면 경고가 붙는다). **사람이 하는 말** > 인터뷰 녹취 12명치야. 행동 변수 뽑아서 퍼소나로 묶어줘. 중간에 코드북은 내가 한번 볼게. **에이전트가 하는 일** 1. `POST /v1/personas` — `rows: [{ userId, utterance, topic? }]`. - 끝까지 한 번에: `autorun: true` - 중간에 세울 거면: `autorun` 없이 저장 → `POST /v1/personas/:id/run` 에 `until: "variables"` 2. 사람이 코드북을 확인하고 고친다 → `PATCH /v1/personas/:id/variables` 3. `POST /v1/personas/:id/run` 에 `from` 을 줘서 나머지 단계(채점 → 축 → 군집 → 카드)를 잇는다. 4. 필요하면 `PATCH /v1/personas/:id/axes` · `PATCH /v1/personas/:id/clusters` 로 축·군집을 손본다. **나오는 것** 행동 변수 목록, 사용자별 점수, 2축 인식 맵, 군집별 퍼소나 카드. `POST /v1/personas/:id/cards/images` 로 카드 이미지, `POST /v1/personas/:id/ask` 로 퍼소나와 대화, `GET /v1/personas/:id/evidence/:utteranceId` 로 "이 말이 어느 근거에서 나왔나" 역참조. **알아 둘 것** 컬럼 이름이 제각각이면 `POST /v1/personas/suggest-mapping` 에 헤더를 주고 추천을 받는다. 사용자 수가 적으면 축 경로가 `synthetic`으로 권장된다(EFA 조건 미달). --- ## 4. 버블 하나 안에서 응답자를 나눠 보기 전체 퍼소나 프로젝트까지는 과하고, **이미 만든 버블 안에서** 응답자 유형만 보고 싶을 때. **사람이 하는 말** > 방금 만든 버블에서 응답자를 유형별로 나눠줘. **에이전트가 하는 일** `POST /v1/bubbles/:id/personas` (세분화 모듈). 행↔텍스트는 입력 순서 id로 정확히 매핑된다. --- ## 5. 결과가 마음에 안 들 때 **사람이 하는 말** > 군집 이름이 너무 밋밋해. 다시 지어줘. > / 이 두 덩어리는 같은 얘기 같은데 합쳐줘. **에이전트가 하는 일** | 사람 말 | 호출 | | --- | --- | | "이름만 다시" | `POST /v1/bubbles/:id/relabel` | | "묶음을 다시" | `POST /v1/bubbles/:id/regroup` | | "전체적으로 다듬어" | `POST /v1/bubbles/:id/refine` | **알아 둘 것** 이미 명확하고 좋은 라벨은 건드리지 않는다. 뉘앙스가 살아 있는 이름을 밋밋한 표준어로 바꾸는 것은 개악이다. 재명명은 명백히 어색하거나 비표준일 때만. --- ## 6. 결과를 팀에 보여주기 **사람이 하는 말** > 이거 팀에 공유할 링크 만들어줘. **에이전트가 하는 일** `POST /v1/bubbles/:id/share` → `/s/...` 공개 뷰어 링크. 내릴 때는 `DELETE /v1/bubbles/:id/share`. 리포트 문서를 따로 만들 거면 design.md 를 따라 HTML 한 장으로 낸다. --- ## 7. 같은 분석을 정기적으로 돌리기 **사람이 하는 말** > 매주 월요일에 지난주 리뷰로 같은 분석을 돌리고, 지난주 것과 비교해줘. **에이전트가 하는 일** 1. `externalKey` 를 안정된 식별자로 쓴다(예: `reviews-2026-W37`). 2. `GET /v1/bubbles/by-key/:externalKey` 로 이미 있는지 먼저 본다. 3. 다시 돌려야 하면 같은 키에 `force: true`. 4. 주차별 결과를 나란히 놓고 비교한다(좌우 비교는 각 칸 최대 48%, 라벨은 `faded`). --- ## 8. 데이터가 크거나 진행 상황을 보여줘야 할 때 **사람이 하는 말** > 3천 건이야. 오래 걸릴 텐데 진행 상황 보이게 해줘. **에이전트가 하는 일** - 비동기: `POST /v1/bubbles` → `202 { jobId, statusUrl }` → `GET /v1/bubbles/jobs/:id` 폴링 - 스트리밍: `POST /v1/bubbles/stream` - 완료 후 리포트는 `GET /v1/bubbles/:id/insight` 가 캐시를 즉시 돌려준다 --- ## 좋은 요청에 들어 있는 것 사람이 이 셋만 말해 주면 에이전트가 되묻지 않는다. 1. **데이터가 어디에 무슨 모양으로 있나** — 파일 경로, 어느 열, 한 행이 무엇인지 2. **무엇을 알고 싶은가** — "무슨 얘기가 오가나"(1번) / "뭘 따지고 만족하나"(2번) / "어떤 사람들인가"(3번)는 서로 다른 분석이다 3. **결과를 어떤 모양으로 받고 싶은가** — 지도 / 리포트 문서 / 공유 링크 / 원본 JSON ## 에이전트가 되물어야 하는 경우 | 상황 | 물어볼 것 | | --- | --- | | 한 사람이 한 줄씩만 있는데 "퍼소나" 요청 | 퍼소나 대신 주제 지도가 맞다고 설명하고 확인받는다 | | 열이 여러 개인데 어느 것을 분석할지 불명확 | 분석 대상 열을 확인한다 | | 이미 집계된 표(항목 + 건수) | 원본 텍스트가 있는지, 없으면 `weights` 로 넘길지 | | 텍스트 5000개 초과 | 분할할지, 샘플링할지 | | 개인정보가 섞인 자유응답 | 보내기 전에 무엇을 지울지 | --- AffinityBubble · 문서 색인