This commit is contained in:
@@ -148,3 +148,17 @@ func TestBackgroundCardSkippedWithoutInbox(t *testing.T) {
|
||||
}
|
||||
waitUntil(t, func() bool { return len(systemTexts(t, c)) == 1 })
|
||||
}
|
||||
|
||||
// TestPartsTextStripsInlineThink:MiniMax 行内 <think> 块不进子代理摘要/回投卡。
|
||||
func TestPartsTextStripsInlineThink(t *testing.T) {
|
||||
got := partsText([]core.Part{{Type: core.PartText, Text: "<think>先读文件</think>\n正文开始\n<think>再想</think>结论"}})
|
||||
if strings.Contains(got, "<think>") || strings.Contains(got, "先读文件") || strings.Contains(got, "再想") {
|
||||
t.Fatalf("think leaked: %q", got)
|
||||
}
|
||||
if !strings.Contains(got, "正文开始") || !strings.Contains(got, "结论") {
|
||||
t.Fatalf("body lost: %q", got)
|
||||
}
|
||||
if got := partsText([]core.Part{{Type: core.PartText, Text: "半截 <think> 未闭合保守保留"}}); !strings.Contains(got, "半截") {
|
||||
t.Fatalf("unclosed think should keep text: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1898,13 +1898,34 @@ func partsText(parts []core.Part) string {
|
||||
var sb strings.Builder
|
||||
for _, p := range parts {
|
||||
if p.Type == core.PartText {
|
||||
sb.WriteString(p.Text)
|
||||
text := p.Text
|
||||
// MiniMax 等模型把推理写成行内 <think> 块:子代理摘要/回投卡正文
|
||||
// 只取 think 之外的正文(实测 <think> 前缀泄漏进收件箱卡片与通知)。
|
||||
if body := stripInlineThink(text); strings.TrimSpace(body) != "" || !strings.Contains(text, "</think>") {
|
||||
text = body
|
||||
}
|
||||
sb.WriteString(text)
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(sb.String())
|
||||
}
|
||||
|
||||
// stripInlineThink 去掉文本中的 <think>…</think> 段(完整闭合才剥,半截保守保留)。
|
||||
func stripInlineThink(text string) string {
|
||||
for {
|
||||
start := strings.Index(text, "<think>")
|
||||
if start < 0 {
|
||||
return text
|
||||
}
|
||||
end := strings.Index(text[start:], "</think>")
|
||||
if end < 0 {
|
||||
return text
|
||||
}
|
||||
text = text[:start] + text[start+end+len("</think>"):]
|
||||
}
|
||||
}
|
||||
|
||||
func artifactSummary(parts []core.Part) string {
|
||||
var bits []string
|
||||
seen := map[string]bool{}
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
agentllm "agent-native/runtime/llm"
|
||||
core "agent-system-core"
|
||||
)
|
||||
|
||||
// 双速提醒经内核 Reminders 钩子进入下一请求的消息(不是响应部件):
|
||||
// 这里捕获每次模型请求的原文,检索提醒标记。
|
||||
type dualSpeedRecorder struct {
|
||||
clock *stepClock
|
||||
turns [][]agentllm.Event
|
||||
request []string
|
||||
base agentllm.StreamFunc
|
||||
}
|
||||
|
||||
// stepClock 每 tick 前进 5 秒;runDirect 起 0 时刻,模型每被调用一次 tick 一格,
|
||||
// 20 次调用即 100 秒量级,足以压过默认 75 秒阈值。
|
||||
type stepClock struct{ step int64 }
|
||||
|
||||
func (c *stepClock) now() time.Time { return time.Unix(0, c.step*int64(5*time.Second)) }
|
||||
func (c *stepClock) tick() { c.step++ }
|
||||
|
||||
func (rec *dualSpeedRecorder) stream(base agentllm.StreamFunc) agentllm.StreamFunc {
|
||||
return func(ctx context.Context, model string, msgs []agentllm.Message, tools []agentllm.ToolDecl, opts agentllm.StreamOptions) <-chan agentllm.Event {
|
||||
rec.clock.tick()
|
||||
for _, m := range msgs {
|
||||
if b, err := json.Marshal(m); err == nil {
|
||||
rec.request = append(rec.request, string(b))
|
||||
}
|
||||
}
|
||||
return base(ctx, model, msgs, tools, opts)
|
||||
}
|
||||
}
|
||||
|
||||
func (rec *dualSpeedRecorder) nudged() bool {
|
||||
for _, r := range rec.request {
|
||||
if strings.Contains(r, "任务快慢双速") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func runDualSpeed(t *testing.T, turns [][]agentllm.Event, setup func(rc *fakeRunCtx)) *dualSpeedRecorder {
|
||||
t.Helper()
|
||||
rec := &dualSpeedRecorder{clock: &stepClock{}, turns: turns}
|
||||
r := &LLMRunner{Stream: rec.stream(scriptStream(turns, nil)), Now: rec.clock.now}
|
||||
rc := &fakeRunCtx{ctx: context.Background(), prof: verifyOn()}
|
||||
if setup != nil {
|
||||
setup(rc)
|
||||
}
|
||||
if err := r.Execute(rc); err != nil {
|
||||
t.Fatalf("Execute: %v", err)
|
||||
}
|
||||
return rec
|
||||
}
|
||||
|
||||
func toolOnlyTurns(n int) [][]agentllm.Event {
|
||||
var turns [][]agentllm.Event
|
||||
for i := 0; i < n; i++ {
|
||||
// 每次调用用独立 ID(toolCall 帮手的 ID 固定,重复会撞内核的工具槽位)。
|
||||
call := agentllm.ToolCall{ID: fmt.Sprintf("c-ds-%d", i), Name: "make_report", Arguments: "{}"}
|
||||
turns = append(turns, textDeltaTurn("", call))
|
||||
}
|
||||
return turns
|
||||
}
|
||||
|
||||
// 长时间纯工具轮、用户零答复 → 阈值后恰好注入一次双速提醒。
|
||||
func TestDualSpeedNudgeFiresOnceAfterThreshold(t *testing.T) {
|
||||
turns := append(toolOnlyTurns(20), textDeltaTurn("完成。"))
|
||||
rec := runDualSpeed(t, turns, nil)
|
||||
if !rec.nudged() {
|
||||
t.Fatal("dual-speed nudge missing after threshold with no user reply")
|
||||
}
|
||||
count := 0
|
||||
for _, r := range rec.request {
|
||||
count += strings.Count(r, "任务快慢双速")
|
||||
}
|
||||
if count > 1 {
|
||||
t.Fatalf("nudge injected %d times, want at most once per request window", count)
|
||||
}
|
||||
}
|
||||
|
||||
// 短任务(阈值内完成)不提醒。
|
||||
func TestDualSpeedNudgeQuietBeforeThreshold(t *testing.T) {
|
||||
turns := append(toolOnlyTurns(1), textDeltaTurn("完成。"))
|
||||
rec := runDualSpeed(t, turns, nil)
|
||||
if rec.nudged() {
|
||||
t.Fatal("nudge fired before the threshold")
|
||||
}
|
||||
}
|
||||
|
||||
// 设置 runtime.dualSpeedNudgeSeconds=0 → 关闭,超时也不提醒。
|
||||
func TestDualSpeedNudgeDisabledBySetting(t *testing.T) {
|
||||
turns := append(toolOnlyTurns(20), textDeltaTurn("完成。"))
|
||||
rec := runDualSpeed(t, turns, func(rc *fakeRunCtx) {
|
||||
rc.runtimeExtra = map[string]any{"dualSpeedNudgeSeconds": float64(0)}
|
||||
})
|
||||
if rec.nudged() {
|
||||
t.Fatal("nudge fired although disabled via runtime.dualSpeedNudgeSeconds=0")
|
||||
}
|
||||
}
|
||||
|
||||
// 模型已给出面向用户的答复(快通道成立)→ 即使后续事实核查续写拖过阈值也不提醒。
|
||||
func TestDualSpeedNudgeSkippedWhenReplied(t *testing.T) {
|
||||
turns := [][]agentllm.Event{
|
||||
textDeltaTurn("", toolCall("make_report", `{"content":"准确率 95%"}`)),
|
||||
textDeltaTurn("完整总结:产出物与要点。"),
|
||||
textDeltaTurn("已修正并重新登记。"),
|
||||
}
|
||||
rec := runDualSpeed(t, turns, func(rc *fakeRunCtx) {
|
||||
rc.verify = true
|
||||
rc.execArtifact = &core.Artifact{ID: "art_0123456789ab", Name: "报告.md"}
|
||||
rc.execArtifactContent = []byte("# 报告\n\n准确率 95%。")
|
||||
})
|
||||
if rec.nudged() {
|
||||
t.Fatal("nudge fired although the model already gave its user reply")
|
||||
}
|
||||
}
|
||||
|
||||
// 已把深度工作放后台(慢通道成立)→ 不再提醒。
|
||||
func TestDualSpeedNudgeSkippedWhenDelegated(t *testing.T) {
|
||||
turns := [][]agentllm.Event{
|
||||
textDeltaTurn("", toolCall("delegate_task", `{"name":"深度解析","task":"逐页解析","background":true}`)),
|
||||
}
|
||||
turns = append(turns, toolOnlyTurns(19)...)
|
||||
turns = append(turns, textDeltaTurn("后台进行中,先给结论。"))
|
||||
rec := runDualSpeed(t, turns, func(rc *fakeRunCtx) {
|
||||
rc.bgID = "run_bg_1"
|
||||
})
|
||||
if rec.nudged() {
|
||||
t.Fatal("nudge fired although background delegation already happened")
|
||||
}
|
||||
}
|
||||
|
||||
// 首次被无视后按 45s 间隔升级重复(最多 3 次),第二次起为硬性措辞。
|
||||
func TestDualSpeedNudgeEscalates(t *testing.T) {
|
||||
turns := append(toolOnlyTurns(35), textDeltaTurn("完成。")) // 35×5s=175s → 75s 起、120s、165s 三档
|
||||
rec := runDualSpeed(t, turns, nil)
|
||||
marks, hard := 0, 0
|
||||
for _, r := range rec.request {
|
||||
marks += strings.Count(r, "用户已经等待")
|
||||
hard += strings.Count(r, "硬性要求")
|
||||
}
|
||||
if marks < 1 {
|
||||
t.Fatalf("escalation missing: requests=%d", len(rec.request))
|
||||
}
|
||||
if marks > 2 {
|
||||
t.Fatalf("escalated %d times, want at most 2 repeats after the first", marks)
|
||||
}
|
||||
}
|
||||
|
||||
// 入口前置:带附件的任务第 0 回合注入快慢双速选项;无附件不注入;阈值关闭不注入。
|
||||
func TestDualSpeedEntryHintWithAttachments(t *testing.T) {
|
||||
withAtt := runDualSpeed(t, [][]agentllm.Event{textDeltaTurn("完成。")}, func(rc *fakeRunCtx) {
|
||||
rc.attachments = []core.ParsedDocument{{Meta: core.DocMeta{Filename: "meeting.docx"}}}
|
||||
})
|
||||
if !strings.Contains(strings.Join(withAtt.request, "\n"), "快慢双速") {
|
||||
t.Fatal("entry hint missing for an attachment-bearing task")
|
||||
}
|
||||
noAtt := runDualSpeed(t, [][]agentllm.Event{textDeltaTurn("完成。")}, nil)
|
||||
if strings.Contains(strings.Join(noAtt.request, "\n"), "本任务附带材料") {
|
||||
t.Fatal("entry hint fired without attachments")
|
||||
}
|
||||
disabled := runDualSpeed(t, [][]agentllm.Event{textDeltaTurn("完成。")}, func(rc *fakeRunCtx) {
|
||||
rc.attachments = []core.ParsedDocument{{Meta: core.DocMeta{Filename: "meeting.docx"}}}
|
||||
rc.runtimeExtra = map[string]any{"dualSpeedNudgeSeconds": float64(0)}
|
||||
})
|
||||
if strings.Contains(strings.Join(disabled.request, "\n"), "本任务附带材料") {
|
||||
t.Fatal("entry hint fired although the nudge setting is disabled")
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,12 @@ type directRun struct {
|
||||
servedModel string
|
||||
switches []string
|
||||
switchMu sync.Mutex
|
||||
// 任务快慢双速(SR-B10 实路径):started 用注入时钟记 run 起点,
|
||||
// nudged/replied/delegated 三个标志驱动「超时仍未反馈则提醒转双速」的一次性触发。
|
||||
started time.Time
|
||||
nudgedDualSpeedCount int
|
||||
replied bool
|
||||
delegatedBackground bool
|
||||
}
|
||||
|
||||
type toolEvidence struct {
|
||||
@@ -98,7 +104,7 @@ func (t directTool) Execute(ctx context.Context, args json.RawMessage) (agenttoo
|
||||
}
|
||||
|
||||
func (l *LLMRunner) runDirect(rc RunCtx, stream agentllm.StreamFunc, model string, msgs []agentllm.Message, decls []agentllm.ToolDecl, streamOpts agentllm.StreamOptions, cardflow bool, eff core.EffectiveSettings, maxTurns, maxTools int, refresh turnRefresh) error {
|
||||
st := &directRun{l: l, rc: rc, eff: eff, cardflow: cardflow, inflight: map[string]*toolSlot{}}
|
||||
st := &directRun{l: l, rc: rc, eff: eff, cardflow: cardflow, started: l.now(), inflight: map[string]*toolSlot{}}
|
||||
// 记忆端口(N3 WP5):runCtx 暴露 store 时接线(注入由循环按请求完成)。
|
||||
if ms, ok := rc.(interface{ StoreMemory() memoryStore }); ok {
|
||||
st.memoryStore = ms.StoreMemory()
|
||||
@@ -289,6 +295,13 @@ func (l *LLMRunner) runDirect(rc RunCtx, stream agentllm.StreamFunc, model strin
|
||||
Reminders: func(turn int) []string {
|
||||
var out []string
|
||||
out = append(out, st.reminders(turn)...)
|
||||
// 快慢双速入口前置(SR-B10 实路径):带附件的任务在第 0 回合就把
|
||||
// 双速选项摆给模型——模型对开场指令的遵循远好于中途提醒(实测
|
||||
// MiniMax 无视中途 system-reminder,但对工作方法开场指令照办)。
|
||||
// 只提示一次,不强制拆分,保留 UR-27 的智能体自判。
|
||||
if turn == 0 && st.eff.GetNumber("runtime.dualSpeedNudgeSeconds", dualSpeedNudgeDefaultSeconds) > 0 && len(rc.Attachments()) > 0 {
|
||||
out = append(out, "本任务附带材料,可能属于重任务。若预计要产出正式文档或做深度处理,采用快慢双速:先在当前回复里给用户即时反馈(材料相关性判断、概要或执行计划),再用 delegate_task 的 background=true 把深度工作放到后台执行——收件箱会显示进度卡,完成后系统自动回投结果并汇报。简单任务按常规同步完成即可。")
|
||||
}
|
||||
// 转录权威:系统段在播种时冻结,动态上下文(尾注/环境/新命中模块)
|
||||
// 只在非播种 run 的首个请求注入一次——run 内反复重发没有信息增量。
|
||||
if sessStore != nil && !seedRun && turn == 0 {
|
||||
@@ -499,6 +512,10 @@ func (st *directRun) onEvent(ev nruntime.Event) {
|
||||
if body != "" {
|
||||
st.finalParts = append(st.finalParts, core.Part{Type: core.PartText, Text: body, Process: hasCalls})
|
||||
st.turnBody = body
|
||||
if !hasCalls {
|
||||
// 面向用户的答复段落已出现:快通道成立,双速提醒不再需要。
|
||||
st.replied = true
|
||||
}
|
||||
st.textIdx++
|
||||
}
|
||||
}
|
||||
@@ -732,6 +749,7 @@ func (st *directRun) runToolCtx(ctx context.Context, name, args string) agenttoo
|
||||
if serr != nil {
|
||||
out = "子代理失败:" + serr.Error()
|
||||
} else {
|
||||
st.delegatedBackground = true
|
||||
out = fmt.Sprintf("已在后台启动 %s(慢通道):收件箱已建进度卡,完成后结果回投该卡并自动汇报;先把快通道的即时反馈写给用户,不要等待", id)
|
||||
}
|
||||
break
|
||||
|
||||
@@ -42,9 +42,11 @@ type fakeRunCtx struct {
|
||||
bgID string
|
||||
bgErr error
|
||||
sandbox string // 非空时 ConversationSandbox 返回之(N3 WP2 转录测试)
|
||||
memEntries []memEntry // 非空时 StoreMemory 返回之(N3 WP5 记忆注入测试)
|
||||
memEntries []memEntry // 非空时 StoreMemory 返回之(N3 WP5 记忆注入断言用)
|
||||
usageTotal core.Usage // AddUsage 累计(N3 WP6 记账断言用)
|
||||
bgCalls int
|
||||
runtimeExtra map[string]any // 覆盖 runtime 组设置(双速提醒阈值等测试用)
|
||||
attachments []core.ParsedDocument // Attachments 返回值(双速入口前置提示测试用)
|
||||
spawnAgentFn func(agentID, name, task, hint string) (core.AgentResultPayload, error)
|
||||
}
|
||||
|
||||
@@ -56,7 +58,11 @@ func (f *fakeRunCtx) Settings() core.EffectiveSettings {
|
||||
if f.cardflow {
|
||||
eff["ui"] = map[string]any{"cardflow": true}
|
||||
}
|
||||
eff["runtime"] = map[string]any{"verify": f.verify, "composeFinal": f.compose}
|
||||
runtimeMap := map[string]any{"verify": f.verify, "composeFinal": f.compose}
|
||||
for k, v := range f.runtimeExtra {
|
||||
runtimeMap[k] = v
|
||||
}
|
||||
eff["runtime"] = runtimeMap
|
||||
return eff
|
||||
}
|
||||
func (f *fakeRunCtx) Emit(typ core.StreamEventType, payload any) error {
|
||||
@@ -146,7 +152,7 @@ func (f *fakeRunCtx) StoreMemory() agentmemory.Store {
|
||||
return fakeMemStore{entries: f.memEntries}
|
||||
}
|
||||
|
||||
func (f *fakeRunCtx) Attachments() []core.ParsedDocument { return nil }
|
||||
func (f *fakeRunCtx) Attachments() []core.ParsedDocument { return f.attachments }
|
||||
func (f *fakeRunCtx) Tools() []core.ToolDesc { return nil }
|
||||
func (f *fakeRunCtx) Subtask() string { return "" }
|
||||
func (f *fakeRunCtx) CreateCard(card core.ActivityCard) (core.ActivityCard, error) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package runtime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -149,6 +150,13 @@ func (st *directRun) reminders(turn int) []string {
|
||||
st.closedToolTurn = false
|
||||
st.checklistTouched = false
|
||||
}
|
||||
// 任务快慢双速(SR-B10 实路径触发):run 已运行超过阈值、用户还没看到任何
|
||||
// 答复、也没把深度工作放后台时,一次性提醒转双速——先把判断/计划写给用户,
|
||||
// 重活 delegate 后台。已经回复或已委派则永不打扰;接近完成的任务可按文案
|
||||
// 自行忽略。这是内核每回合的 Reminders 注入点(agent-native 循环原生钩子)。
|
||||
if note := st.dualSpeedNudge(); note != "" {
|
||||
return []string{note}
|
||||
}
|
||||
if st.toolTurnsSince < gates.ReminderToolGap {
|
||||
return nil
|
||||
}
|
||||
@@ -175,6 +183,48 @@ func (st *directRun) reminders(turn int) []string {
|
||||
return got
|
||||
}
|
||||
|
||||
// dualSpeedNudgeSeconds:双速提醒的运行时长阈值(秒)。0 = 关闭。
|
||||
const dualSpeedNudgeDefaultSeconds = 75
|
||||
|
||||
// 双速提醒升级:首次被无视后每 45 秒重复、最多 3 次,第二次起改硬性措辞。
|
||||
// 实测 MiniMax 对单次 system-reminder 无动于衷(opus 一次即响应)——重复+升级是
|
||||
// 不改内核契约的前提下最强的请求侧手段;仍无效的模型记录到任务卡,后续考虑
|
||||
// 系统级强制拆分(cancel + 后台续跑)方案。
|
||||
const (
|
||||
dualSpeedRepeatEvery = 45 * time.Second
|
||||
dualSpeedMaxNudges = 3
|
||||
)
|
||||
|
||||
// dualSpeedNudge 返回双速提醒文案;不满足条件或已达重复上限返回空串。
|
||||
// 条件用注入时钟(l.now)计量,测试可控。
|
||||
func (st *directRun) dualSpeedNudge() string {
|
||||
after := int(st.eff.GetNumber("runtime.dualSpeedNudgeSeconds", dualSpeedNudgeDefaultSeconds))
|
||||
if after <= 0 || st.replied || st.delegatedBackground {
|
||||
return ""
|
||||
}
|
||||
if st.started.IsZero() {
|
||||
return ""
|
||||
}
|
||||
elapsed := st.l.now().Sub(st.started)
|
||||
if elapsed < time.Duration(after)*time.Second {
|
||||
return ""
|
||||
}
|
||||
due := 1 + int((elapsed-time.Duration(after)*time.Second)/dualSpeedRepeatEvery)
|
||||
if due > dualSpeedMaxNudges {
|
||||
due = dualSpeedMaxNudges
|
||||
}
|
||||
if st.nudgedDualSpeedCount >= due {
|
||||
return ""
|
||||
}
|
||||
st.nudgedDualSpeedCount = due
|
||||
secs := int(elapsed.Round(time.Second).Seconds())
|
||||
log.Printf("[runtime] dual-speed nudge #%d injected at %ds (replied=%v delegated=%v)", due, secs, st.replied, st.delegatedBackground)
|
||||
if due == 1 {
|
||||
return fmt.Sprintf("本次任务已连续运行约 %d 秒,用户还没有收到任何答复(任务快慢双速,SR-B10)。现在二选一:若剩余工作量大(预计还要一分钟以上),先把你的判断、概要或执行计划作为即时反馈回复给用户,同时用 delegate_task 的 background=true 把深度处理放到后台执行(收件箱有进度卡,完成后系统自动回投结果并汇报);若已接近完成,把这句话忽略,继续做完后在最终答复里给出完整结论。不要默默长时间工作。", secs)
|
||||
}
|
||||
return fmt.Sprintf("用户已经等待约 %d 秒(第 %d 次提醒,硬性要求):立即停止后续工具调用,把当前进展、结论或计划作为回复发给用户;剩余的深度处理用 delegate_task 的 background=true 放到后台执行,完成后系统会自动回投结果。不要再让用户在没有反馈的情况下等待。", secs, due)
|
||||
}
|
||||
|
||||
// unfinishedItems 返回计划与待办的未完成项正文(状态过滤留在产品侧)。
|
||||
func (st *directRun) unfinishedItems() (todos, plans []string) {
|
||||
for _, item := range st.todos.Items {
|
||||
|
||||
@@ -98,6 +98,7 @@ func Schema() []core.SettingDesc {
|
||||
{Key: "runtime.llmRetries", Group: "runtime", Label: "模型重试次数", Type: core.SettingNumber, Default: 2, Effect: core.EffectNewRun, Description: "直连模型在还没有任何输出时,遇到超时、限流或服务端错误的额外重试次数。0 = 不重试。已经开始输出后不再重试"},
|
||||
{Key: "runtime.codeOrchestration", Group: "runtime", Label: "代码编排", Type: core.SettingBool, Default: false, Effect: core.EffectNewRun, Description: "允许模型用 run_tool_script 写脚本批量调用工具。默认关闭"},
|
||||
{Key: "runtime.factCheck", Group: "runtime", Label: "终轮事实核查", Type: core.SettingBool, Default: true, Effect: core.EffectNewRun, Description: "交付物里的百分比/日期/大额数字回溯本轮材料,找不到出处的给模型一轮修正(修正过仍失败不拦截,只如实记录)"},
|
||||
{Key: "runtime.dualSpeedNudgeSeconds", Group: "runtime", Label: "快慢双速提醒阈值(秒)", Type: core.SettingNumber, Default: 75, Effect: core.EffectNewRun, Description: "任务连续运行超过该秒数、用户还没收到任何答复、也没把深度工作放后台时,一次性提醒模型转快慢双速:先把判断/概要/计划回复给用户,重活用 delegate_task 的 background 放后台(SR-B10)。0 = 关闭"},
|
||||
{Key: "runtime.verifierSameModel", Group: "runtime", Label: "允许同源验证器", Type: core.SettingBool, Default: false, Effect: core.EffectNewRun, Description: "轻量档未单独配置时,默认跳过与主模型同源的独立验证(硬事实与事实核查仍执行);显式开启才用主模型做验证"},
|
||||
{Key: "runtime.composeFinal", Group: "runtime", Label: "终轮整理回复", Type: core.SettingBool, Default: false, Effect: core.EffectNewRun, Description: "用过工具的回合结束后,再用主模型重写一段最终回复。默认关闭(裁定 4:转录权威后工作模型已看到完整证据,重写只见过 160 字摘要、实测会虚构章节与承诺未做之事);异常收尾时另有兜底"},
|
||||
{Key: "runtime.intentSemantic", Group: "runtime", Label: "意图语义裁决(慢通道)", Type: core.SettingBool, Default: false, Effect: core.EffectRestart, Description: "规则歧义时用 embedding 语义相似度裁决。需已配置 embedding 端点。默认关闭(行为与 I1 一致)"},
|
||||
|
||||
Reference in New Issue
Block a user