오늘도 공부
OpenGranola: 회의 중 실시간으로 “할 말을 찾아주는” 로컬 AI 미팅 코파일럿 본문
AI가 회의록을 써주는 시대는 이미 지났습니다.
이제는 회의가 끝난 뒤 정리해주는 도구보다, 회의가 진행되는 순간에 내가 무슨 말을 해야 하는지 도와주는 도구가 더 흥미롭습니다. OpenGranola는 바로 그 지점을 파고듭니다. 이 프로젝트는 단순히 통화를 녹음하고 요약하는 앱이 아니라, 내 로컬 지식 베이스를 뒤져 지금 이 대화에 필요한 근거와 포인트를 실시간으로 띄워주는 macOS용 미팅 코파일럿입니다. 저장소 설명 그대로 “A meeting note-taker that talks back”에 가깝습니다. (GitHub)
GitHub - yazinsai/OpenGranola
Contribute to yazinsai/OpenGranola development by creating an account on GitHub.
github.com
프로젝트 소개
OpenGranola는 GitHub 사용자 yazinsai가 공개한 오픈소스 프로젝트입니다. 핵심 아이디어는 아주 명확합니다.
- 통화 중 내 마이크 음성과 상대방의 시스템 오디오를 동시에 캡처하고
- 이를 로컬에서 실시간 전사한 다음
- 대화 흐름이 중요 포인트에 도달하면
- 미리 지정해둔 Markdown / 텍스트 노트 폴더를 검색해
- 지금 말하면 좋은 근거, 포인트, 답변 방향을 실시간 제안으로 보여줍니다. (GitHub)
기술 스택도 꽤 분명합니다.
- UI는 SwiftUI
- 패키징은 Swift Package
- 오디오/전사는 FluidAudio
- 업데이트는 Sparkle
- LLM 호출은 OpenRouter 호환 API 또는 Ollama
- 임베딩은 Voyage AI 또는 Ollama 임베딩
- 앱은 **Apple Silicon Mac + macOS 26+**를 전제로 합니다. (GitHub)
즉, 이 프로젝트는 “AI 노트 앱”이라기보다 로컬 우선 아키텍처를 가진 실시간 대화 지원 시스템으로 보는 편이 더 정확합니다.
왜 이 프로젝트가 등장했을까
기존의 미팅 AI 도구는 대부분 아래 두 방향 중 하나였습니다.
첫째, 회의가 끝난 뒤 요약과 액션 아이템을 만들어주는 방식.
둘째, 회의 내용을 서버로 보내고 클라우드에서 처리하는 방식.
문제는 여기 있습니다.
회의 중 내가 진짜 원하는 건 보통 이런 것입니다.
- “지금 이 질문에 뭐라고 답하지?”
- “우리 팀 내부 문서 중 이 주장 뒷받침하는 게 뭐였지?”
- “이 고객이 방금 말한 포인트, 전에 정리한 경쟁사 노트 어디 있었지?”
즉, 필요한 건 사후 요약이 아니라 실시간 맥락 회수입니다.
OpenGranola는 이 문제를 꽤 개발자답게 풉니다.
- 오디오를 로컬에서 잡고
- 로컬에서 전사하고
- 문서 폴더를 벡터화해 캐시해두고
- 대화 흐름이 특정 조건을 만족할 때만
- LLM으로 제안을 생성합니다. (GitHub)
여기서 중요한 건 “항상 생성”하지 않는다는 점입니다. 이 프로젝트는 단순한 채팅 호출기가 아니라, 실시간 이벤트 기반 제안 파이프라인을 갖고 있습니다. 이게 OpenGranola가 흥미로운 이유입니다.
핵심 기능
1) 양방향 대화 전사
README에 따르면 OpenGranola는 통화 양측을 실시간으로 전사합니다. 실제 코드에서도 TranscriptionEngine이 마이크 캡처와 시스템 오디오 캡처를 각각 시작하고, 두 개의 StreamingTranscriber를 병렬로 돌리는 구조입니다. (GitHub)
StreamingTranscriber는 단순히 음성을 바로 텍스트로 바꾸지 않습니다.
- 오디오를 16kHz mono Float32로 맞추고
- Silero VAD 기반 스트리밍 음성 구간 검출을 수행한 뒤
- 의미 있는 발화 세그먼트만 ASR에 넘깁니다
- 긴 발화는 약 3초 단위로 flush 해서 실시간성을 유지합니다. (GitHub)
이 구조 덕분에 불필요한 구간을 줄이고, 실시간성도 확보하려는 의도가 읽힙니다.
2) 로컬 우선 전사와 프라이버시 설계
README는 음성 인식이 Mac에서 로컬로 수행되며, 오디오는 디바이스를 벗어나지 않는다고 설명합니다. 실제 앱도 로컬 전사 모델을 첫 실행 시 내려받는 구조이고, API 키는 Mac Keychain에 저장된다고 안내합니다. (GitHub)
특히 재미있는 부분은 화면 공유 대응입니다. 앱은 기본적으로 화면 공유에서 숨겨지도록 설계되어 있고, NSWindow.SharingType을 .none으로 설정하는 코드가 들어 있습니다. README 역시 “screen sharing에서 hidden by default”라고 설명합니다. (GitHub)
즉, 프라이버시를 “정책 문구”로만 말하는 게 아니라, 앱 윈도우 가시성 레벨까지 제어합니다.
3) 지식 베이스 검색
OpenGranola의 차별점은 여기입니다.
사용자가 지정한 폴더의 .md, .txt 파일을 읽고, 이를 청크 단위로 나눈 뒤 임베딩해서 로컬 캐시에 저장합니다. 파일 내용이 바뀌지 않으면 SHA-256 기반 캐시를 재사용합니다. (GitHub)
검색 시에는 단일 질의가 아니라 queries 배열을 받아 임베딩하고, 상위 후보를 가져온 뒤 Voyage AI를 쓸 경우 rerank를 시도하고 실패하면 cosine similarity로 fallback 합니다. Ollama 사용 시에는 cosine 기반 경로가 자연스러운 기본이 됩니다. (GitHub)
이 말은 곧, 단순한 “문서 찾기”가 아니라 실시간 RAG-lite 시스템이라는 뜻입니다.
4) 대화 맥락에 따라 “필요할 때만” 제안
이 프로젝트에서 가장 잘 만든 부분은 제안 생성 게이트입니다.
SuggestionEngine은 무조건 LLM을 부르지 않습니다. 코드상으로 다음 같은 조건을 둡니다.
- 마지막 처리한 발화 중복 방지
- 최근 제안 이후 쿨다운 90초
- 너무 짧은 발화 제외
- filler utterance 제외
- KB relevance threshold
- relevance / helpfulness / timing / novelty / confidence 점수 임계치 검사 (GitHub)
즉, “상대가 말할 때마다 AI가 떠드는 앱”이 아니라, 말할 가치가 있는 순간만 surface하려는 설계입니다.
이건 실서비스 관점에서 매우 중요합니다. 실시간 보조 도구는 정확도보다 먼저 방해하지 않는 UX가 필요하기 때문입니다.
5) 세션 저장과 로깅
README는 모든 대화가 plain text transcript와 structured session log로 자동 저장된다고 설명합니다. 실제 ContentView를 보면 세션 시작/종료 시 SessionStore, TranscriptLogger를 함께 작동시키고, 발화가 추가될 때마다 세션 레코드를 남깁니다. 상대 발화에 대해서는 suggestion decision, surfaced suggestion, conversation state summary까지 기록하려고 합니다. (GitHub)
이건 나중에 단순 회의록을 넘어서,
- 어떤 순간에 제안이 떴는지
- 어떤 KB 히트가 있었는지
- 어떤 decision gate를 통과했는지
까지 복기할 수 있다는 뜻입니다.
운영 관점에서 보면, 이 구조는 나중에 추천 품질 튜닝 데이터셋으로도 발전시킬 수 있습니다.
프로젝트 아키텍처 분석
OpenGranola의 구조는 크게 5개 레이어로 볼 수 있습니다.
- 오디오 캡처 레이어
- 전사 레이어
- 지식 베이스 인덱싱 / 검색 레이어
- 제안 생성 레이어
- UI / 세션 저장 레이어
아키텍처를 Mermaid로 그리면 대략 이렇게 정리할 수 있습니다.

이 구조에서 핵심은 ContentView가 단순 View가 아니라, 사실상 앱 레벨 조립 지점이라는 점입니다. 여기서 TranscriptStore, KnowledgeBase, TranscriptionEngine, SuggestionEngine, SessionStore, TranscriptLogger가 한 번에 연결됩니다. 상대 발화가 들어오면 suggestion pipeline을 돌리고, 동시에 세션 기록까지 남깁니다. (GitHub)
내부 동작을 조금 더 자세히 보면
1. 오디오 수집
마이크는 MicCapture, 상대방 소리는 SystemAudioCapture에서 수집합니다.
특히 시스템 오디오 캡처는 ScreenCaptureKit 기반이며 capturesAudio = true와 함께 excludesCurrentProcessAudio = true를 설정합니다. 즉, 앱 자신의 소리를 다시 잡지 않도록 고려한 구조입니다. (GitHub)
2. 스트리밍 전사
수집된 오디오는 StreamingTranscriber에서 처리됩니다.
- 샘플 포맷 정규화
- VAD chunk 처리
- speech start / end 이벤트 감지
- 일정 길이 이상일 때만 전사
- 결과를 TranscriptStore에 반영 (GitHub)
3. 지식 검색
지식 폴더는 인덱싱 시 청크로 분할되고 임베딩됩니다.
검색 시에는 여러 질의를 함께 임베딩하고, 상위 결과를 고른 뒤 rerank 또는 cosine fallback을 사용합니다. (GitHub)
4. 제안 게이트
SuggestionEngine은 상대 발화가 들어올 때마다 실행되지만, 내부 필터가 많습니다.
- 너무 짧은 발화는 버리고
- filler를 걸러내고
- 최근 제안이 있으면 쿨다운을 적용하고
- 최종적으로 여러 점수 조건을 만족할 때만 shouldSurface=true로 판단합니다. (GitHub)
여기서 이 프로젝트의 철학이 보입니다.
**“더 많이 말하는 AI”가 아니라 “적절할 때만 말하는 AI”**입니다.
개발자 관점에서 좋은 설계 포인트
상태 관리가 단순하다
SwiftUI + @Observable 조합으로 상태 흐름이 비교적 단순합니다.
TranscriptStore, KnowledgeBase, SuggestionEngine처럼 역할이 나뉘어 있어 읽기가 쉽습니다. (GitHub)
공급자 추상화가 현실적이다
LLM은 OpenRouter와 Ollama를 모두 지원하고, 임베딩은 Voyage AI와 Ollama를 모두 지원합니다. 완벽한 provider abstraction 프레임워크는 아니지만, 실사용에 필요한 정도의 추상화는 잘 잡혀 있습니다. (GitHub)
캐시 전략이 실용적이다
KB 캐시를 파일 해시 기반으로 재사용하는 방식은 아주 고전적이지만 효과적입니다.
폴더형 문서 지식 베이스에서는 이게 가장 단순하고 유지보수하기 좋습니다. (GitHub)
운영 데이터를 남긴다
세션 레코드에 suggestion decision까지 남기는 건 프로토타입을 넘어서 품질 개선 루프를 의식한 구조입니다. (GitHub)
아쉬운 점도 있다
좋은 프로젝트지만, 제품화 관점에서 보완할 부분도 분명합니다.
1) 플랫폼 제약이 크다
Apple Silicon Mac, macOS 26+, Swift 6.2 환경 전제입니다. macOS 네이티브 앱이므로 Windows / Linux 크로스플랫폼성과는 거리가 있습니다. (GitHub)
2) 화면 중심 UX에 묶여 있다
현재 구조는 데스크톱 앱 UX에 최적화되어 있습니다.
슬랙, 캘린더, CRM, 노션 같은 외부 워크플로 통합은 저장소 기준으로 아직 핵심 기능이 아닙니다. README와 저장소 구조상 중심은 “개인용 실시간 회의 보조 앱”입니다. (GitHub)
3) 제안 품질은 결국 KB 품질에 크게 의존한다
RAG형 시스템은 항상 그렇듯, 노트가 부실하면 제안도 부실해집니다.
OpenGranola는 마법 도구가 아니라 내가 정리해 둔 문서 품질을 실시간으로 꺼내주는 도구에 가깝습니다.
언제 사용하면 좋은가
OpenGranola는 모든 회의에 필요한 도구는 아닙니다. 하지만 아래 상황에서는 꽤 강력합니다.
고객 미팅 전후가 많은 스타트업 팀
고객사 배경, 이전 대화, 경쟁사 비교, 가격 정책 노트를 빠르게 불러오고 싶을 때 유용합니다.
세일즈 / 파트너십 콜
상대 질문에 바로 근거를 붙여 답해야 할 때, “말하기 직전의 검색”이 필요합니다.
창업자 / PM / 솔루션 엔지니어
투자자 콜, 고객 인터뷰, 파트너 미팅처럼 맥락 전환이 빠른 통화에서 효과적입니다.
연구 / 컨설팅 / 분석 업무
리서치 노트가 많고, 이를 통화 중 회수해야 하는 사람에게 잘 맞습니다.
반대로, 단순 회의록 자동화만 원한다면 이 프로젝트는 다소 과할 수 있습니다. OpenGranola는 “기록기”보다 실시간 참조 도구에 더 가깝기 때문입니다.
코드로 보면 이런 식의 프로젝트다
실제 앱 사용은 GUI 중심이지만, 개발자 관점에서 흐름을 의사 코드로 재구성하면 대략 이렇습니다.
let transcriptStore = TranscriptStore()
let settings = AppSettings()
let kb = KnowledgeBase(settings: settings)
await kb.index(folderURL: notesFolder)
let engine = TranscriptionEngine(transcriptStore: transcriptStore)
let suggester = SuggestionEngine(
transcriptStore: transcriptStore,
knowledgeBase: kb,
settings: settings
)
await engine.start(locale: settings.locale, inputDeviceID: settings.inputDeviceID)
for utterance in transcriptStore.utterances {
if utterance.speaker == .them {
suggester.onThemUtterance(utterance)
}
}
이 의사 코드는 단순하지만, OpenGranola의 핵심을 잘 보여줍니다.
- 오디오를 받고
- 전사하고
- 상대 발화가 생기면
- 지식 베이스와 LLM을 조합해
- 제안을 만든다
즉, 전형적인 event-driven desktop AI pipeline입니다.
실제 사용 예시
예를 들어 당신이 B2B SaaS 팀에서 고객 데모를 진행한다고 해봅시다.
지식 폴더에 이런 문서가 있습니다.
/customer-notes/acme.md
/competitive/competitor-x.md
/pricing/enterprise-plan.md
/security/soc2-summary.md
고객이 회의 중 이렇게 묻습니다.
“보안 측면에서 엔터프라이즈 고객이 가장 많이 묻는 항목은 뭐죠?”
그러면 OpenGranola가 하는 일은 다음과 비슷합니다.
- 상대 발화를 실시간 전사한다.
- 이 발화가 충분히 길고 의미 있다고 판단한다.
- “security”, “enterprise”, “customer concern” 같은 질의로 KB를 검색한다.
- 관련 청크를 모은다.
- 지금 답변에 쓸 만한 한두 줄 제안을 화면 상단에 띄운다.
결과적으로 사용자는 문서를 찾느라 시선을 빼앗기지 않고, 대화 흐름 안에서 준비된 사람처럼 말할 수 있게 됩니다.
설치와 빌드 관점에서 보면
README 기준으로 DMG 릴리스가 제공되며, 소스 빌드는 ./scripts/build_swift_app.sh로 할 수 있습니다. Swift Package 구조라 개발용 빌드는 swift build -c debug도 가능합니다. 저장소의 최신 릴리스 표시는 2026년 3월 18일 기준 v1.4.3입니다. (GitHub)
예시는 다음처럼 정리할 수 있습니다.
./scripts/build_swift_app.sh
개발 빌드는:
cd OpenGranola
swift build -c debug
프로젝트를 직접 읽어보면 앱 본체는 OpenGranola/, 빌드 및 패키징 스크립트는 scripts/, 에셋은 assets/에 분리돼 있습니다. (GitHub)
이 프로젝트를 어떻게 해석하면 좋을까
OpenGranola를 단순히 “오픈소스 Granola 클론”으로 보면 놓치는 게 많습니다.
이 프로젝트의 본질은 다음에 가깝습니다.
- 로컬 오디오 처리
- 실시간 스트리밍 전사
- 폴더 기반 개인 KB RAG
- 상황 인식형 suggestion gate
- 데스크톱 코파일럿 UX
즉, OpenGranola는 메모 앱이 아니라 실시간 인간 대화에 개입하는 개인용 AI 보조 시스템입니다.
개발자 입장에서는 “RAG를 채팅창 밖으로 꺼내서 실제 회의 흐름에 붙이면 어떤 제품이 되는가”를 보여주는 꽤 좋은 사례입니다. (GitHub)
마무리
OpenGranola가 흥미로운 이유는 거창한 모델 혁신 때문이 아닙니다.
오히려 반대입니다.
이미 존재하는 기술들을 잘 조합합니다.
- 로컬 ASR
- VAD
- 벡터 검색
- rerank
- LLM 생성
- 데스크톱 UI
- 세션 로깅
그리고 그것을 **“회의 중 지금 당장 도움이 되는가”**라는 제품 질문에 맞춰 엮습니다.
그래서 이 프로젝트는 AI 앱이라기보다, 사용자의 기억과 문서를 실시간 대화 위로 올려주는 인터페이스로 보는 편이 더 적절합니다.
개발자가 보기엔 특히 재미있습니다.
RAG를 웹 챗봇이 아니라 실시간 인간 인터랙션에 붙이면 어떤 구조가 필요한지, OpenGranola는 꽤 실용적인 답을 보여주기 때문입니다.
'AI' 카테고리의 다른 글
| AppReveal: 모바일 앱 안에 MCP 서버를 심어, LLM이 네이티브 앱을 직접 이해하게 만드는 방법 (0) | 2026.03.18 |
|---|---|
| workerd: Cloudflare Workers를 로컬과 프로덕션으로 확장하는 JavaScript/Wasm 런타임 (0) | 2026.03.18 |
| 🚀 AI 파인튜닝, 딸깍으로 가능한 Unsloth Studio 완전 분석 (0) | 2026.03.18 |
| GitNexus: 코드베이스를 지식 그래프 형태로 변환 (0) | 2026.03.17 |
| AimangaStudio (만화도 AI로 딸깍?) (0) | 2026.03.17 |
