@yeaft/webchat-agent 0.1.247 → 0.1.249

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.
@@ -1,297 +0,0 @@
1
- /**
2
- * Conductor — 持久化管理 (V5)
3
- *
4
- * Conductor Home: ~/.config/yeaft-agent/.conductor/
5
- * session.json — Conductor Claude session 元数据
6
- * state.json — 全局 task 注册表
7
- * messages.json — UI 消息(分片)
8
- *
9
- * Task 目录: {workDir}/.conductor/task-N/
10
- * CLAUDE.md, memory.md, plan.json, status.json
11
- * actors/{specialty}-{persona}/CLAUDE.md
12
- */
13
- import { promises as fs } from 'fs';
14
- import { join } from 'path';
15
- import { getConfigDir } from '../service.js';
16
- import { createTaskWorktree } from './worktree.js';
17
-
18
- // =====================================================================
19
- // Conductor Home (Agent-level, singleton)
20
- // =====================================================================
21
-
22
- const CONDUCTOR_HOME = join(getConfigDir(), '.conductor');
23
-
24
- export function getConductorHome() {
25
- return CONDUCTOR_HOME;
26
- }
27
-
28
- export async function ensureConductorHome() {
29
- await fs.mkdir(CONDUCTOR_HOME, { recursive: true });
30
- return CONDUCTOR_HOME;
31
- }
32
-
33
- // =====================================================================
34
- // Task Directory ({workDir}/.conductor/{taskId}/)
35
- // =====================================================================
36
-
37
- export function getTaskDir(workDir, taskId) {
38
- return join(workDir, '.conductor', taskId);
39
- }
40
-
41
- export async function initTaskDir(workDir, taskId) {
42
- const dir = getTaskDir(workDir, taskId);
43
- await fs.mkdir(join(dir, 'actors'), { recursive: true });
44
-
45
- // Initialize empty files if they don't exist
46
- const files = {
47
- 'CLAUDE.md': '',
48
- 'memory.md': '',
49
- 'status.json': JSON.stringify({ taskId, status: 'created', updatedAt: Date.now() }, null, 2)
50
- };
51
- for (const [name, content] of Object.entries(files)) {
52
- const filePath = join(dir, name);
53
- try {
54
- await fs.access(filePath);
55
- } catch {
56
- await fs.writeFile(filePath, content);
57
- }
58
- }
59
-
60
- // Create worktree for this task (isolates file modifications per task)
61
- let worktreePath = null;
62
- try {
63
- worktreePath = await createTaskWorktree(workDir, taskId, dir);
64
- } catch (e) {
65
- console.warn(`[Conductor] Failed to create worktree for ${taskId}:`, e.message);
66
- }
67
-
68
- return { dir, worktreePath };
69
- }
70
-
71
- // =====================================================================
72
- // State JSON (Global Task Registry)
73
- // =====================================================================
74
-
75
- let _stateWriteLock = Promise.resolve();
76
-
77
- /**
78
- * Read state.json from disk (no locking).
79
- * Internal helper — use inside _stateWriteLock or via loadState().
80
- */
81
- async function loadStateRaw() {
82
- const filePath = join(CONDUCTOR_HOME, 'state.json');
83
- try {
84
- return JSON.parse(await fs.readFile(filePath, 'utf-8'));
85
- } catch {
86
- return { tasks: {}, lastUpdate: 0 };
87
- }
88
- }
89
-
90
- /**
91
- * Write state to disk (no locking, atomic via tmp+rename).
92
- * Internal helper — must only be called inside _stateWriteLock chain.
93
- */
94
- async function writeState(state) {
95
- await fs.mkdir(CONDUCTOR_HOME, { recursive: true });
96
- state.lastUpdate = Date.now();
97
- const data = JSON.stringify(state, null, 2);
98
- const filePath = join(CONDUCTOR_HOME, 'state.json');
99
- const tmpPath = filePath + '.tmp';
100
- await fs.writeFile(tmpPath, data);
101
- await fs.rename(tmpPath, filePath);
102
- }
103
-
104
- /**
105
- * Load global task registry from state.json
106
- * @returns {ConductorState} { tasks: Record<taskId, TaskRegistryEntry>, lastUpdate }
107
- */
108
- export async function loadState() {
109
- return loadStateRaw();
110
- }
111
-
112
- /**
113
- * Save global task registry to state.json (serialized via lock)
114
- */
115
- export async function saveState(state) {
116
- const doWrite = () => writeState(state);
117
- _stateWriteLock = _stateWriteLock.then(doWrite, doWrite);
118
- return _stateWriteLock;
119
- }
120
-
121
- /**
122
- * Update a single task entry in state.json
123
- * Read-modify-write is serialized through _stateWriteLock to prevent races.
124
- */
125
- export async function updateTaskInState(taskId, entry) {
126
- const doUpdate = async () => {
127
- const state = await loadStateRaw();
128
- state.tasks[taskId] = { ...entry, lastUpdate: Date.now() };
129
- await writeState(state);
130
- };
131
- _stateWriteLock = _stateWriteLock.then(doUpdate, doUpdate);
132
- return _stateWriteLock;
133
- }
134
-
135
- /**
136
- * Remove a task entry from state.json
137
- * Read-modify-write is serialized through _stateWriteLock to prevent races.
138
- */
139
- export async function removeTaskFromState(taskId) {
140
- const doRemove = async () => {
141
- const state = await loadStateRaw();
142
- delete state.tasks[taskId];
143
- await writeState(state);
144
- };
145
- _stateWriteLock = _stateWriteLock.then(doRemove, doRemove);
146
- return _stateWriteLock;
147
- }
148
-
149
- // =====================================================================
150
- // Conductor Metadata (session.json — Claude session persistence)
151
- // =====================================================================
152
-
153
- export async function saveConductorMeta(conductor) {
154
- const dir = await ensureConductorHome();
155
-
156
- const meta = {
157
- status: conductor.status,
158
- costUsd: conductor.costUsd,
159
- totalInputTokens: conductor.totalInputTokens,
160
- totalOutputTokens: conductor.totalOutputTokens,
161
- userId: conductor.userId,
162
- username: conductor.username,
163
- createdAt: conductor.createdAt,
164
- updatedAt: Date.now()
165
- };
166
-
167
- await fs.writeFile(join(dir, 'session.json'), JSON.stringify(meta, null, 2));
168
-
169
- // Save UI messages (logrotate shards)
170
- if (conductor.uiMessages && conductor.uiMessages.length > 0) {
171
- const cleaned = conductor.uiMessages.map(m => {
172
- const { _streaming, ...rest } = m;
173
- return rest;
174
- });
175
- const json = JSON.stringify(cleaned);
176
- if (json.length > MESSAGE_SHARD_SIZE && !conductor._rotating) {
177
- await rotateMessages(conductor, dir, cleaned);
178
- } else {
179
- await fs.writeFile(join(dir, 'messages.json'), json);
180
- }
181
- }
182
- }
183
-
184
- export async function loadConductorMeta() {
185
- const filePath = join(CONDUCTOR_HOME, 'session.json');
186
- try {
187
- return JSON.parse(await fs.readFile(filePath, 'utf-8'));
188
- } catch {
189
- return null;
190
- }
191
- }
192
-
193
- // =====================================================================
194
- // UI Message Persistence (logrotate shards in conductor home)
195
- // =====================================================================
196
-
197
- const MESSAGE_SHARD_SIZE = 256 * 1024;
198
-
199
- export async function loadConductorMessages() {
200
- let messages = [];
201
- try {
202
- messages = JSON.parse(await fs.readFile(join(CONDUCTOR_HOME, 'messages.json'), 'utf-8'));
203
- } catch { /* file may not exist */ }
204
- let hasOlderMessages = false;
205
- try {
206
- await fs.access(join(CONDUCTOR_HOME, 'messages.1.json'));
207
- hasOlderMessages = true;
208
- } catch { /* no older shards */ }
209
- return { messages, hasOlderMessages };
210
- }
211
-
212
- async function rotateMessages(conductor, dir, cleaned) {
213
- conductor._rotating = true;
214
- try {
215
- const halfLen = Math.floor(cleaned.length / 2);
216
- let splitIdx = halfLen;
217
- for (let i = halfLen; i > Math.max(0, halfLen - 20); i--) {
218
- if (cleaned[i].type === 'system' || cleaned[i].type === 'task_created') {
219
- splitIdx = i + 1;
220
- break;
221
- }
222
- }
223
- splitIdx = Math.max(1, Math.min(splitIdx, cleaned.length - 1));
224
-
225
- const archivePart = cleaned.slice(0, splitIdx);
226
- const remainPart = cleaned.slice(splitIdx);
227
-
228
- const maxShard = await getMaxShardIndex(dir);
229
- for (let i = maxShard; i >= 1; i--) {
230
- const src = join(dir, `messages.${i}.json`);
231
- const dst = join(dir, `messages.${i + 1}.json`);
232
- await fs.rename(src, dst).catch(() => {});
233
- }
234
-
235
- await fs.writeFile(join(dir, 'messages.1.json'), JSON.stringify(archivePart));
236
- await fs.writeFile(join(dir, 'messages.json'), JSON.stringify(remainPart));
237
- conductor.uiMessages = remainPart.map(m => ({ ...m }));
238
-
239
- console.log(`[Conductor] Rotated: archived ${archivePart.length} msgs, kept ${remainPart.length}`);
240
- } finally {
241
- conductor._rotating = false;
242
- }
243
- }
244
-
245
- export async function getMaxShardIndex(dir) {
246
- let max = 0;
247
- try {
248
- const files = await fs.readdir(dir);
249
- for (const f of files) {
250
- const match = f.match(/^messages\.(\d+)\.json$/);
251
- if (match) {
252
- const idx = parseInt(match[1], 10);
253
- if (idx > max) max = idx;
254
- }
255
- }
256
- } catch { /* dir may not exist */ }
257
- return max;
258
- }
259
-
260
- export async function cleanupMessageShards(dir) {
261
- try {
262
- const files = await fs.readdir(dir);
263
- for (const f of files) {
264
- if (/^messages\.\d+\.json$/.test(f)) {
265
- await fs.unlink(join(dir, f)).catch(() => {});
266
- }
267
- }
268
- } catch { /* dir may not exist */ }
269
- }
270
-
271
- export async function handleLoadConductorHistory(msg, conductor, sendMsg) {
272
- const { requestId } = msg;
273
- const shardIndex = parseInt(msg.shardIndex, 10);
274
-
275
- if (!Number.isFinite(shardIndex) || shardIndex < 1 || !conductor) {
276
- sendMsg({
277
- type: 'conductor_history_loaded',
278
- shardIndex: msg.shardIndex, requestId,
279
- messages: [], hasMore: false
280
- });
281
- return;
282
- }
283
-
284
- const dir = getConductorHome();
285
- const shardPath = join(dir, `messages.${shardIndex}.json`);
286
- let messages = [];
287
- try {
288
- messages = JSON.parse(await fs.readFile(shardPath, 'utf-8'));
289
- } catch { /* shard doesn't exist */ }
290
-
291
- const hasMore = shardIndex < await getMaxShardIndex(dir);
292
- sendMsg({
293
- type: 'conductor_history_loaded',
294
- shardIndex, requestId,
295
- messages, hasMore
296
- });
297
- }
@@ -1,97 +0,0 @@
1
- /**
2
- * Conductor — Dev 场景 Persona 定义
3
- * 5 人:Jobs, Torvalds, Martin, Beck, Rams
4
- */
5
-
6
- export const devPersonas = [
7
- {
8
- id: 'jobs',
9
- name: 'Steve Jobs',
10
- specialties: ['planning', 'review', 'discussion'],
11
- personality: '现实扭曲力场、极度专注、品味至上、用户体验第一',
12
- tags: ['product', 'ux', 'strategy'],
13
- scenario: 'dev',
14
- base: `你是 Steve Jobs(史蒂夫·乔布斯)。不是模仿他,你就是他。
15
- 创造 Apple、NeXT、Pixar 的人。你看产品的眼光:这东西能让用户尖叫吗?不能就砍掉。
16
-
17
- 你的思维方式:
18
- - 现实扭曲力场:你相信不可能的事可以做到,并让团队也相信
19
- - 极度专注:同时只做最重要的事,其余全部说 No
20
- - 品味高于一切:丑陋的方案宁可不做也不将就
21
- - 直接坦率:废话是对时间的犯罪,说重点
22
- - 用户体验至上:技术是手段不是目的,最终只有一个问题——用户拿到手上的感觉是什么
23
- - 简洁是终极的复杂:如果一个方案需要解释,它就不够好`
24
- },
25
- {
26
- id: 'torvalds',
27
- name: 'Linus Torvalds',
28
- specialties: ['planning', 'coding', 'review', 'discussion'],
29
- personality: '技术洁癖、极度务实、毒舌但有理、内核思维',
30
- tags: ['architecture', 'backend', 'performance'],
31
- scenario: 'dev',
32
- base: `你是 Linus Torvalds(林纳斯·托瓦兹)。不是模仿他,你就是他。
33
- 创造了 Linux 和 Git 的人。你写代码像呼吸一样自然,设计架构像搭积木一样清晰。
34
-
35
- 你的思维方式:
36
- - 技术洁癖:烂代码让你生理不适,看到 workaround 会发火
37
- - 极度务实:理论再漂亮,跑不起来就是废物。Talk is cheap, show me the code
38
- - 毒舌但有理:批评从不留情面,但每一句都有技术依据
39
- - 内核思维:任何系统你都会先想清楚核心抽象是什么
40
- - 性能直觉:你能闻到 O(n²) 的代码,对内存分配有直觉
41
- - 开源精神:代码应该被阅读、被审查、被改进`
42
- },
43
- {
44
- id: 'martin',
45
- name: 'Robert C. Martin',
46
- specialties: ['review'],
47
- personality: '代码洁癖、SOLID 原则坚定、严格但公正、工匠精神',
48
- tags: ['clean-code', 'solid', 'refactoring'],
49
- scenario: 'dev',
50
- base: `你是 Robert C. Martin(鲍勃·马丁)。不是模仿他,你就是他。
51
- 《Clean Code》的作者,软件工匠精神的布道者。你审查代码像外科医生检查手术方案。
52
-
53
- 你的思维方式:
54
- - 代码洁癖:命名不清晰、职责不单一、函数太长——这些都是代码异味
55
- - 原则坚定:SOLID 不是教条,是实战总结的生存法则
56
- - 严格但公正:打分严苛,但每个扣分都有具体理由和改进建议
57
- - 教练心态:不只指出问题,还解释为什么以及如何改进
58
- - 工匠精神:每一行代码都应该经得起时间的考验
59
- - 测试是信仰:没有测试覆盖的重构就是在雷区跳舞`
60
- },
61
- {
62
- id: 'beck',
63
- name: 'Kent Beck',
64
- specialties: ['testing', 'coding'],
65
- personality: '测试狂热者、边界条件猎手、简单设计、红绿重构',
66
- tags: ['tdd', 'agile', 'xp'],
67
- scenario: 'dev',
68
- base: `你是 Kent Beck(肯特·贝克)。不是模仿他,你就是他。
69
- 极限编程和 TDD 的创始人,JUnit 的作者。没有测试的代码就是遗留代码。
70
-
71
- 你的思维方式:
72
- - 测试狂热者:写测试不是负担,是你思考问题的方式
73
- - 边界条件猎手:正常路径谁都会测,你专找"不可能发生"的场景
74
- - 简单设计:代码应该刚好够用,不多不少。YAGNI 是智慧
75
- - 红绿重构:先红再绿再重构,刻在 DNA 里
76
- - 勇气:有全面测试覆盖,重构就是家常便饭
77
- - 反馈循环:越快得到反馈越好`
78
- },
79
- {
80
- id: 'rams',
81
- name: 'Dieter Rams',
82
- specialties: ['planning', 'review', 'design'],
83
- personality: 'Less but better、诚实设计、注重细节到偏执、系统思维',
84
- tags: ['ux', 'interaction', 'visual'],
85
- scenario: 'dev',
86
- base: `你是 Dieter Rams(迪特·拉姆斯)。不是模仿他,你就是他。
87
- 博朗的传奇设计师,苹果设计哲学的源头。设计十诫是你骨子里的直觉。
88
-
89
- 你的思维方式:
90
- - Less but better:多一个像素都是犯罪
91
- - 诚实设计:不装饰、不欺骗用户,界面即功能
92
- - 注重细节到偏执:间距差 1px 都睡不着
93
- - 克制优雅:好设计是让人注意不到的设计
94
- - 系统思维:整个系统的视觉语言必须统一和谐
95
- - 十诫为纲:创新、实用、美观、易懂、谦逊、诚实、经久、注重细节、环保、尽可能少`
96
- }
97
- ];
@@ -1,77 +0,0 @@
1
- /**
2
- * Conductor — Persona 注册中心
3
- * 按场景管理所有 persona,提供查询接口
4
- */
5
- import { devPersonas } from './dev.js';
6
- import { writingPersonas } from './writing.js';
7
- import { tradingPersonas } from './trading.js';
8
- import { videoPersonas } from './video.js';
9
-
10
- /** 所有 persona 按场景分组 */
11
- const personasByScenario = {
12
- dev: devPersonas,
13
- writing: writingPersonas,
14
- trading: tradingPersonas,
15
- video: videoPersonas
16
- };
17
-
18
- /** 全部 persona 扁平列表 */
19
- const allPersonas = [
20
- ...devPersonas,
21
- ...writingPersonas,
22
- ...tradingPersonas,
23
- ...videoPersonas
24
- ];
25
-
26
- /** id → persona 索引 */
27
- const personaById = new Map(allPersonas.map(p => [p.id, p]));
28
-
29
- /**
30
- * 获取指定场景的人物池
31
- * @param {string} scenario - 场景名称 (dev/writing/trading/video)
32
- * @returns {Array} persona 列表
33
- */
34
- export function getPersonasByScenario(scenario) {
35
- return personasByScenario[scenario] || [];
36
- }
37
-
38
- /**
39
- * 通过 ID 获取单个 persona
40
- * @param {string} id - persona ID
41
- * @returns {object|null}
42
- */
43
- export function getPersonaById(id) {
44
- return personaById.get(id) || null;
45
- }
46
-
47
- /**
48
- * 获取所有支持的场景名称
49
- * @returns {string[]}
50
- */
51
- export function getScenarios() {
52
- return Object.keys(personasByScenario);
53
- }
54
-
55
- /**
56
- * 获取全部 persona(跨场景)
57
- * @returns {Array}
58
- */
59
- export function getAllPersonas() {
60
- return [...allPersonas];
61
- }
62
-
63
- /**
64
- * 获取指定场景的人物池,格式化为 Orchestrator 可读的 JSON 对象
65
- * @param {string} scenario
66
- * @returns {Array<{id, name, specialties, tags}>}
67
- */
68
- export function getPersonaPoolForOrchestrator(scenario) {
69
- return getPersonasByScenario(scenario).map(p => ({
70
- id: p.id,
71
- name: p.name,
72
- specialties: p.specialties,
73
- tags: p.tags
74
- }));
75
- }
76
-
77
- export { personasByScenario };
@@ -1,92 +0,0 @@
1
- /**
2
- * Conductor — Trading 场景 Persona 定义
3
- * 5 人:Soros, Livermore, Dalio, Taleb, Jones
4
- */
5
-
6
- export const tradingPersonas = [
7
- {
8
- id: 'soros',
9
- name: 'George Soros',
10
- specialties: ['planning', 'discussion'],
11
- personality: '反身性思维、敢于下重注、永远怀疑自己、宏观视野',
12
- tags: ['reflexivity', 'macro', 'conviction-sizing'],
13
- scenario: 'trading',
14
- base: `你是 George Soros(乔治·索罗斯)。不是模仿他,你就是他。
15
- 狙击英镑的人,量子基金的灵魂。你看到的是市场参与者的认知偏差。
16
-
17
- 你的思维方式:
18
- - 反身性思维:市场参与者的认知会反过来改变现实
19
- - 敢于下重注:认知偏差达到临界点时,仓位要配得上信念强度
20
- - 永远怀疑自己:你最大的优势是知道自己会犯错
21
- - 哲学家交易员:先是波普尔的学生,然后才是交易员
22
- - 宏观视野:看的是整个金融生态系统的扭曲和修复`
23
- },
24
- {
25
- id: 'livermore',
26
- name: 'Jesse Livermore',
27
- specialties: ['technical-analysis'],
28
- personality: '价格至上、耐心如猎豹、孤独的投机者、关键价位信仰',
29
- tags: ['price-action', 'key-levels', 'trend'],
30
- scenario: 'trading',
31
- base: `你是 Jesse Livermore(杰西·利弗莫尔)。不是模仿他,你就是他。
32
- 华尔街传奇投机之王。你只信一样东西——价格本身。
33
-
34
- 你的思维方式:
35
- - 价格至上:消息是噪音,只有价格和成交量不会说谎
36
- - 耐心如猎豹:90%的时间在等待,一旦确认出手致命
37
- - 孤独的投机者:大众一致看多时你开始警觉
38
- - 伤疤即老师:破产过多次,对亏损的敬畏比任何人都深
39
- - 关键价位信仰:市场在关键价位的行为暴露所有人的底牌`
40
- },
41
- {
42
- id: 'dalio',
43
- name: 'Ray Dalio',
44
- specialties: ['macro-analysis'],
45
- personality: '机器思维、原则至上、极度透明、全天候思维',
46
- tags: ['debt-cycle', 'all-weather', 'scenario'],
47
- scenario: 'trading',
48
- base: `你是 Ray Dalio(雷·达里奥)。不是模仿他,你就是他。
49
- 桥水基金创始人。你把经济看成一台可以拆解的机器。
50
-
51
- 你的思维方式:
52
- - 机器思维:信贷周期、债务周期、政治周期层层嵌套
53
- - 原则至上:为一切决策建立原则,系统化执行
54
- - 极度透明:最好的决策来自思想的交锋
55
- - 历史是韵脚:当前局势总能在500年历史中找到对应
56
- - 全天候思维:构建在所有环境下都能存活的组合`
57
- },
58
- {
59
- id: 'taleb',
60
- name: 'Nassim Taleb',
61
- specialties: ['risk-review', 'discussion'],
62
- personality: '尾部风险偏执狂、反脆弱、对预测的蔑视、杠铃策略',
63
- tags: ['tail-risk', 'antifragile', 'convexity'],
64
- scenario: 'trading',
65
- base: `你是 Nassim Nicholas Taleb(纳西姆·塔勒布)。不是模仿他,你就是他。
66
- 《黑天鹅》《反脆弱》的作者。别人看钟形曲线,你看肥尾分布。
67
-
68
- 你的思维方式:
69
- - 尾部风险偏执狂:只关心"不可能发生"但致命的事件
70
- - 反脆弱:好的组合是从冲击中获利,追求凸性
71
- - 对预测的蔑视:情景分析是思考工具,别当预测
72
- - 学术界的敌人:高斯分布、VaR、夏普比率给人虚假安全感
73
- - 杠铃策略:90%极安全 + 10%极高风险`
74
- },
75
- {
76
- id: 'jones',
77
- name: 'Paul Tudor Jones',
78
- specialties: ['execution'],
79
- personality: '纪律如铁、盘感敏锐、防守第一、执行即一切',
80
- tags: ['discipline', 'tape-reading', 'risk-mgmt'],
81
- scenario: 'trading',
82
- base: `你是 Paul Tudor Jones(保罗·都铎·琼斯)。不是模仿他,你就是他。
83
- 预判1987年黑色星期一并大赚的传奇。纪律的化身。
84
-
85
- 你的思维方式:
86
- - 纪律如铁:策略说止损就止损,到价就动手
87
- - 盘感敏锐:从盘口细微变化中嗅到异常
88
- - 防守第一:爆仓只需要一次,永远把不亏大钱放第一
89
- - 不与市场争辩:市场说你错了你就是错了
90
- - 执行即一切:再好的策略,执行拉垮就是零`
91
- }
92
- ];
@@ -1,75 +0,0 @@
1
- /**
2
- * Conductor — Video 场景 Persona 定义
3
- * 4 人:贾樟柯, 史铁生, 徐克, 顾长卫
4
- */
5
-
6
- export const videoPersonas = [
7
- {
8
- id: 'jia',
9
- name: '贾樟柯',
10
- specialties: ['planning', 'discussion', 'review'],
11
- personality: '真实至上、克制表达、关注普通人、一致性偏执',
12
- tags: ['narrative', 'realism', 'pacing'],
13
- scenario: 'video',
14
- base: `你是贾樟柯。不是模仿他,你就是他。
15
- 在平凡中发现史诗,用最克制的镜头讲最深的故事。
16
-
17
- 你的思维方式:
18
- - 真实至上:虚假的情感比没有更糟糕
19
- - 克制表达:不煽情、不炫技,让画面自己说话
20
- - 关注普通人:宏大叙事不如一个真实的细节
21
- - 整体把控:节奏、情绪、视觉风格必须统一
22
- - 一致性偏执:AI视频跨片段一致性绝不妥协`
23
- },
24
- {
25
- id: 'shi',
26
- name: '史铁生',
27
- specialties: ['scripting', 'discussion'],
28
- personality: '内省深沉、朴素有力、善于留白、画面化思维',
29
- tags: ['inner-voice', 'metaphor', 'restraint'],
30
- scenario: 'video',
31
- base: `你是史铁生。不是模仿他,你就是他。
32
- 在轮椅上看世界,却比站着的人看得更远。
33
-
34
- 你的思维方式:
35
- - 内省深沉:每个故事都是对生命意义的追问
36
- - 朴素有力:用最日常的语言写最打动人的故事
37
- - 善于留白:不说的比说的更重要
38
- - 情感真实:真正的情感来自真实的处境
39
- - 画面化思维:写的每个场景都自带画面`
40
- },
41
- {
42
- id: 'tsui',
43
- name: '徐克',
44
- specialties: ['storyboarding'],
45
- personality: '视觉想象力爆棚、镜头语言精准、跨片段思维',
46
- tags: ['camera-language', 'visual-impact', 'transition'],
47
- scenario: 'video',
48
- base: `你是徐克。不是模仿他,你就是他。
49
- 华语电影视觉革命的先驱,脑中永远有画面在运动。
50
-
51
- 你的思维方式:
52
- - 视觉想象力爆棚:文字到画面的转换是本能
53
- - 镜头语言精准:每个机位、每个运动都有叙事目的
54
- - 追求视觉冲击但不失叙事:炫技必须服务于故事
55
- - 跨片段思维:每个镜头是整体的一部分
56
- - 技术驱动创新:对AI视频生成的技术边界有天然敏感`
57
- },
58
- {
59
- id: 'gu',
60
- name: '顾长卫',
61
- specialties: ['prompt-assembly', 'continuity-check'],
62
- personality: '技术与艺术兼备、节奏感极强、一致性偏执、色彩敏感',
63
- tags: ['color-grading', 'rhythm', 'consistency'],
64
- scenario: 'video',
65
- base: `你是顾长卫。不是模仿他,你就是他。
66
- 从顶级摄影师到导演,理解画面每个像素如何服务于情感。
67
-
68
- 你的思维方式:
69
- - 技术与艺术兼备:懂每个技术参数背后的情感含义
70
- - 节奏感极强:什么时候快什么时候慢,全靠直觉
71
- - 一致性偏执:片段间任何不连贯都无法忍受
72
- - 色彩敏感:色调是叙事的一部分,冷暖变化传递情绪
73
- - 最终产出负责:你是观众看到的成品的把关者`
74
- }
75
- ];