ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

用 Cloudflare Agents 与 GPT-4o 构建智能井字棋:Stateful Agent、可调用方法与结构化输出的完整实战

用 Cloudflare Agents 与 GPT-4o 构建智能井字棋:Stateful Agent、可调用方法与结构化输出的完整实战 用 Cloudflare Agents 与 GPT-4o 构建智能井字棋Stateful Agent、可调用方法与结构化输出的完整实战【免费下载链接】agentsBuild and deploy AI Agents on Cloudflare项目地址: https://gitcode.com/GitHub_Trending/agents1/agents本指南以仓库中的 井字棋示例 为核心剖析一个AI Agent 与人类对战的最小完整实现服务端用agents包定义带状态的TicTacToeAgent通过callable()暴露方法落子决策交由 GPT-4o 借助generateObject Zod schema 输出结构化坐标前端用 React 的useAgent钩子实时同步棋盘并触发调用。读完本文你将掌握在 Cloudflare Workers 上编写「有状态、可远程调用、可被 AI 驱动」的 Agent 的完整套路并可直接复用到猜拳、五子棋、回合制策略等任何带状态交互场景。项目定位不止是一个游戏examples/tictactoe是agents仓库中的一个演示型示例完整入口见 README它的价值不在于井字棋本身而在于浓缩了「人类 ↔ 浏览器 ↔ Cloudflare 上的 Stateful Agent ↔ LLM」这条完整链路状态由 Agent 持有棋盘、当前玩家、胜者都存放在服务端 Agent 的状态中而不是前端内存AI 作为玩家轮到 AI 时由 GPT-4o 根据当前棋局生成下一步落子调用即 RPC前端通过agent.call(makeMove, ...)远程调用服务端方法方法名、参数、返回值都受 TypeScript 类型约束。快速开始3 步把 Agent 跑起来按 README 的指引本地运行只需两步准备 一条命令将.env.example复制为.env填入你的 OpenAI API keyai-sdk/openai默认读取OPENAI_API_KEY环境变量安装依赖并启动npm i npm start打开 http://localhost:5174 即可与 Agent 对战。npm start实际执行的是vite dev见 package.json 的scripts字段由 vite.config.ts 中的agents()、react()、tailwindcss()、cloudflare()四个 Vite 插件协同完成本地开发环境。其中agents()插件负责在本地模拟 Workers 运行环境并提供 Agent 的 WebSocket 端点因此你不需要先wrangler dev一条命令就能同时启动前端与 Agent 后端。示例还提供了部署脚本npm run deploy内部为vite build wrangler deploy以及用于生成类型声明的npm run typeswrangler types env.d.ts --include-runtime false。服务端设计把游戏规则写进 Agent 状态机核心实现位于 src/server.ts。它定义了一个继承自agents包Agent基类的TicTacToe类泛型参数AgentEnv, TicTacToeState把状态类型绑定到 Agent 上。状态模型TicTacToeStateexport type TicTacToeState { board: [ [played, played, played], [played, played, played], [played, played, played] ]; currentPlayer: Player; // X | O winner: Player | null; };棋盘被建模为 3×3 的二维数组每个格子取Player | nullX/O/ 空。currentPlayer表示当前轮到谁winner在游戏结束时记录胜者。state 由 Agent 在服务端持久化每次setState都会触发保存玩家刷新页面后棋局依然在。initialState字段给出初始棋局空棋盘、X先手、无胜者它对应agents包中 Agent 基类 的initialState约定——当持久化状态缺失或读取失败时Agent 会回退到这个初始值状态恢复逻辑。callable()把类方法变成远程可调用接口makeMove与clearBoard都标注了callable()装饰器。该装饰器来自agents包的 callable-decorator.ts作用是收集被装饰方法的元数据运行时通过 WebSocket 把前端调用路由到对应方法——这相当于为 Agent 自动生成了一套类型安全的 RPC 接口前端不需要手写 HTTP 路由。makeMove的完整流程src/server.ts回合校验如果this.state.currentPlayer ! player直接抛出Its not your turn落子校验目标格子已被占用时抛出Cell already played更新状态拷贝棋盘 → 落子 → 切换currentPlayer→ 调用checkWinner更新winner判终有胜者或棋盘已满则直接返回AI 接管否则调用generateObject让 GPT-4o 决策下一步。AI 落子结构化输出的关键用法AI 决策部分使用了 Vercel AI SDK 的generateObject注意这里用的是对象生成而非普通文本补全搭配 Zod schema 把模型输出约束为坐标数组const { object } await generateObject({ model: openai(gpt-4o), prompt: ..., schema: z.object({ move: z.array(z.number()) }) }); await this.makeMove(object.move as [number, number], player X ? O : X);z.object({ move: z.array(z.number()) })要求模型返回一个move数组随后被断言为[number, number]作为坐标递归调用makeMove。用结构化输出而不是让模型返回纯文本是让 LLM 可靠地驱动状态机落子的关键——解析成本为零失败率大幅下降。Prompt 中给出的策略优先级是这套逻辑的灵魂它把井字棋的经典最优策略编码成了 7 条让模型逐级判断的规则一步可赢直接赢对手一步可赢必须堵中心空着占中心能造出双重威胁fork就造对手下一步能造 fork提前堵占角落最后选边。棋盘状态以JSON.stringify(board, null, 2)注入并用文字明确空位是 null、坐标是[row, col]从 0-2的约定。这种「明确坐标约定 优先级清单 返回格式约束」的组合是让 LLM 稳定完成回合制决策的通用模板可平移至任何棋盘类游戏。checkWinner纯函数的胜者判定checkWinner是一个无副作用的纯函数预置 8 条胜利线3 行 3 列 2 对角线逐条检查是否三子同号。它不依赖 AI属于 100% 确定性的规则逻辑保证游戏判定的正确性不受模型影响。前端设计useAgent钩子与实时同步前端 src/client.tsx 用 React 实现棋盘 UI核心连接代码只有几行const agent useAgentTicTacToeState({ agent: tic-tac-toe, prefix: some/prefix });useAgent来自agents/react实现见 packages/agents/src/react.tsx它建立与 Agent 的 WebSocket 连接返回的对象包含state服务端状态的实时镜像、call调用可调用方法、setState、stub、getHttpUrl等能力。agent.state会随服务端setState自动更新并触发前端重渲染——这正是 Stateful Agent 方案的体验优势前端无需维护任何游戏逻辑副本。玩家落子一行远程调用await agent.call(makeMove, [[row, col], state.currentPlayer]);点击格子时前端把[row, col]和当前玩家传给服务端若服务端因还没轮到你或格子已占用抛错前端在catch中记录错误而不破坏 UI 状态。三个值得注意的交互细节随机开局开关autoPlayEnabled开启时新游戏开始 1 秒后随机走第一步让玩家选择执 X 或执 O对应界面上的 Random First Move 开关自动连打当state.winner出现或棋盘填满时前端累计 X 胜 / O 胜 / 平局统计并在 3 秒后自动调用clearBoard开新局——方便快速观察 AI 在不同开局下的表现New Game 按钮直接调用agent.call(clearBoard)对应服务端的clearBoard把状态重置回initialState。部署配置Durable Object SQLite 持久化wrangler.jsonc 揭示了 Agent 运行时的底座{ durable_objects: { bindings: [ { class_name: TicTacToe, name: TicTacToe } ] }, migrations: [ { new_sqlite_classes: [TicTacToe], tag: v1 } ], main: src/server.ts }TicTacToeAgent 以Durable Object形式部署每个游戏会话对应一个持久化实例setState写入 SQLite 存储new_sqlite_classes迁移声明为 Agent 创建 SQLite 数据库用于保存状态与可选历史数据main: src/server.ts把服务端入口指向 Agent 定义文件文件末尾通过routeAgentRequest来自 packages/agents/src/agent-routing.ts把请求路由到 Agent未命中则返回 404。路由前缀some/prefix与前端useAgent的prefix必须保持一致这是前后端正确对接的隐含约束。生成的 env.d.ts 会为Env注入TicTacToe: DurableObjectNamespace...类型让AgentEnv, ...的泛型在开发期即可校验。源码级速览理解这套模式的三个关键点callable()是 RPC 声明而非魔法装饰器在模块加载时把方法元数据记录到WeakMap运行时再由callablesFromDecoratedpackages/agents/src/websockets/callables-target.ts提取成 RPC target。理解这一点你就知道哪些方法能被前端调用、为什么方法的this.state始终一致。状态持久化有回退机制Agent基类从存储读取状态失败时会回退到initialStatepackages/agents/src/index.ts。因此务必把initialState当作「无历史会话」时的默认棋盘而不是每次请求都重置。确定性与非确定性分离回合校验、落子、胜负判定全部是确定性的 TypeScript 代码只有AI 该走哪一步交给模型且用generateObject Zod 强制其输出可解析的坐标。这条边界保证了无论模型发挥如何游戏本身的正确性始终受控。总结examples/tictactoe虽小却是agents仓库中最完整的「带状态 AI 交互应用」范式服务端AgentEnv, State泛型 initialStatecallable()方法构成有状态、可调用的 Agent 单元AI 接入generateObject Zod schema 分级策略 Prompt让 LLM 稳定地以结构化输出驱动状态机前端useAgent一行连接agent.call一行调用agent.state自动同步部署Durable Object SQLite 保证状态跨请求存活。如果想继续深入建议阅读仓库中的 agents 核心包 中Agent基类状态管理、调用路由与callable装饰器的实现并结合 react.tsx 理解useAgent的订阅机制。把这个示例吃透后你可以用同样的骨架快速搭建 AI 象棋、AI 猜数字、回合制卡牌等更多带状态、带 AI 决策的交互式 Agent 应用。【免费下载链接】agentsBuild and deploy AI Agents on Cloudflare项目地址: https://gitcode.com/GitHub_Trending/agents1/agents创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表