refactor(instruction): remove governance instruction handling and update related tests
This commit is contained in:
Vendored
+13
-8
@@ -72,17 +72,22 @@ func buildSeedItems(aiConfigID int64, knowledgeIDs []int64, defaultTeamIDs strin
|
|||||||
now := time.Now()
|
now := time.Now()
|
||||||
return []models.AIAgent{
|
return []models.AIAgent{
|
||||||
{
|
{
|
||||||
Name: "测试AI客服",
|
Name: "测试AI客服",
|
||||||
Description: "本地测试 AI 客服 Agent",
|
Description: "本地测试 AI 客服 Agent",
|
||||||
Status: enums.StatusOk,
|
Status: enums.StatusOk,
|
||||||
AIConfigID: aiConfigID,
|
AIConfigID: aiConfigID,
|
||||||
ServiceMode: enums.IMConversationServiceModeAIFirst,
|
ServiceMode: enums.IMConversationServiceModeAIFirst,
|
||||||
SystemPrompt: "你是一个友好的客服助手,请用中文回答用户的问题。",
|
SystemPrompt: `你正在一个有明确工程约束的客服系统中工作。
|
||||||
WelcomeMessage: "您好,欢迎咨询!有什么可以帮助您的?",
|
执行时必须严格遵守当前注入的 Agent 规则和技能规则。
|
||||||
|
如果存在工具白名单限制,只能调用当前允许的工具;信息不足时优先追问,不要伪造事实或跳过必要确认。
|
||||||
|
禁止承诺未经系统确认的处理时效、完成时间、回访时间或联系时间。
|
||||||
|
禁止代表人工团队、技术团队、售后团队承诺后续动作,除非当前上下文已有明确的工具结果、人工确认或知识库事实支持。
|
||||||
|
当用户只表示已发送资料、邮件、截图或附件时,只能确认已收到当前消息或建议等待人工确认,不能自行补充内部处理流程、SLA 或跟进安排。`,
|
||||||
|
WelcomeMessage: "您好,有什么可以帮助您的?",
|
||||||
ReplyTimeoutSeconds: 180,
|
ReplyTimeoutSeconds: 180,
|
||||||
TeamIDs: defaultTeamIDs,
|
TeamIDs: defaultTeamIDs,
|
||||||
HandoffMode: enums.AIAgentHandoffModeWaitPool,
|
HandoffMode: enums.AIAgentHandoffModeWaitPool,
|
||||||
FallbackMessage: "我暂时没有找到足够准确的信息。你可以补充订单号、产品名或更具体的问题,我再继续帮你查。",
|
FallbackMessage: "我暂时没有找到足够准确的信息。你可以补充具体的问题,我再继续帮你查。",
|
||||||
KnowledgeIDs: utils.JoinInt64s(knowledgeIDs),
|
KnowledgeIDs: utils.JoinInt64s(knowledgeIDs),
|
||||||
SkillIDs: defaultSkillIDs,
|
SkillIDs: defaultSkillIDs,
|
||||||
SortNo: 10,
|
SortNo: 10,
|
||||||
|
|||||||
@@ -5,18 +5,16 @@ import "strings"
|
|||||||
type Assembler struct{}
|
type Assembler struct{}
|
||||||
|
|
||||||
type AssemblerInput struct {
|
type AssemblerInput struct {
|
||||||
AgentInstruction string
|
AgentInstruction string
|
||||||
GovernanceInstruction string
|
SkillInstruction string
|
||||||
SkillInstruction string
|
ToolAppendices []string
|
||||||
ToolAppendices []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AssemblySummary struct {
|
type AssemblySummary struct {
|
||||||
SectionTitles []string
|
SectionTitles []string
|
||||||
HasGovernanceRule bool
|
HasAgentRule bool
|
||||||
HasAgentRule bool
|
HasSkillRule bool
|
||||||
HasSkillRule bool
|
HasToolRule bool
|
||||||
HasToolRule bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AssemblyResult struct {
|
type AssemblyResult struct {
|
||||||
@@ -33,14 +31,8 @@ func (a *Assembler) Build(input AssemblerInput) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Assembler) Assemble(input AssemblerInput) AssemblyResult {
|
func (a *Assembler) Assemble(input AssemblerInput) AssemblyResult {
|
||||||
parts := make([]string, 0, 4)
|
parts := make([]string, 0, 3)
|
||||||
summary := AssemblySummary{SectionTitles: make([]string, 0, 4)}
|
summary := AssemblySummary{SectionTitles: make([]string, 0, 3)}
|
||||||
governanceInstruction := strings.TrimSpace(input.GovernanceInstruction)
|
|
||||||
if governanceInstruction != "" {
|
|
||||||
parts = append(parts, buildInstructionSection("系统治理规则", governanceInstruction))
|
|
||||||
summary.HasGovernanceRule = true
|
|
||||||
summary.SectionTitles = append(summary.SectionTitles, "系统治理规则")
|
|
||||||
}
|
|
||||||
if agentInstruction := strings.TrimSpace(input.AgentInstruction); agentInstruction != "" {
|
if agentInstruction := strings.TrimSpace(input.AgentInstruction); agentInstruction != "" {
|
||||||
parts = append(parts, buildInstructionSection("Agent 规则", agentInstruction))
|
parts = append(parts, buildInstructionSection("Agent 规则", agentInstruction))
|
||||||
summary.HasAgentRule = true
|
summary.HasAgentRule = true
|
||||||
|
|||||||
@@ -7,13 +7,12 @@ import (
|
|||||||
|
|
||||||
func TestAssemblerRespectsProvidedSources(t *testing.T) {
|
func TestAssemblerRespectsProvidedSources(t *testing.T) {
|
||||||
result := NewAssembler().Assemble(AssemblerInput{
|
result := NewAssembler().Assemble(AssemblerInput{
|
||||||
GovernanceInstruction: "governance-rule",
|
AgentInstruction: "agent-rule",
|
||||||
AgentInstruction: "agent-rule",
|
SkillInstruction: "skill-rule",
|
||||||
SkillInstruction: "skill-rule",
|
ToolAppendices: []string{"tool-rule-1", "tool-rule-2"},
|
||||||
ToolAppendices: []string{"tool-rule-1", "tool-rule-2"},
|
|
||||||
})
|
})
|
||||||
if !strings.Contains(result.Text, "系统治理规则:\ngovernance-rule") {
|
if !strings.Contains(result.Text, "Agent 规则:\nagent-rule") {
|
||||||
t.Fatalf("missing governance instruction: %s", result.Text)
|
t.Fatalf("missing agent instruction: %s", result.Text)
|
||||||
}
|
}
|
||||||
if !strings.Contains(result.Text, "当前技能上下文:\nskill-rule") {
|
if !strings.Contains(result.Text, "当前技能上下文:\nskill-rule") {
|
||||||
t.Fatalf("missing skill instruction: %s", result.Text)
|
t.Fatalf("missing skill instruction: %s", result.Text)
|
||||||
@@ -21,17 +20,17 @@ func TestAssemblerRespectsProvidedSources(t *testing.T) {
|
|||||||
if !strings.Contains(result.Text, "工具补充规则:\ntool-rule-1") {
|
if !strings.Contains(result.Text, "工具补充规则:\ntool-rule-1") {
|
||||||
t.Fatalf("missing tool appendix: %s", result.Text)
|
t.Fatalf("missing tool appendix: %s", result.Text)
|
||||||
}
|
}
|
||||||
if !result.Summary.HasGovernanceRule || !result.Summary.HasAgentRule || !result.Summary.HasSkillRule || !result.Summary.HasToolRule {
|
if !result.Summary.HasAgentRule || !result.Summary.HasSkillRule || !result.Summary.HasToolRule {
|
||||||
t.Fatalf("unexpected summary: %#v", result.Summary)
|
t.Fatalf("unexpected summary: %#v", result.Summary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAssemblerDoesNotInjectGovernanceInstructionWhenInputIsEmpty(t *testing.T) {
|
func TestAssemblerReturnsEmptyTextWhenInputIsEmpty(t *testing.T) {
|
||||||
result := NewAssembler().Assemble(AssemblerInput{})
|
result := NewAssembler().Assemble(AssemblerInput{})
|
||||||
if result.Text != "" {
|
if result.Text != "" {
|
||||||
t.Fatalf("expected empty assembled text, got: %s", result.Text)
|
t.Fatalf("expected empty assembled text, got: %s", result.Text)
|
||||||
}
|
}
|
||||||
if result.Summary.HasGovernanceRule {
|
if len(result.Summary.SectionTitles) != 0 || result.Summary.HasAgentRule || result.Summary.HasSkillRule || result.Summary.HasToolRule {
|
||||||
t.Fatalf("expected no governance rule summary, got %#v", result.Summary)
|
t.Fatalf("expected empty summary, got %#v", result.Summary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,17 @@
|
|||||||
package instruction
|
package instruction
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"cs-agent/internal/ai/runtime/tooling"
|
||||||
|
|
||||||
runtimetooling "cs-agent/internal/ai/runtime/tooling"
|
|
||||||
"cs-agent/internal/models"
|
"cs-agent/internal/models"
|
||||||
"cs-agent/internal/pkg/toolx"
|
"cs-agent/internal/pkg/toolx"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultGovernanceInstruction = `
|
|
||||||
你正在一个有明确工程约束的客服系统中工作。
|
|
||||||
执行时必须严格遵守当前注入的 Agent 规则和技能规则。
|
|
||||||
如果存在工具白名单限制,只能调用当前允许的工具;信息不足时优先追问,不要伪造事实或跳过必要确认。
|
|
||||||
禁止承诺未经系统确认的处理时效、完成时间、回访时间或联系时间。
|
|
||||||
禁止代表人工团队、技术团队、售后团队承诺后续动作,除非当前上下文已有明确的工具结果、人工确认或知识库事实支持。
|
|
||||||
当用户只表示已发送资料、邮件、截图或附件时,只能确认已收到当前消息或建议等待人工确认,不能自行补充内部处理流程、SLA 或跟进安排。
|
|
||||||
`
|
|
||||||
|
|
||||||
type ToolAppendixProvider struct{}
|
type ToolAppendixProvider struct{}
|
||||||
|
|
||||||
func NewToolAppendixProvider() *ToolAppendixProvider {
|
func NewToolAppendixProvider() *ToolAppendixProvider {
|
||||||
return &ToolAppendixProvider{}
|
return &ToolAppendixProvider{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type GovernanceInstructionProvider struct{}
|
|
||||||
|
|
||||||
func NewGovernanceInstructionProvider() *GovernanceInstructionProvider {
|
|
||||||
return &GovernanceInstructionProvider{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *GovernanceInstructionProvider) Resolve() string {
|
|
||||||
return strings.TrimSpace(defaultGovernanceInstruction)
|
|
||||||
}
|
|
||||||
|
|
||||||
type SkillInstructionProvider struct{}
|
type SkillInstructionProvider struct{}
|
||||||
|
|
||||||
func NewSkillInstructionProvider() *SkillInstructionProvider {
|
func NewSkillInstructionProvider() *SkillInstructionProvider {
|
||||||
@@ -43,7 +22,7 @@ func (p *SkillInstructionProvider) Resolve(selectedSkill *models.SkillDefinition
|
|||||||
return BuildSelectedSkillActivationInstruction(selectedSkill)
|
return BuildSelectedSkillActivationInstruction(selectedSkill)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ToolAppendixProvider) Build(toolDefinitions []runtimetooling.MCPToolDefinition, extraToolCodes map[string]string) []string {
|
func (p *ToolAppendixProvider) Build(toolDefinitions []tooling.MCPToolDefinition, extraToolCodes map[string]string) []string {
|
||||||
appendixParts := make([]string, 0, 1)
|
appendixParts := make([]string, 0, 1)
|
||||||
toolCodes := make([]string, 0, len(toolDefinitions)+len(extraToolCodes))
|
toolCodes := make([]string, 0, len(toolDefinitions)+len(extraToolCodes))
|
||||||
for _, item := range toolDefinitions {
|
for _, item := range toolDefinitions {
|
||||||
|
|||||||
@@ -3,38 +3,45 @@ package instruction
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"cs-agent/internal/ai/runtime/tooling"
|
runtimetooling "cs-agent/internal/ai/runtime/tooling"
|
||||||
"cs-agent/internal/models"
|
"cs-agent/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
assembler *Assembler
|
assembler *Assembler
|
||||||
governanceInstructionProvider *GovernanceInstructionProvider
|
skillInstructionProvider *SkillInstructionProvider
|
||||||
skillInstructionProvider *SkillInstructionProvider
|
toolAppendixProvider *ToolAppendixProvider
|
||||||
toolAppendixProvider *ToolAppendixProvider
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService() *Service {
|
func NewService(
|
||||||
|
assembler *Assembler,
|
||||||
|
skillProvider *SkillInstructionProvider,
|
||||||
|
toolProvider *ToolAppendixProvider,
|
||||||
|
) *Service {
|
||||||
|
if assembler == nil {
|
||||||
|
assembler = NewAssembler()
|
||||||
|
}
|
||||||
|
if skillProvider == nil {
|
||||||
|
skillProvider = NewSkillInstructionProvider()
|
||||||
|
}
|
||||||
|
if toolProvider == nil {
|
||||||
|
toolProvider = NewToolAppendixProvider()
|
||||||
|
}
|
||||||
return &Service{
|
return &Service{
|
||||||
assembler: NewAssembler(),
|
assembler: assembler,
|
||||||
governanceInstructionProvider: NewGovernanceInstructionProvider(),
|
skillInstructionProvider: skillProvider,
|
||||||
skillInstructionProvider: NewSkillInstructionProvider(),
|
toolAppendixProvider: toolProvider,
|
||||||
toolAppendixProvider: NewToolAppendixProvider(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Build(
|
func (s *Service) Build(
|
||||||
aiAgent models.AIAgent,
|
aiAgent models.AIAgent,
|
||||||
selectedSkill *models.SkillDefinition,
|
selectedSkill *models.SkillDefinition,
|
||||||
toolDefinitions []tooling.MCPToolDefinition,
|
toolDefinitions []runtimetooling.MCPToolDefinition,
|
||||||
extraToolCodes map[string]string,
|
extraToolCodes map[string]string,
|
||||||
) AssemblyResult {
|
) AssemblyResult {
|
||||||
governanceInstruction := ""
|
|
||||||
skillInstruction := ""
|
skillInstruction := ""
|
||||||
toolAppendices := make([]string, 0)
|
toolAppendices := make([]string, 0)
|
||||||
if s != nil && s.governanceInstructionProvider != nil {
|
|
||||||
governanceInstruction = s.governanceInstructionProvider.Resolve()
|
|
||||||
}
|
|
||||||
if s != nil && s.skillInstructionProvider != nil {
|
if s != nil && s.skillInstructionProvider != nil {
|
||||||
skillInstruction = s.skillInstructionProvider.Resolve(selectedSkill)
|
skillInstruction = s.skillInstructionProvider.Resolve(selectedSkill)
|
||||||
}
|
}
|
||||||
@@ -46,9 +53,8 @@ func (s *Service) Build(
|
|||||||
assembler = s.assembler
|
assembler = s.assembler
|
||||||
}
|
}
|
||||||
return assembler.Assemble(AssemblerInput{
|
return assembler.Assemble(AssemblerInput{
|
||||||
AgentInstruction: strings.TrimSpace(aiAgent.SystemPrompt),
|
AgentInstruction: strings.TrimSpace(aiAgent.SystemPrompt),
|
||||||
GovernanceInstruction: governanceInstruction,
|
SkillInstruction: skillInstruction,
|
||||||
SkillInstruction: skillInstruction,
|
ToolAppendices: toolAppendices,
|
||||||
ToolAppendices: toolAppendices,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ func (c *RuntimeTraceCollector) SetInstructionSummary(summary InstructionTraceSu
|
|||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
c.Data.Instruction.SectionTitles = append([]string(nil), summary.SectionTitles...)
|
c.Data.Instruction.SectionTitles = append([]string(nil), summary.SectionTitles...)
|
||||||
c.Data.Instruction.HasGovernanceRule = summary.HasGovernanceRule
|
|
||||||
c.Data.Instruction.HasAgentRule = summary.HasAgentRule
|
c.Data.Instruction.HasAgentRule = summary.HasAgentRule
|
||||||
c.Data.Instruction.HasSkillRule = summary.HasSkillRule
|
c.Data.Instruction.HasSkillRule = summary.HasSkillRule
|
||||||
c.Data.Instruction.HasToolRule = summary.HasToolRule
|
c.Data.Instruction.HasToolRule = summary.HasToolRule
|
||||||
|
|||||||
@@ -70,11 +70,10 @@ type RetrieverPolicyTraceItem struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type InstructionTraceSummary struct {
|
type InstructionTraceSummary struct {
|
||||||
SectionTitles []string
|
SectionTitles []string
|
||||||
HasGovernanceRule bool
|
HasAgentRule bool
|
||||||
HasAgentRule bool
|
HasSkillRule bool
|
||||||
HasSkillRule bool
|
HasToolRule bool
|
||||||
HasToolRule bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuntimeTraceData struct {
|
type RuntimeTraceData struct {
|
||||||
@@ -91,11 +90,10 @@ type RuntimeTraceData struct {
|
|||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
} `json:"model"`
|
} `json:"model"`
|
||||||
Instruction struct {
|
Instruction struct {
|
||||||
SectionTitles []string `json:"sectionTitles,omitempty"`
|
SectionTitles []string `json:"sectionTitles,omitempty"`
|
||||||
HasGovernanceRule bool `json:"hasGovernanceRule,omitempty"`
|
HasAgentRule bool `json:"hasAgentRule,omitempty"`
|
||||||
HasAgentRule bool `json:"hasAgentRule,omitempty"`
|
HasSkillRule bool `json:"hasSkillRule,omitempty"`
|
||||||
HasSkillRule bool `json:"hasSkillRule,omitempty"`
|
HasToolRule bool `json:"hasToolRule,omitempty"`
|
||||||
HasToolRule bool `json:"hasToolRule,omitempty"`
|
|
||||||
} `json:"instruction"`
|
} `json:"instruction"`
|
||||||
Input struct {
|
Input struct {
|
||||||
HistoryMessageCount int `json:"historyMessageCount,omitempty"`
|
HistoryMessageCount int `json:"historyMessageCount,omitempty"`
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ func NewAgentFactory() *AgentFactory {
|
|||||||
return &AgentFactory{
|
return &AgentFactory{
|
||||||
chatModelFactory: NewChatModelFactory(),
|
chatModelFactory: NewChatModelFactory(),
|
||||||
toolFactory: NewToolFactory(),
|
toolFactory: NewToolFactory(),
|
||||||
instructionService: instruction.NewService(),
|
instructionService: instruction.NewService(nil, nil, nil),
|
||||||
handlerService: NewAgentHandlerService(nil),
|
handlerService: NewAgentHandlerService(nil),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ func TestAgentHandlerServiceBuildWithCollectorOnly(t *testing.T) {
|
|||||||
handlers, err := service.Build(context.Background(), BuildAgentHandlersInput{
|
handlers, err := service.Build(context.Background(), BuildAgentHandlersInput{
|
||||||
Collector: collector,
|
Collector: collector,
|
||||||
InstructionSummary: einocallbacks.InstructionTraceSummary{
|
InstructionSummary: einocallbacks.InstructionTraceSummary{
|
||||||
SectionTitles: []string{"系统治理规则"},
|
SectionTitles: []string{"Agent 规则"},
|
||||||
HasGovernanceRule: true,
|
HasAgentRule: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -24,7 +24,7 @@ func TestAgentHandlerServiceBuildWithCollectorOnly(t *testing.T) {
|
|||||||
if len(handlers) != 1 {
|
if len(handlers) != 1 {
|
||||||
t.Fatalf("expected 1 handler, got %d", len(handlers))
|
t.Fatalf("expected 1 handler, got %d", len(handlers))
|
||||||
}
|
}
|
||||||
if !collector.Data.Instruction.HasGovernanceRule {
|
if !collector.Data.Instruction.HasAgentRule {
|
||||||
t.Fatalf("instruction summary was not written to collector: %#v", collector.Data.Instruction)
|
t.Fatalf("instruction summary was not written to collector: %#v", collector.Data.Instruction)
|
||||||
}
|
}
|
||||||
if len(collector.Data.Instruction.SectionTitles) != 1 {
|
if len(collector.Data.Instruction.SectionTitles) != 1 {
|
||||||
|
|||||||
@@ -14,11 +14,10 @@ import (
|
|||||||
|
|
||||||
func buildInstructionTraceSummary(summary runtimeinstruction.AssemblySummary) einocallbacks.InstructionTraceSummary {
|
func buildInstructionTraceSummary(summary runtimeinstruction.AssemblySummary) einocallbacks.InstructionTraceSummary {
|
||||||
return einocallbacks.InstructionTraceSummary{
|
return einocallbacks.InstructionTraceSummary{
|
||||||
SectionTitles: append([]string(nil), summary.SectionTitles...),
|
SectionTitles: append([]string(nil), summary.SectionTitles...),
|
||||||
HasGovernanceRule: summary.HasGovernanceRule,
|
HasAgentRule: summary.HasAgentRule,
|
||||||
HasAgentRule: summary.HasAgentRule,
|
HasSkillRule: summary.HasSkillRule,
|
||||||
HasSkillRule: summary.HasSkillRule,
|
HasToolRule: summary.HasToolRule,
|
||||||
HasToolRule: summary.HasToolRule,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,17 +8,16 @@ import (
|
|||||||
|
|
||||||
func TestBuildInstructionTraceSummary(t *testing.T) {
|
func TestBuildInstructionTraceSummary(t *testing.T) {
|
||||||
got := buildInstructionTraceSummary(runtimeinstruction.AssemblySummary{
|
got := buildInstructionTraceSummary(runtimeinstruction.AssemblySummary{
|
||||||
SectionTitles: []string{"系统治理规则", "当前技能上下文"},
|
SectionTitles: []string{"Agent 规则", "当前技能上下文"},
|
||||||
HasGovernanceRule: true,
|
HasAgentRule: true,
|
||||||
HasAgentRule: true,
|
HasSkillRule: true,
|
||||||
HasSkillRule: true,
|
HasToolRule: false,
|
||||||
HasToolRule: false,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if len(got.SectionTitles) != 2 {
|
if len(got.SectionTitles) != 2 {
|
||||||
t.Fatalf("unexpected section titles: %#v", got.SectionTitles)
|
t.Fatalf("unexpected section titles: %#v", got.SectionTitles)
|
||||||
}
|
}
|
||||||
if !got.HasGovernanceRule || !got.HasAgentRule || !got.HasSkillRule {
|
if !got.HasAgentRule || !got.HasSkillRule {
|
||||||
t.Fatalf("unexpected summary flags: %#v", got)
|
t.Fatalf("unexpected summary flags: %#v", got)
|
||||||
}
|
}
|
||||||
if got.HasToolRule {
|
if got.HasToolRule {
|
||||||
|
|||||||
Reference in New Issue
Block a user