회의 전사록을 받아 회의록으로 정리하고 노션 DB에 등록한 뒤 디스코드에 공지까지 한 번에 처리한다.
npx skills add https://github.com/bam-bam-2/solo-skills --skill meeting-minutes
디스코드 음성 회의 → 전사본 수신 → 회의록 작성 → 노션 등록 → 디스코드 공지까지의 정본 절차.
프로젝트 개요·인물·일정은 볼트 프로젝트/프로젝트.md 참조.
작업 폴더: ~/Projects/<프로젝트>/이벤트/버추얼 프로젝트
프로젝트 조사봇이 회의 종료 후 사용자 DM으로 transcript.md를 보낸다.
cd "~/Projects/<프로젝트>/이벤트/버추얼 프로젝트"
TOKEN=$(grep '^프로젝트_DISCORD_BOT_TOKEN=' .env | cut -d= -f2-)
CH=$(curl -s -X POST -H "Authorization: Bot $TOKEN" -H "Content-Type: application/json" \
-H "User-Agent: DiscordBot (https://github.com/bambam/twc-pm, 1.0)" \
-d '{"recipient_id":"483902030243692546"}' \
"https://discord.com/api/v10/users/@me/channels" \
| python3 -c "import json,sys;print(json.load(sys.stdin)['id'])")
bash scripts/twc-dm-download-transcripts.sh "$CH" "회의록 전사/_dm받은전사" 5
원본은 원격 머신 ~/Projects/<프로젝트>/recordings/<ISO시각>_1515265088167346317/ 에도 있다.
저장되는 건 발화자별 음성(.ogg/.pcm)과 transcript.md뿐 — 화면 영상은 없다.
전사본을 열어 "(전사된 발언이 없습니다)"이거나 발화수가 비정상으로 적으면 보령을 본다.
ssh remote-host 'grep -E "녹음 시작|디코더 오류|전사 완료" ~/Projects/<프로젝트>/logs/bot.log | tail -20'
디코더 오류 <userId> The compressed data passed is corrupted 가 보이면 그 사람은 그 시점 이후 녹음이 안 된 것이다.
녹음 시작 직후(수 ms 내)에 뜨면 그 참석자 발언은 회의 전체가 없다. 봇이 들어올 때 이미 말하고 있던 사람이 걸린다.
봇 녹음이 없다고 기록이 없는 게 아니다. 참석자가 개인 녹취를 갖고 있을 수 있다.
회의 주변 텍스트(디스코드 후속 대화)로 재구성한 문서를 먼저 발행하지 말고,
참석자에게 녹취 유무를 먼저 물어볼 것.
8/18 건은 디스코드 기록만으로 발행했다가 나중에 녹취를 받았는데,
회의 본론이 통째로 빠져 있었다. 주변 텍스트에 남는 건 회의 직후 후속 논의지 회의 그 자체가 아니다.
재구성본을 굳이 내야 한다면 "회의에서 나온 것"과 "회의 후에 오간 것"을 별도 절로 분리해야
나중에 원본이 나왔을 때 무엇이 빠졌는지 바로 보인다.
봇과 같은 파이프라인을 손으로 태운다. 무거우니 원격 머신에서 돌린다.
ssh remote-host 'mkdir -p ~/tmp/<이름>'
scp "<mp3>" remote-host:~/tmp/<이름>/raw.mp3
ssh remote-host 'cd ~/tmp/<이름> && /opt/homebrew/bin/ffmpeg -hide_banner -loglevel error -y \
-i raw.mp3 -ac 1 -ar 16000 -c:a libopus -b:a 24k meeting.ogg'
# 42MB mp3 → 5MB ogg. Groq 25MB 제한을 안 넘긴다.
ssh remote-host 'cd ~/tmp/<이름> && KEY=$(grep -h "^GROQ_API_KEY=" ~/.config/watch/.env | cut -d= -f2- | tr -d "\"") && \
curl -s -X POST "https://api.groq.com/openai/v1/audio/transcriptions" \
-H "Authorization: Bearer $KEY" -F "[email protected]" -F "model=whisper-large-v3" \
-F "language=ko" -F "response_format=verbose_json" \
-F "prompt=<고유명사 나열>" -o result.json'
segments의 start에 녹음 시작 시각을 더해 절대 시각으로 바꿔 적으면 회의록 쓸 때 대조하기 쉽다.그렇게 판별했다고 회의록 ⚠️참고에 밝힌다.
60분 회의가 500줄/70KB 정도 된다. sed -n 으로 150~170줄씩 끊어 전부 읽는다.
요약본만 보고 쓰지 않는다. 발화자별 발화수를 먼저 세면 누가 주도했는지 잡힌다.
grep -o '^\[[0-9:]*\] [^:]*:' FILE | sed 's/^\[[0-9:]*\] //' | sort | uniq -c | sort -rn
직전 회의 회의록과 이전 체크포인트 전사를 함께 확인한다. 이번 회의의 "저번에 말한 거"가
무엇인지 모르면 액션아이템의 담당·기한을 틀리게 적는다.
작업 폴더 회의록 전사/ 아래에.
a. 공유본 <날짜>_<회의명>_회의록초안.md
구성 순서(기존 회의록과 동일하게 유지):
> 🗓 콜아웃 — 날짜·시각·길이·참석자·회차 맥락## 🎯 이 회의 한 줄 — 결론 한 문단## 📅 일정## 🧩 무슨 얘기를 했나 — 안건별 소제목, 발언 인용 포함## ✅ 결정사항 — 번호 목록## 📌 다음 할 일 (액션아이템) — 담당·할 일·기한 3열 표## ⚠️ 참고a-2. 이전 회차 미결 항목 대조 (5.4 때문에 필수)
공유본을 쓰기 전에 워치리스트의 진행중 항목을 뽑아 이번 전사와 대조한다.
노션 🔭 프로젝트 워치리스트(YOUR_NOTION_DB_ID)에서 상태=진행중을 조회.
각 항목을 셋 중 하나로 분류해 "무슨 얘기를 했나"에 근거가 남게 쓴다.
예: *"지하 녹음실은 특정 위치를 누르면 계단이 열리고, 방음 온오프 버튼도 작동하는 것을 확인했습니다."*
예: *"해치 손잡이는 추가하겠다고 했으나 이번 회의에서 실물 확인은 없었습니다."*
예: *"창문 격자·스케일 1.2배는 이번 회의에서 언급되지 않아 진행 상황이 확인되지 않았습니다."*
세 번째를 빠뜨리면 봇이 "언급이 없다"를 스스로 해석해야 해서 판정이 흔들린다.
명시해두면 red 로 정확히 유지된다.
b. PM전용메모 <날짜>_<회의명>_PM전용메모(대외비).md
공유본에서 뺀 것 + PM만의 액션. 뺄 것의 기준:
DB: collection://YOUR_NOTION_DB_ID (📋 회의록)
회차는 직전 회의 +1. 먼저 확인:
notion-query-data-sources: SELECT "회차","제목","date:날짜:start" FROM "collection://YOUR_DATA_SOURCE_ID…" ORDER BY "회차" DESC LIMIT 5
참석자 relation은 인물 DB(collection://YOUR_NOTION_DB_ID) 페이지 URL로 넣는다.
| 사람 | 페이지 ID |
|---|---|
| 사용자 | NOTION_DB_ID |
| 참석자 | NOTION_DB_ID |
| 참석자 | NOTION_DB_ID |
| 참석자 | NOTION_DB_ID |
| 참석자 | NOTION_DB_ID |
| 참석자 | NOTION_DB_ID |
| 참석자 | NOTION_DB_ID |
⚠️ 알려진 버그: 노션 MCP로 숫자 속성을 못 넣는다.
회차(number)와 date:날짜:is_datetime(0/1)을 넘기면 문자열로 직렬화돼
Number values must be JavaScript numbers 로 실패한다.
→ 두 필드를 빼고 페이지를 만든 뒤, 브라우저로 열어 회차만 직접 입력한다.
is_datetime은 생략하면 기본 0이라 문제없다.
채널 📃회의-안건및기록 1515265088720736312. 형식은 고정이다.
📋 **회의록 업데이트했습니다!**
· **<날짜> <회의명>** — <핵심 3개를 · 로 연결>
<노션 링크>
-# 사용자(PM)이 작성한 회의록을 봇으로 공유드립니다.
bash scripts/twc-discord-post.sh 1515265088720736312 /tmp/msg.txt
리마인드·담당별 할 일 같은 팀 대상 공지는 파트 채널로 간다:
🗺️맵·트리거-회의 1516891094925971676 / 🖼️디자인-회의 1516891... / 💻영상-회의 1515265089744408642
전에는 조사봇(followup.py)이 최근 24시간 디스코드 텍스트만 보고 미결 항목을 판정했다.
그래서 음성 회의에서 시연·확인되고 끝나는 일(맵팀 작업이 대표적)은 증거가 어디에도
남지 않아, 실제로는 끝났는데 계속 "마감 지났는데 완료 안 됐어요"로 알림이 갔다.
이제 봇이 판정할 때 노션 회의록 본문을 함께 읽는다. 최근 14일 이내·최대 2건·건당 4000자.
환경변수 FOLLOWUP_MINUTES_DAYS / FOLLOWUP_MINUTES_COUNT / FOLLOWUP_MINUTES_CHARS 로 조절.
그래서 회의록 본문 서술이 그대로 봇의 판정 근거다.
계단이 열리는 구조. 방음 온오프 버튼도 있고" 같은 서술 한 줄이 그 항목을 green 으로 닫는다.
확인된 것처럼 쓰면 봇이 잘못 닫는다.
실물 확인·시연 내용을 구체적으로 남기는 게 봇 정확도로 직결된다.
회의록 문장 하나가 워치리스트 항목을 자동으로 닫는다(green 3회 연속 → 상태=완료).
그래서 아래 두 가지가 실제 사고로 이어진다.
| 잘못 쓰면 | 결과 |
|---|---|
| 확인 안 된 걸 확인된 것처럼 서술 | 봇이 green → 잘못된 "순조롭다" 보고 (자동완료는 가드가 막는다) |
| "무슨 얘기를 했나"를 부실하게 씀 | 근거가 없어 계속 red → 끝난 일로 계속 알림이 감 |
회의록 서술만으로는 워치리스트 항목이 자동으로 닫히지 않는다. 상태를 실제로 바꾸려면
두 조건을 모두 넘어야 한다.
(_discord_corroborated). 모델 판단이 아니라 원문 대조다.
마감 건이 닫히는 걸 막는다 (_due_passed).
둘 중 하나라도 실패하면 로그에 자동완료 보류(디스코드 근거 없음) 을 남기고 열어둔다.
green 으로 보고는 되니 알림 소음은 사라지고, 닫는 건 사용자가 DM 한 줄로 한다.
자동완료될 때는 DM 에 근거 문장이 함께 표시된다(_close_line). 잘못 닫혔으면
그 자리에서 알아볼 수 있다.
왜 이 가드가 필요했나 — 회의록 ⚠️참고에 "스케일 1.2배는 이번 회의에서 확인하지 못했다"고
명시했는데도, 모델이 실물 투어 서술을 보고 green 으로 올렸다. 판정의 ev(근거 줄번호)
필드도 전 항목이 31 로 동일하게 채워져 신뢰할 수 없었다. **문서로 지시하는 것만으로는
모델의 과잉 추론을 못 막는다**는 게 실측으로 확인된 사례다.
추측으로 완료를 적지 마라. 전사에 없으면 없다고 쓴다. 회의에서 화면으로 보여준 것과
말로만 언급된 것을 구분해서 적는다("시연으로 확인" vs "하겠다고 말함").
실측 예 — 8/12 회의에서 스꿀에게 8/15 마감으로 6건이 배정됐고, 8/17 클라이언트 리뷰에서
그중 녹음실 문·2층 동선 차단이 실물로 시연됐다. 회의록에 그 서술이 있어서 봇이 두 건을
green(근거: 8/17 회의록)으로 닫고, 전사에 언급이 없던 해치·창문·천장조명·스케일 4건은
red 로 유지했다. 사람이 전사본을 손으로 대조한 결과와 판정이 일치했다.
노션에 올리면 끝이다. 액션아이템을 워치리스트에 샬다 집을 필요 없다.
맥밌니 ~/ops/twc-watchlist-sync/sync.py (launchd com.bambam.twc-watchlist-sync,
매일 07:50·11:50·18:50)가 회의록 DB를 훑어 새 회의록의
"📌 다음 할 일 (액션아이템)" 섹션을 파싱해 🔭 프로젝트 워치리스트에
상태=진행중 / 원처=회의록으로 넣는다. 그럼 조사봇(twc-followup)이
그걸 무는다. LLM을 쓰지 앨는다(토큰 0).
그래서 이제 액션아이템 서식이 곳 그대로 봇의 입력값이다:
담당 | 할 일 | 기한 또는 불릿 [담당] 할 일 — 둘 다 인식한다.8/15, 8/15 전, 이번 주, 내일은 날짜로 자동 변환된다.미정·수시·최우선은 마감일 반입 없이 제목에 (기한: …)로 붙는다.
→ 추상적인 기한을 쓸수록 봇이 못 침다. 되면 날짜로 적어라.
또 적지 말것(새 항목으로 들어간다).
직전 회의가 덮어썰는 구회차 항목은 수동으로 처리한다. 백필 범위 지정:
python3 sync.py --dry-run --backfill 16,15 → 확인 후 --backfill 단독 실행.
로그는 ~/ops/twc-watchlist-sync/watchlist_sync.log.
봇 리포트는 한 번에 최대 6건으로 자른다(FOLLOWUP_MAX_TODO).
넘치면 "같은 성겼의 N건은 다음 회차에"로 뮸리고 다음 회차에 이어 알린다.
그대로 적지 않는다(예: "회의 영상 추출" — 봇은 음성만 녹음한다).
근거가 된다. 추측·희망 섞인 표현("됐을 것", "정리됐습니다")이 없는지 훑는다.
발행 후 누락을 발견하면 채팅으로 알리지 말고 노션 발행본을 바로 고친다.
Take bam-bam-2/meeting-minutes from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.