Практычныя прытамулкі: Java AI у 2026 годзе, LangChain4j ператварае агентавікаў у звычных
Практычныя прыказкі: Java AI у 2026 годзе, LangChain4j – спосаб прытварання агентаў у звычныя элементы: кантракты, перакрыцці та слоты для коду, якія можна выкарыстоўваць командам, якія реалізуюць гэты патэрн.
У гэтым карыце паказвана дорага ад сыр'ёчных матэрыялаў да рабочай системы для: Java AI у 2026 годзе, LangChain4j – прыемнікі, якія ператвараюць агентыў у звычныя інтэрфейсы. Акцэнт ставіцца на практычныя крокі, чыстае перакананне ў правильнасці дзействаў і код, які можна проста падключыць у репазітарый, не прабуючы здогадвацца пра мету. У стадії агульнага перакладу неабходна з'явіць вхідныя даны, адпаведальнага за крок і критэрыя завершэння пры перадзеіснаванні коду. Аператары должны магчымае перадзеіснаваць крок з вядомага пункта контролю, не прабуючы здогадвацца пра схованы стан. Запісвайце час выконання і кост токенав або запытаў разам з функцыйнальнымі рэзултатамі. Відразувыя даны пра косцы запобегаюць неспакойным рахункам, калі дорага пераходзіць з дэмавайшанняў у спакульнаныя сераўысы.
Пачніце з самага клопатнага слоя: дазвольце модэлю вызываць вашы бізнес-метады
Калі працюеце з этапам «Пачатак з найбольшага», спачатку запісайце контракт: неабяжлівыя даны, сигнал успеху і тое, што выканаецца у разе частковага нявыполнення. Такі список пераконтроўваець дапамагае залічыць змяны ў кодзе пазнейша. Зберагайце настройкі за межамі коду прыемлена. Файлы сераўнавання, хранілішчы секрэтных дадзеных і флагі функцыйяў должны знаходзіцца ў аднам месцы, куды аператары можаюць аудытаваць іх без неабяжлівага чытання всей структуры. Кэшавайце стабільныя інструкцыі системы і схемы інструментаў. Перадача ідэнтычных прамулкіў є частым выкарыстоўваннем ресурсаў.
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>1.19.0</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-agentic</artifactId>
<version>1.19.0</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-skills</artifactId>
<version>1.19.0-beta29</version>
</dependency>
class OrderTools {
@Tool("Look up the status of an order by ID")
String queryOrder(@P("Order ID") String orderId) {
return "Order " + orderId + " has shipped and should arrive tomorrow";
}
@Tool("Refund the specified order")
String refund(@P("Order ID") String orderId, @P("Refund reason") String reason) {
return "Order " + orderId + " refund initiated. Reason: " + reason;
}
}
interface CustomerServiceAssistant {
@UserMessage("You are an e-commerce customer service assistant. Answer questions about orders and refunds. The customer's question is {{question}}")
String answer(String question);
}
CustomerServiceAssistant assistant = AiServices.builder(CustomerServiceAssistant.class)
.chatModel(model)
.tools(new OrderTools())
.chatMemory(MessageWindowChatMemory.withMaxMessages(20))
.build();
String reply = assistant.answer("Where is order 20260907001?");
Агент — это інтэрфейс плюс спяльны дыапазон
Калі працуеце над стадзіяй «Агент»، спачатку запісайце контракт: неабяжлівыя даны, сігнал успеху і тое, што выходзіць у разе частковага нявыпання. Такі список пераконтроўкаў дапамагае заліцвачваць змяны ў кодзе пазнейша.
public interface DraftWriter {
@UserMessage("""
You are a technical editor. Write an opening of no more than 200 words on the topic {{topic}}.
Return only the opening, with no explanation.
""")
@Agent(outputKey = "draft", description = "Writes an opening based on a topic")
String write(@V("topic") String topic);
}
public interface Sharpener {
@UserMessage("""
You are a reviewer. Make the following opening more specific and remove filler.
The opening is {{draft}}.
Return only the revised text.
""")
@Agent(outputKey = "draft", description = "Makes the opening more specific")
String polish(@V("draft") String draft);
}
public interface TitlePicker {
@UserMessage("""
Write 3 headlines suitable for a technical blog for the following content, one per line.
The content is {{draft}}.
""")
@Agent(outputKey = "titles", description = "Writes headlines for the content")
String pickTitle(@V("draft") String draft);
}
DraftWriter writer = AgenticServices
.agentBuilder(DraftWriter.class)
.chatModel(model)
.build();
Sharpener sharpener = AgenticServices
.agentBuilder(Sharpener.class)
.chatModel(model)
.build();
TitlePicker titlePicker = AgenticServices
.agentBuilder(TitlePicker.class)
.chatModel(model)
.build();
UntypedAgent editor = AgenticServices
.sequenceBuilder()
.subAgents(writer, sharpener, titlePicker)
.outputKey("titles")
.build();
Object result = editor.invoke(Map.of("topic", "Building agents with LangChain4j"));
Навыкі — це інструкцыі на запит, а не анотацыя @Skill
Калі працуеце на стадыі інструкцый «Навыкі за запатрабованням», спачатку запісайце угоду: неабяжлівыя даны, сигнал успеху і тое, што выходзіць у разе частковага нявыпання. Такі список перакладоў заходзіць пазнейшыя змены коду чыстымі. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выйшоў, прычына нявыпання павінна вказываць на адну адповядальнасць, а не на заплутаны ланцужок задач. Зробіце перапактаванне пасля дорогіх крокаў. Система не павинна зноў выраховваць адплату за той самы вызыв LLM, калі аператар праказвае пазнейшы вузел. Калі працуеце на стадыі інструкцый «Навыкі за запатрабованням», спачатку запісайце угоду: неабяжлівыя даны, сигнал успеху і тое, што выходзіць у разе частковага нявыпання. Такі список перакладоў заходзіць пазнейшыя змены коду чыстымі. Запісвайце час выканання і кост токеноў або запытаў праза функцыйнае рэзультат. Відкрытыя даны пра косцы з’являюцца раніше, таму не будзе неспадзянак у вырахунках, калі процес пераходзіць з дэмаверсіі ў спяльныя сераўы.
skills/refund/SKILL.md
---
name: refund
description: Handle refund requests. Look up the order, check the conditions, then either refund or escalate to a human.
---
When a customer requests a refund:
1. First call queryOrder to check the order status.
2. Only call refund when the order has shipped or is complete.
3. If the order is still awaiting payment, tell the customer to cancel it.
4. If the amount exceeds 500, escalate to a human instead of refunding directly.
List<FileSystemSkill> loadedSkills = FileSystemSkillLoader.loadSkills(Path.of("skills"));
Skills skillSet = Skills.from(loadedSkills);
CustomerServiceAssistant assistant = AiServices.builder(CustomerServiceAssistant.class)
.chatModel(model)
.chatMemory(MessageWindowChatMemory.withMaxMessages(20))
.tools(new OrderTools())
.toolProvider(skillSet.toolProvider())
.systemMessage("You have access to the following skills. When a request relates to one of them, activate it first.\n"
+ skillSet.formatAvailableSkills())
.build();
Дзейніцы длігага часу павінны магчымаць паўзу і прызначэнне
Дзейніцы длігага часу лепш працуюць, калі іх спрыяваць як мерыемую структуру. Зберагчы адну ідеальную транскрыпцію, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану, перш чым расширваць сферу дзеяння. Канфігурацыю трэба залічваць пазначкай занятым ад коду прыемлі. Файлы сэраўнавання, хранілішчы секрэтных дадзеных і флагі функцыйяў павінны знаходзіцца ў адном месцы, куда аператары можуць адбавляць контроль без неабходнасці чытання всей структуры. Стан структуры трэба залічваць плоскім і з адзінаковым типам дадзеных. Вярстаўленыя блокі маскуюць інфармацыю пра тое, який вузел запісаў канкрэтны поле, і спакушаюць прызначэнне пасля перарываў.
public interface OrderWorkflow extends AgenticScopeAccess {
@Agent
String processOrder(@MemoryId String orderId, @V("order") String orderDetails);
}
AgenticScopeAction validateOrder = AgenticServices.agentAction(scope -> {
String order = scope.readState("order", "");
scope.writeState("validated_order", "VALIDATED: " + order);
});
HumanInTheLoop approvalGate = AgenticServices.humanInTheLoopBuilder()
.description("Large orders require manual approval")
.outputKey("approval")
.responseProvider(scope -> new SuspendedResponse<>("manager-approval"))
.build();
AgenticScopeAction shipOrder = AgenticServices.agentAction(scope -> {
String validated = scope.readState("validated_order", "");
String approval = scope.readState("approval", "");
scope.writeState("result", "Order processed " + approval);
});
OrderWorkflow workflow = AgenticServices.sequenceBuilder(OrderWorkflow.class)
.subAgents(validateOrder, approvalGate, shipOrder)
.outputKey("result")
.build();
ResultWithAgenticScope<String> result =
workflow.processOrder("order-10001", "1000 units");
if (result.suspended()) {
result = result.completePendingResponse("Manager approved");
}
AgenticScopePersister.setStore(new MyAgenticScopeStore());
A2A дазволяе аддаленым агентам працаваць як локальныя елементы
A2A дазваляе аддалёжным агентам краща выканаць сваю работу, калі яго спрыявае можлівасць вимеры роботы. Зберагачыце адна ідеальная версія дадзеных, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану, перш чым расширваць сферу дзеяння. Дакументавайце як успішны, так і варыянты вярнэння да нормальнага стану. Перапрыбуткі, кантрольныя етапы з участю людзей і обработка некоректных паведамленняў є частью самага продукту, а не паслядніх корэктаванняў. Храніце стан графа ў простам і типаваным формате. Вярнутыя структуры дадзеных маскуюць інфармацыю пра тое, канферы які вузел запісаў кожны поле, і спакшваюць продовжэнне роботы пасля перарываў.
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-agentic-a2a</artifactId>
<version>1.19.0</version>
</dependency>
public interface RemoteRiskAgent {
@Agent
String check(@V("transaction") String transaction);
}
UntypedAgent riskAgent = AgenticServices
.a2aBuilder("https://risk.internal/a2a", RemoteRiskAgent.class)
.outputKey("risk_result")
.build();
public interface ChatAgent {
@A2AClientAgent(a2aServerUrl = "http://localhost:8080", outputKey = "response")
ResultWithAgenticScope<String> chat(
@V("question") String question,
@A2AContextId @V("contextId") String contextId,
@A2ATaskId @V("taskId") String taskId);
}
ResultWithAgenticScope<String> first = chatAgent.chat("Hello", null, null);
String contextId = (String) first.agenticScope().readState("contextId");
String taskId = (String) first.agenticScope().readState("taskId");
ResultWithAgenticScope<String> second = chatAgent.chat("Continue", contextId, taskId);
Тры практычныя рэкамендаціі для команд, якія работаюць з Java
Тры ўпрактычныя рэкамендацыі для працы на стадіях работаюць найэфектывней, калі іх спрыяваць як мерыемую паверхню. Зберагачыце адны ідеальны прыклад роботы, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану перад расшырэнням масштаба. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выйшае, прычына неудачы павінна вказываць на адну конкрэтную адпаведальнасць, а не на заплутаны ланцюг задач. Рэзервавайце стан графаў у простам і типізаваным формате. Вярнутыя структуры маскуюць інфармацію пра тое, який вузел запісаў канкрэтны поле, і спакшуюць продовжэнне роботы пасля перерываў. Тры ўпрактычныя рэкамендацыі для працы на стадіях работаюць найэфектывней, калі іх спрыяваць як мерыемую паверхню. Зберагачыце адны ідеальны прыклад роботы, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану перад расшырэнням масштаба. Запісвайце часы виконання і косты токеноў або запытак праза функцыйнае рэзультат. Відкрытая інформацыя пра косты з’являецца на ранніх этапах і запобегае неспакою, калі процес пераходзіць з дэмаверсіі ў спяльныя сераўы.
Чэк-ліст для эксплуатацыі
Этап перагляду канцэлекту працюе найкраща, калі яго спрыяваць як мерыемую структуру. Запісайце адны ідеальны прыклад роботы, адзін прыклад неудачы і прыметку па вярнэнню да пачатковага стану, прычым расшырюючы сферу дзеяння.
Спрыяйце цему этапу як кантракту межа вхіднымі даннымі і пераканаленымі выходнымі рэзультатамі. Дайце назвы всім элементам, задаце критэрыя успеху і адмовіцеся ад частковага завершэння без паведамлення.
Зберагаеце стан графа ў простам і типаваным формате. Вкладзеныя блокі маскуюць інфармацыю пра тое, який вузел запісаў кожны поле, і спакоююць працэс пасля перарываў.
Калі дозволяе бюджет, дадзіце тэст на працясную працю, які пераблакуе критычны шлях у системе CI з викорыстаннем фіксатываў, а не рэальных платных API.
Запісвайце часы выконання і кост токенаў або запытаў разам з функцыйнальнымі рэзультатамі. Відразлівасць костаў з самага пачатку запобегае неспакойным рахункам, калі шлях пераходзіць з дэмовай версіі у спяльныя сераўы.
Зберагаеце стан графа ў простам і типаваным формате. Вкладзеныя блокі маскуюць інфармацыю пра тое, який вузел запісаў кожны поле, і спакоююць працэс пасля перарываў.
Перш чым запускать стак, заморозьце версіі, зафіксавце «золаты» транскрыпты для критычнага шляху і паказвце способы абяроны. У спільных средах неабходны ліміты частоты запытоў, перакананні ў належнасці тэрыторіі і чысткі власнік для ротацыі секрэтных даных. Валіце простую надзейнасць працы замест крэатіўных разовых дэманстрацый.
Прымітка для cd02c3f21b63: не кладзіце ключы прадаўцаў у репазітарый, задаце верхнюю межу токенаў на кожную сесію і зберагачыце транскрыпты празаўсюды з фікстурамі eval, каб пазнейшыя замены модэляў заставаліся пораўнанымі.