新增: 待发箱/唤醒原语/预算随窗口 + 治理门禁与场景护栏

- 待发箱地基: V51 ai_outbox 表 + 手写 OutboxRepo(claim CAS/崩溃残留复位/瞬时失败保留) + outbox.rs 调度器(KV 默认关/Notify 唤醒/会话忙跳过不丢)
- Wake 异步唤醒原语: 三条件(delay/file-appear/process-exit)+纯判定+注入走正典 spawn_agentic_session,默认关
- 上下文预算随模型窗口: model_context.rs 三级优先(池显式>预设表>回退 128K);上界取 1M 而非 128K——max_tokens 仅裁剪预算,卡 128K 等于不治根因
- 文件租约补纯函数与单测(未另造双轨:LEASE 早随 cb260ab 落地) / 跨会话召回补纯函数单测(主体早已落地)
- 子代理: 完整卡已消化 + 子会话压缩层 + P3 批量
- 隧道/中继: 指令离线不丢 + Hello proto 版本 + 心跳与 runbook
- df-mcp: 写工具后落 KV 变更标记(供 GUI 轮询兜底,补独立 stdio 进程无感知)
- 迁移链顶点改单一真值常量 LATEST_SCHEMA_VERSION(修 2 处因 V51 失效的断言)
- 治理: i18n 特殊字符提交门禁(含反引号覆盖 + 10 自测) / 根目录卫生门禁 / 看板归档器 / 单一 lockfile(bun) / swap-install 落 temp/ 且兼容无 Get-FileHash
- 场景护栏: 流式正文可见性 e2e(playwright 免真机事件回放) + 列表续写/外置句柄 node 测试
This commit is contained in:
lxy
2026-10-10 08:09:17 +08:00
parent 9c68f98d89
commit b37279f3a2
32 changed files with 4175 additions and 152 deletions
+28 -11
View File
@@ -158,6 +158,10 @@ pub fn register(registry: &mut AiToolRegistry, ctx: SpawnAgentCtx) {
"model": {
"type": "string",
"description": "可选,子代理用模型 id;缺省继承当前会话模型"
},
"temperature": {
"type": "number",
"description": "可选,子代理采样温度(0~2);缺省沿用 provider 配置/默认 0.7,不影响主会话"
}
},
"required": ["task"]
@@ -166,7 +170,7 @@ pub fn register(registry: &mut AiToolRegistry, ctx: SpawnAgentCtx) {
registry,
ctx: SpawnAgentCtx,
"spawn_agent",
"派发一个子代理在隔离上下文中独立完成专项任务(不与用户交互),最终返回结论报告。适用:探索检索/多文件阅读汇总/资料核对等可独立收敛的子任务。参数 task 为自包含任务书(子代理看不到父对话);tools 为可选工具白名单;max_rounds 可选轮数上限(默认 12);model 可选模型(缺省继承当前会话)。注意:子代理无审批通道,写文件/命令执行等高风险工具会被自动拒绝,结论以 report 字段文本返回",
"派发一个子代理在隔离上下文中独立完成专项任务(不与用户交互),最终返回结论报告。适用:探索检索/多文件阅读汇总/资料核对等可独立收敛的子任务。参数 task 为自包含任务书(子代理看不到父对话);tools 为可选工具白名单;max_rounds 可选轮数上限(默认 12);model 可选模型(缺省继承当前会话);temperature 可选采样温度(缺省沿用 provider 配置)。注意:子代理无审批通道,写文件/命令执行等高风险工具会被自动拒绝,结论以 report 字段文本返回",
RiskLevel::Medium,
meta: ToolMeta {
// 审批卡文案模板(模板 {} 按位置填充,键取 task)
@@ -210,6 +214,8 @@ async fn spawn_agent_handler(
.and_then(|v| v.as_str())
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty());
// 采样温度独立覆盖(缺省 None=沿用 provider 配置/默认 0.7,不影响主会话)
let temperature = args.get("temperature").and_then(|v| v.as_f64()).map(|t| t as f32);
let requested: Vec<String> = args
.get("tools")
.and_then(|v| v.as_array())
@@ -241,21 +247,27 @@ async fn spawn_agent_handler(
return Err(anyhow::anyhow!("spawn_agent 无法定位父会话:只能在会话内调用"));
}
// model 不传 → 继承父会话模型 override(与父对话同模型语义)
let child_model = match model {
Some(m) => Some(m),
None => ctx
.session
.lock()
.await
.conv_read(&parent_conv_id)
.and_then(|c| c.model_override.clone()),
// model 不传 → 继承父会话模型 override(与父对话同模型语义);
// lang 恒沿用父会话语言(agent_language,缺省中文),子代理主体与工具纪律段随之同语,
// 否则英文会话派生的子代理仍用中文提示词(报告语言/术语断层)。一次锁内取两者。
let (child_model, parent_lang) = {
let session = ctx.session.lock().await;
let parent = session.conv_read(&parent_conv_id);
let model = match model {
Some(m) => Some(m),
None => parent.and_then(|c| c.model_override.clone()),
};
let lang = parent
.and_then(|c| c.agent_language.clone())
.filter(|s| !s.is_empty())
.unwrap_or_else(|| "zh-CN".to_string());
(model, lang)
};
let child_id = next_child_id(&parent_conv_id);
let task_prompt = build_task_prompt(&task, &allowed, &trimmed);
// P2-3:系统提示词注入实际生效的预算值(父可传 1..20,子代理按真实预算规划)
let system_prompt = sub_agent_system_prompt(max_rounds);
let system_prompt = sub_agent_system_prompt(max_rounds, &parent_lang);
// P3-5:spawn 时刻快照父会话 save 元数据(active provider/created_at)。子代理后台
// 长跑收尾时用户可能已切走会话,出口落库若读 active_* 会继承别的会话元数据;
@@ -286,6 +298,8 @@ async fn spawn_agent_handler(
tools = allowed.len(),
max_rounds,
model = ?child_model,
lang = %parent_lang,
temperature = ?temperature,
"[sub-agent] 派发子代理"
);
@@ -303,6 +317,7 @@ async fn spawn_agent_handler(
allowed,
max_rounds,
child_model,
temperature,
ctx.llm_concurrency.clone(),
parent_save_meta,
)
@@ -316,6 +331,8 @@ async fn spawn_agent_handler(
"agent_id": child_id,
"rounds": outcome.rounds,
"tokens": outcome.tokens,
// 实际生效模型(回退/候选切换后的最终值):父会话据此知晓子代理真用了哪个模型
"model": outcome.model,
"report": outcome.summary,
}))
}