主题
服务器架构
目录
引言
本文面向本地代理服务 Agent(Node.js,包名 ai-helper-agent,版本 2.3.1)的服务器架构文档。Agent 作为 Chrome 扩展的本地代理端,提供 HTTP API、WebSocket 实时通道、文件系统访问、命令执行、技能系统、MCP 工具注册与调用、长期记忆与 RAG 检索等能力。服务采用原生 Node.js http 模块与 ws 实现,不使用 Express;路由、中间件、认证、日志、配置、生命周期管理均集中在单入口文件中,并通过多个子模块解耦业务领域。
需要特别注意:Agent 是独立进程,源码或运行时配置修改后必须重启进程,新接口和配置才会生效。
项目结构
Agent 代码位于 agent/ 目录,入口为 src/server.js,通过 package.json 暴露 CLI 命令 ai-helper-agent 与别名 aha。核心职责划分如下:
| 层级 | 主要职责 | 关键文件 |
|---|---|---|
| 服务入口 | HTTP 请求处理、WebSocket 升级、生命周期、全局错误处理 | agent/src/server.js |
| 配置中心 | 默认值、配置文件读写、内存缓存、写锁、配对信息 | agent/src/config.js |
| 审计日志 | JSON Lines 日志、按日轮转、清理策略、查询接口 | agent/src/logger.js |
| 包元数据 | 版本号、入口、CLI、依赖、RAG 可选依赖 | agent/package.json |
graph TB Client["Chrome 扩展 / 本地客户端"] --> HTTP["HTTP 服务器<br/>原生 http 模块"] Client --> WS["WebSocket 服务器<br/>ws 库"] HTTP --> Router["统一请求处理器<br/>server.js"] Router --> Config["配置模块<br/>config.js"] Router --> Logger["审计日志模块<br/>logger.js"] Router --> FS["文件系统操作"] Router --> Exec["命令执行器"] Router --> Memory["长期记忆路由"] Router --> RAG["RAG 知识库路由"] Router --> Skill["技能系统"] Router --> MCP["MCP 工具注册与调用"]
图表来源
- agent/src/server.js#L388-L425
- agent/src/server.js#L2086-L2153
- agent/src/config.js#L107-L149
- agent/src/logger.js#L132-L163
章节来源
核心组件
- HTTP 服务器:基于 Node.js
http.createServer,所有路由在handleRequest中按路径分支处理,不依赖 Express。 - WebSocket 服务器:基于
ws的WebSocketServer({ noServer: true }),通过server.on('upgrade')接管 WebSocket 升级。 - 配置系统:以
~/.ai-helper-agent/config.json为持久化载体,带内存缓存、mtime 失效、写锁、字段校验、敏感字段加密落盘。 - 日志系统:JSON Lines 格式,按天写入
~/.ai-helper-agent/logs/agent-YYYY-MM-DD.log,支持控制台输出、类别标签、自动清理、分页查询。 - 安全与认证:Bearer Token 认证、CORS 仅放行
chrome-extension://来源、路径白名单、命令分级检查、回收站机制。 - 生命周期管理:启动时初始化搜索工具、RAG 探测、MCP/Skill 异步初始化;优雅关闭终止子进程、断开 WebSocket、关闭 HTTP/WebSocket 服务器、清理 PID 文件。
章节来源
- agent/src/server.js#L388-L425
- agent/src/server.js#L2086-L2153
- agent/src/config.js#L15-L41
- agent/src/logger.js#L7-L11
架构总览
Agent 的服务架构可理解为“单进程多路由”模型:一个 HTTP 服务器承载 REST API,一个 WebSocket 服务器承载命令执行实时流;两者共享认证、配置、日志与安全策略。
sequenceDiagram
participant Ext as "Chrome 扩展"
participant HTTP as "HTTP 服务器"
participant Router as "统一请求处理器"
participant Auth as "认证模块"
participant FS as "文件系统"
participant Exec as "命令执行器"
participant WS as "WebSocket 服务器"
Ext->>HTTP : POST /api/exec {command, cwd}
HTTP->>Router : handleRequest(req,res)
Router->>Auth : verifyToken(Bearer)
Auth-->>Router : extId
Router->>FS : checkPath(cwd)
FS-->>Router : allowed
Router->>Exec : executeCommand(command,cwd,callback)
Exec-->>Router : execId
Router-->>Ext : {execId, wsUrl}
Ext->>WS : ws : //${host} : ${port}/ws/exec/${execId}?token=...
WS->>Exec : addWsClient(execId,ws)
Exec-->>WS : 实时 stdout/stderr
WS-->>Ext : 事件流
图表来源
详细组件分析
HTTP 服务器与路由机制
HTTP 服务器由 startServer 创建,使用原生 http.createServer,所有请求进入 handleRequest。该函数承担以下职责:
- 解析 URL、设置国际化语言上下文。
- 处理 CORS 预检与响应头。
- 区分无需认证接口与需认证接口。
- 按 pathname 分发到具体业务逻辑。
- 统一 JSON 响应封装。
关键路由分组如下:
| 分组 | 典型端点 | 行为 |
|---|---|---|
| 无认证 | /api/pair、/api/status | 配对码生成、平台状态与健康检查 |
| 控制面 | /api/shutdown、/api/agent/restart、/api/agent/update | 本机免认证关闭、远程需认证;重启与更新带冷却与防重入 |
| 文件操作 | /api/fs/read、/api/fs/write、/api/fs/list、/api/fs/download-stream | 路径白名单校验、大小限制、脚本权限剥离、ZIP 打包、流式传输 |
| 命令执行 | /api/exec、/api/exec/stop、/api/exec/detach | 命令安全检查、同步/异步模式、返回 WebSocket 地址 |
| 技能系统 | /api/skill/run、/api/skill/import、/api/skill/save-markdown | 导入、启用、运行、Markdown 管理 |
| MCP | /api/mcp/servers、/api/mcp/call | 添加/删除/连接/断开/切换/调用 |
| RAG | /api/rag/install、/api/rag/detect、/api/rag/* | 可选依赖安装、检测、动态加载路由 |
| 长期记忆 | /api/memory/* | 静态加载 memory 路由 |
flowchart TD
Start(["收到 HTTP 请求"]) --> ParseURL["解析 URL 与 pathname"]
ParseURL --> I18n["设置请求级 i18n 语言"]
I18n --> CORS{"是否 OPTIONS 预检"}
CORS --> |是| Return204["返回 204 空响应"]
CORS --> |否| LoadConfig["每次请求重新加载配置"]
LoadConfig --> NoAuth{"是否无需认证接口"}
NoAuth --> |是| HandleNoAuth["处理 pair/status/shutdown"]
NoAuth --> |否| CheckAuth["验证 Bearer Token"]
CheckAuth --> Valid{"Token 有效?"}
Valid --> |否| Return401["返回 401/403"]
Valid --> |是| Route["按 pathname 分发到业务路由"]
Route --> FileOps["文件上传/读取/下载/搜索"]
Route --> ExecOps["命令执行"]
Route --> SkillOps["技能管理"]
Route --> McpOps["MCP 管理"]
Route --> RagOps["RAG 可选依赖与路由"]
Route --> MemoryOps["长期记忆路由"]
FileOps --> End(["返回 JSON 或二进制流"])
ExecOps --> End
SkillOps --> End
McpOps --> End
RagOps --> End
MemoryOps --> End
图表来源
- agent/src/server.js#L427-L553
- agent/src/server.js#L751-L862
- agent/src/server.js#L1605-L1689
- agent/src/server.js#L1727-L1841
- agent/src/server.js#L1845-L1923
- agent/src/server.js#L2025-L2072
章节来源
- agent/src/server.js#L388-L425
- agent/src/server.js#L427-L553
- agent/src/server.js#L751-L862
- agent/src/server.js#L1605-L1689
- agent/src/server.js#L1727-L1841
- agent/src/server.js#L1845-L1923
- agent/src/server.js#L2025-L2072
中间件处理流程
Agent 没有传统意义上的 Express 中间件栈,而是通过 handleRequest 中的顺序判断模拟中间件效果:
- 国际化中间件:根据
Accept-Language设置各子模块语言。 - CORS 中间件:仅放行
chrome-extension://来源,OPTIONS 预检直接返回。 - 配置刷新中间件:每次请求重新加载配置,避免 workdir 切换后快照过期。
- 认证中间件:除少数无认证接口外,其余均需 Bearer Token。
- 业务路由中间件:按路径分发给文件、命令、技能、MCP、RAG、记忆等模块。
- 错误兜底中间件:顶层 try/catch 捕获未处理异常,返回 500。
flowchart TD
Req["请求进入"] --> Lang["设置 i18n 语言"]
Lang --> Options{"OPTIONS?"}
Options --> |是| CorsPreflight["返回 CORS 预检"]
Options --> |否| ReloadCfg["重新加载配置"]
ReloadCfg --> AuthCheck{"是否需要认证"}
AuthCheck --> |否| PublicRoute["处理公开接口"]
AuthCheck --> |是| VerifyToken["验证 Token"]
VerifyToken --> Valid{"有效?"}
Valid --> |否| Reject["拒绝请求"]
Valid --> |是| Dispatch["分发到业务路由"]
Dispatch --> Resp["统一 JSON 响应"]
图表来源
章节来源
错误处理策略
错误处理分为三层:
| 层级 | 处理方式 |
|---|---|
| 请求级 | parseBody、parseMultipartBody 对超大 body、非法 JSON、非法 multipart 返回 400 |
| 路由级 | 每个业务分支 catch 异常并记录日志,返回结构化 {success,error} |
| 全局级 | handleRequest 外层 catch 返回 500;uncaughtException、unhandledRejection 监听记录但不退出进程 |
此外,端口占用错误会直接退出进程,避免半开服务。
章节来源
- agent/src/server.js#L271-L303
- agent/src/server.js#L310-L383
- agent/src/server.js#L417-L425
- agent/src/server.js#L2293-L2306
- agent/src/server.js#L2156-L2163
WebSocket 通信实现
WebSocket 用于命令执行的实时数据推送。核心流程:
- HTTP 端点
/api/exec返回wsUrl。 - 客户端连接
/ws/exec/{execId},优先使用Authorization: Bearer,回退到?token=。 - 服务端验证 token,失败直接 403。
- 将 WebSocket 加入执行器客户端列表。
- 心跳检测:每 30 秒 ping,未 pong 则 terminate。
- 客户端断开时移除连接,并按受管/旧语义处理超时。
sequenceDiagram
participant Client as "客户端"
participant HTTP as "HTTP 服务器"
participant WS as "WebSocket 服务器"
participant Exec as "命令执行器"
Client->>HTTP : GET /api/exec
HTTP-->>Client : {execId, wsUrl}
Client->>WS : 连接 ws : //${host} : ${port}/ws/exec/${execId}?token=...
WS->>WS : 验证 Authorization 或 ?token
WS->>Exec : addWsClient(execId, ws)
Exec-->>WS : 推送 stdout/stderr
WS-->>Client : 实时消息
Client->>WS : 断开
WS->>Exec : disconnectWsClient(execId, ws)
图表来源
章节来源
服务生命周期管理
生命周期包括启动、运行、关闭三个阶段:
启动阶段:
- 加载配置,开启终端日志输出。
- 异步初始化搜索工具与 RAG 可用性检测。
- 创建 HTTP 服务器与 WebSocket 服务器。
- 启动配对码轮换、回收站定期清理。
- 异步初始化 MCP 与 Skill 注册表,并启动 Skill 目录监听。
运行阶段:
- 处理 HTTP 请求与 WebSocket 升级。
- 维护活跃 socket 集合用于优雅关闭。
- 监控进程信号 SIGINT/SIGTERM。
关闭阶段:
- 停止配对码轮换与周期任务。
- 中止 RAG 安装并回收 npm 进程树。
- 终止所有运行中的命令进程。
- 关闭 MCP 连接。
- 强制断开所有 WebSocket 客户端。
- 销毁活跃 HTTP socket,避免
server.close()挂起。 - 等待 server 与 wss 关闭,清理 PID 文件,退出进程。
- 若 10 秒内未完成,强制退出防止卡死。
stateDiagram-v2 [*] --> 启动中 启动中 --> 运行中 : "HTTP 与 WebSocket 已监听" 运行中 --> 关闭中 : "SIGINT/SIGTERM 或 /api/shutdown" 关闭中 --> 已关闭 : "资源释放完成" 关闭中 --> 已关闭 : "10 秒超时强制退出"
图表来源
章节来源
- agent/src/server.js#L388-L425
- agent/src/server.js#L2165-L2205
- agent/src/server.js#L2239-L2288
- agent/src/server.js#L2290-L2291
配置系统设计
配置系统以 ~/.ai-helper-agent/config.json 为核心,设计要点如下:
- 默认值合并:
DEFAULTS定义端口、主机、工作目录、允许路径、RAG 策略、记忆 embedding 等,用户配置覆盖默认值。 - 内存缓存:
loadConfig使用 mtime 判断是否重新读取磁盘,减少 IO。 - 写锁互斥:
withWriteLock保证并发写不会互相覆盖。 - 敏感字段加密:
memoryEmbedding.apiKey与 pairing token 落盘前加密,内存中保持明文。 - 自动白名单:skills 目录与工作目录自动加入
allowedPaths。 - 字段校验:
updateConfigField仅允许合法 key,否则抛出国际化错误。
环境变量方面,当前配置模块未直接读取环境变量;但日志模块支持 AI_HELPER_LANG 设置日志语言。Agent 启动参数与 CLI 行为由 bin/agent.js 与 package.json scripts 驱动。
章节来源
- agent/src/config.js#L15-L41
- agent/src/config.js#L107-L149
- agent/src/config.js#L154-L188
- agent/src/config.js#L193-L210
- agent/src/config.js#L249-L269
- agent/src/logger.js#L32-L45
- agent/package.json#L18-L25
性能监控、日志记录、健康检查与调试工具
健康检查:
/api/status:返回版本、平台、架构、Node 版本、搜索工具可用性与 RAG 可用性。/api/status/detail:需认证,返回内存使用、运行时长、CPU 时间、工作目录、允许路径、文件限制、运行进程等。/api/heartbeat:需认证,仅刷新在线状态。
性能监控:
- 通过
/api/status/detail获取memoryUsedMB、memoryTotalMB、memoryRssMB、uptimeSeconds、cpuUser、cpuSystem。 - 文件下载支持流式传输,避免一次性读入内存导致 OOM。
- ZIP 打包使用临时文件并在完成后删除。
- 通过
日志记录:
- 分类日志:auth、fs、exec、security、system。
- 控制台输出开关由
setConsoleOutput(true)启用。 - 日志文件按日轮转,保留最多 30 个文件,单个文件超过 10MB 会被清理。
- 提供
/api/logs/query查询最近 7 天日志,支持类别过滤与分页。
调试工具:
- 审计日志查询接口便于前端展示。
- 平台信息与 RAG 探测结果可用于诊断环境差异。
- Skill 目录监听自动重载,便于开发调试。
章节来源
- agent/src/server.js#L487-L500
- agent/src/server.js#L725-L749
- agent/src/server.js#L1705-L1725
- agent/src/server.js#L1267-L1385
- agent/src/logger.js#L132-L163
- agent/src/logger.js#L183-L228
部署指南
Agent 的官方安装渠道是 GitHub Releases 自动构建产物,不建议手动 clone 源码构建。部署时需关注以下方面:
Docker 容器化:
- 基础镜像需满足 Node >= 18。
- 暴露端口
18910(可通过配置修改)。 - 挂载
~/.ai-helper-agent目录以持久化配置、workspace、memory、logs。 - 注意容器内用户权限与宿主目录映射。
环境变量设置:
AI_HELPER_LANG:设置日志语言(zh/en),默认检测系统语言。- 其他运行时参数通过配置文件
config.json设置,如port、host、workdir、allowedPaths、commandTimeout、fileMaxSize。
端口配置:
- 默认端口
18910,可在配置中修改。 - 端口占用时会退出进程,避免半开服务。
- 默认端口
防火墙规则:
- 建议仅开放
127.0.0.1:18910,避免暴露在公网。 - 若需远程访问,应通过反向代理或 SSH 隧道,并确保 Bearer Token 安全。
- 建议仅开放
进程监控与自动重启:
- 支持
/api/agent/restart与/api/agent/update,内部使用两阶段 spawn 重启包装器。 - 更新失败不重启,保持老进程运行并提示用户手动执行。
- 优雅关闭带 10 秒超时保护,防止进程卡死。
- 支持
章节来源
- agent/package.json#L1-L10
- agent/package.json#L18-L25
- agent/package.json#L44-L46
- agent/src/config.js#L15-L41
- agent/src/logger.js#L32-L45
- agent/src/server.js#L555-L603
- agent/src/server.js#L605-L720
- agent/src/server.js#L2156-L2163
- agent/src/server.js#L2239-L2288
依赖关系分析
Agent 的核心依赖如下:
| 依赖 | 用途 |
|---|---|
ws | WebSocket 服务器 |
archiver | ZIP 打包 |
xlsx | Excel 预览解析 |
zod | 数据校验(可能用于部分模块) |
@modelcontextprotocol/sdk | MCP 协议支持 |
RAG 相关依赖标记在 ragDependencies 中,按需安装,不影响主服务启动。
graph TB Server["server.js"] --> WS["ws"] Server --> Archiver["archiver"] Server --> XLSX["xlsx"] Server --> Zod["zod"] Server --> MCP["@modelcontextprotocol/sdk"] Server --> Config["config.js"] Server --> Logger["logger.js"]
图表来源
章节来源
性能与容量特性
- 请求体限制:JSON body 最大 200MB,multipart 上传同样限制。
- 文件读取限制:默认 50MB,可通过
fileMaxSize调整。 - 流式上传:
/api/fs/upload-stream支持原始二进制流,避免 base64 膨胀。 - 流式下载:
/api/fs/download-stream使用createReadStream或分块写入,避免内存峰值。 - ZIP 打包:临时文件写入 tmpdir,完成后删除,避免长期占用磁盘。
- 日志清理:保留最近 30 个日志文件,单个文件超 10MB 清理,间隔 60 秒。
- WebSocket 心跳:30 秒 ping,检测半开连接,防止僵尸连接占用资源。
[本节为通用性能讨论,不直接分析具体文件]
故障排查指南
常见问题与定位方法:
| 问题 | 排查步骤 |
|---|---|
| 端口被占用 | 查看启动日志,确认 EADDRINUSE 错误;检查是否有旧进程残留 |
| 无法连接 WebSocket | 检查 Token 是否正确,浏览器 WebSocket 不支持自定义 Header,需使用 ?token= |
| 文件操作被拒绝 | 检查 allowedPaths 是否包含目标路径,确认路径白名单校验 |
| 命令执行被拒绝 | 查看命令安全检查级别,deny 直接拒绝,confirm 需前端确认 |
| RAG 不可用 | 调用 /api/rag/detect 查看探测结果,必要时执行 /api/rag/install |
| 日志过多 | 使用 /api/logs/query 查询最近 7 天日志,按类别过滤 |
| 进程卡住不退出 | 检查优雅关闭是否触发,确认 10 秒超时是否生效 |
章节来源
- agent/src/server.js#L2156-L2163
- agent/src/server.js#L2089-L2153
- agent/src/server.js#L1605-L1674
- agent/src/server.js#L2025-L2072
- agent/src/server.js#L1705-L1725
- agent/src/server.js#L2239-L2288
结论
Agent 本地代理服务采用单进程多路由架构,以原生 http 与 ws 实现 HTTP 与 WebSocket 服务,通过统一请求处理器实现路由、认证、日志、配置与安全策略。其优势在于轻量、可控、易于调试;风险在于单进程阻塞与长驻进程模块缓存。部署时应严格限制网络暴露范围,合理配置端口与白名单,并利用健康检查、日志查询与优雅关闭机制保障稳定性。对于 Chrome 扩展侧,需注意 MV3 Service Worker 生命周期约束,确保保活与消息传递正确。
[本节为总结性内容,不直接分析具体文件]