作业 1:创建一个带工具调用的 Agent(基础 · 查询服务器状态)
作业目标:在已有 Dify + vLLM + Langfuse 链路上,搭建第一个会调 HTTP 工具的新 Agent。
验收标准:问 Agent「帮我看看 gpu-server-01 的状态」→ 调用工具
getServerStatus→ 返回格式化的 CPU / 内存 / GPU 指标。环境:
192.168.110.207(Dify 1.16.1 + FastAPI 模拟 API)+116.62.10.183(vLLM Qwen3.6-27B)。HTML 成品:
docs/html_cursor/作业1_带工具调用的Agent_服务器状态_cursor.html
运行环境:业务脚本均在 207 固定目录
/opt/workbuddy执行,Python 为/usr/bin/python3。 Windows 开发机仅保留_probe_*调试脚本;同步命令:python scripts_cursor/完整代码约定/00_部署_workbuddy到207.py。
目录
- 做完长什么样
- 第一部分 原理:工具调用 Agent 怎么跑
- 第二部分 前置条件
- 第三部分 详细操作步骤
- 第四部分 CPU 与 GPU 在本作业中的分工(当前 + 未来)
- 第五部分 完整代码程序
- 第六部分 本次实战问题与解决方案
- 第七部分 对本系统的专业建议
- 第八部分 验收清单
做完长什么样
| 项 | 本环境实测值 |
|---|---|
| 模拟 API | http://192.168.110.207:8777/server/status |
| OpenAPI Provider | server_status(UUID 见 _config.py) |
| 工具 operationId | getServerStatus |
| Agent 名称 | 运维巡检 Agent |
| app_id | d00d0382-ca3f-4cf2-9cc9-0cb5aed9404d |
| agent_id | 01a0189c-7d7e-742f-83d1-74c5c16878da |
| 验收问句 | 帮我看看 gpu-server-01 的状态 |
控制台:http://192.168.110.207 → 运维巡检 Agent → 新开对话 → 发验收句。
流式响应中应出现:
"tool":"getServerStatus""tool_input":"{\"server_name\": \"gpu-server-01\"}"observation含45%/62%/78%/18.2/24 GB
第一部分 原理:工具调用 Agent 怎么跑
1.1 与「纯知识库 Agent」的区别
| 知识库 Agent(行政问答) | 本作业 Agent(HTTP 工具) | |
|---|---|---|
| 能力来源 | 检索制度文档 | 调用外部 REST API |
| 中间层 | Embedding + Weaviate + Rerank | Dify OpenAPI 自定义工具 |
| 模型行为 | 调 knowledge_base_search | 调 getServerStatus |
| 数据真实性 | 来自知识库片段 | 来自 HTTP 返回 JSON |
本作业不绑知识库,只验证「模型 → 工具 → 外部 API → 再生成」闭环。
1.2 完整执行流程(逐步)
【0】用户在 Dify 工作室 / API 发问
query = "帮我看看 gpu-server-01 的状态"
conversation_id = ""(必须新开对话)
│
▼
【1】docker-api-1 接收(完整 URL,agent_id 为本环境实测值)
POST http://127.0.0.1/console/api/agent/01a0189c-7d7e-742f-83d1-74c5c16878da/chat-messages
读取 agent_config_snapshots:
- model → qwen3.6-27b
- prompt.system_prompt → 要求必须调 getServerStatus
- tools.dify_tools[] → API 工具元数据
│
▼
【2】docker-agent_backend-1 组装 Agent 运行请求
把 OpenAPI 工具 schema 转成 LLM 可见的 tools 定义
│
▼
【3】第一次 LLM(116 vLLM · Qwen3.6-27B)
模型输出 tool_call:getServerStatus(server_name="gpu-server-01")
※ 依赖 vLLM --tool-call-parser qwen3_xml(专题四/九)
│
▼
【4】Dify 执行 HTTP 工具
经 ssrf_proxy 出网
GET http://192.168.110.207:8777/server/status?server_name=gpu-server-01
│
▼
【5】FastAPI 模拟服务(207 CPU)
返回 JSON:cpu/memory/gpu_util/gpu_memory
│
▼
【6】第二次 LLM
把 tool observation 塞进上下文,按 system_prompt 格式化中文汇报
│
▼
【7】写 messages 表,SSE 推流给前端
可选:Celery → Langfuse Trace1.3 配置落在哪(三层)
工作区「集成 → 工具」 ← 注册 OpenAPI Provider(server_status)
│
Agent 快照 tools.dify_tools[] ← 对话真正挂载哪些工具(provider_id 必须是 UUID)
│
Agent 快照 prompt / model ← 会不会调工具、用什么 LLM改快照必须同时改草稿(agent_config_snapshots + agent_config_drafts)。改完 不要点「构建 / 发布」(专题十一)。
1.4 Dify 1.16 新 Agent 创建口(易错)
| 方式 | 结果 |
|---|---|
POST http://127.0.0.1/console/api/apps + "mode":"agent" | ❌ 400,CreateAppPayload 不允许 agent |
POST http://127.0.0.1/console/api/agent | ✅ 正确,返回 app_id / agent_id |
第二部分 前置条件
| # | 条件 | 怎么确认 |
|---|---|---|
| 1 | Dify 1.16 已跑,能登录控制台 | http://192.168.110.207 |
| 2 | vLLM Qwen3.6-27B 可用 | curl -H "Authorization: Bearer 123456" http://116.62.10.183:8000/v1/models |
| 3 | vLLM 已开工具解析 | --tool-call-parser qwen3_xml(不是 qwen3) |
| 4 | Dify 已配 Qwen 凭据 | 工作区模型供应商 |
| 5 | 207 上 8777 端口未被占用 | `ss -tlnp \ |
| 6 | 207 上已部署 /opt/workbuddy | /opt/workbuddy/dify_api.py + /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/_config.py |
一键前置检查(可选):/usr/bin/python3 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/专题十五_00_创建前检查.py
第三部分 详细操作步骤
推荐:按 3.1~3.5 顺序跑完整 Python 脚本(在 207 上执行)。
等效 UI:每步附手工路径,API 搞不定时用 UI 补。
3.0 一键顺序(脚本)
/usr/bin/python3 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_01_部署模拟服务.py
/usr/bin/python3 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_02_注册OpenAPI工具.py
/usr/bin/python3 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_03_创建Agent空壳.py
/usr/bin/python3 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_04_查ID并挂工具.py
/usr/bin/python3 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_05_对话验收.py期望最后一行:[PASS] Agent 已调用工具并返回状态信息。
换环境只改 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/_config.py 中 SERVER_STATUS_*、HOMEWORK1_*。
3.0.1 全局图景(先读这个,再往下翻 curl 细节)
| 阶段 | 脚本 | 目的 |
|---|---|---|
| 部署 | 01 → 04 | FastAPI 模拟服务器 + Dify OpenAPI + Agent 挂工具 |
| 验收 | 05 | 问「gpu-server-01 状态」并确认 Agent 调了 HTTP 工具 |
用户问「gpu-server-01 状态」
→ Dify Agent(207)
→ OpenAPI Provider「server_status」
→ FastAPI :8788(207 本机,模拟三台服务器 JSON)
→ Agent 汇总 CPU/内存/磁盘 回复各脚本四要素速查(详细 curl 见下方 3.1~3.5):
| 脚本 | 干什么 | 触发什么 | 成功标志 | 典型失败 |
|---|---|---|---|---|
01_部署模拟服务 | 启动 :8788 FastAPI | uvicorn + curl 探针 | [OK] 模拟服务已启动 | 8788 端口占用 |
02_注册OpenAPI | 注册 server_status provider | Dify POST add schema | HTTP 200 | yaml 与路由不一致 |
03_创建Agent空壳 | POST /agent 建「运维巡检 Agent」 | 仅建空壳,无工具 | already exists / 201 | 用错 /apps+agent 模式 400 |
04_查ID并挂工具 | 写快照 tools + system_prompt | psql UPDATE 快照/草稿 | [OK] 已挂工具 | model=null、UUID 空 |
05_对话验收 | 自动发验收 query | Agent streaming + 工具调用 | [PASS] Agent 已调用工具 | Connection error、未调工具 |
下方 3.1~3.5 保留完整 curl 与代码引用;第六部分 有 13 条实战排错表。
控制台 API 完整命令(登录一次,后续接口都能打)
Dify 1.16 控制台接口必须:登录拿 cookie + X-CSRF-Token。密码字段是 base64(不是明文)。脚本 dify_api.py 会 在 207 本机打 http://127.0.0.1/console/api。
ssh root@192.168.110.207
rm -f /tmp/dify_cj.txt
python3 - <<'PY'
import json, base64
open("/tmp/dify_login.json", "w").write(json.dumps({
"email": "38205036@qq.com",
"password": base64.b64encode(b"Difyai123456").decode(),
"language": "zh-Hans",
"remember_me": True,
}))
PY
curl -s -c /tmp/dify_cj.txt -X POST 'http://127.0.0.1/console/api/login' \
-H 'Content-Type: application/json' \
-d @/tmp/dify_login.json
CSRF=$(python3 - <<'PY'
jar=open("/tmp/dify_cj.txt").read()
for line in jar.splitlines():
if "csrf_token" in line:
print(line.split("\t")[-1].strip())
PY
)
echo "CSRF=$CSRF"列出新 Agent(不要用 psql 拼中文名):
curl -s -b /tmp/dify_cj.txt -H "X-CSRF-Token: $CSRF" \
'http://127.0.0.1/console/api/agent?page=1&limit=50'创建空壳(本作业名称「运维巡检 Agent」):
curl -s -b /tmp/dify_cj.txt -c /tmp/dify_cj.txt \
-H "X-CSRF-Token: $CSRF" -H 'Content-Type: application/json' \
-X POST 'http://127.0.0.1/console/api/agent' \
-d '{
"name": "运维巡检 Agent",
"description": "作业 1:带 HTTP 工具的运维巡检 Agent(查询服务器状态)",
"icon_type": "emoji",
"icon": "🖥️",
"icon_background": "#E8F5E9",
"role": "ops"
}'对话验收(本环境 agent_id):
curl -s -b /tmp/dify_cj.txt -c /tmp/dify_cj.txt \
-H "X-CSRF-Token: $CSRF" -H 'Content-Type: application/json' \
--max-time 420 \
-X POST 'http://127.0.0.1/console/api/agent/01a0189c-7d7e-742f-83d1-74c5c16878da/chat-messages' \
-d '{
"inputs": {},
"query": "帮我看看 gpu-server-01 的状态",
"response_mode": "streaming",
"files": [],
"conversation_id": ""
}'模拟 API(不需要登录,可在 Windows 或 207 上打):
curl -s 'http://192.168.110.207:8777/health'
curl -s 'http://192.168.110.207:8777/server/status?server_name=gpu-server-01'
curl -H "Authorization: Bearer 123456" 'http://116.62.10.183:8000/v1/models'3.1 步骤 1 · 部署 FastAPI 模拟服务(207 CPU)
理论
Agent 调用的不是 Dify 内置能力,而是宿主机上的 HTTP 服务。Dify 容器通过 ssrf_proxy 访问 192.168.110.207:8777,因此:
- 服务必须监听
0.0.0.0,不能只绑127.0.0.1(否则容器访问不到)。 - 返回 JSON 字段名要稳定,便于 LLM 格式化。
操作
方式 A(推荐):运行部署脚本(SFTP 上传 + 启动 + 探针)
完整程序:/opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_01_部署模拟服务.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_01_部署模拟服务.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""作业 1 · 步骤 1:把 FastAPI 模拟服务部署到 207 并启动。"""
from __future__ import annotations
import sys
from pathlib import Path
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify
REMOTE_DIR = "/opt/server_status_api"
LOCAL_API = Path(__file__).resolve().parent / "作业1_server_status_api.py"
LOCAL_SH = Path(__file__).resolve().parents[1] / "scripts_bash_cursor" / "作业1_启动_server_status.sh"
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
d.sh("mkdir -p %s" % REMOTE_DIR)
d.sftp.put(str(LOCAL_API), REMOTE_DIR + "/作业1_server_status_api.py")
d.sftp.put(str(LOCAL_SH), REMOTE_DIR + "/start.sh")
d.sh("chmod +x %s/start.sh" % REMOTE_DIR)
# 若旧进程占端口则先停
d.sh(
"if [[ -f /tmp/server_status_api.pid ]]; then "
"kill $(cat /tmp/server_status_api.pid) 2>/dev/null || true; fi"
)
out, err = d.sh("bash %s/start.sh" % REMOTE_DIR)
print(out or err)
r = d.sh(
"curl -s 'http://127.0.0.1:%d/server/status?server_name=gpu-server-01'"
% C.SERVER_STATUS_PORT
)
print("probe:", r[0].strip())
return 0 if "gpu-server-01" in (r[0] or "") else 1
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())方式 B(207 上手工):
完整脚本:scripts_bash_cursor/作业1_带工具调用的Agent_服务器状态/作业1_启动_server_status.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/作业1_带工具调用的Agent_服务器状态/作业1_启动_server_status.sh
#!/usr/bin/env bash
# 作业 1 · 在 207 上启动模拟「查询服务器状态」FastAPI 服务
set -euo pipefail
DIR="/opt/server_status_api"
PORT="${SERVER_STATUS_PORT:-8777}"
PIDFILE="/tmp/server_status_api.pid"
LOG="/tmp/server_status_api.log"
mkdir -p "$DIR"
pip3 install fastapi uvicorn -q
if [[ -f "$PIDFILE" ]] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
echo "server_status_api already running pid=$(cat "$PIDFILE")"
exit 0
fi
cd "$DIR"
nohup python3 -m uvicorn 作业1_server_status_api:app \
--host 0.0.0.0 --port "$PORT" >>"$LOG" 2>&1 &
echo $! >"$PIDFILE"
sleep 1
curl -sf "http://127.0.0.1:${PORT}/health" && echo
echo "started on :${PORT}, log=$LOG"探针
curl -s 'http://192.168.110.207:8777/server/status?server_name=gpu-server-01'
curl -s 'http://192.168.110.207:8777/health'期望:JSON 含 "cpu":"45%" 等字段。
完整 FastAPI 源码
完整程序:/opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_server_status_api.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_server_status_api.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""作业 1 · 模拟「查询服务器状态」HTTP 工具(FastAPI)。
启动(207 上):
pip3 install fastapi uvicorn -q
uvicorn 作业1_server_status_api:app --host 0.0.0.0 --port 8777
或:bash /opt/workbuddy/scripts_bash_cursor/作业1_带工具调用的Agent_服务器状态/作业1_启动_server_status.sh
"""
from __future__ import annotations
from fastapi import FastAPI
app = FastAPI(
title="Server Status API",
version="1.0.0",
description="Dify Agent 作业 1:模拟查询服务器 CPU / 内存 / GPU 状态",
)
@app.get("/server/status", operation_id="getServerStatus", summary="查询服务器状态")
def get_status(server_name: str) -> dict[str, str]:
"""根据 server_name 返回模拟监控指标(本服务不连真实监控)。"""
return {
"server": server_name,
"cpu": "45%",
"memory": "62%",
"gpu_util": "78%",
"gpu_memory": "18.2/24 GB",
}
@app.get("/health")
def health() -> dict[str, str]:
return {"status": "ok"}
if __name__ == "__main__":
import uvicorn
uvicorn.run("作业1_server_status_api:app", host="0.0.0.0", port=8777, reload=False)3.2 步骤 2 · 在 Dify 注册 OpenAPI 自定义工具
理论
Dify 通过 OpenAPI 3.0 Schema 解析出工具名、参数、HTTP 方法和 URL。注册后写入表 tool_api_providers,工作区所有 Agent 可选用。
operationId→ LLM 看到的工具名(本作业getServerStatus)servers.url→ 必须与模拟 API 一致auth_type: none→ 无鉴权模拟服务
操作
方式 A(推荐):
完整程序:/opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_02_注册OpenAPI工具.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_02_注册OpenAPI工具.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""作业 1 · 步骤 2:在 Dify 注册 OpenAPI 自定义工具(查询服务器状态)。"""
from __future__ import annotations
import sys
from pathlib import Path
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
SCHEMA_PATH = (
Path(__file__).resolve().parents[1]
/ "patches_cursor"
/ "作业1_server_status_openapi.yaml"
)
def main() -> int:
schema = SCHEMA_PATH.read_text(encoding="utf-8")
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
# 若已存在则先删再建,保证作业可重复跑
existing = d.get("/workspaces/current/tools/api")
names = []
for item in existing.get("json") or []:
names.append(item.get("name") or item.get("provider"))
print("existing api tools:", names)
if C.SERVER_STATUS_PROVIDER in names:
r_del = d.post(
"/workspaces/current/tool-provider/api/delete",
body={"provider": C.SERVER_STATUS_PROVIDER},
)
pp("delete old", r_del)
body = {
"provider": C.SERVER_STATUS_PROVIDER,
"credentials": {"auth_type": "none"},
"schema_type": "openapi",
"schema": schema,
"icon": {"background": "#E8F5E9", "content": "🖥️"},
"privacy_policy": "",
"custom_disclaimer": "",
"labels": ["ops", "homework1"],
}
r = d.post("/workspaces/current/tool-provider/api/add", body=body)
pp("add api tool", r)
if r.get("code") not in ("200", "201"):
return 1
r2 = d.get(
"/workspaces/current/tool-provider/api/tools?provider=%s"
% C.SERVER_STATUS_PROVIDER
)
pp("list tools", r2, limit=2000)
r3 = d.get("/workspaces/current/tools/api")
print("api tools now:", [
(x.get("name") or x.get("provider")) for x in (r3.get("json") or [])
])
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())方式 B(UI):
- 控制台 → 集成 → 工具 → 创建自定义工具
- 名称:
server_status - 粘贴 OpenAPI(见下方完整 yaml)
- 鉴权:无
- 保存后在工具列表能看到
getServerStatus
完整 OpenAPI Schema
完整补丁:patches_cursor/作业1_带工具调用的Agent_服务器状态/作业1_server_status_openapi.yaml
运行/引用:见 patches_cursor/作业1_带工具调用的Agent_服务器状态/作业1_server_status_openapi.yaml
openapi: 3.0.0
info:
title: Server Status API
version: 1.0.0
description: 作业 1 · 模拟查询服务器 CPU / 内存 / GPU 状态
servers:
- url: http://192.168.110.207:8777
paths:
/server/status:
get:
operationId: getServerStatus
summary: 查询服务器状态
description: 根据服务器名称返回 CPU、内存、GPU 利用率等模拟指标
parameters:
- name: server_name
in: query
required: true
description: 服务器名称,例如 gpu-server-01
schema:
type: string
responses:
"200":
description: 成功
content:
application/json:
schema:
type: object
properties:
server:
type: string
cpu:
type: string
memory:
type: string
gpu_util:
type: string
gpu_memory:
type: stringAPI 等价调用(207 上,先完成上面的登录拿 $CSRF)
# 注册 OpenAPI 工具:schema 很长,务必用脚本上传 yaml,不要手贴。
# 脚本内部等价于:
# POST http://127.0.0.1/console/api/workspaces/current/tool-provider/api/add
/usr/bin/python3 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_02_注册OpenAPI工具.py
# 列出该 provider 下的 tools
curl -s -b /tmp/dify_cj.txt -H "X-CSRF-Token: $CSRF" \
'http://127.0.0.1/console/api/workspaces/current/tool-provider/api/tools?provider=server_status'
# 列出工作区全部 API 工具
curl -s -b /tmp/dify_cj.txt -H "X-CSRF-Token: $CSRF" \
'http://127.0.0.1/console/api/workspaces/current/tools/api'3.3 步骤 3 · 创建新 Agent 空壳
理论
空壳只创建 App + Agent + 默认快照,尚未挂工具和写 Prompt。Dify 1.16 必须用 Roster API。
操作
完整程序:/opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_03_创建Agent空壳.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_03_创建Agent空壳.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""作业 1 · 步骤 3:创建带工具调用的 Agent 空壳(mode=agent)。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
# 若同名已存在,打印 ID 供后续步骤用,不重复创建
r_list = d.get("/agent?page=1&limit=50")
data = (r_list.get("json") or {}).get("data") or []
for item in data:
if item.get("name") == C.HOMEWORK1_APP_NAME:
print("already exists app_id=", item.get("id"), "agent_id=", item.get("agent_id") or item.get("id"))
print("下一步:运行 作业1_04_查ID并挂工具.py")
return 0
# Dify 1.16 新 Agent 用 POST /agent(不是 /apps + mode=agent)
body = {
"name": C.HOMEWORK1_APP_NAME,
"description": "作业 1:带 HTTP 工具的运维巡检 Agent(查询服务器状态)",
"icon_type": "emoji",
"icon": "🖥️",
"icon_background": "#E8F5E9",
"role": "ops",
}
r = d.post("/agent", body)
pp("create agent", r)
j = r.get("json") or {}
print("app_id", j.get("id"))
print("agent_id", j.get("agent_id") or j.get("id"))
if r.get("code") not in ("200", "201"):
return 1
print("下一步:运行 作业1_04_查ID并挂工具.py")
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())UI 等价:控制台 → Agent → 创建 Agent → 名称「运维巡检 Agent」。
记录返回的 app_id、agent_id(或跑步骤 4 自动查)。
3.4 步骤 4 · 挂工具 + System Prompt + 模型
理论
新创建的空壳常见问题:
model为null→ 对话 400tools.dify_tools为空 → 模型不会调工具provider_id写错 → 运行时报 UUID 错误
本步写入快照的 tools.dify_tools[0] 结构:
{
"enabled": true,
"provider_type": "api",
"provider_id": "021a4445-098d-4821-81fe-ff0ebc660ecc",
"tool_name": "getServerStatus",
"credential_type": "unauthorized",
"credential_ref": null
}注意:provider_id 是 tool_api_providers.id(UUID),不是名称 server_status。
操作
完整程序:/opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_04_查ID并挂工具.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_04_查ID并挂工具.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""作业 1 · 步骤 4:查 app/agent/snap/draft ID,挂工具,写提示词。"""
from __future__ import annotations
import json
import sys
import _bootstrap # noqa: F401
import _agent_cfg as AC
import _config as C
from dify_api import Dify, pp
SYSTEM_PROMPT = """你是企业内部运维助手。当用户询问某台服务器的状态、CPU、内存、GPU 时,
必须调用工具 getServerStatus(参数 server_name),并根据工具返回结果用中文格式化汇报。
汇报格式示例:
- 服务器:{server}
- CPU:{cpu}
- 内存:{memory}
- GPU 利用率:{gpu_util}
- GPU 显存:{gpu_memory}
不要编造指标;工具失败时如实说明。"""
def find_ids(d: Dify) -> tuple[str, str, str, str]:
# 列表 API 返回 UTF-8 正常;不要用中文名拼进远程 psql(Windows 编码易乱)
r_list = d.get("/agent?page=1&limit=50")
app_id = agent_id = ""
for item in (r_list.get("json") or {}).get("data") or []:
if item.get("name") == C.HOMEWORK1_APP_NAME:
app_id = item.get("id") or ""
agent_id = item.get("agent_id") or item.get("id") or ""
break
if not agent_id:
raise RuntimeError("列表里找不到应用:%s" % C.HOMEWORK1_APP_NAME)
sql = (
"SELECT a.app_id, a.id, a.active_config_snapshot_id, d.id "
"FROM agents a JOIN agent_config_drafts d ON d.agent_id=a.id "
"WHERE a.id='%s' LIMIT 1;" % agent_id
)
out, err = d.sh(
'docker exec %s psql -U postgres -d dify -t -A -F "|" -c "%s"'
% (C.PG_CONTAINER, sql)
)
line = (out or "").strip().splitlines()
if not line or "|" not in line[0]:
# 有时列表的 id 就是 app_id
sql2 = (
"SELECT a.app_id, a.id, a.active_config_snapshot_id, d.id "
"FROM agents a JOIN agent_config_drafts d ON d.agent_id=a.id "
"WHERE a.app_id='%s' LIMIT 1;" % app_id
)
out, err = d.sh(
'docker exec %s psql -U postgres -d dify -t -A -F "|" -c "%s"'
% (C.PG_CONTAINER, sql2)
)
line = (out or "").strip().splitlines()
if not line or "|" not in line[0]:
raise RuntimeError("找不到快照/草稿: %s %s" % (out, err))
app_id, agent_id, snap_id, draft_id = line[0].split("|")
return app_id, agent_id, snap_id, draft_id
def resolve_tool_meta(d: Dify) -> dict:
# provider_id 必须是 tool_api_providers.id(UUID),不能用 name
out, err = d.sh(
"docker exec %s psql -U postgres -d dify -t -A -c "
"\"SELECT id FROM tool_api_providers WHERE name='%s' LIMIT 1;\""
% (C.PG_CONTAINER, C.SERVER_STATUS_PROVIDER)
)
provider_uuid = (out or "").strip()
if not provider_uuid:
raise RuntimeError("找不到 tool_api_providers.name=%s: %s" % (C.SERVER_STATUS_PROVIDER, err))
r = d.get(
"/workspaces/current/tool-provider/api/tools?provider=%s"
% C.SERVER_STATUS_PROVIDER
)
pp("api tools meta", r, limit=2500)
data = r.get("json") or []
if isinstance(data, dict):
data = data.get("data") or data.get("tools") or [data]
if not data:
raise RuntimeError("未找到 provider=%s 的工具列表" % C.SERVER_STATUS_PROVIDER)
tool = data[0]
tool_name = tool.get("name") or tool.get("tool_name") or "getServerStatus"
return {
"enabled": True,
"provider_type": "api",
"provider_id": provider_uuid,
"tool_name": tool_name,
"credential_type": "unauthorized",
"credential_ref": None,
"runtime_parameters": {},
}
def patch_cfg(cfg: dict, tool_entry: dict) -> dict:
prompt = cfg.setdefault("prompt", {})
if prompt is None:
cfg["prompt"] = {}
prompt = cfg["prompt"]
prompt["system_prompt"] = SYSTEM_PROMPT
tools = cfg.get("tools")
if not isinstance(tools, dict):
tools = {}
cfg["tools"] = tools
tools["cli_tools"] = tools.get("cli_tools") or []
tools["dify_tools"] = [tool_entry]
model = cfg.get("model")
if not isinstance(model, dict):
model = {}
cfg["model"] = model
model["model"] = C.QWEN_MODEL
model["plugin_id"] = "langgenius/openai"
model["model_provider"] = C.OPENAI_PROVIDER
model["credential_ref"] = None
ms = model.get("model_settings")
if not isinstance(ms, dict):
ms = {}
model["model_settings"] = ms
ms["max_tokens"] = ms.get("max_tokens") or 4096
for k in ("stop", "top_p", "temperature", "response_format", "presence_penalty", "frequency_penalty"):
ms.setdefault(k, None)
return cfg
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
app_id, agent_id, snap_id, draft_id = find_ids(d)
print("app_id", app_id)
print("agent_id", agent_id)
print("snap_id", snap_id)
print("draft_id", draft_id)
tool_entry = resolve_tool_meta(d)
print("tool_entry", json.dumps(tool_entry, ensure_ascii=False))
snap = AC.load_cfg(d, "agent_config_snapshots", snap_id)
draft = AC.load_cfg(d, "agent_config_drafts", draft_id)
AC.save_cfg(d, "agent_config_snapshots", snap_id, patch_cfg(snap, tool_entry), "hw1s")
AC.save_cfg(d, "agent_config_drafts", draft_id, patch_cfg(draft, tool_entry), "hw1d")
# 写回 _config 提示(不自动改文件,打印给人工确认)
print("\n请把以下常量写入 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/_config.py(若尚未写):")
print('HOMEWORK1_APP_ID = "%s"' % app_id)
print('HOMEWORK1_AGENT_ID = "%s"' % agent_id)
print('HOMEWORK1_SNAP_ID = "%s"' % snap_id)
print('HOMEWORK1_DRAFT_ID = "%s"' % draft_id)
print("\n[WARN] 改完不要点「构建/发布」;强刷后新开对话验收。")
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())UI 等价(部分):
- 进入 运维巡检 Agent → 工具 → 添加
getServerStatus - 配置 → 写 System Prompt(与脚本中
SYSTEM_PROMPT一致) - 选模型
qwen3.6-27b - 不要点发布;强刷页面
3.5 步骤 5 · 对话验收
理论
验收必须 新开对话(conversation_id="")。失败会话 status=error 上继续问会干扰结果。
操作
完整程序:/opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_05_对话验收.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_05_对话验收.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""作业 1 · 步骤 5:对话验收 —— 问 gpu-server-01 状态,应触发工具并返回指标。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
def resolve_agent_id(d: Dify) -> str:
aid = getattr(C, "HOMEWORK1_AGENT_ID", "") or ""
if aid:
return aid
sql = (
"SELECT a.id FROM apps ap JOIN agents a ON a.app_id=ap.id "
"WHERE ap.name='%s' ORDER BY ap.created_at DESC LIMIT 1;"
% C.HOMEWORK1_APP_NAME.replace("'", "''")
)
out, err = d.sh(
'docker exec %s psql -U postgres -d dify -t -A -c "%s"'
% (C.PG_CONTAINER, sql)
)
aid = (out or "").strip()
if not aid:
raise RuntimeError("找不到 Agent: %s %s" % (out, err))
return aid
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
agent_id = resolve_agent_id(d)
print("agent_id", agent_id)
r = d.post(
"/agent/%s/chat-messages" % agent_id,
body={
"inputs": {},
"query": C.HOMEWORK1_PROBE_QUERY,
"response_mode": "streaming",
"files": [],
"conversation_id": "",
},
extra="-m 420 --max-time 420",
t=450,
)
pp("chat", r, limit=4000)
text = r.get("text") or ""
ok_markers = ("45%", "62%", "78%", "18.2", "gpu-server-01", "getServerStatus")
hits = [m for m in ok_markers if m in text]
print("\n匹配到的验收关键词:", hits)
if r.get("code") not in ("200",) or len(hits) < 2:
print("\n[FAIL] 未看到工具返回的模拟指标,请检查工具挂载与 SSRF 出网。")
return 1
print("\n[PASS] Agent 已调用工具并返回状态信息。")
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())UI 手工验收
- 打开
http://192.168.110.207→ 运维巡检 Agent - 新开对话
- 发送:
帮我看看 gpu-server-01 的状态 - 期望回答:
服务器:gpu-server-01
CPU:45%
内存:62%
GPU 利用率:78%
GPU 显存:18.2/24 GB第四部分 CPU 与 GPU 在本作业中的分工(当前 + 未来)
本作业涉及两类算力:207 CPU 跑模拟 API;116 GPU 跑 Qwen 工具调用推理。
即使你现在只有 CPU 机做 Dify,LLM 仍在 GPU 机;将来有 GPU 资源时,下面「未来路径」可直接照做。
4.1 当前架构(实录)
| 组件 | 机器 | 算力 | 端口 | 作用 |
|---|---|---|---|---|
| FastAPI 模拟 API | 207 | CPU | 8777 | 返回假监控数据 |
| Dify 全家桶 | 207 | CPU + 内存 | 80/443 | Agent 编排、调工具 |
| vLLM Qwen3.6-27B | 116 | GPU H20 | 8000 | 决定是否调工具、格式化回答 |
| Langfuse(可选) | 207 | CPU | 3000 | Trace 观测 |
数据流算力分布:
用户问句 ──► 207 Dify ──► 116 GPU(2 次 LLM)──► 207 CPU(1 次 HTTP GET)──► 116 GPU(汇总回答)瓶颈通常在 116 vLLM 排队(max-num-seqs=8),不在 FastAPI(毫秒级)。
4.2 CPU 路径 · 模拟 API 与 Dify(当前推荐)
| 项 | 建议 |
|---|---|
| 部署位置 | 207 本机 /opt/server_status_api |
| 端口 | 8777(8765 已被「办公 IP 同步控制台」占用) |
| 进程 | uvicorn + nohup,PID 写 /tmp/server_status_api.pid |
| 依赖 | pip3 install fastapi uvicorn(207 默认无 fastapi) |
| 资源 | 单进程 <100MB 内存;无 GPU 需求 |
| 扩展 | 真实监控对接时,仍放 207 或内网 API 网关;保持 Dify 能 curl 到 |
CPU 路径注意:
- 不要用
127.0.0.1写进 OpenAPIservers.url,Dify 容器访问不到。 - 用宿主机 IP
192.168.110.207:8777。 - 防火墙需放行 8777(仅内网)。
4.3 GPU 路径 · LLM 工具调用(116 vLLM)
工具调用必须在支持 function calling 的 LLM 上跑。本环境 Qwen3.6-27B 在 116 GPU。
| 参数 | 当前值 | 说明 |
|---|---|---|
--tool-call-parser | qwen3_xml | 用 qwen3 会 KeyError |
--enable-auto-tool-choice | 开 | 允许模型自选工具 |
--reasoning-parser | qwen3 | thinking 计入 max_tokens |
--max-num-seqs | 8 | 超过则排队,工具 Agent 也慢 |
--gpu-memory-utilization | 0.75 | 与 mxh 业务共享 H20 |
验收工具调用时的 GPU 检查:
# 116 上
nvidia-smi
curl -s -H "Authorization: Bearer 123456" http://127.0.0.1:8000/v1/models
# 207 上测跨机
curl -s -H "Authorization: Bearer 123456" http://116.62.10.183:8000/v1/models带 tools 的 smoke(专题五):
curl -s -X POST http://116.62.10.183:8000/v1/chat/completions \
-H "Authorization: Bearer 123456" -H "Content-Type: application/json" \
-d '{"model":"qwen3.6-27b","messages":[{"role":"user","content":"hi"}],
"tools":[{"type":"function","function":{"name":"getServerStatus",
"parameters":{"type":"object","properties":{"server_name":{"type":"string"}},"required":["server_name"]}}}],
"tool_choice":"auto"}'4.4 未来有 GPU 资源时 · 可选演进
| 场景 | CPU 现状 | 有 GPU 后可选 |
|---|---|---|
| 模拟 API | 207 CPU 足够 | 可不变;或迁 K8s / 容器 |
| LLM 工具调用 | 116 GPU 已满足 | 加 max-num-seqs 或第二副本 |
| 真实 GPU 监控 API | 返回假 gpu_util | 对接 Prometheus / DCGM,仍走 OpenAPI 工具 |
| Embedding/Rerank | 本作业不涉及 | 若同一 Agent 又要知识库又要工具,Rerank 建议 GPU(专题十三) |
未来在同一 Agent 上叠加知识库 + HTTP 工具:
- 快照
knowledge.sets绑库 +tools.dify_tools挂 API 工具(可同时存在)。 - Prompt 写清优先级:「查制度用知识库,查机器状态用 getServerStatus」。
max_tokens建议 8192–16384(比纯知识库小,因 Prompt 更短)。- GPU 并发按 两次 LLM + 可能一次检索 规划,见专题十四容量表。
4.5 本作业资源对照小结
| 问题 | CPU 相关 | GPU 相关 |
|---|---|---|
| 工具不调 | Prompt / 快照未挂工具 | vLLM parser 错 / 模型未配 |
| 工具调了超时 | 8777 不通 / SSRF | 通常无关 GPU |
| 回答很慢 | FastAPI 不是瓶颈 | vLLM 排队或 thinking 长 |
| 400 Internal | 快照 model 空 | 少见 |
第五部分 完整代码程序
以下均为完整可运行程序,不是片段。索引见步骤 3.1~3.5 嵌入块。
| 文件 | 用途 |
|---|---|
/opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_server_status_api.py | FastAPI 服务 |
scripts_bash_cursor/作业1_带工具调用的Agent_服务器状态/作业1_启动_server_status.sh | 207 启动脚本 |
patches_cursor/作业1_带工具调用的Agent_服务器状态/作业1_server_status_openapi.yaml | OpenAPI Schema |
/opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_01_部署模拟服务.py | 部署 + 探针 |
/opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_02_注册OpenAPI工具.py | 注册 Dify 工具 |
/opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_03_创建Agent空壳.py | POST http://127.0.0.1/console/api/agent |
/opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_04_查ID并挂工具.py | 快照挂工具 |
/opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_05_对话验收.py | 自动验收 |
/opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_06_修复配置页InternalServerError.py | 配置页红条:API 工具 UUID 被当成 builtin |
环境常量(HOMEWORK1_*、SERVER_STATUS_*)在 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/_config.py。
第六部分 本次实战问题与解决方案
以下均为 207 环境真实踩坑,按发生顺序 + 速查表。
6.1 问题速查表
| # | 现象 | 根因 | 解决方案 |
|---|---|---|---|
| 1 | POST http://127.0.0.1/console/api/apps + "mode":"agent" 返回 400 | 1.16 创建口改为 /agent | 用 作业1_03,完整 curl 见第三部分「创建空壳」 |
| 2 | invalid input syntax for type uuid: "server_status" | tools.dify_tools[].provider_id 写了名称 | 查 SELECT id FROM tool_api_providers WHERE name='server_status',写 UUID |
| 3 | 对话 completion_request_error / Internal 500 | 同上 | 重跑 作业1_04 |
| 4 | AttributeError: 'NoneType' model | 新空壳快照 model 为 null | 作业1_04 写入 qwen3.6-27b + max_tokens |
| 5 | 8777 起不来 / 返回 HTML | 8765 已被其他 Python 服务占用 | 本作业改用 8777;OpenAPI servers.url 同步改 |
| 6 | 207 无 fastapi | 宿主机未装 | pip3 install fastapi uvicorn(启动脚本已含) |
| 7 | psql 按中文名查 Agent 失败 | Windows → SSH 编码乱码 | 用 GET http://127.0.0.1/console/api/agent?page=1&limit=50,勿拼中文进 psql |
| 8 | 工具注册后列表空 | 未登录 / 权限 | 确认 dify_api.py login 200 |
| 9 | 模型不调工具 | system_prompt 太弱或未挂工具 | 检查快照 tools.dify_tools 与 Prompt |
| 10 | 工具调了但 observation 空 | 8777 从 Dify 容器不可达 | 确认 ssrf_proxy、防火墙、servers.url 用宿主机 IP |
| 11 | 改完 UI 又失效 | 点了「发布」 | 不要发布;重跑步骤 4,新开对话 |
| 12 | vLLM 无 tool_calls | parser 配置错 | --tool-call-parser qwen3_xml |
| 13 | 打开 Agent 配置页红条 Internal Server Error,对话验收仍通过 | 配置页把 OpenAPI 工具 UUID 当成 builtin 插件 去拉凭据 | 跑 作业1_06_修复配置页InternalServerError.py,硬刷新配置页 |
6.2 问题 1 · POST /apps 被拒(详细)
现象:
{"code":"invalid_param","message":"... mode ... Input should be 'chat', 'agent-chat' ..."}根因:Dify 1.16 新 Agent 走 Roster:POST http://127.0.0.1/console/api/agent(见 roster.py AgentAppListApi.post)。
解决:作业1_03_创建Agent空壳.py 已改为 POST http://127.0.0.1/console/api/agent。不要用专题十五旧脚本里的 POST http://127.0.0.1/console/api/apps + "mode":"agent" 做本作业。
6.3 问题 2 · provider_id UUID(详细)
现象(api 日志):
invalid input syntax for type uuid: "server_status"
WHERE tool_api_providers.id = 'server_status'::UUID根因:Agent 快照里 provider_id 误填了 OpenAPI provider 名称。运行时 Dify 按 UUID 查 tool_api_providers 表。
解决:
SELECT id, name FROM tool_api_providers WHERE name='server_status';
-- 021a4445-098d-4821-81fe-ff0ebc660ecc写入快照:
"provider_id": "021a4445-098d-4821-81fe-ff0ebc660ecc"作业1_04 已自动查 UUID 并写入。
6.4 问题 3 · 空壳 model 为 null(详细)
现象:挂工具后对话仍 400 Internal Server Error。
根因:POST /agent 创建的空壳 config_snapshot.model 可能为 null,工具层在拉 LLM 前即失败。
解决:patch_cfg() 强制写入:
model["model"] = "qwen3.6-27b"
model["model_provider"] = "langgenius/openai/openai"
model["model_settings"]["max_tokens"] = 40966.5 问题 4 · 端口 8765 冲突(详细)
现象:/server/status 返回 404 HTML,非 JSON。
根因:8765 上跑的是「办公 IP 同步控制台」,不是本作业 API。
解决:统一改用 8777;_config.py 的 SERVER_STATUS_PORT、openapi.yaml 的 servers.url、防火墙规则三者一致。
6.6 验收通过记录(便于对照)
命令:/usr/bin/python3 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_05_对话验收.py
结果摘要:
- HTTP 200,流式 SSE
tool=getServerStatus,tool_input={"server_name": "gpu-server-01"}- observation 含完整 JSON
- 最终 answer 含
45%、62%、78%、18.2/24 GB - 脚本输出:
[PASS] Agent 已调用工具并返回状态信息。
6.7 问题 13 · 打开配置页 Internal Server Error(对话仍正常)
现象:问「gpu-server-01 的状态」能调工具并答对;一进 配置 页,右上角弹出一条或多条红色 Internal Server Error。
不是:CPU/GPU 资源不足。vLLM 正常、8777 正常、messages.status=normal。
根因:Dify 1.16 新 Agent 配置页会请求:
curl -s -b /tmp/dify_cj.txt -H "X-CSRF-Token: $CSRF" \
'http://127.0.0.1/console/api/workspaces/current/tool-provider/builtin/021a4445-098d-4821-81fe-ff0ebc660ecc/credential/info'本作业工具是 OpenAPI(api),provider_id 是 UUID 021a4445-098d-4821-81fe-ff0ebc660ecc。配置页把它当成 builtin 插件 ID,plugin_daemon 返回 PluginNotFoundError,接口 500,前端弹出红条。
解决(已在 207 打上):
/usr/bin/python3 /opt/workbuddy/scripts_cursor/作业1_带工具调用的Agent_服务器状态/作业1_06_修复配置页InternalServerError.py该脚本从镜像恢复被误改的文件后,只给 get_builtin_tool_provider_credential_info 加兜底:插件不存在时返回空凭据 JSON(HTTP 200)。
你现在要做:浏览器 强制刷新(Ctrl+F5)再打开「运维巡检 Agent」配置页,红条应消失。对话能力不变。
完整专题(原因、步骤、修改前/后完整代码):Dify_专题十六_新Agent配置页InternalServerError_cursor.md / .html
注意:重建 docker-api-1 容器后补丁会丢,需再跑一遍 作业1_06。
第七部分 对本系统的专业建议
7.1 架构
- 模拟 API 与 Dify 同机(207):内网延迟低,ssrf 出网简单;生产换真实监控 API 时只改 OpenAPI URL,Agent 逻辑不变。
- 工具 Provider 工作区级复用:
server_status注册一次,多个 Agent 可挂同一工具;新 Agent 仍要注意 UUID 写入快照。 - 与知识库 Agent 分离:本作业「运维巡检 Agent」与「行政问答 Agent」分开,避免 Prompt 互相干扰;成熟后可合并为一个 Agent(见 §4.4)。
7.2 运维
- 8777 进程纳入巡检:
curl /health;重启 207 后执行bash /opt/server_status_api/start.sh。 - Xinference / vLLM 重启不影响本作业 HTTP 工具,但会影响若同一 Agent 挂了知识库的情况。
- 验收永远新开对话;自动化用
conversation_id=""。 - 改快照后禁止点发布;与专题十一一致。
7.3 安全
- 模拟 API 无鉴权 → 仅内网;上生产必须加 API Key 或 mTLS,并在 Dify 工具 credentials 配置。
- OpenAPI 不要指向内网敏感接口(如 K8s API、数据库)除非有鉴权与 SSRF 白名单。
_config.py含控制台密码,勿泄露。
7.4 培训与扩展
| 下一步 | 建议 |
|---|---|
| 作业 2 | 再加一个 HTTP 工具(如重启服务),练多工具选择 |
| 工作流 | 工具 + 条件分支(Dify Workflow) |
| 真实监控 | FastAPI 改读 Prometheus,字段保持兼容 |
| GPU | 优先保证 vLLM parser;模拟 API 仍 CPU 即可 |
第八部分 验收清单
8.1 部署侧
- [ ]
curl -s http://192.168.110.207:8777/health→{"status":"ok"} - [ ]
curl -s 'http://192.168.110.207:8777/server/status?server_name=gpu-server-01'→ 含45%等字段 - [ ] Dify 工具列表有
server_status/getServerStatus - [ ]
tool_api_providers有 UUID 行
8.2 Agent 配置侧
- [ ] Agent 名「运维巡检 Agent」,
POST http://127.0.0.1/console/api/agent创建(完整 curl 见第三部分) - [ ] 快照
tools.dify_tools[0].provider_id为 UUID - [ ] 快照
model.model=qwen3.6-27b - [ ] system_prompt 要求调 getServerStatus
- [ ] 未点发布
8.3 对话侧
- [ ] 新开对话问:「帮我看看 gpu-server-01 的状态」
- [ ] 界面出现工具调用
- [ ] 回答含 CPU / 内存 / GPU 四项
- [ ]
作业1_05_对话验收.py输出[PASS]
8.4 GPU 侧(116)
- [ ] vLLM
/v1/models通 - [ ] 启动参数含
qwen3_xmlparser - [ ] 工具型对话时
nvidia-smi有推理进程
关联文档
| 文档 | 何时看 |
|---|---|
| 专题十五 | 新 Agent 通用创建 |
| 专题四 / 九 | vLLM 工具 parser |
| 专题十一 | 勿点发布 |
| 专题十四 | vLLM 排队变慢 |
| 内部智能体配置文档专题汇总 | 全局总纲 |
*作业 1 已于 207 环境跑通验收;变更端口或 UUID 后请同步 _config.py 与 OpenAPI yaml。*