Version: 0.9.45.dev.260427

后端:
1. execute 主链路重构为“上下文工具域 + 主动优化候选闭环”——移除 order_guard,粗排后默认进入主动微调,先诊断再从后端候选中选择 move/swap,避免 LLM 自由全局乱搜
2. 工具体系升级为动态注入协议——新增 context_tools_add / remove、工具域与二级包映射、主动优化白名单;schedule / taskclass / web 工具按域按包暴露,msg0 规则包与 execute 上下文同步重写
3. analyze_health 升级为主动优化唯一裁判入口——补齐 rhythm / tightness / profile / feasibility 指标、候选扫描与复诊打分、停滞信号、forced imperfection 判定,并把连续优化状态写回运行态
4. 任务类能力并入新 Agent 执行链——新增 upsert_task_class 写工具与启动注入事务写入;任务类模型补充学科画像与整天屏蔽配置,粗排支持 excluded_days_of_week,steady 策略改为基于目标位置/单日负载/分散度/缓冲的候选打分
5. 运行态与路由补齐优化模式语义——新增 active tool domain/packs、pending context hook、active optimize only、taskclass 写入回盘快照;区分 first_full / global_reopt / local_adjust,并完善首次粗排后默认 refine 的判定

前端:
6. 助手时间线渲染细化——推理内容改为独立 reasoning block,支持与工具/状态/正文按时序交错展示,自动收口折叠,修正 confirm reject 恢复动作

仓库:
7. newAgent 文档整体迁入 docs/backend,补充主动优化执行规划与顺序约束拆解文档,删除旧调试日志文件

PS:这次科研了2天,总算是有些进展了——LLM永远只适合做选择题、判断题,不适合做开放创新题。
This commit is contained in:
Losita
2026-04-27 01:09:37 +08:00
parent 04b5836b39
commit 66c06eed0a
60 changed files with 9163 additions and 1819 deletions

View File

@@ -0,0 +1,674 @@
# NewAgent 架构全景
> 本文档帮助读者建立对 newAgent 的完整心智模型,从宏观到微观逐层展开。
---
## 一、一句话概括
newAgent 是一个 **状态机驱动的有向图**:用户消息进入 Chat 节点,经过意图分类、计划生成、用户确认、工具执行、最终交付,每一步由 Phase 和 PendingInteraction 驱动路由。
---
## 二、宏观架构
### 2.1 目录结构
```
newAgent/
├── graph/ 图骨架:节点注册、边连线、分支路由
├── model/ 数据模型:状态、合约、接口定义
├── node/ 节点实现:每个节点的业务逻辑
├── prompt/ 提示词:每个阶段的 system prompt 和用户 prompt 构造
├── llm/ LLM 客户端文本生成、JSON 解析、流式适配
├── stream/ SSE 输出伪流式推送、OpenAI 兼容格式
├── tools/ 工具层10 个排程工具 + 注册表
├── shared/ 公共工具:重试、时区
├── router/ 路由(当前为空,路由逻辑在 graph/ 中)
├── ROADMAP.md 改造计划
└── ARCHITECTURE.md 本文档
```
### 2.2 图结构
```
START
v
Chat ──────┬── 意图=chat ────→ END直接回复
│ │
│ └── 意图=task ──→ Plan ──┬── continue ──→ Plan继续规划
│ │ │
│ │ ├── ask_user ──→ Interrupt ──→ END
│ │ │
│ │ └── plan_done ──→ Confirm
│ │ │
│ │ ├── 有计划 → 确认卡片
│ │ │ │
│ │ │ v
│ │ │ Interrupt ──→ END
│ │ │
│ │ └── 有 PendingConfirmTool → 确认卡片
│ │
│ v
│ Interrupt ──→ END
│ [用户确认后重新进入图]
v
Chat(resume) ──┬── accept → 恢复 PendingConfirmTool → Execute
└── reject → 回到 planning 或 executing
Execute ──┬── continue(读工具) ──→ Execute继续 ReAct
├── continue(无工具) ──→ Execute
├── confirm(写工具) ──→ Confirm ──→ Interrupt ──→ END
├── ask_user ──→ Interrupt ──→ END
├── next_plan ──┬── 有剩余步骤 → Execute
│ └── 无剩余步骤 → Deliver
├── done ──→ Deliver
└── 轮次耗尽 ──→ Deliver强制
Deliver ──→ END最终总结清理持久化快照
```
### 2.3 一次完整排课的请求序列
```
请求1: 用户发 "帮我安排下周的复习"
→ Chat(intent=task) → Plan(plan_done, 2步计划) → Confirm → Interrupt → END
前端展示确认卡片
请求2: 用户点 "确认"
→ Chat(resume, accept) → 确认通过 → Execute(读工具 get_overview)
→ Execute(读工具 find_free)
→ Execute(写工具 place → confirm) → Confirm → Interrupt → END
前端展示写操作确认卡片
请求3: 用户点 "确认"
→ Chat(resume, accept) → Execute(执行 pending tool place) → 持久化
→ Execute(next_plan → 下一步)
→ Execute(done) → Deliver → END
前端展示最终总结
```
---
## 三、状态模型
理解 newAgent 的关键在于理解 **什么东西在什么时机变化**
### 3.1 三个核心状态对象
```
┌─────────────────────┐ 持久化到 Redis
│ AgentRuntimeState │ ← StateStore.Save/Load
│ │
│ ┌───────────────┐ │
│ │ CommonState │ │ ← 每个节点都可能修改
│ │ - Phase │ │
│ │ - PlanSteps │ │
│ │ - CurrentStep │ │
│ │ - RoundUsed │ │
│ └───────────────┘ │
│ │
│ PendingInteraction │ ← 确认/追问 的交互快照
│ PendingConfirmTool │ ← Execute→Confirm 的临时邮箱
└─────────────────────┘
┌─────────────────────────┐ 不持久化,每次请求重建
│ ConversationContext │
│ - SystemPrompt │ ← 各节点 prompt 函数构造
│ - History []*Message │ ← 对话历史assistant+tool 配对)
│ - PinnedBlocks │ ← 置顶上下文(计划、工具摘要)
│ - ToolSchemas │ ← 工具 schema 注入
└─────────────────────────┘
┌─────────────────────────┐ 懒加载,首次 Execute 时读取
│ ScheduleState │
│ - Window (天数+映射) │
│ - Tasks []ScheduleTask │ ← 工具操作的数据源
└─────────────────────────┘
```
### 3.2 Phase 状态转换
```
PhasePlanning Plan 节点 plan_done + 用户确认后
v
PhaseExecuting Execute 节点执行中
├──→ PhaseWaitingConfirm Execute 输出 action=confirm
│ │
│ v 用户确认
│ PhaseExecuting 恢复继续执行
├──→ PhaseDone Execute 输出 done 或所有步骤完成
└──→ PhaseInterrupted 被中断(追问/确认等待用户输入)
```
### 3.3 PendingInteraction 生命周期
```
场景 A: 计划确认
Plan → plan_done → Confirm 节点 → OpenConfirmInteraction(type="confirm")
→ Interrupt 展示 → END
→ 用户确认 → Chat(resume) → ResumeFromPending() → Phase=executing
场景 B: 写操作确认
Execute → action=confirm → 设置 PendingConfirmTool → Confirm 节点
→ OpenConfirmInteraction(type="confirm", PendingTool=快照)
→ Interrupt 展示 → END
→ 用户确认 → Chat(resume) → PendingConfirmTool 从快照恢复 → Execute 执行工具
场景 C: 追问
Execute → action=ask_user → OpenAskUserInteraction(question)
→ Interrupt 展示 → END
→ 用户回复 → Chat(resume) → Phase 回到 executing
```
### 3.4 PendingConfirmTool 临时邮箱
这个字段 **不持久化**,只在单次图运行中存在:
```
Execute(action=confirm)
→ PendingConfirmTool = {ToolName, ArgsJSON, Summary}
→ Phase = waiting_confirm
→ Confirm 节点读取 → 转入 PendingInteraction.PendingTool
→ PendingConfirmTool 被清空
用户确认后重新进入图:
→ Chat(resume) → 从 PendingInteraction.PendingTool 恢复到 PendingConfirmTool
→ Execute 发现 PendingConfirmTool 非空 → 直接执行工具 → 清空
```
---
## 四、各节点详解
### 4.1 Chat 节点 (`node/chat.go`)
**职责**:入口分流 + 中断恢复
**两条路径**
1. **首次进入**:调 LLM 做意图分类("chat" / "task"chat 直接回复task 转到 Plan
2. **中断恢复**:读取 PendingInteraction根据类型ask_user / confirm走不同恢复路径
**关键逻辑**
```
if HasPendingInteraction():
handleChatResume() // 不调 LLM
else:
chatIntentDecision() // 调 LLM 做意图分类
```
**confirm resume 的 accept/reject 处理**
- accept从 PendingInteraction.PendingTool 恢复 PendingConfirmToolPhase=executing
- reject有 PendingTool不恢复 PendingConfirmToolPhase=executingLLM 换方案)
- reject无 PendingTool调用 RejectPlan()Phase=planning回到规划
### 4.2 Plan 节点 (`node/plan.go`)
**职责**LLM 生成结构化计划
**两阶段 LLM 调用**
1. **Phase 1 快速评估**temperature=0.2, max_tokens=1600, thinking=关闭
- 输出 PlanDecision判断 Complexity(simple/moderate/complex)
2. **Phase 2 深度规划**(仅 complex 任务触发thinking=开启, max_tokens=3200
- 生成更详细的 PlanStep 列表(含 DoneWhen 完成判定条件)
**三种 action**
- `continue`:继续规划(多轮对话中补充信息)
- `ask_user`:追问用户
- `plan_done`:规划完成,输出 PlanSteps
**计划写入 PinnedBlocks**:用 `UpsertPinnedBlock` 把计划文本注入 ConversationContext后续 Execute 阶段自动带入。
### 4.3 Confirm 节点 (`node/confirm.go`)
**职责**:创建确认卡片,不调 LLM
**两种确认**
1. **计划确认**Phase=waiting_confirm, PendingConfirmTool 为空):格式化计划摘要,创建 PendingInteraction
2. **工具确认**PendingConfirmTool 非空):格式化工具操作摘要,把 PendingTool 快照转入 PendingInteraction
**关键**Confirm 节点执行后PendingConfirmTool 被清空(数据已转移到 PendingInteraction.PendingTool
### 4.4 Execute 节点 (`node/execute.go`)
**职责**LLM 主导的 ReAct 循环,这是最复杂的节点。
**入口判断优先级**
```
1. PendingConfirmTool 非空 → executePendingTool() → 结束
2. 无有效 PlanStep → 报错
3. 正常 ReAct → 调 LLM → 处理决策
```
**LLM 调用参数**temperature=0.3, max_tokens=1200, thinking=开启
**JSON 解析失败处理**correction 机制):
```
LLM 输出非 JSON:
→ ConsecutiveCorrections++
→ 追加修正消息到历史
→ return nil图循环回来LLM 看到修正消息后重试)
→ 连续 3 次失败 → 返回硬错误,终止
```
**五种 action 处理**
| action | 行为 | 工具执行? |
|--------|------|-----------|
| continue + tool_call | 读工具直接执行 | 是executeToolCall() |
| continue 无 tool | 仅说话,继续循环 | 否 |
| confirm | 暂存 PendingConfirmTool | 否,等用户确认 |
| ask_user | 打开追问 | 否 |
| next_plan | 推进步骤 | 否 |
| done | 结束所有步骤 | 否 |
**工具执行后历史消息格式**
```
assistant message: {Role: "assistant", ToolCalls: [{ID, Function: {Name, Arguments}}]}
tool message: {Role: "tool", ToolCallID: <匹配ID>, Content: "工具结果"}
```
这对消息必须配对,否则 OpenAI 兼容 API 会拒绝请求。
**轮次预算**MaxRounds 默认 30耗尽强制进入 Deliver。
### 4.5 Interrupt 节点 (`node/interrupt.go`)
**职责**:向用户展示消息后暂停图执行
**三种类型**
- ask_user伪流式展示 DisplayText
- confirm展示确认状态
- 默认:展示通用中断信息
### 4.6 Deliver 节点 (`node/deliver.go`)
**职责**:生成最终总结
- 调 LLMtemperature=0.5, max_tokens=800生成总结
- 失败时降级到机械格式化(逐条列出步骤 + 完成标记)
- 完成后调用 deleteAgentState() 清理 Redis 快照
---
## 五、LLM 交互模式
### 5.1 统一 JSON 协议
所有 LLM 输出都是严格 JSON不是纯文本。每个阶段有自己的合约
**Plan 合约** (`model/plan_contract.go`)
```json
{
"speak": "...",
"action": "continue|ask_user|plan_done",
"reason": "...",
"complexity": "simple|moderate|complex",
"need_thinking": false,
"plan_steps": [{"content": "...", "done_when": "..."}]
}
```
**Execute 合约** (`model/execute_contract.go`)
```json
{
"speak": "...",
"action": "continue|ask_user|confirm|next_plan|done",
"reason": "...",
"goal_check": "next_plan/done 时必填)",
"tool_call": {"name": "工具名", "arguments": {...}}
}
```
### 5.2 JSON 解析容错
`llm/json.go``ParseJSONObject` 能处理:
- LLM 在 JSON 前后附带文字 → 提取中间的 JSON 对象
- Markdown 代码块包裹(```json ... ```)→ 剥离
- 嵌套对象(大括号配对计数)
### 5.3 Correction 循环
当 LLM 输出非法 JSON 时:
```
1. 原始输出作为 assistant 消息追加到历史
2. 修正提示作为 user 消息追加到历史
3. return nil → 图循环回来
4. LLM 看到修正消息,下一轮输出合法 JSON
5. ConsecutiveCorrections 重置为 0
6. 连续 3 次失败 → 硬错误终止
```
---
## 六、工具系统
### 6.1 数据模型
`ScheduleState` 是工具操作的唯一数据源:
```
ScheduleState
├── Window 时间窗口
│ ├── TotalDays 总天数(如 5 或 7
│ └── DayMapping[] day_index → (week, day_of_week) 映射
└── Tasks[] 扁平任务列表
├── source="event" 来自日程表的已有课程/任务
│ ├── Slots[] 压缩的时段范围
│ ├── CanEmbed 是否允许嵌入
│ └── Locked 是否锁定(不可移动)
└── source="task_item" 来自任务类的待安排任务
├── Duration 需要的连续时段数
├── CategoryID 所属 TaskClass.ID
└── Status="pending" 待安排
```
### 6.2 10 个工具
**读工具(直接执行,不需要确认)**
| 工具 | 用途 | 典型调用时机 |
|------|------|------------|
| `get_overview` | 全局概览:天数、占用统计、可嵌入、待安排 | LLM 需要了解全局 |
| `query_range` | 查询指定天/时段的详情 | LLM 需要具体位置信息 |
| `find_free` | 查找连续空闲时段 | LLM 需要找空位放任务 |
| `list_tasks` | 按条件列出任务 | LLM 需要筛选任务 |
| `get_task_info` | 单个任务详情(含嵌入关系) | LLM 需要具体任务信息 |
**写工具(需用户确认)**
| 工具 | 用途 | 关键逻辑 |
|------|------|---------|
| `place` | 放置 pending 任务到时段 | 自动检测嵌入CanEmbed=true 的宿主) |
| `move` | 移动已有任务到新位置 | 冲突检测(排除自身) |
| `swap` | 交换两个等时长任务的时段 | 冲突时自动回滚 |
| `batch_move` | 批量移动多个任务 | 原子性:任一冲突全部回滚 |
| `unplace` | 取消放置,恢复 pending | 清理双向嵌入关系 |
### 6.3 工具执行流程
**读工具**action=continue + tool_call
```
Execute → executeToolCall() → registry.Execute() → 追加 assistant+tool 消息对 → return nil → 图循环
```
**写工具**action=confirm
```
Execute → handleExecuteActionConfirm() → 暂存 PendingConfirmTool
→ Confirm 节点 → Interrupt → 用户确认
→ Chat(resume) → 恢复 PendingConfirmTool
→ Execute → executePendingTool() → registry.Execute() + persistor + 追加消息对
```
### 6.4 持久化路径
```
工具执行成功
→ DiffScheduleState(original, modified) → []ScheduleChange
→ PersistScheduleChanges(事务)
→ applyPlaceChange / applyMoveChange / applyUnplaceChange
```
**当前限制**`applyPlaceChange` 只处理 `source="event"``source="task_item"` 会报错。详见 ROADMAP.md P0 缺口。
---
## 七、SSE 输出系统
### 7.1 ChunkEmitter
所有节点通过 `ChunkEmitter` 向前端推送事件:
```
EmitPseudoAssistantText() → 伪流式文本(分段推送,模拟打字效果)
EmitStatus() → 状态推送("正在执行第2步"
EmitConfirmRequest() → 确认卡片
EmitFinish() / EmitDone() → 结束标记
```
### 7.2 伪流式
LLM 的一次性文本输出通过 `SplitPseudoStreamText` 拆分成多个 chunk
- 按中英文标点断句
- 每个 chunk 8~24 个字符
- 间隔 40ms 推送
### 7.3 OpenAI 兼容格式
`stream/openai.go` 定义了 OpenAI 兼容的 SSE 格式,通过 `ext` 字段扩展:
- `reasoning_text`:思考过程
- `assistant_text`:正文
- `status`:状态更新
- `tool_call` / `tool_result`:工具调用
- `confirm_request`:确认卡片
- `interrupt`:中断消息
---
## 八、持久化模型
### 8.1 三个持久化层次
| 层级 | 机制 | 何时触发 | 存什么 |
|------|------|---------|--------|
| 快照 | AgentStateStore (Redis) | Plan/Confirm/Execute 节点后 | AgentRuntimeState + ConversationContext |
| 变更 | SchedulePersistor (MySQL) | 写工具执行后 | ScheduleState 的 diff |
| 历史 | Redis + MySQL | 图运行完成后 | 完整对话历史 |
### 8.2 快照恢复流程
```
用户发送新消息(图需要从中断恢复)
→ loadOrCreateRuntimeState()
→ StateStore.Load(conversationID)
→ 如果存在:恢复 RuntimeState + ConversationContext
→ 如果不存在:创建全新状态
```
快照在 Deliver 后被 `deleteAgentState()` 清理。
---
## 九、Prompt 体系
### 9.1 prompt 构造模式
所有阶段现在统一共享 `buildUnifiedStageMessages()` 函数:
```
msg0(system) = 全局 system prompt + 阶段 system prompt + 工具简表
msg1(assistant) = 对话历史 + 归档摘要
msg2(assistant) = 阶段工作区
msg3(system) = 阶段状态 + 记忆 + 本轮指令
```
统一构造由 `StageMessagesConfig` 驱动,具体阶段只负责填充各自的 `Msg2Content``Msg3StageState``UserInstruction`
### 9.2 各阶段 prompt 要点
| 阶段 | 核心指令 | 关键约束 |
|------|---------|---------|
| Chat | 分类意图chat vs task | 保守默认为 task |
| Plan | 两阶段:快速评估 + 深度规划 | 简单任务不开启 thinking |
| Execute | ReAct思考→执行→观察 | goal_check 为 next_plan/done 必填 |
| Deliver | 总结计划执行结果 | 失败降级到机械格式化 |
### 9.3 置顶上下文块
```
PinnedBlocks 是跨节点共享的上下文,通过 Key 去重:
execution_context ← Execute 节点注入(当前步骤、完成判定等)
plan ← Plan 节点注入(完整计划文本)
tool_summary ← Execute 节点注入(可用工具摘要)
```
---
## 十、图路由逻辑 (`graph/common_graph.go`)
路由函数是图的核心控制逻辑,决定了每步之后走向哪个节点:
### branchAfterChat
```
if PendingInteraction → Interrupt
else switch Phase:
planning → Plan
executing → Execute
done → Deliver
chatting → END
```
### branchAfterPlan
```
if PendingInteraction → Interrupt
else switch Phase:
waiting_confirm → Confirm
planning → Plancontinue继续规划
executing → Execute不应该发生但防御性路由
```
### branchAfterConfirm
```
if PendingInteraction → Interrupt
else → Execute确认通过
```
### branchAfterExecute
```
if PendingInteraction → Interrupt
else switch Phase:
executing → Execute继续循环
done → Deliver
waiting_confirm → Confirm不应该发生防御性路由
```
### 关键保护机制
所有分支函数都以 `branchIfInterrupted()` 开头:
```go
func branchIfInterrupted(st *AgentGraphState) string {
if st.RuntimeState.HasPendingInteraction() {
return "interrupt"
}
return ""
}
```
这确保任何节点设置了 PendingInteraction 后,图都会走向 Interrupt 节点展示给用户。
---
## 十一、Service 集成层 (`service/agentsvc/agent_newagent.go`)
### 入口函数runNewAgentGraph
```
1. 规范化 conversationID, modelName
2. 确保会话存在Redis 缓存 → DB
3. 构建重试元数据
4. 加载或创建 RuntimeState从 Redis 快照恢复)
5. 构建 AgentGraphRequestConfirmAction 从 extra 取)
6. 包装 Ark 客户端
7. 创建 SSE 适配器 + ChunkEmitter
8. 组装 AgentGraphDeps注入所有依赖
9. 调用 RunAgentGraph()
10. 持久化对话历史到 Redis + MySQL
11. 发送 [DONE] 标记,触发异步标题生成
```
### 依赖注入
```
cmd/start.go:
→ NewScheduleProvider(scheduleDAO, taskClassDAO) → SetScheduleProvider()
→ NewSchedulePersistorAdapter(repoManager) → SetSchedulePersistor()
→ NewDefaultRegistry() → SetToolRegistry()
→ NewRedisStateStore(cacheDAO) → SetAgentStateStore()
```
---
## 十二、如何调试
### 12.1 日志关键字
| 搜索关键字 | 含义 |
|-----------|------|
| `[DEBUG] execute LLM` | Execute 节点的 LLM 原始输出和解析结果 |
| `[DEBUG] plan LLM` | Plan 节点的 LLM 输出 |
| `[WARN] execute 决策不合法` | LLM 输出合法 JSON 但 action 不合法 |
| `[DEBUG] execute LLM 输出解析失败` | JSON 解析失败,触发 correction |
| `PersistScheduleChanges` | 持久化调用 |
| `loadOrCreateRuntimeState` | 状态恢复/创建 |
### 12.2 常见问题排查
**SSE 断开**
1. 检查 `[DEBUG] execute LLM` 日志,看 LLM 输出是否为合法 JSON
2. 如果输出 `[NEXT_PLAN]` 等纯文本 → prompt 问题(已修复,参考 execute.go 的 correction 机制)
3. 如果输出合法 JSON 但 action 不对 → 检查 prompt 的合约文本
**工具不执行**
1. 检查 PendingConfirmTool 是否被正确设置和恢复
2. 检查 ScheduleState 是否为 nil可能 ScheduleProvider 未注入)
3. 检查 history 中 assistant+tool 消息是否配对ToolCallID 是否匹配)
**图循环不退出**
1. 检查 ConsecutiveCorrections 计数(可能 LLM 反复输出非法 JSON
2. 检查 RoundUsed 是否耗尽MaxRounds 默认 30
3. 检查 Phase 是否卡在某个状态
### 12.3 单元测试
```
node/execute_confirm_flow_test.go → 7 个测试,覆盖完整 confirm 回路
node/llm_tool_orchestration_test.go → 5 个测试,覆盖真实排课场景
```
测试使用 mock LLM预定义 JSON 响应序列)和 mock 工具注册表,不依赖外部服务。
---
## 十三、关键设计决策及理由
| 决策 | 理由 |
|------|------|
| Phase 驱动路由而非硬编码序列 | 同一个图支持多种流程直接聊天、排课、追问恢复Phase 是最小状态信号 |
| PendingInteraction 作为中断快照 | 图是无状态的(每次请求重新运行),需要一种机制跨请求传递"等用户回复"的上下文 |
| PendingConfirmTool 作为临时邮箱 | Execute 和 Confirm 之间不能直接传参(中间隔了 Interrupt+END+Chat用运行态字段传递 |
| JSON 协议而非文本标记 | LLM 输出结构化数据,后端用泛型解析,避免正则匹配的不确定性 |
| Correction 机制 | LLM 不是 100% 可靠,需要给修正机会,但限制最大连续次数避免死循环 |
| 伪流式而非真流式 | LLM API 的一次性返回更适合分段推送,真流式实现复杂且收益低 |
| 工具操作扁平 ScheduleState | 避免嵌套数据结构,工具只需关心"在哪里放什么" |
| Diff 持久化 | 只持久化变更部分,减少 DB 操作,支持原子性 |
| PinnedBlocks 注入上下文 | 计划、工具摘要等信息不需要每轮都重复,用置顶块注入一次即可 |
---
## 十四、关键文件速查
| 想了解... | 看这个文件 |
|----------|----------|
| 图怎么连的 | `graph/common_graph.go` |
| 每个节点怎么被调用的 | `node/agent_nodes.go` |
| Chat 怎么分类意图的 | `prompt/chat.go` + `node/chat.go` |
| Plan 怎么生成计划的 | `prompt/plan.go` + `node/plan.go` |
| Execute 的 ReAct 循环 | `node/execute.go` |
| confirm 回路怎么转的 | `node/confirm.go` + `node/chat.go`(handleConfirmResume) |
| LLM 输出什么格式 | `model/plan_contract.go` + `model/execute_contract.go` |
| JSON 解析怎么容错的 | `llm/json.go` |
| correction 怎么追回的 | `node/correction.go` |
| 工具怎么注册和执行的 | `tools/registry.go` |
| 工具操作什么数据 | `tools/state.go` |
| SSE 输出什么格式 | `stream/openai.go` |
| 状态怎么持久化的 | `model/state_store.go` + `conv/schedule_persist.go` |
| 日程数据怎么加载的 | `conv/schedule_provider.go` + `conv/schedule_state.go` |
| Service 怎么组装的 | `service/agentsvc/agent_newagent.go` |
| API 怎么调用的 | `api/agent.go` |
| 距离全链路还差什么 | `ROADMAP.md` |