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