Upload agentframe_quad.py with huggingface_hub
Browse files- agentframe_quad.py +65 -21
agentframe_quad.py
CHANGED
|
@@ -13,6 +13,7 @@ AgentFrame 四层融合实现
|
|
| 13 |
L4 KVPager — 物理层: 热/温/冷三级换页 + 预测性驱逐
|
| 14 |
"""
|
| 15 |
import numpy as np
|
|
|
|
| 16 |
from dataclasses import dataclass, field
|
| 17 |
from typing import Optional, Literal
|
| 18 |
|
|
@@ -29,6 +30,8 @@ class CompressedKV:
|
|
| 29 |
heat: float = 0.5 # 热度 (物理层)
|
| 30 |
last_access: float = 0.0 # 最后访问时间
|
| 31 |
size_bytes: int = 0 # 实际字节数
|
|
|
|
|
|
|
| 32 |
|
| 33 |
|
| 34 |
class AbsorbedMLA:
|
|
@@ -179,10 +182,51 @@ class LandmarkRouter:
|
|
| 179 |
# ============================================================
|
| 180 |
# L4 物理层: KV 换页 + 预测性驱逐
|
| 181 |
# ============================================================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
class KVPager:
|
| 183 |
"""
|
| 184 |
三级存储: 显存(热) / 内存(温) / 磁盘(冷, mmap)
|
| 185 |
-
预测性驱逐: 注意力衰减
|
| 186 |
"""
|
| 187 |
def __init__(self, vram_limit_mb=10240, ram_limit_mb=32768):
|
| 188 |
self.vram_limit = vram_limit_mb * 1024 * 1024
|
|
@@ -193,16 +237,18 @@ class KVPager:
|
|
| 193 |
self.vram_used = 0
|
| 194 |
self.ram_used = 0
|
| 195 |
self.access_log = [] # (chunk_id, time, attention_score)
|
|
|
|
| 196 |
|
| 197 |
def place(self, kv: CompressedKV, now: float = 0.0):
|
| 198 |
-
"""放置: 按
|
| 199 |
-
|
|
|
|
|
|
|
| 200 |
if self.vram_used + kv.size_bytes <= self.vram_limit:
|
| 201 |
self.vram[kv.chunk_id] = kv
|
| 202 |
self.vram_used += kv.size_bytes
|
| 203 |
return
|
| 204 |
-
|
| 205 |
-
if kv.heat >= 0.4:
|
| 206 |
if self.ram_used + kv.size_bytes <= self.ram_limit:
|
| 207 |
self.ram[kv.chunk_id] = kv
|
| 208 |
self.ram_used += kv.size_bytes
|
|
@@ -210,31 +256,31 @@ class KVPager:
|
|
| 210 |
self.disk[kv.chunk_id] = kv
|
| 211 |
|
| 212 |
def access(self, chunk_id: int, attention_score: float, now: float):
|
| 213 |
-
"""访问: 更新
|
| 214 |
self.access_log.append((chunk_id, now, attention_score))
|
| 215 |
kv = self.vram.get(chunk_id) or self.ram.get(chunk_id) or self.disk.get(chunk_id)
|
| 216 |
if kv:
|
| 217 |
-
kv.
|
|
|
|
| 218 |
kv.last_access = now
|
|
|
|
|
|
|
|
|
|
| 219 |
# 磁盘 → 提升
|
| 220 |
-
if chunk_id in self.disk and
|
| 221 |
self.disk.pop(chunk_id)
|
| 222 |
-
kv.heat = 0.6
|
| 223 |
self.place(kv, now)
|
| 224 |
|
| 225 |
def eviction_score(self, kv: CompressedKV, now: float) -> float:
|
| 226 |
-
"""预测性驱逐分数 (越高越该驱逐)"""
|
|
|
|
|
|
|
| 227 |
# 注意力衰减: 最近分数越低越该走
|
| 228 |
logs = [a for cid, t, a in self.access_log if cid == kv.chunk_id]
|
| 229 |
-
|
| 230 |
-
return 0.5
|
| 231 |
-
attn_decay = 1.0 - np.mean(logs[-5:]) # 最近注意力均值
|
| 232 |
-
# 访问频率: 越频繁越留
|
| 233 |
-
freq = len(logs) / max(now, 1.0)
|
| 234 |
-
access_factor = min(freq * 5, 1.0)
|
| 235 |
# 时间衰减: 越久没访问越该走
|
| 236 |
-
time_decay = min((now - kv.last_access) /
|
| 237 |
-
return 0.5 *
|
| 238 |
|
| 239 |
def evict(self, now: float, target_layer: str = "vram"):
|
| 240 |
"""驱逐: 选分数最高的换出"""
|
|
@@ -245,11 +291,9 @@ class KVPager:
|
|
| 245 |
victim = pool.pop(victim_id)
|
| 246 |
if target_layer == "vram":
|
| 247 |
self.vram_used -= victim.size_bytes
|
| 248 |
-
|
| 249 |
-
self.place(victim, now) # 降级到 RAM/DISK
|
| 250 |
else:
|
| 251 |
self.ram_used -= victim.size_bytes
|
| 252 |
-
victim.heat = 0.2
|
| 253 |
self.disk[victim_id] = victim
|
| 254 |
return victim_id
|
| 255 |
|
|
|
|
| 13 |
L4 KVPager — 物理层: 热/温/冷三级换页 + 预测性驱逐
|
| 14 |
"""
|
| 15 |
import numpy as np
|
| 16 |
+
import math
|
| 17 |
from dataclasses import dataclass, field
|
| 18 |
from typing import Optional, Literal
|
| 19 |
|
|
|
|
| 30 |
heat: float = 0.5 # 热度 (物理层)
|
| 31 |
last_access: float = 0.0 # 最后访问时间
|
| 32 |
size_bytes: int = 0 # 实际字节数
|
| 33 |
+
importance: float = 0.5 # 重要性 (遗忘曲线用, 0-1)
|
| 34 |
+
access_count: int = 0 # 访问次数 (遗忘曲线用)
|
| 35 |
|
| 36 |
|
| 37 |
class AbsorbedMLA:
|
|
|
|
| 182 |
# ============================================================
|
| 183 |
# L4 物理层: KV 换页 + 预测性驱逐
|
| 184 |
# ============================================================
|
| 185 |
+
# ============================================================
|
| 186 |
+
# L4 物理层: KV 换页 + 预测性驱逐 (含遗忘曲线)
|
| 187 |
+
# ============================================================
|
| 188 |
+
class ForgettingCurve:
|
| 189 |
+
"""
|
| 190 |
+
遗忘曲线 (源自 Project-Aura): 决定 KV 块的热度衰减
|
| 191 |
+
|
| 192 |
+
公式: S(t) = I · 2^(-t / τ)
|
| 193 |
+
其中:
|
| 194 |
+
S(t) = t 时刻的记忆强度 (热度)
|
| 195 |
+
I = 初始重要性
|
| 196 |
+
τ = 半衰期 (高频访问的块半衰期延长)
|
| 197 |
+
访问增强: boost = log2(access_count+1) × 0.1
|
| 198 |
+
"""
|
| 199 |
+
def __init__(self, default_half_life: float = 100.0):
|
| 200 |
+
# KV 场景时间尺度小 (轮次而非天), 默认 100 轮
|
| 201 |
+
self.default_half_life = default_half_life
|
| 202 |
+
|
| 203 |
+
def compute_half_life(self, kv: CompressedKV) -> float:
|
| 204 |
+
"""根据访问频率动态调整半衰期"""
|
| 205 |
+
base = self.default_half_life
|
| 206 |
+
if kv.access_count > 10:
|
| 207 |
+
base *= 7 # 高频访问延至 7×
|
| 208 |
+
elif kv.access_count > 5:
|
| 209 |
+
base *= 3 # 中频延至 3×
|
| 210 |
+
base *= (0.5 + kv.importance) # 高重要性更持久
|
| 211 |
+
return base
|
| 212 |
+
|
| 213 |
+
def strength(self, kv: CompressedKV, now: float) -> float:
|
| 214 |
+
"""记忆强度: 指数衰减 + 访问增强 (替代简单线性热度)"""
|
| 215 |
+
elapsed = max(now - kv.last_access, 0.0)
|
| 216 |
+
half_life = self.compute_half_life(kv)
|
| 217 |
+
decay = math.pow(2, -elapsed / half_life)
|
| 218 |
+
boost = math.log2(kv.access_count + 1) * 0.1
|
| 219 |
+
return min(1.0, kv.importance * decay + boost)
|
| 220 |
+
|
| 221 |
+
def should_forget(self, kv: CompressedKV, now: float, threshold: float = 0.15) -> bool:
|
| 222 |
+
"""强度低于阈值 → 该换出"""
|
| 223 |
+
return self.strength(kv, now) < threshold
|
| 224 |
+
|
| 225 |
+
|
| 226 |
class KVPager:
|
| 227 |
"""
|
| 228 |
三级存储: 显存(热) / 内存(温) / 磁盘(冷, mmap)
|
| 229 |
+
预测性驱逐: 遗忘曲线强度 + 注意力衰减
|
| 230 |
"""
|
| 231 |
def __init__(self, vram_limit_mb=10240, ram_limit_mb=32768):
|
| 232 |
self.vram_limit = vram_limit_mb * 1024 * 1024
|
|
|
|
| 237 |
self.vram_used = 0
|
| 238 |
self.ram_used = 0
|
| 239 |
self.access_log = [] # (chunk_id, time, attention_score)
|
| 240 |
+
self.curve = ForgettingCurve() # 遗忘曲线 (Aura 移植)
|
| 241 |
|
| 242 |
def place(self, kv: CompressedKV, now: float = 0.0):
|
| 243 |
+
"""放置: 按遗忘曲线强度分层"""
|
| 244 |
+
strength = self.curve.strength(kv, now)
|
| 245 |
+
kv.heat = strength
|
| 246 |
+
if strength >= 0.6:
|
| 247 |
if self.vram_used + kv.size_bytes <= self.vram_limit:
|
| 248 |
self.vram[kv.chunk_id] = kv
|
| 249 |
self.vram_used += kv.size_bytes
|
| 250 |
return
|
| 251 |
+
if strength >= 0.3:
|
|
|
|
| 252 |
if self.ram_used + kv.size_bytes <= self.ram_limit:
|
| 253 |
self.ram[kv.chunk_id] = kv
|
| 254 |
self.ram_used += kv.size_bytes
|
|
|
|
| 256 |
self.disk[kv.chunk_id] = kv
|
| 257 |
|
| 258 |
def access(self, chunk_id: int, attention_score: float, now: float):
|
| 259 |
+
"""访问: 更新访问计数 + 重要性 + 日志"""
|
| 260 |
self.access_log.append((chunk_id, now, attention_score))
|
| 261 |
kv = self.vram.get(chunk_id) or self.ram.get(chunk_id) or self.disk.get(chunk_id)
|
| 262 |
if kv:
|
| 263 |
+
kv.access_count += 1
|
| 264 |
+
kv.importance = min(1.0, kv.importance + abs(attention_score) * 0.05)
|
| 265 |
kv.last_access = now
|
| 266 |
+
# 重新计算强度并分层
|
| 267 |
+
strength = self.curve.strength(kv, now)
|
| 268 |
+
kv.heat = strength
|
| 269 |
# 磁盘 → 提升
|
| 270 |
+
if chunk_id in self.disk and strength >= 0.5:
|
| 271 |
self.disk.pop(chunk_id)
|
|
|
|
| 272 |
self.place(kv, now)
|
| 273 |
|
| 274 |
def eviction_score(self, kv: CompressedKV, now: float) -> float:
|
| 275 |
+
"""预测性驱逐分数 (越高越该驱逐): 遗忘曲线 + 注意��衰减"""
|
| 276 |
+
# 遗忘曲线: 强度越低越该走 (主因子)
|
| 277 |
+
forget_factor = 1.0 - self.curve.strength(kv, now)
|
| 278 |
# 注意力衰减: 最近分数越低越该走
|
| 279 |
logs = [a for cid, t, a in self.access_log if cid == kv.chunk_id]
|
| 280 |
+
attn_decay = 1.0 - np.mean(logs[-5:]) if logs else 0.5
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 281 |
# 时间衰减: 越久没访问越该走
|
| 282 |
+
time_decay = min((now - kv.last_access) / self.curve.compute_half_life(kv), 1.0)
|
| 283 |
+
return 0.5 * forget_factor + 0.3 * attn_decay + 0.2 * time_decay
|
| 284 |
|
| 285 |
def evict(self, now: float, target_layer: str = "vram"):
|
| 286 |
"""驱逐: 选分数最高的换出"""
|
|
|
|
| 291 |
victim = pool.pop(victim_id)
|
| 292 |
if target_layer == "vram":
|
| 293 |
self.vram_used -= victim.size_bytes
|
| 294 |
+
self.place(victim, now) # 降级到 RAM/DISK (按遗忘曲线自动分)
|
|
|
|
| 295 |
else:
|
| 296 |
self.ram_used -= victim.size_bytes
|
|
|
|
| 297 |
self.disk[victim_id] = victim
|
| 298 |
return victim_id
|
| 299 |
|