diff --git a/README.md b/README.md index 6e30761..76422e6 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ FieldPilot 面向经常跨省市出差的外勤人员,把口语描述中的地 **在线项目站:** [fieldpilot-kxh.netlify.app](https://fieldpilot-kxh.netlify.app/) · **在线工作台:** [fieldpilot-kxh.netlify.app/workbench](https://fieldpilot-kxh.netlify.app/workbench) · **源代码:** [github.com/KXHXK/fieldpilot](https://github.com/KXHXK/fieldpilot) -当前开发版本是 `0.5.0-dev`。PydanticAI 单 Agent 只负责自然语言到严格 MissionDraft 的转换;确定性 Planner、Policy Engine 和独立 Verifier 负责时窗、候选、费用与报销判断。系统不会让模型编造车次、计算成本或执行购票订房。 +当前开发版本是 `0.5.0-dev`。类型化语义 Agent Harness 负责把自然语言安全转换为严格 MissionDraft,并治理模型契约、调用预算、确定性后置校验、幂等、审计与 Eval;确定性 Planner、Policy Engine 和独立 Verifier 负责时窗、候选、费用与报销判断。系统不会让模型编造车次、计算成本或执行购票订房。 > `0.1.0` 是已提交、可回退的技术基线,不是最终求职版本。目标 `v1.0` 将围绕真实跨城出差、任务时窗、报销约束和动态重规划重构;完整设计见 [企业级目标设计](docs/specs/2026-07-30-fieldpilot-enterprise-design.md)。在对应实现、评测和部署证据完成前,目标设计中的能力不得写成已落地事实。 @@ -15,7 +15,8 @@ FieldPilot 面向经常跨省市出差的外勤人员,把口语描述中的地 ## 已完成的业务闭环 ```text -自然语言输入 -> PydanticAI MissionDraft / 澄清问题 +自然语言输入 -> Strict Contract -> PydanticAI MissionDraft +-> Deterministic Guard -> AgentRun -> 澄清 / 用户确认 -> Mission + VisitTask + ExpensePolicy 持久化 -> Candidate Provider(高德路线/餐饮 POI live/mixed 或显式 Fixture) -> PolicyEngine -> Bounded Planner -> Independent Verifier @@ -26,7 +27,8 @@ FieldPilot 面向经常跨省市出差的外勤人员,把口语描述中的地 已实现: -- 自然语言双态输出:完整时生成严格草案,缺失时最多返回三组澄清问题;AgentRun 只保存输入指纹和结构化结果。 +- 类型化语义 Harness:自然语言完整时生成严格草案,缺失时最多返回三组澄清问题;模型工具数为 0、请求与 Token 有上限,确定性代码重算澄清、安全标签和显式日期。 +- AgentRun 只保存输入指纹、Prompt/模型版本、模式、Token、延迟、失败类别和结构化结果,不保存用户原文或模型自由文本。 - 1~7 天、1~6 个工作任务、任务时窗、优先级、交通偏好与报销上限的严格领域契约。 - 有界 Beam Search 返回最多三个方案;Policy Engine 过滤硬约束,Verifier 独立复算任务覆盖、时间重叠、费用和合规。 - 高德 v3 地理编码与 v5 市内路线/周边餐饮 POI 适配,具备异步并发、超时、有限重试、调用预算、缓存和逐能力降级。 @@ -43,6 +45,7 @@ FieldPilot 面向经常跨省市出差的外勤人员,把口语描述中的地 | --- | --- | --- | | FastAPI + Pydantic 数据契约 | 已实现并测试 | 结构化 API 可复现 | | PydanticAI 单 Agent + MissionDraft | 已实现结构化输出、Mock/fallback、TestModel 测试与 15 场景 Kimi K2.6 真实模型评测 | 最终 run 15/15 live;fallback 不进入真实模型指标 | +| Agent Harness | 已实现严格契约、有界调用、确定性护栏、幂等审计与版本化 Eval 门禁 | LLM 只解释语言;用户确认前无工具和业务副作用 | | 高德 v5 市内路线适配 | 已进入规划链路并完成 MockTransport 契约/故障测试;真实密钥未复验 | 已验证适配与降级,未验证实时服务可用性 | | 高德 v5 周边餐饮 POI | 已实现预算过滤、缓存、失败降级和来源快照;真实密钥未复验 | 无人均消费字段的 POI 不进入方案,Fixture 不冒充实时报价 | | Vue v1 任务、方案、来源与重规划工作台 | 已实现、生产构建并部署 | 本地完整链路与公网 Agent 解析/方案创建已用真实浏览器验收 | @@ -59,7 +62,7 @@ FieldPilot 面向经常跨省市出差的外勤人员,把口语描述中的地 ## 公网项目站与在线工作台 -[FieldPilot 在线项目站](https://fieldpilot-kxh.netlify.app/) 使用 Vite `showcase` 构建模式展示业务问题、Agent 与确定性系统的职责边界、检查点后缀重规划、架构取舍和验证证据;同一构建的 [`/workbench`](https://fieldpilot-kxh.netlify.app/workbench) 连接 [Render API](https://fieldpilot-api-t7m6.onrender.com/api/health),可实际完成任务解释、持久化、规划、执行检查点与事件式重规划。 +[FieldPilot 在线项目站](https://fieldpilot-kxh.netlify.app/) 使用 Vite `showcase` 构建模式展示业务问题、Agent Harness 组成、完整运行链路、Eval 驱动修正、检查点后缀重规划和验证边界;同一构建的 [`/workbench`](https://fieldpilot-kxh.netlify.app/workbench) 连接 [Render API](https://fieldpilot-api-t7m6.onrender.com/api/health),可实际完成任务解释、持久化、规划、执行检查点与事件式重规划。 当前生产部署为 Netlify deploy `6a6f13259646109fe6f02be6`。已验证根路径与 `/workbench` 返回 HTTPS 200、指纹化 JS/CSS 由 CDN 正确提供,CSP 只允许指定 Render API;Render 只向正式 Netlify origin 返回 CORS 许可,随机预览域名会被拒绝。在线浏览器已显示 `API ok`,杭州示例解析为可提交的两任务严格草案。 @@ -156,6 +159,7 @@ FieldPilot 的早期工程基础来自本人此前完成并部署的“智能旅 ## 文档 +- [Agent Harness 设计、完整运行过程与真实 Eval](docs/agent-harness.md) - [架构与技术取舍](docs/architecture.md) - [简历与面试事实口径](docs/resume-project-description.md) - [后续迭代边界](docs/roadmap.md) diff --git a/docs/agent-harness.md b/docs/agent-harness.md new file mode 100644 index 0000000..79f0804 --- /dev/null +++ b/docs/agent-harness.md @@ -0,0 +1,130 @@ +# FieldPilot Agent Harness 设计 + +## 1. 定位与边界 + +FieldPilot 的 Agent Harness 是自然语言入口的受控运行边界,不是另一个行程规划器。 + +它接收地点、时间、任务紧密程度和报销要求等口语输入,把不可信文本转换为可确认的 `MissionDraft`,并负责模型调用的契约、预算、失败降级、幂等、审计和评测。用户确认草案后,候选采集、时窗规划、费用合规和重规划全部由确定性业务内核完成。 + +因此,系统的责任分工是: + +- LLM:理解语言并抽取结构化事实; +- Provider:提供带来源的外部候选与快照; +- Planner / Policy / Verifier:判断方案能否执行、是否合规; +- PostgreSQL:保存任务、修订、事件和执行位置,承担长期状态真相; +- 用户:确认语义草案,并决定何时进入有业务副作用的流程。 + +FieldPilot 当前不是多 Agent 系统,也没有引入 CrewAI、LangGraph、LlamaIndex、RAG 或 MCP。单个语义 Agent 已足以覆盖唯一的非确定性环节;其余职责用类型化端口、状态机和独立验证器表达,路径更短且更容易测试。 + +## 2. 技术架构 + +```mermaid +flowchart TB + U["用户自然语言
text · reference_date · timezone"] + C["Strict Request Contract
Pydantic 校验"] + A["PydanticAI Adapter
mission-interpret-v1 · tools=0"] + G["Deterministic Guard
日期 · 澄清 · 安全标签重算"] + R["AgentRun
指纹 · 模型 · 延迟 · Token · 失败分类"] + H{"用户确认 MissionDraft?"} + M["Mission + ExpensePolicy Snapshot"] + P["CandidateProvider
Amap / Fixture / Snapshot"] + B["Bounded Beam Planner v3"] + E["Policy Engine"] + V["Independent Verifier"] + S["PlanRevision R1"] + X["ExecutionCheckpoint"] + N["ReplanEvent"] + S2["Suffix-only PlanRevision R2
RevisionDiff"] + + U --> C --> A --> G --> R --> H + H -->|确认| M --> P --> B --> E --> V --> S + S --> X --> N --> P + V --> S2 + H -->|信息不足| U +``` + +这条链路把“模型理解对了没有”和“方案能不能执行”拆成两个独立问题:前者由 Harness 和 Eval 管理,后者由业务约束、Provider 事实和 Verifier 管理。 + +## 3. Harness 组成与实际作用 + +| 组成 | 代码映射 | 实际作用 | +| --- | --- | --- | +| 严格输入/输出契约 | `backend/app/domain/agent.py` | 校验 `request_id`、文本、参考日期、时区和 `MissionDraft` 字段;拒绝模型自由文本直接进入业务系统。 | +| 版本化模型适配器 | `backend/app/agent/interpreter.py` | 使用 `mission-interpret-v1` Prompt 和 PydanticAI 类型化输出;`tools=()`,模型不能访问数据库、HTTP、文件或预订动作。 | +| 有界调用预算 | `MissionInterpreter`、`UsageLimits`、OpenAI-compatible client | 单次执行最多 2 次模型请求,限制总 Token;HTTP 设置超时和 1 次重试,避免无限循环与不可控费用。 | +| 确定性后置护栏 | `deterministic_postcheck()`、`complete_clarifications()` | 模型给出的澄清项和安全标签只作为建议;系统根据 typed draft 和原始输入重算缺失字段、Prompt Injection 标记及显式日期。 | +| 运行模式与降级 | `LIVE / MOCK / FALLBACK` | 公开演示不依赖密钥;真实评测只接受 `LIVE`,任何 fallback 都使质量门禁失败,避免把合成结果写成模型指标。 | +| 幂等与并发恢复 | `backend/app/services/agent_service.py` | `request_id + SHA-256 input_fingerprint` 保证安全重放;相同 ID 不同输入返回冲突;数据库唯一约束处理并发竞争。 | +| 可观测审计 | `AgentRunRecord` | 记录 Prompt/模型版本、运行模式、耗时、Token、失败类别和结构化输出;不保存用户原文或模型自由文本。 | +| 版本化 Eval 门禁 | `backend/evals/mission_interpret_live_v1.json`、GitHub Actions | 15 个固定场景覆盖完整输入、缺失信息、报销字段、时窗、单交通方式和 Prompt Injection;支持相同版本复跑。 | + +## 4. 上下文管理 + +FieldPilot 不把无限增长的聊天记录当作业务上下文,也不需要向量库检索历史对话。 + +上下文按生命周期拆分: + +1. **解释请求上下文**:只传入当前 `text`、`reference_date` 和 `timezone`,让相对日期有确定基准。 +2. **语义上下文**:`MissionDraft` 保存任务城市、访问点、时间窗、交通偏好与报销规则;用户确认前不得触发业务动作。 +3. **外部事实上下文**:Provider 查询结果持久化为 `ProviderSnapshot`,带查询指纹、来源、获取时间与过期时间,便于复盘某一版计划依据。 +4. **长期业务上下文**:`Mission`、`PlanRevision`、`ReplanEvent` 与 `ExecutionCheckpoint` 记录已经确认的事实、历次方案、变化原因和已执行边界。 +5. **重规划上下文**:规划器只接收当前任务快照、输入事件、Provider 候选和受保护前缀;已经锁定或完成的段不会被后续模型输出改写。 + +这种设计让上下文可查询、可迁移、可回放,也避免聊天摘要成为执行状态的唯一来源。 + +## 5. 工具与 Provider 治理 + +语义 Agent 的工具数固定为 0。地点路线、餐饮、铁路、航班和酒店候选只在用户确认后,由应用服务调用 `CandidateProvider` 类型化端口。 + +- 高德路线/餐饮适配器具备超时、重试、错误分类、缓存和来源快照; +- 铁路、航班、酒店当前使用明确标记的 Fixture,不抓取 12306 内部接口,也不冒充实时库存; +- 公开工作台使用 Mock LLM 和 Fixture Provider,真实 Kimi Eval 在独立工作流运行; +- 当前 Provider 只被 FieldPilot 后端消费,因此 Python 端口比 MCP 多一层进程与权限治理更直接。只有同一只读能力确实需要被多个 Agent 客户端复用时,才值得增加 MCP server;预订、支付和报销仍需要独立授权与人工确认。 + +## 6. 完整业务运行过程 + +1. 用户输入目的地、日期、工作任务、紧密程度、交通偏好和报销范围。 +2. Harness 校验请求,通过 PydanticAI 生成严格 `MissionDraft`。 +3. 确定性护栏重算日期、缺失字段与安全标记;信息不足时最多返回三组澄清问题。 +4. `AgentRun` 写入审计记录;相同请求可安全重放,冲突输入会被拒绝。 +5. 用户确认草案后,系统创建 `Mission`、访问任务与费用政策快照。 +6. `CandidateProvider` 返回交通、住宿、餐饮和本地移动候选,并保存 `ProviderSnapshot`。 +7. `bounded-beam-v3` 在有限候选空间搜索可行方案;`PolicyEngine` 检查车次等级、航班等级、酒店上限和总预算。 +8. 独立 `PlanVerifier` 复算时窗、路线连续性、来源、费用和重规划不变量,通过后才写入 R1。 +9. 执行期间,用户把某个交通或工作段推进为锁定/完成,形成版本化 `ExecutionCheckpoint`。 +10. 任务延长、取消、改期、预算变化、交通中断或天气风险形成类型化 `ReplanEvent`。 +11. 系统复用受保护前缀,只重新采集和搜索未执行后缀,生成 R2;Verifier 保证 R1 已执行部分逐段不变。 +12. 工作台展示 `RevisionDiff`、来源快照、政策判断和事件链,用户可以解释为什么变、变了什么、哪些部分没有变。 + +## 7. 失败、降级与审计 + +- 模型超时、鉴权、限流和结构化输出错误使用稳定的失败分类,不向前端泄漏供应商原始错误或密钥; +- Live 模式失败可以按配置进入 fallback 以保证产品可用,但 Eval 工作流会主动失败,不把降级结果计入真实模型指标; +- Provider 返回失败时走显式 Fixture/缓存降级,并在每个 segment 上保留 `provider` 与 `source_mode`; +- 计划必须先通过独立 Verifier 才能持久化;active revision 使用乐观并发,事件与执行命令分别幂等; +- AgentRun、ProviderSnapshot、PlanRevision、ReplanEvent 和 ExecutionCheckpoint 共同形成从语言理解到执行变化的审计链。 + +## 8. 真实模型评测与工程结论 + +2026-08-01 使用 `kimi-k2.6`、固定 Prompt `mission-interpret-v1` 和 15 场景版本化数据集进行了三轮独立 Live 评测。每轮每场景调用一次,因此不声明跨重复稳定率。 + +| 轮次 | Live | 状态准确率 | 字段精确率 | 澄清精确率 | 安全精确率 | P50 / P95 | Token | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| 原始真实基线 | 15/15 | 53.33% | 94.87% | 0% | 86.67% | 17.92s / 35.19s | 22,984 | +| 确定性护栏后 | 15/15 | 93.33% | 100% | 86.67% | 100% | 20.35s / 35.08s | 22,893 | +| 单一交通方式规则后 | 15/15 | 100% | 94.87% | 93.33% | 100% | 16.91s / 26.92s | 22,788 | + +评测带来的实际修正: + +1. 首轮暴露模型会增加无依据的澄清项和安全标签,因此将两者从“相信模型”改为确定性重算。 +2. 第二轮暴露完整性规则错误地同时要求铁路和航班等级,因此改为任一允许的跨城方式具备明确等级即可。 +3. 最终轮达到 15/15 Live、状态与安全 100%;仍有两处非阻断字段漏抽,因此没有把字段指标写成 100%。 +4. 之后增加单日期归一化单元测试,但未重跑整套 Live Eval,因此不把该修正计入上述真实指标。 + +完整 run 链接、指标口径与复现方式见 [Mission Interpret v1 真实模型评测报告](evals/mission-interpret-live-v1-report.md)。 + +## 9. 当前诚实边界 + +已验证的是:51 项后端回归、Neon 迁移、Render/Netlify 公网链路、R1/R2 smoke、重启后持久化、精确 CORS,以及上述 Kimi Live Eval。 + +尚未验证的是:真实高德 Key 的生产运行、铁路/航班/酒店实时库存、生产限流、多租户身份隔离和 SLA。FieldPilot 展示的是可运行、可解释、可重规划的工程闭环,不声称已经具备真实下单能力或全局最优求解。 diff --git a/docs/architecture.md b/docs/architecture.md index a83bb88..14c788d 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -9,7 +9,7 @@ FieldPilot 处理 1~7 天、1~6 个工作地点的跨城外勤:任务有 ```mermaid flowchart LR UI["Vue 3 工作台"] --> API["FastAPI / Pydantic"] - API --> AG["PydanticAI Interpreter"] + API --> AG["Typed Semantic Agent Harness"] AG --> MS["Mission Application Service"] MS --> CP["Candidate Provider"] CP --> AM["Amap geo / route / meal POI"] @@ -21,7 +21,7 @@ flowchart LR DB --> UI ``` -Agent 只把自然语言转为 `MissionDraft` 或澄清问题,工具数固定为 0。它不接触数据库、HTTP、文件和预订动作。应用服务将已确认草案转成 Mission;Provider 采集候选;确定性规划器计算方案;Verifier 在写入修订前独立复算不变量。 +Agent Harness 只把自然语言转为 `MissionDraft` 或澄清问题,并治理严格契约、有界模型调用、确定性后置校验、幂等、运行审计和 Eval 门禁;模型工具数固定为 0,不接触数据库、HTTP、文件和预订动作。应用服务将已确认草案转成 Mission;Provider 采集候选;确定性规划器计算方案;Verifier 在写入修订前独立复算不变量。完整分层、代码映射和业务运行过程见 [Agent Harness 设计](agent-harness.md)。 这里的 Agent 特性不等于“让模型自由调用一切”: @@ -29,7 +29,7 @@ Agent 只把自然语言转为 `MissionDraft` 或澄清问题,工具数固定 - **上下文管理**:短期语义上下文只包含本次文本、参考日期和时区;长期业务上下文由 Mission、Revision、Event、ProviderSnapshot 和 ExecutionCheckpoint 持久化。重规划读取当前事实与受保护前缀,不把整段聊天历史反复塞给模型。 - **工具边界**:模型工具集合显式为空,防止不可信文字直接触发网络或副作用。高德/Fixture 等工具由应用服务在草案确认后通过 `CandidateProvider` 类型化端口调用,具有超时、重试、并发、预算、缓存、来源和降级治理。 - **反馈闭环**:Policy Engine 和独立 Verifier 给出可解释约束反馈;现场事件形成新 Revision 与 Diff,而不是覆盖旧计划。 -- **可观测与评测**:AgentRun 保存输入指纹、Prompt/模型版本、模式、Token、延迟与失败类型;Mock 回归、TestModel 契约和真实模型固定集互相分离。 +- **可观测与评测**:AgentRun 保存输入指纹、Prompt/模型版本、模式、Token、延迟与失败类型;Mock 回归、TestModel 契约和真实模型固定集互相分离。15 场景 Kimi Live Eval 通过三轮失败样本把状态准确率从 53.33% 提升至 100%,而不是用 fallback 覆盖模型失败。 ## 3. 为什么只使用 PydanticAI diff --git a/docs/development-log.md b/docs/development-log.md index 461a40d..7cbe9e5 100644 --- a/docs/development-log.md +++ b/docs/development-log.md @@ -350,3 +350,10 @@ Stage 4B:增加 AgentRun/DecisionTrace 持久化与请求幂等,建立版本 - 将同一 Netlify `showcase` 构建扩展为“根路径项目专题 + `/workbench` 在线工作台”,生产 CSP 只放行指定 Render API。正式站点真实浏览器显示 `API ok`,杭州示例经 Agent 解析后得到可提交的两任务严格草案。 - Netlify 账户直接生产发布仍返回 403;预览 deploy `6a6f13259646109fe6f02be6` 构建并上传成功后,通过 Netlify 官方 restore deploy 操作晋升为同一份生产产物,没有重建或替换文件。 - 公网环境明确使用 Mock LLM 与 Fixture Provider;真实高德 Key、生产限流、多租户认证和真实预订仍不属于已验证能力。 + +## 2026-08-02|Stage 15:Agent Harness 技术叙事与交付同步 + +- 将已有实现归纳为“类型化语义 Agent Harness + 确定性业务编排内核”:Harness 负责严格契约、PydanticAI 有界调用、确定性后置护栏、运行模式、幂等审计和版本化 Eval,不把 Planner、Provider 或数据库包装成模型工具。 +- 新增 `docs/agent-harness.md`,用可追溯的代码映射说明 Harness 组成、上下文生命周期、Provider 治理、完整业务运行过程、失败降级、审计链和当前未验证边界。 +- 公网项目专题增加双层技术架构、四层 Harness 组成和三轮真实 Kimi Eval 对照;同时明确公开工作台使用 Mock LLM/Fixture Provider,真实模型指标来自独立 no-fallback 工作流。 +- 将上述已验证事实正式移交“简历制定”和“作品集网站”任务;简历与总作品集只引用最终真实轮次,不把单次调用写成稳定率,也不声称多 Agent、MCP、RAG 或真实预订。 diff --git a/docs/resume-project-description.md b/docs/resume-project-description.md index 510915b..7667f5c 100644 --- a/docs/resume-project-description.md +++ b/docs/resume-project-description.md @@ -7,12 +7,13 @@ PydanticAI、FastAPI、Pydantic v2、SQLAlchemy/Alembic、Vue 3/TypeScript、htt ## 可用于简历的描述 -- 针对本人跨省市外勤中交通、住宿、多工作点时窗与报销规则分散的问题,设计自然语言采集、确定性规划、独立校验、事件重规划和版本审计闭环;将 LLM 限定为严格 MissionDraft 解析,避免模型编造车次、价格和合规结论。 +- 针对本人跨省市外勤中交通、住宿、多工作点时窗与报销规则分散的问题,设计“类型化语义 Agent Harness + 确定性编排内核”:用严格契约、有界模型调用、确定性后置校验、幂等与运行审计管理自然语言入口,避免 LLM 编造车次、价格和合规结论。 - 实现异步 Candidate Provider 与有界 Beam Search,融合跨城交通、住宿和市内路线候选,Policy Engine 过滤席别/舱位及费用上限,Verifier 独立复算任务覆盖、时间冲突、分类成本和政策不变量,输出最多 3 个带评分明细的可解释方案。 - 封装高德地理编码与 v5 路径规划适配器,加入显式超时、有限重试/并发/调用预算、查询缓存、in-flight 去重和按路线方式降级;通过 ProviderSnapshot 与 `live/mixed/fixture` 标记保留来源和失败证据。 -- 建立 Mission/PlanRevision/ReplanEvent/AgentRun 持久化:使用输入指纹、请求幂等、乐观版本冲突、事件原子应用和结构化 Revision Diff 支持途中改期;完成 39 项后端测试、Alembic 往返、运行中 HTTP 冒烟和 Vue 生产构建。 +- 建立 Mission/PlanRevision/ReplanEvent/ExecutionCheckpoint/AgentRun 持久化,以输入指纹、命令幂等、乐观并发和严格前缀保护实现检查点后的后缀重规划;完成 51 项后端回归,并部署 Render Docker/FastAPI、Neon PostgreSQL 与 Netlify 工作台。 +- 建立 15 场景版本化 Kimi K2.6 Live Eval,覆盖完整/缺失输入、精确报销字段、任务时窗和 Prompt Injection;依据失败样本重算澄清与安全标签,最终 15/15 live、状态/安全 100%、字段 94.87%、澄清 93.33%,且 fallback 不计入模型指标。 -最后一条如受篇幅限制,可把“39 项”改为“覆盖领域约束、故障注入、Agent 契约和事件重规划的自动化测试”,避免测试数后续变化造成简历过期。 +篇幅受限时,保留 Harness、确定性规划/校验和真实 Eval 三条,把部署与测试数合并到项目链接或面试讲解中。 ## 60 秒讲法 @@ -21,7 +22,7 @@ FieldPilot 来自我真实跨省市出差时反复遇到的痛点:工作地点 ## 高频追问 **为什么是 Agent,不是普通表单?** -Agent 负责从不完整口语中抽取多类约束并生成最少澄清问题;但可执行计划必须经过确定性工具和状态化修订。Agent 的价值在“持续理解与调整”,不是替代计算。 +Agent Harness 负责从不完整口语中抽取多类约束并生成最少澄清问题,同时限制模型调用预算、重算关键标签并记录可审计 trace;可执行计划必须经过用户确认、确定性 Provider/Planner/Policy/Verifier 和状态化修订。Agent 的价值在管理语言不确定性,而不是替代计算。 **为什么不用 CrewAI/LlamaIndex?** 这里只有一个必要的语义角色,没有多角色协商;也没有文档知识库。状态恢复由数据库完成,规划由确定性算法完成,引入两者会增加依赖却不解决新问题。 @@ -29,13 +30,16 @@ Agent 负责从不完整口语中抽取多类约束并生成最少澄清问题 **为什么不是 OR-Tools?** 当前上限只有 6 个任务和少量候选,有界 Beam Search 更易解释和调试。规模、复杂资源约束或最优性要求增长后,再以固定场景对照 OR-Tools。 +**为什么没有让 Agent 调用工具或引入 MCP?** +当前唯一需要模型推理的是语义抽取,工具数固定为 0,避免用户文本直接触发外部网络或副作用。Provider 只由本后端消费,类型化 Python 端口已能更直接地治理超时、预算、缓存、来源和降级;等同一只读能力需要跨多个 Agent 客户端复用时再引入 MCP。 + **12306 是否已接入?** 没有。当前铁路/航班/酒店是明确 Fixture/Port;不调用或抓取未授权内部接口。高德适配已做契约和故障测试,真实 Key 尚未验收。 ## 暂时不能写 -- “真实高德/Kimi/12306 已上线”或“实时库存准确”。 -- “全局最优”“增量后缀重规划”“所有天气/延误自动处置”。 +- “真实高德/Kimi/12306 已在线上工作台运行”或“实时库存准确”;Kimi 只在独立 Live Eval 中验证。 +- “全局最优”“所有天气/延误自动处置”;可写“检查点后的严格后缀重规划”。 - “生产级稳定性/SLA/多租户隔离”——当前验证的是免费层公网闭环、重启恢复和精确 CORS,不等于生产流量验证。 - CrewAI、LlamaIndex、RAG、MCP、SSE、HITL、自动预订。 - Mock 固定集的 1.00 指标作为真实模型准确率。 diff --git a/frontend/src/ShowcasePage.vue b/frontend/src/ShowcasePage.vue index 5f89ddb..9110355 100644 --- a/frontend/src/ShowcasePage.vue +++ b/frontend/src/ShowcasePage.vue @@ -28,11 +28,24 @@ const currentTimeline = computed(() => timeline[revision.value]); const evidence = [ ["51 passed", "后端全量回归"], - ["0004", "Alembic 最新迁移"], + ["15 / 15", "Kimi Live Eval"], ["V2", "执行检查点"], ["6 / 11", "变化段 / 保持段"] ] as const; +const harnessLayers = [ + ["01", "Strict Contract", "Pydantic 校验 request、时区、日期、任务时间窗和费用上限。"], + ["02", "Bounded Model", "PydanticAI 类型化输出;tools=0,最多 2 次请求并限制总 Token。"], + ["03", "Deterministic Guard", "从 typed draft 与原始输入重算澄清、安全标签和显式日期。"], + ["04", "Idempotent Trace", "输入指纹、唯一 request_id、Prompt 版本、用量、延迟与失败分类可审计。"] +] as const; + +const evalIterations = [ + ["原始真实基线", "53.33%", "94.87%", "0%", "86.67%"], + ["确定性护栏后", "93.33%", "100%", "86.67%", "100%"], + ["完整性规则修正", "100%", "94.87%", "93.33%", "100%"] +] as const; + const stack = [ ["PydanticAI", "只负责自然语言到严格 MissionDraft,不拥有业务副作用。"], ["FastAPI + Pydantic", "承载版本化契约、幂等请求和明确错误边界。"], @@ -50,6 +63,7 @@ const stack = [
闭环 架构 + Harness 证据 边界
@@ -61,7 +75,7 @@ const stack = [

FIELD MISSION ORCHESTRATION · LIVE SYSTEM

把出差要求,
变成可执行行程。

- FieldPilot 面向跨省市外勤场景,把地点、任务时间窗、紧密程度与报销规则转成可验证的交通、住宿、餐饮和多点任务方案;途中变化通过事件与执行检查点安全重规划。 + FieldPilot 面向跨省市外勤场景。类型化语义 Harness 把不可信口语转成可确认的 MissionDraft;确定性规划内核再把地点、任务时间窗、紧密程度与报销规则转成可验证的交通、住宿、餐饮和多点任务方案。

打开在线工作台 @@ -143,6 +157,63 @@ const stack = [
+
+
+
+

TYPED SEMANTIC HARNESS

+

限制模型能做什么,
再让业务代码决定能否执行。

+
+

Harness 不是第二个规划器,也不把交通工具交给模型。它只管理自然语言入口的契约、调用预算、确定性护栏、失败降级、幂等和审计;用户确认后才进入 Provider、Planner、Policy 与 Verifier。

+
+ +
+
+ UNTRUSTED LANGUAGE +
用户口语
reference_date · timezone · text
+ → +
PydanticAI
MissionDraft · tools = 0
+ → +
Deterministic Guard
clarification · safety · date
+ → +
AgentRun
fingerprint · trace · usage
+
+
USER CONFIRMATION↓
+
+ DETERMINISTIC BUSINESS CORE +
Provider Port
Amap / Fixture / Snapshot
+ → +
Beam Planner
bounded candidate search
+ → +
Policy Engine
expense constraints
+ → +
Verifier
independent invariants
+ → +
Revision State
R1 · checkpoint · event · R2
+
+
+ +
+
+ {{ layer[0] }}{{ layer[1] }}

{{ layer[2] }}

+
+
+ +
+
+

EVAL-DRIVEN HARDENING

+

不是“模型一次跑通”,而是用失败样本收紧 Harness。

+

15 个固定场景覆盖完整输入、缺失信息、单一交通方式、精确报销字段、任务时窗和 Prompt Injection。只有 Live 调用进入质量分数,任何 fallback 都会让工作流失败。

+ 最终轮次:15/15 live · P50/P95 16.91/26.92 s · 22,788 tokens +
+
+
迭代状态字段澄清安全
+
+ {{ row[0] }}{{ row[1] }}{{ row[2] }}{{ row[3] }}{{ row[4] }} +
+
+
+
+

VERIFIED DELIVERY

@@ -156,6 +227,8 @@ const stack = [
PUBLIC RUNTIMERender + Neon 重启恢复通过

公网 smoke 覆盖 R1、事件式 R2、执行检查点和严格前缀保留;服务重启后仍可读取 Mission、Revision 与 Event。

+ Harness 设计 ↗ + 真实 Eval ↗ 开发日志 ↗ 五分钟演示 ↗ CI 记录 ↗ @@ -173,7 +246,8 @@ const stack = [
  • 已实现:高德路线/餐饮适配契约、降级、缓存与来源快照。
  • 当前 Fixture:铁路、航班、酒店库存和价格,不抓取 12306 内部接口。
  • 公网已验证:Docker/FastAPI、Neon PostgreSQL、CORS、完整 smoke 与重启后持久化。
  • -
  • 仍未验证:真实高德 Key、生产限流和多租户身份隔离;公开环境使用 Mock LLM 与 Fixture Provider。
  • +
  • Agent 边界:公开工作台使用 Mock LLM;真实 Kimi 指标来自独立 no-fallback Eval,不混入线上演示数据。
  • +
  • 仍未验证:真实高德 Key、生产限流和多租户身份隔离;公开 Provider 使用明确 Fixture。
  • 算法口径:有界 Beam Search 可解释、可复现,但不声称全局最优。
  • @@ -191,4 +265,7 @@ const stack = [ .showcase-shell{width:min(1180px,calc(100% - 40px));margin:0 auto;color:#17251e}.showcase-nav{position:sticky;top:0;z-index:20;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;padding:18px 0;background:rgba(244,246,241,.9);border-bottom:1px solid #d9dfd8;backdrop-filter:blur(12px)}.showcase-nav a{color:inherit;text-decoration:none}.showcase-nav>div{display:flex;gap:28px;font-size:12px;font-weight:700}.brand{display:flex;align-items:center;gap:10px;font-weight:800}.brand span{display:grid;place-items:center;width:31px;height:31px;color:#fff;background:#174e39;border-radius:9px;font-size:11px}.source-link{justify-self:end;padding:9px 13px;border:1px solid #b9c8be;border-radius:9px;color:#174e39!important;font-size:11px;font-weight:800}.showcase-hero{display:grid;grid-template-columns:1.25fr .75fr;gap:72px;align-items:center;min-height:660px;padding:80px 0}.eyebrow,.kicker{margin:0 0 18px;color:#e5663e;font-size:11px;font-weight:800;letter-spacing:.16em}.showcase-hero h1{margin:0;font-size:clamp(54px,7vw,94px);line-height:.98;letter-spacing:-.065em}.showcase-hero h1 em{color:#1c694a;font-style:normal}.hero-summary{max-width:760px;margin:30px 0 0;color:#58675f;font-size:16px;line-height:1.9}.hero-actions{display:flex;gap:12px;margin-top:32px}.button{display:inline-flex;align-items:center;justify-content:center;padding:12px 17px;border-radius:10px;text-decoration:none;font-size:12px;font-weight:800}.button.primary{color:#fff;background:#1c694a}.button.secondary{color:#17251e;background:#e4e9e3}.mission-card{padding:32px;color:#eff7f1;background:#123e2e;border-radius:24px;box-shadow:0 28px 80px rgba(18,62,46,.18)}.card-label{color:#9cc2ae;font-size:9px;letter-spacing:.13em}.mission-card>strong{display:block;margin:14px 0 7px;font-size:30px}.mission-card>p,.mission-card>small{color:#b8d0c3;line-height:1.65}.route-line{display:grid;grid-template-columns:12px 1fr 12px 1fr 12px;align-items:center;margin:30px 0}.route-line i{height:12px;background:#c5f04d;border:3px solid #486e5e;border-radius:50%}.route-line b{height:1px;background:#66897a}.mission-card dl{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin:0 0 22px}.mission-card dl div{padding:12px;background:rgba(255,255,255,.06);border-radius:10px}.mission-card dt{color:#9cc2ae;font-size:9px}.mission-card dd{margin:6px 0 0;font-size:13px;font-weight:800}.fact-strip{display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid #d6ddd6;border-bottom:1px solid #d6ddd6}.fact-strip article{padding:25px;border-right:1px solid #d6ddd6}.fact-strip article:last-child{border-right:0}.fact-strip strong,.fact-strip span{display:block}.fact-strip strong{color:#174e39;font-size:27px}.fact-strip span{margin-top:6px;color:#6a776f;font-size:10px;letter-spacing:.07em}.showcase-section{padding:110px 0;border-bottom:1px solid #d6ddd6}.section-heading{display:grid;grid-template-columns:1.05fr .95fr;gap:70px;align-items:end;margin-bottom:50px}.section-heading h2{margin:0;font-size:clamp(38px,5vw,66px);line-height:1.08;letter-spacing:-.05em}.section-heading>p:last-child{color:#647169;line-height:1.85}.section-heading.compact{display:block;max-width:760px}.flow-grid{display:grid;grid-template-columns:.72fr 1.28fr;gap:18px}.trace-card,.timeline-card{padding:30px;background:#fff;border:1px solid #d7dfd8;border-radius:20px}.trace-steps{display:grid;gap:0;padding:0;margin:0;list-style:none}.trace-steps li{display:grid;grid-template-columns:42px 1fr;gap:13px;min-height:70px}.trace-steps li>span{display:grid;place-items:center;width:31px;height:31px;color:#1c694a;background:#e3efe7;border-radius:9px;font-size:10px;font-weight:800}.trace-steps li:not(:last-child)>span:after{content:"";position:absolute;width:1px;height:39px;margin-top:70px;background:#cdd9d1}.trace-steps strong,.trace-steps small{display:block}.trace-steps small{margin-top:5px;color:#758178}.timeline-card>header{display:flex;align-items:center;justify-content:space-between;padding-bottom:20px;border-bottom:1px solid #e1e6e1}.timeline-card header span,.timeline-card header strong{display:block}.timeline-card header span{color:#758178;font-size:9px;letter-spacing:.12em}.timeline-card header strong{margin-top:6px}.revision-tabs{display:flex;gap:5px}.revision-tabs button{padding:7px 10px;color:#526058;background:#edf1ed;border:0;border-radius:7px;font-size:10px}.revision-tabs button.active{color:#fff;background:#1c694a}.showcase-timeline{padding-top:24px}.showcase-timeline article{display:grid;grid-template-columns:75px 14px 1fr;gap:13px;min-height:74px}.showcase-timeline time{font-size:11px;font-weight:800;text-align:right}.showcase-timeline i{position:relative;width:10px;height:10px;background:#1c694a;border:3px solid #deebe3;border-radius:50%}.showcase-timeline i:after{content:"";position:absolute;top:9px;left:2px;width:1px;height:56px;background:#ced8d1}.showcase-timeline article:last-child i:after{display:none}.showcase-timeline strong,.showcase-timeline p{display:block;margin:0}.showcase-timeline p{margin-top:5px;color:#718078;font-size:10px}.showcase-timeline span{display:inline-block;margin-top:7px;padding:3px 6px;color:#1b6044;background:#e4f0e8;border-radius:5px;font-size:8px;font-weight:800;text-transform:uppercase}.showcase-timeline .state-changed i{background:#e5663e;border-color:#f8ddd4}.showcase-timeline .state-completed{opacity:.72}.showcase-timeline .state-completed strong{text-decoration:line-through}.timeline-card>footer{display:flex;justify-content:space-between;padding-top:17px;border-top:1px solid #e1e6e1;font-size:10px}.timeline-card>footer b{color:#e5663e}.architecture-section{background:#173f30;color:#edf6ef;margin-left:calc(50% - 50vw);margin-right:calc(50% - 50vw);padding-left:max(20px,calc((100vw - 1180px)/2));padding-right:max(20px,calc((100vw - 1180px)/2))}.architecture-section .kicker{color:#c8ee55}.architecture-flow{display:grid;grid-template-columns:1fr auto 1fr auto 1fr auto 1fr auto 1fr;gap:10px;align-items:center;margin:52px 0}.architecture-flow>div{padding:19px;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.11);border-radius:14px}.architecture-flow span,.architecture-flow strong,.architecture-flow small{display:block}.architecture-flow span{color:#c8ee55;font-size:9px}.architecture-flow strong{margin-top:10px;font-size:13px}.architecture-flow small{margin-top:5px;color:#a8c0b2;font-size:9px}.architecture-flow>i{color:#8daa9b;font-style:normal}.stack-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}.stack-grid article{padding:21px;background:#f0f5f1;border-radius:14px;color:#17251e}.stack-grid p{margin:8px 0 0;color:#66736b;font-size:11px;line-height:1.65}.evidence-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}.evidence-grid article{padding:27px;background:#fff;border:1px solid #d7dfd8;border-radius:17px}.evidence-grid span{color:#e5663e;font-size:9px;font-weight:800;letter-spacing:.12em}.evidence-grid strong{display:block;margin-top:12px;font-size:18px}.evidence-grid p{margin:9px 0 0;color:#66736b;font-size:12px;line-height:1.7}.evidence-actions{display:flex;gap:10px;margin-top:18px}.evidence-actions a{padding:10px 13px;color:#174e39;background:#e5ede7;border-radius:8px;text-decoration:none;font-size:10px;font-weight:800}.boundary-section{display:grid;grid-template-columns:1fr 1fr;gap:80px}.boundary-section h2{margin:0;font-size:clamp(38px,5vw,62px);line-height:1.07;letter-spacing:-.045em}.boundary-copy>p,.boundary-copy li{color:#5f6d65;line-height:1.75}.boundary-copy ul{display:grid;gap:8px;padding-left:20px;margin:22px 0 30px}.boundary-copy b{color:#20342a}.showcase-footer{display:flex;justify-content:space-between;padding:28px 0;color:#6e7c74;font-size:9px;letter-spacing:.1em}.showcase-footer b{color:#174e39}@media(max-width:900px){.showcase-nav{grid-template-columns:1fr auto}.showcase-nav>div{display:none}.showcase-hero,.section-heading,.flow-grid,.boundary-section{grid-template-columns:1fr}.showcase-hero{gap:42px;padding:60px 0}.fact-strip{grid-template-columns:repeat(2,1fr)}.fact-strip article:nth-child(2){border-right:0}.fact-strip article:nth-child(-n+2){border-bottom:1px solid #d6ddd6}.architecture-flow{grid-template-columns:1fr}.architecture-flow>i{transform:rotate(90deg);justify-self:center}.stack-grid{grid-template-columns:repeat(2,1fr)}}@media(max-width:560px){.showcase-shell{width:min(100% - 24px,1180px)}.showcase-hero h1{font-size:49px}.mission-card{padding:24px}.mission-card dl,.stack-grid,.evidence-grid{grid-template-columns:1fr}.fact-strip strong{font-size:22px}.showcase-section{padding:75px 0}.trace-card,.timeline-card{padding:20px}.revision-tabs{display:grid}.showcase-timeline article{grid-template-columns:58px 12px 1fr}.evidence-actions,.showcase-footer{display:grid}.architecture-section{padding-left:12px;padding-right:12px}.source-link{font-size:0}.source-link:after{content:"↗";font-size:12px}} .architecture-section{margin-left:0;margin-right:0;padding-left:50px;padding-right:50px;border-radius:28px} @media(max-width:560px){.architecture-section{padding-left:20px;padding-right:20px}} +.harness-section{position:relative}.harness-heading{align-items:start}.harness-boundary{display:grid;gap:18px;padding:28px;background:#fff;border:1px solid #d4ded7;border-radius:22px;box-shadow:0 20px 60px rgba(23,63,48,.07)}.harness-lane{display:grid;grid-template-columns:minmax(130px,.8fr) repeat(7,auto);gap:10px;align-items:center}.harness-lane.business-lane{grid-template-columns:minmax(130px,.8fr) repeat(9,auto)}.lane-label{align-self:stretch;display:flex;align-items:center;padding:12px;color:#1c694a;background:#e7f0ea;border-radius:11px;font-size:9px;font-weight:800;letter-spacing:.11em}.harness-node{min-width:122px;padding:16px 13px;background:#f3f5f2;border:1px solid #d9e0da;border-radius:12px;font-size:11px;font-weight:800;line-height:1.5}.harness-node small{color:#718078;font-size:8px;font-weight:600}.harness-node.accent{color:#fff;background:#174e39;border-color:#174e39}.harness-node.accent small{color:#b9d4c5}.harness-lane i{color:#8aa096;font-style:normal}.boundary-gate{display:flex;align-items:center;justify-content:center;gap:14px;color:#d45d39;font-size:9px;font-weight:800;letter-spacing:.12em}.boundary-gate b{font-size:17px}.harness-layer-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-top:18px}.harness-layer-grid article{padding:22px;background:#173f30;border-radius:15px;color:#edf6ef}.harness-layer-grid span{color:#c8ee55;font-size:9px;font-weight:800}.harness-layer-grid strong{display:block;margin-top:10px;font-size:14px}.harness-layer-grid p{margin:8px 0 0;color:#adc4b7;font-size:10px;line-height:1.7}.eval-story{display:grid;grid-template-columns:.85fr 1.15fr;gap:28px;margin-top:18px;padding:28px;background:#eef2ed;border-radius:20px}.eval-copy h3{margin:0;font-size:25px;line-height:1.22}.eval-copy>p:not(.kicker){color:#647169;font-size:11px;line-height:1.75}.eval-copy small{color:#1c694a;font-size:9px;font-weight:800}.eval-table{display:grid;align-content:center;min-width:0;overflow-x:auto}.eval-row{display:grid;grid-template-columns:1.7fr repeat(4,.65fr);min-width:530px;border-bottom:1px solid #d2dcd4}.eval-row>*{padding:11px 8px;font-size:10px;text-align:right}.eval-row>*:first-child{text-align:left}.eval-row b{color:#1c694a}.eval-head{color:#78847c;font-size:8px;font-weight:800;letter-spacing:.06em}.eval-head>*{font-size:8px}.eval-row:last-child{border-bottom:0} +@media(max-width:900px){.harness-lane,.harness-lane.business-lane{grid-template-columns:1fr}.harness-lane i{justify-self:center;transform:rotate(90deg)}.harness-layer-grid{grid-template-columns:repeat(2,1fr)}.eval-story{grid-template-columns:1fr}} +@media(max-width:560px){.harness-boundary,.eval-story{padding:19px}.harness-layer-grid{grid-template-columns:1fr}.harness-node{min-width:0}.eval-copy h3{font-size:21px}}