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.
 
 
 
 

103 lines
3.2 KiB

/**
* A2A 内部客户端
*
* 替代 collaboration.ts 的 invokeAgent(),提供与原接口兼容的
* agent 间调用能力,底层走 A2A Service 层。
*
* 设计原则:
* - 接口签名与 invokeAgent 完全兼容,便于无缝替换
* - 递归深度、超时、自调用阻断通过 A2AInvocationContext 传递
* - 未来可切换为真实 HTTP 调用本机 A2A 端点(接口不变)
*/
import type { AgentInvocation, AgentInvocationResult } from "../agent/types";
import { getAgentBySlug } from "../agent/agent";
import { createTask, sendTask } from "./service";
import { textToA2AMessage, a2aMessageToText } from "./converter";
import { DEFAULT_A2A_CONFIG, JSONRPC_ERROR_CODES } from "./types";
import type { A2AInvocationContext } from "./types";
import log4js from "logger";
const logger = log4js.getLogger("APP");
export async function invokeAgentViaA2A(
invocation: AgentInvocation,
options?: {
maxRecursionDepth?: number;
timeoutMs?: number;
maxOutputTokens?: number;
},
): Promise<AgentInvocationResult> {
const { agentSlug, input, context } = invocation;
const maxRecursionDepth = options?.maxRecursionDepth ?? DEFAULT_A2A_CONFIG.maxRecursionDepth;
const timeoutMs = options?.timeoutMs ?? DEFAULT_A2A_CONFIG.timeoutMs;
const maxOutputTokens = options?.maxOutputTokens ?? DEFAULT_A2A_CONFIG.maxOutputTokens;
const userId = context?.userId ?? null;
const callerAgentId = context?.callerAgentId ?? null;
const recursionDepth = context?.recursionDepth ?? 0;
const agent = await getAgentBySlug(agentSlug);
if (!agent) {
return { agentSlug, output: "", ok: false, error: `agent not found: ${agentSlug}` };
}
if (!agent.isCallable) {
return { agentSlug, output: "", ok: false, error: `agent not callable: ${agentSlug}` };
}
if (callerAgentId !== null && callerAgentId === agent.id) {
return { agentSlug, output: "", ok: false, error: `self-invocation blocked: agent ${agentSlug} cannot invoke itself` };
}
if (recursionDepth >= maxRecursionDepth) {
return {
agentSlug,
output: "",
ok: false,
error: `max recursion depth (${maxRecursionDepth}) reached`,
};
}
const message = textToA2AMessage(input, "user");
try {
const { task } = await createTask({
calleeAgentSlug: agentSlug,
message,
callerAgentId,
userId,
});
const a2aContext: A2AInvocationContext = {
callerAgentId,
calleeAgentId: agent.id,
calleeAgentSlug: agentSlug,
userId,
sessionId: task.sessionId,
recursionDepth,
maxRecursionDepth,
timeoutMs,
maxOutputTokens,
};
const completedTask = await sendTask(task.id, a2aContext);
const output = completedTask.message
? a2aMessageToText(completedTask.message)
: "";
logger.info(
"[A2A-CLIENT] invokeAgentViaA2A done agentSlug=%s taskId=%s outputLen=%d",
agentSlug,
task.id,
output.length,
);
return { agentSlug, output, ok: true };
} catch (err) {
const errorMsg = err instanceof Error ? err.message : String(err);
logger.error("[A2A-CLIENT] invokeAgentViaA2A error agentSlug=%s error=%s", agentSlug, errorMsg);
return { agentSlug, output: "", ok: false, error: errorMsg };
}
}