AI Assistant
์ธ๋ถ AI์ ์ฐ๋ํ๊ธฐ ์ํ ํจ๋ยท๋๊ตฌยท์คํ API ์ค์ ๊ณผ ์ฌ์ฉ๋ฒ์ ์๋ดํ๋ค.
RealPivot2๋ AI ๋ชจ๋ธ์ด๋ AI ์๋น์ค๋ฅผ ์ ๊ณตํ์ง ์๋๋ค. ์ธ๋ถ LLMยท์์ด์ ํธ์ ์ฐ๋ํ ์ ์๋ ํจ๋(UI), ํผ๋ฒ ์กฐ์ ๋๊ตฌ(tool), ์คํ API, ๊ทธ๋ฆฌ๊ณ RAG ์ฐ๋ ์ง์ ์ ์ ๊ณตํ๋ค. ์ถ๋ก , ์๋ต ์์ฑ, ์ง์ ์ฝํผ์ค(Corpus) ๊ด๋ฆฌ ๋ฑ์ ์ฐ๋ํ ์ธ๋ถ ๋ชจ๋ธ์ด๋ ์๋ฒ์์ ๋ด๋นํ๋ค.
Book ๊ฐ์๋ ํผ๋ฒ ๋ถ(Book)์ ์ฐธ๊ณ ํ๋ค.
RealPivot2๊ฐ ์ ๊ณตํ๋ ๊ฒ
| ๊ตฌ๋ถ | ๋ด์ฉ |
|---|---|
| AI ํจ๋ | ์ง์ ์
๋ ฅยท๋ํยท์ ์ ๋ฌธ๊ตฌ๋ฅผ ํ์ํ๋ UI (ai.panel) |
| ๋ชจ๋ธ ์ฐ๊ฒฐ | ์ธ๋ถ OpenAI/Gemini API ๋๋ ์์ฒด ์๊ฒฉ ์๋ฒ ์ฐ๋ (ai.models) |
| ๋๊ตฌ(tool) | ํ๋ ์ด๋ยทํํฐยท๋ถ์ ๋ฑ ํผ๋ฒ์ ์กฐ์ํ๋ ๋ช ๋ น ์คํค๋ง |
| ์คํ API | book.aiApi๋ก ๋๊ตฌ๋ฅผ ์คํํ๋ ๊ด๋ฌธ |
| RAG ์ฐ๋ | ์ค์ ์ง์ ๊ฒ์(search_features)ยท๋ก์ปฌ ์๋ฒ ๋ฉ ๊ฒ์(useRag)ยท์ปจํ
์คํธ ํ์ผ ๋ก๋ |
AI ๋ผ์ด์ ์คยทAPI ํคยท์๊ฒฉ ์๋ฒยท์ง์๋ฒ ์ด์ค ๊ตฌ์ถยท์๋ต ํ์ง์ RealPivot2 ๋ฒ์๊ฐ ์๋๋ค. ์ฐ๋ ๋ชฉ์ ๊ณผ ์ฌ์ฉ ํ๊ฒฝ์ ๋ง์ถฐ ์ง์ ๊ตฌ์ถํด์ผ ํ๋ค.
์ง์ ์์น
ํผ๋ฒ ๋ถ(Book) ์ค์ ์ ai ์ต์
(PivotAIOptions)์์ ํ์ฑํํ๋ค. ๊ธฐ๋ณธ๊ฐ์ enabled: false์ด๋ค.
| ์์ฑ | ์ญํ |
|---|---|
enabled | AI ์ฐ๋ UIยท๊ธฐ๋ฅ ์ฌ์ฉ ์ฌ๋ถ |
models | ์ฐ๊ฒฐํ ์ธ๋ถ ๋ชจ๋ธ(๋ก์ปฌ API ๋๋ ์๊ฒฉ ์๋ฒ) |
panel | ํจ๋ ์์นยทํฌ๊ธฐยทํ๋กฌํํฐ UI |
resources | ์ปจํ
์คํธ ํ์ผยทRAG ๊ธฐ๋ฅ ๊ฒ์(featureSearch) ๋ฑ |
autoSubmit | ์ ์ ๋ฌธ๊ตฌ ํด๋ฆญ ์ ๋ฐ๋ก ์ ์กํ ์ง ์ฌ๋ถ |
addMode | ํ
์ด๋ธ ์ถ๊ฐ UI ('dialog' | 'page') |
devMode | ๊ฐ๋ฐ์ ๋ชจ๋(๋ชจ๋ธ์ด ๋๊ธด ์ค์ ๋ด์ฉ ํ์) |
const config = {
ai: {
enabled: true,
models: {
type: 'remote',
name: 'my-server',
modelName: 'gpt-4.1',
url: 'https://my-ai.example.com/v1',
headers: {
Authorization: 'Bearer <YOUR_TOKEN>',
},
},
panel: {
position: 'right',
width: 320,
prompter: {
suggestions: [
'ํ๋งค์๋ ์ด์์น๋ฅผ ํ์งํด์ค.',
'ํ๋งค์๋์ ๋ฐ์ดํฐ๋ฐ๋ฅผ ์ ์ฉํด.',
],
},
},
},
tables: {
name: 'sales',
cube: 'sales',
// fields ...
},
};AI ํจ๋
AI ํจ๋์ ์ธ๋ถ LLM๊ณผ ๋ํํ๋ UI ์์ญ์ด๋ค. ai.panel.position ์ต์
์ผ๋ก ์์น๋ฅผ ์ง์ ํ ์ ์๋ค.
position | ํ์ ์์น |
|---|---|
'right'(๊ธฐ๋ณธ) | ๋ถ ์ค๋ฅธ์ชฝ |
'left' | ๋ถ ์ผ์ชฝ |
'float' | ๋ ์๋ ํจ๋(floatBounds๋ก ์ด๊ธฐ ์์นยทํฌ๊ธฐ) |
'inspector' | ์ธ์คํํฐ ํญ |
'explorer' | ํ์๊ธฐ ํญ (ํ์๊ธฐ) |
ai: {
enabled: true,
panel: {
position: 'explorer',
width: 320,
autoDock: true,
prompter: {
greetings: 'ํผ๋ฒ ๊ตฌ์ฑ์ ์์ฐ์ด๋ก ์์ฒญํด ๋ณด์ธ์.',
placeholder: '์: ์ฐจ์ข
์ ํ์ผ๋ก ์ฎ๊ฒจ์ค',
waitingMessage: '๋ถ์ ์ค...',
suggestions: ['๊ตญ๊ฐ๋ฅผ ํํฐ์ ๋ฃ์ด์ค.'],
},
},
}ai.panel.prompter ํญ๋ชฉ์ ์ธ์ฌ๋ง, ์ ์์ด, placeholder ๋ฑ ํจ๋ UI์ ํ์๋ ๋ฌธ๊ตฌ๋ง ์ค์ ํ๋ฉฐ, ์ค์ ๋ต๋ณ์ ์ฐ๋๋ AI ๋ชจ๋ธ์ด ์์ฑํ๋ค.
ํค๋ยทํญ๋ฐ์ AI ๊ด๋ จ ๋ฒํผ์ ai.enabled๊ฐ true์ผ ๋ ํ์๋๋ค.
์ธ๋ถ ๋ชจ๋ธ ์ฐ๊ฒฐ
ai.models์ ๊ฐ๋ฐ์๊ฐ ์ฌ์ฉํ ๋ชจ๋ธ์ ๋ฑ๋กํ๋ค. ๊ฐ์ฒด ํ๋ ๋๋ ๋ฐฐ์ด๋ก ์ง์ ํ ์ ์๋ค.
์๊ฒฉ ์๋ฒ (type: 'remote')
์์ฒด ๋ฐฑ์๋(๋๋ ํ๋ก์)๋ฅผ ๊ฒฝ์ ํด LLM์ ํธ์ถํ๋ ๋ฐฉ์์ด๋ค.
| ์์ฑ | ์ค๋ช |
|---|---|
name | ๋ชจ๋ธ ์๋ณ ์ด๋ฆ |
modelName | ์๋ฒ์์ ์ฐ๋ ๋ชจ๋ธ ์ด๋ฆ |
url | ์๋ฒ ๋ฒ ์ด์ค URL |
headers | Authorization ๋ฑ ์์ฒญ ํค๋ |
commandsPath / contextPath / queryPath | ํ์ ๊ฒฝ๋ก(๊ธฐ๋ณธ commands ยท context ยท query) |
models: {
type: 'remote',
name: 'my-server',
modelName: 'gpt-4.1',
url: 'https://my-ai.example.com/v1',
headers: { Authorization: 'Bearer <YOUR_TOKEN>' },
}์๊ฒฉ ๋ชจ๋ธ์ ์ ์ฒด ์ค์ ํญ๋ชฉ์ Config ยท ai ยท models๋ฅผ ์ฐธ๊ณ ํ๋ค.
๋ธ๋ผ์ฐ์ ์์ provider API ํธ์ถ (type: 'local')
OpenAIยทGemini API๋ฅผ ๋ธ๋ผ์ฐ์ ์์ ์ง์ ํธ์ถํ๋ค. provider๋ 'openai' ๋๋ 'gemini'์ด๋ค.
| ์์ฑ | ์ค๋ช |
|---|---|
provider | 'openai' | 'gemini' |
apiKey | provider API ํค |
modelName | ํธ์ถํ ๋ชจ๋ธ ์ด๋ฆ |
endpoint | ์ปค์คํ base URL(์ ํ) |
models: {
type: 'local',
name: 'gpt',
provider: 'openai',
modelName: 'gpt-4.1',
apiKey: '<YOUR_OPENAI_API_KEY>',
}๋ก์ปฌ ๋ชจ๋ธ์ ์ ์ฒด ์ค์ ํญ๋ชฉ์ Config ยท ai ยท models๋ฅผ ์ฐธ๊ณ ํ๋ค.
ํผ๋ฒ ๋๊ตฌ(tool)
RealPivot2๋ LLM์ด ํผ๋ฒ์ ์ ์ดํ ์ ์๋๋ก ์กฐ์ ๋๊ตฌ(Tool) ํญ๋ชฉ๋ค์ ์ ๊ณตํ๋ค. ๋๊ตฌ ์์ฒด๋ AI๊ฐ ์๋๋ฉฐ, ์ฐ๋๋ AI๊ฐ ํผ๋ฒ์ ๋ณ๊ฒฝํ ๋ ํธ์ถํ๋ ๋ช ๋ น ์คํค๋ง ๋ชฉ๋ก์ด๋ค.
๋ํ ์์๋ ๋ค์๊ณผ ๊ฐ๋ค.
| ๋๊ตฌ | ์ญํ |
|---|---|
move_field / remove_field / add_value_field | ํ๋ ๋ฐฐ์น |
filter_by_member / set_slicer_filter | ํํฐ |
config | ํ ์ด๋ธ ์ต์ ๋ณ๊ฒฝ |
detect_outliers / analyze_growth ๋ฑ | ๋ถ์ |
search_features | ์ค์ ๋ฐฉ๋ฒ RAG ๊ฒ์(resources.featureSearch.url์ด ์์ ๋) |
focus_cell | ์ ํฌ์ปค์ค |
์ต์
๊ตฌ์กฐ๊ฐ ํ์คํ์ง ์์ ๊ฒฝ์ฐ, AI๊ฐ ์์๋ก ์ถ์ธกํ๊ธฐ๋ณด๋ค๋ search_features ๋๊ตฌ๋ก ์ง์๋ฒ ์ด์ค๋ฅผ ๋จผ์ ๊ฒ์ํ ํ ๋ฐํ๋ config๋ฅผ ์ ์ฉํ๋๋ก ๊ตฌ์ฑํ๋ ๊ฒ์ ๊ถ์ฅํ๋ค.
์คํค๋ง ๋ชฉ๋ก์ getAITools() ยท exportAITools() ยท book.aiApi.getToolSchemas()๋ก ํ์ธํ ์ ์๋ค.
import { getAITools, exportAITools } from 'realpivot2';
const tools = getAITools();
const schemaJson = exportAITools();์ธ๋ถ ์์ด์ ํธ์์ ์คํํ๊ธฐ
์์ฒด ์ฑํ
UIยท์์ด์ ํธ๋ฅผ ์ฐ๋ ๊ฒฝ์ฐ์๋ book.aiApi(PivotAIApi)๋ก ๋์ผ ๋๊ตฌ๋ฅผ ์คํํ ์ ์๋ค. ๋ด์ฅ AI ํจ๋์ ์ฐ์ง ์์๋ ๋๋ค.
const api = control.book.aiApi;
const schemas = api.getToolSchemas();
const state = api.serializeState({ include: ['config'] });
const result = await api.execute({
tool: 'move_field',
arguments: {
// ๋๊ตฌ๋ณ ์ธ์
},
reason: '์ฐจ์ข
์ ํ ์์ญ์ผ๋ก ์ด๋ํฉ๋๋ค.',
});์ปจํ ์คํธยท๋๊ตฌยท์ํ ์กฐํ์ ์คํ ์ง์ ์ ์ด API์ ๋ชจ์ฌ ์๋ค. ์์ธ๋ API ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ๋ค.
RAG ยท ๋ฆฌ์์ค
RealPivot2๋ ์ง์๋ฒ ์ด์ค(์ฝํผ์ค)๋ฅผ ๋ด์ฅยทํธ์คํ ํ์ง ์๋๋ค. ๋์ AI๊ฐ ํผ๋ฒ ์ค์ ๋ฐฉ๋ฒ์ ์ค์ค๋ก ์ฐพ๋๋ก RAG ์ฐ๋์ ์ง์ํ๋ค. ์ปจํ ์คํธ ํ์ผยท๊ฒ์ ์๋ํฌ์ธํธยท์๋ฒ ๋ฉ ์ฝํผ์ค๋ ์ฑ์์ ์ ๊ณตํ๋ค.
์ปจํ ์คํธ ํ์ผ
ai.resources๋ก ๋ชจ๋ธ์ ๋๊ธธ ์ค๋ช
๋ฌธ์ ๊ฒฝ๋ก๋ฅผ ์ง์ ํ๋ค.
| ์์ฑ | ๊ธฐ๋ณธ | ์ค๋ช |
|---|---|---|
systemContextPath | 'rag/system_context.md' | ์์คํ ์ปจํ ์คํธ(์ฌ์ฉ ์ค๋ช ) |
developerContextPath | 'rag/developer_context.md' | ๊ฐ๋ฐ์ ์ปจํ ์คํธ |
ํด๋น ๊ฒฝ๋ก์ ๋งํฌ๋ค์ด(.md) ํ์ผ์ ์น ์ ํ๋ฆฌ์ผ์ด์
์์ ์ ๊ทผ(์๋น)ํ ์ ์๋๋ก ์ค์ ํด์ผ ์ ์ ๋ก๋๋๋ค.
์ค์ ๋ฐฉ๋ฒ ๊ฒ์ (featureSearch)
search_features ๋๊ตฌ๊ฐ ํธ์ถํ๋ ์๊ฒฉ RAG ์๋ํฌ์ธํธ์ด๋ค.
ํํธ๋งต์ด๋ ๋ฐ์ดํฐ๋ฐ ๋ฑ ๋ณต์กํ config ๊ตฌ์กฐ๋ฅผ ๋ชจ๋ฅผ ๋, AI๊ฐ ์ง์๋ฒ ์ด์ค๋ฅผ ๊ฒ์ํ์ฌ ๊ฐ์ ธ์จ ์ค์ ์ config ๋๊ตฌ์ ์ ์ฉํ ์ ์๋๋ก ์ง์ํ๋ค.
| ์์ฑ | ์ค๋ช |
|---|---|
featureSearch.url | POST ๋์ URL(ํ์, ์์ผ๋ฉด ๋๊ตฌ ๋ฏธ๋ ธ์ถ) |
featureSearch.headers | Authorization ๋ฑ |
featureSearch.enabled | ์ฌ์ฉ ์ฌ๋ถ(๊ธฐ๋ณธ true) |
์์ฒญยท์๋ต ๊ณ์ฝ์ ๋ค์๊ณผ ๊ฐ๋ค.
- ์์ฒญ:
POSTbody{ query } - ์๋ต:
{ count, grounding }
ai: {
enabled: true,
resources: {
systemContextPath: 'rag/system_context.md',
developerContextPath: 'rag/developer_context.md',
featureSearch: {
url: '/api/feature-search',
headers: { Authorization: 'Bearer <YOUR_TOKEN>' },
},
},
// models, panel ...
}์ ์ฒด ํญ๋ชฉ์ Config ยท ai ยท resources ยท featureSearch๋ฅผ ์ฐธ๊ณ ํ๋ค.
๋ก์ปฌ ๋ชจ๋ธ RAG (useRag)
type: 'local' ๋ชจ๋ธ์์๋ ์ง์๋ง๋ค ์๋ฒ ๋ฉ ์ฝํผ์ค๋ฅผ ๊ฒ์ํด ํ๋กฌํํธ์ ์ฃผ์
ํ ์ ์๋ค.
์ฝํผ์ค ํ์ผ๋ ์ฑ์ด ์๋นํ๋ฉฐ, RealPivot2๋ ๊ฒ์ยท์ฃผ์
๊ฒฝ๋ก๋ง ์ ๊ณตํ๋ค.
| ์์ฑ | ์ค๋ช | ๊ธฐ๋ณธ |
|---|---|---|
useRag | ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ํ๋กฌํํธ์ ์๋ ์ฃผ์ | false |
embeddingModel | ์ง์ ์๋ฒ ๋ฉ ๋ชจ๋ธ(์ฝํผ์ค์ ๋์ผํด์ผ ํจ) | provider๋ณ ๊ธฐ๋ณธ |
ragTopK | ์์ chunk ์ | 5 |
ragEmbeddingsPath | ์๋ฒ ๋ฉ ๋ฐ์ด๋๋ฆฌ(Float32) | provider๋ณ (rag/openai/... ๋ฑ) |
ragChunksPath | ์๋ฌธ chunk JSON | provider๋ณ |
ragMetaPath | ๋ฉํ(count/dim/model) | provider๋ณ |
models: {
type: 'local',
name: 'gpt',
provider: 'openai',
modelName: 'gpt-4.1',
apiKey: '<YOUR_OPENAI_API_KEY>',
useRag: true,
ragTopK: 5,
}ํจ๋ UI ๋ฉ๋ด์์๋ ๋ก์ปฌ ๋ชจ๋ธ์ RAG ํ์ฑํ ์ฌ๋ถ๋ฅผ ์ ํํ ์ ์๋ค.
featureSearch(์๊ฒฉ ๋๊ตฌ ๊ฒ์) ๋ฐฉ์๊ณผ useRag(๋ก์ปฌ ํ๋กฌํํธ ์ฃผ์
) ๋ฐฉ์์ ๋
๋ฆฝ์ ์ผ๋ก ๋์ํ๋ฏ๋ก, ์ํฉ์ ๋ง์ถฐ ๊ฐ๋ณ ๋๋ ์กฐํฉํ์ฌ ์ฌ์ฉํ ์ ์๋ค.
API๋ก ํจ๋ยท์ง์ ๋ค๋ฃจ๊ธฐ
const ai = control.book.ai;
const panel = ai.panel;
const prompter = panel.prompter;
ai.enabled; // true / false
ai.setActiveModel('my-server');
prompter.send('ํ๋งค์๋์ ํํธ๋งต์ ์ ์ฉํด์ค.');
prompter.reset();์ต์ ๋ณ๊ฒฝ ๊ท์น์ ๊ฐ๋ฐ ๊ฐ์ด๋๋ฅผ ์ฐธ๊ณ ํ๋ค.