portfolio-optimiser/README.md

170 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# portfolio-optimiser
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Python](https://img.shields.io/badge/python-%E2%89%A53.10-blue.svg)](pyproject.toml)
[![Built on Microsoft Agent Framework](https://img.shields.io/badge/built%20on-Microsoft%20Agent%20Framework-0078D4.svg)](https://github.com/microsoft/agent-framework)
A generic, open framework — built on **Microsoft Agent Framework (MAF)** — that finds cost
savings *inside* each project of a portfolio of independent projects. A swarm of agents
generates candidate measures; a **mandatory deterministic validator** (solver + Monte Carlo)
decides the numbers; domain experts judge the outcomes (human-in-the-loop); and the system
**learns from their verdicts** across runs.
> **Status:** the full 8-step agentic loop is wired and proven with load-bearing tests, and the
> end-to-end proof is an **offline simulation** with a scripted stand-in client — no live-model
> run yet. The **ingest layer** (real data sources) is implemented — file/CSV and SQL on both
> stacks with bit-identical golden extractions from the shared spec, plus HTTP as a MAF-only
> demonstrated extension point against a local mock — but exercised only against committed
> fixtures: no bundle has yet been materialized from a live source. A sibling implementation of
> the same method on the **Claude Agents SDK** is built in parallel from the same shared spec.
> **Disclaimer — technical framework only.** Deploying organizations own their processing
> purposes and assessments (DPIA, risk/ROS, security review). The framework ships the technical
> prerequisites — local-only mode, provenance, no silent data egress — but makes no compliance
> guarantees.
## Built on an LLM wiki: Karpathy's idea, Google's format
The knowledge architecture is the heart of the project, and it is deliberately not ours:
- **The idea** is Andrej Karpathy's **"LLM wiki"**: instead of pointing a model at documents
written for people, you curate a small, versioned body of knowledge written *for the model
to read* — concept files, explicit structure, explicit links.
- **The format** is Google Cloud's **[Open Knowledge Format (OKF)](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md)**
(open spec, v0.1), which formalizes that pattern: a knowledge **bundle** is a directory of
markdown files with YAML frontmatter (one required field, `type`), a reserved `index.md`
entry point, and intra-bundle cross-links forming an emergent graph. Custom frontmatter
fields are allowed and must be preserved — which is exactly where this project's own layers
(expert verdicts, ingest provenance) live.
Because OKF is open and vendor-neutral, the *same* bundles are consumed unchanged by both
reference implementations (MAF and the Claude Agents SDK sibling) — the knowledge outlives any
particular agent stack.
**Not RAG.** Agents read a bundle by **navigating** it — `index.md` first, then its
cross-links, with progressive disclosure — never by keyword retrieval or stuffing the whole
bundle into a prompt. Query-time retrieval against the bundle is explicitly forbidden by the
method spec: it would leak the verdict layer around the learning gate.
## AI-first, humans on top
A traditional wiki is built for *people* — optimized for humans finding and reading
information, with machine access bolted on afterwards. This project inverts that order, and is
a concrete example of what that looks like:
- The wiki (the OKF bundle) is written **for the model**: it is the agent's working memory and
the substrate the learning loop reads from and promotes into.
- The **human affordances are layers on top**: experts judge outcomes by dropping a plain JSON
verdict file in an inbox folder; an explicit, fail-closed **promotion gate** is the only path
by which an approved verdict becomes wiki knowledge; reports and reviews are rendered *from*
the machine-readable layers.
Humans stay decisive — nothing enters the wiki without an approval — but the primary reader of
every file is the model, not a person browsing.
## How it works
One run, one project, eight steps — with the learning loop closing across runs:
1. **Understand** — navigate the project's OKF bundle; fold the candidate's *prior expert
verdicts* into the hypothesis prompt (ExpeL-style, retrieved structurally, never by text).
2. **Hypothesise** — one typed candidate measure (strict IR, fail-fast schema).
3. **Debate** — a maker-checker pair argues the reasoning (round-capped).
4. **Validate** — two falsifiers on the same candidate: the **deterministic validator** gates
the numbers (blocking, never optional) and the **checker** gates the reasoning.
5. **Refine** — a rejected attempt retries *informed* by the rejection reason, under hard
attempt and token caps. Unbounded loops are forbidden everywhere.
6. **Propose or discard** — a validated proposal with risk percentiles, or a typed rejection.
7. **Expert feedback** — days later, an expert drops a verdict file in an inbox folder; a
later run picks it up. Fully resumable; no live session assumed.
8. **Promote** — an *approved* verdict is lifted into the wiki as a `type: verdict` concept
file, navigable by the next run. The gate is fail-closed: raw agent output never
self-promotes.
Every proposal carries provenance (citations into the bundle, model, validator decision, token
usage). Every seam above is protected by a **load-bearing test** — a test designed to *fail*
when the seam is detached, so the loop cannot silently degrade into theater.
## How it is set up
- **One shared, framework-neutral core** ([`shared/`](shared/), a git subtree of
[`portfolio-optimiser-commons`](https://git.fromaitochitta.com/ktg/portfolio-optimiser-commons)):
the business concept, the normative [method spec](shared/method-spec.md) and
[ingest spec](shared/ingest-spec.md), the expert-reviewer persona as an Agent Skill, and an
example bundle with a golden suite as the only ground truth. Both stacks implement from the
spec alone.
- **Per project: one OKF bundle** — the bundled examples are hand-curated; the ingest layer that
materializes a bundle from a source (file catalogues/CSV + SQL, HTTP as a MAF-only demonstrated
extension point) via a deterministic, schema-validated manifest that runs *before* the loop is
implemented and exercised against committed fixtures — no bundle has yet been materialized from
a live source.
- **Run:** the `run.py` CLI has **three modes** — a documented partition, since one invocation
cannot exercise every flag:
- **Single-project** — `PROJECT_ID --docs-dir <dir>`, plus optional `--bundle-dir`,
`--verdict-dir`, `--outbox-dir` (which requires `--run-id`), `--dimension-config`,
`--decision`/`--rationale`, and `--live-dry-run`.
- **Portfolio** — `--portfolio`, plus optional `--goals`, `--ledger`, `--dimension-config`;
it stops early and prints a `goal reached: …` line when the accumulated ledger meets a goal.
- **Value report (S5.4, read-only)** — `--report --ledger <file>` rolls up the ledger's realized
savings to stdout: per-project totals, the portfolio total, flagged cross-dimension overlaps
(each counted once), and per-entry provenance. Add `--json` for deterministic JSON instead of
the human table. It makes **no model calls** and is mode-exclusive — only `--ledger`/`--json`
are permitted alongside `--report`; `--report` requires `--ledger`, and a stray `--json`
without `--report` is refused (rc 1, never silently ignored).
```bash
# Single-project, offline drill (builds contracts + clients, stops before the first model call):
uv run python -m portfolio_optimiser.run FV42-GSV-E1 --docs-dir <docs> --bundle-dir <bundle> --live-dry-run
# Portfolio run with a savings goal checked against an accumulated ledger:
uv run python -m portfolio_optimiser.run --portfolio --goals goals.json --ledger ledger.json
# Read-only value report over an accumulated ledger (human table; add --json for JSON):
uv run python -m portfolio_optimiser.run --report --ledger ledger.json
```
The **prior-verdict fold — the learning step — happens only on the `--bundle-dir` path**; a
plain `--docs-dir`-only run is single-shot (no fold). `--decision`/`--rationale` apply to the
single-project path only and are inert in portfolio mode. **`--outbox-dir` must differ from
`--verdict-dir`**: writing the raw outbox into a folder later read as an inbox would re-ingest
raw agent output past the promotion gate (self-contamination) — documented here, deliberately
not CLI-enforced. Stop criteria and budget caps are required at startup. Try the offline
end-to-end proof (no model, no network): `uv run python -m portfolio_optimiser.simulation`.
## What this enables
The reference case is portfolio cost review (the example bundle is a building-energy measure),
but the architecture is designed to generalize to any setting with the same shape — candidate
measures inside independent projects, numbers a deterministic tool can check, and judgement
only an expert has:
- **Portfolio reviews** — cost savings, energy efficiency, maintenance and procurement
measures, proposed per project and validated against the project's own data.
- **Compounding organizational memory** — approved expert verdicts become navigable knowledge;
the next run's hypotheses start from what experts actually decided, including realization
gaps no solver can compute.
- **Auditable AI** — an unbroken provenance chain from expert decision back through proposal,
bundle file and text span, and (with ingest) to the source system, query, and timestamp.
- **Vendor-neutral knowledge** — the same bundles drive two different agent stacks; switching
frameworks does not orphan the organization's curated knowledge.
## Docs
- [Building a knowledge base](docs/knowledge-base-recipe.md) — the team recipe (technical +
domain expert) for curating a bundle, with the honest expectation that a good base takes 12
weeks of dedicated work.
- [Target picture](docs/plan/2026-06-26-maalbilde-agentic-loop.md) — the agentic loop + OKF
knowledge architecture (north star).
- [Prior-art & platform research](docs/research/2026-06-23-prior-art-platform.md) (incl.
implementation register §15).
- [Ingest target picture](docs/plan/2026-07-03-maalbilde-ingest-lag.md) — connectors and the
ingest layer (frozen 2026-07-03).
## Stack & develop
Python ≥3.10 · MAF via the split GA packages (see `pyproject.toml`) · `uv`. Backend profiles:
Azure/Foundry (full) + local (fallback).
```bash
uv sync
uv run pytest
uv run ruff check .
```