☰
工业智能体 Hermes Agent 部署实战:用 Docker Compose 打通 MES 与 IoT 落地链路(含 TaoToken 配置)
2026/9/29 21:31:05 网站建设 项目流程

1. 工业智能体落地为什么总卡在“最后一公里”

MES 和 IoT 这两套系统在工厂里跑了这么多年,数据其实一直都在,问题是它们各自为政。车间主任想知道“3 号车间今天的不良率怎么样”,你得先登 MES 翻质检报表,再切到 IoT 平台查对应时段的工艺参数,两边数据对不上还得手动拉 Excel 做透视。设备报警响了,工程师要同时开着 MES 工单页面和 IoT 传感器曲线,靠人眼比对才能定位是哪个工序出了问题。每天 18:00 的生产日报,专人从两个系统导数据、拼表格、发邮件,一套流程下来四十分钟起步。

Hermes Agent 这类工业智能体的价值就在这里:它不是再做一个新看板,而是把 MES 和 IoT 当成两个可调用的工具,让 LLM 自主决定“要回答这个问题,我该去查哪些接口、拿哪些数据、怎么分析”。你问一句“3 号车间今天不良率”,Agent 会自己调 MES 的质检接口、调 IoT 的实时数据接口、拿到当前时间做日期对齐,最后给你一段带归因的分析,而不是甩给你两张表。

这篇面向已经在做 MES/IoT 集成的工程师,从零给出一套可复制的 Docker Compose 编排骨架,把 Hermes Agent、MCP 工具层、API Gateway 串起来,并且用 TaoToken 统一 Key/API 通道解决多模型切换和密钥管理的问题。整套环境跑起来之后,你能直接复现“对话式数据查询 + 定时巡检 + 日报自动生成”这三条链路。适合谁:手上有 MES/IoT 接口文档、会写 Python、想快速验证工业 Agent 可行性的工程师。

2. TaoToken 前置:统一 Key 与 API 通道

Hermes Agent 本身不绑定模型,它通过 OpenAI 兼容接口调用 LLM。工业场景里你可能会遇到几种情况:测试阶段想用便宜模型跑通链路,生产阶段换更强的模型做归因分析;或者不同车间、不同项目组用不同的 Key 做成本隔离。如果每个容器都硬编码一个 Key,换模型就得改配置重启,很麻烦。

TaoToken 在这里的角色是统一入口:你拿到一个 Key,配一个 base_url,Hermes Agent 和 API Gateway 都指向它。后续换模型只改HERMES_MODEL环境变量,不用动 Key。它的 API 地址是https://taotoken.net/api,兼容 OpenAI 的/v1/chat/completions格式,所以openai这个 Python SDK 直接能用。

你需要先拿到 Key。登录后在控制台创建,建议按项目建不同的 Key,比如hermes-mes-test和hermes-mes-prod,方便后面做用量区分。创建入口在控制台的 API Keys 页面,拿到之后先别急着写进.env,我们后面配置环节统一处理。

注意:Key 只显示一次,创建后立刻复制保存。如果怀疑泄露,直接在控制台吊销重建,不要试图在配置文件里“改一改继续用”。

模型选择上,Function Calling 能力是硬要求,因为 Agent 要靠它决定调哪个工具。测试阶段可以用响应快的模型先把链路跑通,生产环境再换成推理更强的。TaoToken 的模型对话页面可以直接验证某个模型是否支持 tool_calls,建议在写代码之前先在那里发一条带工具的请求确认一下。

3. 可复制配置:Docker Compose 编排骨架

整套环境三个容器:hermes跑 Agent 本体和 Cron,api-gateway跑 FastAPI 做前端统一入口,hermes-net是内部网络。MES 和 IoT 是外部系统,通过环境变量注入地址。

先建目录:

mkdir -p ~/hermes-mes-iot/{api-gateway,tools} cd ~/hermes-mes-iot

3.1 docker-compose.yml

version: "3.8" services: hermes: image: nousresearch/hermes-agent:latest container_name: hermes-agent restart: unless-stopped command: > sh -c "hermes gateway run & hermes cron run" ports: - "26429:26429" - "26430:6430" volumes: - ./hermes-data:/opt/data - ./mcp-config.json:/opt/data/mcp-config.json:ro - ./cron-tasks.yaml:/opt/data/cron-tasks.yaml:ro - ./tools:/opt/tools:ro environment: - OPENAI_API_KEY=${TAOTOKEN_API_KEY} - OPENAI_BASE_URL=${TAOTOKEN_BASE_URL} - HERMES_MODEL=${HERMES_MODEL:-gpt-4o} - FEISHU_APP_ID=${FEISHU_APP_ID:-} - FEISHU_APP_SECRET=${FEISHU_APP_SECRET:-} - HERMES_MCP_CONFIG=/opt/data/mcp-config.json - HERMES_CRON_CONFIG=/opt/data/cron-tasks.yaml - HERMES_DASHBOARD=1 - HERMES_DASHBOARD_HOST=0.0.0.0 - HERMES_DASHBOARD_PORT=6430 - HERMES_DASHBOARD_INSECURE=${HERMES_DASHBOARD_INSECURE:-1} - HERMES_DASHBOARD_BASIC_AUTH_USERNAME=${DASHBOARD_USER:-admin} - HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=${DASHBOARD_PASS:-admin123} - TZ=Asia/Shanghai deploy: resources: limits: cpus: '2' memory: 4G networks: - hermes-net api-gateway: build: context: ./api-gateway dockerfile: Dockerfile container_name: hermes-api-gateway restart: unless-stopped ports: - "8080:8080" volumes: - ./tools:/opt/tools:ro - ./api-gateway:/app environment: - OPENAI_API_KEY=${TAOTOKEN_API_KEY} - OPENAI_BASE_URL=${TAOTOKEN_BASE_URL} - LLM_MODEL=${HERMES_MODEL:-gpt-4o} - MES_API_URL=${MES_API_URL} - MES_API_KEY=${MES_API_KEY:-} - IOT_API_URL=${IOT_API_URL} - IOT_API_KEY=${IOT_API_KEY:-} - TZ=Asia/Shanghai networks: - hermes-net depends_on: - hermes healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 30s timeout: 5s retries: 3 networks: hermes-net: driver: bridge

这里有个关键点:OPENAI_API_KEY和OPENAI_BASE_URL都指向 TaoToken,Hermes 和 Gateway 共用同一套凭证。换模型只改HERMES_MODEL,两个容器同时生效。

3.2 .env 环境变量

# --- TaoToken 统一通道 --- TAOTOKEN_API_KEY=sk-your-taotoken-key-here TAOTOKEN_BASE_URL=https://taotoken.net/api HERMES_MODEL=gpt-4o # --- MES 系统 --- MES_API_URL=http://10.86.3.32:91/mes MES_API_KEY=your-mes-api-key # --- IoT 平台 --- IOT_API_URL=http://10.86.3.33:80/iot IOT_API_KEY=your-iot-api-key # --- Dashboard 安全 --- HERMES_DASHBOARD_INSECURE=1 DASHBOARD_USER=admin DASHBOARD_PASS=ChangeMe123!

注意:生产环境务必把HERMES_DASHBOARD_INSECURE设为 0,并改掉默认密码。Dashboard 暴露的是 Agent 的完整工具调用能力,等于把 MES/IoT 的查询权限开放了。

3.3 MCP 工具配置 mcp-config.json

{ "mcpServers": { "mes-api": { "command": "python3", "args": ["/opt/tools/mes_mcp_server.py"], "env": { "MES_API_URL": "${MES_API_URL}", "MES_API_KEY": "${MES_API_KEY}" }, "description": "MES 制造执行系统 API" }, "iot-api": { "command": "python3", "args": ["/opt/tools/iot_mcp_server.py"], "env": { "IOT_API_URL": "${IOT_API_URL}", "IOT_API_KEY": "${IOT_API_KEY}" }, "description": "IoT 物联网平台 API" } } }

MCP 这层的作用是把 MES/IoT 的 HTTP 接口包装成 Agent 能理解的方法名。Agent 不需要知道workorders这个 endpoint 长什么样,它只需要知道有个get_work_orders方法可以调。

3.4 API Gateway 核心代码

api-gateway/Dockerfile:

FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY main.py . EXPOSE 8080 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

api-gateway/requirements.txt:

fastapi>=0.110.0 uvicorn[standard]>=0.29.0 openai>=1.30.0 pydantic>=2.7.0 python-multipart>=0.0.9

api-gateway/main.py的核心是 Function Calling 循环。这里给出关键片段,完整版可以直接按这个结构补全:

import os, json, time from datetime import datetime from fastapi import FastAPI, HTTPException from pydantic import BaseModel, Field import openai OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "") OPENAI_BASE_URL = os.environ.get("OPENAI_BASE_URL", "https://taotoken.net/api") LLM_MODEL = os.environ.get("LLM_MODEL", "gpt-4o") MES_API_URL = os.environ.get("MES_API_URL", "") IOT_API_URL = os.environ.get("IOT_API_URL", "") client = openai.AsyncOpenAI(api_key=OPENAI_API_KEY, base_url=OPENAI_BASE_URL) app = FastAPI(title="Hermes MES/IoT API Gateway") class ChatRequest(BaseModel): message: str = Field(..., description="用户输入") session_id: str = Field(default=None) TOOLS = [ { "type": "function", "function": { "name": "mes_query", "description": "查询 MES 数据,支持工单、设备、质量、排程", "parameters": { "type": "object", "properties": { "endpoint": {"type": "string"}, "method": {"type": "string", "enum": ["GET", "POST"]}, "params": {"type": "object"} }, "required": ["endpoint"] } } }, { "type": "function", "function": { "name": "iot_query", "description": "查询 IoT 数据,支持实时、历史、报警、能耗、OEE", "parameters": { "type": "object", "properties": { "endpoint": {"type": "string"}, "method": {"type": "string", "enum": ["GET", "POST"]}, "params": {"type": "object"} }, "required": ["endpoint"] } } } ] async def agent_chat(user_message: str): messages = [ {"role": "system", "content": "你是智能制造 AI 助手,精通 MES 和 IoT 数据分析。调用工具时 endpoint 必须是实际存在的 API 路径。回答用中文,数据要准确。"}, {"role": "user", "content": user_message} ] response = await client.chat.completions.create( model=LLM_MODEL, messages=messages, tools=TOOLS, tool_choice="auto", temperature=0.3 ) msg = response.choices[0].message if msg.tool_calls: messages.append({ "role": "assistant", "content": msg.content or "", "tool_calls": [tc.model_dump() for tc in msg.tool_calls] }) for tc in msg.tool_calls: args = json.loads(tc.function.arguments) result = await execute_tool(tc.function.name, args) messages.append({ "role": "tool", "tool_call_id": tc.id, "content": json.dumps(result, ensure_ascii=False) }) final = await client.chat.completions.create( model=LLM_MODEL, messages=messages, temperature=0.3 ) return {"reply": final.choices[0].message.content} return {"reply": msg.content} @app.post("/api/chat") async def chat(req: ChatRequest): result = await agent_chat(req.message) return {"success": True, "data": result} @app.get("/health") async def health(): return {"status": "ok", "timestamp": datetime.now().isoformat()}

execute_tool函数负责把工具名映射到实际的 HTTP 请求,MES 走MES_API_URL,IoT 走IOT_API_URL,用urllib或httpx都行。

3.5 MES/IoT MCP Server

tools/mes_mcp_server.py是一个 stdin/stdout 的 JSON-RPC 服务,Hermes 通过它调用 MES。核心逻辑:

import os, sys, json, urllib.request, urllib.parse MES_API_URL = os.environ.get("MES_API_URL", "") MES_API_KEY = os.environ.get("MES_API_KEY", "") def call_mes(endpoint, method="GET", params=None, data=None): url = f"{MES_API_URL}/{endpoint}" if params: url += "?" + urllib.parse.urlencode(params) headers = {"Content-Type": "application/json", "Authorization": f"Bearer {MES_API_KEY}"} req = urllib.request.Request(url, headers=headers, method=method) if data: req.data = json.dumps(data, ensure_ascii=False).encode("utf-8") try: with urllib.request.urlopen(req, timeout=30) as resp: return json.loads(resp.read().decode("utf-8")) except Exception as e: return {"error": str(e)} def main(): for line in sys.stdin: line = line.strip() if not line: continue req = json.loads(line) method = req.get("method", "") params = req.get("params", {}) if method == "get_work_orders": result = call_mes("workorders", params=params) elif method == "get_quality_data": result = call_mes("quality/inspection", params=params) elif method == "get_equipment_status": result = call_mes("equipment/status", params=params) else: result = {"error": f"Unknown method: {method}"} print(json.dumps({"result": result}, ensure_ascii=False), flush=True) if __name__ == "__main__": main()

IoT 的tools/iot_mcp_server.py结构完全一样,只是把 endpoint 换成devices/realtime、alarms、energy/consumption这些。

注意:上面这些 endpoint 是通用示例,实际接入时必须按你们 MES/IoT 系统的接口文档改。不同厂商的 MES 接口路径差异很大,有的用/api/v1/workorder/list,有的用/mes/queryOrder,照抄跑不通。

3.6 定时任务 cron-tasks.yaml

cron_jobs: - name: daily_production_report schedule: "0 18 * * *" timezone: "Asia/Shanghai" description: "每天18:00汇总MES生产数据,生成日报" prompt: > 请执行以下任务: 1. 调用 mes-api 查询今日所有产线产量、达成率、不良率 2. 调用 iot-api 获取今日设备OEE、故障时长、能耗 3. 对比昨日数据,标注异常波动(±10%以上) 4. 生成结构化日报并推送给生产管理团队 channel: feishu target_group: "生产管理群" enabled: true - name: equipment_health_check schedule: "0 */2 * * *" timezone: "Asia/Shanghai" description: "每2小时巡检关键设备健康状态" prompt: > 请执行设备健康巡检: 1. 通过 iot-api 获取关键设备实时数据 2. 对比基线阈值,识别异常参数 3. 如发现异常,调用 mes-api 查询维护记录并推送告警 channel: feishu target_group: "设备维护群" enabled: true

Cron 这层是工业 Agent 区别于普通对话机器人的关键:它让 Agent 在没人问的时候也在干活。

4. 验证请求与成功结果

配置写完,启动:

cd ~/hermes-mes-iot docker compose up -d --build docker compose ps

三个容器都应该是Up状态。然后验证健康检查:

curl http://localhost:8080/health

返回{"status":"ok","timestamp":"..."}说明 Gateway 起来了。

接着测对话链路:

curl -X POST http://localhost:8080/api/chat \ -H "Content-Type: application/json" \ -d '{"message":"3号车间今天的不良率怎么样?"}'

成功的话你会看到类似这样的返回:

{ "success": true, "data": { "reply": "3号车间今日不良率 2.3%,高于目标 1.5%。问题集中在 14:00-16:00,注塑机 #07 模温波动 ±8℃,建议检查加热系统。" } }

Agent 内部实际执行了:调get_current_time拿日期 → 调mes_query查质检数据 → 调iot_query查注塑机实时参数 → LLM 归因分析。你可以在 Gateway 日志里看到每次 tool_call 的记录:

docker compose logs -f api-gateway

如果日志里出现[Tool Call] mes_query({"endpoint": "quality/inspection", ...}),说明 Function Calling 链路是通的。

再验证定时任务是否注册成功:

docker compose logs hermes | grep cron

应该能看到daily_production_report、equipment_health_check这些任务被加载。想手动触发一次巡检,可以直接调 Gateway 的 inspection 接口:

curl -X POST http://localhost:8080/api/inspection \ -H "Content-Type: application/json" \ -d '{"task_type":"equipment_health","params":{"workshop":"3号车间"}}'

5. 本篇常见错排查

容器起来了但 Agent 不调工具,只回“我无法访问 MES”。九成是模型不支持 Function Calling,或者tool_choice没生效。先去 TaoToken 的模型对话页面发一条带 tools 的请求,确认返回里有tool_calls字段。如果模型不支持,换一个支持 tool_calls 的。

Gateway 报 401 或 403。检查.env里TAOTOKEN_API_KEY有没有多余空格,TAOTOKEN_BASE_URL是不是https://taotoken.net/api(注意不要带/v1,SDK 会自己拼)。MES/IoT 的 401 则是各自的MES_API_KEY/IOT_API_KEY问题,跟 TaoToken 无关。

MCP Server 启动就退出。看docker compose logs hermes,大概率是mcp-config.json里command路径不对,或者tools/目录没挂载进去。容器内路径是/opt/tools/mes_mcp_server.py,宿主机是./tools/mes_mcp_server.py,确认两边对得上。

Agent 调了工具但返回“Unknown method”。说明 LLM 编了一个 MCP Server 里没定义的方法名。解决办法是在 system prompt 里把可用方法列清楚,或者把 MCP Server 的available_methods返回给 LLM 让它自己纠正。

Cron 任务不执行。检查cron-tasks.yaml的缩进,YAML 对空格敏感。另外确认hermes cron run这个进程在容器里活着,docker compose exec hermes ps aux | grep cron看一下。

Dashboard 打不开。端口映射是26430:6430,访问http://your-server:26430。如果设了HERMES_DASHBOARD_INSECURE=0,需要 Basic Auth,用户名密码在.env里。

6. 从跑通到落地:下一步怎么走

链路跑通只是起点。真正落地时,MES/IoT 的接口鉴权方式可能不是 Bearer Token,而是签名或 Session,这时候改 MCP Server 里的call_mes函数就行,Agent 层不用动。数据量大的时候,别让 Agent 一次拉全量,在 MCP Server 里加分页和字段过滤,只返回 LLM 分析需要的字段。

定时任务的 prompt 建议先在模型对话页面手动跑几遍,确认输出格式稳定了再写进cron-tasks.yaml。日报这种要推送到群里的,格式最好固定成 Markdown 表格,飞书/钉钉渲染出来才好看。

如果你后面要接更多系统(WMS、ERP、SCADA),不用改 Hermes 本体,照着mes_mcp_server.py再写一个 MCP Server,在mcp-config.json里加一段就行。Agent 的工具集是动态加载的,加完重启hermes容器即可。

需要长期跑编码类任务或者多步 Agent 编排的,可以看下 Coding Plan 的额度方案,比按次调用更适合持续运行的巡检场景。接入过程中遇到 Key 或通道问题,直接查接入文档里的错误码对照表,比在日志里翻半天快。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询