AI旅行计划
一个由人工智能驱动的个性化旅行计划系统,可以动态生成行程,优化预订,并使用多智能体协调实时协助旅行者。
🏗️ 建筑
系统概述
┌─────────────────────────────────────────────────────────────┐
│ AI Travel Planner │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ A2A Protocol ┌───────────┐ │
│ │ CrewAI │◄────────────────────────────►│ ADK │ │
│ │ Agent │ (HMAC-signed messages) │ Agent │ │
│ └──────┬───────┘ └─────┬─────┘ │
│ │ │ │
│ │ │ │
│ └──────────────┐ ┌─────────────┘ │
│ │ │ │
│ ┌─────▼──────────────▼─────┐ │
│ │ State Store (In-Mem) │ │
│ └──────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ External Integrations (MCP) │ │
│ ├──────────────┬────────────────┬──────────────────────┤ │
│ │ Groq Client │ Gemini Flash │ DuckDuckGo Search │ │
│ └──────────────┴────────────────┴──────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Monitoring & Observability │ │
│ ├──────────────┬────────────────┬──────────────────────┤ │
│ │ Callbacks │ JSON Logger │ Event Tracing │ │
│ └──────────────┴────────────────┴──────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘关键组件
- 多代理系统
- CrewAI代理收集旅行需求,搜索选项(航班、酒店、活动),创建初步提案 - ADK代理根据成本、时间和偏好优化行程;应用预算约束
- A2A协议 (代理人对代理人)
- 带版本控制的JSON消息信封 - 消息完整性的HMAC-SHA256签名 - 用于本地消息传递的内存适配器 - 分布式跟踪的相关ID传播
- 状态管理
- 基于接口的设计(支持内存和Redis) - 共享状态的原子操作 - TTL支持临时数据
- 外部工具集成(MCP)
- Groq:语义搜索和文档存储 - 双子座2.0闪光灯:文本生成和NLP - 鸭鸭搜:网络搜索实时信息 - 预算计算器:货币兑换和财务计算
- 监测和可观察性
- 基于回调的事件系统 - 结构化JSON日志记录 - 监控事件Pydantic模型 - 跟踪和关联ID跟踪
- 结构化输出
- 所有数据模型都使用Pydantic v2 - JSON和Markdown导出功能 - 全程类型安全
🔗 MCP(模型上下文协议)集成
该项目实现了 模型上下文协议 用于标准化工具集成和发现。MCP通过以下方式实现了规划系统和外部工具之间的无缝通信:
MCP架构
┌─────────────────────────────────────────────┐
│ Interactive Planner / Workflow │
└────────────────┬────────────────────────────┘
│
┌────────▼──────────┐
│ MCP Client │
│ (Tool Registry) │
└────────┬──────────┘
│
┌───────────┼───────────┬──────────────┐
│ │ │ │
┌────▼──┐ ┌────▼──┐ ┌────▼──┐ ┌────▼──┐
│Gemini │ │ Groq │ │DuckDu │ │Budget │
│ │ │ LLM │ │ Go │ │ Calc │
│Research │ │ Search │ │ │
└────────┘ └────────┘ └────────┘ └───────┘MCP工具注册表
| 工具 | 模块 | 目的 | 输入 | 输出 |
|---|---|---|---|---|
gemini_research | mcp_client.py | 目的地研究(天气、住宿、景点) | 目的地、日期、兴趣 | 研究结果字典 |
groq_llm | mcp_client.py | 通过LLM生成行程 | 提示、json_mode、温度 | 生成行程json |
duckduckgo_search | mcp_client.py | 网络搜索旅行信息 | 查询,max_results | 搜索结果数组 |
calculator | mcp_client.py | 预算计算与成本优化 | 操作、金额、币种 | 计算结果 |
工具实施文件
src/integrations/
├── mcp_client.py # MCP client, tool definitions, registry
├── mcp_tool_adapter.py # Tool adapters with MCP compliance
├── gemini_research.py # Gemini research client (invoked via MCP)
├── groq_client.py # Groq LLM client (invoked via MCP)
├── duckduckgo_client.py # DuckDuckGo search (invoked via MCP)
└── calculator.py # Budget calculator (invoked via MCP)MCP请求/响应格式
工具请求:
{
"tool_name": "gemini_research",
"arguments": {
"destination": "Paris",
"travel_dates": {
"start_date": "2025-12-01",
"end_date": "2025-12-07"
},
"interests": ["culture", "art"]
},
"trace_id": "trace-abc123",
"correlation_id": "corr-xyz789"
}工具响应:
{
"tool_name": "gemini_research",
"result": {
"destination": "Paris",
"weather_summary": "...",
"accommodation_suggestions": "...",
"top_attractions": "...",
"estimated_daily_cost": 150.0,
"currency": "EUR",
"travel_tips": "...",
"best_time_to_visit": "..."
},
"error": null,
"trace_id": "trace-abc123",
"correlation_id": "corr-xyz789"
}MCP工具发现和调用
列出可用工具:
from src.integrations.mcp_client import get_mcp_client
mcp = get_mcp_client()
tools = mcp.list_tools()
for tool in tools:
print(f"{tool.name}: {tool.description}")
print(f" Category: {tool.category}")
print(f" Schema: {tool.input_schema}")调用工具:
from src.integrations.mcp_tool_adapter import invoke_mcp_tool
import uuid
response = await invoke_mcp_tool(
tool_name="gemini_research",
arguments={
"destination": "Tokyo",
"travel_dates": {
"start_date": "2025-12-20",
"end_date": "2025-12-27"
}
},
trace_id=str(uuid.uuid4()),
correlation_id=str(uuid.uuid4())
)
if response.error:
print(f"Error: {response.error}")
else:
print(f"Research: {response.result}")MCP&A2A协议集成
A2A协议符合MCP:
- 追踪ID:跨工具调用的分布式跟踪
- 关联ID:请求多步骤操作的相关性
- 消息版本控制:确保协议兼容性
- HMAC签名:安全的工具到代理通信
MCP合规检查表
✅ 需求要求
- 规格
- :至少集成2个外部工具
- Gemini 2.0 Flash(研究版)
- Groq LLM(一代)
DuckDuckGo搜索(搜索回退) 预算计算器(优化)✅ MCPClient.list_tools() 工具发现
: 公开所有可用工具✅ MCPToolRequest 请求/响应格式 MCPToolResponse :标准化
/ 模型✅
错误处理 :所有工具都返回带有跟踪ID的结构化错误响应✅
异步支持
:所有工具适配器都完全异步兼容
- 🚀 入门指南
- 先决条件
Python 3.9或更高版本
- pip(Python包管理器)
cd ai-travel-planner- 安装
克隆或导航到存储库
chmod +x scripts/local_run.sh
./scripts/local_run.sh设置环境
# Create virtual environment
python -m venv venv
# Activate virtual environment
.\venv\Scripts\Activate.ps1
# Install dependencies
pip install --upgrade pip
pip install -r requirements.txt- 在Linux/macOS上:
在Windows上: .env.example 配置环境变量 .env 复制
cp .env.example .env到 .env 并填写您的API密钥:
GROQ_API_KEY编辑GEMINI_API_KEY使用您的实际API密钥:CREWAI_API_KEY:您的Groq API密钥ADK_API_KEY:您的Google Gemini API密钥A2A_SHARED_SECRET:您的CrewAI API密钥(如果使用真实CrewAI)- :您的ADK API密钥(如果使用真实的ADK)
:HMAC消息签名的密钥(更改为默认值!)
需要的其他API密钥
python -m src.main examples/sample_itinerary_request.json运行应用程序
python -m src.main path/to/your/request.json带示例请求的演示模式:
- 使用您自己的请求文件:
- 该应用程序将:
- 加载旅行者资料和偏好
- 通过A2A协议协调代理
- 通过MCP调用研究工具(Gemini)
- 通过MCP(Groq LLM)生成行程
examples/generated_itinerary.*
同时输出JSON和Markdown格式
将结果保存到
互动模式(引导式对话)
& .\myenv\Scripts\Activate.ps1
python -m src.interactive_planner使用交互式计划器,系统会提示您输入起点、目的地、日期、预算和首选项。然后,它将进行研究,生成提案,对其进行优化,并保存带时间戳的输出,包括嵌入式研究。
- Windows PowerShell:
- 示例流程:
- 系统会提示您输入旅行基本信息(出发地、目的地、开始/结束日期、预算、兴趣)
- Gemini研究运行(天气、住宿范围、景点、当地提示、指示性费用)
- CrewAI规划师生成初始结构化行程
start_timeADK优化器在保持结构的同时调整成本/时间end_time时间范围被解析为每个活动 - /
examples/itinerary__.json日期时间.md保存的文件:
\+
- ,加上研究降价
- 现在的产出包括:
- JSON和Markdown中的嵌入式研究块
- “概览”摘要(天数、预计总支出、主要主题)
- “热门景点”亮点列表
准确的日常活动时间范围(不再是午夜)
通过优化传播一致的成本明细和总成本
如果中途取消,部分研究可能仍会保存;重新运行以重新生成完整的行程。
pytest🧪 测试
pytest --cov=src --cov-report=html运行所有测试
pytest src/tests/test_models.py
pytest src/tests/test_a2a_protocol.py
pytest src/tests/test_integration.py跑步时覆盖
- 运行特定的测试文件
- 测试覆盖范围包括:
- ✅ Pydantic模型验证和序列化
- ✅ A2A HMAC签名签名与验证
- ✅ 状态存储操作(设置、获取、删除、列表)
✅ 监控回调和事件发布
ai-travel-planner/
├── .env # Environment variables (gitignored)
├── .env.example # Environment template
├── .gitignore # Git ignore rules
├── pyproject.toml # Project metadata
├── requirements.txt # Python dependencies
├── README.md # This file
│
├── envs/
│ └── .env.ci # CI environment variables
│
├── src/
│ ├── main.py # Application entry point
│ │
│ ├── config/
│ │ └── settings.py # Pydantic settings
│ │
│ ├── models/
│ │ └── itinerary.py # All Pydantic models
│ │
│ ├── a2a/
│ │ ├── protocol.py # A2A message protocol
│ │ └── adapters/
│ │ └── in_memory.py # In-memory message adapter
│ │
│ ├── state/
│ │ └── store.py # State store interface & impl
│ │
│ ├── integrations/
│ │ ├── groq_client.py # Groq API wrapper
│ │ ├── gemini_flash_client.py # Gemini API wrapper
│ │ ├── duckduckgo_client.py # DuckDuckGo wrapper
│ │ └── calculator.py # Currency & budget utils
│ │
│ ├── agents/
│ │ ├── crewai_agent/
│ │ │ ├── agent.py # CrewAI agent wrapper
│ │ │ └── handlers.py # Lifecycle handlers
│ │ └── adk_agent/
│ │ └── agent.py # ADK agent wrapper
│ │
│ ├── callbacks/
│ │ ├── monitoring.py # Monitoring callbacks
│ │ └── logger_adapter.py # Logger adapter
│ │
│ ├── logging/
│ │ └── json_logger.py # Structured JSON logger
│ │
│ ├── workflows/
│ │ └── dynamic_planner.py # Workflow orchestration
│ │
│ └── tests/
│ ├── conftest.py # Test configuration
│ ├── test_models.py # Model tests
│ ├── test_a2a_protocol.py # A2A protocol tests
│ ├── test_state_store.py # State store tests
│ ├── test_callbacks.py # Callback tests
│ └── test_integration.py # Integration tests
│
├── examples/
│ ├── sample_itinerary_request.json # Sample input
│ ├── sample_a2a_trace.json # Sample A2A trace
│ ├── generated_itinerary.json # Generated output (JSON)
│ └── generated_itinerary.md # Generated output (Markdown)
│
├── scripts/
│ ├── local_run.sh # Local development script
│ ├── db_migrate.py # Database migration stub
│ └── seed_demo_data.py # Demo data seeder
│
└── ops/
└── commit_history_example.txt # Sample commit history
### Output Naming Pattern
Generated itinerary and research files follow:✅ 端到端工作流集成_📋 项目结构 行程安排\___\.json 行程_\.md
Ensures no overwrites across runs.
### Recent Enhancements
- Time Range Parsing: Activities now reflect scheduled ranges like "09:00 AM - 11:00 AM" instead of defaulting to midnight. Duration is computed as the difference between parsed start and end times.
- Embedded Research: Weather, lodging ranges, attraction summaries, local tips, and indicative costs are stored alongside itinerary output (JSON + Markdown).
- At-a-Glance & Highlights: Quick summary section plus top attractions list in Markdown for rapid scanning.
- Optimization Routing Fixes: ADK optimized plan now reliably stored/retrieved via multiple state keys fallback.
- Serialization Improvements: Robust handling of `Decimal` and `datetime` objects in prompts and outputs.
- Unique Filenames: Timestamp + destination prevents accidental overwrites during iterative planning.
- Resilient JSON Parsing: Fallback logic handles truncated or malformed LLM JSON responses without losing prior valid data.
### Time Parsing Details
The workflow attempts to parse activity time strings of the form:研究
If parsing fails, a safe fallback window (09:00–10:00) is used and logged. Activities are anchored to `start_date + (day_index)` so day offsets are respected.
### Monitoring Log Warning (FYI)
If you see repeated messages like:\_\.md
This stems from a logger adapter assigning `message` explicitly. It is cosmetic; to silence it, adjust the adapter to use a different key (e.g., `original_message`) or avoid overriding `record.message`.
### Planned Next Steps (Suggested)
- Suppress cosmetic logging warnings
- Add JSON highlights array mirroring Markdown top attractions
- Improve LLM JSON schema validation (streamed chunk assembly)
- Add currency conversion for per-day spend vs total
- Optional Redis state backend for multi-session continuity
---上午时-下午时
尝试覆盖LogRecord中的“消息”
🔐 安全说明环境变量 .env 关键的
.env:从不承诺.gitignore文件到版本控制!- 在...里
.env.example默认情况下 - 使用
作为模板
- 将敏感密钥存储在安全保管库中(例如AWS Secrets Manager、Azure密钥保管库)
A2A_SHARED_SECRET - A2A消息安全
- 所有A2A消息都使用HMAC签名
- 更改生产中的默认机密
使用强随机生成的秘密(32+个字符)
- 定期轮换机密
- API密钥
- 在生产使用之前替换所有占位符API密钥
- 为dev/test/prod环境使用不同的密钥
监控API使用情况以发现异常
设置限速和预算警报
chmod 600 .env文件权限
在Linux/macOS上,设置限制权限:
🔧 配置 环境变量 |变量|必填|默认|描述| APP_ENV |----------|----------|---------|-------------| development | |没有| LOG_LEVEL |环境名称| INFO | |没有| SECRET_KEY |日志记录级别| | A2A_SHARED_SECRET |是|-|应用程序密钥| | GROQ_API_KEY |是|-|A2A HMAC签名密钥| | GEMINI_API_KEY |否|-|Groq API密钥| | GEMINI_MODEL |否|-|Gemini API密钥| gemini-2.0-flash | |没有| CREWAI_API_KEY |Gemini型号名称| | ADK_API_KEY |否|-|CrewAI API密钥| | STATE_BACKEND |无|-|ADK API密钥| inmemory | |没有| REDIS_URL |状态后端(内存/redis)| | ENABLE_MONITORING |无|-|Redis连接URL| true | |没有| ALLOW_BOOKING_OPERATIONS |启用监控| false | |没有| DEFAULT_CURRENCY |允许真实预订| USD | |没有| BUDGET_ALERT_THRESHOLD |默认货币| 0.9 |
|没有|
|预算警报阈值|
{
"message_id": "unique-uuid",
"trace_id": "distributed-trace-id",
"correlation_id": "request-correlation-id",
"message_type": "proposal|optimized_plan|query|response|error",
"version": "1.0",
"timestamp": "2025-11-18T10:30:00Z",
"payload": {
"...message-specific-data..."
},
"meta": {
"sender": "agent-id",
"receiver": "agent-id",
"priority": 5,
"ttl": 300
},
"signature": "hmac-sha256-hex-signature"
}📡 A2A协议合同
- 邮件信封消息类型
- 提案:CrewAI代理商的初步旅行建议
- 优化计划:ADK代理商的优化计划
- 怎么翻译:索取信息
- 响应:对查询的响应
错误
:错误通知
from a2a.protocol import sign_message, verify_message
# Sign
signed_msg = sign_message(message)
# Verify
is_valid = verify_message(signed_msg)签名验证
消息使用HMAC-SHA256签名:
- 🎯 API集成说明
src/integrations/groq_client.py - 格罗克API
- 替换中的存根实现
添加实际端点URL和身份验证
- 为生产实施重试逻辑
google-generativeai双子座2.0闪光灯 - 用途
GEMINI_API_KEY软件包(默认情况下未安装) - 集
在环境中
- 支持同步和流模式
- 鸭鸭搜
duckduckgo-search公共HTML接口(不需要API密钥) - 考虑使用
生产包装
- 替代方案:使用Bing或Google自定义搜索API
- CrewAI/ADK
pip install crewai
pip install adk- 当前实现使用存根
安装可用的实际软件包:
使用真实的API调用更新代理包装器
- 🧩 扩展系统
src/agents/new_agent/ - 添加新代理
- 创建代理目录:
- 实现支持A2A的代理类
向工作流编排器注册
- 添加测试
src/integrations/ - 添加外部工具
- 在中创建客户端包装器
- 实现类型化请求/响应模型
添加重试和错误处理
编写单元测试 StateStore 自定义州后端
from state.store import StateStore
class CustomStateStore(StateStore):
async def get(self, key: str) -> Optional[Any]: ...
async def set(self, key: str, value: Any, ttl: Optional[int] = None) -> bool: ...
# ... implement other methods实施
接口:
📊 监控
{
"timestamp": "2025-11-18T10:30:00Z",
"level": "INFO",
"logger": "src.workflows.dynamic_planner",
"message": "Starting planning workflow",
"trace_id": "trace-abc",
"correlation_id": "corr-xyz",
"task_id": "task-001"
}结构化日志
日志以JSON格式编写,带有相关ID: monitoring_events.json监控事件
{
"event_id": "evt-001",
"event_type": "task_start",
"severity": "info",
"trace_id": "trace-abc",
"correlation_id": "corr-xyz",
"task_id": "task-001",
"agent_id": "crewai-planner",
"message": "Task started"
}事件被发送到
:
🐛 故障排除
# Ensure you're in the project root and virtual environment is activated
python -m src.main examples/sample_itinerary_request.json常见问题
- 导入错误:
.env缺少API密钥: - 检查
文件存在并且具有正确的值
# Ensure test dependencies are installed
pip install -r requirements.txt存根实现在没有真正的API密钥的情况下工作
- 测试失败:
- 状态存储错误:
默认值在内存中(无外部依赖关系)
- 对于Redis,确保Redis服务器正在运行
python -m src.interactive_planner所有活动时间显示为上午12:00: - 确保您正在运行带有时间范围解析(增强后)的更新工作流。重新安装依赖关系并重新运行
.
- 验证LLM是否在每日计划中返回时间范围(如果需要,启用调试日志记录)。
- JSON中缺少的研究:
ENABLE_MONITORING=true确认使用了交互模式(非交互主界面可能会根据版本排除某些嵌入步骤)。
确保环境变量
httpx文档
pytest文档
📝 许可证
MIT许可证-有关详细信息,请参阅许可证文件
- 🤝 贡献
- 这是一个示范/脚手架项目。生产中:
- 用真正的API调用替换存根实现
- 添加全面的错误处理
- 实施速率限制
- 添加身份验证和授权
- 设置CI/CD管道
- 添加监控和警报
- 实现数据库持久化
- 添加缓存层
实施预订确认工作流程
添加用户界面(网络/移动)
- 📞 支持
- 有关问题和疑问,请查看:
- 项目文件
______________________________________________________________________
使用示例的测试文件代码注释和文档字符串
