AI 大模型与智能应用
实践与技术

专注分享 AI 大模型、Agent 开发、深度学习等技术实战与架构设计;兼具 C#/.NET 后端开发经验

118
技术文章
8
文章分类
537
技术标签
29971
累计字数
LangChain 与 LangGraph 实战指南
人工智能
2025-08-12 • 0k

LangChain 与 LangGraph 实战指南

LangChain 和 LangGraph 是两个强大的人工智能开发框架。 一、为什么需要 LangGraph? LangChain 虽然提供了丰富的工具链和 Agent 能力,但在实际企业级应用中面临一些挑战: 复杂状态管理困难: 传统 Chain 难以维护跨多个步骤的状态 循环流程不支持: 无法自然表达人类交互中的"重试 - 修正"过程 调试复杂度高: 执行流不透明,问题定位困难 长期任务控制弱: 缺乏对执行流的精确干预能力 LangGraph 通过图结构解决了这些问题,让你能够构建可预测、可控的智能体系统。 二、核心概念:图 vs 链 2.1 语言模型的三种执行模式 # 1. Prompt → LLM (最基础) response = llm(prompt) # 2. Chain (串行流程) # question -> retriever -> prompt -> LLM -> answer # 3. Graph (图结构) # 节点之间可以有任意连接关系(包括循环) 2.2 关键术语 Node: 计算单元,如 retrieval_node、llm_node、tool_node Edge: 节点之间的连接,定义执行流方向 State: 图的共享状态,所有节点读写同一份状态 Cycle: 循环边,允许流程回到之前的节点 三、实战示例:状态管理的艺术 3.1 定义状态 (Schema) 这是 LangGraph 的核心!你的应用所有数据都存在于这个状态中:

LangChain LangGraph AI Agent
阅读更多
langgraph 和 langchain 区别,实际工作中怎么使用
人工智能
2025-06-18 • 0k

langgraph 和 langchain 区别,实际工作中怎么使用

核心区别 LangChain:组件库与线性链。提供模型调用、RAG、工具等基础模块,适合构建线性、确定性的流程(如简单的问答机器人)。 LangGraph:状态机与循环图。基于 LangChain 构建,专为有状态、多步骤、含循环/条件分支的复杂 Agent 设计。它解决了 LangChain 难以处理“循环推理”和“持久化记忆”的痛点。 举例说明 场景 LangChain LangGraph 简单 RAG 检索 → 生成回答(一次性线性流程) ❌ 杀鸡用牛刀 代码生成 Agent ❌ 难以实现“生成→测试→报错→修改→再测试”的自动循环 ✅ 定义节点(生成/测试/修复)+ 条件边(通过/失败),自动循环直到成功 多 Agent 协作 需手动编排消息传递,状态管理困难 ✅ 原生支持 Supervisor/Swarm 模式,共享全局 State 实际工作中的使用策略 选型原则:流程是 DAG(有向无环图)选 LangChain;只要涉及 “循环”、“人工介入(Human-in-the-loop)”、“长期记忆”,直接上 LangGraph。 开发范式:将业务拆解为 State(数据结构)、Nodes(函数逻辑)、Edges(流转规则)。不要写成面条式代码,而是画成图。 生产落地:利用 LangGraph 的 Checkpointing 机制保存中间状态,支持任务暂停/恢复和断点调试;配合 LangSmith 进行 Trace 可视化监控。

LangChain LangGraph AI Agent
阅读更多
RAG系统评估指南:基于RAG Assessment的指标体系与实现
人工智能
2025-04-24 • 0k

RAG系统评估指南:基于RAG Assessment的指标体系与实现

本文讲解如何利用RAG Assessment框架,完成RAG(检索增强生成)系统的量化评估,覆盖核心指标体系与实操实现路径。 一、RAG系统基础流程 一套标准RAG系统的核心链路为:用户问题经Embedding模型向量化后,通过余弦相似度从向量库中召回Top K个相关文本块;将召回的上下文与问题拼接后填入约束型Prompt模板,输入大语言模型生成最终回答,以此减少模型幻觉。 二、RAG Assessment框架概述 该框架专为RAG系统评估设计,无需人工标注,可分别量化检索模块与生成模块的性能,解决两大核心痛点:一是为业务落地的RAG应用提供可量化的效果标准,替代主观判断;二是为集成检索器、父子文档等高阶优化方案提供客观的效果验证依据。 框架输入包含四类数据:用户问题、模型生成答案、检索上下文、标准答案;输出为多维度的量化评估得分。 三、四大核心评估指标 所有指标取值范围均为0-1,分数越高代表对应维度性能越好: 忠实度(Faithfulness):衡量生成答案与检索上下文的事实一致性,基于答案与上下文计算。将答案拆分为多个事实片段,统计有上下文支撑的片段占比,反映模型幻觉程度。 答案相关性(Answer Relevance):衡量生成答案与用户问题的匹配度,基于问题与答案计算。通过答案反向生成问题,计算反向问题与原问题的相似度均值,反映回答的完整性与冗余度。 上下文精确度(Context Precision):衡量相关信息在检索结果中的排序位置,基于问题与上下文计算。统计Top K结果中相关内容的排序占比,反映检索排序的准确性。 上下文召回率(Context Recall):衡量标准答案信息在检索结果中的覆盖程度,基于标准答案与上下文计算。拆分标准答案的事实点,统计能被检索上下文命中的占比,反映检索的完整性。 四、指标对应模块与优化方向 RAG系统的整体性能需结合所有指标综合评判,不同指标可定位不同环节的问题: 检索模块:对应上下文精确度、召回率。得分偏低时,可更换优质Embedding模型,或引入重排序、集成检索器等方案优化。 生成模块:对应忠实度、答案相关性。得分偏低时,可更换能力更强的大语言模型,或优化Prompt约束规则。 五、基于LangChain与Ragas的评估实现 核心实现步骤如下: 环境准备:安装langchain、ragas、arxiv加载工具、ChromaDB向量库等依赖,配置API密钥。 知识库构建:加载目标文档(如RAG Assessment原论文),完成文本分块、向量化,存入ChromaDB并定义检索器。 搭建RAG链路:编写约束型Prompt模板,串联检索、Prompt拼接、大模型生成的完整流程。 构建评估集:准备测试问题与标准答案,通过RAG链路批量生成对应答案与检索上下文,整理为标准评估数据集。 执行评估:调用Ragas的evaluate方法,传入数据集与四大评估指标,生成量化结果;可转为表格形式查看单题得分,定位系统短板并针对性优化。

RAG RAGAS 评估指标
阅读更多
提示词工程实战:从面试技巧到生产级 Prompt 设计
人工智能
2025-02-28 • 0k

提示词工程实战:从面试技巧到生产级 Prompt 设计

在大模型相关岗位的面试中,“你平时怎么写提示词?”是一个看似简单、实际很容易拉开候选人差距的问题。 如果只回答“先设置角色,再描述任务,最后加几个示例”,虽然不能算错,但很难体现真实的项目经验。因为在实际的大模型应用中,提示词并不是一段写得比较漂亮的自然语言,而是模型与业务系统之间的一份“任务协议”。 一个成熟的回答,既要说清楚提示词如何设计,也要说明如何评测、如何防止模型跑偏,以及哪些问题不能只依靠提示词解决。 一、面试时可以这样回答 如果面试官问“你是怎么写提示词的”,可以先用下面这段话概括: 我写提示词时,首先会明确这个提示词在业务链路中的职责,比如它是用于意图分类、Query 改写、RAG 问答、信息抽取,还是 Agent 工具选择。不同任务的目标、约束和评测方式不同,所以我不会使用一套万能 Prompt。 在具体设计时,我通常会定义角色与目标、输入字段、任务步骤、约束边界、输出协议和 Few-shot 示例。对于需要程序继续处理的结果,我会要求模型输出符合 JSON Schema 的结构化数据。 Prompt 写完后,我不会只凭主观感觉判断效果,而是建立测试集,覆盖正常场景、边界场景、异常场景和对抗样本,评估任务准确率、格式遵循率、幻觉率、Token 消耗和响应延迟。 每次调整 Prompt 后都需要重新运行评测集,检查是否引入了回归问题。如果模型仍然不稳定,我还会在代码层增加 Schema 校验、置信度阈值、重试、降级和人工兜底。 所以我理解的提示词工程,不只是把一句话写得更清楚,而是完成任务定义、边界约束、结构化输出和评测闭环。 这个回答的重点,是让面试官意识到你不仅会“写提示词”,还具备工程化落地能力。 二、提示词应该包含哪些部分 一份适合生产环境的提示词,通常可以拆成六个部分。 1. 角色与任务目标 首先要告诉模型,它在当前任务中扮演什么角色,以及最终需要完成什么任务。 例如: 你是企业智能助手中的意图分类模块。 你的任务是识别用户问题的业务意图,并提取相关业务参数。 角色描述不需要堆砌“资深专家”“世界顶级”等无效修饰词。真正重要的是明确模型当前的职责。 2. 输入定义 需要说明模型会接收到哪些内容,以及每个字段代表什么。 例如,一个 RAG 问答任务可能包含: 用户原始问题; 检索到的参考资料; 当前用户的身份信息; 历史对话摘要; 输出格式要求。 如果输入中包含用户内容、检索资料和系统规则,最好使用明确的标签进行隔离: <user_question> {question} </user_question> <context> {retrieved_documents} </context> 这种写法能够降低不同类型信息相互干扰的概率。 3. 任务步骤 对于稍微复杂的任务,可以告诉模型应该依次完成哪些操作。 例如,一个信息抽取任务可以要求模型: 1. 阅读用户问题。 2. 判断用户意图。 3. 提取商品名称、仓库名称和日期范围。 4. 检查必填信息是否完整。 5. 按照规定的 JSON 格式输出结果。 步骤应该围绕最终任务展开,不要要求模型输出冗长的内部思考过程。

提示词工程 Prompt Engineering LLM
阅读更多
赋能企业级开发:ABP vNext 高级实践与核心功能深入解析
后端开发
2024-12-03 • 0k

赋能企业级开发:ABP vNext 高级实践与核心功能深入解析

赋能企业级开发:ABP vNext 高级实践与核心功能深入解析 如果你认为 ABP vNext 只是一个帮你做 CRUD 的框架,那你就太小看它了。它是一个完整的、基于最佳实践的应用开发平台。本文将带你超越 Hello World,探索其如何在真实企业级项目中大放异彩。 在上一篇入门文章后,我们了解了 ABP 的基础。现在,让我们深入其更强大的功能,看看它如何解决实际开发中的复杂场景。 一、 模块化架构:构建复杂系统的基石 ABP 的模块化不仅是代码组织方式,更是架构设计哲学。 1. 定义与依赖 每个模块都是一个独立的单元。创建一个模块非常简单: [DependsOn( // 显式声明依赖 typeof(AbpAccountApplicationModule), typeof(AbpIdentityApplicationModule), typeof(YourModuleProjectDomainModule) // 依赖自己的领域层 )] public class YourModuleApplicationModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { // 配置本模块的服务 Configure<AbpVirtualFileSystemOptions>(options => { options.FileSets.AddEmbedded<YourModuleApplicationModule>("YourCompany.YourModule"); }); } public override void OnApplicationInitialization(ApplicationInitializationContext context) { // 应用启动时的逻辑 var app = context.GetApplicationBuilder(); // ... 中间件配置 } } 2. 模块生命周期 理解 PreConfigureServices, ConfigureServices, PostConfigureServices, OnApplicationInitialization 等生命周期方法,让你能在正确的时机执行代码。 3. 实战场景:插件化系统 你可以将不同功能(如“用户管理”、“订单处理”、“报表生成”)拆分独立成的模块。通过 [DependsOn] 按需组合,甚至可以动态加载模块,实现真正的插件化架构,极大提升了大型项目的可维护性和团队的并行开发效率。 二、 数据过滤器:优雅实现多租户与软删除 这是 ABP 中最“聪明”的功能之一,它能自动为你处理数据过滤逻辑。

ABP vNext 企业级开发 模块化架构
阅读更多
模型蒸馏完全指南:从BERT到BiLSTM的新闻分类实战
人工智能
2024-11-10 • 0k

模型蒸馏完全指南:从BERT到BiLSTM的新闻分类实战

目录 为什么需要模型蒸馏? 核心概念:教师-学生框架 三大关键公式(面试必考) 蒸馏的完整流程 实战代码:BERT → BiLSTM 新闻标题多分类蒸馏 面试高频考点总结 常见误区与调参技巧 一、为什么需要模型蒸馏? 1.1 一句话总结 模型蒸馏 = 让一个"小学霸"(小模型)跟着"老教授"(大模型)学习,用小模型的身体继承大模型的智慧。 1.2 现实痛点 假设你用 BERT-base(1.1亿参数)训练了一个新闻标题分类器,效果很好(准确率 95%)。但当你想部署上线时,问题来了: 问题 具体表现 推理慢 BERT 单条推理约 10-30ms,高并发时扛不住 显存大 模型本身 ~440MB,加上 KV Cache 更夸张 部署难 手机端、嵌入式设备根本跑不动 蒸馏就是解决方案:训练一个 BiLSTM(可能只有几百万参数),让它学习 BERT 的"思考方式",最终得到一个又快又准的小模型。 1.3 蒸馏 vs 其他压缩方法 模型压缩技术全景: ├── 模型蒸馏(Knowledge Distillation) ← 本文重点 │ └── 用大模型的输出指导小模型训练 ├── 模型剪枝(Pruning) │ └── 去掉不重要的权重/神经元 ├── 模型量化(Quantization) │ └── FP32 → INT8,降低精度 └── 权重共享(Weight Sharing) └── 多个参数共享同一组权重 🎯 面试考点:蒸馏是唯一一种"跨架构"的压缩方法——教师和学生的模型结构可以完全不同(如 BERT → BiLSTM),而剪枝和量化通常只能压缩同架构模型。

模型蒸馏 知识蒸馏 BERT
阅读更多
架构反思:引入 Redis 统计在线人数后,JWT 是否还有存在的意义?
架构与设计
2024-10-16 • 0k

架构反思:引入 Redis 统计在线人数后,JWT 是否还有存在的意义?

在构建 .NET Web API 后台时,为了实现在线人数统计,我们通常会引入 Redis 来存储用户的活跃状态。 这时,一个非常敏锐且直击架构本质的问题往往会浮出水面: “既然为了统计在线人数,每次请求都要去 Redis 读写一次,那 JWT ‘无状态、不查库’ 的核心优势不就没了吗?干脆用一个短小的随机字符串(普通 Token),既能节省流量,又能节省计算资源,岂不更好?” 这是一个极好的问题。它触及了软件架构的核心——权衡 (Trade-off)。 答案是:在特定场景下,普通 Token 确实更好;但在大多数现代架构中,JWT + Redis 的组合依然具有不可替代的优势。 本文将从依赖性、性能、架构扩展性三个维度,深入剖析为什么我们依然推荐保留 JWT。 1. 核心区别:强依赖 vs. 弱依赖 这是两者最根本的区别,决定了系统的可用性 (Availability) 上限。 🔵 方案 A:普通 Token (Reference Token) + Redis 在这个方案中,Token 只是一个无意义的随机字符串(一把钥匙)。 流程: 请求到达 -> 解析 Token -> 必须请求 Redis 查找对应的 UserID -> 拿到身份 -> 执行业务。 风险: 这里 Redis 是强依赖。 如果 Redis 宕机、网络抖动或连接数耗尽,整个系统的认证模块瞬间瘫痪。 所有接口都会返回 401,用户无法进行任何业务操作。 🔴 方案 B:JWT (Self-contained) + Redis 在这个方案中,Token 包含数据(一张身份证),自带 UserID 和权限。

JWT Redis 认证授权
阅读更多
注意力机制是如何“注意”的?一个词看懂上下文背后的秘密
人工智能
2024-09-16 • 0k

注意力机制是如何“注意”的?一个词看懂上下文背后的秘密

你是否好奇,当 Transformer 模型读一句话时,是如何动态判断“当前这个词,最该参考前面哪几个词”的?这个过程并非魔法,也不是人工编写的规则,而是由 Q、K、V 三个向量 配合训练完成的一套精妙计算。 可以把整个过程想象成:当前词和前面每一个词进行一次“对话”,然后互相打分,得分高的就重点参考。具体来说,分四步。 第一步:三个关键角色登场 对于序列中的每个词,模型都会为它生成三个向量: Q(Query,查询):代表“当前词”的需求。就像在大声问:“我需要理解我自己,谁有相关信息?” K(Key,键):代表每个词的“标签”。每个词举着名牌,写着:“我主要包含这类信息。” V(Value,值):每个词真正携带的具体内容,是最终要被提取的信息。 第二步:打分——“谁对我更重要” 当要理解词 A 时,模型就用 A 的 Q 向量,去和前面每个词的 K 向量计算点积(内积),得到一个标量分数。数学上,向量方向越相近,点积越大,意味着语义越相关。 例如,“北京”的 Q 遇上“首都”的 K,会因为语义高度相关得到一个很高的分数;而遇到“的”这类虚词的 K,分数往往很低。 第三步:从分数变成概率 这些原始分数经过 Softmax 归一化,被挤压成一组在 0 到 1 之间、总和为 1 的概率值。这个概率,就是最终的“注意力权重”。 于是,“首都”可能分到 0.6,“是”分到 0.1,“的”只分到 0.05。模型就真实地“知道”了:理解“北京”时,重点看“首都”。 第四步:提取信息 最后,用这些权重对所有词的 V 向量进行加权求和。权重高的词的 V 被大量保留,权重低的则几乎被忽略。融合后的向量,就携带了丰富的上下文信息,用来完成当前词的预测或理解。 那么,这一切是由谁决定的? 答案是:数据训练。 Q、K、V 向量的生成矩阵一开始是完全随机的。模型在完成“预测下一个词”或“完形填空”的任务时,会不断犯错,再通过反向传播调整这些矩阵里的参数。经过万亿次调整,模型在无数句子中捕捉到了词与词之间的统计共现规律,涌现出这样的能力: “的”通常是结构词,不重要; “首都”对一个地点名词很重要; 动词和它的主语关系紧密; 代词往往指向前文出现过的实体…… 所以,“最该参考谁”的决定,本质上就是模型在海量文本中学到的、隐藏于 Q 和 K 互动中的语义相关性判断。所谓“多头注意力”,无非是准备很多套不同的 Q、K、V 生成矩阵,让模型能同时从语法、语义、长距离依赖等多个角度去做这样的打分。 这,就是注意力机制的核心秘密。

注意力机制 Transformer QKV
阅读更多
从机器指令到面向对象:编程思想的演进与领域驱动设计
架构与设计
2024-07-24 • 0k

从机器指令到面向对象:编程思想的演进与领域驱动设计

面对领域驱动设计(DDD)的火热,很多熟悉面向对象(OOP)的开发者都会有一个疑问:这和我以前学的OOP是什么关系?是全新的东西,还是旧瓶装新酒?本文将带你回顾编程思想的演进历程,理清DDD与OOP的深刻联系,并展望现代编程思想的融合之道。 一、编程思想的演进脉络:一部与“复杂性”的斗争史 编程的发展史,就是一部工程师们如何不断地抽象和封装,以应对日益增长的软件复杂性的历史。让我们用一张时间线来直观感受这一历程: timeline title 编程思想演进历程 1950年代以前 : 面向机器编程 : 主要与硬件直接交互 1950-1960年代 : 面向过程编程 : 以步骤为中心<br>结构化程序设计 1960-1970年代 : 面向对象编程兴起 : 对象为核心<br>封装、继承、多态 2000年代 : 领域驱动设计提出 : 应对业务复杂性<br>强调领域建模 2000年代至今 : 多种思想并存 : 函数式编程复兴<br>响应式编程等 下面我们来详细解读每个阶段的核心思想与突破: 面向机器与面向过程:效率与结构的初探 面向机器:在计算机诞生初期,编程直接使用机器语言或汇编语言,程序员必须深入了解硬件细节。这种方式效率极低,难以维护,是“与硬件共舞”的时代。 面向过程:随着高级语言(如Fortran, C)的出现,面向过程的思想成为主流。它将程序看作一系列线性步骤(过程),通过函数来实现每个环节,其核心方法是“自顶向下、逐步求精”。然而,当系统变得复杂时,数据和操作分离的特性使得代码的复用和维护变得异常困难,全局变量的滥用等问题凸显。 面向对象编程的兴起:将现实世界映射入代码 为了解耦数据与操作,面向对象编程(OOP) 应运而生。它以类和对象作为基本程序单位,将数据和对数据的操作封装在一起。它通过三大特性来管理复杂性: 封装:收敛内部逻辑,只暴露必要的接口。 继承:实现代码的复用和层次的抽象。 多态:允许同一接口表现出不同的行为,极大地提高了系统的灵活性。 OOP通过对象的概念将现实世界映射到软件系统,更符合人类的思维方式,是程序设计方法学上的一次巨大飞跃。 领域驱动设计的深化:聚焦业务复杂性的战略工具 到了2004年,埃里克·埃文斯(Eric Evans)在其著作《领域驱动设计》中提出了DDD。这时,软件的规模和技术复杂性已经得到较好控制,但业务逻辑本身的复杂性成为了新的挑战。 DDD的核心是建立正确的领域模型,并让这个模型成为项目成员(包括领域专家、设计人员、开发人员)之间沟通的通用语言。它在OOP的基础上,进一步强调了边界的控制,引入了限界上下文和聚合等核心模式,来应对大型、复杂业务系统的设计和拆分问题。 二、DDD与OOP:是继承与发展,而非颠覆与取代 领域驱动设计并非一个全新的编程范式,它可以看作是面向对象思想在复杂业务系统分析和设计上的进一步发展和规范化应用。 1. 核心理念的继承 DDD完全建立在OOP的基石之上。它强调将数据和行为封装在领域对象(如实体、值对象)中,这与OOP的封装思想一脉相承。 DDD中领域模型的演化,也深度依赖于OOP的抽象、继承和多态等特性。 2. 设计层面的发展与超越 DDD在OOP关注技术实现的基础上,向前迈了一步,更强调业务领域的建模本身。 它提供了一套更为丰富的战术工具和战略设计模式,来指导我们构建更清晰的领域模型。 战术工具:精确地定义领域模型的构成元素,如实体、值对象、聚合根、领域服务、领域事件等。 战略设计:从宏观上规划系统的结构和边界,如限界上下文、上下文映射图,用于指导微服务拆分等架构决策。 一个关键的区别:贫血模型 vs. 充血模型

编程思想 面向对象 OOP
阅读更多
Seq2Seq 与注意力机制:从机器翻译到 Transformer 架构
人工智能
2024-07-23 • 0k

Seq2Seq 与注意力机制:从机器翻译到 Transformer 架构

你是否好奇,当AI翻译一句话时,它是怎么做到"翻译到哪个词,就重点看哪个词"的?答案就藏在一个叫做 “专属信息包 C” 的东西里。今天,我们用最通俗的语言,一步步拆解它的诞生过程。 一、先搞懂一个问题:为什么需要"专属信息包"? 假设我们要把中文 “我 爱 深度 学习” 翻译成英文 “I love deep learning”。 在注意力机制出现之前,传统模型的做法非常粗暴: 把整句话压缩成 一个固定长度的向量 C,然后解码器拿着这同一个 C 去翻译每一个英文单词。 这就像什么呢?就像你把一本200页的小说浓缩成一句话,然后要求别人用这句话去复述每一个章节的细节——显然不可能! 翻译 “I” 的时候,需要关注"我" 翻译 “love” 的时候,需要关注"爱" 翻译 “deep” 的时候,需要关注"深度" 每个时刻需要的信息是不同的! 所以我们需要为每个时刻量身定制一个 “专属信息包”——这就是注意力机制中动态计算的 上下文向量 $C_t$。 二、认识三位主角:Q、K、V 在计算"专属信息包"之前,我们先认识三个关键角色。用一个图书馆找书的比喻来理解: 角色 全称 比喻 含义 Q (Query) 查询 你脑子里想找的那本书的"关键词" “我现在需要什么信息?” K (Key) 键 图书馆每本书封面上的"标签" “这里有什么信息?” V (Value) 值 每本书的"实际内容" “这个信息的实际内容是什么?” 当解码器要生成某个单词时,它会: 抛出一个"查询 Q"——我当前需要什么? 拿 Q 去和所有输入词的"标签 K"逐一比对,看谁最相关 根据相关程度,从对应的"内容 V"中提取信息 三、“专属信息包 C"的计算四部曲 好了,现在进入核心环节。我们以翻译 “我 爱 深度 学习” 为例,假设当前解码器正在翻译第2个英文单词 “love”。

Seq2Seq 注意力机制 Transformer
阅读更多