187 aflæsninger

LangChain4J 6 måneder senere

ved Nicolas Fränkel11m2025/05/01
Read on Terminal Reader

For langt; At læse

Hovedfokuset i dette indlæg er integrationen af en MCP-server i en LangChain4J-app. Mens konfigurationen er enkel takket være dokumentationen, er der et par advarsler.
featured image - LangChain4J 6 måneder senere
Nicolas Fränkel HackerNoon profile picture

sidste år,Jeg begyndte at tisse lidtOmkringLangkæden4JDet er et hurtigt voksende projekt, og jeg ønskede at blive fortrolig med opdateringerne.Jeg ønskede også at tjekke, hvordan man integrerer en Model Context Protocol server i LangChain4J.

Version 1 af Beta

Jeg skrev mit sidste indlæg i november 2024 og brugte den nyeste version tilgængelig på det tidspunkt, v0.35. LangChain4J begyndte sin rejse mod 1.0 sidste december.

Date

Release

September 25th, 2024

0.35.0

December 22th, 2024

1.0.0-alpha1

February 10th, 2025

1.0.0-beta1

March 13th, 2025

1.0.0-beta2

April 12th, 2025

1.0.0-beta3

25. september 2024

af 35.0

22. december 2024

1.0.0-alpha 1 af 1

10. februar 2025

Udgivelse af beta1

13. marts 2025

1.0.1 af Beta2

12. april 2025

Udgivelse af beta3


LangChain4J er på vejSemverVedligeholdere brugte lejligheden til at indføre forstyrrende ændringer.I mit tilfælde måtte jeg opdatere min kode for at tage hensyn til forstyrrende API-ændringer.

v0.35

v1.0.0-beta3

val s = Sinks.many()
.unicast()
.onBackpressureBuffer<String>()
chatBot.talk(m.sessionId, m.text)
.onNext(s::tryEmitNext)
.onError(s::tryEmitError)
.onComplete {
s.tryEmitComplete()
}.start()
return ServerResponse.ok().bodyAndAwait(
s.asFlux().asFlow()
)

val s = Sinks.many()
.unicast()
.onBackpressureBuffer<String>()
chatBot.talk(m.sessionId, m.text)
.onPartialResponse(s::tryEmitNext)
.onError(s::tryEmitError)
.onCompleteResponse {
s.tryEmitComplete()
}.start()
return ServerResponse.ok().bodyAndAwait(
s.asFlux().asFlow()
)

Val s = Sænk.many()
.unicast() er
.onBackpressureBuffer<String>()
chatBot.talk (m.sessionId, m.tekst)
.onNæste(s::tryEmitNæste)
.onError(s::tryEmitError)
er fuldstændig
Sæsonen er fuldstændig()
Tilbage til start()
Tilbage til vores hjemmeside.dk (
s.asFlux().asFlow()
)

Val s = Sænk.many()
.unicast() er
.onBackpressureBuffer<String>()
chatBot.talk (m.sessionId, m.tekst)
.onPartialResponse(s::tryEmitNæste)
.onError(s::tryEmitError)
.onFuldt svar {
Sæsonen er fuldstændig()
Tilbage til start()
Tilbage til vores hjemmeside.dk (
s.asFlux().asFlow()
)

Integration af reaktorer

LangChain4J tilbyder en Project Reactor integration; jeg savnede det i mine tidligere musings.a lot.

Jeg brugerAiServices, så jeg tidligere defineret en grænseflade for LangChain4J at implementere på runtime:


interface ChatBot {
    fun talk(@MemoryId sessionId: String, @UserMessage message: String): TokenStream
}


Vi bør tilføje følgende afhængighed:


<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-reactor</artifactId>
    <version>1.0.0-beta3</version>
</dependency>


Vi kan nu ændre returtypen fra aFlux<String>To aTokenStreamHer er den opdaterede underskrift:


interface ChatBot {
    fun talk(@MemoryId sessionId: String, @UserMessage message: String): Flux<String>
}


Det gør oprettelsen afsinkVi kan forenkle koden som følger:


val flux = chatBot.talk(m.sessionId, m.text)
ServerResponse.ok().bodyAndAwait(flux.asFlow())


Husk, at to dages debugging nemt kan spare dig to timer med at læse dokumentationen!

Integration af en kontekstprotokollserver

I dette afsnit vil jeg integrere en <abbr title="Model Context Protocol">MCP</abbr> i min LangChain4J-applikation.

Retrieval-forstørret generation

Man har brug for en masse ressourcer til at træne en <abbr title="Large Language Model">LLM</abbr>: det direkte oversætter til tid og penge. Af denne grund, virksomheder begrænse uddannelsen af nye modelversioner. En model relevans falder over tid som oplysninger akkumuleres og ændres, mens LLM's database er uforanderlig. Desuden er LLM'er uddannet på offentlige data-af natur, mens de fleste virksomheder ønsker at forespørge deres private data også.


Retrieval Augmented Generation var den traditionelle måde at håndtere disse grænser på. Retrieval Augmented Generation er en to-trins proces. I det første trin analyserer værktøjet data, vektoriserer det i henhold til LLM og gemmer det i en vektordatabase; i det andet bruger værktøjet databasen som yderligere data, når du forespørger LLM.

Kontekstprotokollens model

Den nyeste måde at håndtere den statiske karakter af LLMs er MCP.


MCP er en åben protokol, der standardiserer, hvordan applikationer giver kontekst til LLMs. Tænk på MCP som en USB-C-port til AI-applikationer. Ligesom USB-C giver en standardiseret måde at forbinde dine enheder til forskellige perifere enheder og tilbehør, giver MCP en standardiseret måde at forbinde AI-modeller til forskellige datakilder og værktøjer.


Kom i gang med Model Context Protocol

MCP er en åben protokol, der standardiserer, hvordan applikationer giver kontekst til LLMs. Tænk på MCP som en USB-C-port til AI-applikationer. Ligesom USB-C giver en standardiseret måde at forbinde dine enheder til forskellige perifere enheder og tilbehør, giver MCP en standardiseret måde at forbinde AI-modeller til forskellige datakilder og værktøjer.


Kom i gang med Model Context Protocol


MCP har to fordele over RAG:


  • Data processed by a RAG is tailored for a model. If one wants to use a new model, one must re-execute the parsing phase. MCP standardizes the interactions between a client and a server, making them technology-independent.


  • RAG allows the reading of data. MCP allows any API call to either access data dynamically or execute actions!


MCP definererTo transportmulighederFor klient-server kommunikation:


  • stdio: Klienten lancerer en underproces, og kommunikationen foregår over standard ind og standard ud
  • HTTP med server-sendt begivenheder

Arkitektur af løsningen

Efter ovenstående teori er vi nu klar til den praktiske del. Det begynder ved at vælge en MCP-server.Her erDet er et godt udgangspunkt, men jeg har valgtOfficiel GitHub MCP-serverFordi LangChain4J dokumentationen nævner det.


GitHub MCP-serveren tilbyderStadietDet betyder, at vi skal få det binære og starte det fra applikationen. Det er hurtigt i forhold til HTTP-transporten, men i betragtning af den samlede tid, der består af HTTP-opkaldet til modellen og beregningstiden på dens side, er det irrelevant.


Efter en del undersøgelser fandt jegMcp-proxy tilgængeligDet giver dig mulighed for at skifte mellem enten fra stdio til HTTP eller fra HTTP til stdio. Det er også tilgængeligt som et Docker-billede.Dockerfile:


FROM ghcr.io/sparfenyuk/mcp-proxy:latest

ENV VERSION=0.2.0
ENV ARCHIVE_NAME=github-mcp-server_Linux_x86_64.tar.gz

RUN wget https://github.com/github/github-mcp-server/releases/download/v$VERSION/$ARCHIVE_NAME -O /tmp/$ARCHIVE_NAME \ #1
    && tar -xzvf /tmp/$ARCHIVE_NAME -C /opt \                      #2
    && rm /tmp/$ARCHIVE_NAME                                       #3

RUN chmod +x /opt/github-mcp-server                                #4
  1. Download af arkivet
  2. Udtræk det
  3. Fjern arkivet
  4. Gør det binære eksekverbart


Bemærk, at vi ikke kan definereCMDsom binær kun tillader konfiguration af porten og vært med parametre. af denne grund, skal vi udskyde kommandoen på runtime, eller i mit tilfælde, idocker-compose.yaml:


services:
  mcp-server:
    build:
      context: github-mcp-server
    env_file:
      - .env                                                       #1
    command:
      - --pass-environment                                         #2
      - --sse-port=8080                                            #3
      - --sse-host=0.0.0.0                                         #4
      - --                                                         #5
      - /opt/github-mcp-server                                     #6
      - --toolsets
      - all
      - stdio
  1. Vi har brug for en GITHUB_PERSONAL_ACCESS_TOKEN miljøvariabel med en gyldig token til at autentisere på GitHub
  2. Overfør alle miljøvariabler til underprocessen
  3. Indstil lytteporten
  4. Bind til enhver IP
  5. Proxy "forbinder" til stdio MCP-serveren efter dash
  6. Kør serveren med alle indstillinger aktiveret


Billedet vil give/sseEndpoint på port 8080.

Kodning af løsningen

Koden er den nemmeste del. Gå ned tilLangChain4J dokumentation på MCPog følg med. I projektet oversætter det som følger:


bean {
    val transport = HttpMcpTransport.Builder()
        .sseUrl(ref<ApplicationProperties>().mcp.url)              //1
        .logRequests(true)                                         //2
        .logResponses(true)                                        //2
        .build()
    val mcpClient = DefaultMcpClient.Builder()
        .transport(transport)
        .build()
    mcpClient.listTools().forEach { println(it) }                  //3
    McpToolProvider.builder()
        .mcpClients(listOf(mcpClient))
        .build()
}
bean {
    coRouter {
        val chatBot = AiServices
            .builder(ChatBot::class.java)
            .streamingChatLanguageModel(ref<StreamingChatLanguageModel>())
            .chatMemoryProvider { MessageWindowChatMemory.withMaxMessages(40) }
            .contentRetriever(EmbeddingStoreContentRetriever.from(ref<EmbeddingStore<TextSegment>>()))
            .toolProvider(ref<McpToolProvider>())                  //4
            .build()
        POST("/")(PromptHandler(chatBot)::handle)
    }
}
  1. Jeg har tilføjet en ConfigurationProperty-klasse til at parameterisere SSE-URL'en
  2. MCP-protokollen giver en måde at sende logs tilbage til klienten
  3. Ikke nødvendigt, men det hjalp mig med at sikre, at klienten var forbundet til serveren og kunne liste de værktøjer, der blev leveret
  4. Plug i MCP-værktøjsudbyderen oprettet ovenfor i AiServices


På dette tidspunkt skal modellen videresende en anmodning, der matcher et af de registrerede værktøjer til MCP-serveren.


curl -N -H 'Content-Type: application/json' localhost:8080 -d '{ "sessionId": "1", "text": "What are my top three most popular GitHub repos?" }'


Jeg har prøvet flere gange, og jeg har fået svar på disse spørgsmål:


Unfortunately, the provided text does not contain any information about your top three most popular GitHub repositories. The text appears to be a blog post or a personal website, and it mentions some of your projects and experiences with GitHub, but it does not provide any metrics or statistics on the popularity of your repositories.

If you want to know more about the popularity of your GitHub repositories, I would recommend checking out GitHub's own analytics tools, such as GitHub Insights or the Repository Insights API. These tools can provide information about the number of followers, stars, and forks for each repository, as well as other metrics like engagement and activity.


Modellen ignorerede blot værktøjerne på trods af dokumentationen, der hævder det modsatte.

Fastsætter løsningen

Jeg læste LangChain4J-dokumentationen et par gange, men til ingen nytte. Jeg forsøgte at bruge OpenAI og en håndfuld andre AI-værktøjer uden held. De fleste svar bekræftede, at det skulle fungere ud af boksen. Nogle nævnte at ringe til værktøjet direkte, hvilket besejrer formålet; en nævnte, at Ollama ikke understøttede værktøjer. Jeg tjekkede Ollama-bloggen: det annoncerede værktøjets support i 2024.


Den frakoblede arkitektur introducerer flere bevægende stykker. Jeg mistænkte, at der kunne være noget galt i hele opkaldskæden.github-mcp-serverdirekte til applikationsbilledet, og ændrede koden fra HTTP til stdio.


Jeg var ved at give op, da jeg besluttede at vende tilbage til rødderne.Prøve fra dokumentationenDet var mit ha-ha øjeblik.


Prøven bruger OpenAI, mens jeg brugte Ollama. Jeg prøvede MCP med OpenAI, Mistral AI og Ollama. Kun OpenAI-modellen fungerer med MCP. Jeg sendte samme anmodning som ovenfor:


curl -N -H 'Content-Type: application/json' localhost:8080 -d '{ "sessionId": "1", "text": "What are my top three most popular GitHub repos?" }'


Nu mapper OpenAI anmodningen korrekt til det korrekte værktøj og returnerer det svar, jeg forventede:


Here are my findings regarding your top three most popular GitHub repositories:

1. **[opentelemetry-tracing](https://github.com/nfrankel/opentelemetry-tracing)**  
   - **Description**: Demo for end-to-end tracing via OpenTelemetry.  
   - **Stars**: 68  
   - **Forks**: 25  
   - **Open Issues**: 10  

2. **[kaadin](https://github.com/nfrankel/kaadin)**  
   - **Description**: Kotlin DSL for Vaadin.  
   - **Stars**: 44  
   - **Forks**: 12  
   - **Open Issues**: 3  

3. **[jvm-controller](https://github.com/nfrankel/jvm-controller)**  
   - **Description**: Example on how to write a Kubernetes controller in Java.  
   - **Stars**: 33  
   - **Forks**: 10  
   - **Open Issues**: 0  

These repositories demonstrate a range of your interests and contributions in the areas of observability, Kotlin development, and Kubernetes.%    


Fordi vi videregiver en autentiseringstoken til MCP-serveren, som videregiver den til GitHub API, ved sidstnævnte, hvilken bruger der foretager opkaldet.my reposJeg indrømmer, at det er en usædvanlig brugssag for regelmæssige webapplikationer, der dækker flere brugere, men bruger en enkelt godkendelse token hver.


Andre almindelige spørgsmål,E. af G., finde de mest populære repositories på GitHub, er relevante for webapplikationer, da de ikke har implicit kontekst – brugeren.

Konklusionen

Hovedfokuset i dette indlæg er integrationen af en MCP-server i en LangChain4J-app. Mens konfigurationen er enkel takket være dokumentationen, er der et par advarsler.


For det første, hvordan MCP-serveren passer ind i din arkitektur, er stadig op til dig.mcp-proxySå LangChain4J synes at være en lækker abstraktion. Det gør alt muligt for at give dig et stærkt abstraktionslag, men implementationerne under det beskytter dig mod er ikke lige.


Jeg lærte om MCP i den virkelige verden, og det åbnede ganske få døre for projektidéer.


Den fulde kildekode til dette indlæg kan findes påaf Github.


To go further:


  • Kom i gang med modelkontekstprotokollen
  • Find fantastiske MCP servere og klienter
  • LangChain4J - Model Kontekstprotokoll (MCP)



Oprindeligt udgivet på A Java Geek den 27. april 2025

En Java Geek

Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks