基本多智能体协作¶
单个智能体通常可以使用少数工具在单个领域内有效地运作,但即使使用像gpt-4
这样的强大模型,它在使用多个工具方面也可能效率较低。
解决复杂任务的一种方法是采用“分而治之”的方法:为每个任务或领域创建一个专门的智能体,并将任务路由到正确的“专家”。
此笔记本(受论文AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation,Wu 等人启发)展示了使用 LangGraph 实现此方法的一种方式。
生成的图将类似于下面的图表
在我们开始之前,快速注意:这个和其他多智能体笔记本旨在展示如何在 LangGraph 中实现某些设计模式。如果模式适合您的需求,我们建议将其与文档中其他地方描述的一些其他基本模式相结合,以获得最佳性能。
在 [1]
已复制!
// process.env.OPENAI_API_KEY = "sk_...";
// process.env.TAVILY_API_KEY = "sk_...";
// process.env.LANGCHAIN_API_KEY = "sk_...";
// process.env.LANGCHAIN_TRACING_V2 = "true";
// process.env.LANGCHAIN_PROJECT = "Multi-agent Collaboration: LangGraphJS";
// process.env.OPENAI_API_KEY = "sk_..."; // process.env.TAVILY_API_KEY = "sk_..."; // process.env.LANGCHAIN_API_KEY = "sk_..."; // process.env.LANGCHAIN_TRACING_V2 = "true"; // process.env.LANGCHAIN_PROJECT = "Multi-agent Collaboration: LangGraphJS";
在 [2]
已复制!
import {
ChatPromptTemplate,
MessagesPlaceholder,
} from "@langchain/core/prompts";
import { StructuredTool } from "@langchain/core/tools";
import { convertToOpenAITool } from "@langchain/core/utils/function_calling";
import { Runnable } from "@langchain/core/runnables";
import { ChatOpenAI } from "@langchain/openai";
/**
* Create an agent that can run a set of tools.
*/
async function createAgent({
llm,
tools,
systemMessage,
}: {
llm: ChatOpenAI;
tools: StructuredTool[];
systemMessage: string;
}): Promise<Runnable> {
const toolNames = tools.map((tool) => tool.name).join(", ");
const formattedTools = tools.map((t) => convertToOpenAITool(t));
let prompt = ChatPromptTemplate.fromMessages([
[
"system",
"You are a helpful AI assistant, collaborating with other assistants." +
" Use the provided tools to progress towards answering the question." +
" If you are unable to fully answer, that's OK, another assistant with different tools " +
" will help where you left off. Execute what you can to make progress." +
" If you or any of the other assistants have the final answer or deliverable," +
" prefix your response with FINAL ANSWER so the team knows to stop." +
" You have access to the following tools: {tool_names}.\n{system_message}",
],
new MessagesPlaceholder("messages"),
]);
prompt = await prompt.partial({
system_message: systemMessage,
tool_names: toolNames,
});
return prompt.pipe(llm.bind({ tools: formattedTools }));
}
import { ChatPromptTemplate, MessagesPlaceholder, } from "@langchain/core/prompts"; import { StructuredTool } from "@langchain/core/tools"; import { convertToOpenAITool } from "@langchain/core/utils/function_calling"; import { Runnable } from "@langchain/core/runnables"; import { ChatOpenAI } from "@langchain/openai"; /** * 创建一个可以运行一组工具的智能体。 */ async function createAgent({ llm, tools, systemMessage, }: { llm: ChatOpenAI; tools: StructuredTool[]; systemMessage: string; }): Promise{ const toolNames = tools.map((tool) => tool.name).join(", "); const formattedTools = tools.map((t) => convertToOpenAITool(t)); let prompt = ChatPromptTemplate.fromMessages([ [ "system", "您是一位乐于助人的 AI 助手,与其他助手协作。" + " 使用提供的工具来推进回答问题。" + " 如果你无法完全回答,没关系,另一个拥有不同工具的助手 " + " 会在您结束的地方提供帮助。执行您能够执行的操作以取得进展。" + " 如果你或其他任何助手有最终答案或可交付成果," + " 在您的响应前缀加上 FINAL ANSWER,以便团队知道何时停止。" + " 您拥有以下工具:{tool_names}.\n{system_message}", ], new MessagesPlaceholder("messages"), ]); prompt = await prompt.partial({ system_message: systemMessage, tool_names: toolNames, }); return prompt.pipe(llm.bind({ tools: formattedTools })); }
定义状态¶
我们首先定义图的状态。这将只是一个消息列表,以及用于跟踪最新发送者的键
在 [3]
已复制!
import { BaseMessage } from "@langchain/core/messages";
import { Annotation } from "@langchain/langgraph";
// This defines the object that is passed between each node
// in the graph. We will create different nodes for each agent and tool
const AgentState = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: (x, y) => x.concat(y),
}),
sender: Annotation<string>({
reducer: (x, y) => y ?? x ?? "user",
default: () => "user",
}),
})
import { BaseMessage } from "@langchain/core/messages"; import { Annotation } from "@langchain/langgraph"; // 这定义了在每个节点之间传递的对象 // 在图中。我们将为每个智能体和工具创建不同的节点 const AgentState = Annotation.Root({ messages: Annotation({ reducer: (x, y) => x.concat(y), }), sender: Annotation({ reducer: (x, y) => y ?? x ?? "user", default: () => "user", }), })
在 [4]
已复制!
require("esm-hook"); // Only for running this in TSLab. See: https://github.com/yunabe/tslab/issues/72
import { TavilySearchResults } from "@langchain/community/tools/tavily_search";
import { tool } from "@langchain/core/tools";
import * as d3 from "d3";
// ----------ATTENTION----------
// If attempting to run this notebook locally, you must follow these instructions
// to install the necessary system dependencies for the `canvas` package.
// https://npmjs.net.cn/package/canvas#compiling
// -----------------------------
import { createCanvas } from "canvas";
import { z } from "zod";
import * as tslab from "tslab";
const chartTool = tool(
({ data }) => {
const width = 500;
const height = 500;
const margin = { top: 20, right: 30, bottom: 30, left: 40 };
const canvas = createCanvas(width, height);
const ctx = canvas.getContext("2d");
const x = d3
.scaleBand()
.domain(data.map((d) => d.label))
.range([margin.left, width - margin.right])
.padding(0.1);
const y = d3
.scaleLinear()
.domain([0, d3.max(data, (d) => d.value) ?? 0])
.nice()
.range([height - margin.bottom, margin.top]);
const colorPalette = [
"#e6194B",
"#3cb44b",
"#ffe119",
"#4363d8",
"#f58231",
"#911eb4",
"#42d4f4",
"#f032e6",
"#bfef45",
"#fabebe",
];
data.forEach((d, idx) => {
ctx.fillStyle = colorPalette[idx % colorPalette.length];
ctx.fillRect(
x(d.label) ?? 0,
y(d.value),
x.bandwidth(),
height - margin.bottom - y(d.value),
);
});
ctx.beginPath();
ctx.strokeStyle = "black";
ctx.moveTo(margin.left, height - margin.bottom);
ctx.lineTo(width - margin.right, height - margin.bottom);
ctx.stroke();
ctx.textAlign = "center";
ctx.textBaseline = "top";
x.domain().forEach((d) => {
const xCoord = (x(d) ?? 0) + x.bandwidth() / 2;
ctx.fillText(d, xCoord, height - margin.bottom + 6);
});
ctx.beginPath();
ctx.moveTo(margin.left, height - margin.top);
ctx.lineTo(margin.left, height - margin.bottom);
ctx.stroke();
ctx.textAlign = "right";
ctx.textBaseline = "middle";
const ticks = y.ticks();
ticks.forEach((d) => {
const yCoord = y(d); // height - margin.bottom - y(d);
ctx.moveTo(margin.left, yCoord);
ctx.lineTo(margin.left - 6, yCoord);
ctx.stroke();
ctx.fillText(d.toString(), margin.left - 8, yCoord);
});
tslab.display.png(canvas.toBuffer());
return "Chart has been generated and displayed to the user!";
},
{
name: "generate_bar_chart",
description:
"Generates a bar chart from an array of data points using D3.js and displays it for the user.",
schema: z.object({
data: z
.object({
label: z.string(),
value: z.number(),
})
.array(),
}),
}
)
const tavilyTool = new TavilySearchResults();
require("esm-hook"); // 仅适用于在 TSLab 中运行此代码。请参阅:https://github.com/yunabe/tslab/issues/72 import { TavilySearchResults } from "@langchain/community/tools/tavily_search"; import { tool } from "@langchain/core/tools"; import * as d3 from "d3"; // ----------注意---------- // 如果尝试在本地运行此笔记本,则必须按照以下说明 // 为 `canvas` 包安装必要的系统依赖项。 // https://npmjs.net.cn/package/canvas#compiling // ----------------------------- import { createCanvas } from "canvas"; import { z } from "zod"; import * as tslab from "tslab"; const chartTool = tool( ({ data }) => { const width = 500; const height = 500; const margin = { top: 20, right: 30, bottom: 30, left: 40 }; const canvas = createCanvas(width, height); const ctx = canvas.getContext("2d"); const x = d3 .scaleBand() .domain(data.map((d) => d.label)) .range([margin.left, width - margin.right]) .padding(0.1); const y = d3 .scaleLinear() .domain([0, d3.max(data, (d) => d.value) ?? 0]) .nice() .range([height - margin.bottom, margin.top]); const colorPalette = [ "#e6194B", "#3cb44b", "#ffe119", "#4363d8", "#f58231", "#911eb4", "#42d4f4", "#f032e6", "#bfef45", "#fabebe", ]; data.forEach((d, idx) => { ctx.fillStyle = colorPalette[idx % colorPalette.length]; ctx.fillRect( x(d.label) ?? 0, y(d.value), x.bandwidth(), height - margin.bottom - y(d.value), ); }); ctx.beginPath(); ctx.strokeStyle = "black"; ctx.moveTo(margin.left, height - margin.bottom); ctx.lineTo(width - margin.right, height - margin.bottom); ctx.stroke(); ctx.textAlign = "center"; ctx.textBaseline = "top"; x.domain().forEach((d) => { const xCoord = (x(d) ?? 0) + x.bandwidth() / 2; ctx.fillText(d, xCoord, height - margin.bottom + 6); }); ctx.beginPath(); ctx.moveTo(margin.left, height - margin.top); ctx.lineTo(margin.left, height - margin.bottom); ctx.stroke(); ctx.textAlign = "right"; ctx.textBaseline = "middle"; const ticks = y.ticks(); ticks.forEach((d) => { const yCoord = y(d); // height - margin.bottom - y(d); ctx.moveTo(margin.left, yCoord); ctx.lineTo(margin.left - 6, yCoord); ctx.stroke(); ctx.fillText(d.toString(), margin.left - 8, yCoord); }); tslab.display.png(canvas.toBuffer()); return "图表已生成并显示给用户!"; }, { name: "generate_bar_chart", description: "使用 D3.js 从数据点数组生成条形图并将其显示给用户。", schema: z.object({ data: z .object({ label: z.string(), value: z.number(), }) .array(), }), } ) const tavilyTool = new TavilySearchResults();
创建图¶
现在我们已经定义了工具并创建了一些辅助函数,我们将在下面创建各个智能体,并告诉它们如何使用 LangGraph 互相交谈。
定义智能体节点¶
在 LangGraph 中,节点代表执行工作的函数。在我们的示例中,我们将拥有“智能体”节点和“callTool”节点。
每个节点的输入都是图的状态。在我们的案例中,状态将拥有一个消息列表作为输入,以及上一个节点的名称。
首先,让我们定义智能体的节点。
在 [5]
已复制!
import { HumanMessage } from "@langchain/core/messages";
import type { RunnableConfig } from "@langchain/core/runnables";
// Helper function to run a node for a given agent
async function runAgentNode(props: {
state: typeof AgentState.State;
agent: Runnable;
name: string;
config?: RunnableConfig;
}) {
const { state, agent, name, config } = props;
let result = await agent.invoke(state, config);
// We convert the agent output into a format that is suitable
// to append to the global state
if (!result?.tool_calls || result.tool_calls.length === 0) {
// If the agent is NOT calling a tool, we want it to
// look like a human message.
result = new HumanMessage({ ...result, name: name });
}
return {
messages: [result],
// Since we have a strict workflow, we can
// track the sender so we know who to pass to next.
sender: name,
};
}
const llm = new ChatOpenAI({ modelName: "gpt-4o" });
// Research agent and node
const researchAgent = await createAgent({
llm,
tools: [tavilyTool],
systemMessage:
"You should provide accurate data for the chart generator to use.",
});
async function researchNode(
state: typeof AgentState.State,
config?: RunnableConfig,
) {
return runAgentNode({
state: state,
agent: researchAgent,
name: "Researcher",
config,
});
}
// Chart Generator
const chartAgent = await createAgent({
llm,
tools: [chartTool],
systemMessage: "Any charts you display will be visible by the user.",
});
async function chartNode(state: typeof AgentState.State) {
return runAgentNode({
state: state,
agent: chartAgent,
name: "ChartGenerator",
});
}
import { HumanMessage } from "@langchain/core/messages"; import type { RunnableConfig } from "@langchain/core/runnables"; // 运行给定智能体的节点的辅助函数 async function runAgentNode(props: { state: typeof AgentState.State; agent: Runnable; name: string; config?: RunnableConfig; }) { const { state, agent, name, config } = props; let result = await agent.invoke(state, config); // 我们将智能体输出转换为适合 // 附加到全局状态的格式 if (!result?.tool_calls || result.tool_calls.length === 0) { // 如果智能体未调用工具,我们希望它 // 看起来像人类消息。 result = new HumanMessage({ ...result, name: name }); } return { messages: [result], // 由于我们有严格的工作流程,我们可以 // 跟踪发送者,以便知道将谁传递到下一个。 sender: name, }; } const llm = new ChatOpenAI({ modelName: "gpt-4o" }); // 研究智能体和节点 const researchAgent = await createAgent({ llm, tools: [tavilyTool], systemMessage: "您应该为图表生成器提供准确的数据以供使用。", }); async function researchNode( state: typeof AgentState.State, config?: RunnableConfig, ) { return runAgentNode({ state: state, agent: researchAgent, name: "Researcher", config, }); } // 图表生成器 const chartAgent = await createAgent({ llm, tools: [chartTool], systemMessage: "您显示的任何图表都将对用户可见。", }); async function chartNode(state: typeof AgentState.State) { return runAgentNode({ state: state, agent: chartAgent, name: "ChartGenerator", }); }
在 [6]
已复制!
// Example invocation
const researchResults = await researchNode({
messages: [new HumanMessage("Research the US primaries in 2024")],
sender: "User",
});
researchResults;
// 示例调用 const researchResults = await researchNode({ messages: [new HumanMessage("研究 2024 年的美国初选")], sender: "User", }); researchResults;
{ messages: [ AIMessage { "id": "chatcmpl-9yopin5fBlXtA15wWiUlDyiKT9T9P", "content": "", "additional_kwargs": { "tool_calls": [ { "id": "call_SRihR0BFFtw3TlHQtiBDPR3v", "type": "function", "function": "[Object]" } ] }, "response_metadata": { "tokenUsage": { "completionTokens": 22, "promptTokens": 192, "totalTokens": 214 }, "finish_reason": "tool_calls", "system_fingerprint": "fp_3aa7262c27" }, "tool_calls": [ { "name": "tavily_search_results_json", "args": { "input": "US primaries 2024 updates" }, "type": "tool_call", "id": "call_SRihR0BFFtw3TlHQtiBDPR3v" } ], "invalid_tool_calls": [], "usage_metadata": { "input_tokens": 192, "output_tokens": 22, "total_tokens": 214 } } ], sender: 'Researcher' }
定义工具节点¶
我们现在定义一个节点来运行工具
在 [7]
已复制!
import { ToolNode } from "@langchain/langgraph/prebuilt";
const tools = [tavilyTool, chartTool];
// This runs tools in the graph
const toolNode = new ToolNode<typeof AgentState.State>(tools);
import { ToolNode } from "@langchain/langgraph/prebuilt"; const tools = [tavilyTool, chartTool]; // 这在图中运行工具 const toolNode = new ToolNode(tools);
在 [8]
已复制!
// Example invocation
await toolNode.invoke(researchResults);
// 示例调用 await toolNode.invoke(researchResults);
{ messages: [ ToolMessage { "content": "[{\"title\":\"Election Results 2024: Live Election Map | Races by State - POLITICO\",\"url\":\"https://www.politico.com/2024-election/results/\",\"content\":\"Live 2024 election results and maps by state. POLITICO's coverage of 2024 primary races for President, Senate, House and Governors.\",\"score\":0.9798227,\"raw_content\":null},{\"title\":\"Presidential Primary Election Results 2024 | Delegate Count Map by ...\",\"url\":\"https://www.politico.com/2024-election/results/president/\",\"content\":\"Live 2024 Presidential election results, maps and delegate counts by state. POLITICO's coverage of 2024 primary races for President, Senate, House and Governors.\",\"score\":0.97666925,\"raw_content\":null},{\"title\":\"Election 2024: Presidential campaign news, polls and results | CNN Politics\",\"url\":\"https://www.cnn.com/election/2024\",\"content\":\"2024 election guide: Presidential candidates, polls, primaries and caucuses, voter information and results for November 5, 2024\",\"score\":0.92455155,\"raw_content\":null},{\"title\":\"2024 Election news: Latest on the presidential race, polls & results\",\"url\":\"https://www.politico.com/news/2024-elections\",\"content\":\"POLITICO\\nPolitico Logo\\nWASHINGTON & POLITICS\\n2024 ELECTIONS\\nSTATE POLITICS & POLICY\\nGLOBAL POLITICS & POLICY\\nPOLICY NEWS\\nNEWSLETTERS\\nCOLUMNISTS\\nSERIES & MORE\\nPOLITICO Live\\nFollow us\\n2024 Elections\\nThe latest coverage of the 2024 presidential, House and Senate elections.\\n It's a question being debated in courtrooms across the country: Does the 14th Amendment of the U.S. Constitution bar Trump from running for president again because he supported or “engaged in insurrection or rebellion” for his role in the Jan. 6 attack on the Capitol?\\nRead More »\\nFormer House Speaker Kevin McCarthy has endorsed Trump in his 2024 run, but he hasn't always had the nicest things to say about the former president. By ERICA ORDEN\\n12/14/2023 03:15 PM EST\\nUpdated 12/14/2023 04:06 PM EST\\nNEW YORK — An appeals court on Thursday rejected former President Donald Trump’s effort to overturn the gag order barring him from making comments about the staff of the judge presiding over his $250 million civil fraud trial, dealing him another setback in his attempts to fight the restrictions.\\n | Charlie Neibergall/AP\\nPlaybook Deep Dive\\nHow Hunter Biden, Jack Smith, and Trump’s legal troubles are setting the stage for 2024\\nA week of new developments in impeachment, Donald Trump’s D.C. case, and Hunter Biden’s congressional inquiry showcased how the collision of law and politics will determine much of Republicans’ and Democrats’ political fortunes in 2024.\\n | Francis Chung/POLITICO\\nPolitics\\nCornel West thinks Biden won’t make it to the general election\\nThe independent candidate rejected the idea he could be a “spoiler” for Biden in an exclusive meeting with POLITICO.\\n\",\"score\":0.9069832,\"raw_content\":null},{\"title\":\"DNC 2024 live updates: Walz speaks tonight ahead of Harris' remarks ...\",\"url\":\"https://www.nbcnews.com/politics/2024-election/live-blog/election-2024-dnc-live-updates-rcna165228\",\"content\":\"Latest news and live updates on the Democratic National Convention and the 2024 presidential election campaigns as Harris and ... Navy, Coast Guard, Air Force Space Force, or the United States ...\",\"score\":0.8344069,\"raw_content\":null}]", "name": "tavily_search_results_json", "additional_kwargs": {}, "response_metadata": {}, "tool_call_id": "call_SRihR0BFFtw3TlHQtiBDPR3v" } ] }
定义边逻辑¶
我们可以定义一些边缘逻辑,这些逻辑是根据智能体的结果来决定下一步操作的
在 [9]
已复制!
import { AIMessage } from "@langchain/core/messages";
// Either agent can decide to end
function router(state: typeof AgentState.State) {
const messages = state.messages;
const lastMessage = messages[messages.length - 1] as AIMessage;
if (lastMessage?.tool_calls && lastMessage.tool_calls.length > 0) {
// The previous agent is invoking a tool
return "call_tool";
}
if (
typeof lastMessage.content === "string" &&
lastMessage.content.includes("FINAL ANSWER")
) {
// Any agent decided the work is done
return "end";
}
return "continue";
}
import { AIMessage } from "@langchain/core/messages"; // 任何智能体都可以决定结束 function router(state: typeof AgentState.State) { const messages = state.messages; const lastMessage = messages[messages.length - 1] as AIMessage; if (lastMessage?.tool_calls && lastMessage.tool_calls.length > 0) { // 上一个智能体正在调用工具 return "call_tool"; } if ( typeof lastMessage.content === "string" && lastMessage.content.includes("FINAL ANSWER") ) { // 任何智能体都决定工作已经完成 return "end"; } return "continue"; }
定义图¶
我们现在可以将所有内容整合在一起并定义图!
在 [10]
已复制!
import { END, START, StateGraph } from "@langchain/langgraph";
// 1. Create the graph
const workflow = new StateGraph(AgentState)
// 2. Add the nodes; these will do the work
.addNode("Researcher", researchNode)
.addNode("ChartGenerator", chartNode)
.addNode("call_tool", toolNode);
// 3. Define the edges. We will define both regular and conditional ones
// After a worker completes, report to supervisor
workflow.addConditionalEdges("Researcher", router, {
// We will transition to the other agent
continue: "ChartGenerator",
call_tool: "call_tool",
end: END,
});
workflow.addConditionalEdges("ChartGenerator", router, {
// We will transition to the other agent
continue: "Researcher",
call_tool: "call_tool",
end: END,
});
workflow.addConditionalEdges(
"call_tool",
// Each agent node updates the 'sender' field
// the tool calling node does not, meaning
// this edge will route back to the original agent
// who invoked the tool
(x) => x.sender,
{
Researcher: "Researcher",
ChartGenerator: "ChartGenerator",
},
);
workflow.addEdge(START, "Researcher");
const graph = workflow.compile();
import { END, START, StateGraph } from "@langchain/langgraph"; // 1. 创建图 const workflow = new StateGraph(AgentState) // 2. 添加节点;这些节点将完成工作 .addNode("Researcher", researchNode) .addNode("ChartGenerator", chartNode) .addNode("call_tool", toolNode); // 3. 定义边。我们将定义常规边和条件边 // 工作人员完成工作后,向监督者报告 workflow.addConditionalEdges("Researcher", router, { // 我们将过渡到另一个智能体 continue: "ChartGenerator", call_tool: "call_tool", end: END, }); workflow.addConditionalEdges("ChartGenerator", router, { // 我们将过渡到另一个智能体 continue: "Researcher", call_tool: "call_tool", end: END, }); workflow.addConditionalEdges( "call_tool", // 每个智能体节点都更新了 'sender' 字段 // 工具调用节点没有,这意味着 // 此边将路由回最初调用工具的智能体 // (x) => x.sender, { Researcher: "Researcher", ChartGenerator: "ChartGenerator", }, ); workflow.addEdge(START, "Researcher"); const graph = workflow.compile();
调用¶
创建图后,就可以调用它了!让我们让它为我们绘制一些统计数据。
在 [15]
已复制!
const streamResults = await graph.stream(
{
messages: [
new HumanMessage({
content: "Generate a bar chart of the US gdp over the past 3 years.",
}),
],
},
{ recursionLimit: 150 },
);
const prettifyOutput = (output: Record<string, any>) => {
const keys = Object.keys(output);
const firstItem = output[keys[0]];
if ("messages" in firstItem && Array.isArray(firstItem.messages)) {
const lastMessage = firstItem.messages[firstItem.messages.length - 1];
console.dir({
type: lastMessage._getType(),
content: lastMessage.content,
tool_calls: lastMessage.tool_calls,
}, { depth: null });
}
if ("sender" in firstItem) {
console.log({
sender: firstItem.sender,
})
}
}
for await (const output of await streamResults) {
if (!output?.__end__) {
prettifyOutput(output);
console.log("----");
}
}
const streamResults = await graph.stream( { messages: [ new HumanMessage({ content: "生成过去 3 年美国 GDP 的条形图。", }), ], }, { recursionLimit: 150 }, ); const prettifyOutput = (output: Record) => { const keys = Object.keys(output); const firstItem = output[keys[0]]; if ("messages" in firstItem && Array.isArray(firstItem.messages)) { const lastMessage = firstItem.messages[firstItem.messages.length - 1]; console.dir({ type: lastMessage._getType(), content: lastMessage.content, tool_calls: lastMessage.tool_calls, }, { depth: null }); } if ("sender" in firstItem) { console.log({ sender: firstItem.sender, }) } } for await (const output of await streamResults) { if (!output?.__end__) { prettifyOutput(output); console.log("----"); } }
{ type: 'ai', content: '', tool_calls: [ { name: 'tavily_search_results_json', args: { input: 'US GDP over the past 3 years' }, type: 'tool_call', id: 'call_ZrmEsfu4B8SKpDhUJY5vcps8' } ] } { sender: 'Researcher' } ---- { type: 'tool', content: `[{"title":"United States GDP Annual Growth Rate - TRADING ECONOMICS","url":"https://tradingeconomics.com/united-states/gdp-growth-annual","content":"The Gross Domestic Product (GDP) in the United States expanded 3.10 percent in the second quarter of 2024 over the same quarter of the previous year. This page provides the latest reported value for - United States GDP Annual Growth Rate - plus previous releases, historical high and low, short-term forecast and long-term prediction, economic calendar, survey consensus and news.","score":0.88798404,"raw_content":null},{"title":"U.S. GDP 1960-2024 | MacroTrends","url":"https://www.macrotrends.net/global-metrics/countries/USA/united-states/gdp-gross-domestic-product","content":"U.S. gdp for 2021 was $23,315.08B, a 10.71% increase from 2020. U.S. gdp for 2020 was $21,060.47B, a 1.5% decline from 2019. U.S. gdp for 2019 was $21,380.98B, a 4.13% increase from 2018. GDP at purchaser's prices is the sum of gross value added by all resident producers in the economy plus any product taxes and minus any subsidies not included ...","score":0.7666432,"raw_content":null},{"title":"U.S. GDP Growth Rate 1960-2024 | MacroTrends","url":"https://www.macrotrends.net/global-metrics/countries/USA/united-states/gdp-growth-rate","content":"U.S. gdp growth rate for 2021 was 5.95%, a 8.71% increase from 2020. U.S. gdp growth rate for 2020 was -2.77%, a 5.06% decline from 2019. U.S. gdp growth rate for 2019 was 2.29%, a 0.65% decline from 2018. Annual percentage growth rate of GDP at market prices based on constant local currency. Aggregates are based on constant 2010 U.S. dollars.","score":0.6904547,"raw_content":null},{"title":"U.S. GDP by Year, Compared to Recessions and Events - The Balance","url":"https://www.thebalancemoney.com/us-gdp-by-year-3305543","content":"U.S. GDP by Year, Compared to Recessions and Events\\nThe Strange Ups and Downs of the U.S. Economy Since 1929\\nThe Balance / Julie Bang\\nU.S. gross domestic product (GDP) by year is a good overview of economic growth in the United States. Rebasing changes the reference year (or base year) for the real (chained dollar and quantity index) estimates and price indexes and expresses GDP and other NIPA aggregates in terms of the prices of one year. You can compare the GDP by year to fiscal and monetary policies to get a complete picture of what works and what doesn't in the U.S. economy.\\n Real GDP is important because without canceling out the effects of inflation, the GDP could appear to grow, when really all that's happened is an increase in prices.\\n Key Takeaways\\nTypes of GDP\\nThe Bureau of Economic Analysis compiles the data.","score":0.5998954,"raw_content":null},{"title":"US GDP over time - USAFacts","url":"https://usafacts.org/data/topics/economy/economic-indicators/gdp/gross-domestic-product/","content":"Data Adjustments\\nIs the economy growing?\\nRelated Metrics\\nAnnual percent change in real GDP\\n5.7%\\n2021\\nAnnual percent change in real GDP\\n5.7%\\n2021\\nExplore Gross domestic product\\nInteract with the data\\nData Adjustments\\nState Display\\nOur nation, in numbers\\nUSAFacts is a not-for-profit, nonpartisan civic initiative making government data easy for all Americans to access and understand.\\n • Check your spelling\\n• Try other search terms\\n• Use fewer words\\nGross domestic product\\nGross domestic product\\nGross domestic product (GDP) is the value of all goods and services produced in the US. All topics\\nExplore articles, data and trends by topic\\nAbout\\nWhat makes USAFacts different\\nWe frequently add data and we're interested in what would be useful to people. Newsletter\\nData delivered to your inbox\\nKeep up with the latest data and most popular content. But only the official BEA inflation-adjusted \\"real GDP\\" value is used to calculate annual percent change in GDP and therefore how well the economy is doing.","score":0.42083758,"raw_content":null}]`, tool_calls: undefined } ---- { type: 'human', content: 'Here are the U.S. GDP values over the past 3 years:\n' + '\n' + '1. **2021**: $23,315.08 billion\n' + '2. **2022:** No specific value obtained from the search, but we can infer trends from growth rates\n' + '3. **2023:** No specific value obtained from the search, but we can infer trends from growth rates\n' + '\n' + 'Given this, further insight can be derived from additional data sources, economic reports, or databases to fill in the missing GDP values for 2022 and 2023.', tool_calls: undefined } { sender: 'Researcher' } ---- { type: 'ai', content: '', tool_calls: [ { name: 'generate_bar_chart', args: { data: [ { label: '2021', value: 23315.08 }, { label: '2022', value: 25514.3 }, { label: '2023', value: 27857.73 } ] }, type: 'tool_call', id: 'call_CKH68vipCp9DshSVmw2vIZpK' } ] } { sender: 'ChartGenerator' } ----
{ type: 'tool', content: 'Chart has been generated and displayed to the user!', tool_calls: undefined } ---- { type: 'human', content: 'FINAL ANSWER: The bar chart displaying the U.S. GDP over the past 3 years has been generated and displayed. The values used are as follows:\n' + '\n' + '- **2021**: $23,315.08 billion\n' + '- **2022**: $25,514.3 billion (approximate)\n' + '- **2023**: $27,857.73 billion (approximate)\n' + '\n' + 'Please refer to the chart for a visual representation of the data.\n', tool_calls: undefined } { sender: 'ChartGenerator' } ----
点击此处 查看上述运行的 LangSmith 跟踪。