万字长文一文入门AI agent开发《AI agent开发相关概念》

1. 本文大纲

2. 核心功能与相关概念

Agent可以理解为某种能自主理解、规划决策、执行复杂任务的智能体。

Agent = LLM + Planning + Feedback + Tool use

2.1 Agent的核心功能

Agent的核心功能,可以归纳为三个步骤的循环:感知(Perception)、规划(Planning)和行动(Action)

  • 感知是指Agent从环境中收集信息并从中提取相关知识的能力,

  • 规划是指Agent为了某一目标而作出的决策过程,

  • 行动是指基于环境和规划做出的动作,而行动又通过观察(Observation)成为进一步Perception的前提和基础,形成自主的闭环学习过程。

Agent和LLM的一个最大的区别就是Agent可以使用工具,借助外部的工具来扩展功能,从而可以处理更加复杂的任务 。所以Agent是释放LLM潜能的关键,LLM作为驱动Agent的动力,Agent为LLM提供了行动的主观能动性。

2.2 模型参数

  • OpenAI 通用协议,定义标准的出入参。

2.2.1 调用模型的通用参数

2.2.1.1 温度参数 temperature

用于控制生成文本的随机性和多样性的一个超参数,在ChatGPT等大模型中,temperature参数通常设置在02之间(有些模型不允许设置为0,有些模型是在01之间),这个具体取决于生成任务的需求,如果要求更精确的答案,则temperature可以适当调低,如果要求更多样化更随机的答案,就将temperature调高,如果要求比较均衡的效果,就将temperature调为1

2.2.1.2 采样参数Top p

控制模型生成下一个tokens的候选列表的数量,当这个值比较大的时候,就能选择更多候选token,也就是能采样到的词就越多,这样生成结果就越多样化,越随机;当这个值比较小的时候,那么符合概率条件的次就很小,那么就没有几个词能被采样到,因此输出就会非常固定、稳定。

结合Temperature参数来看,就会比较有意思,当Temperature设定的值很高,那么token的概率分布就很均衡,那么Top P如果也比较大,在采样的时候就会更容易随机选token,结果就更不可控 ,如果Top P很小,那么采样的集合就很小,那么仍然是取Top概率的几个token,结果依然比较固定;如果Temperature设定比较小,那么token的概率分布会比较极端,那么即使Top P的值很大,采集到多样化token的概率还是很小,所以生成结果也并不会太多样化。因此,这两个参数是要结合起来进行调节的,都不要太极端,并且需要根据具体业务情况来调节最优参数效果。而且Temperature和Top P如果调的太小,输出确实是固定、稳定的,但是也有出现无线循环的可能性,因为没有其他token有概率,导致调跳不出来这几个固定的词,比如类似"我和你和我和你和我你和我..."这种情况。

2.2.1.3 最大输出字符max tokens

现在基本模型厂商对这个字段都统一定义为了模型最大输出长度,之前还会使用max_new_tokens字段。有一个隐含的概念context window:上下文长度,指的是模型一次能处理的总字数上线,包括你的输入和他的输出。

有些模型限制了最大输入tokens,有些模型没有限制,所以代码中需要动态设置max tokens值

  • 限制了最大输入tokens的模型,需要限制自己模型的输入不能超过这个值

  • 没有限制最大输入tokens的模型,需要限制max_tokens值为 min(model'max'tokens, context'window-input'tokens)

2.3 工具

2.3.1 工具的定义规范

  • 站在模型的角度思考而不是站在业务角度

    • 优秀的工具定义包含使用示例

    • 明确区分和其他工具的界限,需要链式调用多个工具的,尽量封装为1个工具

  • 清晰化参数名称或描述

    • 不要使用多层的对象嵌套,所有参数尽可能打平

    • 接口尽量简单,避免过多参数由服务端自己补全默认参数

    • 一些业务的默认参数不要暴露给模型,由服务端自己补全默认参数

    • 参数描述清晰、边界明确、有明确的输入规范

    • 时间参数要谨慎

  • 返回大模型更容易理解的结果

    • 在不完全依赖模型返回的内容的链路时,允许模型更加自由的输出
  • 更加兼容性的输入

    • 接口一些变量允许模型多种输入,不会因为输入参数非法导致调用失败(调用失败重新生成参数的成本比较高)
  • 完善的报错处理流程

    • 通过报错流程可引导模型调整参数,以及终止调用
  • 做好完善的测试

    • 上线前运行大量输入示例,观察模型容易犯哪些错误,case by case修正

2.3.2 模型如何调用工具?

2.3.2.1 prompt

这种思路一般是两个方式:

  • 考验模型的指令追随能力。通过prompt工程来,把模型所有可以调用的工具接入到prompt中,让模型去选择,比较依赖模型能力

  • 使用小模型,进行工具调用微调。准备大量的输入和输出样本,让模型能够学习到在合适的时机去调用模型。

2.3.2.2 Function Call 协议

Function Call解决了工具调用能力的效率,在单个模型上实现了标准化,但是Function Call没有把所有工具调用统一 ,同时调用工具还有很多其他问题,包括工具管理等这些问题都没有解决 。所以有了后来的MCP

代码中就不维护工具相关的prompt了

  • 输入给一段结构化信息模型,Message 和 tools

  • 大模型有自己的会话模版 Chat Template :大模型在自己内部进行解析,封装为prompt ,再通过标准的输出协议,将think 和toolcalls 返回给应用程序,应用工具根据标准的 think 和toolcalls 进行输入输出了。

不同的大语言模型有不同的会话模板,而各大语言模型会基于自身的会话模板所转化的会话和工具调用语料数据进行微调,因此具有很好的指令跟随能力。模型训练的时候会训练这样的格式,所以使用Function Call协议可以最大程度发挥模型的作用,因为输入的格式符合他训练的格式,相当于模型层把这个给屏蔽了,比自己写prompt要准确率更高一些。

每家厂商的Function Call格式都不一样

2.3.2.3 MCP 目标:一套工具可以适配所有LLM应用程序

Claude MCP,即模型上下文协议(Model Context Protocol),是 Anthropic Claude 的一个开源开放协议,旨在建立 AI 模型和开发环境之间的统一上下文交互,通过提供标准化的上下文信息访问,使 AI 模型能够更好地理解和处理代码。就像给它们之间搭建了一座桥梁,使得开发者可以通过一套标准将 AI 应用和数据源连接起来 。

之前的工具调用、工具描述都是维护在各个agent代码中的,应用和应用之间工具无法复用。基于MCP协议,出来了很多第三方的MCP平台,用户可以把自己的工具托管在MCP平台,平台维护工具的调用、工具的使用描述、参数协议等,各个应用对接不同的MCP Server即可。

对于Agent开发者主要理解server和client:

  • MCP Server:一个服务框架,类似Spring的东西,把工具的逻辑写到MCP Server里,启动服务后,对外提供两个核心接口

    • List接口:查询现在Server里有哪些工具接口可调用,详细返回每个工具接口的描述、参数等信息

    • 工具Api:就是我们实现的各种工具

  • MCP Client:Server对应的SDK,类似RPC客户端的东西,用它来连接MCP Server,主要两个功能

    • 调用List接口:和Server建立连接以后,可以通过Client调用List接口,获取工具列表

    • 调用工具:传入工具名、参数,调用Server上跑着的工具,拿到返回值。

MCP工具传给大模型还是需要上述两个方法,prompt或者是Function Call

2.4 记忆

2.4.1 长期记忆

长期记忆是指Agent中存储时间较长的信息,它类似于人类大脑中的记忆,能够保留大量的数据和经验,并且可以存储很长时间,甚至是永久性的。

  • 事实记忆(Factual Memory):存储有关用户、偏好和领域特定信息的知识

  • 情景记忆(Episodic Memory):过去的交互和经验

  • 语义记忆(Semantic Memory):概念及其关系的理解

2.4.2 短期记忆

短期记忆是指Agent中存储时间较短的信息,它通常用于处理当前任务或对话中需要快速访问的信息。

  • 对话历史(Conversation History)

    • 本次对话的上下文
  • 工作记忆(Working Memory)

    • 本次任务列表,临时产物
  • 注意力上下文(Attention Context)

    • 本次任务的目标

2.4.3 Mem0 记忆框架

2.4.3.1 记忆检索:
  • 向量检索路径:基于语义相似度匹配,进行快速检索。

  • 图谱检索路径:结构化关系推理,匹配存储的三元组(主体-关系-客体),精确匹配推理,并使用BM25重排序机制对检索结构进行优化

  • 混合评分层:通过加权融合不同数据库的检索结果,优先返回高相关性和近似度的信息。

2.4.3.2 记忆写入:
  • 提取阶段:使用LLM(如GPT-4o)从对话中识别实体、关系和关键事实,生成结构化记忆片段。

  • 更新阶段:通过冲突检测算法(如基于图数据库的Neo4j)对比新旧记忆,执行ADD/UPDATE/DELETE操作

  • 评分机制:综合时间衰减因子(如指数衰减函数)、用户交互频率和语义相关性生成记忆权重。

2.5 Agent 类型

2.5.1 React(Reasoning & Acting 思考与行动)

模型通过"思考-行动-观察"循环自主决策,例如在复杂问题的问答过程中需要动态选择工具使用(查询订单、知识库检索)。

  • 优点:灵活应对复杂场景,减少人工规则设计成本。

  • 缺点:可能出现不可控的推理路径,需依赖模型的强推理能力,很可能出现一直不满意的结果。

  • 案例:前段时间全网火爆的Manus,就是属于ReAct模式的自主决策型Agent,而且用了很多CodeAct的模式(基于代码的规划、行动和观察)。

2.5.2 Plan and Execute(规划与执行)

模型先规划完整步骤(如 "数据清洗 → 分析 → 生成报告"),再分步执行。

  • 优点:逻辑清晰,适合需要前置规划的领域(如数据处理)。模型调用次数少,成本比较低。

  • 缺点:若规划阶段出错,后续执行可能偏离目标,会产生错误累计。

2.5.3 Multi Agent类型

  • 转交模式(Handoff):Agent之间根据任务内容自行决策,将任务动态转交给其他Agent,没有固定流程或中央管理员,依赖大模型自主判断转交逻辑。比如,在客户服务领域,售前Agent发现客户问题是售后问题,就将问题转交至售后Agent。

  • 嵌套模式(Nested):Agent的组织是通过分层架构实现嵌套串联的,例如"Agent 1 → Agent 2 → Agent 3"这样的模式层层推进,之后再"Agent 3 → Agent 2→ Agent 1"层层回溯。这样的方式,主要是将其他子Agent作为工具或者插件的形式引入。

  • 主代理模式(Master):有一个主Agent作为中心去调度、编排子Agent,问题进来之后,可以通过中心主Agent去进行任务的规划和分解,然后交给子Agent执行,执行结果返回上报给中心主Agent,然后由主Agent去决定下一步要交给哪个Agent执行。比如,客户服务领域下,我们有很多场景是需要按照标准流程执行的,可以提前将多个子模块的Agent编排起来,然后统一由一个场景主Agent来调度,就可以实现可控的完成复杂、标准流程的执行。

  • 群聊模式(GroupChat)(比较少见):没有一个主Agent作为中心去调度编排,问题进入到一个群聊的环境之后,各个Agent自发根据情况去进行任务的规划和执行,甚至各Agent之间会进行讨论、辩论。比如,在一个钉钉群里,用户提供一个问题之后,多个Agent进行脑暴,给出方案,甚至Agent之间会进行讨论和竞争,来给出最终解决方案。

多Agent系统的模式种类繁多,还有许多其他形式尚未列举,比如反思模式、竞争模式、博弈模式、各种混合模式等等,其实多Agent没有一个固定的框架,这就很像公司或者机构的人员组织架构,有层级化的、有扁平化的、有小组化的、也有垂直化的组织形式,不同体量的公司、不同体量的项目,适合的组织架构可能也不相同,多Agent也一样,适合你的具体场景的模式才是最好的。

2.5.4 Multi agent 和 单 agent 调 tools的区别

如果模型足够强,业务对于成本、延时没有要求 ,我觉得单agent足够。很多论文的研究都发现,模型在multi agent协作、切换的过程中不会明显提高解决问题的能力,子agent返回的内容往往过于片面,多次压缩会放大模型的幻觉问题。现在很多最新的AI原生产品,例如claude code、open manus、cline等这些优秀agent的应用都是直接单agent通过react调用工具完成复杂的任务。

如果业务有以下几个特点,我认为multi agent是有必要的

  • 对延时、成本管控有要求。可以通过微调小模型或者使用较为便宜的模型来完成一个独立的任务

  • 解决问题涉及到领域专业知识,涉及到某个agent子域的问题可以闭环解决。

  • 团队分工问题。不同agent是由不同团队提供的能力,如果其他团队提供的是tool的话,单agent也是可以的

3. Langgraph 框架

3.1 Agent框架开发

目前业务上大部分的核心逻辑都是在java应用层实现的,AI应用的业务迭代速度较快,框架较多,前期调研尝试的次数也会比较多,目前对于java和python应用的支持程度已经很成熟了,所以建议AI应用这块的核心逻辑单独使用一个服务部署。

核心实现层和java应用层可以使用SSE来进行通信 Server-Sent Events ,一种基于 HTTP 协议的技术,允许服务器主动向客户端(如浏览器)推送数据。传统的 HTTP 请求是客户端"拉取"数据,而 SSE 实现了服务器"推送"数据**)**很多工具输出的结果在前端可能需要单独渲染,这部分的工作就可以在java应用层实现。执行过程中的消息持久化和对前台的消息协议转换都在java应用层完成, 保证核心实现层更加轻量。

3.2 开发流程

AI应用的落地很核心的步骤就是跑通整个链路 ,模型能够理解用户输入的业务问题,能够准确构造工具输入的参数,能够识别工具的输出。执行链路如果能够跑通,剩余的就是增加链路稳定性和优化模型效果的问题了。

在业务调研的前期建议大家直接使用平台来进行自己链路的测试搭建 。可以节约前期尝试的成本。当整个demo完成验证和搭建之后,再使用agent框架来实现自己的逻辑,提升agent的可控性和效果

3.3 Langgraph介绍

LangGraph 是基于Langchain框架,一个用于构建复杂、 有状态的多智能体应用的框架,通过图结构建模工作流,支持灵活的状态管理、模块化扩展和高级执行控制。其核心优势在于:

  • 可视化工作流:通过图结构直观定义智能体逻辑。

  • 持久化:检查点在每个超级步保存图状态的快照,允许随时恢复。

  • 模块化设计:通过子图实现功能复用与复杂系统构建。

  • 灵活路由控制:条件边和入口点实现动态流程决策。

  • 人工干预:人工输入集成到自动化流程中,允许在关键阶段进行决策、验证或修正。

3.4 核心概念

3.4.1 工作流建模为图

LangGraph 的核心是将智能体工作流建模为图。可以使用三个关键组件来定义智能体的行为

  • State:State 称为状态,是共享的数据结构,代表应用的当前快照(snapshot),用于在图中传递和存储节点间的数据。关键特性:

    • 使用 TypedDict 定义状态结构,提供类型安全

    • 可以通过 Annotated 定义合并策略

    • State 在所有节点间共享和传递

    • 通过 reducer 函数定义如何合并状态更新

  • Node:Node 称为节点,是Python函数,封装代理(Agent)的逻辑。接收当前State作为输入,执行计算或调用工具,并返回更新后的State,可以包含:

    • LLM 调用

    • 数据处理

    • 业务逻辑

    • 条件判断等

  • Edge:Edge 称为边,是连接图节点间有向线段,是一个Python 函数,根据当前 State确定接下来执行哪个Node。它们可以是条件分支或固定分支。是智能体工作以及不同节点之间如何通信的重要组成部分。一个节点可以有多个出站边。如果一个节点有多个出站边,则所有这些目标节点将在下一个超级步中并行执行。边有几种主要类型:

    • 普通边:直接从一个节点到下一个节点。

    • 条件边:调用函数来确定接下来要去哪个或哪些节点,根据不同条件路由到不同的node。

    • 并行操作:可以并行执行多个任务

通过组合 Nodes 和 Edges,可以创建复杂的、循环的工作流,这些工作流会随时间更新State。

关键点:Nodes 和 Edges就是 包含LLM 的 Python 函数或普通的 Python 代码。

3.4.2 编译compile

Langgraph构建的图都是静态的,编译图时,首先定义状态,然后添加节点和边,最后进行编译。它对图的结构进行了一些基本检查(例如,没有孤立节点)等,或者在编译时添加一些预定义的参数,比如是否加入检查点等等,编译这个步骤是不可避免的。

3.5 高级特性

3.5.1 子图

子图是一个图,作为一个节点被另一个图使用。子图也是图,同样包含上述说的,状态、节点、边、编译、构建五个步骤。

3.5.1.1 有如下两个适用场景:
  • 需要在单个或多个图中重复使用一组节点时,可定义一个子图,即可在多个父图或者一个父图中使用它们,类似于抽象封装。

  • 当希望不同的团队独立地处理图的不同部分时,可以将每个部分定义为一个子图,要确保子图接口(输入和输出模式)遵守规范,构建父图而无需了解子图的任何细节。

有两种方法可以将子图添加到父图

3.5.1.2 编译子图

**编译子图:**父图和子图共享状态键,无需在进入或退出时转换状态即可共享状态,如果父图和子图状态之间传递非共享的键,会被忽略。

3.5.1.3 调用子图

定义一个具有完全不同模式的子图。创建一个调用子图的节点函数**:**此函数需要先将输入(父)状态转换为子图状态,然后再调用子图;并在从节点返回状态更新之前将结果转换回父状态

3.5.2 持久化

LangGraph 内置了通过检查点器(checkpointer)实现的持久化层。当使用检查点器编译图时,检查点器会在每个超级步骤(super-step)保存图状态的检查点(checkpoint)。这些检查点会存储到线程(thread)中 ,执行完图后可访问这些检查点。由于线程允许在执行后访问图的状态,因此实现了人工介入(human-in-the-loop)、记忆(memory)、时间回溯(time travel)和容错(fault tolerance)等强大功能。

线程ID(thread_id)是标识一系列检查点的唯一ID。当使用检查点器(checkpointer)编译图后,调用图时必须在config的可配置部分指定thread_id。

3.5.3 检查点checkpoint

检查点是在每个超级步骤保存的图状态快照,由具有以下关键属性的StateSnapshot对象表示。实际过程中,checkpoint保存的信息足够用户去恢复整个图。

  • config: 与此检查点关联的 Config,基于语义相似度匹配。

  • metadata: 与此检查点关联的 Metadata。

  • values: 此时状态通道的值。

  • next: 图中接下来要执行的节点名称元组。

  • tasks: 包含接下来要执行的任务信息的PregelTask对象元组。如果之前尝试过该步骤,它将包含错误信息。如果图在节点内部被动态中断,任务将包含与中断相关的附加数据。

3.5.3.1 获取状态

获取图的每一个状态时,你必须指定一个Thread_id。你可以通过调用graph.get_state(config)来查看图的最新状态。这将返回一个StateSnapsho t对象,该对象对应于 config 中提供的线程 ID 关联的最新检查点,或者对应于为该线程提供的检查点 ID 关联的检查点。

plaintext 复制代码
# get the latest state snapshot
config = {"configurable": {"thread_id": "1"}}
graph.get_state(config)

# get a state snapshot for a specific checkpoint_id
config = {"configurable": {"thread_id": "1", "checkpoint_id": "1ef663ba-28fe-6528-8002-5a559208592c"}}
graph.get_state(config)
plaintext 复制代码
StateSnapshot(
    values={'foo': 'b', 'bar': ['a', 'b']},
    next=(),
    config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef663ba-28fe-6528-8002-5a559208592c'}},
    metadata={'source': 'loop', 'writes': {'node_b': {'foo': 'b', 'bar': ['b']}}, 'step': 2},
    created_at='2024-08-29T19:19:38.821749+00:00',
    parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef663ba-28f9-6ec4-8001-31981c2c39f8'}}, tasks=()
)
3.5.3.2 获取状态历史记录

可以通过调用graph.get_state_history(config)来获取给定线程的完整图执行历史记录。

  • 返回与 config 中提供的线程 ID 关联的StateSnapshot对象列表

重要的是,检查点将按时间顺序排列,最近的检查点 / StateSnapshot位于列表的首位。

可以从任意的SnapshotId来恢复整个图

plaintext 复制代码
config = {"configurable": {"thread_id": "1"}}
list(graph.get_state_history(config))
plaintext 复制代码
[
    StateSnapshot(
        values={'foo': 'b', 'bar': ['a', 'b']},
        next=(),
        config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef663ba-28fe-6528-8002-5a559208592c'}},
        metadata={'source': 'loop', 'writes': {'node_b': {'foo': 'b', 'bar': ['b']}}, 'step': 2},
        created_at='2024-08-29T19:19:38.821749+00:00',
        parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef663ba-28f9-6ec4-8001-31981c2c39f8'}},
        tasks=(),
    ),
    StateSnapshot(
        values={'foo': 'a', 'bar': ['a']}, next=('node_b',),
        config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef663ba-28f9-6ec4-8001-31981c2c39f8'}},
        metadata={'source': 'loop', 'writes': {'node_a': {'foo': 'a', 'bar': ['a']}}, 'step': 1},
        created_at='2024-08-29T19:19:38.819946+00:00',
        parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef663ba-28f4-6b4a-8000-ca575a13d36a'}},
        tasks=(PregelTask(id='6fb7314f-f114-5413-a1f3-d37dfe98ff44', name='node_b', error=None, interrupts=()),),
    ),
    StateSnapshot(
        values={'foo': '', 'bar': []},
        next=('node_a',),
        config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef663ba-28f4-6b4a-8000-ca575a13d36a'}},
        metadata={'source': 'loop', 'writes': None, 'step': 0},
        created_at='2024-08-29T19:19:38.817813+00:00',
        parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef663ba-28f0-6c66-bfff-6723431e8481'}},
        tasks=(PregelTask(id='f1b14528-5ee5-579c-949b-23ef9bfbed58', name='node_a', error=None, interrupts=()),),
    ),
    StateSnapshot(
        values={'bar': []},
        next=('__start__',),
        config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef663ba-28f0-6c66-bfff-6723431e8481'}},
        metadata={'source': 'input', 'writes': {'foo': ''}, 'step': -1},
        created_at='2024-08-29T19:19:38.816205+00:00',
        parent_config=None,
        tasks=(PregelTask(id='6d27aa2e-d72b-5504-a36f-8620e54a76dd', name='__start__', error=None, interrupts=()),),
    )
]
3.5.3.3 回放

也可以回放之前的图执行。使用thread_id和checkpoint_id调用图,那么我们将重放对应于checkpoint_id的检查点之前已执行的步骤,并且只执行检查点之后的步骤。

  • thread_id 是线程的 ID。

  • checkpoint_id 是指线程中特定检查点的标识符。

调用图时,你必须将这些作为 config 的configurable部分传递

2.5.3.4 checkpoint持久化

Langgraph 提供了内存版本的持久化保存checkpoint的方式,同时内置提供了mongodb、postgre方式,提供了跨容器、跨时间周期访问的能力,具体可参照官网实现。

3.5.4 人工干预(human in loop)

支持将人工输入集成到自动化流程中,允许在关键阶段进行决策、验证、补充或修正。这在基于 LLM 的应用中特别有用,在低容错场景或者模型认为用户表达不清楚,或者在工具执行前发现用户提供参数不够,可以通过 interrupt 函数中断图,等待用户补充后,进行图执行恢复。

3.5.4.1 主要的使用场景:
  • **查看工具调用:**在工具执行之前,人工可以查看、编辑或批准 LLM 请求的工具调用。

  • **验证 LLM 输出:**人工可以查看、编辑或批准 LLM 生成的内容。

  • **提供上下文:**使 LLM 能够明确请求人工输入以进行澄清或提供额外细节。

实现方式: interrupt 函数

一般的使用方式 interrupt 函数+ Command命令,实现暂停+恢复

3.5.4.2 查看和编辑图状态(查看工具调用、验证 LLM 输出):

暂停图以查看和编辑图状态。这对于纠正错误或使用附加信息更新状态很有用。这种模式通常涉及使用人工输入更新状态。

3.5.4.3 提供上下文

获取输入:在图的特定步骤明确请求人工输入。这对于收集附加信息或上下文以告知智能体的决策过程或支持多轮对话很有用。

  • 等待用户输入以继续对话

  • 通过 handoff 路由到另一个智能体(或路由回其自身,例如在循环中)。

3.5.4.4 Command 原语
  • 使用 interrupt函数时,图将在 interrupt 处暂停并等待用户输入。

  • 图执行可以使用 Command 原语恢复,

  • 可以通过 invoke、ainvoke、stream 或 astream 方法传递。

Command 原语提供了几个选项来控制和修改图在恢复期间的状态

  • 向 interrupt 传递值:使用 Command(resume=value) 向图提供数据,例如用户的响应。执行从使用interrupt 的节点开头恢复,但这次 interrupt(...) 调用将返回在 Command(resume=value) 中传递的值,而不是暂停图。

  • 更新图状态:使用 Command(update=update) 修改图状态。请注意,恢复从使用 interrupt 的节点开头开始。执行从使用 interrupt 的节点开头恢复,但使用更新后的状态。

3.5.5 流式返回

使用Langgraph完成整个图结构之后,涉及到执行过程中如何给前端/服务端 返回执行中间结果的问题。

  • 需要使用一个异步队列来返回event事件

  • 至于事件里面的值可以自行约定,

  • 服务端/前端收到对应的event事件后可以进行对应的封装和渲染。

在什么时机发送event事件

  • Langgraph因为是继承于Langchain框架的,可以使用Langchain中的callback机制。

    • 在大模型开始/结束/吐出心的token等环节回调,这时候可以集成callback类,将大模型的输入、出通过Event事件发送给前端/服务端。

4. LLM 应用测评

评测效果是大模型应用的一个巨大的痛点。如果一个LLM应用的效果不能被量化地评估出来,算法、工程同学的工作就无法被评估,优化的方向也就变得很模糊。从实践中看,影响整个应用的效果可能有以下几个方面 :

  • 模型替换

  • 工具升级

  • prompt升级

  • pipeline改变

4.1 端到端的评测

评测一个应用的好坏最直接的办法就是端到端的评测,而这个评测是个十分消耗人力的工作,要想评测结果绝对准确,最好的方式永远是专业的评测团队针对每一条case进行人工打分判别,但事实上大模型应用的迭代速度比较快,只有自动化的评测方式才能满足每周一个迭代的速度。

大模型评测的要求:

  • 端到端的输出内容可以转换成文字类型(编码/VLM)

  • 可以使用prompt对评测指标进行一个清晰的描述

评测指标的prompt也需要进行评测

  • 尽可能减少自动化评测链路与人工评测的指标的差距。

  • 在初版prompt上与人工评测的结果做对比,case by case地去调整prompt。

4.2 组件评测

更容易落地和操作的就是针对Agent应用当中的组件进行单独评测。

  • prompt

  • 工具

  • 子Agent效果

  • 意图识别

  • RAG

  • ......

评测方法:

  • 客观性评测:人工/程序来构造组件的标准输入输出,适合于有固定输入输出的组件

  • 基于人的主观性评测:没有唯一标准,需要多个业务方评委主观感受

  • 基于模型的评测:用更强或专门的模型来评估被测模型输出,优化评测prompt

相关推荐
AI产品测评官2 小时前
Moka与北森用户如何接入世纪云猎,搭建完整AI招聘寻访链路
人工智能
qq_366566502 小时前
2026最新:5款AI视频口型同步工具实测横评,视频翻译后嘴型对不上的终极解决方案
人工智能·计算机视觉·新媒体运营
ofoxcoding2 小时前
在AI API聚合平台配置DeepSeek V3.2提示词缓存实战:快速接入与成本优化指南
人工智能·spring·缓存·ai
Godspeed Zhao2 小时前
Level 4自动驾驶系统设计3——功能与场景3
人工智能·机器学习·自动驾驶
weixin_397574092 小时前
PDF复杂表格的1:1还原引擎:跨页表格自动拼接技术实战
大数据·人工智能·pdf
梦想三三3 小时前
OpenCV银行卡数字识别项目(图像预处理与字符分割)
人工智能·opencv·计算机视觉
m0_634666733 小时前
Anthropic Fable/Mythos 被紧急暂停:前沿模型商业化开始碰到真正的政策墙
人工智能·ai·ai编程
程序员cxuan3 小时前
LobsterAI 快把职业门槛打没了
人工智能·程序员
cqbzcsq3 小时前
CellFlow虚拟细胞论文阅读
论文阅读·人工智能·笔记·学习·生物信息