AI 小说工坊:主编 + 作家多模型协作写作应用

- 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>
This commit is contained in:
jocay
2026-10-18 02:04:40 +08:00
commit 3c9b65d982
106 changed files with 16286 additions and 0 deletions
+70
View File
@@ -0,0 +1,70 @@
<script setup lang="ts">
const store = useProjectStore()
const api = useApi()
const { agents, models } = store
const { data: builtin } = await useAsyncData('builtin-prompts', () => api.get<{ editor: string, writer: string, summarizer: string }>('/api/prompts/builtin'), {
default: () => ({ editor: '', writer: '', summarizer: '' })
})
onMounted(() => store.refreshModels())
const editor = computed(() => agents.value.find(a => a.role === 'editor'))
const summarizer = computed(() => agents.value.find(a => a.role === 'summarizer'))
const writers = computed(() => agents.value.filter(a => a.role === 'writer'))
async function addWriter() {
await api.post(`/api/projects/${store.projectId}/agents`, {
role: 'writer',
name: `作家${writers.value.length + 1}`,
modelId: models.value[0]?.id ?? null
})
await store.refreshAgents()
}
</script>
<template>
<UDashboardPanel id="project-team">
<template #header>
<UDashboardNavbar title="AI 团队">
<template #right>
<UButton icon="i-lucide-user-plus" label="添加作家" @click="addWriter" />
</template>
</UDashboardNavbar>
</template>
<template #body>
<div class="max-w-4xl mx-auto w-full space-y-6 pb-16">
<UAlert
v-if="!models.length"
color="warning"
variant="subtle"
icon="i-lucide-plug-zap"
title="还没有可用模型"
description="请先在「模型与 API」中添加模型。"
:actions="[{ label: '去配置', to: '/settings', color: 'warning', variant: 'solid' }]"
/>
<section class="space-y-3">
<h2 class="font-semibold">
编辑部
</h2>
<AgentCard v-if="editor" :agent="editor" :builtin="builtin.editor" />
<AgentCard v-if="summarizer" :agent="summarizer" :builtin="builtin.summarizer" />
</section>
<section class="space-y-3">
<div class="flex items-center gap-2">
<h2 class="font-semibold">
作家团队
</h2>
<span class="text-xs text-muted">主编会根据「擅长风格」为每章选派作家;你也可以在写作台为某章手动指定作家</span>
</div>
<AgentCard v-for="w in writers" :key="w.id" :agent="w" :builtin="builtin.writer" />
<div v-if="!writers.length" class="text-sm text-muted text-center py-8">
还没有作家
</div>
</section>
</div>
</template>
</UDashboardPanel>
</template>