n2-qln 3.4.1 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +251 -262
- package/README.md +245 -276
- package/dist/index.d.ts +3 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +9 -0
- package/{lib → dist/lib}/config.js +23 -27
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/embedding.d.ts +27 -0
- package/{lib → dist/lib}/embedding.js +39 -47
- package/dist/lib/embedding.js.map +1 -0
- package/dist/lib/executor.d.ts +57 -0
- package/dist/lib/executor.js +175 -0
- package/dist/lib/executor.js.map +1 -0
- package/dist/lib/mcp-discovery.d.ts +83 -0
- package/dist/lib/mcp-discovery.js +203 -0
- package/dist/lib/mcp-discovery.js.map +1 -0
- package/dist/lib/provider-loader.d.ts +13 -0
- package/dist/lib/provider-loader.js +146 -0
- package/dist/lib/provider-loader.js.map +1 -0
- package/dist/lib/registry.d.ts +38 -0
- package/{lib → dist/lib}/registry.js +82 -92
- package/dist/lib/registry.js.map +1 -0
- package/dist/lib/router.d.ts +63 -0
- package/{lib → dist/lib}/router.js +75 -117
- package/dist/lib/router.js.map +1 -0
- package/dist/lib/schema.d.ts +20 -0
- package/{lib → dist/lib}/schema.js +38 -30
- package/dist/lib/schema.js.map +1 -0
- package/dist/lib/store.d.ts +37 -0
- package/dist/lib/store.js +207 -0
- package/dist/lib/store.js.map +1 -0
- package/dist/lib/validator.d.ts +37 -0
- package/dist/lib/validator.js +114 -0
- package/dist/lib/validator.js.map +1 -0
- package/dist/lib/vector-index.d.ts +37 -0
- package/{lib → dist/lib}/vector-index.js +19 -36
- package/dist/lib/vector-index.js.map +1 -0
- package/dist/tools/qln-call.d.ts +41 -0
- package/dist/tools/qln-call.js +353 -0
- package/dist/tools/qln-call.js.map +1 -0
- package/dist/tools/qln-helpers.d.ts +55 -0
- package/dist/tools/qln-helpers.js +88 -0
- package/dist/tools/qln-helpers.js.map +1 -0
- package/dist/types.d.ts +243 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/index.js +3 -79
- package/package.json +12 -5
- package/.github/FUNDING.yml +0 -3
- package/docs/README.md +0 -2
- package/docs/architecture.png +0 -0
- package/lib/executor.js +0 -104
- package/lib/provider-loader.js +0 -126
- package/lib/store.js +0 -217
- package/lib/validator.js +0 -171
- package/tools/qln-call.js +0 -257
package/README.ko.md
CHANGED
|
@@ -4,68 +4,105 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/n2-qln) [](LICENSE) [](https://nodejs.org) [](https://www.npmjs.com/package/n2-qln)
|
|
6
6
|
|
|
7
|
-
**QLN** = **Q**uery **L**ayer **N**etwork — AI와 도구 사이에 위치하는 시맨틱
|
|
7
|
+
**QLN** = **Q**uery **L**ayer **N**etwork — AI와 도구 사이에 위치하는 시맨틱 도구 라우터.
|
|
8
8
|
|
|
9
|
-
> **1,000개 이상의 도구를 1개의 MCP 도구로 라우팅합니다.** AI는
|
|
9
|
+
> **1,000개 이상의 도구를 1개의 MCP 도구로 라우팅합니다.** AI는 라우터만 봅니다 — 1,000개 전체가 아닙니다.
|
|
10
10
|
|
|
11
11
|

|
|
12
12
|
|
|
13
13
|
## 목차
|
|
14
14
|
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
18
|
-
- [설정](#설정)
|
|
15
|
+
- [왜 QLN인가](#왜-qln인가)
|
|
16
|
+
- [v4.1 주요 변경사항](#v41-주요-변경사항)
|
|
17
|
+
- [빠른 시작](#빠른-시작)
|
|
19
18
|
- [작동 방식](#작동-방식)
|
|
20
19
|
- [API 레퍼런스](#api-레퍼런스)
|
|
21
|
-
- [
|
|
22
|
-
- [
|
|
20
|
+
- [MCP 자동 디스커버리](#mcp-자동-디스커버리)
|
|
21
|
+
- [Provider 매니페스트](#provider-매니페스트)
|
|
22
|
+
- [설정](#설정)
|
|
23
23
|
- [프로젝트 구조](#프로젝트-구조)
|
|
24
|
-
- [실전 검증 완료](#실전-검증-완료)
|
|
25
24
|
- [FAQ](#faq)
|
|
26
25
|
- [기여하기](#기여하기)
|
|
27
26
|
|
|
28
|
-
##
|
|
29
|
-
|
|
30
|
-
🔍 **하나의 도구로 모든 것을** — AI는 `n2_qln_call` (~200 토큰)만 봅니다. 1,000개의 개별 도구가 아닙니다. 99.6% 컨텍스트 절감.
|
|
27
|
+
## 왜 QLN인가
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
MCP 도구를 등록할 때마다 컨텍스트 토큰을 소모합니다. 10개? 괜찮습니다. 100개? 느려집니다. **1,000개? 불가능합니다** — 대화 시작 전에 컨텍스트가 가득 찹니다.
|
|
33
30
|
|
|
34
|
-
|
|
31
|
+
QLN이 해결합니다:
|
|
35
32
|
|
|
36
|
-
|
|
33
|
+
1. 모든 도구를 QLN의 SQLite 엔진에 인덱싱
|
|
34
|
+
2. AI는 **도구 1개**만 봅니다: `n2_qln_call` (~200 토큰)
|
|
35
|
+
3. AI가 검색 → 최적 매칭 → 자동 폴백과 함께 실행
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
**결과: ~50,000 토큰 대신 ~200 토큰. 99.6% 절감.**
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
## 기능
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
| 기능 | 설명 |
|
|
42
|
+
|------|------|
|
|
43
|
+
| **1 도구 = 1,000 도구** | AI는 `n2_qln_call` (~200 토큰)만 보고, QLN이 올바른 도구로 라우팅 |
|
|
44
|
+
| **5ms 이하 검색** | 3단계 엔진: 트리거 매칭 → BM25 키워드 → 시맨틱 벡터 |
|
|
45
|
+
| **Auto 모드** | 검색 + 실행 원샷. 신뢰도 게이팅 + 폴백 체인 |
|
|
46
|
+
| **서킷 브레이커** | 실패하는 도구 자동 비활성화, 타임아웃 후 자동 복구 |
|
|
47
|
+
| **MCP 자동 디스커버리** | 외부 MCP 서버 스캔 → 도구 자동 인덱싱 |
|
|
48
|
+
| **부스트 키워드** | 2× BM25 가중치 적용 검색어로 정밀 검색 |
|
|
49
|
+
| **자동 학습 랭킹** | 사용 횟수 + 성공률이 점수에 반영 |
|
|
50
|
+
| **소스 가중치** | 출처별 도구 우선순위 (mcp > plugin > local) |
|
|
51
|
+
| **핫 리로드** | `providers/` 매니페스트 런타임 수정 → 자동 재인덱싱 |
|
|
52
|
+
| **벌크 인젝트** | 한 번의 호출로 수백 개 도구 등록 |
|
|
53
|
+
| **강제 검증** | `verb_target` 네이밍, 최소 설명 길이, 카테고리 제약 |
|
|
54
|
+
| **시맨틱 검색** | 선택적 Ollama 임베딩으로 자연어 매칭 |
|
|
55
|
+
| **네이티브 의존성 제로** | [sql.js](https://github.com/sql-js/sql.js) WASM 기반 — `npm install`이면 끝 |
|
|
56
|
+
| **이중 실행** | 로컬 함수 핸들러 또는 HTTP 프록시 — 혼합 가능 |
|
|
57
|
+
| **TypeScript strict** | v4.0부터 완전 strict 모드 코드베이스 |
|
|
58
|
+
|
|
59
|
+
## v4.1 주요 변경사항
|
|
60
|
+
|
|
61
|
+
### 🔍 MCP 자동 디스커버리
|
|
62
|
+
|
|
63
|
+
연결된 MCP 서버를 스캔하고 도구를 자동 인덱싱 — QLN이 **범용 MCP 허브**가 됩니다.
|
|
43
64
|
|
|
44
|
-
|
|
65
|
+
```javascript
|
|
66
|
+
n2_qln_call({
|
|
67
|
+
action: "discover",
|
|
68
|
+
servers: [
|
|
69
|
+
{ name: "my-server", command: "node", args: ["server.js"] }
|
|
70
|
+
]
|
|
71
|
+
})
|
|
72
|
+
// → my-server에서 47개 도구 발견 (320ms)
|
|
73
|
+
```
|
|
45
74
|
|
|
46
|
-
|
|
75
|
+
### ⚡ 서킷 브레이커
|
|
47
76
|
|
|
48
|
-
|
|
77
|
+
3회 연속 실패 시 도구 자동 비활성화. 60초 후 복구 시도. 연쇄 장애 방지.
|
|
49
78
|
|
|
50
|
-
|
|
79
|
+
```
|
|
80
|
+
closed → 3회 실패 → open (즉시 거부) → 60초 → half-open (재시도) → 성공 → closed
|
|
81
|
+
```
|
|
51
82
|
|
|
52
|
-
|
|
83
|
+
### 🔄 폴백 체인
|
|
53
84
|
|
|
54
|
-
|
|
85
|
+
`auto` 모드에서 최대 3개 후보를 순차 시도. 1순위가 실패하면 자동으로 다음 후보 실행.
|
|
55
86
|
|
|
56
|
-
|
|
87
|
+
```
|
|
88
|
+
auto "알림 보내기" → push_notification 시도 ❌ → send_email 시도 ✅
|
|
89
|
+
```
|
|
57
90
|
|
|
58
|
-
|
|
91
|
+
### 🎯 부스트 키워드
|
|
59
92
|
|
|
60
|
-
|
|
61
|
-
2. AI는 **하나의 도구**만 봅니다: `n2_qln_call` (~200 토큰)
|
|
62
|
-
3. AI가 도구가 필요하면 **검색** → **최적 매칭** → **실행**
|
|
93
|
+
`boostKeywords` 필드로 검색 최적화. BM25 랭킹에서 2배 가중치 적용.
|
|
63
94
|
|
|
64
|
-
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"name": "send_email",
|
|
98
|
+
"description": "이메일을 수신자에게 전송",
|
|
99
|
+
"boostKeywords": "smtp outbound notification mail"
|
|
100
|
+
}
|
|
101
|
+
```
|
|
65
102
|
|
|
66
103
|
---
|
|
67
104
|
|
|
68
|
-
##
|
|
105
|
+
## 빠른 시작
|
|
69
106
|
|
|
70
107
|
```bash
|
|
71
108
|
npm install n2-qln
|
|
@@ -73,20 +110,12 @@ npm install n2-qln
|
|
|
73
110
|
|
|
74
111
|
**요구사항:** Node.js ≥ 18
|
|
75
112
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
## 설정
|
|
81
|
-
|
|
82
|
-
QLN은 MCP 서버입니다. 모든 MCP 호환 AI 클라이언트에 연결할 수 있습니다.
|
|
113
|
+
### MCP 클라이언트 연결
|
|
83
114
|
|
|
84
|
-
|
|
115
|
+
<details>
|
|
116
|
+
<summary><strong>Claude Desktop</strong></summary>
|
|
85
117
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
89
|
-
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
118
|
+
`claude_desktop_config.json` 편집:
|
|
90
119
|
|
|
91
120
|
```json
|
|
92
121
|
{
|
|
@@ -98,12 +127,12 @@ Claude Desktop 설정 파일을 편집합니다:
|
|
|
98
127
|
}
|
|
99
128
|
}
|
|
100
129
|
```
|
|
130
|
+
</details>
|
|
101
131
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
### Cursor
|
|
132
|
+
<details>
|
|
133
|
+
<summary><strong>Cursor</strong></summary>
|
|
105
134
|
|
|
106
|
-
**Settings → MCP Servers → Add Server
|
|
135
|
+
**Settings → MCP Servers → Add Server**:
|
|
107
136
|
|
|
108
137
|
```json
|
|
109
138
|
{
|
|
@@ -112,303 +141,266 @@ Claude Desktop을 재시작하면 `n2_qln_call` 도구가 목록에 나타납니
|
|
|
112
141
|
"args": ["-y", "n2-qln"]
|
|
113
142
|
}
|
|
114
143
|
```
|
|
144
|
+
</details>
|
|
115
145
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
Soul `config.local.js`에 추가:
|
|
119
|
-
|
|
120
|
-
```javascript
|
|
121
|
-
module.exports = {
|
|
122
|
-
mcpServers: {
|
|
123
|
-
'n2-qln': {
|
|
124
|
-
command: 'node',
|
|
125
|
-
args: ['<path-to-qln>/index.js'],
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
npm으로 설치한 경우:
|
|
146
|
+
<details>
|
|
147
|
+
<summary><strong>기타 MCP 클라이언트</strong></summary>
|
|
132
148
|
|
|
133
|
-
|
|
134
|
-
module.exports = {
|
|
135
|
-
mcpServers: {
|
|
136
|
-
'n2-qln': {
|
|
137
|
-
command: 'npx',
|
|
138
|
-
args: ['-y', 'n2-qln'],
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### 기타 MCP 클라이언트
|
|
145
|
-
|
|
146
|
-
QLN은 **stdio 전송** — 표준 MCP 통신 방식을 사용합니다. 모든 MCP 호환 클라이언트에서 연결 가능합니다:
|
|
149
|
+
QLN은 **stdio 전송** — MCP 표준 방식을 사용합니다.
|
|
147
150
|
|
|
148
151
|
```
|
|
149
152
|
command: npx
|
|
150
153
|
args: ["-y", "n2-qln"]
|
|
151
154
|
```
|
|
152
155
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
```
|
|
156
|
-
command: node
|
|
157
|
-
args: ["/absolute/path/to/n2-qln/index.js"]
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
> **💡 팁:** 가장 쉬운 설정 방법? **그냥 AI 에이전트에게 부탁하세요.** *"n2-qln을 내 MCP 설정에 추가해줘"* — 에이전트가 알아서 설정합니다.
|
|
156
|
+
> **팁:** AI 에이전트에게 *"n2-qln을 MCP 설정에 추가해줘"*라고 말하면 됩니다.
|
|
157
|
+
</details>
|
|
161
158
|
|
|
162
159
|
---
|
|
163
160
|
|
|
164
161
|
## 작동 방식
|
|
165
162
|
|
|
166
|
-
### 단계별 예시
|
|
167
|
-
|
|
168
163
|
```
|
|
169
164
|
사용자: "이 페이지 스크린샷 찍어"
|
|
170
165
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
Step 2 → AI 호출: n2_qln_call(action: "exec", tool: "take_screenshot", args: {fullPage: true})
|
|
176
|
-
QLN이 실제 도구로 라우팅 및 실행
|
|
177
|
-
응답: ✅ 스크린샷 저장됨
|
|
166
|
+
AI → n2_qln_call(action: "auto", query: "screenshot page")
|
|
167
|
+
QLN → 3단계 검색 (< 5ms) → take_screenshot (score: 8.0)
|
|
168
|
+
→ 실행 → 필요시 폴백 → 결과 반환
|
|
178
169
|
```
|
|
179
170
|
|
|
180
|
-
AI는 `n2_qln_call`만 사용했습니다. 나머지 999개 도구는 전혀 보지 않았습니다.
|
|
181
|
-
|
|
182
171
|
### 3단계 검색 엔진
|
|
183
172
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
|
187
|
-
|
|
188
|
-
| **
|
|
189
|
-
| **2** | BM25 키워드 | ⚡ 1-3ms | [Okapi BM25](https://en.wikipedia.org/wiki/Okapi_BM25) 랭킹 검색 — IDF 가중치 + 문서 길이 정규화 *(v3.4)* |
|
|
190
|
-
| **3** | 시맨틱 검색 | 🧠 5-15ms | 임베딩 벡터 유사도 검색 *(선택, Ollama 필요)* |
|
|
173
|
+
| 단계 | 방식 | 속도 | 상세 |
|
|
174
|
+
|:---:|--------|:---:|------|
|
|
175
|
+
| **1** | 트리거 매칭 | <1ms | 도구 이름과 트리거의 정확 키워드 매칭 |
|
|
176
|
+
| **2** | BM25 키워드 | 1-3ms | [Okapi BM25](https://en.wikipedia.org/wiki/Okapi_BM25) — IDF 가중치, 길이 정규화, `boostKeywords` 2× 부스트 |
|
|
177
|
+
| **3** | 시맨틱 검색 | 5-15ms | [Ollama](https://ollama.ai) 임베딩 벡터 유사도 *(선택)* |
|
|
191
178
|
|
|
192
|
-
|
|
179
|
+
결과 병합 후 랭킹:
|
|
193
180
|
|
|
194
181
|
```
|
|
195
|
-
final_score =
|
|
196
|
-
+
|
|
197
|
-
+ semantic_score × 2.0
|
|
198
|
-
+ log2(usage_count + 1) × 0.5
|
|
199
|
-
+ success_rate × 1.0
|
|
182
|
+
final_score = trigger × 3.0 + bm25 × 1.0 + semantic × 2.0
|
|
183
|
+
+ log₂(usage + 1) × 0.5 + success_rate × 1.0
|
|
200
184
|
```
|
|
201
185
|
|
|
202
|
-
많이 사용되고 성공률이 높은 도구가 시간이 지날수록 상위에 랭크됩니다.
|
|
203
|
-
|
|
204
186
|
---
|
|
205
187
|
|
|
206
188
|
## API 레퍼런스
|
|
207
189
|
|
|
208
|
-
QLN은 **하나의 MCP 도구** — `n2_qln_call` —
|
|
190
|
+
QLN은 **하나의 MCP 도구** — `n2_qln_call` — 9개 액션을 제공합니다.
|
|
191
|
+
|
|
192
|
+
### auto — 검색 + 실행 (원샷)
|
|
209
193
|
|
|
210
|
-
|
|
194
|
+
권장 액션. 검색 → 최적 매칭 → 폴백 체인으로 실행.
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
n2_qln_call({
|
|
198
|
+
action: "auto",
|
|
199
|
+
query: "스크린샷 찍어", // 자연어 (필수)
|
|
200
|
+
args: { fullPage: true } // 매칭된 도구에 전달 (선택)
|
|
201
|
+
})
|
|
202
|
+
// → [auto] "스크린샷 찍어" → take_screenshot (score: 8.0, 2ms 검색 + 150ms 실행)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**신뢰도 게이트:** 최고 점수가 2.0 미만이면 실행 대신 검색 결과만 반환 — 잘못된 실행 방지.
|
|
206
|
+
|
|
207
|
+
**폴백 체인:** 1순위가 실패하면 자동으로 2, 3순위까지 시도 후 포기.
|
|
208
|
+
|
|
209
|
+
### search — 도구 검색
|
|
211
210
|
|
|
212
211
|
```javascript
|
|
213
212
|
n2_qln_call({
|
|
214
213
|
action: "search",
|
|
215
|
-
query: "
|
|
216
|
-
|
|
217
|
-
topK: 5 // 최대 결과 수, 기본: 5 (선택)
|
|
214
|
+
query: "이메일 보내기",
|
|
215
|
+
topK: 5 // 최대 결과 (기본: 5, 최대: 20)
|
|
218
216
|
})
|
|
219
217
|
```
|
|
220
218
|
|
|
221
|
-
### exec —
|
|
219
|
+
### exec — 특정 도구 실행
|
|
222
220
|
|
|
223
221
|
```javascript
|
|
224
222
|
n2_qln_call({
|
|
225
223
|
action: "exec",
|
|
226
|
-
tool: "take_screenshot",
|
|
227
|
-
args: {
|
|
228
|
-
fullPage: true,
|
|
229
|
-
format: "png"
|
|
230
|
-
}
|
|
224
|
+
tool: "take_screenshot",
|
|
225
|
+
args: { fullPage: true, format: "png" }
|
|
231
226
|
})
|
|
232
227
|
```
|
|
233
228
|
|
|
234
|
-
### create —
|
|
229
|
+
### create — 도구 등록
|
|
235
230
|
|
|
236
231
|
```javascript
|
|
237
232
|
n2_qln_call({
|
|
238
233
|
action: "create",
|
|
239
|
-
name: "read_pdf",
|
|
240
|
-
description: "
|
|
241
|
-
category: "data",
|
|
242
|
-
|
|
243
|
-
tags: ["pdf", "read", "extract"
|
|
244
|
-
|
|
245
|
-
"read this PDF file",
|
|
246
|
-
"extract text from PDF",
|
|
247
|
-
"open the PDF"
|
|
248
|
-
],
|
|
249
|
-
endpoint: "http://127.0.0.1:3100", // 선택, HTTP 기반 도구용
|
|
250
|
-
toolSchema: { filePath: { type: "string" } } // 선택, 입력 스키마
|
|
234
|
+
name: "read_pdf", // verb_target 형식 (필수)
|
|
235
|
+
description: "PDF에서 텍스트를 추출합니다", // 최소 10자 (필수)
|
|
236
|
+
category: "data", // web|data|file|dev|ai|capture|misc
|
|
237
|
+
boostKeywords: "pdf extract parse document", // BM25 부스트 검색어
|
|
238
|
+
tags: ["pdf", "read", "extract"],
|
|
239
|
+
endpoint: "http://127.0.0.1:3100" // HTTP 기반 도구용
|
|
251
240
|
})
|
|
252
241
|
```
|
|
253
242
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
| 규칙 | 요구사항 | 예시 |
|
|
257
|
-
|------|---------|------|
|
|
258
|
-
| **이름** | `verb_target` 형식 (소문자 + 밑줄) | `read_pdf`, `take_screenshot` |
|
|
259
|
-
| **설명** | 최소 10자 | `"Read and extract text from PDF files"` |
|
|
260
|
-
| **카테고리** | 유효한 카테고리 중 하나 | `"data"` |
|
|
261
|
-
| **고유성** | 중복 이름 불가 | — |
|
|
262
|
-
|
|
263
|
-
**유효 카테고리:** `web` · `data` · `file` · `dev` · `ai` · `capture` · `misc`
|
|
264
|
-
|
|
265
|
-
### update — 기존 도구 수정
|
|
243
|
+
### inject — 벌크 등록
|
|
266
244
|
|
|
267
245
|
```javascript
|
|
268
246
|
n2_qln_call({
|
|
269
|
-
action: "
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
247
|
+
action: "inject",
|
|
248
|
+
source: "my-plugin",
|
|
249
|
+
tools: [
|
|
250
|
+
{ name: "tool_a", description: "A를 수행합니다", category: "misc" },
|
|
251
|
+
{ name: "tool_b", description: "B를 수행합니다", category: "dev" }
|
|
252
|
+
]
|
|
274
253
|
})
|
|
275
254
|
```
|
|
276
255
|
|
|
277
|
-
|
|
256
|
+
### discover — MCP 서버 스캔
|
|
257
|
+
|
|
258
|
+
[MCP 자동 디스커버리](#mcp-자동-디스커버리) 참조.
|
|
278
259
|
|
|
279
|
-
### delete
|
|
260
|
+
### update / delete / stats
|
|
280
261
|
|
|
281
262
|
```javascript
|
|
282
|
-
//
|
|
283
|
-
n2_qln_call({
|
|
284
|
-
action: "delete",
|
|
285
|
-
tool: "read_pdf"
|
|
286
|
-
})
|
|
263
|
+
// 필드 수정
|
|
264
|
+
n2_qln_call({ action: "update", tool: "read_pdf", description: "향상된 PDF 리더" })
|
|
287
265
|
|
|
288
|
-
//
|
|
289
|
-
n2_qln_call({
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
266
|
+
// 이름 또는 provider로 삭제
|
|
267
|
+
n2_qln_call({ action: "delete", tool: "read_pdf" })
|
|
268
|
+
n2_qln_call({ action: "delete", provider: "pdf-tools" })
|
|
269
|
+
|
|
270
|
+
// 시스템 통계 (서킷 브레이커 상태 포함)
|
|
271
|
+
n2_qln_call({ action: "stats" })
|
|
294
272
|
```
|
|
295
273
|
|
|
296
274
|
---
|
|
297
275
|
|
|
298
|
-
##
|
|
276
|
+
## MCP 자동 디스커버리
|
|
299
277
|
|
|
300
|
-
|
|
278
|
+
v4.1의 킬러 피처. MCP 서버를 연결하면 QLN이 모든 도구를 자동으로 인덱싱합니다.
|
|
301
279
|
|
|
302
280
|
```javascript
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
},
|
|
311
|
-
};
|
|
281
|
+
n2_qln_call({
|
|
282
|
+
action: "discover",
|
|
283
|
+
servers: [
|
|
284
|
+
{ name: "n2-soul", command: "node", args: ["path/to/soul/index.js"] },
|
|
285
|
+
{ name: "github", command: "npx", args: ["-y", "@modelcontextprotocol/server-github"] }
|
|
286
|
+
]
|
|
287
|
+
})
|
|
312
288
|
```
|
|
313
289
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
290
|
+
**처리 과정:**
|
|
291
|
+
1. QLN이 stdio로 각 서버에 연결
|
|
292
|
+
2. `tools/list`로 모든 도구 조회
|
|
293
|
+
3. `mcp__서버명__도구명` 형식으로 QLN 인덱스에 등록
|
|
294
|
+
4. 도구 이름과 설명에서 `boostKeywords` 자동 생성
|
|
295
|
+
5. 실행을 위해 연결 유지
|
|
317
296
|
|
|
318
|
-
|
|
297
|
+
**재디스커버리는 멱등** — 다시 실행하면 기존 항목 삭제 후 재등록.
|
|
319
298
|
|
|
320
|
-
|
|
299
|
+
---
|
|
321
300
|
|
|
322
|
-
|
|
301
|
+
## Provider 매니페스트
|
|
323
302
|
|
|
324
|
-
|
|
303
|
+
`providers/`에 JSON 파일을 넣으면 부팅 시 자동 인덱싱. 코드 수정 불필요.
|
|
325
304
|
|
|
326
|
-
|
|
305
|
+
```json
|
|
306
|
+
{
|
|
307
|
+
"provider": "my-tools",
|
|
308
|
+
"version": "1.0.0",
|
|
309
|
+
"tools": [
|
|
310
|
+
{
|
|
311
|
+
"name": "send_email",
|
|
312
|
+
"description": "수신자에게 이메일을 전송합니다",
|
|
313
|
+
"category": "communication",
|
|
314
|
+
"triggers": ["email", "send", "mail"],
|
|
315
|
+
"boostKeywords": "smtp outbound notification"
|
|
316
|
+
}
|
|
317
|
+
]
|
|
318
|
+
}
|
|
319
|
+
```
|
|
327
320
|
|
|
328
|
-
|
|
321
|
+
핫 리로드: QLN 실행 중 매니페스트 수정 → 자동 반영.
|
|
329
322
|
|
|
330
|
-
|
|
331
|
-
ollama pull nomic-embed-text
|
|
332
|
-
```
|
|
323
|
+
---
|
|
333
324
|
|
|
334
|
-
|
|
325
|
+
## 설정
|
|
335
326
|
|
|
336
|
-
`config.local.js` 생성:
|
|
327
|
+
설정 없이 바로 동작합니다. 커스터마이즈하려면 `config.local.js` 생성:
|
|
337
328
|
|
|
338
329
|
```javascript
|
|
339
330
|
module.exports = {
|
|
331
|
+
dataDir: './data',
|
|
332
|
+
|
|
333
|
+
// Stage 3 시맨틱 검색 (선택 — Stage 1+2는 이것 없이 작동)
|
|
340
334
|
embedding: {
|
|
341
335
|
enabled: true,
|
|
342
336
|
provider: 'ollama',
|
|
343
|
-
model: 'nomic-embed-text',
|
|
337
|
+
model: 'nomic-embed-text', // 다국어는 'bge-m3'
|
|
344
338
|
baseUrl: 'http://127.0.0.1:11434',
|
|
345
339
|
},
|
|
346
|
-
};
|
|
347
|
-
```
|
|
348
340
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
`nomic-embed-text`는 영어에 최적화되어 있습니다. **한국어, 일본어, 중국어** 등 다른 언어를 사용한다면 다국어 모델로 교체하세요:
|
|
341
|
+
// 도구 실행
|
|
342
|
+
executor: {
|
|
343
|
+
timeout: 20000, // 실행 타임아웃 (ms)
|
|
344
|
+
circuitBreaker: {
|
|
345
|
+
failureThreshold: 3, // 연속 실패 횟수 → 비활성화
|
|
346
|
+
recoveryTimeout: 60000, // 복구 시도까지 대기 시간 (ms)
|
|
347
|
+
},
|
|
348
|
+
},
|
|
359
349
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
350
|
+
// 소스 가중치 (v4.0)
|
|
351
|
+
// 높은 가중치 = 검색 결과에서 높은 우선순위
|
|
352
|
+
search: {
|
|
353
|
+
sourceWeights: {
|
|
354
|
+
mcp: 1.5, // MCP 디스커버리 도구 최우선
|
|
355
|
+
provider: 1.2, // Provider 매니페스트 도구
|
|
356
|
+
local: 1.0, // 수동 생성 도구 (기본)
|
|
357
|
+
},
|
|
358
|
+
},
|
|
363
359
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
enabled: true,
|
|
369
|
-
model: 'bge-m3', // 다국어 지원 (100개 이상 언어)
|
|
360
|
+
// Provider 자동 인덱싱
|
|
361
|
+
providers: {
|
|
362
|
+
enabled: true, // 부팅 시 providers/*.json 자동 로드
|
|
363
|
+
dir: './providers', // 매니페스트 디렉토리
|
|
370
364
|
},
|
|
371
365
|
};
|
|
372
366
|
```
|
|
373
367
|
|
|
374
|
-
|
|
368
|
+
> `config.local.js`는 gitignore 처리. 클라우드 동기화: `dataDir`을 Google Drive / OneDrive / NAS로 지정.
|
|
375
369
|
|
|
376
|
-
###
|
|
370
|
+
### 시맨틱 검색 (선택)
|
|
377
371
|
|
|
378
|
-
|
|
372
|
+
Ollama 없이도 Stage 1 + 2로 충분한 결과를 제공합니다.
|
|
379
373
|
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
};
|
|
374
|
+
```bash
|
|
375
|
+
ollama pull nomic-embed-text # 영어 최적화
|
|
376
|
+
# 또는
|
|
377
|
+
ollama pull bge-m3 # 다국어 (100개 이상 언어)
|
|
385
378
|
```
|
|
386
379
|
|
|
387
|
-
[n2-soul 클라우드 스토리지](https://github.com/choihyunsus/soul#%EF%B8%8F-cloud-storage--store-your-ai-memory-anywhere)와 동일한 방식입니다. SQLite 파일이 해당 폴더에 저장되고, 동기화 서비스가 나머지를 처리합니다.
|
|
388
|
-
|
|
389
380
|
---
|
|
390
381
|
|
|
391
382
|
## 프로젝트 구조
|
|
392
383
|
|
|
393
384
|
```
|
|
394
385
|
n2-qln/
|
|
395
|
-
├──
|
|
396
|
-
├──
|
|
397
|
-
│ ├──
|
|
398
|
-
│
|
|
399
|
-
│
|
|
400
|
-
│
|
|
401
|
-
│
|
|
402
|
-
│
|
|
403
|
-
│
|
|
404
|
-
│
|
|
405
|
-
│
|
|
406
|
-
│
|
|
407
|
-
├──
|
|
408
|
-
│
|
|
409
|
-
|
|
410
|
-
├──
|
|
411
|
-
|
|
386
|
+
├── src/
|
|
387
|
+
│ ├── index.ts # MCP 서버 진입점
|
|
388
|
+
│ ├── types.ts # 공유 타입 정의
|
|
389
|
+
│ └── lib/
|
|
390
|
+
│ ├── config.ts # 설정 로더
|
|
391
|
+
│ ├── store.ts # SQLite 엔진 (sql.js WASM)
|
|
392
|
+
│ ├── schema.ts # 도구 정규화 + boostKeywords 빌더
|
|
393
|
+
│ ├── validator.ts # 강제 검증 (이름, 설명, 카테고리)
|
|
394
|
+
│ ├── registry.ts # 도구 CRUD + 사용량 추적 + 서킷 브레이커 통계
|
|
395
|
+
│ ├── router.ts # 3단계 병렬 검색 (BM25)
|
|
396
|
+
│ ├── vector-index.ts # Float32 centroid hierarchy
|
|
397
|
+
│ ├── embedding.ts # Ollama 임베딩 클라이언트
|
|
398
|
+
│ ├── executor.ts # HTTP/함수 실행기 + 서킷 브레이커
|
|
399
|
+
│ ├── mcp-discovery.ts # MCP 자동 디스커버리 엔진
|
|
400
|
+
│ └── provider-loader.ts
|
|
401
|
+
├── providers/ # 도구 매니페스트 (부팅 시 자동 인덱싱)
|
|
402
|
+
├── config.local.js # 로컬 오버라이드 (gitignored)
|
|
403
|
+
└── data/ # SQLite 데이터베이스 (gitignored)
|
|
412
404
|
```
|
|
413
405
|
|
|
414
406
|
## 기술 스택
|
|
@@ -416,46 +408,43 @@ n2-qln/
|
|
|
416
408
|
| 컴포넌트 | 기술 | 이유 |
|
|
417
409
|
|-----------|-----------|------|
|
|
418
410
|
| 런타임 | Node.js ≥ 18 | MCP SDK 호환성 |
|
|
419
|
-
| 데이터베이스 | SQLite via [sql.js](https://github.com/sql-js/sql.js) (WASM) | 네이티브 의존성 제로, 크로스
|
|
420
|
-
| 임베딩 | [Ollama](https://ollama.ai)
|
|
421
|
-
| 프로토콜 | [MCP](https://modelcontextprotocol.io)
|
|
422
|
-
|
|
|
411
|
+
| 데이터베이스 | SQLite via [sql.js](https://github.com/sql-js/sql.js) (WASM) | 네이티브 의존성 제로, 크로스 플랫폼 |
|
|
412
|
+
| 임베딩 | [Ollama](https://ollama.ai) | 로컬, 빠름, 무료, 선택사항 |
|
|
413
|
+
| 프로토콜 | [MCP](https://modelcontextprotocol.io) | 표준 AI 도구 프로토콜 |
|
|
414
|
+
| 언어 | TypeScript (strict) | 타입 안전, 유지보수성 |
|
|
423
415
|
|
|
424
416
|
## 관련 프로젝트
|
|
425
417
|
|
|
426
418
|
| 프로젝트 | 관계 |
|
|
427
419
|
|---------|------|
|
|
428
|
-
| [n2-soul](https://github.com/choihyunsus/soul) | AI 에이전트 오케스트레이터 — QLN은 Soul의
|
|
420
|
+
| [n2-soul](https://github.com/choihyunsus/soul) | AI 에이전트 오케스트레이터 — QLN은 Soul의 도구 브레인 |
|
|
429
421
|
|
|
430
422
|
## 실전 검증 완료
|
|
431
423
|
|
|
432
|
-
|
|
424
|
+
QLN은 [n2-soul](https://github.com/choihyunsus/soul)의 핵심 도구 라우터로 **2개월 이상 운영 환경에서 검증**되었습니다. 프로토타입이 아니라 매일 사용하는 실전 도구입니다.
|
|
433
425
|
|
|
434
|
-
**Rose**
|
|
435
|
-
|
|
436
|
-
문제가 있거나 아이디어가 있다면 이슈를 열어주세요. 여러분의 활용 사례를 듣고 싶습니다.
|
|
426
|
+
**Rose** 제작 — N2의 첫 번째 AI 에이전트.
|
|
437
427
|
|
|
438
428
|
## FAQ
|
|
439
429
|
|
|
440
|
-
**"왜
|
|
430
|
+
**"왜 도구 1개만 쓰나요?"**
|
|
441
431
|
|
|
442
|
-
|
|
432
|
+
컨텍스트 토큰 때문입니다. 도구 정의 하나당 50~200 토큰. 100개 = 대화 시작 전 10,000 토큰 소모. QLN으로 1,000개 이상의 도구를 ~200 토큰으로 사용합니다.
|
|
443
433
|
|
|
444
|
-
|
|
434
|
+
**"검색이 잘못된 도구를 고르면?"**
|
|
445
435
|
|
|
446
|
-
|
|
436
|
+
폴백 체인 (v4.1)이 자동으로 다음 후보를 시도합니다. 게다가 자동 학습 — 자주 사용되고 성공률 높은 도구가 상위에 오릅니다.
|
|
447
437
|
|
|
448
|
-
|
|
438
|
+
**"Ollama가 꼭 필요한가요?"**
|
|
449
439
|
|
|
450
|
-
1
|
|
451
|
-
2. 기능 브랜치를 생성합니다 (`git checkout -b feature/amazing-feature`)
|
|
452
|
-
3. 변경사항을 커밋합니다 (`git commit -m 'feat: add amazing feature'`)
|
|
453
|
-
4. 브랜치에 Push합니다 (`git push origin feature/amazing-feature`)
|
|
454
|
-
5. Pull Request를 엽니다
|
|
440
|
+
아닙니다. Stage 1 (트리거) + Stage 2 (BM25)가 대부분의 경우를 처리합니다. Ollama는 엣지 케이스에 시맨틱 이해를 추가 — 있으면 좋지만 필수는 아닙니다.
|
|
455
441
|
|
|
456
|
-
##
|
|
442
|
+
## 기여하기
|
|
457
443
|
|
|
458
|
-
|
|
444
|
+
1. 저장소를 Fork합니다
|
|
445
|
+
2. 기능 브랜치 생성 (`git checkout -b feature/amazing-feature`)
|
|
446
|
+
3. 커밋 (`git commit -m 'feat: add amazing feature'`)
|
|
447
|
+
4. Push 후 PR을 엽니다
|
|
459
448
|
|
|
460
449
|
## 라이선스
|
|
461
450
|
|
|
@@ -465,6 +454,6 @@ Apache-2.0
|
|
|
465
454
|
|
|
466
455
|
> *"1,000개 도구를 200 토큰으로. 이건 최적화가 아니라 패러다임 전환이다."*
|
|
467
456
|
|
|
468
|
-
|
|
457
|
+
🔗 [nton2.com](https://nton2.com) · [npm](https://www.npmjs.com/package/n2-qln) · lagi0730@gmail.com
|
|
469
458
|
|
|
470
|
-
<sub
|
|
459
|
+
<sub>Rose가 만들었습니다 — N2의 첫 번째 AI 에이전트. 하루에 수백 번 QLN으로 검색하고, 이 README도 직접 작성했습니다.</sub>
|