Documentation
¶
Overview ¶
Package reflector is the umbrella namespace for the "Reflector" product abstraction: a family of three sibling interfaces -- validator.Validator, evolve.Evaluator, evolve.Reflector -- that each reflect on an artifact and emit, respectively, a commit verdict, a fitness score, or a replay-time side effect. This package defines no new top-level interface. It hosts type-safe adapters so a concrete implementation of one sibling can satisfy another sibling's signature, giving consumers "replaceability" (any of rule / LLM / ML backing can be plugged in wherever any sibling interface is expected) without forcing every author to write three copies.
Why three interfaces stay separate:
- validator.Validator is a synchronous commit gate. Input is a staged DiffInput, output is a Verdict with Severity. Negative verdict blocks the commit.
- evolve.Evaluator is a cheap scorer used in candidate-ranking / GA loops. Input is a Candidate (opaque Payload), output is a scalar fitness plus optional breakdown.
- evolve.Reflector is fire-and-forget replay consumer. Input is a ReplayEvent pushed by LogReplayer, output is error only (err is logged, never halts the replayer).
A merged super-interface would force Reflector to fabricate a synthetic Verdict or force Validator to carry replay semantics. Neither fits. Go idiom is small interfaces composed via wrappers (io.Reader / io.Closer / io.ReadCloser), not one God interface.
What this package delivers:
ValidatorAsEvaluator wraps validator.Validator as evolve.Evaluator EvaluatorAsValidator wraps evolve.Evaluator as validator.Validator ValidatorAsReflector wraps validator.Validator as evolve.Reflector (side-channel) EvaluatorAsReflector wraps evolve.Evaluator as evolve.Reflector (side-channel)
The reverse direction (Reflector -> Validator / Evaluator) is intentionally absent: Reflector.OnEvent has no return payload from which a synchronous Verdict or fitness can be reconstructed.
Replaceability guarantee. Each adapter returns the target interface type directly. A call site written against validator.Validator can be wired with a native impl or with EvaluatorAsValidator(...); the swap is a single construction-site line, and the rest of the call site stays free of reflector imports.
Concurrency. Wrapped implementations must be goroutine-safe. A single underlying Validator / Evaluator may be fan-out wrapped by several adapters (e.g. ValidatorAsEvaluator + ValidatorAsReflector) and therefore reached from multiple goroutines concurrently. If the underlying impl is not goroutine-safe, wrap a synchronizing decorator before passing it to the adapter.
反射器产品抽象的伞形命名空间. 三个同族接口 validator.Validator / evolve.Evaluator / evolve.Reflector 各自对一个待审对象做反思, 分别 产出 commit verdict / fitness / 回放副作用. 本包不引入新的顶层接口, 只提供类型安全的 adapter, 让一个具体实现同时满足另一个同族接口签名. "可替换性" 由此兑现 (规则 / LLM / ML 后端可互换接入任一同族接口的 调用点), 实现方不必重写三份.
为什么三个接口保持独立:
- validator.Validator 是同步 commit 闸. 输入 staged DiffInput, 输出 带 Severity 的 Verdict. 否决即阻断 commit.
- evolve.Evaluator 是候选排序 / GA 循环里的便宜打分器. 输入 Candidate (Payload 不透明), 输出标量 fitness 与可选 breakdown.
- evolve.Reflector 是 fire-and-forget 的回放消费者. 输入 ReplayEvent, 输出仅 error (只记录, 不打断 replayer).
合一的超级接口要么逼 Reflector 伪造 Verdict, 要么逼 Validator 承担 回放语义, 都别扭. Go 惯例是小接口通过 wrapper 组合 (io.Reader / io.Closer / io.ReadCloser), 不是一个 God interface.
本包提供的 adapter:
ValidatorAsEvaluator 把 validator.Validator 包为 evolve.Evaluator EvaluatorAsValidator 把 evolve.Evaluator 包为 validator.Validator ValidatorAsReflector 把 validator.Validator 包为 evolve.Reflector (旁路) EvaluatorAsReflector 把 evolve.Evaluator 包为 evolve.Reflector (旁路)
反向 (Reflector -> Validator / Evaluator) 刻意不做: OnEvent 无返回载荷, 无法反推同步 Verdict 或 fitness.
可替换性保证. 每个 adapter 直接返回目标接口类型. 针对 validator.Validator 写的调用点既可注入原生实现, 也可注入 EvaluatorAsValidator(...); 切换是 构造点一行改动, 其他调用代码不污染 reflector import.
并发语义. 被包装的实现必须 goroutine-safe. 同一底层 Validator / Evaluator 可能被多个 adapter 扇出包装 (例如 ValidatorAsEvaluator + ValidatorAsReflector), 因此会被多 goroutine 并发调用. 若底层实现非 goroutine-safe, 在传入 adapter 前套一层同步装饰器.
Example (EvaluatorAsValidator) ¶
Example_evaluatorAsValidator wraps an evolve.Evaluator as a validator.Validator so a GA-style scorer can plug into the commit-gate pipeline without rewriting the scorer against the Validator interface.
Example_evaluatorAsValidator 把 evolve.Evaluator 包为 validator.Validator, 让 GA 式打分器直接接入 commit-gate 流水线, 无需 针对 Validator 接口重写打分器.
package main
import (
"context"
"encoding/json"
"fmt"
"git.flytoex.net/yuanwei/flyto-agent/pkg/evolve"
"git.flytoex.net/yuanwei/flyto-agent/pkg/reflector"
"git.flytoex.net/yuanwei/flyto-agent/pkg/validator"
)
func main() {
scorer, _ := evolve.NewFuncEvaluator(func(_ context.Context, c evolve.Candidate) (float64, map[string]float64, error) {
payload, _ := c.Payload.(map[string]any)
if payload["risky"] == true {
return 0.2, map[string]float64{"risk": 0.8}, nil
}
return 0.9, map[string]float64{"risk": 0.1}, nil
})
extract := func(d validator.DiffInput) (evolve.Candidate, error) {
var payload map[string]any
if err := json.Unmarshal(d.Raw, &payload); err != nil {
return evolve.Candidate{}, err
}
return evolve.Candidate{ID: d.SourceTool, Payload: payload}, nil
}
v := reflector.EvaluatorAsValidator(scorer, extract, 0.5,
reflector.WithName("ga-gate"),
reflector.WithPolicyVersion("2026-04"),
)
vd, _ := v.Validate(context.Background(), validator.DiffInput{
SourceTool: "SQLCAS",
Raw: []byte(`{"risky": true}`),
})
fmt.Println(v.Name(), vd.Approved, vd.Severity)
}
Output: ga-gate false block
Index ¶
- Variables
- func EvaluatorAsReflector(src evolve.Evaluator, extract EventToCandidate, sink FitnessSink) evolve.Reflector
- func EvaluatorAsValidator(src evolve.Evaluator, extract DiffToCandidate, threshold float64, ...) validator.Validator
- func ValidatorAsEvaluator(src validator.Validator, extract CandidateToDiff, ...) evolve.Evaluator
- func ValidatorAsReflector(src validator.Validator, extract EventToDiff, sink VerdictSink) evolve.Reflector
- type CandidateToDiff
- type DiffToCandidate
- type EvaluatorAsValidatorOption
- type EventToCandidate
- type EventToDiff
- type FitnessSink
- type ValidatorAsEvaluatorOption
- type VerdictSink
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrExtractFailed = errors.New("reflector: extractor failed")
ErrExtractFailed wraps any error returned by a caller-supplied extractor function. Adapters return it with %w so callers can detect the class with errors.Is.
ErrExtractFailed 包装 extractor 回调返回的 error. adapter 以 %w 透出, 调用方用 errors.Is 识别.
Functions ¶
func EvaluatorAsReflector ¶
func EvaluatorAsReflector(src evolve.Evaluator, extract EventToCandidate, sink FitnessSink) evolve.Reflector
EvaluatorAsReflector wraps an evolve.Evaluator so it satisfies evolve.Reflector for side-channel replay scoring. extract turns each ReplayEvent into a Candidate; sink receives the resulting fitness, breakdown, and any Score error or ErrExtractFailed. OnEvent always returns nil.
Panics at construction if src, extract or sink is nil.
EvaluatorAsReflector 把 evolve.Evaluator 包为 evolve.Reflector 做 回放旁路打分. extract 把 ReplayEvent 转为 Candidate; sink 收到产出 的 fitness / breakdown / Score error 或 ErrExtractFailed. OnEvent 始终返回 nil.
构造时 src / extract / sink 任一为 nil 直接 panic.
func EvaluatorAsValidator ¶
func EvaluatorAsValidator(src evolve.Evaluator, extract DiffToCandidate, threshold float64, opts ...EvaluatorAsValidatorOption) validator.Validator
EvaluatorAsValidator wraps an evolve.Evaluator so it satisfies validator.Validator. extract turns each DiffInput into a Candidate the underlying Evaluator can score. fitness >= threshold yields Approved=true with SeverityWarn; below yields Approved=false with Severity controlled by WithBelowThresholdSeverity (default Block). breakdown is forwarded into Verdict.Details.
Panics at construction if src or extract is nil.
EvaluatorAsValidator 把 evolve.Evaluator 包为 validator.Validator. extract 把每个 DiffInput 转为 Candidate 交给底层 Evaluator 打分. fitness >= threshold 产出 Approved=true 且 SeverityWarn; 低于阈值 产出 Approved=false 且 Severity 由 WithBelowThresholdSeverity 控制 (默认 Block). breakdown 透传至 Verdict.Details.
构造时 src 或 extract 为 nil 直接 panic.
func ValidatorAsEvaluator ¶
func ValidatorAsEvaluator(src validator.Validator, extract CandidateToDiff, opts ...ValidatorAsEvaluatorOption) evolve.Evaluator
ValidatorAsEvaluator wraps a validator.Validator so it satisfies evolve.Evaluator. extract turns each Candidate into a DiffInput the underlying Validator can review. If Verdict.Approved is true, the returned fitness is Verdict.Score. Otherwise fitness is 0 (override via WithRejectFitness). Verdict.Details is forwarded into breakdown on a best-effort basis (only numeric entries survive).
Panics at construction if src or extract is nil -- both are mandatory and nil indicates a programming error that should surface early rather than at the first Score call.
ValidatorAsEvaluator 把 validator.Validator 包为 evolve.Evaluator. extract 把每个 Candidate 转为 DiffInput 交给底层 Validator 审查. Verdict.Approved=true 时返回 fitness=Verdict.Score; 否则返回 0 (可用 WithRejectFitness 覆写). Verdict.Details 尽力透传为 breakdown (仅数值条目存活).
构造时 src 或 extract 为 nil 直接 panic -- 两者均为必传, nil 是编程 错误应尽早暴露.
func ValidatorAsReflector ¶
func ValidatorAsReflector(src validator.Validator, extract EventToDiff, sink VerdictSink) evolve.Reflector
ValidatorAsReflector wraps a validator.Validator so it satisfies evolve.Reflector for side-channel replay observation. extract turns each ReplayEvent into a DiffInput; sink receives the resulting Verdict along with any Validate error or ErrExtractFailed. OnEvent always returns nil (Reflector contract: never halt the replayer), so extract / Validate errors flow through sink only.
Panics at construction if src, extract or sink is nil.
ValidatorAsReflector 把 validator.Validator 包为 evolve.Reflector 做回放旁路观察. extract 把 ReplayEvent 转为 DiffInput; sink 收到 产出的 Verdict 及 Validate error 或 ErrExtractFailed. OnEvent 始终 返回 nil (Reflector 契约: 不中断 replayer), extract / Validate 错误 仅通过 sink 传出.
构造时 src / extract / sink 任一为 nil 直接 panic.
Types ¶
type CandidateToDiff ¶
CandidateToDiff converts an evolve.Candidate into a validator.DiffInput. A non-nil error aborts the conversion; adapters wrap it with ErrExtractFailed before propagating.
CandidateToDiff 把 evolve.Candidate 转为 validator.DiffInput. 返回非 nil error 则终止转换, adapter 以 ErrExtractFailed 包装透出.
type DiffToCandidate ¶
DiffToCandidate converts a validator.DiffInput into an evolve.Candidate.
DiffToCandidate 把 validator.DiffInput 转为 evolve.Candidate.
type EvaluatorAsValidatorOption ¶
type EvaluatorAsValidatorOption func(*evaluatorAsValidatorConfig)
EvaluatorAsValidatorOption configures EvaluatorAsValidator.
EvaluatorAsValidatorOption 配置 EvaluatorAsValidator.
func WithBelowThresholdSeverity ¶
func WithBelowThresholdSeverity(s validator.Severity) EvaluatorAsValidatorOption
WithBelowThresholdSeverity controls the Severity attached to the Verdict when fitness < threshold. Default is validator.SeverityBlock (fail-closed). Set to validator.SeverityWarn for advisory-only scoring.
WithBelowThresholdSeverity 控制 fitness < threshold 时 Verdict 的 Severity. 默认 validator.SeverityBlock (fail-closed). 可设为 validator.SeverityWarn 作为 advisory-only 打分.
func WithName ¶
func WithName(n string) EvaluatorAsValidatorOption
WithName overrides the adapter's Name(). Default is "evaluator-as-validator". Validators in the same process must not share a Name; if two EvaluatorAsValidator instances coexist, at least one must carry an explicit WithName.
WithName 覆写 adapter 的 Name(). 默认 "evaluator-as-validator". 同 进程内 Validator 不能同名; 若两个 EvaluatorAsValidator 共存, 至少 一个必须显式传 WithName.
func WithPolicyVersion ¶
func WithPolicyVersion(v string) EvaluatorAsValidatorOption
WithPolicyVersion sets the PolicyVersion field the adapter stamps on every Verdict it emits, so replay audits can tell which policy version approved or rejected a past diff.
WithPolicyVersion 设置 adapter 在产出 Verdict 时写入的 PolicyVersion 字段, 便于回放审计判断当时的策略版本.
type EventToCandidate ¶
type EventToCandidate func(ev evolve.ReplayEvent) (evolve.Candidate, error)
EventToCandidate converts an evolve.ReplayEvent into an evolve.Candidate for side-channel scoring during replay.
EventToCandidate 把 evolve.ReplayEvent 转为 evolve.Candidate, 用于 回放期旁路打分.
type EventToDiff ¶
type EventToDiff func(ev evolve.ReplayEvent) (validator.DiffInput, error)
EventToDiff converts an evolve.ReplayEvent into a validator.DiffInput for side-channel validation during replay.
EventToDiff 把 evolve.ReplayEvent 转为 validator.DiffInput, 用于 回放期旁路校验.
type FitnessSink ¶
type FitnessSink func(ev evolve.ReplayEvent, fitness float64, breakdown map[string]float64, err error)
FitnessSink receives each fitness score produced by EvaluatorAsReflector, plus breakdown and any Score error (or ErrExtractFailed). Same contract as VerdictSink.
FitnessSink 接收 EvaluatorAsReflector 产出的 fitness, 附带 breakdown 与 Score error (或 ErrExtractFailed). 契约与 VerdictSink 一致.
type ValidatorAsEvaluatorOption ¶
type ValidatorAsEvaluatorOption func(*validatorAsEvaluatorConfig)
ValidatorAsEvaluatorOption configures ValidatorAsEvaluator.
ValidatorAsEvaluatorOption 配置 ValidatorAsEvaluator.
func WithRejectFitness ¶
func WithRejectFitness(f float64) ValidatorAsEvaluatorOption
WithRejectFitness sets the fitness value ValidatorAsEvaluator emits when Verdict.Approved is false. Default is 0, which is the safe choice (GA tournaments will not prefer a rejected candidate over an approved one with any positive score). Set to a positive value to preserve gradient signal for GA use cases that need to differentiate "barely rejected" from "hard rejected".
WithRejectFitness 设置 ValidatorAsEvaluator 在 Verdict.Approved=false 时产出的 fitness. 默认 0, 保守选择 (GA 锦标选择不会把"被拒候选" 排在"通过+任意正分"之前). 设为正值可保留 GA 梯度信号, 用于区分 "微拒" 与 "硬拒".
type VerdictSink ¶
type VerdictSink func(ev evolve.ReplayEvent, v validator.Verdict, err error)
VerdictSink receives each Verdict produced by ValidatorAsReflector, along with the originating ReplayEvent and any Validate error (or ErrExtractFailed from a failing extractor). The sink is how the side-channel signal leaves the adapter (log, metric, audit, parameter-evolver backpressure). OnEvent itself always returns nil so it never stops the replayer loop.
VerdictSink 接收 ValidatorAsReflector 产出的每个 Verdict, 附带触发 的 ReplayEvent 与 Validate error (或 extractor 失败时的 ErrExtractFailed). sink 是旁路信号离开 adapter 的唯一出口 (日志 / 指标 / 审计 / 反压给 ParameterEvolver). OnEvent 始终返回 nil, 不中断 replayer.