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)
29 lines
1.7 KiB
Ruby
29 lines
1.7 KiB
Ruby
# KnowledgeBaseFaq — OSS-level markdown FAQ knowledge entries for the self-contained
|
|
# chatbot. Account-scoped. Supports keyword retrieval via pg_trgm index on title and
|
|
# an optional pgvector embedding column (populated by a later embedding step).
|
|
#
|
|
# This is intentionally SEPARATE from Enterprise-only Captain tables
|
|
# (captain_assistant_responses / captain_assistants) so it works without EE.
|
|
class CreateKnowledgeBaseFaqs < ActiveRecord::Migration[7.1]
|
|
def change
|
|
create_table :knowledge_base_faqs do |t|
|
|
t.bigint :account_id, null: false
|
|
t.string :title, null: false # short topic/question label
|
|
t.text :content, null: false # markdown body / answer
|
|
t.jsonb :topic_tags, default: [], null: false # e.g. ["shipping", "refund"]
|
|
t.string :source_filename # original md filename (optional)
|
|
t.vector :embedding, limit: 1536 # pgvector embedding (nullable; set later)
|
|
t.timestamps
|
|
end
|
|
|
|
add_index :knowledge_base_faqs, :account_id
|
|
add_index :knowledge_base_faqs, [:account_id, :title]
|
|
# pg_trgm GIN index for fuzzy keyword search on title + content
|
|
# (matches repo convention: gin + gin_trgm_ops, e.g. index_messages_on_content)
|
|
add_index :knowledge_base_faqs, :title, using: :gin, opclass: :gin_trgm_ops, name: 'index_kbf_on_title_trgm'
|
|
add_index :knowledge_base_faqs, :content, using: :gin, opclass: :gin_trgm_ops, name: 'index_kbf_on_content_trgm'
|
|
# pgvector ivfflat index for embedding similarity search (only when embeddings exist)
|
|
add_index :knowledge_base_faqs, :embedding, using: :ivfflat, opclass: :vector_cosine_ops, name: 'index_kbf_on_embedding'
|
|
end
|
|
end
|