From 9dda9308680c9d3edd912d6d987f755e8d964a02 Mon Sep 17 00:00:00 2001 From: xyz Date: Wed, 7 Jan 2026 12:59:56 +0800 Subject: [PATCH] feat: Implement Multi-Model Council mode with 3-expert workflow --- README.md | 83 +++++++- __pycache__/app.cpython-313.pyc | Bin 21825 -> 22130 bytes __pycache__/config.cpython-313.pyc | Bin 937 -> 1881 bytes agents/__pycache__/base_agent.cpython-313.pyc | Bin 5005 -> 5344 bytes .../research_agent.cpython-313.pyc | Bin 2797 -> 2940 bytes agents/base_agent.py | 7 + agents/research_agent.py | 33 +-- app.py | 193 +++++++++--------- config.py | 33 ++- .../debate_manager.cpython-313.pyc | Bin 6029 -> 6308 bytes .../research_manager.cpython-313.pyc | Bin 4072 -> 4779 bytes orchestrator/debate_manager.py | 9 +- orchestrator/research_manager.py | 58 ++++-- 13 files changed, 271 insertions(+), 145 deletions(-) diff --git a/README.md b/README.md index 42331e8..c161857 100644 --- a/README.md +++ b/README.md @@ -1 +1,82 @@ -# multi-agent \ No newline at end of file +# Multi-Agent Decision Workshop & Deep Research + +这是一个基于多智能体(Multi-Agent)的决策辅助和深度研究系统。它包含两个核心模式: +1. **Deep Research Mode (Deep Research)**: 模仿 Gemini 研究模式,通过规划、执行、撰写三个阶段进行深度分析。 +2. **Debate Workshop (辩论工作坊)**: 让多个 AI 角色从不同视角辩论,帮助你做出更全面的决策。 + +## ✨ 功能特性 + +- **双模式切换**: 侧边栏一键切换 "Deep Research" 和 "Debate Workshop"。 +- **自定义模型角色**: + - 在 Deep Research 模式下,可以分别指定 `Planner` (规划者), `Researcher` (研究员), `Writer` (作家) 使用不同的 LLM。 +- **多模型支持**: 支持 OpenAI (GPT-4o), Anthropic (Claude 3.5), Gemini 等主流模型。 +- **交互式研究**: 生成研究计划后,用户可以介入修改,确保研究方向正确。 +- **流式输出**: 实时展示研究进度和辩论过程。 + +## 🛠️ 安装与使用 + +### 1. 克隆项目 + +```bash +git clone https://github.com/HomoDeusss/multi-agent.git +cd multi-agent +``` + +### 2. 安装依赖 + +确保你安装了 Python 3.8+。 + +```bash +pip install -r requirements.txt +``` + +### 3. 配置 API Key + +你可以通过以下两种方式配置 API Key: + +**方式 A: 创建 `.env` 文件 (推荐)** +复制 `.env.example` 为 `.env`,并填入你的 API Key。 + +```bash +cp .env.example .env +``` + +编辑 `.env` 文件: +```env +AIHUBMIX_API_KEY=your_api_key_here +``` + +**方式 B: 在 UI 中输入** +启动应用后,在侧边栏的 "设置" -> "API Key" 输入框中填入。 + +### 4. 启动应用 + +运行 Streamlit 应用: + +```bash +streamlit run app.py +``` + +会自动在浏览器打开 `http://localhost:8501`。 + +## 📖 使用指南 + +### 🧪 Deep Research Mode (深度研究模式) +1. 在侧边栏选择模式为 **"Deep Research"**。 +2. 在 "研究模型配置" 中,为 Planner, Researcher, Writer 选择合适的模型(推荐分别使用 GPT-4o, Gemini-1.5-pro, Claude-3.5-sonnet)。 +3. 输入你的**研究主题** (例如: "2025年量子计算商业化前景")。 +4. 点击 **"生成研究计划"**。 +5. 系统生成计划后,你可以直接在文本框中**修改计划步骤**。 +6. 点击 **"开始深度研究"**,观察 Agent 逐步执行研究任务。 +7. 下载最终生成的 Markdown 报告。 + +### 🎭 Debate Workshop (辩论工作坊) +1. 在侧边栏选择模式为 **"Debate Workshop"**。 +2. 输入**决策议题** (例如: "我是否应该辞职创业?")。 +3. 选择参与辩论的 **AI 角色** (如: CEO, 风险控制专家, 职业顾问)。 +4. 点击 **"开始辩论"**。 +5. 观看不同角色之间的唇枪舌战,最后生成综合决策建议。 + +## 📝 License + +[MIT License](LICENSE) \ No newline at end of file diff --git a/__pycache__/app.cpython-313.pyc b/__pycache__/app.cpython-313.pyc index af80baf3b6faecc674d22c4f5f43fd6ce2a3f917..e2ee526d6d0e925abca61c95b5b7c03231781633 100644 GIT binary patch delta 8905 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 TGYnhhww7hEU!$crs~q~JO=-sS%ops2shqK zfB#lK?avn#c1EgOIexvAE$}zhp@rpZpwPi|5sUFfM)jNtOT5T;)ro|_l~yqw4aI0A zi6U0$U|dwziB#lsjOuTf>2$Fq|F7g*zy))u=}@dpFB9mrAW3-v!%N)?{kp)(>)n!f z9^)dy_3pN{34=xmgYv2qigjf5jJ)8R-&^>%6! za5r_3=T4pZCNvAsY>`N+tC^Sirm$wRs*Y7almU^=t#gX%?xX~B&;}FLE0|C$K(Vvo zEX#}Fxs+4XksE-;0v0OcEi#@(uZ=V#-qpY&fpJa~AkacfWiWXrg}XM9~@ zEcHH-E8sdHtZ^kl$rPkjEYxkQSk4i?sPK}g4kug9VBZsjSNIzx%#?`@+LfZ6p2o*} zdgVJ~^I^B`)*Z9$FWdU|qr=aBIQ-^wv+e#}WA}mTJzFwC2hj$6dsyaGOc-MRS?3mQ zJlof+6}|cqxNSKHoW`d+MkQJ`wl^EkD*Ejo8&B_dV63=lHN(~0QMME1YQOiq*W;?q z^=93!`1SZ=Dty0x%SR- z5)UB_=u9XD*8kvGh4?OZgZQPPCTd5&h8)mf2aV;>Xd0){+ zquL0o`KPtOj5ZO|#$(4myQlX#;=lkal<|4rMD^7YMcl!>MLfZL!TiAj!Ggg; zdMrh}=}el!leHL6$W*bV7nJClH+cCPxIS=9c4QY} IkpjvB0Mc|m1poj5 diff --git a/agents/__pycache__/base_agent.cpython-313.pyc b/agents/__pycache__/base_agent.cpython-313.pyc index f3cf15b1f8d951a429abab213f9a2876a6d70a19..9378ab4ebf6ca5e63e2634381f32f348e30b9cc1 100644 GIT binary patch delta 711 zcmeBGf1t_xnU|M~0SLM-#%A8#$g9T0C^9*LDV1^F7x6;v;REydg87U1)7do{1vdvWzhlb3#a5JBQd*Rk%m}t0La+cSW+48Y1|+63 zM8oAm8G@J~N|4E5MpLM$22?mvjDZ2G+Q|>OMC(-;(wQ`wOBI2NU-WH%+CS~--pNlp zd!Ovx|9r}d=d)U#tyuVUUiZ@pz0a3#xW$^ApOTtW#T%NJotIypr{D|XYBJwq&qypz zEGa1}QUV%NqzxoA8H-ebR58f43PqAY@)j3Zb9`Q6Zt5)&r^Mn^$Mn=Z+Y&v5upUSU z$k`1HU)UIUgeRCxx1VUg!2LS6&P8sW8^Yos?sZ{}i^3Wgcr7YKi0 g08$^!CQlds%jh|Is)!AL0V5;#Ck7B%Bmqh8>ySR&=X$YNQ?9gnIm?3me`r) z6uJ{4GA+Gk{DN#v>*wEQJ`;`##OWth?W5B#=C`(Twy~92O4-?2vYTE;LpUODm+ahD zCefwsJnD8eVS>~I=}=Puu3#6I^bB|`8TQzv)tFrr#a5wK{%TfxNi%Xuy!f5en-c|K zYkEH|nb#DT^3PJA&KZKKNcgS>`q4pNhu?(-Q<#CYdAL_G*jy%KArBLMJr8Lw+nzob--+-<7NP%GJBM zs+htkSvT+%J5=svF+mn@>Sw(iI}Djn+s@H9n?Xb!!RT9wgs#@9Lv7~sK+sj+@w!r3 zCfEqt3LKAeNmW2Y5$y8@1Iq?uVVyRE$Zt4`2jD=R#0w0ACRJ@lBgw=(x`o`eOk0V_ z?Ln;=?P65b=Kdb{Th#RcqSv+PEfIu)#NaaUc!P4ED-Lm+0%~y(h&>9afhxlHEOUK` z@H*{}Kr`;L2bTkq4wsTOXb^^h zP!ZNFa3 z-|-?a?NYbxcpmdxSQ1Cds1qVg`6Qg~(XWr;T>n?|pwNzDE&inb^<>wF)<}NDk`@7M zs7ny3Fj5wUl6H2uKU58IL<01{LSm|ojU9b0^nil^+w6|Y%YDgPZI$%@(rnE#p{tF^ z#je6*3yxQ(A#_)^S)&f+>V$%|88||{LeKHB#e!kA<8O_61qTf#24_TE1tRftrZ6e- z)$GbP4uJds$Q|;1p?tD(ymD^dFU$>6`iwa+NMZcr${?#xP7jD)oF15@G=8#qym`KG zzgYc0Ht|XJicunyi()Fio9(|s;icj57NA*b8_VK!YUyEmbEw4}MHPrl1EFvr)t_?L zv|60azgNM@7r04=8>9>ztTs|B#fSMDRdf*(K>9hDGaS>dsGy7@(UHu@_wvtHaKe*i zAq?b@;2x7Q&YI7?JQyQJX^_$~*Ur~32wWHXNbt~@$}E2V{D8pqrThc0hgN*utbLe- z#6UHpZ>VkvUt#iM|0$R@Pg*h+TSz2nj*T7`i^x67sGoZswp8S?H^ee1aIeW3 cAOl^~v6z)t#C12HE5M#O;_)$ z>LlZ3JnkNrB{afTKG$M5$(s{fw* zZMObLz3vixzWMe8{={*8F}#K!9}|yw)(+XWz}hZAVY>)LYR(Ef_I3$MxnA6Hz*!*6 z#Ir9Guk>8Oq#*>#-D1P}#i)&9R=v(52ELY%*>7;AGkc2%`uN~!t8@Ix%9dC6oSP-D ze6zG+dzH)NmVL{D3Z7Q+bZjx2>$w}n5@y_RyxK!A*PDV`*KJIHy=1Lfjwu3a>Dz^C zm+E6^TQSjrhv%OcKD|&5WTe@V&KzJmfhaB7pAE~g@FPR9Xc4k26Y2w_KJ{3{CD3=6 zVm<^t`qtR1<)RDFuJi@vifT#sfcg70l!p;U1+pItc0?1!0F5yZD38#|r%FQ-ASeqd zOmZ6JVV;rp6QyZ@sk8wP1cEj(hQ(`pgEjM}#+mA#k8bjSfubSrbQrKmQ$Okorl{t-xteIqc*xs^z{qZq5rdb0-WLe0X}^k}ZZb zCJkZm-&U|A79@EADrN!iVD*zsIS3LBX$d8cehTaLHVZ1r*9$P)cDYy;t6BPn(n5C_W-T)E>FN zd3WuVP(zW`0$k!Y*2esP!yeP4#%egiG74p*p!gtFuX@G0nhvTr7ELgi zt3AHbkD}JFGSZkzf30rqIL0t*M2=K6+7%SpY#XtB&4aeDHSj%LP}OdKNnqZ%%otIb z2=j+gH_|)q((LKfTxtD_r$z){U#UOM>-5?5d$%#n<4j1Sk~w-1$-wp)FD+)P1)ABLcGe$cjkX2sl%P str: + """获取当前使用的模型名称""" + if hasattr(self.llm_client, "model"): + return self.llm_client.model + return "Unknown Model" def generate_response( self, diff --git a/agents/research_agent.py b/agents/research_agent.py index 9248196..d68d79b 100644 --- a/agents/research_agent.py +++ b/agents/research_agent.py @@ -1,4 +1,4 @@ -from typing import Generator, List, Dict +from typing import Generator from utils.llm_client import LLMClient import config @@ -11,25 +11,28 @@ class ResearchAgent: self.role_config = config.RESEARCH_MODEL_ROLES.get(role, {}) self.name = self.role_config.get("name", role.capitalize()) + @property + def model_name(self) -> str: + return self.llm_client.model + def _get_system_prompt(self, context: str = "") -> str: - if self.role == "planner": - return f"""You are a Senior Research Planner. -Your goal is to break down a complex user topic into a structured research plan. -You must create a clear, step-by-step plan that covers different angles of the topic. -Format your output as a Markdown list of steps. + if self.role == "expert_a": + return f"""You are Expert A, a Senior Analyst. +Your goal is to provide a deep, foundational analysis of the user's topic. +Structure your thinking clearly. Propose a solid initial framework or solution. Context: {context}""" - elif self.role == "researcher": - return f"""You are a Deep Researcher. -Your goal is to execute a specific research step and provide detailed, in-depth analysis. -Use your vast knowledge to provide specific facts, figures, and logical reasoning. -Do not be superficial. Go deep. + elif self.role == "expert_b": + return f"""You are Expert B, a Critical Reviewer. +Your goal is to find flaws, risks, and missed opportunities in Expert A's analysis. +Be constructive but rigorous. Don't just agree; add value by challenging assumptions. Context: {context}""" - elif self.role == "writer": - return f"""You are a Senior Report Writer. -Your goal is to synthesize multiple research findings into a cohesive, high-quality report. -The report should be well-structured, easy to read, and provide actionable insights. + elif self.role == "expert_c": + return f"""You are Expert C, a Senior Strategist. +Your goal is to synthesize the final output. +Combine the structural strength of Expert A with the critical insights of Expert B. +Produce a final, polished, comprehensive plan or report. Context: {context}""" else: diff --git a/app.py b/app.py index 223000a..3f5f2d5 100644 --- a/app.py +++ b/app.py @@ -66,23 +66,7 @@ st.markdown(""" DEFAULT_API_KEY = os.getenv("AIHUBMIX_API_KEY", "") # 支持的模型列表 -AVAILABLE_MODELS = { - "gpt-4o": "GPT-4o (推荐)", - "gpt-4o-mini": "GPT-4o Mini (快速)", - "gpt-4-turbo": "GPT-4 Turbo", - "gpt-3.5-turbo": "GPT-3.5 Turbo (经济)", - "claude-3-5-sonnet-20241022": "Claude 3.5 Sonnet", - "claude-3-opus-20240229": "Claude 3 Opus", - "claude-3-haiku-20240307": "Claude 3 Haiku (快速)", - "deepseek-chat": "DeepSeek Chat", - "deepseek-coder": "DeepSeek Coder", - "gemini-1.5-pro": "Gemini 1.5 Pro", - "gemini-1.5-flash": "Gemini 1.5 Flash", - "qwen-turbo": "通义千问 Turbo", - "qwen-plus": "通义千问 Plus", - "glm-4": "智谱 GLM-4", - "moonshot-v1-8k": "Moonshot (月之暗面)", -} +from config import AVAILABLE_MODELS, RESEARCH_MODEL_ROLES # 决策类型 DECISION_TYPES = { @@ -221,17 +205,17 @@ with st.sidebar: # ==================== 主界面逻辑 ==================== if mode == "Deep Research": - st.title("🧪 Deep Research Mode") - st.markdown("*深度研究模式:规划 -> 研究 -> 报告*") + st.title("🧪 Multi-Model Council") + st.markdown("*多模型智囊团:分析 (Expert A) -> 批判 (Expert B) -> 决策 (Expert C)*") # Input - research_topic = st.text_area("研究主题", placeholder="请输入你想深入研究的主题...", height=100) + research_topic = st.text_area("研究/决策主题", placeholder="请输入你想深入研究或决策的主题...", height=100) research_context = st.text_area("补充背景 (可选)", placeholder="任何额外的背景信息...", height=80) - generate_plan_btn = st.button("📝 生成研究计划", type="primary", disabled=not research_topic) + start_research_btn = st.button("🚀 开始多模型协作", type="primary", disabled=not research_topic) - if generate_plan_btn and research_topic: - st.session_state.research_started = False + if start_research_btn and research_topic: + st.session_state.research_started = True st.session_state.research_output = "" st.session_state.research_steps_output = [] @@ -239,84 +223,70 @@ if mode == "Deep Research": config_obj = ResearchConfig( topic=research_topic, context=research_context, - planner_model=roles_config['planner'], - researcher_model=roles_config['researcher'], - writer_model=roles_config['writer'] + expert_a_model=roles_config['expert_a'], + expert_b_model=roles_config['expert_b'], + expert_c_model=roles_config['expert_c'] ) manager.create_agents(config_obj) - with st.spinner("正在制定研究计划..."): - plan_text = "" - for chunk in manager.generate_plan(research_topic, research_context): - plan_text += chunk - st.session_state.research_plan = plan_text - - # Plan Review & Edit - if st.session_state.research_plan: st.divider() - st.subheader("📋 研究计划确认") + st.subheader("🧠 智囊团思考中...") - edited_plan = st.text_area("请审查并编辑计划 (Markdown格式)", value=st.session_state.research_plan, height=300) - st.session_state.research_plan = edited_plan + # Collaborative Execution + current_step_name = "" + current_step_content = "" + step_placeholder = st.empty() + status_container = st.status("正在初始化...", expanded=True) - start_research_btn = st.button("🚀 开始深度研究", type="primary") - - if start_research_btn: - st.session_state.research_started = True - st.session_state.research_steps_output = [] # Reset steps + try: + for event in manager.collaborate(research_topic, research_context): + if event["type"] == "step_start": + current_step_name = event["step"] + current_agent = event["agent"] + current_model = event["model"] + status_container.update(label=f"🔄 {current_step_name} [{current_agent}] ({current_model})", state="running") + step_placeholder = st.empty() + current_step_content = "" + + elif event["type"] == "content": + current_step_content += event["content"] + step_placeholder.markdown(f"**Thinking...**\n\n{current_step_content}") + + elif event["type"] == "step_end": + # Save step result + st.session_state.research_steps_output.append({ + "step": current_step_name, + "output": event["output"] + }) + status_container.write(f"### {current_step_name}\n{event['output']}") + status_container.update(label=f"✅ {current_step_name} 完成", state="running") - # Parse plan lines to get steps (simple heuristic: lines starting with - or 1.) - steps = [line.strip() for line in edited_plan.split('\n') if line.strip().startswith(('-', '*', '1.', '2.', '3.', '4.', '5.'))] - if not steps: - steps = [edited_plan] # Fallback if no list format + status_container.update(label="✅ 所有步骤完成", state="complete", expanded=False) - manager = ResearchManager(api_key=api_key) - config_obj = ResearchConfig( - topic=research_topic, - context=research_context, - planner_model=roles_config['planner'], - researcher_model=roles_config['researcher'], - writer_model=roles_config['writer'] - ) - manager.create_agents(config_obj) - - # Execute Steps - previous_findings = "" - st.divider() - st.subheader("🔍 研究进行中...") - - step_progress = st.container() - - for i, step in enumerate(steps): - with step_progress: - with st.status(f"正在研究: {step}", expanded=True): - findings_text = "" - placeholder = st.empty() - for chunk in manager.execute_step(step, previous_findings): - findings_text += chunk - placeholder.markdown(findings_text) - - st.session_state.research_steps_output.append(f"### {step}\n{findings_text}") - previous_findings += f"\n\nFinding for '{step}':\n{findings_text}" - - # Final Report - st.divider() - st.subheader("📄 最终报告生成中...") - report_placeholder = st.empty() - final_report = "" - for chunk in manager.generate_report(research_topic, previous_findings): - final_report += chunk - report_placeholder.markdown(final_report) + # The last step output is the final plan + if st.session_state.research_steps_output: + final_plan = st.session_state.research_steps_output[-1]["output"] + st.session_state.research_output = final_plan + st.success("✅ 综合方案生成完毕") - st.session_state.research_output = final_report - st.success("✅ 研究完成") - + except Exception as e: + st.error(f"发生错误: {str(e)}") + import traceback + st.code(traceback.format_exc()) + # Show Final Report if available if st.session_state.research_output: st.divider() - st.subheader("📄 最终研究报告") + st.subheader("📄 最终综合方案") st.markdown(st.session_state.research_output) - st.download_button("📥 下载报告", st.session_state.research_output, "research_report.md") + st.download_button("📥 下载方案", st.session_state.research_output, "comprehensive_plan.md") + + # Show breakdown history + with st.expander("查看完整思考过程"): + for step in st.session_state.research_steps_output: + st.markdown(f"### {step['step']}") + st.markdown(step['output']) + st.divider() elif mode == "Debate Workshop": @@ -386,6 +356,23 @@ elif mode == "Debate Workshop": ): selected_agents.append(agent_id) + # 自定义模型配置 (Advanced) + agent_model_map = {} + with st.expander("🛠️ 为每个角色指定模型 (可选)"): + for agent_id in selected_agents: + # Find agent name + agent_name = next((a['name'] for a in all_agents if a['id'] == agent_id), agent_id) + if agent_id in st.session_state.custom_agents: + agent_name = st.session_state.custom_agents[agent_id]['name'] + + agent_model = st.selectbox( + f"{agent_name} 的模型", + options=list(AVAILABLE_MODELS.keys()), + index=list(AVAILABLE_MODELS.keys()).index(model) if model in AVAILABLE_MODELS else 0, + key=f"model_for_{agent_id}" + ) + agent_model_map[agent_id] = agent_model + # 角色数量提示 if len(selected_agents) < 2: st.warning("请至少选择 2 个角色") @@ -434,22 +421,25 @@ elif mode == "Debate Workshop": agent_profiles.AGENT_PROFILES.update(st.session_state.custom_agents) try: - # 初始化客户端和管理器 - provider_val = "aihubmix" # Debate mode default to aihubmix or logic needs to be robust. - # Note: in sidebar "model" and "api_key" were set. "provider" variable is now inside the Sidebar logic block if mode==Debate. - # But wait, I removed the "Advanced Settings" block from the global scope and put it into sub-scope? - # Let's check my sidebar logic above. - - # Refactoring check: - # I removed the provider selection logic from the global sidebar. I should probably add it back or assume a default. - # In the original code, provider selection was in "Advanced Settings". - + # 初始化默认客户端 llm_client = LLMClient( provider="aihubmix", api_key=api_key, base_url="https://aihubmix.com/v1", model=model ) + + # 初始化特定角色的客户端 + agent_clients = {} + for ag_id, ag_model in agent_model_map.items(): + if ag_model != model: # Only create new client if different from default + agent_clients[ag_id] = LLMClient( + provider="aihubmix", + api_key=api_key, + base_url="https://aihubmix.com/v1", + model=ag_model + ) + debate_manager = DebateManager(llm_client) # 配置辩论 @@ -457,7 +447,8 @@ elif mode == "Debate Workshop": topic=topic, context=context, agent_ids=selected_agents, - max_rounds=max_rounds + max_rounds=max_rounds, + agent_clients=agent_clients ) debate_manager.setup_debate(debate_config) @@ -474,7 +465,9 @@ elif mode == "Debate Workshop": ) elif event["type"] == "speech_start": - st.markdown(f"**{event['emoji']} {event['agent_name']}**") + # 显示模型名称 + model_display = f" ({event.get('model_name', 'Unknown')})" + st.markdown(f"**{event['emoji']} {event['agent_name']}**{model_display}", unsafe_allow_html=True) speech_placeholder = st.empty() current_content = "" diff --git a/config.py b/config.py index abc657c..15db778 100644 --- a/config.py +++ b/config.py @@ -18,26 +18,41 @@ AIHUBMIX_BASE_URL = "https://aihubmix.com/v1" DEFAULT_MODEL = "gpt-4o" # AIHubMix 支持的模型 LLM_PROVIDER = "aihubmix" # 默认使用 AIHubMix +# 支持的模型列表 +AVAILABLE_MODELS = { + "gpt-4o": "GPT-4o (OpenAI)", + "gpt-4o-mini": "GPT-4o Mini (OpenAI)", + "claude-3-5-sonnet-20241022": "Claude 3.5 Sonnet (Anthropic)", + "claude-3-opus-20240229": "Claude 3 Opus (Anthropic)", + "gemini-1.5-pro": "Gemini 1.5 Pro (Google)", + "gemini-1.5-flash": "Gemini 1.5 Flash (Google)", + "deepseek-chat": "DeepSeek V3 (DeepSeek)", + "deepseek-reasoner": "DeepSeek R1 (DeepSeek)", + "llama-3.3-70b-instruct": "Llama 3.3 70B (Meta)", + "qwen-2.5-72b-instruct": "Qwen 2.5 72B (Alibaba)", + "mistral-large-latest": "Mistral Large (Mistral)", +} + # 辩论配置 MAX_DEBATE_ROUNDS = 3 # 最大辩论轮数 MAX_AGENTS = 6 # 最大参与 Agent 数量 # 研究模式模型角色配置 RESEARCH_MODEL_ROLES = { - "planner": { - "name": "Planner", + "expert_a": { + "name": "Expert A (Analyst)", "default_model": "gpt-4o", - "description": "负责拆解问题,制定研究计划" + "description": "负责初步分析,提出核心观点和方案" }, - "researcher": { - "name": "Researcher", + "expert_b": { + "name": "Expert B (Critique)", "default_model": "gemini-1.5-pro", - "description": "负责执行具体的研究步骤,深度分析" + "description": "负责批判性分析,指出潜在问题和漏洞" }, - "writer": { - "name": "Writer", + "expert_c": { + "name": "Expert C (Synthesizer)", "default_model": "claude-3-5-sonnet-20241022", - "description": "负责汇总信息,撰写最终报告" + "description": "负责综合各方观点,生成最终决策方案" } } diff --git a/orchestrator/__pycache__/debate_manager.cpython-313.pyc b/orchestrator/__pycache__/debate_manager.cpython-313.pyc index 97dd40182d8bb52817791069e83480f3ef80c55f..85373e6a5647e7bcade5c1ae6be7bc4266497a05 100644 GIT binary patch delta 1940 zcmZ`)OKclO7@o0r*RQpmc>SnN>)5WHc-u5_gVPGNB!niWYSbWNxh*usIQBL+O`K?K z15%4pQ3T}x1cr(T2?SDkUZCPYk$RwjIB<$p)RB2U;Sq_nk*Nz*psn37bP{)W?v zZqH3=MpWQ5aJf3Z;Bi_hhq`YOY$!XLHpmDNWX@S|yt$ZpK(+ zFVchbFT*yxgL;hJ&P#%?5eS~#zmU8v1W5-?8RxN&{$fm-fIp^MXotr`w+ZfGkj?of zqyt7FfXF)d=rcl>#03%hy6`6s(;4v}+)WG8!}e~*su&oALL@}bN--Rzze;_$gWi-9 zhG9&5O=+=)P}x8evUk30JZxuV-ZD6C=!d1O#akW9^&*tPND?|oBHVE(-zz_gvOd#@gOvoto z^3mG#be08Vl~ob4A3jM$%g>TRMP?HoO1GdDQSGEw=JCW-uT+Vao6$RlwMqJsbE5>8 ztNcWNaQ^IzbS+2v>XE+2rp^_+$>&-{rgm4;P2X+%?x`af1!B$TG4e+n{>X}GR2 zAG9A-Adyte?_PlWQcxmJqN`42*%_%jBTME;t9HfdLDyF*&q1&a^B_+z zZl^RXG%{fKZ{6KPpHw__P>JDBsHP0D;`O<5F3MMfch*;PPu;tko^TK1Pw54Ba?7*8 z6kEv;-fsCB@}AcM6_2UH7;CBr3hq4In^*w)0Biue0Ym@+U=zRr0MNEoil2tjMt})` z0Kjtq4=UbUKmZVCZ{;F`*U;~Re?_p0^lOi`=oUM_=x!p`olpJ^T{D4VY9ktNL^>Pc z*orNK#n@GUxGBP*DWO25Y2rgO+7N15_|S@c9Zk+`%E+oLBp2O_)g@(f-4eZK=ZrE9 zd!MlLUk50owi7?Cd0*<`R^k`uLC&>>f6|@bk8a-0Qn@y*4Uj4NkS{qP;f^L^ux3OC eThjnj7aLt?BLxJ?ZGm##l``Occop%E@A)6l+M}WX delta 1674 zcmZ9MO>7%Q6o6;e-i?3O|4wX|#MwBulMRj?@STti5zCVc)gcEQqxm@#(p!=Z?LA@g#j0Pt;k6JR_-A`WH?aZzI)4PYVn5uHZlF zVb;6gQazKH((*)0EL~Y%UQUdbE*A@>OioKIR0`#}bY>=BET=D*$h4wyeo2a3#OSp?@UtP-8gHTbJwlXW;E-3UH*=y+o zH`a;wXuTE^&w#2KGhe`O(s9cKuFy40Ry@jfCDFL8frhM) zJz`3Vi|?SDqJmO&Me~$2;iGX`#eT*CdQ%={*{aI(xS1wxCj+n6_L;Izi77{1sQI13 z=a#-(vz@?+Fhn1L=w)p&pU=*wwQ`1(RgnyX2KGf-0o-teh8*oH(SzpM*-CLsGkRL)0v2txWQng=+I?{v!R#*(eP$5A8?# zoAVcYQ*_%Z`P@55a=CRkU3YyyK8?|lh(3!^AiNa_ZHoqveGeII_U$W7aaj);>Oh3!*z}^%6$r%0Cecl77TE%?J8e9fA zrH!7x;4O&7&hS2ZK z{)}Gr4&xC0$$O{f1ZCKF;w3rcbfbO{e@=g|?_GHVEXCRdi@OMYmca z+tx`eMji!PbP)tyLPtV+T@RN{$gk*TE?SVUS?8v@j4Yn(y{q2U@`k5%(;VKjaf<@& z_norw=M+LxUBpj6@h!!9_ILsyN*?Z^eN;`y7D~BFA%C1?X}7=kxQP8aV%Rkx!Kw=| dbTa9QAYqS=7@jGZ^u#?R;4^p!@s6L8{{cimY$^Z% diff --git a/orchestrator/__pycache__/research_manager.cpython-313.pyc b/orchestrator/__pycache__/research_manager.cpython-313.pyc index ce3acd8170b01465362de76d363593b8a9c6d334..03e900cad896a94fde0bc97ce885bb5c3ec18e39 100644 GIT binary patch literal 4779 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 N+7R~-1;Tp8{{=XkAXoqZ diff --git a/orchestrator/debate_manager.py b/orchestrator/debate_manager.py index 4edd857..69d3452 100644 --- a/orchestrator/debate_manager.py +++ b/orchestrator/debate_manager.py @@ -17,6 +17,7 @@ class DebateConfig: context: str = "" agent_ids: List[str] = None max_rounds: int = 2 + agent_clients: dict = None # Map[agent_id, LLMClient] @dataclass @@ -58,7 +59,12 @@ class DebateManager: # 创建参与的 Agent for agent_id in debate_config.agent_ids: - agent = BaseAgent(agent_id, self.llm_client) + # Check if specific client is provided in config, else use default + client = self.llm_client + if hasattr(debate_config, 'agent_clients') and debate_config.agent_clients and agent_id in debate_config.agent_clients: + client = debate_config.agent_clients[agent_id] + + agent = BaseAgent(agent_id, client) self.agents.append(agent) def run_debate_stream( @@ -106,6 +112,7 @@ class DebateManager: "agent_id": agent.agent_id, "agent_name": agent.name, "emoji": agent.emoji, + "model_name": agent.model_name, "round": round_num } diff --git a/orchestrator/research_manager.py b/orchestrator/research_manager.py index b25d888..5fe3f3c 100644 --- a/orchestrator/research_manager.py +++ b/orchestrator/research_manager.py @@ -8,12 +8,12 @@ import config class ResearchConfig: topic: str context: str = "" - planner_model: str = "gpt-4o" - researcher_model: str = "gemini-1.5-pro" - writer_model: str = "claude-3-5-sonnet-20241022" + expert_a_model: str = "gpt-4o" + expert_b_model: str = "gemini-1.5-pro" + expert_c_model: str = "claude-3-5-sonnet-20241022" class ResearchManager: - """Manages the Deep Research workflow""" + """Manages the Multi-Model Council workflow""" def __init__(self, api_key: str, base_url: str = None, provider: str = "aihubmix"): self.api_key = api_key @@ -31,21 +31,41 @@ class ResearchManager: def create_agents(self, config: ResearchConfig): """Initialize agents with specific models""" - self.agents["planner"] = ResearchAgent("planner", self._get_client(config.planner_model)) - self.agents["researcher"] = ResearchAgent("researcher", self._get_client(config.researcher_model)) - self.agents["writer"] = ResearchAgent("writer", self._get_client(config.writer_model)) + self.agents["expert_a"] = ResearchAgent("expert_a", self._get_client(config.expert_a_model)) + self.agents["expert_b"] = ResearchAgent("expert_b", self._get_client(config.expert_b_model)) + self.agents["expert_c"] = ResearchAgent("expert_c", self._get_client(config.expert_c_model)) - def generate_plan(self, topic: str, context: str) -> Generator[str, None, None]: - """Step 1: Generate Research Plan""" - prompt = f"Please create a comprehensive research plan for the topic: '{topic}'.\nBreak it down into 3-5 distinct, actionable steps." - yield from self.agents["planner"].generate(prompt, context) + def collaborate(self, topic: str, context: str) -> Generator[Dict[str, str], None, None]: + """ + Execute the collaborative research process: + 1. Expert A: Propose Analysis + 2. Expert B: Critique + 3. Expert C: Synthesis & Final Plan + """ + + # Step 1: Expert A Analysis + findings_a = "" + yield {"type": "step_start", "step": "Expert A Analysis", "agent": self.agents["expert_a"].name, "model": self.agents["expert_a"].model_name} + prompt_a = f"Please provide a comprehensive analysis and initial proposal for the topic: '{topic}'.\nContext: {context}" + for chunk in self.agents["expert_a"].generate(prompt_a, context): + findings_a += chunk + yield {"type": "content", "content": chunk} + yield {"type": "step_end", "output": findings_a} - def execute_step(self, step: str, previous_findings: str) -> Generator[str, None, None]: - """Step 2: Execute a single research step""" - prompt = f"Execute this research step: '{step}'.\nPrevious findings: {previous_findings}" - yield from self.agents["researcher"].generate(prompt) + # Step 2: Expert B Critique + findings_b = "" + yield {"type": "step_start", "step": "Expert B Critique", "agent": self.agents["expert_b"].name, "model": self.agents["expert_b"].model_name} + prompt_b = f"Review Expert A's proposal on '{topic}'. Critique it, find gaps, and suggest improvements.\nExpert A's Proposal:\n{findings_a}" + for chunk in self.agents["expert_b"].generate(prompt_b, context): + findings_b += chunk + yield {"type": "content", "content": chunk} + yield {"type": "step_end", "output": findings_b} - def generate_report(self, topic: str, all_findings: str) -> Generator[str, None, None]: - """Step 3: Generate Final Report""" - prompt = f"Write a final comprehensive report on '{topic}' based on these findings:\n{all_findings}" - yield from self.agents["writer"].generate(prompt) + # Step 3: Expert C Synthesis + findings_c = "" + yield {"type": "step_start", "step": "Expert C Synthesis", "agent": self.agents["expert_c"].name, "model": self.agents["expert_c"].model_name} + prompt_c = f"Synthesize a final comprehensive plan for '{topic}' based on Expert A's proposal and Expert B's critique.\nExpert A:\n{findings_a}\nExpert B:\n{findings_b}" + for chunk in self.agents["expert_c"].generate(prompt_c, context): + findings_c += chunk + yield {"type": "content", "content": chunk} + yield {"type": "step_end", "output": findings_c}