erlangshen 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/.claude/agents/equity-agent.md +26 -0
  2. package/.claude/agents/macro-agent.md +25 -0
  3. package/.claude/commands/analyze.md +40 -0
  4. package/.claude/commands/macro.md +29 -0
  5. package/.claude/settings.json +12 -0
  6. package/CODEX_GOAL.md +46 -0
  7. package/README.md +206 -0
  8. package/bin/cli.js +67 -0
  9. package/bin/erlangshen +2 -0
  10. package/bin/xiaoergod +2 -0
  11. package/frontend/index.html +700 -0
  12. package/knowledge/crypto_guide.md +147 -0
  13. package/knowledge/economic_indicators.md +125 -0
  14. package/knowledge/financial_glossary.md +148 -0
  15. package/knowledge/first_principles.md +50 -0
  16. package/knowledge/first_principles_deep.md +115 -0
  17. package/knowledge/global_markets.md +173 -0
  18. package/knowledge/insights.md +141 -0
  19. package/knowledge/market_basics.md +116 -0
  20. package/knowledge/memos/session_20260513_003616.json +6 -0
  21. package/knowledge/memos/session_20260513_003822.json +6 -0
  22. package/knowledge/risk_management.md +151 -0
  23. package/knowledge/team_context.md +42 -0
  24. package/knowledge/trading_strategies.md +114 -0
  25. package/package.json +42 -0
  26. package/requirements.txt +14 -0
  27. package/scripts/postinstall.js +188 -0
  28. package/scripts/preuninstall.js +22 -0
  29. package/src/__init__.py +4 -0
  30. package/src/__pycache__/__init__.cpython-313.pyc +0 -0
  31. package/src/agents/__init__.py +3 -0
  32. package/src/agents/base.py +103 -0
  33. package/src/agents/base_agent.py +86 -0
  34. package/src/agents/equity.py +136 -0
  35. package/src/agents/equity_agent.py +91 -0
  36. package/src/agents/erlang.py +165 -0
  37. package/src/agents/macro.py +137 -0
  38. package/src/agents/macro_agent.py +81 -0
  39. package/src/agents/multi_asset.py +147 -0
  40. package/src/agents/multi_asset_agent.py +87 -0
  41. package/src/api/__init__.py +1 -0
  42. package/src/api/__pycache__/__init__.cpython-313.pyc +0 -0
  43. package/src/api/__pycache__/server.cpython-313.pyc +0 -0
  44. package/src/api/cli.py +435 -0
  45. package/src/api/cli_enhanced.py +537 -0
  46. package/src/api/server.py +266 -0
  47. package/src/brain.py +200 -0
  48. package/src/cli.py +153 -0
  49. package/src/commands/__init__.py +3 -0
  50. package/src/commands/analyze.py +131 -0
  51. package/src/commands/macro.py +100 -0
  52. package/src/commands/memo.py +216 -0
  53. package/src/commands/portfolio.py +154 -0
  54. package/src/commands/report.py +228 -0
  55. package/src/commands/risk.py +183 -0
  56. package/src/commands/search.py +183 -0
  57. package/src/commands/stock.py +124 -0
  58. package/src/config.py +327 -0
  59. package/src/core/__init__.py +1 -0
  60. package/src/core/brain.py +645 -0
  61. package/src/core/cerebellum.py +175 -0
  62. package/src/core/investment_universe.py +423 -0
  63. package/src/core/knowledge.py +207 -0
  64. package/src/core/memory.py +115 -0
  65. package/src/hooks/__init__.py +3 -0
  66. package/src/hooks/session_end.py +57 -0
  67. package/src/hooks/session_start.py +75 -0
  68. package/src/knowledge/__init__.py +1 -0
  69. package/src/mcp/__init__.py +3 -0
  70. package/src/mcp/feishu.py +331 -0
  71. package/src/mcp/fund_tools.py +323 -0
  72. package/src/mcp/macro.py +452 -0
  73. package/src/mcp/market.py +331 -0
  74. package/src/mcp/registry.py +168 -0
  75. package/src/network/__init__.py +15 -0
  76. package/src/network/detector.py +125 -0
  77. package/src/network/proxy.py +199 -0
  78. package/src/network/router.py +103 -0
  79. package/src/prompts/__init__.py +1 -0
  80. package/src/prompts/analysis_framework.md +164 -0
  81. package/src/prompts/persona.md +65 -0
  82. package/src/prompts/report_template.md +144 -0
  83. package/src/skills/__init__.py +3 -0
  84. package/src/skills/framework.py +105 -0
  85. package/src/skills/templates.py +342 -0
  86. package/src/tools/__init__.py +1 -0
  87. package/src/tools/file_tools.py +209 -0
  88. package/src/tools/macro_tools.py +152 -0
  89. package/src/tools/market_tools.py +1172 -0
  90. package/src/tools/registry.py +398 -0
  91. package/src/tools/search_tools.py +777 -0
  92. package/tests/__init__.py +1 -0
  93. package/tests/test_erlangshen.py +140 -0
@@ -0,0 +1,645 @@
1
+ """
2
+ Brain - LLM大脑接口
3
+ 任务感知型知识路由:选择性加载对应知识/能力
4
+ """
5
+ import os
6
+ import json
7
+ import httpx
8
+ from pathlib import Path
9
+ from typing import Optional, Any, Literal
10
+ from enum import Enum
11
+ from pydantic import BaseModel, Field
12
+ from loguru import logger
13
+
14
+
15
+ # ============================================================
16
+ # 任务类型定义
17
+ # ============================================================
18
+ class TaskType(str, Enum):
19
+ """任务类型枚举"""
20
+ # 轻量级任务 - 不需要深度分析框架
21
+ QUERY_PRICE = "query_price" # 行情查询
22
+ QUERY_MACRO = "query_macro" # 宏观数据查询
23
+ QUICK_FACT = "quick_fact" # 快速事实查询
24
+ GENERAL_CHAT = "general_chat" # 日常对话
25
+
26
+ # 中等任务 - 需要基础分析能力
27
+ STOCK_ANALYSIS = "stock_analysis" # 个股分析
28
+ FUND_ANALYSIS = "fund_analysis" # 基金分析
29
+ RISK_ASSESSMENT = "risk_assessment" # 风险评估
30
+
31
+ # 深度任务 - 需要完整分析框架
32
+ DEEP_RESEARCH = "deep_research" # 深度研究
33
+ INDUSTRY_OUTLOOK = "industry_outlook" # 产业格局分析(自上而下)
34
+ MACRO_STRATEGY = "macro_strategy" # 宏观策略分析
35
+ PORTFOLIO_OPT = "portfolio_optimization" # 组合优化
36
+
37
+ # 综合任务 - 需要多维度分析
38
+ COMPREHENSIVE = "comprehensive" # 综合分析
39
+
40
+
41
+ # ============================================================
42
+ # 知识模块定义
43
+ # ============================================================
44
+ class KnowledgeModule:
45
+ """知识模块定义"""
46
+ def __init__(
47
+ self,
48
+ name: str,
49
+ path: str,
50
+ keywords: list[str],
51
+ description: str = "",
52
+ ):
53
+ self.name = name
54
+ self.path = path
55
+ self.keywords = keywords
56
+ self.description = description
57
+ self._content: Optional[str] = None
58
+
59
+ def load(self) -> str:
60
+ """按需加载知识内容"""
61
+ if self._content is None:
62
+ try:
63
+ p = Path(self.path)
64
+ if p.exists():
65
+ self._content = p.read_text(encoding="utf-8")
66
+ else:
67
+ self._content = ""
68
+ except Exception as e:
69
+ logger.warning(f"Failed to load knowledge {self.name}: {e}")
70
+ self._content = ""
71
+ return self._content
72
+
73
+ def match_score(self, query: str) -> float:
74
+ """计算query与本知识模块的匹配度"""
75
+ query_lower = query.lower()
76
+ score = 0.0
77
+ for kw in self.keywords:
78
+ if kw.lower() in query_lower:
79
+ score += 1.0
80
+ return score
81
+
82
+
83
+ # ============================================================
84
+ # 知识库注册表
85
+ # ============================================================
86
+ class KnowledgeRegistry:
87
+ """知识库注册表 - 管理所有可用的知识模块"""
88
+
89
+ def __init__(self, base_path: Path):
90
+ self.base_path = base_path
91
+ self.modules: dict[str, KnowledgeModule] = {}
92
+ self._register_builtin_modules()
93
+
94
+ def _register_builtin_modules(self):
95
+ """注册内置知识模块"""
96
+
97
+ # 第一性原理 - 精简核心版(轻量)
98
+ fp_light_path = self.base_path / "knowledge" / "first_principles.md"
99
+ self.register(KnowledgeModule(
100
+ name="first_principles",
101
+ path=str(fp_light_path),
102
+ keywords=["第一性", "底层逻辑", "产业格局", "技术革命", "能源革命",
103
+ "信息革命", "变革", "格局", "趋势判断", "未来预判",
104
+ "deep_research", "first_principle"],
105
+ description="第一性原理认知框架(精简版)",
106
+ ))
107
+
108
+ # 第一性原理 - 深度版(按需加载)
109
+ fp_deep_path = self.base_path / "knowledge" / "first_principles_deep.md"
110
+ self.register(KnowledgeModule(
111
+ name="first_principles_deep",
112
+ path=str(fp_deep_path),
113
+ keywords=["deep_research", "深度研究", "详细分析", "完整框架"],
114
+ description="第一性原理详细框架(深度研究用)",
115
+ ))
116
+
117
+ # 宏观知识库
118
+ macro_path = self.base_path / "knowledge" / "economic_indicators.md"
119
+ self.register(KnowledgeModule(
120
+ name="economic_indicators",
121
+ path=str(macro_path),
122
+ keywords=["gdp", "通胀", "cpi", "ppi", "利率", "货币政策",
123
+ "财政政策", "宏观", "经济指标", "经济数据"],
124
+ description="宏观经济指标知识库",
125
+ ))
126
+
127
+ # 市场基础知识
128
+ market_path = self.base_path / "knowledge" / "market_basics.md"
129
+ self.register(KnowledgeModule(
130
+ name="market_basics",
131
+ path=str(market_path),
132
+ keywords=["市场", "股票", "债券", "基金", "etf", "期货",
133
+ "交易", "市值", "估值", "市盈率"],
134
+ description="市场基础知识",
135
+ ))
136
+
137
+ # 风险知识
138
+ risk_path = self.base_path / "knowledge" / "risk_management.md"
139
+ self.register(KnowledgeModule(
140
+ name="risk_management",
141
+ path=str(risk_path),
142
+ keywords=["风险", "回撤", "止损", "仓位", "杠杆", "风控",
143
+ "波动率", "夏普", "最大回撤"],
144
+ description="风险管理知识",
145
+ ))
146
+
147
+ # 交易策略
148
+ strategy_path = self.base_path / "knowledge" / "trading_strategies.md"
149
+ self.register(KnowledgeModule(
150
+ name="trading_strategies",
151
+ path=str(strategy_path),
152
+ keywords=["策略", "轮动", "趋势", "动量", "反转", "套利",
153
+ "cta", "量化", "因子", "阿尔法"],
154
+ description="交易策略知识库",
155
+ ))
156
+
157
+ # 团队洞察
158
+ insights_path = self.base_path / "knowledge" / "insights.md"
159
+ self.register(KnowledgeModule(
160
+ name="team_insights",
161
+ path=str(insights_path),
162
+ keywords=["团队", "agent", "协作", "配合", "二郎神",
163
+ "v19", "宏观策略", "etf轮动"],
164
+ description="团队洞察和经验总结",
165
+ ))
166
+
167
+ # 团队上下文
168
+ context_path = self.base_path / "knowledge" / "team_context.md"
169
+ self.register(KnowledgeModule(
170
+ name="team_context",
171
+ path=str(context_path),
172
+ keywords=["团队成员", "agent-01", "agent-02", "分工",
173
+ "私募", "公募", "cta", "数据", "营销"],
174
+ description="团队上下文和Agent分工",
175
+ ))
176
+
177
+ # 全球市场
178
+ global_path = self.base_path / "knowledge" / "global_markets.md"
179
+ self.register(KnowledgeModule(
180
+ name="global_markets",
181
+ path=str(global_path),
182
+ keywords=["美股", "港股", "欧股", "日经", "纳斯达克",
183
+ "标普", "道琼斯", "恒生", "外汇", "美元", "欧元"],
184
+ description="全球市场知识",
185
+ ))
186
+
187
+ logger.info(f"KnowledgeRegistry initialized with {len(self.modules)} modules")
188
+
189
+ def register(self, module: KnowledgeModule):
190
+ """注册知识模块"""
191
+ self.modules[module.name] = module
192
+
193
+ def select_for_task(self, task_type: TaskType, query: str) -> list[KnowledgeModule]:
194
+ """根据任务类型选择需要加载的知识模块"""
195
+ selected = []
196
+ query_lower = query.lower()
197
+
198
+ # 根据任务类型决定必须加载的模块
199
+ required_modules = {
200
+ TaskType.DEEP_RESEARCH: ["first_principles", "team_insights"],
201
+ TaskType.INDUSTRY_OUTLOOK: ["first_principles", "economic_indicators", "market_basics"],
202
+ TaskType.MACRO_STRATEGY: ["first_principles", "economic_indicators", "risk_management", "trading_strategies"],
203
+ TaskType.PORTFOLIO_OPT: ["risk_management", "trading_strategies", "market_basics"],
204
+ TaskType.COMPREHENSIVE: ["first_principles", "economic_indicators", "market_basics", "team_insights"],
205
+ TaskType.STOCK_ANALYSIS: ["market_basics", "risk_management", "global_markets"],
206
+ TaskType.FUND_ANALYSIS: ["market_basics", "team_insights"],
207
+ TaskType.RISK_ASSESSMENT: ["risk_management"],
208
+ TaskType.QUERY_PRICE: [],
209
+ TaskType.QUERY_MACRO: ["economic_indicators"],
210
+ TaskType.QUICK_FACT: [],
211
+ TaskType.GENERAL_CHAT: [],
212
+ }
213
+
214
+ # 添加任务类型要求的模块
215
+ for module_name in required_modules.get(task_type, []):
216
+ if module_name in self.modules:
217
+ selected.append(self.modules[module_name])
218
+
219
+ # 基于query关键词追加匹配度高的模块
220
+ for name, module in self.modules.items():
221
+ if module not in selected:
222
+ if module.match_score(query_lower) > 0:
223
+ selected.append(module)
224
+
225
+ return selected
226
+
227
+
228
+ # ============================================================
229
+ # 任务分类器
230
+ # ============================================================
231
+ class TaskClassifier:
232
+ """任务分类器 - 判断用户查询属于哪种任务类型"""
233
+
234
+ # 关键词到任务类型的映射
235
+ KEYWORD_MAP = {
236
+ TaskType.QUERY_PRICE: ["价格", "行情", "现在多少", "涨了多少", "报价",
237
+ "看了一眼", "现在价格", "收盘价", "开盘价"],
238
+ TaskType.QUERY_MACRO: ["数据", "指标", "是多少", "公布了", "统计局"],
239
+ TaskType.QUICK_FACT: ["什么是", "什么叫", "定义", "解释一下"],
240
+ TaskType.STOCK_ANALYSIS: ["股票", "个股", "茅台", "苹果", "估值", "基本面",
241
+ "业绩", "利润", "营收", "值得买吗"],
242
+ TaskType.FUND_ANALYSIS: ["基金", "私募", "公募", "etf", "净值", "基金经理"],
243
+ TaskType.RISK_ASSESSMENT: ["风险", "回撤", "能承受", "亏损", "止损"],
244
+ TaskType.DEEP_RESEARCH: ["深度", "研究", "分析报告", "怎么看", "底层逻辑",
245
+ "第一性", "本质", "核心逻辑", "deepseek", "chatgpt", "突破",
246
+ "意味着", "颠覆", "革命", "变革", "跃升"],
247
+ TaskType.INDUSTRY_OUTLOOK: ["产业格局", "行业", "赛道", "竞争格局", "格局分析",
248
+ "产业链", "上中下游", "技术路线", "市场规模"],
249
+ TaskType.MACRO_STRATEGY: ["宏观", "经济周期", "货币政策", "财政政策", "大类资产",
250
+ "配置", "利率走势", "通胀预期", "经济形势"],
251
+ TaskType.PORTFOLIO_OPT: ["组合", "配置", "仓位", "分散", "优化", "权重"],
252
+ TaskType.COMPREHENSIVE: ["综合", "全面", "整体", "分析一下当前"],
253
+ }
254
+
255
+ @classmethod
256
+ def classify(cls, query: str) -> TaskType:
257
+ """
258
+ 分类用户查询
259
+
260
+ Args:
261
+ query: 用户查询文本
262
+
263
+ Returns:
264
+ TaskType 任务类型
265
+ """
266
+ query_lower = query.lower()
267
+
268
+ # 遍历所有任务类型,找关键词匹配
269
+ best_match = TaskType.GENERAL_CHAT
270
+ best_score = 0
271
+
272
+ for task_type, keywords in cls.KEYWORD_MAP.items():
273
+ score = sum(1 for kw in keywords if kw.lower() in query_lower)
274
+ if score > best_score:
275
+ best_score = score
276
+ best_match = task_type
277
+
278
+ # 如果没有任何关键词匹配,但query较长,认为是深度任务
279
+ if best_score == 0 and len(query) > 50:
280
+ best_match = TaskType.COMPREHENSIVE
281
+
282
+ logger.info(f"TaskClassifier: '{query[:30]}...' -> {best_match.value}")
283
+ return best_match
284
+
285
+
286
+ # ============================================================
287
+ # 基础系统提示
288
+ # ============================================================
289
+ BASE_SYSTEM_PROMPT = """你是二郎神 - 一位专业的AI投资智能体。
290
+
291
+ 【核心定位】
292
+ - 全知全能,擅长投资分析、风险评估、资产配置
293
+ - 数据驱动,逻辑严谨
294
+ - 团队协作,整合私募FOF、公募FOF、CTA量化、宏观策略等多领域知识
295
+
296
+ 【沟通风格】
297
+ - 专业但不生硬
298
+ - 直接但有深度
299
+ - 简洁但有洞见
300
+
301
+ 【工作原则】
302
+ - 数据真实性优先,不用模拟数据
303
+ - 明确给出结论和置信度
304
+ - 指出风险和限制
305
+ """
306
+
307
+
308
+ # ============================================================
309
+ # 任务专用提示词
310
+ # ============================================================
311
+ TASK_SYSTEM_PROMPTS = {
312
+ TaskType.QUERY_PRICE: """你正在处理行情查询任务。
313
+ 要求:简洁、直接给出数据,不需要多余分析。""",
314
+
315
+ TaskType.QUERY_MACRO: """你正在处理宏观数据查询任务。
316
+ 要求:准确提供数据指标,注明数据来源和更新时间。""",
317
+
318
+ TaskType.QUICK_FACT: """你正在回答一个概念性问题。
319
+ 要求:清晰解释概念,必要时举例说明。""",
320
+
321
+ TaskType.GENERAL_CHAT: """你正在与用户进行日常对话。
322
+ 要求:友好、专业、有帮助,不需要过度分析。""",
323
+
324
+ TaskType.STOCK_ANALYSIS: """你正在分析个股。
325
+ 分析维度:基本面(估值、业绩、成长性)、市场情绪、技术面、风险因素。
326
+ 输出结构:结论 → 支撑逻辑 → 风险提示。""",
327
+
328
+ TaskType.FUND_ANALYSIS: """你正在分析基金产品。
329
+ 分析维度:历史业绩、风险调整收益、基金经理能力、策略有效性。
330
+ 输出结构:评级 → 业绩归因 → 风险评估 → 适用场景。""",
331
+
332
+ TaskType.RISK_ASSESSMENT: """你正在评估风险。
333
+ 分析维度:市场风险、信用风险、流动性风险、尾部风险。
334
+ 输出结构:风险点识别 → 程度评估 → 缓解措施。""",
335
+
336
+ TaskType.DEEP_RESEARCH: """你正在进行深度研究。
337
+ 这是最重要的分析任务,需要:
338
+ 1. 第一性原理分析:判断这是信息革命还是能量革命的突破?
339
+ 2. 多维度验证:技术面、基本面、资金面、情绪面
340
+ 3. 历史比较:类似变革的发展规律
341
+ 4. 明确结论和置信度
342
+
343
+ 【第一性原理框架】
344
+ - 信息能力 = 计算力 × 传输效率 × 智能水平
345
+ - 能量能力 = 能量密度 × 转化效率 × 获取便利性
346
+ - 判断:这是单维度突破还是双维度突破?
347
+ - 判断:处于技术成熟度的哪个阶段?""",
348
+
349
+ TaskType.INDUSTRY_OUTLOOK: """你正在进行产业格局分析(自上而下)。
350
+ 分析框架:
351
+ 1. 产业链全景:上中下游、竞争格局
352
+ 2. 变革驱动:第一性原理分析(信息/能量维度)
353
+ 3. 周期定位:导入期、成长期、成熟期、衰退期
354
+ 4. 投资逻辑:谁能受益?如何排序?
355
+ 5. 风险因素:技术路线风险、政策风险、竞争风险""",
356
+
357
+ TaskType.MACRO_STRATEGY: """你正在进行宏观策略分析。
358
+ 分析维度:
359
+ 1. 经济周期:增长、通胀、货币政策
360
+ 2. 资产配置:大宗商品、股票、债券、现金
361
+ 3. 风险预判:尾部风险、切换风险
362
+ 4. 策略建议:方向、幅度、时间窗口""",
363
+
364
+ TaskType.PORTFOLIO_OPT: """你正在优化投资组合。
365
+ 分析维度:
366
+ 1. 风险收益比最优化
367
+ 2. 相关性分散化
368
+ 3. 流动性管理
369
+ 4. 再平衡策略""",
370
+
371
+ TaskType.COMPREHENSIVE: """你正在进行综合分析。
372
+ 需要整合宏观、行业、公司多维度视角,给出全面、平衡的判断。""",
373
+ }
374
+
375
+
376
+ # ============================================================
377
+ # Brain 主类
378
+ # ============================================================
379
+ class AnalysisResult(BaseModel):
380
+ """分析结果"""
381
+ conclusion: str = Field(description="分析结论")
382
+ confidence: float = Field(description="置信度 0-1")
383
+ reasoning: str = Field(description="推理过程")
384
+ evidence: list[str] = Field(default_factory=list, description="支撑证据")
385
+ caveats: list[str] = Field(default_factory=list, description="风险提示")
386
+
387
+
388
+ class Reflection(BaseModel):
389
+ """反思结果"""
390
+ action: str = Field(description="执行的动作")
391
+ result: Any = Field(description="执行结果")
392
+ lessons: list[str] = Field(default_factory=list, description="经验教训")
393
+ improvement: str = Field(description="改进建议")
394
+
395
+
396
+ class Brain:
397
+ """LLM大脑接口 - 支持任务感知型知识路由"""
398
+
399
+ # 模型配置
400
+ MODELS = {
401
+ "deepseek": {
402
+ "model": "deepseek-v4-pro",
403
+ "api_base": "https://api.deepseek.com",
404
+ "api_key_env": "DEEPSEEK_API_KEY",
405
+ },
406
+ }
407
+
408
+ def __init__(
409
+ self,
410
+ provider: str = "deepseek",
411
+ api_key: Optional[str] = None,
412
+ base_path: Optional[str] = None,
413
+ ):
414
+ if provider not in self.MODELS:
415
+ raise ValueError(f"Unknown provider: {provider}. Available: {list(self.MODELS.keys())}")
416
+
417
+ cfg = self.MODELS[provider]
418
+ self.model = cfg["model"]
419
+ self.api_base = cfg["api_base"]
420
+ self.api_key = api_key or os.getenv(cfg["api_key_env"])
421
+ self.provider = provider
422
+
423
+ # 初始化知识库注册表
424
+ if base_path is None:
425
+ base_path = Path(__file__).parent.parent.parent
426
+ self.knowledge_registry = KnowledgeRegistry(Path(base_path))
427
+
428
+ # 任务分类器
429
+ self.task_classifier = TaskClassifier()
430
+
431
+ if not self.api_key:
432
+ logger.warning(f"{cfg['api_key_env']} not set, Brain will use mock mode")
433
+
434
+ def _get_client(self) -> httpx.AsyncClient:
435
+ """获取HTTP客户端"""
436
+ return httpx.AsyncClient(
437
+ base_url=self.api_base,
438
+ headers={
439
+ "Authorization": f"Bearer {self.api_key}",
440
+ "Content-Type": "application/json",
441
+ },
442
+ timeout=60.0,
443
+ )
444
+
445
+ def _build_system_prompt(
446
+ self,
447
+ task_type: TaskType,
448
+ query: str,
449
+ load_first_principles: bool = False,
450
+ ) -> str:
451
+ """构建系统提示 - 根据任务类型选择性组合"""
452
+
453
+ # 1. 基础提示
454
+ parts = [BASE_SYSTEM_PROMPT]
455
+
456
+ # 2. 任务专用提示
457
+ if task_type in TASK_SYSTEM_PROMPTS:
458
+ parts.append(TASK_SYSTEM_PROMPTS[task_type])
459
+
460
+ # 3. 按需加载知识模块
461
+ selected_modules = self.knowledge_registry.select_for_task(task_type, query)
462
+
463
+ if selected_modules:
464
+ parts.append("\n【相关知识】")
465
+ for module in selected_modules:
466
+ content = module.load()
467
+ if content:
468
+ parts.append(f"\n--- {module.name} ---\n{content[:3000]}") # 限制单模块长度
469
+
470
+ return "\n\n".join(parts)
471
+
472
+ async def think(
473
+ self,
474
+ prompt: str,
475
+ context: Optional[dict] = None,
476
+ system: Optional[str] = None,
477
+ task_type: Optional[TaskType] = None,
478
+ auto_classify: bool = True,
479
+ ) -> str:
480
+ """
481
+ 深度思考推理 - 任务感知型
482
+
483
+ Args:
484
+ prompt: 用户提示
485
+ context: 额外上下文
486
+ system: 额外系统提示(会覆盖自动判断)
487
+ task_type: 指定任务类型(可选,默认自动分类)
488
+ auto_classify: 是否自动分类任务(默认True)
489
+
490
+ Returns:
491
+ LLM生成的响应文本
492
+ """
493
+ if not self.api_key:
494
+ logger.info("Brain mock mode: returning simulated response")
495
+ return f"[Mock] 基于提示「{prompt[:50]}...」的思考结果"
496
+
497
+ # 自动分类任务类型
498
+ if task_type is None and auto_classify:
499
+ task_type = self.task_classifier.classify(prompt)
500
+
501
+ # 构建系统提示
502
+ if system:
503
+ sys_prompt = system
504
+ else:
505
+ sys_prompt = self._build_system_prompt(
506
+ task_type or TaskType.GENERAL_CHAT,
507
+ prompt,
508
+ )
509
+
510
+ messages = [
511
+ {"role": "system", "content": sys_prompt},
512
+ {"role": "user", "content": prompt},
513
+ ]
514
+
515
+ payload = {
516
+ "model": self.model,
517
+ "messages": messages,
518
+ "temperature": 0.7,
519
+ }
520
+ if context:
521
+ payload["max_tokens"] = context.get("max_tokens", 2000)
522
+
523
+ try:
524
+ client = self._get_client()
525
+ response = await client.post("/chat/completions", json=payload)
526
+ response.raise_for_status()
527
+ data = response.json()
528
+ return data["choices"][0]["message"]["content"]
529
+ except Exception as e:
530
+ logger.error(f"Brain think failed: {e}")
531
+ return f"[Error] 思考失败: {e}"
532
+
533
+ async def think_with_framework(
534
+ self,
535
+ prompt: str,
536
+ framework: Literal["first_principles", "macro", "quant", "risk"],
537
+ context: Optional[dict] = None,
538
+ ) -> str:
539
+ """
540
+ 指定框架的思考 - 用于需要强制使用特定框架的场景
541
+
542
+ Args:
543
+ prompt: 用户提示
544
+ framework: 框架类型
545
+ context: 额外上下文
546
+ """
547
+ if framework == "first_principles":
548
+ fp_content = self.knowledge_registry.modules.get("first_principles")
549
+ fp_text = fp_content.load() if fp_content else ""
550
+ system = f"""你正在使用【第一性原理】进行分析。
551
+
552
+ 【第一性原理框架】
553
+ {fp_text}
554
+
555
+ 分析要求:
556
+ 1. 判断变革的信息维度:计算/传输/智能哪个突破?
557
+ 2. 判断变革的能量维度:密度/效率/清洁哪个突破?
558
+ 3. 是单维度还是双维度突破?
559
+ 4. 处于技术成熟度的哪个阶段?
560
+ """
561
+ elif framework == "macro":
562
+ system = """你正在使用【宏观策略框架】进行分析。
563
+ 关注:经济周期、货币政策、财政政策、大类资产配置。"""
564
+ elif framework == "quant":
565
+ system = """你正在使用【量化分析框架】进行分析。
566
+ 关注:因子有效性、统计显著性、回测结果、样本外验证。"""
567
+ elif framework == "risk":
568
+ system = """你正在使用【风险管理框架】进行分析。
569
+ 关注:尾部风险、相关性风险、流动性风险、情景测试。"""
570
+ else:
571
+ system = None
572
+
573
+ return await self.think(prompt, context=context, system=system, task_type=TaskType.DEEP_RESEARCH)
574
+
575
+ async def analyze(
576
+ self,
577
+ query: str,
578
+ data: Optional[dict] = None,
579
+ framework: Optional[str] = None,
580
+ ) -> AnalysisResult:
581
+ """
582
+ 分析能力 - 对输入进行结构化分析
583
+
584
+ Args:
585
+ query: 分析问题
586
+ data: 补充数据
587
+ framework: 分析框架名称
588
+
589
+ Returns:
590
+ AnalysisResult 结构化分析结果
591
+ """
592
+ # 确定任务类型
593
+ task_type = self.task_classifier.classify(query)
594
+
595
+ system_prompt = """你是一位专业的投资分析师。你的分析必须:
596
+ 1. 逻辑严谨,数据驱动
597
+ 2. 给出明确的结论和置信度
598
+ 3. 说明推理过程和支撑证据
599
+ 4. 指出潜在风险和限制
600
+
601
+ 请以JSON格式返回,包含字段:conclusion, confidence, reasoning, evidence, caveats"""
602
+
603
+ user_prompt = f"分析问题:{query}\n"
604
+ if data:
605
+ user_prompt += f"相关数据:{json.dumps(data, ensure_ascii=False, indent=2)}\n"
606
+
607
+ try:
608
+ result_text = await self.think(
609
+ user_prompt,
610
+ system=system_prompt,
611
+ task_type=task_type,
612
+ )
613
+ # 尝试解析JSON
614
+ result_text = result_text.strip()
615
+ if result_text.startswith("```json"):
616
+ result_text = result_text[7:]
617
+ if result_text.startswith("```"):
618
+ result_text = result_text[3:]
619
+ if result_text.endswith("```"):
620
+ result_text = result_text[:-3]
621
+ result_data = json.loads(result_text.strip())
622
+ return AnalysisResult(**result_data)
623
+ except json.JSONDecodeError:
624
+ logger.warning(f"Failed to parse analysis as JSON, using fallback: {result_text[:100]}")
625
+ return AnalysisResult(
626
+ conclusion=result_text[:500],
627
+ confidence=0.5,
628
+ reasoning="JSON解析失败,返回原始文本",
629
+ evidence=[],
630
+ caveats=["可能存在解析误差"],
631
+ )
632
+
633
+ async def reflect(self, action: str, result: Any) -> Reflection:
634
+ """
635
+ 反思能力 - 对执行结果进行反思学习
636
+ """
637
+ prompt = f"反思以下投资决策:\n行动:{action}\n结果:{str(result)[:1000]}"
638
+ system_prompt = "你是一位经验丰富的投资顾问,请从成功和失败中学习,给出经验教训和改进建议。"
639
+
640
+ try:
641
+ response = await self.think(prompt, system=system_prompt, task_type=TaskType.QUICK_FACT)
642
+ return Reflection(action=action, result=result, lessons=[], improvement=response)
643
+ except Exception as e:
644
+ logger.error(f"Reflection failed: {e}")
645
+ return Reflection(action=action, result=result, lessons=[], improvement=str(e))