You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

122 lines
4.3 KiB

import { z } from "zod";
import type { JSONSchema7 } from "json-schema";
import type { ToolExecutor, ToolResult } from "../../registry";
import type { AgentInvokeToolConfig } from "./config";
import { invokeAgentViaA2A } from "../../../a2a/client";
import { listAgents } from "../../../agent/agent";
export const agentInvokeInputSchema = z.object({
agentSlug: z.string().min(1).max(100).describe("目标 agent 的 slug"),
input: z.string().min(1).max(50000).describe("传递给目标 agent 的任务描述/输入内容"),
});
type AgentInvokeInput = z.infer<typeof agentInvokeInputSchema>;
export const agentInvokeExecutor: ToolExecutor<AgentInvokeToolConfig> = {
buildInputSchema(_config: AgentInvokeToolConfig): JSONSchema7 {
return {
type: "object",
properties: {
agentSlug: {
type: "string",
description: "目标 agent 的 slug(唯一标识符)",
},
input: {
type: "string",
description: "传递给目标 agent 的任务描述或输入内容",
},
},
required: ["agentSlug", "input"],
};
},
buildDescription(_config: AgentInvokeToolConfig): string {
return "调用另一个 agent 来处理子任务。目标 agent 必须已启用且标记为可调用(isCallable)。传入 agent slug 和任务描述,返回目标 agent 的回复内容。支持多级调用链,但有递归深度限制。";
},
async buildDescriptionAsync(_config: AgentInvokeToolConfig, ctx?: { agentId?: number | null }): Promise<string> {
const base = this.buildDescription(_config);
const callableAgents = await listAgents(true);
const currentAgentId = ctx?.agentId ?? null;
const available = callableAgents.filter((a) => a.isCallable === 1 && a.id !== currentAgentId);
if (available.length === 0) {
return `${base}\n\n当前没有可调用的 Agent。请勿调用此工具。`;
}
const agentList = available.map((a) => `- slug: "${a.slug}" — ${a.name}: ${a.description ?? "无描述"}`).join("\n");
return `${base}\n\n可调用的 Agent 列表(通过 agentSlug 参数指定):\n${agentList}`;
},
formatResult(result: ToolResult, _config: AgentInvokeToolConfig): string {
if (!result.success) {
return `Agent 调用失败: ${result.error ?? "未知错误"}`;
}
const meta = result.metadata;
const agentSlug = meta?.agentSlug ?? "unknown";
const depth = meta?.recursionDepth ?? 0;
const duration = meta?.durationMs ?? 0;
const data = typeof result.data === "string" ? result.data : JSON.stringify(result.data);
return `[Agent "${agentSlug}" 调用完成 | 深度: ${depth} | 耗时: ${duration}ms]\n${data}`;
},
async execute(
input: unknown,
config: AgentInvokeToolConfig,
ctx: { userId: number | null; agentId?: number | null; recursionDepth?: number },
): Promise<ToolResult> {
const start = Date.now();
const parsed = agentInvokeInputSchema.safeParse(input);
if (!parsed.success) {
return {
success: false,
data: null,
error: `参数校验失败: ${parsed.error.message}`,
metadata: { durationMs: Date.now() - start },
};
}
const data: AgentInvokeInput = parsed.data;
const currentDepth = ctx.recursionDepth ?? 0;
const result = await invokeAgentViaA2A(
{
agentSlug: data.agentSlug,
input: data.input,
context: {
userId: ctx.userId,
callerAgentId: ctx.agentId ?? null,
recursionDepth: currentDepth + 1,
},
},
{
maxRecursionDepth: config.maxRecursionDepth,
timeoutMs: config.timeoutMs,
maxOutputTokens: config.maxOutputTokens,
},
);
if (!result.ok) {
return {
success: false,
data: null,
error: result.error ?? `调用 agent ${data.agentSlug} 失败`,
metadata: { durationMs: Date.now() - start },
};
}
let output = result.output;
if (output.length > config.resultTruncateLength) {
output = output.slice(0, config.resultTruncateLength) + `\n...[结果已截断,原始长度 ${output.length} 字符]`;
}
return {
success: true,
data: output,
metadata: {
durationMs: Date.now() - start,
agentSlug: data.agentSlug,
recursionDepth: currentDepth + 1,
},
};
},
};