Agent 2.0:基于 LCEL 的自主代理
Plan-and-Execute, ReAct 模式作为可组合链
在 LangChain 的演进中,Agent 一直是核心功能之一。LangChain V3 通过 LCEL (LangChain Expression Language) 重新设计了 Agent 架构,创建了更灵活、更可组合的 Agent 2.0。本章将深入探讨基于 LCEL 的自主代理实现,包括 Plan-and-Execute 和 ReAct 模式。
Agent 2.0 的核心概念
Agent 2.0 通过 LCEL 实现了高度的可组合性和灵活性:
typescript
// Agent 2.0 基础接口
interface BaseAgent extends Runnable<string, string> {
// Agent 可以执行的工具
tools: Tool[];
// Agent 的执行逻辑
execute(input: string): Promise<string>;
// 流式执行
streamExecute?(input: string): AsyncGenerator<string>;
}
// 工具接口
interface Tool {
name: string;
description: string;
execute(input: string): Promise<string>;
}
// Agent 状态
interface AgentState {
input: string;
thoughts: string[];
actions: Array<{
tool: string;
input: string;
output: string;
}>;
finalAnswer?: string;
}ReAct 模式实现
ReAct (Reasoning + Acting) 是 Agent 2.0 的核心模式之一:
typescript
// ReAct Agent 实现
class ReActAgent extends Runnable<string, string> {
private llm: BaseLanguageModel;
private tools: Tool[];
private maxIterations: number;
constructor(
llm: BaseLanguageModel,
tools: Tool[],
maxIterations: number = 10
) {
super();
this.llm = llm;
this.tools = tools;
this.maxIterations = maxIterations;
}
async invoke(input: string): Promise<string> {
let state: AgentState = {
input,
thoughts: [],
actions: []
};
for (let i = 0; i < this.maxIterations; i++) {
// 思考步骤
const thought = await this.think(state);
state.thoughts.push(thought);
// 决策步骤
const action = await this.decideAction(thought, state);
// 如果是最终答案,返回结果
if (action.tool === 'Final Answer') {
return action.output;
}
// 执行工具
const toolOutput = await this.executeTool(action.tool, action.input);
state.actions.push({
tool: action.tool,
input: action.input,
output: toolOutput
});
// 检查是否应该终止
if (await this.shouldTerminate(state)) {
return await this.finalize(state);
}
}
return '达到最大迭代次数,未能找到解决方案';
}
private async think(state: AgentState): Promise<string> {
const prompt = new PromptTemplate({
template: `你是一个智能代理,需要解决用户的问题。请思考下一步应该做什么。
问题: {input}
已完成的步骤: {previousSteps}
请提供你的思考过程:`,
inputVariables: ["input", "previousSteps"]
});
const previousSteps = this.formatPreviousSteps(state);
const result = await prompt
.pipe(this.llm)
.pipe(new StringOutputParser())
.invoke({
input: state.input,
previousSteps
});
return result;
}
private async decideAction(
thought: string,
state: AgentState
): Promise<{ tool: string; input: string }> {
const toolsDescription = this.tools
.map(tool => `${tool.name}: ${tool.description}`)
.join('\n');
const prompt = new PromptTemplate({
template: `基于以下思考,决定下一步使用哪个工具:
思考: {thought}
可用工具:
{tools}
请只回答工具名称和输入,格式如下:
工具: <工具名称>
输入: <工具输入>
如果问题已解决,请回答:
工具: Final Answer
输入: <最终答案>`,
inputVariables: ["thought", "tools"]
});
const result = await prompt
.pipe(this.llm)
.pipe(new StringOutputParser())
.invoke({
thought,
tools: toolsDescription
});
// 解析结果
const toolMatch = result.match(/工具:\s*(.*)/);
const inputMatch = result.match(/输入:\s*(.*)/);
return {
tool: toolMatch ? toolMatch[1].trim() : 'Final Answer',
input: inputMatch ? inputMatch[1].trim() : result
};
}
private async executeTool(toolName: string, toolInput: string): Promise<string> {
const tool = this.tools.find(t => t.name === toolName);
if (!tool) {
return `未知工具: ${toolName}`;
}
try {
return await tool.execute(toolInput);
} catch (error) {
return `工具执行失败: ${error.message}`;
}
}
private formatPreviousSteps(state: AgentState): string {
let result = '';
for (let i = 0; i < state.thoughts.length; i++) {
result += `思考 ${i + 1}: ${state.thoughts[i]}\n`;
if (i < state.actions.length) {
const action = state.actions[i];
result += `行动 ${i + 1}: 使用工具 "${action.tool}" 处理 "${action.input}"\n`;
result += `结果 ${i + 1}: ${action.output}\n\n`;
}
}
return result;
}
private async shouldTerminate(state: AgentState): Promise<boolean> {
// 简单的终止条件:达到最大迭代次数或有最终答案
return state.actions.length >= this.maxIterations ||
state.actions.some(action => action.tool === 'Final Answer');
}
private async finalize(state: AgentState): Promise<string> {
// 基于所有信息生成最终答案
const prompt = new PromptTemplate({
template: `基于以下所有信息,提供最终答案:
原始问题: {input}
所有步骤:
{steps}
最终答案:`,
inputVariables: ["input", "steps"]
});
const steps = this.formatPreviousSteps(state);
const result = await prompt
.pipe(this.llm)
.pipe(new StringOutputParser())
.invoke({
input: state.input,
steps
});
return result;
}
}Plan-and-Execute 模式实现
Plan-and-Execute 模式将任务分解为规划和执行两个阶段:
typescript
// Plan-and-Execute Agent
class PlanAndExecuteAgent extends Runnable<string, string> {
private planner: Runnable<string, string[]>;
private executor: ReActAgent;
private llm: BaseLanguageModel;
constructor(
llm: BaseLanguageModel,
tools: Tool[],
maxPlanSteps: number = 5
) {
super();
this.llm = llm;
// 创建规划器
this.planner = this.createPlanner(maxPlanSteps);
// 创建执行器(基于 ReAct Agent)
this.executor = new ReActAgent(llm, tools, 5);
}
private createPlanner(maxSteps: number): Runnable<string, string[]> {
const prompt = new PromptTemplate({
template: `你是一个任务规划专家。请将复杂任务分解为 {maxSteps} 个或更少的子任务。
任务: {input}
请按以下格式列出子任务:
1. 第一个子任务
2. 第二个子任务
3. 第三个子任务
...
子任务列表:`,
inputVariables: ["input", "maxSteps"]
});
return prompt
.pipe(this.llm)
.pipe(new StringOutputParser())
.pipe(new RunnableLambda((output: string) => {
// 解析子任务列表
return output
.split('\n')
.filter(line => /^\d+\./.test(line.trim()))
.map(line => line.replace(/^\d+\.\s*/, '').trim());
}));
}
async invoke(input: string): Promise<string> {
// 第一阶段:规划
console.log('开始任务规划...');
const plan = await this.planner.invoke({
input,
maxSteps: 5
});
console.log('规划完成,子任务:');
plan.forEach((task, index) => {
console.log(` ${index + 1}. ${task}`);
});
// 第二阶段:执行
console.log('\n开始执行子任务...');
const results: string[] = [];
for (let i = 0; i < plan.length; i++) {
console.log(`\n执行子任务 ${i + 1}: ${plan[i]}`);
try {
const result = await this.executor.invoke(plan[i]);
results.push(result);
console.log(`子任务 ${i + 1} 完成: ${result.substring(0, 100)}...`);
} catch (error) {
const errorMsg = `子任务 ${i + 1} 执行失败: ${error.message}`;
results.push(errorMsg);
console.error(errorMsg);
}
}
// 第三阶段:整合结果
console.log('\n整合所有结果...');
const finalPrompt = new PromptTemplate({
template: `基于以下子任务和结果,提供完整的答案:
原始任务: {input}
子任务和结果:
{results}
完整答案:`,
inputVariables: ["input", "results"]
});
const resultsText = results
.map((result, index) => `${index + 1}. ${plan[index]}: ${result}`)
.join('\n\n');
const finalAnswer = await finalPrompt
.pipe(this.llm)
.pipe(new StringOutputParser())
.invoke({
input,
results: resultsText
});
return finalAnswer;
}
}可组合的 Agent 组件
通过 LCEL 实现可组合的 Agent 组件:
typescript
// 工具执行链
class ToolExecutionChain extends Runnable<{ tool: string; input: string }, string> {
private tools: Record<string, Tool>;
constructor(tools: Tool[]) {
super();
this.tools = {};
tools.forEach(tool => {
this.tools[tool.name] = tool;
});
}
async invoke(input: { tool: string; input: string }): Promise<string> {
const tool = this.tools[input.tool];
if (!tool) {
return `未知工具: ${input.tool}`;
}
try {
return await tool.execute(input.input);
} catch (error) {
return `工具执行失败: ${error.message}`;
}
}
}
// 思考链
class ThinkingChain extends Runnable<AgentState, string> {
private llm: BaseLanguageModel;
constructor(llm: BaseLanguageModel) {
super();
this.llm = llm;
}
async invoke(input: AgentState): Promise<string> {
const prompt = new PromptTemplate({
template: `作为智能代理,分析当前情况并决定下一步行动:
问题: {input}
已完成步骤: {previousSteps}
思考:`,
inputVariables: ["input", "previousSteps"]
});
const previousSteps = input.actions
.map((action, index) =>
`${index + 1}. 使用 ${action.tool} 处理 "${action.input}",结果: ${action.output}`
)
.join('\n');
return await prompt
.pipe(this.llm)
.pipe(new StringOutputParser())
.invoke({
input: input.input,
previousSteps: previousSteps || '无'
});
}
}
// 决策链
class DecisionChain extends Runnable<{ thought: string; tools: Tool[] }, { tool: string; input: string }> {
private llm: BaseLanguageModel;
constructor(llm: BaseLanguageModel) {
super();
this.llm = llm;
}
async invoke(input: { thought: string; tools: Tool[] }): Promise<{ tool: string; input: string }> {
const toolsDescription = input.tools
.map(tool => `${tool.name}: ${tool.description}`)
.join('\n');
const prompt = new PromptTemplate({
template: `基于思考结果,选择合适的工具:
思考: {thought}
可用工具:
{tools}
选择:
工具: <工具名>
输入: <工具输入>`,
inputVariables: ["thought", "tools"]
});
const result = await prompt
.pipe(this.llm)
.pipe(new StringOutputParser())
.invoke({
thought: input.thought,
tools: toolsDescription
});
const toolMatch = result.match(/工具:\s*(.*)/);
const inputMatch = result.match(/输入:\s*(.*)/);
return {
tool: toolMatch ? toolMatch[1].trim() : '',
input: inputMatch ? inputMatch[1].trim() : ''
};
}
}
// 组合式 Agent
class ComposableAgent extends Runnable<string, string> {
private thinkingChain: ThinkingChain;
private decisionChain: DecisionChain;
private toolExecutionChain: ToolExecutionChain;
private llm: BaseLanguageModel;
private tools: Tool[];
private maxIterations: number;
constructor(
llm: BaseLanguageModel,
tools: Tool[],
maxIterations: number = 10
) {
super();
this.llm = llm;
this.tools = tools;
this.maxIterations = maxIterations;
this.thinkingChain = new ThinkingChain(llm);
this.decisionChain = new DecisionChain(llm);
this.toolExecutionChain = new ToolExecutionChain(tools);
}
async invoke(input: string): Promise<string> {
let state: AgentState = {
input,
thoughts: [],
actions: []
};
for (let i = 0; i < this.maxIterations; i++) {
// 思考
const thought = await this.thinkingChain.invoke(state);
state.thoughts.push(thought);
// 决策
const decision = await this.decisionChain.invoke({
thought,
tools: this.tools
});
// 检查是否完成
if (decision.tool === 'Final Answer' || !decision.tool) {
return decision.input;
}
// 执行工具
const toolOutput = await this.toolExecutionChain.invoke({
tool: decision.tool,
input: decision.input
});
state.actions.push({
tool: decision.tool,
input: decision.input,
output: toolOutput
});
}
// 最终总结
const summaryPrompt = new PromptTemplate({
template: `基于所有步骤,提供最终答案:
问题: {input}
步骤: {steps}
答案:`,
inputVariables: ["input", "steps"]
});
const steps = state.actions
.map((action, index) =>
`${index + 1}. ${action.tool}: ${action.output}`
)
.join('\n');
return await summaryPrompt
.pipe(this.llm)
.pipe(new StringOutputParser())
.invoke({
input,
steps
});
}
}实际应用示例
让我们看一个完整的实际应用示例,展示如何使用 Agent 2.0 解决复杂问题:
typescript
// 实用工具实现
class CalculatorTool implements Tool {
name = 'Calculator';
description = '用于执行数学计算,支持基本运算 (+, -, *, /, ^, sqrt, sin, cos, etc.)';
async execute(input: string): Promise<string> {
try {
// 安全的数学表达式求值
const result = Function(`"use strict"; return (${input})`)();
return `计算结果: ${result}`;
} catch (error) {
return `计算错误: ${error.message}`;
}
}
}
class SearchTool implements Tool {
name = 'Search';
description = '用于搜索网络信息';
async execute(input: string): Promise<string> {
// 模拟搜索功能
// 实际应用中会调用真实的搜索引擎 API
return `搜索结果: 关于"${input}"的相关信息...`;
}
}
class DateTimeTool implements Tool {
name = 'DateTime';
description = '获取当前日期和时间';
async execute(input: string): Promise<string> {
return `当前时间: ${new Date().toLocaleString()}`;
}
}
class WeatherTool implements Tool {
name = 'Weather';
description = '获取天气信息';
async execute(input: string): Promise<string> {
// 模拟天气查询
return `天气信息: ${input} 当前天气晴朗,温度 25°C`;
}
}
// Agent 2.0 应用示例
class Agent20Demo {
private reactAgent: ReActAgent;
private planExecuteAgent: PlanAndExecuteAgent;
private composableAgent: ComposableAgent;
constructor() {
const tools: Tool[] = [
new CalculatorTool(),
new SearchTool(),
new DateTimeTool(),
new WeatherTool()
];
const llm = new ChatOpenAI({ modelName: 'gpt-3.5-turbo' });
this.reactAgent = new ReActAgent(llm, tools, 5);
this.planExecuteAgent = new PlanAndExecuteAgent(llm, tools, 5);
this.composableAgent = new ComposableAgent(llm, tools, 5);
}
async demonstrateReAct(): Promise<void> {
console.log('=== ReAct Agent 演示 ===\n');
const question = '计算 2023 年距离 2050 年还有多少天?';
console.log(`问题: ${question}\n`);
const result = await this.reactAgent.invoke(question);
console.log(`答案: ${result}\n`);
}
async demonstratePlanAndExecute(): Promise<void> {
console.log('=== Plan-and-Execute Agent 演示 ===\n');
const task = '帮我制定一个周末旅行计划,包括目的地推荐、预算估算和行程安排';
console.log(`任务: ${task}\n`);
const result = await this.planExecuteAgent.invoke(task);
console.log(`计划: ${result}\n`);
}
async demonstrateComposable(): Promise<void> {
console.log('=== 可组合 Agent 演示 ===\n');
const question = '今天是星期几?计算 100 天后是星期几?';
console.log(`问题: ${question}\n`);
const result = await this.composableAgent.invoke(question);
console.log(`答案: ${result}\n`);
}
async runAllDemos(): Promise<void> {
await this.demonstrateReAct();
await this.demonstratePlanAndExecute();
await this.demonstrateComposable();
}
}
// 高级 Agent:自适应 Agent
class AdaptiveAgent extends Runnable<string, string> {
private agents: Array<{ agent: Runnable<string, string>; weight: number }>;
private llm: BaseLanguageModel;
constructor(llm: BaseLanguageModel, tools: Tool[]) {
super();
this.llm = llm;
this.agents = [
{
agent: new ReActAgent(llm, tools, 5),
weight: 0.4
},
{
agent: new PlanAndExecuteAgent(llm, tools, 3),
weight: 0.3
},
{
agent: new ComposableAgent(llm, tools, 5),
weight: 0.3
}
];
}
async invoke(input: string): Promise<string> {
// 根据问题类型选择最合适的 Agent
const agentType = await this.selectAgent(input);
const selectedAgent = this.agents.find(a =>
a.agent.constructor.name.includes(agentType)
) || this.agents[0];
console.log(`选择 Agent 类型: ${agentType}`);
return await selectedAgent.agent.invoke(input);
}
private async selectAgent(input: string): Promise<string> {
const prompt = new PromptTemplate({
template: `根据问题类型,选择最合适的 Agent:
问题: {input}
Agent 类型选项:
1. ReAct - 适合需要推理和工具调用的复杂问题
2. PlanAndExecute - 适合需要分解和规划的大型任务
3. Composable - 适合一般性问题
只回答 Agent 类型名称:`,
inputVariables: ["input"]
});
const result = await prompt
.pipe(this.llm)
.pipe(new StringOutputParser())
.invoke({ input });
return result.trim();
}
}
// 使用自适应 Agent
async function demonstrateAdaptiveAgent() {
console.log('=== 自适应 Agent 演示 ===\n');
const tools: Tool[] = [
new CalculatorTool(),
new SearchTool(),
new DateTimeTool(),
new WeatherTool()
];
const adaptiveAgent = new AdaptiveAgent(
new ChatOpenAI({ modelName: 'gpt-3.5-turbo' }),
tools
);
// 测试不同类型的问题
const testCases = [
'计算 (2 + 3) * 4 - 5 的值',
'帮我规划一次为期一周的欧洲旅行',
'现在几点了?'
];
for (const testCase of testCases) {
console.log(`问题: ${testCase}`);
const result = await adaptiveAgent.invoke(testCase);
console.log(`答案: ${result}\n`);
}
}
// 运行演示
async function runAgentDemos() {
console.log('🚀 LangChain Agent 2.0 演示\n');
// 基础演示
const demo = new Agent20Demo();
await demo.runAllDemos();
// 自适应 Agent 演示
await demonstrateAdaptiveAgent();
}
// 如果直接运行此文件,则执行演示
if (require.main === module) {
runAgentDemos().catch(console.error);
}总结
Agent 2.0 通过 LCEL 实现了更灵活、更可组合的自主代理架构:
- ReAct 模式 - 结合推理和行动的经典模式,通过思考-行动循环解决问题
- Plan-and-Execute 模式 - 将复杂任务分解为规划和执行两个阶段
- 可组合组件 - 通过 LCEL 将 Agent 功能拆分为独立的可重用组件
- 自适应选择 - 根据问题类型动态选择最合适的 Agent 策略
- 工具集成 - 灵活的工具调用机制,扩展 Agent 能力
这些特性使得 Agent 2.0 能够处理更复杂的任务,提供更智能的解决方案,并且具有更好的可维护性和扩展性。
在下一章中,我们将探讨 Tool Calling:如何让 LLM 调用函数并解析参数,了解如何实现更精确的工具调用机制。