
前段时间我按“多模型议会”LLM Council的思路搭了一套自动化流程目标是让 9 个不同模型协作生成一份金融通讯稿。架构听起来很完整资讯摘要、宏观分析、行业研究、数字校验、事实核查、观点生成、风险审查、文风润色、主编终稿每个环节由不同模型负责。但真正跑起来之后问题一个接一个同一份财报两个模型给出的营收增速完全相反审查模型和写作模型互相较劲改了三轮内容反而更差一轮完整跑完token 消耗高得吓人。这篇文章不是来夸“多模型协作多先进”的而是想把这套 9 模型议会系统里真正会坏的地方拆开讲清楚。适合正在做多智能体应用、想用多模型写报告或做内容类产品的开发者也适合已经踩过坑想找排查思路的读者。读完你会知道哪些问题来自模型本身哪些问题来自流程设计哪些问题干脆不应该用模型解决。1. 背景与核心概念1.1 什么是 LLM Council 多模型议会LLM Council也叫多模型委员会、模型议会是一种把多个大语言模型组合到同一个任务里的架构模式。它不是一个标准化框架更像一类设计思路。常见形态有三种平行投票多个模型对同一个问题分别作答最后投票或评分选出最佳答案。角色分工每个模型只负责流水线里的一个环节比如摘要、分析、审查、润色。辩论迭代多个模型互相审阅对方输出提意见再让目标模型修改循环若干轮。9 模型议会是规模比较大的配置。成员一多角色划分可以更细理论上能覆盖更完整的业务链路。但也正因为成员多协调成本、错误传播和上下文污染都会被放大。这个架构的难点不是“怎么让模型说话”而是“怎么让多个模型的输出在同一个流程里可靠地衔接”。1.2 为什么是金融通讯稿金融通讯稿是一个很典型的“适合但没那么简单”的场景。适合是因为金融文本结构化程度高有固定的信息单元比如指数涨跌幅、公司财报数据、宏观指标、风险提示输出模板相对明确。而且金融分析需要多角度交叉验证宏观、行业、公司、风险几个视角天然可以拆给不同模型去做符合议会模式的直觉。不适合也有充分理由。金融资讯对事实准确性要求极高一个数字错误可能造成完全错误的解读时效性又强从数据更新到稿件发布之间的时间窗口很短更麻烦的是最终输出有被读者当成投资建议的风险。这些问题叠加在一起会让 9 模型议会里的每一个薄弱环节都被放大。1.3 9 个模型不等于 9 倍能力这是本文想强调的核心认知。很多团队搭建多模型系统时默认“模型多 质量高”。实际恰恰相反模型数量带来的是系统复杂度提升错误面也随之扩大。9 个模型中任何一个输出不可靠后续环节都会继承这个错误摘要员漏掉关键数据分析员就会在错误数据上继续推演分析员引用了过期信息核查员如果没识别出来主编就会把错误结论写进终稿。也就是说9 模型议会的能力上限取决于组织流程的质量而不是模型数量。本文后面拆解的所有故障几乎都源于这个基本矛盾。2. 九个角色怎么分工2.1 角色清单与职责一个可运行的 9 模型议会需要先定义清楚每个模型的角色边界。下面是我在示例中采用的角色拆分角色主要职责关键输入关键输出news_summarizer资讯压缩、去重、标注来源原始新闻列表结构化摘要条目macro_analyst宏观趋势与市场情绪分析摘要条目宏观判断、风险点industry_analyst行业景气度与估值分析摘要条目行业观点data_checker数字一致性检查分析结论数字冲突清单fact_checker事实溯源与可疑点识别分析结论可疑断言列表view_generator形成投资逻辑与观点通过校验的分析观点草稿risk_reviewer合规与风险提示检查观点草稿合规意见copy_editor改写为订阅者友好语气合规后的草稿优化文案editor_in_chief汇总、排序、最终定稿所有中间结果最终通讯稿注意这里的角色是“逻辑角色”不一定必须对应 9 个不同厂商的模型。你可以用同一个模型的不同 prompt 扮演多个角色也可以真的让 9 个不同模型各干各的。角色拆分越细每个环节的 prompt 越容易控制但流程越复杂衔接处越容易出问题。2.2 数据流与编排方式整个流水线大致如下定时任务拉取原始资讯可以是 RSS、API 或爬虫抓取结果。调用 news_summarizer 把原始资讯转换成结构化摘要条目。macro_analyst 和 industry_analyst 并行分析摘要得到两个分析视角。data_checker 和 fact_checker 对分析结果做校验。校验通过后view_generator 生成观点草稿。risk_reviewer 检查合规风险。copy_editor 润色文案。editor_in_chief 汇总所有中间结果输出终稿。编排方式上宏观分析和行业分析可以并行其他环节大多是串行。串行环节越多整体延迟越高并行环节越多资源竞争和上下文拼接越复杂。这里的每一步都是潜在故障点上游输出格式不对下游直接崩溃一个模型超时整条流水线卡住。2.3 为什么容易“坏”一句话总结多环节串行流程会把单点错误放大成系统性错误。单一模型生成文本时出错影响范围相对可控。但在议会模式里模型 A 的输出是模型 B 的输入模型 B 的输出又成为模型 C 的审查对象。任何一环的错误都会像滚雪球一样传递下去。再加上不同模型的能力、偏好、参数设置不一致很多问题在单一模型测试时根本不会出现。3. 环境准备与版本说明3.1 运行环境我使用的参考环境如下你的实际情况可能不同重点看配置思路操作系统Linux / macOS / Windows 均可Python3.10 或更高版本依赖openai SDK、anthropic SDK、requests具体按接入的模型厂商 SDK 安装运行方式命令行脚本或定时任务均可版本相关说明不同厂商 API 的版本变化很快本文示例中的模型名只作为展示运行时请以你账号实际可用的模型名为准。不要照搬模型名到生产环境尤其是 OpenAI、Anthropic、本地 Ollama 的模型列表经常更新。3.2 模型接入方式接入方式有三种主流选择云端商业 API如 OpenAI、Anthropic、通义千问等响应质量高但按 token 计费。本地开源模型如通过 Ollama、vLLM 部署 Qwen、Llama、DeepSeek 等数据不出内网但需要 GPU 资源。混合模式部分角色用云端模型部分角色用本地模型。混合模式最常见也最容易出问题。不同模型的响应格式、超时时间、速率限制都不同统一封装层必须处理这些差异。另外要明确不是所有模型都必须部署在同一台电脑上。很多人在问“ComfyUI 与 LLM 是否必须在同一台电脑上”其实这取决于工作流的编排方式。在我们这个场景里九个模型分布在多个 API 和本地推理服务上是常态跨机调用带来的超时、限流、鉴权和上下文传输问题远比模型本身的问题更频繁。3.3 项目结构建议按下面结构组织代码llm_council/ ├── main.py # 入口与定时任务 ├── callers.py # 统一模型调用封装 ├── roles.py # 9 个角色定义 ├── pipeline.py # 流水线编排 ├── output/ # 结果输出目录 └── prompts/ # 各角色提示词文件把角色定义、调用封装、流程编排拆开后续排查问题时能快速定位是哪一层出了问题。4. 最小可行的多模型议会实现4.1 统一模型调用层为了让 9 个角色都不需要关心模型来源先写一个统一的调用封装。这是整个系统能跑起来的基础。# 文件路径llm_council/callers.py import os import re import json from openai import OpenAI def call_model( provider: str, model: str, system_prompt: str, user_prompt: str, max_tokens: int 1024, temperature: float 0.3, ) - str: 统一模型调用入口。 provider 支持 openai / anthropic / ollama。 实际使用前请先安装对应 SDK并按你的版本调整参数。 这里给的是最简示例线上建议增加超时、重试和日志。 messages [ {role: system, content: system_prompt}, {role: user, content: user_prompt}, ] if provider openai: client OpenAI( api_keyos.getenv(OPENAI_API_KEY), base_urlos.getenv(OPENAI_BASE_URL), ) resp client.chat.completions.create( modelmodel, messagesmessages, max_tokensmax_tokens, temperaturetemperature, ) return resp.choices[0].message.content if provider anthropic: from anthropic import Anthropic client Anthropic(api_keyos.getenv(ANTHROPIC_API_KEY)) resp client.messages.create( modelmodel, max_tokensmax_tokens, temperaturetemperature, systemsystem_prompt, messages[{role: user, content: user_prompt}], ) return resp.content[0].text if provider ollama: import requests prompt fsystem:\n{system_prompt}\n\nuser:\n{user_prompt} resp requests.post( http://localhost:11434/api/generate, json{model: model, prompt: prompt, stream: False}, timeout180, ) return resp.json()[response] raise ValueError(f不支持的 provider: {provider})实际项目中这个封装层还需要处理超时重试、速率限制、token 截断、调用日志。线上的失败场景往往发生在这些边界里。4.2 定义 9 个角色角色定义可以放在单独文件里方便统一管理 prompt 和模型映射。# 文件路径llm_council/roles.py ROLES { news_summarizer: { provider: openai, model: gpt-4o-mini, system: 你负责把原始新闻压缩成结构化条目每条必须包含标题、日期、核心事实、来源链接。不要添加原文没有的信息。, }, macro_analyst: { provider: anthropic, model: claude-3-5-sonnet, system: 你负责宏观环境分析输出格式为 JSON包含 trend、evidence、risk 三个字段。所有判断必须基于输入摘要不允许猜测数据。, }, industry_analyst: { provider: openai, model: gpt-4o, system: 你负责行业景气度分析输出格式为 JSON包含 outlook、valuation、risk 三个字段。注意与宏观分析相互独立。, }, data_checker: { provider: openai, model: gpt-4o-mini, system: 你负责检查所有数字的一致性。输入中如果出现互相矛盾的数字必须列出冲突项并说明哪个数字更可信、理由是什么。输出格式为 JSON。, }, fact_checker: { provider: anthropic, model: claude-3-5-sonnet, system: 你负责事实核查。对每条关键断言判断是否能在输入材料中找到依据存在可疑信息时必须标记出来。, }, view_generator: { provider: openai, model: gpt-4o, system: 你负责根据通过校验的分析结果生成观点草稿。观点必须与数据一致不能编造未出现的逻辑。, }, risk_reviewer: { provider: anthropic, model: claude-3-5-sonnet, system: 你负责合规审查。检查文本是否可能被理解为投资建议缺少风险提示时给出修改建议。, }, copy_editor: { provider: openai, model: gpt-4o-mini, system: 你负责把草稿改写成订阅者友好语气段落短一些、专有名词保留、不改变数据事实。, }, editor_in_chief: { provider: anthropic, model: claude-3-5-sonnet, system: 你是主编。汇总所有中间结果输出一篇结构完整、含风险提示的金融通讯稿。不要复制审查意见只输出最终内容。, }, }这里的模型名只是示例。实际使用时你应该根据成本、速度和效果选择合适的模型并把模型名放到配置中心或环境变量里而不是写死在代码中。4.3 流水线主流程主流程的关键点有三个并行执行、格式解析、失败降级。下面是一个简化版实现重点展示多角色如何串联。# 文件路径llm_council/pipeline.py import json import re from concurrent.futures import ThreadPoolExecutor from callers import call_model from roles import ROLES def safe_parse_json(raw: str): 解析模型输出的 JSON兼容多余文字和代码块包裹。 if not raw: return None # 去掉 markdown 代码块 cleaned re.sub(r^(?:json)?|$, , raw.strip(), flagsre.MULTILINE) try: return json.loads(cleaned) except json.JSONDecodeError: # 尝试截取第一个大括号到最后一个大括号 start cleaned.find({) end cleaned.rfind(}) if start ! -1 and end ! -1 and end start: try: return json.loads(cleaned[start : end 1]) except json.JSONDecodeError: return None return None def run_one(role_name: str, user_prompt: str) - str: cfg ROLES[role_name] return call_model( providercfg[provider], modelcfg[model], system_promptcfg[system], user_promptuser_prompt, ) def run_pipeline(raw_news: str) - str: # 1. 摘要 summary run_one(news_summarizer, f请压缩以下资讯\n{raw_news}) # 2. 宏观 行业 并行分析 with ThreadPoolExecutor(max_workers2) as pool: macro_future pool.submit(run_one, macro_analyst, summary) industry_future pool.submit(run_one, industry_analyst, summary) macro_text macro_future.result() industry_text industry_future.result() # 3. 数据校验并尝试解析 check_text run_one(data_checker, f宏观结论\n{macro_text}\n\n行业结论\n{industry_text}) check_result safe_parse_json(check_text) # 4. 如果数字冲突严重可回退到 fact_checker 或直接终止 if check_result is None: return pipeline failed: data_checker 输出无法解析为 JSON # 5. 观点生成 view run_one(view_generator, f通过校验的分析结果\n{json.dumps(check_result, ensure_asciiFalse)}) # 6. 风险审查 risk_text run_one(risk_reviewer, view) risk_result safe_parse_json(risk_text) # 7. 润色 edited run_one(copy_editor, view) # 8. 主编终稿 final run_one( editor_in_chief, f请基于以下内容输出最终通讯稿\n\n{edited}\n\n风险审查建议\n{risk_text}, ) return final这段代码的核心价值在于把并行、解析、失败判断这几个最容易“坏”的点单独露出来。后面排查问题时你会发现在这些位置补逻辑比换模型更有效。4.4 预期输出与效果跑通后的输出应该是一篇结构完整的金融通讯稿包含市场回顾、行业观察、风险提示和免责声明。但第一次能跑通只是起点距离“稳定可用”还有很长的路。你在执行时大概率会遇到某一步输出不是合法 JSON、某个模型超时、某个模型把审查建议写进了正文。5. What breaks九模型议会的高频故障拆解5.1 数字不一致与事实幻觉金融场景里最致命的问题是不同模型输出互相矛盾的数据而且每个模型都“信心满满”。比如宏观分析师引用某个指数涨跌幅行业分析师引用了同一指数但数值不同又比如一个模型说“公司营收同比增长 15%”另一个模型说“同比下降 2%”中间没有任何一个环节能直接判断哪个正确。根本原因有几个模型训练数据截止时间不同对同一事件的记忆有差异。模型会把上一轮的输出当成事实即使上一轮是错的。多数模型没有实时数据源所谓“实时”依赖于输入材料是否完整准确。解决方案不能依赖“再找个模型来投票”。更可靠的是让模型在输出数字时附上来源标记比如“根据输入材料 3 的财报数据营收同比增长 15%”。然后再用一套硬编码规则去核对输入材料里是否存在对应数字。数字一致性检查应该优先用代码去比对而不是让另一个模型去猜。5.2 互相否定与无限修订多模型议会里常见的画面是编辑模型写完初稿审查模型挑出 5 个问题写作模型修改后另一个审查模型又挑出 4 个新问题改到第三轮时内容已经偏离原意甚至把正确答案改成了错误答案。这就是模型之间的“无限修订”。原因在于审查模型没有被限制修改范围。它面对任何文本都能找出可以调整的地方因为语言表达本身没有绝对最优。每次修改都在消耗 token也都在增加语义漂移的风险。解决思路是给修订轮次设硬上限比如最多两轮。同时把审查模型的职责从“自由点评”改成“只列出必须修改项”。必须修改项包括数字错误、来源缺失、合规风险。措辞风格类建议直接丢弃不让它进入修改循环。5.3 上下文污染与角色串音上下文污染是指某个模型错误地把其他模型的中间过程当成了事实导致最终结论扭曲。典型现象是主编的输出里出现了“数据检查员认为这里可能有误”“事实核查员对某个数字存疑”这样的审查过程语言而不是干净的结论。原因也很明显我们把太多中间过程拼接进了同一份 prompt。模型无法区分哪些是“已确认事实”哪些是“待核查的判断”。在长上下文中早期指令还会被后续内容稀释模型更容易被靠近输出的那部分内容带偏。解决办法是角色隔离。每个角色只拿到它完成任务所必需的最小上下文。比如主编需要的是“通过校验的最终观点 合规意见”而不是把 9 个模型的原始输出全部塞给它。中间结果可以做结构化落盘让每个环节只读取自己需要的字段。5.4 输出格式漂移与解析失败多模型并存的系统里最头疼的故障不是模型答错而是模型没有按约定格式输出。OpenAI 返回的是纯文本Anthropic 返回 content 数组本地 Ollama 返回的字段名又不一样。更常见的是某个模型在输出 JSON 前后加了一段说明文字或者中途截断导致 json.loads 直接报错。这类问题看起来是小事但一旦加入 9 个模型和多个串行环节概率会被放大。一个环节解析失败后续所有环节都停摆。应对方案是把解析逻辑收敛到统一函数里并做好容错去掉 markdown 代码块标记。提取第一个{到最后一个}之间的内容。如果解析失败让调用层记录原始输出方便排查。必要时做一次带错误信息的重试但重试次数不宜超过一次。5.5 成本与延迟失控9 个模型 × 多轮迭代 × 大上下文token 成本上涨速度远超预期。一次完整流水线可能消耗几十万 token如果中途还有修订循环成本还会翻倍。延迟同样不可控串行环节多任何一个模型慢都会拖垮整体时效而金融资讯恰恰对时效敏感。这里想多说一句部署方式。很多人在问“ComfyUI 与 LLM 是否必须在同一台电脑上”类似的问题也会出现在多模型议会里9 个模型非要部署在同一台机器上吗答案是否定的。真正的常态是混合部署本地模型处理敏感数据云端模型处理高质量生成。但这会引入新的故障面跨机调用超时、API 限流、鉴权失败、上下文传输延迟。部署拓扑越复杂故障排查越难。成本控制的核心不是省每个模型的单次调用而是避免无意义的重复调用先让小模型做初筛只有存在争议的内容才交给大模型。给每个角色设置独立的 token 上限。监控每次调用的输入、输出 token 数和耗时。5.6 合规安全边界模糊金融通讯稿天然处于合规敏感区。9 个模型并不理解不同国家和地区的监管要求它们只会按照 prompt 里的指令输出文本。如果没有额外保护措施最终稿可能读起来像一份“投资建议”这是非常危险的结果。在这套系统里合规不能交给模型自觉。风险审查模型只能作为辅助工具真正的合规底线要靠流程保证输出前强制添加免责声明明确文案定位是“信息摘要”而非“投资建议”发布前必须经过人工审核。涉及未公开的市场敏感数据时也要谨慎决定是否调用第三方云端 API。6. 常见问题清单与排查思路6.1 高频报错对照表问题现象常见原因解决思路json.loads 报 JSONDecodeError模型输出里有多余文字或代码块使用统一 safe_parse_json去掉代码块并截取 JSON 片段同一指数两个模型数值不同模型训练数据截止时间不同强制模型标注来源序号用代码核对原始材料审查循环改不完审查模型做自由点评限制审查范围只允许列必须修改项设置最多两轮重写主编输出里混入审查意见上下文污染主编只接收通过校验的最终观点不接收中间过程某个模型调用超时跨机部署或 API 限流增加超时和重试并对串行关键路径做降级上一轮正常下一轮完全失败模型端版本变化或 prompt 漂移固定模型版本日志记录每次调用参数成本突然翻倍修订循环或上下文过长设置每轮调用 token 上限减少重写轮次6.2 系统化排查步骤遇到问题不要一上来就换模型。按下面顺序排查先复现用同样的输入再跑一次确认是不是随机性问题。看日志检查是哪个环节失败找到第一条报错记录而不是只看最终结果。隔离变量把失败环节的输入输出单独拿出来用单个模型直接调用判断是模型问题还是上游数据问题。检查 prompt确认是否要求了 JSON 输出、是否加入了来源标记要求。检查上下文看看传给该角色的 prompt 里有没有多余的中间过程内容。看成本统计每个角色的 token 消耗找出成本异常点。6.3 如何避免问题再次出现所有模型调用必须记录输入输出这是排查问题的前提。每个角色只接收最小必要上下文减少污染概率。输出格式统一用 JSON并让所有模型遵循同一个 schema。对重写、审查循环设置硬性上限不能无限迭代。每次修改 prompt 后用固定的小样本集做回归测试。7. 从“会坏”到“稳定”工程化改进7.1 先小后大、先主后次如果你是第一次搭多模型协作系统不建议直接上 9 个模型。更合理的路径是先做一个两模型 MVP一个负责起草一个负责挑错再配合一段硬编码数据校验脚本。跑通之后再根据实际短板逐步拆角色、加模型。这样你能清楚知道每个新增模型到底解决了什么问题而不是凭空增加故障点。7.2 用规则引擎做硬校验模型擅长处理语义不擅长精确计算。数字一致性、来源是否存在于输入材料中、免责声明是否包含这些任务应该交给规则引擎或脚本去完成。比如用正则提取“同比增长 xx%”中的数字然后与输入数据源对比。规则引擎不能解决所有幻觉问题但能把最危险的数字错误拦截在发布之前。7.3 上下文隔离与版本追溯给每轮流水线生成一个唯一 ID所有中间结果都带上这个 ID 落盘。这样你可以回溯到任何一个模型的输入是什么、输出是什么。上下文隔离方面每个角色消费的必须是上一环节的结构化输出字段而不是完整对话历史。主编看到的应该是干净的结论而不是一堆待办意见。7.4 成本预算与观测指标为每个角色单独设置成本上限并记录以下指标单次调用的输入 token 数、输出 token 数调用耗时重试次数是否解析失败该角色是否存在修改轮次这些指标能帮你快速判断系统是“模型能力不足”还是“流程设计不合理”。成本预算要提前设不要在月底看账单时才后悔。7.5 人工审核与合规底线不管系统多成熟金融通讯稿发布前必须有人工审核环节。这不是“对模型的信任问题”而是责任边界问题模型出错责任最终在发布方。可以让人工只审核最终稿但审核模板必须包含所有数字是否与原始材料一致、是否存在投资建议表述、是否包含免责声明、是否有明显逻辑断层。7.6 评估集与回归测试为内容生成系统准备一个固定评估集非常重要。收集 10 到 20 条历史资讯记录下每个环节的理想输出作为回归基线。每次修改 prompt、更换模型、调整编排方式后都用这套样本跑一遍对比输出质量。没有评估集的系统永远无法判断一次修改是变好了还是变坏了。8. 总结与下一步9 模型议会这套架构真正难的不是模型数量而是流程控制。每个模型单拎出来都可能很强但把它们串成流水线后会遇到数字不一致、互相否定、上下文污染、格式漂移、成本失控、合规模糊这六类问题。这些问题几乎没有一个是靠换更强模型能解决的它们需要从流程设计、上下文隔离、硬编码校验、人为审核这些层面去解决。如果让我重做一次这套系统我不会再一上来就组 9 个模型。我会先跑一个“起草模型 挑错模型 数字校验脚本”的三角组合确认它能稳定产出 7 分的内容后再把耗时最多的环节拆成独立角色。多模型议会是把双刃剑模型越多、故障面越大。真正让系统稳定的永远是清晰的流程、可控的上下文、可观测的日志和那个最后按下发布键的人。如果你也在跑类似的 Multi-Agent 或 LLM Council 项目欢迎在评论区聊聊你遇到过的模型互相否定问题。