1.6 Preference Learning
Preference learning is not just saving "the user likes X." Real preferences change,
conflict, and depend on context. Store them as explicit PreferenceUpdate thoughts,
attach concepts that describe the context, and supersede older preferences when the user
gives a newer instruction.
The Pattern
- Use
PreferenceUpdatefor direct preference statements. - Use concepts for the preference domain, such as
review-style. - Use
Supersedeswhen a newer preference replaces an older one. - Retrieve preferences with a type filter plus ranked text.
use mentisdb::{
BinaryStorageAdapter, MentisDb, RankedSearchQuery, ThoughtInput,
ThoughtQuery, ThoughtRelation, ThoughtRelationKind, ThoughtType,
};
fn learn_and_update_preferences() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let adapter = BinaryStorageAdapter::for_chain_key(
dir.path(),
"cookbook-preferences",
);
let mut chain = MentisDb::open_with_storage(Box::new(adapter))?;
chain.upsert_agent(
"assistant",
Some("Assistant"),
Some("dev-team"),
Some("Learns user preferences from direct feedback"),
None,
)?;
let first_preference = chain.append_thought(
"assistant",
ThoughtInput::new(
ThoughtType::PreferenceUpdate,
"For code review, the user prefers concise findings before summaries.",
)
.with_concepts(["review-style", "code-review"])
.with_tags(["preference", "user"])
.with_importance(0.8)
.with_confidence(0.9),
)?.id;
chain.append_thought(
"assistant",
ThoughtInput::new(
ThoughtType::PreferenceUpdate,
"For high-risk code review, the user now wants severity, file references, and test gaps before any summary.",
)
.with_relations(vec![ThoughtRelation::new(
ThoughtRelationKind::Supersedes,
first_preference,
)])
.with_concepts(["review-style", "code-review", "risk"])
.with_tags(["preference", "user", "current"])
.with_importance(0.9)
.with_confidence(0.95),
)?;
let query = RankedSearchQuery::new()
.with_text("How should I present a high-risk code review?")
.with_filter(
ThoughtQuery::new()
.with_types(vec![ThoughtType::PreferenceUpdate])
.with_concepts_any(["review-style"]),
)
.with_limit(3);
let results = chain.query_ranked(&query);
assert!(!results.hits.is_empty());
assert!(results.hits[0].thought.content.contains("high-risk"));
Ok(())
}
Do not delete old preferences. Superseding preserves auditability while
letting retrieval favor the newer, narrower instruction.
Conflict Handling
If two preferences both apply, prefer the one with the narrower concept set and higher
importance. If the conflict is real, ask the user and store the answer as a new
PreferenceUpdate that supersedes the ambiguous one.