@keo-ai/axiom 0.1.0 → 0.1.1

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.md CHANGED
@@ -35,6 +35,8 @@ Axiom 是 **keo** 的 LLM 底座库。它不碰业务编排(意图路由、节
35
35
  │ └──────────┘ ┌─────────────┐ │
36
36
  │ │ RAG基础 │ │
37
37
  │ │ Embedding │ │
38
+ │ │ pgvector │ │
39
+ │ │ rerank │ │
38
40
  │ └─────────────┘ │
39
41
  └────────────────────────────────────────────┘
40
42
  ```
@@ -45,7 +47,7 @@ Axiom 是 **keo** 的 LLM 底座库。它不碰业务编排(意图路由、节
45
47
  - ✅ LLM 统一调用 + Provider 故障转移
46
48
  - ✅ Function Call Loop 的驱动、记录、校验、副作用传播
47
49
  - ✅ 记忆系统的抽象与基础实现
48
- - ✅ RAG 基础(Embedding / 向量检索)
50
+ - ✅ RAG 基础(Embedding / pgvector 向量检索 / rerank)
49
51
  - ✅ 基础配置中心
50
52
 
51
53
  **Axiom 不做:**
@@ -60,6 +62,32 @@ Axiom 是 **keo** 的 LLM 底座库。它不碰业务编排(意图路由、节
60
62
 
61
63
  ---
62
64
 
65
+ ## 安装
66
+
67
+ ```bash
68
+ npm install @keo-ai/axiom pg
69
+ ```
70
+
71
+ > `pg` 是 peer dependency,用于 EmbeddingSearch 模块的 pgvector 连接。
72
+
73
+ 在你的项目根目录创建 `.env` 文件:
74
+
75
+ ```bash
76
+ BAILIAN_API_KEY=your-api-key
77
+ ```
78
+
79
+ 然后在你的项目入口文件顶部引入 `dotenv` 以加载环境变量:
80
+
81
+ ```ts
82
+ import 'dotenv/config';
83
+
84
+ // 或指定路径
85
+ import { config } from 'dotenv';
86
+ config({ path: '.env.local' });
87
+ ```
88
+
89
+ ---
90
+
63
91
  ## Predict 模块(LLM 调用)
64
92
 
65
93
  Predict 是 Axiom 的 LLM 调用层。它封装了 Provider 连接、请求组装、故障转移和返回解析,让上层只需关心「用什么模型、传什么消息」。
@@ -78,7 +106,7 @@ export BAILIAN_API_KEY="your-api-key"
78
106
  import { LLM } from '@keo-ai/axiom';
79
107
 
80
108
  const res = await LLM.predict({ model: 'qwen3.7-max', prompt: '你好' });
81
- console.log(res.content);
109
+ console.log(res);
82
110
  ```
83
111
 
84
112
  ### API 概览
@@ -96,9 +124,8 @@ console.log(res.content);
96
124
 
97
125
  | `responseFormat` | 返回值 | 说明 |
98
126
  |---|---|---|
99
- | `'text'` | `string` | 直接返回模型输出的文本 |
127
+ | 未设置 / `'text'` | `string` | 直接返回模型输出的文本 |
100
128
  | `'json'` | `any` | 自动 `JSON.parse` |
101
- | 未设置 | `LLMResponse` | 完整响应(含 `content`、`usage`、`model`) |
102
129
 
103
130
  ```ts
104
131
  // text → string
@@ -170,6 +197,212 @@ try {
170
197
 
171
198
  ---
172
199
 
200
+ ## EmbeddingSearch 模块(向量检索)
201
+
202
+ EmbeddingSearch 是 Axiom 的 RAG 底座。它封装了 `query → embedding → pgvector 检索 → [可选 rerank]` 的完整链路,只需一行代码即可实现语义检索。
203
+
204
+ ### 需要的列
205
+
206
+ 使用 `EmbeddingSearch` 前,先创建 pgvector 表。**只有 `embedding` 列是必需的**(默认列名 `embedding`,可通过 `embeddingColumn` 改),其余列完全自由:
207
+
208
+ | 列名 | 类型 | 说明 |
209
+ |---|---|---|
210
+ | `embedding` | `vector(1024)` | 向量字段,必需,默认列名 `embedding`,可通过 `embeddingColumn` 自定义 |
211
+ | *(任意)* | *(任意)* | 你自己的业务列,可直接用于过滤和返回 |
212
+
213
+ **开 rerank 时的额外要求**:需指定 `rerankKey` 告诉系统哪一列是文本内容(默认 `'content'`)。
214
+
215
+ ```sql
216
+ CREATE EXTENSION IF NOT EXISTS vector;
217
+
218
+ -- 最简表:不开 rerank,只有向量和业务列
219
+ CREATE TABLE products (
220
+ product_id SERIAL PRIMARY KEY,
221
+ name VARCHAR(100),
222
+ category VARCHAR(50),
223
+ price NUMERIC,
224
+ embedding VECTOR(1024)
225
+ );
226
+
227
+ -- 开 rerank 的表:有文本列供 rerank 使用
228
+ CREATE TABLE documents (
229
+ doc_id SERIAL PRIMARY KEY,
230
+ title VARCHAR(100),
231
+ body TEXT, -- rerankKey: 'body'
232
+ embedding VECTOR(1024)
233
+ );
234
+
235
+ CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops);
236
+ ```
237
+
238
+ ### 快速开始
239
+
240
+ 不传 `select` 时默认返回所有列(`SELECT *`):
241
+
242
+ ```ts
243
+ import { EmbeddingSearch } from '@keo-ai/axiom';
244
+ import { Pool } from 'pg';
245
+
246
+ const pool = new Pool({ connectionString: 'postgresql://...' });
247
+
248
+ // 默认 SELECT *,返回所有列
249
+ const results = await EmbeddingSearch.query('衣服质量怎么样', {
250
+ tableName: 'documents',
251
+ }, pool);
252
+
253
+ for (const r of results) {
254
+ console.log(r.id, r.content, r.embeddingScore, r.rerankScore);
255
+ }
256
+ ```
257
+
258
+ 指定 `select` 只返回需要的列:
259
+
260
+ ```ts
261
+ const results = await EmbeddingSearch.query('衣服质量怎么样', {
262
+ tableName: 'documents',
263
+ select: ['id', 'category', 'price'],
264
+ }, pool);
265
+
266
+ // 只返回 id、category、price 三列,以及 embeddingScore 和 rerankScore
267
+ for (const r of results) {
268
+ console.log(r.category, r.price, r.embeddingScore);
269
+ }
270
+ ```
271
+
272
+ 文本列通过 `rerankKey` 指定:
273
+
274
+ ```ts
275
+ const results = await EmbeddingSearch.query('衣服质量怎么样', {
276
+ tableName: 'documents',
277
+ rerankKey: 'body',
278
+ }, pool);
279
+ ```
280
+
281
+ ### 返回结构
282
+
283
+ `SearchResult` 不固定字段,取决于你查询了哪些列,但始终包含两个 score:
284
+
285
+ ```ts
286
+ interface SearchResult {
287
+ [key: string]: unknown; // 你 select 的列
288
+ embeddingScore: number; // cosine similarity,范围 -1 ~ 1
289
+ rerankScore?: number; // 仅 enableRerank 时存在
290
+ }
291
+ ```
292
+
293
+ ### EmbeddingSearch.query 配置项
294
+
295
+ | 配置项 | 类型 | 默认值 | 说明 |
296
+ |---|---|---|---|
297
+ | `tableName` | `string` | — | 检索表名(必填) |
298
+ | `enableRerank` | `boolean` | `true` | 是否启用 rerank |
299
+ | `embeddingTopK` | `number` | `10` | pgvector 检索返回条数(rerank 的候选集) |
300
+ | `finalTopN` | `number` | `5` | 最终结果条数 |
301
+ | `embeddingThreshold` | `number` | `0.8` | embedding 相似度阈值过滤(cosine similarity) |
302
+ | `rerankThreshold` | `number` | `0.1` | rerank 分数阈值过滤 |
303
+ | `embeddingColumn` | `string` | `'embedding'` | 向量列名 |
304
+ | `rerankKey` | `string` | `'content'` | 启用 rerank 时,用于重排序的文本列名 |
305
+ | `dimensions` | `number` | `1024` | embedding 输出维度(1 ~ 1024) |
306
+ | `select` | `string[]` | `undefined` | 指定返回哪些列,不传则 `SELECT *` |
307
+ | `filter` | `Record<string, unknown>` | — | 对表独立列做等值/范围过滤 |
308
+
309
+ ### 过滤
310
+
311
+ 支持对表的任意独立列做等值和范围过滤:
312
+
313
+ ```ts
314
+ // 等值过滤
315
+ const results = await EmbeddingSearch.query('query', {
316
+ filter: { category: '衣服', shop: '旗舰店' },
317
+ }, pool);
318
+
319
+ // 混合:等值 + 数值范围
320
+ const ranked = await EmbeddingSearch.query('query', {
321
+ filter: {
322
+ category: '衣服',
323
+ price: { $gt: 100, $lt: 500 },
324
+ rating: { $gte: 4 },
325
+ },
326
+ }, pool);
327
+
328
+ // 数组:IN 查询
329
+ const multi = await EmbeddingSearch.query('query', {
330
+ filter: {
331
+ category: ['衣服', '裤子', '鞋子'],
332
+ },
333
+ }, pool);
334
+ ```
335
+
336
+ ### 生成 Embedding
337
+
338
+ 如果你只需要把文本转成向量,直接用 `embed`:
339
+
340
+ ```ts
341
+ import { embed } from '@keo-ai/axiom';
342
+
343
+ const vector = await embed('衣服质量怎么样');
344
+ // vector: number[],默认 1024 维
345
+
346
+ // 指定维度(1 ~ 1024)
347
+ const vector256 = await embed('衣服质量怎么样', 256);
348
+ ```
349
+
350
+ | 参数 | 类型 | 默认值 | 说明 |
351
+ |---|---|---|---|
352
+ | `text` | `string` | — | 要转成向量的文本(必填) |
353
+ | `dimensions` | `number` | `1024` | 输出维度(1 ~ 1024) |
354
+
355
+ ### 纯向量检索(已有向量)
356
+
357
+ `EmbeddingSearch.query` 会自动把文本转成向量并做 rerank。如果你**已经有了向量**(比如自己生成的 embedding),只想做最原始的 pgvector 近邻搜索,用 `EmbeddingSearch.vectorSearch`:
358
+
359
+ ```ts
360
+ import { EmbeddingSearch } from '@keo-ai/axiom';
361
+
362
+ const results = await EmbeddingSearch.vectorSearch({
363
+ pool,
364
+ tableName: 'documents',
365
+ embeddingColumn: 'vec',
366
+ vector: queryVector, // 你自己生成的向量
367
+ topK: 10,
368
+ threshold: 0.7,
369
+ filter: { category: '衣服' },
370
+ });
371
+ ```
372
+
373
+ > `EmbeddingSearch.vectorSearch` **不做 embedding,也不做 rerank**,只负责拿你给的向量去 pgvector 里搜近邻。
374
+
375
+ | 参数 | 类型 | 默认值 | 说明 |
376
+ |---|---|---|---|
377
+ | `pool` | `Pool` | — | pg 连接池(必填) |
378
+ | `tableName` | `string` | — | 检索表名(必填) |
379
+ | `vector` | `number[]` | — | 查询向量(必填) |
380
+ | `topK` | `number` | — | 返回条数(必填) |
381
+ | `threshold` | `number` | — | 相似度阈值 |
382
+ | `embeddingColumn` | `string` | `'embedding'` | 向量列名 |
383
+ | `filter` | `Record<string, unknown>` | — | 对表独立列做过滤 |
384
+ | `select` | `string[]` | `undefined` | 指定返回哪些列 |
385
+
386
+ ### 依赖
387
+
388
+ EmbeddingSearch 模块内部调用百炼的 embedding 和 rerank 服务,无需额外配置:
389
+
390
+ - **Embedding**:`text-embedding-v4`,1024 维,兼容 OpenAI 协议
391
+ - **Rerank**:`qwen3-rerank`,百炼原生 API
392
+
393
+ 只需确保 `BAILIAN_API_KEY` 已设置。
394
+
395
+ ### 错误处理
396
+
397
+ 同 Predict 模块:**直接抛异常**。常见错误:
398
+
399
+ - `BAILIAN_API_KEY environment variable is not set` — 未配置 API Key
400
+ - `[embedding] HTTP 4xx/5xx` — embedding 接口异常
401
+ - `[rerank] HTTP 4xx/5xx` — rerank 接口异常
402
+ - `Invalid table name` — 表名包含非法字符
403
+
404
+ ---
405
+
173
406
  ## 设计理念
174
407
 
175
408
  ```
package/dist/index.d.ts CHANGED
@@ -1,5 +1,31 @@
1
+ /**
2
+ * Axiom — LLM 调用底座库。
3
+ *
4
+ * 使用前需确保环境变量已加载(推荐 dotenv):
5
+ * ```ts
6
+ * import 'dotenv/config';
7
+ * import { LLM } from '@keo-ai/axiom';
8
+ *
9
+ * const res = await LLM.predict({ model: 'qwen3.7-max', prompt: '你好' });
10
+ * ```
11
+ *
12
+ * 必需环境变量:
13
+ * - `BAILIAN_API_KEY` — 百炼 API Key
14
+ *
15
+ * 可选环境变量:
16
+ * - `BAILIAN_BASE_URL` — 自定义百炼 API 地址
17
+ * - `BAILIAN_DEFAULT_MODEL` — 默认模型
18
+ *
19
+ * @packageDocumentation
20
+ */
21
+ /** LLM 静态入口类,封装 Provider 连接、故障转移和返回解析 */
1
22
  export { LLM } from './predict';
2
23
  export type { LLMRequest, LLMResponse, Message, ProviderConfig, StreamChunk, } from './predict';
3
24
  export type { LLMProvider, PredictorOptions } from './predict';
4
- export { Predictor, BailianProvider, MODEL_REGISTRY } from './predict';
25
+ /** 预测器核心类,负责模型路由与故障转移 */
26
+ export { Predictor } from './predict';
27
+ /** 百炼 Provider 实现 */
28
+ export { BailianProvider } from './predict';
29
+ /** 模型注册表,定义模型与 Provider 的映射关系 */
30
+ export { MODEL_REGISTRY } from './predict';
5
31
  export type { Model, PredictConfig, PredictWithMessagesConfig } from './predict';
package/dist/index.js CHANGED
@@ -1,9 +1,35 @@
1
1
  "use strict";
2
+ /**
3
+ * Axiom — LLM 调用底座库。
4
+ *
5
+ * 使用前需确保环境变量已加载(推荐 dotenv):
6
+ * ```ts
7
+ * import 'dotenv/config';
8
+ * import { LLM } from '@keo-ai/axiom';
9
+ *
10
+ * const res = await LLM.predict({ model: 'qwen3.7-max', prompt: '你好' });
11
+ * ```
12
+ *
13
+ * 必需环境变量:
14
+ * - `BAILIAN_API_KEY` — 百炼 API Key
15
+ *
16
+ * 可选环境变量:
17
+ * - `BAILIAN_BASE_URL` — 自定义百炼 API 地址
18
+ * - `BAILIAN_DEFAULT_MODEL` — 默认模型
19
+ *
20
+ * @packageDocumentation
21
+ */
2
22
  Object.defineProperty(exports, "__esModule", { value: true });
3
23
  exports.MODEL_REGISTRY = exports.BailianProvider = exports.Predictor = exports.LLM = void 0;
24
+ /** LLM 静态入口类,封装 Provider 连接、故障转移和返回解析 */
4
25
  var predict_1 = require("./predict");
5
26
  Object.defineProperty(exports, "LLM", { enumerable: true, get: function () { return predict_1.LLM; } });
27
+ /** 预测器核心类,负责模型路由与故障转移 */
6
28
  var predict_2 = require("./predict");
7
29
  Object.defineProperty(exports, "Predictor", { enumerable: true, get: function () { return predict_2.Predictor; } });
8
- Object.defineProperty(exports, "BailianProvider", { enumerable: true, get: function () { return predict_2.BailianProvider; } });
9
- Object.defineProperty(exports, "MODEL_REGISTRY", { enumerable: true, get: function () { return predict_2.MODEL_REGISTRY; } });
30
+ /** 百炼 Provider 实现 */
31
+ var predict_3 = require("./predict");
32
+ Object.defineProperty(exports, "BailianProvider", { enumerable: true, get: function () { return predict_3.BailianProvider; } });
33
+ /** 模型注册表,定义模型与 Provider 的映射关系 */
34
+ var predict_4 = require("./predict");
35
+ Object.defineProperty(exports, "MODEL_REGISTRY", { enumerable: true, get: function () { return predict_4.MODEL_REGISTRY; } });
@@ -8,8 +8,6 @@ export interface PredictConfig {
8
8
  readonly model: Model;
9
9
  /** 用户输入的 prompt */
10
10
  readonly prompt: string;
11
- /** 系统提示(可选) */
12
- readonly systemPrompt?: string;
13
11
  readonly temperature?: number;
14
12
  readonly maxTokens?: number;
15
13
  readonly topP?: number;
@@ -21,7 +19,6 @@ export interface PredictConfig {
21
19
  */
22
20
  export interface PredictWithMessagesConfig {
23
21
  readonly model: Model;
24
- readonly systemPrompt?: string;
25
22
  readonly temperature?: number;
26
23
  readonly maxTokens?: number;
27
24
  readonly topP?: number;
@@ -29,9 +26,9 @@ export interface PredictWithMessagesConfig {
29
26
  readonly responseFormat?: 'text' | 'json';
30
27
  }
31
28
  /**
32
- * 将 system prompt 前置到消息列表中。
33
- * @param config - 含可选 systemPrompt 字段的配置
29
+ * 构建消息列表。
30
+ * @param _config - 预测配置(保留以兼容接口,暂不消费)
34
31
  * @param userMessages - 用户提供的消息列表
35
- * @returns 带 system message 前缀的完整列表
32
+ * @returns 完整消息列表
36
33
  */
37
- export declare function buildMessages(config: PredictConfig | PredictWithMessagesConfig, userMessages: ReadonlyArray<Message>): Message[];
34
+ export declare function buildMessages(_config: PredictConfig | PredictWithMessagesConfig, userMessages: ReadonlyArray<Message>): Message[];
@@ -2,16 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildMessages = buildMessages;
4
4
  /**
5
- * 将 system prompt 前置到消息列表中。
6
- * @param config - 含可选 systemPrompt 字段的配置
5
+ * 构建消息列表。
6
+ * @param _config - 预测配置(保留以兼容接口,暂不消费)
7
7
  * @param userMessages - 用户提供的消息列表
8
- * @returns 带 system message 前缀的完整列表
8
+ * @returns 完整消息列表
9
9
  */
10
- function buildMessages(config, userMessages) {
11
- const messages = [];
12
- if (config.systemPrompt) {
13
- messages.push({ role: 'system', content: config.systemPrompt });
14
- }
15
- messages.push(...userMessages);
16
- return messages;
10
+ function buildMessages(_config, userMessages) {
11
+ return [...userMessages];
17
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keo-ai/axiom",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "基于 LLM 的预测与推理库,支持多 Provider 切换",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -10,7 +10,7 @@
10
10
  "license": "MIT",
11
11
  "scripts": {
12
12
  "build": "tsc",
13
- "test": "vitest run"
13
+ "test": "vitest run --exclude '**/*.integration.test.ts'"
14
14
  },
15
15
  "dependencies": {
16
16
  "dotenv": "^17.4.2"