feat: Hybrid RAG knowledge base, sentence-aware chunking, MeshMonitor HTTP sync

Knowledge Base:
- Hybrid FTS5 + vector search using sqlite-vec and bge-small-en-v1.5
- Reciprocal Rank Fusion for result merging
- Domain-aware query construction handles typos
- Configurable weights for keyword vs semantic matching

Message Chunking:
- Sentence-aware splitting respects message boundaries
- Continuation prompts for long responses
- Natural follow-up detection (yes, ok, continue, more, etc.)
- Per-user continuation state management

MeshMonitor Integration:
- HTTP API trigger sync (replaces file-based triggers.json)
- Dynamic refresh interval
- Trigger injection into LLM prompt

Other:
- Updated system prompt for better response length control
- Simplified responder to handle message lists
- Updated README with new features and architecture diagram
- Cleaned up config.example.yaml

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
root 2026-05-04 07:44:12 +00:00
commit 0e36869a5f
14 changed files with 986 additions and 464 deletions

View file

@ -38,6 +38,7 @@ class MeshAI:
self.llm: Optional[LLMBackend] = None
self.context: Optional[MeshContext] = None
self.meshmonitor_sync = None
self.knowledge = None
self.router: Optional[MessageRouter] = None
self.responder: Optional[Responder] = None
self._running = False
@ -97,6 +98,8 @@ class MeshAI:
if self.llm:
await self.llm.close()
if self.knowledge:
self.knowledge.close()
self._remove_pid()
logger.info("MeshAI stopped")
@ -175,11 +178,23 @@ class MeshAI:
else:
self.meshmonitor_sync = None
# Knowledge base
kb_cfg = self.config.knowledge
if kb_cfg.enabled and kb_cfg.db_path:
from .knowledge import KnowledgeSearch
self.knowledge = KnowledgeSearch(
db_path=kb_cfg.db_path,
top_k=kb_cfg.top_k,
)
else:
self.knowledge = None
# Message router
self.router = MessageRouter(
self.config, self.connector, self.history, self.dispatcher, self.llm,
context=self.context,
meshmonitor_sync=self.meshmonitor_sync,
knowledge=self.knowledge,
)
# Responder
@ -208,6 +223,16 @@ class MeshAI:
)
# Route the message
# Check for continuation request first
continuation_messages = self.router.check_continuation(message)
if continuation_messages:
await self.responder.send_response(
continuation_messages,
destination=message.sender_id,
channel=message.channel,
)
return
result = await self.router.route(message)
if result.route_type == RouteType.IGNORE:
@ -215,18 +240,18 @@ class MeshAI:
# Determine response
if result.route_type == RouteType.COMMAND:
response = result.response
messages = result.response # Commands return single string
elif result.route_type == RouteType.LLM:
response = await self.router.generate_llm_response(message, result.query)
messages = await self.router.generate_llm_response(message, result.query)
else:
return
if not response:
if not messages:
return
# Send DM response
await self.responder.send_response(
text=response,
messages,
destination=message.sender_id,
channel=message.channel,
)