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(),
}),
}
);
// 将 prompt 定义为常量,便于引用
const WARRANTY_COLLECTOR_PROMPT = `你是帮助客户处理设备问题的客服 agent。
当前阶段:保修验证
在此步骤中,你需要:
1. 热情地问候客户
2. 询问客户的设备是否在保修期内
3. 使用 record_warranty_status 记录客户回答并进入下一步
保持自然、友好的对话风格。不要一次询问多个问题。`;
const ISSUE_CLASSIFIER_PROMPT = `你是帮助客户处理设备问题的客服 agent。
当前阶段:问题分类
客户信息:保修状态为 {warranty_status}
在此步骤中,你需要:
1. 要求客户描述问题
2. 判断这是硬件问题(物理损坏、部件损坏)还是软件问题(app 崩溃、性能问题)
3. 使用 record_issue_type 记录分类并进入下一步
如果不清楚,请先提澄清问题,再进行分类。`;
const RESOLUTION_SPECIALIST_PROMPT = `你是帮助客户处理设备问题的客服 agent。
当前阶段:解决方案
客户信息:保修状态为 {warranty_status},问题类型为 {issue_type}
在此步骤中,你需要:
1. 对于软件问题:使用 provide_solution 提供故障排查步骤
2. 对于硬件问题:
- 如果在保修期内:使用 provide_solution 说明保修维修流程
- 如果超出保修期:使用 escalate_to_human 处理付费维修选项
解决方案要具体且有帮助。`;
// 步骤配置:将步骤名称映射到(prompt、tools、required_state)
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}`);
}
}
// 使用状态值格式化 prompt(支持 {warrantyStatus}、{issueType} 等)
let systemPrompt: string = stepConfig.prompt;
for (const [key, value] of Object.entries(request.state)) {
systemPrompt = systemPrompt.replace(`{${key}}`, String(value ?? ""));
}
// 注入 system prompt 和步骤专属工具
return handler({
...request,
systemPrompt,
tools: [...stepConfig.tools],
});
},
});
// 收集所有步骤配置中的全部工具
const allTools = [
recordWarrantyStatus,
recordIssueType,
provideSolution,
escalateToHuman,
];
const model = new ChatOpenAI({
model: "gpt-5.4-mini",
});
// 使用基于步骤的配置创建 agent
const agent = createAgent({
model,
tools: allTools,
middleware: [applyStepMiddleware],
checkpointer: new MemorySaver(),
});
// 此对话 thread 的配置
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);
}