Metadata-Version: 2.4
Name: lzm-space
Version: 0.2.0
Summary: 以空间为概念的统一存储、检索、编排引擎 - 像管理文件夹一样管理数据
Author: Lzm
License-Expression: MIT
Project-URL: Homepage, https://github.com/lzm/lzm-space
Project-URL: Issues, https://github.com/lzm/lzm-space/issues
Keywords: space,storage,s3,minio,oss,elasticsearch,rag,knowledge-base,ledger,lzm
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: lzm-edsm>=0.2
Provides-Extra: s3
Requires-Dist: minio>=7.0; extra == "s3"
Provides-Extra: es
Requires-Dist: elasticsearch>=8.0; extra == "es"
Provides-Extra: redis
Requires-Dist: redis>=5.0; extra == "redis"
Provides-Extra: sql
Requires-Dist: asyncpg>=0.29; extra == "sql"
Provides-Extra: all
Requires-Dist: lzm-space[s3]; extra == "all"
Requires-Dist: lzm-space[es]; extra == "all"
Requires-Dist: lzm-space[redis]; extra == "all"
Requires-Dist: lzm-space[sql]; extra == "all"

# lzm-space — 空间引擎

> 编码：utf8 | 作者：Lzm | 日期：2026-07-16

**以空间为概念的统一存储、检索、编排引擎。**

一句话：**像管理文件夹一样管理数据，像仓库台账一样追踪出入库。**

---

## 项目定位

lzm-space 将多个存储后端（S3/ES/SQL/Redis）抽象为统一的"空间"概念，每个空间有独立的权限、台账、路径树，并通过 lzm-edsm 进行事件驱动编排。

```
创建空间 → 获得空间标识
  ├─ 上传文件 → 携带空间标识 → 存入 S3/OSS
  ├─ 知识归档 → 携带空间标识 → 切片向量化 → 存入 ES
  ├─ 结构数据 → 携带空间标识 → 存入 PostgreSQL JSONB
  ├─ 热数据   → 携带空间标识 → 存入 Redis
  └─ 所有操作 → 记录台账（基于 lzm-edsm 事件溯源）
```

## 功能清单

### 存储后端

| 后端 | 用途 | 依赖 | 类 |
|------|------|------|-----|
| S3/MinIO/OSS | 文件/对象存储 | `minio>=7.0` | `S3Backend` |
| Elasticsearch | 知识库/向量检索/全文检索 | `elasticsearch>=8.0` | `ESBackend` |
| PostgreSQL | 结构化数据（JSONB/信息柜） | `asyncpg>=0.29` | `SQLBackend` |
| Redis | 缓存/热数据/记忆 | `redis>=5.0` | `RedisBackend` |

### 用户动线（SpaceClient 统一入口）

| 方法 | 功能 | 层级 |
|------|------|------|
| `create_space()` | 创建空间 | 空间管理 |
| `delete_space()` | 删除空间 | 空间管理 |
| `list_spaces()` | 列出空间 | 空间管理 |
| `upload_file()` | 上传文件到 S3 | 文件管理 |
| `list_files()` | 列出文件 | 文件管理 |
| `get_file()` | 获取文件内容 | 文件管理 |
| `archive_knowledge()` | 归档知识（切片→向量化→ES 索引） | **知识库** |
| `search()` | 搜索（向量/全文/混合三种策略） | **知识库** |
| `expand_search()` | 多跳搜索（事件-实体关系扩展） | **知识库** |
| `remember()` | 缓存记忆 | 记忆管理 |
| `recall()` | 召回记忆 | 记忆管理 |
| `forget()` | 删除记忆 | 记忆管理 |
| `get_ledger()` | 查询台账 | 台账 |

### 搜索策略

| 策略 | 描述 | 需 lzm-plugin |
|:----:|------|:------------:|
| `vector` | 向量余弦相似度搜索（默认） | ✅ |
| `text` | BM25 全文检索（multi_match） | ❌ |
| `hybrid` | 向量 + 全文 → RRF 融合排序 | ✅ |

### P0 差距补全（v0.1.1）

| 功能 | 状态 | 说明 |
|------|:----:|------|
| BM25 全文检索 | ✅ | ES multi_match 跨 content/name/key |
| 事件/实体提取 | ✅ | LLM 驱动的 event/entity 提取（lzm-plugin） |
| 多跳搜索 | ✅ | 实体→事件→实体权重迭代扩展 |
| Rerank 重排序 | ✅ | API / Chat / 本地词法三级兜底（lzm-plugin） |
| SQL 后端（PG JSONB） | ✅ | 链式查询 API + QueryBuilder + 跨空间 Hash Join |
| 链式查询 API | ✅ | QueryBuilder `.filter().groupby().agg().to_list()` |

---

## 技术栈

| 层 | 技术 | 版本 |
|----|------|------|
| 语言 | Python | >=3.11 |
| 编排引擎 | lzm-edsm | >=0.2 |
| S3 存储 | minio | >=7.0（可选） |
| ES 存储 | elasticsearch | >=8.0（可选） |
| Redis | redis-py | >=5.0（可选） |
| 插件系统 | lzm-plugin | >=0.2（可选） |

## 项目状态

- **当前阶段**：信息柜+系统空间功能完成，v0.2.0 全量验证通过
- **版本**：0.2.0
- **测试**：229+ 用例 ✅（含 Mock + PostgreSQL 真实环境双模式验证）
- **v0.2.0 变更**：
  - 新增 `SpaceType.SYSTEM` 系统空间类型（不可删除，默认严格权限模板）
  - 新增信息柜（Cabinet）管理：`register_cabinet()`/`get_cabinet()`/`list_cabinets()`/`unregister_cabinet()`
  - 新增 `CabinetBackend` 包装器，多信息柜共享同一后端连接池，实现数据隔离
  - 新增 `SpaceClient` 信息柜动线：`cabinet_put()`/`cabinet_get()`/`cabinet_delete()`/`cabinet_list()`
  - 新增系统常量模块 `constants.py`（`SYSTEM_SPACE_PATH`、`CABINET_USERS` 等）
  - 系统空间不可删除，权限模板 others 位为 `--------`（无任何权限）
  - 18 个 cabinet 专项测试覆盖全部新增功能
- **进度**：[docs/timeline.md](docs/timeline.md)
- **TODO**：[docs/todo.md](docs/todo.md)

## 快速开始

### 安装

```bash
# 核心（零外部依赖）
pip install lzm-space

# 全部后端
pip install lzm-space[all]

# 指定后端
pip install lzm-space[s3,es,sql]

# + 插件系统（切片/向量化/提取/重排）
pip install lzm-space[all] lzm-plugin[all]
```

### 基础用法

```python
import asyncio
from lzm.space import SpaceClient

client = SpaceClient()

async def main():
    # 1. 创建空间
    space = await client.create_space("my-knowledge")
    print(f"空间创建成功: {space.space_id}")

    # 2. 知识归档（需要 lzm-plugin）
    doc = """
    Python 是一种广泛使用的解释型、高级编程语言。
    它由 Guido van Rossum 于 1989 年底发明。
    Go 是 Google 开发的一种编译型、并发型编程语言。
    Rust 是 Mozilla 开发的一种系统编程语言。
    """
    result = await client.archive_knowledge(
        "my-knowledge", doc,
        chunk_strategy="heading_strict",
        extract_events=True,  # 提取事件/实体（可选）
    )
    print(f"归档完成: {result.chunk_count} 个切片")

    # 3. 向量搜索（默认策略，需 lzm-plugin）
    results = await client.search("my-knowledge", "Python")
    for r in results:
        print(f"  [{r.score:.3f}] {r.content[:60]}")

    # 4. 全文搜索（不需要 lzm-plugin）
    results = await client.search(
        "my-knowledge", "编程语言",
        strategy="text", k=5,
    )
    for r in results:
        print(f"  [BM25:{r.score:.3f}] {r.content[:60]}")

    # 5. 混合搜索（向量 + 全文 + RRF 融合）
    results = await client.search(
        "my-knowledge", "并发语言",
        strategy="hybrid", k=5,
    )
    for r in results:
        print(f"  [hybrid:{r.score:.3f}] {r.content[:60]}")

    # 6. 多跳搜索（需先 archive_knowledge(extract_events=True)）
    results = await client.expand_search(
        "my-knowledge", "编程",
        initial_keys=["Python", "Go"], hops=2, k=5,
    )
    for r in results:
        print(f"  [hop:{r.hop} score:{r.score:.3f}] {r.content[:60]}")

    # 7. 记忆管理
    await client.remember("my-knowledge", "pref:lang", "Python")
    value = await client.recall("my-knowledge", "pref:lang")
    print(f"记忆: {value}")

    # 8. 查看台账
    ledger = await client.get_ledger("my-knowledge", limit=10)
    for entry in ledger:
        print(f"  [{entry.operation}] {entry.data_key}")

    # 9. 结构化数据 — SQL 后端（需安装 asyncpg）
    from lzm.space.backends.sql_backend import SQLBackend
    from lzm.space.backends.base import DataDescriptor

    sql_biz = SQLBackend("order-track", {
        "host": "localhost", "port": 5432,
        "user": "postgres", "password": "...", "database": "biz",
    })
    await sql_biz.put(DataDescriptor(key="order-001", labels={
        "product": "Widget", "amount": 2999, "status": "paid",
    }))
    # 链式查询
    results = await sql_biz.query() \
        .filter("amount > 1000") \
        .filter("status = 'paid'") \
        .groupby("product") \
        .agg({"total": "sum(amount)", "cnt": "count(*)"}) \
        .to_list()
    for r in results:
        print(f"  [SQL] product={r['product']}, total={r['total']}")

asyncio.run(main())
```

### 插件集成

lzm-space 的可选依赖 lzm-plugin 提供知识库所需的计算能力：

```bash
pip install lzm-plugin[all]
```

| 插件 | 功能 | 搜索策略依赖 |
|------|------|:----------:|
| `chunker` | 文档切片 | archive_knowledge |
| `embedding` | 文本向量化 | vector / hybrid 搜索 |
| `extractor` | 事件/实体提取 | expand_search |
| `reranker` | 搜索结果重排序 | — |

---

## 目录结构

```
lzm-space/
├── README.md                         # 总纲
├── pyproject.toml                    # 包配置
├── src/lzm/space/
│   ├── __init__.py                   # 包入口（导出主要类型）
│   ├── core/
│   │   ├── __init__.py
│   │   ├── node.py                   # SpaceNode, SpaceType
│   │   ├── permissions.py            # SpacePermissions, AllowEntry
│   │   ├── path.py                   # 路径解析
│   │   ├── models.py                 # 数据模型（6 个数据类）
│   │   └── client.py                 # SpaceClient 统一入口（774行）
│   ├── manager/
│   │   ├── __init__.py
│   │   └── space_manager.py          # 空间 CRUD + 后端路由
│   ├── backends/
│   │   ├── __init__.py
│   │   ├── base.py                   # StorageBackend ABC + DataDescriptor
│   │   ├── es_backend.py             # Elasticsearch 后端（~580行）
│   │   ├── schema_manager.py         # 列注册与动态列管理（~335行）
│   │   ├── sql_backend.py            # SQL 结构后端 — PostgreSQL 列优先+JSONB（~470行）
│   │   ├── sql_dialect.py            # PostgreSQL 方言封装（~315行）
│   │   ├── sql_query.py              # QueryBuilder + CrossSpaceQuery（~394行）
│   │   ├── redis_backend.py          # Redis 后端（~222行）
│   │   └── s3.py                     # MinIO/S3 后端（~443行）
│   └── ledger/
│       ├── __init__.py
│       ├── engine.py                 # LedgerEngine 双层台账引擎（~458行）
│       ├── models.py                 # LedgerEntry 数据模型
│       ├── pg_store.py               # PostgreSQL 台账持久化（~308行）
│       ├── store.py                  # SQLite 台账 + 全局空间注册表（~485行）
│       └── hooks.py                  # 外部回调注册表
├── docs/
│   ├── timeline.md                   # 开发进度时间线
│   ├── common-errors.md              # 常见错误归档
│   ├── coding-standards.md           # 编码规范
│   ├── todo.md                       # TODO 管理
│   └── logic-records/                # 设计文档
│       ├── 20260716-gap-analysis-vs-sag-projects.md
│       └── 20260716-multi-hop-search.md
└── tests/
    ├── conftest.py                   # 测试夹具
    ├── test_sql_scenarios.py         # SQL 后端业务场景（~4 个场景）
    ├── core/
    │   └── test_client.py            # SpaceClient 测试（22个用例）
    ├── backends/
    │   ├── test_base.py
    │   ├── test_es_backend.py
    │   ├── test_sql_backend.py       # SQL 后端单元+集成测试（列感知版）
    │   ├── test_redis_backend.py
    │   └── test_s3.py
    ├── ledger/
    │   ├── conftest.py               # Mock 连接池
    │   ├── test_engine.py            # LedgerEngine 测试
    │   ├── test_models.py            # LedgerEntry 模型测试
    │   ├── test_pg_integration_real.py # PG 台账真实环境集成测试
    │   ├── test_store.py             # GlobalStore / PerSpaceLedgerStore
    │   └── test_global_store_clean.py
    ├── manager/
    │   ├── test_space_manager.py
    │   └── test_routing.py
    └── scenarios/
        └── test_cabinet_ledger_scenarios.py # 信息柜+台账全流程场景模拟（Mock+PG 双模式）
```

## 核心数据模型

| 类 | 用途 | 示例 |
|----|------|------|
| `SpaceNode` | 空间节点描述 | `{space_id, name, space_type, path}` |
| `DataDescriptor` | 统一数据描述符（跨后端） | `{key, value, labels, data_type}` |
| `SearchResult` | 搜索结果项 | `{descriptor_id, content, score, labels}` |
| `MultiHopSearchResult` | 多跳搜索结果项 | `{..., hop, entity_keys}` |
| `ArchiveResult` | 归档结果 | `{file_descriptor, chunk_count, status}` |
| `MemoryCategory` | 记忆分类（PREFERENCE/CONTEXT/STATE） | `{name, ttl}` |
| `ExtractionResult` | 事件/实体提取结果 | `{events, entities, relations}` |

## 常见问题

### Embedding API 返回 404

OpenAI Python SDK 的 `client.embeddings.create()` 会自动在 `base_url` 后追加 `/embeddings`。
如果 `base_url` 已包含 `/v1/embeddings`，实际请求路径会变成 `/v1/embeddings/embeddings`。

**解决**：传给 Embedding 插件的 `base_url` 应只到 `/v1` 级别。

### Unclosed client session 警告

ESBackend 和 RedisBackend 内部创建了 HTTP 连接池，需要显式关闭。

**解决**：程序退出前调用 `await backend.close()`。

### 后端注册后找不到

`SpaceManager.resolve_backend(space_id, data_type)` 按 **默认路由名** 查找后端。

**解决**：注册时名称需匹配默认路由：
- `data_type="file"` → 注册名 `"s3"`
- `data_type="chunk"` → 注册名 `"es"`
- `data_type="record"` → 注册名 `"sql"`（需安装 asyncpg）
- `data_type="memory"` → 注册名 `"redis"`

## 相关文档

- [开发进度时间线](docs/timeline.md)
- [常见错误归档](docs/common-errors.md)
- [编码规范](docs/coding-standards.md)
- [TODO 管理](docs/todo.md)
- [架构设计](../../docs/analysis/space_engine_architecture.md)
- [插件集成指南](docs/plugin-integration-guide.md)
- [逻辑记录](docs/logic-records/)
- [插件系统](../lzm-plugin/README.md)
- [真实环境演示](demo.py)
