This is an automated email from the ASF dual-hosted git repository.

tomsun28 pushed a commit to branch 2.0.0
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git

commit 5dc7205fdac8f9b8e0594e65ed4bc3f2981bd5ab
Author: tomsun28 <[email protected]>
AuthorDate: Fri Oct 9 16:33:32 2026 +0800

    fix(ai): let tool.search match natural language queries
    
    tool.search matched the raw query as a single substring against tool
    names and descriptions, so "alert analysis policy create" could never
    find alert_analysis_policy.create and the model could not discover
    on-demand tools. Tokenize both sides on non-alphanumeric characters and
    require every query token to appear in a name or description token,
    keeping the literal substring fast path.
    
    Generated with [Devin](https://devin.ai)
    
    Co-Authored-By: Devin 
<158243242+devin-ai-integration[bot]@users.noreply.github.com>
---
 .../ai/gateway/tool/core/AgentToolRegistry.java    | 29 ++++++++++++++++++----
 .../gateway/tool/core/AgentToolRegistryTest.java   | 12 +++++++++
 2 files changed, 36 insertions(+), 5 deletions(-)

diff --git 
a/hertzbeat-ai-gateway/src/main/java/org/apache/hertzbeat/ai/gateway/tool/core/AgentToolRegistry.java
 
b/hertzbeat-ai-gateway/src/main/java/org/apache/hertzbeat/ai/gateway/tool/core/AgentToolRegistry.java
index 2218b74c8b..74c26f3f6a 100644
--- 
a/hertzbeat-ai-gateway/src/main/java/org/apache/hertzbeat/ai/gateway/tool/core/AgentToolRegistry.java
+++ 
b/hertzbeat-ai-gateway/src/main/java/org/apache/hertzbeat/ai/gateway/tool/core/AgentToolRegistry.java
@@ -65,15 +65,34 @@ public class AgentToolRegistry {
             .filter(descriptor -> descriptor.getExposure() == 
AgentToolExposure.MODEL_ON_DEMAND)
             .filter(descriptor -> namespace == null || namespace.isBlank()
                 || namespace.equalsIgnoreCase(descriptor.getNamespace()))
-            .filter(descriptor -> searchText == null || searchText.isBlank()
-                || descriptor.getName().toLowerCase(java.util.Locale.ROOT)
-                    .contains(searchText)
-                || 
descriptor.getDescription().toLowerCase(java.util.Locale.ROOT)
-                    .contains(searchText))
+            .filter(descriptor -> matchesSearchText(descriptor, searchText))
             .limit(MAX_DISCOVERED_TOOLS_PER_SEARCH)
             .toList();
     }
 
+    private boolean matchesSearchText(AgentToolDescriptor descriptor, String 
searchText) {
+        if (searchText == null || searchText.isBlank()) {
+            return true;
+        }
+        String name = descriptor.getName().toLowerCase(java.util.Locale.ROOT);
+        String description = 
descriptor.getDescription().toLowerCase(java.util.Locale.ROOT);
+        if (name.contains(searchText) || description.contains(searchText)) {
+            return true;
+        }
+        // Model queries arrive as natural language ("alert analysis policy 
create") while tool
+        // names are delimited by dots and underscores 
("alert_analysis_policy.create"), so also
+        // compare token-by-token: every query token must appear in a name or 
description token.
+        List<String> queryTokens = 
java.util.Arrays.stream(searchText.split("[^a-z0-9]+"))
+            .filter(token -> !token.isEmpty())
+            .toList();
+        if (queryTokens.isEmpty()) {
+            return false;
+        }
+        String[] haystackTokens = (name + " " + 
description).split("[^a-z0-9]+");
+        return queryTokens.stream().allMatch(queryToken -> 
java.util.Arrays.stream(haystackTokens)
+            .anyMatch(part -> part.contains(queryToken)));
+    }
+
     /**
      * Immutable catalog entry with its single execution function.
      */
diff --git 
a/hertzbeat-ai-gateway/src/test/java/org/apache/hertzbeat/ai/gateway/tool/core/AgentToolRegistryTest.java
 
b/hertzbeat-ai-gateway/src/test/java/org/apache/hertzbeat/ai/gateway/tool/core/AgentToolRegistryTest.java
index c59353ce8b..7c9537fb6a 100644
--- 
a/hertzbeat-ai-gateway/src/test/java/org/apache/hertzbeat/ai/gateway/tool/core/AgentToolRegistryTest.java
+++ 
b/hertzbeat-ai-gateway/src/test/java/org/apache/hertzbeat/ai/gateway/tool/core/AgentToolRegistryTest.java
@@ -77,6 +77,18 @@ class AgentToolRegistryTest {
                 .map(AgentToolDescriptor::getName).toList());
     }
 
+    @Test
+    void shouldMatchNaturalLanguageQueryAgainstDelimitedToolNames() {
+        AgentToolRegistry registry = new AgentToolRegistry();
+        registry.register(handler("alert_analysis_policy.create", 
AgentToolExposure.MODEL_ON_DEMAND));
+        registry.register(handler("jdbc.query", 
AgentToolExposure.MODEL_ON_DEMAND));
+
+        assertEquals(java.util.List.of("alert_analysis_policy.create"),
+            registry.discoverableDescriptors(null, "alert analysis policy 
create").stream()
+                .map(AgentToolDescriptor::getName).toList());
+        assertTrue(registry.discoverableDescriptors(null, 
"no-matching-token").isEmpty());
+    }
+
     @Test
     void shouldRejectUnscopedDiscovery() {
         AgentToolRegistry registry = new AgentToolRegistry();


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to