Skip to content

[codex] add agent harness v0 with replay eval - #11

Open
RCF-117 wants to merge 6 commits into
kaikkd:mainfrom
RCF-117:codex/agent-harness-v0
Open

RCF-117 wants to merge 6 commits into
kaikkd:mainfrom
RCF-117:codex/agent-harness-v0

Conversation

@RCF-117

@RCF-117 RCF-117 commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

背景

当前项目已有真实 LLM workflow、POI、交通和行程校验能力,但缺少统一的 Agent Tool Layer、执行轨迹和 replay/eval 基础设施。本 PR 新增 backend-only 的 Agent Harness v0,为后续 tool calling、memory、POI grounding 和评估体系提供可测试的工程骨架。

本次不替换现有 /plan/stream,不修改前端协议,也不引入完全自主的 agent loop。

本 PR 基于前序 route_sort_day 工具继续开发。PR #9 合并后,本 PR 的实际 diff 会自动收敛到 Harness 和 Eval 相关代码。

核心结构

1. 统一工具协议

新增以下结构:

  • ToolCall
  • ToolResult
  • ToolWarning
  • ToolContext
  • ToolSpec
  • ToolTraceEntry
  • HarnessRequest
  • HarnessRunResult

ToolResult 现在建立了 ok=false => degraded=true 的模型不变量,避免失败工具被误判为健康运行。

2. Tool Registry

Registry 提供工具注册、发现和统一调用入口,目前包含:

  • parse_user_intent
  • estimate_visit_duration
  • compute_transit
  • route_sort_day
  • validate_itinerary

Registry 会在调用 handler 前校验已公布的 input schema,并将 JSON Schema 或 Pydantic 参数错误统一返回为 invalid_tool_args

3. Harness Runner

确定性 Runner 使用固定链路:

parse_user_intent
-> route_sort_day
-> compute_transit
-> validate_itinerary

LLM Planner 可以参与工具规划,但 Harness 会对必需工具重新排序、去重并补齐,模型无法绕过本地执行边界。

当 LLM 未配置、网络异常或 provider 不可用时,Planner 返回 planner_unavailable warning,并降级到固定工具链,不会中断整次运行。

4. 状态保真与交通计算

  • 校验前保留原始 slotarrive_timestay_minutes
  • 到达时间必须为合法 24 小时制 HH:MM 且严格递增
  • 停留时间限制为 1 到 600 分钟
  • 校验重复 POI、缺失或越界坐标、每日角色完整性
  • 相邻路段按现有 2km 阈值选择 walking 或 driving
  • walking 使用独立步行速度估算
  • mode 会进入结果和 replay trace

5. Replay 与隐私边界

Live 记录默认写入 gitignored 的 .agent_records/,文件权限为 0600

默认使用 record_mode=redacted,不会记录:

  • 用户原始 query
  • POI 名称和坐标
  • Planner 原始响应
  • 工具参数
  • warning 中可能包含的地点信息

脱敏记录仍保留工具调用名称、成功或降级状态、warning code、stop 数、缺坐标数、transit 数和 validation 摘要,可继续用于聚合评估。

只有显式设置 include_sensitive=True,或运行 live 测试时设置:

ITRAVEL_RECORD_SENSITIVE=1

才会保存完整 replay。原先受 Git 跟踪的 live JSONL 已删除。

6. Eval Harness

evals/travel_plan_eval.py 同时支持旧 workflow 记录、完整 Agent Harness record 和脱敏摘要。

当前指标包括:

  • compliance rate
  • degradation rate
  • average stop count
  • missing coordinate rate
  • tool warning count

合规率不再以“是否存在输出”判断,而是优先使用 validation.valid;旧 workflow 数据会检查每日角色、时间、坐标、停留时长和相邻交通契约。

根据评审完成的修复

已覆盖本轮评审的 4 个 P1 和 5 个 P2:

  • 必需工具顺序规范化与去重
  • 校验字段保真
  • LLM 故障降级
  • Eval 合规率修正
  • 工具失败向运行级传播
  • Registry 参数校验
  • 时间和停留时长校验
  • walking 与 driving 交通模式
  • 默认记录隐私保护

同时包含 PR #9 的三项 route sort 边界修复。

测试

当前分支:

103 passed, 1 skipped, 1 warning

与最新 origin/main 合并预演:

108 passed, 1 skipped, 1 warning

跳过项是需要显式开启并消耗 API 额度的 live LLM 测试。warning 来自现有 FastAPI TestClient 依赖的弃用提示,与本次修改无关。

设计边界

本次没有:

  • 替换现有 /plan/stream
  • 修改前端协议
  • 引入 LangGraph
  • 实现完全自主 agent loop
  • 强依赖 function calling
  • 接入小红书或携程非官方接口
  • 移除现有 workflow fallback

@RCF-117
RCF-117 marked this pull request as ready for review July 7, 2026 07:19

@kaikkd kaikkd left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c71decc..86e62e2 增量复核,避免重复报告 PR #9。现有测试为 81 passed、1 skipped,但最小复现确认 4 个 P1 和 5 个 P2 语义缺陷,涉及工具链顺序、校验字段保真、LLM 故障降级、eval 有效性及隐私边界。建议修复这些问题并补 runner/eval 级回归测试后再合并。

Comment thread backend/app/agent/planner.py Outdated
else:
# v0 harness 需要稳定、可比较的 trace。即使模型漏掉某个工具,也补齐
# 最小工具链,避免不同 prompt/model 版本生成不可回放的半截记录。
existing = {call.name for call in tool_calls}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 请规范化必需工具的顺序并去重

这里只计算缺项,保留模型给出的原顺序和重复项。包含全部四个工具的逆序响应会被原样接受,degraded=Falselive_runner 随后先 validate、再 compute、最后 sort,造成过期校验和空交通段,重复调用也会重复执行外部工具。

建议根据依赖重建唯一链 parse_user_intent → route_sort_day → compute_transit → validate_itinerary,对乱序/重复返回 warning;可选工具再放入明确的安全插槽。

Comment thread backend/app/agent/runner.py Outdated
"stops": [
{
"order_index": idx,
"arrive_time": None,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 校验前不要丢弃 slotarrive_time

这个转换没有复制 slot,并把所有 arrive_time 强制改成 None。完整的早餐、景点、午餐、晚餐、酒店输入经 runner 后会被误报 incomplete_day_structure;同时 non_monotonic_time 永远看不到原始时间。

请原样保留这两个字段(以及其他校验所需字段),并增加“完整日程应 valid”与“逆序时间应告警”的 runner 级测试。

Comment thread backend/app/agent/planner.py Outdated
),
},
]
raw = "".join(llm.stream_chat(messages, max_tokens=800))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 模型不可用时应退回确定性工具链

stream_chat 在异常处理之外。未配置密钥、网络异常或供应商错误会直接逃逸;模拟 provider down 时,LLMAgentHarnessRunner.run() 在生成任何 trace/record 前崩溃,下面针对无效 JSON 的 fallback 完全不会执行。

建议捕获模型调用异常并返回 planner_unavailable warning、degraded=True 和规范的 fallback calls。

Comment thread evals/travel_plan_eval.py Outdated
]
return {
"id": record.get("id", ""),
"compliant": bool(record.get("days")) or bool(stops),

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] compliance_rate 不能只判断是否存在任意输出

当前只要 days 非空或存在一个 stop 就算 compliant。自带的 missing_coord_degraded fixture 只有一个缺坐标景点,仍被判合规;CLI 因而输出 compliance_rate=1.0。带 validation.valid=false 的 harness record 也会被同样误判。

请优先使用 validation 结果,并为旧 workflow 记录执行每日角色、时间、坐标和交通契约校验;同时修正 fixture 与测试期望。

Comment thread backend/app/agent/runner.py Outdated
state=state,
trace=trace,
warnings=warnings,
degraded=any(entry.result.degraded for entry in trace),

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] registry 应实际执行已公布的 input_schema

目前 schema 只展示给 planner,调用时直接进入 handler。parse_user_intent 缺少 required 的 query 仍返回成功并默认为成都三天;非法 mode、越界坐标和嵌套 stop 结构也不会在统一入口被拒绝。

建议在 handler 前执行 JSON Schema/Pydantic 校验,失败返回结构化 invalid_tool_argsstops 还需要完整的 items schema。

Comment thread backend/app/tools/harness_tools.py Outdated
slot = _slot_from_raw_stop(raw_stop, stop.poi.category)
if slot:
slots.append(slot)
minutes = _hhmm_to_min(stop.arrive_time)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 请校验时间格式和停留时长上限

_hhmm_to_min 接受 99:99,无法解析的字符串则被静默跳过;stay_minutes=9999 也未检查。完整日程将所有时间设为 99:99、停留时间设为 9999 时,工具仍返回 valid=true 且无 warning。

建议复用现有 validators.validate_arrive_time / validate_stay_minutes,对非法值给 warning,并要求到达时间严格递增。

Comment thread backend/app/agent/runner.py Outdated
"from_lat": prev.get("lat"),
"to_lng": cur.get("lng"),
"to_lat": cur.get("lat"),
"mode": "driving",

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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"),

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 默认不要把原始用户内容写进受 Git 跟踪的 fixture

这里原样记录 request、planner raw response 和完整 trace;live 测试的默认路径又是 evals/fixtures/agent_harness_live.jsonl。用户地址、联系方式、精确行程或误贴凭证可能在一次本地 live 测试后进入开源提交,当前测试只搜索字段名 openai_api_key,无法防止这类泄露。

建议默认写入 gitignored 的私有目录、限制文件权限并做脱敏;保留原文或写入 tracked fixture 应显式启用。

@RCF-117

RCF-117 commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

已根据本轮评审完成全部修复,并补充对应回归测试。

主要更新包括:

  • 规范化并去重必需工具链,安排可选工具安全位置
  • 保留 slotarrive_timestay_minutes 后再校验
  • LLM/provider 不可用时降级到确定性工具链
  • 修正 compliance rate,增加旧 workflow 行程契约校验
  • 建立 ok=false => degraded=true 模型不变量
  • Registry 统一校验 JSON Schema 和 Pydantic 参数错误
  • 校验时间格式、严格递增和停留时长上限
  • 按 2km 阈值选择 walking/driving,并保留 mode
  • 默认记录改为脱敏摘要,完整 replay 必须显式启用
  • 删除受 Git 跟踪的 live 用户数据 fixture

当前分支测试:103 passed, 1 skipped
与最新 origin/main 合并预演:零冲突,108 passed, 1 skipped

烦请重新审阅。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants