Agent 的部署流程:从本地启动到服务化

0 阅读10分钟

echo-agent 前身为 2025 年 11 月启动的个人助理项目 fubot,最初面向长期陪伴型个人智能体,围绕认知记忆、上下文延续、用户偏好沉淀、任务闭环与持续自我优化展开。随着真实场景迭代,项目逐步形成多入口接入、统一事件模型、消息总线、Agent Loop、多模型抽象、工具调用、MCP 接入、任务调度、权限审批、运行轨迹、长期记忆和受控自演进等能力。目前已支持微信、QQ、CLI、Gateway、Webhook、Cron 等入口,服务用户超过 20 万、累计下载超过 50 万,是面向长期运行、记忆增强和可持续成长智能体的开源 Agent Runtime。

项目地址:github.com/fuyuxiang/e…

29-cover

很多 Agent 项目在本地演示时都很顺:终端里跑起来,输入一句话,模型能调用工具,结果也能返回。

问题通常出现在下一步:要接入 HTTP,要长期后台运行,要在 CI 里跑评估,要把 workspace 放到持久化目录,要让系统收到 SIGTERM 后干净退出。

这时你会发现,部署 Agent 不是多写几个启动命令。它真正要解决的是:这套系统在不同运行环境里,如何被配置、被装配、被停止、被检查,并且知道自己能影响什么。

本篇只讲一个点:Agent 的部署流程不是“把进程跑起来”,而是把本地可用的 Agent 放进一个可治理的运行模型。

问题入口

普通 Web 服务的部署边界相对清楚:暴露端口,连接数据库,加载配置,处理请求,写日志。

Agent 不一样。它不只是返回文本,还可能读文件、执行命令、调度任务、调用 MCP server、写入长期记忆、连接聊天平台、通过 Gateway 接收外部请求。

如果部署只关心“进程能不能启动”,风险会被压到运行时才暴露:配置从哪里来不清楚,workspace 指到错误目录,凭证跟随 shell 命令泄露,关闭时后台任务还在写数据库,公共 Gateway 暴露了本来只该本地使用的工具。

会调用工具只说明有行动接口;能否服务化运行,要看启动、配置、状态、权限和关闭是否进入同一套工程语义。

运行入口

为了不停留在抽象层面,下面以 echo-agent 的实现为例。

echo-agent 的入口位于 echo_agent/__main__.py,脚本名是 echo-agentpyproject.toml 中声明:

[project.scripts]
echo-agent = "echo_agent.__main__:main"

安装后可以直接运行 echo-agent;没有子命令时,默认等价于启动 Agent。

当前 CLI 支持六类子命令:

子命令作用部署语义
run启动完整 Agent本地或后台主运行模式
setup交互式配置向导首次配置与局部更新
status查看配置状态部署前健康检查
gateway单独启动 Gateway serverHTTP/WebSocket 服务入口
eval运行评估数据集本地或 CI 回归
service管理 Linux systemd 服务长期后台进程

29-部署路径

这个布局的关键,不是“命令多”,而是覆盖了从个人本地使用到生产服务化的路径:先 setup,再 status,本地用 run,外部系统接入用 gateway,质量回归用 eval,长期运行交给 service

顶层仍保留 -c/--config-w/--workspace。部署脚本、CI 和 systemd unit 往往比代码更难频繁调整,入口参数的兼容性本身就是工程成本。

配置与工作区

Agent 启动时,配置不能只来自一个 YAML 文件。生产环境里,默认配置、项目配置、环境变量和 CLI 参数经常同时存在。

echo-agent 的配置加载顺序是:

default.yaml
  -> 用户配置文件
  -> ECHO_AGENT_ 环境变量
  -> CLI overrides
  -> Pydantic Config 校验

用户配置文件可以是 echo-agent.yamlecho-agent.ymlconfig.yamlconfig.yml。查找顺序是指定的 search_dir 或当前目录,然后是 ~/.echo-agent。如果显式传入 config_path,则直接使用该路径。

环境变量使用双下划线表达嵌套路径,例如 ECHO_AGENT_MODELS__DEFAULT_MODELECHO_AGENT_GATEWAY__PORT。合并采用 deep merge:嵌套 dict 递归合并,普通值直接覆盖。这样生产环境只需要覆盖关键字段,不必复制整份配置。

workspace 是另一个容易出错的点。它不是普通临时目录,而是 Agent 的运行状态边界。echo-agent 默认 workspace 是 ~/.echo-agent;如果是相对路径,解析规则取决于来源:

来源相对路径基准
CLI 显式传入 workspace当前工作目录
配置文件中声明 workspace配置文件所在目录
没有配置文件当前工作目录

解析后系统会创建目录。workspace 下会保存数据库、会话、记忆、技能、调度状态、媒体缓存、日志等运行数据。

这个规则让项目内的 echo-agent.yaml 可以把 workspace 设置为 .,让 Agent 数据留在项目工作区;全局个人使用则可以继续落到 ~/.echo-agent

配置回答“系统应该怎么运行”;workspace 回答“系统运行后会把状态写到哪里”。

统一装配

部署最怕的是不同入口各自拼一套运行时。run 一套逻辑,gateway 一套逻辑,eval 又一套逻辑,最后同一份配置在不同模式下表现不一致。

echo-agent 用 _bootstrap() 解决这个问题。它是 CLI 各运行模式共享的装配函数,按依赖顺序创建 Config、Workspace、SQLiteBackend、MessageBus、ModelRouter、LLM providers、Scheduler、TaskManager、WorkflowEngine、AgentLoop、ChannelManager 和 HealthChecker。

29-bootstrap装配

核心流程可以压缩成下面这段伪代码:

config = load_config(config_path, overrides)
workspace = resolve_workspace(config, config_path, cwd)
storage = SQLiteBackend(workspace)
bus = MessageBus(config)
router = ModelRouter(config)
providers = init_providers(config.models.providers, router)
​
agent = AgentLoop(
    bus=bus,
    config=config,
    provider=providers.default,
    workspace=workspace,
    router=router,
    scheduler=scheduler,
    storage=storage,
    task_manager=task_manager,
    workflow_engine=workflow,
)
channels = ChannelManager(config.channels, bus)
health = HealthChecker(bus, agent, storage)

Provider 初始化也有一个重要取舍。系统会遍历 config.models.providers,逐个调用 create_provider(),成功后注册到 ModelRouter,第一个成功 provider 作为默认 provider。

如果某个 provider 创建失败,系统记录 warning,继续尝试其他 provider。如果所有 provider 都失败,或者根本没有配置 provider,系统创建 _StubProvider,返回明确提示。这样 CLI 或 Gateway 仍能启动,用户可以通过 status 或配置向导看到问题,而不是只得到一次进程崩溃。

启动与关闭

echo-agent run 或无子命令时会进入 _run()。启动顺序可以概括为:

解析配置和 workspace
-> bootstrap
-> 安装 SIGINT / SIGTERM handler
-> 启动 MessageBus
-> 启动 AgentLoop
-> 启动 enabled channels
-> 启动 Scheduler
-> 启动 HealthChecker
-> 如启用 Gateway,启动 GatewayServer
-> 等待 shutdown event

这里有两个关键点。

第一,MessageBus 必须先启动。channel、scheduler 和 AgentLoop 都依赖 bus 发布和分发事件;如果 bus 没准备好,外部入口提前接收消息,只会制造丢失和半完成状态。

第二,如果没有活跃输入通道且 Gateway 未启用,系统会退出并清理。这能避免一个看似启动成功、实际没有任何入口的空进程长期挂着。

停止顺序反过来:

Gateway
-> HealthChecker
-> Scheduler
-> Channels
-> AgentLoop
-> MessageBus
-> Storage

先停 Gateway 和 channels,避免新的外部请求进入;再停 scheduler 和 AgentLoop,避免后台任务继续推进;最后停 bus 和 storage,避免数据库先关闭导致正在收尾的任务写入失败。

服务化 Agent 的关闭不是按下停止键,而是把外部入口、后台任务、事件队列和持久化状态依次收束。

Gateway 模式会强制启用 ctx.config.gateway.enabled = True,并允许通过 CLI 覆盖 host 和 port。它仍会启动 bus、agent 和 channels,再创建 GatewayServer。这说明 Gateway 不是另一套 Agent,而是在同一个运行时上打开服务入口。

eval 则是运行时裁剪:它调用 _bootstrap(),启动 bus 和 agent,但不启动聊天 channels;随后创建 EvalRunner,用 eval channel 构造事件,按 dataset 和 tag 运行评估,并可输出 JSON 报告。

服务化

echo-agent service 面向 Linux systemd,支持 installuninstallstartstoprestartstatuslogs。非 Linux 会提示 systemd service management only supported on Linux。

安装时会生成 /etc/systemd/system/echo-agent.service,关键策略包括 Type=simpleRestart=on-failureKillSignal=SIGTERMTimeoutStopSec=30,日志写入 journal。ExecStart 会优先查找 PATH 中的 echo-agent,其次查找当前虚拟环境里的 bin/echo-agent,最后退到 python -m echo_agent run

生产部署时,应明确区分三类对象:

对象建议
代码目录安装包或容器镜像
配置文件echo-agent.yaml,由部署系统管理
Workspace持久化卷,例如 /var/lib/echo-agent 或应用目录下 data

如果使用容器部署,workspace 应挂载为 volume。否则容器重建会丢失 SQLite 数据库、会话、记忆、技能、知识库索引、调度状态、Gateway media cache、日志和 traces。

这也是本地优先和服务化并不矛盾的原因。SQLite、workspace、CLI、可选依赖、setup wizard 和 StubProvider 降级降低了本地学习成本;Gateway、systemd service、scheduler、health checker、多通道和 eval CLI 则让系统可以逐步进入长期运行。

部署边界

Agent 部署与普通聊天服务最大的区别,是它会改变现实系统。普通文本型 Chatbot 主要暴露文本输入和文本输出;Agent 服务还可能暴露文件访问、命令执行、任务调度、外部消息发送、MCP 工具调用和长期记忆。

所以部署安全的核心问题不是“服务监听在哪个端口”,而是四个边界是否明确。

29-部署边界

边界需要回答的问题可检查项
入口边界谁能访问 AgentGateway 鉴权、host、限流、通道开关
行动边界Agent 能影响哪些系统工具分级、exec 审批、MCP 凭证最小化
状态边界长期数据保存在哪里workspace 持久化、备份、缓存与记忆区分
观测边界行为如何被记录和复盘trace、日志权限、工具调用与审批审计

模型 provider 也属于部署边界。provider 决定推理能力和数据流向;配置不明确时,系统可能不可用,也可能把敏感内容发送到不符合预期的外部模型。

凭证更敏感。模型 API key、通道 token、MCP OAuth token、数据库密码、SSH key、Webhook secret 都不应该写进代码仓库或普通日志。生产环境应通过 secret manager、环境变量注入或加密存储管理,并支持轮换。

一个生产可用的 Agent 至少应满足这些基线:Gateway 公开时必须鉴权和限流;危险工具必须受审批约束;workspace 必须持久化和可备份;日志与 trace 能解释每次行动依据;provider 和凭证状态能通过 status 检查;评估集能在发布前回归关键能力和安全边界。

小结

Agent 的部署流程不是从 echo-agent runecho-agent service start 的命令迁移,而是从本地隐式假设到服务化显式边界的迁移。

本地 CLI 模式默认用户就在终端前,工作目录和文件边界由用户自己理解,审批可以即时发生。服务化之后,请求可能来自远程客户端,用户不一定在线,workspace 需要持久化,会话可能并发,错误要转成 API 语义,审批可能跨通道完成。

echo-agent 的 CLI 与部署设计提供了一条渐进路径:用 setup 完成首次配置,用 status 检查运行状态,用统一 _bootstrap() 装配运行时,用 rungateway 启动服务入口,用 eval 做质量回归,用 service 接入 Linux systemd。

真正的工程判断是:部署完成后,系统不仅能运行,还能解释自己当前能做什么、不能做什么、状态在哪里、失败后如何恢复、每次行动为什么发生。只有做到这一点,Agent 才不是一个临时演示进程,而是可以长期承担任务的服务。

(全篇完)


本文为 echo-agent 设计笔记系列第 29 篇。项目源码已开源至 GitHub。如果你对工业级 Agent 的工程落地感兴趣,欢迎加入技术交流群(QQ群号:47572014)参与日常讨论。下一篇我们将探讨 《Agent 测试策略:让能力可回归》,敬请期待。