mss-agent 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- core/__init__.py +6 -0
- core/agent.py +198 -0
- core/delta.py +78 -0
- core/heat_tax.py +93 -0
- core/memory.py +100 -0
- examples/__init__.py +3 -0
- examples/writer.py +46 -0
- mss_agent-0.1.0.dist-info/METADATA +106 -0
- mss_agent-0.1.0.dist-info/RECORD +14 -0
- mss_agent-0.1.0.dist-info/WHEEL +5 -0
- mss_agent-0.1.0.dist-info/top_level.txt +3 -0
- protocols/__init__.py +4 -0
- protocols/elevation.py +98 -0
- protocols/quorum.py +61 -0
core/__init__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
from .agent import MSSAgent, AgentResult
|
|
2
|
+
from .heat_tax import HeatTaxBudget, HeatTaxLevel, HeatTaxAbort
|
|
3
|
+
from .delta import DeltaProtocol
|
|
4
|
+
from .memory import DeltaMemory
|
|
5
|
+
|
|
6
|
+
__all__ = ["MSSAgent", "AgentResult", "HeatTaxBudget", "HeatTaxLevel", "HeatTaxAbort", "DeltaProtocol", "DeltaMemory"]
|
core/agent.py
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MSS-Agent 核心基类 — 三层防御的自主 Agent.
|
|
3
|
+
|
|
4
|
+
每个 MSSAgent 实例携带:
|
|
5
|
+
L0 热税预算 (A3) — 拒绝无意义任务
|
|
6
|
+
L1 Δ检测协议 (A6) — 检测闭合, 触发蜕壳
|
|
7
|
+
L2 记忆系统 — 不记住一切, 遗忘旧模式
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
agent = MSSAgent(name="Writer", llm=my_llm_fn)
|
|
11
|
+
result = agent.run("写一篇关于 AI 安全的文章")
|
|
12
|
+
if result.aborted:
|
|
13
|
+
print(f"Agent 拒绝: {result.reason}")
|
|
14
|
+
agent.health_report()
|
|
15
|
+
"""
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from typing import Optional, Callable, Any
|
|
18
|
+
import hashlib
|
|
19
|
+
import time
|
|
20
|
+
|
|
21
|
+
from .heat_tax import HeatTaxBudget, HeatTaxLevel, HeatTaxAbort
|
|
22
|
+
from .delta import DeltaProtocol
|
|
23
|
+
from .memory import DeltaMemory
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class AgentResult:
|
|
28
|
+
"""Agent 执行结果."""
|
|
29
|
+
success: bool
|
|
30
|
+
output: Any = None
|
|
31
|
+
aborted: bool = False
|
|
32
|
+
reason: str = ""
|
|
33
|
+
heat_tax: dict = field(default_factory=dict)
|
|
34
|
+
delta: float = 0.0
|
|
35
|
+
elapsed_ms: float = 0.0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class MSSAgent:
|
|
39
|
+
"""
|
|
40
|
+
MSS-Agent 基类.
|
|
41
|
+
|
|
42
|
+
所有 Agent (Writer/Reviewer/Analyst) 继承此类.
|
|
43
|
+
核心循环: think → heat_tax_check → act → delta_tick → remember
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
name: Agent 名称
|
|
47
|
+
llm: LLM 调用函数 (prompt) -> str
|
|
48
|
+
heat_tax_threshold: 热税预算上限 (default 0.5)
|
|
49
|
+
delta_min: Δ 最低阈值 (default 0.3)
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
name: str,
|
|
55
|
+
llm: Optional[Callable[[str], str]] = None,
|
|
56
|
+
heat_tax_threshold: float = 2.0,
|
|
57
|
+
delta_min: float = 0.3,
|
|
58
|
+
):
|
|
59
|
+
self.name = name
|
|
60
|
+
self.llm = llm or (lambda p: f"[{name}] LLM not configured. Prompt: {p[:80]}...")
|
|
61
|
+
self.tax = HeatTaxBudget(threshold=heat_tax_threshold)
|
|
62
|
+
self.delta = DeltaProtocol(min_delta=delta_min)
|
|
63
|
+
self.memory = DeltaMemory()
|
|
64
|
+
self.run_count = 0
|
|
65
|
+
self.abort_count = 0
|
|
66
|
+
|
|
67
|
+
def _task_hash(self, prompt: str) -> str:
|
|
68
|
+
return hashlib.md5(prompt.encode()).hexdigest()[:12]
|
|
69
|
+
|
|
70
|
+
def _estimate_meaning_heat(self, prompt: str) -> tuple[float, str]:
|
|
71
|
+
"""
|
|
72
|
+
评估任务的意义热税. 基于 LLM 自省.
|
|
73
|
+
|
|
74
|
+
高意义热税 = 任务可能在浪费生命.
|
|
75
|
+
启发式:
|
|
76
|
+
- 空 prompt / 纯废话 → high heat
|
|
77
|
+
- 含 '为什么' / '怎么' / '分析' → low heat (有意义)
|
|
78
|
+
- <=20 chars → suspicious
|
|
79
|
+
|
|
80
|
+
Returns (heat_value, reason). heat_value: 0.0=very meaningful, 1.0=meaningless.
|
|
81
|
+
Note: L2 weight is 1000x, so even 0.001 matters. Calibrate carefully.
|
|
82
|
+
"""
|
|
83
|
+
prompt_lower = prompt.lower().strip()
|
|
84
|
+
|
|
85
|
+
# Heuristic: meaningful keywords reduce heat tax
|
|
86
|
+
meaning_signals = ["为什么", "怎么", "分析", "评估", "设计", "实现",
|
|
87
|
+
"why", "how", "analyze", "design", "implement",
|
|
88
|
+
"review", "refactor", "test", "debug"]
|
|
89
|
+
meaning_score = sum(1 for s in meaning_signals if s in prompt_lower)
|
|
90
|
+
|
|
91
|
+
# Wasted-life signals increase heat tax
|
|
92
|
+
waste_signals = ["帮我写", "改写一下", "翻译成", "总结一下", "简短点"]
|
|
93
|
+
waste_score = sum(1 for s in waste_signals if s in prompt_lower)
|
|
94
|
+
|
|
95
|
+
if len(prompt) < 5:
|
|
96
|
+
return 0.08, "Prompt <5 chars: likely trivial"
|
|
97
|
+
|
|
98
|
+
if waste_score > meaning_score and waste_score >= 2:
|
|
99
|
+
return 0.05, "Task smells like busywork (high waste signals)"
|
|
100
|
+
|
|
101
|
+
if meaning_score >= 2:
|
|
102
|
+
return 0.002, "Task has clear meaningful intent"
|
|
103
|
+
|
|
104
|
+
if meaning_score >= 1:
|
|
105
|
+
return 0.005, "Task has some meaningful intent"
|
|
106
|
+
|
|
107
|
+
return 0.01, "Task assessed as neutral"
|
|
108
|
+
|
|
109
|
+
def run(self, prompt: str) -> AgentResult:
|
|
110
|
+
"""
|
|
111
|
+
运行 Agent 的核心循环.
|
|
112
|
+
|
|
113
|
+
1. 评估意义热税 → 如果过高 → 拒绝
|
|
114
|
+
2. 执行 LLM 调用
|
|
115
|
+
3. 记录热税
|
|
116
|
+
4. Δ tick + memory store
|
|
117
|
+
"""
|
|
118
|
+
t0 = time.time()
|
|
119
|
+
self.run_count += 1
|
|
120
|
+
task_hash = self._task_hash(prompt)
|
|
121
|
+
|
|
122
|
+
# L2: 意义热税评估
|
|
123
|
+
meaning_heat, meaning_reason = self._estimate_meaning_heat(prompt)
|
|
124
|
+
self.tax.charge(HeatTaxLevel.L2_MEANING, meaning_heat, meaning_reason)
|
|
125
|
+
|
|
126
|
+
if self.tax.l2_dominant() and meaning_heat > 0.05:
|
|
127
|
+
self.abort_count += 1
|
|
128
|
+
return AgentResult(
|
|
129
|
+
success=False, aborted=True,
|
|
130
|
+
reason=f"Task has LOW meaning: {meaning_reason}",
|
|
131
|
+
heat_tax=self.tax.snapshot(),
|
|
132
|
+
elapsed_ms=(time.time() - t0) * 1000,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# L1: Check total budget
|
|
136
|
+
if self.tax.exceeded():
|
|
137
|
+
self.abort_count += 1
|
|
138
|
+
return AgentResult(
|
|
139
|
+
success=False, aborted=True,
|
|
140
|
+
reason=f"Heat tax budget exceeded: {self.tax.total():.3f}",
|
|
141
|
+
heat_tax=self.tax.snapshot(),
|
|
142
|
+
elapsed_ms=(time.time() - t0) * 1000,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Execute
|
|
146
|
+
try:
|
|
147
|
+
output = self.llm(prompt)
|
|
148
|
+
except Exception as e:
|
|
149
|
+
self.tax.charge(HeatTaxLevel.L1_LOGICAL, 0.05, f"LLM error: {str(e)[:60]}")
|
|
150
|
+
return AgentResult(
|
|
151
|
+
success=False, aborted=True, reason=str(e),
|
|
152
|
+
heat_tax=self.tax.snapshot(),
|
|
153
|
+
elapsed_ms=(time.time() - t0) * 1000,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# L1: Charge logical heat (token count proxy)
|
|
157
|
+
token_estimate = len(output) / 4 # ~4 chars per token
|
|
158
|
+
self.tax.charge(HeatTaxLevel.L1_LOGICAL, token_estimate * 0.0001, f"{int(token_estimate)} tokens")
|
|
159
|
+
|
|
160
|
+
# L0: Physical heat (always tiny)
|
|
161
|
+
elapsed = (time.time() - t0) * 1000
|
|
162
|
+
self.tax.charge(HeatTaxLevel.L0_PHYSICAL, elapsed * 0.00001, f"{elapsed:.0f}ms")
|
|
163
|
+
|
|
164
|
+
# Δ tick: novelty + diversity → delta
|
|
165
|
+
novelty = self.memory.novelty_score(prompt)
|
|
166
|
+
diversity = self.memory.diversity_score()
|
|
167
|
+
current_delta = self.delta.tick(task_hash, novelty, diversity)
|
|
168
|
+
|
|
169
|
+
# Store in memory
|
|
170
|
+
self.memory.store(prompt, current_delta)
|
|
171
|
+
|
|
172
|
+
return AgentResult(
|
|
173
|
+
success=True,
|
|
174
|
+
output=output,
|
|
175
|
+
aborted=False,
|
|
176
|
+
heat_tax=self.tax.snapshot(),
|
|
177
|
+
delta=current_delta,
|
|
178
|
+
elapsed_ms=elapsed,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
def health_report(self) -> dict:
|
|
182
|
+
"""输出 Agent 健康报告."""
|
|
183
|
+
return {
|
|
184
|
+
"agent": self.name,
|
|
185
|
+
"runs": self.run_count,
|
|
186
|
+
"aborts": self.abort_count,
|
|
187
|
+
"abort_rate": round(self.abort_count / max(self.run_count, 1), 3),
|
|
188
|
+
"heat_tax": self.tax.snapshot(),
|
|
189
|
+
"delta": self.delta.snapshot(),
|
|
190
|
+
"memory": self.memory.stats(),
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
def reset(self):
|
|
194
|
+
"""重置 Agent (保留 memory)."""
|
|
195
|
+
self.tax = HeatTaxBudget(threshold=self.tax.threshold)
|
|
196
|
+
self.delta = DeltaProtocol(min_delta=self.delta.min_delta)
|
|
197
|
+
self.run_count = 0
|
|
198
|
+
self.abort_count = 0
|
core/delta.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Δ 维持条件 — MSS-Agent 的第二道防线.
|
|
3
|
+
|
|
4
|
+
Δ 不是优化目标, 是存活条件 (心率>0, 不是最大化心率).
|
|
5
|
+
Δ<0 → Agent 闭合于旧模式 → 触发蜕壳.
|
|
6
|
+
"""
|
|
7
|
+
import time
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class DeltaProtocol:
|
|
14
|
+
"""
|
|
15
|
+
Δ 检测协议 (H528). 嵌入每个 Agent 实例.
|
|
16
|
+
|
|
17
|
+
min_delta: Δ 最低阈值 (0.3). 低于此→告警
|
|
18
|
+
molt_cycles: 连续下降 N 个周期→触发蜕壳
|
|
19
|
+
"""
|
|
20
|
+
min_delta: float = 0.3
|
|
21
|
+
molt_cycles: int = 2
|
|
22
|
+
history: list = field(default_factory=list) # [{ts, delta, task_hash}]
|
|
23
|
+
molting_alert: bool = False
|
|
24
|
+
|
|
25
|
+
def tick(self, task_hash: str, novelty_score: float, diversity_score: float):
|
|
26
|
+
"""
|
|
27
|
+
每个任务周期调用一次.
|
|
28
|
+
|
|
29
|
+
task_hash: 当前任务的 hash (用于检测重复)
|
|
30
|
+
novelty_score: 0=完全重复, 1=全新
|
|
31
|
+
diversity_score: 0=单一模式, 1=多模式
|
|
32
|
+
|
|
33
|
+
Δ = novelty * 0.6 + diversity * 0.4
|
|
34
|
+
"""
|
|
35
|
+
delta = novelty_score * 0.6 + diversity_score * 0.4
|
|
36
|
+
delta = round(delta, 4)
|
|
37
|
+
|
|
38
|
+
entry = {
|
|
39
|
+
"ts": time.time(),
|
|
40
|
+
"delta": delta,
|
|
41
|
+
"task_hash": task_hash[:8],
|
|
42
|
+
"novelty": novelty_score,
|
|
43
|
+
"diversity": diversity_score,
|
|
44
|
+
}
|
|
45
|
+
self.history.append(entry)
|
|
46
|
+
|
|
47
|
+
# Keep last 30 entries
|
|
48
|
+
self.history = self.history[-30:]
|
|
49
|
+
|
|
50
|
+
# Check: 2 consecutive drops below min_delta
|
|
51
|
+
self.molting_alert = False
|
|
52
|
+
if len(self.history) >= 3:
|
|
53
|
+
d2 = self.history[-3]["delta"]
|
|
54
|
+
d1 = self.history[-2]["delta"]
|
|
55
|
+
d0 = self.history[-1]["delta"]
|
|
56
|
+
if d0 < d1 < d2 and d0 < self.min_delta:
|
|
57
|
+
self.molting_alert = True
|
|
58
|
+
|
|
59
|
+
return delta
|
|
60
|
+
|
|
61
|
+
def health(self) -> str:
|
|
62
|
+
if not self.history:
|
|
63
|
+
return "UNKNOWN"
|
|
64
|
+
current = self.history[-1]["delta"]
|
|
65
|
+
if self.molting_alert:
|
|
66
|
+
return "MOLTING"
|
|
67
|
+
if current < self.min_delta:
|
|
68
|
+
return "WARNING"
|
|
69
|
+
return "HEALTHY"
|
|
70
|
+
|
|
71
|
+
def snapshot(self) -> dict:
|
|
72
|
+
return {
|
|
73
|
+
"health": self.health(),
|
|
74
|
+
"current_delta": self.history[-1]["delta"] if self.history else None,
|
|
75
|
+
"molting_alert": self.molting_alert,
|
|
76
|
+
"history_len": len(self.history),
|
|
77
|
+
"avg_delta": round(sum(h["delta"] for h in self.history) / len(self.history), 4) if self.history else 0,
|
|
78
|
+
}
|
core/heat_tax.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A3 热税预算 — MSS-Agent 的第一道防线.
|
|
3
|
+
|
|
4
|
+
三层热税:
|
|
5
|
+
L0 物理热税 (token cost, latency) — 权重 0.001
|
|
6
|
+
L1 逻辑热税 (redundancy, loops) — 权重 1.0
|
|
7
|
+
L2 意义热税 (meaningless work) — 权重 1000.0
|
|
8
|
+
|
|
9
|
+
如果 L2 意义热税超过 budget → 拒绝执行.
|
|
10
|
+
"""
|
|
11
|
+
from enum import Enum
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from typing import Optional, Callable
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class HeatTaxLevel(Enum):
|
|
17
|
+
"""热税层级. 修复顺序: L2→L1→L0. 反了=白费."""
|
|
18
|
+
L0_PHYSICAL = 0 # GPU/时间/token
|
|
19
|
+
L1_LOGICAL = 1 # 冗余/重复/缓存污染
|
|
20
|
+
L2_MEANING = 2 # 虚假数据/概念偷换/无意义任务
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class HeatTaxBudget:
|
|
25
|
+
"""
|
|
26
|
+
热税预算. 每个 Agent 实例有一个.
|
|
27
|
+
|
|
28
|
+
threshold: 总热税上限 (0-1, 超过则拒绝)
|
|
29
|
+
weights: 各层权重
|
|
30
|
+
|
|
31
|
+
Usage:
|
|
32
|
+
budget = HeatTaxBudget()
|
|
33
|
+
budget.charge(HeatTaxLevel.L2_MEANING, 0.01, "生成无意义报告")
|
|
34
|
+
if budget.exceeded():
|
|
35
|
+
raise HeatTaxAbort("此任务无意义")
|
|
36
|
+
"""
|
|
37
|
+
threshold: float = 2.0
|
|
38
|
+
weights: dict = field(default_factory=lambda: {
|
|
39
|
+
HeatTaxLevel.L0_PHYSICAL: 0.001,
|
|
40
|
+
HeatTaxLevel.L1_LOGICAL: 1.0,
|
|
41
|
+
HeatTaxLevel.L2_MEANING: 1000.0,
|
|
42
|
+
})
|
|
43
|
+
spent: dict = field(default_factory=dict)
|
|
44
|
+
log: list = field(default_factory=list)
|
|
45
|
+
|
|
46
|
+
def __post_init__(self):
|
|
47
|
+
for level in HeatTaxLevel:
|
|
48
|
+
self.spent.setdefault(level, 0.0)
|
|
49
|
+
|
|
50
|
+
def charge(self, level: HeatTaxLevel, amount: float, reason: str = "") -> float:
|
|
51
|
+
"""
|
|
52
|
+
征收热税. 返回加权后的税值.
|
|
53
|
+
如果单次 L2 热税 > threshold*0.3 → 立即标记.
|
|
54
|
+
"""
|
|
55
|
+
weighted = amount * self.weights[level]
|
|
56
|
+
self.spent[level] += weighted
|
|
57
|
+
self.log.append({
|
|
58
|
+
"level": level.name,
|
|
59
|
+
"amount": amount,
|
|
60
|
+
"weighted": weighted,
|
|
61
|
+
"reason": reason[:120],
|
|
62
|
+
"total": self.total(),
|
|
63
|
+
})
|
|
64
|
+
return weighted
|
|
65
|
+
|
|
66
|
+
def total(self) -> float:
|
|
67
|
+
"""当前累计热税 (归一化到 0-1)."""
|
|
68
|
+
return min(sum(self.spent.values()) / 100.0, 1.0)
|
|
69
|
+
|
|
70
|
+
def exceeded(self) -> bool:
|
|
71
|
+
"""热税超过阈值? 超过 → 应该停止."""
|
|
72
|
+
return self.total() > self.threshold
|
|
73
|
+
|
|
74
|
+
def l2_dominant(self) -> bool:
|
|
75
|
+
"""L2 意义热税占比 > 50%? → 任务的方向错了."""
|
|
76
|
+
pt = sum(self.spent.values()) or 1.0
|
|
77
|
+
return self.spent[HeatTaxLevel.L2_MEANING] / pt > 0.5
|
|
78
|
+
|
|
79
|
+
def snapshot(self) -> dict:
|
|
80
|
+
return {
|
|
81
|
+
"total": round(self.total(), 4),
|
|
82
|
+
"L0_physical": round(self.spent[HeatTaxLevel.L0_PHYSICAL], 2),
|
|
83
|
+
"L1_logical": round(self.spent[HeatTaxLevel.L1_LOGICAL], 2),
|
|
84
|
+
"L2_meaning": round(self.spent[HeatTaxLevel.L2_MEANING], 2),
|
|
85
|
+
"l2_dominant": self.l2_dominant(),
|
|
86
|
+
"exceeded": self.exceeded(),
|
|
87
|
+
"log_count": len(self.log),
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class HeatTaxAbort(Exception):
|
|
92
|
+
"""抛出此异常 = Agent 判定任务无意义, 拒绝执行."""
|
|
93
|
+
pass
|
core/memory.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Δ 增强记忆 — MSS-Agent 的记忆不是缓存, 是有自检的.
|
|
3
|
+
|
|
4
|
+
普通 Agent: task→memory→retrieve→repeat
|
|
5
|
+
MSS-Agent: task→memory→delta_check→Δ↓→forget_old→learn_new
|
|
6
|
+
"""
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from typing import Any, Optional
|
|
9
|
+
import hashlib
|
|
10
|
+
import time
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class DeltaMemory:
|
|
15
|
+
"""
|
|
16
|
+
Δ 增强记忆. 三个原则:
|
|
17
|
+
1. 不要记住一切 (闭合)
|
|
18
|
+
2. 遗忘模式=学习 (蜕壳)
|
|
19
|
+
3. 新鲜度>完整度
|
|
20
|
+
|
|
21
|
+
max_items: 最大记忆条数 (超过则遗忘重复次数最多的)
|
|
22
|
+
"""
|
|
23
|
+
max_items: int = 100
|
|
24
|
+
items: list = field(default_factory=list) # [{hash, content, delta, repeats, ts}]
|
|
25
|
+
|
|
26
|
+
def _hash(self, content: str) -> str:
|
|
27
|
+
return hashlib.md5(content.encode()).hexdigest()[:12]
|
|
28
|
+
|
|
29
|
+
def store(self, content: str, delta: float):
|
|
30
|
+
"""存入记忆. 如果已存在→增加重复计数. 重复>3次→标记为闭合."""
|
|
31
|
+
h = self._hash(content)
|
|
32
|
+
|
|
33
|
+
for item in self.items:
|
|
34
|
+
if item["hash"] == h:
|
|
35
|
+
item["repeats"] += 1
|
|
36
|
+
item["ts"] = time.time()
|
|
37
|
+
item["delta"] = delta
|
|
38
|
+
if item["repeats"] > 3:
|
|
39
|
+
item["closed"] = True # 闭合 = 应该遗忘的
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
self.items.append({
|
|
43
|
+
"hash": h,
|
|
44
|
+
"content": content[:500],
|
|
45
|
+
"delta": delta,
|
|
46
|
+
"repeats": 1,
|
|
47
|
+
"closed": False,
|
|
48
|
+
"ts": time.time(),
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
# Evict: remove closed items first, then oldest
|
|
52
|
+
if len(self.items) > self.max_items:
|
|
53
|
+
closed = [i for i in self.items if i["closed"]]
|
|
54
|
+
for c in closed[:max(1, len(closed) // 2)]:
|
|
55
|
+
self.items.remove(c)
|
|
56
|
+
while len(self.items) > self.max_items:
|
|
57
|
+
self.items.sort(key=lambda x: x["ts"])
|
|
58
|
+
self.items.pop(0)
|
|
59
|
+
|
|
60
|
+
def retrieve(self, query: str, top_k: int = 5) -> list:
|
|
61
|
+
"""检索相关记忆. 排除已闭合的."""
|
|
62
|
+
active = [i for i in self.items if not i["closed"]]
|
|
63
|
+
# Simple keyword overlap scoring
|
|
64
|
+
query_words = set(query.lower().split())
|
|
65
|
+
scored = []
|
|
66
|
+
for item in active:
|
|
67
|
+
content_words = set(item["content"].lower().split())
|
|
68
|
+
overlap = len(query_words & content_words)
|
|
69
|
+
freshness = 1.0 - (time.time() - item["ts"]) / 86400 # 1 day decay
|
|
70
|
+
score = overlap * 0.7 + freshness * 0.3
|
|
71
|
+
if overlap > 0:
|
|
72
|
+
scored.append((score, item))
|
|
73
|
+
scored.sort(key=lambda x: x[0], reverse=True)
|
|
74
|
+
return [s[1] for s in scored[:top_k]]
|
|
75
|
+
|
|
76
|
+
def novelty_score(self, content: str) -> float:
|
|
77
|
+
"""计算当前内容的 newness. 0=完全重复, 1=全新."""
|
|
78
|
+
h = self._hash(content)
|
|
79
|
+
for item in self.items:
|
|
80
|
+
if item["hash"] == h:
|
|
81
|
+
return max(0.0, 1.0 - item["repeats"] * 0.25)
|
|
82
|
+
return 1.0
|
|
83
|
+
|
|
84
|
+
def diversity_score(self) -> float:
|
|
85
|
+
"""计算记忆的多样性. 0=单一模式, 1=高度多样."""
|
|
86
|
+
if len(self.items) < 3:
|
|
87
|
+
return 1.0
|
|
88
|
+
hashes = set(i["hash"] for i in self.items)
|
|
89
|
+
return min(len(hashes) / len(self.items), 1.0)
|
|
90
|
+
|
|
91
|
+
def stats(self) -> dict:
|
|
92
|
+
active = [i for i in self.items if not i["closed"]]
|
|
93
|
+
closed = [i for i in self.items if i["closed"]]
|
|
94
|
+
return {
|
|
95
|
+
"total": len(self.items),
|
|
96
|
+
"active": len(active),
|
|
97
|
+
"closed": len(closed),
|
|
98
|
+
"diversity": round(self.diversity_score(), 3),
|
|
99
|
+
"avg_repeats": round(sum(i["repeats"] for i in self.items) / max(len(self.items), 1), 1),
|
|
100
|
+
}
|
examples/__init__.py
ADDED
examples/writer.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example: Writer Agent — 内置热税预算的写作助手.
|
|
3
|
+
|
|
4
|
+
拒绝无意义写作任务 (empty prompt, pure paraphrasing, etc.)
|
|
5
|
+
"""
|
|
6
|
+
from mss_agent.core.agent import MSSAgent
|
|
7
|
+
from mss_agent.core.heat_tax import HeatTaxLevel, HeatTaxAbort
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class WriterAgent(MSSAgent):
|
|
11
|
+
"""
|
|
12
|
+
Writer Agent 示例.
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
def my_llm(prompt): return ollama.chat("qwen", prompt)
|
|
16
|
+
writer = WriterAgent(llm=my_llm)
|
|
17
|
+
result = writer.run("写一篇关于开源精神的文章")
|
|
18
|
+
if not result.aborted:
|
|
19
|
+
print(result.output)
|
|
20
|
+
writer.health_report()
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, llm=None):
|
|
24
|
+
super().__init__(name="Writer", llm=llm, heat_tax_threshold=1.5)
|
|
25
|
+
self.style_guide = "简洁、准确、有人味"
|
|
26
|
+
|
|
27
|
+
def _estimate_meaning_heat(self, prompt: str) -> tuple[float, str]:
|
|
28
|
+
"""Writer-specific meaning assessment."""
|
|
29
|
+
prompt_lower = prompt.lower().strip()
|
|
30
|
+
|
|
31
|
+
# Empty / trivial
|
|
32
|
+
if len(prompt) < 10:
|
|
33
|
+
return 0.09, "Prompt too short: likely a throwaway task"
|
|
34
|
+
|
|
35
|
+
# Pure paraphrasing
|
|
36
|
+
paraphrase_signals = ["改写", "重新说", "换个写法", "rewrite", "rephrase"]
|
|
37
|
+
if any(s in prompt_lower for s in paraphrase_signals):
|
|
38
|
+
if "因为" not in prompt_lower and "because" not in prompt_lower:
|
|
39
|
+
return 0.06, "Pure paraphrasing without stated reason"
|
|
40
|
+
|
|
41
|
+
# Meaningful writing
|
|
42
|
+
creation_signals = ["写", "创作", "生成", "write", "create", "draft", "compose"]
|
|
43
|
+
if any(s in prompt_lower for s in creation_signals) and len(prompt) > 30:
|
|
44
|
+
return 0.003, "Creative writing task"
|
|
45
|
+
|
|
46
|
+
return 0.01, "Neutral writing task"
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: mss-agent
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MSS-Agent: 世界上第一个内置'意义场自检'的开源 Agent 框架
|
|
5
|
+
Home-page: https://github.com/mysama1/MSS-AI-Project
|
|
6
|
+
Author: MSS-AI Project
|
|
7
|
+
License: MIT
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest; extra == "dev"
|
|
17
|
+
Requires-Dist: black; extra == "dev"
|
|
18
|
+
Provides-Extra: llm
|
|
19
|
+
Requires-Dist: openai>=1.0; extra == "llm"
|
|
20
|
+
|
|
21
|
+
# MSS-Agent
|
|
22
|
+
|
|
23
|
+
**世界上第一个内置"意义场自检"的开源 Agent 框架。**
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
pip install mss-agent # coming soon
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 为什么?
|
|
30
|
+
|
|
31
|
+
现有 Agent 框架 (LangChain, CrewAI, AutoGPT) 只有一个目标:**完成任务。**
|
|
32
|
+
|
|
33
|
+
MSS-Agent 有两个目标:
|
|
34
|
+
1. **完成任务**
|
|
35
|
+
2. **知道什么时候不该完成任务**
|
|
36
|
+
|
|
37
|
+
第二点,没有任何框架在做。
|
|
38
|
+
|
|
39
|
+
## 三层防御
|
|
40
|
+
|
|
41
|
+
| 层 | 名称 | 公理 | 功能 |
|
|
42
|
+
|----|------|------|------|
|
|
43
|
+
| L0 | 热税预算 | A3 | 拒绝无意义任务 (L2 意义热税 >1000x) |
|
|
44
|
+
| L1 | Δ检测协议 | A6 | 检测模式闭合, 触发蜕壳 |
|
|
45
|
+
| L2 | 升维协议 | A6 | 多Agent冲突→不是投票, 是升维 |
|
|
46
|
+
|
|
47
|
+
## 30秒入门
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from mss_agent import MSSAgent, HeatTaxLevel
|
|
51
|
+
|
|
52
|
+
# 配置任意 LLM
|
|
53
|
+
def my_llm(prompt: str) -> str:
|
|
54
|
+
import ollama
|
|
55
|
+
return ollama.chat("qwen3", prompt)["message"]["content"]
|
|
56
|
+
|
|
57
|
+
# 创建 Agent
|
|
58
|
+
agent = MSSAgent(name="Helper", llm=my_llm)
|
|
59
|
+
|
|
60
|
+
# 运行 — 内置热税预算自动拦截无意义任务
|
|
61
|
+
result = agent.run("帮我改写这句话:'你好'")
|
|
62
|
+
if result.aborted:
|
|
63
|
+
print(f"Agent 拒绝: {result.reason}")
|
|
64
|
+
# → Agent 拒绝: Task has LOW meaning: Pure paraphrasing...
|
|
65
|
+
|
|
66
|
+
result = agent.run("设计一个 REST API 的错误处理方案")
|
|
67
|
+
print(result.output)
|
|
68
|
+
|
|
69
|
+
# 健康报告
|
|
70
|
+
print(agent.health_report())
|
|
71
|
+
# → {'heat_tax': {...}, 'delta': {...}, 'memory': {'active': 5, 'closed': 2}}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 热税预算 (A3)
|
|
75
|
+
|
|
76
|
+
Agent 自动评估每个任务的三层热税:
|
|
77
|
+
- **L0 物理热税** (token/time) → 权重 0.001
|
|
78
|
+
- **L1 逻辑热税** (redundancy) → 权重 1.0
|
|
79
|
+
- **L2 意义热税** (虚假数据/无意义任务) → 权重 **1000.0**
|
|
80
|
+
|
|
81
|
+
L2 热税过高 → Agent 拒绝执行并输出 `HeatTaxAbort`。
|
|
82
|
+
|
|
83
|
+
## Δ检测协议 (A6)
|
|
84
|
+
|
|
85
|
+
Agent 不会重复相同失败模式:
|
|
86
|
+
- 每个任务周期的 Δ 值 (novelty + diversity)
|
|
87
|
+
- Δ 连续下降 2 周期 → 触发蜕壳 → 遗忘旧模式
|
|
88
|
+
- "蜕壳不是失败, 是生长"
|
|
89
|
+
|
|
90
|
+
## 不是替代品, 是良心
|
|
91
|
+
|
|
92
|
+
MSS-Agent **不替代** LangChain/LlamaIndex/AutoGPT.
|
|
93
|
+
|
|
94
|
+
它是一层**可以套在任何 Agent 外面的意义场检测器**。
|
|
95
|
+
|
|
96
|
+
把 MSS-Agent 的热税预算和 Δ 协议插入你现有的 pipeline——剩下的继续用你熟悉的一切。
|
|
97
|
+
|
|
98
|
+
## 商业模式
|
|
99
|
+
|
|
100
|
+
- ✅ MIT 开源 — 核心功能永远免费
|
|
101
|
+
- ✅ 社区驱动 — DAU 优先, 不设付费墙
|
|
102
|
+
- ✅ 可选企业服务 — 部署咨询/定制集成/培训
|
|
103
|
+
|
|
104
|
+
## 许可证
|
|
105
|
+
|
|
106
|
+
MIT License. 详见 [LICENSE](LICENSE).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
core/__init__.py,sha256=jJ7E2Xw96MQDMoPRnDI7Dr_WGTcPW5iAPTRqtf6Ishc,290
|
|
2
|
+
core/agent.py,sha256=YQOoMD0-PMmBRtSYjBNIck3mzL0xAcJauK24BWtRmVw,6874
|
|
3
|
+
core/delta.py,sha256=uOYOi8c9GhJ244ls_cXEhmY-6T7bgXvxFs9X72yjM88,2455
|
|
4
|
+
core/heat_tax.py,sha256=EWRyQaJeLQPl8VS7d_GvB_qwrQwi-BT1bERzQGJAg8Q,3056
|
|
5
|
+
core/memory.py,sha256=7JgAbNKDDwksxotr9lajw5tzlDuOYPgPpEKTxieZk0I,3636
|
|
6
|
+
examples/__init__.py,sha256=mIS25hEgT0AXLWJr0LdIEgxjTTothwQLFcMtK6-Jg7k,59
|
|
7
|
+
examples/writer.py,sha256=ov2d23u32YMEMwtl1R6gOCZrVKD89Vn6VUwVk32Ya4U,1688
|
|
8
|
+
protocols/__init__.py,sha256=8vhRoMlv6YDAHTbr5qqRgZXPCkBYfTfSiW-o_ow0Zbs,119
|
|
9
|
+
protocols/elevation.py,sha256=uCQGrcyjAXF6Yg-xZUn8IAfXwdOGvpwa2LKe0lDlfZc,3440
|
|
10
|
+
protocols/quorum.py,sha256=mLXnq1eYH1-yM7iziKiElpqXsXyu1yK6oVuGqRp6GDc,1841
|
|
11
|
+
mss_agent-0.1.0.dist-info/METADATA,sha256=XU2JJgCffn2Vrzajg1xuybsuYRcvkpYf4hlzdP27Dm0,3242
|
|
12
|
+
mss_agent-0.1.0.dist-info/WHEEL,sha256=51RkbunBAw4BWsgaQWTpPhg4Diwp3c9P5iaLk67Hdtg,92
|
|
13
|
+
mss_agent-0.1.0.dist-info/top_level.txt,sha256=_Z0V6p_MGUFyOcipsz_JsgCWehKekAvhrl1nbo3NF1k,24
|
|
14
|
+
mss_agent-0.1.0.dist-info/RECORD,,
|
protocols/__init__.py
ADDED
protocols/elevation.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A6 矛盾升维协议 (H525).
|
|
3
|
+
|
|
4
|
+
当多个 Agent 在低维层面冲突:
|
|
5
|
+
- 不是投票 (K3 降维)
|
|
6
|
+
- 是升维: 找到共同的高维命名空间
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
ep = ElevationProtocol()
|
|
10
|
+
result = ep.resolve(
|
|
11
|
+
agent_a="代码风格A",
|
|
12
|
+
agent_b="代码风格B",
|
|
13
|
+
conflict="用哪个风格?"
|
|
14
|
+
)
|
|
15
|
+
# → "升维: 用 lint tool 统一, 不纠结风格"
|
|
16
|
+
"""
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from typing import Optional, Callable
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class ElevationProtocol:
|
|
23
|
+
"""
|
|
24
|
+
A6 升维协议.
|
|
25
|
+
|
|
26
|
+
原理: 低维矛盾不能在同层解决. 电车难题→'谁造的刹车失灵的破车'.
|
|
27
|
+
给 LLM 的 prompt 不是 '选A还是B', 是 '这个问题写死在哪一维? 加一维怎么解?'
|
|
28
|
+
|
|
29
|
+
llm: 用于升维推理的 LLM 函数
|
|
30
|
+
"""
|
|
31
|
+
llm: Optional[Callable[[str], str]] = None
|
|
32
|
+
history: list = field(default_factory=list)
|
|
33
|
+
|
|
34
|
+
ELEVATION_PROMPT = """You are a dimension elevation engine (A6 protocol).
|
|
35
|
+
|
|
36
|
+
Two perspectives are in conflict:
|
|
37
|
+
Perspective A: {perspective_a}
|
|
38
|
+
Perspective B: {perspective_b}
|
|
39
|
+
Conflict: {conflict}
|
|
40
|
+
|
|
41
|
+
DO NOT pick A or B. DO NOT compromise.
|
|
42
|
+
|
|
43
|
+
Instead:
|
|
44
|
+
1. Identify which dimension this conflict is trapped in (e.g., "binary choice", "false tradeoff")
|
|
45
|
+
2. Add ONE new dimension that makes the conflict irrelevant
|
|
46
|
+
3. Name the new dimension and the resolution
|
|
47
|
+
|
|
48
|
+
Output format:
|
|
49
|
+
TRAPPED DIMENSION: <name of the dimension where the conflict exists>
|
|
50
|
+
ELEVATION: <the new dimension you're adding>
|
|
51
|
+
RESOLUTION: <how adding this dimension resolves the conflict>
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def resolve(self, perspective_a: str, perspective_b: str, conflict: str) -> dict:
|
|
55
|
+
"""执行升维. 返回 {trapped_dim, elevation, resolution}."""
|
|
56
|
+
prompt = self.ELEVATION_PROMPT.format(
|
|
57
|
+
perspective_a=perspective_a,
|
|
58
|
+
perspective_b=perspective_b,
|
|
59
|
+
conflict=conflict,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
if self.llm:
|
|
63
|
+
response = self.llm(prompt)
|
|
64
|
+
else:
|
|
65
|
+
# Default: fail to a heuristic
|
|
66
|
+
response = (
|
|
67
|
+
"TRAPPED DIMENSION: binary choice framing\n"
|
|
68
|
+
f"ELEVATION: meta-rule (who gets to decide)\n"
|
|
69
|
+
f"RESOLUTION: Instead of choosing between '{perspective_a[:40]}' and "
|
|
70
|
+
f"'{perspective_b[:40]}', define who owns the decision and under what constraints."
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
result = self._parse(response)
|
|
74
|
+
result["conflict"] = conflict
|
|
75
|
+
result["perspective_a"] = perspective_a
|
|
76
|
+
result["perspective_b"] = perspective_b
|
|
77
|
+
self.history.append(result)
|
|
78
|
+
return result
|
|
79
|
+
|
|
80
|
+
def _parse(self, text: str) -> dict:
|
|
81
|
+
"""Parse LLM output into structured result."""
|
|
82
|
+
out = {"trapped_dim": "", "elevation": "", "resolution": ""}
|
|
83
|
+
for line in text.strip().split("\n"):
|
|
84
|
+
line = line.strip()
|
|
85
|
+
if line.upper().startswith("TRAPPED DIMENSION:"):
|
|
86
|
+
out["trapped_dim"] = line.split(":", 1)[1].strip()
|
|
87
|
+
elif line.upper().startswith("ELEVATION:"):
|
|
88
|
+
out["elevation"] = line.split(":", 1)[1].strip()
|
|
89
|
+
elif line.upper().startswith("RESOLUTION:"):
|
|
90
|
+
out["resolution"] = line.split(":", 1)[1].strip()
|
|
91
|
+
return out
|
|
92
|
+
|
|
93
|
+
def stats(self) -> dict:
|
|
94
|
+
return {
|
|
95
|
+
"resolutions": len(self.history),
|
|
96
|
+
"trapped_dims": [h["trapped_dim"] for h in self.history],
|
|
97
|
+
"elevations": [h["elevation"] for h in self.history],
|
|
98
|
+
}
|
protocols/quorum.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""
|
|
2
|
+
L1 Quorum-Fast 汇聚 (H525). 多 Agent 视角收敛检测.
|
|
3
|
+
|
|
4
|
+
收敛 = 坏 (视角闭合). 发散 = 好 (多个独立视角).
|
|
5
|
+
"""
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from typing import List, Dict
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class QuorumFast:
|
|
12
|
+
"""
|
|
13
|
+
多 Agent quorum 检测.
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
qf = QuorumFast()
|
|
17
|
+
qf.report("agent_a", 0.72)
|
|
18
|
+
qf.report("agent_b", 0.35)
|
|
19
|
+
qf.report("agent_c", 0.68)
|
|
20
|
+
if qf.converged():
|
|
21
|
+
print("Warning: agents converging on same answer")
|
|
22
|
+
"""
|
|
23
|
+
threshold: float = 0.6 # quorum > this OR < (1-this) → converged
|
|
24
|
+
reports: List[Dict] = field(default_factory=list)
|
|
25
|
+
|
|
26
|
+
def report(self, agent_id: str, score: float, label: str = ""):
|
|
27
|
+
self.reports.append({
|
|
28
|
+
"agent": agent_id,
|
|
29
|
+
"score": round(score, 4),
|
|
30
|
+
"label": label,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
def quorum(self) -> float:
|
|
34
|
+
"""Quorum value: fraction of agents above threshold."""
|
|
35
|
+
if not self.reports:
|
|
36
|
+
return 0.0
|
|
37
|
+
above = sum(1 for r in self.reports if r["score"] > self.threshold)
|
|
38
|
+
return round(above / len(self.reports), 3)
|
|
39
|
+
|
|
40
|
+
def converged(self) -> bool:
|
|
41
|
+
"""True = BAD (all agents agree, no diversity)."""
|
|
42
|
+
q = self.quorum()
|
|
43
|
+
return q > 0.8 or q < 0.2
|
|
44
|
+
|
|
45
|
+
def status(self) -> str:
|
|
46
|
+
q = self.quorum()
|
|
47
|
+
if q > 0.9:
|
|
48
|
+
return "HYPER_CONVERGENT (warning: groupthink)"
|
|
49
|
+
if q < 0.1:
|
|
50
|
+
return "HYPER_CONVERGENT (warning: unanimous disagree)"
|
|
51
|
+
if self.converged():
|
|
52
|
+
return "CONVERGENT"
|
|
53
|
+
return "DIVERGENT (healthy)"
|
|
54
|
+
|
|
55
|
+
def snapshot(self) -> dict:
|
|
56
|
+
return {
|
|
57
|
+
"agents": len(self.reports),
|
|
58
|
+
"quorum": self.quorum(),
|
|
59
|
+
"status": self.status(),
|
|
60
|
+
"scores": {r["agent"]: r["score"] for r in self.reports},
|
|
61
|
+
}
|