Production Governance in 0.5¶
MemoRizz 0.5 makes tool execution, caching, compaction, and delegation explicit runtime contracts. This guide covers the controls that matter when an agent can reach Notion, Google Calendar, business systems, or a code-execution provider.
Compose the complete runtime¶
The builder now covers the same production surface as MemAgent; no
post-build mutation is required:
from memorizz import (
ContextPolicy,
MemAgentBuilder,
SQLiteApprovalStore,
ToolResultPolicy,
governed_tool,
)
from memorizz.long_term.procedural.toolbox import Toolbox
from memorizz.sandbox.providers.e2b_provider import E2BSandboxProvider
@governed_tool(
deterministic=False,
side_effects=True,
requires_approval=True,
approval_reason="Creates an external customer record",
domains=("crm",),
)
def create_customer(name: str, email: str) -> dict:
...
toolbox = Toolbox.from_functions(
[create_customer],
memory_provider=provider,
agent_id="ops-agent",
augment=False,
)
agent = (
MemAgentBuilder()
.with_name("Operations")
.with_model(model)
.with_memory_provider(provider)
.with_tools([create_customer])
.with_toolbox(toolbox)
.with_mcp_servers(notion_and_calendar_servers)
.with_sandbox(
E2BSandboxProvider(
template="memorizz-bounded",
allow_internet_access=False,
cpu_count=2,
memory_mb=1024,
)
)
.with_context_policy(ContextPolicy(tool_top_k=5))
.with_tool_result_policy(ToolResultPolicy(offload_above_chars=8_000))
.with_approval_store(SQLiteApprovalStore("/var/lib/memorizz/approvals.db"))
.with_skills(course_skills, persistence="skillbox")
.with_skill_retrieval(enabled=True, top_k=2)
.with_continual_learning(enabled=False)
.with_delegation(delegates, mode="deterministic", plan=workflow_plan)
.build(validate=True, persist=True)
)
Environment-backed presets reduce that setup further for the standard local Oracle + E2B deployment:
agent = (
MemAgentBuilder()
.with_oracle_from_env(
ensure_ready=True,
provision_if_missing=True,
index_policy="lazy",
)
.with_e2b_from_env(
template="memorizz-bounded",
allow_internet_access=False,
)
.build(validate=True)
)
print(agent.environment_reports["oracle"]["preflight"])
The presets read credentials from the documented environment, never place them in the report, and fail before build when readiness or validation fails.
Toolbox.from_functions(..., augment=False) is deterministic: it does not
create an LLM or global embedding client. Persisted schemas keep required
fields, defaults, enums, nested structures, and
additionalProperties: false. Executable Python is never unpickled; trusted
callables are rebound explicitly or by a reviewed module:qualified_name
reference.
Progressive tool disclosure¶
At the start of each turn, SemanticToolRouter applies agent, user, and Toolbox
scope before selecting top-k tools. The model receives the selected strict
schemas plus stable discover_tools and invoke_tool handles—not the entire
Toolbox. Invocation is allowlisted, aliases and deprecated arguments are
normalized, the Python signature is bound strictly, and duplicate/retry budgets
are enforced.
Setting ContextPolicy(progressive_tool_disclosure=False) restores direct
schema exposure for a small, trusted tool set. It should be an explicit
compatibility choice.
Durable human approval¶
A side-effecting governed tool pauses execution by creating a serializable
ApprovalProposal containing:
- the exact logical tool name and arguments;
- a SHA-256 argument digest and policy reason;
- a single-use proposal/checkpoint ID and thread ID;
- creation and expiry timestamps; and
- the eventual approver identity, decision, and audit timestamp.
approved and confirm are not model-visible arguments. A host lists and
decides proposals, then resumes the stored checkpoint:
pending = agent.list_approval_proposals(status="pending")
proposal_id = pending[0]["proposal_id"]
agent.approve(proposal_id, approver_id="operator@example.com")
resume = agent.resume_approval(proposal_id) # exact stored call, once only
print(resume.tool_result) # exact result captured before continuation
print(resume.assistant_response) # optional subsequent model response
print(resume.consumed) # True
# A deterministic host can skip the subsequent model call:
resume = agent.resume_approval(proposal_id, continue_model=False)
# Alternatives:
agent.reject(proposal_id, approver_id="operator@example.com")
agent.cancel_approval(proposal_id, approver_id="operator@example.com")
The local Playground renders the exact proposal with Approve & resume and
Reject controls. The JSON routes live under
/api/agents/{agent_id}/approvals. MCP-specific CLI/UI approval flows remain
available and use the same durable store semantics.
Size-aware tool results¶
Small tool results remain inline. A full result is persisted exactly once only
when it crosses offload_above_chars or offload_above_tokens; the returned
pointer includes its log ID, SHA-256 digest, size, identifiers, and audit time.
retrieve_tool_log_entry and other configured expansion tools are never
offloaded, so expansion cannot create pointer-to-pointer loops. Missing or
invalid expansions return structured failures.
Semantic cache correctness¶
Semantic similarity is a candidate-reuse signal; it does not establish that an answer is fresh or operationally correct. MemoRizz therefore admits read-only, deterministic turns by default and automatically bypasses cache writes for side-effecting tools. Entries include user/memory/session scope and model, prompt, tool-schema, and data-version fingerprints, with domain-specific freshness limits and hit provenance.
stats = agent.semantic_cache_stats()
# hits, misses, bypasses, writes, evictions, size, last-hit provenance
inspection = agent.inspect_semantic_cache(
"How many units are available?",
user_id="tenant-a",
context={"cache_domain": "inventory", "data_version": "erp-42"},
)
# matched_query/cache_key, similarity, TTL/age, hit_count, bypass_reason,
# invalidation_domains and invalidation_tags; cached response text is omitted.
removed = agent.invalidate_semantic_cache(
domains=["inventory"],
tags=["warehouse-7"],
data_version="catalog-2026-08-12",
)
Invalidate a domain when its source data changes; do not use a high similarity threshold as a substitute for invalidation or a freshness limit.
Explicit compaction scope¶
Never let a background task infer its tenant from whichever request happened to run most recently. Pass the complete scope:
summary_ids = agent.generate_summaries(
memory_id="course-run-17",
user_id="student-42",
thread_id="lesson-3",
days_back=7,
)
user_id=None explicitly selects anonymous/legacy rows. thread_id=None
allows each thread in the selected memory/tenant scope to be compacted into its
own summary; chunks never cross tenant, memory, or thread boundaries.
Oracle compaction and operations¶
Oracle summaries persist canonical source_message_ids, period_start,
period_end, and memory_units_count, with normalized summary-to-message links
and a conversation summary_id marker. Summary creation and original-message
marking occur in one transaction. retrieve_by_id(SUMMARIES, summary_id) and
expand_summary() reconstruct the compacted history losslessly.
Use package-owned bootstrap and diagnostics instead of notebook setup code:
from memorizz import LocalOracleRuntime, OracleProvider
runtime = LocalOracleRuntime.from_env(provision_if_missing=True)
runtime.ensure_ready()
provider = OracleProvider.from_env(
provision_if_missing=True,
index_policy="lazy", # none | lazy | selected | eager
)
report = provider.preflight()
Preflight reports service/version (including version_full)/PDB state,
privileges, embedding models and dimensions, vector columns and indexes,
VECTOR_MEMORY_SIZE, a sizing
recommendation, and exact-search fallback. Scope cleanup is transactional:
Preflight sets ok=False when the configured embedder dimension differs from
any existing Oracle VECTOR column, preventing the first write from failing
later with ORA-51803.
counts = provider.delete_scope(
memory_id="course-run-17",
user_id="student-42",
agent_ids=["planner", "executor"],
)
Sandboxes and delegation¶
E2B uses one bounded Sandbox.create(...) session, so write → execute → read is
coherent. It fails at construction without an API key, normalizes SDK result
shapes, terminates through close()/context-manager semantics, and reports
provider and resource policy in ExecutionResult.metadata. See the
E2B guide.
GraalPy subprocess mode is deliberately labeled as an execution provider, not a strong sandbox. It confines MemoRizz file APIs, allowlists environment keys, applies resource limits, and denies network or fails closed by default. Use the package-shipped Java wrapper source only after compiling it against the matching GraalVM release and validating the resulting JAR in your deployment. MemoRizz verifies the wrapper class/manifest and supplies GraalVM's mandatory resource limits; no prebuilt cross-version JAR is claimed. See the GraalPy guide.
Multi-agent planning and decomposition use the configured LLMProvider and
model; there is no direct OpenAI call or hard-coded model. Deterministic plans
are supported, delegates are operational, tenant/request/tool/trace context is
propagated, shared memory is workflow-scoped, and partial/dependency failures
are returned in the orchestration report.
SubTask.to_dict()/SubTask.from_dict() preserve status and result fields.
The builder converts list-based deterministic plans to JSON-safe dictionaries,
so .build(validate=True, persist=True) can store them in Oracle. Callable
plans remain explicitly runtime-only and persistence rejects them with an
actionable error.
Provider failures, observability, and shutdown¶
run_stream() always emits a terminal error event containing error_code,
exception_type, and optional provider_status_code. Hosts that prefer
exception propagation can opt in:
Authentication failures such as HTTP 401 are raised after the typed event is emitted. Operational summaries avoid notebook-side row deserialization:
summary = agent.observability_summary(
memory_id="course-run-17",
user_id="student-42",
thread_id="lesson-3", # optional
)
Use the agent as a context manager to close sandbox, browser, internet, approval, and memory-provider resources. Cleanup is opt-in and exact-scope:
with agent.lifecycle(
cleanup_scope={
"memory_id": "course-run-17",
"user_id": "student-42",
"agent_ids": [agent.agent_id],
}
) as active_agent:
active_agent.run("...", user_id="student-42")
Governed browser control¶
browser_control is a provider-neutral, opt-in capability. The Browser Use
implementation runs in a separately installed CLI environment and receives a
fixed MemoRizz wrapper through that tool environment's Python interpreter; the
model supplies only a JSON-encoded task and bounded step count. The worker
inherits an allowlist of environment variables, runs from a private temporary
directory, is terminated as a process group on timeout, and always closes its
browser.
Every model call is classified as nondeterministic and side-effecting, so it enters the same durable approval lifecycle described above. Domain allowlists, denylists, direct-IP blocking, headless/vision controls, step limits, and wall timeouts are host policy—not model arguments. See Browser Control.
Deployment checks¶
Do not infer feature availability from memorizz >= 0.5.0 alone:
The Python equivalents are memorizz.capabilities() and
agent.capability_report(preflight=True). They report package/provider
versions and effective feature states, including optional MCP, Oracle, E2B,
GraalPy, and isolated Browser Use dependencies.
For a repository/source deployment, run the release verifier against a staging environment after loading secrets through environment variables:
PYTHONPATH=src python scripts/verify_production_050.py --json
python -m pytest -q
mkdocs build --strict
The verifier exercises the configured Oracle AI Database, in-database vectors,
OpenAI/Anthropic/Tavily, E2B's stateful file round trip, a durably approved
Browser Use task constrained to example.com, the first-party MCP stdio and
authenticated HTTP transports, tenant isolation, and hosted MCP authentication
boundaries. OAuth-success paths still require operator-owned Notion/Google
OAuth grants; an unauthenticated authorization_required result is a boundary
test, not a successful OAuth test. Use --skip-browser only when the isolated
CLI/browser is intentionally absent from that deployment.