Metadata-Version: 2.4
Name: lzm-space
Version: 0.1.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: mysql
Requires-Dist: asyncmy>=0.2; extra == "mysql"
Provides-Extra: redis
Requires-Dist: redis>=5.0; extra == "redis"
Provides-Extra: all
Requires-Dist: lzm-space[s3]; extra == "all"
Requires-Dist: lzm-space[es]; extra == "all"
Requires-Dist: lzm-space[mysql]; extra == "all"
Requires-Dist: lzm-space[redis]; extra == "all"

# lzm-space — 空间引擎

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

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

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

---

## 项目定位

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

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

## 功能清单

### 存储后端

| 后端 | 用途 | 依赖 | 类 |
|------|------|------|-----|
| S3/MinIO | 文件/对象存储 | `minio>=7.0` | `S3Backend` |
| Elasticsearch | 知识库/向量检索/全文检索 | `elasticsearch>=8.0` | `ESBackend` |
| MySQL | 结构化业务数据 | `asyncmy>=0.2` | `MySQLBackend` |
| 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.2.0）

| 功能 | 状态 | 说明 |
|------|:----:|------|
| BM25 全文检索 | ✅ | ES multi_match 跨 content/name/key |
| 事件/实体提取 | ✅ | LLM 驱动的 event/entity 提取（lzm-plugin） |
| 多跳搜索 | ✅ | 实体→事件→实体权重迭代扩展 |
| Rerank 重排序 | ✅ | API / Chat / 本地词法三级兜底（lzm-plugin） |

---

## 技术栈

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

## 项目状态

- **当前阶段**：P0 差距补全完成
- **版本**：0.2.0-dev
- **测试**：171/171 ✅
- **进度**：[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]

# + 插件系统（切片/向量化/提取/重排）
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}")

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行）
│   │   ├── mysql_backend.py          # MySQL 后端（~290行）
│   │   ├── redis_backend.py          # Redis 后端（~222行）
│   │   └── s3.py                     # MinIO/S3 后端（~443行）
│   └── ledger/
│       ├── __init__.py
│       └── engine.py                 # LedgerEngine（377行）
├── 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                   # 测试夹具
    ├── core/
    │   └── test_client.py            # SpaceClient 测试（22个用例）
    ├── backends/
    │   ├── test_base.py
    │   ├── test_es_backend.py
    │   ├── test_mysql_backend.py
    │   ├── test_redis_backend.py
    │   └── test_s3.py
    └── manager/
        ├── test_space_manager.py
        └── test_routing.py
```

## 核心数据模型

| 类 | 用途 | 示例 |
|----|------|------|
| `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"` → 注册名 `"mysql"`
- `data_type="chunk"` → 注册名 `"es"`
- `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)
