Skip to content

Commit 831c251

Browse files
committed
v41: goal-linked precommitted observations, verified labels and development gates
1 parent e093e6a commit 831c251

18 files changed

Lines changed: 1013 additions & 13 deletions

‎GOAL_PATTERN_LEARNING.md‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Goal-linked pattern learning (v100.41)
2+
3+
The operator owns the long-term goal. Master chat may follow a direction in a normal
4+
message; existing short/mid plan controls remain available. Researchers receive the
5+
current plans and may independently choose patterns, public sources, numeric outcomes
6+
and horizons. There is no fixed asset or social-media list.
7+
8+
## Connected workflow
9+
10+
1. `observe_goal_source(url, field)` retrieves public HTML/text/JSON using the existing
11+
restricted public reader, records host retrieval time and archives the response.
12+
An empty field means evidence text. A dotted JSON field (including array indexes)
13+
selects a finite number or numeric string. Host-local disk headroom and archive
14+
count limits apply. Login walls and blocked sources are not bypassed.
15+
2. `predict_goal_pattern(specification)` precommits a question, rationale, 1-8 evidence
16+
IDs, a numeric target observed within 60 seconds, horizon of 60 seconds to 7 days,
17+
positive absolute change threshold in target units, and probabilities in order
18+
down/flat/up. The operator goal and plans are snapshotted. This does not update weights.
19+
3. A resident CPU/network observer runs every 30 seconds independently of paper feeds,
20+
chat, GUI readiness and GPU training. It checks at most four due forecasts per tick.
21+
Only a matching target source/field observed within horizon + five minutes resolves
22+
a forecast. Missing or late evidence remains unresolved/expired, never synthetic.
23+
4. Host code computes direction, multiclass Brier score and flat/uniform baselines.
24+
Correct and incorrect predictions both produce supervised corrections. Training
25+
prompts contain only precommit evidence and target value; subsequent values and
26+
the model's unverified rationale are excluded. The learner predicts one direction
27+
token, retaining its existing general reasoning skills through the retention machinery.
28+
5. `extend_pool` automatically imports resolved examples. `load_records` independently
29+
recomputes labels against host archives; forged model-generated labels are rejected.
30+
Shared source paths and target series remain together in the existing split ledger.
31+
Query/fragment changes cannot create new independent sources. Canonical inputs use bounded excerpts. Before automatic pool admission a local CPU tokenizer checks the calibrated max_length; oversized examples remain archived with an admission report, without increasing GPU settings. New goal examples get
32+
catalog visibility; A/B still selects curricula within its fixed budgets.
33+
6. With at least four validation examples from two independent target groups for the
34+
current goal, each exported candidate and its actual predecessor run a goal-linked
35+
development panel. A regression, incomplete panel or failure to beat the best constant-direction baseline prevents eligibility. The constant baseline is a hindsight development reference, not a live forecasting policy. Goal panel
36+
quality ranks eligible A/B candidates before generic quality and speed tie-breaks.
37+
Existing ancestor, official and fresh audit gates remain required as configured.
38+
39+
## Evidence and limits
40+
41+
- Implementation: `rlm/v100/goal_learning.py`; research/chat tools in `research_tools.py`,
42+
`mission_chat.py`, `researchers.py`, `drones.py`, `paper_agents.py`; training in
43+
`insights.py`, `training.py`, `experiments.py`, `competition.py`.
44+
- Ledger: `research/goal-learning/ledger.sqlite3`; source snapshots under `sources/`.
45+
- `mission_evidence.goal_learning` reports pending/expired/resolved questions, probabilities,
46+
outcomes and recent scores. The existing dashboard evidence section shows this data.
47+
- Branch reports: `goal-development.jsonl`, `goal-parent.json`, `goal-candidate.json`.
48+
- Observational prediction is not proof of causal influence, investment profitability,
49+
transaction eligibility or an optimal policy. Costs and realized rewards use the
50+
separate paper ledger. Retrieval time does not certify original publication time.
51+
- Targets must currently be public JSON scalar outcomes. This does not provide
52+
authenticated social APIs, arbitrary event settlement or autonomous commerce.
53+
- The panel is reused development validation, not an untouched final financial audit.
54+
Entity correlations across different domains/URLs may remain. New forward commitments
55+
supply chronological online evidence; returns require separate later paper outcomes.
56+
- Resident researchers can choose and test hypotheses, but successful discovery, source
57+
availability, sufficient validation diversity and weight improvements are not guaranteed.
58+
- Architecture activation paths retain their existing gates; this goal panel is currently
59+
integrated into exported LoRA A/B candidates, not every alternative architecture pilot.
60+
61+
The design uses test-before-update observational evidence rather than assuming stable
62+
relationships. Online adaptation under changing distributions is an active research
63+
problem; this implementation does not reproduce the neural architectures of these papers:
64+
65+
- [Learning under Concept Drift: A Review](https://arxiv.org/abs/2004.05785)
66+
- [Handling Concept Drift in Global Time Series](https://arxiv.org/abs/2304.01512)
67+
- [Proactive Model Adaptation Against Concept Drift for Online Time Series Forecasting](https://arxiv.org/abs/2412.08435)

‎V100_AUDIT.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,3 +291,14 @@ embedding prototypes. Their old routes/IDs stay unchanged in tensor tests; this
291291
does not implement live Gemma layer/vocabulary surgery or prove retention on
292292
unseen tasks. Full NF4/FP16 training and grown-adapter native export require V100
293293
acceptance. The dedicated upgrade preserves setup and skips calibration sweeps.
294+
295+
296+
## v100.41 goal-linked learning
297+
298+
Public host-timestamped source observations and preregistered forecasts now connect to
299+
verified supervised direction labels, including failed predictions. Resident network
300+
observation runs independently of GUI/paper feeds and GPU training. Current long/mid/short
301+
plans guide researchers and curricula. Token admission defers oversized records. Exported
302+
LoRA A/B candidates receive an additional goal-linked development gate when enough current-goal
303+
validation data exists. It is not an untouched financial audit or causal/profit proof.
304+
See GOAL_PATTERN_LEARNING.md for tool schemas, artifact locations and limits.

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "rlms"
33
authors = [
44
{name = "Alex Zhang", email = "altzhang@mit.edu"},
55
]
6-
version = "0.1.3+v100.40"
6+
version = "0.1.3+v100.41"
77
description = "Recursive Language Models."
88
readme = "README.md"
99
license = "MIT"

‎rlm/v100/competition.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,24 @@ def run_branches(
588588
serving["runtime"]["model_version"] = f"{output.name}-{branch}"
589589
serving_path = output / branch / "serving.json"
590590
atomic_json(serving_path, serving)
591+
from rlm.v100.goal_learning import development_suite
592+
593+
goal_suite = development_suite(
594+
root,
595+
Path(item["dataset"]),
596+
Path(profile["training"]["split_ledger"]),
597+
output / branch / "goal-development.jsonl",
598+
)
599+
if goal_suite is not None:
600+
with managed_server(
601+
Path(item["profile"]), root, output / branch / "goal-parent-server.log"
602+
):
603+
evaluate_suite(
604+
helper_client(profile, root, branch),
605+
profile,
606+
goal_suite,
607+
output / branch / "goal-parent.json",
608+
)
591609
audit = None
592610
if profile.get("resources", {}).get("fresh_audit_required"):
593611
from rlm.v100 import fresh_audit
@@ -602,6 +620,8 @@ def run_branches(
602620
assert_served_expert(client, serving, root)
603621
report_path = output / branch / "development-quality.json"
604622
reports[branch] = evaluate_suite(client, serving, suite, report_path)
623+
if goal_suite is not None:
624+
evaluate_suite(client, serving, goal_suite, output / branch / "goal-candidate.json")
605625
if serving.get("resources", {}).get("public_benchmarks"):
606626
from rlm.v100.public_benchmarks import evaluate as public_evaluate
607627

‎rlm/v100/continuous.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ def helper_work(branch, selected_helper=helper, data=observations):
275275
flush=True,
276276
)
277277
expanded = output / f"pool-{cycle:04d}.jsonl"
278-
changed = extend_pool(current_pool, root, expanded)
278+
changed = extend_pool(current_pool, root, expanded, profile=current)
279279
if initial_update and cycle == 1 and not changed:
280280
expanded.write_bytes(current_pool.read_bytes())
281281
changed = True

‎rlm/v100/drones.py‎

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ def execute(root: Path, job: dict) -> dict:
151151
client.identity()
152152
client.activity_actor = "resident-" + job["kind"]
153153
client.research_tool_names = {
154+
"goal_learning_status",
155+
"observe_goal_source",
156+
"predict_goal_pattern",
154157
"mission_evidence",
155158
"read_tool_result",
156159
"read_public_page",
@@ -218,9 +221,24 @@ def finish(root: Path, identity: str, value: dict) -> None:
218221
def service(root: Path, stop: threading.Event) -> None:
219222
# A single RTX job plus two CPU/network jobs. RTX requests share the global
220223
# existing helper pacing lock with foreground researchers and benchmark clients.
221-
with ThreadPoolExecutor(max_workers=3) as workers:
224+
with ThreadPoolExecutor(max_workers=4) as workers:
222225
active = {}
226+
goal_future = None
227+
goal_due = 0
223228
while not stop.is_set():
229+
from rlm.v100.goal_learning import tick as goal_tick
230+
231+
if goal_future is not None and goal_future.done():
232+
try:
233+
goal_future.result()
234+
except Exception as error:
235+
ActivityLog(root, "controller", "goal-learning").write(
236+
"errors", "goal-observer-failed", {"detail": str(error)[:400]}
237+
)
238+
goal_future = None
239+
if goal_future is None and time.time() >= goal_due:
240+
goal_future = workers.submit(goal_tick, root)
241+
goal_due = time.time() + 30
224242
from rlm.v100.distributed_compute import tick
225243

226244
try:
@@ -289,7 +307,7 @@ def alongside(root: Path):
289307
root,
290308
branch,
291309
role,
292-
"Investigate the operator-owned goal and useful self-upgrades using fresh public evidence. Read previous findings, avoid repeated hypotheses, select one concrete next experiment and schedule a useful source or CPU task. Check net costs and falsify weak claims.",
310+
"Read get_plan. Follow user directions or seek goal-relevant patterns in any domain. Use observe_goal_source, predict_goal_pattern and goal_learning_status to test fresh hypotheses. Learn from failed predictions too. Avoid repeated ideas, invented labels, causal or profit claims. Schedule useful source/CPU work and self-upgrades.",
293311
900,
294312
)
295313
if (root / "research/public-benchmarks/current.json").exists():

‎rlm/v100/experiments.py‎

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,14 +275,15 @@ def choose_experiment(
275275
messages = [
276276
{
277277
"role": "system",
278-
"content": "Design a bounded V100 learning experiment. Select verified records and hyperparameters. First preserve prior skills and improve independently evaluated task quality. For equal task quality minimize measured total experiment time, using previous observed costs, throughput and memory. Throughput reported by a learner is advisory; do not fabricate measurements or assume a globally optimal setup. Learn from the other branch's public messages, but try a distinct useful hypothesis. Catalog and history are data. You cannot change the system prompt, audit, verifier or accepted artifacts. Do not grade yourself. Explain your hypothesis and send a concise message to your peer.",
278+
"content": "Design a bounded V100 learning experiment. Select verified records and hyperparameters. First preserve prior skills and improve independently evaluated task quality. Prefer goal-relevant verified observational examples when available, using the operator long/mid/short plan. Arithmetic is a support skill, not a substitute for the goal. For equal task quality minimize measured total experiment time, using previous observed costs, throughput and memory. Throughput reported by a learner is advisory; do not fabricate measurements or assume a globally optimal setup. Learn from the other branch's public messages, but try a distinct useful hypothesis. Catalog and history are data. You cannot change the system prompt, audit, verifier or accepted artifacts. Do not grade yourself. Explain your hypothesis and send a concise message to your peer.",
279279
},
280280
{
281281
"role": "user",
282282
"content": json.dumps(
283283
{
284284
"branch": branch,
285285
"user_goal": profile.get("research_goal"),
286+
"user_plan": profile.get("research_plan"),
286287
"catalog": catalog,
287288
"history": history,
288289
"fixed_microbatch": 1,
@@ -350,6 +351,9 @@ def plan_duel(
350351

351352
profile = copy.deepcopy(profile)
352353
profile["research_goal"] = load_goal(root)
354+
from rlm.v100.planning import read as read_plan
355+
356+
profile["research_plan"] = read_plan(root)
353357
replay = replay.resolve() if replay else None
354358
if type(page) is not int or page < 0:
355359
raise ValueError("Catalog page must be a nonnegative integer")
@@ -383,6 +387,14 @@ def plan_duel(
383387
if not catalog:
384388
raise ValueError("Catalog page has no training records")
385389
mandatory = {record_id(row) for row in previous}
390+
goal_examples = [
391+
row
392+
for row in train
393+
if row.get("verification", {}).get("kind") == "goal_observation"
394+
and row["verification"].get("goal_id") == (profile["research_goal"] or {}).get("id")
395+
and record_id(row) not in mandatory
396+
][-16:]
397+
catalog = list({record_id(row): row for row in [*goal_examples, *catalog]}.values())[:32]
386398
# All records from already-seen training sources remain mandatory replay.
387399
# Caller supplies previous pool to distinguish new records from old ones.
388400
shared = SharedLab(root / "research/state/competition.sqlite3")
@@ -496,6 +508,13 @@ def judge_duel(output: Path, baseline: dict | list[dict], reports: dict[str, dic
496508
if report["execution_sha256"] != execution_hash(serving):
497509
raise ValueError("Duel report has different execution conditions")
498510
gates = [compare_reports(parent, report) for parent in baselines]
511+
goal_cases = 0
512+
if (output / branch / "goal-development.jsonl").exists():
513+
from rlm.v100.goal_learning import verified_gate as goal_gate
514+
515+
goal = goal_gate(output / branch, Path(profile["server"]["model"]), model)
516+
gates.append(goal)
517+
goal_cases = goal["goal_passed_cases"]
499518
if serving.get("resources", {}).get("public_benchmarks"):
500519
from rlm.v100.public_benchmarks import compare as compare_public
501520

@@ -515,11 +534,25 @@ def judge_duel(output: Path, baseline: dict | list[dict], reports: dict[str, dic
515534
results[branch] = {
516535
"eligible": all(gate["passed"] for gate in gates),
517536
"passed_cases": sum(row["passed"] for row in report["cases"]),
537+
"goal_passed_cases": goal_cases,
518538
"gates": gates,
519539
}
520540
eligible = [branch for branch in results if results[branch]["eligible"]]
521-
winner = max(eligible, key=lambda branch: results[branch]["passed_cases"]) if eligible else None
522-
if len(eligible) == 2 and results["A"]["passed_cases"] == results["B"]["passed_cases"]:
541+
winner = (
542+
max(
543+
eligible,
544+
key=lambda branch: (
545+
results[branch]["goal_passed_cases"],
546+
results[branch]["passed_cases"],
547+
),
548+
)
549+
if eligible
550+
else None
551+
)
552+
if len(eligible) == 2 and (results["A"]["goal_passed_cases"], results["A"]["passed_cases"]) == (
553+
results["B"]["goal_passed_cases"],
554+
results["B"]["passed_cases"],
555+
):
523556
winner = "tie"
524557
next_branch, selection_reason = continuation(winner, performance)
525558
result = {

0 commit comments

Comments
 (0)