187 lines
6.2 KiB
Bash
Executable File
187 lines
6.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 患者模拟定时调度脚本
|
||
# 用途:按 cron 表达式定时触发患者模拟任务,支持批量患者数量和场景组合。
|
||
# 使用方式:
|
||
# bash scheduler.sh # 单次运行(使用默认配置)
|
||
# bash scheduler.sh --config <file> # 指定配置文件
|
||
# 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 <file>] [--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 |