feat: 实现采购运行时规格解析 (#255)
This commit is contained in:
@@ -39,6 +39,78 @@ type AIModelClient interface {
|
||||
Match(context.Context, model.AIProviderConfig, string, AIModelMatchRequest) (AIModelMatchResponse, error)
|
||||
}
|
||||
|
||||
type aiWhitelistedSelectionStatus string
|
||||
|
||||
const (
|
||||
aiSelectionMatched aiWhitelistedSelectionStatus = "matched"
|
||||
aiSelectionUncertain aiWhitelistedSelectionStatus = "uncertain"
|
||||
aiSelectionConflict aiWhitelistedSelectionStatus = "conflict"
|
||||
aiSelectionFailed aiWhitelistedSelectionStatus = "failed"
|
||||
aiSelectionCandidateRejected aiWhitelistedSelectionStatus = "candidate_rejected"
|
||||
aiSelectionDimensionsUnresolved aiWhitelistedSelectionStatus = "dimensions_unresolved"
|
||||
aiSelectionBelowThreshold aiWhitelistedSelectionStatus = "below_threshold"
|
||||
)
|
||||
|
||||
// aiWhitelistedSelection 是 SYB 批量匹配和 Client 真机匹配共用的模型安全结论。
|
||||
// 业务编排可以把“不确定”显示成不同文案,但不能绕过这里的候选、阈值和维度门禁。
|
||||
type aiWhitelistedSelection struct {
|
||||
Status aiWhitelistedSelectionStatus
|
||||
Response AIModelMatchResponse
|
||||
Reason string
|
||||
ConfidenceSet bool
|
||||
ModelCalled bool
|
||||
}
|
||||
|
||||
func selectAIWhitelistedCandidate(ctx context.Context, snapshot AIMatchSnapshot,
|
||||
request AIModelMatchRequest) aiWhitelistedSelection {
|
||||
if snapshot.Client == nil || snapshot.Secret == "" || snapshot.Provider.ProviderID == "" {
|
||||
return aiWhitelistedSelection{Status: aiSelectionFailed, Reason: "AI 服务商运行快照不可用"}
|
||||
}
|
||||
response, err := snapshot.Client.Match(ctx, snapshot.Provider, snapshot.Secret, request)
|
||||
if err != nil {
|
||||
reason := safeAIError(err)
|
||||
if snapshot.Secret != "" {
|
||||
reason = strings.ReplaceAll(reason, snapshot.Secret, "[REDACTED]")
|
||||
}
|
||||
return aiWhitelistedSelection{Status: aiSelectionFailed, Reason: reason, ModelCalled: true}
|
||||
}
|
||||
result := aiWhitelistedSelection{Response: response, Reason: response.Reason,
|
||||
ConfidenceSet: true, ModelCalled: true}
|
||||
if err := validateAIModelMatchResponse(response); err != nil {
|
||||
result.Status, result.Reason, result.ConfidenceSet = aiSelectionFailed, err.Error(), false
|
||||
return result
|
||||
}
|
||||
if response.Conclusion == "uncertain" {
|
||||
result.Status = aiSelectionUncertain
|
||||
return result
|
||||
}
|
||||
if response.Conclusion == "conflict" {
|
||||
result.Status = aiSelectionConflict
|
||||
return result
|
||||
}
|
||||
allowed := false
|
||||
for _, candidate := range request.Candidates {
|
||||
if candidate.ID == response.CandidateID {
|
||||
allowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !allowed {
|
||||
result.Status, result.Reason = aiSelectionCandidateRejected, "模型返回了不在候选白名单中的编号"
|
||||
return result
|
||||
}
|
||||
if len(response.ConflictDimensions) > 0 || len(response.MissingDimensions) > 0 {
|
||||
result.Status, result.Reason = aiSelectionDimensionsUnresolved, "模型报告仍有冲突或缺失维度"
|
||||
return result
|
||||
}
|
||||
if response.ConfidenceBPS < snapshot.Provider.ConfidenceThresholdBPS {
|
||||
result.Status, result.Reason = aiSelectionBelowThreshold, "模型置信度低于管理员设置的自动选择阈值"
|
||||
return result
|
||||
}
|
||||
result.Status = aiSelectionMatched
|
||||
return result
|
||||
}
|
||||
|
||||
type OpenAICompatibleModelClient struct{ doer AIHTTPDoer }
|
||||
|
||||
func NewOpenAICompatibleModelClient(doer AIHTTPDoer) *OpenAICompatibleModelClient {
|
||||
|
||||
Reference in New Issue
Block a user