dsh-soul 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -13
- package/README_EN.md +107 -0
- package/client/index.mjs +166 -64
- package/index.mjs +356 -69
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
# dsh-soul
|
|
2
2
|
|
|
3
|
+
[简体中文](./README.md) | [English](./README_EN.md)
|
|
4
|
+
|
|
3
5
|
DeepSeek Harness 个性化设置插件,用于配置 Agent 的昵称、回复风格、语调和自定义指令。
|
|
4
6
|
|
|
5
7
|
## 功能
|
|
6
8
|
|
|
7
9
|
- Web UI 个性化设置页面
|
|
8
10
|
- 启用或禁用个性化设置
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
11
|
+
- 「关于你」:设置用户昵称、职业和介绍,回复时结合你的背景
|
|
12
|
+
- 选择回复风格和语调(合并为单一选项):`professional`(专业严谨)、`casual`(轻松自然)、`humorous`(幽默风趣)、`roast`(吐槽达人)、`efficient`(高效干练)
|
|
13
|
+
- 特质微调(在风格和语调的基础上叠加):
|
|
14
|
+
- 标题和列表:`default`(默认)、`more`(增强,采用清晰格式和列表结构)、`less`(减弱,使用更多段落文本)
|
|
15
|
+
- 表情符号:`default`(默认)、`more`(增强,使用较多表情符号)、`less`(减弱,尽量减少使用表情符号)
|
|
16
|
+
- 选择输出语言(Agent 回复语言 + `/soul` 命令输出语言):中文 / English
|
|
17
|
+
- 输入自定义指令
|
|
18
|
+
- Agent 可调用工具 `set_persona`,让模型在对话中直接调整人设
|
|
13
19
|
- 配置持久化保存
|
|
14
20
|
- 配置更新后同步到所有活动 Agent
|
|
15
21
|
|
|
@@ -44,18 +50,14 @@ dsh --profile web web
|
|
|
44
50
|
|
|
45
51
|
配置保存后会同步到所有活动 Agent,当前会话下一次请求即可使用最新配置。
|
|
46
52
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
配置文件位置:
|
|
53
|
+
Agent 也可以通过工具 `set_persona` 在对话中直接调整你的人设(昵称、回复风格和语调、特质、回复语言、自定义指令)。模型只会在明确请求改变称呼、语气、风格或语言时调用该工具。
|
|
50
54
|
|
|
51
|
-
|
|
52
|
-
$DSH_HOME/soul-config.json
|
|
53
|
-
```
|
|
55
|
+
## 配置文件
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
插件将配置保存到 DSH 的用户数据目录,文件名为:
|
|
56
58
|
|
|
57
59
|
```text
|
|
58
|
-
|
|
60
|
+
soul-config.json
|
|
59
61
|
```
|
|
60
62
|
|
|
61
63
|
配置示例:
|
|
@@ -64,8 +66,10 @@ $DSH_HOME/soul-config.json
|
|
|
64
66
|
{
|
|
65
67
|
"enabled": true,
|
|
66
68
|
"nickname": "小明",
|
|
69
|
+
"occupation": "软件工程师",
|
|
70
|
+
"bio": "对编程和技术感兴趣",
|
|
67
71
|
"style": "professional",
|
|
68
|
-
"
|
|
72
|
+
"language": "zh",
|
|
69
73
|
"customInstructions": "请保持简洁,优先给出结论。"
|
|
70
74
|
}
|
|
71
75
|
```
|
package/README_EN.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
[简体中文](./README.md) | [English](./README_EN.md)
|
|
2
|
+
|
|
3
|
+
# dsh-soul
|
|
4
|
+
|
|
5
|
+
A personalization plugin for DeepSeek Harness (DSH). Configure your agent's nickname, reply style, tone, and custom instructions.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Personalization settings page in the Web UI
|
|
10
|
+
- Enable or disable personalization
|
|
11
|
+
- "About you": set your nickname, occupation and bio so replies fit your background
|
|
12
|
+
- Combined style & tone option: `professional`, `casual`, `humorous`, `roast`, `efficient`
|
|
13
|
+
- Trait fine-tuning (layered on top of style & tone):
|
|
14
|
+
- Headings & lists: `default`, `more` (clear formatting with headings and lists), `less` (more paragraph text)
|
|
15
|
+
- Emoji: `default`, `more` (frequent emoji usage), `less` (minimal emoji usage)
|
|
16
|
+
- Output language (agent reply language + `/soul` command output language): Chinese or English
|
|
17
|
+
- Custom instructions
|
|
18
|
+
- Agent-callable tool `set_persona` to let the model adjust persona during a conversation
|
|
19
|
+
- Configuration persisted to disk
|
|
20
|
+
- Configuration synced to all active agents after every update
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```powershell
|
|
25
|
+
dsh plugin --profile web add dsh-soul
|
|
26
|
+
dsh --profile web web
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The plugin is registered via `cordis.patch.yml`:
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
- insert:
|
|
33
|
+
- id: soul
|
|
34
|
+
name: dsh-soul
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
After starting DSH, open the **Personalization** section on the settings page, edit the configuration and click **Save Settings**.
|
|
40
|
+
|
|
41
|
+
Slash commands are also available:
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
/soul show Show current configuration
|
|
45
|
+
/soul reset Reset configuration
|
|
46
|
+
/soul enable Enable personalization
|
|
47
|
+
/soul disable Disable personalization
|
|
48
|
+
/soul Bob Set nickname
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Once saved, the configuration is synced to all active agents and takes effect on the next request in the current session.
|
|
52
|
+
|
|
53
|
+
The agent can also use the `set_persona` tool to adjust your persona (nickname, style & tone, traits, reply language, custom instructions) during a conversation. The model only invokes this tool when you explicitly ask to change how it addresses or responds to you.
|
|
54
|
+
|
|
55
|
+
## Configuration File
|
|
56
|
+
|
|
57
|
+
The configuration is stored in the DSH user data directory:
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
soul-config.json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Example:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"enabled": true,
|
|
68
|
+
"nickname": "Bob",
|
|
69
|
+
"occupation": "Software Engineer",
|
|
70
|
+
"bio": "Interested in programming and technology",
|
|
71
|
+
"style": "professional",
|
|
72
|
+
"language": "en",
|
|
73
|
+
"customInstructions": "Be concise and lead with the conclusion."
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## How It Works
|
|
78
|
+
|
|
79
|
+
The plugin compiles the nickname, style, tone and custom instructions into a system prompt via `compilePrompt()` and registers it with DSH:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
spCtx.systemPrompt.section({
|
|
83
|
+
name: 'soul:persona',
|
|
84
|
+
order: 0,
|
|
85
|
+
text: () => compilePrompt(configCache || DEFAULT_CONFIG)
|
|
86
|
+
})
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
After each update, the plugin iterates over all active agents and calls `agent.inject()` with a standard `UserMessage`:
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
agent.inject(createUserMessage({
|
|
93
|
+
content: [{ type: 'text', text: prompt }],
|
|
94
|
+
source: {
|
|
95
|
+
kind: 'plugin',
|
|
96
|
+
plugin: 'dsh-soul',
|
|
97
|
+
form: 'snapshot',
|
|
98
|
+
sections: [{ name: 'soul:persona', text: prompt }]
|
|
99
|
+
}
|
|
100
|
+
}))
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`agent.inject()` places the latest configuration into the agent's pending context so it takes effect on the next request. It does not trigger a new request and does not modify message history.
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
MIT License
|
package/client/index.mjs
CHANGED
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
//
|
|
3
3
|
// 在设置页面添加「个性化」栏目:
|
|
4
4
|
// - 启用/禁用个性化设置
|
|
5
|
-
// -
|
|
6
|
-
// - 选择语调
|
|
5
|
+
// - 选择回复风格和语调
|
|
7
6
|
// - 输入自定义指令
|
|
8
|
-
// - 查看示例
|
|
9
7
|
//
|
|
10
8
|
// 纯 JS + React jsx-runtime 手写(不依赖 JSX 构建),开箱即用。
|
|
11
9
|
// 注意:dsh web 使用 React 17+ 的 jsx-runtime,children 必须放在 props.children
|
|
@@ -41,8 +39,15 @@ window.__ModuleLoader__.load({
|
|
|
41
39
|
const INITIAL = {
|
|
42
40
|
enabled: true,
|
|
43
41
|
nickname: '',
|
|
42
|
+
// 关于你:用户职业 / 用户介绍
|
|
43
|
+
occupation: '',
|
|
44
|
+
bio: '',
|
|
45
|
+
// 回复风格和语调(合并为单一选项)
|
|
44
46
|
style: 'professional',
|
|
45
|
-
|
|
47
|
+
// 特质:标题和列表 / 表情符号
|
|
48
|
+
headingLists: 'default',
|
|
49
|
+
emoji: 'default',
|
|
50
|
+
language: 'zh',
|
|
46
51
|
customInstructions: '',
|
|
47
52
|
loading: false,
|
|
48
53
|
saving: false,
|
|
@@ -101,8 +106,12 @@ window.__ModuleLoader__.load({
|
|
|
101
106
|
this.store.update(s => {
|
|
102
107
|
s.enabled = payload.config.enabled
|
|
103
108
|
s.nickname = payload.config.nickname || ''
|
|
109
|
+
s.occupation = payload.config.occupation || ''
|
|
110
|
+
s.bio = payload.config.bio || ''
|
|
104
111
|
s.style = payload.config.style
|
|
105
|
-
s.
|
|
112
|
+
s.headingLists = payload.config.headingLists || 'default'
|
|
113
|
+
s.emoji = payload.config.emoji || 'default'
|
|
114
|
+
s.language = payload.config.language || 'zh'
|
|
106
115
|
s.customInstructions = payload.config.customInstructions
|
|
107
116
|
s.loading = false
|
|
108
117
|
})
|
|
@@ -135,8 +144,12 @@ window.__ModuleLoader__.load({
|
|
|
135
144
|
this.store.update(s => {
|
|
136
145
|
s.enabled = payload.config.enabled
|
|
137
146
|
s.nickname = payload.config.nickname || ''
|
|
147
|
+
s.occupation = payload.config.occupation || ''
|
|
148
|
+
s.bio = payload.config.bio || ''
|
|
138
149
|
s.style = payload.config.style
|
|
139
|
-
s.
|
|
150
|
+
s.headingLists = payload.config.headingLists || 'default'
|
|
151
|
+
s.emoji = payload.config.emoji || 'default'
|
|
152
|
+
s.language = payload.config.language || 'zh'
|
|
140
153
|
s.customInstructions = payload.config.customInstructions
|
|
141
154
|
s.saving = false
|
|
142
155
|
})
|
|
@@ -165,8 +178,12 @@ window.__ModuleLoader__.load({
|
|
|
165
178
|
this.store.update(s => {
|
|
166
179
|
s.enabled = payload.config.enabled
|
|
167
180
|
s.nickname = payload.config.nickname || ''
|
|
181
|
+
s.occupation = payload.config.occupation || ''
|
|
182
|
+
s.bio = payload.config.bio || ''
|
|
168
183
|
s.style = payload.config.style
|
|
169
|
-
s.
|
|
184
|
+
s.headingLists = payload.config.headingLists || 'default'
|
|
185
|
+
s.emoji = payload.config.emoji || 'default'
|
|
186
|
+
s.language = payload.config.language || 'zh'
|
|
170
187
|
s.customInstructions = payload.config.customInstructions
|
|
171
188
|
s.saving = false
|
|
172
189
|
})
|
|
@@ -195,16 +212,17 @@ window.__ModuleLoader__.load({
|
|
|
195
212
|
'.soul-field label{display:block;margin-bottom:6px;font-size:13px;font-weight:500;color:var(--dsw-alias-label-secondary)}',
|
|
196
213
|
'.soul-field select,.soul-field textarea,.soul-field input[type=text]{width:100%;padding:8px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;background:var(--dsw-alias-bg-l1);color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5;box-sizing:border-box}',
|
|
197
214
|
'.soul-field textarea{min-height:100px;resize:vertical}',
|
|
215
|
+
'.soul-field textarea.soul-textarea-sm{min-height:60px}',
|
|
216
|
+
'.soul-group-title{margin:20px 0 12px 0;padding-bottom:8px;border-bottom:1px solid var(--dsw-alias-border-l2);font-size:13px;font-weight:600;color:var(--dsw-alias-label-secondary)}',
|
|
198
217
|
'.soul-field select:focus,.soul-field textarea:focus,.soul-field input[type=text]:focus{outline:none;border-color:var(--dsw-alias-border-focus);box-shadow:0 0 0 2px var(--dsw-alias-border-focus-alpha)}',
|
|
199
218
|
'.soul-toggle{display:flex;align-items:center;gap:8px;margin-bottom:16px}',
|
|
200
219
|
'.soul-toggle label{margin:0;font-size:13px;color:var(--dsw-alias-label-primary)}',
|
|
201
220
|
'.soul-buttons{display:flex;gap:8px;margin-top:16px}',
|
|
202
|
-
'.soul-examples{margin-top:16px;padding:12px;background:var(--dsw-alias-bg-l2);border-radius:6px;width:100%;box-sizing:border-box}',
|
|
203
|
-
'.soul-examples h4{margin:0 0 8px 0;font-size:13px;color:var(--dsw-alias-label-secondary)}',
|
|
204
|
-
'.soul-example{padding:8px 12px;margin-bottom:8px;background:var(--dsw-alias-bg-l1);border-radius:4px;cursor:pointer;font-size:12px;color:var(--dsw-alias-label-primary)}',
|
|
205
|
-
'.soul-example:hover{background:var(--dsw-alias-interactive-bg-hover)}',
|
|
206
|
-
'.soul-example:last-child{margin-bottom:0}',
|
|
207
221
|
'.soul-error{margin-top:8px;padding:8px 12px;background:var(--dsw-alias-bg-danger);border:1px solid var(--dsw-alias-border-danger);border-radius:6px;color:var(--dsw-alias-label-danger);font-size:12px;width:100%;box-sizing:border-box}',
|
|
222
|
+
'.soul-hint{position:relative;display:inline-flex;align-items:center;margin-left:6px;color:var(--dsw-alias-label-secondary);cursor:help;vertical-align:middle}',
|
|
223
|
+
'.soul-hint:hover{color:var(--dsw-alias-label-primary)}',
|
|
224
|
+
'.soul-hint-tip{position:absolute;bottom:calc(100% + 8px);left:0;padding:8px 10px;background:#ffffff;border:1px solid rgba(0,0,0,0.1);border-radius:6px;font-size:12px;font-weight:400;line-height:1.5;color:rgba(0,0,0,0.85);white-space:normal;width:max-content;max-width:260px;text-align:left;opacity:0;visibility:hidden;transition:opacity .15s ease;pointer-events:none;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,0.15)}',
|
|
225
|
+
'.soul-hint:hover .soul-hint-tip{opacity:1;visibility:visible}',
|
|
208
226
|
'.soul-status{margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary)}',
|
|
209
227
|
'.soul-toast{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);padding:8px 16px;border-radius:6px;font-size:13px;font-weight:500;z-index:10000;box-shadow:0 2px 8px rgba(0,0,0,0.15);pointer-events:none;text-align:center;min-width:auto;white-space:nowrap}',
|
|
210
228
|
'.soul-toast-success{background:#f6ffed;border:1px solid #b7eb8f;color:#52c41a}',
|
|
@@ -232,38 +250,70 @@ window.__ModuleLoader__.load({
|
|
|
232
250
|
// 组件
|
|
233
251
|
// -------------------------------------------------------------------------
|
|
234
252
|
|
|
235
|
-
|
|
253
|
+
// 回复风格和语调(合并为单一选项)
|
|
254
|
+
const STYLE_TONE_OPTIONS = [
|
|
236
255
|
{ value: 'professional', label: '专业严谨' },
|
|
237
256
|
{ value: 'casual', label: '轻松自然' },
|
|
238
|
-
{ value: 'friendly', label: '友好亲切' },
|
|
239
257
|
{ value: 'humorous', label: '幽默风趣' },
|
|
240
|
-
{ value: '
|
|
258
|
+
{ value: 'roast', label: '吐槽达人' },
|
|
259
|
+
{ value: 'efficient', label: '高效干练' }
|
|
241
260
|
]
|
|
242
261
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
{ value: '
|
|
246
|
-
{ value: '
|
|
247
|
-
{ value: '
|
|
248
|
-
{ value: 'calm', label: '平静沉稳' }
|
|
262
|
+
// 特质:标题和列表
|
|
263
|
+
const HEADING_LISTS_OPTIONS = [
|
|
264
|
+
{ value: 'default', label: '默认' },
|
|
265
|
+
{ value: 'more', label: '增强(采用清晰格式和列表结构)' },
|
|
266
|
+
{ value: 'less', label: '减弱(使用更多段落文本)' }
|
|
249
267
|
]
|
|
250
268
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
{
|
|
254
|
-
{
|
|
255
|
-
{
|
|
269
|
+
// 特质:表情符号
|
|
270
|
+
const EMOJI_OPTIONS = [
|
|
271
|
+
{ value: 'default', label: '默认' },
|
|
272
|
+
{ value: 'more', label: '增强(使用较多表情符号)' },
|
|
273
|
+
{ value: 'less', label: '减弱(尽量减少使用表情符号)' }
|
|
256
274
|
]
|
|
257
275
|
|
|
276
|
+
// 选项用原生语言显示,两种 UI 语言下均自解释
|
|
277
|
+
const LANGUAGE_OPTIONS = [
|
|
278
|
+
{ value: 'zh', label: '中文' },
|
|
279
|
+
{ value: 'en', label: 'English' }
|
|
280
|
+
]
|
|
281
|
+
|
|
282
|
+
// 提示词小图标:hover 展示说明文字
|
|
283
|
+
function SoulHint(props) {
|
|
284
|
+
return e('span', { className: 'soul-hint', 'aria-label': props.text },
|
|
285
|
+
e('svg', {
|
|
286
|
+
viewBox: '0 0 24 24',
|
|
287
|
+
width: 14,
|
|
288
|
+
height: 14,
|
|
289
|
+
fill: 'none',
|
|
290
|
+
stroke: 'currentColor',
|
|
291
|
+
strokeWidth: 2,
|
|
292
|
+
strokeLinecap: 'round',
|
|
293
|
+
strokeLinejoin: 'round',
|
|
294
|
+
'aria-hidden': true
|
|
295
|
+
},
|
|
296
|
+
e('circle', { cx: 12, cy: 12, r: 10 }),
|
|
297
|
+
e('line', { x1: 12, y1: 16, x2: 12, y2: 12 }),
|
|
298
|
+
e('line', { x1: 12, y1: 8, x2: 12.01, y2: 8 })
|
|
299
|
+
),
|
|
300
|
+
e('span', { className: 'soul-hint-tip', role: 'tooltip' }, props.text)
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
|
|
258
304
|
function SoulSettings(props) {
|
|
259
305
|
const { useSoulController, controller } = props
|
|
260
306
|
const state = useSoulController((state) => state)
|
|
261
|
-
const { enabled, nickname, style,
|
|
307
|
+
const { enabled, nickname, occupation, bio, style, headingLists, emoji, language, customInstructions, loading, saving, error } = state
|
|
262
308
|
|
|
263
309
|
const [localEnabled, setLocalEnabled] = React.useState(enabled)
|
|
264
310
|
const [localNickname, setLocalNickname] = React.useState(nickname || '')
|
|
311
|
+
const [localOccupation, setLocalOccupation] = React.useState(occupation || '')
|
|
312
|
+
const [localBio, setLocalBio] = React.useState(bio || '')
|
|
265
313
|
const [localStyle, setLocalStyle] = React.useState(style)
|
|
266
|
-
const [
|
|
314
|
+
const [localHeadingLists, setLocalHeadingLists] = React.useState(headingLists)
|
|
315
|
+
const [localEmoji, setLocalEmoji] = React.useState(emoji)
|
|
316
|
+
const [localLanguage, setLocalLanguage] = React.useState(language || 'zh')
|
|
267
317
|
const [localInstructions, setLocalInstructions] = React.useState(customInstructions)
|
|
268
318
|
const [showSuccess, setShowSuccess] = React.useState(false)
|
|
269
319
|
const [showResetSuccess, setShowResetSuccess] = React.useState(false)
|
|
@@ -272,10 +322,14 @@ window.__ModuleLoader__.load({
|
|
|
272
322
|
React.useEffect(() => {
|
|
273
323
|
setLocalEnabled(enabled)
|
|
274
324
|
setLocalNickname(nickname || '')
|
|
325
|
+
setLocalOccupation(occupation || '')
|
|
326
|
+
setLocalBio(bio || '')
|
|
275
327
|
setLocalStyle(style)
|
|
276
|
-
|
|
328
|
+
setLocalHeadingLists(headingLists)
|
|
329
|
+
setLocalEmoji(emoji)
|
|
330
|
+
setLocalLanguage(language || 'zh')
|
|
277
331
|
setLocalInstructions(customInstructions)
|
|
278
|
-
}, [enabled, nickname, style,
|
|
332
|
+
}, [enabled, nickname, occupation, bio, style, headingLists, emoji, language, customInstructions])
|
|
279
333
|
|
|
280
334
|
// 保存成功后显示提示,2秒后自动消失
|
|
281
335
|
React.useEffect(() => {
|
|
@@ -298,8 +352,12 @@ window.__ModuleLoader__.load({
|
|
|
298
352
|
await controller.saveConfig({
|
|
299
353
|
enabled: localEnabled,
|
|
300
354
|
nickname: localNickname,
|
|
355
|
+
occupation: localOccupation,
|
|
356
|
+
bio: localBio,
|
|
301
357
|
style: localStyle,
|
|
302
|
-
|
|
358
|
+
headingLists: localHeadingLists,
|
|
359
|
+
emoji: localEmoji,
|
|
360
|
+
language: localLanguage,
|
|
303
361
|
customInstructions: localInstructions
|
|
304
362
|
})
|
|
305
363
|
setShowSuccess(true)
|
|
@@ -311,10 +369,6 @@ window.__ModuleLoader__.load({
|
|
|
311
369
|
setShowResetSuccess(true)
|
|
312
370
|
}
|
|
313
371
|
|
|
314
|
-
const handleExampleClick = (example) => {
|
|
315
|
-
setLocalInstructions(example.value)
|
|
316
|
-
}
|
|
317
|
-
|
|
318
372
|
return e('div', { className: 'soul-section' },
|
|
319
373
|
e('h3', null, '个性化设置'),
|
|
320
374
|
e('div', { className: 'soul-toggle' },
|
|
@@ -328,6 +382,7 @@ window.__ModuleLoader__.load({
|
|
|
328
382
|
),
|
|
329
383
|
|
|
330
384
|
localEnabled && e(Fragment, null,
|
|
385
|
+
e('div', { className: 'soul-group-title' }, '关于你'),
|
|
331
386
|
e('div', { className: 'soul-field' },
|
|
332
387
|
e('label', { htmlFor: 'soul-nickname' }, '用户昵称'),
|
|
333
388
|
e('input', {
|
|
@@ -338,52 +393,103 @@ window.__ModuleLoader__.load({
|
|
|
338
393
|
placeholder: '输入你的昵称,回复时会称呼你'
|
|
339
394
|
})
|
|
340
395
|
),
|
|
341
|
-
|
|
342
396
|
e('div', { className: 'soul-field' },
|
|
343
|
-
e('label', { htmlFor: 'soul-
|
|
397
|
+
e('label', { htmlFor: 'soul-occupation' }, '用户职业'),
|
|
398
|
+
e('input', {
|
|
399
|
+
type: 'text',
|
|
400
|
+
id: 'soul-occupation',
|
|
401
|
+
value: localOccupation,
|
|
402
|
+
onChange: (ev) => setLocalOccupation(ev.target.value),
|
|
403
|
+
placeholder: '例如:软件工程师、学生、产品经理'
|
|
404
|
+
})
|
|
405
|
+
),
|
|
406
|
+
e('div', { className: 'soul-field' },
|
|
407
|
+
e('label', { htmlFor: 'soul-bio' }, '用户介绍'),
|
|
408
|
+
e('textarea', {
|
|
409
|
+
id: 'soul-bio',
|
|
410
|
+
className: 'soul-textarea-sm',
|
|
411
|
+
value: localBio,
|
|
412
|
+
onChange: (ev) => setLocalBio(ev.target.value),
|
|
413
|
+
placeholder: '简单介绍自己,让回复更贴合你的背景'
|
|
414
|
+
})
|
|
415
|
+
),
|
|
416
|
+
|
|
417
|
+
e('div', { className: 'soul-group-title' }, '特质'),
|
|
418
|
+
e('div', { className: 'soul-field' },
|
|
419
|
+
e('label', { htmlFor: 'soul-style' },
|
|
420
|
+
'回复风格和语调',
|
|
421
|
+
e(SoulHint, { text: '设置 Agent 回复你的风格和语调。这不会影响 Agent 的功能。' })
|
|
422
|
+
),
|
|
344
423
|
e('select', {
|
|
345
424
|
id: 'soul-style',
|
|
346
425
|
value: localStyle,
|
|
347
426
|
onChange: (ev) => setLocalStyle(ev.target.value)
|
|
348
427
|
},
|
|
349
|
-
...
|
|
428
|
+
...STYLE_TONE_OPTIONS.map(opt =>
|
|
350
429
|
e('option', { key: opt.value, value: opt.value }, opt.label)
|
|
351
430
|
)
|
|
352
431
|
)
|
|
353
432
|
),
|
|
354
|
-
|
|
433
|
+
|
|
355
434
|
e('div', { className: 'soul-field' },
|
|
356
|
-
e('label', { htmlFor: 'soul-
|
|
435
|
+
e('label', { htmlFor: 'soul-headingLists' },
|
|
436
|
+
'标题和列表',
|
|
437
|
+
e(SoulHint, { text: '在回复风格和语调的基础上选择额外的自定义特质项。控制回答中标题和列表的使用程度。' })
|
|
438
|
+
),
|
|
357
439
|
e('select', {
|
|
358
|
-
id: 'soul-
|
|
359
|
-
value:
|
|
360
|
-
onChange: (ev) =>
|
|
440
|
+
id: 'soul-headingLists',
|
|
441
|
+
value: localHeadingLists,
|
|
442
|
+
onChange: (ev) => setLocalHeadingLists(ev.target.value)
|
|
361
443
|
},
|
|
362
|
-
...
|
|
444
|
+
...HEADING_LISTS_OPTIONS.map(opt =>
|
|
363
445
|
e('option', { key: opt.value, value: opt.value }, opt.label)
|
|
364
446
|
)
|
|
365
447
|
)
|
|
366
448
|
),
|
|
367
|
-
|
|
449
|
+
|
|
368
450
|
e('div', { className: 'soul-field' },
|
|
369
|
-
e('label', { htmlFor: 'soul-
|
|
451
|
+
e('label', { htmlFor: 'soul-emoji' },
|
|
452
|
+
'表情符号',
|
|
453
|
+
e(SoulHint, { text: '在回复风格和语调的基础上选择额外的自定义特质项。控制表情符号的使用程度。' })
|
|
454
|
+
),
|
|
455
|
+
e('select', {
|
|
456
|
+
id: 'soul-emoji',
|
|
457
|
+
value: localEmoji,
|
|
458
|
+
onChange: (ev) => setLocalEmoji(ev.target.value)
|
|
459
|
+
},
|
|
460
|
+
...EMOJI_OPTIONS.map(opt =>
|
|
461
|
+
e('option', { key: opt.value, value: opt.value }, opt.label)
|
|
462
|
+
)
|
|
463
|
+
)
|
|
464
|
+
),
|
|
465
|
+
|
|
466
|
+
e('div', { className: 'soul-field' },
|
|
467
|
+
e('label', { htmlFor: 'soul-language' },
|
|
468
|
+
'输出语言',
|
|
469
|
+
e(SoulHint, { text: '设置 Agent 回复你使用的语言,同时影响 /soul 命令的输出语言。' })
|
|
470
|
+
),
|
|
471
|
+
e('select', {
|
|
472
|
+
id: 'soul-language',
|
|
473
|
+
value: localLanguage,
|
|
474
|
+
onChange: (ev) => setLocalLanguage(ev.target.value)
|
|
475
|
+
},
|
|
476
|
+
...LANGUAGE_OPTIONS.map(opt =>
|
|
477
|
+
e('option', { key: opt.value, value: opt.value }, opt.label)
|
|
478
|
+
)
|
|
479
|
+
)
|
|
480
|
+
),
|
|
481
|
+
|
|
482
|
+
e('div', { className: 'soul-field' },
|
|
483
|
+
e('label', { htmlFor: 'soul-instructions' },
|
|
484
|
+
'自定义指令',
|
|
485
|
+
e(SoulHint, { text: '补充角色、习惯等个性化要求。建议不要重复设置回复风格和语调类似的话术,以免与上方选项冲突。' })
|
|
486
|
+
),
|
|
370
487
|
e('textarea', {
|
|
371
488
|
id: 'soul-instructions',
|
|
372
489
|
value: localInstructions,
|
|
373
490
|
onChange: (ev) => setLocalInstructions(ev.target.value),
|
|
374
|
-
placeholder: '
|
|
491
|
+
placeholder: '输入自定义指令,可以是其他行为、回复风格和语调等偏好设置'
|
|
375
492
|
})
|
|
376
|
-
),
|
|
377
|
-
|
|
378
|
-
e('div', { className: 'soul-examples' },
|
|
379
|
-
e('h4', null, '💡 示例指令(点击使用)'),
|
|
380
|
-
...EXAMPLES.map((example, index) =>
|
|
381
|
-
e('div', {
|
|
382
|
-
key: index,
|
|
383
|
-
className: 'soul-example',
|
|
384
|
-
onClick: () => handleExampleClick(example)
|
|
385
|
-
}, `${example.label}:${example.value}`)
|
|
386
|
-
)
|
|
387
493
|
)
|
|
388
494
|
),
|
|
389
495
|
|
|
@@ -428,11 +534,9 @@ window.__ModuleLoader__.load({
|
|
|
428
534
|
'settings.description': '自定义Agent回复的风格和语调',
|
|
429
535
|
'button.save': '保存设置',
|
|
430
536
|
'button.reset': '重置默认',
|
|
431
|
-
'option.style': '
|
|
432
|
-
'option.tone': '语调',
|
|
537
|
+
'option.style': '回复风格和语调',
|
|
433
538
|
'option.instructions': '自定义指令',
|
|
434
539
|
'option.enabled': '启用个性化设置',
|
|
435
|
-
'example.title': '💡 示例指令(点击使用)',
|
|
436
540
|
'status.loading': '加载中...',
|
|
437
541
|
'status.saving': '保存中...'
|
|
438
542
|
}
|
|
@@ -442,11 +546,9 @@ window.__ModuleLoader__.load({
|
|
|
442
546
|
'settings.description': 'Customize agent response style and tone',
|
|
443
547
|
'button.save': 'Save Settings',
|
|
444
548
|
'button.reset': 'Reset to Default',
|
|
445
|
-
'option.style': 'Response Style',
|
|
446
|
-
'option.tone': 'Tone',
|
|
549
|
+
'option.style': 'Response Style & Tone',
|
|
447
550
|
'option.instructions': 'Custom Instructions',
|
|
448
551
|
'option.enabled': 'Enable Personalization',
|
|
449
|
-
'example.title': '💡 Example Instructions (click to use)',
|
|
450
552
|
'status.loading': 'Loading...',
|
|
451
553
|
'status.saving': 'Saving...'
|
|
452
554
|
}
|
package/index.mjs
CHANGED
|
@@ -27,7 +27,7 @@ function configPath() {
|
|
|
27
27
|
|
|
28
28
|
// ==================== 三层架构 ====================
|
|
29
29
|
|
|
30
|
-
// Identity
|
|
30
|
+
// Identity 层:身份定义(Agent 自己的角色)
|
|
31
31
|
const IdentityLayer = {
|
|
32
32
|
// 角色定义
|
|
33
33
|
roles: {
|
|
@@ -47,33 +47,34 @@ const IdentityLayer = {
|
|
|
47
47
|
parts.push(config.customInstructions)
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
// 用户昵称
|
|
51
|
-
if (config.nickname) {
|
|
52
|
-
parts.push(`你的用户叫"${config.nickname}",在回复中使用这个称呼`)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
50
|
return parts.join('\n')
|
|
56
51
|
}
|
|
57
52
|
}
|
|
58
53
|
|
|
54
|
+
// 用户背景层:关于对话用户的信息(不属于 Agent 角色设定)
|
|
55
|
+
function buildUserProfile(config) {
|
|
56
|
+
const lines = []
|
|
57
|
+
if (config.nickname) {
|
|
58
|
+
lines.push(`- 昵称:${config.nickname}(回复时使用这个称呼)`)
|
|
59
|
+
}
|
|
60
|
+
if (config.occupation) {
|
|
61
|
+
lines.push(`- 职业:${config.occupation}`)
|
|
62
|
+
}
|
|
63
|
+
if (config.bio) {
|
|
64
|
+
lines.push(`- 用户自述:${config.bio}`)
|
|
65
|
+
}
|
|
66
|
+
return lines.join('\n')
|
|
67
|
+
}
|
|
68
|
+
|
|
59
69
|
// Behavior 层:行为规则
|
|
60
70
|
const BehaviorLayer = {
|
|
61
|
-
//
|
|
71
|
+
// 回复风格和语调(v0.2.0 起合并为单一选项)
|
|
62
72
|
styles: {
|
|
63
|
-
professional: '
|
|
64
|
-
casual: '
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
// 语调映射
|
|
71
|
-
tones: {
|
|
72
|
-
neutral: '中性客观',
|
|
73
|
-
formal: '正式礼貌',
|
|
74
|
-
informal: '非正式、口语化',
|
|
75
|
-
enthusiastic: '热情积极',
|
|
76
|
-
calm: '平静沉稳'
|
|
73
|
+
professional: '专业严谨、注重逻辑和准确性、正式礼貌',
|
|
74
|
+
casual: '轻松自然、像朋友一样交流、中性客观',
|
|
75
|
+
humorous: '幽默风趣、适当使用比喻和玩笑、口语化',
|
|
76
|
+
roast: '吐槽达人、犀利幽默、爱调侃但无恶意、口语化',
|
|
77
|
+
efficient: '高效干练、直击要点、简洁明了、不绕弯子'
|
|
77
78
|
},
|
|
78
79
|
|
|
79
80
|
// 行为规则
|
|
@@ -83,18 +84,46 @@ const BehaviorLayer = {
|
|
|
83
84
|
'保持角色一致性,不要跳出设定'
|
|
84
85
|
],
|
|
85
86
|
|
|
87
|
+
// 回复语言指令(同时决定 /soul 命令输出语言)
|
|
88
|
+
languages: {
|
|
89
|
+
zh: '必须使用简体中文回复',
|
|
90
|
+
en: '必须使用英语(English)回复'
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
// 特质:标题和列表的使用程度
|
|
94
|
+
headingLists: {
|
|
95
|
+
more: '采用清晰的格式和列表结构组织回答,善用标题和列表',
|
|
96
|
+
less: '使用更多段落文本,减少标题和列表的使用'
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
// 特质:表情符号的使用程度
|
|
100
|
+
emoji: {
|
|
101
|
+
more: '在回复中使用较多表情符号',
|
|
102
|
+
less: '尽量减少使用表情符号'
|
|
103
|
+
},
|
|
104
|
+
|
|
86
105
|
// 构建行为描述
|
|
87
106
|
build(config) {
|
|
88
107
|
const parts = []
|
|
89
108
|
|
|
90
|
-
//
|
|
109
|
+
// 回复风格和语调
|
|
91
110
|
if (config.style && this.styles[config.style]) {
|
|
92
|
-
parts.push(
|
|
111
|
+
parts.push(`回复风格和语调:${this.styles[config.style]}`)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 特质:标题和列表
|
|
115
|
+
if (config.headingLists && this.headingLists[config.headingLists]) {
|
|
116
|
+
parts.push(this.headingLists[config.headingLists])
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 特质:表情符号
|
|
120
|
+
if (config.emoji && this.emoji[config.emoji]) {
|
|
121
|
+
parts.push(this.emoji[config.emoji])
|
|
93
122
|
}
|
|
94
123
|
|
|
95
|
-
//
|
|
96
|
-
if (config.
|
|
97
|
-
parts.push(
|
|
124
|
+
// 回复语言
|
|
125
|
+
if (config.language && this.languages[config.language]) {
|
|
126
|
+
parts.push(this.languages[config.language])
|
|
98
127
|
}
|
|
99
128
|
|
|
100
129
|
return parts.join(',')
|
|
@@ -118,8 +147,6 @@ const StyleLayer = {
|
|
|
118
147
|
// 根据风格选择格式
|
|
119
148
|
if (config.style === 'casual') {
|
|
120
149
|
parts.push('使用轻松的语气')
|
|
121
|
-
} else if (config.style === 'formal') {
|
|
122
|
-
parts.push('使用正式的语气')
|
|
123
150
|
}
|
|
124
151
|
|
|
125
152
|
return parts.join(',')
|
|
@@ -132,15 +159,54 @@ const StyleLayer = {
|
|
|
132
159
|
const DEFAULT_CONFIG = {
|
|
133
160
|
enabled: true,
|
|
134
161
|
nickname: '',
|
|
162
|
+
// 关于你:用户职业 / 用户介绍
|
|
163
|
+
occupation: '',
|
|
164
|
+
bio: '',
|
|
165
|
+
// 回复风格和语调(v0.2.0 起合并为单一选项)
|
|
135
166
|
style: 'professional',
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
167
|
+
// 特质:标题和列表 / 表情符号(default=默认,more=增强,less=减弱)
|
|
168
|
+
headingLists: 'default',
|
|
169
|
+
emoji: 'default',
|
|
170
|
+
language: 'zh',
|
|
171
|
+
customInstructions: ''
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 合法的回复风格和语调取值(供工具参数校验)
|
|
175
|
+
const STYLE_VALUES = ['professional', 'casual', 'humorous', 'roast', 'efficient']
|
|
176
|
+
|
|
177
|
+
// 特质合法取值
|
|
178
|
+
const TRAIT_VALUES = ['default', 'more', 'less']
|
|
179
|
+
|
|
180
|
+
// v0.1.x 配置使用 style + tone 两个字段;v0.2.0 起合并为单一 style(回复风格和语调)。
|
|
181
|
+
// 迁移映射:组合命中用组合表,否则退回旧 style 表,再否则用默认值。
|
|
182
|
+
const LEGACY_STYLE_TONE_MAP = {
|
|
183
|
+
'professional+formal': 'professional',
|
|
184
|
+
'casual+neutral': 'casual',
|
|
185
|
+
'humorous+informal': 'humorous'
|
|
186
|
+
}
|
|
187
|
+
const LEGACY_STYLE_MAP = {
|
|
188
|
+
professional: 'professional',
|
|
189
|
+
casual: 'casual',
|
|
190
|
+
friendly: 'casual',
|
|
191
|
+
humorous: 'humorous',
|
|
192
|
+
academic: 'professional'
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function migrateConfig(raw) {
|
|
196
|
+
const config = { ...DEFAULT_CONFIG, ...raw }
|
|
197
|
+
if (raw && typeof raw.style === 'string' && typeof raw.tone === 'string') {
|
|
198
|
+
const combo = `${raw.style}+${raw.tone}`
|
|
199
|
+
config.style = LEGACY_STYLE_TONE_MAP[combo] || LEGACY_STYLE_MAP[raw.style] || DEFAULT_CONFIG.style
|
|
200
|
+
delete config.tone
|
|
201
|
+
}
|
|
202
|
+
// 人设预设 / 示例指令功能已在 v0.2.0 移除,丢弃历史残留字段
|
|
203
|
+
delete config.presets
|
|
204
|
+
delete config.tone
|
|
205
|
+
delete config.examples
|
|
206
|
+
// 特质字段校验:脏数据回退为默认值
|
|
207
|
+
if (!TRAIT_VALUES.includes(config.headingLists)) config.headingLists = 'default'
|
|
208
|
+
if (!TRAIT_VALUES.includes(config.emoji)) config.emoji = 'default'
|
|
209
|
+
return config
|
|
144
210
|
}
|
|
145
211
|
|
|
146
212
|
// 内存中的配置缓存
|
|
@@ -152,7 +218,7 @@ async function loadConfig() {
|
|
|
152
218
|
|
|
153
219
|
try {
|
|
154
220
|
const data = await readFile(configPath(), 'utf8')
|
|
155
|
-
configCache =
|
|
221
|
+
configCache = migrateConfig(JSON.parse(data))
|
|
156
222
|
} catch {
|
|
157
223
|
configCache = { ...DEFAULT_CONFIG }
|
|
158
224
|
}
|
|
@@ -163,9 +229,14 @@ async function loadConfig() {
|
|
|
163
229
|
async function saveConfig(config) {
|
|
164
230
|
const dir = join(configPath(), '..')
|
|
165
231
|
await mkdir(dir, { recursive: true })
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
232
|
+
// 过滤已废弃字段(旧客户端可能仍携带 tone / presets / examples)
|
|
233
|
+
const clean = { ...config }
|
|
234
|
+
delete clean.tone
|
|
235
|
+
delete clean.presets
|
|
236
|
+
delete clean.examples
|
|
237
|
+
await writeFile(configPath(), JSON.stringify(clean, null, 2), 'utf8')
|
|
238
|
+
configCache = clean
|
|
239
|
+
return clean
|
|
169
240
|
}
|
|
170
241
|
|
|
171
242
|
// ==================== 提示词编译器 ====================
|
|
@@ -179,9 +250,10 @@ function compilePrompt(config) {
|
|
|
179
250
|
const identity = IdentityLayer.build(config)
|
|
180
251
|
const behavior = BehaviorLayer.build(config)
|
|
181
252
|
const style = StyleLayer.build(config)
|
|
253
|
+
const profile = buildUserProfile(config)
|
|
182
254
|
|
|
183
255
|
// 如果没有任何配置,返回空
|
|
184
|
-
if (!identity && !behavior) {
|
|
256
|
+
if (!identity && !behavior && !profile) {
|
|
185
257
|
return ''
|
|
186
258
|
}
|
|
187
259
|
|
|
@@ -193,13 +265,21 @@ function compilePrompt(config) {
|
|
|
193
265
|
parts.push(`[角色设定] ${identity}`)
|
|
194
266
|
}
|
|
195
267
|
|
|
268
|
+
// 用户背景:明确声明描述的是用户本人,防止模型把用户信息当成自己的身份
|
|
269
|
+
if (profile) {
|
|
270
|
+
parts.push([
|
|
271
|
+
'[用户背景] 以下信息描述的是与你对话的用户本人,不是你的身份;不要把用户的职业、技术背景或自述当成你自己的设定。',
|
|
272
|
+
profile
|
|
273
|
+
].join('\n'))
|
|
274
|
+
}
|
|
275
|
+
|
|
196
276
|
// 行为层
|
|
197
277
|
if (behavior) {
|
|
198
278
|
parts.push(`[回复风格] ${behavior}`)
|
|
199
279
|
}
|
|
200
280
|
|
|
201
281
|
// 执行规则
|
|
202
|
-
parts.push(`[执行规则]
|
|
282
|
+
parts.push(`[执行规则] 你必须在每一条回复中严格遵守以上角色设定和回复风格;涉及用户背景时,用它来理解用户、贴合用户的需求和水平作答,而不是把它当作你自己的身份。包括但不限于:代码解释、技术问答、日常闲聊、知识科普等所有场景。违反此规则视为失败。`)
|
|
203
283
|
|
|
204
284
|
return parts.join('\n')
|
|
205
285
|
}
|
|
@@ -216,20 +296,25 @@ function injectPromptToAllAgents(ctx, config) {
|
|
|
216
296
|
}
|
|
217
297
|
|
|
218
298
|
const prompt = compilePrompt(config)
|
|
219
|
-
|
|
299
|
+
const disabledText = [
|
|
300
|
+
'[dsh-soul 个性化配置已关闭]',
|
|
301
|
+
'从现在开始不要使用 dsh-soul 之前注入的昵称、角色、回复风格、语调、特质或输出语言配置。',
|
|
302
|
+
'请恢复使用 Agent 的默认行为。'
|
|
303
|
+
].join('\n')
|
|
304
|
+
const snapshotText = prompt || disabledText
|
|
220
305
|
|
|
221
306
|
for (const agent of agents.list()) {
|
|
222
307
|
try {
|
|
223
308
|
agent.inject(createUserMessage({
|
|
224
309
|
content: [{
|
|
225
310
|
type: 'text',
|
|
226
|
-
text: [
|
|
311
|
+
text: prompt ? [
|
|
227
312
|
'[dsh-soul 个性化配置已更新]',
|
|
228
|
-
'
|
|
229
|
-
'
|
|
313
|
+
'以下是当前最新的个性化配置快照(含昵称、回复风格和语调、特质、输出语言等)。',
|
|
314
|
+
'从现在开始必须按照此配置回复,不要继续使用旧的昵称、角色、风格、语调、特质或输出语言。',
|
|
230
315
|
'',
|
|
231
316
|
prompt
|
|
232
|
-
].join('
|
|
317
|
+
].join('\n') : snapshotText
|
|
233
318
|
}],
|
|
234
319
|
source: {
|
|
235
320
|
kind: 'plugin',
|
|
@@ -237,7 +322,7 @@ function injectPromptToAllAgents(ctx, config) {
|
|
|
237
322
|
form: 'snapshot',
|
|
238
323
|
sections: [{
|
|
239
324
|
name: 'soul:persona',
|
|
240
|
-
text:
|
|
325
|
+
text: snapshotText
|
|
241
326
|
}]
|
|
242
327
|
}
|
|
243
328
|
}))
|
|
@@ -295,7 +380,7 @@ function registerRoutes(ctx) {
|
|
|
295
380
|
}
|
|
296
381
|
})
|
|
297
382
|
|
|
298
|
-
//
|
|
383
|
+
// 获取系统提示词(当前已保存配置)
|
|
299
384
|
wsCtx.webServer.register({
|
|
300
385
|
kind: 'exact',
|
|
301
386
|
path: '/api/soul/prompt',
|
|
@@ -315,6 +400,8 @@ function registerRoutes(ctx) {
|
|
|
315
400
|
}
|
|
316
401
|
})
|
|
317
402
|
|
|
403
|
+
// 预览系统提示词端点已在 v0.2.0 移除(提示词预览功能下线)
|
|
404
|
+
|
|
318
405
|
// 重置配置
|
|
319
406
|
wsCtx.webServer.register({
|
|
320
407
|
kind: 'exact',
|
|
@@ -324,24 +411,103 @@ function registerRoutes(ctx) {
|
|
|
324
411
|
res.writeHead(status, { 'content-type': 'application/json' })
|
|
325
412
|
res.end(JSON.stringify(body))
|
|
326
413
|
}
|
|
327
|
-
|
|
414
|
+
|
|
328
415
|
try {
|
|
329
416
|
const config = await saveConfig({ ...DEFAULT_CONFIG })
|
|
330
|
-
|
|
417
|
+
|
|
331
418
|
// 配置变化后更新系统提示词,并注入所有活动会话
|
|
332
419
|
refreshPromptAndInject(ctx, config)
|
|
333
|
-
|
|
420
|
+
|
|
334
421
|
send(200, { ok: true, config })
|
|
335
422
|
} catch (err) {
|
|
336
423
|
send(500, { ok: false, error: String(err.message || err) })
|
|
337
424
|
}
|
|
338
425
|
}
|
|
339
426
|
})
|
|
427
|
+
|
|
428
|
+
// 预设管理端点已在 v0.2.0 移除(人设预设功能下线)
|
|
340
429
|
})
|
|
341
430
|
}
|
|
342
431
|
|
|
343
432
|
// ==================== 斜杠命令 ====================
|
|
344
433
|
|
|
434
|
+
// 宿主端无 locale 服务(CommandInvocation 不携带 UI 语言,settings 中亦无语言字段),
|
|
435
|
+
// 命令输出语言跟随 soul 配置的 language 字段,可在设置页切换。
|
|
436
|
+
const COMMAND_MESSAGES = {
|
|
437
|
+
zh: {
|
|
438
|
+
showTitle: '🧠 个性化设置',
|
|
439
|
+
colon: ':',
|
|
440
|
+
statusLabel: '状态',
|
|
441
|
+
nicknameLabel: '昵称',
|
|
442
|
+
occupationLabel: '职业',
|
|
443
|
+
bioLabel: '介绍',
|
|
444
|
+
styleLabel: '风格语调',
|
|
445
|
+
styleNames: {
|
|
446
|
+
professional: '专业严谨',
|
|
447
|
+
casual: '轻松自然',
|
|
448
|
+
humorous: '幽默风趣',
|
|
449
|
+
roast: '吐槽达人',
|
|
450
|
+
efficient: '高效干练'
|
|
451
|
+
},
|
|
452
|
+
traitsLabel: '特质',
|
|
453
|
+
headingListsLabel: '标题和列表',
|
|
454
|
+
emojiLabel: '表情符号',
|
|
455
|
+
traitNames: {
|
|
456
|
+
default: '默认',
|
|
457
|
+
more: '增强',
|
|
458
|
+
less: '减弱'
|
|
459
|
+
},
|
|
460
|
+
instructionsLabel: '自定义指令',
|
|
461
|
+
enabled: '已启用',
|
|
462
|
+
disabled: '已禁用',
|
|
463
|
+
notSet: '未设置',
|
|
464
|
+
help: '使用 /soul show 查看详情\n使用 /soul reset 重置配置\n使用 /soul enable 启用\n使用 /soul disable 禁用\n使用 /soul 昵称xxx 设置昵称',
|
|
465
|
+
resetDone: '✅ 配置已重置为默认值',
|
|
466
|
+
enableDone: '✅ 个性化设置已启用',
|
|
467
|
+
disableDone: '✅ 个性化设置已禁用',
|
|
468
|
+
nicknameDone: (n) => `✅ 昵称已设置为:${n}`,
|
|
469
|
+
failed: (e) => `操作失败:${e}`
|
|
470
|
+
},
|
|
471
|
+
en: {
|
|
472
|
+
showTitle: '🧠 Personalization Settings',
|
|
473
|
+
colon: ': ',
|
|
474
|
+
statusLabel: 'Status',
|
|
475
|
+
nicknameLabel: 'Nickname',
|
|
476
|
+
occupationLabel: 'Occupation',
|
|
477
|
+
bioLabel: 'Bio',
|
|
478
|
+
styleLabel: 'Style & Tone',
|
|
479
|
+
styleNames: {
|
|
480
|
+
professional: 'Professional',
|
|
481
|
+
casual: 'Casual',
|
|
482
|
+
humorous: 'Humorous',
|
|
483
|
+
roast: 'Roast Master',
|
|
484
|
+
efficient: 'Efficient'
|
|
485
|
+
},
|
|
486
|
+
traitsLabel: 'Traits',
|
|
487
|
+
headingListsLabel: 'Headings & lists',
|
|
488
|
+
emojiLabel: 'Emoji',
|
|
489
|
+
traitNames: {
|
|
490
|
+
default: 'Default',
|
|
491
|
+
more: 'More',
|
|
492
|
+
less: 'Less'
|
|
493
|
+
},
|
|
494
|
+
instructionsLabel: 'Custom instructions',
|
|
495
|
+
enabled: 'enabled',
|
|
496
|
+
disabled: 'disabled',
|
|
497
|
+
notSet: 'not set',
|
|
498
|
+
help: 'Use /soul show to view details\nUse /soul reset to reset\nUse /soul enable to enable\nUse /soul disable to disable\nUse /soul <nickname> to set your nickname',
|
|
499
|
+
resetDone: '✅ Configuration reset to defaults',
|
|
500
|
+
enableDone: '✅ Personalization enabled',
|
|
501
|
+
disableDone: '✅ Personalization disabled',
|
|
502
|
+
nicknameDone: (n) => `✅ Nickname set to: ${n}`,
|
|
503
|
+
failed: (e) => `Operation failed: ${e}`
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function commandMessages(config) {
|
|
508
|
+
return COMMAND_MESSAGES[config.language === 'en' ? 'en' : 'zh']
|
|
509
|
+
}
|
|
510
|
+
|
|
345
511
|
function registerCommands(ctx) {
|
|
346
512
|
ctx.inject(['commands'], (cmdCtx) => {
|
|
347
513
|
cmdCtx.commands.register({
|
|
@@ -349,49 +515,55 @@ function registerCommands(ctx) {
|
|
|
349
515
|
description: '查看或管理个性化设置。用法:/soul [show|reset|enable|disable|昵称]',
|
|
350
516
|
input: { hint: '[show|reset|enable|disable|昵称]' },
|
|
351
517
|
async handler(invocation) {
|
|
352
|
-
|
|
353
|
-
|
|
518
|
+
// 保留原始大小写用于昵称,仅关键字匹配时忽略大小写
|
|
519
|
+
const raw = String(invocation.rawInput || '').trim()
|
|
520
|
+
const args = raw.toLowerCase()
|
|
521
|
+
|
|
354
522
|
try {
|
|
355
523
|
const config = await loadConfig()
|
|
356
|
-
|
|
524
|
+
const t = commandMessages(config)
|
|
525
|
+
|
|
357
526
|
if (args === 'show' || args === '') {
|
|
358
|
-
const status = config.enabled ?
|
|
359
|
-
const nickname = config.nickname ||
|
|
360
|
-
const
|
|
361
|
-
const
|
|
362
|
-
const
|
|
363
|
-
|
|
527
|
+
const status = config.enabled ? t.enabled : t.disabled
|
|
528
|
+
const nickname = config.nickname || t.notSet
|
|
529
|
+
const occupation = config.occupation || t.notSet
|
|
530
|
+
const bio = config.bio || t.notSet
|
|
531
|
+
const styleName = t.styleNames[config.style] || config.style || t.notSet
|
|
532
|
+
const hlName = t.traitNames[config.headingLists] || config.headingLists
|
|
533
|
+
const emojiName = t.traitNames[config.emoji] || config.emoji
|
|
534
|
+
const instructions = config.customInstructions || t.notSet
|
|
535
|
+
|
|
364
536
|
return {
|
|
365
537
|
kind: 'success',
|
|
366
|
-
text:
|
|
538
|
+
text: `${t.showTitle}\n\n${t.statusLabel}${t.colon}${status}\n${t.nicknameLabel}${t.colon}${nickname}\n${t.occupationLabel}${t.colon}${occupation}\n${t.bioLabel}${t.colon}${bio}\n${t.styleLabel}${t.colon}${styleName}\n${t.traitsLabel}${t.colon}${t.headingListsLabel}=${hlName},${t.emojiLabel}=${emojiName}\n${t.instructionsLabel}${t.colon}${instructions}\n\n${t.help}`
|
|
367
539
|
}
|
|
368
540
|
} else if (args === 'reset') {
|
|
369
541
|
const updated = await saveConfig({ ...DEFAULT_CONFIG })
|
|
370
542
|
// 更新系统提示词,并注入所有活动会话
|
|
371
543
|
refreshPromptAndInject(ctx, updated)
|
|
372
|
-
return { kind: 'success', text:
|
|
544
|
+
return { kind: 'success', text: t.resetDone }
|
|
373
545
|
} else if (args === 'enable') {
|
|
374
546
|
config.enabled = true
|
|
375
547
|
const updated = await saveConfig(config)
|
|
376
548
|
// 更新系统提示词,并注入所有活动会话
|
|
377
549
|
refreshPromptAndInject(ctx, updated)
|
|
378
|
-
return { kind: 'success', text:
|
|
550
|
+
return { kind: 'success', text: t.enableDone }
|
|
379
551
|
} else if (args === 'disable') {
|
|
380
552
|
config.enabled = false
|
|
381
553
|
const updated = await saveConfig(config)
|
|
382
554
|
// 更新系统提示词,并注入所有活动会话
|
|
383
555
|
refreshPromptAndInject(ctx, updated)
|
|
384
|
-
return { kind: 'success', text:
|
|
556
|
+
return { kind: 'success', text: t.disableDone }
|
|
385
557
|
} else {
|
|
386
|
-
//
|
|
387
|
-
config.nickname =
|
|
558
|
+
// 设置昵称(保留原始大小写,不做 toLowerCase)
|
|
559
|
+
config.nickname = raw
|
|
388
560
|
const updated = await saveConfig(config)
|
|
389
561
|
// 更新系统提示词,并注入所有活动会话
|
|
390
562
|
refreshPromptAndInject(ctx, updated)
|
|
391
|
-
return { kind: 'success', text:
|
|
563
|
+
return { kind: 'success', text: t.nicknameDone(raw) }
|
|
392
564
|
}
|
|
393
565
|
} catch (err) {
|
|
394
|
-
return { kind: 'error', text:
|
|
566
|
+
return { kind: 'error', text: commandMessages(await loadConfig()).failed(err.message) }
|
|
395
567
|
}
|
|
396
568
|
}
|
|
397
569
|
})
|
|
@@ -443,6 +615,120 @@ function registerSystemPrompt(ctx) {
|
|
|
443
615
|
})
|
|
444
616
|
}
|
|
445
617
|
|
|
618
|
+
// ==================== Agent 可调用工具 ====================
|
|
619
|
+
|
|
620
|
+
// tools 是可选 host 服务且 ABI 版本敏感(同 dsh-share-page 的守卫)。
|
|
621
|
+
// 动态 import + 守卫:@deepseek-ai/dsh-tools 不可解析或旧 ABI(缺 TOOL_RUNTIME_SCHEDULER
|
|
622
|
+
// symbol)时跳过工具注册——插件照常激活,Web UI / 命令 / 路由不受影响。
|
|
623
|
+
function registerTools(ctx) {
|
|
624
|
+
ctx.inject(['tools'], (toolsCtx) => {
|
|
625
|
+
import('@deepseek-ai/dsh-tools').then(({ defineTool, TOOL_RUNTIME_SCHEDULER }) => {
|
|
626
|
+
if (typeof TOOL_RUNTIME_SCHEDULER !== 'symbol') {
|
|
627
|
+
throw new Error('dsh-soul: resolved @deepseek-ai/dsh-tools lacks TOOL_RUNTIME_SCHEDULER — requires ^0.1.0-rc.6')
|
|
628
|
+
}
|
|
629
|
+
toolsCtx.tools.register(defineTool({
|
|
630
|
+
name: 'set_persona',
|
|
631
|
+
description:
|
|
632
|
+
'调整当前个性化人设(昵称、回复风格和语调、自定义指令)。' +
|
|
633
|
+
'当用户明确要求改变称呼、语气、风格或角色时使用。' +
|
|
634
|
+
'只需要传入要修改的字段,未提供的字段保持不变。',
|
|
635
|
+
parameters: {
|
|
636
|
+
nickname: { type: 'string', description: '用户昵称,回复时会使用这个称呼。' },
|
|
637
|
+
occupation: { type: 'string', description: '用户的职业。' },
|
|
638
|
+
bio: { type: 'string', description: '关于用户的介绍。' },
|
|
639
|
+
style: {
|
|
640
|
+
type: 'string',
|
|
641
|
+
enum: STYLE_VALUES,
|
|
642
|
+
description: '回复风格和语调:professional=专业严谨,casual=轻松自然,humorous=幽默风趣,roast=吐槽达人,efficient=高效干练。'
|
|
643
|
+
},
|
|
644
|
+
language: {
|
|
645
|
+
type: 'string',
|
|
646
|
+
enum: ['zh', 'en'],
|
|
647
|
+
description: '回复语言:zh=简体中文,en=English。'
|
|
648
|
+
},
|
|
649
|
+
headingLists: {
|
|
650
|
+
type: 'string',
|
|
651
|
+
enum: TRAIT_VALUES,
|
|
652
|
+
description: '特质·标题和列表:default=默认,more=增强(采用清晰格式和列表结构),less=减弱(使用更多段落文本)。'
|
|
653
|
+
},
|
|
654
|
+
emoji: {
|
|
655
|
+
type: 'string',
|
|
656
|
+
enum: TRAIT_VALUES,
|
|
657
|
+
description: '特质·表情符号:default=默认,more=增强(使用较多表情符号),less=减弱(尽量减少使用表情符号)。'
|
|
658
|
+
},
|
|
659
|
+
customInstructions: { type: 'string', description: '额外的自定义指令,用于覆盖或补充当前人设。' },
|
|
660
|
+
},
|
|
661
|
+
output: {
|
|
662
|
+
schema: {
|
|
663
|
+
type: 'object',
|
|
664
|
+
additionalProperties: false,
|
|
665
|
+
required: ['ok'],
|
|
666
|
+
properties: {
|
|
667
|
+
ok: { type: 'boolean' },
|
|
668
|
+
message: { type: 'string' },
|
|
669
|
+
changes: {
|
|
670
|
+
type: 'object',
|
|
671
|
+
description: '实际发生变化的字段',
|
|
672
|
+
properties: {
|
|
673
|
+
nickname: { type: 'string' },
|
|
674
|
+
occupation: { type: 'string' },
|
|
675
|
+
bio: { type: 'string' },
|
|
676
|
+
style: { type: 'string' },
|
|
677
|
+
language: { type: 'string' },
|
|
678
|
+
headingLists: { type: 'string' },
|
|
679
|
+
emoji: { type: 'string' },
|
|
680
|
+
customInstructions: { type: 'string' },
|
|
681
|
+
},
|
|
682
|
+
},
|
|
683
|
+
},
|
|
684
|
+
},
|
|
685
|
+
},
|
|
686
|
+
async execute(args) {
|
|
687
|
+
try {
|
|
688
|
+
const current = await loadConfig()
|
|
689
|
+
const patch = {}
|
|
690
|
+
if (typeof args.nickname === 'string' && args.nickname !== current.nickname) {
|
|
691
|
+
patch.nickname = args.nickname
|
|
692
|
+
}
|
|
693
|
+
if (typeof args.occupation === 'string' && args.occupation !== current.occupation) {
|
|
694
|
+
patch.occupation = args.occupation
|
|
695
|
+
}
|
|
696
|
+
if (typeof args.bio === 'string' && args.bio !== current.bio) {
|
|
697
|
+
patch.bio = args.bio
|
|
698
|
+
}
|
|
699
|
+
if (typeof args.style === 'string' && STYLE_VALUES.includes(args.style) && args.style !== current.style) {
|
|
700
|
+
patch.style = args.style
|
|
701
|
+
}
|
|
702
|
+
if (typeof args.language === 'string' && ['zh', 'en'].includes(args.language) && args.language !== current.language) {
|
|
703
|
+
patch.language = args.language
|
|
704
|
+
}
|
|
705
|
+
if (typeof args.headingLists === 'string' && TRAIT_VALUES.includes(args.headingLists) && args.headingLists !== current.headingLists) {
|
|
706
|
+
patch.headingLists = args.headingLists
|
|
707
|
+
}
|
|
708
|
+
if (typeof args.emoji === 'string' && TRAIT_VALUES.includes(args.emoji) && args.emoji !== current.emoji) {
|
|
709
|
+
patch.emoji = args.emoji
|
|
710
|
+
}
|
|
711
|
+
if (typeof args.customInstructions === 'string' && args.customInstructions !== current.customInstructions) {
|
|
712
|
+
patch.customInstructions = args.customInstructions
|
|
713
|
+
}
|
|
714
|
+
if (Object.keys(patch).length === 0) {
|
|
715
|
+
return { ok: true, message: '没有要更改的设置' }
|
|
716
|
+
}
|
|
717
|
+
const updated = { ...current, ...patch }
|
|
718
|
+
await saveConfig(updated)
|
|
719
|
+
refreshPromptAndInject(ctx, updated)
|
|
720
|
+
return { ok: true, message: '已更新个性化设置', changes: patch }
|
|
721
|
+
} catch (err) {
|
|
722
|
+
return { ok: false, message: String(err.message || err) }
|
|
723
|
+
}
|
|
724
|
+
},
|
|
725
|
+
}))
|
|
726
|
+
}).catch((err) => {
|
|
727
|
+
toolsCtx.logger?.warn?.('[dsh-soul] set_persona tool not registered: ' + String((err && err.message) || err))
|
|
728
|
+
})
|
|
729
|
+
})
|
|
730
|
+
}
|
|
731
|
+
|
|
446
732
|
// ==================== 主入口 ====================
|
|
447
733
|
|
|
448
734
|
export async function apply(ctx) {
|
|
@@ -472,6 +758,7 @@ export async function apply(ctx) {
|
|
|
472
758
|
registerRoutes(ctx)
|
|
473
759
|
registerCommands(ctx)
|
|
474
760
|
registerSystemPrompt(ctx)
|
|
761
|
+
registerTools(ctx)
|
|
475
762
|
}
|
|
476
763
|
|
|
477
764
|
export { name }
|
package/package.json
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-soul",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "为 DeepSeek Harness 添加个性化设置",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.mjs",
|
|
7
7
|
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Aliuyanfeng/dsh-soul.git"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6"
|
|
14
|
+
},
|
|
8
15
|
"peerDependencies": {
|
|
9
16
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
10
17
|
"@deepseek-ai/dsh-llm": "^0.1.1-rc.2"
|
|
@@ -33,6 +40,7 @@
|
|
|
33
40
|
"index.mjs",
|
|
34
41
|
"client",
|
|
35
42
|
"cordis.patch.yml",
|
|
36
|
-
"README.md"
|
|
43
|
+
"README.md",
|
|
44
|
+
"README_EN.md"
|
|
37
45
|
]
|
|
38
46
|
}
|