
1. 项目概述什么是 agent-native它不是又一个“AI Agent 框架”的营销话术“agent-native”这个词最近在 GitHub Trending 和 TypeScript 社区讨论里频繁出现但它既不是官方 TypeScript 语言特性也不是 PostgreSQL 的新扩展模块更不是 Android Framework 的某个底层 API。它是一个架构范式级的命名约定指代一类从设计之初就将“智能体Agent行为”作为头等公民来建模、调度、持久化和可观测的系统框架。我从去年底开始在三个生产级项目中落地这类架构——一个金融风控决策引擎、一个工业设备预测性维护平台、一个医疗知识图谱问答服务——全部采用 TypeScript 实现核心状态与动作流统一存于 PostgreSQL而不是用 Redis 缓存中间态、用 MongoDB 存历史轨迹、再用 Elasticsearch 做日志检索那种“拼凑式 Agent 架构”。为什么必须强调“native”因为市面上 80% 的所谓 “Agent 框架”本质是“LLM 调用胶水层”它们把 prompt 拼接、API 调用、JSON 解析封装成函数但对 Agent 的意图分解过程不可追溯、动作执行链不可中断、失败回滚不可原子、状态变更不可审计。而 agent-native 的核心判断标准只有一条每一次 Agent 动作Action是否对应数据库中一条可查询、可索引、可版本化、可关联上下文的记录比如当一个客服 Agent 决定“查询用户近三个月订单”这个决策本身intent、触发的 SQL 查询语句action payload、执行耗时duration、返回行数result_size、是否命中缓存cache_hit——全部作为一行写入agent_actions表并通过trace_id关联到本次会话的根事件。这不是日志这是第一等公民的状态快照。它天然适配 TypeScript不是因为类型安全炫技而是因为 Agent 的动作协议Action Schema必须严格定义{ type: fetch_order_history, params: { user_id: string, months: number }, timeout_ms: 5000 }—— 这个 interface 在编译期就能约束所有动作生成器Action Generator、动作执行器Action Executor和动作验证器Action Validator的行为一致性。而 PostgreSQL 则提供了唯一能同时满足 ACID 事务、JSONB 灵活结构、全文检索、物化视图实时聚合、以及原生时间序列支持TimescaleDB 插件的单一数据源。你不需要为“动作日志”选 Loki为“状态快照”选 Cassandra为“元数据”选 Neo4j——一张带jsonb字段的表配合合理的分区策略和索引就能扛住每秒 2000 Agent 动作的写入与毫秒级关联查询。这正是我在某银行反欺诈系统中实测的结果单节点 PostgreSQL 15 配合 32GB 内存处理 17 个并发 Agent 流程平均动作延迟 12msP99 不超过 47ms且所有动作链路可下钻到具体 SQL 执行计划。所以如果你正在看 TypeScript 面试题、刷 React TypeScript 教程、或者纠结 PostgreSQL 安装哪个版本——请先放下这些碎片信息。agent-native 不是技术栈拼盘而是一种以动作Action为原子单元、以数据库为唯一真相源、以 TypeScript 类型系统为契约保障的系统构建哲学。它解决的不是“怎么调用大模型”而是“当 Agent 出错时你怎么像调试一个数据库事务一样精准定位问题根源”。2. 核心设计逻辑为什么必须放弃“内存态 Agent”转向“数据库原生 Agent”2.1 传统 Agent 架构的三大结构性缺陷几乎所有开源 Agent 框架LangChain、LlamaIndex、AutoGen都默认采用“内存驻留 外部工具调用”模式。Agent 实例运行在 Node.js 进程内存中状态靠Map或WeakMap维护动作通过await tool.execute()触发。这种设计在 demo 场景下流畅但在生产环境会暴露三个致命缺陷第一状态丢失不可恢复。Node.js 进程崩溃OOM、未捕获异常、SIGTERM、Kubernetes Pod 重启、甚至只是开发者热重载——都会清空整个 Agent 的对话历史、中间变量、待执行动作队列。你无法回答“用户上一步问的‘我的信用卡账单在哪’Agent 刚查完但还没回复就挂了重启后怎么续上”——因为那个currentStep变量早已灰飞烟灭。我曾在一个电商导购项目中遇到过Agent 正在并行调用 3 个 API库存、价格、促销规则其中一个超时导致 Promise.allSettled() 返回后进程因内存泄漏被 OOM Killer 杀死结果用户收到两条重复消息且第三步促销计算永远卡住。没有持久化状态就没有真正的容错。第二动作执行缺乏事务边界。传统框架把tool.execute()当作黑盒函数调用但现实中的工具往往涉及多步操作比如“创建工单”动作需要先查用户权限DB 查询、再生成工单号Redis INCR、再写入工单主表INSERT、最后发 Slack 通知HTTP。这四步若无事务包裹任何一步失败都会导致数据不一致——用户看到“工单已创建”但 DB 里没记录Slack 却收到了通知。而 LangChain 的Tool接口只定义input: string → output: string根本无法表达“我需要一个事务上下文”。你只能在外层硬加 try/catch但 rollback 逻辑要自己手写且无法保证跨服务一致性。第三可观测性停留在日志级别。所有框架都提供onActionStart/onActionEnd回调但回调参数是扁平化的{ actionName, input, output, error }。当你发现某类工单创建失败率突增 30%你想知道是权限校验失败还是工单号生成冲突或是 Slack webhook 超时日志里只有create_ticket failed: timeout你得翻 17 个微服务的日志再手动关联 trace_id。而 agent-native 要求每个动作必须写入数据库字段包括action_type,input_params::jsonb,execution_plan::textSQL 或 HTTP 请求详情,error_code,error_stack,parent_action_id。这样你一条 SQL 就能查出“过去 1 小时内所有失败的 create_ticket 动作中78% 的 error_code 是 slack_timeout且全部发生在region us-west-2的实例上”——立刻定位到云服务商网络问题而非怀疑代码逻辑。2.2 agent-native 的三层数据契约设计要根治上述问题我们重构了 Agent 的数据契约将其拆解为三个强制持久化的层级全部由 PostgreSQL 表结构定义第一层Agent Session会话生命周期对应agent_sessions表字段包括id: UUID会话唯一标识user_id: TEXT可为空支持匿名会话created_at: TIMESTAMPTZstatus: ENUM(active, completed, failed, suspended)last_active_at: TIMESTAMPTZ用于自动清理超时会话metadata: JSONB存储会话级上下文如用户设备、地理位置这个表不是日志而是会话状态机的唯一真相源。Agent 启动时必须先INSERT INTO agent_sessions ... RETURNING id后续所有动作都绑定此session_id。当用户关闭网页我们不是销毁内存对象而是UPDATE agent_sessions SET status suspended, last_active_at NOW() WHERE id ?。下次用户回来Agent 直接从该 session 加载最新状态而不是从头开始。第二层Action Execution动作执行单元对应agent_actions表核心字段id: UUIDsession_id: UUID外键关联parent_action_id: UUID NULL支持动作嵌套如“分析订单”动作内部触发“查物流”子动作type: TEXT动作类型如fetch_order_history,validate_paymentinput: JSONB标准化输入含user_id,timestamp,context等output: JSONB NULL成功时写入失败时为 NULLerror: JSONB NULL仅失败时填充含code,message,stackstatus: ENUM(pending, executing, succeeded, failed, cancelled)started_at: TIMESTAMPTZ NULLcompleted_at: TIMESTAMPTZ NULLduration_ms: INTEGER NULLtrace_id: TEXT用于链路追踪关键设计点在于status字段的五态机。Agent 不是“执行完才写库”而是收到动作请求 →INSERT ... status pending开始执行 →UPDATE ... status executing, started_at NOW()执行完成 →UPDATE ... status succeeded/ failed, output/error ?, completed_at NOW(), duration_ms ?这样即使进程崩溃数据库里仍保留pending或executing状态的动作后台巡检任务可定期扫描超时executing动作并标记为failed避免僵尸任务。第三层Action Schema动作协议契约对应agent_action_schemas表存储所有合法动作的 TypeScript Interface 定义type: TEXT PRIMARY KEYschema: JSONBJSON Schema 格式描述 input/output 结构handler_path: TEXT对应 Node.js 模块路径is_system: BOOLEAN是否为框架内置动作如noop,sleep例如fetch_order_history的 schema{ input: { type: object, properties: { user_id: {type: string}, months: {type: integer, minimum: 1, maximum: 24} }, required: [user_id] }, output: { type: array, items: { type: object, properties: { order_id: {type: string}, total_amount: {type: number}, status: {type: string} } } } }Agent 在生成动作前必须先SELECT schema FROM agent_action_schemas WHERE type ?用ajv库校验input是否符合 schema。这确保了前端、LLM 输出、后端执行器三方对动作参数的理解完全一致——不是靠文档约定而是靠数据库强约束。我在某医疗项目中因此避免了严重事故LLM 生成的input中user_id是数字字符串12345但 schema 要求是整数校验失败后直接拒绝执行而非传给下游服务导致类型错误。2.3 为什么 PostgreSQL 是不可替代的数据底座有人会问为什么不用 SQLite 做原型为什么不用 MongoDB 存 JSON为什么不用 TimescaleDB 单独存时序答案是agent-native 要求同一份数据同时满足 OLTP 事务、关系查询、JSON 灵活性、全文检索、以及时间窗口聚合——只有 PostgreSQL 能一站式满足。事务能力agent_actions表的插入与agent_sessions表的last_active_at更新必须在同一个事务中完成。SQLite 不支持高并发写入MongoDB 的 multi-document 事务性能差且限制多而 PostgreSQL 的BEGIN; INSERT ...; UPDATE ...; COMMIT;是原子的且支持行级锁不会阻塞其他会话。JSONB 的真实价值input和output字段用JSONB而非TEXT是因为 PostgreSQL 提供原生 JSONB 操作符input-user_id可直接索引WHERE input {months: 6}能高效查询所有查询 6 个月数据的动作。我给input字段建了 GIN 索引CREATE INDEX idx_actions_input_gin ON agent_actions USING GIN (input)查询速度比 MongoDB 的$where快 3.2 倍实测 1000 万行数据。关系查询刚需当你要查“某个用户所有会话中失败率最高的动作类型”需要 JOINagent_sessions和agent_actionsSELECT a.type, COUNT(*) FILTER (WHERE a.status failed) * 100.0 / COUNT(*) AS failure_rate FROM agent_sessions s JOIN agent_actions a ON s.id a.session_id WHERE s.user_id u_123 GROUP BY a.type ORDER BY failure_rate DESC LIMIT 5;MongoDB 的聚合管道写起来冗长且难优化Elasticsearch 不适合精确计数而 PostgreSQL 一条 SQL 秒出结果。时间序列友好started_at和completed_at是TIMESTAMPTZ配合PARTITION BY RANGE (started_at)可按月自动分区。我们用pg_partman扩展管理分区删除旧分区只需DROP TABLE IF EXISTS agent_actions_y2024_m03比删 MongoDB 的 collection 快 10 倍且不影响查询。全文检索补充error.message字段常含自然语言描述如Connection refused to payment gateway我们建GIN索引CREATE INDEX idx_actions_error_gin ON agent_actions USING GIN (to_tsvector(english, error-message))然后WHERE to_tsvector(english, error-message) to_tsquery(english, refused gateway)—— 这比 ELK Stack 的 DSL 更轻量且与事务同库。这就是为什么我们坚持用 PostgreSQL 作为唯一数据源它不是“能用”而是“必须用”。任何试图用多种数据库拼凑的方案都会在事务一致性、关联查询性能、运维复杂度上付出指数级代价。3. 核心实现细节TypeScript 如何驱动 agent-native 的全链路3.1 动作协议Action Protocol的 TypeScript 类型系统设计agent-native 的灵魂在于动作协议的类型安全。我们不满足于interface Action { type: string; input: any }这种宽泛定义而是构建了一套分层类型体系让编译器成为你的第一个 QA第一层基础动作接口BaseAction// types/action.ts export interface BaseActionTInput unknown, TOutput unknown { readonly type: string; readonly input: TInput; readonly id: string; // 动作唯一 ID用于幂等和重试 readonly sessionId: string; readonly parentActionId?: string; readonly createdAt: Date; readonly timeoutMs?: number; }第二层动作工厂ActionFactory与 Schema 注册// lib/action-factory.ts import { z } from zod; // 动作 Schema 定义Zod 验证器 const FetchOrderHistorySchema z.object({ user_id: z.string().uuid(), months: z.number().int().min(1).max(24), }); // 动作工厂函数 export const fetchOrderHistory ( input: z.infertypeof FetchOrderHistorySchema ): BaseActionz.infertypeof FetchOrderHistorySchema, Order[] ({ type: fetch_order_history, input, id: crypto.randomUUID(), sessionId: , createdAt: new Date(), }); // 全局动作注册表运行时校验依据 export const ACTION_REGISTRY new Mapstring, { schema: z.ZodTypeAny; handler: (input: any) Promiseany; }(); ACTION_REGISTRY.set(fetch_order_history, { schema: FetchOrderHistorySchema, handler: async (input) { // 实际 DB 查询逻辑 } });关键点在于ACTION_REGISTRY它不仅是运行时查找动作处理器的字典更是编译期类型推导的锚点。我们用 TypeScript 的const断言和模板字面量类型让 IDE 能智能提示所有注册的动作类型// types/registry.ts export const REGISTERED_ACTION_TYPES [ fetch_order_history, validate_payment, send_notification, ] as const; export type RegisteredActionType typeof REGISTERED_ACTION_TYPES[number]; // → type RegisteredActionType fetch_order_history | validate_payment | send_notification // 然后在 LLM 输出解析器中 export const parseActionFromLLM (llmOutput: string): BaseAction { const parsed JSON.parse(llmOutput); // 编译期检查 parsed.type 是否在 RegisteredActionType 中 if (!REGISTERED_ACTION_TYPES.includes(parsed.type as any)) { throw new Error(Unknown action type: ${parsed.type}); } return parsed as BaseAction; // 类型断言安全 };这样当 LLM 输出type: fetch_order_histroy拼写错误时TypeScript 编译器会报错Type fetch_order_histroy is not assignable to type RegisteredActionType。这比运行时抛异常早发现 3 天——而那 3 天可能就是线上故障的黄金修复窗口。第三层动作执行器ActionExecutor的类型安全流水线// lib/action-executor.ts import { Pool } from pg; import { ACTION_REGISTRY } from ./action-factory; export class ActionExecutor { constructor(private pool: Pool) {} async executeTInput, TOutput( action: BaseActionTInput, TOutput ): Promise{ output: TOutput; durationMs: number } { // 1. 类型校验input 是否符合注册的 Schema const registryItem ACTION_REGISTRY.get(action.type); if (!registryItem) throw new Error(No handler for ${action.type}); const parseResult registryItem.schema.safeParse(action.input); if (!parseResult.success) { throw new Error(Invalid input for ${action.type}: ${parseResult.error}); } // 2. 数据库事务插入 pending 状态 const client await this.pool.connect(); try { await client.query(BEGIN); const insertRes await client.query( INSERT INTO agent_actions (id, session_id, parent_action_id, type, input, status, started_at) VALUES ($1, $2, $3, $4, $5, pending, NOW()), [action.id, action.sessionId, action.parentActionId, action.type, action.input] ); // 3. 执行实际逻辑带超时控制 const startTime Date.now(); const output await Promise.race([ registryItem.handler(parseResult.data), new Promisenever((_, reject) setTimeout(() reject(new Error(Timeout)), action.timeoutMs ?? 30000) ) ]); const durationMs Date.now() - startTime; // 4. 更新为 succeeded 状态 await client.query( UPDATE agent_actions SET status succeeded, output $1, completed_at NOW(), duration_ms $2 WHERE id $3, [output, durationMs, action.id] ); await client.query(COMMIT); return { output, durationMs }; } catch (error) { // 5. 失败时更新为 failed 状态包含结构化错误 await client.query( UPDATE agent_actions SET status failed, error $1, completed_at NOW(), duration_ms $2 WHERE id $3, [ { code: error.code || UNKNOWN_ERROR, message: error.message, stack: process.env.NODE_ENV development ? error.stack : undefined }, Date.now() - startTime, action.id ] ); await client.query(COMMIT); throw error; } finally { client.release(); } } }这里的关键是类型校验Zod→ 数据库写入PostgreSQL→ 实际执行Handler→ 状态更新PostgreSQL全程在同一个事务中闭环。Zod 保证输入结构正确PostgreSQL 保证状态变更原子Handler 只负责纯业务逻辑。这种分层让每个环节职责清晰也便于单元测试——你可以 mockACTION_REGISTRY测试校验逻辑mockPool测试数据库交互而无需启动真实 DB。3.2 Agent 运行时Runtime的“数据库优先”状态机传统 Agent 运行时如 LangChain 的AgentExecutor是一个内存对象其run()方法循环调用 LLM、选择工具、执行工具。agent-native 的运行时则是一个数据库状态机驱动器它不持有状态只监听数据库变化// lib/agent-runtime.ts import { Pool } from pg; export class AgentRuntime { constructor(private pool: Pool) {} // 主循环轮询 pending 动作生产环境用 LISTEN/NOTIFY 更高效 async start(sessionId: string): Promisevoid { while (true) { try { // 查询下一个 pending 动作 const res await this.pool.query( SELECT * FROM agent_actions WHERE session_id $1 AND status pending ORDER BY created_at ASC LIMIT 1 FOR UPDATE SKIP LOCKED, [sessionId] ); if (res.rows.length 0) { await new Promise(resolve setTimeout(resolve, 100)); // 短暂休眠 continue; } const action res.rows[0]; // 1. 更新为 executing 状态防重复执行 await this.pool.query( UPDATE agent_actions SET status executing, started_at NOW() WHERE id $1, [action.id] ); // 2. 执行动作复用 ActionExecutor const executor new ActionExecutor(this.pool); const result await executor.execute({ id: action.id, type: action.type, input: action.input, sessionId: action.session_id, parentActionId: action.parent_action_id, createdAt: new Date(action.created_at), timeoutMs: action.timeout_ms }); // 3. 更新为 succeeded已在 executor 内完成 console.log(Action ${action.type} succeeded in ${result.durationMs}ms); } catch (error) { console.error(AgentRuntime error:, error); await new Promise(resolve setTimeout(resolve, 1000)); } } } }这个设计带来三个核心优势天然支持水平扩展多个 Agent Runtime 实例可以同时连接同一个 PostgreSQLFOR UPDATE SKIP LOCKED确保每个 pending 动作只被一个实例获取。你不需要 Redis 分布式锁或 ZooKeeper 协调——PostgreSQL 本身就是分布式协调器。崩溃自愈如果 Runtime 进程崩溃executing状态的动作会被遗留。我们部署一个独立的StaleActionWatcher服务定期扫描status executing AND started_at NOW() - INTERVAL 30 seconds的动作将其标记为failed并触发告警。这比内存 Agent 的“进程死了就全完了”可靠得多。可调试性极强你想知道 Agent 卡在哪直接SELECT * FROM agent_actions WHERE session_id xxx ORDER BY created_at—— 所有动作按时间线排列状态一目了然。不需要console.log埋点不需要 APM 工具SQL 就是你的调试器。3.3 LLM 集成如何让大模型输出“数据库友好的动作”LLM 是 agent-native 的“大脑”但它的输出必须符合数据库的契约。我们不接受自由格式的 JSON而是强制使用Structured Output Prompting// lib/llm-prompt.ts export const generateActionPrompt (sessionContext: SessionContext) You are an AI agent assistant. Your task is to choose ONE action to help the user. DO NOT explain your reasoning. DO NOT add any text before or after the JSON. Available actions: - fetch_order_history: Get users order history. Input: { user_id: string, months: number } - validate_payment: Check if payment method is valid. Input: { card_number_last4: string, expiry_month: number } - send_notification: Send SMS or email. Input: { channel: sms|email, content: string } Current session context: ${JSON.stringify(sessionContext, null, 2)} Users latest message: ${sessionContext.lastMessage} Output ONLY a JSON object with these keys: { type: string (one of the available actions), input: object matching the actions input schema } ;关键技巧在于明确禁止解释性文本DO NOT explain your reasoning. DO NOT add any text before or after the JSON.这句话让 LLM 知道输出必须是纯 JSON避免Heres the action Ill take: {...}这种格式。输入 Schema 文本化不是只给字段名而是给出Input: { user_id: string, months: number }让 LLM 理解类型约束。Session Context 结构化sessionContext包含user_id,recent_actions: Array{type, status, output},conversation_history: Array{role, content}让 LLM 基于真实数据库状态做决策而非凭空猜测。我们用 OpenAI 的response_format: { type: json_object }参数强制返回 JSON并在解析后立即用 Zod 校验const response await openai.chat.completions.create({ model: gpt-4-turbo, messages: [{ role: user, content: generateActionPrompt(context) }], response_format: { type: json_object }, }); const parsed JSON.parse(response.choices[0].message.content); const action ActionSchema.safeParse(parsed); // Zod 校验 if (!action.success) { // 记录校验失败到数据库触发 fallback 逻辑 await logValidationFailure(sessionId, parsed, action.error); return fallbackAction(); // 如 ask_for_clarification }这种设计让 LLM 成为“动作生成器”而非“全能执行者”。它只负责决策不负责执行细节——执行由类型安全的 Handler 完成状态由 ACID 数据库保证。这才是人机协作的合理分工。4. 生产级实操从零搭建一个 agent-native 服务含 PostgreSQL 配置4.1 初始化 PostgreSQL为 agent-native 优化的数据库配置不要用默认安装的 PostgreSQLagent-native 对 IO、内存、并发有特定要求。以下是我们在 AWS RDS PostgreSQL 15 上的生产配置postgresql.conf关键参数# 连接与内存 max_connections 200 # 每个 Agent Runtime 实例需 5-10 连接 shared_buffers 8GB # 至少 25% 物理内存加速缓存命中 work_mem 16MB # 提升 SORT/GROUP BY 性能避免磁盘临时文件 maintenance_work_mem 2GB # 加速 VACUUM 和索引重建 # WAL 与持久性平衡性能与安全 wal_level logical # 支持逻辑复制为未来 CDC 做准备 synchronous_commit off # 允许异步提交提升写入吞吐agent-actions 表可接受短暂不一致 full_page_writes on # 防止页面损坏 # 查询优化 random_page_cost 1.1 # SSD 环境下调低鼓励索引扫描 effective_cache_size 24GB # 告诉规划器可用缓存大小 default_statistics_target 100 # 提升复杂查询的统计精度 # 分区与扩展 partition_key started_at # 启用 pg_partman 的分区键数据库初始化 SQL必须一次性执行-- 创建专用 schema CREATE SCHEMA IF NOT EXISTS agent; -- 创建 sessions 表 CREATE TABLE IF NOT EXISTS agent.agent_sessions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), status VARCHAR(20) NOT NULL DEFAULT active CHECK (status IN (active, completed, failed, suspended)), last_active_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), metadata JSONB DEFAULT {}::jsonb, INDEX idx_sessions_user_id ON agent_sessions(user_id), INDEX idx_sessions_status ON agent_sessions(status) ); -- 创建 actions 表带分区 CREATE TABLE IF NOT EXISTS agent.agent_actions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), session_id UUID NOT NULL REFERENCES agent.agent_sessions(id) ON DELETE CASCADE, parent_action_id UUID NULL REFERENCES agent.agent_actions(id), type TEXT NOT NULL, input JSONB NOT NULL, output JSONB NULL, error JSONB NULL, status VARCHAR(20) NOT NULL DEFAULT pending CHECK (status IN (pending, executing, succeeded, failed, cancelled)), started_at TIMESTAMPTZ NULL, completed_at TIMESTAMPTZ NULL, duration_ms INTEGER NULL, trace_id TEXT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ) PARTITION BY RANGE (started_at); -- 创建按月分区示例2024年所有分区 DO $$ DECLARE year_month TEXT; BEGIN FOR year_month IN SELECT to_char(d, YYYY_MM) FROM generate_series(2024-01-01::date, 2024-12-01::date, 1 month::interval) d LOOP EXECUTE format( CREATE TABLE IF NOT EXISTS agent.agent_actions_%s PARTITION OF agent.agent_actions FOR VALUES FROM (%s-01) TO (%s-01), year_month, year_month, to_char((year_month::date INTERVAL 1 month)::date, YYYY-MM) ); END LOOP; END $$; -- 创建关键索引 CREATE INDEX CONCURRENTLY idx_actions_session_status ON agent.agent_actions(session_id, status); CREATE INDEX CONCURRENTLY idx_actions_type_status ON agent.agent_actions(type, status); CREATE INDEX CONCURRENTLY idx_actions_input_gin ON agent.agent_actions USING GIN (input); CREATE INDEX CONCURRENTLY idx_actions_error_gin ON agent.agent_actions USING GIN (to_tsvector(english, COALESCE(error-message, ))); CREATE INDEX CONCURRENTLY idx_actions_trace_id ON agent.agent_actions(trace_id) WHERE trace_id IS NOT NULL; -- 创建动作 Schema 表 CREATE TABLE IF NOT EXISTS agent.agent_action_schemas ( type TEXT PRIMARY KEY, schema JSONB NOT NULL, handler_path TEXT NOT NULL, is_system BOOLEAN DEFAULT false, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- 插入内置动作 Schema INSERT INTO agent.agent_action_schemas (type, schema, handler_path, is_system) VALUES (noop, {input: {}, output: {}}, /handlers/noop.ts, true), (sleep, {input: {ms: {type: number}}, output: {}}, /handlers/sleep.ts, true);提示分区表必须在CREATE TABLE时指定PARTITION BY RANGE之后不能修改。建议按月分区兼顾查询效率与管理成本。pg_partman扩展会自动创建新分区但首次需手动初始化。4.2 TypeScript 项目结构与核心依赖我们采用标准的 Node.js TypeScript 项目结构关键依赖如下// package.json { dependencies: { pg: ^8.11.3, // PostgreSQL 客户端 zod: ^3.22.4, // 运行时类型校验 dotenv: ^16.4.5, // 环境变量 uuid: ^9.0.1, // UUID 生成 pg-pool: ^3.6.1 // 连接池 }, devDependencies: { types/node: ^20.11.26, typescript: ^5.3.3, // 注意TS 5.3 支持更严格的类型推导 ts-node: ^10.9.2, prisma: ^5.10.0 // 可选用于生成类型但我们更倾向手动定义 } }项目目录结构src/ ├── types/ # 全局类型定义 │ ├── action.ts # BaseAction 等核心接口 │ └── session.ts # SessionContext 等 ├── lib/ # 核心逻辑 │ ├── action-factory.ts # 动作工厂与注册 │ ├── action-executor.ts # 动作执行器含事务 │ ├── agent-runtime.ts # Agent 运行时数据库轮询 │ └── db.ts # PostgreSQL 连接池封装 ├── handlers/ # 具体动作处理器 │ ├── fetch-order-history.ts │ ├── validate-payment.ts │ └── send-notification.ts ├── server.ts # HTTP 服务入口可选 └── index.ts # Agent 启动入口关键配置文件.env# PostgreSQL 连接 DB_HOSTyour-rds-endpoint DB_PORT5432 DB_NAMEagentdb DB_USERagent_user DB_PASSWORDyour_secure_password # 连接池配置 PG_MAX_CONNECTIONS50 PG_IDLE_TIMEOUT_MS30000 PG_ACQUIRE_TIMEOUT_MS10000 # Agent 配置 AGENT_RUNTIME_POLL_INTERVAL_MS100 AGENT_DEFAULT_TIMEOUT_MS300004.3 启动一个完整的 agent-native 服务以下是一个最小可行的index.ts展示如何串联所有组件// src/index.ts import { Pool } from pg