refactor: 将客服后端重构为宿主可嵌入模块
- 注入数据库、运行时配置、统一响应、文件存储和平台 AI 能力,补充业务读写工具与客户快捷操作契约。 - 移除模块内重复的组织、客户、工单、标签、技能、旧工作流、MCP 和迁移实现,将身份权限与业务主体交由宿主管理。 - 使用 libSQL 重构向量存储,并完善图片消息、访客身份、排队调度、企业微信和支持聊天页面。 - 统一 HTTP、DTO 与 WebSocket 的 snake_case 协议,补齐模块初始化、业务动作和公共载荷等回归测试。
This commit is contained in:
@@ -32,14 +32,13 @@ type Definition struct {
|
||||
// Policy is supplied by the caller's agent/runtime context for one invocation.
|
||||
// An empty AllowedToolCodes means the caller did not impose an allow-list.
|
||||
type Policy struct {
|
||||
AllowedToolCodes []string
|
||||
SkillAllowedToolCodes []string
|
||||
AllowedRiskLevels []string
|
||||
CallCount int
|
||||
TotalCallCount int
|
||||
MaxTotalCalls int
|
||||
MaxArgumentBytes int
|
||||
Confirmed bool
|
||||
AllowedToolCodes []string
|
||||
AllowedRiskLevels []string
|
||||
CallCount int
|
||||
TotalCallCount int
|
||||
MaxTotalCalls int
|
||||
MaxArgumentBytes int
|
||||
Confirmed bool
|
||||
}
|
||||
|
||||
type Invocation struct {
|
||||
@@ -69,24 +68,7 @@ func (r *Registry) Resolve(toolCode string) (Definition, error) {
|
||||
if spec, ok := toolx.GetRegisteredToolSpec(toolCode); ok {
|
||||
return definitionFromSpec(spec), nil
|
||||
}
|
||||
serverCode, toolName := toolx.SplitMCPToolCode(toolCode)
|
||||
if serverCode == "" || toolName == "" {
|
||||
return Definition{}, fmt.Errorf("unsupported tool code: %s", toolCode)
|
||||
}
|
||||
// MCP tools are explicitly selected by an administrator before an Agent can
|
||||
// call them. Treat that persisted allow-list as the authorization boundary;
|
||||
// only tools with an explicit built-in policy require extra confirmation.
|
||||
return Definition{
|
||||
Code: toolCode,
|
||||
Name: toolName,
|
||||
InputSchema: map[string]any{"type": "object", "additionalProperties": true},
|
||||
SourceType: enums.ToolSourceTypeMCP,
|
||||
RiskLevel: RiskLevelRead,
|
||||
RequireConfirmation: false,
|
||||
MaxCallsPerRun: 3,
|
||||
TimeoutMS: 30000,
|
||||
IdempotencyMode: "caller",
|
||||
}, nil
|
||||
return Definition{}, fmt.Errorf("unsupported tool code: %s", toolCode)
|
||||
}
|
||||
|
||||
func (r *Registry) Authorize(definition Definition, policy Policy) error {
|
||||
@@ -102,9 +84,6 @@ func (g *PolicyGuard) Authorize(invocation Invocation) error {
|
||||
if len(policy.AllowedToolCodes) > 0 && !containsCanonicalToolCode(policy.AllowedToolCodes, definition.Code) {
|
||||
return fmt.Errorf("tool is not allowed: %s", definition.Code)
|
||||
}
|
||||
if len(policy.SkillAllowedToolCodes) > 0 && !containsCanonicalToolCode(policy.SkillAllowedToolCodes, definition.Code) {
|
||||
return fmt.Errorf("tool is not allowed by the selected skill: %s", definition.Code)
|
||||
}
|
||||
if len(policy.AllowedRiskLevels) > 0 && !containsString(policy.AllowedRiskLevels, definition.RiskLevel) {
|
||||
return fmt.Errorf("tool risk level is not allowed: %s", definition.RiskLevel)
|
||||
}
|
||||
@@ -147,37 +126,18 @@ func definitionFromSpec(spec toolx.ToolSpec) Definition {
|
||||
definition.InputSchema = requiredObjectSchema([]string{"query"}, map[string]any{"query": map[string]any{"type": "string"}})
|
||||
case toolx.GraphTriageServiceRequest.Code:
|
||||
definition.InputSchema = objectSchema(map[string]any{
|
||||
"goal": map[string]any{"type": "string"},
|
||||
"observedIssue": map[string]any{"type": "string"},
|
||||
"needTicket": map[string]any{"type": "boolean"},
|
||||
"needHumanHandoff": map[string]any{"type": "boolean"},
|
||||
"additionalContext": map[string]any{"type": "string"},
|
||||
"goal": map[string]any{"type": "string"},
|
||||
"observed_issue": map[string]any{"type": "string"},
|
||||
"need_human_handoff": map[string]any{"type": "boolean"},
|
||||
"additional_context": map[string]any{"type": "string"},
|
||||
})
|
||||
case toolx.GraphAnalyzeConversation.Code:
|
||||
definition.InputSchema = objectSchema(map[string]any{
|
||||
"goal": map[string]any{"type": "string"},
|
||||
"observedIssue": map[string]any{"type": "string"},
|
||||
"needTicket": map[string]any{"type": "boolean"},
|
||||
"needHumanHandoff": map[string]any{"type": "boolean"},
|
||||
"needQualityCheck": map[string]any{"type": "boolean"},
|
||||
"additionalContext": map[string]any{"type": "string"},
|
||||
})
|
||||
case toolx.GraphPrepareTicketDraft.Code:
|
||||
definition.InputSchema = objectSchema(map[string]any{
|
||||
"title": map[string]any{"type": "string"},
|
||||
"description": map[string]any{"type": "string"},
|
||||
"issue": map[string]any{"type": "string"},
|
||||
"impact": map[string]any{"type": "string"},
|
||||
"expectedOutcome": map[string]any{"type": "string"},
|
||||
"currentAttempt": map[string]any{"type": "string"},
|
||||
})
|
||||
case toolx.GraphCreateTicketConfirm.Code:
|
||||
definition.RiskLevel = RiskLevelWrite
|
||||
definition.RequireConfirmation = true
|
||||
definition.MaxCallsPerRun = 1
|
||||
definition.IdempotencyMode = "business"
|
||||
definition.InputSchema = requiredObjectSchema([]string{"title", "description"}, map[string]any{
|
||||
"title": map[string]any{"type": "string"}, "description": map[string]any{"type": "string"},
|
||||
"goal": map[string]any{"type": "string"},
|
||||
"observed_issue": map[string]any{"type": "string"},
|
||||
"need_human_handoff": map[string]any{"type": "boolean"},
|
||||
"need_quality_check": map[string]any{"type": "boolean"},
|
||||
"additional_context": map[string]any{"type": "string"},
|
||||
})
|
||||
case toolx.GraphHandoffConversation.Code:
|
||||
definition.RiskLevel = RiskLevelWrite
|
||||
|
||||
Reference in New Issue
Block a user