外部动作本身就是一次可见运行。一个正方形节点已经完整表达它,不需要额外的参与者节点。
外部 Program 运行节点
定义用户从 Programs、fn-form 或其他外部入口直接运行一次函数时,DAG 视图、完成后的 summary handoff 和聊天记录应如何表现。这里只规定顶层运行、终态交付及其现有执行事件的投影,不规定 workflow 内部阶段。
外部调用显示为 conversation layer 的圆角正方形 Program/Function 节点,由 ROOT 直接分出,不补造 User 或 LLM 节点。workflow 的实质结果默认写入文件;成功态 Conclusion 的工作交接由独立 summary 函数生成,不是 workflow 结果正文。非成功终态的 Conclusion 由前端根据状态和错误确定性生成。
端到端流程:执行、交接与聊天投影
节点语言
正常聊天仍然是圆形 User → 三角形 LLM。外部 Program 是 ROOT 下另一项显式动作,不伪装成一次聊天。
专用头像区分顶层 Program 与普通助手消息。标题行沿用现有 Thinking/Functions 折叠条的字号、颜色和箭头,但信息更完整。点击展开后直接显示当前 RuntimeBlock,不重新设计内部函数调用。
聊天窗口视觉合同
RuntimeBlock 和 TreeStep,消息 footer 保持聊天消息的独立层级。Conclusion 与操作 footer 留在折叠区域之外。Conclusion 使用普通聊天正文排版,不使用 blockquote、左侧竖线或引用式缩进;footer 始终位于整条消息最底部,不随执行过程折叠。压缩标题的四种状态
这次工作流完成了资料读取、内容核对和结果整理。执行过程按任务要求保留了完整记录,没有把报告正文重复显示在聊天中。
- 读取并检查输入资料。
- 完成内容整理和一致性核对。
- 保存任务产物并检查执行状态。
任务已完成;没有需要用户继续处理的问题。
工作流失败:Permission denied。
工作流已取消;取消前记录了 3 个调用。
展开、收起与点击行为
| 用户动作或状态变化 | 聊天窗口行为 |
|---|---|
| 运行刚创建 | 立即显示专用头像和压缩标题。若用户未操作,沿用 ExecutionStrip 现有行为:Running... 时默认展开。 |
| 用户点击标题行 | 只切换参数、执行树和 usage。终态 Conclusion 与消息 footer 均不随执行过程折叠。 |
| 展开状态 | 内部完整复用当前函数调用展示;本设计不改变根函数行、嵌套调用、Details、Copy、Retry、Edit & re-run 或版本导航。 |
| 运行结束且用户没有操作过 | 沿用现有 ExecutionStrip 逻辑自动收起并更新终态标题。成功完成且具有 workflow_handoff_v1 时,Conclusion 显示 summary handoff;capped、取消、interrupted 或失败态不调用 LLM summary,由前端生成确定性的状态或错误 Conclusion。footer 始终位于 Conclusion 之后。 |
| 用户已经手动展开或收起 | 运行状态和事件更新不覆盖用户选择。 |
| 窄屏标题超过可用宽度 | 保持单行并从尾部截断,箭头始终可见;完整标题保留为按钮的 accessible name。 |
运行状态只改变正方形,不替换节点
数据语义与可视投影
| 层面 | 当前语义 | 本设计规定 |
|---|---|---|
| 持久化 | 手动顶层函数是一个 role=code 节点;caller 为空,predecessor 是当前 HEAD,空 session 时为 ROOT。 | 不改变存储字段,不新增 synthetic User/LLM。 |
| 历史重载归属 | caller 表示调用所有权;predecessor 只保存 conversation layer 的时间顺序。 | caller 为空或 ROOT 时始终渲染为顶层 Program,即使 predecessor 指向 assistant。只有非空 caller 指向 assistant 时才收进该消息的 runtimeChildren;不得根据 predecessor 推断调用所有权。 |
| DAG conversation layer | 顶层手动函数属于默认可见的 conversation layer;持久化 predecessor 仍指向创建调用时的 HEAD。 | 会话概览使用派生的 ROOT → Program 投影边,把圆角正方形作为 ROOT 直接子级排版;这条概览边不写回存储,也不删除或改写 predecessor。详细图需要时间顺序时仍可读取原始 predecessor。 |
| 节点标签 | 代码节点已有函数名、状态、参数和输出。 | 图上至少显示函数名与现有英文状态;参数和输出进入 Details。 |
| 状态更新 | 运行开始与结束更新同一 code 节点。 | 节点位置与形状不变,只改变描边和状态文本。 |
| 聊天标题 | 顶层 Runtime 已有函数名、状态、时间戳和 contextTree。 | 按“函数名 · 状态 · 时长 · 摘要”生成单行标题。状态词保持现有英文;时长由消息时间与当前时间或根节点 duration 派生。 |
| 标题摘要 | 运行时可统计当前执行树节点数;结束后根节点已有 output 或 error。 | 运行中显示当前可观察的执行树节点数;完成后显示单行输出预览;失败显示单行错误预览;取消显示当前可观察的执行树节点数。只压缩空白并截断,不调用 LLM。 |
| 终态结论 | 旧 summary 实际等于 workflow() 的完整返回值,会把报告正文误当成聊天总结。 | workflow 成功完成后调用一个独立的无工具 summary 函数。它读取原始任务、函数名与状态,以及执行记录中每个 function=agent 步骤最多 240 字的 operational handoff 预览;不读取调用参数、完整产物正文或任何非 agent 函数的结果摘要,避免中间结果经后续调用参数进入 summary。输出采用自然聊天结构:通常先用 2–3 句话概括,多阶段任务再用简短编号列出主要流程,最后判断任务是否完成;单阶段任务可以省略列表。引用、路径和警告只在有助于交接时出现,不作为固定字段。预览不得用于复述主题结论。完整产物默认写入当前 session 的工作目录并只保留在文件与 workflow state 中。 |
| 总结调用 | 前端不会额外调用模型。 | 新增的一次 LLM summary 调用只发生在后端 workflow 成功完成阶段,复用当前 session 已选择的 provider/model,禁用工具并要求 JSON 对象。调用失败或格式错误时使用不声称产物已完成的保守 fallback,不改变已完成状态,也不回退为显示完整产物。capped、取消、interrupted 和失败态不调用 LLM summary,但前端仍生成确定性的状态或错误 Conclusion。 |
| 公共 handoff schema | 完整 raw result、调用参数、错误正文与每步结果保存在 workflow state。 | summary_kind 固定为 workflow_handoff_v1;summary 是简短的聊天式 Markdown,不限制为固定条数或固定字段;return_result 只由原始 task 的确定性 guard 计算,明确否定优先于直接返回措辞;result 默认是 null,只有 guard 为 true 时才读取 state 中的 raw result。公共 items 采用安全字段白名单,只保留步骤身份、状态、哈希与时间;公共 revisions 只保留版本和时间。调用参数、结果与错误正文均不进入公共 payload。 |
| 产物交付 | planner 示例允许 return findings 或直接 return agent(...),没有区分任务产物与 handoff。 | planner 和 SINGLE agent 的执行合同统一规定:报告、代码、表格等实质产物默认写入当前工作目录;最终返回描述已完成工作,以及有用的警告或后续操作,路径只在有助于交接时出现。只有任务明确要求“直接回答、返回正文、在聊天中给出”时才允许把正文作为直接结果。 |
| 展开内容 | 函数内部调用通过 caller 形成 execution layer;聊天侧已有 RuntimeBlock、TreeStep 和 Details。 | 完整复用现有组件和交互,不增加 Planner、Agent 或 workflow 专用视觉层。 |
| 运行时选择 | 直接运行仍属于当前 session;session picker 的 provider_override/model_override 是该 session 的模型选择,没有显式覆盖时由 session 的 agent 配置决定。 | 父进程在创建函数子进程前解析同一选择,并把 provider/model 快照显式传入子进程。子进程不得重新使用全局自动检测覆盖 session 选择。 |
| footer | 顶层 footer 已有时间、Copy、Retry、Edit & re-run 和版本导航。 | footer 不属于执行过程,始终放在整条消息最底部:有 Conclusion 时位于其后,无 Conclusion 时位于折叠标题后。其上方间距复用 assistant 正文到 footer 的 12px 段落间距;出现条件、按钮顺序和行为保持不变。 |
现状、参考与取舍
| 参考面 | 已有能力 | 缺口与决定 |
|---|---|---|
dag-layout-spec.html | ROOT=菱形、User=圆形、LLM=三角形、Code=正方形;场景 5 已定义手动函数。 | 直接采用现有正方形,不增加新节点形状。 |
dag/overview.md | 区分 predecessor 与 caller,顶层手动调用只有一个 code 节点。 | 保留存储中的 predecessor 时间顺序;会话概览单独派生 ROOT → Program 排版边,不补造调用者消息。 |
dag/rendering.md | 手动顶层函数属于 conversation layer,默认可见。 | 当前看不到节点属于实现缺口,不通过修改数据模型解决。 |
当前 RuntimeBlock | 有 tree 时显示执行树,无 tree 时显示一个 running StepRow,顶层已有消息 footer。 | 顶层外层复用 ExecutionStrip 的展开状态逻辑;只有执行内容和 usage 传入折叠区,现有 footer 在其外部原样渲染。嵌套调用的现有展示不变。 |
当前 ExecutionStrip | 运行中默认展开,结束后在用户未操作时收起,用户选择优先;标题采用小字和箭头。 | 直接采用其状态规则和视觉密度,不另写 Program 专用折叠状态机。 |
| 新图框架 | 无必要能力缺口;现有 SVG renderer 已支持 code 方块和状态描边。 | 不引入 React Flow、Cytoscape 或其他依赖。 |
采用:现有 code 正方形、现有分支颜色、现有英文状态词、ExecutionStrip 折叠规则、RuntimeBlock 展开内容。
调整:顶层 Program 增加专用头像;压缩标题比 Thinking/Functions 更详细,包含函数名、状态、时长和结果摘要。
拒绝:虚构 User/LLM、单独新 lane、大卡片、百分比进度、workflow 专用内部时间线、用完整产物正文冒充工作总结。
Conclusion。展开后怎样画根函数、嵌套函数、参数、输出和错误,全部服从当前 RuntimeBlock;不为 agentic_workflow 增加专用内部流程设计。已实现验收标准:summary 与聊天窗口
| 入口 | 可观察结果 |
|---|---|
| 刷新已有聊天后的直接函数运行 | 即使该运行的 predecessor 是上一条 assistant,空 caller 仍保持顶层 Program;具有 workflow_handoff_v1 的成功运行继续显示 Conclusion,消息 footer 始终可见。非空 caller 指向 assistant 的内部运行仍留在该 assistant 消息内。 |
| 聊天记录刚创建 | 显示专用 Program 头像和“函数名 · Running... · 时长 · 步骤数”标题;运行中默认展开现有 RuntimeBlock。 |
| 用户收起顶层运行 | 头像、单行标题、已有的 Conclusion、可选 direct result 和消息 footer 保持可见;只有参数、执行树和 usage 不可见且不占布局空间。 |
| 聊天记录收到执行事件 | 展开区继续由现有 TreeStep 更新;标题步骤数同步更新,用户手动展开/收起状态不被覆盖。 |
| workflow 成功完成 | 未手动操作时自动收起并更新为 Completed;后端独立 summary 调用生成简短的聊天式工作交接:先概括,多阶段时可用编号列出主要流程,最后判断是否完成;不强制引用、路径或固定条数,也不复述报告正文。时间与操作 footer 固定在 Conclusion 后,并与 Conclusion 保持 assistant 消息一致的 12px 段落间距。 |
| workflow capped、取消、interrupted 或失败 | 标题显示对应终态,不调用 LLM summary;前端根据终态、调用数和已有 error 生成确定性的状态或错误 Conclusion。footer 位于该 Conclusion 之后,二者都不进入折叠区域。 |
| 任务要求生成报告、代码或表格但未要求直接返回 | 实质产物写入当前工作目录;Conclusion 概括完成步骤和完成状态,只有在有助于用户继续操作时才写路径,payload 不携带用于聊天渲染的完整结果。 |
| 任务明确要求直接回答或返回正文 | summary 函数仍生成工作总结;独立的 task-only 授权判断标记 return_result=true。Conclusion 在工作总结后显示完整直接结果。 |
| 任务明确否定在聊天中返回正文 | 否定优先;summary 模型、agent preview 和“不写文件”等其他措辞不能把 return_result 改为 true,公共 payload 中的 result 保持 null。 |
| summary 调用失败或返回格式错误 | 显示保守 fallback,不声称文件已经生成;workflow 保持 Completed,完整 raw result 不进入公共 payload,错误只保存在 state。 |
| 窄屏与键盘 | 标题保持单行、尾部截断且箭头可见;按钮支持键盘并暴露 aria-expanded,完整摘要可被辅助技术读取。 |
| 当前 session 选择了自定义 provider/model 后直接运行 | 函数子进程使用相同 provider/model;不访问其他 session 或全局 picker,不因 provider 未列入内置 PROVIDERS 表而失败。 |
| 兼容性 | LLM 调用的普通内部工具仍按 execution layer 聚合;现有 User、LLM、merge、spawn 形状不变。 |
后续 DAG 投影验收标准
| 入口 | 可观察结果 |
|---|---|
空 session 从 Programs 运行 agentic_workflow | DAG 立即出现且只出现一个 ROOT 子级圆角正方形节点,标签为 agentic_workflow 与 Running...。 |
| 已有聊天的 session 从 Programs 运行 | 现有 User → LLM 结构不变;同一 conversation layer 增加一个由 ROOT 直接分出的 Program 节点。 |
| 概览投影处理已有 predecessor | 存储中的 predecessor 保持当前 HEAD;概览 renderer 根据空 caller 派生 ROOT → Program 排版边,不把 Program 误接成 assistant 的内部调用。 |
| 运行完成、失败或取消 | 更新同一 Program 节点的描边和状态,不移动节点,不生成第二个结果节点。 |
本次实现任务
| 范围 | 合同 |
|---|---|
| 生产文件 | agentic_workflow/__init__.py 增加独立 summary 函数、产物交付提示和结构化 handoff;runtime-summary.ts 只把带版本标记的 handoff 当作 Conclusion,并仅在 return_result=true 时暴露原始结果;runtime-block.tsx 在同一 Conclusion 中分开渲染工作总结与明确要求的直接结果。 |
| 公共入口 | 从 Programs 或 fn-form 在会话中直接运行 agentic_workflow,刷新历史会话后也必须得到相同结论。 |
| 兼容与失败 | 非 workflow 的 Function 行、运行中的 workflow、嵌套 LLM 工具调用保持现状;JSON 不完整、失败、取消时不得抛出渲染异常或伪造成功数字。 |
| 明确排除 | 不新增依赖、不改变 DAG、不改变按钮内容、顺序或行为、不修改普通 assistant 消息和嵌套函数,不让 summary 函数使用工具或再次执行任务。 |
| RED / GREEN | Python 公共入口测试先证明当前 SINGLE 与 programmed workflow 把完整结果放进 summary,再验证独立 summary 调用不接收完整结果、调用参数或非 agent 步骤结果,只接收 agent 的短 operational handoff;中间结果即使被后续调用参数引用,也只留在内部 state,不进入 summary prompt 或公共 payload。测试还覆盖默认不返回原始结果、明确直接返回时才携带结果、summary 失败时安全降级,以及 planner/SINGLE 的文件交付合同。npm run check:chat-ui 覆盖新 handoff、显式结果和旧 payload 不再显示完整正文;随后运行 Web typecheck、完整 Web check、文档链接与 diff 检查。 |
实现证据
runtime-block.tsx 仅为顶层 Program 增加专用头像与 ExecutionStrip;执行树与 usage 作为同一折叠内容,footer 位于折叠区和 Conclusion 之后,并由 execution-strip.css 复用 assistant 消息的 12px 正文到 footer 段落间距;嵌套 LLM 工具调用保留原结构。成功态 Conclusion 只把带 workflow_handoff_v1 标记的内容当作工作总结,只有 return_result=true 时才在其后单独显示完整结果;capped、取消、interrupted 与失败态由 runtime-summary.ts 确定性生成状态或错误 Conclusion,不调用模型。旧 payload 的原始 summary 不再被当作工作总结。后端实现:
agentic_workflow/__init__.py 在 workflow 成功后调用一次独立的无工具 summary 函数。它接收任务、执行函数名、状态、原始结果长度,以及仅限 agent 步骤的 240 字 operational handoff;不接收调用参数、完整原始结果或其他函数的结果摘要。正文披露授权由只读原始 task 的确定性 guard 计算,不接受模型或 agent 输出。summary 格式错误或调用失败时生成不声称产物已完成的安全交接,并把错误只留在 state。planner 与 SINGLE agent 同时收到默认文件交付合同。调用参数、原始结果、步骤结果与错误正文继续持久化到 workflow state;公共 items 与 revisions 使用安全字段白名单,不公开这些正文。自动检查:
pytest -q tests/component/agent/test_agentic_workflow.py 的 35 项测试、完整 Web check、npm run build、Web tsc --noEmit、文档链接检查与 git diff --check 通过。测试覆盖默认隐藏原始结果、整个公共 payload 不含步骤正文或错误正文、中间结果经后续参数引用时仍不公开、agent preview 不能授权正文披露、中英文显式与否定返回措辞、明确要求时单独返回、summary 输入不含完整原始结果、调用参数与非 agent 步骤结果、summary 格式错误或调用失败时安全降级并只把错误持久化到 state、planner/SINGLE 文件交付合同、旧 payload 不再显示正文。真实验收:历史会话中的旧 workflow payload 不具备
workflow_handoff_v1 标记,因此不再把旧的 12198 字任务结果显示为 Conclusion。新 handoff 的真实 18100 验收必须使用新建 workflow 运行,且只使用 /Applications/OpenProgram.app、默认 ~/.openprogram 与 18100;不启动 18200。DAG 节点投影未在本次实现中改动。