Class: Llmemory::Extractors::FactExtractor
- Inherits:
-
Object
- Object
- Llmemory::Extractors::FactExtractor
- Defined in:
- lib/llmemory/extractors/fact_extractor.rb
Constant Summary collapse
- CLASSIFY_JSON_SCHEMA =
{ name: "fact_classification", schema: { type: "object", properties: { items: { type: "array", items: { type: "object", properties: { content: { type: "string" }, category: { type: "string" } }, required: ["content", "category"], additionalProperties: false } } }, required: ["items"], additionalProperties: false } }.freeze
Instance Method Summary collapse
- #classify_item(content) ⇒ Object
- #classify_items(contents) ⇒ Object
- #evolve_summary(existing:, new_memories:) ⇒ Object
- #extract_items(conversation_text) ⇒ Object
-
#initialize(llm: nil) ⇒ FactExtractor
constructor
A new instance of FactExtractor.
Constructor Details
#initialize(llm: nil) ⇒ FactExtractor
Returns a new instance of FactExtractor.
8 9 10 |
# File 'lib/llmemory/extractors/fact_extractor.rb', line 8 def initialize(llm: nil) @llm = llm || Llmemory::LLM.client end |
Instance Method Details
#classify_item(content) ⇒ Object
45 46 47 |
# File 'lib/llmemory/extractors/fact_extractor.rb', line 45 def classify_item(content) classify_items([content])[content.to_s] || "general" end |
#classify_items(contents) ⇒ Object
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/llmemory/extractors/fact_extractor.rb', line 72 def classify_items(contents) list = Array(contents).map(&:to_s).reject(&:empty?) return {} if list.empty? if @llm.respond_to?(:invoke_with_json_schema) begin numbered = list.map.with_index { |c, i| "#{i + 1}. #{c}" }.join("\n") prompt = <<~PROMPT Classify each fact into ONE category. Use lowercase with underscores. Examples: work_life, personal_life, preferences, general. Facts: #{numbered} Return one category per fact in the same order. PROMPT parsed = @llm.invoke_with_json_schema(prompt.strip, CLASSIFY_JSON_SCHEMA) mapped = map_classifications(parsed, list) return mapped if mapped.size == list.size rescue Llmemory::LLMError # fall back to per-item classification end end list.each_with_object({}) { |content, acc| acc[content] = classify_item_fallback(content) } end |
#evolve_summary(existing:, new_memories:) ⇒ Object
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/llmemory/extractors/fact_extractor.rb', line 25 def evolve_summary(existing:, new_memories:) memory_list_text = Array(new_memories).map { |m| "- #{m}" }.join("\n") prompt = <<~PROMPT You are a Memory Synchronization Specialist. Topic Scope: User Profile ## Original Profile #{existing.to_s.empty? ? "No existing profile." : existing} ## New Memory Items to Integrate #{memory_list_text} # Task 1. Update: If new items conflict with the Original Profile, overwrite the old facts. 2. Add: If items are new, append them logically. 3. Output: Return ONLY the updated markdown profile. PROMPT @llm.invoke(prompt.strip).to_s end |
#extract_items(conversation_text) ⇒ Object
12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/llmemory/extractors/fact_extractor.rb', line 12 def extract_items(conversation_text) prompt = <<~PROMPT Extract discrete facts from this conversation. Focus on preferences, behaviors, and important details. Conversation: #{conversation_text} Return as JSON array of objects with "content" and "importance" (0-1) keys. Importance: 0.8-0.95 for preferences/corrections/decisions, 0.5-0.8 for factual context, 0.3-0.5 for ephemeral. Example: [{"content": "User prefers Ruby", "importance": 0.9}, {"content": "User mentioned the weather", "importance": 0.4}] PROMPT response = @llm.invoke(prompt.strip) parse_items_response(response) end |