feat(web): add pi-agent tool layer with pipeline integration
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
39
web/server/agent/index.ts
Normal file
39
web/server/agent/index.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { tools, ToolDefinition } from './tools';
|
||||
|
||||
export class VideoAgent {
|
||||
private tools: ToolDefinition[];
|
||||
|
||||
constructor() {
|
||||
this.tools = tools;
|
||||
}
|
||||
|
||||
getToolDefinitions() {
|
||||
return this.tools.map((t) => ({
|
||||
name: t.name,
|
||||
description: t.description,
|
||||
parameters: t.parameters,
|
||||
}));
|
||||
}
|
||||
|
||||
async executeTool(name: string, params: Record<string, unknown>): Promise<string> {
|
||||
const tool = this.tools.find((t) => t.name === name);
|
||||
if (!tool) throw new Error(`Unknown tool: ${name}`);
|
||||
return tool.execute(params);
|
||||
}
|
||||
|
||||
getSystemPrompt(accountContext?: string): string {
|
||||
return `你是美图 Agent,帮助用户进行短视频创作。
|
||||
|
||||
可用账号:${accountContext || '暂无'}
|
||||
|
||||
你可以:
|
||||
1. 帮用户创建新账号
|
||||
2. 查看和管理已有账号
|
||||
3. 执行视频创作 pipeline(分镜→生图→生视频→TTS→成片)
|
||||
4. 管理提示词模板
|
||||
|
||||
用户想创作视频时,一步步引导他们完成流程。`;
|
||||
}
|
||||
}
|
||||
|
||||
export const videoAgent = new VideoAgent();
|
||||
101
web/server/agent/tools.ts
Normal file
101
web/server/agent/tools.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { spawn, execSync } from 'child_process';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
const PROJECT_ROOT = path.resolve(__dirname, '..', '..', '..', '..');
|
||||
const PIPELINE_SCRIPT = path.join(PROJECT_ROOT, '.claude', 'skills', 'video-from-script', 'scripts', 'pipeline.js');
|
||||
|
||||
export interface ToolDefinition {
|
||||
name: string;
|
||||
description: string;
|
||||
parameters: Record<string, unknown>;
|
||||
execute: (params: Record<string, unknown>) => Promise<string>;
|
||||
}
|
||||
|
||||
export const tools: ToolDefinition[] = [
|
||||
{
|
||||
name: 'list_accounts',
|
||||
description: '列出所有可用账号',
|
||||
parameters: { type: 'object', properties: {}, required: [] },
|
||||
execute: async () => {
|
||||
const accountsDir = path.join(PROJECT_ROOT, 'accounts');
|
||||
const dirs = fs.readdirSync(accountsDir, { withFileTypes: true })
|
||||
.filter((d) => d.isDirectory() && !d.name.startsWith('_') && !d.name.startsWith('.'))
|
||||
.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})`;
|
||||
}
|
||||
return d.name;
|
||||
});
|
||||
return dirs.join('\n');
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'run_pipeline_phase',
|
||||
description: '执行 pipeline 阶段 (images/upload/videos/tts/assemble)',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
manifest: { type: 'string', description: 'manifest.json 绝对路径' },
|
||||
phase: { type: 'string', description: '阶段名: images, upload, videos, tts, assemble' },
|
||||
},
|
||||
required: ['manifest', 'phase'],
|
||||
},
|
||||
execute: async (params) => {
|
||||
const { manifest, phase } = params as { manifest: string; phase: string };
|
||||
return new Promise((resolve, reject) => {
|
||||
const proc = spawn('node', [PIPELINE_SCRIPT, 'run', '--manifest', manifest, '--phase', phase], {
|
||||
cwd: PROJECT_ROOT,
|
||||
env: { ...process.env },
|
||||
});
|
||||
let output = '';
|
||||
proc.stdout.on('data', (d: Buffer) => { output += d.toString(); });
|
||||
proc.stderr.on('data', (d: Buffer) => { output += d.toString(); });
|
||||
proc.on('close', (code) => {
|
||||
code === 0 ? resolve(output) : reject(new Error(`Pipeline exit code ${code}: ${output}`));
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'pipeline_status',
|
||||
description: '查看 pipeline 进度',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
manifest: { type: 'string', description: 'manifest.json 绝对路径' },
|
||||
},
|
||||
required: ['manifest'],
|
||||
},
|
||||
execute: async (params) => {
|
||||
const { manifest } = params as { manifest: string };
|
||||
const result = execSync(`node "${PIPELINE_SCRIPT}" status --manifest "${manifest}"`, {
|
||||
cwd: PROJECT_ROOT, encoding: 'utf-8',
|
||||
});
|
||||
return result;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'create_account',
|
||||
description: '创建新账号',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: '账号 ID' },
|
||||
name: { type: 'string', description: '账号名称' },
|
||||
desc: { type: 'string', description: '账号描述' },
|
||||
},
|
||||
required: ['id', 'name'],
|
||||
},
|
||||
execute: async (params) => {
|
||||
const { id, name, desc } = params as { id: string; name: string; desc?: string };
|
||||
const result = execSync(
|
||||
`node "${PIPELINE_SCRIPT}" create-account --id "${id}" --name "${name}" --desc "${desc || ''}" --video-model veo3-fast`,
|
||||
{ cwd: PROJECT_ROOT, encoding: 'utf-8' }
|
||||
);
|
||||
return result;
|
||||
},
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user