LLM应用开发模块化工具箱:从设计模式到实战构建智能体
1. 项目概述:一个面向LLM应用开发的模块化工具箱
如果你正在尝试构建基于大语言模型的应用,无论是想做一个能自动处理邮件的智能助手,还是一个能分析文档并生成报告的系统,你大概率会面临一个共同的起点:从零开始。这意味着你需要自己设计系统提示词、编写工具调用逻辑、规划智能体的工作流,这个过程充满了重复造轮子的低效和不确定性。今天要聊的这个项目,llm_system_template_agents_skills_patterns_tools_prompts,正是为了解决这个痛点而生。它不是一个成品应用,而是一个高度模块化的“工具箱”或“脚手架”,为你提供了一套经过验证的、可复用的组件库,让你能像搭积木一样快速构建起自己的LLM应用。
这个项目的核心价值在于“解耦”和“组合”。它将一个复杂的LLM系统拆解为几个清晰的层次:系统提示词定义了AI的“角色”和基础行为准则;工具赋予了AI调用外部API或执行特定函数的能力;技能是更复杂的、可复用的任务单元;智能体则负责协调这些组件来完成一个完整的目标;而模式则是解决某一类问题的标准化工作流模板。这种设计思想,对于任何希望提升开发效率、保证代码质量、并追求架构清晰的开发者来说,都极具吸引力。无论你是刚接触LLM编程的新手,还是正在为团队寻找标准化方案的资深工程师,这个项目提供的模板和模式都能为你节省大量前期调研和基础编码的时间。
2. 核心架构与设计模式深度解析
2.1 分层架构:从原子工具到智能工作流
理解这个项目的关键在于把握其清晰的分层架构。这并非简单的代码堆积,而是一种经过深思熟虑的、旨在降低复杂性的设计。我们可以将其类比为建造一栋房子:工具是砖块和木材,技能是预制好的门窗和楼梯,模式是客厅或厨房的标准设计图,而智能体则是统筹整个建造过程的项目经理。
工具层是最基础的原子操作。一个工具通常对应一个具体的函数或API调用,例如“获取当前天气”、“搜索网络信息”、“读写本地文件”。在项目中,这些工具被标准化封装,确保它们有统一的输入输出接口,便于被上层组件调用。技能层则构建在工具之上,它代表了一个完整的、可复用的任务。例如,“总结一篇长文章”这个技能,内部可能依次调用了“读取文件”、“调用LLM总结”、“保存结果”等多个工具。技能封装了实现细节,对外提供更高级、更语义化的接口。
模式层是该项目最具特色的部分。它借鉴了软件工程中“设计模式”的思想,针对LLM应用中的常见场景,提炼出可复用的解决方案模板。例如,“问答模式”可能定义了一个标准的流程:接收用户问题 -> 检索相关知识库 -> 组织答案 -> 安全审查 -> 返回结果。直接套用这些模式,开发者就无需重新设计工作流,只需填充具体的实现细节(如自己的知识库接口)。智能体层是最高级的协调者。一个智能体可以理解复杂目标,并动态地组合调用多个技能、工具,或遵循某个模式来完成任务。例如,一个“研究助手”智能体,在接到“分析某个技术趋势”的任务后,可能会自动触发“网络搜索”技能获取资料,再用“多文档总结”模式提炼核心观点,最后调用“生成报告”技能输出结果。
2.2 设计原则:可组合性、数据主权与供应商中立
这个项目的设计背后,贯穿着几个关键的软件工程原则,这也是它区别于许多一次性脚本或封闭框架的地方。
可组合性是第一要义。所有组件都设计为高内聚、低耦合的模块。这意味着你可以轻松地将项目中的“天气查询工具”与你自定义的“出行规划技能”组合,而无需修改工具的内部代码。这种设计极大地提升了代码的复用率和系统的灵活性。
数据主权是一个在AI时代愈发重要的考量。该项目强调避免将用户数据锁定在某个特定的云服务或厂商生态中。它的模板和模式鼓励你将核心逻辑、数据处理流程掌握在自己手中,而将LLM API(如OpenAI、Anthropic的Claude、或本地模型)视为一个可替换的“计算资源”。这通过清晰的接口抽象来实现,例如,定义一个统一的“LLM调用器”接口,背后可以轻松切换不同的模型提供商。
供应商中立原则与数据主权相辅相成。项目没有深度绑定任何一家商业LLM服务。它提供的提示词模板、工具调用规范都是通用的,你可以将其用于Claude Code、GitHub Copilot的底层模型,或是任何兼容OpenAI API格式的本地模型(如通过Ollama部署的)。这种中立性保障了项目的长期生命力和你的技术选型自由。
提示词工程系统化是该项目的另一大贡献。它没有将提示词视为散落在代码各处的魔法字符串,而是将其作为一等公民进行管理。提示词被模板化、参数化,甚至版本化。例如,一个“代码审查”提示词模板,可能包含占位符{code}和{language},在实际调用时被具体值替换。这种管理方式使得提示词的迭代、优化和团队协作变得可行且高效。
3. 核心组件详解与实操配置
3.1 提示词模板库:超越简单文本替换
很多人认为提示词就是一段写给AI的指令。但在这个项目中,提示词模板被提升到了“系统配置”的高度。它通常是一个包含角色定义、任务描述、输出格式约束、以及少样本示例的结构化文本。
一个典型的系统提示词模板可能如下所示(以Markdown格式存储):
# 角色 你是一个资深的{domain}专家,擅长以清晰、结构化的方式分析和解决问题。 # 任务 用户将提供一个关于{task_topic}的请求。你的目标是: 1. 首先,理解用户的核心需求。 2. 然后,遵循{procedure}的步骤进行分析。 3. 最后,将你的分析结果以{output_format}的格式呈现。 # 约束 - 思考过程请放在<thinking>标签内。 - 最终答案请放在<answer>标签内。 - 如果信息不足,请明确询问,不要猜测。 # 示例 用户:帮我分析这段Python代码的复杂度。 <thinking>用户需求是代码复杂度分析。我需要使用大O表示法,分析循环和递归... </thinking> <answer>时间复杂度为O(n^2),因为存在嵌套循环。空间复杂度为O(1)。</answer>在实际使用时,系统会通过模板引擎(如Jinja2)将{domain}、{task_topic}等变量替换为具体的值(如“软件工程”、“性能优化”),从而动态生成精准的提示词。项目库中预置了大量此类模板,覆盖了代码生成、文本总结、数据分析、创意写作等多个场景。实操心得:不要直接硬编码提示词。利用项目的模板系统,将可变的因素参数化。这样,当你需要调整AI的角色或任务时,只需修改模板文件或传入不同的参数,而无需搜索和替换代码中的多个字符串,极大减少了出错概率。
3.2 工具与技能的实现与注册
工具在代码层面通常实现为一个Python函数,并使用装饰器或特定基类进行注册,以便被框架自动发现和管理。以下是一个简化的工具定义示例:
# 假设项目使用类似LangChain的装饰器 from llm_system.decorators import tool @tool(name="get_weather", description="根据城市名称获取当前天气情况") def get_weather(city: str) -> str: """ 调用天气API获取信息。 Args: city: 城市名,例如“北京” Returns: 格式化的天气信息字符串 """ # 这里模拟API调用 # 实际项目中会集成真实的天气API,如OpenWeatherMap weather_data = call_weather_api(city) return f"{city}的天气是{weather_data['condition']},温度{weather_data['temp']}°C。" # 技能则是多个工具和逻辑的组合 from llm_system.skill import Skill class SummarizeDocumentSkill(Skill): name = "summarize_document" description = "总结一个长文档的核心内容" def execute(self, document_path: str, summary_length: str = "medium") -> dict: # 1. 调用工具:读取文档 content = self.invoke_tool("read_file", path=document_path) # 2. 根据长度选择提示词模板 prompt_template = self.get_prompt("summarize", length=summary_length) prompt = prompt_template.render(content=content) # 3. 调用工具:请求LLM生成总结 summary = self.invoke_tool("call_llm", prompt=prompt) # 4. 返回结构化结果 return { "original_file": document_path, "summary": summary, "length": summary_length }注意事项:在定义工具时,务必为其提供清晰、准确的name和description。因为智能体(尤其是基于LLM的规划型智能体)会依赖这些描述来决定在何时调用哪个工具。描述应简明扼要地说明工具的用途、输入和输出。对于技能,重点在于设计好其执行流程和错误处理。例如,在SummarizeDocumentSkill中,如果文件不存在或无法读取,应该在调用read_file后进行检查并抛出清晰的异常,而不是让流程继续下去导致后续调用失败。
3.3 智能体模式与工作流引擎
项目中的“模式”通常体现为预定义的工作流或状态机。它们可能以YAML配置文件、Python类或可视化流程图的形式存在。一个用于处理客户支持的“分类-路由-解决”模式可能如下配置:
# support_ticket_pattern.yaml name: customer_support_triage steps: - name: classify_intent agent: classifier_agent tool: call_llm prompt_template: intent_classification.j2 outputs: [intent, urgency] - name: route_ticket agent: router_agent condition: "{{ intent in ['billing', 'technical'] }}" target_skill: - if: "intent == 'billing'" use: billing_support_skill - if: "intent == 'technical'" use: technical_support_skill default_skill: general_inquiry_skill - name: execute_resolution agent: executor_agent input_from: route_ticket.output skill: "{{ selected_skill }}"这个YAML定义了一个三步骤的工作流。开发者只需提供具体的classifier_agent、billing_support_skill等实现,即可快速获得一个完整的客服工单处理流水线。核心优势在于,业务逻辑(工作流)与实现细节(具体的Agent和Skill)分离。当你想修改流程时(例如在分类后增加一个情感分析步骤),只需调整模式配置文件,而无需重写大量代码。
4. 从零开始:搭建你的第一个LLM智能体应用
4.1 环境准备与项目初始化
假设我们想利用这个模板库,快速构建一个“智能技术文档问答助手”。它的功能是:用户输入一个技术问题,助手能自动搜索相关文档片段,并生成一个简洁准确的答案。
首先,我们需要搭建开发环境。虽然项目文档提到对编程技能要求不高,但进行定制化开发仍需基本的Python环境。
# 1. 克隆仓库(假设你已安装Git) git clone https://github.com/zartin790/llm_system_template_agents_skills_patterns_tools_prompts.git cd llm_system_template_agents_skills_patterns_tools_prompts # 2. 创建并激活Python虚拟环境(强烈推荐,避免包冲突) python -m venv venv # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate # 3. 安装核心依赖 # 项目根目录通常会有requirements.txt或pyproject.toml pip install -r requirements.txt # 4. 安装你所需的额外包,例如用于向量数据库和网络搜索的库 pip install chromadb langchain-community duckduckgo-search关键步骤解析:使用虚拟环境是Python开发的最佳实践,它能将项目的依赖包与系统全局环境隔离。安装的chromadb是一个轻量级向量数据库,用于存储和检索文档嵌入;duckduckgo-search则提供了一个免费的搜索工具。这些选择基于其易用性和开源免费的特性,符合项目“供应商中立”的原则。
4.2 构建核心组件:检索工具与问答技能
接下来,我们创建两个核心组件:一个从向量数据库检索相关文档的工具,和一个整合检索与生成的问答技能。
第一步,实现检索工具。我们在项目的tools/目录下新建一个文件retrieve_docs_tool.py。
# tools/retrieve_docs_tool.py import chromadb from llm_system.decorators import tool # 初始化一个持久的向量数据库客户端 CHROMA_CLIENT = chromadb.PersistentClient(path="./chroma_db") @tool(name="retrieve_technical_docs", description="从技术文档库中检索与问题最相关的3个文档片段。") def retrieve_docs(question: str, collection_name: str = "tech_docs") -> list: """ 基于语义相似度检索文档。 Args: question: 用户的技术问题。 collection_name: 向量数据库集合的名称。 Returns: 一个列表,包含检索到的文档片段及其元数据。 """ try: collection = CHROMA_CLIENT.get_collection(name=collection_name) # 进行相似性搜索,获取最相关的3个结果 results = collection.query( query_texts=[question], n_results=3 ) # 格式化返回结果 documents = [] for i in range(len(results['documents'][0])): doc = { "content": results['documents'][0][i], "source": results['metadatas'][0][i].get("source", "unknown"), "relevance_score": results['distances'][0][i] # 注意:距离越小越相关 } documents.append(doc) return documents except Exception as e: return [{"error": f"检索失败: {str(e)}", "content": ""}]第二步,创建问答技能。我们在skills/目录下新建answer_tech_question_skill.py。
# skills/answer_tech_question_skill.py from llm_system.skill import Skill from llm_system.prompt_manager import PromptManager class AnswerTechQuestionSkill(Skill): name = "answer_tech_question" description = "通过检索文档和推理来回答技术问题。" def __init__(self): self.prompt_manager = PromptManager() def execute(self, user_question: str) -> dict: # 1. 检索相关文档 retrieved_docs = self.invoke_tool("retrieve_technical_docs", question=user_question) # 检查检索是否出错 if retrieved_docs and "error" in retrieved_docs[0]: return {"answer": "抱歉,文档检索服务暂时不可用。", "sources": []} # 2. 构建上下文 context = "\n\n--- 参考文档 ---\n" for doc in retrieved_docs: context += f"来自 {doc['source']}:\n{doc['content']}\n\n" # 3. 获取并渲染提示词模板 # 假设我们在 `prompts/` 目录下有一个 `tech_qa.j2` 模板文件 prompt_template = self.prompt_manager.get_template("tech_qa") final_prompt = prompt_template.render( question=user_question, context=context ) # 4. 调用LLM生成答案 llm_response = self.invoke_tool("call_llm", prompt=final_prompt, model="gpt-4") # 5. 整理来源信息 sources = [doc["source"] for doc in retrieved_docs if doc.get("source")] return { "question": user_question, "answer": llm_response, "sources": sources, "retrieved_count": len(retrieved_docs) }实操要点:在技能中,我们通过self.invoke_tool来调用之前注册的工具,这实现了组件间的松耦合。PromptManager是项目可能提供的一个辅助类,用于加载和渲染Jinja2模板。你需要提前在prompts/tech_qa.j2文件中编写好提示词模板,定义AI如何利用问题和上下文生成答案。
4.3 装配智能体与主程序入口
最后,我们将技能装配到一个智能体中,并创建主程序来运行它。在agents/目录下创建tech_support_agent.py。
# agents/tech_support_agent.py from llm_system.agent import AgentBase from skills.answer_tech_question_skill import AnswerTechQuestionSkill class TechSupportAgent(AgentBase): """技术文档问答智能体""" def __init__(self, name="TechDocHelper"): super().__init__(name) # 注册该智能体可用的技能 self.register_skill(AnswerTechQuestionSkill()) def run(self, input_data: dict) -> dict: """ 智能体的主要运行逻辑。 Args: input_data: 包含用户问题的字典,如 {"question": "如何配置Python虚拟环境?"} """ user_question = input_data.get("question", "") if not user_question: return {"error": "未提供问题。"} # 调用技能执行核心任务 result = self.invoke_skill("answer_tech_question", user_question=user_question) return result现在,创建一个主程序文件main.py来启动一切:
# main.py import sys from agents.tech_support_agent import TechSupportAgent def main(): # 1. 初始化智能体 agent = TechSupportAgent() # 2. 获取用户输入(这里简单从命令行参数读取) if len(sys.argv) > 1: question = " ".join(sys.argv[1:]) else: question = input("请输入您的技术问题: ") # 3. 运行智能体 print(f"\n🤔 正在为您查询: {question}") result = agent.run({"question": question}) # 4. 输出结果 if "error" in result: print(f"❌ 出错: {result['error']}") else: print(f"\n📚 答案: {result['answer']}") if result['sources']: print(f"📖 参考来源: {', '.join(result['sources'])}") if __name__ == "__main__": main()运行你的应用:python main.py "Python中async和await怎么用?"。智能体会自动触发检索、生成流程,并返回答案和来源。至此,你已利用模板库的核心思想,快速构建了一个具备检索增强生成能力的最小可行产品。
5. 高级模式应用与性能调优实战
5.1 实现复杂的链式与分支工作流
基础技能和智能体能满足简单任务。但对于复杂场景,如一个需要多步骤决策的“内容审核”流程,就需要用到项目中的“模式”概念。假设流程是:先检查文本毒性,如果通过则进行事实核查,再不通过则进行摘要生成以供人工复审。
我们可以将此模式实现为一个工作流类:
# patterns/content_moderation_pattern.py from enum import Enum from llm_system.pattern import WorkflowPattern class ModerationResult(Enum): APPROVED = "approved" NEEDS_FACT_CHECK = "needs_fact_check" NEEDS_HUMAN_REVIEW = "needs_human_review" class ContentModerationPattern(WorkflowPattern): name = "content_moderation" def execute(self, content: str) -> dict: # 步骤1: 毒性检查 toxicity_check = self.invoke_skill("toxicity_check", text=content) if toxicity_check.get("is_toxic", True): # 毒性高,直接标记为需人工审核 summary = self.invoke_skill("summarize_content", text=content, reason="toxic_content") return { "final_decision": ModerationResult.NEEDS_HUMAN_REVIEW.value, "reason": "内容含有不当言论。", "summary_for_reviewer": summary } # 步骤2: 事实核查 (仅对非毒性内容) fact_check = self.invoke_skill("fact_check_claim", text=content) if not fact_check.get("is_accurate", False): # 事实存疑,也需人工审核 summary = self.invoke_skill("summarize_content", text=content, reason="factual_dispute") return { "final_decision": ModerationResult.NEEDS_HUMAN_REVIEW.value, "reason": "事实准确性存疑。", "summary_for_reviewer": summary, "disputed_points": fact_check.get("disputes", []) } # 步骤3: 所有检查通过 return { "final_decision": ModerationResult.APPROVED.value, "reason": "内容通过所有自动审核。" }这个模式清晰地定义了业务逻辑流。它的优势在于:审核策略(先毒检再事实核查)被固化在模式中,而具体的检查技能(toxicity_check,fact_check_claim)可以独立开发和替换。例如,你可以将基于关键词的毒检升级为基于深度学习模型的毒检,而无需修改工作流模式本身。
5.2 提示词迭代与性能优化技巧
LLM应用的性能和输出质量,极大程度上取决于提示词。项目提供的模板是起点,而非终点。你需要建立自己的迭代优化流程。
A/B测试框架:不要凭感觉修改提示词。创建一个简单的测试框架,用一批标准问题(测试集)来评估不同提示词版本的效果。
# utils/prompt_ab_test.py import json from agents.tech_support_agent import TechSupportAgent def run_ab_test(prompt_version_a, prompt_version_b, test_questions_file): agent = TechSupportAgent() with open(test_questions_file, 'r') as f: questions = json.load(f) results = [] for q in questions: # 测试版本A agent.prompt_manager.set_active_version("tech_qa", prompt_version_a) result_a = agent.run({"question": q}) # 测试版本B agent.prompt_manager.set_active_version("tech_qa", prompt_version_b) result_b = agent.run({"question": q}) # 这里可以引入人工评估或自动评估指标(如答案相关性、准确性打分) # 暂时简单记录答案长度作为粗略对比 results.append({ "question": q, "answer_a_len": len(result_a.get("answer", "")), "answer_b_len": len(result_b.get("answer", "")) }) return results结构化输出引导:为了便于后续程序化处理,强烈建议在提示词模板中要求LLM输出结构化数据(如JSON)。例如,在tech_qa.j2模板末尾加上:“请以以下JSON格式输出:{"answer": "你的答案", "confidence": "高|中|低", "key_points": ["点1", "点2"]}”。这能显著提升下游系统解析结果的可靠性。
上下文管理与长度优化:当检索到的文档过多时,可能超出LLM的上下文窗口。需要在技能层实现“智能截断”逻辑。不是简单取前N个字符,而是根据相关性分数排序,并优先保留分数最高的片段,同时确保总长度不超过限制。这通常需要在AnswerTechQuestionSkill.execute方法中的“构建上下文”步骤里实现。
5.3 系统监控、日志与错误处理
一个健壮的生产级应用离不开完善的可观测性。项目模板通常预留了集成点。
结构化日志:使用Python的logging模块,为不同组件设置不同日志级别。
import logging # 在技能或工具中 logger = logging.getLogger(__name__) class AnswerTechQuestionSkill(Skill): def execute(self, user_question: str) -> dict: logger.info(f"开始处理问题: {user_question[:50]}...") try: retrieved_docs = self.invoke_tool(...) logger.debug(f"检索到 {len(retrieved_docs)} 个文档片段。") # ... 后续处理 except Exception as e: logger.error(f"技能执行失败,问题: {user_question}, 错误: {e}", exc_info=True) return {"error": "内部处理错误"}关键指标收集:在关键路径上记录性能指标,如工具调用耗时、LLM响应耗时、Token使用量、缓存命中率等。这些数据可以帮助你定位瓶颈。例如,你可以用装饰器来包装工具调用函数,自动记录其执行时间。
优雅降级与熔断:对于依赖外部API的工具(如LLM调用、搜索),必须实现错误处理和降级方案。例如,当主要LLM API超时时,可以自动切换到备用API或返回一个缓存的通用答案。项目中的工具抽象层是实现这种熔断逻辑的理想位置。
6. 常见问题排查与效能提升指南
在实际开发和运行中,你肯定会遇到各种问题。下面是一些典型问题及其排查思路,以及进一步提升系统效能的建议。
6.1 典型问题速查表
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 智能体无法找到或调用技能/工具 | 1. 技能/工具未正确注册。 2. 名称拼写错误。 3. 依赖的模块未导入。 | 1. 检查技能类的name属性与调用时使用的字符串是否完全一致(区分大小写)。2. 在智能体 __init__方法中,确认已调用self.register_skill(...)。3. 确保定义工具/技能的Python文件所在的目录已被添加到 PYTHONPATH,或在主程序中被正确导入。 |
| LLM调用返回无关或格式错误的答案 | 1. 提示词模板渲染错误。 2. 系统提示词(角色定义)太弱或冲突。 3. 上下文信息过多或杂乱。 | 1. 打印出最终发送给LLM的完整提示词,检查变量替换是否正确,格式是否清晰。 2. 强化系统提示词,明确指令,如“你必须严格按照JSON格式输出”。 3. 实现上文提到的“上下文智能截断”,优先保留相关性最高的信息。 |
| 向量检索结果质量差 | 1. 文档嵌入模型不匹配或质量差。 2. 检索参数(如 n_results)设置不合理。3. 文档块切分策略不佳。 | 1. 确保检索时使用的嵌入模型与构建向量数据库时使用的是同一模型系列。 2. 调整 n_results,太小时可能遗漏关键信息,太大时可能引入噪声。可以先从5-10开始尝试。3. 检查文档预处理:是否按语义段落切分?是否保留了必要的上下文(如标题)? |
| 应用响应速度慢 | 1. 工具或LLM调用是同步阻塞的。 2. 未使用缓存。 3. 检索的文档块过大、过多。 | 1. 对于独立的工具调用(如多个并行的网络请求),考虑使用asyncio或线程池改为异步。2. 为频繁查询且结果变化不大的工具(如某些知识检索)添加缓存层(如使用 functools.lru_cache)。3. 优化文档切分策略,避免单个片段过长;在技能逻辑中,对检索结果做更严格的过滤。 |
| 内存使用量持续增长 | 1. 存在全局变量或缓存未及时清理。 2. 大对象(如加载的模型)生命周期管理不当。 | 1. 使用内存分析工具(如tracemalloc)定位泄漏点。2. 对于大型工具(如本地嵌入模型),将其设计为可懒加载的单例,或在不需要时显式卸载。 |
6.2 效能提升进阶技巧
实现工具调用缓存:对于纯函数式、输入相同则输出必然相同的工具(如某些计算、或对静态数据的查询),添加缓存能极大提升性能。
from functools import lru_cache from llm_system.decorators import tool @tool(name="calculate_md5") @lru_cache(maxsize=128) # 缓存最近128次调用 def calculate_file_md5(file_path: str) -> str: import hashlib # ... 计算MD5的逻辑 return md5_hash设计异步智能体:对于I/O密集型应用(如需要同时调用多个外部API),将智能体和工具改造成异步模式可以大幅减少总等待时间。这需要基于asyncio重构,让工具函数返回awaitable对象,并在技能中使用await来调用。
建立评估与回馈闭环:在AnswerTechQuestionSkill的返回结果中,可以加入一个feedback_id。在前端界面提供“答案是否有用”的反馈按钮。将用户反馈(问题、答案、反馈结果)记录到数据库。定期用这些数据微调你的提示词模板,或作为评估检索质量的新训练数据。这是让系统持续进化的关键。
利用项目的模式库进行重构:当你发现自己在多个智能体中重复编写相似的工作流逻辑时(例如,都是“检索->分析->报告”三步走),就是时候考虑将其抽象成一个通用的“检索增强生成模式”了。参考项目中原有的模式实现,创建你自己的模式类。这样,下一个类似项目就可以直接复用这个模式,只需注入不同的检索工具和报告模板即可,开发效率成倍提升。
