资讯详情

围棋小程序中七个LLM Agent的架构设计与实践

📅 2026/11/3 22:00:37 | 华诺云谱 👁 阅读
围棋小程序中七个LLM Agent的架构设计与实践
1. 项目缘起为什么要在围棋小程序里塞进七个Agent做围棋小程序的人多半有过这种体验用户打开小程序落子、悔棋、复盘功能跑通了但留存始终上不去。问题出在哪我观察了很久发现大部分围棋产品的交互是“死”的——用户点一步界面动一下仅此而已。没有反馈没有引导没有那种“对面坐了个懂棋的人”的感觉。去年我开始琢磨能不能用LLM驱动的Agent来填补这个空缺。不是做一个简单的“AI对手”而是把围棋对弈过程中涉及的不同角色拆开每个角色交给一个独立的Agent去处理。这个想法落地之后就有了现在这套架构一个uni-app写的小程序前端后端用Golang的trpc-agent-go框架编排七个Agent每个Agent有自己的场景定位、提示词策略和结构化输出格式。七个Agent分别覆盖对弈陪练、棋局解说、复盘分析、定式推荐、死活题生成、棋力评估、闲聊陪伴。听起来多但拆开之后每个Agent的职责非常清晰提示词好写输出也好控制。这篇文章就把这套东西从头到尾拆一遍包括我踩过的坑和最后跑通的方案。适合谁看如果你正在做小程序LLM的应用或者对Agent编排、结构化输出感兴趣这篇应该能给你省不少时间。如果你只是好奇围棋产品怎么和AI结合也能看到一些实际的设计取舍。2. 整体架构设计uni-app前端加trpc-agent-go后端2.1 为什么选uni-app做前端围棋小程序的用户场景很明确碎片时间下盘棋、看个复盘、做几道死活题。这种场景下原生App太重H5又太轻uni-app刚好卡在中间。一套代码可以编译到微信小程序、H5和App对于我这种一个人维护的项目来说省掉的适配成本非常可观。具体到围棋场景uni-app有几个点特别顺手。Canvas绘图能力足够画棋盘和棋子而且在小程序端的性能表现比我想象中好。我试过用纯DOM画棋盘19路棋盘361个交叉点落子时的重绘会有明显卡顿。换成Canvas之后配合脏矩形重绘落子响应基本在16ms以内肉眼看不到延迟。另一个考虑是状态管理。围棋对弈的状态不算复杂但涉及棋盘、手数、提子、禁着点这些数据用Vuex或者Pinia管理起来很清晰。uni-app对这两者都支持我最后选了Pinia因为它的TypeScript支持更好和后端的类型定义能对上。提示uni-app的Canvas在小程序端和H5端的API有细微差异建议用条件编译处理。我是在#ifdef MP-WEIXIN里用小程序原生Canvas API#ifdef H5里用标准Canvas API避免了不少兼容性问题。2.2 后端为什么用Golang和trpc-agent-go后端选Golang理由很直接并发处理能力强部署简单单二进制文件扔到服务器就能跑。围棋Agent的调用涉及大量LLM请求每个请求的响应时间从几百毫秒到几秒不等用Golang的goroutine来处理并发调用非常自然。trpc-agent-go是我在对比了几个Agent框架之后选的。它的核心优势在于Agent编排的抽象做得干净——每个Agent是一个独立的执行单元有明确的输入输出定义Agent之间的调用链可以通过配置来编排不需要写一堆胶水代码。而且它原生支持结构化输出这对围棋场景特别重要因为棋盘状态、走法建议这些数据必须是结构化的不能是一段自由文本。框架的另一个好处是内置了重试和降级机制。LLM调用偶尔会超时或者返回格式不对trpc-agent-go可以配置自动重试重试失败后走降级逻辑。我在实际运行中把重试次数设为2次降级策略是返回一个预设的兜底响应保证用户不会看到空白或者报错。2.3 七个Agent的职责划分七个Agent不是拍脑袋定的而是按照围棋对弈的完整流程拆出来的。下面这张表是我最终确定的职责划分Agent名称触发场景核心职责输出类型对弈陪练用户落子后生成对手走法结构化走法坐标棋局解说每走一步后用自然语言解释当前局面文本关键点标注复盘分析对局结束后分析胜负关键手结构化报告定式推荐开局阶段推荐当前局面的定式走法序列说明死活题生成用户请求练习根据棋力生成死活题棋盘状态答案棋力评估对局结束后评估用户棋力水平评分段位估算闲聊陪伴用户空闲时围棋相关闲聊自然语言文本这个划分的关键在于每个Agent的输入输出边界非常清晰。对弈陪练只负责生成走法不负责解释棋局解说只负责解释不负责走法。这样每个Agent的提示词可以写得很聚焦LLM的输出质量也更容易控制。3. 七个Agent的场景拆解与提示词设计3.1 对弈陪练Agent让LLM学会“下棋”这是七个Agent里最难做的一个。LLM本质上是一个语言模型让它生成合法的围棋走法需要大量的提示词工程和输出约束。我的做法是不给LLM看完整棋盘。19路棋盘有361个交叉点如果把整个棋盘状态用文本描述给LLMtoken消耗巨大而且LLM很容易在长文本中迷失。我的方案是只给LLM看局部棋盘——以当前落子位置为中心取一个9x9的区域加上全局的形势判断信息比如双方实地大致对比、当前手数、是否进入收官阶段。提示词的核心结构是这样的你是一个围棋对弈Agent当前执黑/白。 棋盘局部状态9x9区域X表示黑子O表示白子.表示空点 ......... ..X.O.... ...X..... ......... 当前手数45 形势判断黑棋实地领先约5目白棋外势较厚 请生成你的下一手走法输出格式为JSON {move: D4, reason: 简要说明}这个提示词的关键在于形势判断信息。LLM不懂围棋但它能理解“实地领先5目”这种描述并据此调整走法策略。我试过不给形势判断LLM的走法会非常随机经常下出明显亏损的棋。输出格式用JSON约束配合trpc-agent-go的结构化输出校验如果LLM返回的坐标不合法比如落在已有棋子的位置框架会自动重试。实测下来第一次调用合法率大概在85%左右重试一次后能到97%以上。注意LLM生成的走法偶尔会“看起来合理但实际上是禁着点”。我的处理方式是在后端加一层合法性校验如果走法非法直接走降级逻辑——用一个简单的蒙特卡洛树搜索生成一个合法走法。这个降级逻辑虽然棋力一般但至少保证对局能继续。3.2 棋局解说Agent把局面翻译成人话棋局解说Agent的定位是“懂棋的朋友在旁边帮你分析”。它的输入是对弈陪练Agent生成的走法和当前棋盘状态输出是一段自然语言解说。这个Agent的提示词设计有一个核心原则说人话不说术语。我试过让LLM用专业术语解说结果用户反馈“看不懂”。后来改成用生活化类比比如“这步棋就像在对方家门口插了一面旗子虽然暂时没威胁但对方得时刻提防着”用户接受度明显提高。提示词示例你是一个围棋解说Agent用通俗易懂的语言解释当前走法。 当前走法黑棋D4 棋盘状态...局部棋盘 请用2-3句话解释这步棋的意图避免使用专业术语用生活化类比。 输出格式{commentary: 解说文本, key_point: 关键点坐标}解说Agent的响应速度要求比对弈陪练高因为用户落子后期待即时反馈。我把这个Agent的LLM调用超时设为3秒超过3秒直接返回一个简短的通用解说比如“这步棋很有想法让我们看看接下来怎么发展”。3.3 复盘分析Agent找出胜负关键手复盘分析Agent是在对局结束后触发的它的任务是分析整盘棋的胜负关键手。这个Agent的输入是完整的对局记录每一步的坐标和手数输出是一份结构化报告。这里有一个技术难点完整对局记录可能很长一盘19路棋局可能有200-300手全部塞给LLM会超出上下文窗口。我的解决方案是分段分析汇总。先把对局按50手一段切分每段交给LLM分析找出该段的关键手然后再把各段的关键手汇总让LLM做最终判断。提示词的核心是引导LLM关注胜率变化。我会在输入中附带每一步的胜率估算这个数据来自一个轻量级的围棋AI引擎不是LLM生成的LLM只需要找出胜率变化最大的几步然后解释原因。输出格式{ key_moves: [ {move_number: 45, coordinate: D4, winrate_change: -15.2, analysis: 这步棋导致胜率大幅下降...}, ... ], summary: 整盘棋的胜负关键在... }3.4 定式推荐Agent开局阶段的走法库定式推荐Agent只在开局阶段前30手触发。它的输入是当前棋盘状态和手数输出是推荐的定式走法序列。这个Agent的提示词里我嵌入了一个定式库的摘要。不是把整个定式库塞进去而是把常见定式的名称和对应的走法序列用简洁的格式列出来让LLM根据当前局面匹配最合适的定式。常见定式 - 星位小飞挂D4-F3-C6 - 三三入侵D4-C3-D3 - ... 当前局面...棋盘状态 请推荐最合适的定式输出格式{pattern_name: 星位小飞挂, moves: [F3, C6], explanation: ...}定式推荐Agent的准确率比较依赖定式库的质量。我最初只放了20个常见定式LLM经常推荐不匹配的定式。后来扩充到80个覆盖了大部分开局场景准确率明显提升。3.5 死活题生成Agent根据棋力动态出题死活题生成Agent是我个人最喜欢的一个。它的输入是用户的棋力评估结果来自棋力评估Agent输出是一道死活题的棋盘状态和答案。这个Agent的提示词设计有一个巧妙之处用模板参数的方式生成题目。我不让LLM从零生成死活题而是预置了一批死活题模板每个模板有难度参数1-10LLM根据用户棋力选择合适的模板然后微调棋盘上的棋子位置。死活题模板库 - 模板A角部死活难度3-5基础形状... - 模板B边上死活难度5-7基础形状... 用户棋力业余3级对应难度4 请选择合适模板并生成题目输出格式{board: ..., answer: [D4, E5], difficulty: 4}这样做的好处是题目质量可控不会出现LLM生成的“无解”死活题。我试过让LLM完全自由生成结果有30%左右的题目要么无解要么有多个答案用户体验很差。3.6 棋力评估Agent给用户一个靠谱的段位棋力评估Agent的输入是用户最近的对局记录胜负、对手棋力、关键手胜率变化输出是一个棋力评分和估算段位。这个Agent的提示词里我加了一个评分公式的说明让LLM按照公式来计算而不是凭感觉给分棋力评分公式 基础分 对手平均棋力 × 0.6 胜率 × 40 调整分 关键手平均胜率变化 × 0.5 最终评分 基础分 调整分 段位映射评分0-20业余1级20-40业余2级... 请根据以下对局记录计算棋力评分 ...用公式约束之后棋力评估的稳定性好了很多。之前LLM给的评分波动很大同样水平的用户有时候评业余1级有时候评业余3级。加了公式之后波动范围缩小到半个段位以内。3.7 闲聊陪伴Agent让小程序有点温度闲聊陪伴Agent是唯一一个不涉及围棋技术的Agent它的职责就是在用户空闲时陪聊。输入是用户的聊天消息输出是自然语言回复。这个Agent的提示词里我设定了一个人设一个下了二十年围棋的业余棋手性格随和喜欢用围棋比喻生活。比如用户说“今天工作好累”Agent会回“下棋也是这样中盘绞杀最耗神该收官的时候就得收官别硬撑”。人设提示词你是一个下了二十年围棋的业余棋手性格随和喜欢用围棋比喻生活。 用户说{user_message} 请用1-2句话回复保持轻松自然的语气。闲聊Agent的LLM调用参数和其他Agent不同我调高了temperature从0.3调到0.8让回复更有变化。其他Agent的temperature都设在0.2-0.4之间保证输出的稳定性。4. 结构化输出的实现细节与踩坑记录4.1 为什么结构化输出是刚需围棋场景对数据格式的要求非常严格。棋盘坐标必须是合法的A-T跳过I1-19走法必须是合法的不能落在已有棋子的位置胜率必须是0-100之间的数字。如果LLM返回一段自由文本后端解析起来会非常痛苦。trpc-agent-go的结构化输出功能解决了这个问题。它通过JSON Schema约束来强制LLM按照指定格式输出。我在每个Agent的配置里都定义了输出Schema框架会在LLM返回后自动校验不符合Schema的响应会被拒绝并触发重试。以对弈陪练Agent为例它的输出Schema是这样的{ type: object, properties: { move: {type: string, pattern: ^[A-HJ-T][1-9]|1[0-9]$}, reason: {type: string, maxLength: 100} }, required: [move, reason] }这个Schema里的pattern正则确保了走法坐标的合法性。如果LLM返回了“I5”这种非法坐标围棋棋盘没有I列Schema校验会直接失败框架自动重试。4.2 提示词里的结构化输出引导光有Schema约束还不够提示词里也需要明确告诉LLM输出格式。我的经验是在提示词末尾用单独的段落强调输出格式并且给一个具体的示例。比如对弈陪练Agent的提示词末尾请严格按照以下JSON格式输出不要添加任何其他内容 {move: D4, reason: 占据星位便于向两侧发展}这个示例很重要。我试过不给示例只给Schema描述LLM的输出格式错误率会高很多。给了示例之后格式错误率从15%降到了3%左右。4.3 处理LLM返回的“意外情况”即使有Schema约束和提示词引导LLM偶尔还是会返回意外内容。我遇到过的典型情况包括返回了多个JSON对象LLM把思考过程也输出了JSON字段名拼写错误比如把“move”写成“moves”坐标格式正确但语义错误比如返回了“A0”但围棋棋盘没有0路针对这些情况我在trpc-agent-go的重试配置里加了一个自定义校验函数。框架在Schema校验通过后还会调用这个函数做二次校验。如果二次校验失败同样触发重试。func validateMove(resp map[string]interface{}) error { move : resp[move].(string) if !isValidCoordinate(move) { return fmt.Errorf(invalid coordinate: %s, move) } if isOccupied(move) { return fmt.Errorf(position occupied: %s, move) } return nil }这个二次校验函数帮我拦截了不少“格式正确但内容错误”的响应。实测下来加上二次校验之后对弈陪练Agent的走法合法率从97%提升到了99.5%以上。4.4 结构化输出的性能开销结构化输出会带来额外的token消耗和延迟。JSON Schema的描述本身要占tokenLLM生成JSON格式的输出也比生成自由文本慢一些。我实测下来结构化输出的延迟比自由文本高20%-30%。对于对弈陪练和棋局解说这两个对延迟敏感的Agent我做了一些优化。对弈陪练Agent的Schema尽量精简只保留必要的字段。棋局解说Agent的输出Schema里commentary字段设了maxLength避免LLM生成过长的解说。实操心得如果你的应用对延迟要求很高可以考虑把结构化输出和自由文本输出分开。关键数据用结构化输出辅助性的解释用自由文本。我在棋局解说Agent里就是这么做的——key_point字段用结构化输出commentary字段用自由文本兼顾了准确性和响应速度。5. 常见问题与排查技巧实录5.1 LLM返回的走法不合法怎么办这是最常见的问题。LLM生成的走法可能落在已有棋子的位置或者落在禁着点。我的处理策略是三层防护第一层是Schema校验用正则表达式确保坐标格式正确。第二层是自定义校验函数检查坐标是否在棋盘范围内、是否被占用。第三层是降级逻辑如果前两层都通过但走法仍然不合法比如打劫规则直接调用一个简单的蒙特卡洛树搜索生成合法走法。三层防护下来对弈陪练Agent的走法合法率能到99.9%以上。剩下0.1%的情况是LLM返回了完全无法解析的内容这时候直接走降级逻辑用户基本感知不到。5.2 Agent响应超时怎么处理LLM调用的响应时间波动很大快的时候几百毫秒慢的时候十几秒。我给每个Agent都设了超时时间超时后走降级逻辑。Agent超时时间降级策略对弈陪练5秒蒙特卡洛树搜索生成走法棋局解说3秒返回通用解说模板复盘分析15秒返回简版复盘报告定式推荐3秒返回最常见的定式死活题生成8秒返回预设的固定题目棋力评估10秒返回基于胜率的粗略估算闲聊陪伴5秒返回预设的闲聊回复超时时间的设定需要根据实际网络环境和LLM服务商的响应速度来调整。我最初把对弈陪练的超时设为3秒结果在晚高峰时段经常触发降级。后来调到5秒降级率从12%降到了3%左右。5.3 七个Agent的调用成本怎么控制七个Agent如果每个都调用LLM成本会很高。我的优化策略是按需触发。不是每个Agent在每次对局中都会触发对弈陪练每次用户落子后触发频率最高棋局解说每3手触发一次不是每手都触发复盘分析对局结束后触发一次定式推荐只在开局前30手触发死活题生成用户主动请求时触发棋力评估对局结束后触发一次闲聊陪伴用户主动发消息时触发这样下来一盘200手的对局对弈陪练调用约200次棋局解说约67次其他Agent加起来不超过10次。总调用次数控制在300次以内成本可控。另外对弈陪练Agent我用了缓存机制。相同的棋盘局部状态如果之前已经请求过直接返回缓存结果。围棋的局部状态重复率其实不低缓存命中率大概在15%左右进一步降低了成本。5.4 小程序端的性能优化uni-app编译到小程序端之后性能瓶颈主要在Canvas绘图和网络请求。Canvas绘图我用了脏矩形重绘只重绘发生变化的区域而不是整个棋盘。网络请求方面我把Agent的调用改成了流式返回用户落子后先显示一个“思考中”的动画Agent返回后再更新界面。还有一个细节是请求合并。用户落子后对弈陪练和棋局解说两个Agent都需要触发。我把这两个请求合并成一个批量请求后端并行调用两个Agent然后一起返回。这样减少了小程序端的网络往返次数响应速度更快。5.5 常见问题速查表问题现象可能原因排查方法解决方案Agent返回空响应LLM服务超时查看后端日志中的超时记录调整超时时间增加重试次数走法坐标非法LLM输出格式错误检查Schema校验日志优化提示词增加示例解说内容重复temperature过低查看LLM调用参数适当调高temperature复盘报告不完整对局记录过长检查分段逻辑调整分段大小增加汇总步骤死活题无解模板选择错误检查模板难度参数增加模板库优化匹配逻辑棋力评估波动大评分公式未生效检查提示词中的公式说明强化公式约束增加示例闲聊回复生硬人设提示词不够具体检查人设描述增加性格细节和示例对话6. 一些实操心得和后续扩展方向这套七个Agent的架构跑了大半年整体稳定性还不错。有几个心得值得单独拎出来说。第一Agent的粒度不是越细越好。我最初想把对弈陪练拆成“布局Agent”“中盘Agent”“收官Agent”三个后来发现拆得太细之后每个Agent的提示词都要重复写一遍棋盘状态描述token消耗反而增加了。最后合并成一个对弈陪练Agent通过提示词里的“当前阶段”参数来区分策略效果更好。第二结构化输出的Schema要尽量宽松。我一开始把Schema写得很严格比如reason字段要求不超过50个字。结果LLM经常超出触发重试浪费了不少token。后来把限制放宽到100个字重试率明显下降。Schema的目的是保证数据可用不是追求完美。第三降级逻辑比想象中重要。LLM服务不可能100%可用网络波动、服务商限流都会导致调用失败。如果没有降级逻辑用户看到的就是报错或者空白。我现在的降级逻辑虽然简单但至少保证了用户体验的连续性。后续我打算在这几个方向继续折腾一是把死活题生成Agent的模板库扩充到500个以上覆盖更多棋力段位二是给复盘分析Agent加上“对比分析”功能让用户可以选择两个不同时期的对局进行对比三是探索用更小的模型来处理对弈陪练Agent的走法生成进一步降低成本。这套东西说到底就是一个思路把复杂问题拆成多个简单问题每个问题交给一个专门的Agent去处理。围棋是这样其他领域应该也差不多。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑