207 lines
5.9 KiB
Go
207 lines
5.9 KiB
Go
package core
|
||
|
||
import "encoding/json"
|
||
|
||
// 智能体画像(4.7):核心定制机制,全部数据化、可导入导出。
|
||
type AgentProfile struct {
|
||
ID string `json:"id"`
|
||
Owner string `json:"owner,omitempty"` // 租户;空 = 共享(builtin)
|
||
Name string `json:"name"`
|
||
Description string `json:"description,omitempty"`
|
||
Builtin bool `json:"builtin,omitempty"`
|
||
Source string `json:"source,omitempty"` // builtin | plugin | user
|
||
SystemPrompt string `json:"systemPrompt"`
|
||
Model ModelRoute `json:"model"`
|
||
Tools ToolPolicy `json:"tools"`
|
||
Context ContextPolicy `json:"context"`
|
||
HITL HITLPolicy `json:"hitl"`
|
||
Budget RunBudget `json:"budget"`
|
||
}
|
||
|
||
// ModelRoute 按用途选择模型(4.7 kind 路由):空串回落到 chat 默认。
|
||
type ModelRoute struct {
|
||
Chat string `json:"chat,omitempty"`
|
||
Lightweight string `json:"lightweight,omitempty"`
|
||
Embedding string `json:"embedding,omitempty"`
|
||
Rerank string `json:"rerank,omitempty"`
|
||
}
|
||
|
||
// ToolPolicy 三清单:allow/deny 决定可见工具,confirm 强制执行前 HITL 确认。
|
||
// 匹配语义:精确名或前缀通配("http.*" 表示 http 前缀);deny 优先于 allow。
|
||
type ToolPolicy struct {
|
||
Allow []string `json:"allow"` // ["*"] 全部
|
||
Deny []string `json:"deny,omitempty"`
|
||
Confirm []string `json:"confirm,omitempty"`
|
||
}
|
||
|
||
// ContextPolicy(4.10):ctxasm 的输入;逐版本增加策略项不动组装器接口。
|
||
type ContextPolicy struct {
|
||
MaxHistoryMessages int `json:"maxHistoryMessages"` // 0 = 默认 40
|
||
SummarizeBeyond bool `json:"summarizeBeyond,omitempty"` // 远期;先存标志
|
||
MemoryBudgetPct int `json:"memoryBudgetPct,omitempty"` // 注入槽份额上限
|
||
KnowledgeBudgetPct int `json:"knowledgeBudgetPct,omitempty"`
|
||
MaxToolResultChars int `json:"maxToolResultChars,omitempty"` // 0 = 默认 20000
|
||
}
|
||
|
||
type HITLPolicy struct {
|
||
AskOnAmbiguity bool `json:"askOnAmbiguity"`
|
||
TimeoutSec *int `json:"timeoutSec,omitempty"`
|
||
}
|
||
|
||
type RunBudget struct {
|
||
MaxTokens *int `json:"maxTokens,omitempty"`
|
||
MaxToolCalls *int `json:"maxToolCalls,omitempty"`
|
||
MaxTurns *int `json:"maxTurns,omitempty"` // LLM 循环轮上限
|
||
}
|
||
|
||
// ToolAllows 判定:deny 优先;allow 含 "*" 或精确/前缀命中。
|
||
func (p ToolPolicy) ToolAllowed(name string) bool {
|
||
if matchAny(p.Deny, name) {
|
||
return false
|
||
}
|
||
if len(p.Allow) == 0 {
|
||
return true
|
||
}
|
||
return matchAny(p.Allow, name)
|
||
}
|
||
|
||
func (p ToolPolicy) NeedsConfirm(name string) bool {
|
||
return matchAny(p.Confirm, name)
|
||
}
|
||
|
||
// matchAny 语义:pattern 精确等于 name,或以 "xxx." 开头表示前缀族(http. → http.*)。
|
||
func matchAny(patterns []string, name string) bool {
|
||
for _, p := range patterns {
|
||
if p == "*" || p == name {
|
||
return true
|
||
}
|
||
if n := len(p); n > 1 && p[n-1] == '.' {
|
||
if len(name) > n-1 && name[:n-1] == p[:n-1] {
|
||
return true
|
||
}
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
// ---- 设置(4.9)----
|
||
|
||
type SettingType string
|
||
|
||
const (
|
||
SettingString SettingType = "string"
|
||
SettingNumber SettingType = "number"
|
||
SettingBool SettingType = "boolean"
|
||
SettingEnum SettingType = "enum"
|
||
SettingSecret SettingType = "secret"
|
||
)
|
||
|
||
type SettingEffect string
|
||
|
||
const (
|
||
EffectHot SettingEffect = "hot" // 立即生效
|
||
EffectNewRun SettingEffect = "new-run" // 仅影响新 run
|
||
EffectRestart SettingEffect = "restart" // 需重启
|
||
)
|
||
|
||
// SettingDesc 描述一个设置项;设置页按此渲染并标注生效时机(schema 驱动)。
|
||
type SettingDesc struct {
|
||
Key string `json:"key"`
|
||
Group string `json:"group"` // 分区:models | profiles | tools | knowledge | ui | plugin:<id>
|
||
Label string `json:"label"`
|
||
Description string `json:"description,omitempty"`
|
||
Type SettingType `json:"type"`
|
||
Default any `json:"default,omitempty"`
|
||
Options []string `json:"options,omitempty"` // enum
|
||
Effect SettingEffect `json:"effect"`
|
||
Secret bool `json:"secret,omitempty"`
|
||
}
|
||
|
||
// EffectiveSettings 是覆盖链(system → user → project → conversation)深合并结果。
|
||
type EffectiveSettings map[string]any
|
||
|
||
// MergeSettings 深合并:后者覆盖前者;map 递归合并,其它类型直接覆盖(4.9 数组显式覆盖)。
|
||
func MergeSettings(base, override map[string]any) map[string]any {
|
||
out := make(map[string]any, len(base)+len(override))
|
||
for k, v := range base {
|
||
out[k] = v
|
||
}
|
||
for k, v := range override {
|
||
if bm, ok := base[k].(map[string]any); ok {
|
||
if vm, ok2 := v.(map[string]any); ok2 {
|
||
out[k] = MergeSettings(bm, vm)
|
||
continue
|
||
}
|
||
}
|
||
out[k] = v
|
||
}
|
||
return out
|
||
}
|
||
|
||
// GetString/GetBool/GetNumber 读取点路径("models.chat");缺省返回 def。
|
||
func (s EffectiveSettings) GetString(path, def string) string {
|
||
v, ok := lookupPath(s, path)
|
||
if !ok {
|
||
return def
|
||
}
|
||
if str, ok2 := v.(string); ok2 {
|
||
return str
|
||
}
|
||
return def
|
||
}
|
||
|
||
func (s EffectiveSettings) GetBool(path string, def bool) bool {
|
||
v, ok := lookupPath(s, path)
|
||
if !ok {
|
||
return def
|
||
}
|
||
if b, ok2 := v.(bool); ok2 {
|
||
return b
|
||
}
|
||
return def
|
||
}
|
||
|
||
func (s EffectiveSettings) GetNumber(path string, def float64) float64 {
|
||
v, ok := lookupPath(s, path)
|
||
if !ok {
|
||
return def
|
||
}
|
||
if f, ok2 := v.(float64); ok2 {
|
||
return f
|
||
}
|
||
return def
|
||
}
|
||
|
||
func lookupPath(m map[string]any, path string) (any, bool) {
|
||
parts := splitPath(path)
|
||
var cur any = m
|
||
for _, p := range parts {
|
||
cm, ok := cur.(map[string]any)
|
||
if !ok {
|
||
return nil, false
|
||
}
|
||
cur, ok = cm[p]
|
||
if !ok {
|
||
return nil, false
|
||
}
|
||
}
|
||
return cur, true
|
||
}
|
||
|
||
func splitPath(path string) []string {
|
||
var out []string
|
||
cur := ""
|
||
for i := 0; i < len(path); i++ {
|
||
switch path[i] {
|
||
case '.':
|
||
out = append(out, cur)
|
||
cur = ""
|
||
default:
|
||
cur += string(path[i])
|
||
}
|
||
}
|
||
return append(out, cur)
|
||
}
|
||
|
||
var _ = json.Marshal // 保持 encoding/json 引用(RawMessage 一致性)
|