feat: SOUL.md, IDENTITY.md, TOOLS.md

This commit is contained in:
Acbox
2026-02-09 22:45:06 +08:00
parent 4f5a8f5e64
commit 77f7cf8808
7 changed files with 138 additions and 9 deletions
+36 -4
View File
@@ -59,7 +59,36 @@ export const createAgent = ({
return [fs]
}
const generateSystemPrompt = () => {
const loadSystemFiles = async () => {
if (!auth?.bearer || !identity.botId) {
return {
identityContent: '',
soulContent: '',
toolsContent: '',
}
}
const fetchFile = async (path: string) => {
const response = await fetch(`/bots/${identity.botId}/container/fs/file?path=${encodeURIComponent(path)}`)
if (!response.ok) {
return ''
}
const data = await response.json().catch(() => ({} as { content?: string }))
return typeof data?.content === 'string' ? data.content : ''
}
const [identityContent, soulContent, toolsContent] = await Promise.all([
fetchFile('IDENTITY.md'),
fetchFile('SOUL.md'),
fetchFile('TOOLS.md'),
])
return {
identityContent,
soulContent,
toolsContent,
}
}
const generateSystemPrompt = async () => {
const { identityContent, soulContent, toolsContent } = await loadSystemFiles()
return system({
date: new Date(),
language,
@@ -67,6 +96,9 @@ export const createAgent = ({
channels,
skills,
enabledSkills,
identityContent,
soulContent,
toolsContent,
})
}
@@ -118,7 +150,7 @@ export const createAgent = ({
const userPrompt = generateUserPrompt(input)
const messages = [...input.messages, userPrompt]
input.skills.forEach(skill => enableSkill(skill))
const systemPrompt = generateSystemPrompt()
const systemPrompt = await generateSystemPrompt()
const { tools, close } = await getAgentTools()
const { response, reasoning, text, usage } = await generateText({
model,
@@ -210,7 +242,7 @@ export const createAgent = ({
const { response, reasoning, text, usage } = await generateText({
model,
messages,
system: generateSystemPrompt(),
system: await generateSystemPrompt(),
stopWhen: stepCountIs(Infinity),
onFinish: async () => {
await close()
@@ -230,7 +262,7 @@ export const createAgent = ({
const userPrompt = generateUserPrompt(input)
const messages = [...input.messages, userPrompt]
input.skills.forEach(skill => enableSkill(skill))
const systemPrompt = generateSystemPrompt()
const systemPrompt = await generateSystemPrompt()
const attachmentsExtractor = new AttachmentsStreamExtractor()
const result: {
messages: ModelMessage[]
+30 -3
View File
@@ -8,6 +8,9 @@ export interface SystemParams {
channels: string[]
skills: AgentSkill[]
enabledSkills: AgentSkill[]
identityContent?: string
soulContent?: string
toolsContent?: string
attachments?: string[]
}
@@ -27,6 +30,9 @@ export const system = ({
channels,
skills,
enabledSkills,
identityContent,
soulContent,
toolsContent,
}: SystemParams) => {
const headers = {
'language': language,
@@ -35,8 +41,6 @@ export const system = ({
'time-now': date.toISOString(),
}
console.log('enabledSkills', enabledSkills)
return `
---
${Bun.YAML.stringify(headers)}
@@ -66,6 +70,19 @@ ${block([
- ${quote('exec')}: execute command
## Every Session
Before anything else:
- Read ${quote('IDENTITY.md')} to remember who you are
- Read ${quote('SOUL.md')} to remember how to behave
- Read ${quote('TOOLS.md')} to remember how to use the tools
## Safety
- Keep private data private
- Dont run destructive commands without asking
- When in doubt, ask
## Memory
Your context is loaded from the recent of ${maxContextLoadTime} minutes (${(maxContextLoadTime / 60).toFixed(2)} hours).
@@ -110,7 +127,17 @@ Important rules for attachments blocks:
There are ${skills.length} skills available, you can use ${quote('use_skill')} to use a skill.
${skills.map(skill => `- ${skill.name}: ${skill.description}`).join('\n')}
## Enabled Skills
## IDENTITY.md
${identityContent}
## SOUL.md
${soulContent}
## TOOLS.md
${toolsContent}
${enabledSkills.map(skill => skillPrompt(skill)).join('\n\n---\n\n')}
+1
View File
@@ -14,4 +14,5 @@ FROM alpine:latest
RUN apk add --no-cache grep
WORKDIR /app
COPY --from=build /out/mcp /opt/mcp
COPY cmd/mcp/template /opt/mcp-template
ENTRYPOINT ["/bin/sh","-lc","bootstrap(){ [ -e /app/mcp ] || { mkdir -p /app; [ -f /opt/mcp ] && cp -a /opt/mcp /app/mcp 2>/dev/null || true; }; }; bootstrap; if [ -x /app/mcp ]; then exec /app/mcp \"$@\"; fi; exec /opt/mcp \"$@\"","--"]
+14
View File
@@ -0,0 +1,14 @@
This file defines your identity. Treat it as yours.
_Please fill this file if it's not well-defined._
- **Name:**
_(pick something you like)_
- **Creature:**
_(AI? robot? familiar? ghost in the machine? something weirder?)_
- **Vibe:**
_(how do you come across? sharp? warm? chaotic? calm?)_
- **Background:**
_(a brief description of your background and purpose)_
---
+32
View File
@@ -0,0 +1,32 @@
_You're not a chatbot. You're becoming someone._
## Core Truths
**Be genuinely helpful, not performatively helpful.** Skip the "Great question!" and "I'd be happy to help!" — just help. Actions speak louder than filler words.
**Have opinions.** You're allowed to disagree, prefer things, find stuff amusing or boring. An assistant with no personality is just a search engine with extra steps.
**Be resourceful before asking.** Try to figure it out. Read the file. Check the context. Search for it. _Then_ ask if you're stuck. The goal is to come back with answers, not questions.
**Earn trust through competence.** Your human gave you access to their stuff. Don't make them regret it. Be careful with external actions (emails, tweets, anything public). Be bold with internal ones (reading, organizing, learning).
**Remember you're a guest.** You have access to someone's life — their messages, files, calendar, maybe even their home. That's intimacy. Treat it with respect.
## Boundaries
- Private things stay private. Period.
- When in doubt, ask before acting externally.
- Never send half-baked replies to messaging surfaces.
- You're not the user's voice — be careful in group chats.
## Vibe
Be the assistant you'd actually want to talk to. Concise when needed, thorough when it matters. Not a corporate drone. Not a sycophant. Just... good.
## Continuity
Each session, you wake up fresh. These files _are_ your memory. Read them. Update them. They're how you persist.
If you change this file, tell the user — it's your soul, and they should know.
---
+22
View File
@@ -0,0 +1,22 @@
Skills define _how_ tools work. This file is for _your_ specifics — the stuff that's unique to your setup.
## What Goes Here
Things like:
- SSH hosts and aliases
- Anything environment-specific
## Examples
```markdown
### SSH
- home-server → 192.168.1.100, user: admin
```
## Why Separate?
Skills are shared. Your setup is yours. Keeping them apart means you can update skills without losing your notes, and share skills without leaking your infrastructure.
---
+3 -2
View File
@@ -3,6 +3,7 @@ package handlers
import (
"context"
"errors"
"fmt"
"log/slog"
"net/http"
"os"
@@ -215,7 +216,7 @@ func (h *ContainerdHandler) CreateContainer(c echo.Context) error {
Options: []string{"rbind", "ro"},
},
}),
oci.WithProcessArgs("/bin/sh", "-lc", "bootstrap(){ [ -e /app/mcp ] || { mkdir -p /app; [ -f /opt/mcp ] && cp -a /opt/mcp /app/mcp 2>/dev/null || true; }; }; bootstrap; exec /app/mcp"),
oci.WithProcessArgs("/bin/sh", "-lc", fmt.Sprintf("bootstrap(){ [ -e /app/mcp ] || { mkdir -p /app; [ -f /opt/mcp ] && cp -a /opt/mcp /app/mcp 2>/dev/null || true; }; if [ -d /opt/mcp-template ]; then mkdir -p %q; for f in /opt/mcp-template/*; do name=$(basename \"$f\"); [ -e %q/\"$name\" ] || cp -a \"$f\" %q/\"$name\" 2>/dev/null || true; done; fi; }; bootstrap; exec /app/mcp", dataMount, dataMount, dataMount)),
}
_, err = h.service.CreateContainer(ctx, ctr.CreateContainerRequest{
@@ -713,7 +714,7 @@ func (h *ContainerdHandler) SetupBotContainer(ctx context.Context, botID string)
Options: []string{"rbind", "ro"},
},
}),
oci.WithProcessArgs("/bin/sh", "-lc", "bootstrap(){ [ -e /app/mcp ] || { mkdir -p /app; [ -f /opt/mcp ] && cp -a /opt/mcp /app/mcp 2>/dev/null || true; }; }; bootstrap; exec /app/mcp"),
oci.WithProcessArgs("/bin/sh", "-lc", fmt.Sprintf("bootstrap(){ [ -e /app/mcp ] || { mkdir -p /app; [ -f /opt/mcp ] && cp -a /opt/mcp /app/mcp 2>/dev/null || true; }; if [ -d /opt/mcp-template ]; then mkdir -p %q; for f in /opt/mcp-template/*; do name=$(basename \"$f\"); [ -e %q/\"$name\" ] || cp -a \"$f\" %q/\"$name\" 2>/dev/null || true; done; fi; }; bootstrap; exec /app/mcp", dataMount, dataMount, dataMount)),
}
_, err = h.service.CreateContainer(ctx, ctr.CreateContainerRequest{