langchain(node.js) 实际应用订单助手

涉及技术点

  1. createAgent(智能体的创建)
  2. tool(工具的调用)
  3. MemorySaver(上下文记忆)
  4. InMemoryStore (将特定信息存到内存)
  5. readline (控制台交互)
  6. zod (输出格式化)
ts 复制代码
import { createAgent, summarizationMiddleware, tool, trimMessages } from "langchain"
import { MemorySaver, InMemoryStore } from "@langchain/langgraph"
import readline from 'readline';

import { getModel } from "../model/index.js";
import z from "zod";
const mainModel = getModel("gpt-4o");
const summaryModel = getModel();


// 下一个订单ID
let nextOrderId = 1;

// 记忆功能
const checkpointer = new MemorySaver();
// 创建内存存储
const store = new InMemoryStore();

const userId = "user123";
const createOrder = tool(
    async ({ customerName, items, totalPrice }, config) => {
        const orderId = `ORD-${String(nextOrderId).padStart(3, "0")}`;
        nextOrderId++;

        const orderData = {
            id: orderId,
            customer: customerName,
            status: "pending",
            total: totalPrice,
            items,
            createdAt: new Date().toISOString(),
        };

        await config.store.put(
            ['orders', userId],
            orderId,
            orderData,
        )
        // 这里必须写return 
        return `订单已创建,客户 ${customerName} 订单商品为 ${items.join(",")},当前商品状态为 ${orderData.status},订单总价为 ${totalPrice} 元。订单编号:${orderId}`
    }, {
    name: 'create_order',
    defaultConfig: '创建订单',
    schema: z.object({
        customerName: z.string().describe("Customer name"),
        items: z.array(z.string()).describe("List of items in the order"),
        totalPrice: z.number().describe("Total order price"),
    })
}
)

const getOrderList = tool(
    async ({ }, config) => {
        try {
            const allOrders = await config.store.search(
                ["orders", userId]
            );

            if (!allOrders || allOrders.length === 0) {
                return "还没有订单";
            }

            const orderSummary = allOrders.map(item => {
                const order = item.value;
                return `客户${order.customer} - ${order.items.join("、")} - ${order.total}元`;
            }).join("\n");

            return `订单列表(共${allOrders.length}个):\n${orderSummary}`;
        } catch (error) {
            return `查询订单列表出错:${error.message}`;
        }
    }, {
    name: 'get_order_list',
    description: "查询所有订单",
    schema: z.object({})
}
)

const getOrderByName = tool(
    async ({ customerName }, config) => {
        try {
            const allOrders = await config.store.search(
                ["orders", userId]
            );
            
            const orderItem = allOrders.find(item =>
                item.value.customer === customerName
            );

            if (orderItem) {
                const order = orderItem.value;
                return `客户 ${customerName} 的订单详情:\n订单ID: ${order.id}\n商品: ${order.items.join("、")}\n总价: ${order.total}元\n状态: ${order.status}`;
            } else {
                return `客户 ${customerName} 没有找到订单`;
            }
        } catch (error) {
            // 错误时也要返回字符串,不要返回 undefined
            return `查询订单出错:${error.message}`;
        }
    }, {
    name: 'get_order_by_name',
    description: "根据客户姓名查询订单详情",
    schema: z.object({
        customerName: z.string().describe("Customer name"),
    })
}
)

const updateOrderStatus = tool(
    async ({ orderId, newStatus }, config) => {
        try {
            const result = await config.store.get(
                ["orders", userId],
                orderId
            );

            if (!result || !result.value) {  // ✅ 检查 result.value
                return `订单 ${orderId} 不存在`;
            }

            const order = result.value;  // ✅ 从 .value 获取数据
            order.status = newStatus;

            // 更新订单
            await config.store.put(
                ["orders", userId],
                orderId,
                order
            );

            return `订单 ${orderId} 状态已更新为 ${newStatus}`;
        } catch (error) {
            return `更新订单状态出错:${error.message}`;
        }
    }, {
    name: 'update_order_status',
    description: "更新订单状态",
    schema: z.object({
        orderId: z.string().describe("Order ID"),
        newStatus: z.string().describe("New order status"),
    })
}
)

const agent = createAgent({
    model: mainModel,
    tools: [createOrder, getOrderList, getOrderByName, updateOrderStatus],
    systemPrompt: `
    你是一个专业的订单助手,负责处理客户订单事务。
        你能够:
        1. 创建新订单
        2. 查询订单详情
        3. 根据订单中的用户名称查询订单
        4. 列出特定客户的所有订单
        5. 更新订单状态(其中pending为待处理,shipped为已发货,delivered为已交付),在返回状态的时候应该返回对应的中文状态
        请根据客户的需求调用相应的工具,并提供清晰准确的回复,在返回的时候不要忘记包含订单ID。
    `,
    // 如果不希望处理其他消息在提示中添加,请不要回答其他问题,只回答与订单相关的问题
    // 那么ai不会处理除订单之外任何问题
    checkpointer,
    store,
    middleware: [
        // 先对消息进行总结
        summarizationMiddleware({
            model: summaryModel,
            trigger: { tokens: 1000 },
            keep: { messages: 25 },
        })
    ],
    preModelHook: async (state) => {
        return {
            // 对消息进行裁剪,保留最后2000个token
            messages: await trimMessages(state.messages, {
                strategy: "last",
                maxTokens: 2000,
                startOn: "human",
                endOn: ["human", "tool"],
                tokenCounter: (msgs) => msgs.length,
            }),
        };
    },
})

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

function askQuestion(query) {
    return new Promise((resolve) => {
        rl.question(query, resolve);
    });
}

async function runChat() {
    console.log("订单助手已启动。输入 'exit' 退出。\n");

    while (true) {
        const userInput = await askQuestion("客户: ");

        if (userInput.toLowerCase() === "exit") {
            console.log("再见!");
            rl.close();
            break;
        }

        const result = await agent.invoke(
            { messages: [{ role: "user", content: userInput }] },
            { configurable: { thread_id: userId } }
        );

        const lastMessage = result.messages[result.messages.length - 1];
        console.log(`助手: ${lastMessage.content}\n`);
    }
}

runChat();
相关推荐
xhxxx30 分钟前
别再让 AI 自由发挥了!用 LangChain + Zod 强制它输出合法 JSON
前端·langchain·llm
余栀丶1 小时前
Microsoft Edge 禁止更新
microsoft·edge
shizhenshide2 小时前
隐形杀手:无感验证(Invisible CAPTCHA)的工作原理与存在性检测方法
microsoft·验证码·ezcaptcha
Gogo8163 小时前
Node.js 生产环境避坑指南:从 PM2“麦当劳理论”到日志全链路治理
node.js·日志·pm2
San30.3 小时前
从零到一:开启 LangChain 的 AI 工程化之旅
人工智能·langchain·node.js
kimi-2226 小时前
create_tool_calling_agent、create_react_agent区别
langchain
风止何安啊6 小时前
Steam玩累了?那用 Node.js 写个小游戏:手把手玩懂 JS 运行环境
前端·javascript·node.js
fighting不想说话6 小时前
NodeJs:前端工程化推手
node.js
XXYBMOOO7 小时前
全面解析 Qt `QMessageBox` 类及其常用方法
开发语言·qt·microsoft
大模型教程7 小时前
大模型LLM入门篇:小白入门一文快速了解大模型(附教程)
langchain·llm·agent