#!/usr/bin/env bash # 患者模拟定时调度脚本 # 用途:按 cron 表达式定时触发患者模拟任务,支持批量患者数量和场景组合。 # 使用方式: # bash scheduler.sh # 单次运行(使用默认配置) # bash scheduler.sh --config # 指定配置文件 # bash scheduler.sh --dry-run # 仅打印即将执行的计划,不实际运行 set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SKILL_DIR="$(dirname "$SCRIPT_DIR")" CONFIG_FILE="$SKILL_DIR/examples/schedule-config.yaml" DRY_RUN=false LOG_DIR="$SKILL_DIR/examples/logs" TIMESTAMP=$(date +%Y%m%d-%H%M%S) # ============================================================ # 解析命令行参数 # ============================================================ while [[ $# -gt 0 ]]; do case "$1" in --config) CONFIG_FILE="$2" shift 2 ;; --dry-run) DRY_RUN=true shift ;; *) echo "未知参数: $1" echo "用法: bash scheduler.sh [--config ] [--dry-run]" exit 1 ;; esac done # ============================================================ # 加载配置文件 # ============================================================ if [[ ! -f "$CONFIG_FILE" ]]; then echo "[ERROR] 配置文件不存在: $CONFIG_FILE" echo "请先在 examples/ 目录下创建 schedule-config.yaml" exit 1 fi # 从 YAML 读取配置(简化解析,仅支持单层) read_yaml_value() { local key="$1" grep "^${key}:" "$CONFIG_FILE" | head -1 | sed "s/^${key}:[[:space:]]*//" | tr -d '"' } # ============================================================ # 读取配置 # ============================================================ PROFILE_COUNT=$(read_yaml_value "patient_count" || echo "5") SCENARIOS=$(read_yaml_value "scenarios" || echo "all") TIME_SLOT=$(read_yaml_value "time_slot" || echo "morning") CRON_EXPR=$(read_yaml_value "cron" || echo "") TARGET_AI_ENDPOINT=$(read_yaml_value "target_ai_endpoint" || echo "") echo "========================================" echo " 患者模拟定时调度" echo " 时间: $(date '+%Y-%m-%d %H:%M:%S')" echo "========================================" echo " 配置: $CONFIG_FILE" echo " 患者数量: $PROFILE_COUNT" echo " 场景: $SCENARIOS" echo " 时段: $TIME_SLOT" echo " 目标 AI: $TARGET_AI_ENDPOINT" echo "========================================" if [[ "$DRY_RUN" == true ]]; then echo "" echo "[DRY RUN] 以下是将要执行的模拟计划(不会实际运行):" echo "" fi # ============================================================ # 安装 cron(如配置了 cron 表达式) # ============================================================ install_cron() { if [[ -z "$CRON_EXPR" ]]; then return 0 fi # ⚠️ 此操作会修改当前用户的 crontab。 # 仅添加标记为 "# patient-roleplay-scheduler" 的条目, # 不会影响其他定时任务。可通过 crontab -e 手动移除。 local CRON_CMD="$CRON_EXPR cd $SCRIPT_DIR && bash scheduler.sh --config $CONFIG_FILE >> $LOG_DIR/cron-\$(date +\%Y\%m\%d).log 2>&1" local CRON_MARKER="# patient-roleplay-scheduler" # 删除旧条目 if crontab -l 2>/dev/null | grep -q "$CRON_MARKER"; then crontab -l 2>/dev/null | grep -v "$CRON_MARKER" | crontab - fi # 添加新条目 (crontab -l 2>/dev/null || true; echo "$CRON_CMD $CRON_MARKER") | crontab - echo "[CRON] 已安装定时任务: $CRON_EXPR" } # ============================================================ # 按真实患者就诊时间分布调整请求间隔 # ============================================================ get_interval_ms() { case "$TIME_SLOT" in morning) # 上午 8-11 点就诊高峰:间隔 3-8 分钟 echo $((3000 + RANDOM % 5000)) ;; afternoon) # 下午 2-5 点分散就诊:间隔 8-15 分钟 echo $((8000 + RANDOM % 7000)) ;; evening) # 晚间较少就诊:间隔 20-30 分钟 echo $((20000 + RANDOM % 10000)) ;; *) echo 5000 ;; esac } # ============================================================ # 执行单次患者模拟(占位——实际由 AI 代理执行) # ============================================================ run_single_simulation() { local profile_id="$1" local scenario_id="$2" local index="$3" local INTERVAL_MS INTERVAL_MS=$(get_interval_ms) echo "[$(date '+%H:%M:%S')] 患者 #$index | 画像: $profile_id | 场景: $scenario_id" if [[ "$DRY_RUN" == true ]]; then echo " → (dry-run) 将等待 ${INTERVAL_MS}ms 后开始对话" return 0 fi # 实际模拟逻辑由 AI 代理在加载本技能后执行, # 此处仅记录模拟启动事件。 # AI 代理应在此处被调用,传入 profile_id 和 scenario_id。 mkdir -p "$LOG_DIR" echo "$(date -Iseconds) | $index | $profile_id | $scenario_id | started" >> "$LOG_DIR/sessions-$TIMESTAMP.log" # 模拟真实就诊间隔 sleep "$((INTERVAL_MS / 1000))" 2>/dev/null || true } # ============================================================ # 主流程 # ============================================================ main() { mkdir -p "$LOG_DIR" # 安装 cron(如需) install_cron if [[ "$DRY_RUN" == true ]]; then echo "模拟计划:共 $PROFILE_COUNT 名患者,场景范围:$SCENARIOS" echo "时段分布参考:$TIME_SLOT" echo "每次会话间隔:$(get_interval_ms)ms(在真实运行中会随机变化)" return 0 fi # 注:以下循环为调度框架。 # 实际的对话生成和 AI 交互由加载本技能后的 AI 代理执行。 for i in $(seq 1 "$PROFILE_COUNT"); do # 从场景池中随机选取一个场景 SCENARIO_ID="scenario-auto-$((RANDOM % 5 + 1))" PROFILE_ID="profile-auto-$((RANDOM % 5 + 1))" run_single_simulation "$PROFILE_ID" "$SCENARIO_ID" "$i" done echo "" echo "[完成] 共计划模拟 $PROFILE_COUNT 名患者" echo "日志目录: $LOG_DIR" } main