npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@innerlife/agent

v0.1.2

Published

Character-agent runtime for building NPCs and dialogue agents with memory, emotion, secrets, personality, and inner continuity

Readme

InnerLife Agent

用于构建有内心连续性的 Character Agent 的 TypeScript runtime,支持人格驱动对话、情绪状态、多层记忆、秘密/心智理论、工具调用、世界书和对话历史召回。

InnerLife 的目标不是单纯做记忆框架,而是给 NPC、虚拟角色和长期对话 Agent 提供稳定的人格、情绪、秘密、关系和自我连续性,让角色像一个有自己想法的个体持续存在。

快速开始

安装依赖

cd innerlife-agent
npm install

环境变量配置

创建 .env 文件(或在系统环境中设置):

# ─── 主 LLM(用于对话、决策)───────────────────────
OPENAI_API_KEY=sk-your-key
OPENAI_BASE_URL=https://api.openai.com/v1    # 可选,默认 OpenAI 官方
OPENAI_MODEL=gpt-4o                          # 可选,默认 gpt-4o

# ─── 激素 LLM(可选,默认复用主 LLM)──────────────────
# 推荐使用轻量模型降成本
HORMONE_LLM_API_KEY=sk-your-key
HORMONE_LLM_BASE_URL=https://api.deepseek.com/v1
HORMONE_LLM_MODEL=deepseek-chat

# ─── 记忆压缩器 LLM(可选,默认复用主 LLM)──────────────
COMPRESSOR_LLM_API_KEY=sk-your-key
COMPRESSOR_LLM_BASE_URL=https://api.deepseek.com/v1
COMPRESSOR_LLM_MODEL=deepseek-chat

# ─── 如果使用 Anthropic ──────────────────────────────
ANTHROPIC_API_KEY=sk-ant-your-key

**envPrefix 机制**:每个 Provider 实例可通过 envPrefix 指定环境变量前缀。例如 envPrefix: 'HORMONE_LLM' 会读取 HORMONE_LLM_API_KEYHORMONE_LLM_BASE_URLHORMONE_LLM_MODEL

查找优先级:显式参数 → {PREFIX}_* 环境变量 → OPENAI_* 默认环境变量 → 硬编码默认值

编译 & 测试

# 类型检查
npm run lint

# 编译
npm run build

# 运行测试
npm test

# 开发模式(监听编译)
npm run dev

# 测试监听模式
npm run test:watch

使用示例

基础用法

import {
  AgentFactory,
  EventPriority,
  OpenAIProvider,
  Runner,
} from '@innerlife/agent';

// 1. 创建工厂
const factory = new AgentFactory({
  provider: new OpenAIProvider(),  // 自动读取 OPENAI_* 环境变量
});

// 2. 构建 Agent
const agent = await factory.create({
  id: 'npc-001',
  personaPath: './personas/my-character.yaml',
});

// 3. 推入一条事件
agent.inbox.push({
  id: 'evt-001',
  type: 'player:dialogue',
  source: 'player-001',
  text: '你好!',
  priority: EventPriority.PLAYER_COMMAND,
  timestamp: Date.now(),
  ttl: 60_000,
});

// 4. 执行一轮
const runner = new Runner();
const result = await runner.run(agent);

console.log(result.dialogue);

多模型配置

const factory = new AgentFactory({
  // 主 LLM —— 大模型,负责对话和决策
  provider: new OpenAIProvider({ model: 'gpt-4o' }),

  // 激素 LLM —— 轻量模型,负责情绪感知
  hormoneProvider: new OpenAIProvider({
    envPrefix: 'HORMONE_LLM',  // 读取 HORMONE_LLM_* 环境变量
  }),

  // 记忆压缩 —— 轻量模型,负责记忆蒸馏
  compressorProvider: new OpenAIProvider({
    envPrefix: 'COMPRESSOR_LLM',
  }),
});

接入 OpenAI 兼容服务

框架支持所有 OpenAI Chat Completions 兼容 API:

// DeepSeek
const deepseek = new OpenAIProvider({
  apiKey: 'sk-xxx',
  baseURL: 'https://api.deepseek.com/v1',
  model: 'deepseek-chat',
});

// Moonshot
const moonshot = new OpenAIProvider({
  apiKey: 'sk-xxx',
  baseURL: 'https://api.moonshot.cn/v1',
  model: 'moonshot-v1-8k',
});

// 本地 Ollama
const ollama = new OpenAIProvider({
  baseURL: 'http://localhost:11434/v1',
  model: 'llama3',
  apiKey: 'ollama',  // Ollama 不需要真实 key
});

// 也可通过环境变量配置,无需改代码
// OPENAI_BASE_URL=http://localhost:11434/v1
// OPENAI_MODEL=llama3
// OPENAI_API_KEY=ollama
const localProvider = new OpenAIProvider();

使用 Anthropic

import { AnthropicProvider } from '@innerlife/agent';

const factory = new AgentFactory({
  provider: new AnthropicProvider({
    model: 'claude-sonnet-4-20250514',
  }),
});

人格 YAML 配置

创建 personas/my-character.yaml

name: 云无涯
description: 清风宗内门弟子,性格沉稳内敛

coreTraits:
  - 沉默寡言
  - 重承诺
  - 对修炼极其认真

speechStyle:
  patterns:
    - "……{content}。"
    - "{content},罢了。"
  vocabulary:
    - 道友
    - 前辈
  forbiddenWords:
    - 哈哈
    - 太棒了
  tone: 沉稳

identityAnchors:
  - fact: 清风宗内门弟子
    importance: critical
  - fact: 修炼无相剑诀
    importance: high

constraints:
  - 不主动与人攀谈
  - 不轻易表露情绪

注册工具

import { z } from 'zod';
import type { ToolDefinition } from '@innerlife/agent';

const WeatherParams = z.object({
  location: z.string().describe('地点'),
});

const checkWeatherTool: ToolDefinition = {
  name: 'check_weather',
  description: '查看当前天气',
  tags: ['weather', 'observation'],
  parameters: WeatherParams,
  canUse: () => true,
  execute: async (params) => {
    const { location } = params as z.infer<typeof WeatherParams>;
    return {
      toolName: 'check_weather',
      success: true,
      output: `${location}当前天气晴,25°C。`,
      metadata: { weather: '晴', temperature: 25 },
    };
  },
};

// 单 Agent 快速注册
agent.toolRegistry.register(checkWeatherTool);

多 Agent 场景下,推荐把工具注册到共享 ToolRegistry,再用每个 Agent 的 tools 白名单、工具的 scopecanUse(agentId, context) 控制可见性,避免逐个 Agent 重复注册:

import { AgentFactory, ToolRegistry } from '@innerlife/agent';

const toolRegistry = new ToolRegistry();
toolRegistry.registerBatch([
  checkWeatherTool,
  // sendMessageTool,
  // lookAroundTool,
]);

const factory = new AgentFactory({
  provider,
  toolRegistry,
});

const npcA = await factory.create({
  id: 'npc-a',
  personaPath: './personas/npc-a.yaml',
  tools: ['check_weather'],
});

const npcB = await factory.create({
  id: 'npc-b',
  personaPath: './personas/npc-b.yaml',
  tools: [], // npcB 看不到 check_weather
});

注册 Hooks

import type { Hook } from '@innerlife/agent';

const riskControlHook: Hook = {
  name: 'risk-control',
  phase: 'post-output',
  priority: 10,
  readonly: true,
  critical: false,
  execute: async (ctx) => {
    // 检查输出是否合规
    return { action: 'continue' };
  },
};

agent.hooks.register(riskControlHook);

Hook 的 phase 必须是具体管道阶段,例如 pre-inboxpost-worldbookpre-llmpost-output。如果多个 Agent 都需要同一批 Hooks,建议封装统一注册函数:

import type { Agent, Hook } from '@innerlife/agent';

const commonHooks: Hook[] = [riskControlHook];

function registerCommonHooks(agent: Agent) {
  for (const hook of commonHooks) {
    agent.hooks.register(hook);
  }
}

添加世界书条目

agent.worldBook.addEntry({
  id: 'realm-system',
  title: '境界体系',
  content: '修仙境界从低到高:练气、筑基、金丹、元婴、化神...',
  category: '修炼体系',
  keywords: ['境界', '修炼', '突破'],
  visibility: 'public_knowledge',
  alwaysActive: true,
  priority: 5,
});

agent.worldBookRetriever.rebuildIndex();

注意:world_truth 表示只有叙事者/游戏系统知道的真相,永远不会注入任何 Agent 的 prompt。角色应该知道的常识用 public_knowledge;只有部分身份知道的内容用 character_known + knowledgeScope

多 Agent 场景下,不要手写复制世界书。把世界书词条维护成一份共享数据,然后在创建 Agent 时批量挂载:

import type { AgentConfig, LoreEntry } from '@innerlife/agent';

const commonLore: LoreEntry[] = [
  {
    id: 'realm-system',
    title: '境界体系',
    content: '修仙境界从低到高:练气、筑基、金丹、元婴、化神...',
    category: '修炼体系',
    keywords: ['境界', '修炼', '突破'],
    visibility: 'public_knowledge',
    alwaysActive: true,
    priority: 5,
  },
];

async function createGameAgent(config: AgentConfig) {
  const agent = await factory.create(config);

  agent.worldBook.addEntries(commonLore);
  agent.worldBookRetriever.rebuildIndex();
  registerCommonHooks(agent);

  return agent;
}

项目结构

innerlife-agent/
├── src/
│   ├── index.ts              # 公共 API 导出
│   ├── core/                 # Agent + Runner + AgentFactory
│   ├── hormone/              # 激素情绪系统
│   ├── inbox/                # 事件收件箱
│   ├── persona/              # 人格配置
│   ├── memory/               # 多层记忆系统
│   ├── prompt/               # Prompt 组装 + 信任边界
│   ├── tools/                # 工具注册与执行
│   ├── skills/               # 技能系统
│   ├── hooks/                # Hooks 扩展引擎
│   ├── worldbook/            # 世界书
│   ├── relationship/         # 关系建模
│   ├── providers/            # LLM Provider(OpenAI / Anthropic)
│   └── observability/        # EventBus + Tracer
├── __tests__/                # 测试用例
├── docs/                     # 架构、指南、专题文档
├── scripts/                  # 本地测试和 TUI 调试脚本
├── package.json
├── tsconfig.json
└── vitest.config.ts

调试技巧

使用 EventBus 监听内部事件

agent.eventBus.on('emotion:applied', (data) => {
  console.log('情绪变化:', data);
});

agent.eventBus.on('tool:executed', (data) => {
  console.log('工具调用:', data);
});

agent.eventBus.on('memory:flush:complete', (data) => {
  console.log('记忆写入:', data);
});

查看执行追踪

Runner 每轮返回的 TurnResult 包含完整的 ExecutionTrace

agent.inbox.push(event);
const result = await runner.run(agent);
console.log(JSON.stringify(result.trace, null, 2));
// 包含:激素阶段、世界书命中、token 用量、工具调用、hooks 执行

使用 FileSystemStore 查看记忆

默认存储路径为 ./data/agents/{agentId}/memory,可直接查看 JSON 文件:

cat ./data/agents/npc-001/memory/store.json | jq .

许可证

UNLICENSED