Metadata-Version: 2.5
Name: semantic-tool-registry
Version: 7.0.1
Summary: 语义注册表基底 + 领域子类（工具域 / 文档域）：向量与全文双检索、R1/R2 精准锚定、录入去重、CRUD、领域存储扩展（指定数据库 / 指定表 / 扩展字段，初始化期定型，注册查询自动适配）
Author-email: cc <3204624858@qq.com>
License-Expression: MIT
License-File: LICENSE
Keywords: document-registry,embedding,full-text-search,llm-tools,rag,reranker,retrieval,semantic-registry,tool-registry,vector-search
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: Chinese (Simplified)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Indexing
Requires-Python: <3.13,>=3.12
Requires-Dist: jieba>=0.42.1
Requires-Dist: numpy>=1.23
Requires-Dist: sentence-transformers>=5.6.1
Requires-Dist: torch>=2.0.0
Requires-Dist: transformers<5.0.0,>=4.50.0
Requires-Dist: zvec>=0.6.0
Provides-Extra: dev
Requires-Dist: black>=23.0.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Provides-Extra: download-cn
Requires-Dist: modelscope>=1.15.0; extra == 'download-cn'
Provides-Extra: onnx
Requires-Dist: onnxruntime>=1.17.0; extra == 'onnx'
Provides-Extra: onnx-export
Requires-Dist: onnx>=1.16.0; extra == 'onnx-export'
Requires-Dist: onnxscript>=0.7.0; extra == 'onnx-export'
Provides-Extra: onnx-gpu
Requires-Dist: onnxruntime-gpu>=1.17.0; extra == 'onnx-gpu'
Provides-Extra: yaml
Requires-Dist: pyyaml>=6.0; extra == 'yaml'
Description-Content-Type: text/markdown

# semantic-tool-registry

面向 LLM / Agent 的**语义注册表**：把「工具」和「文档」登记成可被自然语言精准检索的条目。

同一个引擎提供两个开箱即用的领域：

| 领域 | 用途 | 领域词汇 |
|---|---|---|
| 工具域 `ToolAgent` | 工具 / 函数 / API 的能力注册与检索（含函数路径） | `register_tool` / `search_tools` / … |
| 文档域 `DocumentAgent` | 说明文档的登记、检索与全文取回 | `register_documents` / `search_documents` / … |

---

## 特性

- **双路混合检索**：向量（语义）+ 全文（FTS）双路召回，RRF 融合后再经交叉编码器精排。
- **R1 / R2 精准锚定**：注册与查询都走机械校验；查询携带唯一判别 token 时确定性置顶。
- **录入去重**：语义近同（余弦）与功能级（文本判定）双重判重，避免重复登记。
- **完整 CRUD**：注册 / 查询 / 更新 / 删除，工具域与文档域各自语义对齐，文档全文联动。
- **可派生领域**：继承基底并指定「数据库 + 表名 + 扩展字段」，即得一个新领域注册表。
- **全局单实例**：同进程多线程恰好一个实例；跨进程用 HTTP 服务端共享同一实例。
- **三种调用方式**：Python 导入、命令行、HTTP（任意语言可接）。

---

## 安装

要求 **Python 3.12**。

```bash
# pip
pip install semantic-tool-registry

# uv（新项目先 uv init，已有项目直接 add）
uv add semantic-tool-registry
uv run semantic-tool-registry --help
```

安装可选能力：

| extra | 作用 |
|---|---|
| `onnx` | ONNX 运行时（CPU 包，与 torch 零冲突）—— CPU 上启用 ONNX 后端 |
| `onnx-gpu` | ONNX 运行时 GPU 版（`CUDAExecutionProvider`） |
| `onnx-export` | ONNX 导出工具所需依赖 |
| `download-cn` | 模型自动下载优先走 ModelScope，国内更快 |
| `yaml` | `register_any` 接受 YAML 字符串输入 |

```bash
pip install "semantic-tool-registry[onnx,download-cn]"
uv add "semantic-tool-registry[onnx,download-cn]"
```

> **GPU / CUDA**：`torch` 是唯一与硬件强绑定的依赖，默认安装的是通用版本。
> 需要 CUDA 版时，先按 [pytorch.org](https://pytorch.org/get-started/locally/) 的
> 官方索引装对应 CUDA 的 torch，再安装本包即可；本包不会覆盖已装好的 torch。
> 仓库内的 `install.ps1`（Windows）可按硬件自动完成这一步。

> **首次运行**：嵌入模型与精排模型会在第一次创建 Agent 时自动下载到
> `./data/models`，请预留几百 MB 磁盘与相应下载时间。

---

## 快速上手

### Python

```python
from semantic_tool_registry import (
    tool_register, tool_query, tool_update, tool_delete,
    doc_register, doc_query, doc_fulltext,
)

# —— 工具域 ——
uids = tool_register([
    {"name": "weather_beijing_tool",
     "description": "获取北京实时天气：温度、湿度、风速。标识：weather_beijing_tool"},
    "把人民币金额按实时汇率换算为美元输出。",   # 裸文本也可以
])

hits = tool_query(["北京", "天气"], top_k=1)     # 关键词式：直接检索
tool_update(name="weather_beijing_tool",
            description="获取北京七日天气与生活指数。标识：weather_beijing_tool")
tool_delete(name="weather_beijing_tool")

# —— 文档域 ——
uids = doc_register([
    {"title": "Nginx 502 排障指南",
     "content": "502 Bad Gateway 通常由上游超时或连接池耗尽引起。排查顺序：…"},
])
hits = doc_query(["Nginx", "502"], top_k=1)
full_text = doc_fulltext(uids[0])                # 取全文
```

### 命令行

```bash
semantic-tool-registry tool register --file tools.json
semantic-tool-registry tool query weather_beijing_tool
semantic-tool-registry doc query Nginx 502
```

### HTTP

```bash
semantic-tool-registry serve --port 8600
```

```bash
curl -s http://127.0.0.1:8600/tool/register \
  -H "Content-Type: application/json" \
  -d '[{"name":"weather_beijing_tool","description":"获取北京实时天气。标识：weather_beijing_tool"}]'

curl -s http://127.0.0.1:8600/tool/query \
  -d '{"keywords": ["weather_beijing_tool"], "top_k": 1}'
```

---

## Python API

### 便捷函数（推荐起步）

经全局单实例直接调用，无需自己管理实例。

| 函数 | 说明 |
|---|---|
| `tool_register(items, **kw)` / `doc_register(docs, **kw)` | 注册，返回与输入逐位对齐的 uid 列表（`None` = 被拒或去重） |
| `tool_query(keywords, top_k=1)` / `doc_query(keywords, top_k=1)` | 查询；`keywords` 为列表 = 关键词式，为字符串 = 整句式（见下） |
| `tool_update(...)` / `doc_update(...)` | 更新；`name`/`uid` 定位，`new_name` 改名，`description`/`content` 改内容 |
| `tool_delete(...)` / `doc_delete(...)` | 删除单个（`uid` 或 `name`） |
| `tool_delete_batch(items)` | 批量删除，逐位返回 `bool` |
| `doc_fulltext(uid)` | 取单篇文档全文 |

**关键词式 vs 整句式**

- `tool_query(["北京", "天气"])` —— 关键词式：直接走向量 + 全文混合检索，任何情况下都返回结果。
- `tool_query("北京今天天气怎么样")` —— 整句式：先做查询规格校验（R2），
  若句中已含唯一判别 token 则确定性置顶；若不能唯一定位，则在**配置了 LLM** 时
  由 LLM 规范化成检索锚点后复验。两者都不满足时**不猜测检索**，而是返回一份
  *要求文档*（告诉你缺什么、该怎么改写查询）。

配置 LLM：

```python
from semantic_tool_registry import configure_tool_agent

configure_tool_agent(
    llm_base_url="https://api.example.com/v1",
    llm_api_key="sk-...",
    llm_model="your-model",
)
```

### 配置与单实例

配置必须在首次获取实例**之前**给出，之后再次传配置会被忽略并告警。

```python
from semantic_tool_registry import configure_tool_agent, get_tool_agent

configure_tool_agent(mode="speed", device="gpu", data_path="./mydata")
ag = get_tool_agent()      # 首次调用才真正创建（加载模型）
ag2 = get_tool_agent()     # 同一个对象
assert ag is ag2
```

### 完整能力

```python
ag = get_tool_agent()          # ToolAgent
da = get_document_agent()      # DocumentAgent

ag.register_any(...)           # 任意格式：dict / 函数 / 类 / JSON / XML / CSV / YAML / 混合列表
ag.search_tools_batch(...)     # 批量查询，与输入逐位对齐
ag.auto_search(...)            # 规范化查询（R2 校验 + LLM 增强）
ag.validate_storage_consistency(repair=True)   # 一致性体检与修复

da.search_documents_batch(...) / da.auto_search_documents(...)
da.get_full_text(uid) / da.get_full_texts(uids)
da.delete_documents([uid, "标题"])             # 批删，全文联动
```

注册真实可调用对象（函数 / 类 / 方法）时，会自动写入「函数路径」扩展字段
（`实现文件绝对路径.限定名`），查询结果一并带出。

### 派生新领域

继承基底并在初始化时定型「数据库 + 表名 + 扩展字段」，注册 / 查询 / 更新 / 删除
全自动适配该领域。完整说明（含示例与默认表对照）随源码分发包一同提供，见
`docs/base_agent.md`、`docs/tool_agent.md`、`docs/document_agent.md`。

---

## 命令行

```bash
semantic-tool-registry --help
```

注册，三种输入方式任选：

```bash
# JSON 数组文件（推荐）
semantic-tool-registry tool register --file tools.json
# 内联 JSON
semantic-tool-registry tool register --json '[{"name":"fx_tool","description":"…标识：fx_tool"}]'
# 位置参数：每行一条裸文本
semantic-tool-registry tool register "把人民币金额按实时汇率换算为美元输出。"
```

`tools.json` 示例：

```json
[
  {"name": "weather_beijing_tool",
   "description": "获取北京实时天气：温度、湿度、风速、降水概率。标识：weather_beijing_tool"},
  {"name": "fx_usd_cny_tool",
   "description": "人民币与美元实时汇率换算，支持双向报价。标识：fx_usd_cny_tool"}
]
```

查询 / 更新 / 删除：

```bash
semantic-tool-registry tool query weather_beijing_tool
semantic-tool-registry tool query 北京 天气          # 多词 = 关键词式（精准）
semantic-tool-registry tool query --raw "北京今天天气怎么样"   # 强制关键词式
semantic-tool-registry doc query Nginx 502

semantic-tool-registry tool update --name weather_beijing_tool --description "七日天气…标识：weather_beijing_tool"
semantic-tool-registry tool delete --name fx_usd_cny_tool
semantic-tool-registry doc delete --name "Nginx 502 排障指南"
```

全局参数（写在子命令之前）：

```bash
semantic-tool-registry --data-path D:\mydata tool query weather_beijing_tool
semantic-tool-registry --mode speed doc query "网关 502"
semantic-tool-registry --zvec-lock-wait 120 tool register --file tools.json
```

等价的模块调用：`python -m semantic_tool_registry …`。

---

## HTTP 服务

```bash
semantic-tool-registry serve --port 8600 --data-path ./data
semantic-tool-registry serve --no-preload        # 启动更快，首个请求稍慢
```

| 方法 | 路径 | 请求体 | 返回 |
|---|---|---|---|
| GET | `/health` | — | 健康状态与实例情况 |
| POST | `/tool/register` | `{"items": [...]}` 或裸数组 | `{"uids": [...]}` |
| POST | `/tool/query` | `{"keywords": [...], "top_k": 1}` | `{"results": [...]}` |
| POST | `/tool/update` | `{"uid"/"name", "new_name", "description"}` | `{"uid": "..."}` |
| POST | `/tool/delete` | `{"uid"/"name"}` 或 `{"items": [...]}` | `{"deleted": true}` / `{"results": [...]}` |
| POST | `/doc/register` | `{"items": [{"title","content"}, ...]}` | `{"uids": [...]}` |
| POST | `/doc/query` | `{"keywords": [...], "top_k": 1}` | `{"results": [...]}` |
| GET / POST | `/doc/fulltext` | `?uid=...` 或 `{"uid": ...}` | `{"content": "..."}` |
| POST | `/doc/update` / `/doc/delete` | 同工具域语义 | 同工具域 |

```bash
curl -s http://127.0.0.1:8600/doc/register \
  -d '[{"title":"Nginx 502 排障指南","content":"502 通常由上游超时…"}]'
curl -s http://127.0.0.1:8600/doc/query -d '{"keywords":["Nginx","502"]}'
curl -s "http://127.0.0.1:8600/doc/fulltext?uid=<uid>"
```

### Python 跨进程客户端

服务进程独占向量库，其余进程做瘦客户端 —— 多进程共用一个实例，零并发写冲突。

```python
# 进程 1（唯一持有向量库的进程）：
#   semantic-tool-registry serve --port 8600
# 进程 2、3、…（任意多个，可跨机器）：
from semantic_tool_registry import HttpClient

c = HttpClient("http://127.0.0.1:8600")     # 或 remote("http://…")
c.tool_register([{"name": "shared_tool", "description": "…标识：shared_tool"}])
hits = c.tool_query(["shared_tool"])          # 读到任何进程写入的数据
c.doc_register([{"title": "…", "content": "…"}])
c.health()
```

方法名与本地便捷函数一致，本地直连 ↔ 远程调用切换零成本；纯标准库实现。

---

## 数据与配置

### 目录

默认根目录为当前工作目录下的 `./data`：

| 路径 | 内容 |
|---|---|
| `./data/entry_registry/` | 工具域向量库 |
| `./data/entry_registry/entry_metadata.db` | 工具域元数据（SQLite） |
| `./data/document_registry/` | 文档域向量库与元数据 |
| `./data/models/` | 下载的嵌入 / 精排模型缓存 |
| `./data/onnx_models/` | ONNX 导出产物（仅在 ONNX 后端下使用） |

### 环境变量

| 变量 | 作用 |
|---|---|
| `AI_TOOLS_MODE` | `precision` / `speed`，默认按硬件自动选型 |
| `AI_TOOLS_DEVICE` | `gpu` / `cpu`（或 `auto`） |
| `AI_TOOLS_DATA` | 存储根目录 |
| `AI_TOOLS_ZVEC_WAIT` | 向量库被占用时的排队等待秒数 |
| `HF_ENDPOINT` | Hugging Face 镜像地址（模型下载用），如 `https://hf-mirror.com` |

命令行同名参数优先于环境变量。

### 三种用法的单实例保证

| 用法 | 保证 |
|---|---|
| Python 导入 | 进程内恰好一个（多线程共享） |
| 命令行 | 每次调用恰好一个；进程退出即释放向量库锁 |
| HTTP | 全系统恰好一个：服务进程持有实例，N 个客户端零实例 |

---

## 常见问题

**同一数据目录被多个进程同时占用**

向量库是独占的。三种解法：

1. **服务模式（推荐）**：一个进程 `serve`，其余进程用 `HttpClient` 远程调用。
2. **排队模式**：`--zvec-lock-wait 120`（或构造实例时 `zvec_lock_wait=120`），
   等持有进程退出后自动接管。
3. **各自独立目录**：不同进程用不同 `--data-path`。

**模型下载慢或需要离线**

```bash
export HF_ENDPOINT=https://hf-mirror.com     # Windows: set HF_ENDPOINT=...
pip install "semantic-tool-registry[download-cn]"   # 优先走 ModelScope
```

已有的本地模型目录可经构造参数 `cache_dir` 指定，ONNX 产物目录用 `onnx_root`。

**整句查询返回了一份「要求文档」而不是结果**

这是设计行为：查询无法唯一定位时不会猜测检索。按返回文档给出的提示改写为
关键词式（如多词查询），或按上文配置 LLM 后重试。

**速度与精度怎么选**

`--mode speed` 用小模型换速度，`--mode precision` 换取更好的排序质量；
不指定时按硬件自动选型。CPU 上会自动优先使用 ONNX 后端（需安装 `[onnx]`）。

---

## 许可

MIT
