docs(plan): MAF-utnyttelsesrevisjon U1-U15 + Magentic-utforskningssloeyfe (ORDRE 20260823T125528Z) [skip-docs]
Planarbeid, ingen kode. Maalt: 7/9 av .claudes null-paastander holder (U5/U11 presisert), Magentic er ikke kode-markert eksperimentell og Learn-siden baerer ingen Python-advarsel, men "untested outside Magentic-One" staar. Installert 1.0.0 maalt: single-use workflow, builder/manager-instans bloer (4/4), plan review virker uten revise-cap, max_round gir kanonisk streng, ResetSignal nullstiller ikke deltakere, opentelemetry-sdk mangler. Design: Magentic OVER sloeyfa som mandat-former -> run_project(mandate=) uendret. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013waQJ7HtoNDWnjKeBqw4Vk
This commit is contained in:
parent
ed696d2650
commit
2e33905ffe
1 changed files with 581 additions and 0 deletions
581
docs/plan/2026-08-23-magentic-utforskningssloeyfe.md
Normal file
581
docs/plan/2026-08-23-magentic-utforskningssloeyfe.md
Normal file
|
|
@ -0,0 +1,581 @@
|
|||
# MAF-utnyttelsesrevisjon U1–U15 + plan for Magentic-basert utforskningssløyfe
|
||||
|
||||
> **Ordre** `20260823T125528Z-1876024635-from-.claude` · **Økt 53**, 2026-08-23 · Fable 5/xhigh uten
|
||||
> advisor (operatør-overstyring). Planarbeid: ingen kode i `src/`, ingen tester. **Hvert tall i dette
|
||||
> dokumentet kommer fra en kommando som ble kjørt denne økten** (vedlegg V1–V3); alt som ikke ble
|
||||
> målt er merket «ikke målt» eller «spike».
|
||||
|
||||
## 0. Sammendrag — de seks setningene som bærer resten
|
||||
|
||||
1. **Målingen:** av `.claude`s ni «null i src/»-påstander holder 7 ordrett; U5 og U11 trenger
|
||||
presisering (artefaktet/sitatbæringen finnes, MAF-konstruktet gjør det ikke). Nevner: 32 filer.
|
||||
2. **Magentic-status:** hele Learn-siden hentet — **ingen experimental-advarsel for Python**
|
||||
(eneste «experimental» er et C#-pragma), og installert kode har ingen `@experimental`-markør
|
||||
på Magentic. F14-sitatet «untested … outside of the original Magentic-One design» **står
|
||||
fortsatt**. PyPI latest er 1.1.1 (Production/Stable), men bare 1.0.0/1.0.1 kjører på core 1.9.0.
|
||||
3. **Designet:** Magentic legges **OVER** den normative 8-stegs-sløyfa, ikke inni Steg 3. Utforskningen
|
||||
er en **mandat-former**: prompt + bundles (+ fagpersonens frø-hypoteser) → `Mandate` →
|
||||
`run_project(mandate=…)` **uendret**. Validatoren gater hvert tall i nøyaktig samme gate som i dag.
|
||||
4. **Fire ting ble MÅLT på installert 1.0.0, ikke antatt:** en Magentic-workflow er single-use
|
||||
(høylytt `RuntimeError`), men **builder og manager-instans blør** (4/4 kontaminering, worker
|
||||
hoppet over, fabrikkert sluttsvar); plan-review-rundturen **virker**, men har **ingen
|
||||
revise-cap**; `max_round_count` gir en **kanonisk streng, aldri et typet signal**;
|
||||
`MagenticResetSignal` **nullstiller ikke** deltakernes sesjon (orphan-attributt i 1.0.0).
|
||||
5. **U14 er ikke «én funksjon»:** `setup_observability` finnes ikke; inngangen er
|
||||
`agent_framework.observability.configure_otel_providers`, og **`opentelemetry-sdk` er ikke
|
||||
installert** — kallet krasjer med `ModuleNotFoundError`. U14 er en avhengighetsbeslutning.
|
||||
6. **Rekkefølgen:** S0–S6-spikes (1 økt) → U14 (1) → U4+U13 synkron (2) → U12 + asynkron U13 (1)
|
||||
→ U15 som egen sammenligning (1). U1/U2/U5/U11 avvises med begrunnelse. Seks økter.
|
||||
|
||||
---
|
||||
|
||||
## A. U1–U15-måling (nevner: 32 `.py`-filer i `src/`; kontroll: 117 i `tests/`, 6 i `spikes/`)
|
||||
|
||||
Metode: `grep -rEn --include='*.py' "<regex>" src` per kapabilitet, samme regex mot `tests/` og
|
||||
`spikes/` som kjent-positiv-kontroll, og — for hver regex som ga 0 i alle tre — en tredje kjøring
|
||||
mot installert MAF (`.venv/lib/python3.12/site-packages/agent_framework{,_orchestrations}`) som
|
||||
beviser at spørringen KAN finne (Verifiseringsloven ansikt 4). Skript og rå output: vedlegg V1.
|
||||
En uavhengig andre sveip (Opus-agent, 35 tokens, samme nevner) ga identiske nuller (V3).
|
||||
|
||||
| ID | Kapabilitet (§15.1) | src | tests | spikes | venv-kontroll | Status | Belegg / spørring |
|
||||
|----|----|---:|---:|---:|---:|----|----|
|
||||
| U1 | `ConcurrentBuilder` fan-out | 0 | 4 | 4 | — (tests/spikes finner) | **IKKE** — erstattet av håndrullet bølge | `run.py:1262` `await asyncio.gather(` (D-D). Regex `ConcurrentBuilder\|BuildConcurrent\|FanIn` |
|
||||
| U2 | `WorkflowBuilder` graf | 0 | 0 | 0 | 21 filer | **IKKE** | Regex `WorkflowBuilder\|@executor\|class .*\(Executor\)\|WorkflowContext\|SwitchCase\|add_edge\|add_fan_out` |
|
||||
| U3 | `GroupChatBuilder` debatt | 10 | 7 | 7 | — | **LANDET** | `workflow.py:24` import · `:104` `GroupChatBuilder(` · `:108` `termination_condition=` · `:113` `.with_max_rounds(max_rounds)` |
|
||||
| U4 | Magentic | 0 | 4 | 9 | 4 filer | **IKKE** | tests-treff: `tests/spikes/test_b_footguns.py`, `tests/spikes/test_imports.py`, `tests/test_method_spec_loadbearing.py:40` (framework-navn-svarteliste for spec-prosa — ikke bruk). `.claude`s «kun spikes/b_footguns.py» er riktig for BRUK |
|
||||
| U5 | Agent Skills (`SkillsProvider`) | 0 | 0 | 0 | 3 filer | **IKKE** som MAF-konstrukt; **DELVIS** som artefakt | `persona.py:3`/`:47` leser `shared/skills/expert-reviewer/SKILL.md` via EGEN `load_persona_example`, aldri `SkillsProvider`. Installert core: `@experimental(feature_id=ExperimentalFeature.SKILLS)` på `SkillsProvider` (`_skills.py:1718`) |
|
||||
| U6 | MCP-tools | 6 | 6 | 0 | — | **LANDET** (opt-in) | `mcp_tools.py:42-43` import · `:154` `MCPStdioTool(` · `:170` `MCPStreamableHTTPTool(`. Ingest-sømmen bruker rå `mcp.ClientSession` (`ingest_mcp.py:144-179`) |
|
||||
| U7 | Function tools | 4 | 10 | 0 | — | **LANDET** | `datasource.py:18` `from agent_framework import FunctionTool, tool` · `:65` · `:69` `@tool(` |
|
||||
| U8 | Middleware | 16 | 20 | 0 | — | **LANDET** | `budget.py:27` · `:228` `class BudgetMiddleware(ChatMiddleware)` · `run.py:635/:645` |
|
||||
| U9 | `ContextProvider` | 8 | 11 | 4 | — | **LANDET** | `verdicts.py:29` · `:319` `class ExpeLContextProvider(ContextProvider)` · `run.py:724/:843` |
|
||||
| U10 | Vektorlager | 2 | 19 | 0 | — | **DELVIS** (egen, ikke MAF) | `semretrieval.py:355` `save_vector_store` · `:393` `load_vector_store` — numpy brute-force (D-C). 0 på `AzureAISearch\|cosmos\|qdrant\|redis\|VectorStore` |
|
||||
| U11 | Citation-aware RAG | 26 | 35 | 0 | — | **DELVIS** (egen, med MAF-typer) | `provenance.py:18` `from agent_framework import Annotation, TextSpanRegion` · `:78` `to_annotations()` («display only, NOT the source of truth»); `datasource.py:1` «citation-bearing data source». 0 på `AIContextProvider\|TextSearchProvider\|agentic_retrieval\|KnowledgeAgent` |
|
||||
| U12 | Checkpointing | 3 (kun prosa) | 0 | 0 | 14 filer | **FRAVALGT** — §A.1 | `run.py:22`, `workflow.py:14`, `:115`: «no checkpoint — research 01: durable resume is fragile». 0 kode-treff på `checkpoint_storage\|CheckpointStorage` |
|
||||
| U13 | HITL-gates | 3 | 0 | 0 | — | **DELVIS, og UØVD** | `workflow.py:116` `builder.with_request_info(agents=[agents[-1]])` bak `enable_layer1_hitl=False` (`:78`, `run.py:524`; ingen CLI-flagg). ENESTE test: `tests/test_workflow.py:54` `test_layer1_hitl_option_builds` — bygger, kjører aldri gaten, sender aldri et svar. `run.py` awaiter `debate.run` til ferdig (`:684-686`) og leser aldri strømmen. 0 på `RequestInfoEvent\|approval_mode\|MagenticPlanReview` i src OG tests |
|
||||
| U14 | OpenTelemetry | 0 | 0 | 1 | 4 filer | **IKKE** | Regex `opentelemetry\|setup_observability\|OBSERVABILITY\|get_tracer\|start_as_current_span`. `uv pip list`: KUN `opentelemetry-api 1.42.1` (transitiv) — ingen sdk, ingen exporter |
|
||||
| U15 | Evaluering | 0 | 0 | 0 | 2 filer | **IKKE** | Regex `evaluate_workflow\|LocalEvaluator\|Evaluator\|_evaluation`. `evaluate_workflow` finnes (`_evaluation.py:1833`) under `@experimental(feature_id=ExperimentalFeature.EVALS)` (`:1832`); 20 `@experimental` i modulen |
|
||||
|
||||
Utenfor de 15 (samme kjøring): U16 `CompactionProvider` 0/0/0 (venv 4) · U17 `as_tool(` 0/0/0
|
||||
(venv 1) · U18 `FoundryChatClient` **LANDET** `backends.py:150` · U19 Harness 0/0/0 (venv 1).
|
||||
|
||||
**Mot `.claude`s ni null-påstander:** 7 av 9 holder ordrett (U1, U2, U4, U12, U13, U14, U15). 2 av 9
|
||||
trenger presisering: **U5** og **U11**. De 6 landet-påstandene holder (U3, U6, U7, U8, U9,
|
||||
U10-delvis). Aliasing/re-eksport er utelukket: alle 14 `agent_framework`-importlinjer i `src/` er
|
||||
listet i V3 og hver er et direkte navn; `from agent_framework_orchestrations import` → 0 treff
|
||||
(importstien i bruk er `agent_framework.orchestrations`, `workflow.py:24`).
|
||||
|
||||
### A.1 U12 — fravalgt på et premiss som har råtnet, eller som holder?
|
||||
|
||||
Premisset i koden: «research 01: durable checkpoint resume is fragile» (`workflow.py:14`).
|
||||
**«research 01» finnes ikke som dokument i repoet** (`grep -rn -i "research.01" docs/ STATE*.md`
|
||||
→ 0). Nærmeste dokumenterte grunnlag: kapabilitetskartet
|
||||
(`docs/research/2026-06-24-maf-capability-map.md:54`): «superstep-granular (not mid-LLM-call);
|
||||
resume needs identical graph. Defer past MVP unless needed»; egnethetsplanen 08-09 P9/P10
|
||||
(«pass-nivå-gjenopptakelse er levert via spend-fila; ingen checkpointing»); `docs/extending.md:315`
|
||||
(«A run either completes or is re-run; the async verdict inbox is the resumable boundary»).
|
||||
|
||||
**Vurdering:** begrunnelsen holder **for dagens debatt** (sekunder–minutter; re-kjøring er billigere
|
||||
enn gjenopptakelse). Den holder **ikke** for målbildet: en utforskning som stopper i
|
||||
`MagenticPlanReviewRequest` og venter på en fagperson i dager kan ikke holde en prosess i live, og
|
||||
«re-kjør» kaster planen fagpersonen skulle revidere. **Premisset er ikke råttent — det er utenfor
|
||||
sitt gyldighetsområde.** Det som ble MÅLT i installert core 1.9.0 denne økten (V2 §7): mekanismen
|
||||
FINNES og dekker Magentic-tilstand — `FileCheckpointStorage` (`_checkpoint.py:239`), «restore then
|
||||
send» (`run(responses=…, checkpoint_id=…)` eksplisitt tillatt, `_workflow.py:877`), pending
|
||||
`request_info` re-emitteres ved resume (`_runner_context.py:414-426`), og `MagenticOrchestrator.
|
||||
on_checkpoint_save` skriver kontekst (runde/stall/reset-tellere), ledgere og managerens hele sesjon
|
||||
(`_magentic.py:1266-1284`, `:746`). «Skjør» er i dag konkret: base64-pickle av tilstand med
|
||||
allow-list (`_checkpoint.py:242-250`), graf-signatur må matche (`_runner.py:275-279`), og
|
||||
manager-state-feil ved lagring/gjenoppretting **svelges med warning** (`_magentic.py:1281-1282`,
|
||||
`:1320-1321`). Ingen ende-til-ende-resume med pending plan review er kjørt → **spike S4** (§D).
|
||||
|
||||
---
|
||||
|
||||
## B. Magentic-status mot primærkilden (hele siden hentet 2026-08-23)
|
||||
|
||||
Kilde: `microsoft_docs_fetch` mot
|
||||
`learn.microsoft.com/en-us/agent-framework/workflows/orchestrations/magentic` — hele siden.
|
||||
|
||||
1. **Ingen experimental-advarsel for Python.** Eneste «experimental» på siden: C#-blokken
|
||||
`#pragma warning disable MAAIW001 // Magentic types are experimental` (.NET-diagnostikk).
|
||||
`.claude`s fravær er **bekreftet**, ikke bare ikke-motbevist.
|
||||
2. **F14-sitatet står ordrett:** «it is untested how well the Magentic orchestration will perform
|
||||
outside of the original Magentic-One design», og tipset «consider using the Group Chat pattern
|
||||
instead» for enklere koordinering. F14s VURDERING var riktig lest; operatøren har flyttet
|
||||
PREMISSET (målbildet krever planlegging), ikke sitatet. G8 i §15.3 er halvt riktig: funksjonelle
|
||||
workflows er fortsatt `ExperimentalFeature.FUNCTIONAL_WORKFLOWS`; Magentic er ikke markert.
|
||||
3. **Installert kode:** `agent_framework/_feature_stage.py:43-65` — `ExperimentalFeature` har 13
|
||||
medlemmer (DECLARATIVE_AGENTS, EVALS, FILE_HISTORY, FIDES, FOUNDRY_TOOLS, FOUNDRY_PREVIEW_TOOLS,
|
||||
FUNCTIONAL_WORKFLOWS, HARNESS, MCP_LONG_RUNNING_TASKS, MCP_SKILLS, PROGRESSIVE_TOOLS, SKILLS,
|
||||
TO_PROMPT_AGENT). **Magentic er ikke der**; `grep -n -i "experimental\|feature_stage"
|
||||
agent_framework_orchestrations/*.py` → 0.
|
||||
4. **PyPI** (`curl https://pypi.org/pypi/agent-framework-orchestrations/json`): latest **1.1.1**
|
||||
(2026-08-21), `Development Status :: 5 - Production/Stable`, 26 utgivelser. Krav per versjon:
|
||||
1.0.0/1.0.1 → core ≥1.9.0; 1.0.2/1.1.0 → ≥1.13.0; 1.1.1 → ≥1.15.0. Core latest 1.15.0 (21.08).
|
||||
5. **Installert = pinnet:** `uv pip list` → orchestrations 1.0.0, core 1.9.0; `uv.lock:61-63` låser
|
||||
1.0.0; `pyproject.toml:17` `>=1.0.0`, `:12` core `>=1.9.0,<2` (`tests/test_maf_version_guard.py`).
|
||||
6. **Learn-siden beskriver LATEST API** (`manager_agent=`, `enable_plan_review=`,
|
||||
`intermediate_output_from=`). Installert 1.0.0 har de samme konstruktør-navnene (V2 §2) — men
|
||||
builder-docstringen i 1.0.0 beskriver et HITL-API (`MagenticHumanInterventionRequest`,
|
||||
`with_human_input_on_stall`) som **ikke finnes i koden** (kun i docstrings, `_magentic.py:1388-1559`).
|
||||
Designet i §C er skrevet mot det som er målt installert, aldri mot docstring eller Learn.
|
||||
7. **Core 1.12.0-notatet (21.07, samme minutt som orchestrations 1.0.1):** «Prevent the Magentic
|
||||
manager from duplicating conversation history (#6297)». Om 1.0.1 bærer fiksen er **ikke
|
||||
verifisert** — det er spike S0.
|
||||
|
||||
---
|
||||
|
||||
## C. Design: den utforskende sløyfa
|
||||
|
||||
### C.0 Beslutningen — Magentic OVER sløyfa, ikke INNI Steg 3
|
||||
|
||||
Kjernespørsmålet: Magentic gir manageren frihet til å velge vei; validatoren gater utfallet. Er de
|
||||
forenlige? **Ja — på én betingelse: utforskningen produserer KANDIDATER, aldri FORSLAG.**
|
||||
|
||||
Tre fakta avgjør formen:
|
||||
|
||||
1. **Metodespesifikasjonen er normativ og commons-eid** (`shared/method-spec.md`, pull-only). §3
|
||||
Steg 3 KREVER en to-rolle maker-checker-debatt, rundetak og fersk tilstand per kjøring; Steg 2
|
||||
krever «exactly one candidate measure» per proposer-kall; §8 «Never an unbounded loop, anywhere».
|
||||
En Magentic-sløyfe som ERSTATTER Steg 3 bryter spec-en og krever commons-amendment FØR bygging —
|
||||
og den ville flyttet validatoren INN i grafen. Sømkartet (V3) er entydig: alt som gjør et svar
|
||||
etterprøvbart ligger UTENFOR workflow-grafen og ETTER `debate.run` — generering `run.py:748-759`,
|
||||
validator `generate.py:491`, provenance `run.py:793`, checker-override `:816`, dimensjonsgate
|
||||
`:826`, domfangst `:849`, outbox `:857`.
|
||||
2. **`Mandate` finnes og ER «en prompt om hva som skal gjøres»** (`mandate.py:55-88`): `objective` +
|
||||
`approaches[{id, label, description}]` + `allow_own_proposals` + `success_criteria`. En
|
||||
`Approach.description` er «the expert's own prose reason … fed to the proposer VERBATIM»
|
||||
(`mandate.py:44-49`, `generate.py:308-317`) — altså «hypoteser fagpersoner kommer med».
|
||||
`_evaluate_mandate` (`run.py:292-344`) kjører hver approach gjennom `generate_via_llm` →
|
||||
`validate_proposal` under SAMME meter, og rapporterer `not_evaluated` for det budsjettet ikke
|
||||
rakk (aldri utelatt). `announce` (`mandate.py:130-183`) sier høyt hva kjøringen skal gjøre FØR
|
||||
første betalte kall — repoets eksisterende før-kjøring-menneskesjekk.
|
||||
3. **Validatoren er ren og offline** (`validate_proposal`, `validator.py:194-260`; CBC via PuLP,
|
||||
`_MC_SAMPLES = 512`, ingen `agent_framework`-import) og kan eksponeres som `FunctionTool` (U7)
|
||||
uten å endre hva den er.
|
||||
|
||||
Derfor: **utforskningen er en MANDAT-FORMER.** Inn: operatørens prompt + tilgjengelige bundles
|
||||
(+ valgfrie frø-approaches fra fagpersoner). Ut: en `Mandate` hvis `approaches` er hypotesene
|
||||
utforskningen fant verdt å teste, med managerens begrunnelse som `description`, og `bundle_id` per
|
||||
approach. Deretter `run_project(bundle_dir=…, mandate=…)` **uendret**: debatt (Steg 3) → generering
|
||||
(Steg 2) → deterministisk validator (Steg 4, blokkerende, stempler `validator_decision`) →
|
||||
checker-gate → outbox/HITL (Steg 6–8). Ingen linje i den kjeden endres. Manageren velger VEI i et
|
||||
rom som ikke kan skrive til outbox eller wiki; validatoren gater hvert TALL som forlater rommet, i
|
||||
samme gate som i dag.
|
||||
|
||||
**Tre nivåer av garanti (uttalt, så ingen leser mer inn i det enn det er):**
|
||||
|
||||
| Nivå | Hva | Hvor | Stempel? |
|
||||
|---|---|---|---|
|
||||
| 1 | `quick_validate`-verktøyet i utforskningen | samme `validate_proposal`, samme baseline, kalt av hypothesiser-agenten | **NEI** — rådgivende tall i en utforskningslogg, aldri provenance |
|
||||
| 2 | Pipelinen per approach | `run.py:748` `_evaluate` → `validate_proposal` | **JA** — `ProvenanceStamp.validator_decision` |
|
||||
| 3 | Skriverettigheter | kun pipelinen når outbox (`outbox.write_outbox`) og wiki (`promote_verdict`, gated) | utforskningens sluttsvar er RÅ (`{run_id}-exploration.json`), aldri et forslag |
|
||||
|
||||
Ærlighetsregelen (§1) følger direkte: managerens «final answer» presenteres ALDRI som et resultat.
|
||||
Resultatet er det `_evaluate_mandate` returnerer.
|
||||
|
||||
### C.1 Manager + deltakersett (mot installert 1.0.0-API, V2 §2–3)
|
||||
|
||||
| Rolle | Type | Gjør | Verktøy / kanal |
|
||||
|---|---|---|---|
|
||||
| **manager** | `MagenticBuilder(manager_agent_factory=…)` → `StandardMagenticManager(agent=…)` med EGNE ledger-prompter (konstruktør-parametre `task_ledger_*_prompt`, `progress_ledger_prompt`, `final_answer_prompt`) | planlegger, velger neste taler, fører progress ledger, replanlegger | ingen — men **`BudgetMiddleware` PÅ manager-agenten** (C.2) |
|
||||
| **navigator** | `Agent` | leser bundles PROGRESSIVT (§3 Steg 1) og svarer med sitater | `list_bundles()` · `read_bundle(id)` → `okf.navigate_bundle` + `Bundle.index_summary()`/`bundle_context` · `read_file(id, path)` innenfor `retrieval.safe_resolve` |
|
||||
| **hypothesiser** | `Agent` | former én kandidat om gangen som IR-JSON (gjenbruker `generate._build_messages` + `proposal_response_format()`) | `quick_validate(ir_json)` → `{decision, reason, p10, p50, p90}` (nivå 1) |
|
||||
| **expert_liaison** | menneske/persona | svarer på spørsmål, reviderer retning | plan review (`MagenticPlanReviewRequest.approve()/.revise(feedback)`) — og, etter spike S3b, `AgentApprovalExecutor` som deltaker for svar MIDT i kjøringen |
|
||||
|
||||
**Hvorfor `StandardMagenticManager`, ikke egen subklasse:** egen manager = egen ledger-parsing = egen
|
||||
kopi av Magentic-One-logikken (kø-(p): den andre kopien drifter). Prompt-overstyring dekker behovet
|
||||
(norsk domene, IR-disiplin, «aldri oppgi et tall uten `quick_validate`»). **Målt grunn til å
|
||||
overvåke:** manageren parser ledgeren med en håndrullet JSON-skraper (`_extract_json`,
|
||||
`_magentic.py:406-449`, `ast.literal_eval`-fallback), 3 forsøk (`:588-590`, `:710-723`) → `RuntimeError`;
|
||||
`MagenticProgressLedgerItem.answer` er ikke typesjekket per felt (`:300-302`). Egen manager FØRST når en
|
||||
målt mangel krever det.
|
||||
|
||||
**Hvorfor `manager_agent_factory=` og ikke `manager_agent=`:** målt E4 (V2 §6) — `manager_agent=`
|
||||
konstruerer manageren eagerly (`:1683`) og gir SAMME instans til hvert `build()` (`:1729-1730`);
|
||||
factory-varianten kalles per build (`:1732-1738`).
|
||||
|
||||
### C.2 Progress ledger som styringssignal, koblet mot `budget.py`
|
||||
|
||||
Ledgeren (`_magentic.py:310-314`, navn verifisert): `is_request_satisfied` / `is_in_loop` /
|
||||
`is_progress_being_made` / `next_speaker` / `instruction_or_question`. Stall-regelen (`:1109-1118`):
|
||||
`not is_progress_being_made or is_in_loop` → `stall_count += 1`, ellers **dekrementer** (ikke
|
||||
nullstill); `stall_count > max_stall_count` (strengt `>`) → `_reset_and_replan` (`:1149`): kontekst
|
||||
nullstilles, `reset_count += 1`, replan, plan review på nytt med `is_stalled=True` hvis aktivert.
|
||||
|
||||
To signaler, to vakter, aldri slått sammen (S3.4-invarianten):
|
||||
|
||||
| Signal | Kilde | Vakt | Utfall |
|
||||
|---|---|---|---|
|
||||
| **Semantisk** — stall | ledger | `max_stall_count` → reset+replan → `max_reset_count` | `exploration_stop="stalled"` — strukturert; IKKE krasj, IKKE suksess |
|
||||
| **Ressurs** — tokens | `BudgetMiddleware` (`budget.py:228`) på HVER agent **inkl. manager** | pre-call-guard `:242-244` | `BudgetExceeded(kind="tokens")` → 429-kanalen |
|
||||
| **Ressurs** — runder | Magentic `max_round_count` (= `Budget.max_rounds`) | intern (`:1238`) | **oversettes** av vårt lag til `BudgetExceeded(kind="exploration_rounds", limit, observed)` |
|
||||
|
||||
**Målt grunn til oversettelsen (E5, V2 §4):** `max_round_count`/`max_reset_count` gir **ingen
|
||||
exception** — en kanonisk assistent-melding «Workflow terminated due to reaching maximum round
|
||||
count.» + `_terminated=True` (`:1222-1256`), uten `prepare_final_answer`. Ved transporten er det
|
||||
uskillbart fra suksess. Vårt lag leser derfor `round_count` fra `MagenticContext` (eksponert via
|
||||
`PROGRESS_LEDGER_UPDATED`-eventene, ett per runde) og `orchestrator._terminated`, og produserer det
|
||||
typede stoppet SELV — slik `BudgetExceeded`/429 alt gjør for debatten. Strengen pinnes mot den
|
||||
installerte konstanten (`ROUND_CAP_LOGGER`-presedensen), aldri fri-tekst-matchet.
|
||||
|
||||
**`tick_round` er IKKE koblet til orkestreringsrunder i dag** (sømkart: to kallere, begge i
|
||||
`generate.py:390/:455`; `run.py:632` gir meteret `max(max_rounds*4, 4)` for GENERERINGS-forsøk).
|
||||
Utforskningen får derfor sin egen `Budget` (tokens + runder) i en egen `TokenMeter`, og `run_project`
|
||||
sitt meter er urørt. Stall-telleren er IKKE en budsjettvakt og kobles ikke inn i `TokenMeter`.
|
||||
|
||||
Hver runde logges (ledger + `next_speaker` + `instruction_or_question`) til
|
||||
`{run_id}-exploration.json`, så en stoppet utforskning er lesbar uansett hvilken vakt som fyrte.
|
||||
|
||||
**⚠ Spike S2:** manageren er et `Agent` (`StandardMagenticManager(agent=…)`, `:528`; ett
|
||||
`create_session()` i `__init__`, `:572`; alle kall via `agent.run(…, session=…)`, `:601`), så
|
||||
agent-nivå `ChatMiddleware` SKAL fyre — men det er **ikke målt** for manager-stien, og en feil her
|
||||
gjør «harde token-tak» til en løgn for den mest pratsomme deltakeren. Kontroll: bar
|
||||
`BaseChatClient` no-op-er middleware (`simulation.py:373-375`) — spiken bruker `ScriptedChatClient`.
|
||||
|
||||
### C.3 Grensene — B4 bygges ikke bort
|
||||
|
||||
| Dagens | Magentic (installert) | Regel |
|
||||
|---|---|---|
|
||||
| `with_max_rounds(max_rounds)` (`workflow.py:113`) | `max_round_count` (ctor-kwarg; default `None` = ubegrenset, `:542`) | PÅKREVD, positiv, fail-fast (G1/B4/A4) |
|
||||
| — | `max_stall_count` (default 3, `:540`) | PÅKREVD, positiv — ingen arv av defaulten |
|
||||
| — | `max_reset_count` (default `None`, `:541`) | PÅKREVD, ≥0 |
|
||||
| termination-safety-net (`:108`) | manageren avslutter ved `is_request_satisfied` (`:1104-1107`) **eller** ved ukjent `next_speaker` (`:1128-1131` — stille sluttsvar!) | rundetaket ER nettet; ukjent taler logges som avvik |
|
||||
| — | plan review: **ingen revise-cap, revises telles ikke som runder** (målt E6: 2 manager-kall per revise, 0 ledger-kall) | NY `max_plan_revisions` i kontrakten; hver revise `tick_round()`-es |
|
||||
|
||||
`ExplorationContract` (pydantic, fail-fast, speiler `contracts.TerminationContract`): `max_rounds`,
|
||||
`max_tokens`, `max_stall_count`, `max_reset_count`, `max_plan_revisions`, `enable_plan_review`.
|
||||
Ingen defaults: en utforskning uten uttalte grenser nekter å starte.
|
||||
|
||||
### C.4 B7 — tilstandsisolasjon. MÅLT (V2 §6; skript i V1)
|
||||
|
||||
| Eksperiment | Resultat | Konsekvens |
|
||||
|---|---|---|
|
||||
| **E1** én bygget workflow, to `.run()` | run 2 → `RuntimeError: This Magentic workflow has already been completed…` (`_magentic.py:914-918`), **0 modellkall** | single-use — STERKERE enn GroupChat 1.9.0s stille tomme kjøringer [2,0,0]; kan ikke korrumpere, for den kan ikke kjøre |
|
||||
| **E2** to workflows, ÉN delt `StandardMagenticManager` | run 2s manager-kall ser run 1s oppgave **4/4**; meldingstall vokser 2→…→23→**26**→39 over kjøringsgrensen; run 2s første ledger svarte `satisfied=True` og **workeren ble aldri kalt (0)** — et fabrikkert sluttsvar | ikke bare kost: **en plausibel fasit produsert av null arbeid** |
|
||||
| **E3** kontroll, fersk manager per build | **0/5** kontaminering, 5 kall inkl. `ledger_UNSAT` (workeren KALT) | 5-vs-4-kallformen er diskriminatoren |
|
||||
| **E4** én `MagenticBuilder`, to `.build()` | samme 4/4-blødning som E2 (`:1683`, `:1729-1730`) | «ny workflow» er ikke nok — **ny BUILDER per oppgave**, eller `manager_agent_factory=` |
|
||||
| **E7** `MagenticResetSignal` | `_magentic.py:1366` skriver `self._agent_thread` — lest av INGENTING (1 treff repo-vidt; den levende er `AgentExecutor._session`, 7 treff). Målt: cache/full_conversation tømt, **sesjonsidentitet uendret** | stall-replan gir fersk manager-ledger men **gamle deltakere**; docstringens løfte (`:1341-1344`) holder ikke i 1.0.0 |
|
||||
|
||||
**Regel:** per utforskning **ny builder → ny build → én kjøring**, fersk klient per agent (som
|
||||
`fresh_workflow`/`_default_factory` alt gjør, `run.py:436-440`). Stol aldri på stall-reset for å
|
||||
tømme deltakerminne. `fresh_exploration_workflow()` speiler `fresh_workflow()` (`workflow.py:74`).
|
||||
|
||||
### C.5 HITL-spranget: fra JA/NEI til «be om svar, bruke svarene»
|
||||
|
||||
**Installert flyt (målt E6, V2 §5):** `with_plan_review()`/`enable_plan_review=True` →
|
||||
`ctx.request_info(MagenticPlanReviewRequest{plan, current_progress, is_stalled},
|
||||
MagenticPlanReviewResponse)` (`:1041-1048`) → event `type="request_info"` med `request_id` →
|
||||
kjøringen STOPPER uten output → `workflow.run(responses={request_id: req.approve()})` eller
|
||||
`req.revise("…")` (`_workflow.py:899-908`) → ved revise: feedback inn i historikken, `manager.replan`,
|
||||
`REPLANNED`-event, **ny review-forespørsel** (`:1014-1031`) → ved approve: løkka kjører.
|
||||
Dette er målbildets «be om svar, bruke svarene og jobbe videre» ordrett — og det virker på 1.0.0.
|
||||
|
||||
**Ingen landingsplass i dag** (sømkart §4): `with_request_info` bygges men ingenting konsumerer
|
||||
strømmen; `hitl.py` er et offline inspeksjonsverktøy (id-join outbox − inbox, `hitl.py:159`), ikke
|
||||
en forespørsel/svar-mekanisme. Koblingen mot den asynkrone køa **gjenbruker formen, ikke filene:**
|
||||
|
||||
| Eksisterende | Utforskningen |
|
||||
|---|---|
|
||||
| outbox `{run_id}-outcome.json` med `verdict_id` | **NY artefaktklasse** `{run_id}-plan-review.json` = `{request_id, plan, current_progress, is_stalled, checkpoint_id}` — samme RAW-lag, samme mappe, byte-deterministisk via `outbox._dump` |
|
||||
| inbox `{id}.json` (`verdicts.write_verdict`) | `{request_id}.json` = `{decision: approve\|revise, feedback}` — eksperten SKRIVER, systemet LESER (§3 Steg 7-rolledelingen) |
|
||||
| `hitl.py pending` (id-join) | samme join over den nye klassen; `python -m portfolio_optimiser.hitl pending` viser begge |
|
||||
| `FeedbackContract.decision ∈ {approved, rejected}` | **IKKE gjenbrukt** — en plan-revisjon er ikke en dom; eget lite pydantic-skjema |
|
||||
| `simulation`-personaen (`load_persona_example`) | spiller eksperten SYNKRONT i demoen: `revise("Test også …")` én gang, så `approve()` |
|
||||
|
||||
To tidsskalaer, som Steg 7: **synkron** (persona/operatør i samme prosess — demoen og første
|
||||
leveranse) og **asynkron** (fil-innboks over dager → krever U12: `checkpoint_id` i artefaktet,
|
||||
`run(responses=…, checkpoint_id=…, checkpoint_storage=…)`). Det er DÉT som flytter U12 fra «NEI» til
|
||||
«nå» — og spike S4 avgjør om 1.9.0 bærer det.
|
||||
|
||||
**Svar MIDT i kjøringen (ikke bare ved plan):** plan review er det ENESTE `request_info`
|
||||
orkestratoren selv reiser (4 treff i `_magentic.py`, 0 i basen). Men `AgentApprovalExecutor`
|
||||
(`_orchestration_request_info.py:168`, `propagate_request=True` `:201`) kan stå som DELTAKER:
|
||||
når manageren velger `expert_liaison`, blir dens output en `request_info`, og mennesket svarer med
|
||||
`AgentRequestInfoResponse.approve()`/`.from_strings(…)` (`:55-79`) — svaret blir deltakerens
|
||||
output i historikken. **Ikke målt** → spike S3b. Lander den, er «stille spørsmål … be om svar»
|
||||
dekket både ved plan og underveis.
|
||||
|
||||
### C.6 «Hypoteser fagpersoner kommer med» — tre inngangsdører, i kostnadsrekkefølge
|
||||
|
||||
1. **I mandatet (finnes):** `Approach{label, description}` → proposeren verbatim. Utforskningen tar
|
||||
frø-approaches som INPUT og **MÅ bevare dem i output-mandatet** (en hypotese fagpersonen ba om
|
||||
forsvinner aldri stille — `not_evaluated`-regelen anvendt på utforskning).
|
||||
2. **I plan review (U13):** `revise("Test hypotesen at …")` → replan med teksten i task-ledgeren.
|
||||
3. **Underveis (S3b):** `expert_liaison`-deltakeren — manageren SPØR, fagpersonen svarer, svaret
|
||||
blir kontekst for neste runde.
|
||||
4. **I kunnskapsbasen (senere, commons-sak):** `type: hypothesis`-konseptfil navigatoren finner.
|
||||
Krever OKF-vokabular-beslutning hos commons — **ikke i denne planen**.
|
||||
|
||||
### C.7 OKF-bundles som inngang — hva manageren må vite
|
||||
|
||||
Manageren planlegger mot en **katalog**, aldri innholdet (§3 Steg 1). Per bundle: `id` ·
|
||||
`Bundle.index_summary()` (`okf.py:154`) · kandidat-features (`verdicts.bundle_candidate_features`,
|
||||
`:482`) · **om `cost-baseline.json` finnes** (`okf.load_optional_cost_baseline`, `:411`) — avgjørende:
|
||||
en uforankret bundle kan ikke quick-valideres mot stage 0, og manageren skal vite hvilke hypoteser
|
||||
som KAN testes deterministisk · antall `type: verdict`-filer (`Bundle.verdicts`). Navigatoren åpner
|
||||
én bundle om gangen på managerens instruks. **Multi-bundle er NYTT:** `run_project` tar ÉN
|
||||
`bundle_dir` (`run.py:514`; `bundle_dirs` → 0 treff); utforskningen tar `bundle_dirs`, hver
|
||||
approach bærer `bundle_id`, og pipelinen kjøres per bundle som i dag (`run_portfolio`-formen).
|
||||
|
||||
### C.8 Modulplassering (for byggeøktene, ikke for denne)
|
||||
|
||||
`src/portfolio_optimiser/explore.py` (NY): `ExplorationContract`, `ExplorationResult{mandate,
|
||||
ledger_log, stop, plan_reviews}`, `fresh_exploration_workflow(...)`, `explore(...)`. Importerer
|
||||
`agent_framework.orchestrations` — **aldri** fra `okf.py`/`mandate.py`/`hitl.py` (MAF-fri-guarden
|
||||
`test_okf_is_maf_free`). Verktøyene (`quick_validate`, navigator-tools) bor i `explore.py` og
|
||||
wrapper de rene modulene. `run.py` får ett nytt kallsted (`--explore "<prompt>"` → `explore()` →
|
||||
`run_project(mandate=…)`), `hosting.py` ett nytt whitelistet felt (`explore_prompt`), og
|
||||
`simulation.py` ett nytt scenario. Demo-transkriptet (`tests/golden/demo-transcript.stdout`) er
|
||||
**byte-uendret** fordi utforskningen er opt-in.
|
||||
|
||||
---
|
||||
|
||||
## D. Prioritert vei gjennom de ni gapene
|
||||
|
||||
### D.0 Versjonsbeslutningen (FØR noe bygges — én avgjørelse, ikke en glidning)
|
||||
|
||||
**Anbefaling: bygg mot 1.0.x (installert) i denne planen.** (1) core-bumpen 1.9→1.15 er seks
|
||||
minor-versjoner over privat-API-premisser `test_maf_version_guard` vokter, og tre målte
|
||||
1.9-egenskaper (`ExperimentalWarning`-paret, single-use GroupChat [2,0,0], middleware-no-op på bar
|
||||
klient) må re-måles; (2) en bump er en egen beslutning, ikke et biprodukt av sløyfa; (3) **1.0.1 er et
|
||||
patch-hopp på samme core** som kan tas gratis
|
||||
hvis `uv lock --upgrade-package agent-framework-orchestrations` gir 1.0.1 og suiten er grønn — og
|
||||
som KAN bære #6297 (§B pkt. 7) og/eller E7-fiksen. Det er spike S0. Bumpen til 1.1.x/core 1.15 er
|
||||
en EGEN økt etter denne planen (bonus: `InvocationsHostServer` krever core ≥1.13) — ikke en forutsetning.
|
||||
|
||||
### D.1 Spikes (økt 54 — alle i `spikes/e_magentic.py` + `tests/spikes/`, aldri `src/`)
|
||||
|
||||
| # | Spørsmål | Måling | Felles hvis |
|
||||
|---|---|---|---|
|
||||
| S0 | Gir 1.0.1 samme API og fjerner den E2/E4/E7? | `uv lock --upgrade-package agent-framework-orchestrations` → `uv pip list`; re-kjør V1-skriptene `magentic_b7*.py` + `magentic_reset.py`; `uv run pytest -q` | E7 står → bli på 1.0.0, noter |
|
||||
| S1 | B7 i repoets egen form | E1–E4 + E7 portert til `spikes/` med `ScriptedChatClient` (ikke agentens `FakeClient`) | 4/4 på delt builder, 0/5 på fersk |
|
||||
| S2 | Fyrer `BudgetMiddleware` på MANAGERENS kall? | manager-`Agent(middleware=[BudgetMiddleware(meter)])`, `Budget(max_tokens=1, …)`; assert `BudgetExceeded` propagerer ut av `workflow.run` OG at `meter.tokens > 0` etter ett kall | middleware fyrer ikke → egen klient-wrapper; plan-endring |
|
||||
| S3 | Plan-review-rundtur i repoets form | E6 portert: request_info-event → `run(responses=…)` med `revise` så `approve`; tell manager-kall per revise (målt 2) | rundturen feiler på 1.0.0 |
|
||||
| S3b | `AgentApprovalExecutor` som deltaker | svar injiseres via `from_strings("svar")`, assert svaret står i neste manager-prompt | ikke nåbar → kun plan review (dør 1+2) |
|
||||
| S4 | Resume med pending plan review | `with_checkpointing(FileCheckpointStorage(tmp))`, kjør til review, **ny prosess** (subprosess), `run(responses=…, checkpoint_id=…, checkpoint_storage=…)`; assert `request_info` re-emittert + sluttsvar | feiler → U12 synkron-først, asynkron etter planen |
|
||||
| S5 | `quick_validate`-latens | `validate_proposal` ×20 på `bygg-energi-baseline-mikro` med `time.perf_counter` | > 2 s/kall → budsjettér kall i kontrakten |
|
||||
| S6 | OTEL gratis? | `uv add opentelemetry-sdk` i scratch-venv; `configure_otel_providers(enable_console_exporters=True)`; kjør demoen; tell spans | ingen `workflow.run`-span → U14 er større enn én økt |
|
||||
|
||||
### D.2 Rekkefølgen
|
||||
|
||||
| # | Hva | Gap | Load-bearing for målbildet? | Økt | Modell |
|
||||
|---|---|---|---|---|---|
|
||||
| 0 | **Spikes S0–S6** | U4/U13/U12/U8/U14 | JA — hver er en antakelse planen ellers bygger blindt på | 54 | Opus 5/xhigh |
|
||||
| 1 | **U14 OpenTelemetry** — `opentelemetry-sdk` som deklarert dep, `configure_otel_providers` bak `PORTFOLIO_OTEL=…` (console default; OTLP KUN med eksplisitt `OTEL_EXPORTER_OTLP_ENDPOINT` — «ingen stille egress»), spans på DAGENS pipeline først; egne events for `PLAN_CREATED`/`REPLANNED`/`PROGRESS_LEDGER_UPDATED` (målt: `_magentic.py` emitterer null spans selv) | U14 | JA for «stole på» — og baselinen U15 måler mot | 55 | Opus 5/high |
|
||||
| 2 | **U4 + U13 synkron** — `explore.py` (C.8), `ExplorationContract`, mandat-former, `quick_validate`, navigator-tools, plan review med persona; `simulation`-scenario; `--explore` i CLI | U4, U13 | JA — dette ER målbildet | 56–57 | Opus 5/xhigh |
|
||||
| 3 | **U12 + asynkron U13** — `FileCheckpointStorage`, `{run_id}-plan-review.json` + innboks-svar, `hitl.py pending` utvidet, `--resume <run_id>` | U12, U13 | JA for svar over dager — **betinget av S4** | 58 | Opus 5/xhigh |
|
||||
| 4 | **U15** — EGEN sammenligning på golden-bundelen: utforskning vs. debatt på tre tall (validerte kandidater, tokens, runder), skrevet til `docs/` som måling | U15 | JA for «finner den bedre tiltak» — men MAF-evals AVVISES (eksperimentell i kode, «wrong shape», kapabilitetskart :58) | 59 | Opus 5/high |
|
||||
| — | U1 `ConcurrentBuilder` | U1 | NEI — bølgemodellen er målt, bærer budsjett-reservasjon (S3.4); Concurrent har ingen runder å reservere mot | etter planen, om noensinne | — |
|
||||
| — | U2 `WorkflowBuilder` | U2 | NEI — Magentic ER graf-laget; direkte adopsjon gir null ny kapabilitet | nei | — |
|
||||
| — | U5 `SkillsProvider` | U5 | NEI — `ExperimentalFeature.SKILLS`; egen loader virker; commons eier innholdet | etter planen | — |
|
||||
| — | U11 MAF RAG-providere | U11 | NEI — «navigate, never stuff» forbyr query-time retrieval; sitatbæring finnes | avvist med begrunnelse | — |
|
||||
|
||||
Seks økter (54–59); faller S4, sparer pkt. 3 én økt.
|
||||
|
||||
### D.3 For og mot de fire kandidatene ordren navngir
|
||||
|
||||
**U4 + U13 tidlig — FOR:** selve målbildet; alt annet er instrumentering av status quo. **MOT:** hviler
|
||||
på umålte antakelser (S2, S3b, S4) og på en `request_info`-sti repoet ALDRI har kjørt. Derfor spikes
|
||||
først — ikke omvendt. **Plass #2 og #3.**
|
||||
|
||||
**U14 tidlig — FOR:** (1) repoets deploy-gate; (2) uten sporing er en utforskning en svart boks — og
|
||||
Magentic-koden gir selv null spans, så ledger-eventene må vi logge; (3) LITE i kode: én funksjon +
|
||||
env; (4) instrumentert FØR utforskningen gir U15 sin baseline. **MOT (målt):** det er **ikke gratis i
|
||||
avhengigheter** — `opentelemetry-sdk` + exporter må deklareres (wheel-vekt, `uv sync`, handover-
|
||||
pakka), og `ENABLE_INSTRUMENTATION` er default `True` (`observability.py:697`) så spans LAGES alt i
|
||||
dag og kastes. OTLP-exporter er egress og må være opt-in bak eksplisitt endepunkt. **Plass #1,
|
||||
betinget av S6.**
|
||||
|
||||
**U15 — FOR:** uten måling kan vi ikke påstå at sløyfa er bedre; ærlighetsregelen forbyr påstanden.
|
||||
**MOT `evaluate_workflow`:** eksperimentell i kode (20 dekoratorer) og feil form (kvalitetsscore
|
||||
offline, ikke domeneobjekt). **Konklusjon:** evaluering JA, som egen sammenligning; MAF-flaten
|
||||
NEI inntil `EVALS` forlater `ExperimentalFeature`. **Plass #4** — den trenger noe å måle.
|
||||
|
||||
**U12 — FOR:** asynkron plan review over dager er umulig uten; mekanismen dekker Magentic-tilstand
|
||||
(målt i kode, §A.1). **MOT:** ende-til-ende-resume på 1.9.0 er umålt, restore-feil svelges, og en
|
||||
synkron persona-variant leverer målbildet i demoen uten den. **Konklusjon:** S4 avgjør; bygg i #3 hvis
|
||||
grønn, ellers synkron-først og U12 etter planen, sammen med core-bumpen.
|
||||
|
||||
### D.4 Det som IKKE gjøres (uttalt)
|
||||
|
||||
- Ingen endring i `shared/method-spec.md`. Utforskningen er et lag OVER §3; en amendment
|
||||
(«Step 0 — Explore, optional») foreslås til commons via coord (operatørbeslutning, §F) — planen
|
||||
avhenger ikke av svaret.
|
||||
- Ingen D7-speiling nå: po-claude er PARKERT (STATE); utforskningslaget meldes dit når parkeringen
|
||||
oppheves, på linje med Steg-5-returtypen og demo-innboksen som også venter.
|
||||
- Ingen erstatning av GroupChat-debatten (Steg 3 står). Ingen `open/`-push, versjonsbump, Azure.
|
||||
- Ingen egen `MagenticManagerBase`-subklasse før en målt mangel krever det.
|
||||
|
||||
---
|
||||
|
||||
## E. Verifisering — testbare kriterier (kommandoer, ikke «sjekk at det virker»)
|
||||
|
||||
**Denne økten (planen selv):**
|
||||
|
||||
| # | Kriterium | Kommando → forventet |
|
||||
|---|---|---|
|
||||
| E1 | Nevneren er 32 | `find src -name '*.py' \| wc -l` → `32` |
|
||||
| E2 | Hver null-spørring kan finne | `bash scratchpad/u_measure.sh` (V1) → `venv_files` ≥ 1 for hver av de 9 null-regexene |
|
||||
| E3 | Magentic ikke kode-markert | `grep -c -i "experimental" .venv/lib/python3.12/site-packages/agent_framework_orchestrations/_magentic.py` → `0`; `grep -n "SKILLS\|EVALS" …/agent_framework/_feature_stage.py` → `:54`, `:64` |
|
||||
| E4 | Installert = lås | `uv pip list \| grep orchestrations` → `1.0.0`; `sed -n '61,63p' uv.lock` → `version = "1.0.0"` |
|
||||
| E5 | Single-use + builder-blødning reproduserbar | `uv run python scratchpad/magentic_b7.py` → `E1 run2 … RuntimeError`, `E2 VERDICT … 4/4`, `E3 VERDICT … 0/5` |
|
||||
| E6 | Plan-review-rundtur reproduserbar | `uv run python scratchpad/magentic_b7b.py` → `E6 after APPROVE outputs: ['FINAL: …']`, `E5 outputs: ['Workflow terminated due to reaching maximum round count.']` |
|
||||
| E7 | OTEL-sdk mangler | `.venv/bin/python -c "import opentelemetry.sdk"` → `ModuleNotFoundError` |
|
||||
| E8 | Planen er dok-gate-klassifisert | `uv run pytest -q tests/test_doc_constant_sync_loadbearing.py` → grønn (datert sti = arkiv-klasse) |
|
||||
| E9 | Treet urørt utenom planen | `git status --porcelain` → kun `docs/plan/2026-08-23-…md` + den fremmede `presentasjon`-HTML-en |
|
||||
|
||||
**Byggeøktene (kriterier som FELLES når sømmen detaches — load-bearing-metoden):**
|
||||
|
||||
| Leveranse | Rødt-først-kriterium |
|
||||
|---|---|
|
||||
| S1/B7 | test som deler builder mellom to utforskninger er RØD (4/4); `fresh_exploration_workflow` gjør den grønn (0/N) |
|
||||
| S2/budsjett | `Budget(max_tokens=1)` på manager-agenten → `BudgetExceeded` ut av `explore()`; detach middleware på manageren → testen rød |
|
||||
| C.2/runder | `max_rounds=1` → `BudgetExceeded(kind="exploration_rounds", limit=1, observed=1)`; detach oversettelsen → kjøringen «lykkes» med den kanoniske strengen → rød |
|
||||
| C.3/kontrakt | `ExplorationContract(max_stall_count=None)` → `ValidationError` ved konstruksjon |
|
||||
| C.5/plan review | persona-`revise` én gang → `REPLANNED` i loggen og `plan_reviews == 2`; `max_plan_revisions=1` + persona som alltid reviderer → typet stopp, aldri evig løkke |
|
||||
| C.6/frø | frø-approach `id="fagperson-1"` finnes i output-`Mandate.approaches` uansett hva manageren fant; detach bevaringen → rød |
|
||||
| C.0/nivå 3 | `explore()` skriver ingenting til outbox/wiki: `ls outbox` før/etter → kun `{run_id}-exploration.json`; `git status shared/` ren |
|
||||
| U12/S4 | subprosess-resume: pending `request_info` re-emittert + sluttsvar; detach `checkpoint_storage` → rød |
|
||||
| U14 | demoen under `PORTFOLIO_OTEL=console` → ≥1 `workflow.run`-span på stderr; uten variabelen → stdout byte-identisk med `tests/golden/demo-transcript.stdout` |
|
||||
| Golden | `uv run pytest -q` grønn; `shasum tests/golden/demo-transcript.stdout` uendret |
|
||||
|
||||
---
|
||||
|
||||
## F. Nøkkelantakelser, risiko og åpne beslutninger
|
||||
|
||||
| # | Antakelse | Test | Status |
|
||||
|---|---|---|---|
|
||||
| A1 | Agent-nivå `ChatMiddleware` fyrer på managerens kall | S2 | **umålt — RISIKO** (planens budsjettgaranti hviler her) |
|
||||
| A2 | `BudgetExceeded` reist inni en Magentic-deltaker propagerer ut av `workflow.run` | S2 | målt for GroupChat (live-kjøringen 14.08), umålt for Magentic |
|
||||
| A3 | `request_info`-rundtur på 1.0.0 | E6 | **MÅLT grønn** |
|
||||
| A4 | Resume med pending plan review i NY prosess | S4 | umålt — kode-lest (`_runner_context.py:414-426`) |
|
||||
| A5 | `AgentApprovalExecutor` som deltaker gir svar midt i kjøringen | S3b | umålt |
|
||||
| A6 | Persona-manuset kan drive en Magentic-manager (ledger-JSON) offline | E1–E6 brukte en `FakeClient` med gyldig ledger-JSON | **MÅLT mulig**; portering til `ScriptedChatClient` er S1/S3 |
|
||||
| A7 | Utforskning som opt-in holder demo-transkriptet byte-uendret | `shasum` i E-tabellen | bygges slik; verifiseres per økt |
|
||||
| A8 | 1.0.1 er API-identisk med 1.0.0 | S0 | umålt |
|
||||
|
||||
**Beslutninger som trenger operatøren (speiles i avslutningsblokken):**
|
||||
1. Versjon: bli på 1.0.x (anbefalt) vs. core-bump til 1.15 nå.
|
||||
2. U14 som deklarert avhengighet (`opentelemetry-sdk` + exporter) i wheel/handover — ja/nei.
|
||||
3. HITL-leveranse: synkron-først (anbefalt, uansett S4) vs. asynkron som krav i samme plan.
|
||||
4. Commons-amendment «Step 0 — Explore» — sende forslag nå (anbefalt: ja, via coord, uten å vente).
|
||||
|
||||
---
|
||||
|
||||
## Vedlegg V1 — målingsskript og rå output (U1–U19)
|
||||
|
||||
Skript (`scratchpad/u_measure.sh`, kjørt 2026-08-23; kopiert hit ordrett så målingen kan gjentas):
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
cd /Users/ktg/repos/portfolio-optimiser
|
||||
SRC_N=$(find src -name '*.py' | wc -l | tr -d ' ')
|
||||
TST_N=$(find tests -name '*.py' | wc -l | tr -d ' ')
|
||||
SPK_N=$(find spikes -name '*.py' | wc -l | tr -d ' ')
|
||||
echo "DENOMINATOR src=$SRC_N tests=$TST_N spikes=$SPK_N"
|
||||
m() { s=$(grep -rEn --include='*.py' "$2" src | wc -l | tr -d ' ')
|
||||
t=$(grep -rEn --include='*.py' "$2" tests | wc -l | tr -d ' ')
|
||||
k=$(grep -rEn --include='*.py' "$2" spikes | wc -l | tr -d ' ')
|
||||
printf "%-4s %-60s src=%-4s tests=%-4s spikes=%s\n" "$1" "$2" "$s" "$t" "$k"
|
||||
[ "$s" != "0" ] && grep -rEn --include='*.py' "$2" src | head -12; }
|
||||
m U1 'ConcurrentBuilder|BuildConcurrent|FanIn'
|
||||
m U1b 'asyncio\.gather|create_task\('
|
||||
m U2 'WorkflowBuilder|@executor|class .*\(Executor\)|WorkflowContext|SwitchCase|add_edge|add_fan_out'
|
||||
m U3 'GroupChatBuilder|with_max_rounds|termination_condition|RoundRobinGroupChatManager'
|
||||
m U4 'Magentic'
|
||||
m U5 'SkillsProvider|McpSkillsSource|AgentSkillsProvider|agent_framework.*skills'
|
||||
m U6 'MCPStdioTool|MCPStreamableHTTPTool|MCPWebsocketTool|as_mcp_server'
|
||||
m U7 'FunctionTool|ai_function|@tool|tools=\['
|
||||
m U8 'Middleware|middleware='
|
||||
m U9 'ContextProvider|HistoryProvider|context_providers='
|
||||
m U10 'AzureAISearch|azure\.search|cosmos|qdrant|redis|VectorStore|vector_store|Qdrant|Chroma|chromadb'
|
||||
m U11 'AIContextProvider|TextSearchProvider|citation|SourceName|SourceLink|agentic_retrieval|KnowledgeAgent'
|
||||
m U12 'checkpoint|Checkpoint'
|
||||
m U13 'approval_mode|request_info|RequestInfoEvent|RequestPort|MagenticPlanReview|ApprovalRequired'
|
||||
m U14 'opentelemetry|setup_observability|OBSERVABILITY|get_tracer|start_as_current_span'
|
||||
m U15 'evaluate_workflow|LocalEvaluator|Evaluator|_evaluation|agent_framework.*evaluation'
|
||||
m U16 'CompactionProvider|SlidingWindow'
|
||||
m U17 'as_tool\(|AsAIFunction'
|
||||
m U18 'FoundryChatClient'
|
||||
m U19 'HarnessAgent|ShellExecutor|FileMemoryStore|ToolApprovalAgent'
|
||||
```
|
||||
|
||||
Rå tellinger: `DENOMINATOR src=32 tests=117 spikes=6` · U1 0/4/4 · U1b 1/5/0 · U2 0/0/0 · U3 10/7/7 ·
|
||||
U4 0/4/9 · U5 0/0/0 · U5b(`SKILL\.md`) 1/5/0 · U6 6/6/0 · U6b(rå mcp) 7/6/0 · U7 4/10/0 · U8 16/20/0 ·
|
||||
U9 8/11/4 · U10 2/19/0 · U10b(numpy/cosine) 49/102/1 · U11 26/35/0 · U12 3/0/0 · U13 3/0/0 ·
|
||||
U14 0/0/1 · U15 0/0/0 · U16 0/0/0 · U17 0/0/0 · U18 7/8/0 · U19 0/0/0.
|
||||
|
||||
Kjent-positiv-kontroll mot installert MAF (`grep -rEl <regex> agent_framework agent_framework_orchestrations | wc -l`):
|
||||
U2 → 21 · U5 → 3 · U14 → 4 · U15 → 2 · U16 → 4 · U17 → 1 · U19 → 1 · `checkpoint_storage|CheckpointStorage`
|
||||
→ 14 · `MagenticBuilder|StandardMagenticManager` → 4.
|
||||
|
||||
B7-/HITL-målingsskriptene (`magentic_b7.py`, `magentic_b7b.py`, `magentic_reset.py`) ligger i
|
||||
øktens scratchpad og porteres til `spikes/e_magentic.py` i økt 54 (S1/S3) — de committes ikke
|
||||
herfra, fordi de bruker en ad-hoc `FakeClient` og ikke repoets `ScriptedChatClient`.
|
||||
|
||||
## Vedlegg V2 — installert Magentic-API (orchestrations 1.0.0, `_magentic.py`, 1802 linjer)
|
||||
|
||||
- **Eksport** (`agent_framework_orchestrations/__init__.py:43-62`): `MagenticBuilder`,
|
||||
`MagenticContext`, `MagenticManagerBase`, `MagenticOrchestrator`, `MagenticOrchestratorEvent`/
|
||||
`…EventType`, `MagenticPlanReviewRequest`/`…Response`, `MagenticProgressLedger`/`…Item`,
|
||||
`MagenticResetSignal`, `StandardMagenticManager`, `MagenticAgentExecutor`. **Ikke i koden:**
|
||||
`MagenticHumanIntervention*`, `with_human_input_on_stall` (kun docstrings).
|
||||
- **`MagenticBuilder(*, participants, manager=|manager_factory=|manager_agent=|manager_agent_factory=
|
||||
(nøyaktig én), task_ledger_*_prompt…, progress_ledger_prompt, final_answer_prompt,
|
||||
max_stall_count=UNSET→3, max_reset_count=None, max_round_count=None, enable_plan_review=False,
|
||||
checkpoint_storage=None, output_from, intermediate_output_from)`** (`:1396-1422`). Metoder:
|
||||
`with_plan_review(enable=True)` (`:1516`), `with_checkpointing(storage)` (`:1564`), `build()`
|
||||
(`:1772`; bidireksjonale kanter orkestrator↔deltaker `:1796-1797`). Grensene bor på MANAGEREN
|
||||
(`:1693-1695`, lest tilbake `:905/:1115/:1240`); `manager=` + prompt/limit-argumenter → warning og
|
||||
**stille ignorert** (`:1661-1677`). Ikke-agent-`Executor` som deltaker aksepteres (`:1498-1499`)
|
||||
men får placeholder-beskrivelse (`_base_group_chat_orchestrator.py:115`).
|
||||
- **`StandardMagenticManager(agent, …, max_stall_count=3, max_reset_count=None,
|
||||
max_round_count=None, progress_ledger_retry_count=None→3)`** (`:528-544`). Én `AgentSession` i
|
||||
`__init__` (`:572`), alle kall `agent.run(…, session=…)` (`:601`). Ledger-parsing: fri tekst +
|
||||
`_extract_json` (`:406-449`), dataklasser ikke pydantic (`:288-333`).
|
||||
- **Løkke:** runde-teller ×2 (`:1075-1076`), grense-sjekk FØR inkrement på `_round_index` (`:1238`);
|
||||
stall `:1109-1118`; reset `:1149-1185`; **grense → kanonisk streng + `_terminated`, ingen
|
||||
exception** (`:1222-1256`); sluttsvar `:1206-1220`; ukjent `next_speaker` → stille sluttsvar
|
||||
(`:1128-1131`); single-use-guard `:914-918`.
|
||||
- **Plan review:** request `:829-853` (`plan`, `current_progress`, `is_stalled`; `.approve()`,
|
||||
`.revise(feedback)`), response `:800-826` (`review: list[Message]`, tom = approve `:1010`),
|
||||
reises `:1041-1048`, svares `workflow.run(responses={id: …})` (`_workflow.py:899-908`; ukjent id
|
||||
→ `ValueError` `:946`), revise-håndtering `:1014-1031` (ingen cap, ingen runde-telling).
|
||||
Per-tur-HITL via `AgentApprovalExecutor` (`_orchestration_request_info.py:168`).
|
||||
- **Checkpoint:** `CheckpointStorage`-protokoll `_checkpoint.py:119`, `InMemoryCheckpointStorage`
|
||||
`:192`, `FileCheckpointStorage(storage_path, *, allowed_checkpoint_types)` `:239`;
|
||||
`WorkflowCheckpoint.pending_request_info_events` `:81`; `run(checkpoint_id=, checkpoint_storage=)`
|
||||
`_workflow.py:701-712`, `responses`+`checkpoint_id` tillatt `:877`; superstep-granularitet
|
||||
`_runner.py:143-144`; re-emittering `_runner_context.py:414-426`; Magentic-state
|
||||
`_magentic.py:1266-1284` + manager-sesjon `:746`; feil svelges `:1281-1282`/`:1320-1321`.
|
||||
- **Observability:** `_magentic.py` → 0 spans. Runtime-spans `workflow.build`/`workflow.run`/
|
||||
`executor.process`/`edge_group.process`/`message.send` (`observability.py:253-276`).
|
||||
Inngang `configure_otel_providers(*, enable_sensitive_data, enable_console_exporters, exporters,
|
||||
views, vs_code_extension_port, env_file_path, env_file_encoding)` (`:1168`), `enable_instrumentation`
|
||||
(`:1132`); env `ENABLE_INSTRUMENTATION` (default True, `:697`), `ENABLE_SENSITIVE_DATA`,
|
||||
`ENABLE_CONSOLE_EXPORTERS`, `OTEL_EXPORTER_OTLP_ENDPOINT`/`_PROTOCOL`/`_HEADERS`,
|
||||
`OTEL_SERVICE_NAME`. Ikke re-eksportert fra `agent_framework` (`hasattr` → False).
|
||||
`APPLICATIONINSIGHTS_CONNECTION_STRING` → ikke funnet. `opentelemetry-sdk`: **ikke installert**.
|
||||
- **Evals/Skills:** `evaluate_workflow(*, workflow, workflow_result|queries, evaluators, …)`
|
||||
(`_evaluation.py:1833`, `@experimental(EVALS)` `:1832`); `SkillsProvider(ContextProvider)`
|
||||
(`_skills.py:1719`, `@experimental(SKILLS)` `:1718`; `from_paths` `:1861`).
|
||||
|
||||
## Vedlegg V3 — kommandoer kjørt (utvalg; fullstendige lister i øktens agent-transkripter)
|
||||
|
||||
```
|
||||
find src -name '*.py' | sort | wc -l # 32
|
||||
uv pip list | grep -i -E "agent.framework|^mcp |opentelemetry|azure-ai|anyio"
|
||||
sed -n '61,75p' uv.lock ; grep -n "agent-framework" pyproject.toml
|
||||
curl -s https://pypi.org/pypi/agent-framework-orchestrations/json # + /1.0.0 … /1.1.1/json
|
||||
sed -n '1,80p' .venv/.../agent_framework/_feature_stage.py
|
||||
grep -rn "@experimental|@preview|feature_stage(" .venv/.../agent_framework/*.py .../_workflows/*.py
|
||||
grep -n -i "experimental|feature_stage" .venv/.../agent_framework_orchestrations/*.py # 0
|
||||
bash scratchpad/u_measure.sh # V1
|
||||
grep -rEl <9 null-regexer> .venv/.../agent_framework .venv/.../agent_framework_orchestrations | wc -l
|
||||
grep -rn -i "research.01" STATE.md STATE-arkiv-2026-08-21.local.md docs/ # 0
|
||||
grep -rn "enable_layer1_hitl|layer1" src tests ; grep -rEn 'request_info|RequestInfoEvent' tests # 0
|
||||
grep -rn "agent_framework" src/portfolio_optimiser/*.py # 14 importlinjer
|
||||
microsoft_docs_fetch learn.microsoft.com/en-us/agent-framework/workflows/orchestrations/magentic
|
||||
uv run python scratchpad/magentic_b7.py ; … magentic_b7b.py ; … magentic_reset.py # E1–E7
|
||||
.venv/bin/python -c "import opentelemetry.sdk" # ModuleNotFoundError
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue