Files
smartmate/backend/newAgent/model/plan_contract.go
Losita d47a8bcabd Version: 0.9.25.dev.260417
后端:
1. AIHub 模型分级从 Worker/Strategist 两级重构为 Lite/Pro/Max 三级
- AIHub 结构体从 Worker + Strategist 改为 Lite + Pro + Max,分别对应轻量(标题生成)、标准(Chat 路由/闲聊/交付总结)、高能力(Plan 规划/Execute ReAct)三个能力层级
- config.example.yaml 新增 liteModel / proModel / maxModel 三个模型配置项,替代原 workerModel / strategistModel
- 启动层 InitEino 改为创建三个独立模型实例,抽取公共 baseURL 和 apiKey 减少重复
- pickChatModel 统一返回 Pro 模型,旧 strategist 参数不再生效;pickTitleModel 从 Worker 切到 Lite
- runNewAgentGraph 按 Plan/Execute→Max、Chat/Deliver→Pro 分级注入;Graph 出错回退也切到 Pro
- Memory 模块初始化从 Worker 改为 Pro
2. Plan 节点从"两阶段评估"简化为"单轮深度规划",thinking 开关改为全配置化
- 移除 Phase 1(快速评估 1600 token)+ Phase 2(深度规划 3200 token)的两轮调用逻辑,改为单轮不限 token 深度规划
- PlanDecision 移除 need_thinking 字段,prompt 规则和 JSON contract 同步删除该字段
- 各节点(Plan / Execute / Deliver)thinking 开关从硬编码改为从 AgentGraphDeps 读取,由 config.yaml 的 agent.thinking 段按节点注入
- 新增 agent.thinking 配置段(plan / execute / deliver / memory 四个独立布尔开关),config.example.yaml 补齐默认值
- 新增 resolveThinkingMode 公共函数,plan / execute / deliver 和 memory 决策/抽取链路统一使用
3. Memory 模块 LLM 调用支持 thinking 开关
- Config 新增 LLMThinking 字段,config_loader 从 agent.thinking.memory 读取
- LLMDecisionOrchestrator.Compare 和 LLMWriteOrchestrator.ExtractFacts 的 thinking 模式从硬编码 Disabled 改为读取配置
前端:
1. 移除助手输入区模型选择器及全部偏好持久化逻辑
- 删除 ModelType 类型、selectedModel ref、MODEL_PREFERENCE_STORAGE_KEY 常量
- 删除 isModelType / loadModelPreferenceMap / persistModelPreferenceMap / savePreferredModel / resolvePreferredModel / applyPreferredModelForConversation 六个函数及 modelPreferenceMap ref
- 删除 selectedModel watch 监听、发送消息时的 savePreferredModel 调用、切会话时的 applyPreferredModelForConversation 调用、会话迁移时的模型偏好迁移
- fetchChatStream 的 model 参数硬编码为 'worker'
- 删除模板中"模型"下拉选择器(标准/策略)及对应的全局样式 .assistant-model-select-panel
2. 上下文窗口指示器简化为仅显示总占用
- ContextWindowMeter 移除 msg0~msg3 四段彩色分段逻辑(ContextSegment 接口、segments computed、v-for 渲染)
- 进度条改为单一蓝色条,按 total/budget 比例填充;超预算时变红
- Tooltip 简化为仅显示"总计 X / 预算 Y(Z%)"

仓库:无
2026-04-17 12:27:04 +08:00

152 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package model
import (
"fmt"
"strings"
)
// PlanComplexity 表示规划阶段评估的任务复杂度。
type PlanComplexity string
const (
// PlanComplexitySimple 表示简单明确的操作,步骤之间无复杂依赖。
PlanComplexitySimple PlanComplexity = "simple"
// PlanComplexityModerate 表示多步操作,需要一定推理但不涉及深度分析。
PlanComplexityModerate PlanComplexity = "moderate"
// PlanComplexityComplex 表示需要深度推理、多方案比较或复杂依赖关系的任务。
PlanComplexityComplex PlanComplexity = "complex"
)
// PlanAction 表示规划阶段单轮决策的动作类型。
//
// 设计原则:
// 1. 规划阶段只关心“继续规划 / 追问用户 / 规划完成”这三类动作;
// 2. 这里先不把工具调用塞进 contract避免过早把 plan loop 复杂化;
// 3. 规划层产出的是“自然语言计划”,不是执行层的工具动作。
type PlanAction string
const (
// PlanActionContinue 表示当前信息已足够,继续规划下一轮。
PlanActionContinue PlanAction = "continue"
// PlanActionAskUser 表示当前规划缺少关键信息,需要中断并追问用户。
PlanActionAskUser PlanAction = "ask_user"
// PlanActionDone 表示规划已经完成,可以进入 confirm 或下一阶段。
PlanActionDone PlanAction = "plan_done"
)
// PlanDecision 是 plan prompt 单轮产出的统一决策结构。
//
// 职责边界:
// 1. Speak 是本轮先对用户说的话;若 action=ask_user通常这里会承载要追问的问题
// 2. Action 是规划阶段的下一步动作类型;
// 3. Reason 是给后端和日志看的简短解释;
// 4. PlanSteps 只在 plan_done 时要求返回,表示本轮最终确认下来的完整自然语言计划;
// 5. NeedsRoughBuild 为 true 时Confirm 后自动触发粗排节点,不需要 LLM 在 plan_steps 里手动描述放置步骤;
// 6. TaskClassIDs 是本次粗排涉及的任务类 ID 列表,与 CommonState.TaskClassIDs 保持一致。
type PlanDecision struct {
Speak string `json:"speak,omitempty"`
Action PlanAction `json:"action"`
Reason string `json:"reason,omitempty"`
Complexity PlanComplexity `json:"complexity"`
PlanSteps []PlanStep `json:"plan_steps,omitempty"`
NeedsRoughBuild bool `json:"needs_rough_build,omitempty"`
TaskClassIDs []int `json:"task_class_ids,omitempty"`
}
// Normalize 统一清洗规划决策中的字符串字段。
func (d *PlanDecision) Normalize() {
if d == nil {
return
}
d.Speak = strings.TrimSpace(d.Speak)
d.Action = PlanAction(strings.TrimSpace(string(d.Action)))
d.Reason = strings.TrimSpace(d.Reason)
d.Complexity = PlanComplexity(strings.TrimSpace(string(d.Complexity)))
for i := range d.PlanSteps {
d.PlanSteps[i].Normalize()
}
}
// Validate 校验规划决策的最小合法性。
//
// 校验原则:
// 1. 这里只校验“协议是否自洽”,不校验规划内容是否聪明、是否足够好;
// 2. 只有 plan_done 允许返回完整 plan_steps
// 3. 真正的规划质量判断仍留给后续 node 层和用户确认环节。
func (d *PlanDecision) Validate() error {
if d == nil {
return fmt.Errorf("plan decision 不能为空")
}
d.Normalize()
if d.Action == "" {
return fmt.Errorf("plan decision.action 不能为空")
}
// 复杂度兜底:未填写时默认 moderate不因此拒绝整个决策。
switch d.Complexity {
case PlanComplexitySimple, PlanComplexityModerate, PlanComplexityComplex:
// ok
case "":
d.Complexity = PlanComplexityModerate
default:
return fmt.Errorf("未知 complexity: %s", d.Complexity)
}
switch d.Action {
case PlanActionContinue, PlanActionAskUser:
if len(d.PlanSteps) > 0 {
return fmt.Errorf("%s 动作不应携带 plan_steps", d.Action)
}
return nil
case PlanActionDone:
if len(d.PlanSteps) == 0 {
return fmt.Errorf("plan_done 动作必须携带完整 plan_steps")
}
for i := range d.PlanSteps {
if err := d.PlanSteps[i].Validate(); err != nil {
return fmt.Errorf("plan_steps[%d] 非法: %w", i, err)
}
}
return nil
default:
return fmt.Errorf("未知 plan action: %s", d.Action)
}
}
// PlanStep 表示规划阶段产出的一条自然语言步骤。
//
// 设计说明:
// 1. Content 是步骤正文,后续可直接落到 CommonState.PlanSteps
// 2. DoneWhen 是可选的完成判定描述,用来给 execute 阶段提供最小退出条件;
// 3. 这里仍然保持“自然语言优先”,不把 plan step 过度结构化。
type PlanStep struct {
Content string `json:"content"`
DoneWhen string `json:"done_when,omitempty"`
}
// Normalize 统一清洗 plan step 中的字符串字段。
func (s *PlanStep) Normalize() {
if s == nil {
return
}
s.Content = strings.TrimSpace(s.Content)
s.DoneWhen = strings.TrimSpace(s.DoneWhen)
}
// Validate 校验单条 plan step 的最小合法性。
func (s *PlanStep) Validate() error {
if s == nil {
return fmt.Errorf("plan step 不能为空")
}
s.Normalize()
if s.Content == "" {
return fmt.Errorf("plan step.content 不能为空")
}
return nil
}