mirror of
https://github.com/RuoJi6/ARTEX.git
synced 2026-10-27 17:03:49 +08:00
feat(agent): add_hint 触发的规划轮显式播报「本轮由新增 hint 触发」
此前 add_hint 走裸 notify,触发的规划轮 triggers 为空,开场白说"无具体变动", hint 只折在 graph_overview 里让 planner 自己发现。仿照 set_goals/NotifyGoal, 让 add_hint 记一条 hint 触发事件并带上提示文本。 - TriggerEvent 增加 Hints 字段;renderTriggers 增加 hint 分支,播报「人新增了 N 条战略提示:…」 - ToolSet 增加 notifyHint 回调 + SetNotifyHint;add_hint 整批写完统一唤醒一次 (优先 notifyHint,未接则退回裸 notify),notify 从 addOneHint 上移到 handler - 主 agent(server.go) 与跨任务编排(orchestration.go) 均接上 Task.NotifyHint
This commit is contained in:
1 parent
0b9341ac8b
commit
edbc296e29
6 files changed
+55
-9
No files matched your search
+3
-2
@@ -102,7 +102,7 @@ func mainAgentSystem(goal, dataDir, workDir string) string {
|
||||
// non-nil, receives each execution step (thinking / tool_use / tool_result /
|
||||
// text / result) so the main-agent session shows its work — exactly like the
|
||||
// worker/planner sessions — not just the final answer.
|
||||
func (m *MainAgent) Chat(ctx context.Context, taskID int64, mainSeg int, as *db.AssetStore, ts *db.ExplorationStore, goal, message string, emit func(db.Activity), notify, resume func(), notifyGoal func([]string)) (string, error) {
|
||||
func (m *MainAgent) Chat(ctx context.Context, taskID int64, mainSeg int, as *db.AssetStore, ts *db.ExplorationStore, goal, message string, emit func(db.Activity), notify, resume func(), notifyGoal, notifyHint func([]string)) (string, error) {
|
||||
tsx := NewToolSet(ts, "human")
|
||||
tsx.SetFindingRecorder(m.findingRecorder)
|
||||
if as != nil {
|
||||
@@ -110,9 +110,10 @@ func (m *MainAgent) Chat(ctx context.Context, taskID int64, mainSeg int, as *db.
|
||||
}
|
||||
tsx.SetTaskID(taskID)
|
||||
tsx.SetCoverageEnabled(as == nil || as.CoverageEnabled(taskID))
|
||||
tsx.SetNotify(notify) // add_hint wakes this task's planner (debounced)
|
||||
tsx.SetNotify(notify) // 通用唤醒(无专用回调的写操作走它,debounced)
|
||||
tsx.SetResumeTask(resume) // set_goals 新增目标 → 把已完成/暂停的任务拉回 running
|
||||
tsx.SetNotifyGoal(notifyGoal) // set_goals 新增目标 → 给 planner 记一条「人新增了目标:…」触发
|
||||
tsx.SetNotifyHint(notifyHint) // add_hint 新增提示 → 给 planner 记一条「人新增了 N 条战略提示:…」触发
|
||||
tsx.steerWork = m.steerWork // enable steer_work tool (nil = unavailable)
|
||||
// 领域工具 + 基础默认工具集(Read/Write/Edit/MultiEdit/LS/Glob/Grep/Bash)
|
||||
// 资产覆盖度功能关闭时剔除 add_task_scope/list_untested_assets(不入 prompt)。
|
||||
|
||||
@@ -155,6 +155,7 @@ type TriggerEvent struct {
|
||||
Goals []string // Kind=="goal" 专用:本次 set_goals 新增的目标文本(1 条或多条)
|
||||
OldGoal string // Kind=="goal_edited" 专用:修改前的目标文本
|
||||
NewGoal string // Kind=="goal_edited" 专用:修改后的目标文本
|
||||
Hints []string // Kind=="hint" 专用:本次 add_hint 新增的提示文本(1 条或多条)
|
||||
}
|
||||
|
||||
// renderTriggers spells out the change(s) that fired this round: for a finished
|
||||
@@ -175,6 +176,12 @@ func renderTriggers(ts *db.ExplorationStore, evs []TriggerEvent) string {
|
||||
} else {
|
||||
b.WriteString(fmt.Sprintf("\n- 人(主 agent)新增了 %d 个目标:%s —— 均为新的待达成目标,请逐一为尚无对应意图的目标补充探索方向。", len(ev.Goals), strings.Join(ev.Goals, ";")))
|
||||
}
|
||||
case "hint":
|
||||
if len(ev.Hints) == 1 {
|
||||
b.WriteString(fmt.Sprintf("\n- 人(主 agent)新增了一条战略提示:%s —— 已挂到探索图上,请据此调整/补充探索方向(若尚无对应意图)。", ev.Hints[0]))
|
||||
} else {
|
||||
b.WriteString(fmt.Sprintf("\n- 人(主 agent)新增了 %d 条战略提示:%s —— 均已挂到探索图上,请逐一据此调整/补充探索方向。", len(ev.Hints), strings.Join(ev.Hints, ";")))
|
||||
}
|
||||
case "goal_deleted":
|
||||
b.WriteString(fmt.Sprintf("\n- 人删除了该目标:%s —— 该目标已移除,请据此重判剩余目标/方向(不必再为它派意图)。", ev.Detail))
|
||||
case "goal_edited":
|
||||
|
||||
+27
-5
@@ -123,12 +123,23 @@ type ToolSet struct {
|
||||
// goals decomposer (round-0 has no running planner to inform) and workers → those
|
||||
// fall back to the bare notify.
|
||||
notifyGoal func(texts []string)
|
||||
// notifyHint, if set, wakes the planner AND records ONE "人新增了 N 条战略提示:…"
|
||||
// trigger for a whole add_hint call (batch-aware — one call, one trigger) so the next
|
||||
// round is told the round was fired by a new hint and spells the hint out, instead of
|
||||
// the planner having to spot it folded into the graph overview. Wired for the main
|
||||
// agent + cross-task orchestration; nil elsewhere → falls back to the bare notify.
|
||||
notifyHint func(texts []string)
|
||||
}
|
||||
|
||||
// SetNotifyGoal wires the goal-add trigger callback (see ToolSet.notifyGoal). Set only
|
||||
// by the main-agent chat, so runtime-added goals are announced to the planner by name.
|
||||
func (t *ToolSet) SetNotifyGoal(fn func([]string)) { t.notifyGoal = fn }
|
||||
|
||||
// SetNotifyHint wires the hint-add trigger callback (see ToolSet.notifyHint). Set by
|
||||
// the main-agent chat and cross-task orchestration, so a runtime-added hint fires a
|
||||
// planner round announced by name instead of a bare wake.
|
||||
func (t *ToolSet) SetNotifyHint(fn func([]string)) { t.notifyHint = fn }
|
||||
|
||||
// SetResumeTask wires the task-revive callback (see ToolSet.resumeTask). Set only by
|
||||
// the main-agent chat, so runtime-added goals can pull a finished task back to running.
|
||||
func (t *ToolSet) SetResumeTask(fn func()) { t.resumeTask = fn }
|
||||
@@ -1540,11 +1551,9 @@ func (t *ToolSet) addOneHint(it hintItem) (int64, error) {
|
||||
if len(refs) > 0 {
|
||||
payload["traffic_refs"] = refs
|
||||
}
|
||||
id, err := t.ts.AddNode(db.KindHint, payload, 0, "active", "human", anchors)
|
||||
if err == nil && t.notify != nil {
|
||||
t.notify() // wake the planner so the new hint is read promptly (debounced)
|
||||
}
|
||||
return id, err
|
||||
// 唤醒 planner 不在此处逐条做——由 addHint 在整批写完后统一触发一次(带上提示文本),
|
||||
// 避免一次 add_hint 多条提示逐条刷屏 planner 的触发行。
|
||||
return t.ts.AddNode(db.KindHint, payload, 0, "active", "human", anchors)
|
||||
}
|
||||
|
||||
type goalItem struct {
|
||||
@@ -1745,6 +1754,7 @@ func (t *ToolSet) addHint() actool.CoreTool {
|
||||
|
||||
ids := make([]int64, len(items))
|
||||
errs := map[string]string{}
|
||||
var addedTexts []string
|
||||
for i, it := range items {
|
||||
id, err := t.addOneHint(it)
|
||||
if err != nil {
|
||||
@@ -1752,6 +1762,18 @@ func (t *ToolSet) addHint() actool.CoreTool {
|
||||
continue
|
||||
}
|
||||
ids[i] = id
|
||||
addedTexts = append(addedTexts, strings.TrimSpace(it.Text))
|
||||
}
|
||||
if len(addedTexts) > 0 {
|
||||
// 唤醒 planner(整批一次)。优先 notifyHint:一次 add_hint 记一条「人新增了
|
||||
// N 条战略提示:…」触发,让 planner 明确"本轮由新增 hint 触发"并看到提示内容;
|
||||
// 未接该回调时退回纯 notify(bare wake,hint 仍折在图里供其自行读取)。
|
||||
switch {
|
||||
case t.notifyHint != nil:
|
||||
t.notifyHint(addedTexts)
|
||||
case t.notify != nil:
|
||||
t.notify()
|
||||
}
|
||||
}
|
||||
|
||||
if !batch { // 单条:保持原返回
|
||||
|
||||
@@ -1776,6 +1776,21 @@ func (t *Task) NotifyGoal(texts []string) {
|
||||
t.Notify()
|
||||
}
|
||||
|
||||
// NotifyHint records that one OR MORE hints were added in a single add_hint call —
|
||||
// by the human via the main agent, or by cross-task orchestration — then wakes the
|
||||
// planner, so the next round is told "人新增了 N 条战略提示:…" and looks at them
|
||||
// directly instead of having to spot the new hint folded into the graph overview.
|
||||
// One call → one trigger event (a batched add_hint counts as one, not one per hint).
|
||||
func (t *Task) NotifyHint(texts []string) {
|
||||
if len(texts) == 0 {
|
||||
return
|
||||
}
|
||||
t.trigMu.Lock()
|
||||
t.pendingTriggers = append(t.pendingTriggers, agent.TriggerEvent{Kind: "hint", Hints: texts})
|
||||
t.trigMu.Unlock()
|
||||
t.Notify()
|
||||
}
|
||||
|
||||
// NotifyGoalDeleted records that the human deleted a goal (via 总览的目标管理), then
|
||||
// wakes the planner so the next round spells out which goal was removed. The event
|
||||
// survives an early-returning terminal round (drain happens after the gate).
|
||||
|
||||
@@ -166,7 +166,8 @@ func (s *Server) delegateToTask(ctx context.Context, in json.RawMessage, pick fu
|
||||
if s.m.Assets() != nil {
|
||||
tsx.SetAssetStore(s.m.Assets(), s.m.Assets().Companies())
|
||||
}
|
||||
tsx.SetNotify(t.Notify) // hint writes wake this task's planner (no-op for read tools)
|
||||
tsx.SetNotify(t.Notify) // 通用唤醒(无专用回调的写操作走它;读工具为 no-op)
|
||||
tsx.SetNotifyHint(t.NotifyHint) // add_hint → 记一条「人新增了 N 条战略提示:…」触发并唤醒 planner
|
||||
return pick(tsx).Call(ctx, inner, nil)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -3571,7 +3571,7 @@ func (s *Server) chat(w http.ResponseWriter, r *http.Request) {
|
||||
taskDir := filepath.Join(s.m.dir, "tasks", t.ID)
|
||||
agentMsg := composeAgentMessage(req.Message, req.Attachments, taskDir)
|
||||
s.engine.BeginLLMCall(t.ID)
|
||||
_, err := ma.Chat(ctx, maTaskID, mainSeg, s.m.Assets(), t.Store, t.Goal, agentMsg, emit, t.Notify, resume, t.NotifyGoal)
|
||||
_, err := ma.Chat(ctx, maTaskID, mainSeg, s.m.Assets(), t.Store, t.Goal, agentMsg, emit, t.Notify, resume, t.NotifyGoal, t.NotifyHint)
|
||||
s.engine.EndLLMCall(t.ID)
|
||||
if err != nil && ctx.Err() == nil {
|
||||
s.engine.emitActivity(t, db.Activity{Worker: "mainagent", Kind: "text", IsError: true, Summary: "(主 Agent 出错:" + err.Error() + ")", MainSeg: segPtr})
|
||||
|
||||
Reference in new issue
Block a user