AI
cb.ai는 LLM 채팅을 ConnectBase 서버 프록시로 호출하는 모듈입니다. 동기 응답(chat)과 SSE 스트리밍(chatStream)을 제공하고, AI 데이터베이스(RAG) 검색, 도구(Tool) 호출, 추론 모델의 사고 과정 스트리밍까지 지원합니다.
시작하기 전에
콘솔 > 설정 > AI 에서 사용할 프로바이더와 API 키를 먼저 등록하세요. 등록한 키는 암호화 저장되며 서버에서만 복호화됩니다.
🔒 프로바이더 API 키를 클라이언트에 하드코딩하지 마세요.
chat/chatStream은 ConnectBase 서버 프록시를 경유하므로 클라이언트에는 Public Key만 있으면 됩니다. 브라우저에서api.openai.com같은 프로바이더 URL을 직접 호출하면 DevTools 한 번으로 키가 노출됩니다.
기본 채팅
const res = await cb.ai.chat({
messages: [{ role: 'user', content: '안녕하세요' }],
})
console.log(res.content) // 답변 텍스트
console.log(res.usage) // { promptTokens, completionTokens, totalTokens }provider / model 을 생략하면 콘솔 > 설정 > AI 의 기본 설정을 사용합니다. 요청 단위로 바꿀 수도 있습니다:
const res = await cb.ai.chat({
messages: [{ role: 'user', content: '이 글을 요약해줘: ...' }],
provider: 'gemini',
temperature: 0.3,
maxTokens: 1024,
})스트리밍 (SSE)
await cb.ai.chatStream({
messages: [{ role: 'user', content: '안녕하세요' }],
}, {
onToken: (text) => appendAnswer(text),
onDone: () => finish(),
onError: (err) => showError(err),
})추론 모델(OpenAI o-series, DeepSeek-R1, Qwen3 reasoning 등)을 쓰면 사고 과정이 onReasoning 으로 분리되어 전달되고, 최종 답변은 그대로 onToken 으로 옵니다:
await cb.ai.chatStream({ messages }, {
onReasoning: (thought) => renderThinking(thought),
onToken: (text) => renderAnswer(text),
onDone: () => finish(),
})스트림 취소 (stop 버튼)
AbortSignal 을 전달하면 진행 중인 스트림을 취소할 수 있습니다. abort 는 에러가 아니라 정상 취소로 처리됩니다 — onError 대신 onAbort 가 호출되고 onDone 은 호출되지 않습니다.
const controller = new AbortController()
await cb.ai.chatStream(
{ messages: [{ role: 'user', content: '...' }] },
{
onToken: (text) => appendAnswer(text),
onAbort: () => showCancelled(),
},
{ signal: controller.signal },
)
// stop 버튼 클릭 시
controller.abort()toolGroupId 를 사용하는 서버측 도구 실행 루프도 abort 시 함께 중단됩니다.
AI 데이터베이스(RAG) 연동
knowledgeBaseId 를 지정하면 문서를 먼저 검색해 컨텍스트로 포함한 뒤 답변합니다. 참조된 문서 청크는 onSources 로 전달되어 출처 UI를 만들 수 있습니다.
await cb.ai.chatStream({
messages: [{ role: 'user', content: '환불 정책이 어떻게 되나요?' }],
knowledgeBaseId: 'kb_xxx',
topK: 5,
}, {
onSources: (sources) => renderCitations(sources),
onToken: (text) => renderAnswer(text),
onDone: () => finish(),
})| 옵션 | 설명 |
|---|---|
topK | 검색할 문서 청크 수 |
hybrid | BM25 + 벡터 하이브리드 검색. 미설정 시 앱 embedding 설정을 따르고, false 면 키워드 전용 |
agentic | AI가 검색어를 생성해 다중 라운드로 검색. 진행 상황은 onSearching 으로 수신 |
// Agentic 검색 — "검색 중..." 진행 UI
await cb.ai.chatStream({
messages: [{ role: 'user', content: '작년 대비 정책 변경점을 정리해줘' }],
knowledgeBaseId: 'kb_xxx',
agentic: true,
}, {
onSearching: (p) => showProgress(p.phase, p.queries),
onSources: (sources) => renderCitations(sources),
onToken: (text) => renderAnswer(text),
})문서 업로드/색인은 지식 베이스 (Knowledge / RAG) 문서를 참고하세요.
도구(Tool) 호출
콘솔에서 만든 도구 그룹을 toolGroupId 로 지정하면 서버가 도구 실행 루프를 대신 수행하고, 각 도구의 시작/종료가 onToolEvent 로 전달됩니다:
await cb.ai.chatStream({
messages: [{ role: 'user', content: '오늘 가입자 수 알려줘' }],
toolGroupId: 'tg_xxx',
}, {
onToolEvent: (e) => {
if (e.type === 'tool_start') showRunning(e.name)
if (e.type === 'tool_end') showResult(e.name, e.success)
},
onToken: (text) => renderAnswer(text),
})| 옵션 | 설명 |
|---|---|
tools | 요청에 직접 포함하는 도구 정의 (name, description, inputSchema) |
toolGroupId | 콘솔 도구 그룹 ID — 서버측 도구 실행 루프 사용 |
mcpPublicKey | tools 로 외부 MCP 도구를 넘길 때 해당 MCP 서버의 Public Key (cb_pk_*) |
toolResultMaxChars | 도구 결과를 모델에 재투입하기 전 N자로 절단 (화면 표시는 원문 유지) |
응답 구조
const res = await cb.ai.chat({ messages })
res.content // 답변 텍스트
res.reasoning // 추론 모델의 사고 과정 (추론 모델이 아니면 빈 값)
res.usage // { promptTokens, completionTokens, totalTokens }
res.provider // 실제 사용된 프로바이더
res.model // 실제 사용된 모델
res.sources // RAG 사용 시 참조 문서 청크흔한 에러
| 에러 | 원인 | 해결 |
|---|---|---|
AI provider not configured | 콘솔에 프로바이더/키 미등록 | 콘솔 > 설정 > AI 에서 등록 |
Knowledge base not found | 잘못된 knowledgeBaseId | 콘솔에서 ID 확인 |
스트림 중 onError 호출 | 프로바이더 오류가 스트림 본문(in-band)으로 전달됨 | 에러 메시지 확인 후 재시도 |