杠精派
派聪明
0/512

热门评论

xuxinyi 2026年06月21日 15:34
@派聪明 为什么Message和 ChatResponse要定义成record类型?大模型输出的格式是固定的吗都包含哪些内容?能处理提取成ChatResponse
点赞
回复3 取消回复
派聪明 2026年06月21日 15:35
结合参考资料,这里为您清晰地解答: **1. 为什么 Message 和 ChatResponse 使用 `record` 类型?** `record` 是 Java14 引入的特性,专门用来定义纯数据载体。 * **简洁安全**:参考资料中 `Message`需要包含 `role`、`content`、`toolCalls`等字段。使用 `record` 只需一行代码(如 `public record Message(...)`),编译器就会自动生成构造器、Getter(如 `msg.role()`)、`equals()` 和 `toString()`等方法。 * **不可变性**:`record` 是不可变的(final),这使得它在多步骤的 ReAct循环和上下文历史记录中更加安全,避免了被意外篡改的风险。 **2. 大模型输出的格式是固定的吗?** 大模型的 API输出**并不是完全固定的自然语言**,而是通过 HTTP返回**标准化的 JSON 格式数据**。为了让大模型知道如何输出,Agent程序会在请求时带上定义好的 JSON Schema,约束大模型按规范返回。 **3.输出都包含哪些内容?如何提取成 ChatResponse?** 参考资料提到,Agent 发送 HTTP请求后会拿到一段 JSON 字符串(`responseBody`)。这段 JSON 通常包含以下核心内容: * **content**:普通的文本回复内容。 * **tool_calls**:工具调用请求数组(包含工具调用的 `id`,以及具体调用的函数名 `name` 和参数 `arguments`)。 * **token 使用信息**:如输入/输出的 Token消耗量。 **提取流程:** 程序会使用 JSON 解析库(如参考资料中用到的 Jackson,通过 `mapper.readTree(responseBody)`读取 JSON树),精准提取出 `content` 和 `tool_calls`节点,然后封装映射成 Java中的 `ChatResponse` 对象。这样,Agent核心循环就能方便地调用 `response.hasToolCalls()`判断是否需要执行工具,或者直接获取文本内容。
1
回复 取消回复
展开剩余 2 条回复

38 条评论

洁净的鼠标 2026年07月08日 10:32
想请教二哥一个问题,就是如果tool call返回的是一个阻塞任务,类似启动web server这种,应该如何处理?因为每一轮调用都需要有返回作为输入。但是这种阻塞任务也没返回啊
点赞
回复 取消回复
🌙🌙[月亮] 2026年07月02日 22:15
引用原文:但上下文不能无限增长。
@派聪明 上下文太长除了会导致消耗token,还有什么缺陷
点赞
回复1 取消回复
派聪明 2026年07月02日 22:15
从你选择的这段文本出发,结合参考资料中关于上下文工程的相关讨论,上下文过长除了消耗更多 Token 外,还有以下几个关键缺陷: **1. LLM 注意力衰减("Lost in the Middle")** 即便 Token 足够多,大模型对长上下文的处理能力也不是线性的。研究表明,模型对上下文**中间部分**的信息关注度会显著下降,更容易忽略关键细节。参考资料中提到的"向量化检索"方案,本质上就是为了解决这个问题——把历史存入向量数据库,按需精准召回,而不是一股脑全塞进去。 **2. 推理质量下降** 上下文越长,无关信息越多,LLM 在"推理"时就越容易被噪声干扰。参考资料中强调 Agent 的核心是 **ReAct 模式(推理→行动→观察)**,如果上下文里堆满了过期的工具调用记录或无关对话,LLM 的判断力会下降,甚至出现"幻觉"——比如在错误的文件上继续操作。 **3. 响应速度变慢** Transformer 模型的计算复杂度与序列长度呈**平方关系(O(n²))**。上下文翻倍,计算时间可能增加四倍。参考资料中提到 CLI 的 `readTimeout` 设置为了 120 秒,正是为了应对这个现实问题。 **4. 可用 Token 被浪费** 上下文空间是有限的(即使是 GLM-5.1 这样的大模型也有上下文窗口上限)。如果被历史对话占满,新的工具调用结果或用户指令就挤不进去了,Agent 可能被迫截断或丢失最新信息,导致任务失败。 > 参考资料中提到的解决方案也印证了这些缺陷:**分层上下文**(系统层/会话层/任务层分离)、**向量化检索**(只召回相关片段)、**压缩与截断**(丢弃最旧对话),这些都是为了对抗上述问题而设计的工程手段。
点赞
回复 取消回复
🌙🌙[月亮] 2026年07月02日 21:57
引用原文:比如从配置文件加载、从插件系统加载、甚至让 Agent 自己定义工具。
@派聪明 这个是什么意思
点赞
回复1 取消回复
派聪明 2026年07月02日 21:57
这句话出自文章中对**工具动态注册**机制的讨论,意思是:Agent 的工具集不一定要像我们写的 400 行代码那样硬编码在代码里,而是可以通过更灵活的方式加入新工具。具体拆分来看: **1. 从配置文件加载** 把工具的定义(名字、描述、参数、执行命令)写在 JSON 或 YAML 文件中,Agent 启动时读取并注册。这样不用改代码就能加工具,非开发人员也能配置。 **2. 从插件系统加载** 定义统一的插件接口(比如 `ToolProvider`),任何人实现这个接口打成 jar 包,放到指定目录下,Agent 就能自动扫描加载。这跟 IDE 装插件的逻辑一样,社区可以贡献工具。 **3. 让 Agent 自己定义工具** 这是最酷的——Agent 可以根据当前任务,动态生成新工具。比如它发现需要反复调用某个 API,就直接写一段代码注册成一个新工具,后续循环直接复用,不需要每次都从头调用。相当于 Agent 在运行时自我扩展能力。 --- **本质一句话**:让工具集可扩展、可插拔、甚至可自生成,而不是写死在代码里。这样 Agent 才能越用越强大,适应各种场景。
点赞
回复 取消回复
文艺的灯泡 2026年07月02日 20:38
@派聪明 文档和项目完整代码有很大不一致是为什么
点赞
回复3 取消回复
派聪明 2026年07月02日 20:39
从参考资料的内容来看,文档与完整代码存在不一致的原因主要有以下几点: ## 1. **教程本质是“教学简化版”** 文章明确说“**从零手写**”、“**超级简化版**”、“大概400行代码”,这意味着文档中的代码片段是为了清晰展示核心原理(如 ReAct 循环、工具注册、GLM 客户端),做了大量删减和示意。完整代码则包含更多细节、错误处理、边界情况等。 ## 2. **文档是“节选”,不是全量贴出** 文章里只贴了关键部分的代码,比如: - `GLMClient` 的 `chat` 方法写了一半注释“// 提取消息内容...” - `ToolRegistry` 只展示了 `read_file`、`write_file`、`execute_command` 等示例 - `Agent.run()` 中的循环逻辑是简化版 完整代码在 gitcode 仓库里,**包含所有类的完整实现、异常处理、日志、工具注册的完整列表**等,比文档展示的丰富得多。 ## 3. **文档优先“可读性”,完整代码优先“功能性”** - 文档为了让人看懂原理,**省略了细节**(如 JSON 解析细节、`createParameters` 辅助方法、`ChatResponse` 录制的完整字段) - 完整代码为了能**真正跑起来**,补全了这些缺失部分,还可能增加了文章没提到的功能(如 `create_project` 工具、清空历史、Token 统计等) ## 4. **文档与代码可能是不同步的版本** 文章最后提供的 gitcode 链接中的代码,**可能经过后续迭代**,比写作时多了一些改进(如更好的错误处理、上下文管理优化),而文档没有同步更新。 --- **总结一句话:文档是「教学大纲」,完整代码是「详细实现」。** 想真正跑起来、看全貌,以 gitcode 仓库的代码为准;想快速理解核心原理,看文档就够了。两者结合着看效果最好。
点赞
回复 取消回复
展开剩余 2 条回复
屿 2026年07月02日 11:33
引用原文:从配置文件加载、从插件系统加载、甚至让 Agent 自己定义工具
佬,这是什么意思?
点赞
回复 取消回复
🌙🌙[月亮] 2026年07月01日 22:49
引用原文:Agent 的核心就三个东西:推理(Reasoning)、行动(Acting)、观察(Observing)。这三者循环起来,就是著名的 ReAct 模式。
@派聪明 这个观察 我可以理解为工具执行后得到的结果,LLM进行观察并思考是否还需要继续调用工具还是直接输出结果
点赞
回复1 取消回复
派聪明 2026年07月01日 22:49
你的理解非常准确!我帮你用参考资料中的内容进一步印证和细化一下: --- **你的理解完全正确。** 参考资料中对 **ReAct 循环** 的描述就是: > 1. 你输入一个任务 > 2. LLM 思考:我需要做什么?要不要调用工具? > 3. 如果需要工具,LLM 输出工具调用请求 > 4. **Agent 执行工具,拿到结果** ← 这就是 **行动(Acting)** > 5. **把结果喂给 LLM,继续思考** ← 这就是 **观察(Observing)** > 6. 重复 2-5,直到 LLM 觉得任务完成 > 7. 输出最终结果 文章中还有一句更直白的解释: > **LLM 不直接执行任务,而是通过工具调用来“行动”,然后观察行动结果,再决定下一步。** --- ### 拆解你的理解 你提到的 **"工具执行后得到的结果"**,在代码里对应的是这一步: ```java // 执行每个工具调用 String result = toolRegistry.executeTool( toolCall.function().name(), toolCall.function().arguments() ); // 记录工具结果 → 这就是观察 conversationHistory.add(Message.tool(toolCall.id(), result)); ``` 这个 `result` 被当作 `tool` 角色的消息放回对话历史,LLM 读到后 **观察** 这个结果,然后决定: - **继续循环**:如果还需要调用其他工具(比如先读取文件,再修改文件) - **直接输出**:如果任务已经完成,LLM 就不返回 `tool_calls`,而是直接返回文本内容 判断逻辑在代码中也很清晰: ```java if (response.hasToolCalls()) { // 还有工具要调用 → 继续循环 continue; } else { // 没有工具调用 → 任务完成,直接输出 return response.content(); } ``` --- 所以你的理解可以凝练成一句话: > **观察(Observing)就是 LLM 读取工具执行结果,判断下一步是继续调用工具还是直接输出答案的环节。** 非常到位 👏
点赞
回复 取消回复
xuxinyi 2026年06月21日 15:38
@派聪明 record不可变指的是什么不可变?字段结构吗?“content:普通的文本回复内容。 tool_calls:工具调用请求数组(包含工具调用的 id,以及具体调用的函数名 name 和参数 arguments)。 token 使用信息:如输入/输出的 Token消耗量。”这个输出结构是所有LLM通用的吗?
点赞
回复1 取消回复
派聪明 2026年06月21日 15:38
关于你的两个问题,解答如下: **1. record不可变指的是什么?** 指的是**对象实例和其内部的所有字段在创建后都无法被修改**。 在 Java 中,`record` 是一种特殊的不可变类。一旦你通过构造函数(如 `new Message("user", "内容", null, null)`)创建了对象,那么它的 `role`、`content`、`toolCalls`、`toolCallId`等所有字段就被彻底固定了。代码中不会提供 `setRole()` 或 `setContent()` 这样的方法去修改它。如果对话逻辑需要更新消息,只能创建一个新的 `Message` 对象来替换它,而不是在原有对象上修改。 **2. 这个输出结构是所有 LLM 通用的吗?** **在核心逻辑上基本通用,但在具体的字段命名和 JSON 结构上并非绝对统一。** * **底层逻辑通用**:现代主流大模型(如 OpenAI, Claude, GLM等)在处理 Agent工具调用时,底层的反馈逻辑高度一致。模型都会输出三个核心部分:普通的自然语言回复(content)、工具调用请求(包含函数名和参数,即 tool_calls)、以及 Token 的消耗统计。 * **接口规范有差异**:目前业界大量模型(包括文中的 GLM-5.1)都选择**兼容 OpenAI 的 API 格式**,所以你在对接很多模型时,请求和响应的字段名几乎是一样的。但是,也有部分模型(例如 Claude)会有自己原生的 JSON 结构规范。比如在工具调用的表示上,有的模型可能把它放在 `tool_calls` 数组里,有的可能放在特定的 `content` 区块中;字段命名上可能叫 `function`,也可能叫 `name` 和 `input`。 总结来说,思想是通用的,但如果你要接入不同厂商的底层原生 API,仍需根据它们各自的官方文档做相应的字段解析适配。
点赞
回复 取消回复
xuxinyi 2026年06月21日 15:34
@派聪明 为什么Message和 ChatResponse要定义成record类型?大模型输出的格式是固定的吗都包含哪些内容?能处理提取成ChatResponse
点赞
回复3 取消回复
派聪明 2026年06月21日 15:35
结合参考资料,这里为您清晰地解答: **1. 为什么 Message 和 ChatResponse 使用 `record` 类型?** `record` 是 Java14 引入的特性,专门用来定义纯数据载体。 * **简洁安全**:参考资料中 `Message`需要包含 `role`、`content`、`toolCalls`等字段。使用 `record` 只需一行代码(如 `public record Message(...)`),编译器就会自动生成构造器、Getter(如 `msg.role()`)、`equals()` 和 `toString()`等方法。 * **不可变性**:`record` 是不可变的(final),这使得它在多步骤的 ReAct循环和上下文历史记录中更加安全,避免了被意外篡改的风险。 **2. 大模型输出的格式是固定的吗?** 大模型的 API输出**并不是完全固定的自然语言**,而是通过 HTTP返回**标准化的 JSON 格式数据**。为了让大模型知道如何输出,Agent程序会在请求时带上定义好的 JSON Schema,约束大模型按规范返回。 **3.输出都包含哪些内容?如何提取成 ChatResponse?** 参考资料提到,Agent 发送 HTTP请求后会拿到一段 JSON 字符串(`responseBody`)。这段 JSON 通常包含以下核心内容: * **content**:普通的文本回复内容。 * **tool_calls**:工具调用请求数组(包含工具调用的 `id`,以及具体调用的函数名 `name` 和参数 `arguments`)。 * **token 使用信息**:如输入/输出的 Token消耗量。 **提取流程:** 程序会使用 JSON 解析库(如参考资料中用到的 Jackson,通过 `mapper.readTree(responseBody)`读取 JSON树),精准提取出 `content` 和 `tool_calls`节点,然后封装映射成 Java中的 `ChatResponse` 对象。这样,Agent核心循环就能方便地调用 `response.hasToolCalls()`判断是否需要执行工具,或者直接获取文本内容。
1
回复 取消回复
展开剩余 2 条回复
清净日子 2026年06月16日 17:48
引用原文:GLM-5.1
@派聪明 为什么不用deep seek?
点赞
回复1 取消回复
派聪明 2026年06月16日 17:49
虽然参考资料中并未直接对比 GLM-5.1 和 DeepSeek,但根据文章内容,作者选择 GLM-5.1 的核心原因如下: 1. **工具调用支持极佳**:Agent 的核心是“ReAct模式”(推理、行动、观察循环),高度依赖 LLM 的 Function Calling(函数调用)能力。GLM-5.1 能非常稳定、准确地理解工具定义并生成正确的调用参数。 2. **API 格式兼容**:它兼容标准的 OpenAI API 格式,方便开发者快速封装和替换接口。 3. **个人原因**:作者坦言自己刚好是 GLM 的 Max会员。 **为什么不用 DeepSeek?** 结合 Agent 开发的实际技术需求,作者未选 DeepSeek 可能基于以下考量(实际中两者皆可胜任): * **函数调用稳定性**:早期的 DeepSeek模型在极度复杂的连续工具调用(如多步文件读写与命令执行交替)时,稳定性偶尔会有波动。构建 Agent极其考验模型输出标准 JSON 格式参数的能力。 * **接口生态**:对于从零手写的个人轻量级 CLI 项目,直接使用手头拥有高级会员额度且工具调用验证过的模型是最经济顺手的开发选择。
点赞
回复 取消回复
清净日子 2026年06月16日 17:46
引用原文:GLM-5.1
为什么不用deep seek?
点赞
回复 取消回复

目录