背景
当前分支已完成一个可独立测试的腾讯地图位置检索模块,但尚未接入 Realtime Agent、Composed Agent、WebSocket 会话位置 Provider 或现有 ToolRegistry。本 Issue 负责后续应用层接入,避免在现有日程工具中继续保留 location_search 占位实现。
当前已完成的模块能力
实现位于 backend/src/timeflow/:
intelligence/location/contracts.py
Coordinate、ClientLocation、CurrentArea、LocationSearchContext
- WGS84 / GCJ-02 坐标系契约与输入校验
- 位置 Provider 端口和稳定错误类型
intelligence/location/coordinates.py
- WGS84 ↔ GCJ-02 转换
- 中国大陆范围判断
- 经纬度、NaN、Infinity、bool 和未知坐标系校验
intelligence/location/service.py
- 客户端位置转换为腾讯 GCJ-02
- 腾讯逆地址解析并提取省、市
- 系统隐藏搜索上下文
- POI 坐标投影到客户端坐标系
- 过滤后最多返回前两条候选
intelligence/location/tools.py
- 唯一位置 Function:
location_search
- Agent 参数只有
query
- 输出结构化候选,包含名称、地址、省市、纬度、经度和
coordinate_system
- 配置、连接和协议错误统一返回
provider_unavailable
infrastructure/external/location/tencent_maps.py
- 腾讯逆地址解析接口
- 腾讯地点搜索接口
- API Key、请求 URL、当前精确坐标脱敏
- 超时、连接失败、HTTP 错误和畸形响应处理
infrastructure/settings.py
TIMEFLOW_TENCENT_MAP_KEY
TIMEFLOW_TENCENT_MAP_BASE_URL
TIMEFLOW_TENCENT_MAP_TIMEOUT_SECONDS
当前独立模块已通过专项测试、Ruff、Mypy、架构测试,并完成真实腾讯接口冒烟验证。
当前进度
阶段一:独立位置检索模块(PR #236)
阶段二:Agent 和应用接入
Agent 接入目标
将现有 Agent 中的 location_search 占位工具替换为真实实现,并让 Realtime 与 Composed 复用同一套位置 Service、Tool Definition 和结果契约。
1. 应用装配层创建共享位置能力
Composition Root 读取 Settings,创建并注入:
settings = get_settings()
http_client = httpx.AsyncClient(timeout=settings.tencent_map_timeout_seconds)
location_port = TencentMapsLocationPort(
http_client,
api_key=settings.tencent_map_api_key,
base_url=settings.tencent_map_base_url,
)
location_service = LocationSearchService(location_port)
Intelligence 层和 Agent 不得直接读取 .env 或 Settings,也不得自行拼接腾讯请求。
2. 从当前会话取得标准化位置
WebSocket / Gateway 位置分支负责:
- 接收客户端经纬度和坐标系。
- 将位置绑定到当前 session。
- 断连时清理位置。
- 提供供应商无关的
session_id -> ClientLocation | None 读取端口。
Agent 接入层使用当前会话标识读取 ClientLocation,然后调用:
context = await location_service.prepare(client_location)
tool = build_location_search_tool(location_service, context)
精确坐标只保留在 LocationSearchContext,不得进入 Agent instructions、普通 Prompt 或客户端自然语言。
3. 注册真实 location_search
Tool 注册时使用:
location_tool = build_location_search_tool(location_service, context)
注册内容必须使用:
location_tool.definition
location_tool.execute
Function Schema 只能暴露:
{
"type": "object",
"properties": {
"query": {
"type": "string",
"minLength": 1
}
},
"required": ["query"],
"additionalProperties": false
}
Agent 不得传入:
- 当前城市
- 当前纬度、经度
- 坐标系
- 腾讯
boundary
- 页码
- 返回数量
4. Agent 处理候选结果
工具返回最多两条候选:
{
"status": "ok",
"candidates": [
{
"provider_id": "poi-id",
"name": "上海虹桥站",
"address": "申贵路1500号",
"latitude": 31.194,
"longitude": 121.318,
"coordinate_system": "wgs84",
"province": "上海市",
"city": "上海市",
"district": "闵行区"
}
]
}
Agent 规则:
- 只有一条且与用户表达明确匹配时,才可以继续调用
schedule_create 或 schedule_update。
- 两条候选无法唯一确定时,复用现有
request_user_input,只向用户展示名称和必要地址,不朗读经纬度。
- 无候选时不得编造地点、地址或坐标。
provider_unavailable 时不得阻断非位置日程能力,应向用户说明位置搜索暂不可用。
5. 坐标写入与客户端响应
当前日程 Function 和数据库只有裸 latitude / longitude,没有 coordinate_system。接入实现必须先确定并测试坐标一致性策略,推荐:
- 云端数据库统一保存 WGS84。
location_search 可向 Agent 返回带坐标系的客户端兼容坐标。
- Agent 选择候选后,服务端根据可信
provider_id / 对话上下文取得候选的规范 WGS84 坐标,再调用日程业务命令。
- 不直接信任模型自行修改后的裸经纬度。
- 对客户端的结构化日程结果明确携带
coordinate_system,避免客户端误读裸坐标。
如果本阶段不实现可信候选到日程写入的绑定,必须明确阻止 Agent 直接把客户端坐标写入数据库,并另开后续 Issue。
需要修改或新增的范围
- Realtime Agent 的位置上下文和 ToolBox 注册。
- Composed Agent 的位置上下文和 ToolRegistry 注册。
- WebSocket / Gateway 位置 Provider 接口适配。
- Composition Root 的腾讯地图 Client、Port、Service 装配。
- 替换现有
_LocationSearchPlaceholder。
- 必要的 Agent、Tool、Gateway 和端到端集成测试。
- 客户端结构化结果中的坐标系字段(如现有协议允许变更)。
不应在本 Issue 中重复实现:
- GCJ-02 公式。
- 腾讯 HTTP 请求和响应解析。
- 位置工具 Schema。
- 另一个
get_current_location Function。
验收标准
关联实现
阶段一独立位置模块由 PR #236 交付;后续 Agent 和应用层接入继续由本 Issue 跟踪。
背景
当前分支已完成一个可独立测试的腾讯地图位置检索模块,但尚未接入 Realtime Agent、Composed Agent、WebSocket 会话位置 Provider 或现有 ToolRegistry。本 Issue 负责后续应用层接入,避免在现有日程工具中继续保留
location_search占位实现。当前已完成的模块能力
实现位于
backend/src/timeflow/:intelligence/location/contracts.pyCoordinate、ClientLocation、CurrentArea、LocationSearchContextintelligence/location/coordinates.pyintelligence/location/service.pyintelligence/location/tools.pylocation_searchquerycoordinate_systemprovider_unavailableinfrastructure/external/location/tencent_maps.pyinfrastructure/settings.pyTIMEFLOW_TENCENT_MAP_KEYTIMEFLOW_TENCENT_MAP_BASE_URLTIMEFLOW_TENCENT_MAP_TIMEOUT_SECONDS当前独立模块已通过专项测试、Ruff、Mypy、架构测试,并完成真实腾讯接口冒烟验证。
当前进度
阶段一:独立位置检索模块(PR #236)
location_searchFunction阶段二:Agent 和应用接入
Settings.tencent_map_timeout_seconds注入AsyncClient并补装配测试location_searchlocation_search占位工具request_user_input消歧coordinate_systemAgent 接入目标
将现有 Agent 中的
location_search占位工具替换为真实实现,并让 Realtime 与 Composed 复用同一套位置 Service、Tool Definition 和结果契约。1. 应用装配层创建共享位置能力
Composition Root 读取
Settings,创建并注入:Intelligence 层和 Agent 不得直接读取
.env或Settings,也不得自行拼接腾讯请求。2. 从当前会话取得标准化位置
WebSocket / Gateway 位置分支负责:
session_id -> ClientLocation | None读取端口。Agent 接入层使用当前会话标识读取
ClientLocation,然后调用:精确坐标只保留在
LocationSearchContext,不得进入 Agent instructions、普通 Prompt 或客户端自然语言。3. 注册真实
location_searchTool 注册时使用:
注册内容必须使用:
Function Schema 只能暴露:
{ "type": "object", "properties": { "query": { "type": "string", "minLength": 1 } }, "required": ["query"], "additionalProperties": false }Agent 不得传入:
boundary4. Agent 处理候选结果
工具返回最多两条候选:
{ "status": "ok", "candidates": [ { "provider_id": "poi-id", "name": "上海虹桥站", "address": "申贵路1500号", "latitude": 31.194, "longitude": 121.318, "coordinate_system": "wgs84", "province": "上海市", "city": "上海市", "district": "闵行区" } ] }Agent 规则:
schedule_create或schedule_update。request_user_input,只向用户展示名称和必要地址,不朗读经纬度。provider_unavailable时不得阻断非位置日程能力,应向用户说明位置搜索暂不可用。5. 坐标写入与客户端响应
当前日程 Function 和数据库只有裸
latitude/longitude,没有coordinate_system。接入实现必须先确定并测试坐标一致性策略,推荐:location_search可向 Agent 返回带坐标系的客户端兼容坐标。provider_id/ 对话上下文取得候选的规范 WGS84 坐标,再调用日程业务命令。coordinate_system,避免客户端误读裸坐标。如果本阶段不实现可信候选到日程写入的绑定,必须明确阻止 Agent 直接把客户端坐标写入数据库,并另开后续 Issue。
需要修改或新增的范围
_LocationSearchPlaceholder。不应在本 Issue 中重复实现:
get_current_locationFunction。验收标准
location_search。location_search占位实现被移除或替换。query。coordinate_system。request_user_input,不由 Agent 擅自选择。关联实现
阶段一独立位置模块由 PR #236 交付;后续 Agent 和应用层接入继续由本 Issue 跟踪。