Demonstrate coordinated conversation workflows
This commit is contained in:
@@ -695,6 +695,112 @@ application owns any semantic retry budget. When a
|
||||
pre-execution equality check is required, use `PrepareExecution` and compare
|
||||
its opaque rendered-prompt hash before invoking `RunPrepared`.
|
||||
|
||||
### Capture And Continue A Conversation
|
||||
|
||||
Capture records the messages actually executed and the final assistant content.
|
||||
An exchange is an immutable in-process capability; `Details()` returns detached,
|
||||
encodable history that may contain sensitive content. Ordinary result encoding
|
||||
does not include the exchange. Continuations imply capture and may branch or use
|
||||
a different engine, profile, output contract, credential, or session:
|
||||
|
||||
```go
|
||||
initial, err := engine.Run(ctx, promptkit.RunRequest{PromptID: "proposal", CaptureExchange: true})
|
||||
if err != nil { return err }
|
||||
left, err := engine.Run(ctx, promptkit.RunRequest{PromptID: "refine", ContinueFrom: initial.Exchange})
|
||||
if err != nil { return err }
|
||||
right, err := anotherEngine.Run(ctx, promptkit.RunRequest{PromptID: "alternative", ContinueFrom: initial.Exchange})
|
||||
if err != nil { return err }
|
||||
_, _ = left, right // Independent branches; each result has its own exchange.
|
||||
```
|
||||
|
||||
For a frozen execution, set `CaptureExchange` in the request passed to
|
||||
`PrepareExecution`, then obtain the exchange from `RunPrepared`'s result.
|
||||
Preparation and validation alone do not produce an exchange. Captured history
|
||||
precedes newly rendered definition messages and appended messages. A completed
|
||||
structurally invalid result still has an exchange; an operational error returns
|
||||
no result. The caller chooses what constitutes acceptance.
|
||||
|
||||
```go
|
||||
prepared, err := engine.PrepareExecution(ctx, promptkit.RunRequest{PromptID: "proposal", CaptureExchange: true})
|
||||
if err != nil { return err }
|
||||
result, err := engine.RunPrepared(ctx, prepared)
|
||||
if err != nil { return err }
|
||||
details := result.Exchange.Details()
|
||||
_ = details // Explicitly encode or inspect when appropriate.
|
||||
```
|
||||
|
||||
### Coordinate Application-Directed Turns
|
||||
|
||||
The following sketches use illustrative application functions. The first records
|
||||
each newly delivered response before selecting another question:
|
||||
|
||||
```go
|
||||
outcome, err := engine.Coordinate(ctx, promptkit.CoordinateRequest{
|
||||
Start: initial.Exchange, AdditionalTurns: 3,
|
||||
Next: func(ctx context.Context, turn promptkit.ConversationTurn) (*promptkit.RunRequest, error) {
|
||||
if turn.Result != nil {
|
||||
if !turn.Result.Validation.IsValid { return nil, fmt.Errorf("invalid response") }
|
||||
analysis.Record(turn.Result)
|
||||
}
|
||||
question, ok := analysis.NextQuestion()
|
||||
if !ok { return nil, nil }
|
||||
return &promptkit.RunRequest{PromptID: "research/investigate", Vars: map[string]string{"question": question}}, nil
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
An application can instead start with existing findings, assess each new
|
||||
candidate, and keep its decisions in a closure:
|
||||
|
||||
```go
|
||||
outcome, err := engine.Coordinate(ctx, promptkit.CoordinateRequest{
|
||||
Start: proposal.Exchange, AdditionalTurns: 3, Observer: branchGroup.Observe,
|
||||
Next: func(ctx context.Context, turn promptkit.ConversationTurn) (*promptkit.RunRequest, error) {
|
||||
if turn.Result != nil {
|
||||
if !turn.Result.Validation.IsValid { return nil, fmt.Errorf("invalid revision") }
|
||||
candidate, withdrawn, err := decodeRevision(turn.Result.RawOutput)
|
||||
if err != nil { return nil, err }
|
||||
if withdrawn { branch.MarkWithdrawn(); return nil, nil }
|
||||
findings, err := assess(ctx, engine, candidate, validatorGroup.Observe)
|
||||
if err != nil { return nil, err }
|
||||
branch.Update(candidate, findings)
|
||||
if branch.Done() { return nil, nil }
|
||||
}
|
||||
return &promptkit.RunRequest{PromptID: branch.FollowUpPrompt(), Vars: branch.Variables()}, nil
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
`assess` is application code and explicitly selects its observer on any nested
|
||||
run. A completed turn releases its admission lease before the callback, so that
|
||||
assessment may use the same backend. `stopped` and `exhausted` describe control
|
||||
flow, not content acceptance. Cancellation before callback delivery can return one
|
||||
completed response in `Undelivered`; errors retain their ordinary identities.
|
||||
|
||||
### Collect Selected Execution Facts
|
||||
|
||||
Groups are independent of continuation and enrollment is explicit:
|
||||
|
||||
```go
|
||||
var group, validatorGroup promptkit.ExecutionGroup
|
||||
request.Observer = promptkit.ComposeObservers(diagnosticObserver, group.Observe)
|
||||
prepared, err := engine.PrepareExecution(ctx, request) // no event yet
|
||||
if err != nil { return err }
|
||||
result, err := engine.RunPrepared(ctx, prepared)
|
||||
if err != nil { return err }
|
||||
_ = result
|
||||
// Set CoordinateRequest.Observer to group.Observe for its continuation runs.
|
||||
// A nested validator selects validatorGroup.Observe on its own request.
|
||||
summary, validatorSummary := group.Snapshot(), validatorGroup.Snapshot()
|
||||
_, _ = summary, validatorSummary
|
||||
```
|
||||
|
||||
Only the first terminal event per run contributes counts and usage; coverage of
|
||||
currently observed members is separate from token-usage completeness. Empty
|
||||
groups report complete, known zero totals, while members without terminals do
|
||||
not invent totals. Overlapping groups are not generally additive. See the
|
||||
[offline example](../../examples/go-library/observe/main.go) for complete setup.
|
||||
|
||||
## Inputs, Profiles, And Overrides
|
||||
|
||||
Use `File`, `Inline`, or `InlineWithURI` to construct request inputs. A request
|
||||
|
||||
Reference in New Issue
Block a user