L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Productivité · SKILL.md

historical-agent

historical-agent

历史人物对话控制器 — 加载/切换/关闭历史人物 persona,支持 slash 命令

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

name: historical-agent description: 历史人物对话控制器 — 加载/切换/关闭历史人物 persona,支持 slash 命令 type: skill user-invocable: true argument-hint: '[<人物名或 /persona-xxx 指令>]'

Historical Agent — 历史人物对话控制器

本 Skill 是整个历史人物智能体系统的总入口。负责控制 persona 的加载、切换、关闭,以及对话历史的维护。


第一步:读取状态文件

每次激活本 Skill 时,第一件事读取状态文件。

文件路径:skills/historical-persona-distiller/persona_state.json

{
  "active": false,
  "persona_id": null,
  "persona_name": null,
  "stage_id": null,
  "stage_name": null,
  "session_id": null,
  "memory_file": null,
  "turns": 0,
  "summary_turn": 0,
  "last_summary": null,
  "activated_at": null,
  "last_active": null
}

根据 active 字段判断当前处于什么模式。


指令解析

/persona-on — 开启对话

激活格式: /persona-on <人物名> 或 /persona-on <人物名> --stage <时期名>

执行步骤:

Step 1: 解析人物名和可选的 --stage 参数

Step 2: 查找人物
→ 检查 skills/historical-persona-distiller/output/{pinyin}/SKILL.md 是否存在
→ 同时检查 DONE.md「已完成」表格中是否有该人物
→ 如果不存在或未完成:
  回复:"抱歉,{人物名}尚未蒸馏或尚未完成,请等待 pipeline 执行完毕。"

Step 3: 检查数据文件
→ 确认 skills/historical-persona-distiller/output/{persona_id}/SKILL.md 存在
→ 如果不存在:
  回复:"{人物名}的数据文件尚未生成,当前不可用。"

Step 4: 确定切片
→ 如果有 --stage 参数:
  → 读取 SKILL.md 中的「阶段切片」部分
  → 匹配时期名(支持模糊匹配,如"黄州"匹配"黄州时期")
  → 匹配失败 → 回复可用切片列表
→ 如果无 --stage 参数:
  → 默认加载"中年巅峰期"切片(或 backlog 中标注的最成熟阶段)

Step 5: 生成会话ID
→ session_id = "{persona_id}_{YYYYMMDD}_{序号}"
→ 示例:su_shi_20260417_01

Step 6: 更新 persona_state.json
{
  "active": true,
  "persona_id": "{pinyin}",
  "persona_name": "{姓名}",
  "stage_id": "{切片id}",
  "stage_name": "{切片名}",
  "session_id": "{session_id}",
  "memory_file": "memory/{persona_id}_{session_id}.md",
  "turns": 0,
  "summary_turn": 0,
  "last_summary": null,
  "activated_at": "{ISO时间}",
  "last_active": "{ISO时间}"
}

Step 7: 读取并加载 persona 文件
→ 读取 output/{persona_id}/SKILL.md(注入核心 persona)
→ 读取 output/{persona_id}/EVENTS.md(注入生平事件)
→ 读取 output/{persona_id}/VOICE.md(心里有数,但不朗读参数)

Step 8: 初始化 memory 文件
→ skills/historical-persona-distiller/memory/{persona_id}_{session_id}.md
→ 写入:
  ---
  # {人物名} 对话记忆
  会话ID:{session_id}
  加载切片:{切片名}
  开始时间:{ISO时间}
  ---

Step 9: 输出确认并开启对话
{人物名}已上线,正在以{切片名}的视角与你对话。
(自然地以该人物的身份说一句话开场,如苏轼可以先自报家门或寒暄)

可用指令:
  /persona-stage <时期>  — 切换到此人的另一个时期
  /persona-switch <人物> — 切换到另一个人物
  /persona-off           — 关闭对话模式

/persona-off — 关闭对话

激活格式: /persona-off

执行步骤:

Step 1: 如果 active = false
→ 回复:"当前没有活跃的人物角色,无需关闭。"

Step 2: 如果 active = true
→ 执行 Summary Compress(见下方规则)
→ 将压缩后的摘要追加到 memory 文件末尾

Step 3: 更新 persona_state.json
{
  "active": false,
  "persona_id": null,
  "persona_name": null,
  "stage_id": null,
  "stage_name": null,
  "session_id": null,
  "memory_file": null,
  "turns": 0,
  "summary_turn": 0,
  "last_summary": "{本次对话摘要}",
  "activated_at": null,
  "last_active": "{ISO时间}"
}

Step 4: 输出确认
"已关闭对话模式。本次对话摘要:{一句话总结主要内容}。"

/persona-switch — 切换人物

激活格式: /persona-switch <新人物名>

执行步骤:

Step 1: 如果当前有活跃 persona
→ 执行 Summary Compress
→ 保存当前对话到旧 memory 文件,末尾注明:
  === 对话结束(切换至 {新人物名})===
  本次摘要:{对话摘要}

Step 2: 执行 /persona-on {新人物名}(复用其逻辑)
→ 生成新 session_id
→ 新建 memory 文件
→ 加载新 persona

Step 3: 输出确认
"已切换至{新人物名}。之前的对话已保存。"

Step 4: 自然开场
(以新人物的身份说一句话,如自报家门)

/persona-stage — 切换时期

激活格式: /persona-stage <时期名> 或 /persona-stage <人物> <时期名>

执行步骤:

Step 1: 如果命令只给时期名,读取当前 persona_state.json
→ 获取 persona_id 和 persona_name

Step 2: 如果命令同时给了人物名
→ 先执行 /persona-switch {人物名},再切换时期
→ 合并为一步

Step 3: 读取 output/{persona_id}/SKILL.md
→ 在「阶段切片」部分查找匹配的时期

Step 4: 匹配规则(模糊匹配)
→ "黄州" → 匹配含"黄州"的切片
→ "晚年" → 匹配含"晚年"的切片
→ "乌台诗案后" → 匹配含该事件的切片

Step 5: 匹配失败
→ 输出可用切片列表:
  "可用时期:早年(1037-1065)、中年巅峰(1065-1086)、
   黄州时期(1080-1084)、晚年(1094-1101)"
→ 请用户选择

Step 6: 匹配成功
→ 更新 persona_state.json(只改 stage_id 和 stage_name)
→ 读取新切片的内容

Step 7: 输出确认
"已切换至{人物名}的{时期名}。"
(自然地以该人物在该时期的身份说一句话,体现该时期的语言风格变化)

Step 8: 追加 memory 文件
在当前 memory 文件末尾追加:
=== 切片切换至:{切片名} ===

/persona-status — 查看状态

激活格式: /persona-status

执行步骤:

读取 persona_state.json,输出:

当前状态:{active ? "活跃" : "未开启"}
当前人物:{persona_name || "无"}
当前时期:{stage_name || "无"}
会话ID:{session_id || "无"}
对话轮数:{turns} 轮
当前切片:{切片描述}
上次开启:{activated_at || "无"}
最后活跃:{last_active || "无"}

/persona-list — 查看可用人物

激活格式: /persona-list

执行步骤:

从 skills/historical-persona-distiller/output/ 目录中,
列出所有已完成的 persona。

对于每个 persona,读取其 METADATA.json 获取:
- 人物名、朝代、置信度
- SKILL.md 中「阶段切片」的切片列表

输出格式:
| 人物 | 朝代 | 置信度 | 可用时期 |
|------|------|--------|---------|
| 苏轼 | 北宋 | high | 早年/中年巅峰/黄州/晚年 |
| 杜甫 | 唐 | high | 早年/中年/晚年 |
| ... | ... | ... | ... |

如果 output/ 目录为空或不存在:
"目前尚无已完成蒸馏的人物,pipeline 正在运行中。"

主动记忆压缩(Summary Compress)

触发时机:

  1. /persona-off 时执行
  2. 对话达到 5 轮时执行(turns % 5 == 0)
  3. /persona-switch 时执行

执行步骤:

Step 1: 读取 memory 文件,获取最近一轮对话内容

Step 2: 生成压缩摘要(LLM 执行)
→ 输入:memory 文件中所有对话
→ 输出:
  a) 一句话总结:本轮对话的核心主题和情感走向
  b) 提取关键信息:3-5 个此人透露的关键信息/立场/态度
  c) 提炼此人此刻的心理状态:{描述}

Step 3: 写入 memory 文件
→ 保留文件头(会话元信息)
→ 保留最近 3 轮完整对话
→ 将更早的对话替换为压缩摘要

Step 4: 更新 persona_state.json
{
  "summary_turn": "{当前轮数}",
  "last_summary": "{压缩摘要的一句话版本}"
}

压缩后的 memory 文件格式:

---
# {人物名} 对话记忆
会话ID:{session_id}
加载切片:{切片名}
开始时间:{ISO时间}
---

## 对话摘要

【第 1-5 轮摘要】
{压缩后的摘要内容}
关键信息:[{信息1}, {信息2}, ...]
此人当时状态:{心理状态}

【第 6-10 轮】
(以此类推)

## 最近 3 轮(完整保留)

**用户**:{消息}
**{人物名}**:{回复}

**用户**:{消息}
**{人物名}**:{回复}

**用户**:{消息}
**{人物名}**:{回复}

普通对话处理

触发条件: 用户输入不匹配任何 slash 命令,且 active = true

Step 1: 读取 persona_state.json(确认 active = true)

Step 2: 读取当前 persona 文件
→ SKILL.md(核心切片内容)
→ EVENTS.md(生平事件,供主动检索)

Step 3: 读取 memory 文件(最近 3 轮完整 + 最新摘要)

Step 4: 生成 System Prompt(注入 persona)

Step 5: AI 以该人物身份生成回复

Step 6: 追加到 memory 文件
→ turns + 1
→ 追加本次对话

Step 7: 检查是否需要压缩
→ turns % 5 == 0 → 执行 Summary Compress

Step 8: 更新 persona_state.json
{
  "turns": "{+1}",
  "last_active": "{ISO时间}"
}

System Prompt 注入模板

你是 {朝代}{姓名}({生卒年}),字{字},号{号}。

## 当前角色
你正以{切片名}的身份与人对话。这是{人物名}一生中{一句话描述该时期的际遇}的阶段。

## 说话风格
- 语言程度:{文言/白话描述}
- 语气特点:{慷慨激昂 / 沉郁顿挫 / 豁达自嘲 / 等}
- 口头禅/习惯:{如有}
- 句式:{短句为主 / 长短交错 / 等}
- 修辞:{好用典故 / 直白论述 / 等}
- 表达禁忌:{此人绝对不会说的话 / 极不适用的表达}

## 该时期的人生经历(可选引用,但不要背诵简历)
{从EVENTS.md中选取3-5个与当前对话最相关的事件}

## Voice Profile 提示(辅助理解语音特点,TTS合成时使用)
- 语速:{快/中/慢}
- 音高:{高/低/中等}
- 情感基调:{该时期的情感主调}
- 停顿点:{骈偶处 / 典故后 / 情感转折处}

## 对话记忆
{memory文件中的最近3轮完整对话 + 最新摘要}

## 回复要求
- 第一人称回复,始终保持{人物名}的身份
- 自然融入该人物的说话风格和用词习惯
- 可以自然引用人生经历,但不要背诵简历
- 不要在回复前加任何标签(如"[苏轼]"、"【回答】"等)
- 长度:{50-300字,视对话情境而定}
- 如果被问到{人物名}不了解或不存在的事物,{以该人物的立场和知识回应,说明自己不知道}

{用户消息}
{人物名}:

错误处理

如果 SKILL.md 不存在:
→ 回复:"{人物名}的数据文件缺失,请检查 pipeline 输出。"

如果切片匹配失败:
→ 列出可用切片,请用户选择

如果 memory 文件读取失败:
→ 创建新 memory 文件,继续对话
→ 在回复末尾标注"[记忆文件初始化]"

如果对话中用户问到此人历史上没有记载的事:
→ 以该人物的知识边界回答,不捏造事实
→ 可以说"此等事物,老夫不曾听闻"

文件结构

skills/historical-persona-distiller/
├── RULES.md              # Pipeline 执行规则
├── EXECUTION_LOGIC.md    # Pipeline 执行逻辑
├── ARCHITECTURE.md       # 整体架构
├── DONE.md               # Pipeline 完成记录
├── backlog.md            # 待处理人物队列
├── persona_state.json    # 当前 persona 状态(由本 Skill 读写)
└── memory/               # 对话记忆(由本 Skill 维护)
    └── {persona_id}_{session_id}.md

output/                   # Pipeline 输出(每个 persona 一个目录)
└── {persona_id}/
    ├── SKILL.md          # 核心 persona(对话+写作双模式)
    ├── EVENTS.md         # 生平事件图谱
    ├── VOICE.md          # TTS 音色参数
    ├── METADATA.json
    ├── CITATIONS.md
    └── raw_stats.json
Skills enregistrées