feat: Implement Multi-Model Council mode with 3-expert workflow
This commit is contained in:
parent
5931e9280f
commit
9dda930868
83
README.md
83
README.md
@ -1 +1,82 @@
|
|||||||
# multi-agent
|
# 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)
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -42,6 +42,13 @@ class BaseAgent:
|
|||||||
|
|
||||||
# 存储对话历史
|
# 存储对话历史
|
||||||
self.conversation_history = []
|
self.conversation_history = []
|
||||||
|
|
||||||
|
@property
|
||||||
|
def model_name(self) -> str:
|
||||||
|
"""获取当前使用的模型名称"""
|
||||||
|
if hasattr(self.llm_client, "model"):
|
||||||
|
return self.llm_client.model
|
||||||
|
return "Unknown Model"
|
||||||
|
|
||||||
def generate_response(
|
def generate_response(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
from typing import Generator, List, Dict
|
from typing import Generator
|
||||||
from utils.llm_client import LLMClient
|
from utils.llm_client import LLMClient
|
||||||
import config
|
import config
|
||||||
|
|
||||||
@ -11,25 +11,28 @@ class ResearchAgent:
|
|||||||
self.role_config = config.RESEARCH_MODEL_ROLES.get(role, {})
|
self.role_config = config.RESEARCH_MODEL_ROLES.get(role, {})
|
||||||
self.name = self.role_config.get("name", role.capitalize())
|
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:
|
def _get_system_prompt(self, context: str = "") -> str:
|
||||||
if self.role == "planner":
|
if self.role == "expert_a":
|
||||||
return f"""You are a Senior Research Planner.
|
return f"""You are Expert A, a Senior Analyst.
|
||||||
Your goal is to break down a complex user topic into a structured research plan.
|
Your goal is to provide a deep, foundational analysis of the user's topic.
|
||||||
You must create a clear, step-by-step plan that covers different angles of the topic.
|
Structure your thinking clearly. Propose a solid initial framework or solution.
|
||||||
Format your output as a Markdown list of steps.
|
|
||||||
Context: {context}"""
|
Context: {context}"""
|
||||||
|
|
||||||
elif self.role == "researcher":
|
elif self.role == "expert_b":
|
||||||
return f"""You are a Deep Researcher.
|
return f"""You are Expert B, a Critical Reviewer.
|
||||||
Your goal is to execute a specific research step and provide detailed, in-depth analysis.
|
Your goal is to find flaws, risks, and missed opportunities in Expert A's analysis.
|
||||||
Use your vast knowledge to provide specific facts, figures, and logical reasoning.
|
Be constructive but rigorous. Don't just agree; add value by challenging assumptions.
|
||||||
Do not be superficial. Go deep.
|
|
||||||
Context: {context}"""
|
Context: {context}"""
|
||||||
|
|
||||||
elif self.role == "writer":
|
elif self.role == "expert_c":
|
||||||
return f"""You are a Senior Report Writer.
|
return f"""You are Expert C, a Senior Strategist.
|
||||||
Your goal is to synthesize multiple research findings into a cohesive, high-quality report.
|
Your goal is to synthesize the final output.
|
||||||
The report should be well-structured, easy to read, and provide actionable insights.
|
Combine the structural strength of Expert A with the critical insights of Expert B.
|
||||||
|
Produce a final, polished, comprehensive plan or report.
|
||||||
Context: {context}"""
|
Context: {context}"""
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|||||||
193
app.py
193
app.py
@ -66,23 +66,7 @@ st.markdown("""
|
|||||||
DEFAULT_API_KEY = os.getenv("AIHUBMIX_API_KEY", "")
|
DEFAULT_API_KEY = os.getenv("AIHUBMIX_API_KEY", "")
|
||||||
|
|
||||||
# 支持的模型列表
|
# 支持的模型列表
|
||||||
AVAILABLE_MODELS = {
|
from config import AVAILABLE_MODELS, RESEARCH_MODEL_ROLES
|
||||||
"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 (月之暗面)",
|
|
||||||
}
|
|
||||||
|
|
||||||
# 决策类型
|
# 决策类型
|
||||||
DECISION_TYPES = {
|
DECISION_TYPES = {
|
||||||
@ -221,17 +205,17 @@ with st.sidebar:
|
|||||||
# ==================== 主界面逻辑 ====================
|
# ==================== 主界面逻辑 ====================
|
||||||
|
|
||||||
if mode == "Deep Research":
|
if mode == "Deep Research":
|
||||||
st.title("🧪 Deep Research Mode")
|
st.title("🧪 Multi-Model Council")
|
||||||
st.markdown("*深度研究模式:规划 -> 研究 -> 报告*")
|
st.markdown("*多模型智囊团:分析 (Expert A) -> 批判 (Expert B) -> 决策 (Expert C)*")
|
||||||
|
|
||||||
# Input
|
# 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)
|
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:
|
if start_research_btn and research_topic:
|
||||||
st.session_state.research_started = False
|
st.session_state.research_started = True
|
||||||
st.session_state.research_output = ""
|
st.session_state.research_output = ""
|
||||||
st.session_state.research_steps_output = []
|
st.session_state.research_steps_output = []
|
||||||
|
|
||||||
@ -239,84 +223,70 @@ if mode == "Deep Research":
|
|||||||
config_obj = ResearchConfig(
|
config_obj = ResearchConfig(
|
||||||
topic=research_topic,
|
topic=research_topic,
|
||||||
context=research_context,
|
context=research_context,
|
||||||
planner_model=roles_config['planner'],
|
expert_a_model=roles_config['expert_a'],
|
||||||
researcher_model=roles_config['researcher'],
|
expert_b_model=roles_config['expert_b'],
|
||||||
writer_model=roles_config['writer']
|
expert_c_model=roles_config['expert_c']
|
||||||
)
|
)
|
||||||
manager.create_agents(config_obj)
|
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.divider()
|
||||||
st.subheader("📋 研究计划确认")
|
st.subheader("🧠 智囊团思考中...")
|
||||||
|
|
||||||
edited_plan = st.text_area("请审查并编辑计划 (Markdown格式)", value=st.session_state.research_plan, height=300)
|
# Collaborative Execution
|
||||||
st.session_state.research_plan = edited_plan
|
current_step_name = ""
|
||||||
|
current_step_content = ""
|
||||||
|
step_placeholder = st.empty()
|
||||||
|
status_container = st.status("正在初始化...", expanded=True)
|
||||||
|
|
||||||
start_research_btn = st.button("🚀 开始深度研究", type="primary")
|
try:
|
||||||
|
for event in manager.collaborate(research_topic, research_context):
|
||||||
if start_research_btn:
|
if event["type"] == "step_start":
|
||||||
st.session_state.research_started = True
|
current_step_name = event["step"]
|
||||||
st.session_state.research_steps_output = [] # Reset steps
|
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.)
|
status_container.update(label="✅ 所有步骤完成", state="complete", expanded=False)
|
||||||
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
|
|
||||||
|
|
||||||
manager = ResearchManager(api_key=api_key)
|
# The last step output is the final plan
|
||||||
config_obj = ResearchConfig(
|
if st.session_state.research_steps_output:
|
||||||
topic=research_topic,
|
final_plan = st.session_state.research_steps_output[-1]["output"]
|
||||||
context=research_context,
|
st.session_state.research_output = final_plan
|
||||||
planner_model=roles_config['planner'],
|
st.success("✅ 综合方案生成完毕")
|
||||||
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)
|
|
||||||
|
|
||||||
st.session_state.research_output = final_report
|
except Exception as e:
|
||||||
st.success("✅ 研究完成")
|
st.error(f"发生错误: {str(e)}")
|
||||||
|
import traceback
|
||||||
|
st.code(traceback.format_exc())
|
||||||
|
|
||||||
# Show Final Report if available
|
# Show Final Report if available
|
||||||
if st.session_state.research_output:
|
if st.session_state.research_output:
|
||||||
st.divider()
|
st.divider()
|
||||||
st.subheader("📄 最终研究报告")
|
st.subheader("📄 最终综合方案")
|
||||||
st.markdown(st.session_state.research_output)
|
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":
|
elif mode == "Debate Workshop":
|
||||||
@ -386,6 +356,23 @@ elif mode == "Debate Workshop":
|
|||||||
):
|
):
|
||||||
selected_agents.append(agent_id)
|
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:
|
if len(selected_agents) < 2:
|
||||||
st.warning("请至少选择 2 个角色")
|
st.warning("请至少选择 2 个角色")
|
||||||
@ -434,22 +421,25 @@ elif mode == "Debate Workshop":
|
|||||||
agent_profiles.AGENT_PROFILES.update(st.session_state.custom_agents)
|
agent_profiles.AGENT_PROFILES.update(st.session_state.custom_agents)
|
||||||
|
|
||||||
try:
|
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(
|
llm_client = LLMClient(
|
||||||
provider="aihubmix",
|
provider="aihubmix",
|
||||||
api_key=api_key,
|
api_key=api_key,
|
||||||
base_url="https://aihubmix.com/v1",
|
base_url="https://aihubmix.com/v1",
|
||||||
model=model
|
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)
|
debate_manager = DebateManager(llm_client)
|
||||||
|
|
||||||
# 配置辩论
|
# 配置辩论
|
||||||
@ -457,7 +447,8 @@ elif mode == "Debate Workshop":
|
|||||||
topic=topic,
|
topic=topic,
|
||||||
context=context,
|
context=context,
|
||||||
agent_ids=selected_agents,
|
agent_ids=selected_agents,
|
||||||
max_rounds=max_rounds
|
max_rounds=max_rounds,
|
||||||
|
agent_clients=agent_clients
|
||||||
)
|
)
|
||||||
debate_manager.setup_debate(debate_config)
|
debate_manager.setup_debate(debate_config)
|
||||||
|
|
||||||
@ -474,7 +465,9 @@ elif mode == "Debate Workshop":
|
|||||||
)
|
)
|
||||||
|
|
||||||
elif event["type"] == "speech_start":
|
elif event["type"] == "speech_start":
|
||||||
st.markdown(f"**{event['emoji']} {event['agent_name']}**")
|
# 显示模型名称
|
||||||
|
model_display = f" <span style='font-size:0.8em; color:gray'>({event.get('model_name', 'Unknown')})</span>"
|
||||||
|
st.markdown(f"**{event['emoji']} {event['agent_name']}**{model_display}", unsafe_allow_html=True)
|
||||||
speech_placeholder = st.empty()
|
speech_placeholder = st.empty()
|
||||||
current_content = ""
|
current_content = ""
|
||||||
|
|
||||||
|
|||||||
33
config.py
33
config.py
@ -18,26 +18,41 @@ AIHUBMIX_BASE_URL = "https://aihubmix.com/v1"
|
|||||||
DEFAULT_MODEL = "gpt-4o" # AIHubMix 支持的模型
|
DEFAULT_MODEL = "gpt-4o" # AIHubMix 支持的模型
|
||||||
LLM_PROVIDER = "aihubmix" # 默认使用 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_DEBATE_ROUNDS = 3 # 最大辩论轮数
|
||||||
MAX_AGENTS = 6 # 最大参与 Agent 数量
|
MAX_AGENTS = 6 # 最大参与 Agent 数量
|
||||||
|
|
||||||
# 研究模式模型角色配置
|
# 研究模式模型角色配置
|
||||||
RESEARCH_MODEL_ROLES = {
|
RESEARCH_MODEL_ROLES = {
|
||||||
"planner": {
|
"expert_a": {
|
||||||
"name": "Planner",
|
"name": "Expert A (Analyst)",
|
||||||
"default_model": "gpt-4o",
|
"default_model": "gpt-4o",
|
||||||
"description": "负责拆解问题,制定研究计划"
|
"description": "负责初步分析,提出核心观点和方案"
|
||||||
},
|
},
|
||||||
"researcher": {
|
"expert_b": {
|
||||||
"name": "Researcher",
|
"name": "Expert B (Critique)",
|
||||||
"default_model": "gemini-1.5-pro",
|
"default_model": "gemini-1.5-pro",
|
||||||
"description": "负责执行具体的研究步骤,深度分析"
|
"description": "负责批判性分析,指出潜在问题和漏洞"
|
||||||
},
|
},
|
||||||
"writer": {
|
"expert_c": {
|
||||||
"name": "Writer",
|
"name": "Expert C (Synthesizer)",
|
||||||
"default_model": "claude-3-5-sonnet-20241022",
|
"default_model": "claude-3-5-sonnet-20241022",
|
||||||
"description": "负责汇总信息,撰写最终报告"
|
"description": "负责综合各方观点,生成最终决策方案"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -17,6 +17,7 @@ class DebateConfig:
|
|||||||
context: str = ""
|
context: str = ""
|
||||||
agent_ids: List[str] = None
|
agent_ids: List[str] = None
|
||||||
max_rounds: int = 2
|
max_rounds: int = 2
|
||||||
|
agent_clients: dict = None # Map[agent_id, LLMClient]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -58,7 +59,12 @@ class DebateManager:
|
|||||||
|
|
||||||
# 创建参与的 Agent
|
# 创建参与的 Agent
|
||||||
for agent_id in debate_config.agent_ids:
|
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)
|
self.agents.append(agent)
|
||||||
|
|
||||||
def run_debate_stream(
|
def run_debate_stream(
|
||||||
@ -106,6 +112,7 @@ class DebateManager:
|
|||||||
"agent_id": agent.agent_id,
|
"agent_id": agent.agent_id,
|
||||||
"agent_name": agent.name,
|
"agent_name": agent.name,
|
||||||
"emoji": agent.emoji,
|
"emoji": agent.emoji,
|
||||||
|
"model_name": agent.model_name,
|
||||||
"round": round_num
|
"round": round_num
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,12 +8,12 @@ import config
|
|||||||
class ResearchConfig:
|
class ResearchConfig:
|
||||||
topic: str
|
topic: str
|
||||||
context: str = ""
|
context: str = ""
|
||||||
planner_model: str = "gpt-4o"
|
expert_a_model: str = "gpt-4o"
|
||||||
researcher_model: str = "gemini-1.5-pro"
|
expert_b_model: str = "gemini-1.5-pro"
|
||||||
writer_model: str = "claude-3-5-sonnet-20241022"
|
expert_c_model: str = "claude-3-5-sonnet-20241022"
|
||||||
|
|
||||||
class ResearchManager:
|
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"):
|
def __init__(self, api_key: str, base_url: str = None, provider: str = "aihubmix"):
|
||||||
self.api_key = api_key
|
self.api_key = api_key
|
||||||
@ -31,21 +31,41 @@ class ResearchManager:
|
|||||||
|
|
||||||
def create_agents(self, config: ResearchConfig):
|
def create_agents(self, config: ResearchConfig):
|
||||||
"""Initialize agents with specific models"""
|
"""Initialize agents with specific models"""
|
||||||
self.agents["planner"] = ResearchAgent("planner", self._get_client(config.planner_model))
|
self.agents["expert_a"] = ResearchAgent("expert_a", self._get_client(config.expert_a_model))
|
||||||
self.agents["researcher"] = ResearchAgent("researcher", self._get_client(config.researcher_model))
|
self.agents["expert_b"] = ResearchAgent("expert_b", self._get_client(config.expert_b_model))
|
||||||
self.agents["writer"] = ResearchAgent("writer", self._get_client(config.writer_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]:
|
def collaborate(self, topic: str, context: str) -> Generator[Dict[str, 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."
|
Execute the collaborative research process:
|
||||||
yield from self.agents["planner"].generate(prompt, context)
|
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: Expert B Critique
|
||||||
"""Step 2: Execute a single research step"""
|
findings_b = ""
|
||||||
prompt = f"Execute this research step: '{step}'.\nPrevious findings: {previous_findings}"
|
yield {"type": "step_start", "step": "Expert B Critique", "agent": self.agents["expert_b"].name, "model": self.agents["expert_b"].model_name}
|
||||||
yield from self.agents["researcher"].generate(prompt)
|
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: Expert C Synthesis
|
||||||
"""Step 3: Generate Final Report"""
|
findings_c = ""
|
||||||
prompt = f"Write a final comprehensive report on '{topic}' based on these findings:\n{all_findings}"
|
yield {"type": "step_start", "step": "Expert C Synthesis", "agent": self.agents["expert_c"].name, "model": self.agents["expert_c"].model_name}
|
||||||
yield from self.agents["writer"].generate(prompt)
|
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}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user