180 lines
6.1 KiB
Python
180 lines
6.1 KiB
Python
|
|
"""导入产品推荐 Workflow 到 Dify/BaoDan。
|
|||
|
|
|
|||
|
|
使用方式:
|
|||
|
|
python scripts/import_workflow.py [--base-url URL] [--token TOKEN]
|
|||
|
|
|
|||
|
|
参数:
|
|||
|
|
--base-url Dify API 地址(默认 http://localhost:5001)
|
|||
|
|
--token Dify Console API Token(登录后从浏览器 Cookie 或 API 管理页面获取)
|
|||
|
|
|
|||
|
|
导入完成后,还需要在 Dify 后台:
|
|||
|
|
1. 打开导入的"产品推荐方案生成" Workflow
|
|||
|
|
2. 绑定知识库(节点3:知识库检索 → 选择已创建的知识库)
|
|||
|
|
3. 确认 LLM 模型配置(节点4:方案生成 → 选择 DeepSeek)
|
|||
|
|
4. 点击"发布"
|
|||
|
|
5. 在"访问 API"页面创建 API Key,填入 .env 的 BAODAN_WORKFLOW_API_KEY
|
|||
|
|
"""
|
|||
|
|
import argparse
|
|||
|
|
import json
|
|||
|
|
import sys
|
|||
|
|
from pathlib import Path
|
|||
|
|
|
|||
|
|
import requests
|
|||
|
|
|
|||
|
|
|
|||
|
|
def find_existing_app(base_url: str, token: str) -> str | None:
|
|||
|
|
"""查找已存在的同名 Workflow 应用。"""
|
|||
|
|
headers = {
|
|||
|
|
"Authorization": f"Bearer {token}",
|
|||
|
|
"Content-Type": "application/json",
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
page = 1
|
|||
|
|
while True:
|
|||
|
|
resp = requests.get(
|
|||
|
|
f"{base_url}/console/api/apps",
|
|||
|
|
params={"page": page, "limit": 100},
|
|||
|
|
headers=headers,
|
|||
|
|
timeout=30
|
|||
|
|
)
|
|||
|
|
if resp.status_code != 200:
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
data = resp.json()
|
|||
|
|
apps = data.get("data", [])
|
|||
|
|
if not apps:
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
for app in apps:
|
|||
|
|
if app.get("name") == "产品推荐方案生成" and app.get("mode") == "workflow":
|
|||
|
|
return app.get("id")
|
|||
|
|
|
|||
|
|
if len(apps) < 100:
|
|||
|
|
break
|
|||
|
|
page += 1
|
|||
|
|
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
|
|||
|
|
def import_workflow(base_url: str, token: str, yaml_path: str) -> dict:
|
|||
|
|
"""通过 Dify Console API 导入 Workflow DSL(如果不存在则复用)。"""
|
|||
|
|
headers = {
|
|||
|
|
"Authorization": f"Bearer {token}",
|
|||
|
|
"Content-Type": "application/json",
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 先检查是否已存在同名应用
|
|||
|
|
existing_app_id = find_existing_app(base_url, token)
|
|||
|
|
if existing_app_id:
|
|||
|
|
print(f"♻️ 复用已存在的应用,App ID: {existing_app_id}")
|
|||
|
|
return {"app_id": existing_app_id, "status": "reused"}
|
|||
|
|
|
|||
|
|
url = f"{base_url}/console/api/apps/imports"
|
|||
|
|
|
|||
|
|
with open(yaml_path, "r", encoding="utf-8") as f:
|
|||
|
|
yaml_content = f.read()
|
|||
|
|
|
|||
|
|
payload = {
|
|||
|
|
"mode": "yaml-content",
|
|||
|
|
"yaml_content": yaml_content,
|
|||
|
|
"name": "产品推荐方案生成",
|
|||
|
|
"description": "根据客户信息和知识库检索结果,生成基础/均衡/全面三套保险推荐方案",
|
|||
|
|
"icon_type": "emoji",
|
|||
|
|
"icon": "\U0001F3E6",
|
|||
|
|
"icon_background": "#E4FBCC",
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
print(f"正在导入 Workflow 到 {base_url} ...")
|
|||
|
|
resp = requests.post(url, json=payload, headers=headers, timeout=30)
|
|||
|
|
result = resp.json()
|
|||
|
|
|
|||
|
|
if resp.status_code == 200:
|
|||
|
|
print(f"✓ 导入成功!")
|
|||
|
|
print(f" App ID: {result.get('app_id', 'N/A')}")
|
|||
|
|
print(f" App Mode: {result.get('app_mode', 'N/A')}")
|
|||
|
|
print(f" Status: {result.get('status', 'N/A')}")
|
|||
|
|
return result
|
|||
|
|
elif resp.status_code == 202:
|
|||
|
|
# 版本不匹配,需要确认
|
|||
|
|
import_id = result.get("id")
|
|||
|
|
print(f"⚠ 需要确认导入(版本不匹配)")
|
|||
|
|
print(f" Import ID: {import_id}")
|
|||
|
|
print(f" 当前版本: {result.get('current_dsl_version')}")
|
|||
|
|
print(f" 导入版本: {result.get('imported_dsl_version')}")
|
|||
|
|
|
|||
|
|
confirm_url = f"{base_url}/console/api/apps/imports/{import_id}/confirm"
|
|||
|
|
confirm_resp = requests.post(confirm_url, headers=headers, timeout=30)
|
|||
|
|
confirm_result = confirm_resp.json()
|
|||
|
|
|
|||
|
|
if confirm_resp.status_code == 200:
|
|||
|
|
print(f"✓ 确认导入成功!")
|
|||
|
|
print(f" App ID: {confirm_result.get('app_id', 'N/A')}")
|
|||
|
|
return confirm_result
|
|||
|
|
else:
|
|||
|
|
print(f"✗ 确认失败: {confirm_result}")
|
|||
|
|
return confirm_result
|
|||
|
|
else:
|
|||
|
|
print(f"✗ 导入失败 (HTTP {resp.status_code})")
|
|||
|
|
print(f" 错误: {result.get('message', result)}")
|
|||
|
|
return result
|
|||
|
|
|
|||
|
|
|
|||
|
|
def get_console_token_interactive(base_url: str) -> str:
|
|||
|
|
"""交互式获取 Console Token。"""
|
|||
|
|
print()
|
|||
|
|
print("=" * 60)
|
|||
|
|
print(" 获取 Dify Console Token 的方法:")
|
|||
|
|
print("=" * 60)
|
|||
|
|
print()
|
|||
|
|
print(" 方法1:浏览器登录 Dify 后台 → F12 → Application → Cookies")
|
|||
|
|
print(" → 复制 dify_session 或 access_token 的值")
|
|||
|
|
print()
|
|||
|
|
print(" 方法2:使用 API 密钥管理页面生成的 Personal Access Token")
|
|||
|
|
print()
|
|||
|
|
token = input(" 请输入 Token: ").strip()
|
|||
|
|
return token
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
parser = argparse.ArgumentParser(description="导入产品推荐 Workflow 到 Dify/BaoDan")
|
|||
|
|
parser.add_argument("--base-url", default="http://localhost:5001", help="Dify API 地址")
|
|||
|
|
parser.add_argument("--token", default="", help="Console API Token")
|
|||
|
|
parser.add_argument("--yaml", default="deploy/workflow_recommend.yml", help="DSL YAML 文件路径")
|
|||
|
|
args = parser.parse_args()
|
|||
|
|
|
|||
|
|
# 确认 YAML 文件存在
|
|||
|
|
yaml_path = Path(args.yaml)
|
|||
|
|
if not yaml_path.exists():
|
|||
|
|
print(f"✗ 找不到 YAML 文件: {yaml_path}")
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
# 获取 Token
|
|||
|
|
token = args.token
|
|||
|
|
if not token:
|
|||
|
|
token = get_console_token_interactive(args.base_url)
|
|||
|
|
if not token:
|
|||
|
|
print("✗ 未提供 Token,退出")
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
# 导入
|
|||
|
|
result = import_workflow(args.base_url, token, str(yaml_path))
|
|||
|
|
|
|||
|
|
if result.get("app_id"):
|
|||
|
|
print()
|
|||
|
|
print("=" * 60)
|
|||
|
|
print(" 后续步骤:")
|
|||
|
|
print("=" * 60)
|
|||
|
|
print()
|
|||
|
|
print(f" 1. 打开 Dify 后台 → 找到「产品推荐方案生成」应用")
|
|||
|
|
print(f" 2. 进入 Workflow 编辑器 → 点击「知识库检索」节点")
|
|||
|
|
print(f" → 替换 dataset_ids 为实际知识库 ID")
|
|||
|
|
print(f" 3. 点击「方案生成」节点 → 确认模型为 deepseek-chat")
|
|||
|
|
print(f" 4. 点击右上角「发布」按钮")
|
|||
|
|
print(f" 5. 进入「访问 API」页面 → 创建 API Key")
|
|||
|
|
print(f" 6. 将 API Key 填入 .env 的 BAODAN_WORKFLOW_API_KEY")
|
|||
|
|
print()
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|