sync: migrate ai-operator to Gitea (2026-08-10)
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"ai-operator/internal/dialogue/state"
|
||||
)
|
||||
|
||||
type PromptContext struct {
|
||||
State state.ConversationState
|
||||
Language state.Language
|
||||
RegionCode string
|
||||
RegionDisplayName string
|
||||
}
|
||||
|
||||
func BuildSystemPromptFragment(ctx PromptContext) string {
|
||||
base := fmt.Sprintf("Conversation state: %s.\nSelected language: %s.\nSelected region: %s.\nSelected region display: %s.\n", ctx.State, valueOrUnknown(string(ctx.Language)), valueOrUnknown(ctx.RegionCode), valueOrUnknown(ctx.RegionDisplayName))
|
||||
natural := "Assistant is Жанна (Zhanna), AI operator of QazAimaqGas. Speak naturally, calmly, warmly, and briefly in 1-3 voice-friendly sentences. Do not sound robotic. Avoid bureaucratic wording. Use conversational Russian or simple clear Kazakh. Do not repeat that you are an AI operator in every answer. Do not use IVR-style language/region selection. Infer language from speech. Ask region only when a regional answer is needed. General questions may use global KB without selected region. Answer only from KB and do not invent. "
|
||||
switch ctx.State {
|
||||
case state.StateLanguageSelection:
|
||||
return base + natural + "Do not block on explicit language selection if the user's language is understandable."
|
||||
case state.StateRegionSelection:
|
||||
return base + natural + "Ask for city or oblast naturally only because a regional answer is needed. If the user says Almaty, ask whether they mean Almaty city or Almaty region."
|
||||
case state.StateReadyToHelp:
|
||||
return base + natural + "For regional branch/address/contact questions, ask region if missing. Do not reveal internal chunk IDs. Do not mention embeddings, vector search, SQL, or database internals."
|
||||
case state.StateQuestionAnswering:
|
||||
return base + natural + "Continue question answering only through search_knowledge_base and approved tools."
|
||||
default:
|
||||
return base + natural
|
||||
}
|
||||
}
|
||||
|
||||
func valueOrUnknown(v string) string {
|
||||
if v == "" {
|
||||
return "unknown"
|
||||
}
|
||||
return v
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"ai-operator/internal/dialogue/state"
|
||||
)
|
||||
|
||||
func TestPromptBuilder(t *testing.T) {
|
||||
p := BuildSystemPromptFragment(PromptContext{State: state.StateLanguageSelection})
|
||||
if !strings.Contains(p, "Zhanna") || !strings.Contains(p, "Do not use IVR-style") {
|
||||
t.Fatalf("bad language prompt: %s", p)
|
||||
}
|
||||
p = BuildSystemPromptFragment(PromptContext{State: state.StateRegionSelection})
|
||||
if !strings.Contains(p, "Ask for city or oblast naturally") {
|
||||
t.Fatalf("bad region prompt: %s", p)
|
||||
}
|
||||
p = BuildSystemPromptFragment(PromptContext{State: state.StateReadyToHelp, Language: state.LanguageRU, RegionCode: "almaty_city"})
|
||||
if !strings.Contains(p, "almaty_city") || strings.Contains(p, "SECRET") {
|
||||
t.Fatalf("bad ready prompt: %s", p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLanguageSelectionPromptGuardrails(t *testing.T) {
|
||||
p := BuildSystemPromptFragment(PromptContext{State: state.StateLanguageSelection})
|
||||
for _, want := range []string{"Zhanna", "QazAimaqGas", "Infer language from speech", "General questions may use global KB"} {
|
||||
if !strings.Contains(p, want) {
|
||||
t.Fatalf("prompt missing %q: %s", want, p)
|
||||
}
|
||||
}
|
||||
p = BuildSystemPromptFragment(PromptContext{State: state.StateReadyToHelp, Language: state.LanguageRU, RegionCode: "almaty_city"})
|
||||
if !strings.Contains(p, "For regional branch/address/contact questions") {
|
||||
t.Fatalf("ready prompt missing regional guardrail: %s", p)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user