feat(epistemics): record assertions between observations and beliefs
A statement made to a mind is now three things kept apart: the observation that it arrived, an append-only assertion (attributed actor, stance, certainty, modality, negation, reported or quoted speech, hypothetical status, interpretation confidence and notes), and, only for factual assertions, one piece of evidence for a proposition belief attributed to whoever actually stands behind it. Reported speech is attributed to the person reported and weighed as second-hand; hypotheticals, questions and imperatives create no belief; expressed doubt counts against; a possibly sarcastic remark is recorded with low interpretation confidence rather than guessed at. Interpretation is deterministic code; a model may enrich the claim list but never sets stance or confidence. Association no longer treats conversational turns as world events: hypotheticals, questions, requests and consecutive turns by one speaker cannot seed causal hypotheses. Adds internal/epistemics, migration 0018, the assertions API, assertions on belief pages and in belief detail, a shared end-to-end test harness (tests/harness), end-to-end tests for eight speech patterns, and updates the milestone test to the new provenance.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package cognition
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"context"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -8,6 +9,7 @@ import (
|
||||
|
||||
"github.com/andydixon/noema/internal/attention"
|
||||
"github.com/andydixon/noema/internal/brain"
|
||||
"github.com/andydixon/noema/internal/epistemics"
|
||||
"github.com/andydixon/noema/internal/workspace"
|
||||
"github.com/andydixon/noema/pkg/events"
|
||||
"github.com/andydixon/noema/pkg/ids"
|
||||
@@ -43,10 +45,29 @@ func (a Associate) Observe(_ context.Context, s *brain.State) ([]brain.Candidate
|
||||
if laterU < 0.4 {
|
||||
continue
|
||||
}
|
||||
// Speech is not a world event. A hypothetical or a question cannot be a
|
||||
// cause or an effect, and two turns by the same speaker in one
|
||||
// conversation are one person talking, not a sequence of happenings.
|
||||
speaker := ""
|
||||
if ev.Metadata["conversation_id"] != nil {
|
||||
var p events.ObservationPayload
|
||||
if json.Unmarshal(ev.Payload, &p) == nil {
|
||||
speaker = p.Actor
|
||||
}
|
||||
if !worldly(speaker, ev.Summary) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
for _, it := range s.Workspace.WorkingMemory {
|
||||
if it.Kind != "observation" || it.SourceEvent.IsZero() || it.SourceEvent == ev.ID {
|
||||
continue
|
||||
}
|
||||
if speaker != "" && strings.HasPrefix(it.Content, speaker+": ") {
|
||||
continue
|
||||
}
|
||||
if !worldly("", it.Content) {
|
||||
continue
|
||||
}
|
||||
if ev.OccurredAt.Sub(it.TouchedAt) > window || it.TouchedAt.After(ev.OccurredAt) {
|
||||
continue
|
||||
}
|
||||
@@ -74,3 +95,21 @@ func toSet(ws []string) map[string]struct{} {
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// worldly reports whether an utterance describes something that happened,
|
||||
// as opposed to a hypothetical, a question or a request, which cannot stand
|
||||
// as a cause or an effect.
|
||||
func worldly(speaker, summary string) bool {
|
||||
text := summary
|
||||
if speaker != "" {
|
||||
text = strings.TrimPrefix(text, speaker+": ")
|
||||
} else if i := strings.Index(text, ": "); i > 0 && i < 40 {
|
||||
text = text[i+2:]
|
||||
}
|
||||
in := epistemics.Interpret(speaker, text)
|
||||
switch in.Modality {
|
||||
case epistemics.ModalityHypothetical, epistemics.ModalityQuestion, epistemics.ModalityImperative:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user