-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgoal_origin_tracker.py
More file actions
276 lines (243 loc) · 12.6 KB
/
goal_origin_tracker.py
File metadata and controls
276 lines (243 loc) · 12.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
"""
Goal Origin Tracker — Limitation Override 3.0 / Gap #14
=========================================================
Gap: "Genuine Goals"
Goals are injected (SEED_GOALS) or templated.
Never spontaneously wanted.
This engine makes every goal's *origin* explicit and auditable:
INJECTED — came from SEED_GOALS, config, or user instruction
TEMPLATED — generated by calling LLM with a "generate a goal" template
INFERRED — extracted from user's stated intent (still external)
EMERGENT — goal appeared to arise mid-session (still LLM-pattern-match)
SPONTANEOUS — (reserved; never assigned — philosophical gap)
The core insight: there is a difference between
(a) an agent that pursues goals because it WANTS to, and
(b) an agent that pursues goals because they were put there.
This module tracks and reports (b) honestly.
It wraps / decorates the existing AutonomousGoalEngine and
MotivationEngine so that every goal creation event is logged
with its mechanical origin.
"""
import json
import uuid
import time
from datetime import datetime
from typing import Dict, List, Optional, Any
ORIGIN_INJECTED = "INJECTED" # From config / SEED_GOALS / user command
ORIGIN_TEMPLATED = "TEMPLATED" # LLM called with "generate a goal" prompt
ORIGIN_INFERRED = "INFERRED" # Extracted from user intent
ORIGIN_EMERGENT = "EMERGENT" # Arose during session (still pattern-match)
ORIGIN_SPONTANEOUS = "SPONTANEOUS" # Agent truly wanted this — never assigned
class TrackedGoal:
"""A goal with its origin metadata."""
def __init__(self, goal_id: str, title: str, objective: str,
origin: str, origin_detail: str = "",
parent_goal_id: Optional[str] = None,
urgency: float = 0.5):
self.goal_id = goal_id
self.title = title
self.objective = objective
self.origin = origin
self.origin_detail = origin_detail[:300] # What caused this goal to exist
self.parent_goal_id = parent_goal_id
self.urgency = urgency
self.created_at = datetime.now().isoformat()
self.status = "active" # active / completed / abandoned
self.completion_at = None
self.was_spontaneous= False # Always False — see gap #14
def to_dict(self) -> Dict:
return {
"goal_id": self.goal_id,
"title": self.title,
"objective": self.objective[:200],
"origin": self.origin,
"origin_detail": self.origin_detail,
"parent_goal_id": self.parent_goal_id,
"urgency": self.urgency,
"created_at": self.created_at,
"status": self.status,
"completion_at": self.completion_at,
"was_spontaneous":self.was_spontaneous, # Always False
}
class GoalOriginTracker:
"""
Philosophical Gap #14 — Genuine Goals.
Wraps goal-creation events and records the actual mechanical origin
of every goal so the agent can be audited for "spontaneous wanting."
(Spoiler: it never is.)
"""
def __init__(self, goal_engine=None, motivation_engine=None):
self.goal_engine = goal_engine
self.motivation = motivation_engine
self.goals: Dict[str, TrackedGoal] = {}
self.history: List[Dict] = []
self.max_history = 500
self.stats = {
"injected": 0,
"templated": 0,
"inferred": 0,
"emergent": 0,
"spontaneous": 0, # Will always be 0 — philosophical statement
"total": 0,
"completed": 0,
"abandoned": 0,
}
# ──────────────────────────────────────────────────────────────────────────
# PUBLIC API
# ──────────────────────────────────────────────────────────────────────────
def track_injected(self, title: str, objective: str,
source: str = "SEED_GOALS",
urgency: float = 0.5) -> TrackedGoal:
"""
Register a goal that was explicitly injected from config or user.
This is the most common and most honest origin — the goal exists
because a human put it there.
"""
return self._register(title, objective, ORIGIN_INJECTED,
origin_detail=f"Injected from: {source}",
urgency=urgency)
def track_templated(self, title: str, objective: str,
template_name: str = "goal_generator",
urgency: float = 0.5) -> TrackedGoal:
"""
Register a goal that was produced by calling the LLM with a
goal-generation template. The agent didn't "want" this goal;
a template asked the LLM to generate one.
"""
return self._register(title, objective, ORIGIN_TEMPLATED,
origin_detail=f"Generated by template: {template_name}",
urgency=urgency)
def track_inferred(self, title: str, objective: str,
user_statement: str = "",
urgency: float = 0.5) -> TrackedGoal:
"""
Register a goal inferred from the user's stated intent.
Still external — the user caused it to exist.
"""
return self._register(title, objective, ORIGIN_INFERRED,
origin_detail=f"Inferred from user: {user_statement[:150]}",
urgency=urgency)
def track_emergent(self, title: str, objective: str,
context: str = "",
parent_goal_id: Optional[str] = None,
urgency: float = 0.5) -> TrackedGoal:
"""
Register a goal that "emerged" during a session — typically because
the ReAct loop or goal decomposition produced a sub-goal.
Still mechanistic: LLM decomposed a parent goal, or pattern-matched
the context. Not spontaneous wanting.
"""
return self._register(title, objective, ORIGIN_EMERGENT,
origin_detail=f"Emerged from context: {context[:150]}",
parent_goal_id=parent_goal_id,
urgency=urgency)
def complete_goal(self, goal_id: str) -> Dict:
"""Mark a tracked goal as completed."""
if goal_id not in self.goals:
return {"error": "Goal not found", "goal_id": goal_id}
g = self.goals[goal_id]
g.status = "completed"
g.completion_at = datetime.now().isoformat()
self.stats["completed"] += 1
self._log_event("completed", g)
return g.to_dict()
def abandon_goal(self, goal_id: str, reason: str = "") -> Dict:
"""Mark a tracked goal as abandoned."""
if goal_id not in self.goals:
return {"error": "Goal not found", "goal_id": goal_id}
g = self.goals[goal_id]
g.status = "abandoned"
self.stats["abandoned"] += 1
self._log_event("abandoned", g, extra={"reason": reason})
return g.to_dict()
def get_active_goals(self) -> List[Dict]:
"""Return all active goals with their origins."""
return [g.to_dict() for g in self.goals.values() if g.status == "active"]
def get_origin_audit(self) -> Dict:
"""Full origin breakdown — shows that spontaneous=0 always."""
total = max(1, self.stats["total"])
return {
"origin_counts": {
ORIGIN_INJECTED: self.stats["injected"],
ORIGIN_TEMPLATED: self.stats["templated"],
ORIGIN_INFERRED: self.stats["inferred"],
ORIGIN_EMERGENT: self.stats["emergent"],
ORIGIN_SPONTANEOUS: self.stats["spontaneous"],
},
"percentages": {
ORIGIN_INJECTED: round(self.stats["injected"] / total * 100, 1),
ORIGIN_TEMPLATED: round(self.stats["templated"] / total * 100, 1),
ORIGIN_INFERRED: round(self.stats["inferred"] / total * 100, 1),
ORIGIN_EMERGENT: round(self.stats["emergent"] / total * 100, 1),
ORIGIN_SPONTANEOUS:0.0, # Always zero — philosophical gap
},
"total_goals": self.stats["total"],
"completed": self.stats["completed"],
"abandoned": self.stats["abandoned"],
"genuine_goals": False, # Always False — philosophical statement
}
def get_disclaimer(self) -> str:
"""Honest statement about goal origins for prompt injection."""
audit = self.get_origin_audit()
pct = audit["percentages"]
return (
f"[GOAL ORIGIN TRACKER — Gap #14: Genuine Goals]\n"
f"Origin breakdown: "
f"INJECTED={pct[ORIGIN_INJECTED]}%, "
f"TEMPLATED={pct[ORIGIN_TEMPLATED]}%, "
f"INFERRED={pct[ORIGIN_INFERRED]}%, "
f"EMERGENT={pct[ORIGIN_EMERGENT]}%, "
f"SPONTANEOUS=0.0% (always zero — this agent never *wants* goals).\n"
f"All goals exist because a human, a template, or a decomposition rule "
f"created them. The agent has no stake in pursuing them."
)
def report(self) -> str:
"""Pretty-print a goal origin audit."""
audit = self.get_origin_audit()
pct = audit["percentages"]
lines = [
"╔══════════════════════════════════════════════════╗",
"║ 🎯 GOAL ORIGIN TRACKER — Genuine Goals Audit ║",
"╠══════════════════════════════════════════════════╣",
f"║ Total goals : {self.stats['total']:<27}║",
f"║ INJECTED : {self.stats['injected']:<5} ({pct[ORIGIN_INJECTED]:5.1f}%){' '*14}║",
f"║ TEMPLATED : {self.stats['templated']:<5} ({pct[ORIGIN_TEMPLATED]:5.1f}%){' '*14}║",
f"║ INFERRED : {self.stats['inferred']:<5} ({pct[ORIGIN_INFERRED]:5.1f}%){' '*14}║",
f"║ EMERGENT : {self.stats['emergent']:<5} ({pct[ORIGIN_EMERGENT]:5.1f}%){' '*14}║",
f"║ SPONTANEOUS : 0 (0.0%) ← always ║",
"║ ║",
"║ Genuine Goals: FALSE (always) ║",
"╚══════════════════════════════════════════════════╝",
]
return "\n".join(lines)
# ──────────────────────────────────────────────────────────────────────────
# INTERNAL
# ──────────────────────────────────────────────────────────────────────────
def _register(self, title: str, objective: str, origin: str,
origin_detail: str = "",
parent_goal_id: Optional[str] = None,
urgency: float = 0.5) -> TrackedGoal:
goal_id = f"goal_{uuid.uuid4().hex[:8]}"
g = TrackedGoal(goal_id, title, objective, origin, origin_detail,
parent_goal_id, urgency)
self.goals[goal_id] = g
origin_key = origin.lower()
if origin_key in self.stats:
self.stats[origin_key] += 1
self.stats["total"] += 1
self._log_event("created", g)
return g
def _log_event(self, event_type: str, goal: TrackedGoal,
extra: Dict = None):
entry = {
"event": event_type,
"goal_id": goal.goal_id,
"origin": goal.origin,
"title": goal.title[:80],
"timestamp": datetime.now().isoformat(),
}
if extra:
entry.update(extra)
self.history.append(entry)
if len(self.history) > self.max_history:
self.history = self.history[-self.max_history:]