① 输入需求
默认走规则层术语匹配(零 LLM、毫秒级);勾「深度解析」则走 term_resolver.resolve_terms 全链路(Layer C > B > A,与线上路由期同一入口,通常 30~150s)。
⑦ 权威说明 · 链路细节与实测
以下内容为 2026-09-07 对 sdwesp_design「列出 → 新增 → 查找 → 移交」四步复合需求的完整复盘,含源码位置与真实执行日志。
6.1 结论
✓
这条需求走的是 「术语解析 → 确定性多步拆句 → 每步能力 DAG 零 LLM 执行」通道,不是设计总监的 Supervisor 编排——Supervisor 只在某步 DAG 失败时兜底。它命中 4 个工作空间术语,被确定性拆成 4 步,每步各自派生一条 SDWESP 工具链。
6.2 源码位置速查
| 环节 | 源码落点 | 要点 |
| HTTP 入口 | server_handler_post.py:57 → server_handler_post_ops.py:831 | 只有流式端点 /api/workspace/<ws>/chat/stream,无 /chat |
| 8 阶段编排 | workspace_chat_flow.py:2371 / :829 | :840 写 session_context["workspace_name"],后续一切作用域依据 |
| 协调者 | workspace_chat_flow.py:77 | 无顶层 coordinator → 取 roles 池首项 = sdwesp_design_orchestrator |
| 成员宇宙 | workspace_scope.py:1031 | 5 个成员 → 不触发路由层单成员退化 |
| 术语解析 | term_resolver.py:1504 / :302 / :334 | triggers 是字面子串包含(k in message,按长度降序最长匹配),非打分非语义 |
| 拆句 | workspace_chat_flow.py:1751 | 连接词(然后/接着/随后…)+ 写动词(新增/删除/修改…)开新步,零 LLM |
| DAG 派生 | capability_dag.py:95 | 解开 {mode: sequence, steps:[…]};顺序与接线由契约 edges/bindings 派生,scope 只声明节点集 |
| DAG 执行 | capability_dag.py:1148 | 槽位回查 → 候选×原文回填 → 逐层执行 → recheck 读后校验 |
| 回退 | group_manager.py:1959 / :2019 | 群主是 supervisor → selected 强制置空 → _run_single_agent → 8085 |
| 工具出口 | adapters/base.rs:1215 / :222 | config.id=="sdwesp" → send_via_curl(reqwest 对 SDWESP gunicorn 恒 401) |
6.3 四步工具链(本次需求)
| 步 | 术语 | 工具链 | 关键参数 |
| 1 | data.list | publish_get_list → reply_get_list | GET /publish?ptype → GET /reply?publishid&pageid=0 |
| 2 | data.create | publish_get_list → pflowlist_get_list → flow_put_start | flowname=新建;data={"说明":{"newvalue":"多层次"}} |
| 3 | data.get | publish_get_list → pflowlist_get_list → flow_put_start | flowname=查找;data={"filtercond":{"说明":"多步验证3"}} |
| 4 | data.transfer | publish_get_list → reply_get_list → reply_get_action → flow_put_start | action=myflow 取 flowid(移交≈1866);replyid + receivers=[圣达维 userid] |
⚠ newvalue 铁律:写字段新值必须包成 {"字段":{"newvalue":值}};裸值会被 SDWESP megernodedata 静默丢弃,返回"修改成功"但字段不变(假成功)。该铁律写在 sdwesp_platform.json 的 description 里,Rust 运行时无校验,靠提示约束。
6.4 实测执行结果(2026-09-07 01:57–01:59)
[WS_MULTI] 多意图拆分为 4 步:['列出【我要说】」模版下的数据','新增一条记录,内容是 (说明=多层次)','查找 说明=多步验证3 的记录','移交给圣达维']
[WS_DAG] data.list 确定性执行完成(369ms,零 LLM)publish_get_list → reply_get_list ✅
[WS_DAG] data.create 确定性执行完成(367ms,零 LLM)→ pflowlist_get_list → flow_put_start ✅
[WS_DAG] data.get 确定性执行完成(391ms,零 LLM)→ pflowlist_get_list → flow_put_start ✅
[WS_DAG] data.transfer 在 tool 阶段失败(D_INTERNAL: HTTP 404 (/flow))→ 回退 Agent 路径 ❌
[group_manager] owner sdwesp_design_orchestrator 是 supervisor 架构 → 单成员退化
[INFO] 子Agent 'form_designer' 10 / 'flow_designer' 37 / 'org_designer' 20 / 'sdwesp_developer' 143 个工具
[DEBUG architecture] type=supervisor, sub_count=4
[LoopController] 连续失败 3 次,冷静 300s
- 前三步各约 0.37 秒、零 LLM 完成——这是能力 DAG 档相对 Agent 路径的核心价值。
- 第四步(移交)失败并回退:同一次执行里步骤 2/3 用同一个
flow_put_start(PUT /flow)是成功的,所以 404 的根因不在路由本身,而在 replyid / flowid / receivers 三者之一;平台另提供 sdwesp_flow_post_start(POST /flow)可作对照验证。
- 历史另一次同需求失败:
data.transfer 在 wire 阶段失败(D_WIRE_NO_MATCH: 槽位值 ['我要说','移交'] 未在上游「name」字段命中任何行)——「流程名」槽位被误抽成多值。
6.4.1 接收人解析:必须走 publish/users 别名表
现象:步骤 4 回退 Agent 路径后被闸拦下 —— 无法识别接收人:圣达维(不在该工作区用户表中,请确认姓名或指定 userid)
| 调用 | 实测结果 |
sdwesp_publish_get_list | 模板「我要说」→ publishid=334 |
sdwesp_publish_users(publishid=334) | 18 条候选,「圣达维」别名 → userid=5 |
sdwesp_user_get_list(通用表) | 只有「真实姓名」,查无圣达维 |
根因:publishid 原来只有一个来源(模板名),失败时异常被静默吞掉直接 422。
已修复为:publishid 多路获取(继承 → 槽位 → 原文「模板名=」→ 原文【】)+ 全量模板兜底
(存活优先,因已删除模板 title 带「(已删除)」且排在列表最前)+ [USER_REF] 失败留痕
+ 解析结果回写 session_context._resolved_user_ids 供执行期复用。
6.5 关键机制(为什么这样设计)
- triggers 是子串包含:术语命中不是语义相似度,而是
any(tr in message for tr in triggers)。想让某句话稳定命中术语,就把原话里的词写进 triggers。
- 读写消歧:多链命中时,消息含写动词 → 在写链中选;无写动词 → 排除写链(误执行写链会脏写);不能唯一确定 → 拒绝并交 Agent,宁慢不错。
- 继承白名单:只有
模板名 / replyid 跨步继承。流程名、字段值、接收人是"动作-值级"槽位,继承会污染后续步骤(实测:查找步继承新增步的"流程名=新建",误用新建流程发起查找)。
- 单意图硬闸:整单消息若含顺序连接词或多个句末标点,DAG 档直接拒绝——DAG 只执行一条链,第二意图会被静默丢弃,比慢更危险。
- 读后校验(recheck):写工具成功后回查一次真实状态,防「200 但业务无变化」的假成功(如移交给不存在的人)。
- 接收人有两张表:移交候选人在
GET /publish/users?publishid= 的「别名」字段,
通用 /user 表是「真实姓名」——两者不同源,只查通用表会漏掉圣达维这类姓名。
- curl 通道:SDWESP 的 gunicorn/Flask 对 Rust reqwest 恒返回 401(与 curl/requests 同 Method/URL/Headers 也如此),无法靠改 Header 消除,只能走系统 curl。