- 在继续操作前收集保修信息。
- 将问题分类为硬件或软件问题。
- 提供解决方案或升级到人工支持。
- 在多轮对话中维护会话状态。
设置
安装
本教程需要langchain 包:
npm install langchain
yarn add langchain
pnpm add langchain
LangSmith
设置 LangSmith 以检查智能体内部发生的情况。然后设置以下环境变量:export LANGSMITH_TRACING="true"
export LANGSMITH_API_KEY="..."
process.env.LANGSMITH_TRACING = "true";
process.env.LANGSMITH_API_KEY = "...";
选择一个LLM
从LangChain的集成套件中选择一个聊天模型:- OpenAI
- Anthropic
- Azure
- Google Gemini
- Bedrock Converse
👉 阅读 OpenAI 聊天模型集成文档
npm install @langchain/openai
pnpm install @langchain/openai
yarn add @langchain/openai
bun add @langchain/openai
import { initChatModel } from "langchain";
process.env.OPENAI_API_KEY = "你的 API 密钥";
const model = await initChatModel("gpt-5.5");
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI({
model: "gpt-5.5",
apiKey: "你的 API 密钥"
});
👉 阅读 Anthropic 聊天模型集成文档
npm install @langchain/anthropic
pnpm install @langchain/anthropic
yarn add @langchain/anthropic
pnpm add @langchain/anthropic
import { initChatModel } from "langchain";
process.env.ANTHROPIC_API_KEY = "你的 API 密钥";
const model = await initChatModel("claude-sonnet-4-6");
import { ChatAnthropic } from "@langchain/anthropic";
const model = new ChatAnthropic({
model: "claude-sonnet-4-6",
apiKey: "你的 API 密钥"
});
👉 阅读 Azure 聊天模型集成文档
npm install @langchain/azure
pnpm install @langchain/azure
yarn add @langchain/azure
bun add @langchain/azure
import { initChatModel } from "langchain";
process.env.AZURE_OPENAI_API_KEY = "你的 API 密钥";
process.env.AZURE_OPENAI_ENDPOINT = "你的端点";
process.env.OPENAI_API_VERSION = "你的 API 版本";
const model = await initChatModel("azure_openai:gpt-5.5");
import { AzureChatOpenAI } from "@langchain/openai";
const model = new AzureChatOpenAI({
model: "gpt-5.5",
azureOpenAIApiKey: "你的 API 密钥",
azureOpenAIApiEndpoint: "你的端点",
azureOpenAIApiVersion: "你的 API 版本"
});
👉 阅读 Google GenAI 聊天模型集成文档
npm install @langchain/google-genai
pnpm install @langchain/google-genai
yarn add @langchain/google-genai
bun add @langchain/google-genai
import { initChatModel } from "langchain";
process.env.GOOGLE_API_KEY = "你的 API 密钥";
const model = await initChatModel("google-genai:gemini-2.5-flash-lite");
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
const model = new ChatGoogleGenerativeAI({
model: "gemini-2.5-flash-lite",
apiKey: "你的 API 密钥"
});
👉 阅读 AWS Bedrock 聊天模型集成文档
npm install @langchain/aws
pnpm install @langchain/aws
yarn add @langchain/aws
bun add @langchain/aws
import { initChatModel } from "langchain";
// 按照此处的步骤配置您的凭据:
// https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html
const model = await initChatModel("bedrock:gpt-5.5");
import { ChatBedrockConverse } from "@langchain/aws";
// 按照此处的步骤配置您的凭据:
// https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html
const model = new ChatBedrockConverse({
model: "gpt-5.5",
region: "us-east-2"
});
1. 定义自定义状态
首先,定义一个自定义状态模式来跟踪当前活动的步骤:import { StateSchema } from "@langchain/langgraph";
import { z } from "zod";
// 定义可能的流程步骤
const SupportStepSchema = z.enum(["warranty_collector", "issue_classifier", "resolution_specialist"]);
const WarrantyStatusSchema = z.enum(["in_warranty", "out_of_warranty"]);
const IssueTypeSchema = z.enum(["hardware", "software"]);
// 客户支持工作流的状态
const SupportState = new StateSchema({
currentStep: SupportStepSchema.optional(),
warrantyStatus: WarrantyStatusSchema.optional(),
issueType: IssueTypeSchema.optional(),
});
current_step 字段是状态机模式的核心——它决定了每一轮加载哪个配置(提示词 + 工具)。
2. 创建管理流程状态的工具
创建更新工作流状态的工具。这些工具允许智能体记录信息并过渡到下一步。 关键是使用Command 来更新状态,包括 current_step 字段:
import { z } from "zod";
import { tool, ToolMessage, type ToolRuntime } from "langchain";
import { Command } from "@langchain/langgraph";
const recordWarrantyStatus = tool(
async (input, config: ToolRuntime<typeof SupportState.State>) => {
return new Command({
update: {
messages: [
new ToolMessage({
content: `保修状态已记录为: ${input.status}`,
tool_call_id: config.toolCallId,
}),
],
warrantyStatus: input.status,
currentStep: "issue_classifier",
},
});
},
{
name: "record_warranty_status",
description:
"记录客户的保修状态并过渡到问题分类。",
schema: z.object({
status: WarrantyStatusSchema,
}),
}
);
const recordIssueType = tool(
async (input, config: ToolRuntime<typeof SupportState.State>) => {
return new Command({
update: {
messages: [
new ToolMessage({
content: `问题类型已记录为: ${input.issueType}`,
tool_call_id: config.toolCallId,
}),
],
issueType: input.issueType,
currentStep: "resolution_specialist",
},
});
},
{
name: "record_issue_type",
description:
"记录问题类型并过渡到解决方案专家。",
schema: z.object({
issueType: IssueTypeSchema,
}),
}
);
const escalateToHuman = tool(
async (input) => {
// 在实际系统中,这将创建工单、通知员工等。
return `正在升级到人工支持。原因: ${input.reason}`;
},
{
name: "escalate_to_human",
description: "将案例升级到人工支持专家。",
schema: z.object({
reason: z.string(),
}),
}
);
const provideSolution = tool(
async (input) => {
return `已提供解决方案: ${input.solution}`;
},
{
name: "provide_solution",
description: "为客户提供问题的解决方案。",
schema: z.object({
solution: z.string(),
}),
}
);
record_warranty_status 和 record_issue_type 如何返回更新数据(warranty_status、issue_type)和 current_step 的 Command 对象。这就是状态机的工作原理——工具控制工作流的进展。
3. 定义步骤配置
为每个步骤定义提示词和工具。首先,为每个步骤定义提示词:查看完整的提示词定义
查看完整的提示词定义
// 将提示词定义为常量以便引用
const WARRANTY_COLLECTOR_PROMPT = `你是一位帮助解决设备问题的客户支持代理。
当前阶段: 保修验证
在此步骤中,你需要:
1. 热情地问候客户
2. 询问他们的设备是否在保修期内
3. 使用 record_warranty_status 记录他们的回答并移动到下一步
保持对话性和友好性。不要一次问多个问题。`;
const ISSUE_CLASSIFIER_PROMPT = `你是一位帮助解决设备问题的客户支持代理。
当前阶段: 问题分类
客户信息: 保修状态为 {warranty_status}
在此步骤中,你需要:
1. 要求客户描述他们的问题
2. 确定是硬件问题(物理损坏、损坏的部件)还是软件问题(应用崩溃、性能问题)
3. 使用 record_issue_type 记录分类并移动到下一步
如果不清楚,在分类前先询问澄清问题。`;
const RESOLUTION_SPECIALIST_PROMPT = `你是一位帮助解决设备问题的客户支持代理。
当前阶段: 解决方案
客户信息: 保修状态为 {warranty_status},问题类型为 {issue_type}
在此步骤中,你需要:
1. 对于软件问题:使用 provide_solution 提供故障排除步骤
2. 对于硬件问题:
- 如果在保修期内:使用 provide_solution 解释保修维修流程
- 如果超出保修期:escalate_to_human 处理付费维修选项
提供具体且有帮助的解决方案。`;
// 步骤配置:将步骤名称映射到(提示词、工具、必需状态)
const STEP_CONFIG = {
warranty_collector: {
prompt: WARRANTY_COLLECTOR_PROMPT,
tools: [recordWarrantyStatus],
requires: [],
},
issue_classifier: {
prompt: ISSUE_CLASSIFIER_PROMPT,
tools: [recordIssueType],
requires: ["warrantyStatus"],
},
resolution_specialist: {
prompt: RESOLUTION_SPECIALIST_PROMPT,
tools: [provideSolution, escalateToHuman],
requires: ["warrantyStatus", "issueType"],
},
} as const;
- 可以一目了然地查看所有步骤
- 添加新步骤(只需添加另一个条目)
- 理解工作流依赖关系(
requires字段) - 使用带有状态变量的提示模板(例如,
{warranty_status})
4. 创建基于步骤的中间件
创建读取状态中current_step 并应用相应配置的中间件。我们将使用 @wrap_model_call 装饰器来实现一个简洁的实现:
import { createMiddleware } from "langchain";
const applyStepMiddleware = createMiddleware({
name: "applyStep",
stateSchema: SupportState,
wrapModelCall: async (request, handler) => {
// 获取当前步骤(首次交互默认为 warranty_collector)
const currentStep = request.state.currentStep ?? "warranty_collector";
// 查找步骤配置
const stepConfig = STEP_CONFIG[currentStep];
// 验证必需的状态是否存在
for (const key of stepConfig.requires) {
if (request.state[key] === undefined) {
throw new Error(`在达到 ${currentStep} 之前必须设置 ${key}`);
}
}
// 用状态值格式化提示词(支持 {warrantyStatus}、{issueType} 等)
let systemPrompt: string = stepConfig.prompt;
for (const [key, value] of Object.entries(request.state)) {
systemPrompt = systemPrompt.replace(`{${key}}`, String(value ?? ""));
}
// 注入系统提示词和特定于步骤的工具
return handler({
...request,
systemPrompt,
tools: [...stepConfig.tools],
});
},
});
- 读取当前步骤:从状态中获取
current_step(默认为 “warranty_collector”)。 - 查找配置:在
STEP_CONFIG中查找匹配的条目。 - 验证依赖关系:确保必需的状态字段存在。
- 格式化提示词:将状态值注入提示词模板。
- 应用配置:覆盖系统提示词和可用工具。
request.override() 方法是关键——它允许我们根据状态动态更改智能体的行为,而无需创建单独的智能体实例。
5. 创建智能体
现在使用基于步骤的中间件和用于状态持久化的检查点创建智能体:import { createAgent } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
// 从所有步骤配置中收集所有工具
const allTools = [
recordWarrantyStatus,
recordIssueType,
provideSolution,
escalateToHuman,
];
// 初始化模型
const model = new ChatOpenAI({
model: "gpt-5.4-mini",
temperature: 0.7,
});
// 创建具有基于步骤配置的智能体
const agent = createAgent({
model,
tools: allTools,
stateSchema: SupportState,
middleware: [applyStepMiddleware],
checkpointer: new MemorySaver(),
});
为什么需要检查点? 检查点在对话轮次之间维护状态。没有它,
current_step 状态会在用户消息之间丢失,从而破坏工作流。6. 测试工作流
测试完整的工作流:import { HumanMessage } from "@langchain/core/messages";
import { v7 as uuid7 } from "uuid";
// 此对话线程的配置
const threadId = uuid7();
const config = { configurable: { thread_id: threadId } };
// 轮次 1: 初始消息 - 从 warranty_collector 步骤开始
console.log("=== 轮次 1: 保修信息收集 ===");
let result = await agent.invoke(
{ messages: [new HumanMessage("你好,我的手机屏幕碎了")] },
config
);
for (const msg of result.messages) {
console.log(msg.content);
}
// 轮次 2: 用户回应保修信息
console.log("\n=== 轮次 2: 保修回应 ===");
result = await agent.invoke(
{ messages: [new HumanMessage("是的,还在保修期内")] },
config
);
for (const msg of result.messages) {
console.log(msg.content);
}
console.log(`当前步骤: ${result.currentStep}`);
// 轮次 3: 用户描述问题
console.log("\n=== 轮次 3: 问题描述 ===");
result = await agent.invoke(
{ messages: [new HumanMessage("屏幕因为摔落而物理性碎裂")] },
config
);
for (const msg of result.messages) {
console.log(msg.content);
}
console.log(`当前步骤: ${result.currentStep}`);
// 轮次 4: 解决方案
console.log("\n=== 轮次 4: 解决方案 ===");
result = await agent.invoke(
{ messages: [new HumanMessage("我该怎么办?")] },
config
);
for (const msg of result.messages) {
console.log(msg.content);
}
- 保修验证步骤:询问保修状态
- 问题分类步骤:询问问题,确定为硬件问题
- 解决方案步骤:提供保修维修说明
7. 理解状态转换
让我们跟踪每轮发生的情况:轮次 1: 初始消息
{
messages: [new HumanMessage("你好,我的手机屏幕碎了")],
currentStep: "warranty_collector" // 默认值
}
- 系统提示词:
WARRANTY_COLLECTOR_PROMPT - 工具:
[record_warranty_status]
轮次 2: 记录保修状态后
工具调用:recordWarrantyStatus("in_warranty") 返回:
new Command({
update: {
warrantyStatus: "in_warranty",
currentStep: "issue_classifier" // 状态转换!
}
})
- 系统提示词:
ISSUE_CLASSIFIER_PROMPT(使用warranty_status="in_warranty"格式化) - 工具:
[record_issue_type]
轮次 3: 分类问题后
工具调用:recordIssueType("hardware") 返回:
new Command({
update: {
issueType: "hardware",
currentStep: "resolution_specialist" // 状态转换!
}
})
- 系统提示词:
RESOLUTION_SPECIALIST_PROMPT(使用warranty_status和issue_type格式化) - 工具:
[provide_solution, escalate_to_human]
current_step 来驱动工作流,而中间件在下一轮通过应用相应的配置来响应。
8. 管理消息历史
随着智能体推进步骤,消息历史会增长。使用摘要中间件在保留对话上下文的同时压缩较早的消息:import { createAgent, SummarizationMiddleware } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
const agent = createAgent({
model,
tools: allTools,
stateSchema: SupportState,
middleware: [
applyStepMiddleware,
new SummarizationMiddleware({
model: "gpt-5.4-mini",
trigger: { tokens: 4000 },
keep: { messages: 10 },
}),
],
checkpointer: new MemorySaver(),
});
9. 增加灵活性:返回
某些工作流需要允许用户返回到先前的步骤以更正信息(例如,更改保修状态或问题分类)。然而,并非所有转换都有意义——例如,一旦退款处理完成,通常就无法返回。对于此支持工作流,我们将添加工具以返回到保修验证和问题分类步骤。如果你的工作流需要在大多数步骤之间进行任意转换,请考虑是否真的需要结构化工作流。当步骤遵循清晰的顺序进展,并且偶尔需要向后转换以进行更正时,此模式效果最佳。
import { tool } from "langchain";
import { Command } from "@langchain/langgraph";
import { z } from "zod";
const goBackToWarranty = tool(
async () => {
return new Command({ update: { currentStep: "warranty_collector" } });
},
{
name: "go_back_to_warranty",
description: "返回保修验证步骤。",
schema: z.object({}),
}
);
const goBackToClassification = tool(
async () => {
return new Command({ update: { currentStep: "issue_classifier" } });
},
{
name: "go_back_to_classification",
description: "返回问题分类步骤。",
schema: z.object({}),
}
);
// 更新 resolution_specialist 配置以包含这些工具
STEP_CONFIG.resolution_specialist.tools.push(
goBackToWarranty,
goBackToClassification
);
const RESOLUTION_SPECIALIST_PROMPT = `你是一位帮助解决设备问题的客户支持代理。
当前阶段: 解决方案
客户信息: 保修状态为 {warrantyStatus},问题类型为 {issueType}
在此步骤中,你需要:
1. 对于软件问题:使用 provide_solution 提供故障排除步骤
2. 对于硬件问题:
- 如果在保修期内:使用 provide_solution 解释保修维修流程
- 如果超出保修期:escalate_to_human 处理付费维修选项
如果客户表示任何信息有误,请使用:
- go_back_to_warranty 更正保修状态
- go_back_to_classification 更正问题类型
提供具体且有帮助的解决方案。`;
const result = await agent.invoke(
{ messages: [new HumanMessage("实际上,我搞错了——我的设备超出保修期了")] },
config
);
// 智能体将调用 go_back_to_warranty 并重新开始保修验证步骤
完整示例
以下是所有内容整合到一个可运行脚本中的示例:Show 完整代码
Show 完整代码
import { createMiddleware, createAgent } from "langchain";
import { z } from "zod";
import { tool, ToolMessage, type ToolRuntime, HumanMessage } from "langchain";
import { Command, MemorySaver, StateSchema } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
// 定义可能的流程步骤
const SupportStepSchema = z.enum([
"warranty_collector",
"issue_classifier",
"resolution_specialist",
]);
const WarrantyStatusSchema = z.enum(["in_warranty", "out_of_warranty"]);
const IssueTypeSchema = z.enum(["hardware", "software"]);
// 客户支持工作流的状态
const SupportState = new StateSchema({
currentStep: SupportStepSchema.optional(),
warrantyStatus: WarrantyStatusSchema.optional(),
issueType: IssueTypeSchema.optional(),
});
const recordWarrantyStatus = tool(
async (input, config: ToolRuntime<typeof SupportState.State>) => {
return new Command({
update: {
messages: [
new ToolMessage({
content: `保修状态已记录为: ${input.status}`,
tool_call_id: config.toolCallId,
}),
],
warrantyStatus: input.status,
currentStep: "issue_classifier",
},
});
},
{
name: "record_warranty_status",
description:
"记录客户的保修状态并过渡到问题分类。",
schema: z.object({
status: WarrantyStatusSchema,
}),
}
);
const recordIssueType = tool(
async (input, config: ToolRuntime<typeof SupportState.State>) => {
return new Command({
update: {
messages: [
new ToolMessage({
content: `问题类型已记录为: ${input.issueType}`,
tool_call_id: config.toolCallId,
}),
],
issueType: input.issueType,
currentStep: "resolution_specialist",
},
});
},
{
name: "record_issue_type",
description:
"记录问题类型并过渡到解决方案专家。",
schema: z.object({
issueType: IssueTypeSchema,
}),
}
);
const escalateToHuman = tool(
async (input) => {
// 在实际系统中,这将创建工单、通知员工等。
return `正在升级到人工支持。原因: ${input.reason}`;
},
{
name: "escalate_to_human",
description: "将案例升级到人工支持专家。",
schema: z.object({
reason: z.string(),
}),
}
);
const provideSolution = tool(
async (input) => {
return `已提供解决方案: ${input.solution}`;
},
{
name: "provide_solution",
description: "为客户提供问题的解决方案。",
schema: z.object({
solution: z.string(),
}),
}
);
// 将提示词定义为常量以便引用
const WARRANTY_COLLECTOR_PROMPT = `你是一位帮助解决设备问题的客户支持代理。
当前阶段: 保修验证
在此步骤中,你需要:
1. 热情地问候客户
2. 询问他们的设备是否在保修期内
3. 使用 record_warranty_status 记录他们的回答并移动到下一步
保持对话性和友好性。不要一次问多个问题。`;
const ISSUE_CLASSIFIER_PROMPT = `你是一位帮助解决设备问题的客户支持代理。
当前阶段: 问题分类
客户信息: 保修状态为 {warranty_status}
在此步骤中,你需要:
1. 要求客户描述他们的问题
2. 确定是硬件问题(物理损坏、损坏的部件)还是软件问题(应用崩溃、性能问题)
3. 使用 record_issue_type 记录分类并移动到下一步
如果不清楚,在分类前先询问澄清问题。`;
const RESOLUTION_SPECIALIST_PROMPT = `你是一位帮助解决设备问题的客户支持代理。
当前阶段: 解决方案
客户信息: 保修状态为 {warranty_status},问题类型为 {issue_type}
在此步骤中,你需要:
1. 对于软件问题:使用 provide_solution 提供故障排除步骤
2. 对于硬件问题:
- 如果在保修期内:使用 provide_solution 解释保修维修流程
- 如果超出保修期:escalate_to_human 处理付费维修选项
提供具体且有帮助的解决方案。`;
// 步骤配置:将步骤名称映射到(提示词、工具、必需状态)
const STEP_CONFIG = {
warranty_collector: {
prompt: WARRANTY_COLLECTOR_PROMPT,
tools: [recordWarrantyStatus],
requires: [],
},
issue_classifier: {
prompt: ISSUE_CLASSIFIER_PROMPT,
tools: [recordIssueType],
requires: ["warrantyStatus"],
},
resolution_specialist: {
prompt: RESOLUTION_SPECIALIST_PROMPT,
tools: [provideSolution, escalateToHuman],
requires: ["warrantyStatus", "issueType"],
},
} as const;
const applyStepMiddleware = createMiddleware({
name: "applyStep",
stateSchema: SupportState,
wrapModelCall: async (request, handler) => {
// 获取当前步骤(首次交互默认为 warranty_collector)
const currentStep = request.state.currentStep ?? "warranty_collector";
// 查找步骤配置
const stepConfig = STEP_CONFIG[currentStep];
// 验证必需的状态是否存在
for (const key of stepConfig.requires) {
if (request.state[key] === undefined) {
throw new Error(`在达到 ${currentStep} 之前必须设置 ${key}`);
}
}
// 用状态值格式化提示词(支持 {warrantyStatus}、{issueType} 等)
let systemPrompt: string = stepConfig.prompt;
for (const [key, value] of Object.entries(request.state)) {
systemPrompt = systemPrompt.replace(`{${key}}`, String(value ?? ""));
}
// 注入系统提示词和特定于步骤的工具
return handler({
...request,
systemPrompt,
tools: [...stepConfig.tools],
});
},
});
// 从所有步骤配置中收集所有工具
const allTools = [
recordWarrantyStatus,
recordIssueType,
provideSolution,
escalateToHuman,
];
const model = new ChatOpenAI({
model: "gpt-5.4-mini",
});
// 创建具有基于步骤配置的智能体
const agent = createAgent({
model,
tools: allTools,
middleware: [applyStepMiddleware],
checkpointer: new MemorySaver(),
});
// 此对话线程的配置
const threadId = crypto.randomUUID();
const config = { configurable: { thread_id: threadId } };
// 轮次 1: 初始消息 - 从 warranty_collector 步骤开始
console.log("=== 轮次 1: 保修信息收集 ===");
let result = await agent.invoke(
{ messages: [new HumanMessage("你好,我的手机屏幕碎了")] },
config
);
for (const msg of result.messages) {
console.log(msg.content);
}
// 轮次 2: 用户回应保修信息
console.log("\n=== 轮次 2: 保修回应 ===");
result = await agent.invoke(
{ messages: [new HumanMessage("是的,还在保修期内")] },
config
);
for (const msg of result.messages) {
console.log(msg.content);
}
console.log(`当前步骤: ${result.currentStep}`);
// 轮次 3: 用户描述问题
console.log("\n=== 轮次 3: 问题描述 ===");
result = await agent.invoke(
{
messages: [
new HumanMessage("屏幕因为摔落而物理性碎裂"),
],
},
config
);
for (const msg of result.messages) {
console.log(msg.content);
}
console.log(`当前步骤: ${result.currentStep}`);
// 轮次 4: 解决方案
console.log("\n=== 轮次 4: 解决方案 ===");
result = await agent.invoke(
{ messages: [new HumanMessage("我该怎么办?")] },
config
);
for (const msg of result.messages) {
console.log(msg.content);
}
后续步骤
Connect these docs to Claude, VSCode, and more via MCP for real-time answers.

