Upload folder using huggingface_hub
Browse files- agentframe/README.md +18 -5
- agentframe/__init__.py +2 -2
- agentframe/api/server.py +2 -2
- agentframe/core/quad.py +93 -15
- agentframe/pyproject.toml +2 -2
- agentframe/tests/test_core.py +22 -0
agentframe/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
|
| 3 |
**脑 = DeepSeek · 手 = 工具执行 · 记忆 = 四层上下文保持**
|
| 4 |
|
| 5 |
-
版本 4.
|
| 6 |
|
| 7 |
---
|
| 8 |
|
|
@@ -29,7 +29,7 @@
|
|
| 29 |
|
| 30 |
| 能力 | 说明 | 验证状态 |
|
| 31 |
|------|------|---------|
|
| 32 |
-
| **KV 压缩** |
|
| 33 |
| **Top-K 保护** | 关键块 16bit + 其余 4bit = **0/100 翻转** | ✓ 实测 (独立实验, 未接入主流程) |
|
| 34 |
| **分层组织** | Sector(16)-Block(256)-Module(1024) 硬件对齐 | ⚠️ 设计未接入 |
|
| 35 |
| **Aura 遗忘** | S(t)=I·2^(-t/τ) 指数遗忘 + 访问增强 | ✓ 有测试 |
|
|
@@ -144,9 +144,9 @@ agentframe/
|
|
| 144 |
|
| 145 |
## 关键技术 (实测数据)
|
| 146 |
|
| 147 |
-
1. **吸收式 MLA**: 只缓存 576 维潜在向量 (512 kv_lora + 64 k_pe), 不展开 KV
|
| 148 |
-
- 270KB → 30.4KB (8.9x) →
|
| 149 |
-
-
|
| 150 |
2. **Top-K 自适应精度保护** (实测, 未接入主流程): 路由层已知 Top-K → 关键块 16bit + 其余 4bit
|
| 151 |
- 1bit 符号补偿 × (17/50 翻转) / 排序保护 × (29/100) / **Top-K 块 16bit ✓ (0/100)**
|
| 152 |
3. **Sector-Block-Module** (设计, 未接入): 16 token=Sector, 16 Sector=Block(256), 4 Block=Module(1024)
|
|
@@ -256,6 +256,19 @@ GPL-3.0 © Cloud LTE Studio
|
|
| 256 |
|
| 257 |
## Changelog
|
| 258 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 259 |
### v4.1.0 (2026-08-18) — Couple 跨轮共现预取
|
| 260 |
|
| 261 |
**新增**
|
|
|
|
| 2 |
|
| 3 |
**脑 = DeepSeek · 手 = 工具执行 · 记忆 = 四层上下文保持**
|
| 4 |
|
| 5 |
+
版本 4.2.0 · GPL-3.0 · Cloud LTE Studio
|
| 6 |
|
| 7 |
---
|
| 8 |
|
|
|
|
| 29 |
|
| 30 |
| 能力 | 说明 | 验证状态 |
|
| 31 |
|------|------|---------|
|
| 32 |
+
| **KV 压缩** | 真 INT4 打包: 576维→288B q4+64B scales = **29.1x** (cos 0.998) | ✓ 有测试 (对齐 L40S 28.4x) |
|
| 33 |
| **Top-K 保护** | 关键块 16bit + 其余 4bit = **0/100 翻转** | ✓ 实测 (独立实验, 未接入主流程) |
|
| 34 |
| **分层组织** | Sector(16)-Block(256)-Module(1024) 硬件对齐 | ⚠️ 设计未接入 |
|
| 35 |
| **Aura 遗忘** | S(t)=I·2^(-t/τ) 指数遗忘 + 访问增强 | ✓ 有测试 |
|
|
|
|
| 144 |
|
| 145 |
## 关键技术 (实测数据)
|
| 146 |
|
| 147 |
+
1. **吸收式 MLA + 真 INT4 打包**: 只缓存 576 维潜在向量 (512 kv_lora + 64 k_pe), 不展开 KV
|
| 148 |
+
- 270KB → 30.4KB (8.9x) → **INT4 打包 352B/层 (29.1x)** — 对齐 L40S 真实推理实测 (DeepSeek-V2-Lite 15.7B, 28.4x)
|
| 149 |
+
- 对称量化 + per-channel scale + nibble 打包 (colibrì quant.h 移植), 往返余弦相似度 0.998
|
| 150 |
2. **Top-K 自适应精度保护** (实测, 未接入主流程): 路由层已知 Top-K → 关键块 16bit + 其余 4bit
|
| 151 |
- 1bit 符号补偿 × (17/50 翻转) / 排序保护 × (29/100) / **Top-K 块 16bit ✓ (0/100)**
|
| 152 |
3. **Sector-Block-Module** (设计, 未接入): 16 token=Sector, 16 Sector=Block(256), 4 Block=Module(1024)
|
|
|
|
| 256 |
|
| 257 |
## Changelog
|
| 258 |
|
| 259 |
+
### v4.2.0 (2026-08-18) — 真 INT4 打包 (28.4x 落地)
|
| 260 |
+
|
| 261 |
+
**新增**
|
| 262 |
+
- **真 INT4 打包存储** (移植 colibrì quant.h pack_int4): 对称量化 (absmax/7) + per-channel scale + nibble 打包
|
| 263 |
+
- 576 维 latent → 288B q4 + 64B scales = 352B/块 (原 float32 2304B)
|
| 264 |
+
- 每 token 27 层 9.28KB → **29.1x 真实压缩** (对齐 L40S 实测 28.4x)
|
| 265 |
+
- 往返精度: 余弦相似度 0.9979
|
| 266 |
+
- `ReversibleQuantizer.quantize_int4()/dequant_int4()`: 打包/解包接口
|
| 267 |
+
- CompressedKV 新增 q4/scales 字段: 真实存储 + save/load 持久化
|
| 268 |
+
- n_ch 默认 32→16 (每通道 36 维, 压缩比与精度平衡)
|
| 269 |
+
|
| 270 |
+
**测试**: 核心测试 11 项 (+test_int4_packing: 往返精度 cos>0.99 + 压缩比>25x)
|
| 271 |
+
|
| 272 |
### v4.1.0 (2026-08-18) — Couple 跨轮共现预取
|
| 273 |
|
| 274 |
**新增**
|
agentframe/__init__.py
CHANGED
|
@@ -5,9 +5,9 @@ AgentFrame — Agent 专用上下文保持框架
|
|
| 5 |
手 = 工具执行 (Function Calling)
|
| 6 |
记忆 = 四层上下文保持 (认知 × 路由 × 存储 × 物理)
|
| 7 |
|
| 8 |
-
版本: 4.
|
| 9 |
"""
|
| 10 |
-
__version__ = "4.
|
| 11 |
|
| 12 |
from .core.quad import (
|
| 13 |
CompressedKV,
|
|
|
|
| 5 |
手 = 工具执行 (Function Calling)
|
| 6 |
记忆 = 四层上下文保持 (认知 × 路由 × 存储 × 物理)
|
| 7 |
|
| 8 |
+
版本: 4.2.0 (先明·第四代: 真 INT4 打包 + Couple 预取 + LFRU 滞回 + 安全加固)
|
| 9 |
"""
|
| 10 |
+
__version__ = "4.2.0"
|
| 11 |
|
| 12 |
from .core.quad import (
|
| 13 |
CompressedKV,
|
agentframe/api/server.py
CHANGED
|
@@ -107,7 +107,7 @@ class AgentFrameAPI:
|
|
| 107 |
|
| 108 |
@app.get("/health")
|
| 109 |
def health():
|
| 110 |
-
return jsonify({"status": "ok", "version": "4.
|
| 111 |
"sessions": len(self.sessions),
|
| 112 |
"system": "AgentFrame Context API"})
|
| 113 |
|
|
@@ -257,7 +257,7 @@ def main():
|
|
| 257 |
config = AgentFrameConfig.from_env()
|
| 258 |
port = int(sys.argv[1]) if len(sys.argv) > 1 else config.api.port
|
| 259 |
api = AgentFrameAPI(config)
|
| 260 |
-
print(f"🚀 AgentFrame API v4.
|
| 261 |
print(f" 四层上下文保持: 认知×路由×存储×物理")
|
| 262 |
print(f" 会话: POST /v1/sessions | 摄入: /ingest | 查询: /ask")
|
| 263 |
print(f" 监听: {config.api.host}:{port}")
|
|
|
|
| 107 |
|
| 108 |
@app.get("/health")
|
| 109 |
def health():
|
| 110 |
+
return jsonify({"status": "ok", "version": "4.2.0",
|
| 111 |
"sessions": len(self.sessions),
|
| 112 |
"system": "AgentFrame Context API"})
|
| 113 |
|
|
|
|
| 257 |
config = AgentFrameConfig.from_env()
|
| 258 |
port = int(sys.argv[1]) if len(sys.argv) > 1 else config.api.port
|
| 259 |
api = AgentFrameAPI(config)
|
| 260 |
+
print(f"🚀 AgentFrame API v4.2.0")
|
| 261 |
print(f" 四层上下文保持: 认知×路由×存储×物理")
|
| 262 |
print(f" 会话: POST /v1/sessions | 摄入: /ingest | 查询: /ask")
|
| 263 |
print(f" 监听: {config.api.host}:{port}")
|
agentframe/core/quad.py
CHANGED
|
@@ -33,6 +33,9 @@ class CompressedKV:
|
|
| 33 |
importance: float = 0.5 # 重要性 (遗忘曲线用, 0-1)
|
| 34 |
access_count: int = 0 # 访问次数 (遗忘曲线用)
|
| 35 |
max_heat: float = 0.0 # 历史峰值热度 (LFRU 滞回用, 防抖)
|
|
|
|
|
|
|
|
|
|
| 36 |
# ===== 自适应 Top-K 精度保护 (3-Agent 讨论室产出 + 实测迭代) =====
|
| 37 |
protected: bool = False # 是否为 Top-K 高精度块 (路由层标记)
|
| 38 |
reversible: bool = False # 启用保护机制
|
|
@@ -147,7 +150,7 @@ class HierarchicalKV:
|
|
| 147 |
|
| 148 |
class ReversibleQuantizer:
|
| 149 |
"""
|
| 150 |
-
自适应 Top-K 精度保护
|
| 151 |
=================================================================
|
| 152 |
讨论室方案: 1bit 误差符号补偿 → 实测无效 (Top-1 翻转 34%)
|
| 153 |
实测根因: 99.9% 翻转发生在分数差<0.1 的接近竞争, 补偿无法消除噪声
|
|
@@ -155,12 +158,67 @@ class ReversibleQuantizer:
|
|
| 155 |
→ 翻转率 0/100 (完美), 存储压缩大部分保留
|
| 156 |
|
| 157 |
核心原则: 精度预算花在"可能参与 Top-K 竞争"的块上
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
@staticmethod
|
| 160 |
def quantize(latent: np.ndarray, quant_bits: int, n_ch: int = 32,
|
| 161 |
reversible: bool = False):
|
| 162 |
"""
|
| 163 |
-
per-channel 非对称量化
|
| 164 |
返回: (量化后值, 保护标记, 大小字节)
|
| 165 |
"""
|
| 166 |
d = latent.shape[-1]
|
|
@@ -212,7 +270,8 @@ class AbsorbedMLA:
|
|
| 212 |
K_ROPE = 64
|
| 213 |
DIM = KV_LORA_RANK + K_ROPE # 576
|
| 214 |
|
| 215 |
-
def __init__(self, n_layers=27, quant_bits=4, n_ch=
|
|
|
|
| 216 |
self.n_layers = n_layers
|
| 217 |
self.quant_bits = quant_bits
|
| 218 |
self.n_ch = n_ch
|
|
@@ -231,16 +290,30 @@ class AbsorbedMLA:
|
|
| 231 |
return self._quantize(latent)
|
| 232 |
|
| 233 |
def _quantize(self, latent: np.ndarray) -> CompressedKV:
|
| 234 |
-
"""
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
self.chunks[kv.chunk_id] = kv
|
| 245 |
return kv
|
| 246 |
|
|
@@ -258,8 +331,13 @@ class AbsorbedMLA:
|
|
| 258 |
return ReversibleQuantizer.compensate(kv)
|
| 259 |
|
| 260 |
def bytes_per_token(self) -> float:
|
| 261 |
-
"""每 token 每层字节 (27 层总)"""
|
| 262 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 263 |
return per_layer * self.n_layers
|
| 264 |
|
| 265 |
def memory_bytes(self, seq_len: int) -> int:
|
|
|
|
| 33 |
importance: float = 0.5 # 重要性 (遗忘曲线用, 0-1)
|
| 34 |
access_count: int = 0 # 访问次数 (遗忘曲线用)
|
| 35 |
max_heat: float = 0.0 # 历史峰值热度 (LFRU 滞回用, 防抖)
|
| 36 |
+
# ===== 真 INT4 打包 (colibrì quant.h 移植) =====
|
| 37 |
+
q4: Optional[np.ndarray] = None # INT4 打包字节 [288] (真实存储)
|
| 38 |
+
scales: Optional[np.ndarray] = None # per-channel scale [n_ch] (真实存储)
|
| 39 |
# ===== 自适应 Top-K 精度保护 (3-Agent 讨论室产出 + 实测迭代) =====
|
| 40 |
protected: bool = False # 是否为 Top-K 高精度块 (路由层标记)
|
| 41 |
reversible: bool = False # 启用保护机制
|
|
|
|
| 150 |
|
| 151 |
class ReversibleQuantizer:
|
| 152 |
"""
|
| 153 |
+
自适应 Top-K 精度保护 + 真 INT4 打包 (colibrì quant.h 移植)
|
| 154 |
=================================================================
|
| 155 |
讨论室方案: 1bit 误差符号补偿 → 实测无效 (Top-1 翻转 34%)
|
| 156 |
实测根因: 99.9% 翻转发生在分数差<0.1 的接近竞争, 补偿无法消除噪声
|
|
|
|
| 158 |
→ 翻转率 0/100 (完美), 存储压缩大部分保留
|
| 159 |
|
| 160 |
核心原则: 精度预算花在"可能参与 Top-K 竞争"的块上
|
| 161 |
+
|
| 162 |
+
INT4 打包 (colibrì quant.h pack_int4):
|
| 163 |
+
对称量化 s = amax/7, 每个 int4 存 nibble (v+8), 两个 nibble 塞 1 字节
|
| 164 |
+
576 维 latent → 288B q4 + n_ch×4B scales
|
| 165 |
"""
|
| 166 |
+
@staticmethod
|
| 167 |
+
def quantize_int4(latent: np.ndarray, n_ch: int = 16) -> tuple:
|
| 168 |
+
"""
|
| 169 |
+
真 INT4 打包 (colibrì pack_int4 移植): 对称量化 + per-channel scale
|
| 170 |
+
返回: (q4 打包字节 np.uint8, scales np.float32, 大小字节)
|
| 171 |
+
"""
|
| 172 |
+
d = latent.shape[-1]
|
| 173 |
+
ch = d // n_ch
|
| 174 |
+
tc = latent.reshape(n_ch, ch)
|
| 175 |
+
# 对称量化: s = amax / 7 (int4 范围 -8..7, 对称用 7)
|
| 176 |
+
amax = np.abs(tc).max(axis=-1) # [n_ch]
|
| 177 |
+
scales = amax / 7.0
|
| 178 |
+
scales = np.where(scales < 1e-8, 1e-8, scales) # 防除零
|
| 179 |
+
q = np.round(tc / scales[:, None]).astype(np.int32)
|
| 180 |
+
q = np.clip(q, -8, 7)
|
| 181 |
+
# nibble 打包: (v0+8) | ((v1+8)<<4), 两个 int4 塞 1 字节
|
| 182 |
+
q_flat = q.reshape(-1)
|
| 183 |
+
n_pairs = (q_flat.shape[0] + 1) // 2
|
| 184 |
+
q4 = np.zeros(n_pairs, dtype=np.uint8)
|
| 185 |
+
q4[0::1] = (q_flat[0::2] + 8).astype(np.uint8)
|
| 186 |
+
if q_flat.shape[0] % 2 == 1:
|
| 187 |
+
# 奇数: 最后补一个 0
|
| 188 |
+
q4 = np.zeros(n_pairs, dtype=np.uint8)
|
| 189 |
+
q4[:len(q_flat)//2] = ((q_flat[0::2][:len(q_flat)//2] + 8)
|
| 190 |
+
| ((q_flat[1::2][:len(q_flat)//2] + 8) << 4)).astype(np.uint8)
|
| 191 |
+
q4[-1] = (q_flat[-1] + 8).astype(np.uint8)
|
| 192 |
+
else:
|
| 193 |
+
q4 = ((q_flat[0::2] + 8) | ((q_flat[1::2] + 8) << 4)).astype(np.uint8)
|
| 194 |
+
size = int(n_pairs) + int(n_ch * 4) # q4 字节 + scales 字节
|
| 195 |
+
return q4, scales.astype(np.float32), size
|
| 196 |
+
|
| 197 |
+
@staticmethod
|
| 198 |
+
def dequant_int4(q4: np.ndarray, scales: np.ndarray, d: int = 576) -> np.ndarray:
|
| 199 |
+
"""
|
| 200 |
+
解包: q4 nibbles → float32 (检索/路由用)
|
| 201 |
+
"""
|
| 202 |
+
vals = np.empty(d, dtype=np.float32)
|
| 203 |
+
n_pairs = q4.shape[0]
|
| 204 |
+
lo = (q4 & 0x0F).astype(np.int32) - 8
|
| 205 |
+
hi = ((q4 >> 4) & 0x0F).astype(np.int32) - 8
|
| 206 |
+
vals[0::2] = lo
|
| 207 |
+
if d % 2 == 1:
|
| 208 |
+
vals[1::2][:len(hi)] = hi
|
| 209 |
+
else:
|
| 210 |
+
vals[1::2] = hi
|
| 211 |
+
# per-channel scale 反缩放
|
| 212 |
+
n_ch = scales.shape[0]
|
| 213 |
+
ch = d // n_ch
|
| 214 |
+
vals = vals.reshape(n_ch, ch) * scales[:, None]
|
| 215 |
+
return vals.reshape(-1)
|
| 216 |
+
|
| 217 |
@staticmethod
|
| 218 |
def quantize(latent: np.ndarray, quant_bits: int, n_ch: int = 32,
|
| 219 |
reversible: bool = False):
|
| 220 |
"""
|
| 221 |
+
per-channel 非对称量化 (兼容旧路径)
|
| 222 |
返回: (量化后值, 保护标记, 大小字节)
|
| 223 |
"""
|
| 224 |
d = latent.shape[-1]
|
|
|
|
| 270 |
K_ROPE = 64
|
| 271 |
DIM = KV_LORA_RANK + K_ROPE # 576
|
| 272 |
|
| 273 |
+
def __init__(self, n_layers=27, quant_bits=4, n_ch=16, reversible=False):
|
| 274 |
+
"""n_ch=16: 每通道 36 维, 288B q4 + 64B scales = 352B/层 → 27层 9.3KB ≈ 28.4x"""
|
| 275 |
self.n_layers = n_layers
|
| 276 |
self.quant_bits = quant_bits
|
| 277 |
self.n_ch = n_ch
|
|
|
|
| 290 |
return self._quantize(latent)
|
| 291 |
|
| 292 |
def _quantize(self, latent: np.ndarray) -> CompressedKV:
|
| 293 |
+
"""真 INT4 打包 (colibrì pack_int4): 存 q4 字节 + scales, 不再存 float32"""
|
| 294 |
+
if self.quant_bits == 4:
|
| 295 |
+
q4, scales, size = ReversibleQuantizer.quantize_int4(latent, self.n_ch)
|
| 296 |
+
# latent 保留解包值供路由检索, 但 size_bytes 记真实打包大小
|
| 297 |
+
deq = ReversibleQuantizer.dequant_int4(q4, scales, latent.shape[-1])
|
| 298 |
+
kv = CompressedKV(
|
| 299 |
+
chunk_id=len(self.chunks),
|
| 300 |
+
latent=deq,
|
| 301 |
+
quant_bits=self.quant_bits,
|
| 302 |
+
size_bytes=size,
|
| 303 |
+
reversible=self.reversible,
|
| 304 |
+
q4=q4,
|
| 305 |
+
scales=scales,
|
| 306 |
+
)
|
| 307 |
+
else:
|
| 308 |
+
deq, _, size = ReversibleQuantizer.quantize(
|
| 309 |
+
latent, self.quant_bits, self.n_ch, self.reversible)
|
| 310 |
+
kv = CompressedKV(
|
| 311 |
+
chunk_id=len(self.chunks),
|
| 312 |
+
latent=deq,
|
| 313 |
+
quant_bits=self.quant_bits,
|
| 314 |
+
size_bytes=size,
|
| 315 |
+
reversible=self.reversible,
|
| 316 |
+
)
|
| 317 |
self.chunks[kv.chunk_id] = kv
|
| 318 |
return kv
|
| 319 |
|
|
|
|
| 331 |
return ReversibleQuantizer.compensate(kv)
|
| 332 |
|
| 333 |
def bytes_per_token(self) -> float:
|
| 334 |
+
"""每 token 每层字节 (27 层总): INT4 打包 = 288B q4 + n_ch*4B scales"""
|
| 335 |
+
if self.quant_bits == 4:
|
| 336 |
+
per_layer = self.DIM // 2 + self.n_ch * 4 # q4 nibbles + scales
|
| 337 |
+
elif self.quant_bits < 16:
|
| 338 |
+
per_layer = self.DIM * (self.quant_bits / 8)
|
| 339 |
+
else:
|
| 340 |
+
per_layer = self.DIM * 2
|
| 341 |
return per_layer * self.n_layers
|
| 342 |
|
| 343 |
def memory_bytes(self, seq_len: int) -> int:
|
agentframe/pyproject.toml
CHANGED
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
| 4 |
|
| 5 |
[project]
|
| 6 |
name = "agentframe"
|
| 7 |
-
version = "4.
|
| 8 |
-
description = "AgentFrame v4.
|
| 9 |
readme = "README.md"
|
| 10 |
requires-python = ">=3.10"
|
| 11 |
license = { text = "GPL-3.0" }
|
|
|
|
| 4 |
|
| 5 |
[project]
|
| 6 |
name = "agentframe"
|
| 7 |
+
version = "4.2.0"
|
| 8 |
+
description = "AgentFrame v4.2 — 真 INT4 打包 (29.1x) + Couple 跨轮预取 + LFRU 滞回 + MemoryDirector"
|
| 9 |
readme = "README.md"
|
| 10 |
requires-python = ">=3.10"
|
| 11 |
license = { text = "GPL-3.0" }
|
agentframe/tests/test_core.py
CHANGED
|
@@ -195,6 +195,27 @@ def test_couple_prefetch():
|
|
| 195 |
print("✅ test_couple_prefetch (RAM 满时 LFRU 腾位)")
|
| 196 |
|
| 197 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 198 |
if __name__ == "__main__":
|
| 199 |
test_ingest_and_retrieve()
|
| 200 |
test_similar_text_retrieval()
|
|
@@ -206,4 +227,5 @@ if __name__ == "__main__":
|
|
| 206 |
test_tool_safety()
|
| 207 |
test_api_auth()
|
| 208 |
test_couple_prefetch()
|
|
|
|
| 209 |
print("\n🎉 全部核心测试通过!")
|
|
|
|
| 195 |
print("✅ test_couple_prefetch (RAM 满时 LFRU 腾位)")
|
| 196 |
|
| 197 |
|
| 198 |
+
def test_int4_packing():
|
| 199 |
+
"""真 INT4 打包 (colibrì quant.h 移植): 往返精度 + 真实压缩比"""
|
| 200 |
+
import numpy as np
|
| 201 |
+
from agentframe.core.quad import ReversibleQuantizer, AbsorbedMLA
|
| 202 |
+
|
| 203 |
+
rng = np.random.default_rng(42)
|
| 204 |
+
latent = np.tanh(rng.normal(0, 1, 576).astype(np.float32))
|
| 205 |
+
q4, scales, size = ReversibleQuantizer.quantize_int4(latent, n_ch=16)
|
| 206 |
+
deq = ReversibleQuantizer.dequant_int4(q4, scales, 576)
|
| 207 |
+
# 往返精度: 余弦相似度 > 0.99
|
| 208 |
+
sim = float(deq @ latent) / (np.linalg.norm(deq) * np.linalg.norm(latent))
|
| 209 |
+
assert sim > 0.99, f"INT4 往返相似度 {sim:.4f}"
|
| 210 |
+
# 真实压缩: 352B vs 原始 2304B
|
| 211 |
+
assert size == 288 + 64, f"打包大小 {size} != 352"
|
| 212 |
+
# 每 token 压缩比对齐 L40S 实测 28.4x
|
| 213 |
+
m = AbsorbedMLA(n_layers=27, quant_bits=4, n_ch=16)
|
| 214 |
+
ratio = 270 * 1024 / m.bytes_per_token()
|
| 215 |
+
assert ratio > 25, f"压缩比 {ratio:.1f}x 不足"
|
| 216 |
+
print(f"✅ test_int4_packing (cos={sim:.4f}, {ratio:.1f}x)")
|
| 217 |
+
|
| 218 |
+
|
| 219 |
if __name__ == "__main__":
|
| 220 |
test_ingest_and_retrieve()
|
| 221 |
test_similar_text_retrieval()
|
|
|
|
| 227 |
test_tool_safety()
|
| 228 |
test_api_auth()
|
| 229 |
test_couple_prefetch()
|
| 230 |
+
test_int4_packing()
|
| 231 |
print("\n🎉 全部核心测试通过!")
|