Refactor localization strings to English across various services and UI components
- Updated titles and subtitles in channel_service.go for web channel and WeChat MP channel configurations. - Changed handoff messages in conversation_human_dispatch_service.go to English. - Modified notification messages in event handlers for ticket and conversation assignments to English. - Adjusted ticket creation messages in ticket_service.go to reflect English localization. - Updated default locale settings in i18n configuration files to English (en-US). - Changed HTML language attribute in layout.tsx to English. - Refactored widget locale detection logic in agent-desk-sdk.ts to prioritize English.
This commit is contained in:
@@ -18,7 +18,7 @@ const (
|
||||
const (
|
||||
BootstrapAdminUsername = "admin"
|
||||
BootstrapAdminPassword = "ChangeMe123!"
|
||||
BootstrapAdminNickname = "超级管理员"
|
||||
BootstrapAdminNickname = "Super Admin"
|
||||
)
|
||||
|
||||
// Permission 权限结构体
|
||||
@@ -278,11 +278,111 @@ var PermissionMap = make(map[string]Permission)
|
||||
|
||||
// init 初始化 PermissionMap
|
||||
func init() {
|
||||
normalizeBuiltinPermissionNames()
|
||||
for _, permission := range Permissions {
|
||||
PermissionMap[permission.Code] = permission
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeBuiltinPermissionNames() {
|
||||
for i := range Permissions {
|
||||
Permissions[i].Name = builtinPermissionName(Permissions[i].Code, Permissions[i].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func builtinPermissionName(code string, fallback string) string {
|
||||
if name, ok := builtinPermissionNameOverrides[code]; ok {
|
||||
return name
|
||||
}
|
||||
resourceKey, actionKey, ok := splitPermissionCode(code)
|
||||
if !ok {
|
||||
return fallback
|
||||
}
|
||||
action, ok := builtinPermissionActionLabels[actionKey]
|
||||
if !ok {
|
||||
return fallback
|
||||
}
|
||||
resource, ok := builtinPermissionResourceLabels[resourceKey]
|
||||
if !ok {
|
||||
return fallback
|
||||
}
|
||||
return action + " " + resource
|
||||
}
|
||||
|
||||
func splitPermissionCode(code string) (string, string, bool) {
|
||||
for i := 0; i < len(code); i++ {
|
||||
if code[i] == '.' {
|
||||
return code[:i], code[i+1:], i > 0 && i < len(code)-1
|
||||
}
|
||||
}
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
var builtinPermissionActionLabels = map[string]string{
|
||||
"view": "View",
|
||||
"create": "Create",
|
||||
"update": "Update",
|
||||
"delete": "Delete",
|
||||
"assignRole": "Assign roles to",
|
||||
"assignPermission": "Assign permissions to",
|
||||
"sync": "Sync",
|
||||
"revoke": "Revoke",
|
||||
"assign": "Assign",
|
||||
"transfer": "Transfer",
|
||||
"close": "Close",
|
||||
"send": "Send",
|
||||
"tag": "Manage tags for",
|
||||
"handover": "Handle handoffs for",
|
||||
"recycle": "Recycle",
|
||||
"linkCustomer": "Link customers to",
|
||||
"changeStatus": "Change status for",
|
||||
"progress": "Update progress for",
|
||||
"updateStatus": "Update status for",
|
||||
"config": "Configure service rules for",
|
||||
"batchGenerate": "Batch generate",
|
||||
"call": "Call",
|
||||
}
|
||||
|
||||
var builtinPermissionResourceLabels = map[string]string{
|
||||
"user": "users",
|
||||
"role": "roles",
|
||||
"permission": "permissions",
|
||||
"session": "sessions",
|
||||
"conversation": "conversations",
|
||||
"ticket": "tickets",
|
||||
"notification": "notifications",
|
||||
"quickReply": "quick replies",
|
||||
"tag": "tags",
|
||||
"company": "companies",
|
||||
"channel": "channels",
|
||||
"customer": "customers",
|
||||
"agent": "agents",
|
||||
"agentTeam": "agent teams",
|
||||
"agentTeamSchedule": "agent team schedules",
|
||||
"asset": "file assets",
|
||||
"aiAgent": "AI Agents",
|
||||
"aiConfig": "AI configurations",
|
||||
"knowledgeBase": "knowledge bases",
|
||||
"knowledgeDocument": "knowledge documents",
|
||||
"knowledgeFAQ": "knowledge FAQs",
|
||||
"skillDefinition": "Skill definitions",
|
||||
"mcp": "MCP tools",
|
||||
}
|
||||
|
||||
var builtinPermissionNameOverrides = map[string]string{
|
||||
"user.assignRole": "Assign user roles",
|
||||
"role.assignPermission": "Assign role permissions",
|
||||
"session.revoke": "Revoke sessions",
|
||||
"conversation.send": "Send conversation messages",
|
||||
"conversation.linkCustomer": "Link conversation customer",
|
||||
"ticket.changeStatus": "Change ticket status",
|
||||
"ticket.progress": "Update ticket progress",
|
||||
"agent.config": "Configure agent service rules",
|
||||
"agentTeamSchedule.batchGenerate": "Batch generate agent team schedules",
|
||||
"mcp.view": "View MCP debug information",
|
||||
"mcp.call": "Call MCP tools",
|
||||
}
|
||||
|
||||
type RoleSpec struct {
|
||||
Name string
|
||||
Code string
|
||||
@@ -290,10 +390,10 @@ type RoleSpec struct {
|
||||
}
|
||||
|
||||
var Roles = []RoleSpec{
|
||||
{Name: "超级管理员", Code: RoleCodeSuperAdmin, SortNo: 1},
|
||||
{Name: "管理员", Code: RoleCodeAdmin, SortNo: 2},
|
||||
{Name: "客服组长", Code: RoleCodeCsTeamLeader, SortNo: 3},
|
||||
{Name: "客服", Code: RoleCodeCsUser, SortNo: 4},
|
||||
{Name: "Super Admin", Code: RoleCodeSuperAdmin, SortNo: 1},
|
||||
{Name: "Admin", Code: RoleCodeAdmin, SortNo: 2},
|
||||
{Name: "Support Team Lead", Code: RoleCodeCsTeamLeader, SortNo: 3},
|
||||
{Name: "Support Agent", Code: RoleCodeCsUser, SortNo: 4},
|
||||
}
|
||||
|
||||
var RolePermissions = map[string][]Permission{
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package constants
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestBuiltinAuthSeedNamesDefaultToEnglish(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if BootstrapAdminNickname != "Super Admin" {
|
||||
t.Fatalf("BootstrapAdminNickname = %q, want %q", BootstrapAdminNickname, "Super Admin")
|
||||
}
|
||||
|
||||
roles := map[string]string{}
|
||||
for _, role := range Roles {
|
||||
roles[role.Code] = role.Name
|
||||
}
|
||||
|
||||
tests := map[string]string{
|
||||
RoleCodeSuperAdmin: "Super Admin",
|
||||
RoleCodeAdmin: "Admin",
|
||||
RoleCodeCsTeamLeader: "Support Team Lead",
|
||||
RoleCodeCsUser: "Support Agent",
|
||||
}
|
||||
for code, want := range tests {
|
||||
if got := roles[code]; got != want {
|
||||
t.Fatalf("role %s name = %q, want %q", code, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
permissions := map[string]string{}
|
||||
for _, permission := range Permissions {
|
||||
permissions[permission.Code] = permission.Name
|
||||
}
|
||||
|
||||
permissionTests := map[string]string{
|
||||
"user.view": "View users",
|
||||
"ticket.create": "Create tickets",
|
||||
"conversation.send": "Send conversation messages",
|
||||
"channel.view": "View channels",
|
||||
"agent.view": "View agents",
|
||||
}
|
||||
for code, want := range permissionTests {
|
||||
if got := permissions[code]; got != want {
|
||||
t.Fatalf("permission %s name = %q, want %q", code, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ var (
|
||||
|
||||
func Bundle() *i18n.Bundle {
|
||||
bundleOnce.Do(func() {
|
||||
b := i18n.NewBundle(language.SimplifiedChinese)
|
||||
b := i18n.NewBundle(language.AmericanEnglish)
|
||||
b.RegisterUnmarshalFunc("toml", toml.Unmarshal)
|
||||
for _, name := range []string{
|
||||
"locales/active.zh-CN.toml",
|
||||
|
||||
@@ -6,15 +6,15 @@ const contextLocaleKey = "i18nx.locale"
|
||||
|
||||
func Locale(ctx *gin.Context) string {
|
||||
if ctx == nil {
|
||||
return LocaleZhCN
|
||||
return DefaultLocale
|
||||
}
|
||||
value, ok := ctx.Get(contextLocaleKey)
|
||||
if !ok {
|
||||
return LocaleZhCN
|
||||
return DefaultLocale
|
||||
}
|
||||
locale, ok := value.(string)
|
||||
if !ok {
|
||||
return LocaleZhCN
|
||||
return DefaultLocale
|
||||
}
|
||||
return NormalizeLocale(locale)
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@ func TestNormalizeLocale(t *testing.T) {
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "default for blank", in: "", want: LocaleZhCN},
|
||||
{name: "default for blank", in: "", want: LocaleEnUS},
|
||||
{name: "exact chinese", in: "zh-CN", want: LocaleZhCN},
|
||||
{name: "underscore chinese", in: "zh_CN", want: LocaleZhCN},
|
||||
{name: "short chinese", in: "zh", want: LocaleZhCN},
|
||||
{name: "exact english", in: "en-US", want: LocaleEnUS},
|
||||
{name: "underscore english", in: "en_US", want: LocaleEnUS},
|
||||
{name: "short english", in: "en", want: LocaleEnUS},
|
||||
{name: "unsupported falls back", in: "fr-FR", want: LocaleZhCN},
|
||||
{name: "unsupported falls back", in: "fr-FR", want: LocaleEnUS},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -60,13 +60,13 @@ func TestResolveLocalePrefersXLocale(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslateFallsBackToChinese(t *testing.T) {
|
||||
func TestTranslateFallsBackToEnglish(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := TLocale(LocaleEnUS, "error.auth.expired", nil); got != "Your session has expired. Please sign in again." {
|
||||
t.Fatalf("english translation = %q", got)
|
||||
}
|
||||
if got := TLocale("fr-FR", "error.auth.expired", nil); got != "未登录或登录已过期" {
|
||||
if got := TLocale("fr-FR", "error.auth.expired", nil); got != "Your session has expired. Please sign in again." {
|
||||
t.Fatalf("fallback translation = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ func T(ctx *gin.Context, messageID string, data map[string]any) string {
|
||||
}
|
||||
}
|
||||
}
|
||||
return TLocale(LocaleZhCN, messageID, data)
|
||||
return TLocale(DefaultLocale, messageID, data)
|
||||
}
|
||||
|
||||
func TLocale(locale string, messageID string, data map[string]any) string {
|
||||
@@ -22,8 +22,8 @@ func TLocale(locale string, messageID string, data map[string]any) string {
|
||||
if message != "" {
|
||||
return message
|
||||
}
|
||||
if normalized != LocaleZhCN {
|
||||
message = localize(LocaleZhCN, messageID, data)
|
||||
if normalized != DefaultLocale {
|
||||
message = localize(DefaultLocale, messageID, data)
|
||||
if message != "" {
|
||||
return message
|
||||
}
|
||||
@@ -32,7 +32,7 @@ func TLocale(locale string, messageID string, data map[string]any) string {
|
||||
}
|
||||
|
||||
func localize(locale string, messageID string, data map[string]any) string {
|
||||
localizer := i18n.NewLocalizer(Bundle(), locale, LocaleZhCN)
|
||||
localizer := i18n.NewLocalizer(Bundle(), locale, DefaultLocale)
|
||||
message, err := localizer.Localize(&i18n.LocalizeConfig{
|
||||
MessageID: messageID,
|
||||
TemplateData: data,
|
||||
|
||||
@@ -9,8 +9,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
LocaleZhCN = "zh-CN"
|
||||
LocaleEnUS = "en-US"
|
||||
LocaleZhCN = "zh-CN"
|
||||
LocaleEnUS = "en-US"
|
||||
DefaultLocale = LocaleEnUS
|
||||
)
|
||||
|
||||
var supportedLocales = map[string]string{
|
||||
@@ -26,17 +27,17 @@ var supportedLocales = map[string]string{
|
||||
func NormalizeLocale(value string) string {
|
||||
key := strings.ToLower(strings.TrimSpace(value))
|
||||
if key == "" {
|
||||
return LocaleZhCN
|
||||
return DefaultLocale
|
||||
}
|
||||
if locale, ok := supportedLocales[key]; ok {
|
||||
return locale
|
||||
}
|
||||
return LocaleZhCN
|
||||
return DefaultLocale
|
||||
}
|
||||
|
||||
func ResolveRequestLocale(req *http.Request) string {
|
||||
if req == nil {
|
||||
return LocaleZhCN
|
||||
return DefaultLocale
|
||||
}
|
||||
if locale := normalizeSupportedLocale(req.Header.Get("X-Locale")); locale != "" {
|
||||
return locale
|
||||
@@ -47,7 +48,7 @@ func ResolveRequestLocale(req *http.Request) string {
|
||||
if locale := normalizeSupportedLocale(req.URL.Query().Get("locale")); locale != "" {
|
||||
return locale
|
||||
}
|
||||
return LocaleZhCN
|
||||
return DefaultLocale
|
||||
}
|
||||
|
||||
func Middleware() gin.HandlerFunc {
|
||||
|
||||
@@ -58,87 +58,87 @@ var (
|
||||
Code: "graph/triage_service_request",
|
||||
ServerCode: "graph",
|
||||
Name: "triage_service_request",
|
||||
Title: "升级分流判断",
|
||||
Description: "Graph Tool。用于综合分析当前对话,判断应继续解答、整理工单草稿还是转人工,并在需要建单时一并整理工单草稿。",
|
||||
Title: "Triage service request",
|
||||
Description: "Graph Tool. Analyzes the current conversation to decide whether to continue answering, prepare a ticket draft, or hand off to a human. It also prepares a ticket draft when ticket creation is recommended.",
|
||||
SourceType: enums.ToolSourceTypeGraph,
|
||||
RuntimeStatic: true,
|
||||
Appendix: strings.TrimSpace(`
|
||||
当你需要判断“继续解答 / 建单 / 转人工”这类复杂升级路径时,优先先调用 triage_service_request 这个 Graph Tool,并遵守以下规则:
|
||||
1. 该工具会综合当前对话输出 recommendedAction,并在需要建单时附带 ticketDraft。
|
||||
2. 如果 recommendedAction=continue_answering,则优先继续澄清或解答,不要直接升级。
|
||||
3. 如果 recommendedAction=prepare_ticket,则优先使用 ticketDraft 或继续补充缺失字段,再调用 create_ticket_with_confirmation。
|
||||
4. 如果 recommendedAction=handoff_to_human,则确认理由充分后再调用 handoff_to_human。
|
||||
5. 当升级路径不明确时,优先使用该工具,而不是直接凭主 prompt 做复杂分流判断。
|
||||
When you need to decide between continuing the answer, creating a ticket, or handing off to a human, call triage_service_request first and follow these rules:
|
||||
1. The tool returns recommendedAction and includes ticketDraft when ticket creation is needed.
|
||||
2. If recommendedAction=continue_answering, continue clarifying or answering instead of escalating directly.
|
||||
3. If recommendedAction=prepare_ticket, use ticketDraft or collect missing fields before calling create_ticket_with_confirmation.
|
||||
4. If recommendedAction=handoff_to_human, confirm the reason is sufficient before calling handoff_to_human.
|
||||
5. When the escalation path is unclear, use this tool instead of making a complex routing decision from the main prompt alone.
|
||||
`),
|
||||
}
|
||||
GraphAnalyzeConversation = ToolSpec{
|
||||
Code: "graph/analyze_conversation",
|
||||
ServerCode: "graph",
|
||||
Name: "analyze_conversation",
|
||||
Title: "分析对话风险与摘要",
|
||||
Description: "Graph Tool。用于整理当前对话摘要、识别风险信号,并给出继续解答、建单或转人工的建议。",
|
||||
Title: "Analyze conversation risk and summary",
|
||||
Description: "Graph Tool. Summarizes the current conversation, identifies risk signals, and recommends whether to continue answering, create a ticket, or hand off to a human.",
|
||||
SourceType: enums.ToolSourceTypeGraph,
|
||||
RuntimeStatic: true,
|
||||
Appendix: strings.TrimSpace(`
|
||||
当对话可能涉及投诉升级、退款赔偿、明显负面情绪、是否要建单、是否要转人工等复杂判断时,优先调用 analyze_conversation 这个 Graph Tool,并遵守以下规则:
|
||||
1. 该工具用于输出结构化摘要、风险信号和下一步建议,不代表实际已经建单或转人工。
|
||||
2. 如果工具建议为 handoff_to_human,应先确认是否满足转人工条件,再考虑调用 handoff_to_human。
|
||||
3. 如果工具建议为 prepare_ticket,应优先调用 prepare_ticket_draft 或继续补充信息,而不是直接建单。
|
||||
4. 如果工具建议为 continue_answering,优先继续澄清和解答,不要过早升级动作。
|
||||
When the conversation may involve escalation, refunds, compensation, clear negative sentiment, ticket creation, or human handoff, call analyze_conversation first and follow these rules:
|
||||
1. This tool returns a structured summary, risk signals, and next-step recommendation. It does not create tickets or hand off to a human.
|
||||
2. If the tool recommends handoff_to_human, confirm the handoff conditions before calling handoff_to_human.
|
||||
3. If the tool recommends prepare_ticket, call prepare_ticket_draft or collect more information before creating a ticket.
|
||||
4. If the tool recommends continue_answering, continue clarifying and answering instead of escalating too early.
|
||||
`),
|
||||
}
|
||||
GraphPrepareTicketDraft = ToolSpec{
|
||||
Code: "graph/prepare_ticket_draft",
|
||||
ServerCode: "graph",
|
||||
Name: "prepare_ticket_draft",
|
||||
Title: "整理工单草稿",
|
||||
Description: "Graph Tool。用于根据当前会话和已收集信息整理工单草稿,输出建议标题、描述、缺失字段和追问建议。",
|
||||
Title: "Prepare ticket draft",
|
||||
Description: "Graph Tool. Prepares a ticket draft from the current conversation and collected information, including a suggested title, description, missing fields, and follow-up questions.",
|
||||
SourceType: enums.ToolSourceTypeGraph,
|
||||
RuntimeStatic: true,
|
||||
Appendix: strings.TrimSpace(`
|
||||
当用户已经表达了建单、投诉、报障、售后处理等诉求,但工单标题、描述或问题整理还比较散乱时,优先调用 prepare_ticket_draft 这个 Graph Tool,并遵守以下规则:
|
||||
1. 该工具用于整理工单草稿,会返回建议标题、建议描述、缺失字段和追问建议。
|
||||
2. 如果工具返回 ready=false,优先根据 missingFields 和 followUpQuestions 继续追问,不要直接创建工单。
|
||||
3. 如果工具返回 ready=true,再结合结果考虑调用 create_ticket_with_confirmation。
|
||||
4. 该工具用于“整理草稿”,不代表已经创建工单。
|
||||
When the user has asked to create a ticket, file a complaint, report an issue, or request after-sales handling, but the title or description is still unclear, call prepare_ticket_draft first and follow these rules:
|
||||
1. This tool prepares a ticket draft and returns a suggested title, suggested description, missing fields, and follow-up questions.
|
||||
2. If ready=false, ask follow-up questions based on missingFields and followUpQuestions instead of creating a ticket directly.
|
||||
3. If ready=true, use the result to consider calling create_ticket_with_confirmation.
|
||||
4. This tool only prepares a draft. It does not create a ticket.
|
||||
`),
|
||||
}
|
||||
GraphCreateTicketConfirm = ToolSpec{
|
||||
Code: "graph/create_ticket_with_confirmation",
|
||||
ServerCode: "graph",
|
||||
Name: "create_ticket_with_confirmation",
|
||||
Title: "创建工单确认流程",
|
||||
Description: "Graph Tool。用于封装建单参数整理、用户确认、真正建单和结果返回的确定性流程。",
|
||||
Title: "Create ticket confirmation flow",
|
||||
Description: "Graph Tool. Handles ticket parameter preparation, user confirmation, actual ticket creation, and result return.",
|
||||
SourceType: enums.ToolSourceTypeGraph,
|
||||
DirectAccess: true,
|
||||
RuntimeStatic: true,
|
||||
Aliases: []string{"builtin/create_ticket_with_confirmation"},
|
||||
Appendix: strings.TrimSpace(`
|
||||
你可以在确认信息充分后调用 create_ticket_with_confirmation 这个 Graph Tool 来创建工单,但必须遵守以下规则:
|
||||
1. 只有在用户明确表达希望提交工单、投诉、报障、售后处理等诉求时,才考虑调用该工具。
|
||||
2. 调用前你必须已经整理出清晰的工单标题和问题描述;如果信息还比较散乱,优先先调用 prepare_ticket_draft 或继续追问,不要过早调用。
|
||||
3. 一旦准备创建工单,必须调用 create_ticket_with_confirmation 工具,禁止直接口头宣称“已经创建工单”。
|
||||
4. 该 Graph Tool 会先向用户发起确认。用户确认后才会真正创建工单;用户取消则结束本次建单流程。
|
||||
5. 如果用户只是咨询、抱怨或泛泛表达不满,但没有明确要求建单,优先继续澄清,不要主动创建工单。
|
||||
You can call create_ticket_with_confirmation after enough information has been collected, but follow these rules:
|
||||
1. Only consider this tool when the user explicitly wants to submit a ticket, complaint, issue report, or after-sales request.
|
||||
2. Before calling it, prepare a clear ticket title and issue description. If the information is still scattered, call prepare_ticket_draft or ask follow-up questions first.
|
||||
3. Once you are ready to create a ticket, you must call create_ticket_with_confirmation. Do not simply claim in text that the ticket has been created.
|
||||
4. This Graph Tool asks the user for confirmation first. The ticket is created only after the user confirms; if the user cancels, the flow ends.
|
||||
5. If the user is only asking questions, complaining generally, or expressing dissatisfaction without explicitly requesting a ticket, continue clarifying instead of proactively creating one.
|
||||
`),
|
||||
}
|
||||
GraphHandoffConversation = ToolSpec{
|
||||
Code: "graph/handoff_to_human",
|
||||
ServerCode: "graph",
|
||||
Name: "handoff_to_human",
|
||||
Title: "转人工确认流程",
|
||||
Description: "Graph Tool。用于封装转人工原因整理、用户确认、真正转人工和结果返回的确定性流程。",
|
||||
Title: "Human handoff confirmation flow",
|
||||
Description: "Graph Tool. Handles handoff reason preparation, user confirmation, actual human handoff, and result return.",
|
||||
SourceType: enums.ToolSourceTypeGraph,
|
||||
DirectAccess: true,
|
||||
RuntimeStatic: true,
|
||||
Appendix: strings.TrimSpace(`
|
||||
你可以在确认需要人工介入后调用 handoff_to_human 这个 Graph Tool 来转人工,但必须遵守以下规则:
|
||||
1. 只有在用户明确要求人工客服,或你已经判断该问题必须由人工继续处理时,才调用该工具。
|
||||
2. 调用前先尽量整理清楚转人工原因;如果理由含糊,先追问或澄清,不要直接转人工。
|
||||
3. 一旦决定转人工,必须调用 handoff_to_human 工具,禁止只在回复里口头说“我帮你转人工了”。
|
||||
4. 该 Graph Tool 会先向用户发起确认。用户确认后才会真正转人工;用户取消则结束本次转人工流程。
|
||||
5. 如果问题仍可由当前对话继续解决,优先继续解答,不要过早转人工。
|
||||
6. 如果工具返回 terminal=true 且 shouldRetry=false,说明转人工流程已经结束,禁止重复调用该工具。
|
||||
You can call handoff_to_human after confirming that human help is needed, but follow these rules:
|
||||
1. Only call this tool when the user explicitly asks for a human agent or you have determined that the issue must be handled by a human.
|
||||
2. Before calling it, summarize the handoff reason clearly. If the reason is vague, ask a follow-up question first.
|
||||
3. Once you decide to hand off, you must call handoff_to_human. Do not simply say in text that you have connected the user to a human.
|
||||
4. This Graph Tool asks the user for confirmation first. The handoff happens only after the user confirms; if the user cancels, the flow ends.
|
||||
5. If the issue can still be solved in the current conversation, continue helping instead of escalating too early.
|
||||
6. If the tool returns terminal=true and shouldRetry=false, the handoff flow has ended. Do not call it repeatedly.
|
||||
`),
|
||||
}
|
||||
RegisteredToolSpecs = []ToolSpec{
|
||||
|
||||
Reference in New Issue
Block a user