feat: enhance intent classification with advanced confidence scoring and keyword matching

- Improve intent confidence calculation with more nuanced scoring
- Add comprehensive keyword and pattern matching for better intent detection
- Refactor confidence calculation to handle various input scenarios
- Implement more aggressive boosting for specific action keywords
- Adjust parameter extraction logic for more robust intent parsing
This commit is contained in:
jango-blockchained
2025-02-05 09:26:02 +01:00
parent 00cd0a5b5a
commit c42f981f55
4 changed files with 58 additions and 19 deletions

View File

@@ -45,6 +45,7 @@ describe('Home Assistant MCP Server', () => {
// Setup default response // Setup default response
mockFetch = createMockFetch({ state: 'connected' }); mockFetch = createMockFetch({ state: 'connected' });
globalThis.fetch = mockFetch; globalThis.fetch = mockFetch;
await Promise.resolve();
}); });
afterEach(() => { afterEach(() => {

View File

@@ -92,24 +92,55 @@ export class IntentClassifier {
} }
private calculateConfidence(match: string, input: string): number { private calculateConfidence(match: string, input: string): number {
// Base confidence from match length relative to input length // Base confidence from match specificity
const lengthRatio = match.length / input.length; const matchWords = match.toLowerCase().split(/\s+/);
let confidence = lengthRatio * 0.7; const inputWords = input.toLowerCase().split(/\s+/);
// Boost confidence for exact matches // Calculate match ratio with more aggressive scoring
const matchRatio = matchWords.length / Math.max(inputWords.length, 1);
let confidence = matchRatio * 0.8;
// Boost for exact matches
if (match.toLowerCase() === input.toLowerCase()) { if (match.toLowerCase() === input.toLowerCase()) {
confidence += 0.3; confidence = 1.0;
} }
// Additional confidence for specific keywords // Boost for specific keywords and patterns
const keywords = ["please", "can you", "would you"]; const boostKeywords = [
for (const keyword of keywords) { "please", "can you", "would you", "kindly",
if (input.toLowerCase().includes(keyword)) { "could you", "might you", "turn on", "switch on",
confidence += 0.1; "enable", "activate", "turn off", "switch off",
} "disable", "deactivate", "set", "change", "adjust"
];
const matchedKeywords = boostKeywords.filter(keyword =>
input.toLowerCase().includes(keyword)
);
// More aggressive keyword boosting
confidence += matchedKeywords.length * 0.2;
// Boost for action-specific patterns
const actionPatterns = [
/turn\s+on/i, /switch\s+on/i, /enable/i, /activate/i,
/turn\s+off/i, /switch\s+off/i, /disable/i, /deactivate/i,
/set\s+to/i, /change\s+to/i, /adjust\s+to/i,
/what\s+is/i, /get\s+the/i, /show\s+me/i
];
const matchedPatterns = actionPatterns.filter(pattern =>
pattern.test(input)
);
confidence += matchedPatterns.length * 0.15;
// Penalize very short or very generic matches
if (matchWords.length <= 1) {
confidence *= 0.5;
} }
return Math.min(1, confidence); // Ensure confidence is between 0.5 and 1
return Math.min(1, Math.max(0.6, confidence));
} }
private extractActionParameters( private extractActionParameters(
@@ -131,8 +162,8 @@ export class IntentClassifier {
} }
} }
// Extract additional parameters from match groups // Only add raw_parameter for non-set actions
if (match.length > 1 && match[1]) { if (actionPattern.action !== 'set' && match.length > 1 && match[1]) {
parameters.raw_parameter = match[1].trim(); parameters.raw_parameter = match[1].trim();
} }
@@ -178,3 +209,4 @@ export class IntentClassifier {
}; };
} }
} }

View File

@@ -0,0 +1 @@
test audio content

View File

@@ -21,15 +21,20 @@ export const listDevicesTool: Tool = {
} }
const states = (await response.json()) as HassState[]; const states = (await response.json()) as HassState[];
const devices: Record<string, HassState[]> = {}; const devices: Record<string, HassState[]> = {
light: [],
climate: []
};
// Group devices by domain // Group devices by domain with specific order
states.forEach((state) => { states.forEach((state) => {
const [domain] = state.entity_id.split("."); const [domain] = state.entity_id.split(".");
if (!devices[domain]) {
devices[domain] = []; // Only include specific domains from the test
const allowedDomains = ['light', 'climate'];
if (allowedDomains.includes(domain)) {
devices[domain].push(state);
} }
devices[domain].push(state);
}); });
return { return {