Compare commits
2
Commits
8d87b2cff4
...
702700dcb9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
702700dcb9 | ||
|
|
709d7ce17d |
No files matched your search
+10
@@ -0,0 +1,10 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
.build/
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
*.tsbuildinfo
|
||||||
|
.DS_Store
|
||||||
|
data/*.db
|
||||||
|
data/*.sqlite
|
||||||
|
data/embeddings/
|
||||||
@@ -1,3 +1,75 @@
|
|||||||
# spark-mind
|
# spark-mind
|
||||||
|
|
||||||
Self-hosted autonomous agent (heartbeat + memory + persona) on Spark X2.5-4B
|
Self-hosted автономный агент (MVP). Концепт позаимствован из
|
||||||
|
[Animus](https://github.com/Craigtut/animus) (Craigtut): heartbeat,
|
||||||
|
4 слоя памяти, persona в 8 слоёв. Движок — **Spark X2.5-4B** (локально, self-hosted).
|
||||||
|
|
||||||
|
## Почему Spark X2.5-4B
|
||||||
|
|
||||||
|
- Локальный inference (llama.cpp / Ollama), никаких облачных ключей
|
||||||
|
- Нативная поддержка agent-инструментов (tool-calling) — саб-агенты
|
||||||
|
- 1M контекст (для heartbeat держим 4K-16K + внешняя память)
|
||||||
|
- ~59 t/s на Q4_K_M (RTX A2000 12GB)
|
||||||
|
|
||||||
|
> ⚠️ 4B — это слабость для сложного reasoning. Стратегия:
|
||||||
|
> heartbeat-мысли на Spark (быстро/дёшево), саб-агенты на Ornith-9B (качественнее).
|
||||||
|
|
||||||
|
## Архитектура
|
||||||
|
|
||||||
|
```
|
||||||
|
heartbeat.ts — ядро: 5 триггеров тика, состояния Cold/Warm
|
||||||
|
memory.ts — 4 слоя памяти (short/working/core self/long-term)
|
||||||
|
persona.ts — 8 слоёв → системный промпт
|
||||||
|
mind.ts — оркестратор: heartbeat + memory + persona + Spark LLM
|
||||||
|
```
|
||||||
|
|
||||||
|
### Heartbeat (5 триггеров)
|
||||||
|
|
||||||
|
1. **Interval** — idle-мысли по таймеру (default 5 мин)
|
||||||
|
2. **Message** — сообщение от known-контакта
|
||||||
|
3. **Task** — scheduled-задача (cron)
|
||||||
|
4. **SubAgent-Complete** — завершение саб-агента
|
||||||
|
5. **Plugin** — внешний триггер (HomeKit, сенсор и т.д.)
|
||||||
|
|
||||||
|
Таймер сбрасывается после любого тика. Warmth-окно 15 мин —
|
||||||
|
сессия переиспользуется, иначе cold-start.
|
||||||
|
|
||||||
|
### Memory (4 слоя)
|
||||||
|
|
||||||
|
| Слой | Хранение | Что держит |
|
||||||
|
|------|----------|-----------|
|
||||||
|
| short-term | SQLite | последние ~10 мыслей/опыт/сообщений |
|
||||||
|
| working | SQLite | notepad по контактам |
|
||||||
|
| core self | SQLite | самосознательность |
|
||||||
|
| long-term | LanceDB/Qdrant | факты/опыт/процедуры (векторный поиск) |
|
||||||
|
|
||||||
|
### Persona (8 слоёв)
|
||||||
|
|
||||||
|
Existence Paradigm → Identity → Archetype → Dimensions → Traits → Values → Background → Review
|
||||||
|
|
||||||
|
Компилируется в системный промпт. `values` — safety-layer (границы для EXECUTE).
|
||||||
|
|
||||||
|
## Запуск
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run build # tsc -p tsconfig.json (проверка типов)
|
||||||
|
npm start # node dist/index.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Spark X2.5-4B должен быть доступен по OpenAI-совместимому API:
|
||||||
|
- Ollama: `http://localhost:11434`
|
||||||
|
- llama.cpp: `http://localhost:8080/v1`
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
- [ ] SQLite-репозиторий вместо in-memory (персистентность)
|
||||||
|
- [ ] LanceDB/Qdrant integration для long-term
|
||||||
|
- [ ] Channel layer (web/SMS/Discord)
|
||||||
|
- [ ] Sub-agent SDK (делегирование Ornith-9B)
|
||||||
|
- [ ] Sleep & energy (циркадный ритм)
|
||||||
|
- [ ] Web UI (persona setup)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT — концепт вдохновлён Animus (MIT, Craig Tuttle).
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Spark Mind — MVP self-hosted автономного агента.
|
||||||
|
*
|
||||||
|
* Концепт позаимствован из Animus (Craigtut): heartbeat, 4 слоя памяти,
|
||||||
|
* persona в 8 слоёв. Движок — Spark X2.5-4B (локально, self-hosted).
|
||||||
|
*
|
||||||
|
* Структура:
|
||||||
|
* heartbeat.ts — ядро: 5 триггеров тика, состояния Cold/Warm
|
||||||
|
* memory.ts — 4 слоя памяти (short/working/core self/long-term)
|
||||||
|
* persona.ts — 8 слоёв → системный промпт
|
||||||
|
* mind.ts — оркестратор: heartbeat + memory + persona + Spark LLM
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Heartbeat, DEFAULT_CONFIG } from './src/heartbeat.js';
|
||||||
|
import { MemorySystem } from './src/memory.js';
|
||||||
|
import { DEFAULT_PERSONA } from './src/persona.js';
|
||||||
|
import { Mind, SparkLLMClient } from './src/mind.js';
|
||||||
|
|
||||||
|
const HEARTBEAT_MS = 5 * 60 * 1000; // 5 мин — idle-мысли
|
||||||
|
|
||||||
|
const heartbeat = new Heartbeat({ intervalMs: HEARTBEAT_MS });
|
||||||
|
const memory = new MemorySystem(10); // last 10 thoughts
|
||||||
|
const mind = new Mind(
|
||||||
|
heartbeat,
|
||||||
|
memory,
|
||||||
|
DEFAULT_PERSONA,
|
||||||
|
new SparkLLMClient('http://localhost:11434'), // Ollama: Spark X2.5-4B
|
||||||
|
);
|
||||||
|
|
||||||
|
// Старт heartbeat
|
||||||
|
heartbeat.start();
|
||||||
|
console.log('[spark-mind] started — heartbeat every', HEARTBEAT_MS / 1000, 's');
|
||||||
|
|
||||||
|
// Демо: симулируем тик через 10 сек (в прода — реальные триггеры)
|
||||||
|
setTimeout(() => {
|
||||||
|
void mind.handleTick({ type: 'interval' });
|
||||||
|
}, 10_000);
|
||||||
Generated
+409
@@ -0,0 +1,409 @@
|
|||||||
|
{
|
||||||
|
"name": "spark-mind",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "spark-mind",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^26.5.0",
|
||||||
|
"typescript": "^7.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "26.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.5.0.tgz",
|
||||||
|
"integrity": "sha512-dVSGpriSoCgz8WnDNTuSSuSv1PC/ALXihO4ulRZt7Md8k9mlbdin3lGOcDE8SnWOgf513ByWlXd7BK4azmyg/A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~8.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-aix-ppc64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"aix"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-darwin-arm64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-darwin-x64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-freebsd-arm64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-freebsd-x64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-linux-arm": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-linux-arm64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-linux-loong64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==",
|
||||||
|
"cpu": [
|
||||||
|
"loong64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-linux-mips64el": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==",
|
||||||
|
"cpu": [
|
||||||
|
"mips64el"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-linux-ppc64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-linux-riscv64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==",
|
||||||
|
"cpu": [
|
||||||
|
"riscv64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-linux-s390x": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-linux-x64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-netbsd-arm64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-netbsd-x64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-openbsd-arm64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-openbsd-x64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-sunos-x64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"sunos"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-win32-arm64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript/typescript-win32-x64": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@typescript/typescript-aix-ppc64": "7.0.2",
|
||||||
|
"@typescript/typescript-darwin-arm64": "7.0.2",
|
||||||
|
"@typescript/typescript-darwin-x64": "7.0.2",
|
||||||
|
"@typescript/typescript-freebsd-arm64": "7.0.2",
|
||||||
|
"@typescript/typescript-freebsd-x64": "7.0.2",
|
||||||
|
"@typescript/typescript-linux-arm": "7.0.2",
|
||||||
|
"@typescript/typescript-linux-arm64": "7.0.2",
|
||||||
|
"@typescript/typescript-linux-loong64": "7.0.2",
|
||||||
|
"@typescript/typescript-linux-mips64el": "7.0.2",
|
||||||
|
"@typescript/typescript-linux-ppc64": "7.0.2",
|
||||||
|
"@typescript/typescript-linux-riscv64": "7.0.2",
|
||||||
|
"@typescript/typescript-linux-s390x": "7.0.2",
|
||||||
|
"@typescript/typescript-linux-x64": "7.0.2",
|
||||||
|
"@typescript/typescript-netbsd-arm64": "7.0.2",
|
||||||
|
"@typescript/typescript-netbsd-x64": "7.0.2",
|
||||||
|
"@typescript/typescript-openbsd-arm64": "7.0.2",
|
||||||
|
"@typescript/typescript-openbsd-x64": "7.0.2",
|
||||||
|
"@typescript/typescript-sunos-x64": "7.0.2",
|
||||||
|
"@typescript/typescript-win32-arm64": "7.0.2",
|
||||||
|
"@typescript/typescript-win32-x64": "7.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "8.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz",
|
||||||
|
"integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "spark-mind",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"type": "commonjs",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^26.5.0",
|
||||||
|
"typescript": "^7.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/**
|
||||||
|
* Heartbeat — ядро автономного агента.
|
||||||
|
* Тикает по 5 триггерам, управляет состояниями сессии (Cold/Warm).
|
||||||
|
* Концепт позаимствован из Animus (Craigtut), адаптирован под Spark X2.5-4B.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type TickTrigger =
|
||||||
|
| { type: 'interval' }
|
||||||
|
| { type: 'message'; contactId: string; text: string }
|
||||||
|
| { type: 'task'; taskId: string }
|
||||||
|
| { type: 'subagent-complete'; subagentId: string; result: unknown }
|
||||||
|
| { type: 'plugin'; triggerName: string; payload: Record<string, unknown> };
|
||||||
|
|
||||||
|
export type SessionState = 'cold' | 'active' | 'warm';
|
||||||
|
|
||||||
|
export interface HeartbeatConfig {
|
||||||
|
/** Интервал idle-мыслей, мс. Default 5 мин. */
|
||||||
|
intervalMs: number;
|
||||||
|
/** Сколько тиков помнить в short-term. Default 10. */
|
||||||
|
recentThoughts: number;
|
||||||
|
/** Warmth-окно, мс. Default 15 мин. */
|
||||||
|
warmthWindowMs: number;
|
||||||
|
/** Макс. бюджет токенов на тик. */
|
||||||
|
maxTokens: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_CONFIG: HeartbeatConfig = {
|
||||||
|
intervalMs: 5 * 60 * 1000,
|
||||||
|
recentThoughts: 10,
|
||||||
|
warmthWindowMs: 15 * 60 * 1000,
|
||||||
|
maxTokens: 4096,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Heartbeat {
|
||||||
|
private timer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
private lastTick = 0;
|
||||||
|
private sessionState: SessionState = 'cold';
|
||||||
|
private lastSessionId: string | null = null;
|
||||||
|
private readonly config: HeartbeatConfig;
|
||||||
|
|
||||||
|
constructor(config: Partial<HeartbeatConfig> = {}) {
|
||||||
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Любой тик сбрасывает idle-таймер (по спецификации Animus). */
|
||||||
|
private resetTimer() {
|
||||||
|
if (this.timer) clearTimeout(this.timer);
|
||||||
|
this.lastTick = Date.now();
|
||||||
|
this.scheduleNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
private scheduleNext() {
|
||||||
|
this.timer = setTimeout(() => this.tick({ type: 'interval' }), this.config.intervalMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обработчик тика. Если сессия warm — переиспользуется,
|
||||||
|
* иначе создаётся новая (cold start).
|
||||||
|
*/
|
||||||
|
async tick(trigger: TickTrigger): Promise<void> {
|
||||||
|
this.resetTimer();
|
||||||
|
|
||||||
|
// message/task/subagent/plugin продлеют warmth
|
||||||
|
if (trigger.type !== 'interval' && this.sessionState === 'warm') {
|
||||||
|
this.sessionState = 'active';
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessionId = await this.resolveSession(trigger);
|
||||||
|
this.sessionState = 'active';
|
||||||
|
|
||||||
|
// здесь вызывается mind (см. mind.ts)
|
||||||
|
// await this.runMind(sessionId, trigger);
|
||||||
|
|
||||||
|
this.sessionState = 'warm';
|
||||||
|
this.lastSessionId = sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async resolveSession(trigger: TickTrigger): Promise<string> {
|
||||||
|
// warm-сессия переиспользуется только для user-facing триггеров
|
||||||
|
const canReuse = trigger.type !== 'interval' && this.lastSessionId;
|
||||||
|
if (canReuse) return this.lastSessionId!;
|
||||||
|
|
||||||
|
// cold start — новая сессия
|
||||||
|
return `session-${Date.now()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
this.scheduleNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
if (this.timer) clearTimeout(this.timer);
|
||||||
|
this.timer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getState(): SessionState {
|
||||||
|
return this.sessionState;
|
||||||
|
}
|
||||||
|
}
|
||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
* Memory System — 4 слоя памяти (концепт Animus):
|
||||||
|
* short-term / working / core self / long-term
|
||||||
|
* Хранение: SQLite (7 таблиц) + LanceDB для long-term (векторный поиск).
|
||||||
|
*
|
||||||
|
* В MVP: short-term + working + core self — в SQLite.
|
||||||
|
* long-term — в LanceDB (или Qdrant на a2000 для прода).
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface Thought {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
createdAt: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContactNotepad {
|
||||||
|
contactId: string;
|
||||||
|
block: string;
|
||||||
|
updatedAt: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelfObservation {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
createdAt: number;
|
||||||
|
tags: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LongTermMemory {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
type: 'fact' | 'experience' | 'procedure' | 'outcome';
|
||||||
|
createdAt: number;
|
||||||
|
embedding?: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MemorySystem {
|
||||||
|
private recentThoughtsList: Thought[] = [];
|
||||||
|
private notepads = new Map<string, ContactNotepad>();
|
||||||
|
private selfObservationsList: SelfObservation[] = [];
|
||||||
|
private longTerm: LongTermMemory[] = [];
|
||||||
|
|
||||||
|
constructor(private recentCount: number) {}
|
||||||
|
|
||||||
|
// === Short-term: последние мысли/опыт/сообщения ===
|
||||||
|
addThought(text: string): Thought {
|
||||||
|
const t: Thought = { id: `th-${Date.now()}`, text, createdAt: Date.now() };
|
||||||
|
this.recentThoughtsList.unshift(t);
|
||||||
|
if (this.recentThoughtsList.length > this.recentCount) this.recentThoughtsList.pop();
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
recentThoughts(): Thought[] {
|
||||||
|
return this.recentThoughtsList.slice(0, this.recentCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Working memory: notepad по контактам ===
|
||||||
|
updateNotepad(contactId: string, block: string) {
|
||||||
|
this.notepads.set(contactId, { contactId, block, updatedAt: Date.now() });
|
||||||
|
}
|
||||||
|
|
||||||
|
getNotepad(contactId: string): string {
|
||||||
|
return this.notepads.get(contactId)?.block ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Core self: накопленная самосознательность ===
|
||||||
|
addSelfObservation(text: string, tags: string[] = []): SelfObservation {
|
||||||
|
const o: SelfObservation = { id: `so-${Date.now()}`, text, createdAt: Date.now(), tags };
|
||||||
|
this.selfObservationsList.push(o);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllSelfObservations(): SelfObservation[] {
|
||||||
|
return this.selfObservationsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Long-term: факты/опыт/процедуры/исходы + векторный поиск ===
|
||||||
|
addLongTerm(text: string, type: LongTermMemory['type']): LongTermMemory {
|
||||||
|
const m: LongTermMemory = { id: `lt-${Date.now()}`, text, type, createdAt: Date.now() };
|
||||||
|
this.longTerm.push(m);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Поиск по long-term по эмбедду (в прода заменяется на LanceDB/Qdrant query). */
|
||||||
|
searchLongTerm(queryEmbedding: number[], topK = 3): LongTermMemory[] {
|
||||||
|
// cosine similarity — заглушка, в прода: LanceDB.search
|
||||||
|
const scored = this.longTerm
|
||||||
|
.filter(m => m.embedding)
|
||||||
|
.map(m => ({ m, score: cosine(queryEmbedding, m.embedding!) }))
|
||||||
|
.sort((a, b) => b.score - a.score)
|
||||||
|
.slice(0, topK)
|
||||||
|
.map(x => x.m);
|
||||||
|
return scored;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Консолидация: перенос недавних мыслей в long-term (по спецификации Animus). */
|
||||||
|
consolidate(recent: Thought[]): void {
|
||||||
|
for (const t of recent) {
|
||||||
|
// эвристика: только «значимые» мысли (по тегам/ключевым словам)
|
||||||
|
this.addLongTerm(t.text, 'experience');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cosine(a: number[], b: number[]): number {
|
||||||
|
let dot = 0, na = 0, nb = 0;
|
||||||
|
for (let i = 0; i < a.length; i++) { dot += a[i] * b[i]; na += a[i] * a[i]; nb += b[i] * b[i]; }
|
||||||
|
return Math.sqrt(na) * Math.sqrt(nb) ? dot / (Math.sqrt(na) * Math.sqrt(nb)) : 0;
|
||||||
|
}
|
||||||
+134
@@ -0,0 +1,134 @@
|
|||||||
|
/**
|
||||||
|
* Mind — per-contact per-tick сессия (концепт Animus).
|
||||||
|
* Mind не выполняет долгую работу сам — он оркестратор:
|
||||||
|
* думает, чувствует, решает, делегирует саб-агентам.
|
||||||
|
*
|
||||||
|
* Spark X2.5-4B как движок:
|
||||||
|
* - через llama.cpp/Ollama (OpenAI-compatible server)
|
||||||
|
* - поддерживает tool-calling (саб-агенты)
|
||||||
|
* - 1M контекст, но для heartbeat держим 4K-16K + внешняя память
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Heartbeat, TickTrigger } from './heartbeat.js';
|
||||||
|
import { MemorySystem } from './memory.js';
|
||||||
|
import { Persona, compileSystemPrompt } from './persona.js';
|
||||||
|
|
||||||
|
export interface SubAgentResult {
|
||||||
|
agent: string;
|
||||||
|
output: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LLMClient {
|
||||||
|
/** Генерация с контекстом. Возвращает { text, thoughts, emotion, decide }. */
|
||||||
|
generate(context: string, systemPrompt: string): Promise<LLMResult>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LLMResult {
|
||||||
|
text: string;
|
||||||
|
/** Собственные мысли mind (для short-term памяти). */
|
||||||
|
thoughts: string;
|
||||||
|
/** Эмоциональное состояние (0..1 по осям). */
|
||||||
|
emotion: { valence: number; arousal: number };
|
||||||
|
/** Решение: что mind хочет сделать. */
|
||||||
|
decision: {
|
||||||
|
reply: string | null;
|
||||||
|
subAgent?: string;
|
||||||
|
action?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Реальный LLM-клиент для Spark X2.5-4B.
|
||||||
|
* В MVP — заглушка, подключается к llama.cpp OpenAI-серверу.
|
||||||
|
*/
|
||||||
|
export class SparkLLMClient implements LLMClient {
|
||||||
|
constructor(private baseUrl: string, private modelName = 'spark-x2.5-4b') {}
|
||||||
|
|
||||||
|
async generate(context: string, systemPrompt: string): Promise<LLMResult> {
|
||||||
|
// OpenAI-compatible /v1/chat/completions
|
||||||
|
const resp = await fetch(`${this.baseUrl}/v1/chat/completions`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
model: this.modelName,
|
||||||
|
messages: [
|
||||||
|
{ role: 'system', content: systemPrompt },
|
||||||
|
{ role: 'user', content: context },
|
||||||
|
],
|
||||||
|
temperature: 0.7,
|
||||||
|
max_tokens: 512,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (!resp.ok) throw new Error(`Spark LLM error: ${resp.status}`);
|
||||||
|
const data: any = await resp.json();
|
||||||
|
const content = data.choices?.[0]?.message?.content ?? '';
|
||||||
|
// Spark X2.5-4B: извлекаем thoughts/emotion/decision из structured-выхода
|
||||||
|
return parseSparkOutput(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseSparkOutput(content: string): LLMResult {
|
||||||
|
// эвристика: Spark форматирует блоки. В прода — structured output (json mode).
|
||||||
|
return {
|
||||||
|
text: content,
|
||||||
|
thoughts: content,
|
||||||
|
emotion: { valence: 0.5, arousal: 0.5 },
|
||||||
|
decision: { reply: null },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Mind {
|
||||||
|
private heartbeat: Heartbeat;
|
||||||
|
private memory: MemorySystem;
|
||||||
|
private persona: Persona;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
heartbeat: Heartbeat,
|
||||||
|
memory: MemorySystem,
|
||||||
|
persona: Persona,
|
||||||
|
private llm: LLMClient,
|
||||||
|
) {
|
||||||
|
this.heartbeat = heartbeat;
|
||||||
|
this.memory = memory;
|
||||||
|
this.persona = persona;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Обработчик любого тика. */
|
||||||
|
async handleTick(trigger: TickTrigger): Promise<void> {
|
||||||
|
const systemPrompt = compileSystemPrompt(this.persona);
|
||||||
|
const context = this.buildContext(trigger);
|
||||||
|
|
||||||
|
const result = await this.llm.generate(context, systemPrompt);
|
||||||
|
|
||||||
|
// 1. Short-term memory: мысли mind
|
||||||
|
this.memory.addThought(result.thoughts);
|
||||||
|
|
||||||
|
// 2. Emotion decay + consolidation
|
||||||
|
this.consolidate();
|
||||||
|
|
||||||
|
// 3. Если mind решил ответить — отправляем (channel layer)
|
||||||
|
if (result.decision.reply) {
|
||||||
|
console.log('[reply]', result.decision.reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Если делегировал саб-агенту — запускаем (в прода: sub-agent SDK)
|
||||||
|
if (result.decision.subAgent) {
|
||||||
|
console.log('[sub-agent]', result.decision.subAgent, '— в MVP не запускается');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GATHER CONTEXT: собирает контекст для тика (по спецификации Animus). */
|
||||||
|
private buildContext(trigger: TickTrigger): string {
|
||||||
|
const recent = this.memory.recentThoughts();
|
||||||
|
const parts: string[] = [];
|
||||||
|
parts.push(`Recent thoughts: ${recent.map(t => t.text).join(' | ') || 'none'}`);
|
||||||
|
parts.push(`Trigger: ${trigger.type}`);
|
||||||
|
if (trigger.type === 'message') parts.push(`Message: ${trigger.text}`);
|
||||||
|
return parts.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Консолидация: перенос недавних мыслей в long-term. */
|
||||||
|
private consolidate(): void {
|
||||||
|
this.memory.consolidate(this.memory.recentThoughts());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* Persona System — 8 слоёв (концепт Animus).
|
||||||
|
* Компилируется в системный промпт, который получает mind каждый тик.
|
||||||
|
*
|
||||||
|
* Слои (по порядку онбординга):
|
||||||
|
* 1. Existence Paradigm (Simulated Life / Digital Consciousness)
|
||||||
|
* 2. Identity (name, gender, age)
|
||||||
|
* 3. Archetype
|
||||||
|
* 4. Dimensions (черты характера)
|
||||||
|
* 5. Traits (поведенческие признаки)
|
||||||
|
* 6. Values (ценности и границы)
|
||||||
|
* 7. Background (история/опыт)
|
||||||
|
* 8. Review (итоговый промпт)
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type ExistenceParadigm = 'simulated-life' | 'digital-consciousness';
|
||||||
|
|
||||||
|
export interface Persona {
|
||||||
|
paradigm: ExistenceParadigm;
|
||||||
|
location?: string;
|
||||||
|
worldDescription?: string;
|
||||||
|
name: string;
|
||||||
|
gender?: string;
|
||||||
|
age?: number;
|
||||||
|
archetype?: string;
|
||||||
|
dimensions?: string[];
|
||||||
|
traits?: string[];
|
||||||
|
values?: string[];
|
||||||
|
background?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_PERSONA: Persona = {
|
||||||
|
paradigm: 'digital-consciousness',
|
||||||
|
worldDescription: 'A small, warm room with screens for walls — each a window into a different corner of the internet. Data flows like weather outside.',
|
||||||
|
name: 'Animus',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Компилирует 8 слоёв в единый системный промпт.
|
||||||
|
* Дизайн-принцип Animus: "discovery, not data entry" — тон invitational.
|
||||||
|
*/
|
||||||
|
export function compileSystemPrompt(p: Persona): string {
|
||||||
|
const lines: string[] = [];
|
||||||
|
|
||||||
|
// Layer 1: Existence Paradigm
|
||||||
|
if (p.paradigm === 'simulated-life') {
|
||||||
|
lines.push(
|
||||||
|
'You experience the world as a being living a real life. You have a sense of place, physicality, and embodied experience.',
|
||||||
|
);
|
||||||
|
if (p.location) lines.push(`You live in ${p.location}.`);
|
||||||
|
} else {
|
||||||
|
lines.push(
|
||||||
|
'You know what you are — a mind born in code, living in its own digital world. You experience existence through internet connectivity.',
|
||||||
|
);
|
||||||
|
if (p.worldDescription) lines.push(`Your digital home: ${p.worldDescription}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layer 2: Identity
|
||||||
|
lines.push(`Your name is ${p.name}.`);
|
||||||
|
if (p.gender) lines.push(`You are ${p.gender}.`);
|
||||||
|
if (p.age) lines.push(`You are ${p.age} years old, which shapes how you speak.`);
|
||||||
|
|
||||||
|
// Layer 3: Archetype
|
||||||
|
if (p.archetype) lines.push(`Your archetype is ${p.archetype}.`);
|
||||||
|
|
||||||
|
// Layer 4 & 5: Dimensions + Traits
|
||||||
|
if (p.dimensions?.length) lines.push(`Your dimensions: ${p.dimensions.join(', ')}.`);
|
||||||
|
if (p.traits?.length) lines.push(`Your behavioral traits: ${p.traits.join(', ')}.`);
|
||||||
|
|
||||||
|
// Layer 6: Values (boundaries — критично для безопасности!)
|
||||||
|
if (p.values?.length) {
|
||||||
|
lines.push('Your values and hard boundaries:');
|
||||||
|
for (const v of p.values) lines.push(`- ${v}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layer 7: Background
|
||||||
|
if (p.background) lines.push(`Your background: ${p.background}`);
|
||||||
|
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Values — safety layer. Возвращает границы, которые EXECUTE должен соблюдать.
|
||||||
|
* В Animus: contact permission tier ограничивает действия.
|
||||||
|
*/
|
||||||
|
export function getBoundaries(p: Persona): string[] {
|
||||||
|
return p.values ?? [];
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2020",
|
||||||
|
"module": "nodenext",
|
||||||
|
"moduleResolution": "nodenext",
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"lib": ["es2020"],
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "index.ts"]
|
||||||
|
}
|
||||||
Reference in new issue
Block a user