ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

前端工程师转型AI Agent:TypeScript工程化实战指南

前端工程师转型AI Agent:TypeScript工程化实战指南 1. 项目概述这不是一次“学点AI就转行”的速成班而是一场前端工程师的系统性能力迁移“前端转AI Agent”这个标题在2026年已经不再是个噱头而是大量一线开发者正在真实发生的职场演进路径。我带过37个从React/Vue团队出来的同学做AI工程化落地其中21人已稳定在AI Infra、Agent平台或智能应用产品线担任核心角色。他们不是去重学Python写模型而是把过去五年里打磨出的工程化直觉、状态管理思维、组件化抽象能力、以及对用户交互链路的深刻理解完整迁移到了AI Agent的构建范式中。TypeScript不是可选项它是整个迁移过程的“安全带”——Zod校验的是Agent输入输出的契约Node是本地推理服务与外部工具调用的调度中枢Next.js和Nuxt则分别承担了面向终端用户的智能界面如Copilot UI与面向企业客户的低代码Agent编排控制台。你不需要从零开始理解transformer的反向传播但必须清楚当一个Agent调用17个工具后返回结果它的错误边界在哪里Zod schema如何定义“工具调用失败但需重试”的结构Node进程如何优雅处理LLM流式响应中断Next.js的Server Actions怎样与RAG pipeline的chunking策略对齐这些才是真实项目里卡住90%转型者的硬骨头。本文不讲大模型原理只拆解一个前端工程师在2026年亲手交付一个可上线AI Agent产品的完整技术栈断层、补全逻辑与踩坑实录。2. 行业转型底层逻辑为什么前端是AI Agent工程化的天然适配者2.1 前端工程师的三大隐性资产在AI时代被重新定价很多前端同学误以为转AI要“放弃过去”实际恰恰相反——你最值钱的不是Vue语法而是那些在无数个需求评审、性能优化、跨端兼容中锤炼出的系统性工程思维。我们来拆解这三块被严重低估的资产第一是状态驱动架构的直觉。前端早已习惯用state effect描述世界用户点击触发actionreducer更新stateUI根据state派生。AI Agent的本质就是更复杂的state machine当前对话历史是state用户query是actionLLM生成的tool call是effect工具执行结果是新的state最终回复是派生视图。Zod schema在这里扮演的就是reducer的type guard——它强制规定每一次state transition的合法输入/输出结构。我见过太多Python背景的AI工程师写出if tool_calls in response这种脆弱判断而一个用Zod定义AgentState的前端会直接写const parsed AgentState.safeParse(response)类型安全从第一行代码就开始。第二是组件化抽象能力。传统前端把UI拆成Button、Modal、TableAI Agent工程则把能力拆成SearchTool、DatabaseQueryTool、CodeExecutorTool。每个Tool就是一个独立组件有明确props输入schema、children执行逻辑、emits回调事件。Next.js的Server Components天然支持这种模式——你可以把SearchTool query{q} onResult{handleResult} /作为Agent workflow中的一个节点其内部完全封装了向Elasticsearch发送请求、处理分页、格式化高亮的全部细节。这种“能力即组件”的思维让前端工程师比纯算法背景的人更快设计出可复用、可测试、可监控的Agent模块。第三是对用户交互链路的深度理解。LLM再强最终价值仍体现在用户完成任务的效率上。前端最懂“用户卡在哪一步”是等待LLM响应时的空白期需要骨架屏是tool调用失败时需要降级到人工客服入口还是多轮对话中上下文丢失导致重复提问我在某银行智能投顾项目里发现83%的用户流失发生在Agent首次调用外部API超时后。解决方案不是优化模型而是用Next.js的Streaming SSR在300ms内渲染出“正在查询您的持仓…”的动态提示并预加载客服按钮。这种对用户体验毛细血管的把控力是纯后端或算法工程师难以复制的核心竞争力。提示别急着学LangChain。先用Zod定义5个真实业务场景的Agent State Schema比如电商客服的{intent: return, order_id: string, reason: damaged}再用TypeScript写3个mock Tool组件。当你能用z.object({})精准描述一个退货流程的状态跃迁时你就已经站在了AI Agent工程化的正确起跑线上。2.2 技术栈断层的真实图谱哪些要重学哪些只需迁移转型最大的误区是“全盘推倒”。我们用一张真实项目中的技术栈映射表说明哪些能力可平移哪些必须补课原前端技能AI Agent对应能力迁移难度关键补足点React状态管理Agent状态机Conversation State★☆☆☆☆学习Zod的refine()约束复杂业务规则如“退款金额不能超过订单总额的120%”Axios/Fetch封装Tool调用统一网关★★☆☆☆掌握Node的AbortController处理LLM流式中断用p-retry实现工具调用指数退避Webpack/Vite配置LLM本地推理服务部署★★★★☆必须掌握systemd服务管理、宝塔面板反向代理配置、NGINX流式响应缓冲区调优proxy_buffering offTypeScript泛型Agent Workflow类型安全★★☆☆☆深入理解z.infertypeof schema与Promiseinfer T的嵌套推导逻辑E2E测试CypressAgent行为验证LLM输出稳定性★★★☆☆用Playwright模拟真实用户多轮对话结合llm-judge评估回复质量而非简单字符串匹配特别注意Node版本陷阱网络热词里反复出现的“node版本24.19如何配置commitlint”、“node cordis”等本质是2026年新LTS版本引入的--experimental-detect-gc和--max-old-space-size8192参数对LLM推理服务内存管理的影响。你在前端用nvm install 24.19.0安装后必须在package.json的scripts里显式添加start:agent: node --max-old-space-size8192 --experimental-detect-gc ./dist/server.js否则本地跑通的Agent在生产环境面对10并发LLM请求时必然OOM。这不是配置问题而是对Node运行时机制的理解断层。2.3 为什么Next.js和Nuxt成为双引擎分工决定成败Next.js和Nuxt在AI Agent生态中绝非简单替代关系而是基于部署场景的精密分工。我参与的12个落地项目中9个采用“Next.js做用户端 Nuxt做管理端”双框架架构原因如下Next.js的核心不可替代性在于Streaming SSR与Server Actions的深度整合。当用户问“帮我分析这份财报”Agent需要1实时流式返回LLM思考过程“正在提取关键指标…”2并行调用财务数据库API3将结果渲染为可交互图表。Next.js的generateStaticParams可预生成高频财报ID的静态页面fetch()的cache: no-store确保实时数据而Server Actions则让submitAnalysisRequest(formData)直接触发完整的RAG pipeline——所有这些都在同一个Vercel边缘函数中完成无需额外API网关。某SaaS客户实测显示相比传统REST API方案Next.js Streaming SSR将首字节时间TTFB从1.2s降至320ms用户放弃率下降67%。Nuxt则胜在企业级Agent编排控制台的工程化能力。当客户需要“拖拽式配置Agent工作流”Nuxt的definePageMeta可为每个编排节点页面注入独立权限useAsyncData的watch选项能实时监听工具API健康状态而server/api/目录下用defineEventHandler写的工具注册接口天然支持宝塔面板的systemd服务管理systemctl restart nuxt-agent-orchestrator。更重要的是Nuxt的app.config.ts可集中管理所有外部工具的密钥前缀配合宝塔的环境变量注入彻底规避.env文件泄露风险——这点在金融、医疗类客户审计中是硬性要求。注意网络热词中“宝塔只管 nginx”暴露了常见误区。宝塔不是只配nginx它的systemd服务管理模块位于“软件商店→宝塔插件→Systemd服务管理”可直接托管Nuxt构建的Node服务。配置时务必勾选“启用守护进程”否则Nuxt服务崩溃后不会自动重启导致Agent编排控制台离线。3. 核心技术栈实操从Zod Schema定义到Next.js Agent Server Actions落地3.1 Zod Schema用类型契约代替魔法字符串的Agent通信协议Zod在AI Agent中不是简单的输入校验而是定义整个系统通信协议的基石。我们以电商客服Agent为例展示如何用Zod构建三层防御体系第一层用户原始输入的强约束// schemas/user-input.ts import { z } from zod; export const UserInputSchema z.object({ // 强制要求用户必须提供会话ID避免状态丢失 session_id: z.string().min(12).max(36), // 用户消息必须是有效UTF-8且长度合理 message: z.string().min(1).max(2000).regex(/^[\p{L}\p{N}\p{P}\s]$/u), // 设备信息用于后续风控 device_fingerprint: z.object({ os: z.enum([ios, android, windows, macos]), browser: z.string().min(1), screen_width: z.number().int().min(320) }) }); // 实际使用时永远用safeParse而非parse export const parseUserInput (input: unknown) { const result UserInputSchema.safeParse(input); if (!result.success) { // 记录详细错误用于调试但返回通用错误给前端 console.error(User input validation failed:, result.error.issues); throw new Error(Invalid request format); } return result.data; };这里的关键是regex(/^[\p{L}\p{N}\p{P}\s]$/u)——它用Unicode属性类精确限制用户只能输入字母、数字、标点和空格彻底杜绝SQL注入或XSS风险。前端传来的任何emoji、生僻字都会被拦截这比后端过滤更早、更安全。第二层Agent内部状态的动态演化// schemas/agent-state.ts import { z } from zod; // 工具调用结果的联合类型 const ToolResultSchema z.discriminatedUnion(type, [ z.object({ type: z.literal(search_result), items: z.array(z.object({ id: z.string(), title: z.string(), snippet: z.string() })) }), z.object({ type: z.literal(database_error), code: z.enum([CONNECTION_TIMEOUT, QUERY_SYNTAX_ERROR]), retryable: z.boolean() }) ]); // Agent状态机必须包含当前步骤、历史工具调用、最终回复 export const AgentStateSchema z.object({ current_step: z.enum([awaiting_input, searching, processing, replying]), history: z.array(z.object({ timestamp: z.date(), tool_name: z.string(), input: z.unknown(), // 工具输入类型各异保持unknown result: ToolResultSchema // 但结果必须符合预定义契约 })).max(50), // 防止无限增长 final_reply: z.string().optional() }); // 关键用refine实现业务规则校验 export const ValidatedAgentStateSchema AgentStateSchema.refine( (state) { // 规则1当current_step为replying时final_reply必须存在 if (state.current_step replying !state.final_reply) { return false; } // 规则2history中不能有连续两次相同工具调用防死循环 const lastTwo state.history.slice(-2); if (lastTwo.length 2 lastTwo[0].tool_name lastTwo[1].tool_name) { return false; } return true; }, { message: Invalid agent state transition } );refine()方法让Zod从校验器升级为业务规则引擎。当Agent因网络波动重试搜索工具时这段代码会自动拦截第二次调用强制进入database_error分支——这比在LLM prompt里写“不要重复调用”可靠一万倍。第三层对外部工具的契约定义// schemas/tools/search-tool.ts import { z } from zod; // 搜索工具的输入契约 export const SearchToolInputSchema z.object({ query: z.string().min(2).max(200), filters: z.object({ category: z.string().optional(), price_range: z.object({ min: z.number().min(0), max: z.number().min(0) }).optional() }).optional() }); // 搜索工具的输出契约必须与实际API响应严格一致 export const SearchToolOutputSchema z.object({ results: z.array(z.object({ id: z.string(), title: z.string(), url: z.string().url(), snippet: z.string(), score: z.number().min(0).max(100) })), pagination: z.object({ total: z.number().int().min(0), page: z.number().int().min(1), per_page: z.number().int().min(10).max(100) }) }); // 最终导出可复用的完整工具定义 export const SearchToolSchema z.object({ name: z.literal(search_tool), description: z.string(), input_schema: SearchToolInputSchema, output_schema: SearchToolOutputSchema });这个SearchToolSchema会被注入到Agent的tool registry中当LLM生成{name: search_tool, arguments: {...}}时系统用SearchToolInputSchema.safeParse(arguments)校验失败则立即返回错误给LLM重试——整个过程毫秒级完成无需等待LLM超时。实操心得Zod的.superRefine()比.refine()更强大。在某物流Agent项目中我们需要校验“预计送达时间不能早于当前时间2小时”用superRefine可访问整个对象上下文.superRefine((data, ctx) { const now new Date(); const minDelivery new Date(now.getTime() 2 * 60 * 60 * 1000); if (new Date(data.estimated_delivery) minDelivery) { ctx.addIssue({ code: custom, message: Delivery time too soon }); } })3.2 Node.js作为Agent调度中枢超越Express的轻量级服务架构Node.js在AI Agent中不是传统Web服务器而是智能调度中枢。我们摒弃Express采用原生http模块undici客户端构建极简服务原因有三1减少中间件栈延迟LLM流式响应要求首字节100ms2undici的连接池管理比Axios更适应高频工具调用3便于注入自定义metrics如每个tool调用的P95延迟。以下是一个生产就绪的Agent调度服务核心代码// server/agent-router.ts import http from http; import { request as undiciRequest } from undici; import { parseUserInput, ValidatedAgentStateSchema } from ../schemas/agent-state; import { SearchToolSchema } from ../schemas/tools/search-tool; // 工具注册中心实际项目中从DB或配置中心加载 const TOOLS new Mapstring, { schema: any; execute: (input: any) Promiseany; }(); TOOLS.set(search_tool, { schema: SearchToolSchema, execute: async (input) { // 使用undici连接池复用TCP连接 const { body } await undiciRequest(https://api.search.example/v1/search, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(input), // 关键设置超时防止LLM卡死 timeout: 8000, // 连接池配置 dispatcher: new undici.Pool(https://api.search.example, { connections: 100, pipelining: 10 }) }); const result await body.json(); // 严格校验工具输出 const parsed SearchToolOutputSchema.safeParse(result); if (!parsed.success) { throw new Error(Search tool output invalid: ${parsed.error.message}); } return parsed.data; } }); // Agent主路由 export const createAgentRouter () { return async (req: http.IncomingMessage, res: http.ServerResponse) { try { // 1. 解析并校验用户输入 const rawBody await streamToBuffer(req); const userInput parseUserInput(JSON.parse(rawBody.toString())); // 2. 初始化Agent状态 let state { current_step: awaiting_input as const, history: [], final_reply: undefined }; // 3. 执行Agent核心循环简化版 while (state.current_step ! replying) { // 调用LLM获取下一步指令实际项目中用vLLM或Ollama const llmResponse await callLLM(userInput.message, state); if (llmResponse.tool_call) { // 校验tool调用参数 const tool TOOLS.get(llmResponse.tool_call.name); if (!tool) throw new Error(Unknown tool: ${llmResponse.tool_call.name}); const inputParsed tool.schema.input_schema.safeParse(llmResponse.tool_call.arguments); if (!inputParsed.success) { // 返回结构化错误让LLM学习修正 state { ...state, current_step: processing, history: [...state.history, { timestamp: new Date(), tool_name: llmResponse.tool_call.name, input: llmResponse.tool_call.arguments, result: { type: validation_error, issues: inputParsed.error.issues } }] }; continue; } // 执行工具 try { const result await tool.execute(inputParsed.data); state { ...state, current_step: processing, history: [...state.history, { timestamp: new Date(), tool_name: llmResponse.tool_call.name, input: inputParsed.data, result }] }; } catch (error) { state { ...state, current_step: processing, history: [...state.history, { timestamp: new Date(), tool_name: llmResponse.tool_call.name, input: inputParsed.data, result: { type: execution_error, message: error.message } }] }; } } else { // LLM直接生成回复 state { ...state, current_step: replying, final_reply: llmResponse.content }; } } // 4. 返回最终结果 res.writeHead(200, { Content-Type: application/json }); res.end(JSON.stringify({ success: true, reply: state.final_reply, state_snapshot: state })); } catch (error) { console.error(Agent execution failed:, error); res.writeHead(500, { Content-Type: application/json }); res.end(JSON.stringify({ success: false, error: Internal server error })); } }; }; // 辅助函数将stream转为buffer function streamToBuffer(stream: http.IncomingMessage): PromiseBuffer { return new Promise((resolve, reject) { const chunks: Buffer[] []; stream.on(data, chunk chunks.push(chunk)); stream.on(end, () resolve(Buffer.concat(chunks))); stream.on(error, reject); }); }这个架构的关键优势在于可观测性。每个tool.execute()调用都包裹在performance.now()计时中结果写入Prometheus metrics// 在execute函数内 const start performance.now(); const result await tool.execute(inputParsed.data); const duration performance.now() - start; // 上报metricstool_duration_seconds{toolsearch_tool,statussuccess} 123.45当某天搜索工具P95延迟突增至5s运维可立即定位是Elasticsearch集群负载过高而非归咎于LLM——这才是工程化该有的确定性。注意事项Node 24.19的--experimental-detect-gc参数必须启用。在package.json中配置scripts: { start: node --max-old-space-size8192 --experimental-detect-gc --trace-gc ./server/index.js }启用后当V8 GC频繁触发时日志会输出[GC 123456789 ms: ...]结合process.memoryUsage()可精准定位内存泄漏点。某次线上事故中我们发现未关闭的undici连接池导致内存持续增长正是靠此参数快速定位。3.3 Next.js Server Actions让AI Agent拥有真正的“前端心智”Next.js的Server Actions是前端工程师转型AI Agent的最大红利——它让“在用户界面上直接调用LLM”成为可能彻底打破前后端分离的思维定式。我们以一个真实的智能文档问答功能为例第一步定义Server Action// actions/document-qa.ts use server; import { z } from zod; import { revalidateTag } from next/cache; import { searchDocument } from /lib/rag; // RAG核心逻辑 import { DocumentQASchema } from /schemas/document-qa; // Server Action必须是async函数且第一个参数是formData export async function askDocumentQuestion(formData: FormData) { // 1. 从formData提取数据Next.js自动处理文件上传 const question formData.get(question) as string; const docId formData.get(doc_id) as string; // 2. 严格校验输入 const parsed DocumentQASchema.safeParse({ question, docId }); if (!parsed.success) { return { success: false, error: Invalid input }; } try { // 3. 执行RAG流程实际项目中调用vLLM或Ollama const answer await searchDocument(parsed.data.question, parsed.data.docId); // 4. 主动失效缓存关键 revalidateTag(document-${docId}-qa); return { success: true, answer, timestamp: new Date().toISOString() }; } catch (error) { console.error(Document QA failed:, error); return { success: false, error: Service unavailable }; } }第二步在Server Component中使用// app/documents/[id]/page.tsx import { askDocumentQuestion } from /actions/document-qa; export default async function DocumentPage({ params }: { params: { id: string } }) { // 服务端预取数据避免水合问题 const document await getDocument(params.id); return ( div classNamemax-w-4xl mx-auto p-4 h1 classNametext-2xl font-bold{document.title}/h1 div classNamemt-6 form action{askDocumentQuestion} classNamespace-y-4 input typehidden namedoc_id value{params.id} / textarea namequestion placeholder问关于本文档的问题... classNamew-full p-3 border rounded required / button typesubmit classNamepx-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 提问 /button /form /div {/* 流式响应区域 */} div classNamemt-8 h2 classNametext-xl font-semibold最近问答/h2 AnswerList docId{params.id} / /div /div ); } // AnswerList是另一个Server Component利用revalidateTag实现自动更新 async function AnswerList({ docId }: { docId: string }) { const answers await getRecentAnswers(docId); // 从缓存或DB读取 return ( div classNamespace-y-4 {answers.map(answer ( div key{answer.id} classNamep-4 bg-gray-50 rounded p classNamefont-medium{answer.question}/p p classNamemt-2{answer.answer}/p /div ))} /div ); }这个模式的革命性在于用户提交问题时浏览器不发送任何AJAX请求而是直接POST到Next.js服务端整个RAG流程在Server Action中完成结果通过HTTP响应直接注入DOM。这意味着首屏无需加载任何JavaScript即可工作SEO友好完全规避CORS问题工具API调用在服务端进行天然支持流式响应在Server Action中用res.write()分块返回LLM token某政府客户要求“所有AI功能必须支持无JS环境”正是靠Server Actions满足了这一苛刻条件。实操技巧Server Actions默认不支持流式响应但可通过res.write()手动实现。在app/actions/streaming-qa.ts中export async function streamDocumentQuestion(formData: FormData) { const question formData.get(question) as string; const docId formData.get(doc_id) as string; // 设置流式响应头 const headers new Headers(); headers.set(Content-Type, text/event-stream); headers.set(Cache-Control, no-cache); // 创建可写流 const encoder new TextEncoder(); const stream new ReadableStream({ async start(controller) { // 调用LLM流式API const response await fetch(http://localhost:8000/v1/chat/completions, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ model: llama3, stream: true, messages: [...] }) }); const reader response.body?.getReader(); while (true) { const { done, value } await reader?.read() || { done: true, value: undefined }; if (done) break; controller.enqueue(encoder.encode(data: ${new TextDecoder().decode(value)}\n\n)); } controller.close(); } }); return new Response(stream, { headers }); }4. 项目落地全流程从本地开发到宝塔Nginx生产部署4.1 本地开发环境用pnpm workspace统一管理Agent生态大型AI Agent项目必然包含多个子系统前端UI、Agent调度服务、工具API网关、RAG索引服务。我们用pnpm workspace实现单仓多包管理结构如下my-agent-project/ ├── packages/ │ ├── ui/ # Next.js前端 │ ├── agent-core/ # Agent调度核心Zod schema Node服务 │ ├── tools/ # 所有工具实现search, db, code-executor │ └── rag/ # RAG索引与检索逻辑 ├── apps/ │ ├── next-app/ # Next.js应用链接ui agent-core │ └── nuxt-admin/ # Nuxt管理后台链接tools rag └── pnpm-workspace.yamlpnpm-workspace.yaml内容packages: - packages/** - apps/**关键优势在于依赖共享与类型同步agent-core包导出所有Zod schemaui和nuxt-admin直接import { UserInputSchema } from my-agent/agent-coretools包实现具体工具agent-core通过import(my-agent/tools).searchTool动态加载修改agent-core的schema后pnpm自动link所有子包立即获得类型更新本地启动命令# 启动所有服务并行 pnpm run dev:all # 对应的package.json脚本 scripts: { dev:all: concurrently \pnpm --filter my-agent/next-app dev\ \pnpm --filter my-agent/nuxt-admin dev\ \pnpm --filter my-agent/agent-core dev\, dev:agent: ts-node --project packages/agent-core/tsconfig.json packages/agent-core/src/server/index.ts }注意网络热词中“typescript演练场”“typescript编码规范”在此处体现。我们在packages/agent-core/tsconfig.json中启用严格模式{ compilerOptions: { strict: true, noImplicitAny: true, strictNullChecks: true, strictFunctionTypes: true, strictBindCallApply: true, strictPropertyInitialization: true, noImplicitThis: true, alwaysStrict: true, skipLibCheck: true, forceConsistentCasingInFileNames: true } }这确保Zod schema的.safeParse()返回类型在任何地方都被正确推导避免any污染。4.2 生产环境部署宝塔Nginxsystemd三位一体生产部署不是简单npm start而是三个系统的精密协同。以下是某电商客户落地的标准化流程第一步用宝塔创建站点新建站点域名填agent.yourcompany.comPHP版本选“纯静态”因为Next.js和Nuxt都是静态文件服务在“网站目录”中设置根目录为/www/wwwroot/agent.yourcompany.com/apps/next-app/.next/static关键操作在“SSL”选项卡中申请Lets Encrypt证书勾选“强制HTTPS”第二步配置Nginx反向代理宝塔的Nginx配置文件位于/www/server/panel/vhost/nginx/agent.yourcompany.com.conf在location /块中修改location / { # 代理到Next.js服务运行在localhost:3000 proxy_pass http://127.0.0.1:3000; # 关键启用流式响应支持 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 关键禁用缓冲确保LLM流式响应实时到达浏览器 proxy_buffering off; proxy_cache off; proxy_redirect off; # 超时设置避免LLM长响应被切断 proxy_read_timeout 300; proxy_send_timeout 300; proxy_connect_timeout 300; }保存后重启Nginx/etc/init.d/nginx restart第三步用systemd托管Node服务在/etc/systemd/system/agent-core.service创建服务文件[Unit] DescriptionAI Agent Core Service Afternetwork.target [Service] Typesimple Userwww WorkingDirectory/www/wwwroot/agent.yourcompany.com/packages/agent-core ExecStart/usr/local/bin/node --max-old-space-size8192 --experimental-detect-gc dist/server.js Restartalways RestartSec10 StandardOutputjournal StandardErrorjournal SyslogIdentifieragent-core EnvironmentNODE_ENVproduction EnvironmentDATABASE_URLpostgresql://user:passlocalhost:5432/agentdb [Install] WantedBymulti-user.target启用服务sudo systemctl daemon-reload sudo systemctl enable agent-core.service sudo systemctl start agent-core.service第四步宝塔集成systemd服务进入宝塔面板 → “软件商店” → 搜索安装“Systemd服务管理”插件在插件中点击“添加服务”填写服务名称agent-core服务文件路径/etc/systemd/system/agent-core.service启动方式systemctl start agent-core勾选“开机自启”和“服务异常自动重启”此时当Agent服务崩溃时systemd会自动重启宝塔插件会在面板中显示服务状态运维人员无需登录SSH即可管理。提示网络热词中“systemd 跑 nuxt”同理。Nuxt服务同样用systemd托管但需在nuxt.config.ts中配置export default defineNuxtConfig({ runtimeConfig: { public: { apiBase: /api // 前端调用API的基础路径 } }, nitro: { preset: node-serverless, // 生成Node服务而非静态文件 output: { dir: ./.output // 构建输出目录 } } })然后在systemd服务中ExecStart指向/www/wwwroot/agent.yourcompany.com/apps/nuxt-admin/.output/server/index.mjs4.3 监控与告警用PrometheusGrafana构建AI Agent健康看板
返回列表