이 튜토리얼은 사내 지식과 공개 웹을 조사해 인용이 포함된 보고서를 만들고, 필요하면 음성으로 재생하는 시스템을 설계하는 방법을 다룬다. 검색 계층은 onyx, 작업 분리는 CrewAI, 음성 출력은 Voxtral TTS가 맡는다. 핵심은 하나의 에이전트에 모든 자료를 쌓지 않고 수집, 분석, 집필의 입력과 출력을 분리하는 것이다.
먼저 확인할 제약
원문은 이 구성을 “100% 오픈소스, 셀프호스팅”으로 소개하지만 실제 도입 시에는 경계를 명확히 해야 한다.
| 구성 요소 | 확인된 제공 방식 | 운영 시 주의점 |
|---|---|---|
| Onyx Community Edition | 핵심 기능 MIT, 자체 호스팅 가능 | Enterprise 기능은 별도 라이선스다 |
| Onyx 벤치마크 제출 구성 | GPT-5.2 + Serper + Firecrawl 사용 | 제출 점수는 완전 로컬 구성의 점수가 아니다 |
| CrewAI | MCP mcps 필드와 Flows 제공 | 선택하는 LLM·도구의 데이터 전송 정책을 확인한다 |
| Voxtral 4B TTS 2603 | 오픈 웨이트 TTS, 로컬 서빙 가능 | CC BY-NC 4.0이므로 상업 용도에 그대로 사용할 수 없다 |
데이터 주권이 목적이라면 Onyx 서버뿐 아니라 LLM 추론, 웹 검색·크롤러, 로그 저장소, 음성 모델까지 어떤 네트워크 경계를 넘는지 먼저 문서화해야 한다.
목표 아키텍처
질의 입력(텍스트 또는 음성)
|
v
Researcher Crew ---- Onyx MCP ---- 사내 인덱스 / 공개 웹 / URL 본문
| (근거 + URL)
v
Analyst Crew ------ 중복 제거 / 상충 주장 표시 / 근거 묶음
|
v
Writer Crew ------- 인용 포함 Markdown 보고서
|
+----------> 선택 사항: Voxtral TTS로 보고서 음성화Researcher만 외부 검색 도구에 접근하게 하고, Analyst와 Writer에는 앞 단계가 반환한 구조화 결과만 건넨다. 이 경계가 있어야 원문 청크와 결론이 섞이지 않고, 어느 단계에서 잘못된 주장이 생겼는지 추적할 수 있다.
1. Onyx를 검색 계층으로 준비한다
내부 자료를 지속적으로 검색해야 한다면 Standard 배포가 적합하다. 공식 설치 스크립트를 실행하고 설치 안내에서 Standard를 선택한다.
curl -fsSL https://onyx.app/install_onyx.sh | bash배포가 끝나면 필요한 커넥터만 연결한다. 예를 들어 기술 조사 시스템이라면 GitHub, Google Drive, Jira 또는 Confluence가 우선순위가 될 수 있다. 운영 데이터로 시험하기 전에 테스트 문서로 권한 제한과 삭제 동기화를 확인한다.
MCP 서버를 켜면 외부 리서치 에이전트가 Onyx 검색 계층을 호출할 수 있다.
# Onyx 환경 설정에 추가
MCP_SERVER_ENABLED=true자체 호스팅 MCP 엔드포인트는 공식 문서 기준 http://YOUR_ONYX_DOMAIN:8090/이며 Bearer 토큰 인증을 사용한다. 노출되는 핵심 도구는 search_indexed_documents, search_web, open_urls다.
2. CrewAI Researcher에 Onyx MCP를 연결한다
CrewAI는 에이전트의 mcps 필드에 원격 MCP 서버를 지정할 수 있다. 토큰은 소스 코드에 넣지 말고 환경 변수에서 읽는다.
import os
from crewai import Agent
from crewai.mcp import MCPServerHTTP
ONYX_MCP_URL = os.environ["ONYX_MCP_URL"]
ONYX_TOKEN = os.environ["ONYX_TOKEN"]
researcher = Agent(
role="Senior Researcher",
goal="질문과 관련된 근거를 찾고 각 주장에 원문 URL을 붙인다.",
backstory="검색 결과를 결론처럼 쓰지 않고 근거와 불확실성을 기록한다.",
mcps=[
MCPServerHTTP(
url=ONYX_MCP_URL,
headers={"Authorization": f"Bearer {ONYX_TOKEN}"},
cache_tools_list=True,
)
],
)Researcher의 출력 형식은 자유로운 보고서보다 구조화된 근거 목록이 낫다.
{
"findings": [
{
"claim": "검증할 주장",
"source_url": "https://...",
"source_type": "internal_or_web",
"evidence": "주장을 지지하거나 반박하는 발췌 요약",
"confidence": "high_or_medium_or_low"
}
],
"unanswered_questions": ["추가 검색이 필요한 항목"]
}3. 조사, 분석, 집필을 별도 단계로 나눈다
CrewAI Flows는 여러 Crew와 작업을 이벤트 기반으로 연결할 수 있다. 각 단계는 필요한 정보만 받아야 한다.
| 단계 | 입력 | 출력 | 허용 도구 |
|---|---|---|---|
| Researcher | 사용자의 질문, 조사 범위 | URL이 붙은 근거 목록 | Onyx MCP |
| Analyst | 근거 목록 | 중복 제거된 주장, 충돌, 누락 사항 | 필요 없음 |
| Writer | 분석 결과와 인용 목록 | 최종 Markdown 보고서 | 필요 없음 |
Analyst는 서로 다른 출처가 사실상 같은 말을 반복하는지, 명시적으로 충돌하는지 표시한다. Writer는 근거가 없는 결론을 새로 만들지 않고, 충돌이 해결되지 않으면 보고서에 그대로 남긴다.
from crewai.flow.flow import Flow, listen, start
class DeepResearchFlow(Flow):
@start()
def collect_evidence(self):
return run_researcher(self.state["query"])
@listen(collect_evidence)
def analyze_evidence(self, evidence):
return run_analyst(evidence)
@listen(analyze_evidence)
def write_report(self, analysis):
return run_writer(analysis)위 코드는 단계 경계를 보여주는 뼈대다. 실제 구현에서는 각 출력에 JSON 스키마 검증, 요청 시간 제한, 실행 ID, 사용한 URL 목록을 추가한다.
4. 반복 조사와 인용 검증을 넣는다
첫 검색 결과만으로 집필하면 빠진 근거를 발견하기 어렵다. Analyst 결과에 missing_questions가 남아 있으면 제한된 횟수만 Researcher를 다시 호출한다.
초기 조사 -> 분석
|
누락 또는 충돌 있음? -- 아니오 --> 집필
|
예
v
추가 질의 생성 -> 재조사 -> 분석프로덕션에서는 다음 기준을 명시한다.
- 반복 횟수와 전체 실행 시간의 상한
- 인용 URL이 없는 문장을 최종 결론에서 제외하는 규칙
- 내부 자료와 공개 웹 자료가 충돌할 때의 표시 방식
- 민감 문서가 최종 보고서 또는 음성 출력에 포함될 수 있는 권한 정책
- 검색 질의, 결과 문서 ID, 최종 인용 목록을 감사 로그로 보존하는 기간
5. 음성 출력은 선택적으로 붙인다
Mistral의 Voxtral 4B TTS 2603은 vLLM Omni로 로컬에서 텍스트 음성 변환(TTS, Text-to-Speech)을 제공할 수 있다. BF16 모델은 공식 모델 카드 기준 16GB 이상의 GPU 메모리를 요구한다.
uv pip install -U vllm vllm-omni
vllm serve mistralai/Voxtral-4B-TTS-2603 --omni보고서가 승인된 뒤에만 TTS를 호출하는 것이 좋다. 미검증 초안을 음성으로 배포하면 틀린 내용을 검토 없이 소비하기 쉽고, 민감한 내부 보고서라면 음성 파일 자체가 추가 보호 대상이 된다.
또한 Voxtral 4B TTS 2603은 CC BY-NC 4.0으로 제공된다. 내부 검증이나 비상업적 프로토타입에는 고려할 수 있지만, 상업 서비스에 넣으려면 사용권을 별도로 검토하거나 다른 음성 계층을 선택해야 한다.
운영 체크리스트
| 점검 항목 | 이유 |
|---|---|
| 커넥터별 접근 권한과 삭제 동기화 시험 | 검색 결과를 통한 문서 노출 방지 |
| LLM·검색 API·크롤러의 외부 전송 경로 기록 | “셀프호스팅” 범위 오해 방지 |
| 모든 최종 주장에 URL 또는 내부 문서 식별자 요구 | 보고서 감사와 반박 가능성 확보 |
| 실행 시간·검색 횟수·토큰 비용 상한 설정 | 딥 리서치의 비용 폭증 방지 |
| Voxtral 라이선스와 음성 결과물 저장 정책 검토 | 상업 사용 및 민감 정보 위험 관리 |
관련 문서
- onyx — 지식 커넥터, Deep Research, MCP 서버를 제공하는 검색 계층
- rag — 검색 증강 생성의 기본 구조
- multi-agent-research-assistant-tutorial-python — 역할별 리서치 에이전트 설계 패턴
- feynman — 독립적으로 사용할 수 있는 병렬 딥리서치 도구
참고 자료
- How to build a Deep Researcher — X @akshay_pachaar (2026-05-26 수집)
- onyx-dot-app/onyx — GitHub 공식 저장소
- Onyx Deep Research Benchmark Results — Onyx 공식 제출 저장소
- Onyx MCP Server — Onyx 공식 문서 (2026-05-26 확인)
- MCP Servers as Tools in CrewAI — CrewAI 공식 문서 (2026-05-26 확인)
- Flows — CrewAI 공식 문서 (2026-05-26 확인)
- Voxtral 4B TTS 2603 — Mistral 모델 카드 (2026-05-26 확인)