langchain4j — 本课题摘录
读了哪几篇: 02-ai-services(一轮的中央装配流水线)、03-tool-calling(工具调用的往返循环)。
其余四篇(核心抽象层、结构化输出与护栏、RAG、多 agent)本轮没读。
这一家最值钱的一条:它把"一轮到底要按什么顺序做哪些事"写死成一条流水线,而且明说"顺序不是随意的"。
它对本课题回答了什么
决定一:一轮的装配顺序 —— 本课题最完整的一份清单
它把"直接调模型时每次都要手写的模板代码"列成五件,然后把这五件全收进框架:拼系统/用户消息、把历史塞进请求并把新回答塞回历史、检索并拼进提示词、要 JSON 再解析成对象、模型要调工具时执行、回填、再调一次。
真正的装配顺序是十七步,每一步都依赖前一步的产物:
① 取会话记忆
② 拼系统消息(+ 变换)
③ 拼用户消息(模板变量填充)
④ 发「开始」事件
⑤ 检索增强 —— 只改用户消息
⑥ 合并多模态内容
⑦ 输入护栏
⑧ 判断是不是流式
⑨ 定输出格式(JSON schema 或追加格式指令)
⑩ 装配消息列表 + 写进会话记忆
⑪ 内容审核(异步提交)
⑫ 建工具上下文
⑬ 发请求拿到第一个回复
⑭ 进入工具往返循环 ← 循环在这里,不在最外层
⑮ 输出护栏
⑯ 按返回类型解析
⑰ 发「完成」事件并返回
(依据:前沿库 · LangChain4j · AiServices:一个 Java 接口如何变成一次 LLM 调用 —— AiServices 的 invoke 是十七步固定顺序的中央编排,文档明说「顺序不是随意的,每一步都依赖前一步的产物」,工具往返循环是其中第 14 步)
这张清单对我们直接有用:它就是"一轮到底有几件事"的答案。 我们的最小循环只需要其中的 ①②③⑩⑬⑭,但知道完整清单才知道自己省了什么。
两个顺序上不显然、容易踩坑的点:
- 检索增强只改用户消息,不改系统消息。 检索到的内容一定拼在用户消息里,系统消息只作为只读上下文传进去。 (依据:前沿库 · LangChain4j · AiServices:一个 Java 接口如何变成一次 LLM 调用 —— retrievalAugmentor.augment 的输出被强转回 UserMessage,system message 只作为只读 Metadata 传入——检索内容一定拼在用户消息里)
- 输入护栏排在检索之后。 所以护栏看到的是已经注入检索内容的最终用户消息,不是用户原始输入。想拿原文的护栏得自己从参数里取。 (依据:前沿库 · LangChain4j · AiServices:一个 Java 接口如何变成一次 LLM 调用 —— 顺序是 RAG → 多模态合并 → 输入护栏,护栏看到的是已注入检索内容的最终用户消息而非原始输入)
第 2 条是个很好的"顺序决定语义"的例子:同样两个部件,前后一换,护栏防的东西就变了。
决定四:轮数熔断,而且要看清它数的是什么
循环体是十步,第一步就是熔断:剩余往返次数减到零就抛异常。默认上限 100。
关键细节:它数的是"模型返回带工具调用的轮数",不是工具个数——一轮里模型调五个工具只算一次。 (依据:前沿库 · LangChain4j · 工具调用:从 @Tool 反射到 round-trip 循环 —— maxToolCallingRoundTrips 默认 100,数的是模型返回带工具调用的轮数而非工具个数,一轮调五个工具只算一次)
这条要记进配方。 "最多 20 轮"到底是二十次模型调用还是二十个工具调用,不同库口径不一样,写文档时必须说清。