[LangGraph] 循环
1. 快速上手
实现循环的图
// 快速上手
import { StateGraph, START, END } from "@langchain/langgraph";
import { z } from "zod/v4";
// 定义状态的Schema
const Schema = z.object({
count: z.number(), // 计数器
});
// 根据Schema生成对应的ts类型
type TState = z.infer<typeof Schema>;
// 辅助函数
function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// 构建图 - 返回编译后的图实例
function buildGraph(maxCount: number) {
// 节点
async function increment(state: TState) {
const next = state.count + 1;
await sleep(1500);
return {
count: next,
};
}
// 条件函数
function routeFunc(state: TState) {
// 判断是否还没有到达最大值
return state.count < maxCount ? "true" : "false";
}
return new StateGraph(Schema)
.addNode("increment", increment)
.addEdge(START, "increment")
.addConditionalEdges("increment", routeFunc, {
true: "increment",
false: END,
})
.compile();
}
// 主方法
async function main() {
// 提取用户在终端输入的参数
// --max 最大值
// --start 起始值
// node index.ts --max 10 --start 5
const argv = globalThis.process.argv.slice(2);
const args: Record<string, string> = {};
for (let i = 0; i < argv.length; i += 2) {
args[argv[i].replace(/^--/, "")] = argv[i + 1];
}
const max = Number(args.max ?? 5); // 提取出来 max 值,如果没有 max,取默认值 5
const start = Number(args.start ?? 0); // 提取出来 start 值,如果没有 start,取默认值 0
const graph = buildGraph(max);
console.log(`开始执行:起始值count为${start},最大值为${max}`);
const stream = await graph.stream({
count: start,
});
for await (const update of stream) {
console.log(`每一次的更新:`);
console.log(update);
console.log("\n");
}
console.log("循环结束");
}
main();
2. 订单状态轮询
场景需求
构建一个流程用来轮询订单状态,直到:
- 订单处理成功(正常结束)
- 尝试次数超过最大值(超时结束)
- 中途发生网络异常、第三方服务异常等偶发错误,但流程仍能继续
流程图
flowchart TD
START([START])
END([END])
START --> fetchStatus
subgraph 查询订单状态
fetchStatus[fetchStatus<br/>查询订单状态]
end
fetchStatus --> shouldContinue
subgraph 继续判断
shouldContinue{shouldContinue<br/>是否继续轮询?}
end
shouldContinue -->|处理成功| END
shouldContinue -->|超出最大次数| END
shouldContinue -->|继续重试| waitNext
subgraph 等待后继续轮询
waitNext[waitNext<br/>等待 2 秒后重试]
end
waitNext --> fetchStatus
// 订单状态轮询
import { StateGraph, START, END } from "@langchain/langgraph";
import { z } from "zod/v4";
// 订单状态枚举
export const OrderStatus = {
PENDING: 0,
PROCESSING: 1,
SUCCESS: 2,
FAIL: 3,
NETWORK_ERR: 4,
INTERNAL_ERR: 5,
} as const;
// 订单状态类型
export type OrderStatus = (typeof OrderStatus)[keyof typeof OrderStatus];
// 订单状态描述映射
const StatusMap: Record<OrderStatus, string> = {
[OrderStatus.PENDING]: "待处理",
[OrderStatus.PROCESSING]: "处理中",
[OrderStatus.SUCCESS]: "处理成功",
[OrderStatus.FAIL]: "处理失败",
[OrderStatus.NETWORK_ERR]: "网络错误",
[OrderStatus.INTERNAL_ERR]: "内部错误",
};
// 图的Schema
const Schema = z.object({
orderId: z.string().describe("订单的id"),
status: z.enum(OrderStatus).describe("订单状态"),
attempt: z.number().describe("轮询的次数"),
});
// 根据Schema生成 ts 类型
type TState = z.infer<typeof Schema>;
// 工具 sleep
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
// 模拟外部查询
async function mockFetchOrder(
orderId: string,
attempt: number,
): Promise<OrderStatus> {
// 日志:这是第几次轮询
console.log(`当前是第${attempt}次查询`);
await sleep(2000); // 模拟异步请求等待一段时间
// 更新状态
if (Math.random() < 0.1) return OrderStatus.NETWORK_ERR; // 一定的几率,网络出错
if (Math.random() < 0.1) return OrderStatus.FAIL; // 一定的几率,其它错误
if (attempt < 3) return OrderStatus.PENDING;
if (attempt === 3) return OrderStatus.PROCESSING;
if (attempt === 4) return OrderStatus.SUCCESS;
return OrderStatus.INTERNAL_ERR;
}
function buildGraph(maxRetries: number) {
async function fetchStatus(state: TState) {
const status = await mockFetchOrder(state.orderId, state.attempt);
console.log(`订单${state.orderId}的状态是:${StatusMap[status]}`);
const isFinal =
status === OrderStatus.SUCCESS ||
status === OrderStatus.FAIL ||
status === OrderStatus.NETWORK_ERR ||
status === OrderStatus.INTERNAL_ERR;
return { status, attempt: isFinal ? state.attempt : state.attempt + 1 };
}
async function waitNext() {
console.log(`等待2秒后继续轮询`);
await sleep(2000);
return {};
}
function routeFunc(state: TState) {
if (
state.status === OrderStatus.SUCCESS ||
state.status === OrderStatus.FAIL ||
state.status === OrderStatus.NETWORK_ERR ||
state.status === OrderStatus.INTERNAL_ERR
) {
return "done";
}
if (state.attempt >= maxRetries) {
return "timeout";
}
return "retry";
}
return new StateGraph(Schema)
.addNode("fetchStatus", fetchStatus)
.addNode("waitNext", waitNext)
.addEdge(START, "fetchStatus")
.addConditionalEdges("fetchStatus", routeFunc, {
done: END,
timeout: END,
retry: "waitNext",
})
.addEdge("waitNext", "fetchStatus")
.compile();
}
async function main() {
const orderId = "1234567890";
const maxRetries = 5;
const graph = buildGraph(maxRetries);
const result = await graph.stream({
orderId,
status: OrderStatus.PENDING,
attempt: 1,
});
for await (const chunk of result) {
console.log(chunk);
}
}
main();

浙公网安备 33010602011771号