☰
七个Agent拆解围棋小程序:提示词设计与结构化输出实战
2026/9/26 8:14:57 网站建设 项目流程

最近我在打磨自己的围棋小程序时,遇到了一个挺现实的麻烦:功能越加越多,用户问的问题五花八门,一个大模型接口根本扛不住。不是模型能力不够,而是同一个提示词很难同时处理棋谱识别、复盘讲解、死活题推荐、术语查询这些差异极大的任务。后来我把思路从“一个Prompt通吃”改成“七个Agent各管一摊”,整个项目一下子顺了。

这篇内容就专门拆一下这七个Agent的场景划分、提示词设计,以及最容易被忽略的结构化输出套路。如果你正在做小程序、做AI应用,或者只是好奇Agent落地时到底怎么分工,这篇应该能给你一些能直接搬走的经验。

1. 围棋小程序里为什么要拆出七个 Agent

先说一个可能让你意外的事:Agent在我这里没有多么玄乎,它就是“带了明确角色、明确任务、明确输出格式的模型调用”。拆出七个Agent的本质,是让每一次模型调用都只干一件小事,把它干到最准。

1.1 一个全能Agent根本忙不过来

我最开始的做法很朴素:一个系统提示词,把围棋百科、复盘分析、死活题生成、段位评估全部塞进去。结果就是用户问“这个局部有什么棋可以下”,它回复的时候很可能会先长篇大论讲围棋历史,或者在JSON里给出一个不存在的坐标。原因是围棋语料的领域性太强,术语坐标、提子规则、打劫规则、目数计算这些信息混在一起,模型很容易被无关上下文干扰。

后来我把任务拆开,每个Agent的上下文短了一半,准确率立刻上来了。围棋用户问问题通常非常具体,比如“帮我看看这步棋是不是有问题”“这个死活题答案是什么”,一个Agent只处理一种场景,模型不需要做任务识别,也不需要切换思维模式。实测下来,同样一个模型,拆开之后的正答率提升非常明显,幻觉明显减少。

1.2 七个Agent的分工全景

我的小程序当前有七个Agent,命名很直接,方便和前端接口对应:

  1. 棋谱识别Agent:负责把用户上传的棋谱图片或文本转成标准的棋谱坐标序列。
  2. 复盘总结Agent:负责把整盘对局的关键胜负处、阶段划分、关键手找出来。
  3. 死活题生成Agent:负责按用户棋力生成死活题,并给出正解和变化图。
  4. 棋局讲解Agent:负责针对某个局面,用自然语言解释下一手选点和背后的道理。
  5. 术语百科Agent:负责回答围棋术语、规则、历史相关的问题。
  6. 棋力测评Agent:负责通过让用户做几道题,估算大致的棋力区间。
  7. 每日一题推荐Agent:负责结合用户水平每天推荐一题,并生成推荐理由。

这七个Agent不是平均用力的,棋谱识别和复盘总结最吃结构化输出,死活题生成最吃提示词约束,术语问答则用纯自然语言就够。把它们的输出方式分开设计,后面的工程逻辑才说得清。

1.3 拆分带来的成本和收益

拆成七个Agent,最直接的代价就是每个接口都要维护一份提示词,模型调用次数也会变多。表面上好像更费钱了,实际上节省了更多。因为不需要反复重试修正,不需要给一次调用塞超长上下文,结果稳定之后成本反而更低。

另外一个好处是可维护性。每次更新提示词,只影响一个Agent,不用担心“改了一个功能把另一个功能搞坏了”。对于小程序这类需要频繁迭代的产品来说,多花一点管理成本换稳定性,非常划算。后面第五部分会展开讲我在这上面踩过的坑。

2. 七个Agent的场景拆解与提示词设计

提示词设计在Agent开发里其实不那么神秘,核心就三件事:告诉模型它是谁、它要做什么、输出长什么样。真正难的是把每个场景的规则写得足够具体。

2.1 棋谱识别Agent:坐标规则必须写进提示词

用户在小程序里传一张棋谱图片,这个Agent要先完成“图片到坐标序列”的转换。围棋坐标有国际比赛通用的SGF规则,也有中文语境里常用的“左上角星位”这种描述方式。为了让模型输出能直接被渲染成棋盘,我要求它统一输出字母坐标。

我的系统提示词核心部分是这样的:

你是围棋棋谱识别Agent。用户会提交棋谱图片或一段棋谱文本。 你需要提取棋谱中的落子序列,并输出JSON数组。 要求: 1. 坐标使用SGF字母坐标,横坐标A到S,纵坐标A到S,跳过I。 2. 默认黑棋先手,交替落子。 3. 如果图片模糊或无法确认坐标,在uncertainty字段中标记true,不要猜测。 4. 不要输出任何解释文字,直接输出JSON。

最关键的就是第3条。一开始我让模型“尽量识别”,结果它把模模糊糊的棋子位置硬编码成坐标,用户看着棋盘上一堆错位棋子直接懵了。改成“不确定就标记uncertainty”之后,虽然识别率没有拉满,但至少不会瞎编,用户体验老实了很多。

输出的结构大概长这样:

{ "moves": ["B1", "W2", "B3", "W4"], "uncertainty": false, "ko_rules_applied": true }

2.2 复盘总结Agent:从棋谱数组到阶段评语

复盘总结Agent输入是一整局棋的坐标序列,输出是分阶段的评语。这个场景最大的问题是模型对“棋谱很长”这件事没有耐心,经常只分析前面几十手,就把后面草草略过。我后来在提示词里明确要求“按手数区间切片分析”,效果立竿见影。

你是围棋复盘Agent。你会收到一个完整的落子序列moves[]。 请按以下三个阶段分别总结: - 布局阶段:先手如何展开,重点判断是否落子过于集中或过于松散。 - 中盘阶段:关键战斗从第几手开始,胜负手是什么。 - 官子阶段:哪些地方的目数出入最大。 只输出JSON,不要输出客套话。

这里涉及一个很微妙的点:模型并不真的会“看棋”,它是根据落子序列推算出来的。所以你必须在提示词里给它一个可执行的推理路径,否则它很容易用通用围棋知识糊弄你。比如“布局阶段:双方争夺大场”这种评语,任何棋谱都能套,等于没说。

复盘Agent输出我用了不少枚举值来控制质量:

{ "summary": "白棋中盘战斗处理不当,左侧大龙被杀导致败局", "phase_reviews": [ { "phase": "opening", "start_move": 1, "end_move": 50, "comment": "双方布局平稳,黑棋稍显保守" }, { "phase": "middle", "start_move": 51, "end_move": 120, "comment": "白棋第78手方向错误,形成孤棋" }, { "phase": "endgame", "start_move": 121, "end_move": 200, "comment": "黑棋官子略微退让,但优势仍未动摇" } ] }

2.3 死活题生成Agent:题目不是随便摆的

这个Agent是最容易翻车的。因为很多人以为让模型“出一道死活题”就行,实际上模型天然倾向于出那些网上题库里反复出现的经典题,比如“直四”“曲四”“刀把五”这些。它们不是错,而是太基础,用户做完第一道就嫌简单。

我在提示词里加入了一个“难度校准”机制:先让用户选目标棋力区间,再把对应区间常见的死活题难度特征写清楚。比如“业余5段用户需要三步以上计算的题目,包含至少一个先手交换”。

你是死活题生成Agent。用户目标棋力:{kifu_level}。 请生成一道活着或吃掉对方的死活题。 要求: - 题目坐标不能使用棋盘边缘两路内的常见大眼摆法。 - 必须包含至少一个关键第一手,正解和失败图都要给出。 - 正解描述控制在100字以内,用坐标说明手法。 - 输出JSON格式,不要附加题目外解说。

为什么死活题生成要单独一个Agent?因为它的输出形式非常特殊:需要包括题目盘面、正解、失败变化、解说。如果把题目生成和普通问答混在一起,模型经常会把“正解”写成自然语言的一段话,前端根本没法处理。单独抽出来以后,我才能稳定拿到下面这样的数据:

{ "difficulty": 3, "board_size": 19, "stones": [ {"x": 3, "y": 4, "color": "B"}, {"x": 15, "y": 16, "color": "W"} ], "correct_answer": ["B15", "W16", "B17"], "variation": [ {"move": "W14", "result": "白失败"} ], "explanation": "黑先B15破眼,白B16做眼,黑B17扳,形成假眼。" }

2.4 其余四个Agent:讲解、术语、棋力、每日一题

这四个Agent相对简单,但提示词侧重点各不相同。棋局讲解Agent要求的是“先结论后理由”,避免模型长篇大论扯到天边去。我会在提示词里限定“回复控制在150字以内,先说出推荐落子坐标,再用两句话解释理由”。术语百科Agent则要防止它像普通百科一样答一堆和围棋无关的背景,我会限定“只解释围棋本义,不涉及文化发散”。

棋力测评Agent比较特殊,它是根据用户对题目难度的回答来推算棋力。它输出的是一个区间,而不是一个精确段位,因为靠几道题测段位本身就是不科学的。我会在提示词里明确告诉模型“如果信息不足,输出一个较宽的区间,不要强行精确”。

每日一题推荐Agent本质上是一个排序任务,它要把题库里的题按“用户最近答题正确率”和“题目标签”这两个维度排序。这个场景不太需要模型输出长文本,反而是把模型当作一个打分器:给每道题输出一个0到1的推荐分,然后小程序端取分最高的那一道。

3. 结构化输出:让Agent的回复能直接被小程序使用

如果你做过一段时间的AI功能开发,一定见过这种场景:模型给你输出一大段漂亮的中文总结,前端根本不知道怎么拆成结构化数据;或者模型给了JSON,但字段名是乱的,数组里偶尔混进一个空对象。这些都是结构化输出没做好的典型问题。

3.1 为什么必须结构化输出

小程序的本质是用户往界面上点一下,后端处理完,返回可渲染的数据。如果模型输出的是自然语言,前端就得写一堆解析逻辑,还不一定能解析对。比如复盘总结如果是一整段文字,我怎么把“布局阶段30手”展示成图表?怎么标记“第78手方向错误”让棋盘跳转到那一手?

所以我要求所有Agent输出JSON。棋谱识别输出moves数组,复盘Agent输出phase_reviews数组,死活题生成输出stones数组。这样做有三个好处:

  • 前端直接渲染,不需要人肉解析长文本。
  • 能对关键字段做校验,比如坐标合法性的校验。
  • 缓存和持久化很方便,可以把每次对局分析结果存到数据库里。

这里要强调一下,我不是在让模型“学会编程”,而是让它的回复天然适配小程序的数据结构。如果你现在做类似功能,尽量把“能否直接序列化为JSON”当成一个设计原则来要求自己。

3.2 让模型稳定输出JSON的三个关键动作

第一个动作是“给例子”。模型对JSON的偏好比想象中强,但如果你不给一个具体例子,它还是会在某些边缘情况里输出Markdown代码块。我最开始就吃过这个亏,后来在每个Agent的系统提示词最后强制加了一行:

只输出JSON,不要输出```json标记,不要输出任何其他文字。

第二个动作是“利用模型平台的结构化调用能力”。现在的Agent开发框架大多支持声明式输出格式,你可以直接定义好返回对象的结构,模型平台会保证输出符合这个结构。我复测下来,用平台内置的结构化能力比纯提示词约束稳定很多,尤其在中文术语复杂的场景下,出错率能降低不少。

第三个动作是“前端兜底校验”。即使模型平台给出了干净JSON,我还是会在小程序端做一次快速校验。校验逻辑不复杂,就是检查数组长度、坐标字符串格式、分数范围这几个关键点。万一校验失败,就让用户看到“分析失败,请重试”,而不是展示一个空棋盘。

3.3 结构化字段设计原则:小字段、枚举值、唯一ID

结构化的字段设计看起来简单,实际上还是有一些门道的。我的原则是三句话:字段保持小、能用枚举不用自由文本、能带ID就带ID。

“小字段”指的是把“阶段”从一大段评论中抽出来,单独放phase字段,而不是塞在comment里。这样前端做图表、筛选、跳转都方便。“枚举值”指的是像phase这种尽量用opening、middle、endgame,而不是“开局阶段”、“中盘战”这种模型随机发挥的短语。枚举值减少后面匹配的麻烦。“唯一ID”指的是遇到题目推荐、术语查询这种场景,让Agent输出一个题目ID或者术语ID,而不是把整个词条文本复制出来。这样小程序可以只存一份词条数据,Agent只负责给ID,时长偏长也不怕。

结构化输出最后还要考虑一件事:失败时的降级方案。我一开始只接受“完美JSON”,一旦解析失败就报错。后来发现用户问的问题多种多样,特别是短期对话中用户会追加一句“不对,我说的是另一步棋”,这时如果模型返回“抱歉我理解错了”而不是JSON,解析一定炸。我的处理方法是:在小程序端解析失败后,把这个Agent的输出转成纯文本模式展示,不强制结构化。牺牲一点渲染效果,保住用户能正常看到回复。

4. 实操链路:从提示词到小程序页面的完整流程

前面讲了很多设计思路,这一部分把从提示词到小程序页面的完整链条串起来。你如果现在就在做类似项目,可以直接对照这个流程来搭。

4.1 提示词的组织与版本管理

七个Agent意味着至少有七份提示词。我建议不要把它们直接硬编码在小程序前端,而是放在后端的一个提示词配置表里。每一次模型调用都从配置表读取最新的提示词,方便更新和回滚。

我的配置表字段大致是这样:

字段说明
agent_nameAgent的唯一标识
prompt_version提示词版本号
system_prompt系统提示词正文
output_schema期望的JSON结构描述
enabled是否启用
remark更新说明

为什么要版本控制?因为你会发现提示词不是写一次就完事的,它会随着需求变化不断迭代。没有版本号,一旦新提示词引入Bug,很难快速回滚。我自己的做法是:每次修改提示词,先在一个小程序测试页面里跑几组固定用例,确认没问题再上线。固定用例很重要,因为模型输出具有随机性,不固定测试的话很难判断是提示词改坏了还是模型抽风。

4.2 请求层与解析层的工程细节

模型请求不可能在小程序前端直接发,因为API Key会暴露,而且提示词也不太方便藏。所以标准的做法是小程序端把用户输入和一个意图标识发给后端,后端根据意图选择对应Agent调用模型,再把结构化的结果返回给小程序。

后端的伪代码逻辑大概长这样:

def run_agent(user_input, agent_name, context): prompt = load_prompt(agent_name) messages = [ {"role": "system", "content": prompt}, {"role": "user", "content": user_input} ] response = llm_chat(messages, json_mode=True) validated = validate_schema(response, agent_name) if not validated: response = llm_chat(messages + [{"role": "user", "content": "请严格按要求输出JSON"}]) validated = validate_schema(response, agent_name) return validated

这里有一个经验:如果第一轮输出非法JSON,我不建议直接报错,而是让模型“重新输出一次”,并强调“严格按要求输出JSON”。多数情况下,第二次生成的正确率会明显提升。因为加一个显式提醒等于把提示词里的约束又重新强调了,对模型来说是有效的。

小程序端收到后端返回后,直接JSON.parse并渲染。主要注意点就是不要假设字段永远存在,例如用户关闭了AI功能后,“analysis”字段可能为空。前端要有一套默认态。

4.3 一个实际问答流程的完整示例

为了让你把流程串起来,我举一个具体的例子。

用户上传了一盘棋的图片,棋谱识别Agent先把它转成坐标序列。用户随后问:“这盘棋我输在哪里?”

步骤一:小程序端把这张图片上传到后端,后端调用棋谱识别Agent,得到moves数组。

步骤二:后端把moves数组作为上下文,交给复盘总结Agent,让它把全盘阶段评语生成出来。

步骤三:后端把阶段评语和关键手坐标一起返回给小程序,前端在高亮棋盘上打标记,同时展示“中盘第78手方向错误”的评语卡片。

这里的上下文传递格外重要。你不可能让复盘Agent去读取图片,而是让它读取棋谱识别Agent产生的moves数组。Agent之间通过结构化数据协作,而不是通过自然语言反复沟通。这个模式在Agent开发里叫“数据接力”,它会极大减少模型计算量,也让每一步的调试变得更简单。

5. 踩过的坑和排查技巧

这部分是实打实踩过之后才总结出来的,可能比前面的理论更有参考价值。我把遇到的高频问题和排查技巧整理成了一张表。

5.1 高发问题速查表

现象原因解决办法
模型返回Markdown代码块提示词没写“不要输出代码块标记”在提示词末尾加一句强制JSON约束
坐标越界模型自行生成了S棋盘外的坐标添加坐标合法性校验,过滤非法值
复盘漏掉中盘内容没按手数区间切片,模型偏好只分析前几十手强制分阶段总结,每段给start_move和end_move
JSON字段里有中文字符串但没引号没用结构化输出能力改用平台内置的JSON Mode
用户追加提问导致解析失败Agent返回了道歉文本而不是结构化数据解析失败时降级为纯文本展示
死活题难度忽高忽低提示词里没有难度校准,模型自由发挥让用户先选棋力,再把难度特征写进提示词

5.2 围棋领域特有的坑

围棋领域的结构化输出有个别的地方特别容易出问题。一个是“坐标点”的表达方式不统一,有的用户说“左上角”,有的说“B1”,有的说“第1步”。我最后的方案是让模型统一转成SGF字母坐标,并且在前端做一个坐标转换器,把不同输入格式全部转成内部标准。这个转换逻辑放在前端处理会比让模型处理更稳定。

另一个坑是“打劫”的处理。棋谱识别Agent在识别连续提劫的棋谱时,容易把中间某步漏掉或误判,导致整个棋谱数据错位。我的规避方法是:在输出moves数组后增加一个后处理函数,检查“同一位置有没有重复落子”,如果重复了就提示用户“棋谱可能需要人工校验”。虽然不能100%解决,但至少能在源头拦住一部分异常对局。

还有“目数计算”的问题。模型能估算“白棋目数领先”,但让它在结构化输出里给出一个精确的数字,稳定性就差了。后来我把目数输出的数值范围放宽,输出“领先5到10目”而不是“领先8目”。用户看着也不会觉得不对,但模型生成时压力小很多,准确率自然高一些。

5.3 关于测试和回归的一点心得

七个Agent做多了以后,你会发现最让大家头疼的不是Agent本身,而是“改坏”。今天优化了死活题生成Agent,结果它多了几句废话;明天调整了复盘Agent的提示词,结果返回的JSON结构变了。为了避免这种情况,一定要有一个回归测试的用例集。

我的做法是把每个Agent的固定测试问题存成一个JSON文件,每次提示词改动后跑一遍。比如棋谱识别Agent至少传3张不同风格的棋谱图,复盘Agent至少传3盘不同结局的棋谱,死活题生成Agent至少跑3个不同段位请求。确认所有用例都通过之后才发布新版本。

按照我的个人经验,与其相信所谓的大模型能力多强,不如在提示词、结构化输出和回归测试上多花点时间。围棋小程序里的这七个Agent,技术上算不上什么黑科技,但它们验证了一个很朴素的道理:Agent拆得越细,每个Agent的提示词越聚焦,最终给到用户的结果就越稳定。希望这篇分享能帮你少走一点弯路,特别是在结构化输出这方面。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询