# 13. AI（向量检索 / FAQ / 文档）

> **条件启用**：这一组接口依赖 `vjmap` 服务端的嵌入（embedding）模型与向量库。
> 若未正确配置，调用会返回 `500`（例如缺少 `bge-large-zh-v1.5-f16.gguf` 模型文件）。
> 在你的环境若未开启 AI 子服务，直接跳过本章。

本章覆盖 3 个接口：

| No. | 接口 | URL | Method | 说明 |
|-----|------|-----|--------|------|
| 13.1 | vectorSearch | `/api/v1/service/vectorSearch` | GET / POST | 相似性检索 |
| 13.2 | chatFaq | `/api/v1/service/chatFaq` | GET | 获取 FAQ 知识库 |
| 13.3 | vectorDocs | `/api/v1/service/vectorDocs` | GET | 获取向量库已有文档元数据 |

---

## 13.0 启用探针

SDK 提供 `aiEnabled()` / `ai_enabled()`（内部调用 `chatFaq`）判断 AI 子服务是否可用；
失败（抛 `VjmapError`）则视为未启用，跳过后续调用即可。

> ⚠️ `aiEnabled()` 只探测 `chatFaq` 一个端点。**`chatFaq` 可用不代表 `vectorSearch`/`vectorDocs` 可用**——向量检索依赖独立子服务，`aiEnabled()===true` 时 `vectorSearch` 仍可返回 HTTP 500。调用方仍需对每个 AI 端点单独 try/catch。

```python
if not svc.ai_enabled():
    raise SystemExit("AI 未启用，略")
```

---

## 13.1 vectorSearch — 相似性检索

按给定自然语言 `query` 在向量库中检索最相似的若干条文档。

### 请求

两种请求方式：

| 方式 | URL | Body | 备注 |
|------|-----|------|------|
| GET | `GET /api/v1/service/vectorSearch?query=...&numDocuments=...` | 无 | 简单场景用 |
| POST（推荐） | `POST /api/v1/service/vectorSearch` | JSON | 支持 `documents` 等复杂字段 |

### 主要参数

| 字段 | 类型 | 默认 | 说明 |
|------|------|------|------|
| `query` | string | — | **必填** 查询文本 |
| `numDocuments` | int | 20 | 返回条数 |
| `collection` | string | `"vj"` | 向量库集合名 |
| `where` | string | — | metadata 过滤表达式（[chromem-go](https://github.com/philippgille/chromem-go) 语法） |
| `whereDocument` | string | — | document 内容过滤表达式 |
| `searchDescription` | string | — | 附加搜索上下文，帮助模型更精准 |
| `disableSplitSentence` | bool | false | 关闭"先分句再分别 embed" |
| `disableSearchKnowledge` | bool | false | 仅搜 `documents`，不搜知识库 |
| `documents` | array | — | 临时附带的用户文档（不会持久化）。每项：`{content:"...", metadata:{...}}` |

POST body 还支持以下字段（`VectorSearchData` 结构体，GET query 不暴露）：

| 字段 | 类型 | 说明 |
|------|------|------|
| `whereDesc` | string | 对 `searchDescription` 检索结果的 metadata 过滤 |
| `whereDocumentDesc` | string | 对 `searchDescription` 检索结果的内容过滤 |
| `whereUser` | string | 对 `documents`（用户文档）的 metadata 过滤 |
| `whereUserDocument` | string | 对 `documents` 的内容过滤 |

### 响应

```json
{
  "status": "success",
  "data": [
    {
      "id": "__createPolygon",
      "content": "根据给定的点坐标数组直接绘制一个多边形",
      "metadata": { "...": "..." },
      "similarity_score": 0.8273
    },
    { "...": "..." }
  ]
}
```

### SDK 示例

```python
res = svc.vector_search("如何画多边形", num_documents=5)
for doc in res.get("data", []):
    print(doc["id"], doc.get("similarity_score"))
```

```js
const res = await svc.vectorSearch('如何画多边形', { numDocuments: 5 });
res.data.forEach(d => console.log(d.id, d.similarity_score));
```

### 失败样例

当前测试环境缺少 embedding 模型文件，真实请求返回：

```json
{
  "status": 500,
  "body": {
    "code": 8,
    "msg": "D:/Program Files/vjmapserver/data/ai/models/bge-large-zh-v1.5-f16.gguf is not exist"
  }
}
```

> **部署提示**：部署方需根据官方文档下载 embedding 模型文件，放置于
> 服务端的 `data/ai/models/` 目录下，并在配置中指定模型名。未配置时，
> `vectorSearch` 会失败但不影响其他子系统运行。

---

## 13.2 chatFaq — FAQ 知识库

### 请求

```
GET /api/v1/service/chatFaq
```

### 响应

返回一个按"主题"分组的字符串数组字典。

```json
{
  "status": "success",
  "data": {
    "UI交互": [
      "弹出学生表单输入对话框，包括姓名、年龄(默认18)、性别",
      "获取地图的当前中心点坐标X和Y、地图级别，生成json对象，根据这数据弹出数据信息框..."
    ],
    "信息窗口": [
      "在坐标[30,50]处添加一个信息窗口，内容为\"Hello,World\""
    ],
    "几何算法": [ "..." ]
  }
}
```

数据源来自服务端 `./data/ai/faq/*.md`，HTTP 缓存 `Cache-Control: public, max-age=864000`（10 天）。

### SDK 示例

```python
faq = svc.chat_faq()
for topic, items in faq["data"].items():
    print(f"- {topic}: {len(items)} 项")
```

```js
const { data } = await svc.chatFaq();
for (const [topic, items] of Object.entries(data)) {
  console.log(`- ${topic}: ${items.length} 项`);
}
```

---

## 13.3 vectorDocs — 向量库文档

### 请求

```
GET /api/v1/service/vectorDocs
```

### 响应

返回一个数组，每项是知识库里的一条文档元数据：

```json
{
  "status": "success",
  "data": [
    {
      "id": "__createPolygon",
      "content": "根据给定的点坐标数组直接绘制一个多边形",
      "metadata": {
        "category": "draw",
        "code": "...",
        "comments": "...",
        "function": "__createPolygon",
        "param": "..."
      },
      "similarity_score": 0
    }
  ]
}
```

`similarity_score` 恒为 0（此接口不做相似度计算，仅导出文档）。

### SDK 示例

```python
docs = svc.vector_docs()["data"]
print("知识库条目数：", len(docs))
print("分类统计：", {
    c: sum(1 for d in docs if d["metadata"].get("category") == c)
    for c in sorted({d["metadata"].get("category", "") for d in docs})
})
```

```js
const { data: docs } = await svc.vectorDocs();
console.log('条目数：', docs.length);
```

---

## 使用建议

1. **部署前先跑探针**：初始化客户端后先调 `chat_faq()`，失败则关闭 AI UI 入口。
2. **前端搜索**：优先用 POST `vectorSearch`，把 `searchDescription` 带上业务上下文能显著提升命中率。
3. **冷启动**：第一次调用 `vectorSearch` 会加载 embedding 模型，响应可能秒级；
   第二次起基本在毫秒级。
4. **安全**：`vectorDocs` 直接返回所有文档内容（含 `code` 字段），不要把它暴露给未鉴权用户。

---
