docs(roadmap): 更新 Phase 4b 状态为已完成
This commit is contained in:
+17
-10
@@ -1,13 +1,13 @@
|
|||||||
# AG Core Roadmap
|
# AG Core Roadmap
|
||||||
|
|
||||||
> 定稿日期:2026-05-11
|
> 定稿日期:2026-05-11
|
||||||
> 最后更新:2026-06-11(Phase 4a 编码实施完成;Phase 4b/4c 仍待启动)
|
> 最后更新:2026-06-11(Phase 4b 编码实施完成;Phase 4c 仍待启动)
|
||||||
|
|
||||||
## 愿景
|
## 愿景
|
||||||
|
|
||||||
AG Core 定位为构建 AI 智能体的底层工具箱,通过模块化、可插拔的架构,提供大模型调用、提示词工程、工具系统、记忆检索四大核心能力,支持快速组合出符合业务需求的智能体应用。
|
AG Core 定位为构建 AI 智能体的底层工具箱,通过模块化、可插拔的架构,提供大模型调用、提示词工程、工具系统、记忆检索四大核心能力,支持快速组合出符合业务需求的智能体应用。
|
||||||
|
|
||||||
**当前状态**:Phase 0 基础设施已全部完成,Phase 1 提示词工程已全部完成,Phase 2 工具系统已全部完成,Phase 3 记忆系统已全部完成,Phase 4a 核心胶水层已全部完成(109 个测试通过,0 警告),Phase 4b/4c 待启动。
|
**当前状态**:Phase 0 基础设施已全部完成,Phase 1 提示词工程已全部完成,Phase 2 工具系统已全部完成,Phase 3 记忆系统已全部完成,Phase 4a 核心胶水层已全部完成,Phase 4b 任务执行已全部完成(113 个测试通过,0 警告),Phase 4c 待启动。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -161,10 +161,10 @@ AG Core 定位为构建 AI 智能体的底层工具箱,通过模块化、可
|
|||||||
**前置条件**:Phase 4a 已完成。
|
**前置条件**:Phase 4a 已完成。
|
||||||
|
|
||||||
**交付物**:
|
**交付物**:
|
||||||
1. `TaskAgent` trait — `run(goal)` 自主式 + `execute_plan(plan)` 外部驱动式
|
1. ✅ `TaskAgent` trait — `run(goal)` 自主式 + `execute_plan(plan)` 外部驱动式
|
||||||
2. `PlanParser` trait + `JsonPlanParser` 参考实现
|
2. ✅ `PlanParser` trait + `JsonPlanParser` 参考实现
|
||||||
3. `AgentError` 追加 PlanParse 变体
|
3. ✅ `AgentError` 追加 PlanParse 变体(共 7 个变体)
|
||||||
4. Hook 事件扩展:OnPlanStepComplete + plan_step_index 字段
|
4. ✅ Hook 事件扩展:OnPlanStepComplete + plan_step_index 字段
|
||||||
|
|
||||||
**依赖**:Phase 4a
|
**依赖**:Phase 4a
|
||||||
|
|
||||||
@@ -172,7 +172,14 @@ AG Core 定位为构建 AI 智能体的底层工具箱,通过模块化、可
|
|||||||
|
|
||||||
**预估规模**:约 200 行代码(增量)
|
**预估规模**:约 200 行代码(增量)
|
||||||
|
|
||||||
**状态**:⏳ 待 Phase 4a 完成后启动
|
**实际新增**:
|
||||||
|
- 修改文件 2 个(llm/hooks.rs +5 行;agent/error.rs +10 行)
|
||||||
|
- 新增代码约 150 行(含测试;纯实现约 90 行)
|
||||||
|
- 新增内联测试 4 个;全量测试 109 → 113(0 失败)
|
||||||
|
- clippy 0 警告
|
||||||
|
- 无新增外部依赖
|
||||||
|
|
||||||
|
**状态**:✅ Phase 4b 全部交付物已完成
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -206,7 +213,7 @@ graph BT
|
|||||||
P2["<b>Phase 2: Tool System</b><br/>Tool Registry<br/>PermissionChecker<br/>MCP Client"]:::done
|
P2["<b>Phase 2: Tool System</b><br/>Tool Registry<br/>PermissionChecker<br/>MCP Client"]:::done
|
||||||
P3["<b>Phase 3: Memory System</b><br/>MemoryStore<br/>ConversationMemory<br/>KnowledgeStore"]:::done
|
P3["<b>Phase 3: Memory System</b><br/>MemoryStore<br/>ConversationMemory<br/>KnowledgeStore"]:::done
|
||||||
P4a["<b>Phase 4a: Core Glue</b><br/>AgentSession<br/>RuntimeBundle<br/>Plan/Step 纯数据"]:::done
|
P4a["<b>Phase 4a: Core Glue</b><br/>AgentSession<br/>RuntimeBundle<br/>Plan/Step 纯数据"]:::done
|
||||||
P4b["<b>Phase 4b: Task Execution</b><br/>TaskAgent<br/>PlanParser<br/>JsonPlanParser"]:::pending
|
P4b["<b>Phase 4b: Task Execution</b><br/>TaskAgent<br/>PlanParser<br/>JsonPlanParser"]:::done
|
||||||
P4c["<b>Phase 4c: Session Memory</b><br/>SessionMemory"]:::pending
|
P4c["<b>Phase 4c: Session Memory</b><br/>SessionMemory"]:::pending
|
||||||
|
|
||||||
P1 --> P0
|
P1 --> P0
|
||||||
@@ -310,7 +317,7 @@ graph BT
|
|||||||
|
|
||||||
## 下一步行动
|
## 下一步行动
|
||||||
|
|
||||||
1. **Phase 4b/4c 启动评估**:Phase 4a 已交付(109 测试通过,0 clippy 警告)。可按需启动 Phase 4b(任务执行:TaskAgent + PlanParser/JsonPlanParser)或 Phase 4c(会话级记忆:SessionMemory)—— 二者无相互依赖,可任选其一
|
1. **Phase 4c 启动评估**:Phase 4a + 4b 已交付(113 测试通过,0 clippy 警告)。可启动 Phase 4c(会话级记忆:SessionMemory + RuntimeBundle/Builder 扩展 + AgentSession 接入)
|
||||||
2. **Context 切换备忘**:`docs/note-context-switch-design.md` 记录了多 context 切换方案讨论,作为 v0.2+ 扩展项的输入
|
2. **Context 切换备忘**:`docs/note-context-switch-design.md` 记录了多 context 切换方案讨论,作为 v0.2+ 扩展项的输入
|
||||||
3. **参考项目调研沉淀**:已完成 OpenClaw / Hermes / OpenHuman / OpenHarness 横向调研,结果沉淀至 `docs/note-agent-harness-references.md`,作为 v0.2+ 扩展项的输入
|
3. **参考项目调研沉淀**:已完成 OpenClaw / Hermes / OpenHuman / OpenHarness 横向调研,结果沉淀至 `docs/note-agent-harness-references.md`,作为 v0.2+ 扩展项的输入
|
||||||
4. **Phase 3 备用设计就绪**:`docs/note-knowledge-graph-design.md` 记录了 KnowledgeGraph、高级评分、RecallBased 淘汰等设计,v0.2+ 记忆扩展可直接参考
|
4. **Phase 3 备用设计就绪**:`docs/note-knowledge-graph-design.md` 记录了 KnowledgeGraph、高级评分、RecallBased 淘汰等设计,v0.2+ 记忆扩展可直接参考
|
||||||
@@ -321,5 +328,5 @@ graph BT
|
|||||||
- ✅ Phase 2 Tool System — 全部交付物已完成
|
- ✅ Phase 2 Tool System — 全部交付物已完成
|
||||||
- ✅ Phase 3 Memory System — 全部交付物已完成
|
- ✅ Phase 3 Memory System — 全部交付物已完成
|
||||||
- ✅ Phase 4a Core Glue — 全部交付物已完成
|
- ✅ Phase 4a Core Glue — 全部交付物已完成
|
||||||
- ⏳ Phase 4b Task Execution — 依赖 4a
|
- ✅ Phase 4b Task Execution — 全部交付物已完成
|
||||||
- ⏳ Phase 4c Session Memory — 依赖 4a
|
- ⏳ Phase 4c Session Memory — 依赖 4a
|
||||||
|
|||||||
+2
-1
@@ -22,4 +22,5 @@ pub use builder::AgentBuilder;
|
|||||||
pub use error::AgentError;
|
pub use error::AgentError;
|
||||||
pub use runtime::{AgentConfig, RuntimeBundle};
|
pub use runtime::{AgentConfig, RuntimeBundle};
|
||||||
pub use session::AgentSession;
|
pub use session::AgentSession;
|
||||||
pub use task::{Plan, Step, StepStatus};
|
pub use task::{Plan, PlanParser, Step, StepStatus, TaskAgent};
|
||||||
|
pub use task::JsonPlanParser;
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ pub enum AgentError {
|
|||||||
#[error("记忆错误: {0}")]
|
#[error("记忆错误: {0}")]
|
||||||
Memory(#[from] MemoryError),
|
Memory(#[from] MemoryError),
|
||||||
|
|
||||||
|
/// Plan 解析失败(Phase 4b 新增)。
|
||||||
|
#[error("Plan 解析错误: {0}")]
|
||||||
|
PlanParse(String),
|
||||||
|
|
||||||
/// 钩子阻断操作(Agent 层特有)。
|
/// 钩子阻断操作(Agent 层特有)。
|
||||||
#[error("钩子阻断: {0}")]
|
#[error("钩子阻断: {0}")]
|
||||||
HookBlocked(String),
|
HookBlocked(String),
|
||||||
@@ -63,6 +67,7 @@ impl AgentError {
|
|||||||
),
|
),
|
||||||
Self::Tool(e) => e.is_recoverable(),
|
Self::Tool(e) => e.is_recoverable(),
|
||||||
Self::Memory(e) => e.is_recoverable(),
|
Self::Memory(e) => e.is_recoverable(),
|
||||||
|
Self::PlanParse(_) => false,
|
||||||
Self::HookBlocked(_) | Self::LimitExceeded(_) | Self::Config(_) | Self::Other(_) => {
|
Self::HookBlocked(_) | Self::LimitExceeded(_) | Self::Config(_) | Self::Other(_) => {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -132,6 +137,11 @@ mod tests {
|
|||||||
assert!(!AgentError::Other("unknown".into()).is_recoverable());
|
assert!(!AgentError::Other("unknown".into()).is_recoverable());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn plan_parse_not_recoverable() {
|
||||||
|
assert!(!AgentError::PlanParse("bad json".into()).is_recoverable());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_llm_via_question_mark() {
|
fn from_llm_via_question_mark() {
|
||||||
fn returns_llm() -> Result<(), LlmError> {
|
fn returns_llm() -> Result<(), LlmError> {
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
use crate::agent::error::AgentError;
|
use crate::agent::error::AgentError;
|
||||||
use crate::llm::types::ChatResponse;
|
use crate::llm::types::ChatResponse;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
/// 任务规划 —— 一组有序的 Step。
|
/// 任务规划 —— 一组有序的 Step。
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Plan {
|
pub struct Plan {
|
||||||
@@ -78,6 +80,96 @@ impl StepStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Plan 解析接口 —— 将 LLM 原始输出转换为 `Plan` 数据结构。
|
||||||
|
///
|
||||||
|
/// **注入式**:上层应用可以注入自定义解析器(如基于 XML / YAML / 自定义 DSL),
|
||||||
|
/// `JsonPlanParser` 是参考实现而非默认实现。
|
||||||
|
#[async_trait]
|
||||||
|
pub trait PlanParser: Send + Sync {
|
||||||
|
/// 将 LLM 原始输出解析为 `Plan`。
|
||||||
|
///
|
||||||
|
/// - `raw`:LLM 返回的原始文本
|
||||||
|
/// - `goal`:规划目标(用于填充 `Plan.goal`)
|
||||||
|
async fn parse(&self, raw: &str, goal: &str) -> Result<Plan, AgentError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// JSON 格式的 Plan 解析器(参考实现)。
|
||||||
|
///
|
||||||
|
/// 期望 LLM 输出形如:
|
||||||
|
/// ```json
|
||||||
|
/// {"steps": [{"description": "..."}, ...]}
|
||||||
|
/// ```
|
||||||
|
/// 的 JSON 文本。解析失败返回 `AgentError::PlanParse`。
|
||||||
|
pub struct JsonPlanParser;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl PlanParser for JsonPlanParser {
|
||||||
|
async fn parse(&self, raw: &str, goal: &str) -> Result<Plan, AgentError> {
|
||||||
|
let parsed: serde_json::Value = serde_json::from_str(raw)
|
||||||
|
.map_err(|e| AgentError::PlanParse(format!("JSON 解析失败: {e}")))?;
|
||||||
|
|
||||||
|
let steps_array = parsed
|
||||||
|
.get("steps")
|
||||||
|
.and_then(|v| v.as_array())
|
||||||
|
.ok_or_else(|| AgentError::PlanParse("缺少 'steps' 数组".into()))?;
|
||||||
|
|
||||||
|
let steps: Vec<Step> = steps_array
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, item)| {
|
||||||
|
let description = item
|
||||||
|
.get("description")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.ok_or_else(|| {
|
||||||
|
AgentError::PlanParse(format!("步骤 {i} 缺少 'description' 字段"))
|
||||||
|
})?;
|
||||||
|
Ok(Step::new(i, description))
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, AgentError>>()?;
|
||||||
|
|
||||||
|
if steps.is_empty() {
|
||||||
|
return Err(AgentError::PlanParse(
|
||||||
|
"Plan 至少需要一个步骤".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Plan {
|
||||||
|
id: uuid(),
|
||||||
|
goal: goal.to_string(),
|
||||||
|
steps,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 任务型智能体 —— 自主规划与执行。
|
||||||
|
///
|
||||||
|
/// 与基础 `Agent` trait 分离:`Agent` 定义"角色"(system prompt + 工具集),
|
||||||
|
/// `TaskAgent` 定义"规划/执行"行为(如何拆 Plan、如何执行 Plan)。
|
||||||
|
#[async_trait]
|
||||||
|
pub trait TaskAgent: Send + Sync {
|
||||||
|
/// 自主式入口:根据目标生成 Plan 并执行。
|
||||||
|
///
|
||||||
|
/// 实现内部应调用 `PlanParser::parse` 从 LLM 输出生成 Plan,
|
||||||
|
/// 然后调用 `execute_plan` 执行。
|
||||||
|
async fn run(&mut self, goal: &str) -> Result<Plan, AgentError>;
|
||||||
|
|
||||||
|
/// 外部驱动式入口:执行预定义的 Plan。
|
||||||
|
///
|
||||||
|
/// 逐步调用 `AgentSession::submit_turn`,每步完成后触发
|
||||||
|
/// `OnPlanStepComplete` hook,更新步骤状态。
|
||||||
|
async fn execute_plan(&mut self, plan: &mut Plan) -> Result<(), AgentError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 生成简易唯一 ID(仅用于 Plan 标识,非加密安全)。
|
||||||
|
fn uuid() -> String {
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
let ts = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.as_nanos();
|
||||||
|
format!("{ts:x}")
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -118,4 +210,34 @@ mod tests {
|
|||||||
assert_eq!(plan.steps[0].index, 0);
|
assert_eq!(plan.steps[0].index, 0);
|
||||||
assert_eq!(plan.steps[1].index, 1);
|
assert_eq!(plan.steps[1].index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 烟雾测试 1:JsonPlanParser 解析合法 JSON。
|
||||||
|
#[tokio::test]
|
||||||
|
async fn json_plan_parser_success() {
|
||||||
|
let parser = JsonPlanParser;
|
||||||
|
let input = r#"{"steps": [{"description": "step one"}, {"description": "step two"}]}"#;
|
||||||
|
let plan = parser.parse(input, "my goal").await.unwrap();
|
||||||
|
assert_eq!(plan.goal, "my goal");
|
||||||
|
assert_eq!(plan.steps.len(), 2);
|
||||||
|
assert_eq!(plan.steps[0].description, "step one");
|
||||||
|
assert_eq!(plan.steps[1].description, "step two");
|
||||||
|
assert!(plan.steps.iter().all(|s| s.status.is_pending()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 烟雾测试 2:JsonPlanParser 解析失败返回 AgentError::PlanParse。
|
||||||
|
#[tokio::test]
|
||||||
|
async fn json_plan_parser_invalid_json() {
|
||||||
|
let parser = JsonPlanParser;
|
||||||
|
let err = parser.parse("not json", "goal").await.unwrap_err();
|
||||||
|
assert!(matches!(err, AgentError::PlanParse(_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 烟雾测试 3:JsonPlanParser 空步骤返回错误。
|
||||||
|
#[tokio::test]
|
||||||
|
async fn json_plan_parser_empty_steps() {
|
||||||
|
let parser = JsonPlanParser;
|
||||||
|
let input = r#"{"steps": []}"#;
|
||||||
|
let err = parser.parse(input, "goal").await.unwrap_err();
|
||||||
|
assert!(matches!(err, AgentError::PlanParse(_)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ pub enum HookEvent {
|
|||||||
OnTurnStart,
|
OnTurnStart,
|
||||||
/// Agent 会话完成一轮 turn 之后(Phase 4a 新增)。
|
/// Agent 会话完成一轮 turn 之后(Phase 4a 新增)。
|
||||||
OnTurnEnd,
|
OnTurnEnd,
|
||||||
|
/// TaskAgent 完成一个 Plan 步骤后触发(Phase 4b 新增)。
|
||||||
|
OnPlanStepComplete,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 此次钩子调用的上下文。
|
/// 此次钩子调用的上下文。
|
||||||
@@ -35,6 +37,8 @@ pub struct HookContext<'a> {
|
|||||||
pub attempt: u32,
|
pub attempt: u32,
|
||||||
/// 当前 turn 序号(0-based,仅 OnTurnStart / OnTurnEnd 可用,Phase 4a 新增)。
|
/// 当前 turn 序号(0-based,仅 OnTurnStart / OnTurnEnd 可用,Phase 4a 新增)。
|
||||||
pub turn_index: Option<u32>,
|
pub turn_index: Option<u32>,
|
||||||
|
/// 当前 plan step 序号(0-based,仅 OnPlanStepComplete 可用,Phase 4b 新增)。
|
||||||
|
pub plan_step_index: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> HookContext<'a> {
|
impl<'a> HookContext<'a> {
|
||||||
@@ -45,6 +49,7 @@ impl<'a> HookContext<'a> {
|
|||||||
error: None,
|
error: None,
|
||||||
attempt: 0,
|
attempt: 0,
|
||||||
turn_index: None,
|
turn_index: None,
|
||||||
|
plan_step_index: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +73,12 @@ impl<'a> HookContext<'a> {
|
|||||||
self.turn_index = Some(turn_index);
|
self.turn_index = Some(turn_index);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 设置 plan step 序号(仅 OnPlanStepComplete 使用,Phase 4b 新增)。
|
||||||
|
pub(crate) fn with_plan_step_index(mut self, plan_step_index: usize) -> Self {
|
||||||
|
self.plan_step_index = Some(plan_step_index);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 钩子执行结果。
|
/// 钩子执行结果。
|
||||||
|
|||||||
Reference in New Issue
Block a user