This commit is contained in:
parent
8fa86ef6c0
commit
1a409796d1
|
|
@ -52,7 +52,6 @@ const formSchema = z.object({
|
||||||
.regex(/^\d{4}-\d{2}-\d{2}$/, "日期格式必須為 YYYY-MM-DD"),
|
.regex(/^\d{4}-\d{2}-\d{2}$/, "日期格式必須為 YYYY-MM-DD"),
|
||||||
analysts: z.array(z.string()).min(1, "請至少選擇一位分析師"),
|
analysts: z.array(z.string()).min(1, "請至少選擇一位分析師"),
|
||||||
research_depth: z.number().int().min(1).max(5),
|
research_depth: z.number().int().min(1).max(5),
|
||||||
analysis_mode: z.enum(["fast", "deep"]),
|
|
||||||
quick_think_llm: z.string().min(1, "請選擇快速思維模型"),
|
quick_think_llm: z.string().min(1, "請選擇快速思維模型"),
|
||||||
deep_think_llm: z.string().min(1, "請選擇深層思維模型"),
|
deep_think_llm: z.string().min(1, "請選擇深層思維模型"),
|
||||||
embedding_model: z.string().min(1, "請選擇嵌入式模型"),
|
embedding_model: z.string().min(1, "請選擇嵌入式模型"),
|
||||||
|
|
@ -112,7 +111,6 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
||||||
analysis_date: format(new Date(), "yyyy-MM-dd"),
|
analysis_date: format(new Date(), "yyyy-MM-dd"),
|
||||||
analysts: ["market", "social", "news", "fundamentals"], // 預設全選
|
analysts: ["market", "social", "news", "fundamentals"], // 預設全選
|
||||||
research_depth: 3, // 預設中等層級
|
research_depth: 3, // 預設中等層級
|
||||||
analysis_mode: "deep", // 預設深層分析
|
|
||||||
market_type: "us", // 預設美股
|
market_type: "us", // 預設美股
|
||||||
quick_think_llm: "gpt-5-mini",
|
quick_think_llm: "gpt-5-mini",
|
||||||
deep_think_llm: "gpt-5-mini",
|
deep_think_llm: "gpt-5-mini",
|
||||||
|
|
@ -475,35 +473,6 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 分析模式行 */}
|
|
||||||
<div className="md:col-span-2 grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="analysis_mode"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>分析模式</FormLabel>
|
|
||||||
<Select
|
|
||||||
onValueChange={field.onChange}
|
|
||||||
defaultValue={field.value}
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="選擇分析模式" />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="fast">快速分析 (~15-25 分鐘)</SelectItem>
|
|
||||||
<SelectItem value="deep">深層分析 (~1 小時)</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<FormDescription>快速模式跳過辯論,深層模式包含投資和風險辯論</FormDescription>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 第二行:研究深度、快速思維模型、深層思維模型、嵌入式模型(4列) */}
|
{/* 第二行:研究深度、快速思維模型、深層思維模型、嵌入式模型(4列) */}
|
||||||
<div className="md:col-span-2 grid grid-cols-1 md:grid-cols-4 gap-6">
|
<div className="md:col-span-2 grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||||
<FormField
|
<FormField
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
from tradingagents.agents.utils.agent_utils import get_fundamentals, get_balance_sheet, get_cashflow, get_income_statement, get_insider_sentiment, get_insider_transactions
|
from tradingagents.agents.utils.agent_utils import get_fundamentals, get_balance_sheet, get_cashflow, get_income_statement, get_insider_sentiment, get_insider_transactions, filter_messages_for_analyst
|
||||||
from tradingagents.agents.utils.prompts import get_fundamentals_analyst_prompt, get_agent_role_instruction, get_context_message
|
from tradingagents.agents.utils.prompts import get_fundamentals_analyst_prompt, get_agent_role_instruction, get_context_message
|
||||||
from tradingagents.dataflows.config import get_config
|
from tradingagents.dataflows.config import get_config
|
||||||
|
|
||||||
|
|
@ -60,7 +60,8 @@ def create_fundamentals_analyst(llm, language: str = "zh-TW"):
|
||||||
|
|
||||||
chain = prompt | llm.bind_tools(tools)
|
chain = prompt | llm.bind_tools(tools)
|
||||||
|
|
||||||
result = chain.invoke(state["messages"])
|
tool_names = {t.name for t in tools}
|
||||||
|
result = chain.invoke(filter_messages_for_analyst(state["messages"], tool_names))
|
||||||
|
|
||||||
# Report logic: only save report when LLM gives final response
|
# Report logic: only save report when LLM gives final response
|
||||||
report = state.get("fundamentals_report", "")
|
report = state.get("fundamentals_report", "")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
from tradingagents.agents.utils.agent_utils import get_stock_data, get_indicators
|
from tradingagents.agents.utils.agent_utils import get_stock_data, get_indicators, filter_messages_for_analyst
|
||||||
from tradingagents.agents.utils.prompts import get_language_instruction, get_agent_role_instruction, get_context_message
|
from tradingagents.agents.utils.prompts import get_language_instruction, get_agent_role_instruction, get_context_message
|
||||||
from tradingagents.dataflows.config import get_config
|
from tradingagents.dataflows.config import get_config
|
||||||
|
|
||||||
|
|
@ -140,7 +140,8 @@ Please provide a professional, precise, and actionable technical analysis report
|
||||||
|
|
||||||
chain = prompt | llm.bind_tools(tools)
|
chain = prompt | llm.bind_tools(tools)
|
||||||
|
|
||||||
result = chain.invoke(state["messages"])
|
tool_names = {t.name for t in tools}
|
||||||
|
result = chain.invoke(filter_messages_for_analyst(state["messages"], tool_names))
|
||||||
|
|
||||||
# Report logic: only save report when LLM gives final response
|
# Report logic: only save report when LLM gives final response
|
||||||
report = state.get("market_report", "")
|
report = state.get("market_report", "")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
from tradingagents.agents.utils.agent_utils import get_news, get_global_news
|
from tradingagents.agents.utils.agent_utils import get_news, get_global_news, filter_messages_for_analyst
|
||||||
from tradingagents.agents.utils.prompts import get_news_analyst_prompt, get_agent_role_instruction, get_context_message
|
from tradingagents.agents.utils.prompts import get_news_analyst_prompt, get_agent_role_instruction, get_context_message
|
||||||
from tradingagents.dataflows.config import get_config
|
from tradingagents.dataflows.config import get_config
|
||||||
|
|
||||||
|
|
@ -57,7 +57,8 @@ def create_news_analyst(llm, language: str = "zh-TW"):
|
||||||
prompt = prompt.partial(tool_names=", ".join([tool.name for tool in tools]))
|
prompt = prompt.partial(tool_names=", ".join([tool.name for tool in tools]))
|
||||||
|
|
||||||
chain = prompt | llm.bind_tools(tools)
|
chain = prompt | llm.bind_tools(tools)
|
||||||
result = chain.invoke(state["messages"])
|
tool_names = {t.name for t in tools}
|
||||||
|
result = chain.invoke(filter_messages_for_analyst(state["messages"], tool_names))
|
||||||
|
|
||||||
# Report logic: only save report when LLM gives final response
|
# Report logic: only save report when LLM gives final response
|
||||||
report = state.get("news_report", "")
|
report = state.get("news_report", "")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
from tradingagents.agents.utils.agent_utils import get_news
|
from tradingagents.agents.utils.agent_utils import get_news, filter_messages_for_analyst
|
||||||
from tradingagents.agents.utils.prompts import get_social_analyst_prompt, get_agent_role_instruction, get_context_message
|
from tradingagents.agents.utils.prompts import get_social_analyst_prompt, get_agent_role_instruction, get_context_message
|
||||||
from tradingagents.dataflows.config import get_config
|
from tradingagents.dataflows.config import get_config
|
||||||
|
|
||||||
|
|
@ -57,7 +57,8 @@ def create_social_media_analyst(llm, language: str = "zh-TW"):
|
||||||
|
|
||||||
chain = prompt | llm.bind_tools(tools)
|
chain = prompt | llm.bind_tools(tools)
|
||||||
|
|
||||||
result = chain.invoke(state["messages"])
|
tool_names = {t.name for t in tools}
|
||||||
|
result = chain.invoke(filter_messages_for_analyst(state["messages"], tool_names))
|
||||||
|
|
||||||
# Report logic: only save report when LLM gives final response
|
# Report logic: only save report when LLM gives final response
|
||||||
report = state.get("sentiment_report", "")
|
report = state.get("sentiment_report", "")
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from langchain_core.messages import HumanMessage, RemoveMessage
|
from langchain_core.messages import HumanMessage, RemoveMessage, AIMessage, ToolMessage
|
||||||
|
|
||||||
# 從獨立的工具程式檔案匯入工具
|
# 從獨立的工具程式檔案匯入工具
|
||||||
from tradingagents.agents.utils.core_stock_tools import (
|
from tradingagents.agents.utils.core_stock_tools import (
|
||||||
|
|
@ -20,6 +20,42 @@ from tradingagents.agents.utils.news_data_tools import (
|
||||||
get_global_news
|
get_global_news
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def filter_messages_for_analyst(messages, analyst_tool_names):
|
||||||
|
"""
|
||||||
|
Filter shared state messages to only include conversations relevant to this analyst.
|
||||||
|
|
||||||
|
In parallel analyst execution, all analysts share state["messages"]. When analyst A
|
||||||
|
makes tool_use calls, those get merged into shared state. When analyst B runs its
|
||||||
|
second iteration, it sees orphaned tool_use blocks from analyst A without matching
|
||||||
|
tool_results, causing anthropic.BadRequestError (400).
|
||||||
|
|
||||||
|
This function keeps only:
|
||||||
|
- Non-tool messages (HumanMessage etc.)
|
||||||
|
- AIMessages whose tool_calls ALL belong to this analyst's tools
|
||||||
|
- ToolMessages whose tool_call_id matches this analyst's tool calls
|
||||||
|
"""
|
||||||
|
analyst_tool_call_ids = set()
|
||||||
|
for msg in messages:
|
||||||
|
if isinstance(msg, AIMessage) and msg.tool_calls:
|
||||||
|
for tc in msg.tool_calls:
|
||||||
|
if tc["name"] in analyst_tool_names:
|
||||||
|
analyst_tool_call_ids.add(tc["id"])
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for msg in messages:
|
||||||
|
if isinstance(msg, AIMessage) and msg.tool_calls:
|
||||||
|
if all(tc["name"] in analyst_tool_names for tc in msg.tool_calls):
|
||||||
|
result.append(msg)
|
||||||
|
# Skip AI messages containing tool_calls from other parallel analysts
|
||||||
|
elif isinstance(msg, ToolMessage):
|
||||||
|
if msg.tool_call_id in analyst_tool_call_ids:
|
||||||
|
result.append(msg)
|
||||||
|
else:
|
||||||
|
result.append(msg)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def create_msg_delete():
|
def create_msg_delete():
|
||||||
"""
|
"""
|
||||||
建立一個刪除訊息的函式。
|
建立一個刪除訊息的函式。
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue