From c5342e3df721d33b110b6080e85062e7d8bb2c91 Mon Sep 17 00:00:00 2001 From: Teingi Date: Sat, 5 Sep 2026 01:54:34 +0800 Subject: [PATCH 1/3] docs(rfc): define scope-owned prompt management --- .../0000_scope_owned_prompt_management.md | 688 ++++++++++++++++++ .../0000_scope_owned_prompt_management.md | 665 +++++++++++++++++ 2 files changed, 1353 insertions(+) create mode 100644 docs/en/rfcs/0000_scope_owned_prompt_management.md create mode 100644 docs/zh/rfcs/0000_scope_owned_prompt_management.md diff --git a/docs/en/rfcs/0000_scope_owned_prompt_management.md b/docs/en/rfcs/0000_scope_owned_prompt_management.md new file mode 100644 index 000000000..a7236c769 --- /dev/null +++ b/docs/en/rfcs/0000_scope_owned_prompt_management.md @@ -0,0 +1,688 @@ +- Proposal Name: `scope_owned_prompt_management` +- Start Date: 2026-09-05 +- RFC PR: [oceanbase/powercontext#0000](https://github.com/oceanbase/powercontext/pull/0000) +- Tracking Issue: [oceanbase/powercontext#1465](https://github.com/oceanbase/powercontext/issues/1465) +- Related RFCs: [RFC 0014](0014_memory_layer_design.md), [RFC 0016](0016_pydantic_ai_inference_integration.md), [RFC 0051](0051_experience_skill_artifact_families.md), [RFC 0080](0080_memory_search_reranking.md), [RFC 1396](1396_handoff_access_control.md), [RFC 1437](1437_source_artifact_rest_api.md) + +# Summary + +This RFC defines Scope-owned, versioned management for the operational prompts used by the built-in PowerContext +Runtime. A registered operational Prompt is stored as an Artifact with `family=prompt`; its stable `artifact_id` is a +Server-registered prompt key such as `memory.extract`. Scope remains the only ownership and isolation boundary. Agent +or user integrations first resolve their existing Scope binding and do not create a second Prompt binding model. +Here isolation means the durable persistence and resolution partition; authentication and authorization remain +separate Server concerns. + +A Prompt revision has one canonical content shape: `schema_version`, `mode`, `instructions`, and +`demonstrations`. A demonstration is a typed input and expected output pair. Product surfaces may present +output-producing and no-op demonstrations as positive and negative groups, but the wire and persistence contracts do +not use informal `*_examples` fields. + +The design adds no database tables and no Prompt-specific CRUD surface. It reuses the existing Artifact create, +current read, conditional replace, current-family list, and exact-revision read operations. It adds exactly two HTTP +operations: a generic Artifact revision-history list and a Prompt demonstration generator that never saves its result. +Rollback reads an immutable old revision and writes its content as a new monotonically increasing revision. + +At inference time the Runtime resolves and freezes one exact Prompt selection for the operation's Scope. Editable +instructions replace only the operation's tunable guidance. Server-owned trust rules, structured input and output +schemas, credentials, model settings, tool authority, and resource limits remain outside Prompt content. + +# Motivation + +The built-in Runtime currently has six prompt-driven operation types and seven built-in instruction variants: + +| Prompt key | Operation | Current built-in variants | +| --- | --- | --- | +| `memory.extract` | Extract durable Memory candidates from bounded evidence | `coding`, `conversation` | +| `memory.rerank` | Rerank coarse Memory search candidates | listwise reranking | +| `experience.incubate` | Propose Experience candidates from Task Outcomes | incubation | +| `experience.generate` | Generate one Experience proposal from selected evidence | explicit generation | +| `skill.generate` | Generate one managed Skill proposal | explicit generation | +| `handoff.generate` | Generate one Handoff from bounded evidence | handoff generation | + +These instructions are versioned in source code and bound to structured generators when the Runtime is composed. This +is safe and deterministic for one deployment, but it cannot meet four product requirements: + +1. different Scopes cannot define different extraction or generation guidance; +2. an operator cannot inspect the complete history of a Scope's prompt changes; +3. a previous configuration cannot be restored without changing deployment configuration; and +4. an inference trace cannot distinguish a built-in selection from a particular custom Prompt revision. + +Putting `agent_id` or `user_id` on a Prompt record would conflict with the PowerContext ownership model. Agents and +users may be mapped to different Scopes through `ScopeBinding`, but durable state is owned by the resolved Scope. A +dedicated Prompt table and a parallel set of Prompt CRUD endpoints would duplicate the revision, head, optimistic +concurrency, lineage, and Scope isolation already provided by Artifact. + +The design therefore makes operational Prompt configuration one more Scope-owned Artifact family while retaining a +small, explicit Runtime extension boundary. + +# Guide-level explanation + +## User model + +The Dashboard presents one configuration page for the current Scope. Each registered prompt key has two modes: + +- **Auto** uses the built-in instructions selected by the deployed Runtime; and +- **Custom** uses Scope-owned instructions and demonstrations. + +For `memory.extract`, a custom editor can show three sections: + +1. extraction instructions; +2. positive demonstrations whose expected output contains one or more Memory candidates; and +3. negative demonstrations whose expected output contains no candidates. + +All demonstrations are persisted in one ordered `demonstrations` array. The Prompt Definition for the operation +classifies a valid expected output as output-producing or no-op when a UI needs grouping. Classification is derived +from the typed output and is not a second persisted policy field. + +For example, the stored content is: + +```json +{ + "schema_version": "powercontext.prompt.v1", + "mode": "custom", + "instructions": "Keep durable testing preferences and verified failure lessons. Ignore transient requests.", + "demonstrations": [ + { + "input": { + "evidence": [ + { + "evidence_id": "source-1", + "text": "Before every release I run the core smoke test; it takes about 20 minutes." + } + ], + "current_entries": [] + }, + "expected_output": { + "candidates": [ + { + "kind": "preference", + "text": "The user runs the core smoke test before every release; it takes about 20 minutes.", + "evidence_ids": ["source-1"], + "intent": "add" + } + ] + } + }, + { + "input": { + "evidence": [{"evidence_id": "source-2", "text": "Please send me the code from the previous message."}], + "current_entries": [] + }, + "expected_output": {"candidates": []} + } + ] +} +``` + +`input` and `expected_output` must validate against the registered schemas for `memory.extract`. The Server does not +accept free-form demonstration labels as a substitute for a valid expected output. + +## Scope selection + +A Prompt is addressed as: + +```text +ArtifactAddress( + scope_id, + ArtifactRef(family="prompt", artifact_id=prompt_key, revision=revision), +) +``` + +The complete identity of a Prompt revision therefore includes the Scope. The same `memory.extract` key may have +different revisions in two Scopes without sharing state. + +An Agent-facing or user-facing integration performs this sequence: + +```text +Agent or user identity + | + v +existing ScopeBinding resolution + | + v +one resolved scope_id + | + v +Scope-owned Prompt head +``` + +There is no implicit parent-Scope inheritance, cross-Scope `latest`, or Agent/User fallback. A copied configuration is +a new Artifact revision in the destination Scope and has an independent future lifecycle. + +## Create and update + +The existing generic Artifact API creates a custom Prompt head: + +```http +POST /v1/scopes/project:payments/artifacts +Content-Type: application/json +``` + +```json +{ + "family": "prompt", + "prompt_key": "memory.extract", + "content": { + "schema_version": "powercontext.prompt.v1", + "mode": "custom", + "instructions": "Keep durable payment debugging decisions and verified failure lessons.", + "demonstrations": [] + } +} +``` + +The Server uses `prompt_key` as `artifact_id` and commits revision 1. Unknown or disabled prompt keys are rejected. +Callers cannot allocate arbitrary operational Prompt identities. + +Updating a Prompt uses the existing conditional replacement operation: + +```http +PUT /v1/scopes/project:payments/artifacts/prompt/memory.extract +If-Match: "revision:4" +Content-Type: application/json +``` + +The body contains the complete replacement `content`. A successful request commits revision 5; revision 4 remains +immutable and readable. Concurrent replacement with a stale ETag fails with `412 Precondition Failed`. + +Switching back to Auto is also a revisioned change: + +```json +{ + "content": { + "schema_version": "powercontext.prompt.v1", + "mode": "auto", + "instructions": "", + "demonstrations": [] + } +} +``` + +When no Prompt Artifact exists, behavior is also Auto. Persisting an Auto revision is useful when an operator wants the +history to show an explicit return to the built-in selection. + +## Generate demonstrations + +The only Prompt-specific endpoint generates editable demonstration suggestions: + +```http +POST /v1/scopes/project:payments/prompts/memory.extract/demonstrations +Content-Type: application/json +``` + +```json +{ + "instructions": "Keep durable testing preferences and verified failure lessons.", + "demonstration_count": 6 +} +``` + +The response contains exactly six schema-valid demonstrations: + +```json +{ + "prompt_key": "memory.extract", + "demonstrations": [ + { + "input": {"evidence": [], "current_entries": []}, + "expected_output": {"candidates": []} + } + ] +} +``` + +The shortened array above illustrates the item shape only. The endpoint does not create or replace an Artifact, +advance a head, or silently combine its output with current content. The caller reviews and edits the suggestions, then +uses the normal Artifact create or replace operation to save them. + +## Inspect history and roll back + +The generic revision-history endpoint lists immutable revisions newest first: + +```http +GET /v1/scopes/project:payments/artifacts/prompt/memory.extract/revisions?limit=50 +``` + +Each item includes the exact identity, content digest, and lineage identities. Full content is retrieved through the +existing exact-revision operation: + +```http +GET /v1/scopes/project:payments/artifacts/prompt/memory.extract/revisions/2 +``` + +Rollback is deliberately not a history rewrite and does not require a third endpoint: + +1. read the old exact revision; +2. read the current head and ETag; +3. replace the current head with the old revision's `content` and the current ETag; and +4. receive a new revision whose content digest matches the restored revision. + +For example, restoring revision 2 while revision 5 is current creates revision 6. Revisions 2 through 5 remain +readable. Generic request audit identifies the actor and request; equality with the restored content is visible through +the content digest. The Server never moves the head pointer backward. + +# Reference-level explanation + +## Terminology and boundaries + +| Term | Meaning | +| --- | --- | +| Operational Prompt | Scope-owned configuration for one registered built-in inference operation | +| Prompt key | Stable Server-registered operation identifier used as the Prompt Artifact ID | +| Prompt Definition | Server-owned typed contract for a prompt key | +| Prompt revision | One immutable `family=prompt` Artifact revision in one Scope | +| Built-in selection | Server-shipped default guidance and version used by Auto mode | +| Compiled prompt | Invariant instructions, selected guidance, demonstrations, and structured schema contract used for one call | + +An operational Prompt is not ordinary user input, Source evidence, a managed Skill, a model credential, or an +arbitrary system message. A managed Skill tells an Agent when and how to perform a reusable capability. An operational +Prompt tunes one fixed PowerContext inference operation without adding tools or authority. + +RFC 1396 reserved a future `family=prompt` lifecycle for reusable parameterized task templates and classified current +internal generation prompts as Server-only configuration. This RFC changes the latter boundary only: registered +operational prompts may be customized by Scope-owned Prompt Artifacts. It does not introduce the reusable task-template +lifecycle, approval state, `prompt.use`, or exact Prompt sharing described as future work in RFC 1396. + +## Prompt Definitions + +The Server registers Prompt Definitions during Runtime composition. Registration is fixed for the lifetime of the +composed Runtime. Each Definition provides: + +```python +class PromptDefinition(Protocol): + key: str + definition_version: str + input_type: type[BaseModel] + output_type: type[BaseModel] + builtin_version: str + invariant_instructions: str + default_instructions: str + + def is_noop_output(self, output: BaseModel, /) -> bool: ... +``` + +The initial registry contains exactly the six keys listed in Motivation. `memory.extract` retains the deployment's +validated `coding` or `conversation` profile as its Auto selection, so the six logical operations still account for +seven current built-in instruction variants. + +The registry, not persisted Prompt content, owns: + +- structured input and output types; +- invariant evidence, safety, secret, citation, and identity rules; +- built-in selection and built-in version; +- model and request settings; +- input and output size limits; +- demonstration no-op classification; and +- compatibility with a Prompt content schema version. + +Two Definitions cannot register the same key. An unknown key, duplicate key, incompatible Definition, or missing +built-in selection fails Runtime composition rather than being accepted as untyped configuration. + +## Prompt Artifact content + +`PromptContent` is strict and rejects unknown fields: + +```python +class PromptDemonstration(BaseModel): + input: JsonValue + expected_output: JsonValue + + +class PromptContent(BaseModel): + schema_version: Literal["powercontext.prompt.v1"] + mode: Literal["auto", "custom"] + instructions: str + demonstrations: tuple[PromptDemonstration, ...] +``` + +The following validation rules are part of the public contract: + +- all four fields are required; +- `auto` requires empty `instructions` and an empty `demonstrations` array; +- `custom` requires instructions with at least one non-whitespace character; +- instructions are trimmed NFC text and are limited to 32,768 characters; +- demonstrations preserve caller order and are limited to 50 items; +- every `input` and `expected_output` validates strictly against the Prompt Definition's registered types; +- each demonstration is limited to 64 KiB of canonical JSON; and +- the complete canonical Prompt content is limited to 256 KiB. + +Demonstrations contain desired behavior only. A negative demonstration is represented by an input whose valid +`expected_output` is the operation's no-op result, not by storing an intentionally wrong output. This prevents the +compiler from teaching the model invalid behavior and keeps the representation valid across all prompt keys. + +## Persistence and identity + +No database tables or Prompt binding records are added. Persistence reuses: + +| Existing storage | Prompt use | +| --- | --- | +| `pc_artifacts` | Immutable Prompt content by `(scope_id, prompt, prompt_key, revision)` | +| `pc_artifact_heads` | Current Prompt revision by `(scope_id, prompt, prompt_key)` | +| existing Artifact lineage tables | Exact Artifact inputs when Prompt configuration participates in generated lineage | +| existing system provenance Source | Canonical create or replace request provenance | + +The Prompt family adds a family-owned writer and registered content model to the existing Artifact repository. It does +not add Prompt-specific repositories, head logic, revision counters, or transactions. + +Prompt keys use the existing Artifact ID syntax and are additionally allow-listed by the Runtime registry. The initial +keys are globally stable wire vocabulary. Renaming a key is a compatibility change; aliases require an explicit future +migration and must not silently merge histories. + +RFC 1437 generates Artifact IDs and fixes the Create outer shape to `family` plus `content` for its four families, +with Handoff as a Server-known singleton exception. This RFC narrowly amends that rule for the new Prompt family. +`CreatePromptArtifactRequest` adds the required top-level `prompt_key`; the family writer validates it against the +fixed registry and uses it as `artifact_id`. Existing family request shapes do not change. The caller selects a known +operation but still cannot allocate an arbitrary Artifact ID. Hiding this resource selector inside Prompt content +would duplicate identity and make replacement content depend on the Create transport shape. + +## Revision semantics + +The normal Artifact guarantees apply: + +- create commits revision 1 and fails with `409 Conflict` when the key already has a head in the Scope; +- replace commits one complete next revision atomically; +- revisions are immutable positive integers and never reused; +- the head advances only after content, lineage, and derived state are durable; +- `If-Match` is required for replacement; +- an exact-revision read never resolves `latest`; and +- there is no physical delete or history rewrite in this RFC. + +Replacing a Prompt with canonically identical content is allowed and produces another revision. This preserves an +explicit operator action and keeps rollback behavior uniform. Clients may compare `content_digest` before writing when +they want to avoid a no-op revision. + +## Runtime resolution and compilation + +Prompt resolution occurs per inference operation after `scope_id` is fixed and before the first model request. It does +not occur only once at Runtime composition. + +The resolver returns one immutable value: + +```python +class ResolvedPrompt(BaseModel): + key: str + definition_version: str + selection: Literal["built_in", "artifact"] + artifact: ArtifactRef | None + selected_version: str + compiled_digest: str + instructions: str + demonstrations: tuple[PromptDemonstration, ...] +``` + +Resolution follows this algorithm: + +1. select the registered Prompt Definition for the operation; +2. read the current `family=prompt` head for `(scope_id, prompt_key)`; +3. use the built-in selection when no head exists or its mode is Auto; +4. otherwise validate the custom revision against the selected Definition; +5. compile Server-owned invariant instructions, the selected guidance, demonstrations, and the structured output + contract in a fixed order; +6. compute a canonical compiled digest; and +7. freeze the result for the complete logical operation, including retries. + +A head change during an in-flight extraction, rerank, generation, or Handoff operation affects only the next logical +operation. A Memory flush freezes its Prompt before processing its bounded Source window. Existing Memory and other +Artifacts are not automatically regenerated when a Prompt head changes. + +The compiler never concatenates custom text ahead of higher-priority invariants. If the inference adapter supports +typed example messages, it emits demonstrations through that mechanism. Otherwise it serializes them with a +Server-owned delimiter and canonical JSON encoding that user text cannot terminate or reinterpret. + +Implementations may cache compiled prompts by `(definition_version, selection identity, compiled_digest)`. They must +invalidate head lookup by the same consistency rules as Artifact current reads and must never use a head from another +Scope. + +## Provenance and observability + +Every inference span records: + +- `powercontext.prompt.key`; +- `powercontext.prompt.selection` as `built_in` or `artifact`; +- exact Artifact family, ID, and revision when custom; +- built-in and Definition versions; +- compiled prompt digest; and +- demonstration count. + +Prompt bodies and demonstration bodies are not logged or emitted as metric labels. + +When a durable generated Artifact already records Artifact inputs, its lineage includes the exact custom Prompt +`ArtifactRef` used for generation. This is configuration lineage, not factual evidence: it grants no transitive read, +does not satisfy a Source citation requirement, and does not allow Prompt instructions to support a factual claim. +Built-in selections have no synthetic ArtifactRef and remain identifiable through the recorded built-in version and +compiled digest. Ephemeral outputs such as Memory rerank decisions record the same identity only on the operation +trace. + +## HTTP contract + +The complete v1 HTTP surface is: + +| Status | Method and path | operationId | Purpose | +| --- | --- | --- | --- | +| Existing, extended | `POST /v1/scopes/{scope_id}/artifacts` | `create_artifact` | Accept `family=prompt` and a registered `prompt_key` | +| Existing, extended | `GET /v1/scopes/{scope_id}/artifacts/prompt` | `list_artifacts` | List current Prompt heads in the Scope | +| Existing, extended | `GET /v1/scopes/{scope_id}/artifacts/prompt/{prompt_key}` | `get_artifact` | Read the current Prompt head and ETag | +| Existing, extended | `PUT /v1/scopes/{scope_id}/artifacts/prompt/{prompt_key}` | `replace_artifact` | Commit a complete next Prompt revision | +| Existing, extended | `GET /v1/scopes/{scope_id}/artifacts/prompt/{prompt_key}/revisions/{revision}` | `get_artifact_revision` | Read one exact immutable revision | +| **New** | `GET /v1/scopes/{scope_id}/artifacts/{family}/{artifact_id}/revisions` | `list_artifact_revisions` | List immutable revisions for any Artifact family | +| **New** | `POST /v1/scopes/{scope_id}/prompts/{prompt_key}/demonstrations` | `generate_prompt_demonstrations` | Generate validated suggestions without saving | + +The new endpoint count is exactly two. There is no Prompt-specific list, get, create, update, rollback, publish, +validate, preview, activate, or delete endpoint. + +The additional Create union member is: + +```json +{ + "family": "prompt", + "prompt_key": "memory.extract", + "content": { + "schema_version": "powercontext.prompt.v1", + "mode": "custom", + "instructions": "Keep durable preferences.", + "demonstrations": [] + } +} +``` + +`prompt_key` is present only on Prompt Create. Replace selects the same identity from the path and continues to accept +only `content`, consistent with RFC 1437. The generated system Source is target-bound to the resulting +`artifact_id=prompt_key` and revision using the existing lineage-only mechanism. + +`list_artifact_revisions` accepts the existing bounded `limit` and opaque `cursor` pagination model. It orders by +revision descending and returns an `ArtifactRevisionPage` whose items contain `scope_id`, `family`, `artifact_id`, +`revision`, `sources`, `artifacts`, and `content_digest`; it omits content. A cursor is bound to the complete Scope, +family, Artifact ID, authorization constraint, filter, and snapshot boundary. + +`generate_prompt_demonstrations` accepts this strict request: + +```json +{ + "instructions": "non-blank custom instructions", + "demonstration_count": 6 +} +``` + +`demonstration_count` is an integer from 1 through 20. The response returns the path `prompt_key` and exactly that +many typed demonstrations. The Server validates and normalizes model output before responding. It retries only within +the configured inference request budget; an incomplete or invalid result fails the whole request and is never saved. + +## Error and concurrency semantics + +| Condition | HTTP result | +| --- | --- | +| Scope, Prompt head, or exact revision is not visible or does not exist | `404 Not Found` | +| Prompt head already exists during create | `409 Conflict` | +| Missing `If-Match` on replace | `428 Precondition Required` | +| Stale or mismatched `If-Match` | `412 Precondition Failed` | +| Unknown prompt key, invalid mode/content, schema-invalid demonstration, or unsupported family/key combination | `422 Unprocessable Entity` | +| Invalid or expired pagination cursor | existing `400` or `410` cursor semantics | +| Required inference provider is unavailable for demonstration generation | `503 Service Unavailable` | +| Provider output remains invalid within the request budget | `500 Internal Server Error` with a stable public error code | + +Errors do not echo Prompt or demonstration bodies. A caller cannot distinguish a hidden Scope or Prompt from a missing +one through response details. + +## Authorization and trust boundary + +Operational Prompt configuration changes the behavior of every compatible inference operation in a Scope. Under the +access-control model, current read, history list, and exact read require the corresponding Scope read authority; +create, replace, and demonstration generation require `scope.admin`. The legacy static bearer continues to map to its +configured administrative Principal. A family-specific writer enforces this stronger mutation rule instead of +weakening all generic Artifact writes. + +The Runtime's internal use of the current Scope Prompt is part of the already-authorized domain operation. It is not an +implicit exact-resource share. Operational Prompt revisions are not shareable through `prompt.user` in this RFC, and +the Prompt access profile advertises no grantable exact roles for them. + +Custom Prompt content is untrusted configuration. It cannot: + +- override evidence-as-data treatment, citation requirements, secret exclusion, or identity allocation rules; +- change the registered input or output schema; +- select a model, provider, credential, header, timeout, retry budget, or token budget; +- enable tools, network, filesystem access, publication, or another capability; +- resolve or reference another Scope implicitly; or +- cause generated demonstrations to be saved without an explicit Artifact write. + +The demonstration generator treats supplied instructions as data within a Server-owned meta-prompt. Generated +examples undergo the same secret and size validation as manually supplied demonstrations. + +## Non-HTTP surfaces + +The Dashboard uses the HTTP operations above. Its Agent or user selector resolves to a Scope before it reads or writes +Prompt state. The three-section editor is a presentation of one `PromptContent`; it is not another API contract. + +The Python Runtime gains the Prompt Definition registry, Scope Prompt resolver, compiler, and family writer described +in this RFC. Public Python callers may use the existing generic Artifact client for persistence. There is no new MCP +tool, CLI command, or host-specific configuration file in v1. + +## Compatibility and migration + +This design is backward compatible for Scopes that have no Prompt Artifact. They continue to use the exact built-in +selection chosen today, including the configured `memory_extraction_profile` and rerank enablement. + +Implementation requires: + +1. splitting each current instruction constant into immutable invariants and replaceable default guidance without + changing Auto-mode compiled behavior; +2. registering the six Prompt Definitions; +3. adding `prompt` to the base Artifact family contract, generated HTTP models, mapper, repository type registry, and + family management writer registry; +4. resolving Prompt state per Scope at the six inference entry points; +5. adding the two OpenAPI operations and regenerating checked-in HTTP sources; and +6. emitting the Prompt identity and digest in tracing and generated Artifact lineage where applicable. + +No SQL migration or content backfill is required. Existing deployments need no Prompt rows. An implementation must +prove that Auto mode compiles to behavior-equivalent instructions before custom mode is enabled. + +## Validation requirements + +The implementation is complete only when tests demonstrate: + +- two Scopes can hold different current revisions for the same prompt key without leakage; +- an Agent or user binding resolves to the expected Scope before Prompt lookup; +- absent and explicit Auto configurations preserve current built-in behavior; +- custom instructions and typed demonstrations reach the correct generator only; +- all six registered keys resolve, and unknown keys fail closed; +- a revision is immutable, history pagination is stable, and exact reads do not follow the head; +- stale replacement fails and a rollback creates a new revision without deleting history; +- a Prompt changed during an in-flight operation is used only by the next operation; +- generated demonstrations validate against the registered input/output schemas and are never persisted implicitly; +- custom content cannot change invariant instructions, structured schemas, model settings, tools, or authority; +- traces contain exact Prompt identity or built-in version and digest without Prompt bodies; and +- the OpenAPI contract, generated HTTP sources, unit tests, and at least one real Runtime end-to-end scenario pass. + +# Drawbacks + +Adding `prompt` to Artifact expands the meaning of Artifact lineage beyond factual evidence to include an explicitly +classified configuration input. Consumers must continue to distinguish configuration lineage from Source citations. + +Per-operation Prompt resolution adds a repository read or cache validation to inference paths. Correct caching is +Scope-sensitive and must not trade isolation for fewer reads. + +Custom guidance can reduce output quality even when it remains within hard invariants and schemas. Version history and +rollback limit the operational impact but cannot guarantee that a custom prompt is useful. The demonstration generator +also consumes inference capacity and may fail when the provider is unavailable. + +The initial design deliberately lacks drafts and approval. A `scope.admin` change becomes current immediately after +the Artifact transaction commits. + +# Rationale and alternatives + +## Reuse Artifact rather than add Prompt tables + +Artifact already provides the required Scope key, immutable revisions, current head, atomic replacement, ETag +concurrency, content digest, lineage, and exact reads. Reimplementing these semantics in `prompts`, `prompt_versions`, +and `prompt_bindings` would add synchronization and migration risk without creating a distinct domain guarantee. + +## Reuse generic CRUD rather than add Prompt CRUD + +Prompt-specific create, get, update, history, rollback, and delete endpoints would duplicate the Artifact contract. +Only history listing is missing generically, and every Artifact family benefits from it. Demonstration generation is a +real Prompt-specific action and therefore receives the only Prompt-specific endpoint. + +## Scope ownership rather than Agent or user ownership + +PowerContext state is isolated by Scope. Agent and user identity are integration and authorization concerns. Reusing +ScopeBinding keeps one durable ownership model and allows two Agents to share Prompt behavior intentionally by sharing +a Scope, or to differ by using separate Scopes. + +## Typed demonstrations rather than example strings + +Free-form positive and negative strings do not state the complete model input/output contract and are hard to validate. +Typed `input` and `expected_output` pairs can be compiled deterministically, tested, and reused across all six +operations. No-op expected output represents a negative case without teaching an invalid answer. + +## New revision rather than moving the head backward + +A backward-moving head would erase the sequence of operator decisions and create ambiguous cache and audit behavior. +Copying old content into a new conditional replacement preserves monotonic history and uses the same failure semantics +as every update. + +## Alternatives not selected + +- **Deployment-only prompt configuration:** cannot vary by Scope or provide Scope-local history. +- **A raw prompt field on every inference request:** weakens authorization, audit, caching, and reproducibility. +- **Parent-Scope inheritance:** makes effective configuration depend on a moving graph and complicates isolation. +- **A mutable cross-Scope Prompt reference:** lets one Scope change another Scope's behavior without a local revision. +- **A Prompt DSL or variables:** increases compiler and injection complexity before concrete use cases require it. +- **Draft, review, and activation workflows:** duplicate Candidate/Review concepts and are not needed for the first + administrative vertical slice. +- **A dedicated rollback endpoint:** adds an action whose safe semantics are already expressed by exact read plus + conditional replacement. + +Doing nothing leaves operational prompts fixed at deployment composition and forces customers to fork Runtime code or +run separate deployments for different prompt behavior. + +# Prior art + +PowerContext's Memory, Experience, Skill, and Handoff families already use immutable Artifact revisions and current +heads. The base Artifact REST API already exposes create, current read, conditional replacement, family list, and exact +revision read. This RFC applies those established primitives to operational Prompt configuration rather than creating +a parallel management system. + +The current source code also versions every built-in instruction string. Those versions remain useful as the identity +of Auto selections and as part of the compiled-prompt trace. + +# Unresolved questions + +There are no unresolved questions required to implement this RFC. The following subjects are explicit non-goals and +require later design work if demanded: + +- reusable parameterized task Prompt Artifacts and `prompt.use` sharing; +- draft, approval, scheduled activation, or staged rollout; +- parent-Scope inheritance or organization-level defaults; +- automatic evaluation, quality scoring, or A/B traffic allocation; +- cross-Scope import and publication workflows; and +- a general Prompt templating language. + +# Future possibilities + +A later RFC may add Prompt evaluation cases kept separate from demonstrations, compare revisions against a stable +dataset, and gate activation on explicit quality thresholds. Another may define reusable task Prompt packages and +least-privilege `prompt.use` sharing without changing the operational Prompt identity introduced here. + +Organization defaults, scheduled activation, or percentage rollout can be layered above Scope-local immutable +revisions if their ownership and precedence rules are specified explicitly. None of these extensions requires changing +the v1 rule that an inference operation freezes one exact effective Prompt selection before its first model request. diff --git a/docs/zh/rfcs/0000_scope_owned_prompt_management.md b/docs/zh/rfcs/0000_scope_owned_prompt_management.md new file mode 100644 index 000000000..113756857 --- /dev/null +++ b/docs/zh/rfcs/0000_scope_owned_prompt_management.md @@ -0,0 +1,665 @@ +- Proposal Name: `scope_owned_prompt_management` +- Start Date: 2026-09-05 +- RFC PR: [oceanbase/powercontext#0000](https://github.com/oceanbase/powercontext/pull/0000) +- Tracking Issue: [oceanbase/powercontext#1465](https://github.com/oceanbase/powercontext/issues/1465) +- Related RFCs: [RFC 0014](0014_memory_layer_design.md)、[RFC 0016](0016_pydantic_ai_inference_integration.md)、[RFC 0051](0051_experience_skill_artifact_families.md)、[RFC 0080](0080_memory_search_reranking.md)、[RFC 1396](1396_handoff_access_control.md)、[RFC 1437](1437_source_artifact_rest_api.md) + +# Summary + +本 RFC 为内置 PowerContext Runtime 使用的 operational prompt 定义 Scope-owned、可版本化的管理方案。一个已注册的 +operational Prompt 以 `family=prompt` 的 Artifact 存储;其稳定 `artifact_id` 是 Server 注册的 prompt key,例如 +`memory.extract`。Scope 仍然是唯一的 ownership 与隔离边界。Agent 或用户集成首先解析已有的 Scope binding, +不会再创建第二套 Prompt binding 模型。 +这里的隔离指持久化与解析的分区;authentication 与 authorization 仍然是独立的 Server concern。 + +Prompt revision 使用唯一的 canonical content 结构:`schema_version`、`mode`、`instructions` 和 +`demonstrations`。一条 demonstration 是一对类型化的 input 与 expected output。产品界面可以把会产生结果与 +no-op 的 demonstration 展示为正例和反例两组,但 wire contract 和 persistence contract 不使用不正式的 +`*_examples` 字段。 + +本设计不增加数据库表,也不增加 Prompt 专用 CRUD 接口。它复用已有 Artifact create、current read、conditional +replace、current-family list 与 exact-revision read 操作。正式新增的 HTTP 操作恰好只有两个:通用 Artifact +revision history list,以及只生成但绝不保存 Prompt demonstration 的接口。回滚通过读取不可变旧 revision,再将 +其 content 写成一个新的、单调递增的 revision 完成。 + +执行 inference 时,Runtime 会按该操作的 Scope 解析并冻结一个精确的 Prompt selection。可编辑 instructions 只替换 +该操作中可调节的 guidance。Server-owned trust rules、结构化 input/output schema、credential、model setting、tool +authority 与 resource limit 始终位于 Prompt content 之外。 + +# Motivation + +内置 Runtime 当前有六类由 prompt 驱动的操作,以及七个内置 instructions 变体: + +| Prompt key | Operation | 当前内置变体 | +| --- | --- | --- | +| `memory.extract` | 从有界 evidence 中抽取持久 Memory candidate | `coding`、`conversation` | +| `memory.rerank` | 对 Memory search 的 coarse candidate 重排 | listwise reranking | +| `experience.incubate` | 从 Task Outcome 提议 Experience candidate | incubation | +| `experience.generate` | 从选定 evidence 生成一个 Experience proposal | explicit generation | +| `skill.generate` | 生成一个 managed Skill proposal | explicit generation | +| `handoff.generate` | 从有界 evidence 生成一个 Handoff | handoff generation | + +这些 instructions 在源代码中有版本,并在 Runtime composition 时绑定到 structured generator。这对单个部署而言安全且 +确定,但无法满足四项产品需求: + +1. 不同 Scope 无法定义不同的抽取或生成 guidance; +2. 操作者无法查看某个 Scope 中 prompt 变更的完整历史; +3. 无法在不修改部署配置的情况下恢复旧配置; +4. inference trace 无法区分 built-in selection 与某个特定 custom Prompt revision。 + +在 Prompt 记录上增加 `agent_id` 或 `user_id` 会违背 PowerContext ownership model。Agent 与用户可以通过 +`ScopeBinding` 映射到不同 Scope,但持久状态由解析后的 Scope 拥有。专用 Prompt 表以及平行的 Prompt CRUD 接口会重复 +Artifact 已有的 revision、head、optimistic concurrency、lineage 与 Scope isolation。 + +因此,本设计把 operational Prompt configuration 作为另一种 Scope-owned Artifact family,同时保留一个小而明确的 +Runtime extension boundary。 + +# Guide-level explanation + +## 用户模型 + +Dashboard 为当前 Scope 展示一个配置页。每个已注册 prompt key 有两种 mode: + +- **Auto** 使用部署 Runtime 选定的内置 instructions; +- **Custom** 使用 Scope-owned instructions 与 demonstrations。 + +对于 `memory.extract`,custom editor 可以展示三个区域: + +1. 抽取 instructions; +2. expected output 包含一条或多条 Memory candidate 的正向 demonstrations; +3. expected output 不包含 candidate 的反向 demonstrations。 + +所有 demonstrations 都持久化在一个有序 `demonstrations` 数组中。当 UI 需要分组时,由该 operation 的 Prompt +Definition 根据合法 expected output 判断它会产生结果还是 no-op。分类从类型化 output 推导,不是第二个持久化 policy +字段。 + +例如,存储的 content 为: + +```json +{ + "schema_version": "powercontext.prompt.v1", + "mode": "custom", + "instructions": "保留持久的测试偏好和已经验证的失败经验,忽略临时请求。", + "demonstrations": [ + { + "input": { + "evidence": [ + { + "evidence_id": "source-1", + "text": "每次发布前我都会执行核心链路的冒烟测试,耗时大约 20 分钟。" + } + ], + "current_entries": [] + }, + "expected_output": { + "candidates": [ + { + "kind": "preference", + "text": "用户每次发布前都会执行核心链路的冒烟测试,耗时大约 20 分钟。", + "evidence_ids": ["source-1"], + "intent": "add" + } + ] + } + }, + { + "input": { + "evidence": [{"evidence_id": "source-2", "text": "请把上一条消息中的代码发给我。"}], + "current_entries": [] + }, + "expected_output": {"candidates": []} + } + ] +} +``` + +`input` 与 `expected_output` 必须符合 `memory.extract` 已注册的 schema。Server 不接受用自由文本 +demonstration label 代替合法 expected output。 + +## Scope selection + +Prompt 的完整地址为: + +```text +ArtifactAddress( + scope_id, + ArtifactRef(family="prompt", artifact_id=prompt_key, revision=revision), +) +``` + +因此,Prompt revision 的完整身份包含 Scope。同一个 `memory.extract` key 可以在两个 Scope 中拥有不同 revision, +且两者不共享状态。 + +面向 Agent 或用户的集成执行以下过程: + +```text +Agent 或用户身份 + | + v +解析已有 ScopeBinding + | + v +一个确定的 scope_id + | + v +Scope-owned Prompt head +``` + +不存在隐式 parent-Scope inheritance、跨 Scope `latest` 或 Agent/User fallback。复制配置会在目标 Scope 创建新的 +Artifact revision,之后拥有独立的 lifecycle。 + +## 创建与更新 + +已有通用 Artifact API 用于创建 custom Prompt head: + +```http +POST /v1/scopes/project:payments/artifacts +Content-Type: application/json +``` + +```json +{ + "family": "prompt", + "prompt_key": "memory.extract", + "content": { + "schema_version": "powercontext.prompt.v1", + "mode": "custom", + "instructions": "保留持久的支付调试决策和已经验证的失败经验。", + "demonstrations": [] + } +} +``` + +Server 使用 `prompt_key` 作为 `artifact_id` 并提交 revision 1。未知或被禁用的 prompt key 会被拒绝。调用方不能 +自行分配任意 operational Prompt identity。 + +更新 Prompt 使用已有 conditional replacement 操作: + +```http +PUT /v1/scopes/project:payments/artifacts/prompt/memory.extract +If-Match: "revision:4" +Content-Type: application/json +``` + +请求体包含完整 replacement `content`。请求成功后提交 revision 5;revision 4 仍不可变并可读取。并发更新携带 +stale ETag 时返回 `412 Precondition Failed`。 + +切回 Auto 也是一次有版本记录的变更: + +```json +{ + "content": { + "schema_version": "powercontext.prompt.v1", + "mode": "auto", + "instructions": "", + "demonstrations": [] + } +} +``` + +不存在 Prompt Artifact 时同样采用 Auto。操作者希望在历史中明确记录“恢复内置选择”时,可以持久化一个 Auto +revision。 + +## 生成 demonstrations + +唯一的 Prompt 专用接口生成可编辑的 demonstration 建议: + +```http +POST /v1/scopes/project:payments/prompts/memory.extract/demonstrations +Content-Type: application/json +``` + +```json +{ + "instructions": "保留持久的测试偏好和已经验证的失败经验。", + "demonstration_count": 6 +} +``` + +响应包含恰好六条符合 schema 的 demonstrations: + +```json +{ + "prompt_key": "memory.extract", + "demonstrations": [ + { + "input": {"evidence": [], "current_entries": []}, + "expected_output": {"candidates": []} + } + ] +} +``` + +上面的缩短数组只用于展示 item 结构。该接口不会创建或替换 Artifact,不推进 head,也不会把输出静默合并到 current +content。调用方评审并编辑这些建议后,再通过正常 Artifact create 或 replace 操作保存。 + +## 查看历史与回滚 + +通用 revision-history 接口按 revision 从新到旧列出不可变历史: + +```http +GET /v1/scopes/project:payments/artifacts/prompt/memory.extract/revisions?limit=50 +``` + +每个 item 包含精确 identity、content digest 与 lineage identity。完整 content 通过已有 exact-revision 操作读取: + +```http +GET /v1/scopes/project:payments/artifacts/prompt/memory.extract/revisions/2 +``` + +回滚不改写历史,也不需要第三个接口: + +1. 读取旧的 exact revision; +2. 读取 current head 与 ETag; +3. 使用旧 revision 的 `content` 和 current ETag 替换 current head; +4. 得到一个新 revision,其 content digest 与被恢复 revision 相同。 + +例如,revision 5 为 current 时恢复 revision 2,会创建 revision 6。Revision 2 到 5 继续可读。通用 request audit +标识 actor 与 request;与旧 content 相同可通过 content digest 确认。Server 绝不把 head pointer 向后移动。 + +# Reference-level explanation + +## 术语与边界 + +| Term | 含义 | +| --- | --- | +| Operational Prompt | 一个已注册内置 inference operation 的 Scope-owned configuration | +| Prompt key | Server 注册并用作 Prompt Artifact ID 的稳定 operation identifier | +| Prompt Definition | 一个 prompt key 的 Server-owned typed contract | +| Prompt revision | 一个 Scope 中不可变的 `family=prompt` Artifact revision | +| Built-in selection | Auto mode 使用的 Server 内置 guidance 与 version | +| Compiled prompt | 一次调用实际使用的 invariant instructions、selected guidance、demonstrations 与 structured schema contract | + +Operational Prompt 不是普通 user input、Source evidence、managed Skill、model credential,也不是任意 system message。 +Managed Skill 告诉 Agent 何时以及如何执行可复用能力;operational Prompt 只调节 PowerContext 中一个固定的 inference +operation,不增加 tool 或 authority。 + +RFC 1396 为 reusable parameterized task template 预留了未来的 `family=prompt` lifecycle,并把当前内部 generation +prompt 归类为 Server-only configuration。本 RFC 只修改后一个边界:已注册 operational prompt 可以由 Scope-owned +Prompt Artifact 自定义。本 RFC 不引入 RFC 1396 作为未来能力描述的 reusable task-template lifecycle、approval +state、`prompt.use` 或 exact Prompt sharing。 + +## Prompt Definitions + +Server 在 Runtime composition 时注册 Prompt Definition。注册结果在 composed Runtime 的整个生命周期内保持固定。每个 +Definition 提供: + +```python +class PromptDefinition(Protocol): + key: str + definition_version: str + input_type: type[BaseModel] + output_type: type[BaseModel] + builtin_version: str + invariant_instructions: str + default_instructions: str + + def is_noop_output(self, output: BaseModel, /) -> bool: ... +``` + +初始 registry 恰好包含 Motivation 中列出的六个 key。`memory.extract` 在 Auto selection 时继续使用部署校验过的 +`coding` 或 `conversation` profile,因此六类逻辑 operation 仍然对应当前七个内置 instructions 变体。 + +以下内容由 registry 而不是 persisted Prompt content 拥有: + +- structured input/output type; +- invariant evidence、safety、secret、citation 与 identity rule; +- built-in selection 与 built-in version; +- model 与 request setting; +- input/output size limit; +- demonstration no-op classification; +- 与 Prompt content schema version 的兼容规则。 + +两个 Definition 不能注册相同 key。未知 key、重复 key、不兼容 Definition 或缺少 built-in selection 时,Runtime +composition 必须失败,而不是接受 untyped configuration。 + +## Prompt Artifact content + +`PromptContent` 是 strict model,并拒绝未知字段: + +```python +class PromptDemonstration(BaseModel): + input: JsonValue + expected_output: JsonValue + + +class PromptContent(BaseModel): + schema_version: Literal["powercontext.prompt.v1"] + mode: Literal["auto", "custom"] + instructions: str + demonstrations: tuple[PromptDemonstration, ...] +``` + +以下校验规则属于 public contract: + +- 四个字段全部必填; +- `auto` 要求空 `instructions` 与空 `demonstrations` 数组; +- `custom` 要求 instructions 至少包含一个非空白字符; +- instructions 是 trimmed NFC text,最多 32,768 个字符; +- demonstrations 保持调用方顺序,最多 50 条; +- 每个 `input` 与 `expected_output` 都必须严格符合 Prompt Definition 注册的类型; +- 每条 demonstration 的 canonical JSON 最多 64 KiB; +- 完整 canonical Prompt content 最多 256 KiB。 + +Demonstration 只包含期望行为。反例通过一个 input 及其对应的 operation no-op `expected_output` 表示,而不是存储故意 +错误的 output。这样 compiler 不会教给模型无效行为,而且 representation 可以统一覆盖全部 prompt key。 + +## Persistence 与 identity + +不增加数据库表或 Prompt binding record。Persistence 复用: + +| 已有存储 | Prompt 用途 | +| --- | --- | +| `pc_artifacts` | 按 `(scope_id, prompt, prompt_key, revision)` 保存不可变 Prompt content | +| `pc_artifact_heads` | 按 `(scope_id, prompt, prompt_key)` 保存 current Prompt revision | +| 已有 Artifact lineage tables | Prompt configuration 参与 generated lineage 时保存 exact Artifact input | +| 已有 system provenance Source | 保存 canonical create 或 replace request provenance | + +Prompt family 在现有 Artifact repository 中增加 family-owned writer 和已注册 content model。它不增加 Prompt 专用 +repository、head logic、revision counter 或 transaction。 + +Prompt key 使用已有 Artifact ID 语法,并由 Runtime registry 额外执行 allow-list 校验。初始 key 是全局稳定的 wire +vocabulary。重命名 key 属于兼容性变更;alias 需要显式的未来 migration,不能静默合并 history。 + +RFC 1437 为其四种 family 生成 Artifact ID,并把 Create outer shape 固定为 `family` 加 `content`,其中 Handoff 是 +Server 已知 singleton 的例外。本 RFC 只为新增 Prompt family 局部修订该规则。`CreatePromptArtifactRequest` 增加必填 +顶层 `prompt_key`;family writer 使用固定 registry 校验它,并把它作为 `artifact_id`。已有 family 的 request shape +不变。调用方只能选择已知 operation,仍不能分配任意 Artifact ID。把该 resource selector 隐藏在 Prompt content 中会 +重复 identity,也会使 replacement content 依赖 Create transport shape。 + +## Revision semantics + +适用正常 Artifact 保证: + +- create 提交 revision 1;该 key 在 Scope 中已有 head 时返回 `409 Conflict`; +- replace 原子提交一个完整的 next revision; +- revision 是不可变正整数,绝不复用; +- 只有 content、lineage 与 derived state 全部持久后才推进 head; +- replacement 必须携带 `If-Match`; +- exact-revision read 绝不解析 `latest`; +- 本 RFC 不提供 physical delete 或 history rewrite。 + +用 canonical 相同的 content 替换 Prompt 是合法操作,并会产生另一个 revision。这样可以保留明确的 operator action, +也让 rollback 行为一致。客户端希望避免 no-op revision 时,可以在写入前比较 `content_digest`。 + +## Runtime resolution 与 compilation + +Prompt resolution 在 `scope_id` 确定后、第一次 model request 前,针对每个 inference operation 执行,而不是只在 +Runtime composition 时执行一次。 + +Resolver 返回一个不可变值: + +```python +class ResolvedPrompt(BaseModel): + key: str + definition_version: str + selection: Literal["built_in", "artifact"] + artifact: ArtifactRef | None + selected_version: str + compiled_digest: str + instructions: str + demonstrations: tuple[PromptDemonstration, ...] +``` + +解析算法如下: + +1. 为 operation 选择已注册 Prompt Definition; +2. 读取 `(scope_id, prompt_key)` 对应的 current `family=prompt` head; +3. head 不存在或 mode 为 Auto 时使用 built-in selection; +4. 否则使用所选 Definition 校验 custom revision; +5. 以固定顺序编译 Server-owned invariant instructions、selected guidance、demonstrations 与 structured output + contract; +6. 计算 canonical compiled digest; +7. 为完整逻辑 operation 冻结结果,包括其 retry。 + +在执行中的 extraction、rerank、generation 或 Handoff operation 期间修改 head,只影响下一个逻辑 operation。Memory +flush 在处理其 bounded Source window 前冻结 Prompt。Prompt head 变更不会自动重新生成已有 Memory 或其他 Artifact。 + +Compiler 绝不把 custom text 拼接到更高优先级 invariant 之前。如果 inference adapter 支持 typed example message, +则通过该机制传入 demonstrations;否则使用 Server-owned delimiter 与 canonical JSON encoding 序列化,user text 无法 +结束或重新解释该边界。 + +实现可以按 `(definition_version, selection identity, compiled_digest)` 缓存 compiled prompt。它必须按照 Artifact +current read 相同的一致性规则使 head lookup 失效,也绝不能使用另一个 Scope 的 head。 + +## Provenance 与 observability + +每个 inference span 记录: + +- `powercontext.prompt.key`; +- `powercontext.prompt.selection`,值为 `built_in` 或 `artifact`; +- custom 时的 exact Artifact family、ID 与 revision; +- built-in version 与 Definition version; +- compiled prompt digest; +- demonstration count。 + +Prompt body 与 demonstration body 不写入日志,也不作为 metric label。 + +当一个 durable generated Artifact 已经记录 Artifact input 时,其 lineage 包含 generation 使用的 exact custom Prompt +`ArtifactRef`。这是 configuration lineage,而不是 factual evidence:它不授予 transitive read,不满足 Source citation +requirement,也不能让 Prompt instructions 支撑事实声明。Built-in selection 不创建 synthetic ArtifactRef,通过记录的 +built-in version 与 compiled digest 标识。Memory rerank decision 等 ephemeral output 只在 operation trace 中记录相同 +identity。 + +## HTTP contract + +完整 v1 HTTP surface 为: + +| 状态 | Method 与 path | operationId | 用途 | +| --- | --- | --- | --- | +| 已有,扩展 | `POST /v1/scopes/{scope_id}/artifacts` | `create_artifact` | 接受 `family=prompt` 与已注册 `prompt_key` | +| 已有,扩展 | `GET /v1/scopes/{scope_id}/artifacts/prompt` | `list_artifacts` | 列出 Scope 中 current Prompt heads | +| 已有,扩展 | `GET /v1/scopes/{scope_id}/artifacts/prompt/{prompt_key}` | `get_artifact` | 读取 current Prompt head 与 ETag | +| 已有,扩展 | `PUT /v1/scopes/{scope_id}/artifacts/prompt/{prompt_key}` | `replace_artifact` | 提交完整 next Prompt revision | +| 已有,扩展 | `GET /v1/scopes/{scope_id}/artifacts/prompt/{prompt_key}/revisions/{revision}` | `get_artifact_revision` | 读取一个 exact immutable revision | +| **新增** | `GET /v1/scopes/{scope_id}/artifacts/{family}/{artifact_id}/revisions` | `list_artifact_revisions` | 列出任意 Artifact family 的 immutable revisions | +| **新增** | `POST /v1/scopes/{scope_id}/prompts/{prompt_key}/demonstrations` | `generate_prompt_demonstrations` | 生成经过校验但不保存的建议 | + +新增 endpoint 数量恰好是两个。不存在 Prompt-specific list、get、create、update、rollback、publish、validate、 +preview、activate 或 delete endpoint。 + +新增的 Create union member 为: + +```json +{ + "family": "prompt", + "prompt_key": "memory.extract", + "content": { + "schema_version": "powercontext.prompt.v1", + "mode": "custom", + "instructions": "保留持久偏好。", + "demonstrations": [] + } +} +``` + +`prompt_key` 只出现在 Prompt Create 中。Replace 从 path 选择同一个 identity,并按照 RFC 1437 继续只接受 +`content`。生成的 system Source 使用已有 lineage-only 机制,target-bound 到最终的 +`artifact_id=prompt_key` 与 revision。 + +`list_artifact_revisions` 接受已有 bounded `limit` 与 opaque `cursor` pagination model。它按 revision 降序排列, +返回 `ArtifactRevisionPage`;item 包含 `scope_id`、`family`、`artifact_id`、`revision`、`sources`、 +`artifacts` 与 `content_digest`,不包含 content。Cursor 绑定完整 Scope、family、Artifact ID、authorization +constraint、filter 与 snapshot boundary。 + +`generate_prompt_demonstrations` 接受以下 strict request: + +```json +{ + "instructions": "非空 custom instructions", + "demonstration_count": 6 +} +``` + +`demonstration_count` 是 1 到 20 的整数。响应返回 path 中的 `prompt_key` 和恰好对应数量的 typed +demonstrations。Server 在响应前校验并规范化 model output。它只会在已配置 inference request budget 内重试;不完整 +或无效结果使整个请求失败,且绝不保存。 + +## Error 与 concurrency semantics + +| 条件 | HTTP 结果 | +| --- | --- | +| Scope、Prompt head 或 exact revision 不可见或不存在 | `404 Not Found` | +| create 时 Prompt head 已存在 | `409 Conflict` | +| replace 缺少 `If-Match` | `428 Precondition Required` | +| `If-Match` stale 或不匹配 | `412 Precondition Failed` | +| 未知 prompt key、无效 mode/content、demonstration 不符合 schema,或不支持的 family/key 组合 | `422 Unprocessable Entity` | +| pagination cursor 无效或过期 | 已有 `400` 或 `410` cursor semantics | +| demonstration generation 需要的 inference provider 不可用 | `503 Service Unavailable` | +| provider output 在 request budget 内始终无效 | `500 Internal Server Error`,并返回稳定 public error code | + +Error 不回显 Prompt 或 demonstration body。调用方无法通过响应细节区分隐藏的 Scope/Prompt 与不存在的资源。 + +## Authorization 与 trust boundary + +Operational Prompt configuration 会改变一个 Scope 中所有兼容 inference operation 的行为。在 access-control model +下,current read、history list 与 exact read 需要相应 Scope read authority;create、replace 与 demonstration +generation 需要 `scope.admin`。Legacy static bearer 继续映射到配置的 administrative Principal。Family-specific writer +执行这个更严格的 mutation rule,不会削弱所有 generic Artifact write。 + +Runtime 内部使用 current Scope Prompt 是已授权 domain operation 的组成部分,不是隐式 exact-resource share。本 RFC +不允许通过 `prompt.user` 共享 operational Prompt revision,其 Prompt access profile 不声明可授予的 exact role。 + +Custom Prompt content 是不可信 configuration。它不能: + +- 覆盖 evidence-as-data、citation requirement、secret exclusion 或 identity allocation rule; +- 修改已注册 input/output schema; +- 选择 model、provider、credential、header、timeout、retry budget 或 token budget; +- 启用 tool、network、filesystem access、publication 或其他 capability; +- 隐式解析或引用另一个 Scope; +- 在没有显式 Artifact write 的情况下保存生成的 demonstrations。 + +Demonstration generator 把 supplied instructions 当作 Server-owned meta-prompt 中的数据。生成案例接受与手工案例相同的 +secret 与 size validation。 + +## 非 HTTP surface + +Dashboard 使用上述 HTTP 操作。其 Agent 或用户 selector 会先解析为 Scope,再读写 Prompt state。三段式 editor 只是 +一个 `PromptContent` 的展示方式,不是另一套 API contract。 + +Python Runtime 增加本 RFC 所述 Prompt Definition registry、Scope Prompt resolver、compiler 与 family writer。Public +Python caller 可以使用已有 generic Artifact client 做 persistence。V1 不增加 MCP tool、CLI command 或 host-specific +configuration file。 + +## Compatibility 与 migration + +对没有 Prompt Artifact 的 Scope,本设计保持向后兼容。它们继续使用与当前完全相同的 built-in selection,包括已配置 +的 `memory_extraction_profile` 和 rerank enablement。 + +实现需要: + +1. 把当前每个 instruction constant 拆为 immutable invariant 与 replaceable default guidance,同时不改变 Auto mode + 的 compiled behavior; +2. 注册六个 Prompt Definition; +3. 在 base Artifact family contract、generated HTTP model、mapper、repository type registry 与 family management + writer registry 中增加 `prompt`; +4. 在六个 inference entry point 中按 Scope 解析 Prompt state; +5. 增加两个 OpenAPI operation 并重新生成 checked-in HTTP sources; +6. 在 tracing,以及适用时 generated Artifact lineage 中发出 Prompt identity 与 digest。 + +不需要 SQL migration 或 content backfill。已有部署不需要 Prompt row。在启用 custom mode 前,实现必须证明 Auto mode +编译出的 instructions 在行为上与当前实现等价。 + +## Validation requirements + +只有测试证明以下内容后,实现才算完整: + +- 两个 Scope 可以为同一个 prompt key 保存不同 current revision,且不会泄漏; +- Agent 或 user binding 在 Prompt lookup 前解析到预期 Scope; +- 缺省和显式 Auto configuration 都保持当前 built-in behavior; +- custom instructions 与 typed demonstrations 只进入正确 generator; +- 六个注册 key 均可解析,未知 key fail closed; +- revision 不可变、history pagination 稳定、exact read 不跟随 head; +- stale replacement 失败,rollback 创建新 revision 且不删除历史; +- operation 执行期间修改 Prompt,只被下一次 operation 使用; +- generated demonstrations 符合已注册 input/output schema,且绝不隐式持久化; +- custom content 无法修改 invariant instruction、structured schema、model setting、tool 或 authority; +- trace 包含 exact Prompt identity 或 built-in version 与 digest,但不包含 Prompt body; +- OpenAPI contract、generated HTTP sources、unit tests 与至少一个真实 Runtime end-to-end scenario 通过。 + +# Drawbacks + +将 `prompt` 加入 Artifact,会把 Artifact lineage 的含义从 factual evidence 扩展到显式分类的 configuration input。 +Consumer 必须继续区分 configuration lineage 与 Source citation。 + +按 operation 解析 Prompt,会在 inference path 增加 repository read 或 cache validation。正确缓存必须 Scope-sensitive, +不能为了减少读取牺牲隔离。 + +Custom guidance 即使没有突破 hard invariant 与 schema,仍可能降低 output quality。Version history 与 rollback 可以限制 +运营影响,但不能保证 custom prompt 有效。Demonstration generator 还会消耗 inference capacity,并可能在 provider +不可用时失败。 + +初始设计有意不提供 draft 与 approval。`scope.admin` 的变更在 Artifact transaction 提交后立即成为 current。 + +# Rationale and alternatives + +## 复用 Artifact,而不是增加 Prompt 表 + +Artifact 已经提供所需的 Scope key、immutable revision、current head、atomic replacement、ETag concurrency、content +digest、lineage 与 exact read。在 `prompts`、`prompt_versions` 和 `prompt_bindings` 中重新实现这些语义只会增加同步与 +migration 风险,不会产生新的 domain guarantee。 + +## 复用 generic CRUD,而不是增加 Prompt CRUD + +Prompt-specific create、get、update、history、rollback 与 delete endpoint 会重复 Artifact contract。当前只缺少通用 +history listing,而且每个 Artifact family 都能从中受益。Demonstration generation 是真正的 Prompt-specific action, +因此获得唯一的 Prompt-specific endpoint。 + +## Scope ownership,而不是 Agent 或 user ownership + +PowerContext state 按 Scope 隔离。Agent 与 user identity 属于 integration 与 authorization concern。复用 +ScopeBinding 可以保持唯一持久 ownership model:两个 Agent 可以通过共享 Scope 有意共享 Prompt 行为,也可以通过不同 +Scope 获得不同行为。 + +## Typed demonstrations,而不是 example strings + +自由文本正反例无法表达完整 model input/output contract,也难以校验。类型化的 `input` 与 `expected_output` 对可以 +确定地编译、测试,并统一覆盖六个 operation。No-op expected output 表示反例,无需教给模型错误答案。 + +## 创建新 revision,而不是把 head 向后移动 + +向后移动 head 会抹掉 operator decision sequence,并造成含糊的 cache 与 audit 行为。把旧 content 复制到一次新的 +conditional replacement 中,可以保留单调历史,并使用与所有更新相同的 failure semantics。 + +## 未选择的替代方案 + +- **仅使用 deployment prompt configuration:** 无法按 Scope 变化,也没有 Scope-local history。 +- **在每个 inference request 上携带 raw prompt 字段:** 削弱 authorization、audit、caching 与 reproducibility。 +- **Parent-Scope inheritance:** 使 effective configuration 依赖变化中的 graph,并增加 isolation 复杂度。 +- **Mutable cross-Scope Prompt reference:** 允许一个 Scope 在没有本地 revision 的情况下改变另一个 Scope 的行为。 +- **Prompt DSL 或 variables:** 在出现具体需求前增加 compiler 与 injection 复杂度。 +- **Draft、review 与 activation workflow:** 重复 Candidate/Review 概念,且首个 administrative vertical slice 不需要。 +- **专用 rollback endpoint:** exact read 加 conditional replacement 已经表达其安全语义。 + +如果不做此设计,operational prompt 仍固定在 deployment composition 阶段,客户只能 fork Runtime code 或运行多个部署 +来获得不同 prompt 行为。 + +# Prior art + +PowerContext 的 Memory、Experience、Skill 与 Handoff family 已经使用 immutable Artifact revision 与 current head。 +Base Artifact REST API 已经暴露 create、current read、conditional replacement、family list 与 exact revision read。 +本 RFC 把这些成熟 primitive 用于 operational Prompt configuration,不创建平行管理系统。 + +当前源代码也为每个 built-in instruction string 提供版本。这些 version 会继续作为 Auto selection 的 identity,并成为 +compiled-prompt trace 的一部分。 + +# Unresolved questions + +实现本 RFC 不需要解决其他未决问题。以下内容是明确 non-goal;如果未来有需求,需要另行设计: + +- reusable parameterized task Prompt Artifact 与 `prompt.use` sharing; +- draft、approval、scheduled activation 或 staged rollout; +- parent-Scope inheritance 或 organization-level default; +- automatic evaluation、quality scoring 或 A/B traffic allocation; +- cross-Scope import 与 publication workflow; +- 通用 Prompt templating language。 + +# Future possibilities + +后续 RFC 可以增加与 demonstrations 分开管理的 Prompt evaluation case,用稳定 dataset 对比 revision,并以显式质量阈值 +控制 activation。另一份 RFC 可以定义 reusable task Prompt package 与 least-privilege `prompt.use` sharing,而不改变 +本 RFC 引入的 operational Prompt identity。 + +如果明确规定 ownership 与 precedence,organization default、scheduled activation 或 percentage rollout 可以构建在 +Scope-local immutable revision 之上。这些扩展都不需要改变 v1 规则:一个 inference operation 必须在第一次 model +request 前冻结一个精确的 effective Prompt selection。 From b70b413e59d3f66fe64beba8c279808b100d584c Mon Sep 17 00:00:00 2001 From: Teingi Date: Sat, 5 Sep 2026 01:59:17 +0800 Subject: [PATCH 2/3] docs(rfc): number scope-owned prompt management RFC --- ...ompt_management.md => 1468_scope_owned_prompt_management.md} | 2 +- ...ompt_management.md => 1468_scope_owned_prompt_management.md} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename docs/en/rfcs/{0000_scope_owned_prompt_management.md => 1468_scope_owned_prompt_management.md} (99%) rename docs/zh/rfcs/{0000_scope_owned_prompt_management.md => 1468_scope_owned_prompt_management.md} (99%) diff --git a/docs/en/rfcs/0000_scope_owned_prompt_management.md b/docs/en/rfcs/1468_scope_owned_prompt_management.md similarity index 99% rename from docs/en/rfcs/0000_scope_owned_prompt_management.md rename to docs/en/rfcs/1468_scope_owned_prompt_management.md index a7236c769..2cf646983 100644 --- a/docs/en/rfcs/0000_scope_owned_prompt_management.md +++ b/docs/en/rfcs/1468_scope_owned_prompt_management.md @@ -1,6 +1,6 @@ - Proposal Name: `scope_owned_prompt_management` - Start Date: 2026-09-05 -- RFC PR: [oceanbase/powercontext#0000](https://github.com/oceanbase/powercontext/pull/0000) +- RFC PR: [oceanbase/powercontext#1468](https://github.com/oceanbase/powercontext/pull/1468) - Tracking Issue: [oceanbase/powercontext#1465](https://github.com/oceanbase/powercontext/issues/1465) - Related RFCs: [RFC 0014](0014_memory_layer_design.md), [RFC 0016](0016_pydantic_ai_inference_integration.md), [RFC 0051](0051_experience_skill_artifact_families.md), [RFC 0080](0080_memory_search_reranking.md), [RFC 1396](1396_handoff_access_control.md), [RFC 1437](1437_source_artifact_rest_api.md) diff --git a/docs/zh/rfcs/0000_scope_owned_prompt_management.md b/docs/zh/rfcs/1468_scope_owned_prompt_management.md similarity index 99% rename from docs/zh/rfcs/0000_scope_owned_prompt_management.md rename to docs/zh/rfcs/1468_scope_owned_prompt_management.md index 113756857..4ea432dac 100644 --- a/docs/zh/rfcs/0000_scope_owned_prompt_management.md +++ b/docs/zh/rfcs/1468_scope_owned_prompt_management.md @@ -1,6 +1,6 @@ - Proposal Name: `scope_owned_prompt_management` - Start Date: 2026-09-05 -- RFC PR: [oceanbase/powercontext#0000](https://github.com/oceanbase/powercontext/pull/0000) +- RFC PR: [oceanbase/powercontext#1468](https://github.com/oceanbase/powercontext/pull/1468) - Tracking Issue: [oceanbase/powercontext#1465](https://github.com/oceanbase/powercontext/issues/1465) - Related RFCs: [RFC 0014](0014_memory_layer_design.md)、[RFC 0016](0016_pydantic_ai_inference_integration.md)、[RFC 0051](0051_experience_skill_artifact_families.md)、[RFC 0080](0080_memory_search_reranking.md)、[RFC 1396](1396_handoff_access_control.md)、[RFC 1437](1437_source_artifact_rest_api.md) From ce3ea94924b2fe1d9e668a9a2cf7d951388cfc0d Mon Sep 17 00:00:00 2001 From: Teingi Date: Sat, 5 Sep 2026 10:24:39 +0800 Subject: [PATCH 3/3] docs(rfc): clarify prompt compatibility and provenance --- .../1468_scope_owned_prompt_management.md | 243 +++++++++++++++--- .../1468_scope_owned_prompt_management.md | 216 +++++++++++++--- 2 files changed, 377 insertions(+), 82 deletions(-) diff --git a/docs/en/rfcs/1468_scope_owned_prompt_management.md b/docs/en/rfcs/1468_scope_owned_prompt_management.md index 2cf646983..ea8cc017e 100644 --- a/docs/en/rfcs/1468_scope_owned_prompt_management.md +++ b/docs/en/rfcs/1468_scope_owned_prompt_management.md @@ -60,11 +60,14 @@ small, explicit Runtime extension boundary. ## User model -The Dashboard presents one configuration page for the current Scope. Each registered prompt key has two modes: +The Dashboard presents one configuration page for the current Scope. Each supported built-in prompt key has two modes: - **Auto** uses the built-in instructions selected by the deployed Runtime; and - **Custom** uses Scope-owned instructions and demonstrations. +Disabled operations and injected components that do not support customization are shown as unavailable, with a reason. +They are not presented as configurable Auto/Custom operations merely because their keys are registered. + For `memory.extract`, a custom editor can show three sections: 1. extraction instructions; @@ -88,7 +91,8 @@ For example, the stored content is: "evidence": [ { "evidence_id": "source-1", - "text": "Before every release I run the core smoke test; it takes about 20 minutes." + "evidence_type": "source", + "content": "Before every release I run the core smoke test; it takes about 20 minutes." } ], "current_entries": [] @@ -106,7 +110,13 @@ For example, the stored content is: }, { "input": { - "evidence": [{"evidence_id": "source-2", "text": "Please send me the code from the previous message."}], + "evidence": [ + { + "evidence_id": "source-2", + "evidence_type": "source", + "content": "Please send me the code from the previous message." + } + ], "current_entries": [] }, "expected_output": {"candidates": []} @@ -172,8 +182,10 @@ Content-Type: application/json } ``` -The Server uses `prompt_key` as `artifact_id` and commits revision 1. Unknown or disabled prompt keys are rejected. -Callers cannot allocate arbitrary operational Prompt identities. +The Server uses `prompt_key` as `artifact_id` and commits revision 1. Unknown keys are rejected; Custom writes also +require that the effective component supports customization. Auto writes remain available for registered keys so an +operator can clear a custom selection even when its operation is unavailable. Callers cannot allocate arbitrary +operational Prompt identities. Updating a Prompt uses the existing conditional replacement operation: @@ -214,11 +226,11 @@ Content-Type: application/json ```json { "instructions": "Keep durable testing preferences and verified failure lessons.", - "demonstration_count": 6 + "demonstration_count": 1 } ``` -The response contains exactly six schema-valid demonstrations: +The response contains exactly one schema-valid demonstration: ```json { @@ -232,9 +244,9 @@ The response contains exactly six schema-valid demonstrations: } ``` -The shortened array above illustrates the item shape only. The endpoint does not create or replace an Artifact, -advance a head, or silently combine its output with current content. The caller reviews and edits the suggestions, then -uses the normal Artifact create or replace operation to save them. +The endpoint does not create or replace an Artifact, advance a head, or silently combine its output with current +content. The caller reviews and edits the suggestions, then uses the normal Artifact create or replace operation to +save them. ## Inspect history and roll back @@ -316,8 +328,37 @@ The registry, not persisted Prompt content, owns: - demonstration no-op classification; and - compatibility with a Prompt content schema version. -Two Definitions cannot register the same key. An unknown key, duplicate key, incompatible Definition, or missing -built-in selection fails Runtime composition rather than being accepted as untyped configuration. +Two Definitions cannot register the same key. A duplicate key, internally inconsistent Definition, or missing built-in +selection fails Runtime composition rather than being accepted as untyped configuration. Registration describes a +known contract, not whether the effective component supports customization; that distinction is reported through +capabilities below. + +### Definition compatibility + +`schema_version` identifies the persisted Prompt envelope. `definition_version` identifies the deployed operation +contract; `builtin_version` identifies its default guidance. These identifiers are not interchangeable. V1 keeps the +four-field Prompt content shape and adopts the following compatibility policy instead of storing another version +selector on every revision: + +- Within one prompt key and content schema version, a Definition update must continue accepting every previously + valid demonstration and preserve the meaning of its input and expected output. Changes to default guidance need not + change the typed contract. A breaking typed or semantic change under the same key is not a supported v1 upgrade. +- Historical reads return immutable stored content and its original digest without revalidating demonstrations + against the active Definition. They remain available when the operation is disabled, injected, or incompatible. + Validation on resolution must not rewrite historical content or insert new defaults into its stored representation. +- Writes, including rollback by copying old content, validate against the deployed compatible Definition. If an + incompatible payload is encountered, return `422` with `prompt_definition_incompatible`, leaving the head unchanged. + Resolving an incompatible existing custom head fails the affected operation with `503` and the same code; it must + not silently select Auto. Management reads and an explicit replacement with Auto remain available for recovery. +- Before upgrading, validate existing custom heads against the target Definitions. An incompatible head must be + explicitly migrated as a new revision or reset to Auto before the affected operation is enabled. Migration can + change content and digest; it is not an exact-content rollback. + +All workers serving one deployment must use the same Definition, built-in profile/version, and compiler versions. +Mixed-version workers are outside v1 support; deployment tooling must drain old workers before switching versions. +Version identifiers and compiled digests aid diagnosis but do not themselves coordinate a rolling upgrade. Restoring +Prompt content restores custom guidance, not the old model, compiler, or deployment, and does not promise identical +inference output. ## Prompt Artifact content @@ -347,9 +388,10 @@ The following validation rules are part of the public contract: - each demonstration is limited to 64 KiB of canonical JSON; and - the complete canonical Prompt content is limited to 256 KiB. -Demonstrations contain desired behavior only. A negative demonstration is represented by an input whose valid -`expected_output` is the operation's no-op result, not by storing an intentionally wrong output. This prevents the -compiler from teaching the model invalid behavior and keeps the representation valid across all prompt keys. +Demonstrations contain desired behavior only. Where an operation has a valid no-op result, a negative demonstration +uses that result as its `expected_output`, not an intentionally wrong output. An operation without a valid no-op uses +ordinary input/output demonstrations; its classifier never returns true and the UI does not invent an empty result +or a negative group. `memory.extract` supports both groups through non-empty and empty `candidates`. ## Persistence and identity @@ -372,9 +414,9 @@ migration and must not silently merge histories. RFC 1437 generates Artifact IDs and fixes the Create outer shape to `family` plus `content` for its four families, with Handoff as a Server-known singleton exception. This RFC narrowly amends that rule for the new Prompt family. `CreatePromptArtifactRequest` adds the required top-level `prompt_key`; the family writer validates it against the -fixed registry and uses it as `artifact_id`. Existing family request shapes do not change. The caller selects a known -operation but still cannot allocate an arbitrary Artifact ID. Hiding this resource selector inside Prompt content -would duplicate identity and make replacement content depend on the Create transport shape. +fixed registry and uses it as `artifact_id`. Existing generic Create family request shapes do not change. The caller +selects a known operation but still cannot allocate an arbitrary Artifact ID. Hiding this resource selector inside +Prompt content would duplicate identity and make replacement content depend on the Create transport shape. ## Revision semantics @@ -411,7 +453,8 @@ class ResolvedPrompt(BaseModel): demonstrations: tuple[PromptDemonstration, ...] ``` -Resolution follows this algorithm: +Supported built-in components use the following algorithm. Injected or disabled components follow the availability +rules below before dispatch; registration alone does not route a resolved Prompt into them. 1. select the registered Prompt Definition for the operation; 2. read the current `family=prompt` head for `(scope_id, prompt_key)`; @@ -426,9 +469,10 @@ A head change during an in-flight extraction, rerank, generation, or Handoff ope operation. A Memory flush freezes its Prompt before processing its bounded Source window. Existing Memory and other Artifacts are not automatically regenerated when a Prompt head changes. -The compiler never concatenates custom text ahead of higher-priority invariants. If the inference adapter supports -typed example messages, it emits demonstrations through that mechanism. Otherwise it serializes them with a -Server-owned delimiter and canonical JSON encoding that user text cannot terminate or reinterpret. +The compiler preserves Server-owned instructions and their message priority. If the inference adapter supports typed +example messages, it emits demonstrations through that mechanism. Otherwise it uses canonical JSON and escaped, +Server-owned delimiters. Escaping protects the serialized boundaries; neither delimiters nor message priority prove +that the model will obey semantic instructions. Code-enforced checks remain necessary after inference. Implementations may cache compiled prompts by `(definition_version, selection identity, compiled_digest)`. They must invalidate head lookup by the same consistency rules as Artifact current reads and must never use a head from another @@ -436,7 +480,7 @@ Scope. ## Provenance and observability -Every inference span records: +Every managed inference span records: - `powercontext.prompt.key`; - `powercontext.prompt.selection` as `built_in` or `artifact`; @@ -454,12 +498,52 @@ Built-in selections have no synthetic ArtifactRef and remain identifiable throug compiled digest. Ephemeral outputs such as Memory rerank decisions record the same identity only on the operation trace. +### Handoff generation through finalize and commit + +Handoff prepare, finalize, and commit can be separate requests. The Prompt selected by prepare must travel with the +draft; finalize and commit must never reconstruct generation provenance from the then-current Prompt head. + +The existing Handoff transport values gain an optional `generation` envelope, separate from editable draft text. It +contains a Server-authenticated receipt binding the Scope, exact custom Prompt reference or built-in selection, +Definition and built-in versions, compiled digest, and digest of the original generated draft. The Server issues the +receipt after successful generation; callers cannot establish verified provenance by supplying a raw Prompt reference. +A signed, purpose-bound receipt needs no new table. Receipt verification must work across deployment workers and +remain valid for outstanding drafts during key rotation. Private signing material is never exposed to clients. + +The lifecycle is: + +1. Prepare freezes the selection and returns the generated draft with its `generation` envelope. +2. Finalize verifies the receipt and Scope, validates the edited draft normally, and carries the envelope into + `PreparedHandoff`. Commit verifies it again rather than trusting a client-supplied prepared value. +3. Commit compares the final editable fields with the original draft digest. Persisted generation metadata records + `unchanged` or `edited`. An edited Handoff retains its generation origin, but does not claim that the model produced + the final text. Existing evidence, citation, authorization, and optimistic-concurrency checks still apply. +4. The verified exact custom Prompt reference becomes configuration lineage. Generation metadata is stored in the + existing Handoff Artifact JSON and participates in its canonical content digest and no-op comparison. Receipt bytes + and signing keys are not stored in the Artifact. A different generation origin cannot inherit an old origin merely + because the visible text is identical. + +These checks also apply to generic Handoff writes and internal activation paths. Persisted generation metadata is +Server-derived: copying metadata from a read response is not a substitute for a valid receipt. Without a receipt, +writers reject supplied verified metadata and accept only explicitly unattributed content. + +For example, a draft generated with Prompt revision 2 still records revision 2 when revision 3 is current at commit. +An intervening Prompt update does not invalidate that draft. An absent envelope means manual/unattributed content, +with no inferred Prompt lineage; a present invalid, tampered, or cross-Scope envelope is rejected with `422`, not +silently downgraded. Dropping the envelope loses verified attribution and never grants additional authority. + +This requires coordinated changes to the strict Handoff Python models, existing HTTP request/response schemas, +mappers, and clients, including readers of persisted Handoff generation metadata. Old payloads without the field stay +valid. Older clients that do not preserve it cannot offer verified generation provenance. This is an extension of +existing Handoff contracts and JSON storage, not a new endpoint or database table. + ## HTTP contract The complete v1 HTTP surface is: | Status | Method and path | operationId | Purpose | | --- | --- | --- | --- | +| Existing, extended | `GET /v1/capabilities` | `get_capabilities` | Report effective per-key customization support and deployed built-in identity | | Existing, extended | `POST /v1/scopes/{scope_id}/artifacts` | `create_artifact` | Accept `family=prompt` and a registered `prompt_key` | | Existing, extended | `GET /v1/scopes/{scope_id}/artifacts/prompt` | `list_artifacts` | List current Prompt heads in the Scope | | Existing, extended | `GET /v1/scopes/{scope_id}/artifacts/prompt/{prompt_key}` | `get_artifact` | Read the current Prompt head and ETag | @@ -471,6 +555,9 @@ The complete v1 HTTP surface is: The new endpoint count is exactly two. There is no Prompt-specific list, get, create, update, rollback, publish, validate, preview, activate, or delete endpoint. +Existing Handoff prepare/finalize/commit payloads also carry `generation` as specified above; their routes and +operation IDs remain unchanged. + The additional Create union member is: ```json @@ -517,6 +604,10 @@ the configured inference request budget; an incomplete or invalid result fails t | Missing `If-Match` on replace | `428 Precondition Required` | | Stale or mismatched `If-Match` | `412 Precondition Failed` | | Unknown prompt key, invalid mode/content, schema-invalid demonstration, or unsupported family/key combination | `422 Unprocessable Entity` | +| Custom write or suggestion generation targets a disabled or unsupported component | `422` with `prompt_customization_unavailable` | +| Historical content is incompatible with the deployed Definition during replacement | `422` with `prompt_definition_incompatible`; no head change | +| Existing custom head cannot be honored by the effective component or Definition | `503` with `prompt_customization_unavailable` or `prompt_definition_incompatible` | +| Handoff generation receipt is invalid or belongs to another Scope | `422` with `invalid_handoff_generation` | | Invalid or expired pagination cursor | existing `400` or `410` cursor semantics | | Required inference provider is unavailable for demonstration generation | `503 Service Unavailable` | | Provider output remains invalid within the request budget | `500 Internal Server Error` with a stable public error code | @@ -529,38 +620,93 @@ one through response details. Operational Prompt configuration changes the behavior of every compatible inference operation in a Scope. Under the access-control model, current read, history list, and exact read require the corresponding Scope read authority; create, replace, and demonstration generation require `scope.admin`. The legacy static bearer continues to map to its -configured administrative Principal. A family-specific writer enforces this stronger mutation rule instead of -weakening all generic Artifact writes. +configured administrative Principal. The Server's policy-enforcement point checks the Prompt family mutation rule +before dispatching to its writer; other generic Artifact writes keep their own authorization rules. The Runtime's internal use of the current Scope Prompt is part of the already-authorized domain operation. It is not an implicit exact-resource share. Operational Prompt revisions are not shareable through `prompt.user` in this RFC, and the Prompt access profile advertises no grantable exact roles for them. -Custom Prompt content is untrusted configuration. It cannot: - -- override evidence-as-data treatment, citation requirements, secret exclusion, or identity allocation rules; -- change the registered input or output schema; -- select a model, provider, credential, header, timeout, retry budget, or token budget; -- enable tools, network, filesystem access, publication, or another capability; -- resolve or reference another Scope implicitly; or -- cause generated demonstrations to be saved without an explicit Artifact write. +Custom Prompt content is untrusted configuration. The enforcement boundary is explicit: -The demonstration generator treats supplied instructions as data within a Server-owned meta-prompt. Generated -examples undergo the same secret and size validation as manually supplied demonstrations. +| Code-enforced constraint | Required mechanism | +| --- | --- | +| Prompt cannot replace the registered schema or operation contract | Strict request models and registered input/output validation | +| Prompt cannot change credentials, model settings, resource budgets, tools, or authority | Those values are absent from Prompt content and are selected only by Server composition and authorization | +| Prompt cannot allocate identity, escape Scope, or cite arbitrary missing evidence | Existing family writers, Scope-bound resolution, and operation-specific reference/identity checks | +| Suggestions cannot save themselves | Generator has no Artifact write action; persistence requires a separately authorized write | +| Prompt cannot remove Server instructions from the compiled request | Server-owned compiler controls message roles and instruction assembly | + +Evidence-as-data treatment, truthful citation support, and exclusion of secrets from arbitrary natural-language output +also depend on model behavior. Keeping these instructions immutable does not guarantee compliance. In particular, +Memory output validation checks structure and referenced evidence; its free-form candidate text does not provide a +general semantic secret detector. V1 makes secret exclusion best-effort and promises no universal secret filter for +instructions, demonstrations, or generated content. Credentials must stay outside model inputs, and operators must +not put secrets in Prompt content. Any deployment-specific filtering must document its coverage and false negatives. + +The demonstration generator places supplied instructions as data within a Server-owned meta-prompt and applies the +same typed validation and size limits as manual demonstrations. Adversarial tests must separately exercise attempted +schema/authority changes, instruction injection, and synthetic secret-like evidence. Report both rejected structural +violations and semantic leakage observed with the actual model; schema acceptance alone is not evidence of a model +bypass, and passing a finite test set is not proof of universal secret exclusion. ## Non-HTTP surfaces The Dashboard uses the HTTP operations above. Its Agent or user selector resolves to a Scope before it reads or writes Prompt state. The three-section editor is a presentation of one `PromptContent`; it is not another API contract. +The Dashboard bundles labels and editor metadata for the six fixed keys. It obtains deployment-specific support from +the existing `GET /v1/capabilities` response, extended with a `prompts` map. Every registered key is present, even when +there is no Prompt Artifact. Each entry contains: + +| Field | Contract | +| --- | --- | +| `status` | `supported`, `disabled`, or `unsupported`, based on the effective composed component | +| `reason` | `null` when supported; a stable reason such as `operation_disabled`, `provider_not_configured`, or `injected_component` otherwise | +| `definition_version` | Deployed registered contract version | +| `builtin_version` | Deployed built-in guidance version; not a claim that an injected component uses it | +| `builtin_profile` | `coding` or `conversation` for Memory extraction, otherwise `null` | + +`supported` means both Custom selection and demonstration generation are wired to the built-in implementation; a +transient provider failure still returns `503` rather than changing the configuration contract. This response reports +no Scope-owned content, heads, credentials, or model secrets. No separate Prompt discovery endpoint is needed. + +For a supported key, the Dashboard combines that metadata with the Scope's generic Prompt Artifact list: missing head +or an Auto head means the reported built-in selection; Custom means the stored revision. For disabled/unsupported +keys, it disables Custom and suggestion actions, displays the reason, and keeps saved content/history readable. +An existing custom head that cannot run is shown as blocked, with an explicit return-to-Auto action. An injected +component is labeled externally managed, not falsely described as using the reported built-in profile. + The Python Runtime gains the Prompt Definition registry, Scope Prompt resolver, compiler, and family writer described in this RFC. Public Python callers may use the existing generic Artifact client for persistence. There is no new MCP tool, CLI command, or host-specific configuration file in v1. +### Injected Runtime components + +`open_builtin_runtime` already accepts injected Memory/Experience candidate pipelines, Experience/Skill generators, +Handoff pipelines, and Memory rerankers. Their existing protocols do not accept `ResolvedPrompt`. V1 does not change +those protocols or require third-party implementations to participate in managed Prompt customization. + +- A key is `supported` only when the effective built-in component receives the Scope-resolved selection. The presence + of a configured model alone is insufficient. Injecting only a low-level inference provider into a compatible + built-in pipeline does not make that pipeline unsupported. +- A replaced pipeline/generator/reranker is `unsupported` with reason `injected_component`; an unconfigured operation + is `disabled`. Custom create/replace and demonstration generation reject either status. No request may save a + Custom selection successfully and then silently ignore it. +- History and current reads remain available. Writing Auto is allowed for any registered key. With an absent or Auto + head, injected components retain their existing behavior; they do not emit a fabricated built-in Prompt selection. +- If a deployment switches a previously managed key to an injected or disabled component while a Scope has a Custom + head, invocation fails with `prompt_customization_unavailable` before calling that component. The operator must + explicitly select Auto or restore a compatible built-in component. No history is deleted or rewritten. + +An opt-in third-party Prompt participation protocol is outside v1; advertising unsupported status is sufficient. + ## Compatibility and migration -This design is backward compatible for Scopes that have no Prompt Artifact. They continue to use the exact built-in -selection chosen today, including the configured `memory_extraction_profile` and rerank enablement. +For Scopes with no Prompt Artifact, built-in components retain their current selection, including the configured +`memory_extraction_profile` and rerank enablement. Injected components retain their existing behavior. Transport and +Handoff content model extensions still require coordinated schema/client updates; this is not a claim that old strict +readers accept new response fields. Implementation requires: @@ -569,9 +715,11 @@ Implementation requires: 2. registering the six Prompt Definitions; 3. adding `prompt` to the base Artifact family contract, generated HTTP models, mapper, repository type registry, and family management writer registry; -4. resolving Prompt state per Scope at the six inference entry points; -5. adding the two OpenAPI operations and regenerating checked-in HTTP sources; and -6. emitting the Prompt identity and digest in tracing and generated Artifact lineage where applicable. +4. resolving Prompt state per Scope at the six inference entry points and checking effective component support; +5. adding the two OpenAPI operations, extending existing capabilities and Handoff payloads, and regenerating + checked-in HTTP sources and client mappings; +6. carrying verified Handoff generation metadata through prepare/finalize/commit and recording configuration lineage; +7. emitting Prompt identity and digest in tracing, and validating target Definitions before deployment upgrades. No SQL migration or content backfill is required. Existing deployments need no Prompt rows. An implementation must prove that Auto mode compiles to behavior-equivalent instructions before custom mode is enabled. @@ -584,12 +732,23 @@ The implementation is complete only when tests demonstrate: - an Agent or user binding resolves to the expected Scope before Prompt lookup; - absent and explicit Auto configurations preserve current built-in behavior; - custom instructions and typed demonstrations reach the correct generator only; -- all six registered keys resolve, and unknown keys fail closed; +- all six registered keys report effective support, supported components resolve their Prompt, and unknown keys fail + closed; - a revision is immutable, history pagination is stable, and exact reads do not follow the head; - stale replacement fails and a rollback creates a new revision without deleting history; +- compatible Definition upgrades accept existing custom heads and permit exact-content rollback; incompatible + replacement leaves the head unchanged, and incompatible resolution fails without an Auto fallback; +- history remains readable when the deployed Definition cannot execute a revision; - a Prompt changed during an in-flight operation is used only by the next operation; +- Handoff generation with revision 2 followed by a head change to revision 3 still commits revision 2 as its origin; + edited drafts, missing receipts, tampering, cross-Scope replay, key rotation, and provenance-aware no-op comparison + follow the lifecycle above across separate requests; - generated demonstrations validate against the registered input/output schemas and are never persisted implicitly; -- custom content cannot change invariant instructions, structured schemas, model settings, tools, or authority; +- injected implementations for every applicable key report unsupported and reject Custom writes and suggestions; + an existing custom head blocks invocation after a component switch, while absent/Auto preserves injected behavior; +- an empty Prompt Artifact list still renders all keys, effective availability, and the deployed extraction profile; +- code rejects schema, authority, and Scope violations; model-backed adversarial tests separately report semantic + compliance and synthetic secret leakage without assuming prompt instructions enforce either; - traces contain exact Prompt identity or built-in version and digest without Prompt bodies; and - the OpenAPI contract, generated HTTP sources, unit tests, and at least one real Runtime end-to-end scenario pass. diff --git a/docs/zh/rfcs/1468_scope_owned_prompt_management.md b/docs/zh/rfcs/1468_scope_owned_prompt_management.md index 4ea432dac..8f09a631d 100644 --- a/docs/zh/rfcs/1468_scope_owned_prompt_management.md +++ b/docs/zh/rfcs/1468_scope_owned_prompt_management.md @@ -58,11 +58,14 @@ Runtime extension boundary。 ## 用户模型 -Dashboard 为当前 Scope 展示一个配置页。每个已注册 prompt key 有两种 mode: +Dashboard 为当前 Scope 展示一个配置页。每个支持自定义的 built-in prompt key 有两种 mode: - **Auto** 使用部署 Runtime 选定的内置 instructions; - **Custom** 使用 Scope-owned instructions 与 demonstrations。 +禁用的 operation 和不支持自定义的注入组件显示为不可用,并说明原因。不能仅因为 key 已注册,就把它展示为可配置的 +Auto/Custom operation。 + 对于 `memory.extract`,custom editor 可以展示三个区域: 1. 抽取 instructions; @@ -86,7 +89,8 @@ Definition 根据合法 expected output 判断它会产生结果还是 no-op。 "evidence": [ { "evidence_id": "source-1", - "text": "每次发布前我都会执行核心链路的冒烟测试,耗时大约 20 分钟。" + "evidence_type": "source", + "content": "每次发布前我都会执行核心链路的冒烟测试,耗时大约 20 分钟。" } ], "current_entries": [] @@ -104,7 +108,13 @@ Definition 根据合法 expected output 判断它会产生结果还是 no-op。 }, { "input": { - "evidence": [{"evidence_id": "source-2", "text": "请把上一条消息中的代码发给我。"}], + "evidence": [ + { + "evidence_id": "source-2", + "evidence_type": "source", + "content": "请把上一条消息中的代码发给我。" + } + ], "current_entries": [] }, "expected_output": {"candidates": []} @@ -170,8 +180,9 @@ Content-Type: application/json } ``` -Server 使用 `prompt_key` 作为 `artifact_id` 并提交 revision 1。未知或被禁用的 prompt key 会被拒绝。调用方不能 -自行分配任意 operational Prompt identity。 +Server 使用 `prompt_key` 作为 `artifact_id` 并提交 revision 1。未知 key 会被拒绝;Custom 写入还要求实际组件支持 +自定义。已注册 key 始终允许写入 Auto,以便操作者在 operation 不可用时清除 custom selection。调用方不能自行分配 +任意 operational Prompt identity。 更新 Prompt 使用已有 conditional replacement 操作: @@ -212,11 +223,11 @@ Content-Type: application/json ```json { "instructions": "保留持久的测试偏好和已经验证的失败经验。", - "demonstration_count": 6 + "demonstration_count": 1 } ``` -响应包含恰好六条符合 schema 的 demonstrations: +响应包含恰好一条符合 schema 的 demonstration: ```json { @@ -230,8 +241,8 @@ Content-Type: application/json } ``` -上面的缩短数组只用于展示 item 结构。该接口不会创建或替换 Artifact,不推进 head,也不会把输出静默合并到 current -content。调用方评审并编辑这些建议后,再通过正常 Artifact create 或 replace 操作保存。 +该接口不会创建或替换 Artifact,不推进 head,也不会把输出静默合并到 current content。调用方评审并编辑这些建议后, +再通过正常 Artifact create 或 replace 操作保存。 ## 查看历史与回滚 @@ -310,8 +321,30 @@ class PromptDefinition(Protocol): - demonstration no-op classification; - 与 Prompt content schema version 的兼容规则。 -两个 Definition 不能注册相同 key。未知 key、重复 key、不兼容 Definition 或缺少 built-in selection 时,Runtime -composition 必须失败,而不是接受 untyped configuration。 +两个 Definition 不能注册相同 key。重复 key、Definition 内部不一致或缺少 built-in selection 时,Runtime composition +必须失败,而不是接受 untyped configuration。注册描述已知 contract,不代表实际组件支持自定义;后文的 capabilities +负责报告这一区别。 + +### Definition compatibility + +`schema_version` 标识持久化 Prompt envelope,`definition_version` 标识部署中的 operation contract, +`builtin_version` 标识默认 guidance。三者不能混用。V1 保留四字段 Prompt content,不为每条 revision 再保存一个 +版本选择字段,而是采用以下兼容策略: + +- 同一 prompt key 和 content schema version 下,Definition 更新必须继续接受所有之前合法的 demonstration,并保持 + input 与 expected output 的含义。调整默认 guidance 不一定改变 typed contract。在同一 key 下破坏类型或语义兼容 + 不属于 v1 支持的升级。 +- 历史读取返回不可变的存储 content 和原始 digest,不使用当前 Definition 重新校验 demonstration。Operation 被禁用、 + 替换为注入组件或出现不兼容时,历史仍可读。Resolution 校验不能改写历史 content,也不能向存储表示中插入新默认值。 +- 写入,包括复制旧 content 回滚,使用部署中的兼容 Definition 校验。遇到不兼容 payload,返回 `422` 和 + `prompt_definition_incompatible`,head 不变。解析不兼容的已有 custom head 时,受影响 operation 返回 `503` 和 + 相同 code,不能静默回退 Auto。管理读取和显式替换为 Auto 始终可用于恢复。 +- 升级前,用目标 Definition 检查已有 custom head。不兼容 head 必须先显式迁移为新 revision,或切回 Auto,才能启用 + 受影响 operation。迁移可能改变 content 和 digest,不等于 exact-content rollback。 + +同一部署的所有 worker 必须使用相同的 Definition、built-in profile/version 和 compiler version。V1 不支持混合版本 +worker;部署工具必须先排空旧 worker 再切换版本。Version identifier 和 compiled digest 用于诊断,本身不能协调滚动 +升级。恢复 Prompt content 只恢复 custom guidance,不恢复旧 model、compiler 或部署,也不保证推理输出完全相同。 ## Prompt Artifact content @@ -341,8 +374,9 @@ class PromptContent(BaseModel): - 每条 demonstration 的 canonical JSON 最多 64 KiB; - 完整 canonical Prompt content 最多 256 KiB。 -Demonstration 只包含期望行为。反例通过一个 input 及其对应的 operation no-op `expected_output` 表示,而不是存储故意 -错误的 output。这样 compiler 不会教给模型无效行为,而且 representation 可以统一覆盖全部 prompt key。 +Demonstration 只包含期望行为。Operation 有合法 no-op result 时,反例使用该结果作为 `expected_output`,而不是故意 +错误的 output。没有合法 no-op 的 operation 只使用普通 input/output demonstration,其 classifier 始终返回 false, +UI 不虚构空结果或反例分组。`memory.extract` 通过非空和空 `candidates` 支持正反两组。 ## Persistence 与 identity @@ -363,9 +397,9 @@ vocabulary。重命名 key 属于兼容性变更;alias 需要显式的未来 m RFC 1437 为其四种 family 生成 Artifact ID,并把 Create outer shape 固定为 `family` 加 `content`,其中 Handoff 是 Server 已知 singleton 的例外。本 RFC 只为新增 Prompt family 局部修订该规则。`CreatePromptArtifactRequest` 增加必填 -顶层 `prompt_key`;family writer 使用固定 registry 校验它,并把它作为 `artifact_id`。已有 family 的 request shape -不变。调用方只能选择已知 operation,仍不能分配任意 Artifact ID。把该 resource selector 隐藏在 Prompt content 中会 -重复 identity,也会使 replacement content 依赖 Create transport shape。 +顶层 `prompt_key`;family writer 使用固定 registry 校验它,并把它作为 `artifact_id`。已有 generic Create family 的 +request shape 不变。调用方只能选择已知 operation,仍不能分配任意 Artifact ID。把该 resource selector 隐藏在 Prompt +content 中会重复 identity,也会使 replacement content 依赖 Create transport shape。 ## Revision semantics @@ -401,7 +435,8 @@ class ResolvedPrompt(BaseModel): demonstrations: tuple[PromptDemonstration, ...] ``` -解析算法如下: +支持自定义的 built-in component 使用以下算法。注入或禁用组件在分派前遵循后文的可用性规则;注册本身不会让它们 +接收 resolved Prompt。 1. 为 operation 选择已注册 Prompt Definition; 2. 读取 `(scope_id, prompt_key)` 对应的 current `family=prompt` head; @@ -415,16 +450,16 @@ class ResolvedPrompt(BaseModel): 在执行中的 extraction、rerank、generation 或 Handoff operation 期间修改 head,只影响下一个逻辑 operation。Memory flush 在处理其 bounded Source window 前冻结 Prompt。Prompt head 变更不会自动重新生成已有 Memory 或其他 Artifact。 -Compiler 绝不把 custom text 拼接到更高优先级 invariant 之前。如果 inference adapter 支持 typed example message, -则通过该机制传入 demonstrations;否则使用 Server-owned delimiter 与 canonical JSON encoding 序列化,user text 无法 -结束或重新解释该边界。 +Compiler 保留 Server-owned instruction 及其 message priority。如果 inference adapter 支持 typed example message, +则通过该机制传入 demonstrations;否则使用 canonical JSON 和经过转义的 Server-owned delimiter。转义保护序列化 +边界;delimiter 和 message priority 都不能证明模型会遵守语义指令。推理后仍需执行代码层校验。 实现可以按 `(definition_version, selection identity, compiled_digest)` 缓存 compiled prompt。它必须按照 Artifact current read 相同的一致性规则使 head lookup 失效,也绝不能使用另一个 Scope 的 head。 ## Provenance 与 observability -每个 inference span 记录: +每个托管 inference span 记录: - `powercontext.prompt.key`; - `powercontext.prompt.selection`,值为 `built_in` 或 `artifact`; @@ -441,12 +476,48 @@ requirement,也不能让 Prompt instructions 支撑事实声明。Built-in sel built-in version 与 compiled digest 标识。Memory rerank decision 等 ephemeral output 只在 operation trace 中记录相同 identity。 +### Handoff generation 经 finalize 到 commit + +Handoff prepare、finalize 和 commit 可以是独立请求。Prepare 选定的 Prompt 必须随 draft 传递;finalize 和 commit +不能读取届时的 current Prompt head 来重建 generation provenance。 + +已有 Handoff transport value 增加可缺省的 `generation` envelope,与可编辑 draft text 分离。它包含 Server 可验证 +的 receipt,绑定 Scope、exact custom Prompt reference 或 built-in selection、Definition 与 built-in version、 +compiled digest,以及原始 generated draft 的 digest。Server 在生成成功后签发 receipt;调用方不能仅提交一个原始 +Prompt reference 就获得已验证 provenance。用途绑定的签名 receipt 不需要新表。Receipt 必须能够跨部署 worker 验证, +密钥轮换时继续支持尚未完成的 draft。私有签名材料不能向客户端暴露。 + +生命周期如下: + +1. Prepare 冻结 selection,返回 generated draft 及其 `generation` envelope。 +2. Finalize 验证 receipt 和 Scope,正常校验编辑后的 draft,并把 envelope 传入 `PreparedHandoff`。Commit 再次验证, + 不信任客户端提交的 prepared value。 +3. Commit 比较最终可编辑字段与原始 draft digest。持久化 generation metadata 记录 `unchanged` 或 `edited`。编辑后的 + Handoff 保留生成来源,但不声称最终文本由模型原样生成。已有 evidence、citation、authorization 和 optimistic + concurrency 校验仍然适用。 +4. 已验证的 exact custom Prompt reference 进入 configuration lineage。Generation metadata 保存于已有 Handoff + Artifact JSON,参与 canonical content digest 和 no-op 比较;receipt 字节与签名密钥不写入 Artifact。即使可见 + 文本相同,不同 generation origin 也不能错误继承旧来源。 + +上述检查同样适用于 generic Handoff write 和内部 activation 路径。持久化 generation metadata 由 Server 推导,复制 +读取响应中的 metadata 不能代替合法 receipt。没有 receipt 时,writer 拒绝调用方提供的已验证 metadata,只接受 +明确未归因的 content。 + +例如,使用 Prompt revision 2 生成的 draft,在 commit 时即使 current 已是 revision 3,仍记录 revision 2。中间的 +Prompt 更新不会使 draft 失效。Envelope 缺省表示手工或未归因 content,不推断 Prompt lineage;已提供但无效、被篡改 +或跨 Scope 的 envelope 返回 `422`,不能静默降级。丢弃 envelope 会失去已验证归因,不会获得额外权限。 + +这要求同步扩展 strict Handoff Python model、已有 HTTP request/response schema、mapper 与客户端,以及持久化 Handoff +generation metadata 的读取方。不含该字段的旧 payload 仍合法。不保留该字段的旧客户端无法提供已验证 generation +provenance。这是已有 Handoff contract 和 JSON storage 的扩展,不增加 endpoint 或数据库表。 + ## HTTP contract 完整 v1 HTTP surface 为: | 状态 | Method 与 path | operationId | 用途 | | --- | --- | --- | --- | +| 已有,扩展 | `GET /v1/capabilities` | `get_capabilities` | 报告各 key 的实际自定义支持状态和部署内置标识 | | 已有,扩展 | `POST /v1/scopes/{scope_id}/artifacts` | `create_artifact` | 接受 `family=prompt` 与已注册 `prompt_key` | | 已有,扩展 | `GET /v1/scopes/{scope_id}/artifacts/prompt` | `list_artifacts` | 列出 Scope 中 current Prompt heads | | 已有,扩展 | `GET /v1/scopes/{scope_id}/artifacts/prompt/{prompt_key}` | `get_artifact` | 读取 current Prompt head 与 ETag | @@ -458,6 +529,8 @@ identity。 新增 endpoint 数量恰好是两个。不存在 Prompt-specific list、get、create、update、rollback、publish、validate、 preview、activate 或 delete endpoint。 +已有 Handoff prepare/finalize/commit payload 同样按前文约定携带 `generation`,route 与 operation ID 不变。 + 新增的 Create union member 为: ```json @@ -504,6 +577,10 @@ demonstrations。Server 在响应前校验并规范化 model output。它只会 | replace 缺少 `If-Match` | `428 Precondition Required` | | `If-Match` stale 或不匹配 | `412 Precondition Failed` | | 未知 prompt key、无效 mode/content、demonstration 不符合 schema,或不支持的 family/key 组合 | `422 Unprocessable Entity` | +| Custom 写入或生成建议的目标组件被禁用或不支持自定义 | `422`,code 为 `prompt_customization_unavailable` | +| replacement 的历史 content 与部署 Definition 不兼容 | `422`,code 为 `prompt_definition_incompatible`;head 不变 | +| 实际组件或 Definition 无法执行已有 custom head | `503`,code 为 `prompt_customization_unavailable` 或 `prompt_definition_incompatible` | +| Handoff generation receipt 无效或属于其他 Scope | `422`,code 为 `invalid_handoff_generation` | | pagination cursor 无效或过期 | 已有 `400` 或 `410` cursor semantics | | demonstration generation 需要的 inference provider 不可用 | `503 Service Unavailable` | | provider output 在 request budget 内始终无效 | `500 Internal Server Error`,并返回稳定 public error code | @@ -514,37 +591,85 @@ Error 不回显 Prompt 或 demonstration body。调用方无法通过响应细 Operational Prompt configuration 会改变一个 Scope 中所有兼容 inference operation 的行为。在 access-control model 下,current read、history list 与 exact read 需要相应 Scope read authority;create、replace 与 demonstration -generation 需要 `scope.admin`。Legacy static bearer 继续映射到配置的 administrative Principal。Family-specific writer -执行这个更严格的 mutation rule,不会削弱所有 generic Artifact write。 +generation 需要 `scope.admin`。Legacy static bearer 继续映射到配置的 administrative Principal。Server 的 policy +enforcement point 在分派到 writer 前检查 Prompt family mutation rule;其他 generic Artifact write 保留各自授权规则。 Runtime 内部使用 current Scope Prompt 是已授权 domain operation 的组成部分,不是隐式 exact-resource share。本 RFC 不允许通过 `prompt.user` 共享 operational Prompt revision,其 Prompt access profile 不声明可授予的 exact role。 -Custom Prompt content 是不可信 configuration。它不能: +Custom Prompt content 是不可信 configuration,执行边界如下: -- 覆盖 evidence-as-data、citation requirement、secret exclusion 或 identity allocation rule; -- 修改已注册 input/output schema; -- 选择 model、provider、credential、header、timeout、retry budget 或 token budget; -- 启用 tool、network、filesystem access、publication 或其他 capability; -- 隐式解析或引用另一个 Scope; -- 在没有显式 Artifact write 的情况下保存生成的 demonstrations。 - -Demonstration generator 把 supplied instructions 当作 Server-owned meta-prompt 中的数据。生成案例接受与手工案例相同的 -secret 与 size validation。 +| 代码强制约束 | 必需机制 | +| --- | --- | +| Prompt 不能替换已注册 schema 或 operation contract | Strict request model 与已注册 input/output 校验 | +| Prompt 不能修改 credential、model setting、resource budget、tool 或 authority | Prompt content 不含这些配置入口,仅由 Server composition 与 authorization 决定 | +| Prompt 不能分配 identity、越过 Scope 或引用任意缺失 evidence | 已有 family writer、Scope-bound resolution 与 operation-specific reference/identity check | +| 建议不能自行保存 | Generator 没有 Artifact write action;持久化需要单独授权的写入 | +| Prompt 不能从编译请求中移除 Server instruction | Server-owned compiler 控制 message role 与 instruction assembly | + +Evidence-as-data、引用是否真实支持声明,以及任意自然语言输出中的 secret exclusion,还依赖模型行为。Instruction +不可编辑不等于模型保证遵守。尤其是 Memory output validation 检查结构和 evidence reference,但自由文本 candidate +不具备通用的语义 secret 检测能力。V1 将 secret exclusion 明确为 best-effort,不承诺对 instructions、demonstrations +或 generated content 提供万能 secret filter。Credential 必须留在 model input 之外,操作者不得把 secret 写入 Prompt +content。部署专用的过滤器必须说明覆盖范围与漏检限制。 + +Demonstration generator 把 supplied instructions 放在 Server-owned meta-prompt 中作为数据,并执行与手工案例相同的 +typed validation 和 size limit。对抗测试必须分别覆盖 schema/authority 修改尝试、instruction injection,以及合成的 +secret-like evidence。报告被拒绝的结构违规,也报告真实模型上观察到的语义泄漏;schema 接受某段文本不能单独证明 +模型被绕过,有限测试集通过也不能证明绝对不泄密。 ## 非 HTTP surface Dashboard 使用上述 HTTP 操作。其 Agent 或用户 selector 会先解析为 Scope,再读写 Prompt state。三段式 editor 只是 一个 `PromptContent` 的展示方式,不是另一套 API contract。 +Dashboard 内置六个固定 key 的标签与 editor metadata,通过扩展已有 `GET /v1/capabilities` 响应中的 `prompts` map +获得部署支持状态。无论是否存在 Prompt Artifact,每个已注册 key 都有一条记录: + +| 字段 | Contract | +| --- | --- | +| `status` | `supported`、`disabled` 或 `unsupported`,依据实际 composed component | +| `reason` | supported 时为 `null`;否则为 `operation_disabled`、`provider_not_configured` 或 `injected_component` 等稳定原因 | +| `definition_version` | 部署中已注册 contract version | +| `builtin_version` | 部署的内置 guidance version;不表示注入组件使用它 | +| `builtin_profile` | Memory extraction 为 `coding` 或 `conversation`,其他 key 为 `null` | + +`supported` 表示 Custom selection 和 demonstration generation 都已接入 built-in implementation;provider 临时故障 +仍返回 `503`,不改变配置契约。该响应不包含 Scope-owned content、head、credential 或 model secret,不需要另加 Prompt +discovery endpoint。 + +对 supported key,Dashboard 将上述 metadata 与 Scope 的通用 Prompt Artifact list 合并:没有 head 或 head 为 Auto +时,展示报告的 built-in selection;Custom 时展示已存 revision。对 disabled/unsupported key,禁用 Custom 和生成建议 +操作,显示原因,仍允许读取已存内容和历史。无法执行的已有 custom head 显示为 blocked,并提供显式切回 Auto 操作。 +注入组件标记为外部管理,不能误称它正在使用所报告的 built-in profile。 + Python Runtime 增加本 RFC 所述 Prompt Definition registry、Scope Prompt resolver、compiler 与 family writer。Public Python caller 可以使用已有 generic Artifact client 做 persistence。V1 不增加 MCP tool、CLI command 或 host-specific configuration file。 +### 注入的 Runtime 组件 + +`open_builtin_runtime` 已支持注入 Memory/Experience candidate pipeline、Experience/Skill generator、Handoff +pipeline 和 Memory reranker。它们现有的 protocol 不接收 `ResolvedPrompt`。V1 不修改这些 protocol,也不要求第三方 +实现参与托管 Prompt 自定义。 + +- 只有实际 built-in component 接收 Scope-resolved selection,key 才是 `supported`。仅配置 model 不足以证明支持。 + 只向兼容 built-in pipeline 注入底层 inference provider,不会使该 pipeline 变成 unsupported。 +- 被替换的 pipeline/generator/reranker 为 `unsupported`,reason 是 `injected_component`;未配置的 operation 为 + `disabled`。两种状态都拒绝 Custom create/replace 和 demonstration generation,不能保存成功后静默忽略 Custom。 +- History 和 current read 始终可用。任何已注册 key 都允许写入 Auto。Head 不存在或为 Auto 时,注入组件保持原有行为, + 不生成虚假的 built-in Prompt selection 记录。 +- 如果部署把此前托管的 key 切换成注入或禁用组件,而某 Scope 仍有 Custom head,则在调用组件前以 + `prompt_customization_unavailable` 拒绝 operation。操作者必须显式选择 Auto,或恢复兼容 built-in component, + 不删除或改写历史。 + +第三方主动接入 Prompt 的扩展 protocol 不在 v1 范围内;明确报告 unsupported 即可。 + ## Compatibility 与 migration -对没有 Prompt Artifact 的 Scope,本设计保持向后兼容。它们继续使用与当前完全相同的 built-in selection,包括已配置 -的 `memory_extraction_profile` 和 rerank enablement。 +对没有 Prompt Artifact 的 Scope,built-in component 保留当前 selection,包括已配置的 `memory_extraction_profile` +和 rerank enablement;注入组件保留原有行为。Transport 和 Handoff content model 扩展仍要求 schema/client 同步更新, +不能据此声称旧 strict reader 会接受新 response field。 实现需要: @@ -553,9 +678,11 @@ configuration file。 2. 注册六个 Prompt Definition; 3. 在 base Artifact family contract、generated HTTP model、mapper、repository type registry 与 family management writer registry 中增加 `prompt`; -4. 在六个 inference entry point 中按 Scope 解析 Prompt state; -5. 增加两个 OpenAPI operation 并重新生成 checked-in HTTP sources; -6. 在 tracing,以及适用时 generated Artifact lineage 中发出 Prompt identity 与 digest。 +4. 在六个 inference entry point 中按 Scope 解析 Prompt state,并检查实际组件支持状态; +5. 增加两个 OpenAPI operation,扩展已有 capabilities 与 Handoff payload,重新生成 checked-in HTTP sources 和 + client mapping; +6. 在 prepare/finalize/commit 间传递已验证的 Handoff generation metadata,记录 configuration lineage; +7. 在 tracing 中记录 Prompt identity 与 digest,并在部署升级前校验目标 Definition。 不需要 SQL migration 或 content backfill。已有部署不需要 Prompt row。在启用 custom mode 前,实现必须证明 Auto mode 编译出的 instructions 在行为上与当前实现等价。 @@ -568,12 +695,21 @@ configuration file。 - Agent 或 user binding 在 Prompt lookup 前解析到预期 Scope; - 缺省和显式 Auto configuration 都保持当前 built-in behavior; - custom instructions 与 typed demonstrations 只进入正确 generator; -- 六个注册 key 均可解析,未知 key fail closed; +- 六个注册 key 均报告实际支持状态,支持的组件解析其 Prompt,未知 key fail closed; - revision 不可变、history pagination 稳定、exact read 不跟随 head; - stale replacement 失败,rollback 创建新 revision 且不删除历史; +- 兼容的 Definition 升级接受已有 custom head 并允许 exact-content rollback;不兼容 replacement 不改变 head, + 不兼容 resolution 失败而不回退 Auto; +- 部署 Definition 无法执行某 revision 时,其历史仍然可读; - operation 执行期间修改 Prompt,只被下一次 operation 使用; +- Handoff 用 revision 2 生成、head 随后变成 revision 3,commit 仍记录 revision 2 来源;跨独立请求的编辑 draft、 + receipt 缺省、篡改、跨 Scope 重放、密钥轮换及考虑 provenance 的 no-op 比较都符合上述生命周期; - generated demonstrations 符合已注册 input/output schema,且绝不隐式持久化; -- custom content 无法修改 invariant instruction、structured schema、model setting、tool 或 authority; +- 各适用 key 的注入实现均报告 unsupported 并拒绝 Custom 写入和生成建议;组件切换后已有 custom head 阻止调用, + head 不存在或为 Auto 时仍保留注入行为; +- Prompt Artifact list 为空时仍展示全部 key、实际支持状态和部署的 extraction profile; +- 代码拒绝 schema、authority 和 Scope 违规;真实模型对抗测试单独报告语义遵守情况和合成 secret 泄漏, + 不假设 Prompt instruction 能强制保证二者; - trace 包含 exact Prompt identity 或 built-in version 与 digest,但不包含 Prompt body; - OpenAPI contract、generated HTTP sources、unit tests 与至少一个真实 Runtime end-to-end scenario 通过。