Retained Python method guide / 保留的 Python 方法指南

September 20, 2026 · View on GitHub

For standalone J++ source, use the Rust guide and implemented grammar. This page documents the retained Python 3.12+ builder. Install it from this checkout with python -m pip install ., or install a delivered wheel with python -m pip install /path/to/jpp_language-0.1.0a3-py3-none-any.whl. No research directory, PYTHONPATH setting, or kernel snapshot is needed.

本页保留 Python 构建器的使用方法;正式独立源码入口见上面的 Rust 指南。安装 Python 包后可以运行:

jpp demo
jpp methods --output method-report.json
jpp partial --output partial-report.json

The second command runs a complete feedback method, constructs nested checking methods at runtime, replaces an internal checker, and saves the common plan, actual dynamic structures and outputs. [0] produces x; [-4,...,4] produces x if x >= 0 else -x. These are finite-domain checks with synthetic observations, not measured JEV accuracy. Output is written to your chosen file, never into the installed package. Full source: jev_compose.method_example (included in wheel).

jpp partial uses an already sufficient candidate combination while other questions remain unresolved, then changes the strategy and improves it without repeating prior checks. See partial results and continuations for the protocol, complete example and direct-control comparison. jpp partial 演示先使用足够的部分结果,再换策略补问;旧检查保留。

Define, compose, execute

from foundation import jv
from jev_compose import component, execute, identity, product, iterate, Iteration
from jev_compose.fixtures import runtime

@component("increment", int, int)
def increment(n):
    return n + 1

@component("enough", int, bool)
def enough(n):
    return n >= 3

@component("answer", Iteration, int)
def answer(result):
    return result.state

method = iterate(increment, enough, limit=5).then(answer)
larger_method = product(method, identity(int))
result = execute(larger_method, 0, runtime())
assert result.value == (3, 0)
print(larger_method.describe())
print(jv.plan(larger_method.program()))
print(result.trace)

Put definitions in a .py file and run it with Python. component checks visible source with the existing kernel checker. Signatures check nominal types and container shapes; use typing.Any for an intentional open boundary. Ordinary Python/dataclasses implement data and exact algorithms.

OperationContract
a.then(b)Pass a's output to b; types must connect.
product(a,b,...)Same input for all; run in declaration order; return tuple.
branch(predicate,yes,no)Predicate returns bool; arms have identical signatures.
iterate(step,done,limit=N)step S→S, done S→bool; check before stepping; at most N steps, N+1 checks. Returns Iteration with state/steps/reason (done or limit).
a.bind(factory,T,effects=...,factory_effects=...)Run a, pass result to factory, then run returned Component on that same result. T is continuation output type.
replace_at((child_index,...),replacement)Rebuild supported structure, preserving original and checking connections. Children: then=(a,b), branch=(predicate,yes,no), product=parts, iterate=(step,done), bind=(a,factory).

A method is a normal Component value: pass it to a constructor, return it from a function, or compose it again. Returning it alone does not run it. For bind, effects bounds the returned method's capabilities. factory_effects describes the factory's own calls; passing a Component factory derives its declaration. An explicit superset is allowed. Plain unclassified factories default to *. Declarations are not proofs of arbitrary host-code purity.

from jev_compose import Component

@component("choose_method", int, Component)
def choose(n):
    return increment if n < 3 else identity(int)

dynamic = identity(int).bind(choose, int, effects=frozenset())
assert execute(dynamic, 1, runtime()).value == 2

inquire(prepare,update,done,limit=N,observer=observe) constructs an iterative inquiry: prepare S→Request, update (S,Observation)→S, done S→bool. feedback(propose,inspect,update,done,limit=N) constructs candidate/check/update: propose S→list, inspect (S,list)→list, update (S,list)→S. Both use the same public combinators; their internal steps can be replaced by child path.

Observe and retain uncertainty / 判断与未决

from jev_compose import Request, observe

@component("read_flag", Request, object, effects=("judge",))
def read_flag(request):
    answer = observe(request)
    return answer.value if answer.resolved else None

request = Request(jv.state(on=jv.mat({"flag": True})),
                  jv.test("Does the flag hold?", calib=jv.calib("demo.flag")))
assert execute(read_flag, request, runtime()).value is True

The provided fixtures.runtime() is explicitly synthetic and free of model API calls. For custom deterministic observations, pass runtime(rule=rule) where rule(state_json, question_id, question_dict) returns e.g. {"type":"noul","noul":0.99} (true), 0.01 (false), or 0.5 (unresolved). The fixture registers demo.flag, demo.membership, demo.pick, demo.complexity. It does not establish real calibration. Production uses the ordinary jv.Runtime with its real client/profile/calibration, not a different composition runtime.

Observation keeps value/resolved/cause/request/model_id; to_dict() exports provenance. batch_observe(requests) registers independent questions before consuming results. at_least(observations,k) derives count bounds; unresolved returns None when the bound is insufficient. refine updates only selected unresolved observations, preserving known observations even with matching IDs.

Plan and inspect a run / 调试

method.structure() returns the versioned read-only structure used by common jv.plan(method.program()). Product costs sum; loop bounds include N+1 checks; dynamic continuations stay symbolic. Planning never invokes factories.

execute(method,input,runtime(),budget=jv.Budget(calls=...)) returns Execution(value,stats,structure,trace). Each trace event identifies its component. Dynamic bind events additionally contain call_id, generated_structure, generated_description, continuation_trace_start/end, and result. Nested event ranges identify which method actually ran. These records belong to this execution and do not mutate the reusable method. The raw structure retains callables; export descriptions instead of trying to serialize closures.

Budgets and action replay remain owned by the shared kernel, including nested programs. done means the author's condition was met, not universal correctness. No new syntax or automatic natural-language compiler is included in this release.

Export a report / 导出报告

Do not call dataclasses.asdict() on the entire execution: read-only structures retain functions. Keep the portable generated description and stringify the plan. For ordinary values and dataclass results, this is a complete starting point:

import json
from dataclasses import fields, is_dataclass
from collections.abc import Mapping

def encode(value):
    if is_dataclass(value):
        return {f.name: getattr(value, f.name) for f in fields(value)}
    if isinstance(value, Mapping):
        return dict(value)
    if isinstance(value, (set, frozenset)):
        return sorted(value)
    if hasattr(value, "to_dict"):
        return value.to_dict()
    raise TypeError(f"Add a serializer for {type(value).__name__}")

report = {
    "value": result.value,
    "plan": str(jv.plan(larger_method.program())),
    "trace": [{k: v for k, v in event.items() if k != "generated_structure"}
              for event in result.trace],
}
with open("my-report.json", "w") as output:
    json.dump(report, output, default=encode, indent=2)

An independent author used only this guide and the installed wheel to write a new flaky-test method. It identifies inconsistent test outcomes, passes the entire detector into a report constructor, and runs the returned method through bind. Run python examples/flaky_method.py from a checkout after installing the package. It needs no model calls or source-path setup.