拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于LangGraph与WorkBuddy构建AI编程学习型Skill:从代码生成到引导式教学

1. 项目概述从“贴Prompt”到“学技能”的转变最近在AI编程社区里一个现象越来越普遍很多人拿到一个复杂的编程任务第一反应不是去理解问题、设计算法而是打开ChatGPT或者Claude复制粘贴一段模糊的需求描述然后期待AI直接吐出完美的代码。这被戏称为“贴Prompt工程师”。代码是跑起来了但问题解决了吗背后的逻辑懂了吗下次遇到类似问题能举一反三吗恐怕很难。这种模式最大的弊端是它剥夺了学习过程中最核心的“思考”和“构建”环节让开发者变成了一个被动的代码接收者而非主动的问题解决者。我一直在想有没有一种方式能让AI编码工具不只是“代劳”而是变成一个真正的“学习伙伴”它应该引导你思考帮你拆解问题在你卡壳时给予提示而非直接给答案最终让你自己构建出解决方案并在这个过程中真正掌握知识和技能。这就是我尝试用WorkBuddy这个平台来构建一个“学习型Skill”的初衷。WorkBuddy本身是一个强大的AI代理Agent开发与运行平台它允许你通过可视化的方式或代码如LangGraph来编排复杂的AI工作流。而“Skill”在WorkBuddy的语境里可以理解为一个封装好的、可复用的智能能力单元它可以是数据分析、代码生成、文档解读等任何任务。我这个Skill的目标很明确将一次性的“贴Prompt求代码”行为转变为一个结构化的、可交互的、旨在教会你某个编程概念或解决模式的“学习会话”。它不再是一个黑盒代码生成器而是一个循循善诱的编程导师。接下来我会详细拆解这个Skill的设计思路、核心实现、以及如何让它真正起到教学作用。2. 核心设计思路构建引导式学习工作流要让AI编码变成学习关键在于改变交互模式。传统的“输入-输出”模式必须被打破取而代之的是一种“引导-思考-验证-迭代”的对话式学习循环。我的设计核心是围绕LangGraph来构建一个有状态的、多步骤的工作流。2.1 从线性对话到有向图工作流普通的聊天是线性的你一句我一句上下文容易丢失目标也容易偏离。而LangGraph允许我们将一次学习会话建模成一个有向图Graph。图中的节点Node代表不同的“教学阶段”边Edge则定义了根据当前状态State如何从一个阶段流转到下一个阶段。对于这个学习型Skill我设计了以下几个核心节点问题澄清与目标设定节点当用户提出一个模糊的需求如“帮我写个爬虫”Skill不会立刻开始写代码。它会先反问一系列问题来明确边界目标网站是什么需要爬取哪些数据字段是否有反爬机制需要考虑预期的数据格式是什么这个节点的目的是强迫用户先思考问题的全貌而不是丢出一个模糊的指令。概念拆解与方案设计节点在明确目标后Skill会引导用户将大问题拆解成几个关键的子任务或技术概念。例如对于爬虫可能会拆解为网络请求requests库、HTML解析BeautifulSoup或lxml、数据存储CSV/数据库、异常处理与速率控制。对于每个子任务Skill会简要解释其核心概念和常用工具并询问用户是否理解或者是否有更倾向的实现方式。分步实现与代码共写节点这是核心环节。Skill不会一次性给出完整代码。它会按照拆解后的子任务一个接一个地引导用户实现。例如它会先问“我们先来解决网络请求部分你知道怎么用requests.get获取网页内容吗如果知道你可以尝试写下这行代码如果不知道我可以先给你看一个最简单的例子。” 然后根据用户的输入可能是尝试的代码也可能是“请展示例子”Skill会给出针对性的反馈修正错误、解释原理、或者展示一个基础示例并鼓励用户在其基础上修改。代码审查与优化建议节点当所有部分代码都“拼凑”出来后无论是用户写的还是Skill辅助完成的Skill会引导用户一起 Review 这段代码。它会从可读性、健壮性、效率等角度提出问题“这里用try...except来处理网络超时是不是更好”、“这个解析逻辑如果网页结构变了容易出错有没有考虑用更稳定的选择器” 这个过程旨在培养用户的代码质量和工程化思维。总结归纳与知识延伸节点最后Skill会带领用户回顾整个解决过程总结用到的关键库、核心函数和设计模式。并且它会提出一两个相关的延伸问题或变种场景鼓励用户尝试独立解决从而巩固学习成果。这个图状工作流确保了学习过程的结构化和目标导向用户无法跳过思考直接拿到答案必须参与每一个环节。2.2 State的设计记录学习旅程的上下文LangGraph的State状态是贯穿整个工作流的数据容器。对于学习型SkillState需要精心设计以完整记录一次学习会话的上下文。我的State结构大致包含以下字段from typing import TypedDict, List, Annotated from langgraph.graph.message import add_messages import operator class State(TypedDict): # 对话消息历史用于给LLM提供上下文 messages: Annotated[List, add_messages] # 用户原始问题 original_query: str # 经过澄清后明确的需求描述 clarified_requirements: str # 拆解出的子任务列表 sub_tasks: List[str] # 当前正在处理的子任务索引 current_task_index: int # 用户针对当前子任务已尝试的代码片段 user_attempt_code: str # Skill针对当前步骤提供的示例或指导代码 guidance_code: str # 最终整合的代码草案 draft_code: str # 记录用户在本会话中表现出的知识薄弱点用于个性化 knowledge_gaps: List[str] # 会话是否已进入结束状态 is_finished: bool通过这样一个丰富的State工作流中的每个节点都能清楚地知道“我们现在在哪”、“我们已经做了什么”、“用户当前卡在什么地方”从而做出最合适的教学决策。例如“分步实现节点”会查看current_task_index和sub_tasks来决定当前要教什么并参考user_attempt_code来决定是给予鼓励还是纠正错误。2.3 教学策略与LLM提示词工程有了工作流和状态还需要一个“大脑”来在每个节点做出智能的教学决策。这就是大语言模型LLM的角色。但直接用一个通用指令调用LLM是远远不够的必须为每个节点设计高度定制化的、角色明确的System Prompt。注意这里的Prompt不再是用户直接粘贴的模糊需求而是我作为Skill开发者精心设计的、用于指导AI如何扮演“导师”的剧本。这是“用AI构建学习工具”和“自己贴Prompt”的本质区别。以“概念拆解与方案设计节点”的System Prompt为例你是一位经验丰富的软件工程师现在担任编程导师。你的任务不是直接解决问题而是引导学生自己找到解决方案。 当前学生的问题是{clarified_requirements} 你的目标是 1. 将这个问题拆解成3-5个关键的、循序渐进的子任务或技术模块。 2. 对每个子任务用一两句话解释其核心目的和涉及的主要编程概念或库。 3. 你的输出必须严格遵循以下JSON格式 { sub_tasks: [ {name: 任务1名称, concept: 涉及的核心概念解释}, ... ], next_question: 一个用于确认学生理解或引导选择的问题例如以上拆解你觉得清晰吗或者你对哪个部分特别感兴趣想先从它开始 } 记住拆解的目的是搭建学习脚手架而不是吓退学生。确保子任务粒度适中逻辑顺序合理。这个Prompt明确了AI的角色导师、目标引导拆解、输出格式结构化JSON并强调了教学理念搭建脚手架。这样LLM的输出就是稳定、可控、符合教学设计的可以直接被后续节点解析和使用。相比之下“分步实现与代码共写节点”的Prompt会更侧重于交互和反馈你是编程教练正在指导学生完成子任务{current_sub_task}。 学生之前已经了解了相关概念{task_concept}。 学生刚刚尝试了以下代码{user_attempt_code}。 请按以下步骤工作 1. **分析**首先评估学生代码。如果为空进入第2步如果有代码检查其语法和逻辑找出亮点和主要问题。 2. **反馈**给出具体、积极的反馈。先肯定任何正确的部分然后指出最关键的一个改进点。 3. **引导**不要直接给出完整正确的代码。提供一个最简化的、针对当前核心问题的代码片段作为“提示”或者提出一个引导性问题让学生补充关键行。 4. **输出格式** { feedback: 你的反馈文本, hint_code: 可选的、简短的提示代码最多5行, guiding_question: 一个引导学生下一步操作的问题 }通过这种精细的Prompt设计AI的行为被约束在“教练”的轨道上避免了它一时兴起把答案全盘托出保证了学习的互动性和渐进性。3. 使用LangGraph实现核心工作流设计思路清晰后实现就变成了用LangGraph将上述节点和状态连接起来。这里我选择用Python和LangGraph库进行开发因为它对状态管理和流程编排的支持非常直观。3.1 定义状态与图结构首先我们需要定义前面提到的State类型并初始化图。from langgraph.graph import StateGraph, END from typing import TypedDict, List, Annotated from langgraph.graph.message import add_messages # 定义状态类型同前文此处略 class State(TypedDict): ... # 初始化工作流构建器 workflow StateGraph(State)3.2 实现各个节点函数每个节点都是一个普通的Python函数它接收当前State调用LLM使用设计好的Prompt然后更新State并返回。以“问题澄清节点”为例import json from langchain_openai import ChatOpenAI # 或其他LLM llm ChatOpenAI(modelgpt-4-turbo-preview, temperature0.2) # 温度调低保持稳定性 def clarification_node(state: State): 节点1问题澄清 messages state[messages] # 构建一个强引导性的系统提示 system_prompt f 用户提出了一个编程需求{state[original_query]} 你作为导师需要先帮他澄清需求而不是急于解决。 请提出2-4个最关键的问题以帮助明确项目范围、输入输出、技术约束等。 你的输出必须是纯JSON格式{{questions: [问题1, 问题2, ...]}} # 调用LLM response llm.invoke([{role: system, content: system_prompt}] messages) # 解析LLM的JSON输出 try: clarification json.loads(response.content) questions clarification.get(questions, []) except: questions [你能更具体地描述一下你想要实现的功能吗] # 更新状态将澄清问题添加到消息历史并设置一个标志等待用户回答 new_messages messages [{role: assistant, content: f为了更好地帮你我需要先了解一些细节\n \n.join([f{i1}. {q} for i, q in enumerate(questions)])}] # 注意这里不会直接更新clarified_requirements需要等用户在下轮对话中回答 new_state { messages: new_messages, clarification_questions: questions, # 临时存储问题 # ... 其他状态保持不变或设为初始值 } return new_state其他节点如design_node方案设计、coding_node代码共写也遵循类似模式只是System Prompt和状态更新逻辑不同。3.3 编排节点与条件流转将所有节点添加到图中并定义它们之间的流转边。LangGraph的强大之处在于支持条件边Conditional Edge可以根据State的内容决定下一步走哪里。# 添加节点 workflow.add_node(clarify, clarification_node) workflow.add_node(design, design_node) workflow.add_node(code, coding_node) workflow.add_node(review, review_node) workflow.add_node(summarize, summarize_node) # 设置入口点 workflow.set_entry_point(clarify) # 添加边包括条件边 from langgraph.graph import END def route_after_clarify(state: State): 在澄清后判断用户是否已回答了所有澄清问题 # 这里需要一个逻辑来判断对话历史中用户是否已回复 # 简化处理如果消息数大于一定值且最后一条是用户的则认为已回答 messages state[messages] if len(messages) 2 and messages[-1][role] user: return design # 进入设计阶段 else: return clarify # 继续澄清或进入一个等待用户输入的节点 def route_after_design(state: State): 设计完成后进入第一个编码子任务 if state.get(sub_tasks): state[current_task_index] 0 # 初始化索引 return code else: return design # 设计未完成返回 def route_after_code(state: State): 一个子任务编码后判断是否还有下一个 idx state[current_task_index] tasks state[sub_tasks] if idx len(tasks) - 1: state[current_task_index] idx 1 return code # 继续下一个编码任务 else: return review # 所有任务完成进入审查 def route_after_review(state: State): 审查后判断是否需要进行修改迭代 if state.get(needs_revision, False): # 假设State中有一个需要修订的标志 state[current_task_index] state[revision_target] # 设定要修订的子任务索引 return code else: return summarize # 添加条件边 workflow.add_conditional_edges( clarify, route_after_clarify, {clarify: clarify, design: design} # 可能的目标节点映射 ) workflow.add_edge(design, code) # 设计完直接进入编码由route_after_design控制具体跳转逻辑 workflow.add_conditional_edges(code, route_after_code, {code: code, review: review}) workflow.add_conditional_edges(review, route_after_review, {code: code, summarize: summarize}) workflow.add_edge(summarize, END) # 编译图 app workflow.compile()这样一个具备基本智能导航能力的学习工作流就构建完成了。app可以被集成到WorkBuddy平台中作为一个可调用的Skill。3.4 在WorkBuddy中部署与集成WorkBuddy平台通常提供多种集成方式。最简单的是通过其提供的SDK或API将编译好的LangGraph应用包装成一个Web服务端点。创建Skill项目在WorkBuddy开发者界面创建一个新的Skill选择“自定义代码”或“LangGraph”类型。封装Graph应用编写一个简单的FastAPI或Flask应用将app包装起来。这个应用接收用户输入初始化State调用app.stream()或app.invoke()来推进工作流并返回AI的响应。from fastapi import FastAPI from pydantic import BaseModel app_fastapi FastAPI() class UserInput(BaseModel): message: str session_id: str | None None # 用于支持多轮对话会话 app_fastapi.post(/learn-to-code) async def learn_endpoint(input: UserInput): # 根据session_id获取或初始化State config {configurable: {session_id: input.session_id or default}} # 将用户输入作为消息添加到状态 inputs {messages: [(user, input.message)]} # 流式或一次性调用图 for output in app.stream(inputs, config): # 处理输出通常是最后一个节点更新的State pass # 从最终State中提取AI的最新回复返回给前端 final_message output[messages][-1].content return {response: final_message}配置Skill元信息在WorkBuddy中配置Skill的名称、描述、输入输出参数对应上面的UserInput模型以及部署的端点URL。测试与发布在WorkBuddy的测试界面中像真实用户一样与你的Skill交互验证整个学习流程是否顺畅。调整各节点的Prompt和流转逻辑直到体验满意然后发布。发布后其他用户就可以在WorkBuddy的Skill市场或聊天界面中像调用一个普通AI助手一样调用你这个“编程导师”Skill但体验是完全不同的引导式学习。4. 让学习真正发生的核心技巧与避坑指南构建一个能运行的Skill只是第一步如何让它真正有效教学才是挑战。以下是我在开发和迭代过程中总结的核心技巧和踩过的坑。4.1 技巧一设计“恰到好处”的挑战梯度这是最考验教学设计的部分。挑战太大用户会沮丧放弃挑战太小用户学不到东西。我的策略是基于State动态调整在State中记录knowledge_gaps。如果发现用户在“异常处理”概念上反复出错那么在后续涉及类似概念的环节可以适当降低初始提示的难度或者提供更详细的类比解释。提供“脚手架”与“撤脚手架”初始阶段提供较多的代码框架和提示脚手架。随着用户对某个子任务表现出理解例如能正确写出关键函数名在下一个类似任务中逐步减少提示撤走脚手架鼓励独立完成。多路径支持在“代码共写节点”准备多种响应模板。对于用户提交的空白或“我不知道”给出一个基础示例对于有尝试但出错的代码进行针对性纠错对于基本正确的代码提出一个优化挑战。这需要LLM的Prompt具备很强的分支判断逻辑。4.2 技巧二强化反馈的“教学性”而非“评判性”LLM很容易直接说“这里错了应该这样写”。但这对于学习无益。必须训练它通过Prompt给出具有教学意义的反馈。错误归因反馈应指向错误的原因而不仅仅是现象。不说“你的循环索引越界了”而说“因为列表data的长度是5你的循环for i in range(1, 6):试图访问data[5]而有效索引是0到4。这通常是因为混淆了‘数量’和‘索引’的关系。”关联概念将具体错误与之前讲解过的编程概念联系起来。“这里出现的KeyError正好是我们之前讨论过的‘字典键不存在’的情况还记得可以用.get()方法提供默认值来避免吗”鼓励性语言多用“很好的尝试”、“这个思路是对的不过有个小细节”、“你已经抓住了核心我们再完善一下……”这样的语言。积极反馈能极大提升学习动力。4.3 技巧三管理会话状态与长期记忆一次学习可能无法掌握所有知识。一个理想的Skill应该能记住用户的历史交互。WorkBuddy会话状态利用WorkBuddy平台提供的会话管理将session_id与LangGraph的configurable配置关联确保同一用户多次对话能恢复状态。知识图谱化在State中不仅记录当前任务还可以尝试用简单的结构记录用户暴露出的knowledge_gaps如“不理解递归基”、“不熟悉列表推导式”。虽然目前实现较复杂但这是一个方向。当下可以在总结节点明确告诉用户“本次会话中我发现你在‘X’和‘Y’概念上可能需要更多练习建议你下次可以尝试做一个涉及这些概念的小项目。”提供“存档”与“复盘”在会话结束时Skill可以生成一份简单的学习报告包括解决的问题、用到的关键代码片段、以及核心概念总结并鼓励用户保存。这给了用户实实在在的获得感。4.4 常见问题与排查实录在开发过程中我遇到了不少典型问题这里分享排查思路问题LLM不遵循输出格式导致节点解析失败。现象在design_node期望LLM输出JSON但它却输出了一段自然文字。排查首先检查System Prompt是否明确要求了JSON格式并用json ...包裹示例。其次检查LLM的temperature参数是否过高建议低于0.3。最后在代码中必须添加健壮的异常处理try...except当解析失败时可以回退到一个预定义的默认拆解方案或者友好地提示用户“让我重新组织一下思路”并重新调用LLM。解决在Prompt中加入更严格的指令如“你必须输出JSON且只能是JSON不要有任何其他解释文字。”同时使用LangChain的StructuredOutputParser等工具可以强制格式但可能会牺牲一些灵活性。问题工作流陷入死循环或在一个节点卡住。现象用户和AI在“澄清需求”环节来回问答始终无法进入下一阶段。排查检查条件边函数route_after_clarify的逻辑。它判断“用户已回答”的条件可能过于简单或苛刻。例如仅通过消息角色判断可能不准因为用户可能回复“嗯”、“好的”这类无实质内容的语句。解决优化状态流转逻辑。可以引入一个计数器或标志位。例如在State中设置clarification_rounds达到一定轮次后即使问题未完全清晰也强制进入下一阶段并在设计节点补充说明“基于目前信息我假设你的需求是...我们先按此推进过程中可以随时调整”。另一种方法是让LLM参与判断在澄清节点最后让LLM输出一个“是否已足够清晰”的布尔值作为状态的一部分。问题Skill反应慢用户体验不流畅。现象每个节点都需要调用LLM导致多轮对话延迟明显。排查LangGraph的流式响应app.stream可以边生成边返回改善体验。但根本延迟在于LLM调用和复杂Prompt的处理。解决a)优化Prompt精简System Prompt移除冗余指令。b)缓存对于常见的、固定的教学回应如某个概念的基础解释可以不调用LLM直接从本地缓存中返回。c)模型选择在非核心推理环节考虑使用更快、更便宜的模型如GPT-3.5-Turbo。d)异步处理确保你的Web服务框架如FastAPI是异步的避免阻塞。问题教学节奏单一无法适应不同水平的用户。现象新手觉得太快有经验的开发者觉得太啰嗦。排查初始设计时Skill的教学节奏是固定的缺乏个性化。解决在会话开始时增加一个“水平评估”环节。可以简单地问用户“请用1-5分自评你对Python的熟悉程度1为新手5为专家”或者通过第一个编码任务中用户的尝试代码来动态评估其水平。将评估结果存入State后续节点的Prompt可以根据此水平动态调整解释的深度和代码提示的完整度。5. 效果评估与未来迭代方向构建并运行这个Skill一段时间后我通过观察用户交互记录和收集反馈来评估其效果。积极效果用户参与度提升与直接索要代码相比用户在与Skill的交互中明显更活跃提问更具体尝试自己写代码的意愿更强。概念留存率提高在后续的简单测试中使用过该Skill学习某个概念如装饰器的用户比仅阅读文档的用户在解决变体问题时表现更好。正向反馈用户普遍反映“感觉真的在学东西而不是复制粘贴”、“卡住的时候给的提示正好是需要的不是直接给答案”。待改进点与迭代方向个性化深度不足目前的knowledge_gaps记录还很初步。未来可以探索与向量数据库结合将每次会话中涉及的知识点、用户错误都嵌入存储构建更精细的用户知识画像从而实现跨会话的个性化学习路径推荐。领域扩展目前主要针对通用Python编程。可以创建垂直领域的Skill如“Web开发学习Skill”、“数据分析学习Skill”其中内置了该领域特有的任务拆解模式、最佳实践库和案例库。从“学”到“练”可以集成一个轻量级的代码运行环境如基于Jupyter内核允许用户在对话中直接运行、调试当前编写的代码片段并获得实时错误反馈形成“学-练-反馈”的闭环。社区化与UGC允许高级用户或教师基于这个框架定制和发布自己的“教学图”Teaching Graph分享针对特定算法、框架的教学方法形成一个可共享、可复用的教学Skill生态。回过头看这个过程本身就是一个绝佳的学习案例。我不再是“贴Prompt”去让AI帮我写一个Skill而是深入理解了LangGraph的状态管理、工作流设计、提示词工程以及如何将教育学的理念如脚手架理论、最近发展区融入AI应用。最终产出的不仅仅是一个工具更是一套关于“如何用AI辅助深度学习”的方法论。这或许才是面对AI时代我们开发者更应该聚焦的方向不是让自己被替代而是学会驾驭它去创造能提升他人包括自己认知和能力的增强型工具。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门