首页 / 资讯中心 / 文章详情

A2A协议与LangChain.js实战:用TaoToken统一Key构建微型软件工厂

A2A协议与LangChain.js实战:用TaoToken统一Key构建微型软件工厂 ★ FEATURED ARTICLE
1. 为什么单 Agent 写不动“贪吃蛇”而 A2A 能你可能已经用 LangChain.js 写过单 Agent一个 Prompt 里塞进需求分析、写代码、写测试模型上下文一长就开始胡言乱语。我试过让一个 Agent 同时干 PM、Dev、QA 三件事结果它把测试报告写成了代码注释还把需求文档里的“贪吃蛇”理解成了“贪吃蛇游戏服务器”。问题不在模型而在架构。单个 Agent 的上下文窗口有限技能树也不可能无限扩展。当你面对“开发一个贪吃蛇游戏”这种模糊且复杂的任务时真正需要的是一个虚拟团队——多智能体协作系统MAS, Multi-Agent System。A2AAgent-to-Agent协议就是解决 Agent 之间“怎么说话、怎么发现彼此、怎么移交任务”的设计模式。它不是某个硬性标准而是一套通信规范每个 Agent 既是 Server监听端口响应请求也是 Client主动调用其他 Agent。消息层推荐 JSON-RPC 2.0因为 Agent 关注的是“行为”Action而非“资源”ResourcePOST /generate_code这种指令式语义比 RESTful 更符合直觉。本文聚焦 Node.js 环境下用 LangChain.js 实现 A2A 协议 Agent 协作通过 TaoToken 统一 Key/API 通道打通多 Agent 调用。你会拿到可复制的config.toml与settings.json配置骨架、JSON-RPC 消息示例以及本地启动微型软件工厂的完整验证步骤。适合已经写过单 Agent、想跑通多 Agent 协作链路的 Node.js 开发者。2. TaoToken 前置统一 Key 与 API 通道多 Agent 协作最烦的事情之一是每个 Agent 都要配一遍 API Key、Base URL、模型名。PM 用 GPT-4oDev 用 ClaudeQA 用另一个模型配置文件散落各处改一个环境变量要翻五个文件。TaoToken 在这里的角色是统一入口一个 Key、一个 API 通道所有 Agent 通过它调用不同模型。你不需要在每个 Agent 里硬编码不同的供应商地址只需要在配置里指定模型名请求统一走 TaoToken 的 API 端点。2.1 获取 API Key访问 TaoToken 控制台的 API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite创建一个新 Key。建议按项目命名比如a2a-software-factory方便后续排查。拿到 Key 后不要直接写进代码。我们用.env文件管理配合dotenv加载。2.2 配置骨架config.toml 与 settings.json虽然 Node.js 项目常用.env但多 Agent 场景下用config.toml管理 Agent 角色、端口、能力标签用settings.json管理模型参数结构更清晰。config.toml负责“谁在哪里、能干什么”# config.toml - Agent 注册与端口配置 [registry] port 3000 host localhost [agents.pm] name PM_Agent port 3001 capabilities [requirements_analysis] model gpt-4o-mini temperature 0.7 [agents.dev] name Dev_Agent port 3002 capabilities [coding] model gpt-4o-mini temperature 0.3 [agents.qa] name QA_Agent port 3003 capabilities [testing] model gpt-4o-mini temperature 0.2settings.json负责“怎么调用模型”{ llm: { baseURL: https://taotoken.net/api, apiKeyEnv: TAOTOKEN_API_KEY, defaultModel: gpt-4o-mini, timeout: 30000, maxRetries: 2 }, rpc: { version: 2.0, contentType: application/json } }注意baseURL填https://taotoken.net/api不要加 UTM 参数。API Key 通过环境变量TAOTOKEN_API_KEY注入不要提交到 Git。2.3 安装依赖在项目根目录执行npm init -y npm install express body-parser axios dotenv langchain/openai langchain/core iarna/tomliarna/toml用来解析config.tomllangchain/openai负责 LLM 调用axios处理 Agent 之间的 HTTP 通信。3. 可复制配置JSON-RPC 消息与 Agent 运行时这一章是核心。我们把 A2A 协议的四个层面落到代码传输层用 HTTP消息层用 JSON-RPC 2.0发现层用注册中心语义层用 LangChain 的 Prompt 链。3.1 JSON-RPC 消息示例A2A 协议要求 Agent 之间用标准化的 JSON-RPC 2.0 通信。一个完整的请求-响应长这样请求PM 调用 Dev 的write_code方法{ jsonrpc: 2.0, method: write_code, params: { requirement: 实现一个贪吃蛇游戏的核心逻辑包含蛇的移动、食物生成、碰撞检测 }, id: 1712345678901 }成功响应{ jsonrpc: 2.0, result: { code: class SnakeGame { ... }, qa_report: Pass: 逻辑完整建议增加边界测试 }, id: 1712345678901 }错误响应{ jsonrpc: 2.0, error: { code: -32601, message: Method not found }, id: 1712345678901 }为什么用 JSON-RPC 而不是 RESTful因为 Agent 之间传递的是“行为”而非“资源”。write_code是一个动作POST /generate_code这种指令式语义比PUT /code/1更符合 Agent 的直觉。而且 JSON-RPC 无状态、轻量天然支持method和params的映射非常适合把 RPC 方法直接映射为 Agent 类的方法。3.2 协议封装与注册中心先写协议封装类把 JSON-RPC 的请求、成功、错误三种消息格式固定下来// protocol.js export class JsonRpcProtocol { static request(method, params, id Date.now()) { return { jsonrpc: 2.0, method, params, id }; } static success(result, id) { return { jsonrpc: 2.0, result, id }; } static error(code, message, id) { return { jsonrpc: 2.0, error: { code, message }, id }; } }注册中心是 A2A 的“大脑”。Agent 启动时向注册中心汇报自己的能力和 URL调用方通过能力标签查找目标 Agent而不是硬编码 IP。这实现了位置解耦// registry.js import express from express; import bodyParser from body-parser; const REGISTRY_PORT 3000; const registryApp express(); registryApp.use(bodyParser.json()); const services {}; registryApp.post(/register, (req, res) { const { name, url, capabilities } req.body; services[name] { url, capabilities }; console.log([Registry] 服务上线: ${name} [${capabilities.join(, )}]); res.json({ status: ok }); }); registryApp.post(/discover, (req, res) { const { capability } req.body; const foundName Object.keys(services).find((key) services[key].capabilities.includes(capability) ); if (foundName) { const service services[foundName]; console.log([Registry] 发现请求: ${capability} - ${service.url}); res.json({ url: service.url }); } else { res.status(404).json({ error: Service not found }); } }); registryApp.listen(REGISTRY_PORT, () { console.log([Registry] 注册中心启动于 http://localhost:${REGISTRY_PORT}); });3.3 Agent 运行时基类把 Agent 的通用行为启动 HTTP 服务、处理 JSON-RPC、向注册中心汇报、调用其他 Agent抽成基类。这样具体的业务 Agent 只需要关注 Prompt 和逻辑// agent-runtime.js import express from express; import bodyParser from body-parser; import axios from axios; import { ChatOpenAI } from langchain/openai; import { JsonRpcProtocol } from ./protocol.js; const REGISTRY_PORT 3000; export class AgentRuntime { constructor(name, port, capabilities, modelConfig) { this.name name; this.port port; this.capabilities capabilities; this.app express(); this.app.use(bodyParser.json()); this.model new ChatOpenAI({ modelName: modelConfig.model, temperature: modelConfig.temperature, configuration: { baseURL: process.env.TAOTOKEN_BASE_URL || https://taotoken.net/api, apiKey: process.env.TAOTOKEN_API_KEY, }, }); this.app.post(/json-rpc, (req, res) this.handleRpc(req, res)); } async start() { return new Promise((resolve) { this.app.listen(this.port, async () { console.log([${this.name}] 启动于 Port ${this.port}); await this.registerSelf(); resolve(); }); }); } async registerSelf() { try { await axios.post(http://localhost:${REGISTRY_PORT}/register, { name: this.name, url: http://localhost:${this.port}/json-rpc, capabilities: this.capabilities, }); } catch (e) { console.error([${this.name}] 注册失败: registry offline?); } } async handleRpc(req, res) { const { jsonrpc, method, params, id } req.body; if (jsonrpc ! 2.0) { return res.status(400).json(JsonRpcProtocol.error(-32600, Invalid Request, id)); } console.log([${this.name}] 收到任务: ${method}); try { if (this[method] typeof this[method] function) { const result await this[method](params); res.json(JsonRpcProtocol.success(result, id)); } else { res.status(404).json(JsonRpcProtocol.error(-32601, Method not found, id)); } } catch (error) { console.error(error); res.status(500).json(JsonRpcProtocol.error(-32000, error.message, id)); } } async callOtherAgent(capability, method, params) { console.log([${this.name}] 寻找具备 ${capability} 能力的队友...); const discoverRes await axios.post( http://localhost:${REGISTRY_PORT}/discover, { capability } ); const targetUrl discoverRes.data.url; console.log([${this.name}] 发送任务到 ${targetUrl}: ${method}); const rpcPayload JsonRpcProtocol.request(method, params); const response await axios.post(targetUrl, rpcPayload); if (response.data.error) throw new Error(response.data.error.message); return response.data.result; } }3.4 业务 Agent 实现有了基类定义具体 Agent 就像写普通业务类。LangChain 在这里负责处理非结构化的自然语言逻辑// agents.js import { ChatPromptTemplate } from langchain/core/prompts; import { StringOutputParser } from langchain/core/output_parsers; import { AgentRuntime } from ./agent-runtime.js; export class PMAgent extends AgentRuntime { constructor(config) { super(PM_Agent, config.port, [requirements_analysis], config); } async start_project({ user_input }) { console.log([PM] 正在分析用户需求: ${user_input}); const chain ChatPromptTemplate.fromTemplate( 将此需求转化为简短的技术需求文档(PRD)包含功能点和技术栈建议: {input} ) .pipe(this.model) .pipe(new StringOutputParser()); const prd await chain.invoke({ input: user_input }); console.log([PM] PRD 生成完毕); const codeResult await this.callOtherAgent(coding, write_code, { requirement: prd, }); return { status: finished, prd_summary: prd.slice(0, 50) ..., final_output: codeResult, }; } } export class DevAgent extends AgentRuntime { constructor(config) { super(Dev_Agent, config.port, [coding], config); } async write_code({ requirement }) { console.log([Dev] 收到 PRD开始写代码...); const chain ChatPromptTemplate.fromTemplate( 根据需求写一段 JavaScript 代码。只返回代码块不要 Markdown: {req} ) .pipe(this.model) .pipe(new StringOutputParser()); const code await chain.invoke({ req: requirement }); console.log([Dev] 代码编写完毕); const testReport await this.callOtherAgent(testing, review_code, { code, requirement, }); return { code, qa_report: testReport }; } } export class QAAgent extends AgentRuntime { constructor(config) { super(QA_Agent, config.port, [testing], config); } async review_code({ code, requirement }) { console.log([QA] 收到代码运行静态分析...); const chain ChatPromptTemplate.fromTemplate( 需求: {req}\n代码: {code}\n请给出简短评测结论(Pass/Fail)和改进建议: ) .pipe(this.model) .pipe(new StringOutputParser()); const report await chain.invoke({ req: requirement, code }); console.log([QA] 测试完毕); return report; } }4. 验证请求本地启动微型软件工厂配置和代码都齐了现在跑通整条链路。4.1 启动入口// index.js import dotenv/config; import fs from fs; import TOML from iarna/toml; import axios from axios; import { JsonRpcProtocol } from ./protocol.js; import { PMAgent, DevAgent, QAAgent } from ./agents.js; const config TOML.parse(fs.readFileSync(./config.toml, utf-8)); async function main() { const pm new PMAgent({ ...config.agents.pm, model: config.agents.pm.model }); const dev new DevAgent({ ...config.agents.dev, model: config.agents.dev.model }); const qa new QAAgent({ ...config.agents.qa, model: config.agents.qa.model }); await Promise.all([pm.start(), dev.start(), qa.start()]); await new Promise((r) setTimeout(r, 1000)); console.log(\n); console.log( A2A 分布式网络已建立 (HTTP/JSON-RPC)); console.log(\n); const userPrompt 帮我写一个简单的 Node.js HTTP Server返回 Hello World; try { console.log([User] 提交需求给 PM...); const response await axios.post( http://localhost:3001/json-rpc, JsonRpcProtocol.request(start_project, { user_input: userPrompt }) ); console.log(\n 最终交付成果 ); console.log(JSON.stringify(response.data.result, null, 2)); } catch (e) { console.error(调用失败:, e.message); } } main();4.2 环境变量与启动命令创建.env文件TAOTOKEN_API_KEY你的Key TAOTOKEN_BASE_URLhttps://taotoken.net/api启动注册中心单独终端node registry.js启动软件工厂node index.js4.3 预期输出你会看到类似微服务日志的流式输出[Registry] 注册中心启动于 http://localhost:3000 [PM_Agent] 启动于 Port 3001 [Dev_Agent] 启动于 Port 3002 [QA_Agent] 启动于 Port 3003 [Registry] 服务上线: PM_Agent [requirements_analysis] [Registry] 服务上线: Dev_Agent [coding] [Registry] 服务上线: QA_Agent [testing] [User] 提交需求给 PM... [PM] 正在分析用户需求: 帮我写一个简单的 Node.js HTTP Server返回 Hello World [PM] PRD 生成完毕 [PM] 寻找具备 coding 能力的队友... [Registry] 发现请求: coding - http://localhost:3002/json-rpc [PM] 发送任务到 http://localhost:3002/json-rpc: write_code [Dev] 收到 PRD开始写代码... [Dev] 代码编写完毕 [Dev] 寻找具备 testing 能力的队友... [Registry] 发现请求: testing - http://localhost:3003/json-rpc [Dev] 发送任务到 http://localhost:3003/json-rpc: review_code [QA] 收到代码运行静态分析... [QA] 测试完毕最终交付成果里包含prd_summary、code和qa_report三个字段说明 PM → Dev → QA 的 A2A 调用链完整跑通。5. 本篇常见错排查5.1 注册中心 404Service not found现象[Registry] 未找到具备 coding 的服务Dev Agent 明明启动了。原因通常是 Agent 启动后注册请求还没完成PM 就开始发现。解决在main()里Promise.all启动所有 Agent 后加一个setTimeout等待 1 秒确保注册完成。生产环境可以用重试机制发现失败后隔 500ms 重试三次。5.2 JSON-RPC 返回 -32601 Method not found现象调用write_code时返回方法不存在。检查两点一是handleRpc里this[method]的映射方法名必须和 JSON-RPC 请求里的method完全一致大小写敏感二是write_code必须定义在 Agent 类上不能是私有方法或箭头函数属性箭头函数不在原型链上this[method]找不到。5.3 TaoToken 调用返回 401现象LLM 调用报鉴权失败。检查.env里的TAOTOKEN_API_KEY是否被dotenv正确加载。index.js第一行必须是import dotenv/config且.env文件在项目根目录。另外确认baseURL是https://taotoken.net/api不要多加路径或参数。5.4 端口冲突 EADDRINUSE现象启动时报EADDRINUSE: address already in use :::3001。说明上一个进程没退干净。用lsof -i :3001找到进程号kill -9掉。或者改config.toml里的端口号。建议每个 Agent 端口在配置里集中管理避免硬编码。5.5 Agent 之间循环调用现象PM 调用 DevDev 又调用 PM日志无限循环。A2A 协议里没有强制的调用链终止机制需要你在业务逻辑里控制。比如 Dev 的write_code只调用testing能力不调用requirements_analysis。能力标签的设计要避免环形依赖。如果确实需要复杂编排引入一个 Orchestrator Agent 做流程控制而不是让 Agent 互相随意调用。6. 下一步把 Key 管好把链路跑长跑通这个微型软件工厂后你会发现 A2A 的真正价值在于解耦和可扩展。PM 根本不需要知道 QA 的存在它只关心“能力”。这意味着你可以随时替换 Dev Agent 的实现——从 GPT-4o-mini 换成更强的模型或者换成用 Python 写的 Agent只要它注册了coding能力并遵循 JSON-RPC 协议整条链路无需修改。多 Agent 协作的调用量会比单 Agent 高一个数量级统一 Key 和 API 通道能省掉大量配置维护成本。你可以在 TaoToken 控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite里查看各模型的调用情况按项目分配不同的 Key。如果后续要接入长期编码或 Agent 工作流可以了解 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite它针对高频编码场景做了通道优化。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite里面有各语言 SDK 的完整示例。先把本地这三个 Agent 跑稳再试着加第四个 Security Agent注册security_audit能力在 Dev 和 QA 之间插入一道安全审查。链路越长A2A 的解耦优势越明显。
阅读完成 · 觉得有帮助?
咨询建站