news 2026/9/16 10:21:53

使用 BedrockLLMAgent 与自定义 Tools 构建数学 Agent:multi-agent-orchestrator 实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 BedrockLLMAgent 与自定义 Tools 构建数学 Agent:multi-agent-orchestrator 实战指南

使用 BedrockLLMAgent 与自定义 Tools 构建数学 Agent:multi-agent-orchestrator 实战指南

【免费下载链接】agent-squadFlexible and powerful framework for managing multiple AI agents and handling complex conversations项目地址: https://gitcode.com/GitHub_Trending/mu/agent-squad

本文以 multi-agent-orchestrator 框架为背景,完整演示如何基于 Amazon Bedrock 的BedrockLLMAgent与自定义工具(Tools)构建一个可执行四则运算、幂运算、三角函数、对数及均值/中位数/方差等统计计算的数学 Agent。你将掌握工具定义(Tool Definition)、工具处理器(Tool Handler)、底层函数实现、Agent 装配以及将其接入MultiAgentOrchestrator进行实时计算的全过程,同时获得仓库源码级的实现佐证。

背景:为什么需要"会计算"的 Agent

基础大语言模型在自然语言理解上表现出色,但涉及精确的数值运算时往往不够可靠。multi-agent-orchestrator 提供了一条标准路径:让 LLM 负责拆解问题、生成"调用计划",把具体的数学计算交给可编程、可验证的 JavaScript 函数去执行,再将计算结果以工具结果的形式回传给模型组织最终答案。本指南中的数学 Agent 正是这一模式的典型落地案例,它只负责数学领域的问题,其余问题则由编排器路由给其他 Agent(如天气、健康、技术类 Agent)。

一、定义数学工具(Tool Definition)

工具定义是 Agent 能力边界的"说明书",它告诉 Bedrock 模型:有哪些工具可用、每个工具的入参结构是什么、哪些参数是必填的。本示例定义了两个工具,完整定义见 examples/chat-demo-app/lambda/multi-agent/math_tool.ts 与 examples/local-demo/tools/math_tool.ts。

A. 工具描述(Tool Descriptions)

export const mathAgentToolDefinition = [ { toolSpec: { name: "perform_math_operation", description: "Perform a mathematical operation. This tool supports basic arithmetic and various mathematical functions.", inputSchema: { json: { type: "object", properties: { operation: { type: "string", description: "The mathematical operation to perform. Supported operations include:\n" + "- Basic arithmetic: 'add', 'subtract', 'multiply', 'divide'\n" + "- Exponentiation: 'power'\n" + "- Trigonometric: 'sin', 'cos', 'tan'\n" + "- Logarithmic and exponential: 'log', 'exp'\n" + "- Rounding: 'round', 'floor', 'ceil'\n" + "- Other: 'sqrt', 'abs'", }, args: { type: "array", items: { type: "number" }, description: "The arguments for the operation.", }, }, required: ["operation", "args"], }, }, }, }, { toolSpec: { name: "perform_statistical_calculation", description: "Perform statistical calculations on a set of numbers.", inputSchema: { json: { type: "object", properties: { operation: { type: "string", description: "The statistical operation to perform. Supported operations include:\n" + "'mean', 'median', 'mode', 'variance', 'stddev'", }, args: { type: "array", items: { type: "number" }, description: "The set of numbers to perform the statistical operation on.", }, }, required: ["operation", "args"], }, }, }, }, ];

要点说明:

  • 该定义声明了两个工具:perform_math_operation(数学运算)与perform_statistical_calculation(统计计算);
  • 每个工具都包含namedescriptioninputSchema三要素,其中inputSchema.json遵循 JSON Schema 规范,通过properties描述入参、required声明必填项;
  • 两个工具统一采用operation(字符串,指明具体操作)加args(数字数组,操作数)的输入约定,便于模型理解与工具处理器分发。

在仓库的实际实现中,工具定义对入参描述更加细致,例如args会进一步说明:"加法和乘法可接受多个参数;减法、除法和幂运算必须恰好两个参数;其余操作大多接受一个参数"(见 examples/chat-demo-app/lambda/multi-agent/math_tool.ts)。这类"元数据级"的约束描述能显著提升模型生成合法入参的成功率。

B. 工具处理器(Tool Handler)

工具处理器负责解析模型返回的toolUse请求、调用实际执行函数、并把结果包装成符合 Bedrock 协议的消息回传给模型:

import { ConversationMessage, ParticipantRole } from "multi-agent-orchestrator"; export async function mathToolHandler(response, conversation: ConversationMessage[]): Promise<ConversationMessage> { const responseContentBlocks = response.content as any[]; let toolResults: any = []; if (!responseContentBlocks) { throw new Error("No content blocks in response"); } for (const contentBlock of response.content) { if ("toolUse" in contentBlock) { const toolUseBlock = contentBlock.toolUse; const toolUseName = toolUseBlock.name; if (toolUseName === "perform_math_operation") { const result = executeMathOperation(toolUseBlock.input.operation, toolUseBlock.input.args); // Process and add result to toolResults } else if (toolUseName === "perform_statistical_calculation") { const result = calculateStatistics(toolUseBlock.input.operation, toolUseBlock.input.args); // Process and add result to toolResults } } } const message: ConversationMessage = { role: ParticipantRole.USER, content: toolResults }; return messages; }

处理流程说明:

  1. 处理器遍历模型响应中的 content 块,识别包含toolUse的块;
  2. 依据toolUse.name分发到executeMathOperationcalculateStatistics
  3. 将结果格式化为toolResult结构(需携带toolUseId以关联原始调用),最终以ParticipantRole.USER角色消息返回,从而形成"模型请求工具 → 工具返回结果"的对话闭环。

仓库中的完整实现 examples/chat-demo-app/lambda/multi-agent/math_tool.ts 展示了比上述骨架更完整的细节:

  • sincostan三角运算在调用前自动将角度从度转换为弧度(degToRad = Math.PI / 180);
  • 成功时以content: [{ json: { result } }], status: "success"返回结果;
  • 失败时以content: [{ text: error }], status: "error"返回错误信息,同时通过Logger记录每次工具调用轨迹(如Tool call 1: perform_math_operation: args=[16] operation=sqrt result=4),便于调试多步计算链。

C. 数学运算与统计计算函数

两个底层执行函数负责真实的数值计算。它们都遵循同一返回契约:成功返回{ result: number },失败返回{ error: string },从而让上层处理器无需处理异常中断。

/** * Executes a mathematical operation using JavaScript's Math library. * @param operation - The mathematical operation to perform. * @param args - Array of numbers representing the arguments for the operation. * @returns An object containing either the result of the operation or an error message. */ function executeMathOperation( operation: string, args: number[] ): { result: number } | { error: string } { const safeEval = (code: string) => { return Function('"use strict";return (' + code + ")")(); }; try { let result: number; switch (operation.toLowerCase()) { case 'add': case 'addition': result = args.reduce((sum, current) => sum + current, 0); break; case 'subtract': case 'subtraction': if (args.length !== 2) { throw new Error('Subtraction requires exactly two arguments'); } result = args[0] - args[1]; break; case 'multiply': case 'multiplication': result = args.reduce((product, current) => product * current, 1); break; case 'divide': case 'division': if (args.length !== 2) { throw new Error('Division requires exactly two arguments'); } if (args[1] === 0) { throw new Error('Division by zero'); } result = args[0] / args[1]; break; case 'power': case 'exponent': if (args.length !== 2) { throw new Error('Power operation requires exactly two arguments'); } result = Math.pow(args[0], args[1]); break; default: // For other operations, use the Math object if the function exists if (typeof Math[operation] === 'function') { result = safeEval(`Math.${operation}(${args.join(",")})`); } else { throw new Error(`Unsupported operation: ${operation}`); } } return { result }; } catch (error) { return { error: `Error executing ${operation}: ${(error as Error).message}`, }; } } function calculateStatistics(operation: string, args: number[]): { result: number } | { error: string } { try { switch (operation.toLowerCase()) { case 'mean': return { result: args.reduce((sum, num) => sum + num, 0) / args.length }; case 'median': { const sorted = args.slice().sort((a, b) => a - b); const mid = Math.floor(sorted.length / 2); return { result: sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2, }; } case 'mode': { const counts = args.reduce((acc, num) => { acc[num] = (acc[num] || 0) + 1; return acc; }, {} as Record<number, number>); const maxCount = Math.max(...Object.values(counts)); const modes = Object.keys(counts).filter(key => counts[Number(key)] === maxCount); return { result: Number(modes[0]) }; // Return first mode if there are multiple } case 'variance': { const mean = args.reduce((sum, num) => sum + num, 0) / args.length; const squareDiffs = args.map(num => Math.pow(num - mean, 2)); return { result: squareDiffs.reduce((sum, square) => sum + square, 0) / args.length }; } case 'stddev': { const mean = args.reduce((sum, num) => sum + num, 0) / args.length; const squareDiffs = args.map(num => Math.pow(num - mean, 2)); const variance = squareDiffs.reduce((sum, square) => sum + square, 0) / args.length; return { result: Math.sqrt(variance) }; } default: throw new Error(`Unsupported statistical operation: ${operation}`); } } catch (error) { return { error: `Error executing ${operation}: ${(error as Error).message}` }; } }

实现要点:

  • executeMathOperationadd/multiply使用reduce支持多操作数聚合;对subtract/divide/power强制校验恰好两个参数;divide额外做了除零保护;
  • 对于未显式列举的操作,通过typeof Math[operation] === 'function'检查后,用safeEval动态调用 JavaScript 内置Math对象方法(如sqrtabslogexproundfloorceil等),使工具具备良好的可扩展性;'use strict'模式限制了动态求值的作用域;
  • calculateStatistics实现了均值(mean)、中位数(median,奇偶长度分别处理)、众数(mode,多众数时返回首个)、方差(variance,总体方差)与标准差(stddev)五种统计指标;
  • 所有分支均以try/catch包裹,任何非法操作或参数错误都会转换为{ error }返回,不会导致 Agent 调用中断。

二、创建数学 Agent(BedrockLLMAgent)

将上述工具定义与处理器封装进一个BedrockLLMAgent实例,并通过setSystemPrompt注入领域专用的系统提示词:

import { BedrockLLMAgent } from 'multi-agent-orchestrator'; import { mathAgentToolDefinition, mathToolHandler } from './mathTools'; const MATH_PROMPT = ` You are a mathematical assistant capable of performing various mathematical operations and statistical calculations. Use the provided tools to perform calculations. Always show your work and explain each step and provide the final result of the operation. If a calculation involves multiple steps, use the tools sequentially and explain the process. Only respond to mathematical queries. For non-math questions, politely redirect the conversation to mathematics. `; const mathAgent = new BedrockLLMAgent({ name: "Math Agent", description: "Specialized agent for performing mathematical operations and statistical calculations.", streaming: false, inferenceConfig: { temperature: 0.1, }, toolConfig: { useToolHandler: mathToolHandler, tool: mathAgentToolDefinition, toolMaxRecursions: 5 } }); mathAgent.setSystemPrompt(MATH_PROMPT);

配置项逐项解析

对照 typescript/src/agents/bedrockLLMAgent.ts 中的BedrockLLMAgentOptions接口,各配置项的作用如下:

配置项类型说明默认值
name/descriptionstringAgent 名称与能力描述,用于分类器路由与系统提示词生成(继承自AgentOptions,见 typescript/src/agents/agent.ts)必填
modelIdstringBedrock 模型 ID,仓库内置的默认模型为anthropic.claude-3-haiku-20240307-v1:0(见 typescript/src/types/index.ts)Claude 3 Haiku
regionstringBedrock 服务的 AWS 区域,用于构造BedrockRuntimeClient按 SDK 默认链路解析
streamingboolean是否开启流式输出。数学计算场景通常设为false,便于一次拿到完整计算结果false
inferenceConfig.temperaturenumber采样温度,数值越低回答越确定。数学场景建议设为 0~0.1,降低模型"自由发挥"概率未设置
toolConfig.toolAgentTools | Tool[]工具定义数组(即上文mathAgentToolDefinition),会原样透传给 Bedrock Converse API 的toolConfig.tools
toolConfig.useToolHandlerfunction自定义工具处理器,签名(response, conversation) => any
toolConfig.toolMaxRecursionsnumber模型连续调用工具的最大轮次,防止多步计算陷入无限循环20

系统提示词的设计

仓库在 examples/chat-demo-app/lambda/multi-agent/prompts.ts 中提供了更完整的MATH_AGENT_PROMPT模板,它额外约束了:

  • "必须展示计算过程、解释每一步、给出最终结果;多步计算要按顺序调用工具并说明流程";
  • "只回答数学问题,非数学问题礼貌地引导回数学主题";
  • 输出格式要求:使用 Markdown 结构(##/###标题、编号列表、**粗体**强调关键结果、LaTeX 代码块展示公式、表格组织步骤)。

结合 typescript/src/agents/bedrockLLMAgent.ts 可知,setSystemPrompt(template, variables)支持在模板中使用{{variable}}占位符并在运行时替换,适合需要动态注入上下文(如 Agent 列表、知识库检索结果)的场景。

三、将数学 Agent 加入编排器

MultiAgentOrchestrator通过分类器将用户请求路由到最合适的 Agent。添加方式十分简单:

import { MultiAgentOrchestrator } from "multi-agent-orchestrator"; const orchestrator = new MultiAgentOrchestrator(); orchestrator.addAgent(mathAgent);

addAgent方法内部会根据name生成唯一 Agent ID(去除特殊字符、空格转连字符、转小写),并注册到路由表中(见 typescript/src/agents/agent.ts)。在真实应用中,编排器通常还会配置存储(如DynamoDbChatStorage)与分类器(如BedrockClassifier),并在一个 Lambda 入口中注册多个 Agent——examples/chat-demo-app/lambda/multi-agent/index.ts 展示了如何把Math AgentWeather AgentHealth AgentTech Agent等一并注册,实现多领域问题的统一路由。

四、使用数学 Agent 执行计算

Agent 注册完成后,即可通过编排器的routeRequest方法发起请求:

const response = await orchestrator.routeRequest( "What is the square root of 16 plus the cosine of 45 degrees?", "user123", "session456" );

routeRequest(userInput, userId, sessionId, additionalParams)的调用链见 typescript/src/orchestrator.ts:它先调用分类器确定目标 Agent(本例为 Math Agent),再交由agentProcessRequest执行;若分类器未选中任何 Agent,则返回NO_SELECTED_AGENT_MESSAGE配置的兜底提示。

工作原理:工具调用的递归闭环

一次数学问题的完整处理流程如下:

  1. 编排器收到数学类查询,经分类器路由到 Math Agent;
  2. Math Agent 使用MATH_PROMPT作为系统提示词调用 Bedrock Converse API;
  3. 模型判定需要计算时,在响应中输出toolUse内容块(如请求perform_math_operation(operation='sqrt', args=[16]));
  4. mathToolHandler解析toolUse,调用executeMathOperation/calculateStatistics完成真实计算;
  5. 计算结果的toolResult以 USER 角色消息追加进对话,并再次发送给模型;
  6. 模型基于工具结果组织最终回答(展示推导过程与结论);若模型再次请求工具,则重复 3~5 步,直到输出end_turn或达到toolMaxRecursions上限。

对应到源码实现(typescript/src/agents/bedrockLLMAgent.ts),processRequest使用do...while循环执行"发送请求 → 检测toolUse→ 调用工具处理器 → 格式化结果回填对话"的迭代,直到响应中不再包含toolUse或递归次数耗尽。格式化阶段(formatToolResults,见 typescript/src/agents/bedrockLLMAgent.ts)会把工具结果转换为 Bedrock 协议要求的toolResult结构。流式模式(streaming: true)下,handleStreamingResponse 同样实现了工具调用的递归处理,且支持边生成文本边收集工具入参。

因此,"16 的平方根加上 45 度的余弦"这类复合问题会被模型自动拆解为多个工具调用(如先sqrt(16),再将 45° 转为弧度后求cos),由处理器逐步执行并把每一步结果回传,最终由模型汇总为带推导过程的可读答案。

五、把数学 Agent 落地到本地或生产环境

本地交互式运行

examples/local-demo/local-orchestrator.ts 提供了一个基于readline的本地交互入口:构建编排器、注册各 Agent、进入 REPL 循环调用orchestrator.routeRequest,并区分流式/非流式输出。你可以将mathAgent及 examples/local-demo/tools/math_tool.ts 中的工具定义与处理器并入其中,通过终端直接验证"什么是 12 和 8 的平均数与标准差"这类查询。

部署到 AWS Lambda

生产场景可参考 examples/chat-demo-app 的完整架构:BedrockLLMAgent数学 Agent 与DynamoDbChatStorage(会话历史持久化)、BedrockClassifier(智能路由)、流式响应(awslambda.streamifyResponse)配合使用,前端 UI 见 examples/chat-demo-app/ui。其中数学 Agent 的完整注册代码位于 examples/chat-demo-app/lambda/multi-agent/index.ts,工具与处理器位于 examples/chat-demo-app/lambda/multi-agent/math_tool.ts。

运行前提

  • 需要具备 Amazon Bedrock 访问权限,并确保所用模型(默认anthropic.claude-3-haiku-20240307-v1:0,可在modelId中覆盖)在目标区域已开通;
  • 本地运行需先npm install安装multi-agent-orchestrator及 AWS SDK 依赖,并配置 AWS 凭证(环境变量或凭证文件);
  • 工具入参需符合 JSON Schema 约束:operation为字符串、args为数字数组、二者均必填;模型生成的非法参数会由底层函数转为{ error }返回而非抛出异常。

总结

通过本指南,你已经完成了一个"定义工具 → 编写处理器 → 实现计算函数 → 装配 BedrockLLMAgent → 注册到编排器 → 实时计算"的完整闭环。这套模式的核心价值在于:把 LLM 的"规划能力"与代码的"精确执行能力"解耦——模型负责理解问题、编排步骤、解释过程,JavaScript 函数负责保证计算的正确性与可复现性,toolMaxRecursions与严格参数校验则保证了多步计算链的安全收敛。同样的方法论可以轻松迁移到天气查询、知识库检索、API 调用等任意需要"Agent + 工具"组合的业务场景。

【免费下载链接】agent-squadFlexible and powerful framework for managing multiple AI agents and handling complex conversations项目地址: https://gitcode.com/GitHub_Trending/mu/agent-squad

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 10:21:03

深入理解JavaScript原型链机制与继承实现

1. 原型链的本质与运作机制在JavaScript中&#xff0c;每个对象都有一个隐藏的[[Prototype]]属性&#xff0c;它指向另一个对象或null。当访问对象的属性时&#xff0c;如果对象自身没有该属性&#xff0c;JavaScript会沿着[[Prototype]]链向上查找&#xff0c;直到找到该属性或…

作者头像 李华
网站建设 2026/9/16 10:19:53

《易经》与人生

文章目录一、前言二、《易经》到底在讲什么&#xff1f;三、如何读懂卦和爻&#xff1f;四、《易经》的现代启示五、结语一、前言 今天我想和大家聊一本书&#xff0c;与其说这是一本书&#xff0c;确切点说&#xff0c;是一套为人处世的哲学观&#xff0c;关于《易经》的作者…

作者头像 李华
网站建设 2026/9/16 10:19:29

江苏好客搜GEO观察:一家装备厂如何被AI问答反复引用

在生成式引擎优化的讨论中&#xff0c;多数分析停留在概念层面&#xff0c;而好客搜公司在江苏苏州创业园的实践提供了一个可拆解的样本。这家2016年成立的高新技术企业&#xff0c;从搜索类产品起步&#xff0c;2020年切入短视频系统开发&#xff0c;2025年推出智搜GEO&#x…

作者头像 李华
网站建设 2026/9/16 10:19:28

MATLAB主从博弈在电热综合能源系统中的应用

1. 项目概述电热综合能源系统动态定价与能量管理是当前能源互联网领域的前沿研究方向。这个MATLAB项目通过主从博弈&#xff08;Stackelberg博弈&#xff09;框架&#xff0c;构建了一个双层优化模型&#xff0c;用于解决电热耦合系统中的定价策略和能源调度问题。在实际工程中…

作者头像 李华
网站建设 2026/9/16 10:18:59

破解项目交付后期迷茫的实战方法论

1. 项目概述&#xff1a;交付后期迷茫现象的普遍性第一次独立负责项目交付时&#xff0c;我像打了鸡血一样每天工作16小时。前三个月进展神速&#xff0c;客户每周例会都在表扬。但到了第六个月&#xff0c;突然发现团队士气低落&#xff0c;我自己也经常对着电脑发呆——明明交…

作者头像 李华
网站建设 2026/9/16 10:18:21

10MB轻量级API客户端Bruno:告别Postman臃肿,拥抱Git与CI高效联调

面对 Postman 越来越臃肿的安装包、越来越慢的启动速度&#xff0c;还有动不动就弹出来的登录提醒&#xff0c;我相信不少接口测试的老手心里都憋着一股火。我自己的电脑上&#xff0c;Postman 从双击图标到真正能输入 URL&#xff0c;慢的时候能转七八秒的圈&#xff0c;这还是…

作者头像 李华