LangGraph课程生成流程中course_reviewer与save_course步骤标题和描述不一致问题排查求助
LangGraph课程生成流程中course_reviewer与save_course步骤标题和描述不一致问题排查求助
最近几天一直在折腾LangGraph,还属于刚入门的新手。学习过程中我做了一个LangGraph代理,用来根据用户的课程想法和目标受众生成课程内容。前面的步骤都顺顺利利的,直到评估环节——但每次走到save_course步骤时,发现传入的课程标题和描述,竟然和course_reviewer里用的不是同一版!有没有大佬能帮我分析下原因?是不是我在流程设计或者工具调用上写错了什么?
以下是我写的完整代码(基于NestJS+LangGraph+Google Gemini):
import { AIMessage, BaseMessage, HumanMessage } from '@langchain/core/messages'; import { DynamicStructuredTool } from '@langchain/core/tools'; import { Annotation, MemorySaver, StateGraph } from '@langchain/langgraph'; import { ToolNode } from '@langchain/langgraph/prebuilt'; import { Injectable } from '@nestjs/common'; import z from 'zod'; import { ChatGoogleGenerativeAI } from '@langchain/google-genai'; type courseData = { user_thought: string; targeted_audience: string; enable_knowledge_store: boolean; knowledge_store_id: string; course_structure_id?: string; }; @Injectable() export class LangCourseService { private readonly app; constructor() { this.app = this.buildflow(); } private buildflow() { const CourseMetadataAgentAnnotation = Annotation.Root({ messages: Annotation<BaseMessage[]>, userInput: Annotation<string>, courseInput: Annotation<courseData>, courseScore: Annotation<number>, courseOutput: Annotation<{ course_title: string; short_Desc: string; detailed_desc: string; tags: string[]; }>, }); const course_reviewer = new DynamicStructuredTool({ name: 'course_reviewer', description: ``, schema: z.object({ course_title: z.string().describe('Title of the course'), course_description: z.string().describe('Description of the course'), }), func: async ({ course_title, course_description }) => { console.log("Course Title", course_title) console.log("course description", course_description) const prompt = `Evaluate the following course based on clarity, relevance, and detail. Return a score between 1 and 10, and provide a short comment. Course Title: ${course_title} Course Description: ${course_description} Format: Score: <number> Comment: <text> ` const agents = await model.invoke([new HumanMessage(prompt)]); const text = agents.content as string; const match = text.match(/Score:\s*(\d+)/i); const commentMatch = text.match(/Comment:\s*(.*)/i); console.log("Score",match ? parseInt(match[1]) : 0); return { score: match ? parseInt(match[1]) : 0, comments: commentMatch ? commentMatch[1] : 'No comment', }; }, }); const save_course = new DynamicStructuredTool({ name: 'save_course', description: `Saves the course details to the database. * PURPOSE: Saves the course details to the database. * INPUT: A data object containing course details. * OUTPUT: An object with the course ID and a success message. * EXAMPLES: - "Save the course details to the database." - "Store the course information in the system."`, schema: z.object({ course_title: z.string().describe('Title of the course'), course_description: z.string().describe('Description of the course'), }), func: async ({ course_title, course_description }) => { console.log('Saving course:', { course_title, course_description }); return { course_id: 'course-12345', message: 'Course saved successfully', }; }, }); const shouldContinue = ({ messages, }: typeof CourseMetadataAgentAnnotation.State) => { const lastMessage = messages[messages.length - 1] as AIMessage; return lastMessage.tool_calls?.length ? 'tools' : '__end__'; }; const tools = [course_reviewer, save_course]; const tool = new ToolNode(tools); const model = new ChatGoogleGenerativeAI({ apiKey: 'AIzaSyBs1GoNE5PScHx8_U6ErN2Y2CdNVutsIjQ', model: 'models/gemini-2.5-flash', }); const chatModel = async ( state: typeof CourseMetadataAgentAnnotation.State, ) => { const messages: BaseMessage[] = [ new AIMessage(`You are a helpful course generation assistant. Your job is to: 1. Generate a course based on the user's course idea and targeted audience. 2. Use the "course_reviewer" tool to evaluate the generated course. 3. If the score is less than or equal to 5, regenerate a new course and re-evaluate. 4. Repeat until the score is greater than 5. 5. Once a good-quality course is achieved (score > 5), call the "save_course" tool with the course details. Always ensure: - The course is well-structured, relevant to the audience, and clearly described. - You follow the tool call format properly and wait for score feedback before proceeding. - Always use the same course content that was evaluated.`), ...state.messages ]; const response = await model.bindTools(tools).invoke(messages); return { ...state, messages: [...state.messages, response], }; }; const checkpointSaver = new MemorySaver(); return new StateGraph(CourseMetadataAgentAnnotation) .addNode('chatModel', chatModel) .addNode('tools', tool) .addEdge('__start__', 'chatModel') .addEdge('tools', 'chatModel') .addConditionalEdges('chatModel', shouldContinue) .compile({ checkpointer: checkpointSaver }); } }
我本来的逻辑很明确:生成课程后用course_reviewer打分,如果分≤5就重新生成再评估,直到分>5,然后调用save_course把那版通过评估的课程内容存起来——而且在给大模型的提示里还特意强调了"Always use the same course content that was evaluated",但实际运行时,save_course的console里打出来的标题和描述,就是和course_reviewer里的不一样,完全摸不着头脑哪里出问题了...
内容来源于stack exchange
相关产品推荐
相关产品推荐

