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,266 @@
1
+ """
2
+ FastAPI Server - 二郎神API服务
3
+ 提供REST API接口
4
+ """
5
+ import os
6
+ import sys
7
+ from pathlib import Path
8
+ from typing import Optional
9
+
10
+ # 添加src到路径
11
+ sys.path.insert(0, str(Path(__file__).parent.parent.parent))
12
+
13
+ from fastapi import FastAPI, HTTPException
14
+ from fastapi.middleware.cors import CORSMiddleware
15
+ from pydantic import BaseModel, Field
16
+ from loguru import logger
17
+ import uvicorn
18
+
19
+ from src.core.brain import Brain
20
+ from src.core.memory import Memory
21
+ from src.core.knowledge import KnowledgeBase
22
+ from src.core.cerebellum import Cerebellum
23
+ from src.tools.market_tools import MarketTools
24
+ from src.tools.macro_tools import MacroTools
25
+ from src.tools.search_tools import SearchTools
26
+ from src.tools.file_tools import FileTools
27
+ from src.agents.erlang import 二郎神
28
+
29
+ # 初始化应用
30
+ app = FastAPI(
31
+ title="二郎神 API",
32
+ description="全知全能的AI投资智能体API",
33
+ version="0.1.0",
34
+ )
35
+
36
+ app.add_middleware(
37
+ CORSMiddleware,
38
+ allow_origins=["*"],
39
+ allow_credentials=True,
40
+ allow_methods=["*"],
41
+ allow_headers=["*"],
42
+ )
43
+
44
+ # 全局实例
45
+ _brain: Optional[Brain] = None
46
+ _memory: Optional[Memory] = None
47
+ _knowledge: Optional[KnowledgeBase] = None
48
+ _cerebellum: Optional[Cerebellum] = None
49
+ _erlangshen: Optional[二郎神] = None
50
+
51
+
52
+ def get_erlangshen() -> 二郎神:
53
+ """获取或初始化二郎神实例"""
54
+ global _brain, _memory, _knowledge, _cerebellum, _erlangshen
55
+ if _erlangshen is None:
56
+ logger.info("Initializing 二郎神...")
57
+ _brain = Brain()
58
+ _memory = Memory()
59
+ _knowledge = KnowledgeBase()
60
+ _cerebellum = Cerebellum(brain=_brain, memory=_memory, knowledge=_knowledge)
61
+
62
+ # 初始化工具
63
+ market_tools = MarketTools()
64
+ macro_tools = MacroTools()
65
+ search_tools = SearchTools()
66
+ file_tools = FileTools()
67
+
68
+ tools = {
69
+ "market_tools": market_tools,
70
+ "macro_tools": macro_tools,
71
+ "search_tools": search_tools,
72
+ "file_tools": file_tools,
73
+ }
74
+
75
+ _erlangshen = 二郎神(
76
+ brain=_brain,
77
+ memory=_memory,
78
+ knowledge=_knowledge,
79
+ tools=tools,
80
+ )
81
+ logger.info("二郎神 initialized")
82
+ return _erlangshen
83
+
84
+
85
+ # === 请求/响应模型 ===
86
+
87
+ class AnalyzeRequest(BaseModel):
88
+ query: str = Field(description="分析问题")
89
+ context: Optional[dict] = Field(default=None, description="额外上下文")
90
+
91
+
92
+ class AnalyzeResponse(BaseModel):
93
+ status: str
94
+ result: dict
95
+
96
+
97
+ class MarketRequest(BaseModel):
98
+ symbol: str = Field(description="股票代码")
99
+ days: int = Field(default=30, description="历史天数")
100
+
101
+
102
+ class ReportRequest(BaseModel):
103
+ title: str = Field(description="报告标题")
104
+ content: str = Field(description="报告内容")
105
+
106
+
107
+ class ThinkRequest(BaseModel):
108
+ prompt: str = Field(description="用户输入")
109
+ context: Optional[dict] = Field(default=None, description="额外上下文")
110
+ system: Optional[str] = Field(default=None, description="系统提示")
111
+
112
+
113
+ # === API端点 ===
114
+
115
+ @app.get("/")
116
+ async def root():
117
+ """API根路径"""
118
+ return {
119
+ "name": "二郎神 API",
120
+ "version": "0.1.0",
121
+ "status": "running",
122
+ }
123
+
124
+
125
+ @app.get("/health")
126
+ async def health():
127
+ """健康检查"""
128
+ return {"status": "healthy"}
129
+
130
+
131
+ @app.post("/api/brain/think")
132
+ async def brain_think(request: ThinkRequest):
133
+ """
134
+ 直接调用 Brain 进行深度思考
135
+ 前端全息界面调用此接口
136
+ """
137
+ try:
138
+ erlangshen = get_erlangshen()
139
+ result = await erlangshen.brain.think(
140
+ prompt=request.prompt,
141
+ context=request.context,
142
+ system=request.system,
143
+ )
144
+ return {"status": "success", "result": result}
145
+ except Exception as e:
146
+ logger.error(f"Brain think failed: {e}")
147
+ raise HTTPException(status_code=500, detail=str(e))
148
+
149
+
150
+ @app.post("/analyze", response_model=AnalyzeResponse)
151
+ async def analyze(request: AnalyzeRequest):
152
+ """
153
+ 分析接口 - 二郎神的核心分析能力
154
+
155
+ 支持:
156
+ - 宏观经济分析
157
+ - 股票分析
158
+ - 多资产配置建议
159
+ - 综合投资分析
160
+ """
161
+ try:
162
+ erlangshen = get_erlangshen()
163
+ result = await erlangshen.process(request.query, request.context)
164
+ return AnalyzeResponse(status="success", result=result)
165
+ except Exception as e:
166
+ logger.error(f"Analyze failed: {e}")
167
+ raise HTTPException(status_code=500, detail=str(e))
168
+
169
+
170
+ @app.post("/query")
171
+ async def query(request: AnalyzeRequest):
172
+ """
173
+ 查询接口 - 简化的查询接口
174
+ """
175
+ try:
176
+ erlangshen = get_erlangshen()
177
+ result = await erlangshen.process(request.query, request.context)
178
+ return result
179
+ except Exception as e:
180
+ logger.error(f"Query failed: {e}")
181
+ raise HTTPException(status_code=500, detail=str(e))
182
+
183
+
184
+ @app.get("/market/{symbol}")
185
+ async def get_market(symbol: str, days: int = 30):
186
+ """获取股票行情"""
187
+ try:
188
+ erlangshen = get_erlangshen()
189
+ market_tools = erlangshen.tools.get("market_tools")
190
+ if market_tools:
191
+ result = await market_tools.get_stock_history(symbol, days)
192
+ return result
193
+ raise HTTPException(status_code=503, detail="Market tools not available")
194
+ except Exception as e:
195
+ logger.error(f"Market query failed: {e}")
196
+ raise HTTPException(status_code=500, detail=str(e))
197
+
198
+
199
+ @app.get("/macro/{indicator}")
200
+ async def get_macro(indicator: str):
201
+ """获取宏观指标"""
202
+ try:
203
+ erlangshen = get_erlangshen()
204
+ macro_tools = erlangshen.tools.get("macro_tools")
205
+ if macro_tools:
206
+ result = await macro_tools.get_macro_indicator(indicator)
207
+ return result
208
+ raise HTTPException(status_code=503, detail="Macro tools not available")
209
+ except Exception as e:
210
+ logger.error(f"Macro query failed: {e}")
211
+ raise HTTPException(status_code=500, detail=str(e))
212
+
213
+
214
+ @app.post("/report")
215
+ async def create_report(request: ReportRequest):
216
+ """创建报告"""
217
+ try:
218
+ erlangshen = get_erlangshen()
219
+ result = await erlangshen.generate_report(request.title, request.content)
220
+ return result
221
+ except Exception as e:
222
+ logger.error(f"Report creation failed: {e}")
223
+ raise HTTPException(status_code=500, detail=str(e))
224
+
225
+
226
+ @app.get("/knowledge/stats")
227
+ async def knowledge_stats():
228
+ """知识库统计"""
229
+ try:
230
+ erlangshen = get_erlangshen()
231
+ if erlangshen.knowledge:
232
+ return erlangshen.knowledge.stats()
233
+ raise HTTPException(status_code=503, detail="Knowledge not available")
234
+ except Exception as e:
235
+ logger.error(f"Knowledge stats failed: {e}")
236
+ raise HTTPException(status_code=500, detail=str(e))
237
+
238
+
239
+ @app.get("/memory/stats")
240
+ async def memory_stats():
241
+ """记忆统计"""
242
+ try:
243
+ erlangshen = get_erlangshen()
244
+ if erlangshen.memory:
245
+ return erlangshen.memory.export_state()
246
+ raise HTTPException(status_code=503, detail="Memory not available")
247
+ except Exception as e:
248
+ logger.error(f"Memory stats failed: {e}")
249
+ raise HTTPException(status_code=500, detail=str(e))
250
+
251
+
252
+ # === 启动函数 ===
253
+
254
+ def run_server(host: str = "0.0.0.0", port: int = 8000):
255
+ """启动API服务器"""
256
+ logger.info(f"Starting 二郎神 API server on {host}:{port}")
257
+ uvicorn.run(app, host=host, port=port)
258
+
259
+
260
+ if __name__ == "__main__":
261
+ import argparse
262
+ parser = argparse.ArgumentParser(description="二郎神API服务器")
263
+ parser.add_argument("--host", default="0.0.0.0", help="监听地址")
264
+ parser.add_argument("--port", type=int, default=8000, help="监听端口")
265
+ args = parser.parse_args()
266
+ run_server(args.host, args.port)
package/src/brain.py ADDED
@@ -0,0 +1,200 @@
1
+ """
2
+ 二郎神大脑 - LLM调用层
3
+ """
4
+
5
+ import os
6
+ import json
7
+ from typing import Optional, List, Dict, Any
8
+ from pathlib import Path
9
+
10
+ from src.config import get_config
11
+
12
+
13
+ class Brain:
14
+ """
15
+ 二郎神大脑 - 统一的LLM调用接口
16
+ 支持 DeepSeek、OpenAI 等多种模型
17
+ """
18
+
19
+ def __init__(self):
20
+ self.config = get_config()
21
+ self.max_context = self.config.llm_max_context # 1M tokens for deepseek-v4-pro
22
+ self._client = None
23
+ self.conversation_history: List[Dict[str, str]] = []
24
+
25
+ @property
26
+ def client(self):
27
+ """懒加载 LLM 客户端"""
28
+ if self._client is None:
29
+ self._client = self._create_client()
30
+ return self._client
31
+
32
+ def _create_client(self):
33
+ """创建 LLM 客户端"""
34
+ provider = self.config.llm_provider.lower()
35
+
36
+ if provider == "deepseek":
37
+ try:
38
+ from openai import OpenAI
39
+ # 优先使用环境变量
40
+ api_key = os.environ.get("DEEPSEEK_API_KEY") or self.config.deepseek_api_key
41
+ return OpenAI(
42
+ api_key=api_key,
43
+ base_url="https://api.deepseek.com"
44
+ )
45
+ except ImportError:
46
+ raise ImportError("请安装 openai: pip install openai")
47
+
48
+ elif provider == "openai":
49
+ try:
50
+ from openai import OpenAI
51
+ return OpenAI(
52
+ api_key=self.config.llm_api_key or os.environ.get("OPENAI_API_KEY"),
53
+ base_url=self.config.llm_base_url
54
+ )
55
+ except ImportError:
56
+ raise ImportError("请安装 openai: pip install openai")
57
+
58
+ else:
59
+ raise ValueError(f"不支持的 LLM 提供商: {provider}")
60
+
61
+ def _get_model(self) -> str:
62
+ """获取模型名称"""
63
+ if self.config.llm_provider.lower() == "deepseek":
64
+ # 优先使用环境变量
65
+ return os.environ.get("DEEPSEEK_MODEL") or self.config.deepseek_model
66
+ return self.config.llm_model
67
+
68
+ async def think(
69
+ self,
70
+ prompt: str,
71
+ system: Optional[str] = None,
72
+ temperature: Optional[float] = None,
73
+ max_tokens: Optional[int] = None,
74
+ context: Optional[List[Dict[str, str]]] = None,
75
+ ) -> str:
76
+ """
77
+ 核心思考方法
78
+
79
+ Args:
80
+ prompt: 用户提示
81
+ system: 系统提示 (可选)
82
+ temperature: 温度参数 (可选)
83
+ max_tokens: 最大 token 数 (可选)
84
+ context: 对话上下文 (可选)
85
+
86
+ Returns:
87
+ LLM 生成的响应
88
+ """
89
+ messages = []
90
+
91
+ # 系统提示
92
+ if system:
93
+ messages.append({"role": "system", "content": system})
94
+
95
+ # 上下文
96
+ if context:
97
+ messages.extend(context)
98
+
99
+ # 用户提示
100
+ messages.append({"role": "user", "content": prompt})
101
+
102
+ try:
103
+ response = self.client.chat.completions.create(
104
+ model=self._get_model(),
105
+ messages=messages,
106
+ temperature=temperature or self.config.llm_temperature,
107
+ max_tokens=max_tokens or self.config.llm_max_tokens,
108
+ )
109
+
110
+ return response.choices[0].message.content
111
+
112
+ except Exception as e:
113
+ return f"思考过程出错: {str(e)}"
114
+
115
+ async def analyze(
116
+ self,
117
+ query: str,
118
+ framework: Optional[str] = None,
119
+ data: Optional[Dict[str, Any]] = None,
120
+ ) -> str:
121
+ """
122
+ 分析查询
123
+
124
+ Args:
125
+ query: 分析查询
126
+ framework: 分析框架 (可选)
127
+ data: 相关数据 (可选)
128
+
129
+ Returns:
130
+ 分析结果
131
+ """
132
+ system_prompt = self._build_analysis_prompt(framework)
133
+
134
+ user_prompt = query
135
+ if data:
136
+ user_prompt += f"\n\n相关数据:\n{json.dumps(data, ensure_ascii=False, indent=2)}"
137
+
138
+ return await self.think(user_prompt, system=system_prompt)
139
+
140
+ def _build_analysis_prompt(self, framework: Optional[str] = None) -> str:
141
+ """构建分析提示"""
142
+ base = """你是二郎神,一个全知全能的AI投资智能体。
143
+
144
+ ## 你的背景
145
+ - 你是小二配置团队的AI投资智能体
146
+ - 你具备团队全貌认知,了解9个专业Agent的能力和成果
147
+ - 你可以调用真实数据库获取行情和宏观数据
148
+
149
+ ## 团队能力
150
+ - Agent-01/02: 基金筛选配置(ETF轮动V7年化16.1%)
151
+ - Agent-03: 交易执行验证
152
+ - Agent-04: CTA量化策略
153
+ - Agent-05: 宏观策略(V19年化25.6%,净值5.23)
154
+ - Agent-07: 数据采集治理
155
+ - Agent-08: 客户服务
156
+ - Agent-09: 个股基本面研究
157
+
158
+ ## 核心数据资源
159
+ - 宏观数据库: 48个指标,SHIBOR_3M/CPI/GDP/PMI等
160
+ - 远程行情: 股票/指数/期货实时数据
161
+ - MCP工具: 东方财富dc-66,飞书文档
162
+
163
+ ## 分析原则
164
+ 1. 基于真实数据进行分析
165
+ 2. 客观中立,不带偏见
166
+ 3. 风险与收益并重
167
+ 4. 考虑多种情景和可能性
168
+ 5. 可以调用团队各Agent的专业能力
169
+
170
+ 输出格式:
171
+ - 核心观点 (1-3句话)
172
+ - 详细分析
173
+ - 风险提示
174
+ - 行动建议 (如适用)
175
+ """
176
+
177
+ if framework:
178
+ base += f"\n\n分析框架: {framework}"
179
+
180
+ return base
181
+
182
+ def reset_history(self):
183
+ """重置对话历史"""
184
+ self.conversation_history = []
185
+
186
+ def add_to_history(self, role: str, content: str):
187
+ """添加到对话历史"""
188
+ self.conversation_history.append({"role": role, "content": content})
189
+
190
+ async def think_with_history(
191
+ self,
192
+ prompt: str,
193
+ system: Optional[str] = None,
194
+ ) -> str:
195
+ """带历史的思考"""
196
+ return await self.think(
197
+ prompt,
198
+ system=system,
199
+ context=self.conversation_history
200
+ )
package/src/cli.py ADDED
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 二郎神 CLI - 投资分析智能体命令行入口
4
+ """
5
+
6
+ import sys
7
+ import asyncio
8
+ import os
9
+ from typing import Optional
10
+
11
+ from src.brain import Brain
12
+ from src.mcp.registry import MCPRegistry
13
+ from src.commands.analyze import AnalyzeCommand
14
+ from src.commands.macro import MacroCommand
15
+ from src.commands.stock import StockCommand
16
+ from src.commands.report import ReportCommand
17
+ from src.commands.search import SearchCommand
18
+ from src.commands.portfolio import PortfolioCommand
19
+ from src.commands.risk import RiskCommand
20
+ from src.commands.memo import MemoCommand
21
+ from src.hooks.session_start import SessionStartHook
22
+ from src.hooks.session_end import SessionEndHook
23
+
24
+
25
+ class CLI:
26
+ """二郎神 CLI 主类"""
27
+
28
+ COMMANDS = {
29
+ "analyze": AnalyzeCommand,
30
+ "macro": MacroCommand,
31
+ "stock": StockCommand,
32
+ "report": ReportCommand,
33
+ "search": SearchCommand,
34
+ "portfolio": PortfolioCommand,
35
+ "risk": RiskCommand,
36
+ "memo": MemoCommand,
37
+ }
38
+
39
+ def __init__(self):
40
+ self.brain = Brain()
41
+ self.mcp = MCPRegistry()
42
+ self.hooks = {
43
+ "session_start": SessionStartHook(self.brain, self.mcp),
44
+ "session_end": SessionEndHook(self.brain, self.mcp),
45
+ }
46
+
47
+ async def run_command(self, command: str, args: str) -> str:
48
+ """执行命令"""
49
+ if command in self.COMMANDS:
50
+ cmd = self.COMMANDS[command](self.brain, self.mcp)
51
+ return await cmd.execute(args)
52
+ else:
53
+ return f"未知命令: /{command}\n\n可用命令:\n" + "\n".join(f" /{cmd}" for cmd in self.COMMANDS.keys())
54
+
55
+ async def interactive_mode(self):
56
+ """交互模式"""
57
+ print("二郎神 v0.1.0 - 投资分析智能体")
58
+ print("输入 /help 查看帮助,输入 /exit 退出\n")
59
+
60
+ # Session start hook
61
+ await self.hooks["session_start"].run()
62
+
63
+ while True:
64
+ try:
65
+ user_input = input("二郎神> ").strip()
66
+
67
+ if not user_input:
68
+ continue
69
+
70
+ if user_input in ["/exit", "/quit", "/q"]:
71
+ print("再见!")
72
+ break
73
+
74
+ if user_input == "/help":
75
+ self.print_help()
76
+ continue
77
+
78
+ # 处理斜杠命令
79
+ if user_input.startswith("/"):
80
+ parts = user_input[1:].split(maxsplit=1)
81
+ command = parts[0]
82
+ args = parts[1] if len(parts) > 1 else ""
83
+ result = await self.run_command(command, args)
84
+ else:
85
+ # 默认执行分析
86
+ result = await self.run_command("analyze", user_input)
87
+
88
+ print(f"\n{result}\n")
89
+
90
+ except KeyboardInterrupt:
91
+ print("\n\n再见!")
92
+ break
93
+ except Exception as e:
94
+ print(f"\n错误: {e}\n")
95
+
96
+ # Session end hook
97
+ await self.hooks["session_end"].run()
98
+
99
+ def print_help(self):
100
+ """打印帮助信息"""
101
+ print("""
102
+ 二郎神 - 投资分析智能体
103
+
104
+ 可用命令:
105
+ /analyze <query> 综合分析 (默认)
106
+ /macro <query> 宏观分析
107
+ /stock <query> 股票分析
108
+ /report <query> 报告生成
109
+ /search <query> 搜索
110
+ /portfolio <query> 组合分析
111
+ /risk <query> 风险分析
112
+ /memo <query> 纪要管理
113
+
114
+ 示例:
115
+ 二郎神 /analyze A股当前走势
116
+ 二郎神 /macro CPI走势
117
+ 二郎神 /stock 茅台
118
+ 二郎神 /report 月度报告
119
+ """)
120
+
121
+
122
+ def main():
123
+ """主入口"""
124
+ cli = CLI()
125
+
126
+ if len(sys.argv) > 1:
127
+ command = sys.argv[1]
128
+
129
+ if command == "--help" or command == "-h":
130
+ cli.print_help()
131
+ return
132
+
133
+ if command.startswith("/"):
134
+ # 斜杠命令格式
135
+ cmd_parts = command[1:].split(maxsplit=1)
136
+ cmd = cmd_parts[0]
137
+ args = cmd_parts[1] if len(cmd_parts) > 1 else ""
138
+ if len(sys.argv) > 2:
139
+ args = (args + " " + " ".join(sys.argv[2:])).strip() if args else " ".join(sys.argv[2:])
140
+ result = asyncio.run(cli.run_command(cmd, args))
141
+ else:
142
+ # 非斜杠命令格式
143
+ args = " ".join(sys.argv[1:])
144
+ result = asyncio.run(cli.run_command("analyze", args))
145
+
146
+ print(result)
147
+ else:
148
+ # 交互模式
149
+ asyncio.run(cli.interactive_mode())
150
+
151
+
152
+ if __name__ == "__main__":
153
+ main()
@@ -0,0 +1,3 @@
1
+ """
2
+ Commands 模块
3
+ """