Install
openclaw skills install @arronjack/pasm-npcGive your game an NPC that remembers players, has moods, and is shaped by player feedback. Persistent memory with salience-aware eviction, emotion-driven replies, stage-unlocked actions. Offline, no LLM. Installs with one command (pip install pasm-agents, brings the pasm-skills base).
openclaw skills install @arronjack/pasm-npc给游戏装一个有记忆、有情绪、会被玩家反馈塑形的 NPC。 零 LLM 依赖、断网可用、状态可持久化 —— 不是写死的台词表,是真的能记住玩家。
from pasm_agents import NpcAgent
npc = NpcAgent(agent_id="herbalist", persona={
"name": "陈伯", "role": "河边摆摊的草药老头",
"temper": 0.55, "energy": 0.40, "play": 0.30,
"tone": "慢悠悠、爱讲道理",
})
npc.observe("玩家第一次来买跌打药", tags=["玩家", "买药"], salience=4, category="日常")
print(npc.act()) # 'talk' / 'wave' / 'peek' / ...
print(npc.chat("有跌打药吗")) # 召回相关记忆
print(npc.mood) # 情绪值(注意:是属性,不加括号)
npc.feedback("praise", action="talk") # 显式夸"talk"这个动作
npc.save() # ~/.pasm-agents/herbalist/
| 能力 | 说明 |
|---|---|
| 记忆 | 玩家做过的事逐条入库;重要度 salience 分 1/3/5,容量触顶时按「重要度 + 新旧」淘汰 —— 里程碑("玩家救了我一命")不会被日常琐事挤掉 |
| 情绪 | 有随事件变化的情绪值;同一句话在高兴和生气时说出来的不一样 |
| 动作 | 从动作池里选动作执行,动作池随成长阶段解锁;被夸的动作做得多,被凶的做得少 |
| 持久化 | 落盘到 ~/.pasm-agents/<agent_id>/,跨进程自动恢复上次状态 |
你主要就用这五个方法:observe() 记一件事 · chat() 说 · act() 做 · feedback() 塑形 · save() 存。
本技能的内容在 pasm-agents 仓,但它依赖基座 pasm-skills ——
BaseAgent(记忆读写 / 情绪 / 动作选择 / 反馈 / 持久化的通用实现)在基座里,
产品智能体只是在其上定了 persona、动作池和回复模板。
| 是什么 | 装它 | |
|---|---|---|
| pasm-skills(基座) | 只提供能力,不含任何智能体 | pip install pasm-skills |
| pasm-agents(本技能来源) | 游戏 NPC / 老人陪伴 / 学习陪伴 + 7 个验证智能体 | pip install pasm-agents |
装
pasm-agents会自动带上基座,一条命令搞定。 只装基座时python -m pasm_skills list显示 0 个智能体 —— 那是刻意的,不是你装错了。 想用基座写自己的智能体:https://github.com/arronJack/pasm-skills/blob/master/docs/TUTORIAL.md
pasm.cognitive.memory_layers / learning / pasm.modules.emotion 时优先用;
找不到核心时降级到内置轻量适配,并在 tier 字段写明档位,永不隐藏降级。observe(..., salience=1..5)。容量触顶时按「重要度 + 新旧」淘汰,
salience=5 的里程碑记忆("玩家救了我一命")不会被日常琐事挤掉。feedback(kind, action=)。不指定就只能加强"当前最偏好的动作",
长期下来行为分布会被推到极端 —— 这是 npc-lifelong 验证智能体压出的产品级要求。~/.pasm-agents/<agent_id>/,跨进程加载自动恢复上次状态。pip install pasm-agents # 自动带上基座 pasm-skills(推荐)
# 没有 PyPI 环境时改源码安装:
# git clone https://gitee.com/arronzheng/pasm-agents && cd pasm-agents && pip install -e .
pasm-agents demo npc # 预置剧本,立刻看到效果
pasm-agents run npc --id=my_herbalist # 交互模式
pasm-agents inspect my_herbalist # 看落盘快照(记忆 / 情绪 / 权重)
persona 里三个性格量决定动作基线(0~1):
| 字段 | 含义 | 高 → 偏向 | 低 → 偏向 |
|---|---|---|---|
temper | 脾气 | 主动搭话、反应大 | 沉默、旁观 |
energy | 精力 | 走动、干活 | 休息、打盹 |
play | 玩心 | 蹦跳、逗趣 | 正经、务实 |
再加 tone(语气描述,写进回复模板)与 role(身份,用于自我介绍)。
# 日常记忆(默认 salience=1,容量触顶时先被淘汰)
npc.observe("玩家买了三副跌打药")
# 里程碑记忆(重要度 5,永远不会被挤掉)
npc.observe("玩家在河边把我从水里拉了上来",
brief="我差点淹死", tags=["玩家", "河边", "救命"],
salience=5, category="重大")
# 检索:chat 内部会自动召回;也可以手动拿
print(npc.recall("河边", k=3)) # -> [{'title': ..., 'brief': ..., ...}, ...]
print(npc.chat("还记得上次在河边吗"))
observe 完整签名:
observe(title, brief="", tags=None, *, salience=1, category="日常")
salience 语义:1 日常 / 3 值得记 / 5 里程碑。
print(npc.act()) # 选一个动作执行
print(npc.mood) # 当前情绪(**属性**,不是方法)
npc.feel("被玩家救起", valence=+0.8) # 手动注入一次情绪事件(-1 ~ +1)
npc.feedback("praise", action="talk") # 被夸 → talk 权重上升
npc.feedback("scold", action="hop") # 被凶 → hop 权重下降
动作池随成长阶段解锁:
| 阶段 | 动作 |
|---|---|
| 0(初始) | wave hop peek talk |
| 1 | + ball |
| 2 | + dance spin |
| 3 | + think |
print(npc.grow()) # 手动升一档(按交互数也会自动触发)
pasm-agents run npc --id=my_herbalist --persona-file=personas/herbalist.json
进交互后直接打字对话。可用命令:
| 命令 | 作用 |
|---|---|
act | 执行一个动作 |
mood | 看当前情绪 |
observe <文本> | 手动写一条记忆(salience=2) |
feedback <praise|poke|scold> [动作] | 给反馈,可指定动作 |
grow | 升一档 |
snapshot | 导出快照(JSON) |
quit | 退出并 save |
| tier | 含义 | 何时启用 |
|---|---|---|
bionic | 仿生:完整 PASM 核心 + emotion 模块(需 torch) | pip install pasm-agents[torch] |
core | 完整:PASM 核心(memory + learning,无 torch) | PASM_PYTHON 指向带核心的 Python |
light | 轻量:纯内置(重要度淘汰 + 字面检索 + softmax 权重) | 任何机器 |
落盘 JSON 与 summary() 都带 tier 字段,调用方一眼能判断当前档位。
子类化 NpcAgent 覆盖两处即可:
from pasm_agents import NpcAgent
class Guard(NpcAgent):
def action_pool(self):
return ["patrol", "salute", "warn", "rest"]
def _render_reply(self, text, facts, mood):
# facts 是检索到的记忆,mood 是当前情绪 —— 据此渲染语气
return "……(自己写)"
_render_reply(text, facts, mood) -> str 是唯一的渲染钩子,chat() 会调它。
本仓的 pasm_agents/verifiers/npc_lifelong.py 是专门验证这类 NPC 的智能体:
跑 90 天 / 270 段经历,断言核心能否正确记忆、里程碑会不会被挤掉、情绪会不会漂、行为会不会僵化。
它压出的真问题已经被修进 基座 pasm_skills/sdk/base.py 的 _CoreAdapter —— 你用的就是修过的版本。
要查最新问题清单,看 docs/AGENTS.md。
pasm-companion —— 老人陪伴智能体pasm-tutor —— 学习陪伴智能体pasm-longterm-verify —— 验证层入口:回答"跑久了还是好的吗"