langchain4j [特性] 在使用AiService和流式传输时返回检索到的片段(RAG)

x7yiwoj4  于 3个月前  发布在  其他
关注(0)|答案(7)|浏览(197)

我正在使用一个会话式的RAG。我想返回带有元数据的源文档,以便能够在前端展示它们。还没有找到一种简单易行的方法吗?难道只能通过完全自定义的链来实现吗?
目前我正在使用这样的设置:

interface RagService {
        String answer(String query);
    }
QueryTransformer queryTransformer = new CompressingQueryTransformer(chatLanguageModel);

        ContentRetriever contentRetriever = EmbeddingStoreContentRetrieverCustom.builder()
                .embeddingStore(embeddingStore)
                .embeddingModel(embeddingModel)
                .maxResults(5)
                .minScore(0.6)
                .build();

        RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
                .queryTransformer(queryTransformer)
                .contentRetriever(contentRetriever)
                .build();

        ChatMemory chatMemory = chatMemoryService.getChatMemoryMaxTokens(memoryId);
        RagService ragService = AiServices.builder(RagService.class)
                .chatLanguageModel(chatLanguageModel)
                .retrievalAugmentor(retrievalAugmentor)
                .chatMemory(chatMemory)
                .build();
nnsrf1az

nnsrf1az1#

你好,@hannesroth88 这个功能优先级很高,很快就会添加。
目前你可以使用一个自定义的ContentInjector代理来拦截对真实ContentInjector(例如DefaultContentInjector)的调用,在那里你可以访问注入的内容。虽然丑陋,但还是可行的:)

qltillow

qltillow2#

我正在努力将文档传回调用AiService的父类。在此功能请求之前,我已经更改了如何在代理ContentRetriever中检索相关资源的方法:

public class EmbeddingStoreContentRetrieverCustom implements ContentRetriever {

    // .... constructors ...

    @Override
    public List<Content> retrieve(Query query) {
        Embedding embeddedText = embeddingModel.embed(query.text()).content();

        // Pass knowledgeId before embeddedText in the findRelevant method
        List<EmbeddingMatch<TextSegment>> relevant = embeddingStore.findRelevantByKnowledgeId(knowledgeId, embeddedText, maxResults, minScore);

        return relevant.stream()
                .map(EmbeddingMatch::embedded)
                .map(Content::from)
                .collect(toList());
    }

我可以从这里开始吗?但是一个好的解决方案是什么来传递它呢?
我认为我想要避免将源一路传到AiService。这可能是很多工作,而且真的很丑陋;)

wqnecbli

wqnecbli3#

一个不太好的方法是将检索到的List<Content>存储在EmbeddingStoreContentRetrieverCustom的字段中,然后一旦AiService返回,就从那里获取它。
我会努力找到一种更好的方法来实现这个功能。也许可以这样:

interface Assistant {

    WithSources<String> chat(String userMessage);
}

WithSources<String> answerWithSources = assistant.chat(...);
String answer = answerWithSources.content();
List<Content> sources = answerWithSources.sources();

WDYT?

3zwtqj6y

3zwtqj6y4#

一个不太优雅的方案是将检索到的List<Content>存储在EmbeddingStoreContentRetrieverCustom的字段中,然后在AiService返回后,从那里获取它。
我会努力寻找一种更好的方法来实现这个功能。也许可以这样:

interface Assistant {

    WithSources<String> chat(String userMessage);
}

WithSources<String> answerWithSources = assistant.chat(...);
String answer = answerWithSources.content();
List<Content> sources = answerWithSources.sources();

WDYT?
这正是我想要的!💪
同时感谢提示。当然,我完全忽略了我的ContentRetriever EmbeddingStoreContentRetrieverCustom是一个在调用AiService的类中的示例。所以没有必要使用唯一标识符等来实现。
毕竟,这不是那么丑陋了。

mzaanser

mzaanser5#

你好,关于添加这个功能有什么新消息吗?

你好@hannesroth88,这个功能优先级很高,很快就会添加。现在你可以使用一个自定义的ContentInjector代理来拦截对真实ContentInjector(例如DefaultContentInjector)的调用,在那里你可以访问注入的内容。虽然丑陋,但还是可行的:)

gzjq41n4

gzjq41n46#

你好,ndkells,抱歉,还没有时间处理这个问题。

5tmbdcev

5tmbdcev7#

这个功能在 #1040 中已经实现。但是我们也应该为流媒体(例如 TokenStream )实现相同的功能。在实施之前,请考虑多种解决方案。其中一个方案是:

interface Assistant {

    TokenStream chat(String userMessage);
}

assistant.chat("What is the capital of Germany?")
                .onRetrieved(contents -> ...) // new callback
                .onNext(token -> ...)
                .onComplete(response -> ...)
                .onError(error -> ...)
                .start();

但是不确定这是否能与 Flux 良好地协同工作。

相关问题