Conversation
| else: | ||
| # v0 harness 需要稳定、可比较的 trace。即使模型漏掉某个工具,也补齐 | ||
| # 最小工具链,避免不同 prompt/model 版本生成不可回放的半截记录。 | ||
| existing = {call.name for call in tool_calls} |
There was a problem hiding this comment.
[P1] 请规范化必需工具的顺序并去重
这里只计算缺项,保留模型给出的原顺序和重复项。包含全部四个工具的逆序响应会被原样接受,degraded=False;live_runner 随后先 validate、再 compute、最后 sort,造成过期校验和空交通段,重复调用也会重复执行外部工具。
建议根据依赖重建唯一链 parse_user_intent → route_sort_day → compute_transit → validate_itinerary,对乱序/重复返回 warning;可选工具再放入明确的安全插槽。
| "stops": [ | ||
| { | ||
| "order_index": idx, | ||
| "arrive_time": None, |
There was a problem hiding this comment.
[P1] 校验前不要丢弃 slot 和 arrive_time
这个转换没有复制 slot,并把所有 arrive_time 强制改成 None。完整的早餐、景点、午餐、晚餐、酒店输入经 runner 后会被误报 incomplete_day_structure;同时 non_monotonic_time 永远看不到原始时间。
请原样保留这两个字段(以及其他校验所需字段),并增加“完整日程应 valid”与“逆序时间应告警”的 runner 级测试。
| ), | ||
| }, | ||
| ] | ||
| raw = "".join(llm.stream_chat(messages, max_tokens=800)) |
There was a problem hiding this comment.
[P1] 模型不可用时应退回确定性工具链
stream_chat 在异常处理之外。未配置密钥、网络异常或供应商错误会直接逃逸;模拟 provider down 时,LLMAgentHarnessRunner.run() 在生成任何 trace/record 前崩溃,下面针对无效 JSON 的 fallback 完全不会执行。
建议捕获模型调用异常并返回 planner_unavailable warning、degraded=True 和规范的 fallback calls。
| ] | ||
| return { | ||
| "id": record.get("id", ""), | ||
| "compliant": bool(record.get("days")) or bool(stops), |
There was a problem hiding this comment.
[P1] compliance_rate 不能只判断是否存在任意输出
当前只要 days 非空或存在一个 stop 就算 compliant。自带的 missing_coord_degraded fixture 只有一个缺坐标景点,仍被判合规;CLI 因而输出 compliance_rate=1.0。带 validation.valid=false 的 harness record 也会被同样误判。
请优先使用 validation 结果,并为旧 workflow 记录执行每日角色、时间、坐标和交通契约校验;同时修正 fixture 与测试期望。
| state=state, | ||
| trace=trace, | ||
| warnings=warnings, | ||
| degraded=any(entry.result.degraded for entry in trace), |
There was a problem hiding this comment.
[P2] 请把 ok=False 传播到运行级失败状态
ToolResult 允许 ok=False, degraded=False,这里却只聚合 degraded。用注册工具返回该结果时,全部工具都失败,最终 HarnessRunResult.degraded 仍为 false;live_runner.py 有同样问题。
建议建立 not ok ⇒ degraded 的模型不变量,并在两个 runner 中同时检查 not entry.result.ok。
| ], | ||
| ) | ||
| try: | ||
| return handler(call.args, context) |
There was a problem hiding this comment.
[P2] registry 应实际执行已公布的 input_schema
目前 schema 只展示给 planner,调用时直接进入 handler。parse_user_intent 缺少 required 的 query 仍返回成功并默认为成都三天;非法 mode、越界坐标和嵌套 stop 结构也不会在统一入口被拒绝。
建议在 handler 前执行 JSON Schema/Pydantic 校验,失败返回结构化 invalid_tool_args;stops 还需要完整的 items schema。
| slot = _slot_from_raw_stop(raw_stop, stop.poi.category) | ||
| if slot: | ||
| slots.append(slot) | ||
| minutes = _hhmm_to_min(stop.arrive_time) |
There was a problem hiding this comment.
[P2] 请校验时间格式和停留时长上限
_hhmm_to_min 接受 99:99,无法解析的字符串则被静默跳过;stay_minutes=9999 也未检查。完整日程将所有时间设为 99:99、停留时间设为 9999 时,工具仍返回 valid=true 且无 warning。
建议复用现有 validators.validate_arrive_time / validate_stay_minutes,对非法值给 warning,并要求到达时间严格递增。
| "from_lat": prev.get("lat"), | ||
| "to_lng": cur.get("lng"), | ||
| "to_lat": cur.get("lat"), | ||
| "mode": "driving", |
There was a problem hiding this comment.
[P2] 不要把所有相邻段硬编码为 driving
两个 runner 都固定传 driving,而 registry 又宣称 compute_transit 支持 walking。最小复现中 123 米路段的 walking 与 driving 均返回 14 秒,且 state 里没有 mode;这会让短距离步行耗时明显失真,replay 也无法还原方式。
建议按现有距离阈值选择方式,实现对应估算/高德调用,并在结果与测试中保留 mode。
| "created_at": datetime.now(UTC).isoformat(), | ||
| "id": f"agent-{datetime.now(UTC).strftime('%Y%m%d%H%M%S%f')}", | ||
| "degraded": result.degraded, | ||
| "request": request.model_dump(mode="json"), |
There was a problem hiding this comment.
[P2] 默认不要把原始用户内容写进受 Git 跟踪的 fixture
这里原样记录 request、planner raw response 和完整 trace;live 测试的默认路径又是 evals/fixtures/agent_harness_live.jsonl。用户地址、联系方式、精确行程或误贴凭证可能在一次本地 live 测试后进入开源提交,当前测试只搜索字段名 openai_api_key,无法防止这类泄露。
建议默认写入 gitignored 的私有目录、限制文件权限并做脱敏;保留原文或写入 tracked fixture 应显式启用。
|
已根据本轮评审完成全部修复,并补充对应回归测试。 主要更新包括:
当前分支测试: 烦请重新审阅。 |
背景
当前项目已有真实 LLM workflow、POI、交通和行程校验能力,但缺少统一的 Agent Tool Layer、执行轨迹和 replay/eval 基础设施。本 PR 新增 backend-only 的 Agent Harness v0,为后续 tool calling、memory、POI grounding 和评估体系提供可测试的工程骨架。
本次不替换现有
/plan/stream,不修改前端协议,也不引入完全自主的 agent loop。核心结构
1. 统一工具协议
新增以下结构:
ToolCallToolResultToolWarningToolContextToolSpecToolTraceEntryHarnessRequestHarnessRunResultToolResult现在建立了ok=false => degraded=true的模型不变量,避免失败工具被误判为健康运行。2. Tool Registry
Registry 提供工具注册、发现和统一调用入口,目前包含:
parse_user_intentestimate_visit_durationcompute_transitroute_sort_dayvalidate_itineraryRegistry 会在调用 handler 前校验已公布的 input schema,并将 JSON Schema 或 Pydantic 参数错误统一返回为
invalid_tool_args。3. Harness Runner
确定性 Runner 使用固定链路:
LLM Planner 可以参与工具规划,但 Harness 会对必需工具重新排序、去重并补齐,模型无法绕过本地执行边界。
当 LLM 未配置、网络异常或 provider 不可用时,Planner 返回
planner_unavailablewarning,并降级到固定工具链,不会中断整次运行。4. 状态保真与交通计算
slot、arrive_time和stay_minutesHH:MM且严格递增5. Replay 与隐私边界
Live 记录默认写入 gitignored 的
.agent_records/,文件权限为0600。默认使用
record_mode=redacted,不会记录:脱敏记录仍保留工具调用名称、成功或降级状态、warning code、stop 数、缺坐标数、transit 数和 validation 摘要,可继续用于聚合评估。
只有显式设置
include_sensitive=True,或运行 live 测试时设置:才会保存完整 replay。原先受 Git 跟踪的 live JSONL 已删除。
6. Eval Harness
evals/travel_plan_eval.py同时支持旧 workflow 记录、完整 Agent Harness record 和脱敏摘要。当前指标包括:
合规率不再以“是否存在输出”判断,而是优先使用
validation.valid;旧 workflow 数据会检查每日角色、时间、坐标、停留时长和相邻交通契约。根据评审完成的修复
已覆盖本轮评审的 4 个 P1 和 5 个 P2:
同时包含 PR #9 的三项 route sort 边界修复。
测试
当前分支:
与最新
origin/main合并预演:跳过项是需要显式开启并消耗 API 额度的 live LLM 测试。warning 来自现有 FastAPI TestClient 依赖的弃用提示,与本次修改无关。
设计边界
本次没有:
/plan/stream