Преглед на файлове

1. 丰富了错误模式管理的内置模式列表,从 8 条扩展到 25 条,新增覆盖 OpenAI 标准错误码、Anthropic 过载、国内大模型中文错误、网络异常、JSON 解析失败等场景;
2. 内置模式改为在配置文件中维护,应用升级时按名称增量补齐,用户对已有模式的编辑和禁用状态不会被覆盖;
3. 调整了左侧菜单结构与错误模式页面样式,“标签管理”、“模型管理”、“错误模式管理”合并为“其他管理”分组,页面去除 Hermes 字样、表格配色统一为深蓝色风格。

weisijie преди 2 седмици
родител
ревизия
844a853cb3

+ 123 - 0
backend/src/main/java/com/agent/management/config/ErrorPatternProperties.java

@@ -0,0 +1,123 @@
+package com.agent.management.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 错误模式管理配置。
+ *
+ * <p>对应 {@code application.yml} 的 {@code app.error-pattern} 段:
+ * <pre>
+ * app:
+ *   error-pattern:
+ *     builtin:
+ *       - name: LLM SDK 重试耗尽
+ *         pattern: API call failed after
+ *         description: OpenAI SDK 重试耗尽仍未成功
+ * </pre>
+ *
+ * <p>启动时若数据库为空,{@code builtin} 列表会被写入作为内置默认模式;
+ * 用户自定义后不会被覆盖;可通过"重置为默认"恢复(清空自定义 + 重新启用内置)。</p>
+ *
+ * <p>未在 yml 中配置时,使用代码内的 {@link #DEFAULT_BUILTINS} 兜底,
+ * 覆盖常见 OpenAI 兼容服务(智谱 / DeepSeek / Moonshot / Anthropic 等)的典型错误描述。</p>
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "app.error-pattern")
+public class ErrorPatternProperties {
+
+    /**
+     * 内置默认模式列表。yml 未配置时使用 {@link #DEFAULT_BUILTINS}。
+     */
+    private List<BuiltinPattern> builtin = new ArrayList<>(DEFAULT_BUILTINS);
+
+    @Data
+    public static class BuiltinPattern {
+        /** 模式名称(唯一) */
+        private String name;
+        /** 匹配子串(大小写不敏感) */
+        private String pattern;
+        /** 说明(可选) */
+        private String description;
+
+        public BuiltinPattern() {}
+
+        public BuiltinPattern(String name, String pattern, String description) {
+            this.name = name;
+            this.pattern = pattern;
+            this.description = description;
+        }
+    }
+
+    /**
+     * 代码内兜底默认列表(当 yml 未配置时使用)。
+     *
+     * <p>覆盖范围:</p>
+     * <ul>
+     *   <li>SDK 重试 / HTTP 4xx / 5xx 通用错误</li>
+     *   <li>OpenAI 标准错误:rate_limit / quota / insufficient_quota / invalid_api_key / context_length / server_error / model_not_found</li>
+     *   <li>智谱 / DeepSeek / Moonshot 中文错误描述</li>
+     *   <li>Anthropic: overloaded_error</li>
+     *   <li>网络层错误:connection refused / timeout / ECONNREFUSED / ETIMEDOUT / socket hang up</li>
+     *   <li>工具调用与 JSON 解析错误</li>
+     *   <li>通用中文错误:服务暂时不可用 / 请求失败 / 系统繁忙</li>
+     * </ul>
+     */
+    public static final List<BuiltinPattern> DEFAULT_BUILTINS = List.of(
+            new BuiltinPattern("LLM SDK 重试耗尽", "API call failed after",
+                    "OpenAI SDK 重试耗尽仍未成功,通常是限流或下游故障"),
+            new BuiltinPattern("HTTP 4xx 客户端错误", "HTTP 4",
+                    "LLM 服务返回 4xx(含 429 限流、401 鉴权、400 参数等)"),
+            new BuiltinPattern("HTTP 5xx 服务端错误", "HTTP 5",
+                    "LLM 服务返回 5xx,服务端临时故障"),
+            new BuiltinPattern("Rate Limit(英文)", "rate limit",
+                    "OpenAI 兼容服务的标准限流错误"),
+            new BuiltinPattern("OpenAI 配额耗尽", "insufficient_quota",
+                    "OpenAI 标准配额耗尽错误码"),
+            new BuiltinPattern("额度 / 配额", "quota",
+                    "额度耗尽类错误(中英文 quota 关键词)"),
+            new BuiltinPattern("OpenAI 鉴权失败", "invalid_api_key",
+                    "OpenAI 标准鉴权失败错误码"),
+            new BuiltinPattern("认证失败(英文)", "invalid api key",
+                    "API Key 无效或被吊销"),
+            new BuiltinPattern("认证失败(中文)", "无效的 api",
+                    "中文鉴权失败描述(兼容智谱等中文错误消息)"),
+            new BuiltinPattern("上下文超限(英文)", "context_length_exceeded",
+                    "输入超过模型上下文窗口"),
+            new BuiltinPattern("上下文超限(中文)", "上下文长度",
+                    "中文上下文超限描述"),
+            new BuiltinPattern("模型不存在", "model_not_found",
+                    "指定的模型标识不存在或无权访问"),
+            new BuiltinPattern("OpenAI 服务端错误", "server_error",
+                    "OpenAI 标准 5xx 错误码"),
+            new BuiltinPattern("Anthropic 过载", "overloaded_error",
+                    "Anthropic 服务过载错误码"),
+            new BuiltinPattern("智谱限流(具体)", "该模型当前访问量过大",
+                    "智谱 AI glm 系列特定限流描述"),
+            new BuiltinPattern("通用中文限流", "请稍后重试",
+                    "通用中文限流提示(含 QPS/并发限制等场景)"),
+            new BuiltinPattern("通用中文系统繁忙", "系统繁忙",
+                    "服务端繁忙类错误(多见于国内 LLM 服务)"),
+            new BuiltinPattern("通用中文请求失败", "请求失败",
+                    "通用请求失败描述(兜底捕获)"),
+            new BuiltinPattern("网络连接拒绝", "connection refused",
+                    "下游服务不可达(连接被拒绝)"),
+            new BuiltinPattern("网络超时", "connection timeout",
+                    "下游服务响应超时"),
+            new BuiltinPattern("ECONNREFUSED", "ECONNREFUSED",
+                    "Node.js / Python HTTP 客户端的连接拒绝错误码"),
+            new BuiltinPattern("ETIMEDOUT", "ETIMEDOUT",
+                    "HTTP 客户端的连接超时错误码"),
+            new BuiltinPattern("Socket 挂起", "socket hang up",
+                    "HTTP 连接被对端中途切断"),
+            new BuiltinPattern("JSON 解析失败", "unexpected token",
+                    "LLM 返回非合法 JSON(结构化输出场景)"),
+            new BuiltinPattern("工具调用失败(英文)", "tool call failed",
+                    "Agent 工具调用抛出异常")
+    );
+}

+ 55 - 23
backend/src/main/java/com/agent/management/service/impl/HermesErrorPatternServiceImpl.java

@@ -1,6 +1,7 @@
 package com.agent.management.service.impl;
 
 import com.agent.management.common.exception.BusinessException;
+import com.agent.management.config.ErrorPatternProperties;
 import com.agent.management.model.entity.HermesErrorPattern;
 import com.agent.management.repository.HermesErrorPatternRepository;
 import com.agent.management.service.HermesErrorPatternService;
@@ -12,9 +13,14 @@ import org.springframework.stereotype.Service;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
- * Hermes 错误嗅探模式服务实现。
+ * 错误嗅探模式服务实现。
+ *
+ * <p>内置默认模式来源:{@code application.yml} 的 {@code app.error-pattern.builtin},
+ * 由 {@link ErrorPatternProperties} 注入;未配置时使用代码内 {@link ErrorPatternProperties#DEFAULT_BUILTINS}。</p>
  *
  * <p>启动时若数据文件不存在或为空,自动写入内置默认模式({@link #initBuiltInPatterns})。
  * 用户自定义后不被覆盖。</p>
@@ -25,41 +31,67 @@ import java.util.List;
 public class HermesErrorPatternServiceImpl implements HermesErrorPatternService {
 
     private final HermesErrorPatternRepository repository;
+    private final ErrorPatternProperties properties;
 
     /**
      * 嗅探扫描范围:只检查 finalText 的前 N 个字符。
      *
-     * <p>Hermes 的错误描述通常出现在 finalText 开头(AIAgent.run() 把错误作为 final_response 返回),
+     * <p>错误描述通常出现在 finalText 开头(AIAgent.run() 把错误作为 final_response 返回),
      * 限制扫描范围可避免对超长正常回复做无效 contains。500 字符覆盖所有已知错误前缀。</p>
      */
     private static final int SCAN_LIMIT = 500;
 
+    /**
+     * 启动时初始化内置模式。
+     *
+     * <p>分两种情况:</p>
+     * <ul>
+     *   <li>数据文件为空(全新部署):写入全部 {@link ErrorPatternProperties#getBuiltin()}。</li>
+     *   <li>数据文件已有模式(升级部署):按 {@code name} 增量补齐新增的内置模式,
+     *       已有条目(含用户对其的编辑/禁用状态)保持不变。</li>
+     * </ul>
+     */
     @PostConstruct
     public void initBuiltInPatterns() {
         List<HermesErrorPattern> existing = repository.findAll();
-        if (!existing.isEmpty()) {
-            log.info("[HermesErrorPattern] 已存在 {} 条模式,跳过内置初始化", existing.size());
+        List<ErrorPatternProperties.BuiltinPattern> source =
+                properties.getBuiltin() != null && !properties.getBuiltin().isEmpty()
+                        ? properties.getBuiltin()
+                        : ErrorPatternProperties.DEFAULT_BUILTINS;
+
+        if (existing.isEmpty()) {
+            // 全新部署:写入全部内置
+            List<HermesErrorPattern> builtIn = new ArrayList<>(source.size());
+            for (ErrorPatternProperties.BuiltinPattern b : source) {
+                if (isBlank(b.getName()) || isBlank(b.getPattern())) continue;
+                builtIn.add(builtIn(b.getName(), b.getPattern(), b.getDescription()));
+            }
+            repository.saveAll(builtIn);
+            log.info("[HermesErrorPattern] 已写入 {} 条内置默认模式", builtIn.size());
             return;
         }
-        List<HermesErrorPattern> builtIn = new ArrayList<>();
-        builtIn.add(builtIn("LLM SDK 重试耗尽", "API call failed after",
-                "OpenAI SDK 重试耗尽仍未成功,通常是限流或下游故障"));
-        builtIn.add(builtIn("HTTP 4xx 客户端错误", "HTTP 4",
-                "LLM 服务返回 4xx(含 429 限流、401 鉴权、400 参数等)"));
-        builtIn.add(builtIn("HTTP 5xx 服务端错误", "HTTP 5",
-                "LLM 服务返回 5xx,服务端临时故障"));
-        builtIn.add(builtIn("Rate Limit(英文)", "rate limit",
-                "OpenAI 兼容服务的标准限流错误"));
-        builtIn.add(builtIn("智谱限流(具体)", "该模型当前访问量过大",
-                "智谱 AI glm 系列特定限流描述"));
-        builtIn.add(builtIn("额度 / 配额", "quota",
-                "额度耗尽类错误(中英文 quota 关键词)"));
-        builtIn.add(builtIn("认证失败(英文)", "invalid api key",
-                "API Key 无效或被吊销"));
-        builtIn.add(builtIn("认证失败(中文)", "无效的 api",
-                "中文鉴权失败描述(兼容智谱等中文错误消息)"));
-        repository.saveAll(builtIn);
-        log.info("[HermesErrorPattern] 已写入 {} 条内置默认模式", builtIn.size());
+
+        // 升级部署:按 name 增量补齐(保留用户对已有内置的编辑/禁用状态)
+        Set<String> existingNames = existing.stream()
+                .map(HermesErrorPattern::getName)
+                .collect(Collectors.toSet());
+        List<HermesErrorPattern> toAdd = new ArrayList<>();
+        for (ErrorPatternProperties.BuiltinPattern b : source) {
+            if (isBlank(b.getName()) || isBlank(b.getPattern())) continue;
+            if (existingNames.contains(b.getName())) continue;
+            toAdd.add(builtIn(b.getName(), b.getPattern(), b.getDescription()));
+        }
+        if (toAdd.isEmpty()) {
+            log.info("[HermesErrorPattern] 已存在 {} 条模式,无需补齐", existing.size());
+        } else {
+            repository.saveAll(toAdd);
+            log.info("[HermesErrorPattern] 增量补齐 {} 条新增内置模式(原有 {} 条保留)",
+                    toAdd.size(), existing.size());
+        }
+    }
+
+    private static boolean isBlank(String s) {
+        return s == null || s.trim().isEmpty();
     }
 
     private HermesErrorPattern builtIn(String name, String pattern, String description) {

+ 82 - 0
backend/src/main/resources/application.yml.example

@@ -22,6 +22,88 @@ skill:
 app:
   json-store-dir: ${JSON_STORE_DIR:./data/json}
 
+  # ========== 错误模式嗅探(智能体节点失败兜底识别) ==========
+  # 启动时若数据文件为空,自动写入以下内置模式;用户自定义后不会被覆盖。
+  # 通过"重置为默认"可清空自定义 + 重新启用全部内置模式。
+  # pattern 字段:子串匹配、大小写不敏感;建议保持粒度适中,过短易误报。
+  error-pattern:
+    builtin:
+      - name: LLM SDK 重试耗尽
+        pattern: API call failed after
+        description: OpenAI SDK 重试耗尽仍未成功,通常是限流或下游故障
+      - name: HTTP 4xx 客户端错误
+        pattern: HTTP 4
+        description: LLM 服务返回 4xx(含 429 限流、401 鉴权、400 参数等)
+      - name: HTTP 5xx 服务端错误
+        pattern: HTTP 5
+        description: LLM 服务返回 5xx,服务端临时故障
+      - name: Rate Limit(英文)
+        pattern: rate limit
+        description: OpenAI 兼容服务的标准限流错误
+      - name: OpenAI 配额耗尽
+        pattern: insufficient_quota
+        description: OpenAI 标准配额耗尽错误码
+      - name: 额度 / 配额
+        pattern: quota
+        description: 额度耗尽类错误(中英文 quota 关键词)
+      - name: OpenAI 鉴权失败
+        pattern: invalid_api_key
+        description: OpenAI 标准鉴权失败错误码
+      - name: 认证失败(英文)
+        pattern: invalid api key
+        description: API Key 无效或被吊销
+      - name: 认证失败(中文)
+        pattern: 无效的 api
+        description: 中文鉴权失败描述(兼容智谱等中文错误消息)
+      - name: 上下文超限(英文)
+        pattern: context_length_exceeded
+        description: 输入超过模型上下文窗口
+      - name: 上下文超限(中文)
+        pattern: 上下文长度
+        description: 中文上下文超限描述
+      - name: 模型不存在
+        pattern: model_not_found
+        description: 指定的模型标识不存在或无权访问
+      - name: OpenAI 服务端错误
+        pattern: server_error
+        description: OpenAI 标准 5xx 错误码
+      - name: Anthropic 过载
+        pattern: overloaded_error
+        description: Anthropic 服务过载错误码
+      - name: 智谱限流(具体)
+        pattern: 该模型当前访问量过大
+        description: 智谱 AI glm 系列特定限流描述
+      - name: 通用中文限流
+        pattern: 请稍后重试
+        description: 通用中文限流提示(含 QPS/并发限制等场景)
+      - name: 通用中文系统繁忙
+        pattern: 系统繁忙
+        description: 服务端繁忙类错误(多见于国内 LLM 服务)
+      - name: 通用中文请求失败
+        pattern: 请求失败
+        description: 通用请求失败描述(兜底捕获)
+      - name: 网络连接拒绝
+        pattern: connection refused
+        description: 下游服务不可达(连接被拒绝)
+      - name: 网络超时
+        pattern: connection timeout
+        description: 下游服务响应超时
+      - name: ECONNREFUSED
+        pattern: ECONNREFUSED
+        description: Node.js / Python HTTP 客户端的连接拒绝错误码
+      - name: ETIMEDOUT
+        pattern: ETIMEDOUT
+        description: HTTP 客户端的连接超时错误码
+      - name: Socket 挂起
+        pattern: socket hang up
+        description: HTTP 连接被对端中途切断
+      - name: JSON 解析失败
+        pattern: unexpected token
+        description: LLM 返回非合法 JSON(结构化输出场景)
+      - name: 工具调用失败(英文)
+        pattern: tool call failed
+        description: Agent 工具调用抛出异常
+
   # ========== 知识库子系统 ==========
   kb:
     # 总开关:false 时所有 kb 接口返回 503(运维应急降级用)

+ 63 - 4
backend/src/test/java/com/agent/management/service/HermesErrorPatternServiceTest.java

@@ -1,6 +1,7 @@
 package com.agent.management.service;
 
 import com.agent.management.common.exception.BusinessException;
+import com.agent.management.config.ErrorPatternProperties;
 import com.agent.management.model.entity.HermesErrorPattern;
 import com.agent.management.repository.HermesErrorPatternRepository;
 import com.agent.management.repository.json.JsonStore;
@@ -9,6 +10,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.List;
 
@@ -38,7 +40,8 @@ class HermesErrorPatternServiceTest {
     void setUp() {
         JsonStore store = new JsonStore(tempDir);
         repository = new HermesErrorPatternRepository(store);
-        service = new HermesErrorPatternServiceImpl(repository);
+        // 注入默认配置(与 Spring 容器外行为一致:使用 ErrorPatternProperties.DEFAULT_BUILTINS)
+        service = new HermesErrorPatternServiceImpl(repository, new ErrorPatternProperties());
         // 触发 @PostConstruct 逻辑(单元测试不走 Spring 生命周期)
         service.initBuiltInPatterns();
     }
@@ -120,7 +123,8 @@ class HermesErrorPatternServiceTest {
     @Test
     void initBuiltInPatterns_writesDefaultsOnFirstRun() {
         List<HermesErrorPattern> all = service.list();
-        assertThat(all.size()).isGreaterThanOrEqualTo(8);
+        // 默认内置数量与 ErrorPatternProperties.DEFAULT_BUILTINS 一致(当前 25 条)
+        assertThat(all.size()).isEqualTo(ErrorPatternProperties.DEFAULT_BUILTINS.size());
         // 都应是内置
         assertThat(all.stream().filter(p -> Boolean.TRUE.equals(p.getIsBuiltIn())).count()).isEqualTo(all.size());
     }
@@ -135,12 +139,67 @@ class HermesErrorPatternServiceTest {
         // 再次触发初始化(模拟重启)
         service.initBuiltInPatterns();
 
-        // 内置数量仍是 8 条(不会被重新写入覆盖)
-        assertThat(service.list().size()).isGreaterThanOrEqualTo(8);
+        // 内置数量保持不变(不会被重新写入覆盖)
+        assertThat(service.list().size()).isEqualTo(ErrorPatternProperties.DEFAULT_BUILTINS.size());
         // 那条内置仍是禁用状态
         assertThat(repository.findById(first.getId()).orElseThrow().getEnabled()).isFalse();
     }
 
+    @Test
+    void initBuiltInPatterns_incrementallyAppendsMissingBuiltin() throws Exception {
+        // 模拟"老部署":独立目录预置少量数据(1 条同名老内置已被禁用 + 1 条自定义)
+        Path legacyDir = Files.createTempDirectory("legacy-error-patterns");
+        JsonStore legacyStore = new JsonStore(legacyDir);
+        HermesErrorPatternRepository legacyRepo = new HermesErrorPatternRepository(legacyStore);
+        HermesErrorPatternServiceImpl legacyService =
+                new HermesErrorPatternServiceImpl(legacyRepo, new ErrorPatternProperties());
+
+        HermesErrorPattern oldBuiltin = new HermesErrorPattern();
+        oldBuiltin.setName("LLM SDK 重试耗尽");   // 与默认列表同名
+        oldBuiltin.setPattern("old-pattern-text");
+        oldBuiltin.setEnabled(false);             // 用户已禁用
+        oldBuiltin.setIsBuiltIn(true);
+        oldBuiltin.onCreate();
+        legacyRepo.save(oldBuiltin);
+
+        HermesErrorPattern userCustom = new HermesErrorPattern();
+        userCustom.setName("用户自定义模式");
+        userCustom.setPattern("custom-pat");
+        userCustom.setEnabled(true);
+        userCustom.setIsBuiltIn(false);
+        userCustom.onCreate();
+        legacyRepo.save(userCustom);
+
+        // 触发增量补齐
+        legacyService.initBuiltInPatterns();
+
+        List<HermesErrorPattern> all = legacyService.list();
+        // 1 同名保留 + 1 自定义 + (默认 25 - 1 同名) 新增 = 26
+        assertThat(all.size()).isEqualTo(ErrorPatternProperties.DEFAULT_BUILTINS.size() + 1);
+
+        // 同名老内置保持原样(pattern 与 enabled 都未被覆盖)
+        HermesErrorPattern stillOld = all.stream()
+                .filter(p -> "LLM SDK 重试耗尽".equals(p.getName()))
+                .findFirst().orElseThrow();
+        assertThat(stillOld.getPattern()).isEqualTo("old-pattern-text");
+        assertThat(stillOld.getEnabled()).isFalse();
+
+        // 用户自定义保留
+        assertThat(all.stream().anyMatch(p -> "用户自定义模式".equals(p.getName()))).isTrue();
+
+        // 默认列表中此前缺失的项被补齐
+        assertThat(all.stream().anyMatch(p -> "认证失败(英文)".equals(p.getName()))).isTrue();
+        assertThat(all.stream().anyMatch(p -> "JSON 解析失败".equals(p.getName()))).isTrue();
+    }
+
+    @Test
+    void initBuiltInPatterns_noopWhenAllDefaultsPresent() {
+        // setUp 已写入全部默认;再次触发(模拟重启)不应有任何新增
+        int before = service.list().size();
+        service.initBuiltInPatterns();
+        assertThat(service.list().size()).isEqualTo(before);
+    }
+
     // ========== CRUD ==========
 
     @Test

+ 13 - 5
frontend/src/components/layout/AppSidebar.vue

@@ -15,7 +15,8 @@ import {
   GitNetworkOutline,
   BookOutline,
   SparklesOutline,
-  WarningOutline
+  WarningOutline,
+  AppsOutline
 } from '@vicons/ionicons5'
 import { NIcon } from 'naive-ui'
 
@@ -45,15 +46,22 @@ const menuItems = [
       { key: '/history', label: '运行记录', icon: PlayCircleOutline }
     ]
   },
-  { key: '/tags', label: '标签管理', icon: PricetagsOutline },
-  { key: '/models', label: '模型管理', icon: SettingsOutline },
-  { key: '/hermes-errors', label: '错误模式管理', icon: WarningOutline }
+  {
+    group: '其他管理',
+    icon: AppsOutline,
+    children: [
+      { key: '/tags', label: '标签管理', icon: PricetagsOutline },
+      { key: '/models', label: '模型管理', icon: SettingsOutline },
+      { key: '/error-patterns', label: '错误模式管理', icon: WarningOutline }
+    ]
+  }
 ]
 
 // 分组展开状态:按 group 名称独立控制
 const groupExpanded = ref({
   '知识库管理': true,
-  '智能体管理': true
+  '智能体管理': true,
+  '其他管理': true
 })
 
 function toggleGroup(name) {

+ 2 - 2
frontend/src/router/index.js

@@ -54,8 +54,8 @@ const routes = [
     meta: { title: '模型管理' }
   },
   {
-    path: '/hermes-errors',
-    name: 'HermesErrorPatterns',
+    path: '/error-patterns',
+    name: 'ErrorPatterns',
     component: () => import('../views/system/HermesErrorPatterns.vue'),
     meta: { title: '错误模式管理' }
   },

+ 35 - 4
frontend/src/views/system/HermesErrorPatterns.vue

@@ -218,15 +218,15 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="hermes-error-page">
+  <div class="error-pattern-page">
     <div class="page-header">
       <div class="header-left">
         <h2 class="page-title">
           <n-icon size="22"><WarningOutline /></n-icon>
-          Hermes 错误模式管理
+          错误模式管理
         </h2>
         <div class="page-subtitle">
-          检测 Hermes Agent 响应中的 LLM 调用失败(HTTP 4xx / 限流 / 鉴权 / 配额),命中即按节点失败处理
+          检测智能体响应中的 LLM 调用失败(HTTP 4xx / 限流 / 鉴权 / 配额),命中即按节点失败处理
         </div>
       </div>
       <div class="header-right">
@@ -268,11 +268,13 @@ onMounted(() => {
 
     <div class="table-wrapper">
       <n-data-table
+        class="pattern-table"
         :columns="columns"
         :data="patterns"
         :loading="loading"
         :bordered="false"
         :single-line="false"
+        :striped="true"
         size="small"
         flex-height
         style="height: calc(100vh - var(--header-height) - 250px);"
@@ -326,7 +328,7 @@ onMounted(() => {
 </template>
 
 <style scoped>
-.hermes-error-page {
+.error-pattern-page {
   padding: 24px;
 }
 
@@ -396,4 +398,33 @@ onMounted(() => {
   border-radius: var(--radius-md);
   padding: 8px;
 }
+
+/* ========== NDataTable 颜色覆盖(与模型管理页保持一致) ========== */
+.pattern-table :deep(.n-data-table) {
+  --n-td-color: #0f1e3c;
+  --n-td-color-hover: rgba(37, 99, 235, 0.22);
+  --n-th-color: rgba(37, 99, 235, 0.28);
+  --n-th-text-color: var(--text-primary);
+  --n-td-text-color: var(--text-primary);
+  --n-border-color: rgba(56, 189, 248, 0.18);
+  --n-td-color-striped: #0a1628;
+}
+.pattern-table :deep(.n-data-table-th) {
+  background: rgba(37, 99, 235, 0.28) !important;
+  color: var(--text-primary) !important;
+  border-bottom: 1px solid rgba(56, 189, 248, 0.35) !important;
+  font-size: 13px;
+  font-weight: 600;
+}
+.pattern-table :deep(.n-data-table-td) {
+  background: #0f1e3c !important;
+  color: var(--text-primary) !important;
+  border-bottom: 1px solid rgba(56, 189, 248, 0.1) !important;
+}
+.pattern-table :deep(.n-data-table-tr--striped .n-data-table-td) {
+  background: #0a1628 !important;
+}
+.pattern-table :deep(.n-data-table-tr:hover .n-data-table-td) {
+  background: rgba(37, 99, 235, 0.18) !important;
+}
 </style>

+ 15 - 1
prompt.md

@@ -1444,4 +1444,18 @@ strict模式下,不识别多余字段,只识别必需字段是否存在。
 
 ---
 
-我的需求都写在 prompt.md 文件中。请根据我的需求(倒数几个需求),重新总结变更。同类变更放到一起。变更说明不要过于技术化。
+我的需求都写在 prompt.md 文件中。请根据我的需求(倒数几个需求),重新总结变更。同类变更放到一起。变更说明不要过于技术化。
+
+---
+
+1. 标签管理、模型管理、错误模式管理,合并到“其他管理”菜单下;
+2. “错误模式管理”页面,不要明确提Hermes的名字,因为模式也可用于后续其他智能体框架;
+3. “错误模式管理”页面,表格的表头颜色、表格颜色等样式,与“模型管理”页面保持一致;
+4. “错误模式管理”页面,内置的错误模式,再丰富一些;同时将内置错误模式管理放到application.yml,同步更新application.yml.example。
+
+---
+
+为什么前端页面显示还是只有8条错误模式?
+
+---
+