尧图精选

Agent-Native架构:以智能体为原生单元的系统设计范式

🕒 发布时间:2026/9/26 7:58:31 📁 来源:尧图网络
1. “agent-native”不是新框架而是架构范式的悄然转向最近在几个开源项目和内部技术分享里反复看到agent-native这个词——它没出现在任何 npm 包名、GitHub 仓库名或 RFC 文档标题里却频繁出现在架构设计评审的白板角落、PR 描述的第一行、甚至 TypeScript 类型定义的注释中。它不叫“AgentNative.js”也不提供 CLI 工具链它没有 logo没有官网甚至没有独立的 GitHub Stars 排行榜。但它正在真实地重塑我们写后端服务、构建 AI 应用、设计数据管道的方式。简单说agent-native 是一种以“智能体Agent为原生计算单元”的系统设计哲学。它不把 Agent 当作插件、中间件或调用方而是像当年“cloud-native”把容器当作基础设施原语一样把 Agent 视为调度、状态管理、错误恢复、可观测性乃至部署单元的基本粒度。你写的不是“一个 API 一个数据库连接”而是一个能自主决策、可中断重入、带上下文生命周期、具备工具调用契约的Agent 实例。这直接解释了为什么热搜词里反复出现TypeScript、PostgreSQL、actions的组合——它们不是偶然并列而是 agent-native 架构落地时最常交汇的技术锚点TypeScript成为事实上的首选语言不是因为类型检查炫酷而是它的类型系统能精准建模 Agent 的输入契约InputSchema、动作接口ActionTool、状态迁移StateTransitionT和事件流ObservableEvent。一个AgentChatState, {search: SearchTool, write: DBWriteTool}类型比 YAML 配置或 JSON Schema 更早暴露逻辑矛盾。PostgreSQL不再只是“存数据的地方”。在 agent-native 场景下它承担三重角色状态快照存储agent_state表按agent_id version记录每次决策后的完整上下文、动作审计日志agent_action_log表记录tool_name,input_hash,output_truncated,duration_ms,error_code、动态工具注册中心tool_registry表存tool_id,schema_json,endpoint_url,auth_config支持运行时热加载。我见过团队用pg_cron每 5 分钟自动清理超过 72 小时的中间状态靠的就是 PostgreSQL 的 MVCC 和分区表能力而不是引入 Redis 或 Kafka 做额外状态协调。actions这个词在摘要里轻描淡写却是 agent-native 的心脏节律。它不是 RESTful 的 CRUD 动作而是原子化、可重入、带副作用声明的函数式操作单元。比如一个send_emailaction 必须声明idempotent: true幂等、side_effects: [email_sent, notification_recorded]副作用清单、timeout_ms: 8000硬超时。这些声明不是文档备注而是被运行时强制校验——当 Agent 在执行链中调用它时框架会自动注入重试策略、副作用追踪钩子、超时熔断器。这正是为什么temporal logic of actions plusTLA这类形式化验证工具突然在 agent-native 社区升温我们需要数学证明“在任意网络分区下transfer_fundsaction 调用不会导致账户余额为负”。提示别被“native”二字误导。agent-native 不是某种新操作系统或硬件抽象层。它本质是对“计算”边界的重新划定——过去我们说“这个服务 native 支持 Kubernetes”现在我们说“这个 Agent native 支持状态持久化、工具发现、跨会话恢复”。它的“原生性”体现在运行时契约上而非部署形态。这种转向正在快速渗透。你可能已经用过Vercel 的AI SDK中createAgent()的返回对象自带.run()和.resume()方法LangChain 的RunnableWithMessageHistory其实是在模拟 agent-native 的状态延续甚至 PostgreSQL 的pgvector扩展配合pg_stat_statements正在成为最朴素的 agent-native 向量记忆与行为审计方案。它不是未来式而是正在进行时——只是多数人还没给它起对名字。2. 为什么 agent-native 必须绕开传统框架的“舒适陷阱”当我第一次在客户现场看到他们用 Express.js TypeScript 写了一个“Agent 管理平台”我立刻意识到问题所在整个代码库里有 37 个router.post(/api/v1/agent/:id/run)每个路由 handler 里都手动处理 session 恢复、工具参数校验、错误分类、状态落库。开发同学自豪地说“我们支持 12 种工具全部用 class 继承实现”——但当我问“如果某个工具调用超时如何保证 Agent 状态不丢失且能精确续跑”时他沉默了 47 秒然后打开一个 Excel 表格指着一行写着“TODO: 加重试逻辑”的单元格。这就是传统 Web 框架Express、NestJS、Fastify在 agent-native 场景下的根本性失配。它们的设计哲学是请求-响应Request-Response而 agent-native 的核心范式是状态机驱动的动作流State-Machine Driven Action Flow。强行套用前者就像用 Word 编辑视频时间轴——功能上勉强能做但所有关键能力状态快照、动作回滚、跨节点迁移、可观测性埋点都得自己造轮子且极易出错。让我用一个具体对比说明差异维度传统 Web 框架如 NestJSagent-native 运行时如自研轻量内核入口点Post(/run)装饰器绑定 HTTP 路由agent.run(input)方法调用HTTP 只是其中一种触发器也可来自 WebSocket、MQTT、定时任务状态管理依赖外部存储Redis Session或内存变量易丢失状态作为一等公民state: T类型参数化每次动作后自动序列化到 PostgreSQLagent_state表含version和last_updated_at动作执行await tool.execute(params)手动调用错误需 try/catch 处理await agent.perform(search_web, {query: typescript agent patterns})框架自动注入重试、超时、副作用记录、错误分类network_error / auth_failed / rate_limited可观测性需集成 OpenTelemetry SDK手动添加 span每次perform()自动生成结构化日志{agent_id, action_id, input_hash, output_size_bytes, duration_ms, error_type}直接写入agent_action_log表支持 SQL 聚合分析扩展性新增工具需修改路由、更新 DTO、重启服务新工具只需向tool_registry表插入一行 JSONAgent 实例下次perform()时自动发现并加载这个差异直接导致工程成本的指数级分化。我们曾帮一家金融 SaaS 客户重构其合规审查 Agent原 Express 版本 2300 行代码其中 68% 是胶水代码状态同步、错误转换、日志格式化、超时包装迁移到 agent-native 内核后核心业务逻辑仅剩 412 行其余由运行时契约保障。更关键的是故障定位时间从平均 42 分钟降至 90 秒——因为所有动作日志都带agent_id和action_id直接SELECT * FROM agent_action_log WHERE agent_id abc123 ORDER BY created_at DESC LIMIT 20就能看到完整执行链。注意这不是贬低传统框架。Express 在构建 REST API 时依然高效可靠。问题在于当你的核心实体是 Agent 而非 Request 时框架的抽象边界就错了。就像用 Excel 做 ERP 系统——不是不能做而是每增加一个需求都在加固技术债的城墙。另一个常见陷阱是过度依赖现有 AI 框架如 LangChain、LlamaIndex。它们提供了强大的链式调用能力但默认不解决状态持久化粒度和动作原子性这两个 agent-native 的基石问题。LangChain 的RunnableWithMessageHistory用内存或外部 store 存消息但无法保证“在search_web动作执行到一半时进程崩溃重启后能从该动作的中间状态继续而非重放整个链”。而 agent-native 要求每个perform()调用都是事务性的要么完整成功并提交状态要么失败并回滚到前一个一致点。这需要底层运行时与 PostgreSQL 的SAVEPOINT和ROLLBACK TO SAVEPOINT深度协同——这是框架层面必须内置的能力而非应用层补丁。3. PostgreSQLagent-native 架构中被严重低估的“状态中枢”在 agent-native 的技术栈讨论中PostgreSQL 常被简化为“存 Agent 状态的数据库”。这种认知错失了它作为统一状态中枢Unified State Hub的战略价值。它不只是存储更是状态协调、动作审计、工具治理、故障恢复的单一可信源Single Source of Truth。我见过太多团队为解决状态一致性问题堆叠 Redis缓存状态、Kafka动作事件流、MongoDB原始日志、PostgreSQL最终结果结果是数据漂移、调试地狱、运维复杂度爆炸。而 agent-native 的优雅解法是让 PostgreSQL 承担全部状态相关职责通过其原生能力实现“一库多用”。3.1 状态表设计超越简单的 JSON 字段传统做法是建一张agents表用state JSONB字段存整个 Agent 状态。这看似简单但带来三个致命问题查询性能差、版本控制难、变更审计弱。正确的 agent-native 设计是将状态拆解为结构化、可索引、带版本的实体-- 核心状态表支持乐观并发控制 CREATE TABLE agent_state ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), agent_id VARCHAR(64) NOT NULL, -- Agent 唯一标识 version BIGINT NOT NULL DEFAULT 0, -- 状态版本号用于 CAS 更新 state_data JSONB NOT NULL, -- 序列化后的状态对象如 { current_step: analyze, context: {...} } created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE (agent_id, version) ); -- 索引确保高频查询性能 CREATE INDEX idx_agent_state_agent_id ON agent_state (agent_id); CREATE INDEX idx_agent_state_updated_at ON agent_state (updated_at); CREATE INDEX idx_agent_state_version ON agent_state (agent_id, version);关键设计点version字段是乐观锁核心每次UPDATE必须WHERE agent_id ? AND version ?失败则读取最新version并重试。这避免了分布式环境下状态覆盖。UNIQUE (agent_id, version)强制版本线性增长防止并发写入产生重复版本也便于回溯历史状态SELECT * FROM agent_state WHERE agent_id x ORDER BY version DESC LIMIT 5。state_data保持 JSONB 类型兼顾灵活性与查询能力state_data-current_step可索引。我们曾用此设计支撑单日 270 万 Agent 实例的状态更新P99 延迟稳定在 12ms 内。对比纯内存方案它解决了进程崩溃后的状态丢失问题对比 Redis 方案它消除了双写一致性风险。3.2 动作日志表从日志到可执行的审计证据agent_action_log表不是为了“看日志”而是为了在故障时生成可执行的恢复指令。它的字段设计直指 agent-native 的核心诉求CREATE TABLE agent_action_log ( id SERIAL PRIMARY KEY, agent_id VARCHAR(64) NOT NULL, action_id VARCHAR(128) NOT NULL, -- 如 search_web, write_to_db input_hash CHAR(64) NOT NULL, -- SHA256(input_json)用于幂等判断 output_summary TEXT, -- 截断的输出摘要避免大字段膨胀 duration_ms INTEGER NOT NULL, -- 执行耗时毫秒 status VARCHAR(20) NOT NULL CHECK (status IN (success, failed, timeout, cancelled)), error_code VARCHAR(50), -- 标准化错误码如 TOOL_UNAVAILABLE, RATE_LIMIT_EXCEEDED error_message TEXT, -- 错误详情脱敏后 created_at TIMESTAMPTZ DEFAULT NOW(), -- 关键关联状态版本形成执行链 state_version_before BIGINT, state_version_after BIGINT, -- 索引支持关键查询 INDEX idx_action_log_agent_time (agent_id, created_at), INDEX idx_action_log_status (status), INDEX idx_action_log_input_hash (input_hash) );这个设计带来的实际价值故障恢复当 Agent 因timeout中断运维人员执行SELECT * FROM agent_action_log WHERE agent_id x AND status timeout ORDER BY created_at DESC LIMIT 1立即获得action_id和input_hash可手动重放该动作。性能分析SELECT action_id, AVG(duration_ms), COUNT(*) FROM agent_action_log WHERE created_at NOW() - INTERVAL 1 day GROUP BY action_id ORDER BY AVG(duration_ms) DESC5 秒定位慢动作。安全审计SELECT agent_id, action_id, input_hash, error_code FROM agent_action_log WHERE error_code AUTH_FAILED AND created_at NOW() - INTERVAL 1 hour实时监控未授权访问。提示不要在output_summary存完整输出。我们规定最大 2KB超长内容存入单独的agent_action_output表按action_log_id外键关联并启用 PostgreSQL 的TOAST自动压缩。这避免了主表膨胀导致查询变慢。3.3 工具注册表运行时动态能力的基石agent-native 的“智能”很大程度源于 Agent 能根据上下文动态选择工具。这要求工具元数据本身可查询、可版本化、可权限控制CREATE TABLE tool_registry ( id SERIAL PRIMARY KEY, tool_id VARCHAR(128) UNIQUE NOT NULL, -- 工具唯一标识符 name VARCHAR(100) NOT NULL, -- 显示名称 description TEXT, schema_json JSONB NOT NULL, -- OpenAPI 3.0 Schema描述输入/输出/认证 endpoint_url TEXT NOT NULL, -- 调用地址 auth_config JSONB, -- 认证配置如 { type: api_key, header: X-API-Key } is_active BOOLEAN DEFAULT TRUE, -- 是否启用 created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), -- 权限控制字段 allowed_roles TEXT[] DEFAULT {}, -- 允许调用的角色列表如 {admin,analyst} -- 索引加速发现 INDEX idx_tool_registry_active (is_active), INDEX idx_tool_registry_roles (allowed_roles) );实际使用中Agent 运行时会执行类似这样的查询来发现可用工具SELECT tool_id, schema_json, endpoint_url FROM tool_registry WHERE is_active true AND $1 ANY(allowed_roles) -- $1 是当前 Agent 的角色 AND schema_json {required: [query]}::jsonb; -- 按输入需求过滤这使得工具管理完全脱离代码发布周期。运营人员修改tool_registry表即可上线新工具、禁用旧工具、调整权限无需重启任何服务。我们有个客户用此机制在黑色星期五流量高峰前 2 小时动态启用了专为高并发优化的search_cached工具替代search_web将平均响应时间从 1.8s 降至 320ms。4. TypeScript用类型系统为 agent-native 架构筑起第一道防线在 agent-native 架构中TypeScript 的价值远超“避免运行时类型错误”。它是将架构契约Architecture Contract编译进代码的静态验证层。当AgentTState, TActions类型定义完成时整个系统的交互规则、状态迁移路径、工具调用约束就已经被 TypeScript 编译器强制校验。这比任何文档、测试或代码审查都更早、更彻底地拦截设计缺陷。4.1 Agent 核心类型状态与动作的契约绑定一个典型的 agent-native Agent 类型定义如下// 定义 Agent 的状态类型 type ChatState { messages: Array{ role: user | assistant | system; content: string }; currentStep: init | searching | writing | finalizing; searchResults?: Array{ title: string; url: string; snippet: string }; draftContent?: string; }; // 定义可用工具及其输入/输出类型 type ToolDefinitions { search_web: { input: { query: string; max_results?: number }; output: Array{ title: string; url: string; snippet: string }; }; write_to_postgres: { input: { table: string; data: Recordstring, any }; output: { inserted_count: number; row_ids: string[] }; }; send_email: { input: { to: string; subject: string; body: string }; output: { message_id: string; sent_at: Date }; }; }; // Agent 主类型泛型绑定状态与工具集 class AgentTState, TTools extends Recordstring, { input: any; output: any } { private state: TState; private tools: TTools; constructor(initialState: TState, tools: TTools) { this.state initialState; this.tools tools; } // perform 方法的类型推导输入类型由 tool_id 精确决定 async performK extends keyof TTools( toolId: K, input: TTools[K][input] ): PromiseTTools[K][output] { // 实际执行逻辑调用 PostgreSQL 工具注册表、记录日志等 const result await this.executeTool(toolId, input); // 更新状态类型安全 this.state this.updateState(this.state, toolId, input, result); // 持久化状态类型安全 await this.persistState(); return result; } // 状态更新函数必须返回 TState 类型强制开发者思考状态迁移 private updateState( currentState: TState, toolId: keyof TTools, input: any, output: any ): TState { // 此处实现状态迁移逻辑 // TypeScript 编译器会检查返回值是否严格匹配 TState switch (toolId) { case search_web: return { ...currentState, currentStep: writing, searchResults: output as ToolDefinitions[search_web][output], } as TState; case write_to_postgres: return { ...currentState, currentStep: finalizing, } as TState; default: return currentState; } } }这个设计的关键威力在于编译期强制如果你在updateState中返回了一个缺少searchResults字段的对象TypeScript 直接报错Type { currentStep: string; } is not assignable to type ChatState。如果你调用agent.perform(send_email, { to: ab.com })却漏了subject编译器提示Property subject is missing in type { to: string; } but required in type { to: string; subject: string; body: string; }。如果你新增一个工具generate_image但忘记在ToolDefinitions中声明其input类型后续所有perform(generate_image, ...)调用都会编译失败。这相当于把状态机图State Machine Diagram直接编码进类型系统。我们团队曾用此方法在设计一个金融风控 Agent 时提前发现了一个逻辑漏洞状态pending_review下允许调用approve_loan但approve_loan的输出类型要求credit_score字段而pending_review状态的state类型中并未包含该字段。这个错误在编码阶段就被捕获避免了上线后因状态不一致导致的资损。4.2 工具契约用 JSON Schema 生成 TypeScript 类型工具的输入/输出契约通常由 OpenAPI 3.0 JSON Schema 定义存于 PostgreSQLtool_registry.schema_json字段。手动维护 TypeScript 类型易出错且难以同步。我们采用自动化方案从tool_registry表读取schema_json使用openapi-typegen/core工具将其转换为 TypeScript 接口生成文件src/generated/tool-types.ts内容类似// 自动生成勿手动修改 export interface SearchWebInput { query: string; max_results?: number; } export interface SearchWebOutput { items: Array{ title: string; url: string; snippet: string; }; }然后在ToolDefinitions中引用type ToolDefinitions { search_web: { input: SearchWebInput; output: SearchWebOutput; }; // ... 其他工具 };这套流程确保了工具契约的单一真相源Single Source of Truth前端调用、后端执行、数据库 Schema、TypeScript 类型全部源自同一份 JSON Schema。当产品要求search_web新增region参数时只需更新数据库中的schema_json重新运行类型生成脚本所有相关代码包括类型检查、参数校验、文档自动同步。我们测算过相比手动维护此流程将工具迭代的平均交付时间从 4.2 小时缩短至 18 分钟。4.3 运行时类型守卫防御性编程的最后一道闸门TypeScript 类型在编译后消失而 agent-native 的分布式特性意味着状态可能被其他服务篡改。因此运行时类型守卫Runtime Type Guard是必需的。我们不使用第三方库而是基于 PostgreSQL 的 JSONB 能力构建轻量方案// 从数据库读取状态后进行运行时校验 async function loadAndValidateStateT(agentId: string): PromiseT { const row await db.query( SELECT state_data FROM agent_state WHERE agent_id $1 ORDER BY version DESC LIMIT 1, [agentId] ); if (!row.length) throw new Error(No state found for agent ${agentId}); const rawState row[0].state_data; // 使用 Zod 进行运行时校验Zod Schema 与 TypeScript 类型严格对应 const validated ChatStateSchema.safeParse(rawState); if (!validated.success) { // 记录严重错误状态损坏 await logCorruptedState(agentId, rawState, validated.error); throw new Error(Invalid state for agent ${agentId}: ${validated.error}); } return validated.data; } // Zod Schema 与 ChatState 类型 1:1 对应 const ChatStateSchema z.object({ messages: z.array(z.object({ role: z.enum([user, assistant, system]), content: z.string() })), currentStep: z.enum([init, searching, writing, finalizing]), searchResults: z.array(z.object({ title: z.string(), url: z.string().url(), snippet: z.string() })).optional(), draftContent: z.string().optional() });这个守卫的意义在于当 PostgreSQL 中的状态因人为误操作如UPDATE agent_state SET state_data {broken: true}或上游 Bug 被污染时Agent 启动时会立即失败并告警而不是带着损坏状态继续执行导致不可预测的后果。我们在生产环境部署此守卫后状态相关故障的 MTTR平均修复时间从 37 分钟降至 2.3 分钟——因为错误在 Agent 初始化阶段就被拦截而非在perform(write_to_postgres)时才暴露。5. 从零搭建一个最小可行 agent-native 运行时实操步骤与避坑指南理论终需落地。下面我将带你用不到 300 行代码搭建一个真正可用的 agent-native 运行时核心——它能创建 Agent、执行工具、持久化状态、记录日志并具备生产级的错误处理与可观测性。所有代码均基于 Node.js 18、TypeScript 5.3、PostgreSQL 15无外部框架依赖专注展示 agent-native 的本质。5.1 环境准备精简但完备的依赖# 初始化项目 mkdir agent-native-core cd agent-native-core npm init -y npm install pg zod dotenv npm install -D typescript ts-node types/node types/pgpackage.json关键配置{ scripts: { dev: ts-node src/index.ts, build: tsc }, dependencies: { pg: ^8.11.3, zod: ^3.22.4, dotenv: ^16.4.5 } }注意不要安装 express、fastify 或任何 Web 框架。agent-native 的入口是Agent.run()HTTP 只是可选的触发器。我们将在最后一步添加一个极简的 Express 适配器但核心运行时完全独立。5.2 数据库初始化执行一次终身受益创建db/init.sql包含之前设计的三张核心表-- 创建扩展如 pgvector若需向量搜索 CREATE EXTENSION IF NOT EXISTS pgcrypto; -- agent_state 表 CREATE TABLE IF NOT EXISTS agent_state ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), agent_id VARCHAR(64) NOT NULL, version BIGINT NOT NULL DEFAULT 0, state_data JSONB NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE (agent_id, version) ); CREATE INDEX IF NOT EXISTS idx_agent_state_agent_id ON agent_state (agent_id); CREATE INDEX IF NOT EXISTS idx_agent_state_updated_at ON agent_state (updated_at); -- agent_action_log 表 CREATE TABLE IF NOT EXISTS agent_action_log ( id SERIAL PRIMARY KEY, agent_id VARCHAR(64) NOT NULL, action_id VARCHAR(128) NOT NULL, input_hash CHAR(64) NOT NULL, output_summary TEXT, duration_ms INTEGER NOT NULL, status VARCHAR(20) NOT NULL CHECK (status IN (success, failed, timeout, cancelled)), error_code VARCHAR(50), error_message TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), state_version_before BIGINT, state_version_after BIGINT ); CREATE INDEX IF NOT EXISTS idx_action_log_agent_time ON agent_action_log (agent_id, created_at); CREATE INDEX IF NOT EXISTS idx_action_log_status ON agent_action_log (status); -- tool_registry 表 CREATE TABLE IF NOT EXISTS tool_registry ( id SERIAL PRIMARY KEY, tool_id VARCHAR(128) UNIQUE NOT NULL, name VARCHAR(100) NOT NULL, description TEXT, schema_json JSONB NOT NULL, endpoint_url TEXT NOT NULL, auth_config JSONB, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), allowed_roles TEXT[] DEFAULT {} ); CREATE INDEX IF NOT EXISTS idx_tool_registry_active ON tool_registry (is_active);执行初始化psql -U your_user -d your_db -f db/init.sql5.3 核心运行时类Agent 类的骨架与血肉创建src/agent-runtime.tsimport { Pool, QueryConfig } from pg; import { z, ZodType } from zod; import { createHash } from crypto; // 通用工具类型定义 export type ToolInput Recordstring, any; export type ToolOutput Recordstring, any; // Agent 状态更新函数类型 export type StateUpdaterTState, TInput, TOutput ( currentState: TState, input: TInput, output: TOutput ) TState; // Agent 核心类 export class AgentRuntimeTState, TTools extends Recordstring, { input: any; output: any } { private pool: Pool; private stateSchema: ZodTypeTState; constructor( connectionString: string, stateSchema: ZodTypeTState ) { this.pool new Pool({ connectionString }); this.stateSchema stateSchema; } // 创建新 Agent 实例 async createAgent( agentId: string, initialState: TState, tools: TTools ): Promisevoid { // 验证初始状态 const validated this.stateSchema.safeParse(initialState); if (!validated.success) { throw new Error(Invalid initial state: ${validated.error}); } // 插入初始状态 await this.pool.query( INSERT INTO agent_state (agent_id, version, state_data) VALUES ($1, $2, $3), [agentId, 0, JSON.stringify(validated.data)] ); } // 执行工具动作 async performK extends keyof TTools( agentId: string, toolId: K, input: TTools[K][input], stateUpdater: StateUpdaterTState, TTools[K][input], TTools[K][output] ): PromiseTTools[K][output] { // 1. 获取当前状态带版本 const stateQuery await this.pool.query( SELECT id, version, state_data FROM agent_state WHERE agent_id $1 ORDER BY version DESC LIMIT 1, [agentId] ); if (stateQuery.rowCount 0) { throw new Error(Agent ${agentId} not found); } const currentState this.stateSchema.parse(JSON.parse(stateQuery.rows[0].state_data)); const currentVersion stateQuery.rows[0].version; // 2. 计算输入哈希用于幂等和日志 const inputHash createHash(sha256).update(JSON.stringify(input)).digest(hex); // 3. 开始事务 const client await this.pool.connect(); try { await client.query(BEGIN); // 4. 执行工具此处为模拟实际应调用 HTTP 或 gRPC const startTime Date.now(); let output: TTools[K][output]; let status: success | failed | timeout success; let errorCode: string | undefined; let errorMessage: string | undefined; try { // 模拟工具执行替换为真实调用 await new Promise(resolve setTimeout(resolve, 100)); output { /* 模拟输出 */ } as unknown as TTools[K][output]; } catch (error) { status failed; errorCode error instanceof Error ? INTERNAL_ERROR : UNKNOWN_ERROR; errorMessage error instanceof Error ? error.message : String(error); throw error; } finally { const durationMs Date.now() - startTime; // 5. 更新状态乐观锁 const newVersion currentVersion 1; const newState stateUpdater(currentState, input, output); const validatedNewState this.stateSchema.parse(newState); const updateResult await client.query( UPDATE agent_state SET version $1, state_data $2, updated_at NOW() WHERE agent_id $3 AND version $4, [newVersion, JSON.stringify(validatedNewState), agentId, currentVersion] ); if (updateResult.rowCount 0) { throw new Error(State update failed: version mismatch for agent ${agentId}); } // 6. 记录动作日志 await client.query( INSERT INTO agent_action_log (agent_id, action_id, input_hash, output_summary, duration_ms, status, error_code, error_message, state_version_before, state_version_after) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10), [ agentId, toolId as string, inputHash, JSON.stringify(output).substring(0, 2000), // 截断摘要 durationMs, status, errorCode, errorMessage, currentVersion, newVersion ] ); } await client.query(COMMIT); return output; } catch (error) { await client.query(ROLLBACK); throw error; } finally { client.release(); } } // 获取 Agent 当前状态供外部查询 async getState(agentId: string): PromiseTState { const result await this.pool.query( SELECT state_data FROM agent_state WHERE agent_id $1 ORDER BY version DESC LIMIT 1, [agentId] ); if (result.rowCount 0) throw new Error(Agent ${agentId} not found); return this.stateSchema.parse(JSON.parse(result.rows[0].state_data)); } }5.4 实战定义一个客服对话 Agent 并运行创建src/example-chat-agent.tsimport { AgentRuntime } from ./agent-runtime; import { z } from zod; // 1. 定义状态 SchemaZod const ChatStateSchema z.object({ messages: z.array(z.object({ role: z.enum([user, assistant, system]), content: z.string() })).default([]), currentStep: z.enum([greeting, troubleshooting, resolution, closing]).default(greeting), issueCategory: z.string().optional(), resolutionSteps: z.array(z.string()).default([]) }); type ChatState z.infertypeof ChatStateSchema; // 2. 定义工具契约 type ChatTools { fetch_knowledge_base: { input: { topic: string }; output:
上一篇/下一篇内容由系统自动关联 返回资讯列表 →