3c9b65d982
- Nuxt 4 + Nuxt UI 4 + AI SDK 7 + Drizzle/SQLite - 主编规划大纲与章节简报、选派作家;作家流式写入章节文件并实时推送前端 - 逐章确认模式(默认)与自动模式;摘要与故事记忆 - 酒馆角色卡(PNG/JSON,V1-V3)与世界书导入 - 自定义最高优先级系统提示词、审稿/修改/多版本、任务日志与提示词检视 - 模拟 LLM 服务与端到端测试脚本 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
106 lines
4.7 KiB
Vue
106 lines
4.7 KiB
Vue
<script setup lang="ts">
|
||
import { ROLE_LABEL } from '#shared/types'
|
||
|
||
const props = defineProps<{ agent: AgentItem, builtin: string }>()
|
||
const store = useProjectStore()
|
||
const api = useApi()
|
||
|
||
const form = reactive({
|
||
name: props.agent.name,
|
||
modelId: props.agent.modelId,
|
||
systemPrompt: props.agent.systemPrompt,
|
||
styleNotes: props.agent.styleNotes,
|
||
enabled: props.agent.enabled
|
||
})
|
||
watch(() => props.agent, (a) => {
|
||
Object.assign(form, { name: a.name, modelId: a.modelId, systemPrompt: a.systemPrompt, styleNotes: a.styleNotes, enabled: a.enabled })
|
||
})
|
||
|
||
const dirty = computed(() => form.name !== props.agent.name || form.modelId !== props.agent.modelId || form.systemPrompt !== props.agent.systemPrompt || form.styleNotes !== props.agent.styleNotes || form.enabled !== props.agent.enabled)
|
||
const saving = ref(false)
|
||
async function save() {
|
||
saving.value = true
|
||
try {
|
||
await api.put(`/api/agents/${props.agent.id}`, { ...form })
|
||
await store.refreshAgents()
|
||
api.toast.add({ title: `${form.name} 已保存`, color: 'success' })
|
||
}
|
||
finally {
|
||
saving.value = false
|
||
}
|
||
}
|
||
async function remove() {
|
||
if (!window.confirm(`删除作家「${props.agent.name}」?`)) return
|
||
await api.del(`/api/agents/${props.agent.id}`)
|
||
await store.refreshAgents()
|
||
}
|
||
|
||
const modelItems = computed(() => store.models.value.map(m => ({ label: `${m.providerName} / ${m.label}`, value: m.id })))
|
||
const showBuiltin = ref(false)
|
||
|
||
const ICON = { editor: 'i-lucide-crown', writer: 'i-lucide-pen-tool', summarizer: 'i-lucide-notebook-pen' } as const
|
||
const DESC = {
|
||
editor: '负责规划章节大纲、为每章撰写简报、选派作家、审稿。建议使用推理能力强的模型(如 Claude)。',
|
||
writer: '根据主编的简报写出章节正文。可以创建多位风格不同的作家,使用不同模型。',
|
||
summarizer: '章节确认后提炼摘要并更新人物状态、伏笔等故事记忆。可以用便宜快速的模型;未配置时使用主编的模型。'
|
||
} as const
|
||
</script>
|
||
|
||
<template>
|
||
<UCard :class="form.enabled ? '' : 'opacity-60'">
|
||
<template #header>
|
||
<div class="flex items-center gap-3">
|
||
<UIcon :name="ICON[agent.role]" class="size-5 text-primary" />
|
||
<UInput v-model="form.name" variant="ghost" class="font-semibold max-w-56" />
|
||
<UBadge variant="subtle" size="sm">
|
||
{{ ROLE_LABEL[agent.role] }}
|
||
</UBadge>
|
||
<UBadge v-if="agent.createdBy === 'editor'" color="info" variant="subtle" size="sm">
|
||
主编创建
|
||
</UBadge>
|
||
<div class="ms-auto flex items-center gap-2">
|
||
<USwitch v-if="agent.role === 'writer'" v-model="form.enabled" size="sm" label="启用" />
|
||
<UButton v-if="agent.role === 'writer'" icon="i-lucide-trash-2" size="xs" color="error" variant="ghost" @click="remove" />
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<div class="space-y-4">
|
||
<p class="text-xs text-muted">
|
||
{{ DESC[agent.role] }}
|
||
</p>
|
||
<div class="grid sm:grid-cols-2 gap-4">
|
||
<UFormField label="模型" required>
|
||
<USelect
|
||
:model-value="form.modelId ?? undefined"
|
||
:items="modelItems"
|
||
placeholder="选择模型"
|
||
@update:model-value="(v: string) => (form.modelId = v)"
|
||
class="w-full"
|
||
/>
|
||
</UFormField>
|
||
<UFormField v-if="agent.role === 'writer'" label="擅长风格" hint="主编选派作家时参考">
|
||
<UInput v-model="form.styleNotes" placeholder="例如:细腻的情感描写、快节奏动作戏" class="w-full" />
|
||
</UFormField>
|
||
</div>
|
||
<UFormField label="自定义系统提示词" hint="最高优先级,与内置提示词冲突时以此为准">
|
||
<UTextarea
|
||
v-model="form.systemPrompt"
|
||
autoresize
|
||
:rows="4"
|
||
:maxrows="20"
|
||
class="w-full"
|
||
:placeholder="agent.role === 'writer' ? '例如:你的文字冷峻克制,擅长用白描和短句营造张力;对话不加多余的动作提示……' : agent.role === 'editor' ? '例如:每章必须有一个明确的小高潮;感情线推进要克制……' : '例如:摘要要保留关键对白原句……'"
|
||
/>
|
||
</UFormField>
|
||
<div>
|
||
<UButton :label="showBuiltin ? '收起内置提示词' : '查看内置提示词'" :icon="showBuiltin ? 'i-lucide-chevron-up' : 'i-lucide-chevron-down'" size="xs" color="neutral" variant="link" class="px-0" @click="showBuiltin = !showBuiltin" />
|
||
<pre v-if="showBuiltin" class="mt-2 text-xs whitespace-pre-wrap rounded-md bg-elevated p-3 text-muted">{{ builtin }}</pre>
|
||
</div>
|
||
<div class="flex justify-end">
|
||
<UButton label="保存" icon="i-lucide-save" size="sm" :loading="saving" :disabled="!dirty" @click="save" />
|
||
</div>
|
||
</div>
|
||
</UCard>
|
||
</template>
|