Эх сурвалжийг харах

Merge branch 'feature/Eliza-009-Dont-Repeat-Yourself' of gremlin/elizaservice into develop

gremlin 4 жил өмнө
parent
commit
df329b157e
3 өөрчлөгдсөн 80 нэмэгдсэн , 46 устгасан
  1. 40 0
      eliza/data.go
  2. 33 46
      eliza/eliza.go
  3. 7 0
      eliza/functions.go

+ 40 - 0
eliza/data.go

@@ -0,0 +1,40 @@
+package eliza
+
+// InteractiveBot This is the interface to comunicate with the bot
+type InteractiveBot interface {
+	ReplyTo(statement string) (string, *Personality)
+}
+
+// Chatbot Defines data to be used by a chatbot personality - essentially
+// Its personality script and execution context
+type Chatbot struct {
+	Personality *Personality
+	Context     *ChatbotContext
+}
+
+// ChatbotContext is the white board structure
+type ChatbotContext struct {
+	EngineVersion string
+	Session       SessionData
+}
+
+// ChatbotInteraction defines a individual question/answer interaction with a caller
+// The raw answer is the response selected before any substitution
+type ChatbotInteraction struct {
+	Time         string `json:"time,omitempty" yaml:"time,omitempty"`
+	Question     string `json:"question,omitempty" yaml:"question,omitempty"`
+	PatternGroup string `json:"patternGroup,omitempty" yaml:"patternGroup,omitempty"`
+	Pattern      string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
+	RawAnswer    string `json:"rawAnswer,omitempty" yaml:"rawAnswer,omitempty"`
+	Answer       string `json:"answer,omitempty" yaml:"answer,omitempty"`
+}
+
+// SessionData defines information about the current bot and its interaction within a specific session
+type SessionData struct {
+	SessionID    string               `json:"sessionID" yaml:"sessionID"`
+	StartTime    string               `json:"startTime,omitempty" yaml:"startTime,omitempty"`
+	User         string               `json:"user,omitempty" yaml:"user,omitempty"`
+	Bot          string               `json:"bot,omitempty" yaml:"bot,omitempty"`
+	BotVersion   string               `json:"botVersion,omitempty" yaml:"botVersion,omitempty"`
+	Conversation []ChatbotInteraction `json:"conversation,omitempty" yaml:"conversation,omitempty"`
+}

+ 33 - 46
eliza/eliza.go

@@ -13,65 +13,30 @@ import (
 	"time"
 )
 
-// InteractiveBot This is the interface to comunicate with the bot
-type InteractiveBot interface {
-	ReplyTo(statement string) (string, *Personality)
-}
-
-// Chatbot Defines data to be used by a chatbot personality - essentially
-// Its personality script and execution context
-type Chatbot struct {
-	Personality *Personality
-	Context     *ChatbotContext
-}
-
-// ChatbotContext is the white board structure
-type ChatbotContext struct {
-	EngineVersion string
-	Session       SessionData
-}
-
-// ChatbotInteraction defines a individual question/answer interaction with a caller
-type ChatbotInteraction struct {
-	Time         string `json:"time,omitempty" yaml:"time,omitempty"`
-	Question     string `json:"question,omitempty" yaml:"question,omitempty"`
-	PatternGroup string `json:"patternGroup,omitempty" yaml:"patternGroup,omitempty"`
-	Pattern      string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
-	Answer       string `json:"answer,omitempty" yaml:"answer,omitempty"`
-}
-
-// SessionData defines information about the current bot and its interaction within a specific session
-type SessionData struct {
-	SessionID    string               `json:"sessionID" yaml:"sessionID"`
-	StartTime    string               `json:"startTime,omitempty" yaml:"startTime,omitempty"`
-	User         string               `json:"user,omitempty" yaml:"user,omitempty"`
-	Bot          string               `json:"bot,omitempty" yaml:"bot,omitempty"`
-	BotVersion   string               `json:"botVersion,omitempty" yaml:"botVersion,omitempty"`
-	Conversation []ChatbotInteraction `json:"conversation,omitempty" yaml:"conversation,omitempty"`
-}
-
 // NewBotPersonality is a utility method to create a bot instance from a personality and its context
 func NewBotPersonality(personality *Personality, context *ChatbotContext) *Chatbot {
 	return &Chatbot{personality, context}
 
 }
 
-func NewChatbotInteraction(question, patterngroup, answer string) ChatbotInteraction {
-	return ChatbotInteraction{Time: time.Now().UTC().String(), Question: question, PatternGroup: patterngroup, Answer: answer}
+func NewChatbotInteraction(question, patterngroup, rawanswer, answer string) ChatbotInteraction {
+	return ChatbotInteraction{Time: time.Now().UTC().String(), Question: question, PatternGroup: patterngroup, RawAnswer: rawanswer, Answer: answer}
 }
 
-func NewChatbotInteractionWithPattern(question, patterngroup, pattern, answer string) ChatbotInteraction {
-	return ChatbotInteraction{Time: time.Now().UTC().String(), Question: question, PatternGroup: patterngroup, Pattern: pattern, Answer: answer}
+func NewChatbotInteractionWithPattern(question, patterngroup, pattern, rawanswer, answer string) ChatbotInteraction {
+	return ChatbotInteraction{Time: time.Now().UTC().String(), Question: question, PatternGroup: patterngroup, Pattern: pattern, RawAnswer: rawanswer, Answer: answer}
 }
 
 // ReplyTo will construct a reply for a given statement using ELIZA's rules.
 func (p *Chatbot) ReplyTo(statement string) ChatbotInteraction {
 	// First, preprocess the statement for more effective matching
 	statement = p.preprocess(statement)
+	var rawResponse string
 
 	// Then, we check if this is a quit statement
 	if p.IsQuitStatement(statement) {
-		return NewChatbotInteraction(statement, "QuitResponses", p.GoodbyeResponse())
+		rawResponse = p.GoodbyeResponse()
+		return NewChatbotInteraction(statement, "QuitResponses", rawResponse, rawResponse)
 	}
 
 	// Next, we try to match the statement to a statement that ELIZA can
@@ -97,13 +62,14 @@ func (p *Chatbot) ReplyTo(statement string) ChatbotInteraction {
 					response = fmt.Sprintf(response, fragment)
 				}
 				//			fmt.Printf("For Statement \"%s\" got a hit with pattern \"%s\" Responded With \"%s\"\n", statement, pattern, response)
-				return NewChatbotInteractionWithPattern(statement, "Psychobabble", questionPattern, p.replacePlaceHolders(response))
+				return NewChatbotInteractionWithPattern(statement, "Psychobabble", questionPattern, response, p.replacePlaceHolders(response))
 			}
 		}
 	}
 
 	// If no patterns were matched, return a default response.
-	return NewChatbotInteraction(statement, "DefaultResponses", p.replacePlaceHolders(p.randChoice(p.Personality.DefaultResponses)))
+	rawResponse = p.randChoice(p.Personality.DefaultResponses)
+	return NewChatbotInteraction(statement, "DefaultResponses", rawResponse, p.replacePlaceHolders(rawResponse))
 }
 
 // Greetings will return a random introductory sentence for ELIZA.
@@ -152,8 +118,29 @@ func (p *Chatbot) randChoice(list []string) string {
 		return ""
 	}
 	rand.Seed(time.Now().UnixNano())
-	randIndex := rand.Intn(len(list))
-	return list[randIndex]
+	maxAttempts := 2 * len(list)
+	// try looking for an unspoken response at random which has not been used recently
+	// 1st hash previous responses
+	previousResponseHash := make(map[uint32]bool)
+	for _, r := range p.Context.Session.Conversation {
+		previousResponseHash[hash(r.RawAnswer)] = true
+	}
+
+	var candidate string
+	for i := 1; i <= maxAttempts; i++ {
+
+		randIndex := rand.Intn(len(list))
+		candidate = list[randIndex]
+		h := hash(candidate)
+		_, known := previousResponseHash[h]
+		// if not known - use it
+		//	fmt.Println(randIndex, known, candidate)
+		if !known {
+			return candidate
+		}
+	}
+	// if here then use default
+	return candidate
 }
 
 /**

+ 7 - 0
eliza/functions.go

@@ -2,6 +2,7 @@ package eliza
 
 import (
 	"fmt"
+	"hash/fnv"
 	"time"
 )
 
@@ -31,3 +32,9 @@ func dayOfWeek() string {
 	dt := time.Now().Weekday()
 	return dt.String()
 }
+
+func hash(s string) uint32 {
+	h := fnv.New32a()
+	h.Write([]byte(s))
+	return h.Sum32()
+}