多轮对话生成架构图与智能体设计实践指南
时间:2026-07-25 | 作者:318050 | 阅读:0引子
前段时间做了一个小工具,能把自然语言直接转成架构图——用户输入一句话描述流程,LLM生成Mermaid代码,右边实时预览。用了几个月,反复踩坑,最后发现失败案例可以归纳为两类。
第一类:LLM生成的Mermaid代码看起来没问题,但mermaid.js一渲染就报Parse error,用户看到红色堆栈就走了。这个问题不是调整prompt就能解决的,模型偶尔就是会写错,能做的只能是让它错完还能自己修复。
第二类更隐蔽。用户说“画个登录流程”,LLM顺手补一个“发送验证码”进去,但既没问用户也没留档。图出来之后用户想改,但找不到锚点——中间那份需求根本没被显性化记录下来。
这两个问题指向同一个核心:单次调用不够用。必须在“提取需求”、“生成代码”、“校验”之间加一层结构,任何一步崩了都能兜住。第二版就是把它改成一个Agent——多轮追问、语法自愈、能记住用户明说过“不要”的东西。这篇讲讲这套架构具体长什么样。
支持八种图:Sequence · Flowchart · ERD · State · Class · Mindmap · Gantt · Architecture。两种模式:auto(LLM自己猜要画哪种)+ manual(用户明确指定)。
一、这不是一个Loop,是两个
多轮生成UML看起来是一个循环,拆开其实是两个,共享同一份状态,但目标完全不同。
外层 · 需求澄清 Loop用户自然语言 → 提取事实 → 发现缺失 → 追问 → 更新需求↓需求达到可绘制状态内层 · UML 生成修复 Loop结构化 spec → 生成 UML → 语法 / 语义校验 → 修复 → 输出
| 维度 | 外层澄清 | 内层生成修复 |
|---|---|---|
| 输入 | 自然语言 | 结构化 spec |
| 输出 | 结构化 spec | Mermaid 代码 |
| 成功条件 | schema 关键字段填齐 | Parser 通过 + 语义 Validator 通过 |
| 决策者 | Planner(问不问) | Validator(修不修) |
| 失败兜底 | 预算耗尽 → 强行 proceed + 记录 assumption | 修复 3 次上限 → 降级呈现失败图 |
如果混在一起做,就会遇到“这个歧义要不要问用户”这个死循环——因为外层歧义(用户没说)和内层歧义(LLM生成偏了)看起来都是“缺信息”,但实际处理路径完全不同。前者走追问,消耗预算;后者走内部修复,不打扰用户。
二、Agent内部六角色分工
内部再拆,可以把Agent想象成一个团队。这个比喻是落地过程中试出来最好用的一版,后面很多设计都围绕它展开。
| 角色 | 做什么 | 明确不做什么 |
|---|---|---|
| Extractor | 从对话历史提取事实、填 spec | 不猜、不推断,留 unknown |
| Evaluator | 遍历 spec,识别缺失与歧义 | 不判断问不问 |
| Planner | 拿缺失清单 + 预算 + ask_log决定下一步 | 不自己去问用户 |
| Generator | 生成 Mermaid,可以补合理 assumption | 但每个 assumption 必须留档 render_context |
| Validator | 语法校验 + 擅自检测(spec-assumption 对账) | 有错就退回 Generator 重试 |
| Orchestrator | 按顺序调度上述五个,维护 AgentState | 不做任何判断 |
一开始把这些角色揉在一个巨型prompt里,让LLM一次调用同时做提取、判断、生成。跑了两周开始debug时就崩溃了——一段错误的输出,说不清是Extractor记错、Evaluator漏了、还是Generator瞎猜。角色拆开之后,每一步都是纯函数,输入定了输出就定了,能单独mock、单独测试。
Functional Core / Imperative Shell 是这个分工的正式说法——Orchestrator是唯一stateful组件,其他五个都是纯函数。
2.1 调用关系:严格顺序的流水线
六个角色不是并联,是一条严格顺序的流水线,Orchestrator是唯一调度者。
用户消息↓Orchestrator(append 到对话历史,维护 AgentState)↓Extractor────→ 从对话历史提取事实,填 spec↓Evaluator────→ 遍历 spec,输出 MissingSlot 清单↓Planner────→(清单 + budget + ask_log)→ Decision│├─ AskClarification ──→ QuestionStrategy ──→ User│ ↑(下一轮从最上面开始)│└─ Proceed ──→ Generator ──→ 生成 Mermaid↓Validator · 语法校验│├─ FAIL → 内层修复循环(退回 Generator,上限 3)│└─ OK → Validator · 语义校验(spec-assumption 对账)↓擅自添加?│├─ 是 → 图 + Confrontation 一起呈现│└─ 否 → 直接呈现↓ User
- Orchestrator是唯一stateful组件。别的角色都是“输入→输出”的纯函数,不记得上一次。多轮对话的所有记忆全在Orchestrator维护的AgentState里——conversation_history、spec、render_context、ask_log、budget。
- Planner是唯一分叉点。整个Agent“问还是画”就它一个人决定,想调追问节奏改这一个函数。
- 内层修复循环发生在Generator和Validator之间,不惊动用户。Mermaid语法自愈就靠这一段。
- 语义校验不阻塞出图。发现擅自添加时,图和追问一起呈现——用户看着图判断,比看着抽象需求容易得多。
2.2 关键规矩:Extractor保守,Generator可以猜
这是整个架构的地基。
用户说:“画个登录流程,用户输密码,系统验证后返回结果。”
Extractor输出:
{actors: ["用户", "系统"],events: [{ from: "用户", to: "系统", action: "输入密码" },{ from: "系统", to: "用户", action: "返回结果" },],sync: "unknown"// ← 用户没说同步/异步,绝对不能猜}
sync: "unknown"是追问的信号——Evaluator看到unknown会标“G2歧义”,Planner拿到就决定要不要问。
如果Extractor“贴心”地补了sync: "sync"(理由:登录一般是同步),这个信号就消失了。Evaluator看到具体值不会标歧义,Planner就不会问。用户从头到尾没被问过同步异步,但图画的是同步。这就是把Agent的判断力偷偷让渡给了LLM的直觉,而且还没有留痕。
Generator没有这个约束。它可以补一个“验证码服务”进Mermaid,但每个assumption必须写进render_context.assumptions。Validator后面拿spec + assumptions对账——图里出现的每个元素必须能追溯到“用户明说”或“Generator的显式假设”。追溯不到的,就叫擅自添加(unauthorized addition),Validator会拉出来做Confrontation。
一个案例:用户后来说“响应不要,只画到存数据库就行”。这句话触发的东西比较多——撤销响应箭头、重新生成。但重新生成时有个坑,LLM惯性又给你补上响应箭头。不能靠prompt治(prompt治得住第一次治不住N次),得靠数据结构。所以spec除了正事实还有一栏叫负事实:
type SequenceSpec = {actors: Actor[]events: Event[]sync: "sync" | "async" | "unknown"// 负事实(用户明确说"没有"的东西)actors_negative: Actor[]events_negative: EventPattern[]}
Generator的prompt每轮包含负事实hint(“用户明确表示不要以下元素:响应流程”),生成时主动规避。就算Generator忘了,Validator兜住——负事实里出现的元素被再次擅自添加,直接判CRITICAL。
三、Planner独占预算决策
第二条架构决策:所有“问不问用户”的判断集中在一个纯函数里,叫Planner。
type PlannerDecision =| { kind: "AskClarification"; slot: MissingSlot }| { kind: "Proceed" }| { kind: "RequestConfirm" }function plan(candidatePool: MissingSlot[],budget: number,askLog: PastQuestion[],): PlannerDecision
输入契约:
- Evaluator和Validator只识别问题,不判断“该问不该问”。
- Planner是Agent里唯一的追问决策入口。
- Planner无状态,
ask_log由Orchestrator传入——让它能区分“未问过的unknown” vs “问过没答的unknown”,避免重复追问。
一个session有个全局预算(实测取5)。只有Agent主动打扰用户才消耗预算——用户主动改需求、修图、要求重画、对Confrontation的回答,全部不扣预算。
区分之前用的是“每轮对话消耗一个预算”。跑了一段时间发现用户越用越不敢说话——每次开口都可能触发追问,慢慢用户学会直接说“先画看看”跳过所有澄清。这不是想要的效果,澄清是价值最高的一步,用户躲开澄清就等于Agent白干。改成“只有主动追问扣预算”之后用户敢说话了。
工程收益有三条:
- 可回放:序列化AgentState就能复现任意一轮决策。收到issue时能完整重放,不用猜“那次conversation长啥样”。
- 可测:每个纯函数拿假的输入就能测边界——budget=1 / ask_log有2次回避 / spec全空。
- 可fork:同一个state用两个不同Planner策略跑,直接A/B对比。
四、Two-Pipeline:预算之内的追问 vs 预算之外的核对
早期版本把所有追问和核对都塞进Planner,发现两类东西性质根本不一样:
- 画之前发现缺关键信息——得阻塞用户,先问再画,得花预算。
- 画之后发现擅自添加——图已经出了,让用户看着图核对更直观,不该花预算。
硬塞进一个pipeline会出问题——预算被后一类蚕食,前一类就没机会问关键的东西了。所以架构裂成两条pipeline:
Pipeline A · pre-render · budget-gated · Planner 决策Evaluator MissingSlot → Planner → AskClarification → User(budget - 1)Pipeline B · post-render · budget-free · Orchestrator 自动附加低置信 MetaExtractor 推断┐Validator HIGH/CRITICAL 擅自添加 ├→ pending_confrontationsGenerator 生成期兜底 assumption┘ ↓Renderer 出图时一起呈现↓ User 确认↓升级到 spec / 撤销 + regenerate
| 维度 | Pipeline A | Pipeline B |
|---|---|---|
| 触发时机 | 生成前 | 生成后(附着于图) |
| 决策者 | Planner | Orchestrator(自动附加) |
| 消耗预算 | 1 | 0 |
| 阻塞流程 | 是 | 否(可跳过) |
| Question kind | Clarification | Confrontation |
| 用户答“是”效果 | 填 spec | 升级 assumption 到 spec |
| 用户答“否”效果 | 记 ask_log(回避) | 撤销 + regenerate + 写入负事实 |
五、加一种新图类型的成本
新图 = 新增4个sealed permit分支:
type UmlSpecification =| SequenceSpec| ClassSpec| FlowchartSpec| StateSpec| ERDSpec| MindmapSpec| GanttSpec| ArchSpec // ← 新加一种,只加这一行 + 4 个 permit
每种图独立实现四个组件:
- Extractor —— 如何从自然语言提取事实
- Evaluator —— 哪些字段是G1阻塞 / G2歧义 / G3精细
- Validator —— 擅自检测规则 + Severity判定
- QuestionStrategy —— 措辞(时序图问“参与者”,类图问“实体”)
不动的组件:Planner、Orchestrator、AgentState、共享enum、MetaExtractor(auto/manual分类)、Rescope逻辑。图类型的差异被“上下夹”的两层吸收掉了——输入侧由Extractor / Evaluator / Validator各自识别、转换为共享enum;输出侧由QuestionStrategy把payload渲染成贴合图类型的自然语言。
六、一个完整 case · 5 轮对话
Turn 1User: "画个用户注册流程时序图"MetaExtractor → { type: Sequence, confidence: HIGH, source: USER_EXPLICIT }Extractor → { actors: ["用户"], events: [], sync: unknown }Evaluator → [actors_count G1, events G1, sync G2]Planner(budget=5) → AskClarification(actors_count)Agent: "除了用户,还有哪些角色参与?"[budget: 5 → 4]Turn 2-3User: 前端 / 后端 / 数据库,以及各步骤Planner 逐轮问 events / sync[budget: 4 → 3 → 2]Turn 4User: "先画看看"← SkipToGenerate,不消耗 budgetGenerator #1 → 惯性加"发邮件通知"(spec / assumptions 都没有)Validator 语法校验 → FAIL(未声明的邮件系统)内层修复循环触发 → Generator #2 → 去掉邮件 → 语法 OKValidator 语义校验 → 3 个未声明的响应箭头 → HIGH 擅自添加→ 挂 pending Confrontation输出:图 + Confrontation "我加了完整响应流程,想保留吗?"Turn 5User: "响应不要,只画到存数据库就行"← ConfrontationAnswer NO写入负事实:events_negative.append({action: "响应流程"})触发 regenerate → 无响应箭头 → 呈现新图[budget 不变,Confrontation = 0]
5轮预算,Agent实际主动追问3次,剩余2轮备用。这就是全局预算的弹性——Planner知道什么时候要收敛,不会把用户问烦。
七、几个取舍
LLM场景下“纯函数”是近似真,不是数学真。Extractor的描述一开始写的是“精确幂等”,跑了几次发现LLM每次输出都有小方差,重跑结果不完全一致——用户会觉得“Agent忘了我上一轮说过的话”。工程上用低temperature + 严格schema + 每轮diff提示(“根据你最新的说法,我重新理解了整段对话”)能缓解,但架构层的诚实做法是承认这一点。描述改成“结构近似幂等,语义相似性尽力而为”。
Confrontation “否”的默认值取LLM top-1,不引入业务权重。MetaExtractor会给多个候选(比如“这可能是时序图/状态图/流程图”),置信度最高的作为默认。不引入业务规则加权(比如“程序员多写时序图,给sequence加权”),保持debug简单——出错时你知道模型给的原始分布是啥,不用查规则表。
类型中途切换用Rescope,不删对话历史,只重跑Extractor。用户Turn 3说“换成状态图吧”,累积的SequenceSpec怎么办?三个候选:Discard(丢弃)简单但信息白费用户会怒;Migrate(映射)N映射规则,错映射比丢弃还糟;Rescope保留conversation_history + ask_log + budget,清空spec + render_context + pending_confrontations,重跑StateExtractor派生新spec。
八、如果你也在做多轮 LLM Agent
不管你做的是画图、写SQL、生成API mock,这三条是最能省事的:
两层嵌套Loop拆开做。“问清楚”和“画出来”是两件事,共享数据但目标不同。混在一起会陷入“这个歧义要问吗”的死循环。
Extractor保守 + Generator可以猜,分工严格。这是“擅自检测能工作”的地基。Extractor帮用户补常识就等于把Agent的判断力交给了LLM的直觉;Generator的每个assumption必须留档render_context.assumptions,Validator才能对账。
所有policy集中在一个Planner纯函数里。想调追问节奏、改预算策略、加分段预算——只动一处代码。别的角色都是“识别问题”,只有Planner做“决定要不要问”。工程收益(可回放/可测/可fork)三条全靠这个分工吃到。
来源:整理自互联网
免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。
相关文章
更多-
- 飞象老师官网首页官方直达入口
- 时间:2026-07-24
-
- 节点1关键词抽取实现方法
- 时间:2026-07-24
-
- 飞象老师在线平台官方入口直达
- 时间:2026-07-23
-
- 阿里云全新Meoo CLI命令行工具现已正式发布上线
- 时间:2026-07-22
-
- MCP Server渗透测试自检:结果惊出一身冷汗
- 时间:2026-07-22
-
- BioMatrix 原生统一生物序列结构与语言模型
- 时间:2026-07-21
-
- 爱诗科技推出首款实时视频游戏引擎PixVerse Game
- 时间:2026-07-19
-
- 调试Agent的三种方法:日志、断点与可视化
- 时间:2026-07-17
精选合集
更多大家都在玩
热门话题
大家都在看
更多-
- iOS 13.5.1电池续航差是电池耗电问题吗
- 时间:2026-07-25
-
- 苹果教育优惠开启 附购买攻略
- 时间:2026-07-25
-
- 苹果iOS 14 beta 2 测试版主要更新内容:除细节变化外修复多项Bug
- 时间:2026-07-25
-
- iOS 14 beta 2 是否解决内存占用过多问题?
- 时间:2026-07-25
-
- 受欢迎的奥特曼游戏有哪些
- 时间:2026-07-25
-
- iOS 14信息应用5大更新变化
- 时间:2026-07-25
-
- iOS 14正式版上线时间公布 官方全新介绍
- 时间:2026-07-25
-
- 最新苹果iOS 14 Beta 2版本更新内容全解析与升级教程
- 时间:2026-07-25
