声明式Agent规格、手动 reload、DataAgent(NL2SQL)与语音通道
传统方式定义 Agent 需要编写 Java 代码(@Component + 继承 AbstractLlmAgent)。AgentSpec 提供声明式 YAML/JSON 规格,当前重点是规格解析、校验、配置合并和管理端维护,适合作为 Agent 配置治理与后续动态 Agent 化的基础。
内置示例文件:项目在 hub-api/src/main/resources/agent-specs/ 下提供了 2 个开箱即用的示例:
translator-agent.yml(翻译助手)和 writer-agent.yml(写作助手),
展示了声明式 Agent 的完整字段用法(含 display 前端元数据)。
启用 agent.defaults.agent-spec.enabled=true 后会在启动时扫描、解析并合并到运行配置。
# agent-specs/deep_research.yml id: "deep_research" name: "深度研究Agent" description: "执行多步骤深度调研任务" avatar: "research" systemPrompt: | 你是专业研究分析师... tools: [web_search, web_reader, note_taking] capabilities: [HISTORY_CONTEXT, MODEL_SELECTION, SOURCE_CITATION] timeoutSeconds: 300 maxRetries: 1 modelId: null
| 字段 | 必填 | 说明 | 默认值 |
|---|---|---|---|
id | 是 | Agent 唯一标识,全局不可重复 | - |
name | 是 | Agent 显示名称 | - |
modelId | 否 | 使用的 LLM 模型 ID | 系统默认 |
timeoutSeconds | 否 | 单次请求超时时间 | 300 |
maxRetries | 否 | 最大重试次数 | 1 |
systemPrompt | 是 | 系统提示词(支持多行) | - |
tools | 否 | 工具列表(匹配 ToolProvider 注册名) | [] |
capabilities | 否 | 能力声明,使用 AgentCapability 枚举名 | [HISTORY_CONTEXT] |
display | 否 | 前端展示元数据 | 默认显示配置 |
AgentSpec 启动加载流程:
Application Startup
│
▼
┌─────────────────────────────────────────────────────┐
│ ① Startup Scan │
│ 扫描 agent.defaults.agent-spec.spec-dir │
│ 默认 classpath:agent-specs/ │
└──────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ ② Parse YAML │
│ SnakeYAML 解析 → AgentSpec POJO │
└──────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ ③ Validate │
│ ├── 必填字段检查:id / name / systemPrompt │
│ ├── timeoutSeconds 合法性 │
│ └── 能力枚举尽量转换,非法值会被忽略 │
└──────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ ④ Merge Config │
│ 合并到 AgentReactProperties.agents │
│ 作为运行时配置来源之一 │
└──────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ ⑤ Runtime Use │
│ 被运行时配置读取;动态 Agent 注册仍是演进点 │
└─────────────────────────────────────────────────────┘
管理端可创建、编辑、删除规格文件,并通过 reload 接口重新扫描规格目录;当前代码没有 AgentSpec 专用 WatchService 文件监听,也没有直接替换 AgentRegistry 中的运行中 Agent 实例。
手动 reload 流程:
AgentSpecView / Admin API
│ POST /api/admin/agent-specs/reload
▼
┌─────────────────────────────────────────────────────┐
│ ① Rescan │
│ 重新扫描 spec-dir 与 classpath 示例 │
└──────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ ② Reload │
│ 重新解析变更的 YAML 文件 │
│ 校验新配置的合法性 │
└──────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ ③ Refresh Cache │
│ 刷新 loadedSpecs 管理端缓存 │
│ 启动阶段由 Initializer 合并到运行配置 │
└─────────────────────────────────────────────────────┘
| 配置来源 | 优先级 | 说明 |
|---|---|---|
| AgentSpec YAML | 最低 | 声明式 spec 文件中的配置 |
| agent-config.yml | 中 | 全局 Agent 配置文件 |
| Per-agent override | 高 | 针对特定 Agent 的覆盖配置 |
| 代码 Bean 定义 | 最高 | @Component 注解的 Java 代码定义 |
DataAgent 将用户的自然语言问题转换为 SQL 查询,自动执行并格式化返回结果。适用于业务数据查询、报表生成等场景。
| 防护层 | 机制 | 说明 |
|---|---|---|
| SQL 安全检查 | 字符串白名单 | 必须以 SELECT 开头,并拦截独立出现的 DML/DDL 关键词 |
| 语句白名单 | 仅 SELECT | 禁止 UPDATE / DELETE / DROP / INSERT |
| 字段脱敏 | 列名关键词 | 列名包含 password/secret/token/key 等时返回 [REDACTED] |
| 查询超时 | 30s 限制 | 防止慢查询拖垮数据库 |
| 结果集限制 | 最大 100 行 | 无 LIMIT 时自动追加 LIMIT 100,并设置 Statement maxRows |
| 只读连接 | Connection readOnly | 执行查询前设置连接只读,未单独实现 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 ✅ │
│ ├── 禁止关键词:无 DML/DDL ✅ │
│ ├── 自动 LIMIT:最多 100 行 ✅ │
│ └── 敏感列脱敏:password/token/key ✅ │
└──────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ ④ Execute(只读查询) │
│ conn.setReadOnly(true) → Statement → 超时 30s │
│ stmt.setMaxRows(100) │
└──────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ ⑤ Result Format(结果格式化) │
│ 表格渲染 + 自然语言总结 │
│ "上月销售额 TOP5:1. 产品A (¥128万)..." │
└─────────────────────────────────────────────────────┘
VoiceChannelController 为 Agent 系统增加语音入口,通过 ASR(自动语音识别)把音频转文本,再复用标准 Agent 调用链路;当输出模式为 audio 时,再通过 TTS 返回音频。
VoiceChannel 全链路流程:
🎤 用户语音输入
│
▼
┌─────────────────────────────────────────────────────┐
│ ① ASR(Automatic Speech Recognition) │
│ 支持:OpenAI Whisper 兼容接口 │
│ 当前实现:上传音频文件 → 同步转写 │
│ 输出:"帮我查一下今天的天气" │
└──────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ ② Text Processing │
│ ASR 文本 → 等同文本输入 │
│ 通过 ChannelServiceSupport 复用 Agent 调用链路 │
│ 进入标准 Agent 执行流程 │
└──────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ ③ Agent Processing │
│ ReAct Loop / Graph 执行 │
│ 聚合回复文本 │
└──────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ ④ TTS(Text-to-Speech) │
│ 支持:OpenAI TTS 兼容接口 │
│ 同步合成完整回复文本 │
└──────────────────┬──────────────────────────────────┘
▼
🔊 语音播放给用户
| 阶段 | 当前实现 | 可继续优化 |
|---|---|---|
| ASR | Multipart 上传音频,同步调用转写 API | 流式 ASR / 分片上传 |
| Agent | 复用标准 Agent 调用链路,聚合文本回复 | 语音场景下的首 Token 流式回传 |
| TTS | 同步合成完整回复文本 | 分句合成 / 流式音频返回 |
| 传输 | HTTP API:/api/voice/chat、/asr、/tts、/voices | WebSocket 双工语音会话 |
# application.yml — AgentSpec 相关配置
agent:
defaults:
agent-spec:
enabled: true
spec-dir: "classpath:agent-specs/"
# DataAgent 当前安全边界在 NL2SqlToolProvider 代码常量中:
# MAX_ROWS=100, QUERY_TIMEOUT_SECONDS=30, SELECT 白名单,
# password/secret/token/key 等列名关键词脱敏。
# VoiceChannel 配置
hub:
voice:
enabled: true
asr-provider: openai
tts-provider: openai
default-voice: alloy
default-language: zh-CN
max-audio-size-mb: 25
max-text-length: 5000
output-format: mp3
speed: 1.0
AgentRegistry 中正在运行的 Agent 实例。因此不存在“旧实例优雅下线、新实例接管”的已完成链路;这部分可以作为后续动态 Agent 化设计讲。
setMaxRows;4. 查询超时 30s;5. 敏感列名关键词脱敏;6. 连接设置 readOnly。关键:永远不信任 LLM 的输出,对待 LLM 生成的 SQL 与对待用户输入一样谨慎。
setMaxRows(100)、setQueryTimeout(30)、SELECT 白名单和敏感字段脱敏。EXPLAIN 全表扫描拦截、Read Replica 路由、并发闸门和表级黑名单还未在该工具中实现,适合作为生产增强项讲。
VoiceChannelController 调用时没有传入会话 ID 和历史记录,因此更接近一次语音入口的单轮 Agent 调用。它也不会自动做“200 字摘要”或“TTS 摘要模式”;如果语音回复过长,只会按配置的 maxTextLength 截断传给 TTS。面试时可把语音多轮会话和语音摘要作为后续体验优化点。
/api/admin/agent-specs 查看已加载规格;不存在 /api/admin/agents/spec-status 这个状态端点。