stock-ai-agent/backend/app/agent/smart_agent.py
2026-02-03 16:42:26 +08:00

978 lines
33 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
智能Agent - 真正使用LLM进行全面分析
"""
import re
import json
from typing import Dict, Any, Optional, List
from app.config import get_settings
from app.agent.context import ContextManager
from app.agent.skill_manager import skill_manager
from app.skills.market_data import MarketDataSkill
from app.skills.technical_analysis import TechnicalAnalysisSkill
from app.skills.fundamental import FundamentalSkill
from app.skills.visualization import VisualizationSkill
from app.skills.brave_search import BraveSearchSkill
from app.services.llm_service import llm_service
from app.services.tushare_service import tushare_service
from app.utils.logger import logger
class SmartStockAgent:
"""智能股票分析Agent - 深度集成LLM"""
def __init__(self):
"""初始化Agent"""
self.context_manager = ContextManager()
self.settings = get_settings()
# 注册技能
self._register_skills()
# 检查LLM是否可用
self.use_llm = bool(self.settings.zhipuai_api_key) and llm_service.client is not None
if self.use_llm:
logger.info("Smart Agent初始化完成LLM深度集成模式 + Brave搜索")
else:
logger.warning("Smart Agent初始化完成规则模式建议配置LLM")
def _register_skills(self):
"""注册所有技能"""
skill_manager.register(MarketDataSkill())
skill_manager.register(TechnicalAnalysisSkill())
skill_manager.register(FundamentalSkill())
skill_manager.register(VisualizationSkill())
skill_manager.register(BraveSearchSkill())
logger.info("技能注册完成包含Brave搜索")
async def process_message(
self,
message: str,
session_id: str,
user_id: Optional[str] = None
) -> Dict[str, Any]:
"""
处理用户消息(智能版)
Args:
message: 用户消息
session_id: 会话ID
user_id: 用户ID
Returns:
响应结果
"""
logger.info(f"处理消息: {message[:50]}...")
# 保存用户消息
self.context_manager.add_message(session_id, "user", message)
# 第一步使用LLM理解问题意图
intent_analysis = await self._analyze_question_intent(message)
if not intent_analysis:
response = {
"message": "抱歉,我无法理解您的问题。请重新描述您的需求。",
"metadata": {"type": "error"}
}
self.context_manager.add_message(session_id, "assistant", response["message"])
return response
# 第二步:根据意图类型处理
question_type = intent_analysis['type']
if question_type == 'stock_specific':
# 针对特定股票的问题
response = await self._handle_stock_question(intent_analysis, message)
elif question_type == 'macro_finance':
# 宏观金融问题
response = await self._handle_macro_question(intent_analysis, message)
elif question_type == 'knowledge':
# 金融知识问答
response = await self._handle_knowledge_question(intent_analysis, message)
else:
response = {
"message": "抱歉,我暂时无法处理这类问题。",
"metadata": {"type": "error"}
}
# 保存助手响应
self.context_manager.add_message(
session_id,
"assistant",
response["message"],
metadata=response.get("metadata")
)
return response
def _is_comprehensive_analysis(self, message: str) -> bool:
"""
判断是否需要全面分析
默认情况下,如果用户只是简单提到股票名称或代码,就进行全面分析
只有明确要求特定信息时(如"技术指标""K线图"等),才做单一查询
"""
# 明确要求单一查询的关键词
single_query_keywords = [
"k线", "图表", "走势图", "kline",
"技术指标", "macd", "rsi", "均线", "kdj",
"基本面", "公司信息", "行业",
"实时行情", "价格", "涨跌"
]
# 如果明确要求单一查询返回False
if any(keyword in message.lower() for keyword in single_query_keywords):
return False
# 默认进行全面分析
return True
async def _comprehensive_analysis(
self,
stock_code: str,
stock_name: Optional[str],
message: str
) -> Dict[str, Any]:
"""
全面分析:整合多个数据源 + LLM深度分析
Args:
stock_code: 股票代码
stock_name: 股票名称
message: 用户消息
Returns:
综合分析结果
"""
logger.info(f"执行全面分析: {stock_code}")
display_name = stock_name or stock_code
# 1. 并行获取所有数据
try:
# 获取实时行情
quote_result = await skill_manager.execute_skill(
"market_data",
stock_code=stock_code,
data_type="quote"
)
# 获取技术指标
technical_result = await skill_manager.execute_skill(
"technical_analysis",
stock_code=stock_code,
indicators=["ma", "macd", "rsi", "kdj"]
)
# 获取基本面
fundamental_result = await skill_manager.execute_skill(
"fundamental",
stock_code=stock_code
)
# 获取最新新闻Brave搜索
search_query = f"{display_name} {stock_code} 股票 最新消息"
news_result = await skill_manager.execute_skill(
"brave_search",
query=search_query,
search_type="news",
count=5,
freshness="pw" # 过去一周
)
# 整合数据
all_data = {
"stock_code": stock_code,
"stock_name": display_name,
"quote": quote_result.get("data") if quote_result.get("success") else None,
"technical": technical_result.get("data") if technical_result.get("success") else None,
"fundamental": fundamental_result.get("data") if fundamental_result.get("success") else None,
"news": news_result.get("results") if news_result and not news_result.get("error") else None
}
# 2. 使用LLM进行深度分析
if self.use_llm:
analysis = await self._llm_comprehensive_analysis(all_data, message)
else:
analysis = self._rule_based_analysis(all_data)
return {
"message": analysis,
"metadata": {
"type": "comprehensive",
"data": all_data
}
}
except Exception as e:
logger.error(f"全面分析失败: {e}")
return {
"message": f"分析{display_name}时出错:{str(e)}",
"metadata": {"type": "error"}
}
async def _llm_comprehensive_analysis(
self,
data: Dict[str, Any],
user_message: str
) -> str:
"""使用LLM进行深度综合分析"""
# 获取当前时间
from datetime import datetime
current_time = datetime.now().strftime("%Y-%m-%d %H:%M")
# 获取行情数据的交易日期
quote_date = "未知"
if data.get('quote') and data['quote'].get('trade_date'):
quote_date = data['quote']['trade_date']
# 构建新闻摘要
news_summary = ""
news_source_info = ""
if data.get('news'):
news_summary = "\n【消息面分析】\n"
news_summary += f"数据来源Brave Search API\n"
news_summary += f"搜索时间:{current_time}\n"
news_summary += f"新闻范围:过去一周内相关新闻\n\n"
for idx, news_item in enumerate(data['news'][:5], 1):
news_summary += f"{idx}. {news_item.get('title', '无标题')}\n"
news_summary += f" 来源: {news_item.get('source', '未知')}\n"
news_summary += f" 摘要: {news_item.get('description', '无描述')}\n"
news_summary += f" 发布时间: {news_item.get('published', '未知')}\n\n"
news_source_info = "消息来源Brave搜索引擎数据可能存在延迟"
else:
news_summary = "\n【消息面分析】\n暂无最新新闻数据\n"
# 构建详细的分析提示
prompt = f"""你是一位专业的股票分析师。请对{data['stock_name']}({data['stock_code']})进行全面分析,用简洁专业但易懂的语言回答。
用户问题:{user_message}
【实时行情数据】
数据来源Tushare Pro API
交易日期:{quote_date}
{json.dumps(data.get('quote'), ensure_ascii=False, indent=2) if data.get('quote') else '数据获取失败'}
【技术指标数据】
数据来源Tushare Pro API基于历史K线数据计算
计算截止日期:{quote_date}
{json.dumps(data.get('technical'), ensure_ascii=False, indent=2) if data.get('technical') else '数据获取失败'}
【基本面数据】
数据来源Tushare Pro API
{json.dumps(data.get('fundamental'), ensure_ascii=False, indent=2) if data.get('fundamental') else '数据获取失败'}
{news_summary}
请按以下结构进行分析,并在每个部分明确标注数据来源和时效性:
## 一、基本面分析
分段说明公司情况,每个要点独立成段:
- 第一段:公司主营业务和行业地位
- 第二段:所属行业发展前景
- 第三段:如果有新闻,简要分析对公司的影响{news_source_info}
## 二、技术面分析(数据截止:{quote_date}
使用清晰的分段结构,每个技术指标独立成段:
**价格走势**
当前价格走势特征(上涨/下跌/震荡),结合成交量分析。
**均线系统**
短期均线MA5、MA10与长期均线MA20、MA60的位置关系判断当前趋势多头/空头/震荡)。
**MACD指标**
DIF和DEA的位置关系MACD柱状图变化判断动能强弱和买卖信号。
**RSI指标**
当前RSI值的位置是否超买>70或超卖<30短期走势预判。
**支撑与压力**
关键支撑位和压力位的具体价格区间。
## 三、市场情绪分析
分段分析市场情绪:
- 第一段:当前市场情绪(乐观/谨慎/悲观)及原因
- 第二段:如果有新闻,分析是利好还是利空
- 第三段:短期可能的催化因素
## 四、投资建议
基于技术面分析,给出具体的操作建议和点位:
**短期1-2周操作建议**
- 明确的操作建议:买入/持有/观望/减仓
- **具体点位建议**
- 如果建议买入:给出建议买入价格区间(基于支撑位)
- 如果建议卖出:给出建议卖出价格区间(基于压力位)
- 止损位:明确的止损价格点位
- 止盈位:明确的止盈价格点位
- 操作理由:基于技术指标的具体分析
**中期1-3个月策略**
- 趋势判断(上涨/下跌/震荡)
- 关键价格区间:
- 上方目标位:具体价格
- 下方支撑位:具体价格
- 策略建议
**长期(半年以上)**
投资价值评估和长期持有建议。
**风险提示**
- 主要风险点和注意事项
- 需要关注的关键价格位
## 五、总结
用一句话概括核心观点。
---
**数据说明**
- 行情数据来源Tushare Pro截止{quote_date}
- 技术指标基于历史K线数据计算截止{quote_date}
- 新闻数据Brave搜索搜索时间{current_time},范围:过去一周)
写作要求:
1. 语言简洁专业,避免过度修饰和比喻
2. 专业术语后用括号简单解释,例如"RSI超买指标>70股价可能回调"
3. **重要:每个分析点必须独立成段,段落之间用空行分隔**
4. **技术面分析部分,每个指标必须使用加粗标题(**标题**)并独立成段**
5. 分析要客观理性,基于数据而非情绪
3. 分析要客观理性,基于数据而非情绪
4. 结论要明确,不要模棱两可
5. 控制在500-600字
6. 最后必须声明:"以上分析仅供参考,不构成投资建议。股市有风险,投资需谨慎。"
"""
try:
analysis = llm_service.chat(
messages=[{"role": "user", "content": prompt}],
temperature=0.7,
max_tokens=2000
)
if analysis:
return f"{data['stock_name']}({data['stock_code']}) - AI深度分析】\n\n{analysis}"
else:
return self._rule_based_analysis(data)
except Exception as e:
logger.error(f"LLM分析失败: {e}")
return self._rule_based_analysis(data)
async def _llm_single_analysis(
self,
intent: Dict[str, Any],
result: Dict[str, Any],
stock_code: str,
stock_name: Optional[str],
user_message: str
) -> Dict[str, Any]:
"""使用LLM对单一查询进行分析"""
data = result.get("data", result)
display_name = stock_name or stock_code
# 根据查询类型构建不同的prompt
if intent["type"] == "technical":
prompt = f"""你是一位专业的股票分析师。用户询问了{display_name}({stock_code})的技术指标。
用户问题:{user_message}
【技术指标数据】
{json.dumps(data, ensure_ascii=False, indent=2)}
请进行专业的技术分析:
## 技术指标解读
1. 均线系统分析:
- 短期均线MA5、MA10与长期均线MA20、MA60的位置关系
- 判断当前趋势(多头/空头/震荡)
2. MACD指标分析
- DIF和DEA的位置关系
- MACD柱状图的变化趋势
- 判断动能强弱
3. RSI指标分析
- 当前RSI值的位置超买/超卖/中性)
- 短期可能的走势
4. KDJ指标分析如有
- K、D、J值的位置关系
- 金叉/死叉信号
## 综合判断
- 短期走势预判1-2周
- 关键支撑位和压力位
- 操作建议(买入/持有/观望/减仓)
## 风险提示
- 主要技术风险点
写作要求:
1. 语言简洁专业,直接给出分析结论
2. 基于数据进行分析,不要编造
3. 控制在300-400字
4. 最后声明:"以上分析仅供参考,不构成投资建议。股市有风险,投资需谨慎。"
"""
elif intent["type"] == "quote":
prompt = f"""你是一位专业的股票分析师。用户询问了{display_name}({stock_code})的实时行情。
用户问题:{user_message}
【实时行情数据】
{json.dumps(data, ensure_ascii=False, indent=2)}
请进行专业的行情分析:
## 行情解读
1. 当日表现:
- 涨跌幅分析
- 成交量分析
- 振幅分析
2. 价格位置:
- 当前价格相对开盘价、最高价、最低价的位置
- 判断多空力量对比
3. 短期判断:
- 当日走势特征
- 短期可能的走势
- 操作建议
写作要求:
1. 语言简洁专业,直接给出分析结论
2. 基于数据进行分析,不要编造
3. 控制在200-300字
4. 最后声明:"以上分析仅供参考,不构成投资建议。股市有风险,投资需谨慎。"
"""
elif intent["type"] == "fundamental":
prompt = f"""你是一位专业的股票分析师。用户询问了{display_name}({stock_code})的基本面信息。
用户问题:{user_message}
【基本面数据】
{json.dumps(data, ensure_ascii=False, indent=2)}
请进行专业的基本面分析:
## 公司概况
- 公司主营业务
- 所属行业和地域
- 上市时间和市场
## 行业分析
- 所属行业的发展前景
- 行业地位和竞争优势
## 投资价值
- 基本面评估
- 长期投资价值
- 关注要点
写作要求:
1. 语言简洁专业,直接给出分析结论
2. 基于数据进行分析,不要编造
3. 控制在200-300字
4. 最后声明:"以上分析仅供参考,不构成投资建议。股市有风险,投资需谨慎。"
"""
else:
# 其他类型,使用通用分析
prompt = f"""你是一位专业的股票分析师。用户询问了{display_name}({stock_code})的相关信息。
用户问题:{user_message}
【数据】
{json.dumps(data, ensure_ascii=False, indent=2)}
请基于提供的数据进行专业分析,给出有价值的见解和建议。
写作要求:
1. 语言简洁专业,直接给出分析结论
2. 基于数据进行分析,不要编造
3. 控制在200-300字
4. 最后声明:"以上分析仅供参考,不构成投资建议。股市有风险,投资需谨慎。"
"""
try:
analysis = llm_service.chat(
messages=[{"role": "user", "content": prompt}],
temperature=0.7,
max_tokens=1500
)
if analysis:
return {
"message": f"{display_name}({stock_code}) - AI分析】\n\n{analysis}",
"metadata": {"type": intent["type"], "data": data}
}
else:
# LLM失败使用原始格式化
return self._format_response(intent, result, stock_code, stock_name)
except Exception as e:
logger.error(f"LLM单一分析失败: {e}")
return self._format_response(intent, result, stock_code, stock_name)
def _rule_based_analysis(self, data: Dict[str, Any]) -> str:
"""基于规则的分析LLM不可用时的备选方案"""
parts = [f"{data['stock_name']}({data['stock_code']}) - 综合分析】\n"]
# 行情信息
if data.get('quote'):
quote = data['quote']
parts.append("## 一、实时行情")
parts.append(f"最新价:{quote.get('close', 0):.2f}")
parts.append(f"涨跌幅:{quote.get('pct_chg', 0):.2f}%")
parts.append(f"成交量:{quote.get('vol', 0):.0f}")
parts.append("")
# 技术分析
if data.get('technical'):
tech = data['technical'].get('indicators', {})
parts.append("## 二、技术指标")
if 'ma' in tech:
ma = tech['ma']
parts.append(f"均线系统MA5={ma.get('ma5')}, MA10={ma.get('ma10')}, MA20={ma.get('ma20')}")
if 'macd' in tech:
macd = tech['macd']
parts.append(f"MACDDIF={macd.get('dif')}, DEA={macd.get('dea')}")
if 'rsi' in tech:
rsi = tech['rsi']
rsi6 = rsi.get('rsi6', 50)
if rsi6 > 70:
parts.append(f"RSI{rsi6:.1f}(超买区域,注意回调风险)")
elif rsi6 < 30:
parts.append(f"RSI{rsi6:.1f}(超卖区域,可能存在反弹机会)")
else:
parts.append(f"RSI{rsi6:.1f}(中性区域)")
parts.append("")
# 基本面
if data.get('fundamental'):
fund = data['fundamental']
parts.append("## 三、基本信息")
parts.append(f"所属行业:{fund.get('industry', '未知')}")
parts.append(f"上市日期:{fund.get('list_date', '未知')}")
parts.append("")
# 简单建议
parts.append("## 四、参考建议")
parts.append("建议结合更多信息进行综合判断。")
parts.append("")
parts.append("⚠️ 以上分析仅供参考,不构成投资建议。股市有风险,投资需谨慎。")
return "\n".join(parts)
async def _single_query(
self,
stock_code: str,
stock_name: Optional[str],
message: str
) -> Dict[str, Any]:
"""单一查询处理 - 使用LLM进行分析"""
# 识别意图
intent = self._recognize_intent(message, stock_code)
# 执行技能
result = await skill_manager.execute_skill(
intent["skill"],
**intent["params"]
)
# 格式化响应
if not result.get("success", True):
return {
"message": f"查询失败:{result.get('error', '未知错误')}",
"metadata": {"type": "error"}
}
# 所有查询都使用LLM进行分析除了可视化
if intent["type"] != "visualization" and self.use_llm:
return await self._llm_single_analysis(intent, result, stock_code, stock_name, message)
else:
return self._format_response(intent, result, stock_code, stock_name)
def _recognize_intent(self, message: str, stock_code: str) -> Dict[str, Any]:
"""识别查询意图"""
message_lower = message.lower()
# K线图
if any(kw in message_lower for kw in ["k线", "图表", "走势图", "kline"]):
return {
"type": "visualization",
"skill": "visualization",
"params": {"stock_code": stock_code}
}
# 技术分析
if any(kw in message_lower for kw in ["技术", "指标", "macd", "rsi", "均线"]):
return {
"type": "technical",
"skill": "technical_analysis",
"params": {"stock_code": stock_code, "indicators": ["ma", "macd", "rsi"]}
}
# 基本面
if any(kw in message_lower for kw in ["基本面", "公司", "行业", "信息"]):
return {
"type": "fundamental",
"skill": "fundamental",
"params": {"stock_code": stock_code}
}
# 默认:实时行情
return {
"type": "quote",
"skill": "market_data",
"params": {"stock_code": stock_code, "data_type": "quote"}
}
def _format_response(
self,
intent: Dict[str, Any],
result: Dict[str, Any],
stock_code: str,
stock_name: Optional[str]
) -> Dict[str, Any]:
"""格式化响应"""
data = result.get("data", result)
display_name = stock_name or stock_code
if intent["type"] == "quote":
message = f"""{display_name}】实时行情
交易日期:{data.get('trade_date', '')}
最新价:{data.get('close', 0):.2f}
涨跌幅:{data.get('pct_chg', 0):+.2f}%
涨跌额:{data.get('change', 0):+.2f}
开盘价:{data.get('open', 0):.2f}
最高价:{data.get('high', 0):.2f}
最低价:{data.get('low', 0):.2f}
成交量:{data.get('vol', 0):.0f}
成交额:{data.get('amount', 0):.0f}千元"""
return {
"message": message,
"metadata": {"type": "quote", "data": data}
}
elif intent["type"] == "technical":
indicators = data.get("indicators", {})
parts = [f"{display_name}】技术指标\n"]
if "ma" in indicators:
ma = indicators["ma"]
parts.append(f"均线MA5={ma.get('ma5')}, MA10={ma.get('ma10')}, MA20={ma.get('ma20')}")
if "macd" in indicators:
macd = indicators["macd"]
parts.append(f"MACDDIF={macd.get('dif')}, DEA={macd.get('dea')}, MACD={macd.get('macd')}")
if "rsi" in indicators:
rsi = indicators["rsi"]
parts.append(f"RSIRSI6={rsi.get('rsi6')}, RSI12={rsi.get('rsi12')}")
return {
"message": "\n".join(parts),
"metadata": {"type": "technical", "data": data}
}
elif intent["type"] == "visualization":
return {
"message": f"已生成【{display_name}】的K线图",
"metadata": {"type": "chart", "data": data}
}
elif intent["type"] == "fundamental":
message = f"""{display_name}】基本信息
股票代码:{data.get('ts_code', '')}
所属地域:{data.get('area', '')}
所属行业:{data.get('industry', '')}
上市市场:{data.get('market', '')}
上市日期:{data.get('list_date', '')}"""
return {
"message": message,
"metadata": {"type": "fundamental", "data": data}
}
return {
"message": "查询完成",
"metadata": {"type": "data", "data": data}
}
async def _analyze_question_intent(self, message: str) -> Optional[Dict[str, Any]]:
"""
使用LLM分析问题意图
Args:
message: 用户消息
Returns:
意图分析结果: {
'type': 'stock_specific' | 'macro_finance' | 'knowledge',
'description': '问题描述',
'keywords': ['关键词列表'],
'stock_names': ['股票名称'] (如果是stock_specific类型)
}
"""
if not self.use_llm:
logger.warning("LLM未配置无法分析意图")
return None
prompt = f"""分析用户的金融问题,判断问题类型和关键信息。
用户问题:{message}
请分析这个问题属于以下哪一类:
1. **stock_specific** - 针对特定股票的问题
例如:"贵州茅台怎么样""分析一下比亚迪""600519的技术指标"
2. **macro_finance** - 宏观金融问题(不针对特定股票)
例如:"现在A股市场怎么样""最近有什么投资机会""如何看待当前经济形势"
3. **knowledge** - 金融知识问答
例如:"什么是MACD""如何看K线图""价值投资是什么"
请以JSON格式返回分析结果
{{
"type": "问题类型",
"description": "问题的简要描述",
"keywords": ["关键词1", "关键词2"],
"stock_names": ["股票名称"] (仅当type为stock_specific时)
}}
只返回JSON不要有任何其他内容。"""
try:
result = llm_service.chat(
messages=[{"role": "user", "content": prompt}],
temperature=0.3,
max_tokens=300
)
if not result:
logger.warning("LLM返回空结果")
return None
# 清理结果移除可能的markdown代码块标记
result = result.strip()
if result.startswith("```json"):
result = result[7:]
if result.startswith("```"):
result = result[3:]
if result.endswith("```"):
result = result[:-3]
result = result.strip()
# 检查是否为空
if not result:
logger.warning("LLM返回内容为空")
return None
# 解析JSON
intent = json.loads(result)
logger.info(f"意图分析结果: {intent}")
return intent
except json.JSONDecodeError as e:
logger.error(f"意图分析JSON解析失败: {e}, 原始响应: {result[:200] if result else 'None'}")
return None
except Exception as e:
logger.error(f"意图分析失败: {e}")
return None
async def _handle_stock_question(
self,
intent_analysis: Dict[str, Any],
message: str
) -> Dict[str, Any]:
"""处理针对特定股票的问题"""
stock_names = intent_analysis.get('stock_names', [])
if not stock_names:
return {
"message": "抱歉,我没有识别到您提到的股票。请提供更明确的股票代码或名称。",
"metadata": {"type": "error"}
}
# 提取第一个股票(暂时只处理单只股票)
stock_keyword = stock_names[0]
# 使用Tushare搜索股票
search_results = tushare_service.search_stock(stock_keyword)
if not search_results:
return {
"message": f"抱歉,未找到股票\"{stock_keyword}\"。请确认股票名称或代码是否正确。",
"metadata": {"type": "error"}
}
stock = search_results[0]
stock_code = stock['symbol']
stock_name = stock['name']
logger.info(f"处理股票问题: {stock_name}({stock_code})")
# 判断是否需要全面分析
is_comprehensive = self._is_comprehensive_analysis(message)
if is_comprehensive:
return await self._comprehensive_analysis(stock_code, stock_name, message)
else:
return await self._single_query(stock_code, stock_name, message)
async def _handle_macro_question(
self,
intent_analysis: Dict[str, Any],
message: str
) -> Dict[str, Any]:
"""处理宏观金融问题"""
keywords = intent_analysis.get('keywords', [])
description = intent_analysis.get('description', '')
logger.info(f"处理宏观问题: {description}")
# 使用Brave搜索获取最新信息
search_query = f"A股市场 {' '.join(keywords)} 最新分析"
try:
news_result = await skill_manager.execute_skill(
"brave_search",
query=search_query,
search_type="news",
count=5,
freshness="pw"
)
# 构建新闻摘要
news_summary = ""
if news_result and not news_result.get("error"):
results = news_result.get("results", [])
if results:
news_summary = "\n【最新市场动态】\n"
for idx, news_item in enumerate(results[:5], 1):
news_summary += f"{idx}. {news_item.get('title', '无标题')}\n"
news_summary += f" 来源: {news_item.get('source', '未知')}\n"
news_summary += f" 时间: {news_item.get('published', '未知')}\n\n"
# 使用LLM进行分析
prompt = f"""你是一位专业的金融分析师。用户询问了宏观金融问题。
用户问题:{message}
问题分析:{description}
关键词:{', '.join(keywords)}
{news_summary}
请基于当前市场情况和最新动态,给出专业的分析和建议:
## 市场现状分析
- 当前市场整体情况
- 主要影响因素
## 趋势判断
- 短期趋势
- 中长期展望
## 投资建议
- 投资策略建议
- 风险提示
写作要求:
1. 语言简洁专业,避免过度修饰
2. 分析要客观理性,基于事实
3. 控制在400-500字
4. 最后声明:"以上分析仅供参考,不构成投资建议。股市有风险,投资需谨慎。"
"""
analysis = llm_service.chat(
messages=[{"role": "user", "content": prompt}],
temperature=0.7,
max_tokens=1500
)
if analysis:
return {
"message": f"【宏观市场分析】\n\n{analysis}",
"metadata": {"type": "macro_analysis"}
}
except Exception as e:
logger.error(f"宏观问题处理失败: {e}")
return {
"message": "抱歉,暂时无法获取相关信息。请稍后再试。",
"metadata": {"type": "error"}
}
async def _handle_knowledge_question(
self,
intent_analysis: Dict[str, Any],
message: str
) -> Dict[str, Any]:
"""处理金融知识问答"""
description = intent_analysis.get('description', '')
keywords = intent_analysis.get('keywords', [])
logger.info(f"处理知识问答: {description}")
# 直接使用LLM回答
prompt = f"""你是一位专业的金融教育专家。用户询问了金融知识问题。
用户问题:{message}
请用通俗易懂的语言解释这个概念或回答这个问题:
## 核心概念
- 清晰定义和解释
## 实际应用
- 如何在投资中应用
- 注意事项
## 举例说明
- 用简单的例子帮助理解
写作要求:
1. 语言通俗易懂,避免过多专业术语
2. 如果使用专业术语,要简单解释
3. 控制在300-400字
4. 重点是帮助用户理解,而不是炫耀知识
"""
try:
answer = llm_service.chat(
messages=[{"role": "user", "content": prompt}],
temperature=0.7,
max_tokens=1200
)
if answer:
return {
"message": f"【金融知识解答】\n\n{answer}",
"metadata": {"type": "knowledge"}
}
except Exception as e:
logger.error(f"知识问答处理失败: {e}")
return {
"message": "抱歉,暂时无法回答您的问题。请稍后再试。",
"metadata": {"type": "error"}
}
# 创建全局实例
smart_agent = SmartStockAgent()