Skills 上下文与状态管理
Claude 的每次 API 调用都是无状态的,它不会自动记住上一次会话的内容。
但在一次连续对话中,所有历史消息都在上下文窗口里。理解这一点,能帮助你写出在对话中表现稳定的 Skill。
Skills 的状态存在于哪里
| 状态类型 | 存储位置 | 有效范围 |
|---|---|---|
| 对话历史 | 上下文窗口 | 当前会话,关闭后消失 |
| 中间文件 | /home/claude/ |
当前会话,任务完成后可能清除 |
| 输出文件 | /mnt/user-data/outputs/ |
持久化,用户可下载 |
| 已安装的 Skill | /mnt/skills/ |
跨会话持久化 |
Skills 本身不具备数据库或持久存储能力。需要跨会话保存的数据,应由用户自己保存(如下载文件),或写入持久化输出目录。
在 SKILL.md 中管理多轮对话状态
对于需要多轮交互的 Skill,应在 SKILL.md 中明确定义对话状态的流转逻辑。
实例
## 多轮交互流程
本 Skill 分三个阶段运行:
### 阶段一:信息收集
询问用户提供以下信息:
1. 数据文件路径
2. 分析目标
在获得全部信息前,不执行分析。
将已收集的信息以列表形式展示,告知用户还缺少什么。
### 阶段二:确认执行
收集完成后,展示执行计划,等待用户确认:
> 我将对 {文件名} 执行 {分析目标},预计需要约 {时间} 秒。确认继续吗?
### 阶段三:执行并输出
用户确认后执行,实时告知进度,完成后输出结果。
如果用户在任意阶段说"重新开始",清除当前收集的信息,回到阶段一。
本 Skill 分三个阶段运行:
### 阶段一:信息收集
询问用户提供以下信息:
1. 数据文件路径
2. 分析目标
在获得全部信息前,不执行分析。
将已收集的信息以列表形式展示,告知用户还缺少什么。
### 阶段二:确认执行
收集完成后,展示执行计划,等待用户确认:
> 我将对 {文件名} 执行 {分析目标},预计需要约 {时间} 秒。确认继续吗?
### 阶段三:执行并输出
用户确认后执行,实时告知进度,完成后输出结果。
如果用户在任意阶段说"重新开始",清除当前收集的信息,回到阶段一。
使用文件保存中间状态
对于复杂的多步骤任务,可以将中间状态写入 JSON 文件,实现步骤间的状态传递。
实例
# 文件路径:scripts/state_manager.py
import json
import os
from datetime import datetime
# 状态文件存储路径(当前会话工作目录)
STATE_FILE = "/home/claude/skill_state.json"
def save_state(state: dict) -> None:
"""保存当前执行状态到文件"""
state["updated_at"] = datetime.now().isoformat()
with open(STATE_FILE, "w", encoding="utf-8") as f:
json.dump(state, f, ensure_ascii=False, indent=2)
print(f"状态已保存:{STATE_FILE}")
def load_state() -> dict:
"""加载上次保存的状态,若不存在则返回初始状态"""
if not os.path.exists(STATE_FILE):
return {
"phase": "collecting", # 当前阶段:collecting / confirming / executing / done
"file_path": None,
"target": None,
"result": None,
"created_at": datetime.now().isoformat()
}
with open(STATE_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def clear_state() -> None:
"""清除状态(用于"重新开始"场景)"""
if os.path.exists(STATE_FILE):
os.remove(STATE_FILE)
print("状态已清除,重新开始")
# 使用示例
if __name__ == "__main__":
# 加载或初始化状态
state = load_state()
print(f"当前阶段:{state['phase']}")
# 更新状态:已收集文件路径
state["file_path"] = "/mnt/user-data/uploads/runoob_data.csv"
state["phase"] = "confirming"
save_state(state)
import json
import os
from datetime import datetime
# 状态文件存储路径(当前会话工作目录)
STATE_FILE = "/home/claude/skill_state.json"
def save_state(state: dict) -> None:
"""保存当前执行状态到文件"""
state["updated_at"] = datetime.now().isoformat()
with open(STATE_FILE, "w", encoding="utf-8") as f:
json.dump(state, f, ensure_ascii=False, indent=2)
print(f"状态已保存:{STATE_FILE}")
def load_state() -> dict:
"""加载上次保存的状态,若不存在则返回初始状态"""
if not os.path.exists(STATE_FILE):
return {
"phase": "collecting", # 当前阶段:collecting / confirming / executing / done
"file_path": None,
"target": None,
"result": None,
"created_at": datetime.now().isoformat()
}
with open(STATE_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def clear_state() -> None:
"""清除状态(用于"重新开始"场景)"""
if os.path.exists(STATE_FILE):
os.remove(STATE_FILE)
print("状态已清除,重新开始")
# 使用示例
if __name__ == "__main__":
# 加载或初始化状态
state = load_state()
print(f"当前阶段:{state['phase']}")
# 更新状态:已收集文件路径
state["file_path"] = "/mnt/user-data/uploads/runoob_data.csv"
state["phase"] = "confirming"
save_state(state)
状态已保存:/home/claude/skill_state.json 当前阶段:collecting 状态已保存:/home/claude/skill_state.json
防止上下文污染
当用户在同一对话中多次触发 Skill,历史对话中的旧参数可能"污染"新的执行。
在 SKILL.md 中明确规定优先级,可以避免这类问题。
实例
## 参数优先级规则
同一次对话中若存在多组参数,按以下优先级处理:
1. **用户最新消息中明确指定的参数** → 最高优先级,始终使用
2. **本次请求中上传的文件** → 高优先级
3. **对话历史中提到的参数** → 低优先级,仅在当前消息未指定时使用
4. **Skill 定义的默认值** → 兜底
若对参数来源有疑问,在执行前向用户确认:"我将使用 runoob_v2.csv(您刚上传的文件),而非之前的 runoob_v1.csv,是否正确?"
同一次对话中若存在多组参数,按以下优先级处理:
1. **用户最新消息中明确指定的参数** → 最高优先级,始终使用
2. **本次请求中上传的文件** → 高优先级
3. **对话历史中提到的参数** → 低优先级,仅在当前消息未指定时使用
4. **Skill 定义的默认值** → 兜底
若对参数来源有疑问,在执行前向用户确认:"我将使用 runoob_v2.csv(您刚上传的文件),而非之前的 runoob_v1.csv,是否正确?"
上下文长度与性能的关系
随着对话变长,上下文窗口中的内容越来越多,Claude 处理的成本也会增加。
对于长时间运行的多轮 Skill,应当注意以下优化策略:
| 策略 | 做法 | 效果 |
|---|---|---|
| 避免重复输出大块内容 | 引用之前输出的文件路径,而非重新输出内容 | 减少上下文占用 |
| 使用文件传递中间数据 | 将大型数据集写入文件,对话中只传递路径 | 避免大数据占满上下文 |
| 及时告知用户阶段性完成 | 每个阶段结束后明确说"第一步完成" | 让用户知道进度,减少追问 |
上下文窗口是有限的资源。Skill 中传递的数据量越大,剩余给推理和生成的空间就越少,响应质量可能下降。对于大文件,始终通过文件路径而非内容传递。
