LLM应用架构实战:从Prompt工程到AI-Agent工作流设计
1. 从开源项目到实战指南我的LLM应用架构探索之路过去半年我几乎把所有业余时间都泡在了大语言模型LLM的应用开发上。和Thoughtworks的同事、开源社区的伙伴们一起我们折腾出了一系列项目从Prompt工程到应用架构从微调模型到IDE插件几乎把LLM能落地的方向都摸了一遍。这个过程里我最大的感触是技术浪潮来了光看热闹没用得亲手去“造轮子”才能真正理解它为什么转、怎么转、以及转起来会卡在哪儿。市面上关于ChatGPT、AIGC的讨论很多但大多是概念和演示缺的是从零到一构建一个可维护、可扩展、真正解决实际问题的LLM应用的那套“工程化”心法。这正是我和伙伴们通过“phodal/aigc”这一系列开源项目想要填补的空白——它不是一本教科书而是一份来自一线实战的、带着温度与坑点的架构设计笔记。如果你是一名开发者、技术负责人或者对如何将LLM能力深度集成到自己的产品或研发流程中感兴趣那么接下来我要分享的正是我们趟过的路、总结的模式和踩过的坑。我们将围绕几个核心问题展开如何系统性地设计和编写Prompt如何为LLM设计友好的应用架构如何基于开源模型定制专属能力以及LLM将如何重塑我们熟悉的软件开发工序我会尽量避免空泛的理论而是结合我们具体的开源项目拆解背后的设计思路、技术选型和实操细节。无论你是想快速上手构建一个聊天应用还是计划在团队内引入AI辅助编程相信这些从真实项目中沉淀的经验都能给你带来直接的参考。2. LLM能力基石超越简单问答的Prompt工程体系很多人对LLM应用的第一印象就是打开ChatGPT的网页对话框输入一个问题然后等待一个答案。这确实是最直接的交互但如果你想构建一个稳定、可靠、能处理复杂逻辑的应用这种“一次性问答”的模式是远远不够的。Prompt提示词是驱动LLM的“代码”其质量直接决定了应用的智商上限。我们意识到必须像管理代码一样去管理Prompt并为其建立一套工程方法。2.1 Prompt的编写模式从技巧到可复用的设计模式早期使用Prompt大家热衷于收集各种“神奇咒语”比如“请扮演一个资深专家…”。但这是一种脆弱的方式换一个模型或场景可能就失效了。我们从软件设计模式中汲取灵感开始系统性地归纳Prompt编写模式。这不仅仅是技巧而是解决某一类问题的可复用方案。例如我们总结的“链式思考Chain-of-Thought”模式核心不是简单要求模型“逐步思考”而是通过精心设计的Prompt结构引导模型显式地展示推理过程。在一个客服场景的意图分类项目中我们最初的Prompt是“判断用户query的意图类别咨询、投诉、办理。”效果不稳定。后来我们应用了链式思考模式你是一个客服意图分类专家。请按以下步骤工作 1. 首先逐句分析用户query提取关键实体和情绪词。 2. 然后根据第一步的提取结果对照意图定义进行匹配 - 咨询包含“怎么”、“如何”、“多少钱”、“时间”等疑问词寻求信息。 - 投诉包含“不满”、“差”、“慢”、“故障”等负面词汇表达抱怨。 - 办理包含“申请”、“开通”、“预约”、“提交”等动作词希望完成业务。 3. 最后综合以上分析输出最匹配的单一意图类别。 用户query{用户输入}这个模式的成功在于它将黑盒的思考过程“白盒化”不仅提高了分类准确率更重要的是当分类出错时我们可以回溯到模型的“思考步骤”进行调试比如是实体提取错了还是匹配规则理解有偏差。这为Prompt的迭代优化提供了清晰的路径。另一个关键模式是“少样本学习Few-Shot Learning”模式。当任务复杂或定义模糊时单纯用语言描述不如直接给例子。我们在开发一个从自然语言生成数据报表SQL语句的应用时就大量使用了此模式。Prompt中会包含3-5个精心挑选的“用户问题-SQL语句”对这些例子覆盖了不同的查询维度如时间筛选、分组聚合、多表关联。模型通过类比这些例子来生成新的SQL。这里的选择和构造示例本身就是一门学问需要覆盖边界情况和常见错误我们通常会结合历史查询日志来构建这个示例库。实操心得Prompt模式不是银弹盲目套用模式可能适得其反。我们曾在一个创意写作项目中机械地使用链式思考导致生成的文案僵硬、缺乏灵气。后来意识到对于发散性任务更需要的是“角色扮演Role-Playing”模式或“种子词Seed Word”激发模式。关键是根据任务类型分类、生成、推理、创意选择合适的模式或组合模式。建立一个团队的Prompt模式库并附上适用场景和效果说明能极大提升协作效率。2.2 Prompt即代码版本化、测试与协作管理当Prompt数量增多、逻辑变复杂后像管理配置文件一样用文本文件存储会很快陷入混乱。我们提出了“Prompt即代码”的理念并实践在ClickPrompt等工具中。这意味着版本控制使用Git管理Prompt的迭代历史清晰看到每次修改的内容、作者和意图便于回滚和审计。模块化与复用将常用的Prompt片段如系统指令、示例库、输出格式规范抽象为可导入的模块。例如一个“安全审查”模块可以被多个业务Prompt引入。测试与验证为关键Prompt编写测试用例。这可以是基于输入-输出的断言测试也可以是使用另一组LLM调用进行质量评估的“模型测试”。我们在CI/CD流水线中集成Prompt测试确保修改不会破坏现有功能。参数化与模板将Prompt中可变的部分如用户输入、业务规则参数化通过模板引擎动态生成最终Prompt。这使得同一套逻辑能快速适配不同场景。在我们的实践中一个用于代码审查的Prompt可能会被组织成这样的结构prompts/ ├── system_instructions/ │ └── senior_engineer.md # 定义角色和基础原则 ├── templates/ │ └── code_review.j2 # Jinja2模板包含参数占位符 ├── examples/ │ └── good_feedback.json # 少样本示例数据 └── tests/ └── test_review.py # 使用pytest验证对典型代码片段的输出是否包含关键点通过这种方式Prompt从散落的“魔法字符串”变成了可维护、可测试的工程资产。3. LLM时代的应用架构设计迎接以AI为核心的变化当LLM从对话界面走向业务系统后端传统的分层架构如MVC开始显得力不从心。LLM的不确定性、长上下文、流式响应等特性要求我们重新思考应用架构。我们探索并提出了Unit Mesh架构思想其核心是将LLM视为一个具有强大认知和生成能力的“计算单元”围绕它构建松散耦合、事件驱动、可观测的服务网格。3.1 交互范式演进从ChatUI到AI-Agent工作流ChatUI聊天界面是LLM最自然的交互形式但在复杂业务中单一的聊天窗口容易成为“垃圾输入、垃圾输出”的黑洞。我们通过ChatFlow项目探索了如何将聊天升级为结构化的工作流。例如一个旅游规划Agent不再是用户问“帮我规划一下北京三日游”然后模型直接吐出一大段文本。ChatFlow会将其分解为一个可执行的工作流意图识别与槽位填充首先用一个专门的LLM调用或分类器识别用户意图为“旅游规划”并提取关键槽位目的地北京天数3未明确槽位如预算、兴趣待澄清。多轮澄清自动追问用户“您的预算是多少对历史古迹、美食还是自然风光更感兴趣”子任务编排根据填充完整的槽位并行或串行调用多个“技能单元”Unit信息查询Unit调用搜索引擎API或知识库获取北京景点、天气、交通信息。行程生成Unit由LLM基于查询结果生成详细的每日行程草案。预算估算Unit根据行程调用价格查询API估算大致费用。结果整合与呈现将各个Unit的结果汇总由LLM润色成一份完整的、个性化的旅游计划并以图文并茂的形式甚至可生成地图草图返回给用户。在这个架构下LLM扮演了“大脑”的角色负责理解、规划和协调而具体的、确定性的任务如数据查询、计算则由传统的、可靠的微服务Unit完成。这种模式显著提升了复杂任务的完成度和可靠性。3.2 Unit Mesh架构详解弹性、可观测与治理Unit Mesh架构的灵感来源于服务网格Service Mesh但将“服务”替换为功能各异的“Unit”单元。一个Unit可以是一个纯LLM调用一个传统API一个数据库查询或者它们的组合。核心组件包括AI Unit封装了LLM调用包含Prompt模板、模型配置、上下文管理逻辑。它是处理非确定性任务的核心。Tool Unit封装了确定性的工具调用如计算器、搜索引擎、业务API。Orchestrator编排器负责接收用户请求理解意图并动态编排AI Unit和Tool Unit的执行流程。它本身可以是一个LLM基于ReAct等框架也可以是一个基于规则或图的引擎。Context Broker上下文经纪人管理对话或工作流的上下文状态确保信息在不同Unit间正确传递。这是解决LLM“记忆力”有限的关键。Observability Layer可观测层必须全面监控每个LLM调用的耗时、Token消耗、费用、输入输出并进行日志记录和追踪。这对于调试、成本控制和效果优化至关重要。我们在Unit Runtime项目中提供了一个轻量级的运行时环境帮助开发者快速搭建和测试这样的AI应用流水线。它允许你以配置的方式定义Units和流程并一键启动一个可交互的测试环境。避坑指南LLM应用架构的三大陷阱过度依赖LLM试图用LLM处理所有逻辑包括精确计算、实时数据获取导致响应慢、成本高、结果不稳定。正确做法严格区分“认知型任务”适合LLM和“执行型任务”适合传统代码遵循“LLM as a Coordinator”原则。上下文管理混乱简单地将所有历史对话都塞进上下文很快会触及Token限制且无关信息会干扰模型。正确做法实现智能的上下文窗口管理如摘要历史、选择性记忆、向量检索关联片段。缺乏可观测性把LLM当黑盒出了问题无法定位是Prompt问题、模型问题还是数据问题。正确做法在架构设计初期就植入可观测性记录每次调用的Prompt、参数、完整响应和中间结果建立效果评估指标如准确率、用户满意度。4. 面向特定场景的深度定制从Prompt工程到模型微调对于通用问题ChatGPT等大模型已经足够出色。但一旦涉及专业领域如医疗、法律、金融或企业内部私有知识通用模型就会显得“泛而不精”。这时就需要走向深度定制我们探索了两条主要路径上下文工程Prompt Engineering和模型微调Fine-Tuning。4.1 上下文工程让模型“阅读”你的知识库这是成本较低、见效较快的方式。核心思想不是改变模型本身而是通过Prompt为模型提供相关的上下文信息。检索增强生成RAG是当前最主流的实现模式。其工作流程如下知识库预处理将你的文档PDF、Word、Wiki等进行切片转换成文本片段。向量化与索引使用嵌入模型如OpenAI的text-embedding-ada-002将文本片段转换为向量存入向量数据库如Pinecone、Chroma、Milvus。检索当用户提问时将问题也转换为向量在向量数据库中搜索最相似的几个文本片段。增强生成将检索到的相关片段作为上下文和用户问题一起构造最终的Prompt送给LLM生成答案。我们实践中的关键点在于检索质量。如果检索到的片段不相关LLM就会“胡编乱造”。我们通过以下方式优化智能分块不是简单按字数切分而是根据文档结构如标题、段落进行语义分块避免将一个完整概念切断。多路召回结合关键词搜索如BM25和向量搜索兼顾精确匹配和语义相似度。重排序Re-ranking使用一个更精细的交叉编码器模型对初步检索的结果进行重新排序将最相关的排在前面。在ArchGuard Co-mate项目中我们应用RAG构建了一个架构知识助手。它将架构决策记录、设计模式文档、系统架构图描述等存入向量库。当开发者询问“我们系统目前为何采用事件驱动架构”时RAG会检索到相关的决策文档和会议纪要作为上下文让LLM生成一个基于事实的、准确的回答而不是凭空想象。4.2 模型微调打造专属的“领域专家”当你的任务非常特定、且拥有大量高质量数据时微调是更彻底的选择。它能让模型学习到特定的风格、格式和领域知识。我们通过Unit Minions项目深入实践了基于开源模型如LLaMA、ChatGLM的微调。微调的核心步骤数据准备这是最耗时但最关键的一步。你需要准备大量“指令-输出”对。例如对于代码生成任务数据格式可能是{instruction: 用Python写一个快速排序函数, output: def quicksort(arr): ...}。数据质量要求极高需要清洗、去重、格式化。选择基座模型与微调方法全参数微调效果最好但需要大量GPU资源适用于数据量极大、不差钱的场景。参数高效微调PEFT如LoRALow-Rank Adaptation这是我们主要采用的方法。它只训练模型参数中插入的一些低秩矩阵大大减少了计算量和显存消耗效果接近全参数微调。在Unit Minions中我们提供了详细的LoRA训练脚本和配置。训练与评估在准备好的数据上运行训练脚本。训练过程中和结束后需要在独立的验证集上评估模型效果评估指标可能包括生成代码的编译通过率、测试用例通过率、文本生成的BLEU分数等。部署与应用将训练好的LoRA权重与基座模型合并导出为可服务的模型文件通过类似Text Generation InferenceTGI或vLLM这样的高性能框架进行部署。我们在DevTi项目中微调了一个专注于“从用户故事生成测试用例”的模型。我们收集了数千条高质量的用户故事及其对应的Gherkin语法测试场景作为训练数据。微调后的模型在生成测试用例的完整性、准确性和格式规范性上远超通用模型。微调实战经验数据与评估是关键数据质量 数据数量1000条精心构造、标注一致的数据远胜于10万条嘈杂、矛盾的数据。我们花了70%的时间在数据清洗和标注上。警惕过拟合如果模型在训练集上表现完美在验证集上却很差那就是过拟合。需要增加数据多样性、使用数据增强、或调整正则化参数。评估要贴近真实场景不要只看损失函数下降。设计端到端的评估任务比如让微调后的模型直接生成一段代码然后跑单元测试看通过率。我们建立了自动化的评估流水线每次训练后自动运行。成本考量即使是LoRA训练也需要GPU资源。云服务按小时计费需要精确预估时间和成本。对于大多数中小企业从高质量的Prompt工程和RAG开始性价比更高。5. 重塑软件开发工序AI原生研发流程的实践LLM和Copilot类工具的出现不仅仅是多了个“自动补全”它正在改变需求分析、设计、编码、测试、运维的每一个环节。我们思考并实践了一套AI 2.0时代的软件开发工序。5.1 需求与设计阶段从模糊想法到清晰规约传统上产品经理撰写冗长的PRD工程师再将其转化为技术方案存在信息损耗。现在LLM可以成为中间的“翻译器”和“加速器”。自动化用户故事拆分我们将粗略的产品需求描述输入给像DevTi这样的微调模型它可以自动生成格式规范的用户故事As a... I want... So that...和验收标准。架构探索与决策辅助在ArchGuard Co-mate中我们可以描述系统概要和约束如“高并发、数据最终一致性”让LLM基于内置的架构知识库生成几个备选的架构模式如CQRS、事件溯源并分析其利弊辅助架构师决策。生成设计草案根据用户故事LLM可以快速生成数据库ER图的草稿、API接口的粗略定义甚至UI线框图描述极大提升了早期设计和沟通的效率。5.2 编码与测试阶段从助手到协同者这是GitHub Copilot等工具已经大显身手的领域但我们的AutoDev插件试图走得更远。它不仅仅是代码补全而是深度集成到IDE和研发流程中的“AI副驾驶”。上下文感知的代码生成AutoDev能读取你当前的代码文件、项目结构、甚至关联的Jira任务描述生成高度相关、符合项目规范的代码。例如当你在实现一个“用户注册”API时它知道你已经在User实体中定义了哪些字段并据此生成相应的DTO、Service层方法和Controller。自动化测试生成基于已有的业务代码自动生成单元测试框架。它不仅能生成测试用例还能尝试理解业务逻辑生成有意义的测试数据和对边界情况的测试。智能代码审查结合团队定义的编码规范和安全规则如SQL注入、硬编码密码AutoDev可以在代码提交前进行实时审查提出改进建议而不仅仅是检查语法。自动化代码重构接受如“将这个函数拆分成两个提高可读性”这样的自然语言指令并安全地执行重构操作。5.3 运维与知识管理构建自进化的系统LLM的应用不止于开发期。智能日志分析传统的日志检索依赖关键词难以发现复杂关联。通过LLM分析实时日志流可以自动归纳异常模式、推测根因并用自然语言生成运维报告。自动化文档生成与更新代码变更后LLM可以自动分析diff更新对应的API文档、部署手册或内部Wiki。我们尝试让CI流水线在每次合并后自动调用LLM生成本次更新的变更摘要。构建团队知识大脑将项目文档、会议纪要、故障报告、优秀代码案例全部通过RAG注入到一个内部知识助手。新成员可以随时提问快速了解项目历史和设计决策老成员也能快速检索到模糊记忆中的细节。这一套工序的改变其核心是将人类从重复性、机械性的脑力劳动中解放出来更专注于高层次的创意、决策和复杂问题求解。人机协同而非机器替代是现阶段更现实的图景。6. 常见问题与实战排查指南在实际开发和部署LLM应用的过程中我们遇到了无数的问题。下面将一些最常见的问题及其排查思路整理成表希望能帮你少走弯路。问题现象可能原因排查步骤与解决方案LLM响应内容完全无关或胡言乱语1. Prompt指令不清晰或矛盾。2. 上下文窗口被无关历史淹没。3. 模型温度temperature参数过高。1.简化并强化指令在Prompt开头用“你是一个…你必须…”明确角色和任务。使用分隔符如清晰区分指令和内容。2.清理上下文实现上下文管理策略只保留最近几轮或最相关的对话。对于长对话尝试让模型自行总结之前的关键点。3.调整参数将temperature调低如0.1-0.3以获得更确定性的输出。处理长文档或复杂任务时模型“忘记”了前文指令超出了模型的上下文长度限制。1.分而治之将长文档分割成块分别处理后再汇总。使用Map-Reduce模式让LLM先对每个块生成摘要Map再对所有摘要进行总结Reduce。2.外部记忆使用向量数据库RAG存储文档知识每次只检索相关片段送入上下文。3.升级模型考虑使用支持更长上下文如128K的模型。基于RAG的应用答案不准确或包含幻觉1. 检索到的文档片段不相关。2. 即使片段相关模型也未正确利用。1.优化检索检查嵌入模型是否适合你的领域尝试混合检索关键词向量调整检索返回的数量k值引入重排序模型。2.优化Prompt在Prompt中明确指示模型“严格基于以下上下文回答如果上下文未提供信息请直接说不知道”。在上下文中用明显标记如##参考文档##。3.添加引用溯源要求模型在答案中注明依据的原文片段编号便于人工复核。微调后的模型效果不如预期1. 训练数据质量差或数量不足。2. 过拟合或欠拟合。3. 任务定义或评估方式有问题。1.审查数据随机抽样检查训练数据确保“指令-输出”对高质量、无噪声。尝试增加数据量或进行数据增强。2.分析学习曲线观察训练集和验证集损失。如果验证集损失先降后升是过拟合需增加正则化、减少模型容量或增加数据。如果两者都高是欠拟合可能需增加模型容量或训练轮数。3.端到端测试用一批未参与训练的真实任务测试模型进行人工评估这比单纯的指标更可靠。应用响应速度慢延迟高1. LLM API调用网络延迟高。2. 模型自身生成速度慢特别是大模型。3. 应用逻辑复杂串行调用过多。1.选择合适的地理区域如果使用云服务确保API端点离你的用户或服务器最近。2.模型选型在效果和速度间权衡。对于实时性要求高的场景考虑更小的模型或推理优化框架如vLLM。使用流式响应streaming让用户先看到部分结果。3.异步与并行将独立的LLM调用或工具调用改为异步并行。优化工作流减少不必要的步骤。Token消耗巨大成本失控1. Prompt过于冗长包含不必要信息。2. 上下文管理不当积累了太多历史。3. 未对输入输出长度进行限制。1.精简Prompt删除冗余的描述和示例。使用更高效的指令。2.压缩上下文对历史对话进行智能摘要。在RAG中优化检索策略只送入最相关的少量片段。3.设置硬性限制在代码中为输入和输出设置max_tokens限制并做好截断和异常处理。4.监控与告警建立成本监控仪表盘设置每日/每周Token消耗告警阈值。7. 开源项目实战从想法到可运行的原型理论再多不如亲手运行一个例子。让我以构建一个简单的“智能技术文档助手”为例串联起前面提到的多项技术。这个助手能基于你的项目源码和文档回答技术问题。7.1 技术栈选择与项目初始化我们选择轻量、易上手的方案后端框架FastAPIPython轻量且异步支持好。LLM接口OpenAI API或兼容的开源模型本地部署如通过Ollama运行Llama 3。向量数据库ChromaDB轻量级易于集成适合原型开发。文本嵌入模型all-MiniLM-L6-v2Hugging Face一个轻量且效果不错的开源句子嵌入模型可本地运行避免API调用。前端简单的Streamlit界面快速构建交互。首先创建项目并安装依赖mkdir tech-doc-assistant cd tech-doc-assistant python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate pip install fastapi uvicorn chromadb sentence-transformers streamlit openai pypdf27.2 核心模块一文档处理与向量化创建ingest.py负责读取文档假设为PDF和Markdown分块并存入向量库。import os from PyPDF2 import PdfReader from sentence_transformers import SentenceTransformer import chromadb from chromadb.config import Settings # 初始化嵌入模型和向量数据库客户端 embed_model SentenceTransformer(all-MiniLM-L6-v2) chroma_client chromadb.Client(Settings(chroma_db_implduckdbparquet, persist_directory./chroma_db)) collection chroma_client.create_collection(nametech_docs) def extract_text_from_pdf(pdf_path): 从PDF提取文本 reader PdfReader(pdf_path) text for page in reader.pages: text page.extract_text() \n return text def split_text(text, chunk_size500, chunk_overlap50): 按语义和长度分块这里简化处理按固定长度 words text.split() chunks [] for i in range(0, len(words), chunk_size - chunk_overlap): chunk .join(words[i:i chunk_size]) chunks.append(chunk) return chunks def ingest_documents(docs_dir): 遍历目录处理所有文档 doc_id 0 for filename in os.listdir(docs_dir): if filename.endswith(.pdf): path os.path.join(docs_dir, filename) text extract_text_from_pdf(path) chunks split_text(text) for chunk in chunks: # 生成向量 embedding embed_model.encode(chunk).tolist() # 存入ChromaDB collection.add( documents[chunk], embeddings[embedding], metadatas[{source: filename}], ids[fdoc_{doc_id}] ) doc_id 1 # 类似地处理.md文件... print(f已入库 {doc_id} 个文本块。) if __name__ __main__: ingest_documents(./your_docs_directory)注意实际生产环境中分块策略需要更精细比如按标题、段落进行语义分块并使用更好的PDF解析库如pymupdf。同时需要处理文档更新和去重。7.3 核心模块二检索与问答接口创建api.py提供检索和问答的FastAPI端点。from fastapi import FastAPI, HTTPException from pydantic import BaseModel from sentence_transformers import SentenceTransformer import chromadb from chromadb.config import Settings import openai import os app FastAPI() embed_model SentenceTransformer(all-MiniLM-L6-v2) chroma_client chromadb.Client(Settings(persist_directory./chroma_db, chroma_db_implduckdbparquet)) collection chroma_client.get_collection(nametech_docs) openai.api_key os.getenv(OPENAI_API_KEY) class QueryRequest(BaseModel): question: str top_k: int 3 app.post(/ask) async def ask_question(req: QueryRequest): # 1. 将问题转换为向量 query_embedding embed_model.encode(req.question).tolist() # 2. 从向量库检索相关文档片段 results collection.query( query_embeddings[query_embedding], n_resultsreq.top_k ) if not results[documents]: raise HTTPException(status_code404, detail未找到相关文档。) # 3. 构建Prompt上下文 context \n\n---\n\n.join(results[documents][0]) prompt f你是一个技术文档助手请严格根据以下提供的上下文信息来回答问题。如果上下文没有提供足够信息请直接说“根据现有文档我无法回答这个问题”。 上下文 {context} 问题{req.question} 答案 # 4. 调用LLM生成答案 try: response openai.ChatCompletion.create( modelgpt-3.5-turbo, # 或 gpt-4 messages[{role: user, content: prompt}], temperature0.1, max_tokens500 ) answer response.choices[0].message.content.strip() except Exception as e: raise HTTPException(status_code500, detailfLLM调用失败: {str(e)}) # 5. 返回答案和引用来源 sources [metadata.get(source, 未知) for metadata in results[metadatas][0]] return { answer: answer, sources: list(set(sources)) # 去重 }7.4 前端界面与整合创建app.py使用Streamlit构建一个简单的Web界面。import streamlit as st import requests import os st.title( 智能技术文档助手) st.markdown(基于项目文档回答你的技术问题。) # 配置后端API地址 API_URL os.getenv(API_URL, http://localhost:8000) question st.text_input(请输入你的问题, placeholder例如如何配置数据库连接池) if st.button(提问) and question: with st.spinner(正在检索文档并思考中...): try: response requests.post( f{API_URL}/ask, json{question: question, top_k: 3} ) if response.status_code 200: result response.json() st.success(答案) st.write(result[answer]) if result[sources]: st.info(f参考文档{, .join(result[sources])}) else: st.error(f请求失败{response.text}) except requests.exceptions.ConnectionError: st.error(无法连接到后端服务请确保API服务已启动。)7.5 运行与测试将你的技术文档PDF/MD放入./your_docs_directory目录。运行文档处理脚本python ingest.py。启动后端API服务uvicorn api:app --reload --port 8000。在另一个终端启动前端界面streamlit run app.py。打开浏览器访问Streamlit提供的地址通常是http://localhost:8501即可开始问答。这个原型虽然简单但完整实现了RAG的核心流程。你可以在此基础上扩展增加更多文件格式支持、优化分块和检索策略、加入对话历史管理、对接更强大的开源模型等。通过这个动手过程你会对LLM应用开发的各个环节有更具体、更深刻的理解。