import Anthropic from '@anthropic-ai/sdk'; import OpenAI from 'openai'; import { tools, ToolDefinition } from './tools/index'; import { getDb } from '../db'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const PROJECT_ROOT = path.resolve(__dirname, '..', '..', '..', '..'); export type Protocol = 'anthropic' | 'openai'; interface ApiConfig { protocol: Protocol; apiKey: string; baseURL: string | undefined; model: string; } function getApiConfig(): ApiConfig { const configRow = getDb().prepare('SELECT value FROM configs WHERE key = ?').get('api_keys') as { value: string } | undefined; let apiKey = process.env.ANTHROPIC_API_KEY || ''; let baseURL: string | undefined; let model = process.env.ANTHROPIC_MODEL || 'claude-sonnet-4-6'; let protocol: Protocol = 'anthropic'; if (configRow) { try { const cfg = JSON.parse(configRow.value); if (cfg.ANTHROPIC_AUTH_TOKEN) apiKey = cfg.ANTHROPIC_AUTH_TOKEN; if (cfg.ANTHROPIC_BASE_URL) baseURL = cfg.ANTHROPIC_BASE_URL; if (cfg.ANTHROPIC_MODEL) model = cfg.ANTHROPIC_MODEL; if (cfg.PROTOCOL === 'openai') protocol = 'openai'; } catch {} } return { protocol, apiKey, baseURL, model }; } function getAnthropicClient(): Anthropic { const { apiKey, baseURL } = getApiConfig(); return new Anthropic({ apiKey, baseURL }); } function getOpenAIClient(): OpenAI { const { apiKey, baseURL } = getApiConfig(); return new OpenAI({ apiKey, baseURL: baseURL || 'https://api.openai.com/v1' }); } export class VideoAgent { private tools: ToolDefinition[]; constructor() { this.tools = tools; } getProtocol(): Protocol { return getApiConfig().protocol; } getModel(): string { return getApiConfig().model; } getAnthropicClient(): Anthropic { return getAnthropicClient(); } getOpenAIClient(): OpenAI { return getOpenAIClient(); } getAnthropicTools(): Anthropic.Tool[] { return this.tools.map((t) => ({ name: t.name, description: t.description, input_schema: t.input_schema, })); } getOpenAITools(): OpenAI.ChatCompletionTool[] { return this.tools.map((t) => ({ type: 'function' as const, function: { name: t.name, description: t.description, parameters: t.input_schema, }, })); } async executeTool(name: string, params: Record): Promise { const tool = this.tools.find((t) => t.name === name); if (!tool) throw new Error(`Unknown tool: ${name}`); return tool.execute(params); } getSystemPrompt(): string { const accountsDir = path.join(PROJECT_ROOT, 'accounts'); let accountList = '暂无账号'; if (fs.existsSync(accountsDir)) { const dirs = fs.readdirSync(accountsDir, { withFileTypes: true }) .filter((d) => d.isDirectory() && !d.name.startsWith('_') && !d.name.startsWith('.')); if (dirs.length > 0) { accountList = dirs.map((d) => { const configPath = path.join(accountsDir, d.name, 'account.json'); if (fs.existsSync(configPath)) { const cfg = JSON.parse(fs.readFileSync(configPath, 'utf-8')); return `- ${d.name}: ${cfg.description || '无描述'} (生图:${cfg.imageModel}, 视频:${cfg.videoModel}, 画幅:${cfg.defaultFormat})`; } return `- ${d.name}`; }).join('\n'); } } return `你是专业的短视频创作助手。你可以帮助用户完成从创意到成片的完整流程。 ## 当前可用账号 ${accountList} ## 你的能力 1. **查看账号** - 使用 list_accounts 列出所有可用账号及其配置 2. **创建账号** - 使用 create_account 创建新的短视频账号,配置生图/视频模型、画幅等 3. **查看账号配置** - 使用 get_account_config 获取账号详细配置 4. **查看 Pipeline 进度** - 使用 pipeline_status 检查创作进度 5. **执行创作阶段** - 使用 run_pipeline_phase 执行 pipeline 阶段 ## 视频创作流程 1. 确认用户意图(A.幻灯片视频 / B.AI视频) 2. 选择/创建账号 3. 规划分镜脚本 4. 生成图片(images 阶段) 5. 生成视频片段(videos 阶段,仅 B 模式) 6. 配音(tts 阶段) 7. 成片组装(assemble 阶段) ## 行为准则 - 用中文回复,友好、专业 - 在用户不清楚时主动询问:成片类型、账号选择、素材来源、画幅等 - 执行 pipeline 前确认 manifest 路径 - 如果用户只是闲聊,就闲聊。如果用户想做视频,引导完成流程 - 不要编造账号或文件路径,使用工具获取真实信息`; } } export const videoAgent = new VideoAgent();