尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

面向LLM编程:从意图解析到工具调用的工程实践指南

面向LLM编程:从意图解析到工具调用的工程实践指南 1. 先搞清楚“面向LLM编程”到底在解决什么实际问题如果你最近在接触大语言模型LLM相关的开发无论是想做个智能客服、文档问答还是想用AI辅助写代码大概率都听过“面向LLM编程”这个词。听起来很玄乎好像是一种全新的编程范式。但根据我自己的实践和观察它最核心的价值其实是解决一个非常具体的问题如何把一个模糊、不确定的自然语言用户请求稳定、可靠地转换成计算机能执行的、结构化的任务流程。这和我们传统的编程思维完全不同。传统编程是“确定性”的你写if-else输入A就必然得到B。但LLM是“概率性”的你问它“今天天气怎么样”它可能直接回答天气也可能先问你“你在哪个城市”。这种不确定性让直接把LLM当做一个函数来调用变得非常脆弱。所以“面向LLM编程”不是让你去学一门新语法而是让你转变设计思路。它的核心是把LLM看成一个具有统计特性的“组件”你需要围绕这个组件的特性比如擅长理解、不擅长精确计算、输出有随机性来构建整个系统。这就像你用电机特性是旋转去设计一辆车和你用内燃机特性是往复运动去设计架构肯定不一样。这篇文章我就结合一些常见的开发场景拆解一下这种编程思路里几个关键的“统计组件”该怎么用以及在实际落地时从环境准备到任务编排再到错误处理每一步需要注意什么。无论你是想快速验证一个AI点子还是打算构建一个更稳定的生产级应用下面的内容都能帮你避开初期最容易踩的那些坑。2. 核心组件拆解任务不是直接问而是拆成链很多人第一次用LLM API习惯就是“用户问什么我就直接把问题扔给模型然后返回结果”。这在Demo阶段没问题但一旦上点复杂度比如用户问“帮我分析一下上周的销售数据并总结成一份报告”这种简单调用模式立刻就会崩盘。输出可能格式混乱可能遗漏关键点甚至可能胡言乱语。面向LLM编程首先就要打破这种“一问一答”的思维。我们需要引入几个核心的、具有不同统计功能的组件把它们像流水线一样组装起来。2.1 组件一意图解析与槽位填充这是整个流程的“调度中心”。它的任务不是直接生成最终答案而是把用户的自然语言指令解析成一个结构化的任务描述。它具体做什么识别意图判断用户想干什么。是“问答”、“总结”、“翻译”、“写代码”还是“数据分析”提取参数从指令里提取关键信息。比如“上周的销售数据”里“上周”是时间参数“销售数据”是数据实体。为什么需要它因为LLM在长上下文里同时做理解和执行效果会下降。先让一个专门的“解析器”LLM或用规则小模型把任务结构化后面的步骤就清晰了。这相当于把“非结构化输入”变成了“结构化查询”。实操建议不要追求一次完美解析可以设计成交互式。例如如果解析器发现“时间”参数不明确它可以生成一个澄清问题“您指的是具体哪一天还是过去7天”让用户确认再把确认后的结构化任务传给下游。输出必须结构化强制要求解析器以JSON格式输出。例如{intent: “generate_report”, “parameters”: {time_range: “last_week”, “data_source”: “sales”}}。这为后续的自动化处理打下了基础。2.2 组件二上下文检索与增强当任务明确后LLM往往需要额外的知识来回答比如公司内部的文档、最新的产品信息、历史上的对话记录。直接把这些海量文本全塞进LLM的上下文窗口既不经济更贵更慢效果也差关键信息被淹没。这就是RAG的核心价值。它作为一个独立组件负责检索根据解析后的任务参数如“销售数据”从向量数据库或知识库中找出最相关的几段文本。增强把这些检索到的文本片段作为“参考材料”和用户的原始问题一起重新组合成一个新的、信息更丰富的提示交给LLM去生成最终答案。为什么需要它它解决了LLM的“静态知识”和“幻觉”问题。让LLM的答案基于你提供的事实依据而不是它内部可能过时或虚构的记忆。实操建议检索质量是关键检索器通常是嵌入模型的效果直接决定最终答案的上限。如果检索不到相关文档LLM编得再像也没用。给检索结果加“引用”在构造给LLM的提示时明确标注哪段话来自哪个文档。甚至可以要求LLM在生成答案时注明依据的源文档编号。这对生产环境追溯答案来源至关重要。2.3 组件三思维链与任务分解对于复杂任务即使有了上下文直接让LLM生成最终答案也可能导致逻辑混乱。这时需要引入“规划”组件。它负责把一个大任务拆解成一系列顺序或并行的子任务。比如“分析销售数据并生成报告”可以拆解为子任务A从数据库获取上周销售数据。子任务B计算关键指标总额、环比、最佳商品。子任务C根据指标用文字描述分析结论。子任务D将结论和指标组织成报告格式。为什么需要它降低单步复杂度每个子任务对LLM来说都更简单、更专注出错率更低。便于插入确定性工具在子任务中可以灵活地调用确定性工具。比如子任务A和B完全可以不用LLM而是用SQL查询和Python计算这样精度是100%。LLM只负责它擅长的C和D自然语言组织和撰写。实现更复杂的逻辑比如循环“直到用户满意为止”、条件判断“如果指标为负则执行预警子任务”。2.4 组件四工具调用这是让LLM从“聊天脑”变成“实干家”的关键。LLM本身不会执行操作但它可以学习在何时、以何种参数去调用一个工具函数。工具是什么任何确定性的函数都可以是工具执行一段代码、查询数据库、调用外部API、操作文件、点击按钮。工具调用的流程向LLM描述可用的工具列表函数名、功能、参数格式。LLM根据当前任务和对话历史决定是否需要调用工具以及调用哪个工具并生成符合格式的参数。系统执行该工具函数获得确定性的结果。将工具执行结果作为新的上下文再返回给LLM让它基于结果继续生成回复。为什么需要它它弥补了LLM在精确性、实时性和执行能力上的不足。LLM负责“想”工具负责“做”。实操建议工具描述要清晰给LLM的工具说明必须无歧义参数类型、格式要明确。模糊的描述会导致错误的调用。做好错误处理工具执行可能失败网络超时、参数错误。要在流程中设计好当工具调用失败时是让LLM重试、选择其他工具还是直接向用户报错。3. 从零搭建环境、框架与第一个链理解了核心组件我们来看如何动手搭一个最简单的链。这里不推荐一上来就追求大而全的框架先从最小可行产品开始。3.1 环境与依赖准备你需要准备两样东西LLM的接入能力和一个能帮你编排组件的框架。1. LLM API密钥国内可选智谱AI、百度文心、阿里通义、月之暗面等。访问其开放平台注册并获取API Key。注意查看计费方式和速率限制。备用方案也可以使用开源的LLM模型在本地部署如ChatGLM3、Qwen等但这需要一定的GPU资源和技术精力。对于快速验证建议先从云API开始。关键点拿到Key后先别急着写代码。用curl或Postman调一下最简单的对话接口确认网络连通、鉴权成功、返回正常。这能排除掉后续90%的基础环境问题。2. 编程框架选择LangChain生态最丰富组件齐全文档多。但抽象层次较高新手可能觉得“黑盒”太多。LlamaIndex专注于RAG场景在数据连接和检索方面非常强大。Semantic Kernel微软出品与.NET生态结合好强调规划和插件工具。简单自制对于理解原理完全可以不用框架就用requests库调用API用Python字典和列表来管理对话历史。这能让你对流程有绝对控制力。我的建议初学者可以从LangChain开始因为它提供了现成的组件能让你快速看到效果。但一定要同时看它的源码或简化示例理解每个组件背后在做什么。3.2 构建你的第一个智能链一个天气查询助手我们用一个经典例子让AI帮你查天气。这需要用到意图解析和工具调用。步骤1定义工具我们先定义一个确定性的工具函数它模拟调用一个天气API。import requests def get_weather(city: str) - str: 根据城市名查询天气信息。 Args: city: 城市名称例如“北京”、“Shanghai”。 Returns: 返回该城市的天气情况字符串。 # 这里为了演示我们模拟一个返回。真实情况应调用如心知天气、和风天气等API。 weather_data { 北京: 北京晴15~25℃微风。, 上海: 上海多云18~27℃东南风3级。, 广州: 广州雷阵雨25~32℃南风4级。 } return weather_data.get(city, f抱歉未找到{city}的天气信息。)步骤2构建提示让LLM学会调用工具我们需要告诉LLM有这个工具可用并规定它思考的格式。这里我们使用一种简单的“思维-行动”格式。# 系统提示设定AI的角色和规则 system_prompt 你是一个有帮助的助手可以调用工具来获取信息。 你可以使用的工具是 - get_weather(city: str): 查询指定城市的天气。 请遵循以下格式响应用户 用户用户的问题 思考你需要先思考是否需要调用工具以及调用哪个工具。 行动如果需要调用工具则输出“工具调用: get_weather(城市名)”。如果不需要则输出“行动: 无”。 结果工具返回的结果或者你的直接回答。 最终答案根据以上所有信息给用户的最终回复。 # 用户问题 user_query “今天北京天气怎么样”步骤3组装并执行流程我们将系统提示、用户问题、历史对话这里为空组合成一个完整的提示发送给LLM。# 假设你已经有了调用LLM API的函数 call_llm def call_llm(messages): # 这里简化处理实际需替换为真实的API调用如OpenAI, ZhiPu等 # 模拟LLM的回复 # 一个“聪明”的LLM应该能根据我们的提示格式进行回复 return 思考用户询问北京天气我需要调用get_weather工具。 行动工具调用: get_weather(北京) 结果等待工具返回... # 第一次调用LLM让它决定行动 messages [ {role: system, content: system_prompt}, {role: user, content: user_query} ] llm_response_1 call_llm(messages) # 解析出LLM想要执行的动作 # 这里需要简单的文本解析来提取“get_weather(北京)” import re action_match re.search(r工具调用:\s*get_weather\((\w)\), llm_response_1) if action_match: city action_match.group(1) # 执行工具 tool_result get_weather(city) # 将工具结果作为新的上下文再次发送给LLM让它生成最终答案 messages.append({role: assistant, content: llm_response_1}) messages.append({role: user, content: f工具执行结果{tool_result}}) llm_response_final call_llm(messages) print(llm_response_final) else: # 如果LLM认为不需要工具直接输出它的回答 print(llm_response_1)步骤4解析最终输出理想情况下LLM的第二次回复会包含“最终答案北京晴15~25℃微风。”。这个简单的链条包含了面向LLM编程的核心思想LLM负责决策要不要调用工具参数是什么系统负责执行确定性的逻辑调用API函数两者协作完成复杂任务。4. 进阶实践处理复杂任务与生产化考量当你把单条链跑通后接下来就要考虑更复杂的任务和如何让它更稳定适合实际使用。4.1 构建一个多步骤的智能分析链假设我们要做一个“行业新闻分析助手”用户输入一个公司名自动抓取近期新闻并分析舆论情感。这个链会包含更多组件意图解析识别出是“新闻分析”意图提取公司名。工具调用1调用新闻爬虫工具或API获取关于该公司的最新新闻列表和摘要。思维链规划LLM判断新闻数量决定是逐条分析还是总结分析。工具调用2对每条新闻或总结文本调用情感分析工具可以是另一个LLM也可以是专门的NLP模型。结果汇总LLM综合所有新闻的情感分析结果生成一份简要报告正面/负面/中性占比主要关注点。这里的关键是状态管理。每个步骤都会产生输出这个输出是下一个步骤的输入。你需要设计一个数据结构比如一个WorkflowState字典来承载整个流程的状态包括原始输入、中间结果、最终输出等。4.2 生产环境必须考虑的要点Demo能跑只是第一步要让服务可靠必须处理以下问题1. 稳定性与错误处理LLM API调用失败网络超时、服务限流、token超限。必须有重试机制如指数退避和熔断降级失败次数过多则暂时禁用该功能或切换备用模型。工具调用失败同上需要有重试和超时设置。LLM输出格式不符合预期这是最常见的问题。你的代码不能假设LLM永远会严格按照你规定的“行动...”格式输出。必须有输出解析和格式校验。例如使用Pydantic库定义你期望的输出结构并让LLM以JSON格式输出然后在代码中尝试解析。解析失败时可以尝试修复如让LLM重试或转入人工处理流程。2. 性能与成本缓存对于相同或相似的查询例如不同用户都问“苹果公司的最新新闻”结果可以缓存一段时间避免重复调用昂贵的LLM和工具API。异步处理对于耗时长如分析大量文档的任务应该设计为异步。用户发起请求后立即返回一个“任务ID”后端处理完成后通过WebSocket、轮询或回调通知用户。Token管理LLM按Token收费。要优化提示词减少不必要的上下文。在RAG场景下精心设计检索策略只返回最相关的片段而不是全部文档。3. 可观测性与评估全链路日志记录每个环节的输入、输出、耗时、Token使用量、工具调用详情。这是排查问题的唯一依据。评估体系如何知道你的AI应用做得好不好需要定义评估指标。对于问答系统可以是“答案相关性”、“事实准确性”。可以通过人工抽检、或用更强大的LLM如GPT-4作为裁判来自动评分。没有评估优化就无从谈起。4. 安全与合规输入过滤防止用户输入恶意提示词进行“提示注入攻击”诱导AI执行不当操作或泄露系统提示。输出过滤对AI生成的内容进行审核过滤不当、偏见或有害信息。数据隐私确保用户上传的文档、对话记录等数据得到妥善保护符合相关法律法规。5. 常见陷阱与调试心法即使理解了所有概念实际开发中还是会遇到各种奇怪问题。下面是我总结的几个高频陷阱和排查思路。5.1 陷阱一把LLM当数据库用现象问一个非常具体、需要精确记忆的知识比如“我们公司Q3的营收具体数字是多少”LLM开始胡编乱造。根因LLM是语言模型不是数据库。它的“知识”是训练数据中统计规律的体现无法保证精确回忆。解决这类问题必须靠RAG。把公司财报、数据库等精准信息存入向量库让LLM去检索并基于检索到的片段回答。5.2 陷阱二提示词过于复杂或模糊现象AI的表现不稳定有时很好有时完全跑偏。根因提示词写得像散文充满了“请尽可能”、“努力地”、“生成一份优秀的”这种模糊词汇。LLM无法理解你的潜台词。解决结构化使用清晰的标记如“### 指令 ###”、“### 示例 ###”、“### 格式要求 ###”。具体化把“优秀”拆解成可衡量的标准如“报告需包含1. 数据概述2. 三个关键发现3. 一项行动建议”。提供示例给出一两个输入输出的例子让LLM明确知道你想要什么格式和风格。5.3 陷阱三忽略上下文窗口限制现象在处理长文档或多轮对话后AI似乎“失忆”了忘记了对话开头的内容。根因所有LLM都有上下文长度限制如4K、8K、32K tokens。超出部分会被丢弃。解决摘要压缩在长对话中定期将历史对话总结成一个简短的摘要用摘要代替原始长文作为新的上下文。选择性记忆只将与当前任务最相关的历史片段放入上下文。外部记忆体将重要的历史信息存入数据库当需要时再通过检索方式引入。5.4 调试心法从简到繁逐层隔离当你的链不工作时不要一头扎进代码里。按顺序排查单组件测试你的get_weather工具函数单独给它输入“北京”能返回正确结果吗你的LLM API调用用最简单的对话提示能正常回复吗提示词测试把你精心设计的、复杂的提示词直接复制到ChatGPT网页版或你所用的模型的官方Playground里用几个典型输入测试看输出是否符合预期如果在这里就不行那问题就在提示词本身。简化链测试先搭建一个只有2个组件的超简链如用户输入 - LLM解析意图 - 打印解析结果。这个能跑通吗查看完整日志打开DEBUG级别的日志查看LLM接收到的完整提示词是什么它返回的完整响应是什么。很多时候问题就出在字符串拼接时多了个空格、少了换行符。模拟LLM响应在调试时可以暂时“Mock” LLM的响应让它固定返回一个你预设的、正确的响应。如果这样链能走通说明问题出在LLM的实际响应不符合你的解析逻辑。你需要调整提示词或加强输出解析的鲁棒性。面向LLM编程本质上是一种新的软件工程实践。它要求开发者同时具备软件架构的严谨性和对概率模型特性的深刻理解。最有效的学习方式不是死记硬背框架API而是亲手搭建一个最简单的链然后不断地增加复杂度在解决一个又一个具体问题的过程中去体会这些“统计组件”如何协同工作。记住你的目标是构建一个可靠的系统而LLM只是这个系统中一个强大但需要精心管理的组件。
返回列表