mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
feat: SOUL.md, IDENTITY.md, TOOLS.md
This commit is contained in:
+36
-4
@@ -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[]
|
||||
|
||||
@@ -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
|
||||
- Don’t 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')}
|
||||
|
||||
|
||||
@@ -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 \"$@\"","--"]
|
||||
|
||||
@@ -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)_
|
||||
|
||||
---
|
||||
@@ -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.
|
||||
|
||||
---
|
||||
@@ -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,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{
|
||||
|
||||
Reference in New Issue
Block a user