25 KiB
代码库「工作流(Workflow)功能」挂载点调研报告
调研目标:为即将新增的「工作流(Workflow)功能」定位代码库内的接入挂载点。 前提理解:工作流 = 用户把「一套既定流程:工作方式→验证方式→结束方式」存为一个工作流文件夹(类似现有 skill 文件夹机制,可附带验证过的脚本/代码文件);之后在对话中复用以现有智能体循环+执行引擎运行它;可能有独立 URL(如
/workflow/new)或作为普通对话的一种运行方式;阶段/整体结束时由审核智能体审核(复用两套现有审核:工具审核 + 目标模式 goal_review)。调研约束:仅只读操作,未修改任何项目文件。所有结论均基于实际读到的代码,标注文件路径与行号;不确定处明确写「未确认」。 所有代码路径均以项目根
/Users/jojo/Desktop/agents/正在修复中/agents为准(相对路径书写)。
1. Skill 系统全链路(工作流文件夹需仿照它)
1.1 目录结构与 SKILL.md frontmatter
- 源码树 skill 库:
agentskills/(每个 skill 一个子目录,内含SKILL.md,可含references/、scripts/等附带文件)。示例:agentskills/terminal-guide/SKILL.md、agentskills/skill-creator/SKILL.md。 - SKILL.md frontmatter 格式(YAML 简单解析):
后端校验逻辑见--- name: terminal-guide description: 持久化终端使用指南。…… ---modules/skills_manager.py::validate_skill_directory(第 146 行起),只强制要求存在name:与description:(re.search(r"(?m)^\s*name\s*:", content)与 description 同理)。frontmatter 全文解析见_parse_frontmatter(L174)。 - 目录名校验:
_is_valid_skill_id(L202),用SKILL_ID_PATTERN。
工作流接入判断:工作流文件夹可直接复用这套「SKILL.md + 附带脚本」结构。建议新增一个 WORKFLOW.md(或复用 frontmatter 扩展字段),并在目录中携带验证过的 scripts/ 目录。校验时除 SKILL.md 的 name/description 外,可扩展校验「工作方式→验证方式→结束方式」三段定义。
1.2 skill 创建/归档工具后端实现
- 工具定义:
core/main_terminal_parts/tools_definition/file_tools.pyL139 定义create_skill(描述「验证并归档一个已创建好的 skill 文件夹」)。 - handler:
core/main_terminal_parts/tools_execution.py_resolve_create_skill_source_dir(L525):解析 source_dir(相对/绝对/宿主机任意路径)。_get_create_skill_target_root(L539):归档目标根目录 —— 优先infer_private_skills_dir(self.data_dir),否则data_dir父级下agentskills。_handle_create_skill_tool(L545):调用archive_skill_directory(source, target_root)归档;成功后同步工作区 skills(sync_workspace_skills)。
- 归档函数:
modules/skills_manager.py::archive_skill_directory(L182)——先validate_skill_directory校验,再shutil.move移动到目标库;目标存在则拒绝(不覆盖)。 - 私有 skill 目录推断:
modules/skills_manager.py::infer_private_skills_dir(L97)——host 模式统一~/.astrion/astrion/<mode>/agentskills;web/docker 按用户路径推断。
工作流接入判断:可仿 create_skill 新增 create_workflow 工具(或复用同一归档通道),handler 在 tools_execution.py 的 handle_tool_call 分发链(L960,elif tool_name 分支约 L1130)注册;归档逻辑复用 archive_skill_directory 的校验+移动+拒绝覆盖机制。
1.3 skill 如何注入提示词
- 注入点:
core/main_terminal_parts/context/messages.pyL305-320,build_messages内按 system prompt 顺序追加 skills 列表:skills_catalog = get_skills_catalog(private_dir=infer_private_skills_dir(self.data_dir)) enabled_skills = merge_enabled_skills(...) def _build_skills_system_prompt(): template = self.load_prompt("skills_system") return build_skills_prompt(template, build_skills_list(...)) skills_prompt = self._get_or_init_frozen_prompt("frozen_skills_prompt", _build_skills_system_prompt) if skills_prompt: messages.append({"role":"system","content":skills_prompt}) - 模板:
prompts/skills_system.txt(占位{skills_list},提示用read_skill读取)。 - enabled/sync:
merge_enabled_skills(skills_manager L373)、sync_workspace_skills(L409)——把启用 skill 复制到工作区.astrion/skills/。 - skill_hints 意图识别:
config/skill_hints.json(关键词→hint),用于在用户消息中检测相关 skill 并注入提示(读_inject_intent相关)。
工作流接入判断:工作流列表可仿 skills_prompt 在 messages.py build_messages 增加一段 system prompt 注入当前工作流上下文(当前已激活工作流的流程说明),复用 _get_or_init_frozen_prompt 冻结机制与 load_prompt 模板机制。
1.4 skill 列表/读取 API
- 列表 API:
server/tasks/skills.pyGET /api/skills(L126,@tasks_bp.route)→_list_workspace_skills(L83)读取工作区.astrion/skills/*/SKILL.md,解析 name/description/path。 - 读取:工具
read_skill(file_tools.py L132)→ handler_handle_read_skill_tool(tools_execution.py 中)。
工作流接入判断:仿 server/tasks/skills.py 新增 GET /api/workflows(读工作流库目录),用于前端列表展示与「作为对话新类型创建」时选择工作流。
2. 两套审核智能体架构(工作流阶段审核复用)
2.1 工具审核 approval_agent
- 实现:
modules/approval_agent.py(由配置CONFIG_PATH = Path(resolve_deploy_config("auto_approval.json"))指定,L16 →config/auto_approval.json[.example])。 - 调用方式:
modules/auto_approval_service.py::run_auto_approval(L84)构造ApprovalAgent(web_terminal=web_terminal)并await agent.review(payload_text=..., progress_cb=..., cancel_check=...)。build_auto_approval_payload(L15)组装用户输入+最近工具操作+触发风险标记。 - 输入:payload 纯文本(审批场景上下文 + 当前工具调用函数名/参数)。
- 输出:
{"decision": "approved"|"rejected", "reason": str, "source": "approval_agent"}。内部通过工具approve_decision(L168)返回,异常/配置缺失时兜底{"decision":"rejected", ...}。 - 执行模式:readonly 或注入只读 run_command 取证(与 goal_review 同构)。
工作流接入判断:工作流的「工具/操作级」安全审核可直接复用 ApprovalAgent,通过 auto_approval_service 同构入口调用;无需新造。若需按工作流阶段应用不同审核,可在 run_auto_approval 之外包装一个「阶段审核」封装函数。
2.2 目标审核 goal_review
- 实现:
modules/goal_review_agent.py(配置CONFIG_PATH = resolve_deploy_config("goal_review.json"),L20)。GoalReviewAgent(web_terminal=...),async review(payload_text, review_mode, progress_cb, cancel_check)。 - 输出:
{"status":"done"|"continue", "message": str, "source":"goal_review_agent"};兜底保守返回 continue。review_mode 由modules/goal_state_manager.py常量REVIEW_MODE_READONLY="readonly"/REVIEW_MODE_ACTIVE="active"(L34-35)决定是否注入只读 run_command。 - 目标模式如何触发审核:
server/goal_flow.py:maybe_start_goal(L56):用户显式开启目标模式时启动(读 personalization 的 review_mode/end_conditions)。inject_goal_prompt(L88):注入目标模式提示词。handle_goal_after_turn(L147):在主循环「主模型一轮无 tool_calls」时拦截——先空转保护/边界检查,再调GoalReviewAgent.review;status=="continue"→append_review记录审核历史 +inject_runtime_user_message(CONTINUE_PREFIX + message)回写续命 user 消息到 messages,调用方continue回主循环开下一轮;status=="done"→mark_done,任务结束。
- 状态落盘:
modules/goal_state_manager.py,工作区{data_dir}/goal_state.json(含 goal、review_mode、turns、review_history 等)。 - 触发入口(后端):创建任务时传
goal_mode→server/tasks/models.pycreate_chat_task存 session_data →run_chat_task_sync时置terminal._goal_mode_requested=True(models.py L961/L998-999)→chat_flow_task_main.py L1863检测并maybe_start_goal。
工作流接入判断(关键复用点):工作流的「阶段结束/整体结束审核」几乎可 1:1 复用 goal_review 的回写机制——在 chat_flow_task_main.py 主循环的 no-tool-call 结束判定处(L2226 附近 if not tool_calls:),当前已有 if goal_is_active(workspace): handle_goal_after_turn(...) 分支;工作流运行时可仿照新增「工作流阶段/整体审核」调用 GoalReviewAgent(或新 WorkflowReviewAgent),并把 continue/done 语义映射为「进入下一阶段/结束工作流」。建议抽出一个与 goal_flow.py 同构的 workflow_flow.py 编排模块,减少对主循环侵入。
3. 智能体循环与执行引擎(工作流运行时复用)
3.1 主循环入口链路(骨架级)
- 唯一入口(门闸):
server/chat_flow.py::process_message_task(L130)——先acquire_adopted_main_task_gate拿对话级门闸(§12 单写者不变量),然后loop.create_task(handle_task_with_sender(...))。任何新增的主任务入口必须走此门闸(AGENTS.md §12.4)。 - 实际任务执行:
server/chat_flow_task_main.py::handle_task_with_sender(L1411):- L1855
messages = web_terminal.build_messages(context, message);tools = web_terminal.define_tools()。 - L1863 目标模式启动/续注入。
- L1962
while max_iterations is None or iteration < max_iterations:主循环迭代:- 每轮
run_streaming_attempts(messages=messages, tools=tools, ...)(L2082,导入自server/chat_flow_stream_loop.py)调模型,返回 full_response / tool_calls。 - 有 tool_calls → L2325
execute_tool_calls(...)执行工具(结果 append 到 messages/对话历史),continue下一轮。 - 无 tool_calls(L2226
if not tool_calls:)→ 目标审核分支(L2230)→ 多智能体注入分支(L2314)→ 否则break结束。
- 每轮
- L1855
- 工具执行循环:
server/chat_flow_tool_loop.py::execute_tool_calls(L409)→_execute_tool_calls_impl(L426,守护包装 try/finally 复位_tool_loop_active):- 白名单校验(allowed_tool_names)→ 权限评估
web_terminal.evaluate_tool_permission→ 逐个tool_call解析/执行,结果web_terminal.context_manager.add_conversation("tool", ...)+messages.append({"role":"tool", ...})。 - 工具真实执行在
core/main_terminal_parts/tools_execution.py::handle_tool_call(L960,elif tool_name = handler 分发大表)。
- 白名单校验(allowed_tool_names)→ 权限评估
工作流接入判断:工作流运行不需要新增主循环,直接复用 process_message_task 门闸 + handle_task_with_sender 迭代即可(把工作流定义作为额外的 system 上下文 + 结束阶段审核挂钩即可)。若想「阶段结束审核后进入下一阶段并继续迭代」,参照 goal_review 的 continue → inject_runtime_user_message + continue 模式,完全在现有消息/迭代机制内实现。
3.2 运行模式 work_mode 三档(plan/ask/execute)——新增一种运行方式的最相近参照
- 核心实现:
core/main_terminal_parts/tools_policy.py:WORK_MODES = {"plan","ask","execute"},WORK_MODE_DEFAULT="plan"(L88);set_work_mode/switch_work_mode(处理 plan⇄只读权限+沙箱联动)/get_work_mode;权限模式PERMISSION_MODES与 plan 锁。 - 怎么注入提示词规则:
core/main_terminal_parts/context/mode.py_build_work_mode_rules(mode)(L192):三档规则文本唯一来源(plan/ask/execute 各自的 detailed_rules)。_build_work_mode_message(L220):load_prompt("work_mode")模板 +replace work_mode/work_mode_label/mode_description/detailed_rules。- 属于
_RUNTIME_MODE_KINDS(L133,含 permission_mode/execution_mode/network_permission/work_mode),切换走 drift 通知(_RUNTIME_MODE_SOURCE第 花 种 "work_mode_change")。 - 冻结注入:
messages.py::build_messagesL203-208_get_or_init_frozen_mode_prompt("frozen_work_mode_prompt", self._build_work_mode_message)。
- 怎么影响工具可用性:work_mode 主要通过提示词约束(三档规则正文),而非硬性工具过滤(行为差异在规则里,如 ask 禁用 ask_user 工具由提示词说明;plan 的只读是权限层联动)。工具的硬开关由
tool_category_states/disabled_tools(同一文件 tools_policy.py)控制。
工作流接入判断:若把「工作流运行」作为与 work_mode 平行的「一种运行方式」,可仿此模式:新增一类提示词规则(如 workflow.txt 模板 + _build_workflow_rules)+ 在 messages.py 追加一段冻结注入;运行会话期间注入工作流当前阶段上下文。但注意 work_mode 是「与用户交互节奏」正交档位,与工作流(一套流程)语义不同——更倾向工作流作为「消息级上下文+审核挂钩」,而非新增第四档 work_mode。建议工作流保持并在 _RUNTIME_MODE_KINDS 之外独立管理(不要混入 work_mode 切档,避免交互节奏冲突)。
3.3 提示词模板机制(prompts/ 组织 + mode.py 构建)
- 目录:
prompts/*.txt(如main_system.txt、work_mode.txt、skills_system.txt、goal_review_agent.txt、execution_mode/、multi_agent/、sub_agent/)。 - 加载:
core/main_terminal_parts/context/prompt.py::load_prompt(name)(L157)——Path(PROMPTS_DIR)/f"{name}.txt",存在则读,否则返回兜底文案。 - 构建规则:
context/mode.py负责把「运行期状态」渲染进 prompt(permission/execution/work_mode/network),context/messages.py在build_messages(L109)按固定 order 追加各 system prompt:主 prompt → 权限 → 执行环境 → 运行模式 → 最近对话 → 个性化 → 工作区 → AGENTS.md → skills → 记忆 → 自定义 → 禁用提示。 - 冻结机制:
_get_or_init_frozen_prompt/_get_or_init_frozen_mode_prompt对可变但需稳定的 system 段做一次性构建缓存。
工作流接入判断:新增 prompts/workflow.txt 模板(或 prompts/workflow/ 子目录),在 messages.py::build_messages 适当的 order 位(建议主 prompt 后、skills 前/后)插入工作流上下文 system 段,并用 load_prompt("workflow") 加载。完全复用现有模板/冻结机制。
4. 前端入口与路由(/workflow/new 怎么挂)
4.1 路由定义位置与现有 /multiagent 兼容重定向
- 前端未用 vue-router,而是手动处理
window.location.pathname(无 createRouter/createWebHistory,全库无 vue-router)。 - 路由解析/重定向核心:
static/src/app/methods/ui/route.ts::bootstrapRoute(L26 起):
即旧let path = window.location.pathname.replace(/^\/+/, ''); if (path === 'multiagent/new' || path === 'multiagent') { history.replaceState({}, '', '/new'); path = 'new'; } else if (path.startsWith('multiagent/')) { const bareId = ...; history.replaceState({}, '', `/${bareId}`); path = bareId; }/multiagent/*→ 重定向到裸/conv_xxx;/multiagent/new→/new(对话类型是 metadata 属性,不再是路由概念)。- 若裸新建路由:清空当前对话、恢复 draft → 结束。
- 否则按
conv_<id>调this.enterConversation(convId, ...)(app/methods/conversation/bootstrap.ts)加载并恢复。
isExplicitNewConversationRoute():判断是否显式新建路由。
工作流接入判断:/workflow/new 可在 bootstrapRoute 增加一个分支:if (path === 'workflow/new' || path === 'workflow') → 进入「新建工作流对话」模式(清空 + 标记 newConversationType='workflow'),history.replaceState 保持 URL 为 /workflow/new。后端需相应 with_terminal/路由能接受该路径(未确认:当前 route 无条件把 path 当 conv id,需额外分支避开)。
4.2 对话类型机制(newConversationType / currentConversationType)
- 状态定义:
static/src/app/state.tscurrentConversationType(L57):当前已打开对话类型'normal'|'multi_agent'|null,由enterConversation从 metadata 恢复(bootstrap.tsL66)。newConversationType(L59):空对话待创建类型'agent'|'multi_agent',localStorage 持久化(key 见 state.ts 上方NEW_CONVERSATION_TYPE_STORAGE_KEY),由 input composer 的agent-type-switcher修改。
- 创建对话按类型选 API:
- 首条消息创建:
static/src/app/methods/message/send.tsL269:const isMultiAgent = this.newConversationType === 'multi_agent'; const createUrl = isMultiAgent ? '/api/multiagent/conversations' : '/api/conversations'; - 侧边栏新建:
static/src/app/methods/conversation/action.tsL134/L276:按useConversationStore().sidebarConversationType选同两个 URL。
- 首条消息创建:
- 输入栏类型选择器:
components/input/InputComposer.vueL328agent-type-switcher(渲染newConversationType选项:智能体/多智能体)。
工作流接入判断(关键):若工作流作为新对话类型:需在 newConversationType/currentConversationType 类型联合中增加 'workflow';send.ts L269 与 action.ts L134/276 增加「工作流创建」的 URL 分支(如 /api/workflow/conversations 或复用 /api/conversations+workflow_id 参数);InputComposer.vue 的 agent-type-switcher 增加工作流选项。注意 types 是字符串字面量联合(L780 newConversationType?: 'agent'|'multi_agent'),需同步扩展 TS 类型与所有 switch 点。
4.3 输入栏运行模式切换器 work-mode-switcher
- 实现位置:
static/src/components/input/InputComposer.vueL354:<div class="agent-type-switcher work-mode-switcher" ...>,渲染workModeOptions,@click开workModeMenuOpen菜单。 - 切换逻辑:
static/src/app/methods/ui/workMode.ts:fetchWorkMode(GET /api/work-mode,带 conversation_id)、changeWorkMode(POST /api/work-mode,409 空闲才可切)、toggleWorkModeMenu/closeWorkModeMenu。plan 联动更新前端 permissionMode/executionMode 显示。 - 后端 API:
server/chat/permission.pyGET/POST /api/work-mode(L402/L418)。
工作流接入判断:若工作流作为「普通对话的一种运行方式」(而非新 URL),可在 work-mode-switcher 同区域加一个「工作流选择器」(选当前激活的工作流 / 另存为新工作流),方法放 static/src/app/methods/ui/workflow.ts(同构 workMode.ts),后端提供 GET/POST /api/workflow 子资源。
5. 数据存储约定(工作流文件夹放哪)
- 运行态数据根:
config/paths.py:RUNTIME_ROOT = ~/.astrion/astrion(L83-84,可ASTRION_DATA_ROOT覆盖);_MODE = _runtime_mode()分流 host/web(TERMINAL_SANDBOX_MODE决定)。各目录见 §1.5:DATA_DIR/LOGS_DIR/USER_SPACE_DIR/API_USER_SPACE_DIR(L102-106)。 - CUSTOM_SKILLS_DIR:
str(Path(RUNTIME_ROOT)/_MODE/"agentskills")(L109)——私有 skill/工作流的运行态位置。host 模式IS_HOST_MODE(L112)。 - 源码树 seed:
AGENT_SKILLS_DIR(默认./agentskills,L163)、PROMPTS_DIR(./prompts,L162)、AGENT_SKILLS_DIR默认源码树。 - resolve_deploy_config 回退链(L135):
<data_root>/config/<filename>(部署者真实配置)→ 源码树config/<filename>(seed)→ 源码树config/<filename>.example(示例兜底)。deploy_config_path(L126)用于可写配置(不回退)。 - 工作区级 .astrion:
WORKSPACE_SKILLS_DIRNAME=".astrion/skills"(L170)、WORKSPACE_MEMORY_DIRNAME=".astrion/memory"(L171)、WORKSPACE_REVIEW_DIRNAME=".astrion/review"(L172)。注意:这是工作区项目内(源码树或其他工作目录)的.astrion,与运行态~/.astrion/astrion/<mode>不同。
工作流接入判断(关键决策点):
- 源码树 vs 运行态:skill 采用「源码树 seed(
agentskills/)” + “运行态私有(~/.astrion/astrion/<mode>/agentskills/,CUSTOM_SKILLS_DIR)双源」,通过get_skills_catalog(base_dir=AGENT_SKILLS_DIR, private_dir=CUSTOM_SKILLS_DIR)合并(skills_manager.py L282);启用后同步到工作区.astrion/skills/(sync_workspace_skills)。 - 现有机制同时支持两种:工作流库若仿 skill,可在源码树
workflows/(seed)+ 运行态~/.astrion/astrion/<mode>/workflows(CUSTOM_SKILLS_DIR 同级)建库,再按启用/激活同步到工作区.astrion/workflows/。判断:用户创建的工作流属「运行态/用户私有」,应落运行态(含脚本的可执行文件),源码树只放内置种子示例;这与 AGENTS.md §1.5「运行态数据不落源码树」一致。 - 部署级配置(如
auto_approval、goal_review)走resolve_deploy_config;工作流若需独立审核配置可仿config/workflow_review.json[.example]加入该回退链。
总结:新增工作流功能的最小改动路径建议(文件级)
基于上述调研,判断实现「工作流」的最小改动路径如下(按依赖顺序,每个文件给出改动性质):
-
config/paths.py(改动):新增CUSTOM_WORKFLOWS_DIR(运行态,仿CUSTOM_SKILLS_DIRL109)、WORKFLOWS_DIR(源码树 seed,仿AGENT_SKILLS_DIRL163)、WORKSPACE_WORKFLOWS_DIRNAME(仿 L170.astrion/workflows)三个路径常量。 -
modules/workflows_manager.py(新增):仿modules/skills_manager.py提供validate_workflow_directory/archive_workflow_directory/get_workflows_catalog/sync_workspace_workflows,校验「工作方式→验证方式→结束方式」三段 + 附带 scripts。 -
core/main_terminal_parts/tools_definition/file_tools.py+tools_execution.py(改动):新增create_workflow工具定义(仿 create_skill L139)与 handler_handle_create_workflow_tool(仿 L545/530),在handle_tool_call分发链(L960 的 elif 分支)注册;归档复用modules/workflows_manager。 -
prompts/workflow.txt(新增)+core/main_terminal_parts/context/messages.py(改动):新增工作流上下文模板;在build_messages(L109,skills 段 L305 附近)追加「当前激活工作流」的 system 上下文注入(复用load_prompt+_get_or_init_frozen_prompt冻结)。 -
server/workflow_flow.py(新增)+server/chat_flow_task_main.py(改动):仿server/goal_flow.py编排「阶段/整体审核」。接入点:主循环if not tool_calls:结束判定(L2226)处,仿 goal 分支(L2230)增加工作流审核调用——复用modules/goal_review_agent.py(或其派生WorkflowReviewAgent)返回 done/continue;continue 注入续命 user 消息回写 messages(复用chat_flow_task_support.inject_runtime_user_message)。注意:任何新增主任务入口必须遵守 §12 门闸(process_message_task)。 -
server/tasks/workflows.py(新增,可并入 skills.py)仿server/tasks/skills.py:GET /api/workflows(列表/选择)+ 创建工作流对话的后端端点(如/api/workflow/conversations或复用/api/conversations+workflow_id)。 -
static/src/app/methods/ui/route.ts(改动):bootstrapRoute(L26)增加/workflow/new(及/workflow)分支,进入「新建工作流对话」模式并history.replaceState;避免被当作 conv id 解析。 -
前端对话类型链路(改动):
static/src/app/state.ts(L57/59 类型联合加'workflow')、static/src/app/methods/message/send.ts(L269 createUrl 分支)、static/src/app/methods/conversation/action.ts(L134/276 分支)、static/src/components/input/InputComposer.vue(L328 agent-type-switcher / L354 work-mode-switcher 区域加「工作流选择器」)。若新增「选择/另存工作流」UI,仿static/src/app/methods/ui/workMode.ts建static/src/app/methods/ui/workflow.ts。
附注 / 未确认项:
/workflow/new后端的with_terminal路由是否接受该路径、前端是否需新 socket/status 兼容——未确认,需在实现时验证。- 工作流「阶段结束审核后进入下一阶段」与 goal mode「目标完成后结束」的边界语义略有差异,需在
workflow_flow.py内明确「阶段衔接」与「整体结束」的状态机,避免与chat_flow_task_main.py现有 break/continue 逻辑冲突。- work_mode 三档(plan/ask/execute)是「交互节奏」正交档位;工作流不应作为第四档 work_mode,而应作为独立的「消息级上下文 + 审核挂钩」,避免与现有
_RUNTIME_MODE_KINDSdrift 通知机制冲突。