feat(chatbot): OSS self-contained guardrail + knowledge-base chatbot
Built-in AI chatbot (no EE, uses Llm::Resolver) that answers in-scope chats
from a knowledge base and hands off to a human when needed. Selected per
inbox via an Integrations::Hook with app_id 'chatbot'.
- Integrations::Chatbot::ProcessorService (mirrors Dialogflow/Captain)
wired via HookListener + HookJob + apps.yml(chatbot, inbox)
- Chatbot::DecisionService: 1-call default ({in_scope/refuse/handoff}),
2-call option; thread-safe prompt threading
- Chatbot::KnowledgeRetriever: keyword top-k over KB (+ embedding reserved)
- KnowledgeBaseFaq + import service (md per-heading + front-matter tags)
- Chatbot::ConfigService + admin chatbot_config endpoint
- refs off-topic (e.g. fortune-telling); handoff = bot_handoff! (pending->open)
This commit is contained in:
34
app/models/knowledge_base_faq.rb
Normal file
34
app/models/knowledge_base_faq.rb
Normal file
@@ -0,0 +1,34 @@
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: knowledge_base_faqs
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# account_id :bigint not null
|
||||
# title :string not null
|
||||
# content :text not null
|
||||
# topic_tags :jsonb default: [] not null
|
||||
# source_filename :string
|
||||
# embedding :vector(1536)
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
class KnowledgeBaseFaq < ApplicationRecord
|
||||
belongs_to :account
|
||||
# pgvector KNN support (matches repo pattern: has_neighbors + nearest_neighbors)
|
||||
has_neighbors :embedding, normalize: true
|
||||
|
||||
validates :title, presence: true
|
||||
validates :content, presence: true
|
||||
|
||||
# topic_tags stored as jsonb array; expose string-list helpers for import/retrieval.
|
||||
def topic_tag_list
|
||||
Array(topic_tags)
|
||||
end
|
||||
|
||||
def topic_tag_list=(value)
|
||||
self.topic_tags = Array(value).map(&:strip).reject(&:blank?)
|
||||
end
|
||||
|
||||
scope :for_account, ->(account) { where(account_id: account.id) }
|
||||
scope :with_embedding, -> { where.not(embedding: nil) }
|
||||
end
|
||||
Reference in New Issue
Block a user