云计算百科
云计算领域专业知识百科平台

VoltAgent 实战:用 TypeScript 构建可观测的 AI Agent

摘要

从零实现 Agent 时,除了模型调用,还要处理工具注册、记忆、流式响应、工作流、人工确认、MCP 接入和运行观测。只用一个循环不断调用模型,原型可以运行,任务一复杂就容易失控。

VoltAgent 是一个开源 TypeScript Agent 工程平台,提供 Agent Runtime、工具、记忆、工作流、MCP、RAG 和多智能体等能力,同时配套可观测与运营能力。本文从创建项目开始,构建一个带工具和记忆的客服 Agent,再讨论工作流暂停恢复、权限边界和生产部署注意事项。

一、背景与问题

一个工具型 Agent 的基本循环如下:

用户问题
→ 模型理解任务
→ 判断是否调用工具
→ 执行工具
→ 把工具结果交给模型
→ 继续调用或生成最终答案

如果所有逻辑都手写在一个文件中,很快会遇到:

  • 工具参数校验分散。
  • 对话记忆无法持久化。
  • 多步任务没有统一状态。
  • 工具失败后难以重试或恢复。
  • 客户端断开后任务状态不明确。
  • Agent 的 Token、延迟和工具调用无法追踪。

VoltAgent 的设计重点是把 Agent 工程拆成运行时、工具、记忆、工作流和观测等可组合部分。官方仓库将其定位为 TypeScript Agent 工程平台,并提供 @voltagent/core、工作流、工具注册、MCP 和记忆等能力。

二、核心概念

1. Agent

Agent 通常由以下元素组成:

元素作用
Instructions 角色、边界和行为规则
Model 实际调用的模型
Tools Agent 可以调用的外部能力
Memory 跨轮次保存的上下文
Runtime 执行模型与工具循环
Guardrails 输入输出和动作约束

Agent 的工具集合越多,模型需要处理的工具描述越多,Prompt 成本和误调用概率也会增加。

2. Tool

一个合格的工具不只是一个函数,还应包含:

  • 明确的名称和描述。
  • 可校验的输入 Schema。
  • 权限要求。
  • 超时和重试边界。
  • 是否产生副作用。
  • 审计信息。

查询工单和修改工单的安全级别不同,不能只因为它们都是 TypeScript 函数就采用同样的策略。

3. Memory

记忆至少分为:

  • 短期记忆:当前会话消息。
  • 长期记忆:用户偏好、历史事实和任务结果。
  • 业务状态:订单、工单、审批等可验证数据。

业务状态不能只放在模型记忆中。模型记忆可以帮助理解上下文,但不能替代数据库中的真实状态。

4. Workflow

Workflow 适合描述可预测的多步流程,例如报销审批、文档处理和客服升级。它与自由规划的 Agent 不同:

类型特点
Agent 模型动态决定下一步
Workflow 开发者预先定义步骤和分支
Agent + Workflow Agent 负责局部决策,Workflow 负责整体边界

高风险流程通常应让 Workflow 掌握主流程,把 Agent 限制在某个步骤内。

三、工作原理

1. VoltAgent 运行结构

VoltAgent 应用
├─ Agent
│ ├─ Instructions
│ ├─ Model Provider
│ ├─ Tools
│ └─ Memory
├─ Workflow
│ ├─ Step
│ ├─ Suspend
│ └─ Resume
├─ Server
└─ Logger / Observability

应用启动后,服务端暴露 Agent 和工作流的运行入口,业务系统可以通过 HTTP 或其他集成方式发起任务。

2. 工具调用循环

输入消息
→ 组装系统指令、历史消息和工具 Schema
→ 调用模型
→ 如果返回文本:结束
→ 如果返回工具调用:
校验参数
检查权限
执行工具
记录结果
回到模型

循环必须设置最大步数、最大总耗时和总 Token 预算。模型“认为还需要继续”不等于系统必须无限执行。

3. 工作流暂停与恢复

涉及人工审批的流程不能一直占用 HTTP 请求。更合理的方式是:

执行步骤
→ 发现需要人工确认
→ 持久化状态
→ suspend
→ 返回任务 ID
→ 人工提交决定
→ resume
→ 继续后续步骤

暂停状态要保存在可靠存储中,不能只存在 Node.js 进程内存。

四、实战示例

1. 创建项目

VoltAgent 官方仓库提供了创建项目的 CLI 入口:

npm create voltagent-app@latest
cd my-agent-app
npm install

创建时根据提示选择模型供应商和项目选项。实际命令和模板会随版本变化,建议以当前仓库 README 和官方文档为准。

2. 配置模型密钥

OPENAI_API_KEY=replace-with-development-key

开发环境可以使用 .env,但不要将真实密钥提交到仓库。生产环境应通过 Secret 管理系统注入,并限制应用进程读取范围。

3. 定义一个工具

import { z } from "zod";

export const queryTicketTool = {
name: "query_ticket",
description: "查询当前用户有权限访问的工单信息",
parameters: z.object({
ticketId: z.string().min(1),
}),
execute: async ({ ticketId }: { ticketId: string }) => {
const ticket = await ticketRepository.findVisible(ticketId);
if (!ticket) {
return { found: false };
}
return {
found: true,
id: ticket.id,
status: ticket.status,
summary: ticket.summary,
};
},
};

示例中的 findVisible 应在数据库查询层执行租户和用户权限过滤。不能先查出全部工单,再把权限判断交给模型。

4. 创建 Agent

import { Agent, Memory, VoltAgent } from "@voltagent/core";
import { LibSQLMemoryAdapter } from "@voltagent/libsql";
import { createPinoLogger } from "@voltagent/logger";
import { honoServer } from "@voltagent/server-hono";
import { openai } from "@ai-sdk/openai";
import { queryTicketTool } from "./tools/query-ticket";

const memory = new Memory({
storage: new LibSQLMemoryAdapter({
url: "file:./.voltagent/memory.db",
}),
});

const supportAgent = new Agent({
name: "support-agent",
instructions: `
你是客服工单助手。
只能查询当前用户有权限访问的工单。
无法确认的信息要明确说明,不要编造工单状态。
涉及修改、关闭或删除工单时,必须要求人工确认。
`
,
model: openai("gpt-4o-mini"),
tools: [queryTicketTool],
memory,
});

const logger = createPinoLogger({
name: "support-agent",
level: "info",
});

new VoltAgent({
agents: {
supportAgent,
},
server: honoServer(),
logger,
});

这里使用了持久化记忆,但记忆数据库仍需要备份、加密和访问控制。对话记忆不应无限增长,应该设置会话过期和摘要策略。

5. 启动开发服务

npm run dev

启动后先用本地测试请求验证:

curl -X POST http://localhost:3141/agents/support-agent \\
-H "Content-Type: application/json" \\
-d '{"message":"请查询工单 INC-1001"}'

不同版本的服务路由可能存在差异,实际调用路径以启动日志和项目模板生成的路由为准。

6. 增加工作流边界

对于“查询后决定是否升级”的任务,可以将关键步骤显式建模:

import { createWorkflowChain } from "@voltagent/core";
import { z } from "zod";

export const escalationWorkflow = createWorkflowChain({
id: "ticket-escalation",
name: "Ticket Escalation",
purpose: "根据工单信息决定是否升级到人工团队",
input: z.object({
ticketId: z.string(),
reason: z.string(),
}),
result: z.object({
status: z.enum(["pending", "escalated", "rejected"]),
note: z.string(),
}),
});

生产实现中可以继续追加查询、规则判断、人工确认和结果写入步骤。涉及外部写操作时,不要只让模型输出“已升级”,必须由后端工具真正执行并返回可验证结果。

五、常见问题与实践建议

1. 为什么 Agent 总是调用错误的工具?

检查工具描述是否重叠、参数 Schema 是否清晰、工具数量是否过多,以及是否把不相关工具全部暴露给每个 Agent。可以按任务动态提供工具集合,并为工具名称使用稳定、具体的动词。

2. 记忆会不会泄露其他用户的信息?

会,尤其是在会话 ID、用户 ID 和租户 ID 没有严格绑定时。每次读取记忆都要使用服务端生成的作用域,不能直接信任客户端传来的 conversation ID。

3. 是否应该让 Agent 自动执行写操作?

默认不应该。写操作需要权限检查、参数校验、幂等键、审计和人工确认。只要操作涉及资金、删除、权限变更或对外发送消息,就应设置更严格的审批边界。

4. 如何限制 Agent 的成本?

设置单任务最大步数、输入和输出 Token 上限、工具调用次数、总耗时和模型级预算。长任务可以采用低成本模型做规划,高能力模型只处理关键步骤。

5. VoltOps 等观测能力能替代自建监控吗?

平台观测适合查看 Agent 执行过程和开发调试,但企业仍需将关键指标接入自己的日志、指标、审计和告警系统。尤其是租户费用、业务权限和敏感数据访问,不能只依赖第三方控制台。

六、进阶思考

1. Agent 与业务系统的边界

建议采用以下边界:

Agent:理解自然语言、规划、选择工具
业务服务:权限、事务、数据校验、状态变更
数据库:最终事实和一致性
观测系统:执行记录、成本和质量数据

Agent 可以提出动作,但真正的业务变更应由业务服务完成。

2. MCP 接入

VoltAgent 可以连接 MCP 服务,把外部工具和数据源纳入 Agent。接入时仍要建立工具白名单、服务器身份、传输安全、参数审计和超时策略。

MCP 解决的是工具连接协议,不会自动解决业务权限。一个 MCP Server 暴露的工具仍然需要由应用决定哪些用户和 Agent 可以使用。

3. 可恢复执行

长任务需要保存:

  • taskId 和 parentTaskId。
  • 当前步骤和输入摘要。
  • 工具调用记录。
  • 重试次数和错误分类。
  • 暂停原因和恢复凭据。
  • 最终结果与费用。

这样客户端断线、Pod 重启或模型超时后,任务仍然可以查询、重试或人工接管。

4. 评估 Agent 行为

应为每个重要 Agent 准备固定任务集,检查:

  • 是否选择正确工具。
  • 是否遵守权限边界。
  • 是否在需要时请求确认。
  • 是否能从工具错误中恢复。
  • 是否产生多余调用。
  • 是否控制 Token 和延迟。

Agent 的评估对象不只是最终文本,还包括决策轨迹和工具动作。

结论

VoltAgent 将 TypeScript Agent 开发拆分为 Agent、工具、记忆、工作流、MCP 和观测等能力,适合用来构建比简单聊天接口更复杂的任务型应用。它可以减少重复的运行时工程,但不会替代业务权限、数据一致性和安全审查。

实践时建议先实现只读工具,再加入持久化记忆和工作流;最后接入写操作、MCP 和多智能体协作。每一步都要设置最大步数、超时、预算、审计和可恢复状态。

参考资料

  • VoltAgent GitHub Repository:https://github.com/VoltAgent/voltagent
  • VoltAgent Documentation:https://voltagent.dev/docs/
  • Model Context Protocol:https://modelcontextprotocol.io/
  • Zod Documentation:https://zod.dev/
  • 赞(0)
    未经允许不得转载:网硕互联帮助中心 » VoltAgent 实战:用 TypeScript 构建可观测的 AI Agent
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!