以空间为概念的统一存储、检索、编排引擎 - 像管理文件夹一样管理数据
Project description
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
- TODO:docs/todo.md
快速开始
安装
# 核心(零外部依赖)
pip install lzm-space
# 全部后端
pip install lzm-space[all]
# 指定后端
pip install lzm-space[s3,es,sql]
# + 插件系统(切片/向量化/提取/重排)
pip install lzm-space[all] lzm-plugin[all]
基础用法
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 提供知识库所需的计算能力:
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"
相关文档
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lzm_space-0.2.0.tar.gz.
File metadata
- Download URL: lzm_space-0.2.0.tar.gz
- Upload date:
- Size: 72.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
30adc0fbfa3795deaee4e872439fadfe7b471886bb74d6de555fd8b4f4943a70
|
|
| MD5 |
50d03a29878cdaa6e0e9315f15938684
|
|
| BLAKE2b-256 |
24b0e8b7a4830a5a32702e15f198419255adb23de0e1ec3e448410f2bc613e18
|
File details
Details for the file lzm_space-0.2.0-py3-none-any.whl.
File metadata
- Download URL: lzm_space-0.2.0-py3-none-any.whl
- Upload date:
- Size: 80.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
30b75efb849dea11e257470e1d630eaaefb798cf4f2c78a61dfb02770ba61d93
|
|
| MD5 |
1e3dfa5fee4e0d3f4dacfff260dfe964
|
|
| BLAKE2b-256 |
ccf916128e7f77fb4ea3559c9154e28883101bc96ad021fdaa64a598aae252e8
|