From 3dcdbb5659fb0bac4dcdfa3e0123301949b36974 Mon Sep 17 00:00:00 2001 From: MarkLo Date: Wed, 26 Nov 2025 13:39:14 +0800 Subject: [PATCH] --- backend/app/services/pdf_generator.py | 45 +++++++++++-------- .../agents/analysts/fundamentals_analyst.py | 4 +- .../agents/analysts/market_analyst.py | 15 ++++--- tradingagents/agents/analysts/news_analyst.py | 15 ++++--- .../agents/analysts/social_media_analyst.py | 4 +- .../agents/managers/research_manager.py | 28 +++--------- tradingagents/agents/managers/risk_manager.py | 21 ++------- .../agents/researchers/bear_researcher.py | 37 +++------------ .../agents/researchers/bull_researcher.py | 41 +++-------------- .../agents/risk_mgmt/aggresive_debator.py | 3 +- .../agents/risk_mgmt/conservative_debator.py | 3 +- .../agents/risk_mgmt/neutral_debator.py | 3 +- tradingagents/agents/trader/trader.py | 3 +- 13 files changed, 78 insertions(+), 144 deletions(-) diff --git a/backend/app/services/pdf_generator.py b/backend/app/services/pdf_generator.py index c0c06dc9..09597377 100644 --- a/backend/app/services/pdf_generator.py +++ b/backend/app/services/pdf_generator.py @@ -224,7 +224,7 @@ class PDFGenerator: def _clean_markdown(self, text: str) -> str: """ Clean markdown formatting for PDF - IMPROVED VERSION - Fixes spurious character issues and improves cleaning logic + Simplified regex patterns to prevent encoding artifacts Args: text: Markdown text @@ -232,47 +232,54 @@ class PDFGenerator: Returns: Cleaned text """ + import unicodedata + + # 0. Normalize Unicode to prevent encoding issues + text = unicodedata.normalize('NFKC', text) + # 1. Remove markdown links but keep text text = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', text) - # 2. Remove bold markers (improved version) + # 2. Remove bold markers (simplified version) text = re.sub(r'\*\*(.+?)\*\*', r'\1', text) - - # 3. Remove italic markers (more precise to avoid side effects) - text = re.sub(r'(? 200: - recommendation = recommendation[:200] + "...(已截斷)" past_memory_str += recommendation + "\n\n" - # 截斷辯論歷史 - 這是最容易超過限制的部分 - # 增加限制以容納更長的辯論內容 - history = truncate_text(history, 3000) + # 截斷辯論歷史 - 這是最容易超過限制的部分 - 移除截斷以保留完整內容 # 建立提示 (prompt) prompt = f"""**重要:您必須使用繁體中文(Traditional Chinese)回覆所有內容。** @@ -90,7 +73,8 @@ def create_research_manager(llm, memory): - 辯論歷史:{history} 【輸出要求】 -**字數要求**:**至少800字以上** +**字數要求**:**800-1500字** +**嚴格遵守字數限制,少於800字或超過1500字的報告將被退回** **內容結構**: 1. 決策摘要(150字以上):明確的買入/賣出/持有決策與核心理由 2. 論證評估(200字以上):公正評估雙方最強論點與分歧點,不偏袒任何一方 diff --git a/tradingagents/agents/managers/risk_manager.py b/tradingagents/agents/managers/risk_manager.py index 6af03d58..642a78fe 100644 --- a/tradingagents/agents/managers/risk_manager.py +++ b/tradingagents/agents/managers/risk_manager.py @@ -41,21 +41,7 @@ def create_risk_manager(llm, memory): sentiment_report = state["sentiment_report"] trader_plan = state["investment_plan"] - # 定義文本截斷函數以避免超過 token 限制 - def truncate_text(text, max_chars): - """截斷文本到指定字符數""" - if len(text) <= max_chars: - return text - return text[:max_chars] + "\n...(內容已截斷)" - - - # 為每個報告設置合理的字符限制 - # 增加限制以確保 800+ 字的報告不被截斷 - market_research_report = truncate_text(market_research_report, 2000) - sentiment_report = truncate_text(sentiment_report, 2000) - news_report = truncate_text(news_report, 2500) - fundamentals_report = truncate_text(fundamentals_report, 2000) - trader_plan = truncate_text(trader_plan, 2000) + # 移除截斷邏輯以保留完整報告內容 # 整合當前情況 curr_situation = f"{market_research_report}\n\n{sentiment_report}\n\n{news_report}\n\n{fundamentals_report}" @@ -74,7 +60,7 @@ def create_risk_manager(llm, memory): # 截斷辯論歷史 - 這是最容易超過限制的部分 # 增加限制以容納更長的辯論內容(風險辯論通常有3方,比投資辯論更長) - history = truncate_text(history, 3000) + history = history # 移除截斷,保留完整歷史 # 建立提示 (prompt) @@ -96,7 +82,8 @@ def create_risk_manager(llm, memory): - 辯論歷史:{history} 【輸出要求】 -**字數要求**:**至少800字以上** +**字數要求**:**800-1500字** +**嚴格遵守字數限制,少於800字或超過1500字的報告將被退回** **內容結構**: 1. 風控結論(150字以上):風險評級與最終決策的明確陳述 2. 論證評估(200字以上):三方風險觀點的綜合評估,公正分析 diff --git a/tradingagents/agents/researchers/bear_researcher.py b/tradingagents/agents/researchers/bear_researcher.py index 14d5f63b..b41d27ed 100644 --- a/tradingagents/agents/researchers/bear_researcher.py +++ b/tradingagents/agents/researchers/bear_researcher.py @@ -42,47 +42,19 @@ def create_bear_researcher(llm, memory): news_report = state["news_report"] fundamentals_report = state["fundamentals_report"] - # 整合當前情況並智能截斷以避免超過 token 限制 - # 估算:1 個中文字符 ≈ 2.5 tokens,1 個英文字符 ≈ 0.25 tokens - # 目標:將每個報告限制在合理的字符數內,總共不超過約 15000 字符(約 20000-30000 tokens) - - def truncate_text(text, max_chars): - """智能截斷文本到指定字符數,在句子邊界處截斷""" - if len(text) <= max_chars: - return text - - truncated = text[:max_chars] - for delimiter in ['。', '\n', ',', '、', ' ']: - last_pos = truncated.rfind(delimiter) - if last_pos > max_chars * 0.8: - return text[:last_pos + 1] + "\n\n...(為控制長度已精簡)" - return truncated + "...(為控制長度已精簡)" - - - # 為每個報告設置合理的字符限制 - # 增加限制以確保 800+ 字的報告不被截斷 - market_research_report = truncate_text(market_research_report, 2000) - sentiment_report = truncate_text(sentiment_report, 2000) - news_report = truncate_text(news_report, 2500) - fundamentals_report = truncate_text(fundamentals_report, 2000) - + # 整合當前情況 - 移除截斷邏輯以保留完整報告內容 curr_situation = f"{market_research_report}\n\n{sentiment_report}\n\n{news_report}\n\n{fundamentals_report}" # 從記憶體中獲取過去相似情況的經驗 past_memories = memory.get_memories(curr_situation, n_matches=2) - # 將過去的經驗格式化為字串(限制長度) + # 將過去的經驗格式化為字串 past_memory_str = "" for i, rec in enumerate(past_memories, 1): recommendation = rec["recommendation"] - # 限制每條記憶的長度 - if len(recommendation) > 200: - recommendation = recommendation[:200] + "...(已截斷)" past_memory_str += recommendation + "\n\n" - # 建立提示 (prompt) - 限制歷史長度以控制總 token 數 - history = truncate_text(history, 300) - current_response = truncate_text(current_response, 200) + # 建立提示 (prompt) - 保留完整歷史以確保context完整性 prompt = f"""**重要:您必須使用繁體中文(Traditional Chinese)回覆所有內容。** @@ -106,7 +78,8 @@ def create_bear_researcher(llm, memory): - 過往經驗:{past_memory_str} 【輸出要求】 -**字數要求**:**至少800字以上** +**字數要求**:**800-1500字** +**嚴格遵守字數限制,少於800字或超過1500字的報告將被退回** **內容結構**: 1. 核心警示(150字以上):清晰且強勢地陳述看跌理由,展現堅定立場 2. 風險論證(450-500字):用詳實數據支撐風險分析,層層揭露隱患 diff --git a/tradingagents/agents/researchers/bull_researcher.py b/tradingagents/agents/researchers/bull_researcher.py index 77870b77..668016c7 100644 --- a/tradingagents/agents/researchers/bull_researcher.py +++ b/tradingagents/agents/researchers/bull_researcher.py @@ -42,51 +42,19 @@ def create_bull_researcher(llm, memory): news_report = state["news_report"] fundamentals_report = state["fundamentals_report"] - # 整合當前情況並智能截斷以避免超過 token 限制 - # 估算:1 個中文字符 ≈ 2.5 tokens,1 個英文字符 ≈ 0.25 tokens - # 目標:將每個報告限制在合理的字符數內,總共不超過約 15000 字符(約 20000-30000 tokens) - - def truncate_text(text, max_chars): - """智能截斷文本到指定字符數,在句子邊界處截斷""" - if len(text) <= max_chars: - return text - - # 在max_chars附近尋找句子結束標記 - truncated = text[:max_chars] - - # 尋找最後一個句號、換行或逗號 - for delimiter in ['。', '\n', ',', '、', ' ']: - last_pos = truncated.rfind(delimiter) - if last_pos > max_chars * 0.8: # 至少保留80%的內容 - return text[:last_pos + 1] + "\n\n...(為控制長度已精簡)" - - # 如果找不到合適的分隔符,直接在字符處截斷 - return truncated + "...(為控制長度已精簡)" - - # 為每個報告設置合理的字符限制 - # 增加限制以確保 800+ 字的報告不被截斷 - market_research_report = truncate_text(market_research_report, 2000) - sentiment_report = truncate_text(sentiment_report, 2000) - news_report = truncate_text(news_report, 2500) - fundamentals_report = truncate_text(fundamentals_report, 2000) - + # 整合當前情況 - 移除截斷邏輯以保留完整報告內容 curr_situation = f"{market_research_report}\n\n{sentiment_report}\n\n{news_report}\n\n{fundamentals_report}" # 從記憶體中獲取過去相似情況的經驗 past_memories = memory.get_memories(curr_situation, n_matches=2) - # 將過去的經驗格式化為字串(限制長度) + # 將過去的經驗格式化為字串 past_memory_str = "" for i, rec in enumerate(past_memories, 1): recommendation = rec["recommendation"] - # 限制每條記憶的長度 - if len(recommendation) > 200: - recommendation = recommendation[:200] + "...(已截斷)" past_memory_str += recommendation + "\n\n" - # 建立提示 (prompt) - 限制歷史長度以控制總 token 數 - history = truncate_text(history, 300) - current_response = truncate_text(current_response, 200) + # 建立提示 (prompt) - 保留完整歷史以確保context完整性 prompt = f"""**重要:您必須使用繁體中文(Traditional Chinese)回覆所有內容。** @@ -110,7 +78,8 @@ def create_bull_researcher(llm, memory): - 過往經驗:{past_memory_str} 【輸出要求】 -**字數要求**:**至少800字以上** +**字數要求**:**800-1500字** +**嚴格遵守字數限制,少於800字或超過1500字的報告將被退回** **內容結構**: 1. 核心論點(150字以上):清晰且強勢地陳述看漲理由,展現必勝信心 2. 成長論證(450-500字):用詳實數據支撐成長邏輯,層層推進論述 diff --git a/tradingagents/agents/risk_mgmt/aggresive_debator.py b/tradingagents/agents/risk_mgmt/aggresive_debator.py index 0f8143b3..7b938c9f 100644 --- a/tradingagents/agents/risk_mgmt/aggresive_debator.py +++ b/tradingagents/agents/risk_mgmt/aggresive_debator.py @@ -85,7 +85,8 @@ def create_risky_debator(llm): - 對手觀點:{current_safe_response}, {current_neutral_response} 【輸出要求】 -**字數要求**:**至少800字以上** +**字數要求**:**800-1500字** +**嚴格遵守字數限制,少於800字或超過1500字的報告將被退回** **內容結構**: 1. 核心主張(150字以上):清晰且強勢地陳述積極策略的理由,展現必勝信心 2. 機會分析(450-500字):詳細論證上檔潛力,層層推進論述 diff --git a/tradingagents/agents/risk_mgmt/conservative_debator.py b/tradingagents/agents/risk_mgmt/conservative_debator.py index 3aec1911..05bcc071 100644 --- a/tradingagents/agents/risk_mgmt/conservative_debator.py +++ b/tradingagents/agents/risk_mgmt/conservative_debator.py @@ -86,7 +86,8 @@ def create_safe_debator(llm): - 對手觀點:{current_risky_response}, {current_neutral_response} 【輸出要求】 -**字數要求**:**至少800字以上** +**字數要求**:**800-1500字** +**嚴格遵守字數限制,少於800字或超過1500字的報告將被退回** **內容結構**: 1. 核心警示(150字以上):清晰且強勢地陳述保守建議的理由,展現堅定立場 2. 風險盤點(450-500字):詳細分析下檔風險,層層揭露隱患 diff --git a/tradingagents/agents/risk_mgmt/neutral_debator.py b/tradingagents/agents/risk_mgmt/neutral_debator.py index baf9d32e..05bf9357 100644 --- a/tradingagents/agents/risk_mgmt/neutral_debator.py +++ b/tradingagents/agents/risk_mgmt/neutral_debator.py @@ -85,7 +85,8 @@ def create_neutral_debator(llm): - 對手觀點:{current_risky_response}, {current_safe_response} 【輸出要求】 -**字數要求**:**至少800字以上** +**字數要求**:**800-1500字** +**嚴格遵守字數限制,少於800字或超過1500字的報告將被退回** **內容結構**: 1. 核心觀點(150字以上):清晰陳述平衡策略的理由與價值 2. 風險報酬評估(450-500字):客觀分析損益比,綜合評估雙方論點 diff --git a/tradingagents/agents/trader/trader.py b/tradingagents/agents/trader/trader.py index 0016b6a1..222a7325 100644 --- a/tradingagents/agents/trader/trader.py +++ b/tradingagents/agents/trader/trader.py @@ -100,7 +100,8 @@ def create_trader(llm, memory): - 過去反思:{past_memory_str} 【輸出要求】 -**字數要求**:**至少800字以上** +**字數要求**:**800-1500字** +**嚴格遵守字數限制,少於800字或超過1500字的報告將被退回** **內容結構**: 1. 執行摘要(150字以上):最終決策與核心理由的清晰陳述 2. 決策整合(150字以上):研究與風控觀點的平衡整合過程