feat(i18n): Enhance internationalization support for tool specifications and notifications

- Added TitleKey and DescriptionKey fields to ToolSpec for dynamic localization.
- Updated tool specifications to use i18nx for titles, descriptions, and appendices.
- Refactored notification messages to utilize i18nx for localization in conversation and ticket assignment events.
- Improved localization in the debug dialog component for skill definitions.
- Added new translation keys in English and Chinese for ticket and conversation assignment notifications.
- Ensured that fallback messages are properly localized based on user locale.
This commit is contained in:
mlogclub
2026-06-03 09:36:33 +08:00
parent 5b6c54de34
commit 3439d20537
23 changed files with 520 additions and 265 deletions
@@ -10,6 +10,7 @@ import (
"agent-desk/internal/models"
"agent-desk/internal/pkg/dto"
"agent-desk/internal/pkg/dto/request"
"agent-desk/internal/pkg/i18nx"
"agent-desk/internal/services"
componenttool "github.com/cloudwego/eino/components/tool"
@@ -89,7 +90,7 @@ func (g *CreateTicketGraph) Run(ctx context.Context, argumentsInJSON string) (st
Handled: true,
Terminal: true,
Action: "ticket_created",
ReplyText: fmt.Sprintf("Ticket created. Ticket no: %s. Title: %s.", strings.TrimSpace(item.TicketNo), strings.TrimSpace(item.Title)),
ReplyText: i18nx.Getf(i18nx.DefaultLocale, "graph.ticketCreated", strings.TrimSpace(item.TicketNo), strings.TrimSpace(item.Title)),
ShouldRetry: false,
}), nil
case ConfirmationDecisionCancel:
@@ -134,7 +135,7 @@ func (g *CreateTicketGraph) buildCreateRequest(argumentsInJSON string) (request.
}
func (g *CreateTicketGraph) buildConfirmationPrompt(req request.CreateTicketFromConversationRequest) string {
return fmt.Sprintf("I am ready to create a ticket for you.\nTitle: %s\nDescription: %s\nPlease reply with \"Confirm\" or \"Cancel\".",
return i18nx.Getf(i18nx.DefaultLocale, "graph.createTicketConfirmPrompt",
strings.TrimSpace(req.Title), strings.TrimSpace(req.Description))
}
+3 -2
View File
@@ -8,6 +8,7 @@ import (
"agent-desk/internal/ai/runtime/tooling"
"agent-desk/internal/models"
"agent-desk/internal/pkg/i18nx"
"agent-desk/internal/pkg/tracex"
"agent-desk/internal/services"
@@ -122,7 +123,7 @@ func (g *HandoffGraph) Run(ctx context.Context, argumentsInJSON string) (string,
}
func (g *HandoffGraph) buildReason(argumentsInJSON string) (string, error) {
reason := "The user needs human support."
reason := i18nx.Get("graph.defaultHandoffReason")
var args handoffGraphArgs
if strings.TrimSpace(argumentsInJSON) != "" {
if err := json.Unmarshal([]byte(argumentsInJSON), &args); err != nil {
@@ -136,7 +137,7 @@ func (g *HandoffGraph) buildReason(argumentsInJSON string) (string, error) {
}
func (g *HandoffGraph) buildConfirmationPrompt(reason string) string {
return fmt.Sprintf("I am ready to connect you to a human support agent.\nReason: %s\nPlease reply with \"Confirm\" or \"Cancel\".", strings.TrimSpace(reason))
return i18nx.Getf(i18nx.DefaultLocale, "graph.handoffConfirmPrompt", strings.TrimSpace(reason))
}
func parseHandoffDecision(value string) ConfirmationDecision {
+13 -6
View File
@@ -1,15 +1,22 @@
package graphs
import "strings"
import (
"strings"
"agent-desk/internal/pkg/i18nx"
)
const (
InterruptTypeTicketCreationConfirmation = "ticket_creation_confirmation"
InterruptTypeHandoffConfirmation = "handoff_confirmation"
ConfirmOrCancelPrompt = `Please reply with "Confirm" or "Cancel".`
NeedExplicitConfirmationPrompt = `I need your explicit confirmation. Please reply with "Confirm" or "Cancel".`
ConfirmationExpiredReply = "This confirmation has expired. Please start again."
CancelCreateTicketReply = "Ticket creation has been cancelled."
CancelHandoffReply = "Human handoff has been cancelled."
)
var (
ConfirmOrCancelPrompt = i18nx.Get("graph.confirmOrCancel")
NeedExplicitConfirmationPrompt = i18nx.Get("graph.needExplicitConfirmation")
ConfirmationExpiredReply = i18nx.Get("graph.confirmationExpired")
CancelCreateTicketReply = i18nx.Get("graph.cancelCreateTicket")
CancelHandoffReply = i18nx.Get("graph.cancelHandoff")
)
type ConfirmationDecision string
@@ -7,6 +7,7 @@ import (
applicationruntime "agent-desk/internal/ai/application/runtime"
"agent-desk/internal/models"
"agent-desk/internal/pkg/i18nx"
svc "agent-desk/internal/services"
)
@@ -39,7 +40,7 @@ func buildConversationInterrupt(conversation models.Conversation, message models
func resolveInterruptPrompt(summary *applicationruntime.Summary) string {
if summary == nil || len(summary.Interrupts) == 0 {
return "Please provide more information and try again."
return i18nx.Get("conversation.interrupt.defaultPrompt")
}
if prompt := extractInterruptMessage(summary.Interrupts[0].InfoPreview); prompt != "" {
return prompt
@@ -47,7 +48,7 @@ func resolveInterruptPrompt(summary *applicationruntime.Summary) string {
if prompt := strings.TrimSpace(summary.Interrupts[0].InfoPreview); prompt != "" {
return prompt
}
return "Please provide more information and try again."
return i18nx.Get("conversation.interrupt.defaultPrompt")
}
func extractInterruptMessage(infoPreview string) string {
@@ -6,6 +6,7 @@ import (
"agent-desk/internal/ai/runtime/graphs"
"agent-desk/internal/ai/runtime/registry"
"agent-desk/internal/models"
"agent-desk/internal/pkg/i18nx"
"agent-desk/internal/pkg/toolx"
einotool "github.com/cloudwego/eino/components/tool"
@@ -48,7 +49,7 @@ func (t *AnalyzeConversationTool) Build(ctx registry.Context) (einotool.BaseTool
func (t *AnalyzeConversationTool) Info(ctx context.Context) (*schema.ToolInfo, error) {
return &schema.ToolInfo{
Name: toolx.GraphAnalyzeConversation.Name,
Desc: "Graph Tool. Summarizes the current conversation, identifies complaint/payment/sentiment risk signals, and recommends whether to continue answering, create a ticket, or hand off to a human.",
Desc: i18nx.Get("tool.graph.analyzeConversation.info"),
ParamsOneOf: schema.NewParamsOneOfByJSONSchema(&einojsonschema.Schema{
Version: einojsonschema.Version,
Type: "object",
@@ -57,42 +58,42 @@ func (t *AnalyzeConversationTool) Info(ctx context.Context) (*schema.ToolInfo, e
Key: "goal",
Value: &einojsonschema.Schema{
Type: "string",
Description: "Analysis goal, such as whether to hand off to a human, create a ticket, or perform risk review.",
Description: i18nx.Get("tool.graph.analyzeConversation.param.goal"),
},
},
orderedmap.Pair[string, *einojsonschema.Schema]{
Key: "observedIssue",
Value: &einojsonschema.Schema{
Type: "string",
Description: "Main issue or request observed in the conversation.",
Description: i18nx.Get("tool.graph.analyzeConversation.param.observedIssue"),
},
},
orderedmap.Pair[string, *einojsonschema.Schema]{
Key: "needTicket",
Value: &einojsonschema.Schema{
Type: "boolean",
Description: "Whether to focus on evaluating ticket creation.",
Description: i18nx.Get("tool.graph.triageServiceRequest.param.needTicket"),
},
},
orderedmap.Pair[string, *einojsonschema.Schema]{
Key: "needHumanHandoff",
Value: &einojsonschema.Schema{
Type: "boolean",
Description: "Whether to focus on evaluating human handoff.",
Description: i18nx.Get("tool.graph.triageServiceRequest.param.needHumanHandoff"),
},
},
orderedmap.Pair[string, *einojsonschema.Schema]{
Key: "needQualityCheck",
Value: &einojsonschema.Schema{
Type: "boolean",
Description: "Whether to focus on risk or quality review.",
Description: i18nx.Get("tool.graph.analyzeConversation.param.needQualityCheck"),
},
},
orderedmap.Pair[string, *einojsonschema.Schema]{
Key: "additionalContext",
Value: &einojsonschema.Schema{
Type: "string",
Description: "Additional context, such as disputes, complaint points, or business constraints already identified.",
Description: i18nx.Get("tool.graph.analyzeConversation.param.additionalContext"),
},
},
)),
@@ -6,6 +6,7 @@ import (
"agent-desk/internal/ai/runtime/graphs"
"agent-desk/internal/ai/runtime/registry"
"agent-desk/internal/models"
"agent-desk/internal/pkg/i18nx"
"agent-desk/internal/pkg/toolx"
einotool "github.com/cloudwego/eino/components/tool"
@@ -52,7 +53,7 @@ func (t *CreateTicketGraphTool) Build(ctx registry.Context) (einotool.BaseTool,
func (t *CreateTicketGraphTool) Info(ctx context.Context) (*schema.ToolInfo, error) {
return &schema.ToolInfo{
Name: toolx.GraphCreateTicketConfirm.Name,
Desc: "Graph Tool. Handles ticket parameter preparation, user confirmation, actual ticket creation, and result return. Use only when the user explicitly asks to create a ticket and the title and description are clear.",
Desc: i18nx.Get("tool.graph.createTicketConfirm.info"),
ParamsOneOf: schema.NewParamsOneOfByJSONSchema(&einojsonschema.Schema{
Version: einojsonschema.Version,
Type: "object",
@@ -65,14 +66,14 @@ func (t *CreateTicketGraphTool) Info(ctx context.Context) (*schema.ToolInfo, err
Key: "title",
Value: &einojsonschema.Schema{
Type: "string",
Description: "Ticket title. Concisely summarizes the issue.",
Description: i18nx.Get("tool.graph.createTicketConfirm.param.title"),
},
},
orderedmap.Pair[string, *einojsonschema.Schema]{
Key: "description",
Value: &einojsonschema.Schema{
Type: "string",
Description: "Ticket description. Clearly captures the user's issue, symptoms, and request.",
Description: i18nx.Get("tool.graph.createTicketConfirm.param.description"),
},
},
)),
@@ -6,6 +6,7 @@ import (
"agent-desk/internal/ai/runtime/graphs"
"agent-desk/internal/ai/runtime/registry"
"agent-desk/internal/models"
"agent-desk/internal/pkg/i18nx"
"agent-desk/internal/pkg/toolx"
einotool "github.com/cloudwego/eino/components/tool"
@@ -52,7 +53,7 @@ func (t *HandoffGraphTool) Build(ctx registry.Context) (einotool.BaseTool, error
func (t *HandoffGraphTool) Info(ctx context.Context) (*schema.ToolInfo, error) {
return &schema.ToolInfo{
Name: toolx.GraphHandoffConversation.Name,
Desc: "Graph Tool. Handles handoff reason preparation, user confirmation, actual human handoff, and result return. Use only when the user explicitly asks for a human agent or you have confirmed that human handling is required. Do not repeat the call when the result has terminal=true and shouldRetry=false.",
Desc: i18nx.Get("tool.graph.handoffConversation.info"),
ParamsOneOf: schema.NewParamsOneOfByJSONSchema(&einojsonschema.Schema{
Version: einojsonschema.Version,
Type: "object",
@@ -61,7 +62,7 @@ func (t *HandoffGraphTool) Info(ctx context.Context) (*schema.ToolInfo, error) {
Key: "reason",
Value: &einojsonschema.Schema{
Type: "string",
Description: "Handoff reason. Briefly explain why a human is needed, such as explicit user request, manual verification, or after-sales handling.",
Description: i18nx.Get("tool.graph.handoffConversation.param.reason"),
},
},
)),
@@ -6,6 +6,7 @@ import (
"agent-desk/internal/ai/runtime/graphs"
"agent-desk/internal/ai/runtime/registry"
"agent-desk/internal/models"
"agent-desk/internal/pkg/i18nx"
"agent-desk/internal/pkg/toolx"
einotool "github.com/cloudwego/eino/components/tool"
@@ -48,7 +49,7 @@ func (t *PrepareTicketDraftTool) Build(ctx registry.Context) (einotool.BaseTool,
func (t *PrepareTicketDraftTool) Info(ctx context.Context) (*schema.ToolInfo, error) {
return &schema.ToolInfo{
Name: toolx.GraphPrepareTicketDraft.Name,
Desc: "Graph Tool. Prepares a ticket draft from the current conversation and collected information. Use it before create_ticket_with_confirmation when the ticket content needs to be organized.",
Desc: i18nx.Get("tool.graph.prepareTicketDraft.info"),
ParamsOneOf: schema.NewParamsOneOfByJSONSchema(&einojsonschema.Schema{
Version: einojsonschema.Version,
Type: "object",
@@ -57,42 +58,42 @@ func (t *PrepareTicketDraftTool) Info(ctx context.Context) (*schema.ToolInfo, er
Key: "title",
Value: &einojsonschema.Schema{
Type: "string",
Description: "Prepared ticket title. Optional.",
Description: i18nx.Get("tool.graph.prepareTicketDraft.param.title"),
},
},
orderedmap.Pair[string, *einojsonschema.Schema]{
Key: "description",
Value: &einojsonschema.Schema{
Type: "string",
Description: "Prepared ticket description. Optional.",
Description: i18nx.Get("tool.graph.prepareTicketDraft.param.description"),
},
},
orderedmap.Pair[string, *einojsonschema.Schema]{
Key: "issue",
Value: &einojsonschema.Schema{
Type: "string",
Description: "The issue or error message the user is experiencing.",
Description: i18nx.Get("tool.graph.prepareTicketDraft.param.issue"),
},
},
orderedmap.Pair[string, *einojsonschema.Schema]{
Key: "impact",
Value: &einojsonschema.Schema{
Type: "string",
Description: "Impact scope, such as unable to sign in, unable to place an order, or business interruption.",
Description: i18nx.Get("tool.graph.prepareTicketDraft.param.impact"),
},
},
orderedmap.Pair[string, *einojsonschema.Schema]{
Key: "expectedOutcome",
Value: &einojsonschema.Schema{
Type: "string",
Description: "The user's expected outcome or request.",
Description: i18nx.Get("tool.graph.prepareTicketDraft.param.expectedOutcome"),
},
},
orderedmap.Pair[string, *einojsonschema.Schema]{
Key: "currentAttempt",
Value: &einojsonschema.Schema{
Type: "string",
Description: "当前已尝试过的处理步骤,可选。",
Description: i18nx.Get("tool.graph.prepareTicketDraft.param.currentAttempt"),
},
},
)),
@@ -6,6 +6,7 @@ import (
"agent-desk/internal/ai/runtime/graphs"
"agent-desk/internal/ai/runtime/registry"
"agent-desk/internal/models"
"agent-desk/internal/pkg/i18nx"
"agent-desk/internal/pkg/toolx"
einotool "github.com/cloudwego/eino/components/tool"
@@ -48,7 +49,7 @@ func (t *TriageServiceRequestTool) Build(ctx registry.Context) (einotool.BaseToo
func (t *TriageServiceRequestTool) Info(ctx context.Context) (*schema.ToolInfo, error) {
return &schema.ToolInfo{
Name: toolx.GraphTriageServiceRequest.Name,
Desc: "Graph Tool. Analyzes the current conversation to decide whether to continue answering, prepare a ticket draft, or hand off to a human. When ticket creation is recommended, it returns a structured ticket draft suggestion.",
Desc: i18nx.Get("tool.graph.triageServiceRequest.info"),
ParamsOneOf: schema.NewParamsOneOfByJSONSchema(&einojsonschema.Schema{
Version: einojsonschema.Version,
Type: "object",
@@ -57,35 +58,35 @@ func (t *TriageServiceRequestTool) Info(ctx context.Context) (*schema.ToolInfo,
Key: "goal",
Value: &einojsonschema.Schema{
Type: "string",
Description: "Analysis goal, such as whether to escalate, create a ticket, or hand off to a human.",
Description: i18nx.Get("tool.graph.triageServiceRequest.param.goal"),
},
},
orderedmap.Pair[string, *einojsonschema.Schema]{
Key: "observedIssue",
Value: &einojsonschema.Schema{
Type: "string",
Description: "Main issue or dispute observed in the conversation.",
Description: i18nx.Get("tool.graph.triageServiceRequest.param.observedIssue"),
},
},
orderedmap.Pair[string, *einojsonschema.Schema]{
Key: "needTicket",
Value: &einojsonschema.Schema{
Type: "boolean",
Description: "Whether to focus on evaluating ticket creation.",
Description: i18nx.Get("tool.graph.triageServiceRequest.param.needTicket"),
},
},
orderedmap.Pair[string, *einojsonschema.Schema]{
Key: "needHumanHandoff",
Value: &einojsonschema.Schema{
Type: "boolean",
Description: "Whether to focus on evaluating human handoff.",
Description: i18nx.Get("tool.graph.triageServiceRequest.param.needHumanHandoff"),
},
},
orderedmap.Pair[string, *einojsonschema.Schema]{
Key: "additionalContext",
Value: &einojsonschema.Schema{
Type: "string",
Description: "Additional context, such as risk signals or constraints already identified.",
Description: i18nx.Get("tool.graph.triageServiceRequest.param.additionalContext"),
},
},
)),