Documentation
¶
Index ¶
- Constants
- func AllowAllPermissionPolicy() PermissionPolicy
- func BuiltinProfiles() []*Profile
- func DefaultPermissionPolicy() PermissionPolicy
- func EnsureBuiltinProfiles(ctx context.Context, repo ProfileRepository) error
- func MessageBlockContent(data any) (string, bool)
- func NewEventBus() EventBus
- func NewHistoryService(store HistoryStore) HistoryService
- func NewMessageService(store MessageStore) MessageService
- func NewSessionService(store SessionStore) SessionService
- func NewTaskRuntime(svc *AgentService, task *Task, opts ...RuntimeOption) Runtime
- func NewUserPermissionPolicy(provider UserAgentConfigProvider) PermissionPolicy
- func ProjectTimelineEvent(sessionID, taskID int64, event StreamEvent) (MessageCreateParams, bool)
- func ReadBuiltinPrompt(name string) (string, error)
- func ReasoningBlockContent(data any) (string, bool)
- func SetBuiltinPromptDir(dir string)
- func TimelineEventSummary(prefix string, data any) string
- type AgentService
- func (s *AgentService) AnswerQuestion(ctx context.Context, id int64, answer AskUserAnswer, by int64) error
- func (s *AgentService) ApprovePermission(ctx context.Context, id int64, by int64) error
- func (s *AgentService) CancelTask(ctx context.Context, taskID int64) error
- func (s *AgentService) CreateTask(ctx context.Context, req Request, facts TaskFacts) (*Task, error)
- func (s *AgentService) DeleteMemory(ctx context.Context, id int64) error
- func (s *AgentService) DeleteProfile(ctx context.Context, id int64) error
- func (s *AgentService) DenyPermission(ctx context.Context, id int64, by int64) error
- func (s *AgentService) GetMemory(ctx context.Context, id int64) (*Memory, error)
- func (s *AgentService) GetPendingPermissions(ctx context.Context, taskID int64) ([]*PermissionRequest, error)
- func (s *AgentService) GetPendingQuestions(ctx context.Context, taskID int64) ([]*QuestionRequest, error)
- func (s *AgentService) GetProfile(ctx context.Context, id int64) (*Profile, error)
- func (s *AgentService) GetSystemPromptByProfileWithContext(ctx context.Context, profile *Profile, rc *runtimecontext.RuntimeContext, ...) string
- func (s *AgentService) GetSystemSpromptByProfile(ctx context.Context, profile *Profile, vars map[string]any, ...) string
- func (s *AgentService) GetTask(ctx context.Context, id int64) (*Task, error)
- func (s *AgentService) GetTaskRequest(ctx context.Context, taskID int64) (*Request, error)
- func (s *AgentService) ListMemory(ctx context.Context, userID int64, offset, limit int, kinds ...MemoryKind) ([]*Memory, int64, error)
- func (s *AgentService) ListProfiles(ctx context.Context) ([]*Profile, error)
- func (s *AgentService) PagePermissions(ctx context.Context, offset, limit int, taskID int64, ...) ([]*PermissionRequest, int64, error)
- func (s *AgentService) PageQuestions(ctx context.Context, offset, limit int, taskID int64, ...) ([]*QuestionRequest, int64, error)
- func (s *AgentService) PageTasks(ctx context.Context, offset, limit int, statuses ...TaskStatus) ([]*Task, int64, error)
- func (s *AgentService) Recover(ctx context.Context) error
- func (s *AgentService) RenderSystemPrompt(ctx context.Context, userID int64, profileID int64, vars map[string]any) (*Profile, string, error)
- func (s *AgentService) ResolveProfile(ctx context.Context, id int64) (*Profile, error)
- func (s *AgentService) ResumeTask(ctx context.Context, taskID int64) error
- func (s *AgentService) RetrieveMemory(ctx context.Context, userID int64, query string, limit int) ([]*Memory, error)
- func (s *AgentService) RunTask(ctx context.Context, req Request, facts TaskFacts) (*Task, error)
- func (s *AgentService) RunTaskSync(ctx context.Context, req Request, facts TaskFacts, opts ...RuntimeOption) (*Result, error)
- func (s *AgentService) RunTaskSyncByID(ctx context.Context, taskID int64, opts ...RuntimeOption) (*Result, error)
- func (s *AgentService) SaveMemory(ctx context.Context, memory *Memory) error
- func (s *AgentService) SaveProfile(ctx context.Context, profile *Profile) error
- func (s *AgentService) SetTaskFinishedHandler(h TaskFinishedHandler)
- func (s *AgentService) StartTask(ctx context.Context, taskID int64) error
- func (s *AgentService) Subscribe(taskID int64, handler EventHandler) func()
- type ChatService
- func (s *ChatService) CreateTurn(ctx context.Context, in TurnInput) (*Task, Session, error)
- func (s *ChatService) GetSession(ctx context.Context, id int64, q SessionMessageQuery) (SessionDetail, error)
- func (s *ChatService) GetTaskTimeline(ctx context.Context, taskID, userID int64) ([]Message, error)
- func (s *ChatService) ListMessages(ctx context.Context, sessionID int64) ([]Message, error)
- func (s *ChatService) ListSessions(ctx context.Context, userID int64, offset, limit int) ([]Session, int64, error)
- func (s *ChatService) ListTaskMessages(ctx context.Context, sessionID, taskID int64) ([]Message, error)
- func (s *ChatService) StartTurn(ctx context.Context, taskID int64) error
- type MemoryConfig
- type MemoryManager
- func (m *MemoryManager) Delete(ctx context.Context, id int64) error
- func (m *MemoryManager) Extract(ctx context.Context, turn MemoryTurn) ([]*Memory, error)
- func (m *MemoryManager) Get(ctx context.Context, id int64) (*Memory, error)
- func (m *MemoryManager) List(ctx context.Context, userID int64, offset, limit int, kinds ...MemoryKind) ([]*Memory, int64, error)
- func (m *MemoryManager) Retrieve(ctx context.Context, q MemoryQuery) ([]*Memory, error)
- func (m *MemoryManager) Save(ctx context.Context, memory *Memory) error
- func (m *MemoryManager) Touch(ctx context.Context, id int64) error
- type PermissionManager
- func (m *PermissionManager) Approve(ctx context.Context, id int64, by int64) (*PermissionRequest, error)
- func (m *PermissionManager) Consume(ctx context.Context, id int64) error
- func (m *PermissionManager) Create(ctx context.Context, taskID int64, sessionID string, operation Operation) (*PermissionRequest, error)
- func (m *PermissionManager) Deny(ctx context.Context, id int64, by int64) (*PermissionRequest, error)
- func (m *PermissionManager) FindByToolCall(ctx context.Context, taskID int64, toolCallID string) (*PermissionRequest, error)
- func (m *PermissionManager) Get(ctx context.Context, id int64) (*PermissionRequest, error)
- func (m *PermissionManager) GetPending(ctx context.Context, taskID int64) ([]*PermissionRequest, error)
- func (m *PermissionManager) Page(ctx context.Context, offset, limit int, taskID int64, ...) ([]*PermissionRequest, int64, error)
- func (m *PermissionManager) Wait(ctx context.Context, id int64) (PermissionDecision, error)
- type ProfileManager
- func (m *ProfileManager) Delete(ctx context.Context, id int64) error
- func (m *ProfileManager) Get(ctx context.Context, id int64) (*Profile, error)
- func (m *ProfileManager) List(ctx context.Context) ([]*Profile, error)
- func (m *ProfileManager) Resolve(ctx context.Context, id int64) (*Profile, error)
- func (m *ProfileManager) Save(ctx context.Context, p *Profile) error
- type QuestionManager
- func (m *QuestionManager) Answer(ctx context.Context, id int64, answer AskUserAnswer, by int64) (*QuestionRequest, error)
- func (m *QuestionManager) Cancel(ctx context.Context, id int64) (*QuestionRequest, error)
- func (m *QuestionManager) Create(ctx context.Context, taskID int64, sessionID string, input AskUserInput) (*QuestionRequest, error)
- func (m *QuestionManager) CreateForToolCall(ctx context.Context, taskID int64, sessionID string, input AskUserInput, ...) (*QuestionRequest, error)
- func (m *QuestionManager) FindByToolCall(ctx context.Context, taskID int64, toolCallID string) (*QuestionRequest, error)
- func (m *QuestionManager) Get(ctx context.Context, id int64) (*QuestionRequest, error)
- func (m *QuestionManager) GetPending(ctx context.Context, taskID int64) ([]*QuestionRequest, error)
- func (m *QuestionManager) Page(ctx context.Context, offset, limit int, taskID int64, ...) ([]*QuestionRequest, int64, error)
- func (m *QuestionManager) Wait(ctx context.Context, id int64) (AskUserAnswer, error)
- type RuntimeOption
- type ServiceConfig
- type SessionDetail
- type SessionMessageQuery
- type TimelineRecorder
- type TurnInput
Constants ¶
const ( // DefaultSessionMessageLimit 是会话详情默认返回的消息条数。 DefaultSessionMessageLimit = 20 // MaxSessionMessageLimit 是单次可请求的最大消息条数(防止前端一次拉全量)。 MaxSessionMessageLimit = 200 )
会话详情分页参数默认值与上限。
Variables ¶
This section is empty.
Functions ¶
func AllowAllPermissionPolicy ¶
func AllowAllPermissionPolicy() PermissionPolicy
AllowAllPermissionPolicy 返回一个“全部放行”的策略实例。
func BuiltinProfiles ¶
func BuiltinProfiles() []*Profile
BuiltinProfiles 返回框架内置 Profile 的种子数据。新增内置 Profile 时在此追加。
func DefaultPermissionPolicy ¶
func DefaultPermissionPolicy() PermissionPolicy
DefaultPermissionPolicy 返回框架内置的默认策略实例。
func EnsureBuiltinProfiles ¶
EnsureBuiltinProfiles 把内置 Profile 同步到仓库(数据库初始化工具)。
func MessageBlockContent ¶
MessageBlockContent 从 StreamEventMessage 的 Data 中提取完整 assistant 文本。
投影与轮末兜底共用它,保证「什么算一条 assistant 文本」只有一个口径。
func NewHistoryService ¶
func NewHistoryService(store HistoryStore) HistoryService
NewHistoryService 基于 Store 构建文件历史服务。
func NewMessageService ¶
func NewMessageService(store MessageStore) MessageService
NewMessageService 创建消息服务。
func NewSessionService ¶
func NewSessionService(store SessionStore) SessionService
NewSessionService 创建会话服务。
func NewTaskRuntime ¶
func NewTaskRuntime(svc *AgentService, task *Task, opts ...RuntimeOption) Runtime
func NewUserPermissionPolicy ¶
func NewUserPermissionPolicy(provider UserAgentConfigProvider) PermissionPolicy
NewUserPermissionPolicy 创建用户感知的权限策略。
func ProjectTimelineEvent ¶
ProjectTimelineEvent 把一条流式完整块事件投影为可持久化的会话消息。
sessionID / taskID 写入消息归属;taskID 为 0 表示不属于 agent_tasks(如子 Agent 会话)。 返回 ok=false 表示该事件不应落库。
func ReadBuiltinPrompt ¶
ReadBuiltinPrompt 按提示词名读取内置提示词文件。
func ReasoningBlockContent ¶
ReasoningBlockContent 从 reasoning 事件体中提取完整思考文本。
func SetBuiltinPromptDir ¶
func SetBuiltinPromptDir(dir string)
SetBuiltinPromptDir 覆盖内置提示词目录(空串表示恢复默认:相对当前工作目录)。
func TimelineEventSummary ¶
TimelineEventSummary 把事件体序列化为消息正文(序列化失败时退化为 "<prefix> event")。
Types ¶
type AgentService ¶
type AgentService struct {
// contains filtered or unexported fields
}
AgentService 是 Agent 执行架构的编排层(即 design.md 中的 AgentTaskManager)。
它把 Client(Provider 门面)、TaskRepository、PermissionManager、 EventBus 与 PermissionPolicy 组合成一条完整的调用链路:
RunTask 创建任务 → 启动 goroutine 执行 Agent → Agent 通过 Runtime.Emit 输出事件(持久化 + 广播) → Agent 通过 Runtime.RequestPermission 请求权限(持久化 pending + 任务置为 waiting) → UI 调用 ApprovePermission / DenyPermission(更新 DB + 唤醒 Agent) → Agent 恢复执行,直至 completed / failed
Recover 用于后端重启后重建运行态。
func NewService ¶
func NewService(cfg ServiceConfig) *AgentService
NewService 创建 AgentService,未提供的依赖使用安全的默认值。
注意:仓库 / 管理器(Tasks / Events / Perms / Questions / Memory / Profiles)由调用方 注入(见 container 的依赖注入配置)。数据库才是状态源,因此这里不再提供内存仓库兜底; 仅 EventBus / PermissionPolicy 这类无状态组件提供默认值。
func (*AgentService) AnswerQuestion ¶
func (s *AgentService) AnswerQuestion(ctx context.Context, id int64, answer AskUserAnswer, by int64) error
AnswerQuestion 提交用户对一次提问的作答(供 UI / HTTP 层调用)。
func (*AgentService) ApprovePermission ¶
ApprovePermission 批准权限请求(供 UI / HTTP 层调用)。
两种情形:
- 任务仍在本进程执行(存在 waiter):Approve 会唤醒 Wait,无需额外动作;
- 任务因进程重启而失去 goroutine:批准后立即从持久化检查点真正续跑(ResumeTask)。
func (*AgentService) CancelTask ¶
func (s *AgentService) CancelTask(ctx context.Context, taskID int64) error
CancelTask 取消任务:取消执行上下文并置为 canceled。
func (*AgentService) CreateTask ¶
func (s *AgentService) CreateTask(ctx context.Context, req Request, facts TaskFacts) (*Task, error)
CreateTask 创建任务(不启动执行)。
facts 是任务的生命周期属性(用户 / 会话 / 项目),由调用方显式提供, 不再从 Request 或 RuntimeContext 推导。
供 HTTP 层使用:先拿到 taskID 订阅事件,再调用 StartTask 启动,避免错过 task.created 等早期事件。
func (*AgentService) DeleteMemory ¶
func (s *AgentService) DeleteMemory(ctx context.Context, id int64) error
DeleteMemory 删除记忆,并广播记忆事件。
func (*AgentService) DeleteProfile ¶
func (s *AgentService) DeleteProfile(ctx context.Context, id int64) error
DeleteProfile 删除自定义 Profile(内置 Profile 不可删除)。
func (*AgentService) DenyPermission ¶
DenyPermission 拒绝权限请求(供 UI / HTTP 层调用)。
若任务已无活跃 goroutine(后端重启),拒绝后直接把等待中的任务置为 failed, 保证会话轮次能正常收尾(onTaskFinished),而不是永远停在 waiting_permission。
func (*AgentService) GetMemory ¶
func (s *AgentService) GetMemory(ctx context.Context, id int64) (*Memory, error)
GetMemory 按 ID 查询单条记忆。
func (*AgentService) GetPendingPermissions ¶
func (s *AgentService) GetPendingPermissions(ctx context.Context, taskID int64) ([]*PermissionRequest, error)
GetPendingPermissions 查询任务当前待确认的权限请求。
func (*AgentService) GetPendingQuestions ¶
func (s *AgentService) GetPendingQuestions(ctx context.Context, taskID int64) ([]*QuestionRequest, error)
GetPendingQuestions 查询任务当前待作答的提问请求。
func (*AgentService) GetProfile ¶
func (s *AgentService) GetProfile(ctx context.Context, id int64) (*Profile, error)
GetProfile 按 ID 返回 Profile(全局可见)。
func (*AgentService) GetSystemPromptByProfileWithContext ¶
func (s *AgentService) GetSystemPromptByProfileWithContext(ctx context.Context, profile *Profile, rc *runtimecontext.RuntimeContext, facts TaskFacts, selectedSkills []string) string
GetSystemPromptByProfileWithContext 按 Profile 渲染系统提示词,并带上本次调用的 运行时上下文(模板变量)与执行事实(用户 / 项目 / 会话),使记忆注入优先命中 所属上下文。rc / facts 均可为零值。
func (*AgentService) GetSystemSpromptByProfile ¶
func (s *AgentService) GetSystemSpromptByProfile(ctx context.Context, profile *Profile, vars map[string]any, selectedSkills []string) string
applyProfile 依据 Profile 生成本轮最终的 SystemPrompt。
入参:
- profile:已解析的 Agent Profile(为 nil 时返回空提示词,表示不附加系统提示词);
- vars:渲染 Profile.SystemPrompt(pongo2 模板)所用的变量,通常为 RuntimeContext.SystemPromptVars;
- userID:记忆的归属用户(为空时不注入背景);
- selectedSkills:本轮启用的技能名(为空表示全部);据此把技能元信息(name / description)注入提示词,正文由模型调用 get_skill 工具按需加载。
处理顺序:渲染 Profile 模板 → 注入技能指令 → 按 Profile.Context 开关注入记忆。 模板渲染失败时退回原始模板,保证调用仍可进行。 GetSystemSpromptByProfile 按 Profile 渲染系统提示词,并按需注入技能说明与记忆背景。
vars 是模板变量(来自 RuntimeContext.SystemPromptVars);记忆注入的用户 / 上下文维度 需要 TaskFacts,因此没有业务场景的调用请使用 GetSystemPromptByProfileWithContext(或 RenderSystemPrompt)。
func (*AgentService) GetTask ¶
func (s *AgentService) GetTask(ctx context.Context, id int64) (*Task, error)
GetTask 查询任务。
func (*AgentService) GetTaskRequest ¶
func (s *AgentService) GetTaskRequest(ctx context.Context, taskID int64) (*Request, error)
GetTaskRequest 按任务 ID 加载任务,返回任务持久化的 Request 内容。
供 UI 调试 / 审计使用。注意:这里返回的是任务创建时的原始请求,未应用 Agent Profile; 真正发送给 Provider 的系统提示词在任务执行时由 run 经 applyProfile 渲染 (Profile 模板 + RuntimeContext.SystemPromptVars + 背景注入)。
func (*AgentService) ListMemory ¶
func (s *AgentService) ListMemory(ctx context.Context, userID int64, offset, limit int, kinds ...MemoryKind) ([]*Memory, int64, error)
ListMemory 分页查询某用户的记忆;kinds 为空表示全部类别。
func (*AgentService) ListProfiles ¶
func (s *AgentService) ListProfiles(ctx context.Context) ([]*Profile, error)
ListProfiles 返回全部 Profile(内置 + 自定义,全局可见)。
func (*AgentService) PagePermissions ¶
func (s *AgentService) PagePermissions(ctx context.Context, offset, limit int, taskID int64, statuses ...PermissionStatus) ([]*PermissionRequest, int64, error)
PagePermissions 分页查询权限请求;taskID 为 0 表示全部任务,statuses 为空表示全部状态。
func (*AgentService) PageQuestions ¶
func (s *AgentService) PageQuestions(ctx context.Context, offset, limit int, taskID int64, statuses ...QuestionStatus) ([]*QuestionRequest, int64, error)
PageQuestions 分页查询提问请求;taskID 为 0 表示全部任务,statuses 为空表示全部状态。
func (*AgentService) PageTasks ¶
func (s *AgentService) PageTasks(ctx context.Context, offset, limit int, statuses ...TaskStatus) ([]*Task, int64, error)
PageTasks 分页查询任务(offset/limit 由上层根据 types.Pagination 计算)。
func (*AgentService) Recover ¶
func (s *AgentService) Recover(ctx context.Context) error
Recover 在后端重启后重建运行态(见 design.md 第 6 / 19 节)。
行为:
- running 且无活跃 goroutine:执行循环已随进程消失,置为 failed(保留检查点, 用户可通过 ResumeTask 手动续跑;错误信息标注 resumable);
- waiting_permission 且无活跃 goroutine:保留状态与 pending 权限,等待用户操作; 批准后 ApprovePermission 会自动 ResumeTask,拒绝则直接置为 failed。
注意:running → failed 会触发 onTaskFinished。ChatService 的收尾逻辑不依赖 内存 turns 表(重启后为空),而是用 Task.SessionID 从数据库恢复收尾,因此 会话的 CurrentTaskID / 错误消息仍能被正确清理。
func (*AgentService) RenderSystemPrompt ¶
func (s *AgentService) RenderSystemPrompt(ctx context.Context, userID int64, profileID int64, vars map[string]any) (*Profile, string, error)
RenderSystemPrompt 按 Profile ID 解析 Profile,并用 vars 渲染其系统提示词模板, 返回真正的系统提示词(供 DescribeEnv 等前端展示 / 调试场景使用)。
vars 是模板变量(通常为 RuntimeContext.SystemPromptVars),必须透传给 buildSystemPrompt,否则模板里的 {{...}} 变量会渲染成空值。
Profile 不存在时返回 nil Profile、空串与 nil 错误。
func (*AgentService) ResolveProfile ¶
func (s *AgentService) ResolveProfile(ctx context.Context, id int64) (*Profile, error)
ResolveProfile 按 ID 解析 Profile,与 run 内应用 Profile 的解析路径保持一致 (id 为 0 时回退到内置默认 Profile)。
func (*AgentService) ResumeTask ¶
func (s *AgentService) ResumeTask(ctx context.Context, taskID int64) error
ResumeTask 从持久化检查点续跑任务(供 UI 手动触发,或权限 / 提问决策后自动触发)。
前置条件:
- 任务存在检查点(Provider 在执行期写入);
- 任务无活跃 goroutine(否则返回 ErrTaskAlreadyRunning);
- 任务不是 completed / canceled(终态不可续跑)。
执行链:MarkResumed → 持久化 running → 启动 goroutine → Provider 读取检查点, 重放未完成的工具调用(已批准的权限会被 consume 后直接放行),随后继续 Agent 循环。
func (*AgentService) RetrieveMemory ¶
func (s *AgentService) RetrieveMemory(ctx context.Context, userID int64, query string, limit int) ([]*Memory, error)
RetrieveMemory 检索与 query 相关的记忆。
func (*AgentService) RunTask ¶
func (s *AgentService) RunTask(ctx context.Context, req Request, facts TaskFacts) (*Task, error)
RunTask 创建任务并异步执行。
执行与调用方 ctx 解耦:任务在独立 goroutine 中运行,可跨多次 HTTP 请求存活, 通过权限确认 / 取消 / 恢复驱动其生命周期。返回的任务对象用于前端轮询 / 订阅。
事件消费只有两条通路:实时推送走 EventBus(Subscribe),历史落库走 TimelineRecorder(agent_messages);不再接受同步 handler。
func (*AgentService) RunTaskSync ¶
func (s *AgentService) RunTaskSync(ctx context.Context, req Request, facts TaskFacts, opts ...RuntimeOption) (*Result, error)
RunTaskSync 同步执行一次性任务:创建 → 运行 → 返回聚合结果。
供需要一次性拿到结果的业务(如 AISummaryWorker)使用,替代直接调用 agent.Client.Invoke。 与 RunTask / StartTask 不同,它不启动 goroutine,直接在调用方同步执行。
同步执行没有 UI 承接权限确认,因此默认无人值守:权限 ask 直接放行(见 RunTaskSyncByID)。 如需保留原有的人工确认语义,可传 WithAutoApprove(false)。
若调用方需要在任务创建后、执行前拿到 taskID(例如把摘要记录立即关联到任务), 请改用 CreateTask + RunTaskSyncByID 的两步形式。
func (*AgentService) RunTaskSyncByID ¶
func (s *AgentService) RunTaskSyncByID(ctx context.Context, taskID int64, opts ...RuntimeOption) (*Result, error)
RunTaskSyncByID 同步执行已创建的任务,返回聚合结果。
与 RunTaskSync 的区别在于任务创建被前置:调用方可以先 CreateTask 拿到 taskID 并完成 关联(例如 AISummaryWorker 在生成前把摘要与任务绑定),再调用本方法执行。
权限:同步执行没有 UI 承接权限确认,默认以「无人值守」方式执行——策略判定为 ask 的操作 直接放行,不会持久化 pending 权限请求、也不会把任务置为 waiting_permission 阻塞等待; 策略明确 deny 的操作仍会被拒绝。调用方可用 opts 覆盖(如 WithAutoApprove(false) 恢复人工确认)。
func (*AgentService) SaveMemory ¶
func (s *AgentService) SaveMemory(ctx context.Context, memory *Memory) error
SaveMemory 创建或更新一条记忆,并广播记忆事件(实时通知层)。
func (*AgentService) SaveProfile ¶
func (s *AgentService) SaveProfile(ctx context.Context, profile *Profile) error
SaveProfile 创建或更新 Profile(内置 Profile 也允许更新)。
func (*AgentService) SetTaskFinishedHandler ¶
func (s *AgentService) SetTaskFinishedHandler(h TaskFinishedHandler)
SetTaskFinishedHandler 注册任务终态回调(completed / failed / canceled)。
典型用法:ChatService 在构造时把自己的「轮次收尾」挂到这里,使会话锁释放、 最终回答落库不再依赖 Provider 是否补发 done / error 流事件。 回调在任务终态写库后、终态事件广播前同步执行;重复注册会覆盖前一个。
func (*AgentService) StartTask ¶
func (s *AgentService) StartTask(ctx context.Context, taskID int64) error
StartTask 启动已创建任务的异步执行。
func (*AgentService) Subscribe ¶
func (s *AgentService) Subscribe(taskID int64, handler EventHandler) func()
Subscribe 订阅任务事件(taskID 为 0 表示订阅全部任务),返回取消订阅函数。 供 WS / SSE 实时推送层使用;刷新后的历史请从 agent_messages 读取 (事件流仅用于实时广播,不再持久化)。
type ChatService ¶
type ChatService struct {
// contains filtered or unexported fields
}
ChatService 编排多轮对话:负责历史拼接 + 复用 AgentService 执行每一轮。
func NewChatService ¶
func NewChatService(agent *AgentService, sessions SessionService, messages MessageService, projects coreinterfaces.ProjectService) *ChatService
NewChatService 创建 ChatService。
构造时把自己的轮次收尾挂到 AgentService 的任务终态回调上:即使 Provider 没有 补发 done / error 事件,任务进入终态也会释放会话锁、写入兜底消息。
func (*ChatService) CreateTurn ¶
func (s *ChatService) CreateTurn(ctx context.Context, in TurnInput) (*Task, Session, error)
CreateTurn 准备一轮执行(不启动):
- 取得会话级锁(保证同一会话轮次串行);
- 加载 / 创建会话,追加 user 消息并持久化;
- 读取完整历史组装 Request(SessionID = 会话 ID),交给 AgentService.CreateTask;
- 登记本轮中间态,返回 task 供上层(handler)在启动前订阅 WS。
锁在本轮结束时由 finishTurn 释放。
func (*ChatService) GetSession ¶
func (s *ChatService) GetSession(ctx context.Context, id int64, q SessionMessageQuery) (SessionDetail, error)
GetSession 查询会话及一页历史消息(默认最新一页,按 Seq 升序)。
历史消息可能非常大(长回复 / 工具输出),因此不再一次返回全部: 首次只取最新一页,用户向上滚动时把上一页返回的 NextCursor 作为 BeforeSeq 继续加载。
func (*ChatService) GetTaskTimeline ¶
func (s *ChatService) GetTaskTimeline(ctx context.Context, taskID, userID int64) ([]Message, error)
GetTaskTimeline 按任务 ID 返回该任务的执行时间线(已落库的事实流)。
校验任务归属:只有任务所属用户才能读取自己的时间线,避免越权。
func (*ChatService) ListMessages ¶
func (s *ChatService) ListMessages(ctx context.Context, sessionID int64) ([]Message, error)
ListMessages 查询某个会话的全部历史消息(按 Seq 升序)。
func (*ChatService) ListSessions ¶
func (s *ChatService) ListSessions(ctx context.Context, userID int64, offset, limit int) ([]Session, int64, error)
ListSessions 分页查询会话。
会话按用户当前激活项目隔离:先用 GetActiveProjectByUserID 取出活动项目, 再按 ProjectID 过滤;userID 为 0 表示全部用户(不做项目过滤)。
func (*ChatService) ListTaskMessages ¶
func (s *ChatService) ListTaskMessages(ctx context.Context, sessionID, taskID int64) ([]Message, error)
ListTaskMessages 查询某个会话中属于指定任务的消息(按 Seq 升序)。
用于「一轮执行的事实流回填」:刷新 / 切换会话后,前端用该任务的已落库时间线 (reasoning / assistant / tool_call / tool_result / error)还原执行过程, 弥补增量事件(text / reasoning_delta)只广播不落库导致的空白。
状态源是 agent_messages;同一会话里其它轮次的消息不会返回。
type MemoryConfig ¶
type MemoryConfig struct {
Repo MemoryRepository
Extractor MemoryExtractor
}
MemoryConfig 是 MemoryManager 的依赖配置。
type MemoryManager ¶
type MemoryManager struct {
// contains filtered or unexported fields
}
MemoryManager 负责记忆的创建、查询、检索与提取编排。
它是 memory 域的「门面」:上层(AgentService / HTTP 层)只依赖 MemoryManager, 不直接感知 Repository / Retriever / Extractor 的具体实现。
func NewMemoryManager ¶
func NewMemoryManager(cfg MemoryConfig) *MemoryManager
NewMemoryManager 创建记忆管理器。
func (*MemoryManager) Delete ¶
func (m *MemoryManager) Delete(ctx context.Context, id int64) error
Delete 删除记忆。
func (*MemoryManager) Extract ¶
func (m *MemoryManager) Extract(ctx context.Context, turn MemoryTurn) ([]*Memory, error)
Extract 调用已配置的提取器,从一轮执行中提取待写入的记忆。 未配置提取器时返回空(不提取)。
func (*MemoryManager) Get ¶
func (m *MemoryManager) Get(ctx context.Context, id int64) (*Memory, error)
Get 按 ID 查询记忆。
func (*MemoryManager) List ¶
func (m *MemoryManager) List(ctx context.Context, userID int64, offset, limit int, kinds ...MemoryKind) ([]*Memory, int64, error)
List 分页查询某用户的记忆;kinds 为空表示全部类别。
func (*MemoryManager) Retrieve ¶
func (m *MemoryManager) Retrieve(ctx context.Context, q MemoryQuery) ([]*Memory, error)
Retrieve 按「业务上下文 + 关键词」检索记忆,并刷新这些记忆的访问时间(Touch)。
type PermissionManager ¶
type PermissionManager struct {
// contains filtered or unexported fields
}
PermissionManager 负责权限请求的创建、持久化与决策流转。
它是“数据库才是状态源”的核心实现:
- Create:把权限请求写入 Repository(pending),等待 UI 确认;
- Approve / Deny:更新 Repository 状态,并唤醒正在阻塞等待的 Agent;
- Wait:Agent 侧阻塞等待决策(内存 channel 只是运行时同步机制,真正状态在 Repository)。
注意:事件广播由上层 AgentService 负责(单一事件出口),PermissionManager 只维护状态。 Approve / Deny 与 Wait 通过互斥锁 + waiter channel 协同,避免“决策先于等待注册”的竞态。
func NewPermissionManager ¶
func NewPermissionManager(repo PermissionRepository) *PermissionManager
NewPermissionManager 创建权限管理器。
func (*PermissionManager) Approve ¶
func (m *PermissionManager) Approve(ctx context.Context, id int64, by int64) (*PermissionRequest, error)
Approve 批准权限请求,返回更新后的权限请求;并唤醒正在等待的 Agent。
func (*PermissionManager) Consume ¶
func (m *PermissionManager) Consume(ctx context.Context, id int64) error
Consume 把已批准的权限请求标记为 consumed(Agent 已据此恢复执行)。
恢复路径会在执行工具前显式调用它:先消费已批准的权限,再执行工具, 保证即使进程再次崩溃,重启后也能识别“该权限已经放行过”。
func (*PermissionManager) Create ¶
func (m *PermissionManager) Create(ctx context.Context, taskID int64, sessionID string, operation Operation) (*PermissionRequest, error)
Create 创建一个 pending 状态的权限请求并返回。
func (*PermissionManager) Deny ¶
func (m *PermissionManager) Deny(ctx context.Context, id int64, by int64) (*PermissionRequest, error)
Deny 拒绝权限请求,返回更新后的权限请求;并唤醒正在等待的 Agent。
func (*PermissionManager) FindByToolCall ¶
func (m *PermissionManager) FindByToolCall(ctx context.Context, taskID int64, toolCallID string) (*PermissionRequest, error)
FindByToolCall 返回某任务下由指定工具调用发起的权限请求(最新一条)。
恢复执行时,Agent 会重新执行未完成的工具调用;该方法让 Runtime 复用重启前 已经落库的权限决策(approved / denied),而不是再弹一次权限确认。 匹配依据是 Operation.Metadata["tool_call_id"](创建权限时由 Runtime 自动写入)。
func (*PermissionManager) Get ¶
func (m *PermissionManager) Get(ctx context.Context, id int64) (*PermissionRequest, error)
Get 按 ID 查询权限请求。
func (*PermissionManager) GetPending ¶
func (m *PermissionManager) GetPending(ctx context.Context, taskID int64) ([]*PermissionRequest, error)
GetPending 返回某任务当前全部待确认的权限请求。
type ProfileManager ¶
type ProfileManager struct {
// contains filtered or unexported fields
}
ProfileManager 负责 Profile 的解析、列表与增删改查编排。
func NewProfileManager ¶
func NewProfileManager(repo ProfileRepository) *ProfileManager
NewProfileManager 创建 Profile 管理器。
func (*ProfileManager) Delete ¶
func (m *ProfileManager) Delete(ctx context.Context, id int64) error
Delete 删除自定义 Profile;内置 Profile 不可删除。
func (*ProfileManager) Get ¶
func (m *ProfileManager) Get(ctx context.Context, id int64) (*Profile, error)
Get 按 ID 返回 Profile(全局可见)。
func (*ProfileManager) List ¶
func (m *ProfileManager) List(ctx context.Context) ([]*Profile, error)
List 返回全部 Profile(内置 + 自定义,按名称升序)。
type QuestionManager ¶
type QuestionManager struct {
// contains filtered or unexported fields
}
QuestionManager 负责用户提问(AskUser)的创建、持久化与作答流转。
与 PermissionManager 完全同构,是“数据库才是状态源”的又一实现:
- Create:把提问写入 Repository(pending),等待 UI 作答;
- Answer:更新 Repository 状态并唤醒正在阻塞等待的 Agent;
- Wait:Agent 侧阻塞等待作答(内存 channel 只做运行时同步,真正状态在 Repository)。
事件广播由上层 AgentService 负责(单一事件出口),QuestionManager 只维护状态。
func NewQuestionManager ¶
func NewQuestionManager(repo QuestionRepository) *QuestionManager
NewQuestionManager 创建提问管理器。
func (*QuestionManager) Answer ¶
func (m *QuestionManager) Answer(ctx context.Context, id int64, answer AskUserAnswer, by int64) (*QuestionRequest, error)
Answer 提交用户作答,返回更新后的提问请求。
支持「一题一页」的逐题作答:本次作答会合并进 q.Answer 作为暂存进度并持久化, 提问保持 pending;直到所有必答问题都有答案,才迁移到 answered 并唤醒 Agent。 因此用户可以在未全部答完时翻页、回看并修改之前的答案,界面刷新后进度也不丢失。
提交的问题 ID 必须属于本次提问(防止误把答案提交到别的提问)。
func (*QuestionManager) Cancel ¶
func (m *QuestionManager) Cancel(ctx context.Context, id int64) (*QuestionRequest, error)
Cancel 取消一个待作答的提问(任务被取消等);被取消的等待返回 ErrQuestionCanceled。
func (*QuestionManager) Create ¶
func (m *QuestionManager) Create(ctx context.Context, taskID int64, sessionID string, input AskUserInput) (*QuestionRequest, error)
Create 创建一个 pending 状态的提问请求并返回。
func (*QuestionManager) CreateForToolCall ¶
func (m *QuestionManager) CreateForToolCall(ctx context.Context, taskID int64, sessionID string, input AskUserInput, toolCallID string) (*QuestionRequest, error)
CreateForToolCall 创建提问请求,并记录发起它的工具调用 ID。
func (*QuestionManager) FindByToolCall ¶
func (m *QuestionManager) FindByToolCall(ctx context.Context, taskID int64, toolCallID string) (*QuestionRequest, error)
FindByToolCall 返回某任务下由指定工具调用发起的提问(最新一条)。
恢复执行时用于复用重启前的提问进度 / 已作答结果,避免重复弹出提问。
func (*QuestionManager) Get ¶
func (m *QuestionManager) Get(ctx context.Context, id int64) (*QuestionRequest, error)
Get 按 ID 查询提问请求。
func (*QuestionManager) GetPending ¶
func (m *QuestionManager) GetPending(ctx context.Context, taskID int64) ([]*QuestionRequest, error)
GetPending 返回某任务当前全部待作答的提问请求。
type RuntimeOption ¶
type RuntimeOption func(*taskRuntime)
RuntimeOption 定制 TaskRuntime 的行为(如权限是否自动放行)。
func WithAutoApprove ¶
func WithAutoApprove(auto bool) RuntimeOption
WithAutoApprove 开关「无人值守权限放行」。
开启后:策略判定为 ask(需人工确认)的操作直接按 allow 放行,既不持久化 pending 权限请求,也不把任务置为 waiting_permission;策略明确判定为 deny 的操作仍然拒绝。 关闭(默认)时完全保留原有行为:ask → 持久化 pending → 阻塞等待 UI 决策。
适用场景:没有 UI 承接权限确认的同步调用(RunTaskSync / RunTaskSyncByID)。
type ServiceConfig ¶
type ServiceConfig struct {
Client *agentclient.Client
Tasks TaskRepository
Perms *PermissionManager
Questions *QuestionManager
// Messages 是时间线消息(agent_messages)的持久化服务。
Messages MessageService
Bus EventBus
Policy PermissionPolicy
Memory *MemoryManager
Profiles *ProfileManager
Skills *skill.Registry
}
ServiceConfig 是 AgentService 的依赖配置。
type SessionDetail ¶
type SessionDetail struct {
Session
Messages []Message `json:"messages"`
// HasMore 表示是否还有更早的消息(前端向上滚动时继续加载)。
HasMore bool `json:"has_more"`
// NextCursor 是继续向上翻页的游标(本页最小 Seq);HasMore 为 false 时无意义。
NextCursor int `json:"next_cursor"`
}
SessionDetail 是会话详情:会话本体 + 一页历史消息 + 向上翻页游标。
type SessionMessageQuery ¶
type SessionMessageQuery struct {
// BeforeSeq 是向上翻页游标:只返回 Seq 小于该值的消息;0 表示取最新一页。
BeforeSeq int
// Limit 是本页最大条数;<=0 使用 DefaultSessionMessageLimit。
Limit int
}
SessionMessageQuery 是会话消息的分页参数。
type TimelineRecorder ¶
type TimelineRecorder struct {
// contains filtered or unexported fields
}
TimelineRecorder 把流式完整块事件即时投影并写入 agent_messages。
它是「事件 → 会话消息」的唯一写入入口,取代原先「轮末从 AgentEvent 回放事件」的做法:
- 轮内即时落库:Agent 循环中途退出(进程重启 / panic / 长时间等待权限)时, 已经产生的时间线不会丢,为后续恢复留下事实;
- 投影规则复用 ProjectTimelineEvent,与子 Agent 会话保持同一口径;
- 只有 EventStream 里可投影的完整块(reasoning / message / tool_call / tool_result)产生消息; 增量(text / reasoning_delta)与生命周期信号(turn_start / done / error ...)不落库;
- Message.Data 保留事件原始结构,后续可从消息表重建结构化工具调用;
- RecordResult 是「结果兜底」:Provider 没发完整 message 块时,用最终结果补一条 assistant_final,保证刷新 / 恢复后回答不会丢。
不属于本组件职责的两类消息(它们不是事件流的产物,由 ChatService 直接写入):
- user 消息:CreateTurn 在启动任务前写入;
- assistant 最终回答 / error:finishTurn 在轮末写入。
nil receiver 安全:未注入 MessageService 时所有方法都是空操作。
func NewTimelineRecorder ¶
func NewTimelineRecorder(messages MessageService) *TimelineRecorder
NewTimelineRecorder 创建时间线记录器;messages 为 nil 时返回 nil(等价于关闭落库)。
func (*TimelineRecorder) Forget ¶
func (r *TimelineRecorder) Forget(taskID int64)
Forget 在任务进入终态后清理 assistant 记录,避免长期运行下无限增长。
func (*TimelineRecorder) Record ¶
func (r *TimelineRecorder) Record(ctx context.Context, sessionID, taskID int64, payload any)
Record 把一条事件 payload 投影为会话消息并落库;不可投影时静默跳过。
这是 Emit 的热点路径:只处理完整块事件,增量事件在 publish 侧就不会进来; 落库失败只记 Warn,绝不影响 Agent 主流程。
func (*TimelineRecorder) RecordResult ¶
func (r *TimelineRecorder) RecordResult(ctx context.Context, sessionID, taskID int64, content string)
RecordResult 是「Provider 没有发出完整 message 块」时的兜底:用最终结果补一条 assistant_final,保证刷新 / 恢复后回答不会丢。
已经有 KindAssistant 块(Record 落过)时直接跳过,避免历史里出现两份相同文本。 必须在任务进入终态(Forget 清理缓存)之前调用。
type TurnInput ¶
type TurnInput struct {
UserID int64
SessionID int64 // 为 0 则创建新会话
// ProjectID 是新建会话所属项目(通常取当前用户激活项目),0 时由 ChatService 解析。
ProjectID int64
Message string // 本轮用户输入
Provider string
Model string
// RuntimeContext 是本轮调用的运行时上下文(业务类型 / 变量 / 工作目录)。
RuntimeContext *runtimecontext.RuntimeContext
ProfileID int64 // AgentProfile ID(为 0 取默认)
WorkingDir string
}
TurnInput 是一次「轮次」的输入,由 HTTP 层构造。