Version: 0.9.75.dev.260505

后端:
1.收口阶段 6 agent 结构迁移,将 newAgent 内核与 agentsvc 编排层迁入 services/agent
- 切换 Agent 启动装配与 HTTP handler 直连 agent sv,移除旧 service agent bridge
- 补齐 Agent 对 memory、task、task-class、schedule 的 RPC 适配与契约字段
- 扩展 schedule、task、task-class RPC/contract 支撑 Agent 查询、写入与 provider 切流
- 更新迁移文档、README 与相关注释,明确 agent 当前切流点和剩余 memory 迁移面
This commit is contained in:
Losita
2026-05-05 16:00:57 +08:00
parent e1819c5653
commit d7184b776b
174 changed files with 2189 additions and 1236 deletions

View File

@@ -0,0 +1,252 @@
package model
import "strings"
const (
// PhaseChatting 表示当前请求只需正常聊天,不进入 plan / execute 主链路。
PhaseChatting Phase = "chatting"
// PhaseInterrupted 表示本轮执行被"待用户交互"显式打断,当前连接应结束并等待恢复。
PhaseInterrupted Phase = "interrupted"
)
const PendingInteractionSnapshotVersion = 1
const (
// PendingMetaAskUserSpeakStreamed 表示 ask_user 文本已在上游节点流式推送过。
// interrupt 节点据此决定是否跳过二次正文推送,避免前端出现重复气泡。
PendingMetaAskUserSpeakStreamed = "ask_user_speak_streamed"
// PendingMetaAskUserHistoryAppended 表示 ask_user 文本已在上游写入过 history。
// interrupt 节点据此避免二次追加历史,防止上下文重复。
PendingMetaAskUserHistoryAppended = "ask_user_history_appended"
)
// PendingInteractionType 表示当前挂起交互的类型。
type PendingInteractionType string
const (
PendingInteractionTypeAskUser PendingInteractionType = "ask_user"
PendingInteractionTypeConfirm PendingInteractionType = "confirm"
PendingInteractionTypeConnectionLost PendingInteractionType = "connection_lost"
)
// PendingInteractionStatus 表示挂起交互的生命周期状态。
type PendingInteractionStatus string
const (
PendingInteractionStatusOpen PendingInteractionStatus = "open"
PendingInteractionStatusResolved PendingInteractionStatus = "resolved"
PendingInteractionStatusCanceled PendingInteractionStatus = "canceled"
)
// PendingToolCallSnapshot 保存"待确认工具调用"的最小快照。
//
// 职责边界:
// 1. 负责保存真正落库 / 落缓存恢复执行所需的最小信息;
// 2. ArgsJSON 约定存已经序列化好的参数快照,避免此处反向依赖具体 tool 参数结构;
// 3. 不负责工具执行,不负责幂等校验,不负责回滚。
type PendingToolCallSnapshot struct {
ToolName string `json:"tool_name"`
ArgsJSON string `json:"args_json"`
Summary string `json:"summary"`
}
// PendingInteraction 保存"本轮需要中断并等待用户后续动作"的交互快照。
//
// 设计目的:
// 1. ask_user 与 confirm 都不是业务 tool而是流程级中断所以单独建模
// 2. ResumeNode / ResumePhase / ResumeStep 用来记录恢复点,避免用户回答后整条链路从头乱跑;
// 3. 该结构设计成可被 Redis + MySQL 直接存储的快照骨架,后续只需要补序列化与持久化接线。
//
// TODO(agent/api): 后续由"用户追问回复接口 / 确认回调接口"读取这份快照并恢复运行。
type PendingInteraction struct {
Version int `json:"version"`
InteractionID string `json:"interaction_id"`
Type PendingInteractionType `json:"type"`
Status PendingInteractionStatus `json:"status"`
DisplayText string `json:"display_text"`
ResumeNode string `json:"resume_node"`
ResumePhase Phase `json:"resume_phase"`
ResumeStep int `json:"resume_step"`
PendingTool *PendingToolCallSnapshot `json:"pending_tool,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
}
// AgentRuntimeState 是 graph 运行时真正流转的状态容器。
//
// 职责边界:
// 1. CommonState 继续只负责主流程控制;
// 2. PendingInteraction 负责承载"需要中断后恢复"的交互快照;
// 3. 这样既不污染 CommonState 的职责,又能让 graph 在一次入参里拿到完整运行态。
type AgentRuntimeState struct {
*CommonState `json:"common_state"`
// PendingInteraction 承载挂起交互的持久化快照。
PendingInteraction *PendingInteraction `json:"pending_interaction,omitempty"`
// PendingConfirmTool 是 Execute → Confirm 之间传递待确认工具信息的临时邮箱。
// Execute 节点写入Confirm 节点读出并清空,不参与持久化。
PendingConfirmTool *PendingToolCallSnapshot `json:"-"`
}
// NewAgentRuntimeState 创建 graph 运行态。
func NewAgentRuntimeState(state *CommonState) *AgentRuntimeState {
rt := &AgentRuntimeState{CommonState: state}
rt.EnsureCommonState()
return rt
}
// EnsureCommonState 保证运行态里始终有一份可用的流程状态。
//
// 步骤说明:
// 1. 若 CommonState 为空,则补一份最小默认值,避免 graph / node 层空指针;
// 2. 若 Phase 尚未设置,则默认回到 planning保持当前主链路的保守起点
// 3. 若 MaxRounds 未设置,则回填默认值,避免编译后运行时无上限循环。
func (s *AgentRuntimeState) EnsureCommonState() *CommonState {
if s == nil {
return nil
}
if s.CommonState == nil {
s.CommonState = &CommonState{}
}
if s.CommonState.Phase == "" {
s.CommonState.Phase = PhasePlanning
}
if s.CommonState.MaxRounds <= 0 {
s.CommonState.MaxRounds = DefaultMaxRounds
}
return s.CommonState
}
// HasPendingInteraction 判断当前是否存在待恢复交互。
func (s *AgentRuntimeState) HasPendingInteraction() bool {
if s == nil || s.PendingInteraction == nil {
return false
}
return s.PendingInteraction.Status == PendingInteractionStatusOpen
}
// PendingInteractionType 返回当前挂起交互类型。
func (s *AgentRuntimeState) PendingInteractionType() PendingInteractionType {
if !s.HasPendingInteraction() {
return ""
}
return s.PendingInteraction.Type
}
// OpenAskUserInteraction 打开一个"向用户追问"的中断快照。
func (s *AgentRuntimeState) OpenAskUserInteraction(interactionID, question, resumeNode string) {
s.openPendingInteraction(
PendingInteractionTypeAskUser,
interactionID,
question,
resumeNode,
nil,
)
}
// OpenConfirmInteraction 打开一个"写操作待确认"的中断快照。
func (s *AgentRuntimeState) OpenConfirmInteraction(interactionID, confirmText, resumeNode string, pendingTool *PendingToolCallSnapshot) {
s.openPendingInteraction(
PendingInteractionTypeConfirm,
interactionID,
confirmText,
resumeNode,
pendingTool,
)
}
// ResumeFromPending 从挂起交互恢复主流程。
//
// 步骤说明:
// 1. 仅当存在 open 状态的 pending interaction 时才执行恢复;
// 2. 恢复时回写之前快照下来的 phase / step确保继续跑的是原任务位置而不是新分支
// 3. 恢复成功后清空挂起快照,避免同一份 pending 被重复消费。
func (s *AgentRuntimeState) ResumeFromPending() bool {
if !s.HasPendingInteraction() {
return false
}
flowState := s.EnsureCommonState()
pending := s.PendingInteraction
flowState.Phase = pending.ResumePhase
flowState.CurrentStep = pending.ResumeStep
pending.Status = PendingInteractionStatusResolved
s.PendingInteraction = nil
return true
}
// ClearPendingInteraction 直接清空挂起交互。
//
// 职责边界:
// 1. 仅负责粗暴清空快照;
// 2. 不自动恢复 phase / step避免误把"取消交互"与"恢复执行"混为一谈;
// 3. 若需要恢复流程,应优先使用 ResumeFromPending。
func (s *AgentRuntimeState) ClearPendingInteraction() {
if s == nil || s.PendingInteraction == nil {
return
}
s.PendingInteraction.Status = PendingInteractionStatusCanceled
s.PendingInteraction = nil
}
// SetPendingInteractionMetadata 为当前 open 状态的 pending interaction 写入元信息。
//
// 职责边界:
// 1. 仅对当前挂起交互打运行态标记,不参与业务语义判断;
// 2. 若当前没有 pending interaction则静默跳过
// 3. metadata 仅用于节点间协作(如避免 ask_user 重复推送)。
func (s *AgentRuntimeState) SetPendingInteractionMetadata(key string, value any) {
if s == nil || s.PendingInteraction == nil || s.PendingInteraction.Status != PendingInteractionStatusOpen {
return
}
trimmedKey := strings.TrimSpace(key)
if trimmedKey == "" {
return
}
if s.PendingInteraction.Metadata == nil {
s.PendingInteraction.Metadata = make(map[string]any)
}
s.PendingInteraction.Metadata[trimmedKey] = value
}
func (s *AgentRuntimeState) openPendingInteraction(
interactionType PendingInteractionType,
interactionID string,
displayText string,
resumeNode string,
pendingTool *PendingToolCallSnapshot,
) {
if s == nil {
return
}
flowState := s.EnsureCommonState()
resumePhase := flowState.Phase
if resumePhase == "" {
resumePhase = PhasePlanning
}
s.PendingInteraction = &PendingInteraction{
Version: PendingInteractionSnapshotVersion,
InteractionID: strings.TrimSpace(interactionID),
Type: interactionType,
Status: PendingInteractionStatusOpen,
DisplayText: strings.TrimSpace(displayText),
ResumeNode: strings.TrimSpace(resumeNode),
ResumePhase: resumePhase,
ResumeStep: flowState.CurrentStep,
PendingTool: clonePendingToolCallSnapshot(pendingTool),
}
// 1. 一旦进入 pending 状态,当前连接上的 graph 应立即停止向后执行。
// 2. 这里先统一把 Phase 置为 interrupted后续恢复时再按快照写回原阶段。
// 3. 这样分支函数只需要判断 HasPendingInteraction(),无需猜测"当前 phase 是否仍可信"。
flowState.Phase = PhaseInterrupted
}
func clonePendingToolCallSnapshot(snapshot *PendingToolCallSnapshot) *PendingToolCallSnapshot {
if snapshot == nil {
return nil
}
copied := *snapshot
return &copied
}