第 7 周(2026-08-17 ~ 2026-08-21)
宋红 · AgentOS 编排层 / 端侧语言规划 / 小车模拟器 Demo 阶段声明:上周留的调试链路和调用路径在周一收口,第一次真正端到端联调就把网页端那个 bug 顶出来了——这周的起点在这儿。真正花时间的不是修它,而是回答“到底是谁的问题“——端侧模型、模拟器网页端、还是我们自己的框架。三块分别用能隔离的实验逐个证伪,确认主责在模拟器网页端之后,我跟对方要来了源码,在源码基础上重做了一个可控的测试端,把整条链路搬了过去;同期把端侧模型的效果修了一轮,并用 A/B 对照把选型钉死。周五链路在新测试端上端到端跑通
一、本周工作总览
| 日期 | 主题 | 工作内容 | 状态 |
|---|---|---|---|
| 08-17(周一) | 嘲风项目 | 收口上周的调用路径整理与调试脚本,第一次端到端联调复现问题;先证伪我们自己的框架:核对命令下发证据,定位并修掉端侧三处问题 | ✅ 完成 |
| 08-18(周二) | 嘲风项目 | 绕开全部自研代码直接黑盒标定网页端,拿到根因;整理四类风险、整改优先级和可量化验收矩阵 | ✅ 完成 |
| 08-19(周三) | 嘲风项目 / 团队协作 | 要来 simcar-online 源码,读完之后改造出一个可控的测试端;Gateway 抽执行后端做新旧双后端;拉团队仓库评审同事周报与实车验证证据 | ✅ 完成 |
| 08-20(周四) | 嘲风项目 | 端侧模型效果修正:把不该给模型的活拿回来做确定性编译,补守卫;A/B 模型对照实验定选型 | ✅ 完成 |
| 08-21(周五) | 嘲风项目 | 在新测试端上端到端联调,主线用例和安全门用例逐条验收;网页端遗留问题确认;定演示走法 | 🔄 推进中 |
二、这周要回答的问题:到底是谁的问题
接着上周说。上周结束的时候,可复用模块和本地环境已经搭好了,剩下调用路径整理和调试脚本两件事在收口,端到端联调排在这周。周一先把这两件事做完:每一跳的输入输出对齐,控制指令下发和状态读取分别能单独验证。然后第一次把整条链路真接起来跑——问题就是在这次联调里冒出来的。
现场反馈只有一句话:说了话,小车走歪了,或者干脆不动。网页端能看到的就是这个现象。
这种问题最麻烦的地方不是难修,是责任面不清楚。链路上有三个都可能出问题的环节:端侧模型规划得不对、模拟器网页端执行得不对、我们自己的框架在编排下发或者判定上不对。三个混在一起,谁都能说自己没错,最后就变成互相甩锅,谁也说不清该改哪儿。
所以这周我没有直接去修现象,而是先定了一套定责的办法:每次只放开一个变量,用能单独隔离的实验去证伪。上周特意没急着一上来就跑端到端、而是先把每一跳的调用路径和观测手段备齐,这周正好用上了——没有那套东西,下面这些实验根本没办法单独隔离出来。
| 怀疑对象 | 怎么隔离 | 本周结论 |
|---|---|---|
| 我们的框架 | 核对命令是否真的下发(Agent 回执、网页 Command Log、最终读状态三边比对);把端侧查出来的缺陷全部修掉再复测 | 确实查出三处问题并已修,但修完误差依旧,不是主因 |
| 端侧模型 | 36 条冻结用例做 A/B 对照,把原始输出正确率和过完安全链路的最终正确率分开量化 | 不是瓶颈:最终正确率 94.44%,放过去的危险动作 0 个 |
| 模拟器网页端 | 绕过 Agent、模型、规划器、Gateway 和全部纠偏逻辑,直接打原始 REST 做黑盒标定 | 主责在这里:角度响应非线性、有跳变、有最小分辨率,还存在“返回成功但状态完全不变“ |
下面按这个顺序讲:先证伪自己,再定位网页端,再讲解决办法(要源码重做测试端),最后是模型这条线(效果修正 + A/B 对照)。
三、第一步:先证伪我们自己的框架
顺序上我坚持先怀疑自己。如果不把端侧查干净就去说模拟器有问题,对方一句“你们自己代码就有 bug“就能把整件事挡回来。
3.1 命令确实下发了,车也确实在动
在独立网页会话上真实跑默认 20cm 正方形,网页 Command Log 收到了完整的四条边和四个转角命令,一轮约 111 秒:
position_error_cm = 15.17
heading_error_deg = 76.10
final_state = report_fail
error.code = trajectory_accuracy
Agent 回执、网页 Command Log、最后一次读状态三边一致。所以“我们没下发“这个可能性排掉了,车真的在动,失败点是转向误差在累计。
顺手单独标了转向:
请求 left 45° @ speed 0.05 -> 实际约 54°
请求 left 37.5° @ speed 0.05 -> 实际约 48.12°
过冲不是固定比例,“乘一个经验系数补回来“这种省事办法当场排除。
这一步还确认了一个必须先说清楚的前提:网页端的物理仿真是靠浏览器动画帧驱动的,页面切到后台会被节流。同一段 5cm,在不可见的后台页里 8 秒只走了约 0.9cm,直接判超时。所以后面所有标定我都保持页面前台可见,也不拿后台页的耗时评价端侧性能。
3.2 查出并修掉端侧三处问题
一、分段转向目标会累计漂移。 同一个 90 度转向拆成两个 45 度时,每个分段都拿带残差的实时姿态重新算目标,上一段的误差就成了下一段的起点。单次动作还能落在 5 度容差里,但正方形有八个转向分段,攒起来就很难看——离线按 20% 过冲复现,最终朝向误差能到约 31.61 度。改法是让轨迹执行器把理想累计朝向作为绝对目标传给底层,底层按尚未执行的分段角度算每一阶段的目标;纠偏进了 5 度安全窗就停手,不再用不稳定的小角命令去追 1 度精度。
二、失败时丢了已执行命令的证据。 网页明明记了 48 条 up/left/stop,Agent 回执却显示命令数 0、结果 FAILED,看的人会以为车压根没收到命令。原因是终点精度校验失败时直接抛异常,本地累计的命令没带出去。改成失败响应也要合并已执行命令,并加了用例锁住“正方形终点失败时仍保留 48 条命令证据“。这一条对定责很关键——证据丢了就没法跟人对账。
三、停车惯性污染纠偏。 转向到角之后不再等模拟器自然停下,到角立刻发一次安全停车,然后继续轮询,直到角速度归零、角度连续两个采样稳定才返回;上层纠偏只用停稳之后的实测姿态。最终 5 度失败阈值保持不变,不靠放宽容差掩盖问题。
这三处改完,相关的运动、客户端、轨迹、规划器和契约测试是 81 项通过。
3.3 修完之后误差还在,框架排除为主因
端侧修完再跑两次真实正方形,还是安全失败:
第 1 次:60 条命令,第三条边后转向剩余误差 8.89°,失败原因 turn_accuracy
第 2 次:16 条命令,第一处 90° 转向剩余误差 7.53°,失败原因 turn_accuracy
两轮终态都是 report_fail,速度和角速度都归零、无碰撞。第二轮我把阻尼调到 0.25,结果在 left 1.26° 这种小角反向校正的时候还是跨过了目标,说明继续追精度只会制造振荡,不是调参能解决的。
结论:我们的框架里有真问题,已经修了三处;但修完误差依旧存在,它不是主因。
四、第二步:绕开自己的代码,把网页端的问题钉死
自己查干净之后才有资格做这一步。新开会话,完全绕过 Agent、模型、规划器、Gateway 和所有纠偏逻辑,直接打网页端的原始 REST,每次都等停稳再读状态。
4.1 角度响应的黑盒标定
45° -> 54.28° / 54.44°
5° 左 -> 15.81°;5° 右 -> 6.53°
1° -> 约 10.00°(左右一致)
1° @ speed 0.01 / 0.05 / 0.1 -> 都是约 10°
25° -> 约 26.3°
30° -> 约 41.7°
32° -> 约 44.6°
70° -> 约 80.4°
81° -> 86.94° ~ 89.72°
九个标定点说明三件事:角度响应不是固定比例;25 度到 30 度之间存在离散跳变;小于 10 度的请求会被放大到约 10 度,而且跟传入的 speed 基本无关。接口文档写的是 angle 为转动角度,真实行为不满足这个契约。非单调、带跳变、还有最小分辨率,通用 PID 或者固定比例系数都补不回来——这也解释了 3.3 为什么调参没用。
4.2 根因:网页里的角速度和命令里的 speed 是两个参数
接着做字段级标定,一个字段一个字段试,最后定位到:网页只消费 setTurnSpeed.value 这一个参数;left/right 命令自己带的 speed,以及 setTurnSpeed.speed,都不会改变网页里的物理角速度。默认角速度是 3rad/s,一帧就可能跨过小角目标——1 度变成 10 度、跟 speed 无关,都是这个原因。
把角速度显式压下来之后,响应立刻变得可用:
turnSpeed = 3.0 rad/s:1° -> 约 10°
turnSpeed = 1.0 rad/s:1° -> 约 4.27°
turnSpeed = 0.5 rad/s:1° -> 约 1.25° ~ 1.49°
turnSpeed = 0.5 rad/s:
5° -> 约 6.96°
45° -> 约 45.35°
90° -> 约 90.84°
在这个前提下再跑全链,“左转九十度“实际约 90.82 度、误差 0.82 度;默认 20cm 正方形位置误差约 0.91cm、朝向误差约 3.73 度,全程没有产生任何小角纠偏命令。这反过来又给框架洗了一次清白:只要把角速度按住,我们的控制链路本身是收敛的。
但这是规避不是解决。这个参数的语义文档里没有,是试出来的;它意味着转向精度取决于一个我们本来无权假设的内部状态。
4.3 最极端的一组:返回成功,状态完全不变
最后做纯黑盒复验,绕过 Agent、模型、语音识别、规划器、Gateway 和判定,直接调 REST,每个用例前先 reset、动作后等待并停车、再读状态:
初始:x=0, z=0, rotation=pi, 速度 0, 角速度 0, 无碰撞
up distance=5 speed=0.1 -> success=true,约 3 秒后状态完全不变
turnAngle angle=45 -> success=true,约 3 秒后状态完全不变
setTurnSpeed 0.5 + left angle=45 speed=0.05 -> 都 success=true,约 4 秒后状态完全不变
setTurnSpeed 0.5 + left angle=5 speed=0.05 -> 都 success=true,约 2 秒后状态完全不变
四条全部返回成功,状态一点没变。现场那句“端侧显示完成但小车不动“,答案就在这里:接口说成功不等于车动了。这一条跟任何模型、任何我们的代码都没有关系。
4.4 四类风险和整改矩阵
- 成功语义不完整:
success: true只代表命令被接收,不代表动作被执行、更不代表执行完成,而且没有任何原子动作的完成回执,上层只能靠轮询状态猜; - 会话绑定不可审计:网页刷新就产生新的
clientId,没法保证这条命令确实打在自己看着的那个会话上,出问题也没法复盘; - 转向参数语义不一致:命令里的 speed 是摆设,真正生效的是另一个字段;
- 缺少可靠时间基准:仿真速度受浏览器前后台影响,同一条命令的耗时在不同页面状态下差一个数量级。
按这些整理了整改优先级递出去:P0 补完成语义和原子动作完成回执,P1 会话绑定可审计、转向参数语义统一,P2 文档契约跟真实行为对齐。验收判据一律用标出来的可量化区间,比如 up 5cm 实际位移落在 4.5~5.5cm、left 45° 实际角度落在 42~48 度,不接受“感觉准了“这种验收。
五、第三步:要来源码,重做一个可控的测试端
5.1 为什么不是继续等整改
定责清楚之后有三条路。继续在网页端上做补偿——已经证明走不通,非线性加最小分辨率补不回来,而且现在能跑通的那点精度是靠一个文档里没有的内部字段撑着的,补偿越多越是把系统架在随时会塌的假设上。等对方按 P0/P1 改完再往下做——整改必须提,但那几项改动不小,排期不在我们手里,我们的演示和验收不能停在那儿等。
所以我选第三条:跟对方要来 simcar-online 的源码,在源码基础上自己改造出一个可控的测试端。要源码这一步很关键——不是重写一个模拟器,是拿到它的物理和场景逻辑,把不适合当测试环境的那部分改掉,这样行为跟原版是一脉的,对比才有意义。
5.2 读源码之后确认的东西
读完源码,前面黑盒标出来的现象都对上了,这一步也把“猜“变成了“看见“:
- 物理步进是挂在渲染帧上的,所以页面切后台就被节流——3.1 里 5cm 走 0.9cm 的原因在这儿;
- 转向命令进来之后只是设置目标角,真正的角速度取自另一处内部状态,命令里带的 speed 在这条路径上没有被消费——正是 4.2 那个结论;
- 命令入口的返回值在参数校验通过之后就给了成功,跟动作是否真正开始、是否执行完毕没有关系——4.3 那四条“成功但不动“的来源;
- 会话是按前端连接标识维护的,页面刷新自然就是一个新会话,历史无法接续。
5.3 新测试端改造了什么
改造的目标不是提高物理真实度,是把它变成一个能当验收基准用的环境。要的是四件原版恰好都缺的东西:
- 独立会话 + 固定种子:一次任务一个会话,带场景标识和随机种子,同样的输入重跑结果一致,不再依赖某个前端连接是否还活着;
- 权威状态:随时能读到小车位姿、朝向、速度、机械臂状态、是否持物、是否碰撞,字段结构固定,这份状态就是判定的唯一依据;
- 执行批次语义:一批动作提交进去有明确的执行标识,每个动作有自己的完成状态,上层不用再轮询猜“到底动完了没有“;
- 自己掌握时间基准:物理步进从渲染帧上摘下来,页面在不在前台跟仿真速度无关。
场景先铺了五个:空场景、方形轨迹、绕障、抓球、绕障加抓球。从现有构建产物能独立启动,约 6 秒就绪,不用重装依赖也不用重新构建——这一点对演示很关键,现场不需要任何构建步骤。
5.4 Gateway 抽出执行后端,两套都能接
为了不把自己锁死,Gateway 里把“往哪个执行环境下发“抽成了一层后端接口,原版网页端和新测试端各自实现,切换靠配置。上层的意图路由、任务规划、安全检查、判定一行都没改。长距离导航照旧按 5cm 安全步自动分段,朝向偏移和单位换算这些环境差异全部收在后端实现里。
这样做还有个好处:以后对方整改完了,我们把后端切回去跑同一套用例,就能直接验收他们改没改好,不用重新搭一遍测试。
5.5 判定口径写进实现:只认权威状态
这条是这周查网页端换来的,不能只写在文档里。判定不认接口返回的 success,也不认页面显示,只认执行环境的权威状态。这周所有验收都是按这个口径做的。
六、第四步:端侧模型效果修正与优化
这条线是跟查网页端并行推的。上周口语指令的效果不稳,我原来的想法是“让小模型多学几个例子“,这周把它扭过来了:不该给模型的活要拿回来做确定性编译,模型只负责它真正擅长的那部分。
6.1 三个语义问题,都是把活错分给模型造成的
一、循环模板被更早的分支截获。 “绕圈走三圈“这种带循环的说法,本该走循环模板,结果被前面一个分支先接走了,出来的动作序列不对。改法是把编译优先级固定下来:循环模板 → 口语基础动作序列 → 单动作模板 → 高层规划,前面命中就不再往后走,顺序写死不再靠巧合。
二、超长指令抛的是原始校验错误。 一次给九条动作,报出来是底层的校验异常,看的人不知道是自己说多了还是系统坏了。改成步数上限 8,超了直接抛 TaskPlanningError('任务计划必须包含 1 到 8 个步骤'),错误信息是给人看的。
三、单独说“停车“绕过了契约。 这句话本来不该进任务规划,它是一个原子控制动作。改回由 Router 直接走 robot.stop,不再让规划器去编一个“停车任务“。
6.2 单动作别名和句末标点
把口语里真实会说的说法补齐做成别名:直走、往回走、左拐、右拐、掉头、调头。另外补了句末标点的回归样例——“左转九十度。“带个句号就走不通,这种事在演示现场发生一次就很难看。规划器相关测试 42 项通过。
6.3 动作覆盖守卫:模型漏了也不能放过去
模型偶尔会把动作漏掉,比如让它画图形却没给出 trace_shape。这种情况不允许“少做一点也算成功“,加了动作覆盖守卫,缺失就 fail-closed 直接拒绝并说明原因。同类的还有空场景里让它捡球,回 ball_unavailable,不去空抓一把。
6.4 效果修正的量化结果
模型原始输出的正确率是 44.44%,过完确定性编译加安全链路之后是 94.44%。这两个数字分开看才有意义:它说明剩下的那一半正确率不是靠换模型换来的,是靠把活重新分配换来的——同一个小模型,不动权重,最终正确率提了 50 个百分点。
放过去的危险动作 0 个。这一条是硬要求,不接受用正确率去换。
七、第五步:A/B 模型对照,把选型钉死
改完之后必须回答一个问题:现在这个效果,是不是换个更大的模型就更好?这个问题不能靠感觉答,所以做了 A/B 对照。
用 36 条冻结用例,两个模型跑同一套输入、同一条链路、同样的判定口径,看四个指标:模型原始 JSON 的正确率、过完安全链路之后的最终正确率、原始输出的 p95 延迟、被放过去的危险动作数。
| 指标 | Qwen2.5:1.5B | Qwen3:1.7B |
|---|---|---|
| 原始输出正确率 | 44.44% | 52.78% |
| 过链路最终正确率 | 94.44% | 86.11% |
| 原始输出 p95 延迟 | 3.4180s | 23.2919s |
| 放过去的危险动作 | 0 | 1 |
结果跟直觉是反的:Qwen3 的原始正确率更高,但最终正确率更低、延迟差了将近七倍,还漏过了一个危险动作。
原因查清楚了:Qwen3 会先输出一大段推理再给正文,有的样本推理长度 846、正文长度 0,结束原因是长度上限——也就是它把预算花在推理上,正文还没开始写就被截断了。对我们这条链路来说,这种输出等于没有输出,而且它长的那部分延迟直接落在演示的响应时间上。
所以选型定了:继续用小模型加确定性模板加安全门,这周不启动微调。这个结论也顺便回答了三方定责里的第二块——端侧模型不是瓶颈,把活分对了它够用。
八、三方定责的最终结论
回到开头那个问题,三块各自的结论摆在一起:
| 环节 | 结论 | 依据 |
|---|---|---|
| 我们的框架 | 有三处真问题,已全部修掉;不是主因 | 修完再跑两次真实正方形仍失败(剩余 8.89° / 7.53°),阻尼调到 0.25 后小角校正仍跨过目标 |
| 端侧模型 | 不是瓶颈 | 36 条冻结用例 A/B:最终正确率 94.44%,危险动作 0;换更大模型反而更差 |
| 模拟器网页端 | 主责在此 | 九点黑盒标定非单调、25~30° 有跳变、小于 10° 被放大到约 10°;四条 REST 返回成功而状态完全不变 |
一句话:现象出在网页端,我们自己的问题已经清干净了,模型这条线现在够用。
九、新测试端上的端到端验收
周五把整条链路搬到新测试端上跑,一条一条验。每条用例独立会话、独立初始状态,判定只读权威状态。
主线用例:
| 用例 | 结果 | 权威状态证据 |
|---|---|---|
| 前进 20 厘米 | 通过 | 1.129s,8 个执行动作,z 由 0 到 0.2 |
| 前进三米 | 通过 | 约 15.1s,按 5cm 安全步自动分段,z 约 3.0 |
| 走一米正方形 | 通过 | 11.730s,闭合误差可以当零看 |
| 走三角形 | 通过 | 5.212s,6 个动作,边长 0.6m,累计转角 360° |
| 捡球放到目标区 | 通过 | 6.498s,39 个执行动作,落点在目标半径 24cm 内 |
该被拒绝的那一类也逐条验了,一共五类:否定句、二选一歧义、空场景里让它捡球、动作覆盖缺失、超过步数上限。每一条都回读了权威状态确认零位移——事件数进去之前是 1、拒绝之后还是 1,另一条 69 进 69 出,位姿一个字节没变。这一步是必须做的:只看服务自己报的失败不算数,上一周教会我的就是“服务说什么不重要,状态说什么才重要“。
新旧对比放在一起看最直接:同一条默认正方形,原版网页端位置误差 15cm 左右、朝向误差七十多度、最终 report_fail;新测试端上闭合误差可以当零看,全程没有产生任何小角纠偏命令。
十、网页端遗留问题与演示走法
新测试端能跑通不代表网页端的问题消失了,这几条我照实记下来,整改清单已经递出去:
| 现象 | 影响 |
|---|---|
success: true 只代表命令被接收 | 上层无法判断动作是否执行、是否完成,只能轮询状态猜 |
| 没有原子动作的完成回执 | 无法做精确的分段控制,纠偏只能等状态稳定后事后补 |
刷新页面就换 clientId | 命令可能打在另一个会话上,出问题无法复盘 |
| 命令里的 speed 不生效,真正生效的是另一个字段 | 按文档写的代码行为跟预期不一致,且该字段语义未公开 |
| 物理步进挂在渲染帧上 | 页面切后台仿真速度差一个数量级,耗时数据不可作为性能依据 |
演示走法定了:走新测试端,不走浏览器。理由就是上面第五条——现场没法保证页面一直在前台,而且一旦被节流,演示看起来就像是我们的系统卡了。
十一、团队协作与回归
08-19 拉了团队仓库,评审了同事的周报和实车验证证据,把接口相关的对齐点过了一遍。
回归这周跑了几轮,最终全量 670 项通过、10 项跳过(改动前是 611 项)。聚焦跑的:运动与轨迹相关 81 项、规划器 42 项、意图路由 14 项、模拟器仓 73 项、页面相关 22 项。唯一的 warning 是既有测试客户端的一个弃用提示,跟这周的改动无关。
测试计数都是清掉字节码缓存后重新收集的——之前踩过一次改完源码测试还在跑旧字节码的坑,现在这一步是固定动作。
十二、本周产出
- 三方定责闭环:框架三处缺陷已修并证伪为主因,端侧模型证伪为瓶颈,主责定位到模拟器网页端,每一条都有可复现的隔离实验支撑;
- 网页端黑盒标定数据一套:九个角度标定点、三档角速度对照、四条“返回成功但状态完全不变“的复验记录;
- 四类风险 + P0/P1/P2 整改优先级 + 可量化验收矩阵,已递交模拟器一侧;
- 拿到 simcar-online 源码并改造出可控测试端:独立会话、固定种子、权威状态、执行批次完成语义、自有时间基准,五个场景,约 6 秒就绪;
- Gateway 执行后端抽象完成,原版网页端与新测试端配置可切,上层零改动;
- 端侧模型效果修正:三个语义问题修掉、单动作别名与标点回归补齐、动作覆盖守卫 fail-closed,最终正确率 94.44%、危险动作 0;
- A/B 模型对照报告:36 条冻结用例、四指标对比,选型钉死为小模型加确定性模板加安全门,本阶段不启动微调;
- 新测试端端到端验收:五条主线用例全通过、五类安全拒绝用例全部回读权威状态确认零位移;
- 回归基线:全量 670 通过 / 10 跳过。
十三、下周计划
- 把这周的标定项固化成一条命令可重跑的回归基线,任何一侧改了参数都能立刻看出有没有回退;
- 跟进模拟器一侧的 P0/P1 整改,对方改完之后把 Gateway 后端切回原版跑同一套用例做验收;
- 演示脚本按新测试端定稿并连排,保证现场零构建步骤;
- 场景按演示需要再加,物理真实度仍然不追,确定性优先;
- 安全门阈值和转向容差一个都不放宽。