travel-agent-cli 0.1.0 → 0.2.1

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 (37) hide show
  1. package/README.md +76 -21
  2. package/bin/cli.js +14 -11
  3. package/package.json +8 -4
  4. package/python/agents/__init__.py +19 -0
  5. package/python/agents/analysis_agent.py +234 -0
  6. package/python/agents/base.py +377 -0
  7. package/python/agents/collector_agent.py +304 -0
  8. package/python/agents/manager_agent.py +251 -0
  9. package/python/agents/planning_agent.py +161 -0
  10. package/python/agents/product_agent.py +672 -0
  11. package/python/agents/report_agent.py +172 -0
  12. package/python/analyzers/__init__.py +10 -0
  13. package/python/analyzers/hot_score.py +123 -0
  14. package/python/analyzers/ranker.py +225 -0
  15. package/python/analyzers/route_planner.py +86 -0
  16. package/python/cli/commands.py +254 -0
  17. package/python/collectors/__init__.py +14 -0
  18. package/python/collectors/ota/ctrip.py +120 -0
  19. package/python/collectors/ota/fliggy.py +152 -0
  20. package/python/collectors/weibo.py +235 -0
  21. package/python/collectors/wenlv.py +155 -0
  22. package/python/collectors/xiaohongshu.py +170 -0
  23. package/python/config/__init__.py +30 -0
  24. package/python/config/models.py +119 -0
  25. package/python/config/prompts.py +105 -0
  26. package/python/config/settings.py +172 -0
  27. package/python/export/__init__.py +6 -0
  28. package/python/export/report.py +192 -0
  29. package/python/main.py +632 -0
  30. package/python/pyproject.toml +51 -0
  31. package/python/scheduler/tasks.py +77 -0
  32. package/python/tools/fliggy_mcp.py +553 -0
  33. package/python/tools/flyai_tools.py +251 -0
  34. package/python/tools/mcp_tools.py +412 -0
  35. package/python/utils/__init__.py +9 -0
  36. package/python/utils/http.py +73 -0
  37. package/python/utils/storage.py +288 -0
package/README.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # travel-agent-cli
2
2
 
3
- AI 驱动的旅行目的地推荐 Agent - 命令行工具
3
+ AI 驱动的旅行目的地推荐 Agent - 命令行工具(集成 FlyAI 旅行搜索)
4
+
5
+ ## 新功能 v0.2.0
6
+
7
+ - 🆕 **FlyAI 旅行搜索**:集成 Fliggy MCP,支持航班、酒店、景点实时查询
8
+ - 🔧 修复多 Agent 初始化问题
9
+ - 📝 优化 CLI 输出格式
4
10
 
5
11
  ## 安装
6
12
 
@@ -9,7 +15,7 @@ AI 驱动的旅行目的地推荐 Agent - 命令行工具
9
15
  npm install -g travel-agent-cli
10
16
 
11
17
  # 或者使用 npx 直接运行(无需安装)
12
- npx travel-agent-cli run -k "海岛游"
18
+ npx travel-agent-cli flyai "5 天日本行程"
13
19
  ```
14
20
 
15
21
  ### 安装要求
@@ -67,6 +73,23 @@ travel-agent analyze "北欧极光"
67
73
 
68
74
  ## 命令说明
69
75
 
76
+ ### flyai - FlyAI 旅行搜索(新)
77
+
78
+ ```bash
79
+ travel-agent flyai <查询> [选项]
80
+
81
+ 选项:
82
+ --destination 目的地城市
83
+ --duration 行程天数/日期
84
+ --budget 预算范围
85
+
86
+ 示例:
87
+ travel-agent flyai "5 天日本行程规划"
88
+ travel-agent flyai "北京到上海航班" --destination 上海
89
+ travel-agent flyai "三亚海景酒店" --destination 三亚
90
+ travel-agent flyai "东京景点推荐" --destination 东京
91
+ ```
92
+
70
93
  ### run - 运行完整工作流
71
94
 
72
95
  ```bash
@@ -77,8 +100,6 @@ travel-agent run [选项]
77
100
  -n, --top 推荐目的地数量 (默认:10)
78
101
  -p, --plan-top 规划路线的目的地数量 (默认:3)
79
102
  --no-ota 不采集 OTA 数据
80
- -S, --schedule 启用定时任务
81
- -c, --cron Cron 表达式
82
103
  ```
83
104
 
84
105
  ### analyze - 旅行产品可行性分析
@@ -102,18 +123,21 @@ travel-agent model <子命令> [选项]
102
123
  use 切换模型
103
124
 
104
125
  选项:
105
- -p, --provider LLM 提供商 (anthropic/openai/deepseek/azure/ollama)
126
+ -p, --provider LLM 提供商 (anthropic/openai/deepseek/azure/ollama/qwen)
106
127
  -m, --model 模型名称
107
128
  ```
108
129
 
109
- ### agent - Agent 相关操作
130
+ ### agents - 查看 Agent 团队信息
110
131
 
111
132
  ```bash
112
- travel-agent agent <操作>
133
+ travel-agent agents
113
134
 
114
- 操作:
115
- info 查看 Agent 团队信息
116
- run 运行工作流
135
+ 输出:
136
+ - Manager (协调者)
137
+ - Collector (采集 Agent)
138
+ - Analyst (分析 Agent)
139
+ - Planner (规划 Agent)
140
+ - Reporter (报告 Agent)
117
141
  ```
118
142
 
119
143
  ### config - 配置管理
@@ -126,37 +150,68 @@ travel-agent config [选项]
126
150
  --init 初始化配置文件
127
151
  ```
128
152
 
153
+ ### status - 系统状态
154
+
155
+ ```bash
156
+ travel-agent status
157
+
158
+ 输出:
159
+ - 版本号
160
+ - 当前 LLM 提供商和模型
161
+ - API 配置状态
162
+ ```
163
+
129
164
  ## 支持的 LLM 提供商
130
165
 
131
166
  | 提供商 | 默认模型 | 配置项 |
132
167
  |--------|---------|--------|
133
- | Anthropic | claude-sonnet-4-6 | ANTHROPIC_API_KEY |
168
+ | Anthropic | claude-sonnet-4-5-20250929 | ANTHROPIC_API_KEY |
134
169
  | OpenAI | gpt-4o | OPENAI_API_KEY |
135
170
  | DeepSeek | deepseek-chat | DEEPSEEK_API_KEY |
136
171
  | Azure OpenAI | gpt-4 | AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT |
137
172
  | Ollama | llama3 | OLLAMA_BASE_URL (本地部署) |
173
+ | Qwen (通义千问) | qwen-max | DASHSCOPE_API_KEY |
138
174
 
139
- ## 示例
175
+ ## FlyAI 旅行搜索(Fliggy MCP 集成)
140
176
 
141
- ### 搜索海岛游推荐
177
+ FlyAI 旅行搜索已集成 **Fliggy MCP**(飞猪开放平台)服务:
142
178
 
143
- ```bash
144
- travel-agent run -k "海岛游" -n 10
179
+ - **沙箱模式**:默认使用,返回模拟数据,无需配置
180
+ - **生产模式**:需配置飞猪开放平台 App Key App Secret
181
+
182
+ 配置方式(可选):
183
+ ```ini
184
+ # 在 .env 文件中添加
185
+ FLIGGY_APP_KEY=your_app_key
186
+ FLIGGY_APP_SECRET=your_app_secret
145
187
  ```
146
188
 
147
- ### 分析极光旅行产品
189
+ ## 示例
148
190
 
191
+ ### FlyAI 旅行搜索
149
192
  ```bash
150
- travel-agent analyze "北欧极光"
151
- ```
193
+ # 综合旅行规划
194
+ travel-agent flyai "5 天日本行程规划"
195
+
196
+ # 航班搜索
197
+ travel-agent flyai "北京到上海航班" --destination 上海
152
198
 
153
- ### 每天凌晨 2 点自动运行
199
+ # 酒店搜索
200
+ travel-agent flyai "三亚海景酒店" --destination 三亚
154
201
 
202
+ # 景点搜索
203
+ travel-agent flyai "东京景点推荐" --destination 东京
204
+ ```
205
+
206
+ ### 搜索海岛游推荐
155
207
  ```bash
156
- travel-agent run -S -c "0 2 * * *"
208
+ travel-agent run -k "海岛游" -n 10
157
209
  ```
158
210
 
159
- ### 保存到指定文件
211
+ ### 分析极光旅行产品
212
+ ```bash
213
+ travel-agent analyze "北欧极光"
214
+ ```
160
215
 
161
216
  ```bash
162
217
  travel-agent analyze "日本赏樱" -o ./analysis_report.md
package/bin/cli.js CHANGED
@@ -11,8 +11,8 @@ const fs = require('fs');
11
11
 
12
12
  // 获取包的安装路径
13
13
  const packagePath = path.join(__dirname, '..');
14
- const projectRoot = path.join(packagePath, '..');
15
- const mainPy = path.join(projectRoot, 'main.py');
14
+ const pythonDir = path.join(packagePath, 'python');
15
+ const mainPy = path.join(pythonDir, 'main.py');
16
16
 
17
17
  // 检查 Python 是否可用
18
18
  function findPython() {
@@ -34,9 +34,9 @@ function findPython() {
34
34
  // 检查虚拟环境是否存在
35
35
  function getVenvPython() {
36
36
  const venvPaths = [
37
- path.join(projectRoot, 'venv', 'bin', 'python'),
38
- path.join(projectRoot, '.venv', 'bin', 'python'),
39
- path.join(projectRoot, 'venv', 'Scripts', 'python.exe'),
37
+ path.join(pythonDir, 'venv', 'bin', 'python'),
38
+ path.join(pythonDir, '.venv', 'bin', 'python'),
39
+ path.join(pythonDir, 'venv', 'Scripts', 'python.exe'),
40
40
  ];
41
41
 
42
42
  for (const venvPath of venvPaths) {
@@ -64,15 +64,18 @@ function run() {
64
64
  travel-agent <command> [options]
65
65
 
66
66
  可用命令:
67
- run 运行完整工作流(多 Agent 协作)
68
- analyze 分析旅行产品可行性
69
- agent Agent 相关命令
70
- config 管理配置
71
- model 管理 LLM 模型配置
67
+ run 运行完整工作流(数据采集 分析评分 → 路线规划 → 报告生成)
68
+ analyze 分析旅行产品可行性(市场调研 → 资源评估 → 生成报告)
69
+ agents 查看 Agent 团队信息
70
+ status 显示系统状态(版本、模型、API 配置)
71
+ model 管理 LLM 模型配置(list/status/use)
72
+ config 管理配置(--show/--init)
73
+ help 显示帮助信息
72
74
 
73
75
  示例:
74
76
  travel-agent run -k "海岛游" # 搜索海岛游推荐
75
77
  travel-agent analyze "北欧极光" # 分析极光旅行产品
78
+ travel-agent agents # 查看 Agent 团队
76
79
  travel-agent model list # 查看支持的模型
77
80
  travel-agent --help # 显示帮助
78
81
 
@@ -115,7 +118,7 @@ function run() {
115
118
  const pyArgs = [mainPy, ...args];
116
119
  const child = spawn(pythonCmd, pyArgs, {
117
120
  stdio: 'inherit',
118
- cwd: projectRoot
121
+ cwd: pythonDir
119
122
  });
120
123
 
121
124
  child.on('error', (err) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "travel-agent-cli",
3
- "version": "0.1.0",
4
- "description": "AI 驱动的旅行目的地推荐 Agent - 命令行工具",
3
+ "version": "0.2.1",
4
+ "description": "AI 驱动的旅行目的地推荐 Agent - 命令行工具(集成 FlyAI 旅行搜索)",
5
5
  "bin": {
6
6
  "travel-agent": "bin/cli.js",
7
7
  "travel-agent-cli": "bin/cli.js"
@@ -18,13 +18,17 @@
18
18
  "llm",
19
19
  "cli",
20
20
  "旅行",
21
- "推荐"
21
+ "推荐",
22
+ "flyai",
23
+ "fliggy",
24
+ "机票",
25
+ "酒店"
22
26
  ],
23
27
  "author": "Your Name <your.email@example.com>",
24
28
  "license": "MIT",
25
29
  "repository": {
26
30
  "type": "git",
27
- "url": "https://github.com/your-username/travel-agent.git",
31
+ "url": "git+https://github.com/your-username/travel-agent.git",
28
32
  "directory": "npm-package"
29
33
  },
30
34
  "engines": {
@@ -0,0 +1,19 @@
1
+ """Agents 模块 - 多 Agent 系统
2
+
3
+ 导出所有 Agent 类
4
+ """
5
+ from agents.base import BaseAgent
6
+ from agents.collector_agent import CollectionAgent
7
+ from agents.analysis_agent import AnalysisAgent
8
+ from agents.planning_agent import PlanningAgent
9
+ from agents.report_agent import ReportAgent
10
+ from agents.manager_agent import ManagerAgent
11
+
12
+ __all__ = [
13
+ "BaseAgent",
14
+ "CollectionAgent",
15
+ "AnalysisAgent",
16
+ "PlanningAgent",
17
+ "ReportAgent",
18
+ "ManagerAgent",
19
+ ]
@@ -0,0 +1,234 @@
1
+ """分析 Agent - 负责数据分析和目的地排序"""
2
+ from typing import Dict, Any, List, Optional
3
+ import json
4
+ from agents.base import BaseAgent
5
+ from analyzers.hot_score import HotScoreCalculator
6
+ from config.models import SocialPost, WenlvInfo
7
+
8
+
9
+ class AnalysisAgent(BaseAgent):
10
+ """分析 Agent
11
+
12
+ 职责:
13
+ - 计算目的地热度评分
14
+ - 综合分析并排序
15
+ - 生成推荐理由
16
+ """
17
+
18
+ name = "analysis_agent"
19
+ role = "旅行数据分析师"
20
+ goal = "分析采集的数据,识别热门目的地,给出专业评分和推荐理由"
21
+
22
+ def __init__(
23
+ self,
24
+ provider: Optional[str] = None,
25
+ model: Optional[str] = None,
26
+ use_tools: bool = False,
27
+ ):
28
+ super().__init__(provider, model, use_tools)
29
+ self.score_calculator = HotScoreCalculator()
30
+
31
+ async def execute_local(self, task: str, context: Dict[str, Any]) -> str:
32
+ """本地执行分析"""
33
+ posts = context.get("posts", [])
34
+ wenlv_infos = context.get("wenlv_infos", [])
35
+ analysis = context.get("analysis", {})
36
+
37
+ # 如果有分析数据,生成本地推荐
38
+ if analysis:
39
+ destinations = analysis.get("destinations", [])
40
+ top_n = 10
41
+
42
+ recommendations = []
43
+ for i, dest in enumerate(destinations[:top_n], 1):
44
+ recommendations.append({
45
+ "name": dest.get("name", "未知目的地"),
46
+ "rank": i,
47
+ "score": dest.get("scores", {}).get("total_score", 5.0),
48
+ "reason": f"基于 {dest.get('post_count', 0)} 条社交媒体内容和 {dest.get('wenlv_count', 0)} 条文旅信息推荐",
49
+ "estimated_cost": "¥3000-5000",
50
+ "suggested_days": 5,
51
+ "best_time": "春秋季节",
52
+ "highlights": ["特色美食", "文化体验", "自然风光"]
53
+ })
54
+
55
+ result = {
56
+ "destinations": recommendations,
57
+ "summary": "本地分析推荐结果"
58
+ }
59
+ return json.dumps(result, ensure_ascii=False)
60
+
61
+ # 简化的本地分析
62
+ destinations = self._extract_destinations(posts, wenlv_infos)
63
+
64
+ recommendations = []
65
+ for i, dest in enumerate(destinations[:10], 1):
66
+ recommendations.append({
67
+ "name": dest,
68
+ "rank": i,
69
+ "score": 5.0,
70
+ "reason": "本地分析结果",
71
+ "estimated_cost": "¥3000-5000",
72
+ "suggested_days": 5,
73
+ "best_time": "春秋季节",
74
+ "highlights": ["特色美食", "文化体验"]
75
+ })
76
+
77
+ result = {
78
+ "destinations": recommendations,
79
+ "summary": "本地分析推荐结果"
80
+ }
81
+ return json.dumps(result, ensure_ascii=False)
82
+
83
+ def _extract_destinations(
84
+ self,
85
+ posts: List[Dict],
86
+ wenlv_infos: List[Dict]
87
+ ) -> List[str]:
88
+ """从数据中提取目的地"""
89
+ common_destinations = [
90
+ "三亚", "云南", "大理", "丽江", "西双版纳",
91
+ "四川", "成都", "九寨沟", "川西",
92
+ "北京", "上海", "广州", "深圳",
93
+ "浙江", "杭州", "乌镇", "苏州", "南京",
94
+ "江苏", "陕西", "西安",
95
+ "广西", "桂林", "阳朔",
96
+ "海南", "西藏", "拉萨", "新疆", "喀纳斯",
97
+ "甘肃", "敦煌", "青海", "青海湖",
98
+ "黑龙江", "哈尔滨", "吉林", "长白山"
99
+ ]
100
+
101
+ destinations = {}
102
+
103
+ for post in posts:
104
+ text = f"{post.get('title', '')} {post.get('content', '')}"
105
+ for dest in common_destinations:
106
+ if dest in text:
107
+ if dest not in destinations:
108
+ destinations[dest] = {"posts": 0, "wenlv": 0}
109
+ destinations[dest]["posts"] += 1
110
+
111
+ for info in wenlv_infos:
112
+ region = info.get("region", "")
113
+ if region and region != "全国":
114
+ if region not in destinations:
115
+ destinations[region] = {"posts": 0, "wenlv": 0}
116
+ destinations[region]["wenlv"] += 1
117
+
118
+ # 按热度排序
119
+ sorted_dests = sorted(
120
+ destinations.items(),
121
+ key=lambda x: x[1]["posts"] * 2 + x[1]["wenlv"] * 3,
122
+ reverse=True
123
+ )
124
+
125
+ return [d[0] for d in sorted_dests]
126
+
127
+ async def analyze_destinations(
128
+ self,
129
+ posts: List[Dict],
130
+ wenlv_infos: List[Dict],
131
+ flights: List[Dict] = None,
132
+ hotels: List[Dict] = None
133
+ ) -> Dict[str, Any]:
134
+ """分析目的地数据
135
+
136
+ Args:
137
+ posts: 社交媒体帖子列表
138
+ wenlv_infos: 文旅信息列表
139
+ flights: 航班数据(可选)
140
+ hotels: 酒店数据(可选)
141
+
142
+ Returns:
143
+ 分析结果字典
144
+ """
145
+ # 提取目的地
146
+ destinations = self._extract_destinations(posts, wenlv_infos)
147
+
148
+ # 为每个目的地计算评分
149
+ scored_destinations = []
150
+ for dest in destinations:
151
+ # 筛选相关数据
152
+ related_posts = [
153
+ p for p in posts
154
+ if dest in p.get("title", "") or dest in p.get("content", "")
155
+ ]
156
+ related_wenlv = [
157
+ w for w in wenlv_infos
158
+ if dest in w.get("title", "") or dest in w.get("content", "") or dest == w.get("region")
159
+ ]
160
+
161
+ # 计算评分
162
+ score_info = self.score_calculator.calculate_destination_score(
163
+ destination=dest,
164
+ posts=[SocialPost(**p) if isinstance(p, dict) else p for p in related_posts],
165
+ wenlv_infos=[WenlvInfo(**w) if isinstance(w, dict) else w for w in related_wenlv],
166
+ )
167
+
168
+ scored_destinations.append({
169
+ "name": dest,
170
+ "scores": score_info,
171
+ "post_count": len(related_posts),
172
+ "wenlv_count": len(related_wenlv),
173
+ })
174
+
175
+ # 按总分排序
176
+ scored_destinations.sort(
177
+ key=lambda x: x["scores"]["total_score"],
178
+ reverse=True
179
+ )
180
+
181
+ return {
182
+ "destinations": scored_destinations,
183
+ "total_analyzed": len(scored_destinations),
184
+ "top_destinations": [d["name"] for d in scored_destinations[:5]]
185
+ }
186
+
187
+ async def generate_recommendations(
188
+ self,
189
+ analysis_result: Dict[str, Any],
190
+ top_n: int = 10
191
+ ) -> str:
192
+ """生成推荐结果
193
+
194
+ Args:
195
+ analysis_result: 分析结果
196
+ top_n: 推荐数量
197
+
198
+ Returns:
199
+ 推荐的 JSON 字符串
200
+ """
201
+ task = f"""基于以下分析结果,生成 TOP{top_n}旅行目的地推荐。
202
+
203
+ 对于每个目的地,请提供:
204
+ 1. 排名
205
+ 2. 目的地名称
206
+ 3. 综合得分(基于提供的分数)
207
+ 4. 推荐理由(100-200 字)
208
+ 5. 预估费用范围
209
+ 6. 建议游玩天数
210
+ 7. 最佳旅行时间
211
+ 8. 亮点特色(2-3 个)
212
+
213
+ 分析数据:
214
+ {json.dumps(analysis_result, ensure_ascii=False, indent=2)}
215
+
216
+ 请输出 JSON 格式:
217
+ {{
218
+ "destinations": [
219
+ {{
220
+ "name": "目的地",
221
+ "rank": 1,
222
+ "score": 8.5,
223
+ "reason": "推荐理由...",
224
+ "estimated_cost": "¥3000-5000",
225
+ "suggested_days": 5,
226
+ "best_time": "3-5 月",
227
+ "highlights": ["亮点 1", "亮点 2"]
228
+ }}
229
+ ],
230
+ "summary": "整体总结"
231
+ }}
232
+ """
233
+
234
+ return await self.execute(task, {"analysis": analysis_result})