尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

从AI Demo到产品:构建感知-决策-行动-学习的智能循环系统

从AI Demo到产品:构建感知-决策-行动-学习的智能循环系统 1. 从“玩具”到“工具”为什么你的AI Demo需要一个Loop最近和几个做AI应用的朋友聊天发现一个挺普遍的现象大家花了不少心思用LangChain、Spring AI或者各种Agent框架搭出了一个Demo界面炫酷功能也跑通了在内部演示或者给投资人看的时候总能收获一片“Wow”。但当你真的把这个Demo交给一个真实用户让他连续用上几天甚至只是几个小时问题就来了。用户可能会抱怨“它怎么老记不住我刚才说了什么”“这个问题我明明纠正过它了怎么又犯同样的错误”“每次都要我重新描述一遍需求太麻烦了。”这时候开发者往往会陷入一种“打地鼠”式的开发循环用户报一个Bug就赶紧去改Prompt发现一个逻辑漏洞就急忙去补规则。Demo看起来越来越“完善”但本质上它还是一个脆弱的、需要人工持续喂养和调试的“展示品”而不是一个能自主运转、持续进化的“产品”。问题的核心就在于缺少了一个Loop循环。在AI应用的语境里Loop远不止是编程里的for或while循环。它是一个完整的、闭环的“感知-决策-行动-学习”系统。一个没有Loop的AI Demo就像一辆没有方向盘和刹车的汽车也许发动机模型马力很强但一旦上路方向无法修正错误无法避免最终只能撞墙。而一个构建了有效Loop的AI应用则具备了“自动驾驶”的雏形它能通过交互感知环境用户输入、系统状态根据既定策略Prompt、工作流做出决策和行动生成回复、调用工具最关键的是它能收集行动的结果用户反馈、执行成功与否并利用这些结果来优化下一次的决策微调Prompt、调整工作流参数、甚至重新规划步骤。所以当我们在谈“构建自己的第一个Loop”时我们谈的是如何给你的AI Demo装上“方向盘”和“刹车”让它从一个静态的、被动的、需要你手把手教的“展示程序”转变为一个动态的、主动的、能在真实环境中学习和成长的“产品原型”。这个转变是AI应用从“玩具”迈向“工具”的关键一步。2. Loop的核心构成不止于代码的“感知-学习”闭环理解Loop我们不能只盯着代码。一个完整的、工程化的Loop通常由四个相互咬合的齿轮构成它们共同驱动着AI应用的智能化演进。2.1 感知层高质量数据输入的起点一切智能的开始于感知。对于AI应用而言感知就是收集所有与交互相关的上下文信息。这远不止是用户当前的一句提问user_input。一个健壮的感知层应该收集对话历史不仅仅是上一条消息而是结构化的、带有角色用户/助手和时序的完整会话记录。这对于需要理解上下文连贯性的任务如多轮对话、复杂问题拆解至关重要。用户画像与状态用户ID、历史偏好、当前所处的业务环节例如在电商App中是“浏览商品”还是“申请售后”。这些信息能帮助模型进行个性化响应。系统状态与环境变量当前时间、地理位置、可用的API服务状态、数据库查询结果等。例如一个订餐Agent需要知道现在是午餐时间且用户常点的那家餐厅正在营业。动作执行结果反馈如果上一步AI调用了某个工具如查询天气、创建待办事项那么工具返回的成功、失败状态以及具体结果数据是至关重要的感知信息。在实际构建时我习惯用一个SessionContext对象来封装所有这些信息。这个对象会在整个Loop生命周期中传递作为AI模型做出决策的“眼睛和耳朵”。# 一个简化的上下文对象示例 class SessionContext: def __init__(self, session_id): self.session_id session_id self.user_id None self.conversation_history [] # 格式[{role: user/assistant, content: ...}, ...] self.user_profile {} self.system_state {} self.last_action_result None # 存储上一次工具调用的结果2.2 决策与规划层从Prompt到工作流引擎这是Loop的大脑负责将感知到的信息转化为一系列可执行的动作计划。初级Demo往往直接把用户输入塞给大模型然后祈祷它能给出完美答案。而产品化的思路是引入“规划”。动态Prompt构建你的Prompt不应该是静态的字符串模板。它应该是一个函数接收SessionContext作为输入动态组装。例如如果conversation_history显示用户正在反复询问同一个概念你的Prompt函数可以自动在系统指令里加入“请用更简单的比喻重新解释”的指令。任务分解与工作流对于复杂请求如“帮我规划一个三天的北京旅行包括预算、交通和景点”决策层不应让模型一次性生成所有内容。更可靠的方式是先让模型或一个专用的规划器输出一个任务列表[“确定用户偏好和预算” “查询北京景点信息” “规划每日行程” “估算交通和住宿费用”]。然后由工作流引擎可以是简单的状态机也可以是Camunda、Airflow等驱动每个子任务的执行。这就是ReAct (Reasoning and Acting)或Plan-and-Execute模式的核心。工具选择Tool Calling决策层需要根据当前上下文判断是否需要以及调用哪个外部工具函数。这通常通过让大模型输出结构化数据如JSON来实现其中包含tool_name和tool_input。# 一个动态Prompt构建函数的例子 def build_system_prompt(context: SessionContext) - str: base_instruction “你是一个旅行助手...” if context.user_profile.get(‘language_preference’) ‘simple’: base_instruction “\n请务必使用简单易懂的语言回答。” if len(context.conversation_history) 10: base_instruction “\n对话已较长请注意总结关键信息避免重复。” # 如果上次工具调用失败可以加入特殊指令 if context.last_action_result and context.last_action_result.get(‘status’) ‘error’: base_instruction f“\n上次尝试{context.last_action_result[‘action’]}时失败原因是{context.last_action_result[‘error’]}。请尝试其他方案或向用户说明。” return base_instruction2.3 执行层可靠地连接外部世界决策层产生了计划执行层负责脚踏实地地完成它。这一层的关键词是可靠性与容错。工具执行封装每个被AI调用的工具函数都应该有清晰的输入输出定义、严格的参数校验和完整的异常处理。一个查询数据库的工具不仅要处理SQL执行成功的情况更要处理好连接失败、查询超时、结果为空等各种边缘情况并返回结构化的结果包括状态码、错误信息、数据给上游。异步与超时控制AI应用经常需要调用网络API必须设置合理的超时时间并考虑使用异步调用避免一个缓慢的外部服务拖垮整个会话。结果规范化不同工具返回的数据格式五花八门。执行层需要将这些结果“翻译”成LLM能够更好理解的、统一的文本或结构化描述以便反馈给决策层进行下一步分析。例如数据库返回的JSON行数据可以格式化为“查询到3条记录1. XX 价格YY2. ...”。一个常见的误区是开发者只关注工具调用成功的那条“快乐路径”。而产品化的思维要求你必须为每一条可能的分支成功、部分成功、失败、超时设计好处理逻辑和反馈信息。2.4 学习与优化层让Loop真正“转”起来这是让Loop从“循环”升级为“增强循环”的灵魂所在。前面的三层构成了一个基本的闭环但如果没有学习和优化这个闭环就是机械的、不会进步的。学习层负责收集运行过程中的“信号”并利用它们来改进系统。显式反馈收集这是最直接的学习信号。在AI回复后提供“点赞/点踩”按钮或者在对话结束时引导用户评分。这些数据是黄金。隐式反馈推断用户虽然没有直接评价但其行为隐含了反馈。例如纠正用户如果紧接着AI的回复说“不对应该是XXX”这就是一个强烈的负反馈信号。追问用户就同一个问题换种方式再问一遍可能意味着之前的回答不够清晰或完整。放弃用户没有继续当前话题而是开启了新话题可能意味着AI没有解决他的问题。采纳用户直接复制了AI生成的代码或文案去使用这是一个正反馈信号。数据管道与评估需要建立一条后台管道将上述反馈数据连同对应的完整SessionContext安全地存储下来。定期例如每天对这些数据进行分析评估哪些类型的Prompt容易导致用户不满哪些工具调用失败率高哪些任务分解逻辑经常出错。优化动作基于分析结果优化就可以有的放矢Prompt迭代针对高频问题改写或增补Prompt中的示例Few-shot。工作流调整发现某个子任务总是失败可以修改任务规划逻辑增加一个备选方案或前置检查。工具增强某个API调用错误率高可能是参数问题也可能是需要更换备用接口。模型微调当积累了足够多、质量高的输入理想输出配对数据时可以考虑对基础模型进行轻量级的微调LoRA, QLoRA让它更贴合你的垂直领域。学习层的工作往往是离线、异步的。它不要求实时改变正在运行的Loop而是通过分析历史数据生成新的策略新Prompt、新工作流配置在下一次部署时更新到系统中从而完成整个“感知-决策-行动-学习”的大循环。3. 实战为一个“智能技术客服Demo”注入Loop假设我们有一个简单的“智能技术客服Demo”它基于GPT-4 API能回答一些关于某个编程框架比如Spring AI的常见问题。目前它只是一个问答机器人用户问它答没有记忆没有优化。现在我们来把它产品化。3.1 第一步构建基础闭环与上下文感知首先我们不能让每次问答都是独立的。我们需要引入一个会话上下文管理器。import json from typing import List, Dict, Any class TechSupportSession: def __init__(self, session_id: str): self.session_id session_id self.history: List[Dict] [] # 存储对话轮次 self.user_tech_stack: List[str] [] # 推断的用户技术栈 self.unsolved_problems: List[str] [] # 本次会话中未解决的问题用户未确认解决 self.feedback_history: List[Dict] [] # 存储反馈 def add_interaction(self, user_input: str, ai_response: str): self.history.append({user: user_input, assistant: ai_response}) # 简单推断技术栈从用户问题中提取关键词 if spring in user_input.lower(): if Spring AI not in self.user_tech_stack: self.user_tech_stack.append(Spring AI) # 历史记录不宜过长可设置窗口如只保留最近20轮 if len(self.history) 20: self.history self.history[-20:] def get_context_for_prompt(self) - str: 将上下文格式化为LLM可理解的文本 context_str “当前会话历史\n” for i, interaction in enumerate(self.history[-5:]): # 最近5轮作为上下文 context_str f“用户{interaction[‘user’]}\n助手{interaction[‘assistant’]}\n” if self.user_tech_stack: context_str f“\n根据对话你正在使用或询问的技术可能涉及{‘ ’.join(self.user_tech_stack)}” if self.unsolved_problems: context_str f“\n注意用户之前提到过这些问题尚未解决{‘ ’.join(self.unsolved_problems)}。请优先关注或跟进。” return context_str现在我们的主Prompt就不再是静态的了def build_tech_support_prompt(user_query: str, session: TechSupportSession) - str: system_message f“””你是一个专业的{‘、’.join(session.user_tech_stack) if session.user_tech_stack else ‘Java/Spring’}技术专家客服。你的回答需准确、清晰、友好。 {session.get_context_for_prompt()} 请基于以上对话历史如果存在来理解当前问题保持回答的连贯性。 如果用户的问题是基于一个未解决的历史问题请先尝试解决它。 当前用户的问题是{user_query} “”” return system_message这样AI在回答时就能“记得”几分钟前聊过什么避免了“金鱼记忆”的尴尬。这就是最基础的状态感知Loop。3.2 第二步引入工具调用与执行层用户的问题可能不仅仅是知识问答还涉及实际操作比如“给我一个Spring AI连接OpenAI的示例代码”。我们可以让AI不仅能回答还能“生成”并“验证”。我们设计一个CodeGeneratorTool和一个SimpleCodeValidatorTool伪验证例如检查语法、导入是否存在。import subprocess import sys class CodeGeneratorTool: staticmethod def run(language: str, task: str) - Dict[str, Any]: # 这里实际上会调用LLM生成代码。为简化我们模拟。 prompt f“用{language}写一个代码片段实现{task}。只返回代码块。” # 假设调用LLM得到 generated_code generated_code “public class Demo { ... }” # 模拟生成的代码 return {“status”: “success”, “action”: “generate_code”, “output”: generated_code} class SimpleCodeValidatorTool: staticmethod def run(language: str, code: str) - Dict[str, Any]: if language “java”: # 非常简单的检查是否有明显的语法错误如缺少分号、括号不匹配这里仅示例 if “public class” in code and “{” in code and “}” in code: # 可以尝试用javac进行简单编译检查需安装JDK try: # 这是一个高风险操作生产环境需要沙箱隔离此处仅为概念演示。 # with open(‘/tmp/Temp.java’, ‘w’) as f: # f.write(code) # result subprocess.run([‘javac’, ‘/tmp/Temp.java’], capture_outputTrue, textTrue, timeout5) # if result.returncode 0: # return {“status”: “success”, “message”: “代码语法检查通过基础”} # else: # return {“status”: “error”, “message”: f“编译错误{result.stderr}”} return {“status”: “success”, “message”: “代码结构看起来基本完整模拟检查”} except Exception as e: return {“status”: “error”, “message”: f“验证过程异常{str(e)}”} else: return {“status”: “error”, “message”: “生成的代码不符合Java类的基本结构”} else: return {“status”: “warning”, “message”: f“暂不支持对{language}的自动验证”}接下来我们需要增强决策层。当用户请求涉及代码时我们让AI先规划一个动作序列。我们可以通过一个简单的“规划Prompt”来实现def plan_actions(user_query: str, session: TechSupportSession) - List[str]: planning_prompt f“”” 用户请求“{user_query}” 请判断是否需要执行以下动作如果需要请按顺序输出动作编号如 1, 3。 可用动作 1. 直接知识问答适用于概念解释、步骤说明等。 2. 生成示例代码适用于“给我一个XX的例子/代码片段”这类请求。 3. 验证生成代码在生成代码后对其做基本检查如果支持。 请只输出动作编号列表用逗号分隔。 “”” # 调用LLM获取规划结果例如返回 “2, 3” planned_action_ids [“2”, “3”] # 模拟规划结果 return planned_action_ids主流程就会变成接收用户输入。调用plan_actions进行规划。按顺序执行动作如果是动作2则调用CodeGeneratorTool如果是动作3则将上一步的结果传给SimpleCodeValidatorTool。将工具执行的结果整合到最终回复中例如“已为您生成示例代码[代码块]。经初步检查代码结构完整。”这样我们就构建了一个规划-执行LoopAI不再只是“动口”还能协调“动手”了。3.3 第三步实现学习与优化层现在我们的客服能记忆、能规划、能执行了。最后一步是让它能“成长”。我们需要收集反馈并用于优化。首先在每次交互后我们主动收集反馈可以在前端界面添加按钮。def collect_explicit_feedback(session_id: str, interaction_index: int, is_helpful: bool, feedback_text: str “”): # 将反馈存储到数据库或文件关联到具体的会话和交互轮次 feedback_record { “session_id”: session_id, “interaction_index”: interaction_index, “is_helpful”: is_helpful, “feedback_text”: feedback_text, “timestamp”: datetime.now().isoformat() } # save_to_db(feedback_record) print(f“反馈已记录{feedback_record}”)其次实现隐式反馈的推断逻辑。这可以在add_interaction方法中增强def add_interaction(self, user_input: str, ai_response: str): # ... 原有记录历史逻辑 ... # 隐式反馈推断 last_interaction self.history[-2] if len(self.history) 2 else None current_interaction {“user”: user_input, “assistant”: ai_response} if last_interaction: # 规则1用户纠正 - 如果用户输入包含“不对”、“错了”、“应该是”等且指向上一条AI回复 correction_keywords [“不对” “错了” “不是这样的” “应该是” “纠正一下”] if any(keyword in user_input.lower() for keyword in correction_keywords): self.feedback_history.append({ “type”: “implicit_correction”, “target_interaction”: len(self.history)-2, “inferred_sentiment”: “negative”, “evidence”: user_input }) # 将上一个问题标记为“未解决” problem_desc last_interaction[‘user’][:50] # 截取前50字符作为问题描述 if problem_desc not in self.unsolved_problems: self.unsolved_problems.append(problem_desc) # 规则2用户追问 - 用户紧接着问“然后呢”、“具体怎么做”可能上一步回答不够具体 if user_input.lower() in [“然后呢” “具体怎么做” “还有呢”]: self.feedback_history.append({ “type”: “implicit_follow_up”, “target_interaction”: len(self.history)-2, “inferred_sentiment”: “neutral”, # 中性可能是需要更多细节 “evidence”: user_input })有了反馈数据显式隐式我们就可以定期进行离线分析了。例如写一个每周运行的脚本def weekly_feedback_analysis(): # 1. 从数据库拉取过去一周的反馈数据 # feedback_data fetch_feedback_from_db(last_7_days) # 2. 分析负面反馈集中的问题类型 # negative_feedback filter_by_sentiment(feedback_data, “negative”) # 通过聚类或关键词提取找出常被用户纠正或点踩的问题模式例如“如何配置SSL证书”、“Bean注解报错” # 3. 检查对应问题的AI原始回复和使用的Prompt # 找出导致这些问题的Prompt模式或知识盲区。 # 4. 优化措施 # a. 知识库补充针对高频问题在向量知识库中添加更准确、更详细的文档片段。 # b. Prompt模板优化在系统指令中为特定问题类型增加更明确的回答指南或示例。 # c. 工具链增强如果发现很多问题需要执行某个特定操作如检查配置文件可以考虑开发一个新的验证工具并让AI在回答相关问题时主动调用它。 print(“完成本周反馈分析已生成优化建议。”) # 将优化建议如新的Prompt模板、需要添加的知识点保存下来供开发人员审核和部署。通过这个每周运行的“学习Loop”我们的客服系统就能像滚雪球一样越用越聪明。最初它可能只会回答文档里的问题但通过不断吸收用户的纠正和追问它会逐渐覆盖更多边缘案例回答也会变得更加精准和实用。4. 避坑指南构建Loop时最容易踩的五个坑在从零开始构建第一个Loop的过程中我踩过不少坑也见过很多团队在这里跌倒。下面这五个问题如果你能提前意识到并避开能省下至少80%的调试时间。4.1 状态管理混乱会话数据成了“泥球”这是初期最常见的架构问题。开发者图省事把用户ID、对话历史、临时变量全都塞进一个全局字典或者附着在请求对象上到处传递。很快代码就变得难以维护状态在哪里被修改的完全理不清。正确做法从一开始就设计一个清晰的会话Session对象如我们前面示例中的TechSupportSession。这个对象是会话状态唯一的权威来源。所有需要访问或修改会话状态的模块如上下文构建器、工具执行器、反馈收集器都通过这个对象接口来进行。并且要考虑会话的持久化。用户刷新页面或半小时后回来之前的对话历史不应该消失。最简单的方案是使用Redis或数据库以session_id为键进行存储和读取。4.2 工具调用缺乏防护变成系统漏洞让AI调用外部工具执行代码、访问数据库、调用API是强大的也是危险的。一个没有防护的CodeGeneratorTool如果被恶意用户诱导生成并执行了rm -rf /或无限循环后果不堪设想。核心防护措施沙箱环境任何代码执行类工具必须在完全隔离的沙箱如Docker容器、安全的云函数环境中运行并严格限制资源CPU、内存、运行时间。权限最小化工具执行身份应具有完成其功能所需的最小权限。数据库查询工具只能用只读账号。输入验证与净化对AI传递给工具的所有参数进行严格校验。比如一个调用requests.get(url)的工具必须检查url是否为允许的内网或可信白名单域名防止SSRF攻击。用户确认对于高风险操作如删除数据、发送邮件在执行前应设计一个确认环节让AI生成一个确认信息给用户用户明确同意后再执行。4.3 过度依赖单一LLM进行复杂规划让LLM自己规划一系列动作Plan-and-Execute听起来很美好但在复杂场景下LLM可能会产生不切实际、无法执行甚至循环依赖的计划。比如它可能规划出一个需要“未来”步骤结果作为输入的步骤。稳健策略采用分层规划或模板化工作流。对于你业务中非常明确、固定的复杂流程如“用户投诉处理流程”不要完全让LLM自由发挥。可以预先定义好几个标准的工作流模板Workflow Template每个模板由一系列预定义的步骤组成。LLM的角色是“识别”用户输入属于哪个模板并填充模板中的参数如投诉单号、问题类型然后由可靠的工作流引擎如Camunda、Airflow甚至是你自己写的一个状态机来驱动执行。LLM只在某些决策点介入。对于需要灵活规划的场景可以让LLM先生成一个高阶计划大纲然后由一个更简单的、基于规则的“计划验证器”来检查其可行性和逻辑顺序修正后再执行。4.4 忽略反馈数据的质量与偏见“有反馈数据就能优化”是一个天真的想法。低质量或有偏见的反馈数据会导致系统越优化越差。显式反馈稀疏大多数用户懒得点击“点赞/点踩”。你收集到的显式反馈可能只占交互量的1%而且这1%很可能来自极端满意或极端不满的用户不能代表沉默的大多数。隐式反馈噪声大我们前面用规则推断“用户纠正”但规则是粗糙的。用户说“不对”可能是在纠正AI也可能是在自言自语或纠正自己之前的描述。直接把这些都当作负反馈会引入大量噪声。幸存者偏差你能收集到反馈的会话都是用户坚持到了最后的。那些因为AI回答太差而直接关闭页面或离开的用户你没有任何数据。这会导致你低估问题的严重性。应对方法多维度信号融合不要只依赖一种反馈。结合显式评分、隐式行为停留时间、是否完成目标、业务指标转化率、解决率进行综合判断。主动抽样与人工评估定期如每天随机抽取一定比例的会话记录由人工进行标注和评估。这是校准自动反馈系统、发现盲区的黄金标准。A/B测试任何重大的Prompt修改或工作流调整在上线全量前先进行小流量的A/B测试用真实的业务数据而不仅仅是反馈分数来判断优劣。4.5 陷入“Prompt炼金术”的无限循环这是AI应用开发中最容易消耗时间的陷阱效果不好改Prompt改了还不好再改团队可能花费数周时间在调整Prompt的措辞、顺序、Few-shot示例上陷入一种“玄学”调试。破局思路建立假设驱动的迭代流程。定义清晰的成功指标不要用“感觉更好”来评估。定义可量化的指标如“首次回复解决率”、“用户纠正率”、“平均会话轮次”。提出假设效果不好先分析日志提出一个具体假设。例如“用户关于‘配置SSL’的问题解决率低可能是因为知识库中文档过于陈旧。”设计实验验证针对这个假设设计改进方案。不是去改主Prompt而是去更新“配置SSL”相关的知识库文档或者为这类问题设计一个专用的工具如SSL配置检查脚本。测量与对比在小流量或特定问题上测试你的改进方案严格对比改进前后的指标变化。归因与沉淀如果指标提升确认是否确实是你假设的原因起了作用然后将这个有效的模式更新特定知识、增加专用工具沉淀为一种标准操作流程。记住Prompt工程很重要但它只是工具箱里的一种工具。当Prompt调整边际效益递减时你应该果断转向其他杠杆优化知识库、增加可靠的工具、改进工作流设计、甚至考虑微调模型。构建Loop的目标是打造一个稳健的系统而不是培养一个最会“猜”你心思的Prompt。
返回列表