Agent 任务规划器(Planner)
🧠 Agent 任务规划器(Planner)完全解析
一、Planner 到底是什么?
一句话定义:
Planner 是 Agent 的"大脑",负责把用户一句模糊需求,变成一组可执行、可验证、可控的步骤。
核心职责:
用户说:"帮我分析一下这个项目" ↓ Planner 输出: 1. 读取 README 2. 分析目录结构 3. 识别技术栈 4. 检查核心模块 5. 分析安全风险 6. 输出报告
关键原则:
- ✅ Planner 只规划,不执行
- ✅ 每一步必须清晰、可验证
- ✅ 高风险操作必须显式标记
- ✅ 输出必须结构化(JSON)
二、Planner 的五层架构(Deep Agent 模型)
┌─────────────────────────────────────────────┐ │ 1️⃣ Task Understanding(任务理解) │ │ 把用户输入转换为结构化任务规格 │ └─────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────┐ │ 2️⃣ HTN 层级任务分解 │ │ Hierarchical Task Networks │ │ 任务 = 方法 → 子任务 │ └─────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────┐ │ 3️⃣ Tool-Aware Decomposition(工具感知分解) │ │ 根据可用工具选择最优实现路径 │ └─────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────┐ │ 4️⃣ Plan Graph Construction(计划图构建) │ │ 生成带依赖关系的 DAG 任务图 │ └─────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────┐ │ 5️⃣ Self-Projection & Refinement(自预测优化)│ │ 预判潜在问题,提前修正计划 │ └─────────────────────────────────────────────┘
三、核心数据结构
3.1 Step 定义(带依赖和验证)
type Step = { id: string; // 步骤唯一 ID title: string; // 步骤标题 description: string; // 详细说明 dependsOn: string[]; // 依赖的前置步骤 ID owner: "researcher" | "implementer" | "tester" | "reviewer"; tool?: string; // 使用的工具名 verification: string; // 如何验证这一步完成了 checkpoint: string; // 完成后的状态快照 expectedOutput: string; // 预期产出 riskLevel: "low" | "medium" | "high"; stopCondition?: string; // 什么情况下停止 };
3.2 Plan 输出格式
{ "goal": "分析项目代码质量", "steps": [ { "id": "step_1", "title": "读取项目说明", "description": "读取 README 和 package 配置", "tool": "read_file", "expectedOutput": "项目基本信息", "riskLevel": "low" }, { "id": "step_2", "title": "分析目录结构", "tool": "list_files", "riskLevel": "low" } ] }
3.3 Task Graph(DAG 有向无环图)
一、什么是 DAG?
DAG = Directed Acyclic Graph(有向无环图)
拆开看:
- 有向(Directed):边有方向,表示"谁依赖谁"
- 无环(Acyclic):没有循环依赖,不会死锁
- 图(Graph):节点 + 边的数据结构
直观理解:
A → B → C ↘ D → E 箭头表示"必须先完成" A 完成后才能做 B B 完成后才能做 C 和 D D 完成后才能做 E
二、为什么任务规划要用 DAG?
问题场景
用户说:"帮我实现一个用户登录系统"
线性计划(❌ 有问题):
1. 设计数据库 2. 实现注册接口 3. 实现登录接口 4. 写单元测试 5. 写 API 文档
问题: 步骤 4(测试)必须等步骤 2+3 都完成才能开始 步骤 5(文档)其实可以等步骤 1 完成就开始写 线性计划无法表达并行和复杂依赖
DAG 计划(✅ 正确)
[1. 设计数据库] ↓ [2. 定义数据模型] ↙ ↘ [3. 实现注册] [4. 实现登录] [5. 写 API 文档] ↘ ↙ [6. 单元测试] ↓ [7. 集成测试]
Task Graph 的优势
| 优势 | 说明 |
|---|---|
| 并行执行 | 独立节点可同时执行,大幅提速 |
| 清晰依赖 | 一眼看出谁等谁,避免顺序错误 |
| 失败隔离 | 某节点失败不影响无关分支 |
| 可恢复 | 从 checkpoint 恢复,不用重头开始 |
| 可解释 | 人类可读的任务树,方便 Debug |
| 可评测 | Progress = 完成节点数 / 总节点数 |
Task Graph(DAG)的本质:
用图结构表达任务之间的依赖关系,支持并行执行、失败恢复和进度追踪。
核心价值:
- 🚀 并行执行,提速
- 📊 清晰依赖,避免错误
- 🔄 失败隔离,局部重跑
- 📈 进度可量化,可追踪
四、执行流程(核心循环)
const plan = createPlan("trace replay"); const done = new Set<string>(); while (true) { // 1️⃣ 计算下一步可执行的步骤 const ready = nextReadySteps(plan, done); if (ready.length === 0) break; for (const step of ready) { // 2️⃣ 执行步骤 const result = await runStep(step); if (result.ok) { // 3️⃣ 成功:标记完成 + 保存 checkpoint done.add(step.id); saveCheckpoint(step.id, result.evidence); continue; } // 4️⃣ 失败:决策如何处理 const decision = decideFailure(step, result); if (decision.type === "retry") await retry(step); if (decision.type === "replan") { // 5️⃣ 重规划:打补丁,不是推倒重来 plan = applyGraphPatch(plan, decision.patch); } if (decision.type === "stop") { return summarizeBlocked(step, result); } } }
五、失败处理策略(4 种)
| 策略 | 触发条件 | 处理方式 |
|---|---|---|
| Retry | 网络抖动、测试偶发失败 | 同一步重跑 |
| Replan | 发现原假设不成立、需要新增依赖 | 修改任务图(打补丁) |
| Fallback | 原方案不可行 | 换更小实现方案 |
| Stop | 需要用户确认、权限不足 | 暂停等待人工输入 |
关键原则:
Replan 是对 task graph 的补丁,不是把历史抹掉重来!
六、HTN 层级任务分解(核心算法)
HTN(Hierarchical Task Networks) 是现代 Deep Agents 的主流框架。
核心思想:任务 = 方法(methods)→ 子任务(subtasks)
为什么 HTN 重要:
- ✅ 可解释:你能看到任务树
- ✅ 可回滚:某个节点失败 → 只重跑该节点
- ✅ 便于评测:Progress Rate = 完成节点数 / 总节点数
- ✅ 支持长期任务:树深越大,支持的动作序列越长
七、任务树生成算法(4 种)
7.1 Top-down 多轮推理
Task → Subtask 1, 2, 3…
Subtask 1 → Sub-subtask 1.1, 1.2…
类似深度优先搜索(DFS)+ 结果修正
7.2 Tree-of-Thought(ToT)多路径展开
展开多条候选路径 → LLM Judge 评分 → 选择最优 plan
7.3 Self-Projection(未来预测)
问 LLM:"假设你按这个计划执行,未来可能遇到什么问题?"
- 依赖冲突
- 工具不可用
- 缺少文件/环境
- 步骤顺序问题
7.4 约束求解(Constraint Satisfaction)
LLM 生成计划 → 约束求解器检查:
- 任务顺序是否符合依赖
- 工具是否足够执行
- 输出格式是否满足要求
八、风险控制(三级风险)
| 风险等级 | 操作示例 | 处理方式 |
|---|---|---|
| Low | 读取文件、搜索资料、分析文本 | 直接执行 |
| Medium | 创建文件、修改草稿、调用外部 API | 记录日志 |
| High | 删除文件、覆盖代码、执行 shell、提交 git | 必须人工确认 |
企业级强化要素
| 强化点 | 作用 |
|---|---|
| Plan Cache | 同类任务不必重新规划,大幅降低成本 |
| Dynamic Replanning | 执行中根据上下文变化调整剩余节点 |
| Mixture-of-Agents | Planner 用 o3-mini,执行用便宜模型,反思用 PRM |
| Plan Compression | 100 步压缩为 10 层抽象步骤,提升稳定性 |
| 任务树可视化 | Debug、演示、风控审批(金融/能源必备) |
关键设计决策总结
✅ Planner 只负责规划,不直接执行 ✅ 计划必须结构化(JSON) ✅ 高风险步骤必须显式标记 ✅ Replan 是打补丁,不是推倒重来 ✅ 每个 step 必须有验证方式和 checkpoint ✅ 失败时明确选择 retry/replan/fallback/stop ✅ 使用 done 集合 + 依赖关系计算 ready steps
验收标准(Planner 合格的标准)
✅ 能把模糊目标拆成可执行步骤
✅ 能标记工具和风险等级
✅ 能输出结构化计划
✅ 能被校验和复用
✅ 支持依赖关系和并行执行
✅ 支持失败恢复和 replan
一个简单案例
Model
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp3.TaskModel { public enum AiTaskStatus { /// <summary> /// 等待中 /// </summary> Pending, // 等待中 Ready, // 依赖已满足,可执行 Running, // 执行中 Done, // 已完成 Failed, // 失败 Blocked, // 被阻塞 Skipped // 已跳过 } //风险等级 public enum RiskLevel { Low,//低 Medium,//普通 High //高 } public class TaskNode { /// <summary> /// 任务ID /// </summary> public string Id { get; set; } = Guid.NewGuid().ToString("N")[..8]; /// <summary> /// 任务标题 /// </summary> public string Title { get; set; } = ""; /// <summary> /// 任务描述 /// </summary> public string Description { get; set; } = ""; /// <summary> /// 任务依赖项--可能依赖多个任务 /// </summary> public List<string> DependsOn { get; set; } = new(); /// <summary> /// 该任务调用的工具名称 /// </summary> public string? ToolName { get; set; } /// <summary> /// 任务状态-默认等待执行 /// </summary> public AiTaskStatus Status { get; set; } = AiTaskStatus.Pending; /// <summary> /// 风险登记 默认低风险 /// </summary> public RiskLevel RiskLevel { get; set; } = RiskLevel.Low; /// <summary> /// 任务的拥有者 /// Researcher 🔍 调研、分析、收集信息 "现状是什么?" 搜索、读取、分析 ///Implementer 🔨 实现、编码、创建内容 "怎么做出来?" 写代码、创建文件 ///Tester 🧪 验证、测试、检查质量 "做得对吗?" 运行测试、校验 ///Reviewer 👁️ 审查、评估、把关 "做得好吗?" 代码审查、文档审核 /// </summary> public string? Owner { get; set; } /// <summary> /// // 如何验证这一步完成了 /// </summary> public string Verification { get; set; } = ""; /// <summary> /// // 完成后的状态快照 /// </summary> public string Checkpoint { get; set; } = ""; public object? Outputs { get; set; } /// <summary> /// 错误信息 /// </summary> public string? Error { get; set; } /// <summary> /// 重试次数 /// </summary> public int RetryCount { get; set; } = 0; /// <summary> /// 允许的最大重试次数 /// </summary> public int MaxRetries { get; set; } = 3; /// <summary> /// 可执行任务时的状态要求 等待 或 依赖已满足,可执行 /// </summary> public bool CanExecute => Status == AiTaskStatus.Ready || Status == AiTaskStatus.Pending; /// <summary> /// 可结束任务时的状态要求 已完成 或 已跳过 /// </summary> public bool IsComplete => Status == AiTaskStatus.Done || Status == AiTaskStatus.Skipped; } /// <summary> /// 任务图(Task Graph) /// 表示一个完整的任务计划,由多个相互依赖的任务节点组成 /// 使用有向无环图(DAG)结构管理任务依赖关系 /// </summary> public class TaskGraph { /// <summary> /// 任务的最终目标 /// 例如:"实现用户登录系统"、"分析项目代码质量" /// </summary> public string Goal { get; set; } = ""; /// <summary> /// 任务节点字典 /// Key: 节点 ID(唯一标识) /// Value: 任务节点对象(包含任务详情、状态、依赖等) /// /// 使用 Dictionary 的原因: /// - 通过 ID 快速查找节点 O(1) /// - 便于依赖关系引用(dependsOn 存储的是 ID 列表) /// - 方便增删改操作 /// </summary> public Dictionary<string, TaskNode> Nodes { get; set; } = new(); /// <summary> /// 任务图创建时间 /// 用于追踪计划生成时间,便于日志和调试 /// </summary> public DateTime CreatedAt { get; set; } = DateTime.Now; /// <summary> /// 任务图最后更新时间 /// 每次修改任务节点(状态变更、增删节点等)后应更新此字段 /// 用于: /// - 判断计划是否过期 /// - 追踪执行进度历史 /// - 持久化时检测并发修改 /// </summary> public DateTime UpdatedAt { get; set; } = DateTime.Now; /// <summary> /// 用户的原始输入提示词 /// 例如:"帮我实现一个用户登录系统,包括注册、登录、密码找回功能" /// /// 用途: /// - 重新规划时参考原始意图 /// - 日志记录和问题排查 /// - 用户查看历史任务时了解上下文 /// </summary> public string? OriginalPrompt { get; set; } /// <summary> /// 总节点数量(只读计算属性) /// 等价于 Nodes.Count /// /// 用途: /// - 计算进度百分比 /// - 评估任务规模 /// - 日志输出和 UI 展示 /// </summary> public int TotalNodes => Nodes.Count; /// <summary> /// 已完成的节点数量(只读计算属性) /// 统计所有状态为 Done 或 Skipped 的节点 /// /// 用途: /// - 计算进度百分比 /// - 判断任务是否全部完成 /// - UI 进度条展示 /// </summary> public int CompletedNodes => Nodes.Values.Count(n => n.IsComplete); /// <summary> /// 失败的节点数量(只读计算属性) /// 统计所有状态为 Failed 的节点 /// /// 用途: /// - 判断任务是否部分失败 /// - 触发失败处理逻辑(retry/replan/stop) /// - 生成执行报告 /// </summary> public int FailedNodes => Nodes.Values.Count(n => n.Status == AiTaskStatus.Failed); /// <summary> /// 任务执行进度百分比(只读计算属性) /// 计算公式:(已完成节点数 / 总节点数) × 100 /// /// 返回值范围:0.0 ~ 100.0 /// - 0.0: 尚未开始 /// - 50.0: 完成一半 /// - 100.0: 全部完成 /// /// 边界处理: /// - 总节点数为 0 时返回 0,避免除零异常 /// /// 用途: /// - UI 进度条展示 /// - 日志输出执行状态 /// - 判断是否可以结束执行循环 /// </summary> public double Progress => TotalNodes > 0 ? (double)CompletedNodes / TotalNodes * 100 : 0; /// <summary> /// 克隆当前任务图(深拷贝) /// /// 用途: /// - Replan 时保留原始计划作为参考 /// - 持久化前创建快照 /// - 并行执行多个计划变体 /// - 回滚到之前的状态 /// /// 拷贝内容: /// - Goal: 任务目标 /// - Nodes: 节点字典(浅拷贝,节点对象本身不复制) /// - OriginalPrompt: 原始提示词 /// - CreatedAt: 保持原创建时间 /// - UpdatedAt: 设置为当前时间(表示新副本的创建时间) /// /// 注意事项: /// - Nodes 是浅拷贝(Dictionary 复制,但 TaskNode 对象引用不变) /// - 如需完全深拷贝,需要遍历 Nodes 并克隆每个 TaskNode /// /// 返回:新的 TaskGraph 实例 /// </summary> public TaskGraph Clone() { return new TaskGraph { Goal = this.Goal, Nodes = new Dictionary<string, TaskNode>(this.Nodes), OriginalPrompt = this.OriginalPrompt, CreatedAt = this.CreatedAt, UpdatedAt = DateTime.Now // 副本的更新时间设为当前 }; } } /// <summary> /// 任务执行结果 /// 封装单次任务或整个任务图的执行结果,包含成功状态、输出数据、错误信息等 /// /// 设计模式:Result Pattern(结果模式) /// 优点: /// - 避免使用异常控制流程 /// - 明确区分成功/失败两种状态 /// - 携带丰富的上下文信息(证据、最终状态等) /// - 便于链式调用和模式匹配 /// </summary> public class ExecutionResult { /// <summary> /// 执行是否成功 /// true: 任务成功完成 /// false: 任务失败(可能部分完成、完全失败、被阻塞等) /// /// 用途: /// - 快速判断执行结果 /// - 决定是否继续后续步骤 /// - 生成最终报告 /// </summary> public bool Success { get; set; } /// <summary> /// 执行产出物 /// 类型:object?(可以是任意类型) /// /// 可能的值: /// - TaskGraph: 完整的任务图(执行完成后) /// - string: 文本结果(如分析报告) /// - byte[]: 二进制数据(如生成的文件) /// - 匿名对象:结构化数据 /// /// 注意: /// - 成功时通常包含有效输出 /// - 失败时可能包含部分结果(如已完成的中间产物) /// - 调用方需要根据实际场景进行类型转换 /// </summary> public object? Output { get; set; } /// <summary> /// 执行证据 /// 用于证明任务已完成或失败的文本描述 /// /// 内容示例: /// - "完成 5/5 个任务,所有测试通过" /// - "已生成报告文件:/path/to/report.md" /// - "API 响应:200 OK,耗时 120ms" /// - "测试覆盖率:85%" /// /// 用途: /// - Checkpoint 保存(用于任务恢复) /// - 日志记录和审计 /// - 向用户展示执行摘要 /// - Planner 决策参考(replan 时分析失败原因) /// </summary> public string? Evidence { get; set; } /// <summary> /// 错误信息 /// 仅在 Success = false 时有效 /// /// 内容: /// - 异常消息 /// - 失败原因描述 /// - 建议的解决方案 /// /// 示例: /// - "工具不存在:web_search" /// - "部分任务未完成:测试失败、代码审查未通过" /// - "用户未批准高风险操作" /// /// 用途: /// - 日志记录 /// - 向用户报告失败原因 /// - FailureDecision 的输入(决定 retry/replan/stop) /// </summary> public string? Error { get; set; } /// <summary> /// 最终状态快照 /// 执行结束时所有任务节点的状态字典 /// /// Key: 节点 ID /// Value: 任务节点对象(包含最终状态、错误信息、产出等) /// /// 用途: /// - 调试和排查问题(查看每个节点的最终状态) /// - 持久化后恢复执行 /// - 生成详细执行报告 /// - Replan 时分析哪些节点需要调整 /// /// 注意: /// - 仅在完整任务图执行完成后设置 /// - 单次节点执行时通常为 null /// </summary> public Dictionary<string, TaskNode>? FinalState { get; set; } /// <summary> /// 创建成功结果(静态工厂方法) /// /// 用法示例: /// <code> /// var result = ExecutionResult.Ok( /// output: graph, /// evidence: "完成 10/10 个任务" /// ); /// </code> /// /// 参数说明: /// - output: 执行产出物(可选) /// - evidence: 执行证据(可选) /// /// 返回:Success = true 的 ExecutionResult 实例 /// </summary> public static ExecutionResult Ok(object? output = null, string? evidence = null) => new() { Success = true, Output = output, Evidence = evidence }; /// <summary> /// 创建失败结果(静态工厂方法) /// /// 用法示例: /// <code> /// var result = ExecutionResult.Fail( /// error: "测试失败:3 个用例未通过", /// output: partialGraph // 可选:携带部分结果 /// ); /// </code> /// /// 参数说明: /// - error: 错误描述(必填) /// - output: 部分产出物(可选,如已完成的中间结果) /// /// 返回:Success = false 的 ExecutionResult 实例 /// </summary> public static ExecutionResult Fail(string error, object? output = null) => new() { Success = false, Output = output, Error = error }; } /// <summary> /// 失败处理决策 /// 当任务节点执行失败时,由 Planner 生成的处理决策 /// /// 决策流程: /// 1. 节点执行失败 → 捕获异常 /// 2. 调用 LLM 分析失败原因 → 生成 FailureDecision /// 3. 根据 Type 执行相应处理(retry/replan/fallback/stop) /// /// 设计原则: /// - 不是所有失败都要重试 /// - 根据失败类型选择最优策略 /// - 避免无限重试循环 /// </summary> public class FailureDecision { /// <summary> /// 决策类型 /// 可选值: /// - "retry": 重试当前步骤(适用于偶发错误、网络抖动) /// - "replan": 重新规划(适用于原假设不成立、需要调整计划) /// - "fallback": 降级方案(适用于原方案不可行,换简单方案) /// - "stop": 停止执行(适用于需要用户确认、权限不足) /// /// 决策依据: /// - 错误类型(可恢复 vs 不可恢复) /// - 重试次数(是否已达上限) /// - 任务关键性(是否阻塞后续步骤) /// - 用户配置(是否允许自动重试) /// </summary> public string Type { get; set; } = ""; // "retry" | "replan" | "fallback" | "stop" /// <summary> /// 决策理由 /// LLM 生成的自然语言解释,说明为什么选择这个策略 /// /// 示例: /// - "网络超时错误,建议重试(retry)" /// - "发现缺少前置依赖,需要新增步骤(replan)" /// - "原方案需要管理员权限,建议降级为只读模式(fallback)" /// - "需要用户确认是否删除文件,暂停等待批准(stop)" /// /// 用途: /// - 日志记录 /// - 向用户解释决策原因 /// - 调试时理解 Planner 的思考过程 /// </summary> public string Reason { get; set; } = ""; /// <summary> /// 图补丁(仅当 Type = "replan" 时有效) /// 包含对 TaskGraph 的修改指令 /// /// 修改类型: /// - AddNodes: 新增任务节点 /// - RemoveNodes: 删除任务节点 /// - UpdateDependencies: 修改依赖关系 /// /// 示例场景: /// 原计划:A → B → C /// 执行 B 时发现需要先做 D /// Patch: AddNodes = [D], UpdateDependencies = {B: [A, D]} /// /// 注意: /// - Patch 是增量修改,不是替换整个图 /// - 保持历史节点的 checkpoint 不丢失 /// - 应用 Patch 后需要重新计算可执行节点 /// </summary> public GraphPatch? Patch { get; set; } } /// <summary> /// 任务图补丁 /// 用于对 TaskGraph 进行增量修改(replan 时使用) /// /// 设计思想: /// - 不推倒重来,只修改必要的部分 /// - 保留已完成节点的状态和 checkpoint /// - 最小化重规划的成本 /// /// 应用场景: /// - 执行中发现缺少前置步骤 /// - 某步骤失败需要插入修复步骤 /// - 用户变更需求,调整部分任务 /// - 发现并行机会,优化依赖关系 /// </summary> public class GraphPatch { /// <summary> /// 新增节点列表 /// 需要添加到 TaskGraph 中的新任务节点 /// /// 示例场景: /// 原计划没有"安装依赖"步骤,执行时发现需要先安装 /// AddNodes = [new TaskNode { Id = "install_deps", Title = "安装项目依赖" }] /// /// 注意事项: /// - 新增节点的 DependsOn 必须引用已存在的节点 ID /// - 新增节点的状态应为 Pending /// - 需要在应用补丁后重新计算可执行节点 /// </summary> public List<TaskNode> AddNodes { get; set; } = new(); /// <summary> /// 删除节点 ID 列表 /// 需要从 TaskGraph 中移除的任务节点 ID /// /// 示例场景: /// 用户变更需求,不再需要"生成 PDF 报告"步骤 /// RemoveNodes = ["generate_pdf_report"] /// /// 注意事项: /// - 删除节点前需检查是否有其他节点依赖它 /// - 已完成的节点通常不建议删除(可标记为 Skipped) /// - 删除节点后需要更新后续节点的依赖关系 /// </summary> public List<string> RemoveNodes { get; set; } = new(); /// <summary> /// 更新依赖关系 /// Key: 需要修改依赖关系的节点 ID /// Value: 新的依赖节点 ID 列表 /// /// 示例场景: /// 原计划:test → implement /// 发现需要先安装依赖:test → [implement, install_deps] /// UpdateDependencies = { "test": ["implement", "install_deps"] } /// /// 注意事项: /// - 新的依赖列表必须全部是图中存在的节点 ID /// - 不能创建循环依赖(会破坏 DAG 结构) /// - 修改依赖后需要重新拓扑排序 /// /// 用途: /// - 添加新的前置依赖 /// - 移除不必要的依赖(优化并行度) /// - 调整执行顺序 /// </summary> public Dictionary<string, List<string>> UpdateDependencies { get; set; } = new(); } }
main 入口
using AiTest; using ConsoleApp3.TaskModel; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.OpenAI; using System.ComponentModel; namespace ConsoleApp3 { internal class Program { static async Task Main(string[] args) { Console.WriteLine("╔════════════════════════════════════════╗"); Console.WriteLine("║ SK 1.78.0 任务规划器简化示例 ║"); Console.WriteLine("╚════════════════════════════════════════╝\n"); // 1️⃣ 创建 Kernel(使用阿里云 DashScope) var kernel = CreateKernel(); // 2️⃣ 注册自定义插件 var simplePlugin = new SimplePlugin(); var plugin = KernelPluginFactory.CreateFromObject(simplePlugin, pluginName: "simple"); kernel.Plugins.Add(plugin); // 3️⃣ 创建简单任务图 var graph = CreateSimpleGraph(); PrintGraph(graph); // 4️⃣ 执行任务 Console.WriteLine("\n🚀 开始执行任务...\n"); var result = await ExecuteGraphAsync(kernel, graph); // 5️⃣ 输出结果 Console.WriteLine($"\n{(result.Success ? "✅" : "❌")} {result.Evidence ?? result.Error}"); Console.WriteLine($"最终进度:{graph.Progress:F1}%"); } /// <summary> /// 创建 Kernel(使用阿里云 DashScope) /// </summary> static Kernel CreateKernel() { var builder = Kernel.CreateBuilder(); builder.AddOpenAIChatCompletion( modelId: ConstParm.modelId, apiKey: ConstParm.apiKey, endpoint: new Uri(ConstParm.endpoint)); return builder.Build(); } /// <summary> /// 创建简单任务图(3 个节点演示) /// </summary> static TaskGraph CreateSimpleGraph() { var graph = new TaskGraph { Goal = "分析项目结构", OriginalPrompt = "帮我分析一下当前目录的 C# 项目结构" }; // 节点 1: 读取项目文件(无依赖) graph.Nodes["read_project"] = new TaskNode { Id = "read_project", Title = "读取项目文件", Description = "读取 .csproj 文件了解项目配置", Owner = "researcher", ToolName = "simple", DependsOn = new List<string>(), RiskLevel = RiskLevel.Low }; // 节点 2: 分析代码结构(依赖节点 1) graph.Nodes["analyze_code"] = new TaskNode { Id = "analyze_code", Title = "分析代码结构", Description = "分析项目中的类和方法", Owner = "researcher", ToolName = "simple", DependsOn = new List<string> { "read_project" }, RiskLevel = RiskLevel.Low }; // 节点 3: 生成报告(依赖节点 2) graph.Nodes["generate_report"] = new TaskNode { Id = "generate_report", Title = "生成分析报告", Description = "生成项目结构分析报告", Owner = "implementer", ToolName = "simple", DependsOn = new List<string> { "analyze_code" }, RiskLevel = RiskLevel.Low }; return graph; } /// <summary> /// 执行任务图(简化版) /// </summary> static async Task<ExecutionResult> ExecuteGraphAsync(Kernel kernel, TaskGraph graph) { var completed = new HashSet<string>(); var chatService = kernel.GetRequiredService<IChatCompletionService>(); int maxIterations = graph.TotalNodes * 2; int iteration = 0; while (iteration < maxIterations) { iteration++; // 1️⃣ 找出可执行的节点(依赖已满足) var readyNodes = graph.Nodes.Values .Where(n => n.CanExecute && n.DependsOn.All(d => completed.Contains(d))) .ToList(); if (readyNodes.Count == 0) break; // 2️⃣ 执行每个节点 foreach (var node in readyNodes) { Console.WriteLine($" → 执行:{node.Title}"); node.Status = AiTaskStatus.Running; try { // 调用 SK 执行 var prompt = $""" 请完成以下任务: 标题:{node.Title} 描述:{node.Description} 请给出简洁的执行结果摘要(50 字以内)。 """; var executionSettings = new OpenAIPromptExecutionSettings { Temperature = 0.7f, // 创造性 (0~1) MaxTokens = 1000, // 最大输出 token TopP = 0.9f // 核采样 }; var response = await chatService.GetChatMessageContentAsync( new ChatHistory(prompt), executionSettings: executionSettings, kernel ); // 成功 node.Status = AiTaskStatus.Done; node.Outputs = response.Content; node.Checkpoint = $"完成于 {DateTime.Now:HH:mm:ss}"; completed.Add(node.Id); Console.WriteLine($" ✓ 完成:{node.Title}"); } catch (Exception ex) { node.Status = AiTaskStatus.Failed; node.Error = ex.Message; node.RetryCount++; Console.WriteLine($" ✗ 失败:{node.Title} - {ex.Message}"); // 简单重试逻辑 if (node.RetryCount >= node.MaxRetries) { return ExecutionResult.Fail($"节点失败:{node.Title}", graph); } } } } // 检查结果 var allDone = graph.Nodes.Values.All(n => n.IsComplete); return allDone ? ExecutionResult.Ok(graph, $"完成 {graph.CompletedNodes}/{graph.TotalNodes} 个任务") : ExecutionResult.Fail("部分任务未完成", graph); } /// <summary> /// 打印任务图 /// </summary> static void PrintGraph(TaskGraph graph) { Console.WriteLine($"📊 任务图:{graph.Goal}"); Console.WriteLine($" 节点数:{graph.TotalNodes}\n"); foreach (var node in graph.Nodes.Values) { var deps = node.DependsOn.Count > 0 ? $" [依赖:{string.Join(", ", node.DependsOn)}]" : ""; Console.WriteLine($" • {node.Title}{deps}"); } } } /// <summary> /// 简单插件示例 /// </summary> public class SimplePlugin { [KernelFunction("read_file"), Description("读取文件内容")] public async Task<string> ReadFile([Description("文件路径")] string path) { if (!File.Exists(path)) throw new FileNotFoundException($"文件不存在:{path}"); return await File.ReadAllTextAsync(path); } [KernelFunction("list_files"), Description("列出目录文件")] public string ListFiles([Description("目录路径")] string path) { if (!Directory.Exists(path)) throw new DirectoryNotFoundException($"目录不存在:{path}"); var files = Directory.GetFiles(path, "*.cs", SearchOption.AllDirectories); return $"找到 {files.Length} 个 C# 文件"; } [KernelFunction("analyze_code"), Description("分析代码结构")] public async Task<string> AnalyzeCode([Description("项目路径")] string path) { await Task.Delay(100); // 模拟分析 return "分析完成:发现 5 个类,20 个方法"; } } }

浙公网安备 33010602011771号