This commit is contained in:
MarkLo127 2026-03-12 23:07:36 +08:00
parent 8fa86ef6c0
commit 1a409796d1
6 changed files with 49 additions and 40 deletions

View File

@ -52,7 +52,6 @@ const formSchema = z.object({
.regex(/^\d{4}-\d{2}-\d{2}$/, "日期格式必須為 YYYY-MM-DD"),
analysts: z.array(z.string()).min(1, "請至少選擇一位分析師"),
research_depth: z.number().int().min(1).max(5),
analysis_mode: z.enum(["fast", "deep"]),
quick_think_llm: z.string().min(1, "請選擇快速思維模型"),
deep_think_llm: 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"),
analysts: ["market", "social", "news", "fundamentals"], // 預設全選
research_depth: 3, // 預設中等層級
analysis_mode: "deep", // 預設深層分析
market_type: "us", // 預設美股
quick_think_llm: "gpt-5-mini",
deep_think_llm: "gpt-5-mini",
@ -475,35 +473,6 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
/>
</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列 */}
<div className="md:col-span-2 grid grid-cols-1 md:grid-cols-4 gap-6">
<FormField

View File

@ -1,7 +1,7 @@
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import time
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.dataflows.config import get_config
@ -60,7 +60,8 @@ def create_fundamentals_analyst(llm, language: str = "zh-TW"):
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 = state.get("fundamentals_report", "")

View File

@ -1,7 +1,7 @@
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import time
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.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)
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 = state.get("market_report", "")

View File

@ -1,7 +1,7 @@
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import time
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.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]))
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 = state.get("news_report", "")

View File

@ -1,7 +1,7 @@
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import time
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.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)
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 = state.get("sentiment_report", "")

View File

@ -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 (
@ -20,6 +20,42 @@ from tradingagents.agents.utils.news_data_tools import (
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():
"""
建立一個刪除訊息的函式