29 - AgentSpec与新型Agent

声明式Agent配置、热重载机制、DataAgent(NL2SQL)与VoiceAgent(语音通道)

一、AgentSpec 声明式配置

为什么需要 AgentSpec?

传统方式定义 Agent 需要编写 Java 代码(@Component + 继承 AbstractLlmAgent),对于运营/产品人员门槛较高。AgentSpec 提供声明式 YAML 配置,无需 Java 代码即可定义 Agent,大幅降低新 Agent 的创建门槛。

核心价值:将 Agent 定义从"编码"降维到"配置",让非开发人员也能快速创建和调整 Agent,实现"5 分钟上线一个新 Agent"。

YAML Spec 格式示例

内置示例文件:项目在 hub-api/src/main/resources/agent-specs/ 下提供了 2 个开箱即用的示例: translator-agent.yml(翻译助手)和 writer-agent.yml(写作助手), 展示了声明式 Agent 的完整字段用法(含 display 前端元数据)。 启用 agent.defaults.agent-spec.enabled=true 后自动加载注册。

# agents/deep_research.agent.yml
agent:
  id: "deep_research"
  name: "深度研究Agent"
  model: "gpt-4o"
  runtime-mode: "graph-parallel"
  max-iterations: 15
  system-prompt: |
    你是专业研究分析师...
  tools: [web_search, web_reader, note_taking]
  interceptors: [todo-list, context-editing]
  hooks: [summarization]

Spec 字段说明

字段必填说明默认值
idAgent 唯一标识,全局不可重复-
nameAgent 显示名称-
model使用的 LLM 模型default-model
runtime-mode执行模式:react / graph-parallelreact
max-iterations最大 ReAct 循环次数10
system-prompt系统提示词(支持多行)-
tools工具列表(匹配 ToolProvider 注册名)[]
interceptors拦截器列表[]
hooksHook 列表[]

二、加载与热重载机制

启动加载流程

  AgentSpec 启动加载流程:

  Application Startup
       │
       ▼
  ┌─────────────────────────────────────────────────────┐
  │ ① Startup Scan                                      │
  │   扫描 classpath:/agents/*.agent.yml                │
  │   扫描 config/agents/*.agent.yml                    │
  └──────────────────┬──────────────────────────────────┘
                     ▼
  ┌─────────────────────────────────────────────────────┐
  │ ② Parse YAML                                        │
  │   SnakeYAML 解析 → AgentSpec POJO                   │
  └──────────────────┬──────────────────────────────────┘
                     ▼
  ┌─────────────────────────────────────────────────────┐
  │ ③ Validate                                          │
  │   ├── Schema 校验(必填字段检查)                    │
  │   ├── 引用校验(tools/interceptors 是否已注册)     │
  │   └── ID 冲突检查(是否与代码定义的 Agent 重复)    │
  └──────────────────┬──────────────────────────────────┘
                     ▼
  ┌─────────────────────────────────────────────────────┐
  │ ④ Create Bean                                       │
  │   根据 Spec 动态创建 AgentBean                      │
  │   注入 model / tools / interceptors / hooks         │
  └──────────────────┬──────────────────────────────────┘
                     ▼
  ┌─────────────────────────────────────────────────────┐
  │ ⑤ Register                                          │
  │   注册到 AgentRegistry,可通过 agentId 路由         │
  └─────────────────────────────────────────────────────┘

热重载机制(FileWatcher)

基于 Java WatchService 监听 agent spec 目录的文件变更,实现运行时热重载:

  热重载流程:

  FileWatcher (WatchService)
       │ 检测到 .agent.yml 文件变更
       ▼
  ┌─────────────────────────────────────────────────────┐
  │ ① Detect Changes                                    │
  │   ENTRY_MODIFY / ENTRY_CREATE / ENTRY_DELETE        │
  └──────────────────┬──────────────────────────────────┘
                     ▼
  ┌─────────────────────────────────────────────────────┐
  │ ② Reload                                            │
  │   重新解析变更的 YAML 文件                          │
  │   校验新配置的合法性                                │
  └──────────────────┬──────────────────────────────────┘
                     ▼
  ┌─────────────────────────────────────────────────────┐
  │ ③ Recreate Agent Bean                               │
  │   旧 Bean 标记为 deprecated(引用计数 > 0 时延迟销毁)│
  │   新 Bean 注册到 AgentRegistry                      │
  │   新请求路由到新实例                                │
  └─────────────────────────────────────────────────────┘

配置优先级

优先级(从低到高):AgentSpec YAML < agent-config.yml 全局配置 < per-agent override(代码 Bean 定义优先级最高)
配置来源优先级说明
AgentSpec YAML最低声明式 spec 文件中的配置
agent-config.yml全局 Agent 配置文件
Per-agent override针对特定 Agent 的覆盖配置
代码 Bean 定义最高@Component 注解的 Java 代码定义

三、DataAgent(NL2SQL 自然语言查询)

核心能力

DataAgent 将用户的自然语言问题转换为 SQL 查询,自动执行并格式化返回结果。适用于业务数据查询、报表生成等场景。

安全防护措施

防护层机制说明
SQL 注入检测语法分析解析 SQL AST,检测异常结构
语句白名单仅 SELECT禁止 UPDATE / DELETE / DROP / INSERT
字段脱敏ACL 控制敏感列(密码、身份证)自动隐藏
查询超时30s 限制防止慢查询拖垮数据库
结果集限制最大 1000 行强制 LIMIT,防止大数据量返回
只读连接Read Replica使用只读数据库副本

执行流程

  DataAgent NL2SQL 执行流程:

  User: "上个月销售额最高的 5 个产品是什么?"
       │
       ▼
  ┌─────────────────────────────────────────────────────┐
  │ ① Intent Parse(意图解析)                          │
  │   识别:查询类型=聚合排序、时间=上月、实体=产品     │
  │   确认目标表:products, orders                      │
  └─────────��────────┬──────────────────────────────────┘
                     ▼
  ┌─────────────────────────────────────────────────────┐
  │ ② SQL Generation(SQL 生成)                        │
  │   LLM + Schema 上下文 → 生成 SQL                    │
  │   SELECT p.name, SUM(o.amount) as total             │
  │   FROM orders o JOIN products p ON o.product_id=p.id│
  │   WHERE o.created_at >= '2026-03-01'                │
  │   GROUP BY p.name ORDER BY total DESC LIMIT 5       │
  └──────────────────┬──────────────────────────────────┘
                     ▼
  ┌─────────────────────────────────────────────────────┐
  │ ③ Safety Check(安全检查)                          │
  │   ├── 白名单检查:仅 SELECT ✅                     │
  │   ├── 注入检测:无异常结构 ✅                       │
  │   ├── EXPLAIN 分析:无全表扫描 ✅                   │
  │   └── 字段 ACL:隐藏敏感列 ✅                      │
  └──────────────────┬──────────────────────────────────┘
                     ▼
  ┌─────────────────────────────────────────────────────┐
  │ ④ Sandbox Execute(沙箱执行)                       │
  │   只读连接 → PreparedStatement → 超时 30s           │
  │   并发控制:最多 5 个同时查询                       │
  └──────────────────┬──────────────────────────────────┘
                     ▼
  ┌─────────────────────────────────────────────────────┐
  │ ⑤ Result Format(结果格式化)                       │
  │   表格渲染 + 自然语言总结                           │
  │   "上月销售额 TOP5:1. 产品A (¥128万)..."          │
  └─────────────────────────────────────────────────────┘

四、VoiceAgent(语音通道)

语音处理架构

VoiceAgent 为 Agent 系统增加语音交互能力,通过 ASR(自动语音识别)和 TTS(文本转语音)实现全链路语音对话。

  VoiceAgent 全链路流程:

  🎤 用户语音输入
       │
       ▼
  ┌─────────────────────────────────────────────────────┐
  │ ① ASR(Automatic Speech Recognition)               │
  │   支持:Whisper ASR                                 │
  │   流式:音频分片 → 增量转写                         │
  │   输出:"帮我查一下今天的天气"                       │
  └──────────────────┬──────────────────────────────────┘
                     ▼
  ┌─────────────────────────────────────────────────────┐
  │ ② Text Processing                                   │
  │   ASR 文本 → 等同文本输入                           │
  │   共享 ConversationHistory(与文本 Agent 同源)     │
  │   进入标准 Agent 执行流程                           │
  └──────────────────┬──────────────────────────────────┘
                     ▼
  ┌─────────────────────────────────────────────────────┐
  │ ③ Agent Processing                                   │
  │   ReAct Loop / Graph 执行                           │
  │   流式响应生成                                      │
  └──────────────────┬──────────────────────────────────┘
                     ▼
  ┌─────────────────────────────────────────────────────┐
  │ ④ TTS(Text-to-Speech)                             │
  │   支持:MiniMax TTS                                 │
  │   分句合成:第一句完成即播放                        │
  │   流式输出:边生成边合成边播放                      │
  └──────────────────┬──────────────────────────────────┘
                     ▼
  🔊 语音播放给用户

流式语音处理

阶段流式策略延迟优化
ASR音频分片(chunk)→ 增量转写不等完整语音,边听边转
Agent首 Token 流式响应不等完整生成,首 Token 即返回
TTS分句合成第一句准备好即播放,不等全文
传输WebSocket 长连接避免 HTTP 握手开销
端到端延迟目标:< 3 秒。通过全链路流式处理,用户感知到的响应延迟 = ASR 首字延迟 + Agent 首 Token 延迟 + TTS 首句合成延迟,而非各阶段耗时之和。

五、配置参考

# application.yml — AgentSpec 相关配置
agent:
  spec:
    enabled: true
    scan-paths:
      - classpath:/agents/
      - config/agents/
    hot-reload:
      enabled: true
      watch-interval: 5s       # 文件变更检测间隔
      graceful-shutdown: true   # 优雅切换(等待活跃对话结束)

# DataAgent 配置
agent:
  data-agent:
    enabled: true
    datasource: read-replica     # 使用只读数据库副本
    query-timeout: 30s           # 查询超时
    max-rows: 1000               # 最大返回行数
    max-concurrent: 5            # 最大并发查询数
    allowed-statements: [SELECT] # 允许的 SQL 语句类型
    redacted-columns:            # 脱敏字段列表
      - password
      - id_card
      - bank_card

# VoiceAgent 配置
agent:
  voice-agent:
    enabled: true
    asr:
      provider: whisper
      language: zh
      streaming: true
    tts:
      provider: minimax
      voice-id: "female-yujie"
      speed: 1.0
      streaming: true

六、面试高频问题

Q: AgentSpec 热重载时,正在执行中的对话如何处理?
A: 优雅切换:旧 Agent 实例保留直到活跃对话结束。新请求路由到新实例。引用计数:active_conversations > 0 时延迟销毁旧实例。具体流程:FileWatcher 检测到变更 → 创建新 Agent Bean → 新请求路由到新实例 → 旧实例引用计数归零后 GC 回收。类似于 Kubernetes 的滚动更新策略。
Q: NL2SQL 如何防止 SQL 注入?Agent 生成的 SQL 也可能恶意?
A: 多层防护:1. SQL 白名单(仅 SELECT);2. 参数化查询(PreparedStatement);3. 结果集大小限制;4. 执行超时(30s);5. 字段级 ACL(隐藏敏感列);6. 只读数据库连接。即使 LLM 被提示词注入攻击诱导生成恶意 SQL,白名单和只读连接也能兜底。关键:永远不信任 LLM 的输出,对待 LLM 生成的 SQL 与对待用户输入一样谨慎。
Q: 语音 Agent 的延迟如何优化?端到端延迟目标是多少?
A: 目标 < 3s。优化策略:1. 流式 ASR(边听边转写);2. 首 Token 流式响应(不等完整生成);3. TTS 分句合成(第一句准备好即播放);4. WebSocket 长连接避免握手开销。核心思想:全链路流式管道——各阶段并行执行而非串行等待,用户感知延迟 = max(各阶段首包延迟) 而非 sum(各阶段总耗时)。
Q: AgentSpec 与代码定义的 Agent 如何共存?优先级冲突怎么办?
A: 明确优先级:代码 Bean > per-agent config > agent-config.yml > AgentSpec。相同 ID 冲突:代码定义优先,Spec 被忽略并记录 WARN 日志。推荐工作流:Spec 用于快速原型和运营调整,验证稳定后迁移到代码定义。两者通过 AgentRegistry 统一管理,对外暴露一致的路由接口。
Q: DataAgent 查询大表时如何防止拖垮数据库?
A: 五层防护:1. LIMIT 强制注入(最大 1000 行);2. EXPLAIN 分析执行计划(全表扫描直接拒绝);3. 只读 Replica 路由(不影响主库);4. 查询超时 30s(超时强制 kill);5. 并发控制(Semaphore 限制同时最多 5 个查询)。额外措施:慢查询日志告警,DBA 可配置表级黑名单禁止查询超大表。
Q: 语音 Agent 如何处理多轮对话的上下文?语音没有"历史消息"显示?
A: 与文本 Agent 共享同一 ConversationHistory。语音转文字后等同文本输入,进入标准对话管理流程。区别处理:1. 语音输入可能有 ASR 错误(需容错,如"帮我查一下天气"可能被识别为"帮我差一下天气");2. 响应需简短(语音不适合长文本,超过 200 字自动摘要);3. 可选 TTS 摘要模式(只播放关键结论,详细内容推送到文本通道)。
Q: AgentSpec 的 tools 字段如何与 MCP 工具对接?
A: tools 列表中的字符串与 ToolProvider 注册的 toolName 匹配。MCP 工具通过 McpToolProvider 自动注册到 ToolProvider 注册表。Spec 中引用 MCP 工具名即可,无需额外配置。运行时校验:Spec 中未注册的工具名会在启动时记录 WARN 告警,但不阻止 Agent 创建(缺失工具在运行时会跳过)。
Q: 如果 AgentSpec 的 YAML 格式有误,如何处理?会影响其他 Agent 启动吗?
A: 隔离加载:每个 Spec 文件独立解析,单个失败记录 ERROR 日志但不影响其他 Agent。校验阶段:Schema Validation(必填字段检查)→ 引用检查(tools/interceptors 是否已注册)→ ID 唯一性检查。降级处理:无效 Spec 跳过,不创建 Agent Bean。管理端可通过 /api/admin/agents/spec-status 查看所有 Spec 的加载状态和错误详情。