<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Stefan Kopf</title>
    <description>The latest articles on DEV Community by Stefan Kopf (@stefankopfxaldon).</description>
    <link>https://dev.to/stefankopfxaldon</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4186189%2F91a53946-00e8-4c2a-bb18-0764572d7827.jpg</url>
      <title>DEV Community: Stefan Kopf</title>
      <link>https://dev.to/stefankopfxaldon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/stefankopfxaldon"/>
    <language>en</language>
    <item>
      <title>Connect LlamaIndex to Alfresco with OpenDMA: Reader, Retriever, and Agent Tools</title>
      <dc:creator>Stefan Kopf</dc:creator>
      <pubDate>Fri, 16 Oct 2026 19:28:29 +0000</pubDate>
      <link>https://dev.to/stefankopfxaldon/connect-llamaindex-to-alfresco-with-opendma-reader-retriever-and-agent-tools-285b</link>
      <guid>https://dev.to/stefankopfxaldon/connect-llamaindex-to-alfresco-with-opendma-reader-retriever-and-agent-tools-285b</guid>
      <description>&lt;p&gt;Connecting LlamaIndex to Alfresco looks simple at first: read documents, extract text, build an index. In practice, the repository context becomes important very quickly.&lt;/p&gt;

&lt;p&gt;At that point there are usually three different tasks that all look similar from a distance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ingest Alfresco content into a LlamaIndex &lt;code&gt;Index&lt;/code&gt;, for example a &lt;code&gt;VectorStoreIndex&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;retrieve content live from Alfresco without building a separate vector store&lt;/li&gt;
&lt;li&gt;give an agent tools to browse Alfresco, inspect metadata, search, and read selected documents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The OpenDMA LlamaIndex integration provides all three surfaces. You can use &lt;code&gt;AlfrescoReader&lt;/code&gt;, &lt;code&gt;AlfrescoRetriever&lt;/code&gt;, or &lt;code&gt;AlfrescoToolSpec&lt;/code&gt;, depending on where the repository should stay in the application flow.&lt;/p&gt;

&lt;p&gt;The interesting part is not getting bytes out of Alfresco once. The interesting part is keeping the repository context: site, path, type, aspects, properties, permissions, and the query language the repository already has.&lt;/p&gt;

&lt;p&gt;The examples in this article are available as a runnable Python project in the &lt;a href="https://github.com/xaldon/opendma-demo-alfresco-llamaindex-basics" rel="noopener noreferrer"&gt;OpenDMA Alfresco–LlamaIndex sample repository&lt;/a&gt;. You can clone the repository and follow along with the examples below.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenDMA between LlamaIndex and Alfresco
&lt;/h2&gt;

&lt;p&gt;OpenDMA is a vendor-neutral API for Document Management and ECM systems. An OpenDMA adaptor plays a similar role to a database driver: it maps a concrete system like Alfresco to the OpenDMA model.&lt;/p&gt;

&lt;p&gt;For a Python LlamaIndex application, the important part is the OpenDMA REST endpoint. The application talks to OpenDMA over HTTP. Behind that endpoint, an Alfresco adaptor maps Alfresco objects, folders, documents, content streams, metadata, classes, aspects, and search into the OpenDMA API.&lt;/p&gt;

&lt;p&gt;This keeps the LlamaIndex code at the ECM level. The application does not need to scatter Alfresco Content Services REST API calls for nodes, content download, sites, search, aspects, and properties through its own code.&lt;/p&gt;

&lt;p&gt;The same application shape can later be used with other ECM systems through other OpenDMA adaptors, for example Documentum, FileNet P8, OnBase, CMOD, OpenText, or SharePoint. In an enterprise deployment, the OpenDMA or ECI layer is also the place where concerns like auditing, identity translation, RBAC, SIEM integration, or identity forwarding belong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use &lt;code&gt;AlfrescoReader&lt;/code&gt; when you want to build an index
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;AlfrescoReader&lt;/code&gt; is the LlamaIndex Alfresco reader for ingestion. Use it when you want to load Alfresco content into a LlamaIndex &lt;code&gt;Index&lt;/code&gt;, for example a &lt;code&gt;VectorStoreIndex&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is a convenience reader for Alfresco repositories exposed through OpenDMA. It uses Alfresco defaults and understands Alfresco site short names. The following example reads from the &lt;code&gt;swsdp&lt;/code&gt; site of the standard Alfresco sample setup and uses Docling for legacy Word documents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.readers.docling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DoclingReader&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.readers.opendma&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AlfrescoReader&lt;/span&gt;

&lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AlfrescoReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:7070/opendma/alf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sites&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;swsdp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;file_extractor_per_mimetype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/msword&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;DoclingReader&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each returned LlamaIndex &lt;code&gt;Document&lt;/code&gt; contains the extracted text in &lt;code&gt;text&lt;/code&gt; and OpenDMA metadata in &lt;code&gt;metadata&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;sites&lt;/code&gt; parameter is useful for Alfresco, but it is not the only loading strategy. The reader also supports document IDs, folder IDs, recursive folder traversal, and OpenDMA queries. When reading a site, do not assume this only means the Document Library. Alfresco sites can also contain wiki pages, links, and other objects below the site folders.&lt;/p&gt;

&lt;p&gt;In a production application, this ingestion step usually should not run over the whole repository every time. The normal shape is to run it for new and changed documents and use a persistent vector store.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the Alfresco metadata
&lt;/h2&gt;

&lt;p&gt;With ECM content, metadata is not decoration. It is often the difference between a demo and a usable application.&lt;/p&gt;

&lt;p&gt;Documents loaded through OpenDMA do not only carry text chunks. They also carry OpenDMA metadata, Alfresco metadata, and integration metadata. That includes values such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;opendma:Title&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;opendma:CheckedOut&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;alfresco:Site&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;alfresco:Path&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;alfresco:cm:name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;alfresco:cm:creator&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;alfresco:cm:accessed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;content_state&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can inspect the metadata before inserting documents into an index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opendma:Title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alfresco:Site&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alfresco:Path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This matters because a real Alfresco repository is not a small set of clean tutorial files. It may contain hundreds of thousands or millions of documents, many teams, meeting notes, contracts, agreements, product specifications, working drafts, and project folders with similar vocabulary.&lt;/p&gt;

&lt;p&gt;A plain similarity search across all indexed repository content can return irrelevant chunks. If the question is about a particular project or team site, &lt;code&gt;alfresco:Site&lt;/code&gt; and &lt;code&gt;alfresco:Path&lt;/code&gt; are useful signals. If checked-out content should be excluded, &lt;code&gt;opendma:CheckedOut&lt;/code&gt; can be part of the retrieval logic.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/OpenDMA/llama-index-opendma/blob/main/docs/tutorials/02_metadata_aware_retrieval.md" rel="noopener noreferrer"&gt;metadata-aware retrieval tutorial&lt;/a&gt; shows this more explicitly. It first indexes the Alfresco sample site, then adds similar content from a second site and demonstrates how retrieval quality degrades when all chunks are treated as one flat pool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not just write a small Alfresco REST reader?
&lt;/h2&gt;

&lt;p&gt;For a prototype, a small custom reader is tempting. Download content from Alfresco, turn it into text, and hand it to LlamaIndex.&lt;/p&gt;

&lt;p&gt;That works until the reader needs to know more about the repository.&lt;/p&gt;

&lt;p&gt;The code quickly grows from "download this node's content" into site discovery, folder traversal, metadata extraction, content type handling, aspects and properties, path reconstruction, checked-out state, full-text search, MIME-specific parsing, and access-control-aware deployment choices.&lt;/p&gt;

&lt;p&gt;OpenDMA puts these concerns behind an ECM abstraction. The LlamaIndex application can still be plain Python, but it does not become the place where the Alfresco integration is rebuilt piece by piece.&lt;/p&gt;

&lt;p&gt;This also affects content conversion. The OpenDMA reader decodes plain text directly and delegates richer formats to LlamaIndex file readers through MIME-type mappings. In the example above, legacy Word files are explicitly mapped to Docling. Where repository renditions are part of the OpenDMA/adaptor deployment, they belong in the repository integration layer rather than being reimplemented separately in every application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use &lt;code&gt;AlfrescoRetriever&lt;/code&gt; when you do not want a vector store
&lt;/h2&gt;

&lt;p&gt;Sometimes a vector store is the right choice. If you need semantic retrieval over a curated corpus, &lt;code&gt;AlfrescoReader&lt;/code&gt; plus a LlamaIndex index is a good fit.&lt;/p&gt;

&lt;p&gt;But building a vector store has a cost. You need to ingest content, extract text, chunk it, calculate embeddings, store the result, and keep it synchronized with Alfresco. In an enterprise repository, access rights and compliance boundaries are often the harder part.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AlfrescoRetriever&lt;/code&gt; is the other option. It implements the LlamaIndex retriever API and performs live full-text search against Alfresco through OpenDMA.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.core.node_parser&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SentenceSplitter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.readers.docling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DoclingReader&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.retrievers.opendma&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AlfrescoRetriever&lt;/span&gt;

&lt;span class="n"&gt;retriever&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AlfrescoRetriever&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:7070/opendma/alf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sites&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;swsdp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;file_extractor_per_mimetype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/msword&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;DoclingReader&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;transformations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;SentenceSplitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk_overlap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;similarity_top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;retriever&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;meeting notes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The retriever turns the input string into an Alfresco AFTS &lt;code&gt;TEXT&lt;/code&gt; query. If &lt;code&gt;sites&lt;/code&gt; is set, it adds site restrictions. The result is converted into LlamaIndex nodes and returned as &lt;code&gt;NodeWithScore&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is important: &lt;code&gt;AlfrescoRetriever&lt;/code&gt; is full-text retrieval, not semantic vector search. The &lt;code&gt;similarity_top_k&lt;/code&gt; parameter limits the number of LlamaIndex nodes returned after document loading and node parsing. It is not a vector similarity score.&lt;/p&gt;

&lt;p&gt;The advantage is that every retrieval call goes through Alfresco via OpenDMA. There is no up-front ingestion of the whole repository, no embeddings for all content, and no separate copy of repository text that needs to be synchronized.&lt;/p&gt;

&lt;p&gt;With effective-user authentication or identity forwarding, Alfresco remains the system deciding what the query can return, including repository permissions and governance/security markings enforced in the Alfresco/OpenDMA path. If the OpenDMA endpoint is called with a shared technical or admin account, the application still has to handle user-level authorization. That part does not disappear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agentic retrieval without a vector store
&lt;/h2&gt;

&lt;p&gt;A live full-text retriever works especially well when an agent is allowed to make more than one search attempt.&lt;/p&gt;

&lt;p&gt;The first query might be too specific:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;localisation of new website design
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next query might use fewer terms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;website design
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or it might focus on the domain word that actually appears in the documents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;localisation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does not turn Alfresco full-text search into semantic search. A vector store will often retrieve better when the user's words do not match the document text. But an agentic workflow can compensate in many practical cases by changing the query terms and restricting the search to the relevant site.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/OpenDMA/llama-index-opendma/blob/main/docs/tutorials/04_agentic_rag_retriever.md" rel="noopener noreferrer"&gt;Agentic RAG with OpenDMA Retriever tutorial&lt;/a&gt; shows this pattern with an agent that uses &lt;code&gt;AlfrescoRetriever&lt;/code&gt; inside a &lt;code&gt;search_content&lt;/code&gt; tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use &lt;code&gt;AlfrescoToolSpec&lt;/code&gt; when the agent needs to browse
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;AlfrescoRetriever&lt;/code&gt; is for retrieval. &lt;code&gt;AlfrescoToolSpec&lt;/code&gt; is for agents that need to inspect the repository.&lt;/p&gt;

&lt;p&gt;That is a different task. Sometimes the answer is not found by retrieving the best text chunks. The agent may first need to list sites, browse a folder, inspect metadata, find a candidate document, and only then read text from it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AlfrescoToolSpec&lt;/code&gt; exposes tools such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;alfresco_list_sites&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;opendma_list_children&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;opendma_get_metadata&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;opendma_describe_class&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;opendma_search&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;opendma_read_text&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tools are configured for a fixed OpenDMA endpoint, account, and repository. This is a better surface for an agent than a generic HTTP client and arbitrary credentials.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.core.agent&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FunctionAgent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.llms.openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.tools.opendma&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AlfrescoToolSpec&lt;/span&gt;

&lt;span class="n"&gt;tool_spec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AlfrescoToolSpec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:7070/opendma/alf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FunctionAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AlfrescoAgent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tool_spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_tool_list&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Use repository metadata and listings before reading document text. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Read document text only when metadata is not enough.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The prompt matters. Reading document text is more expensive than listing children or getting metadata. A useful agent should browse first, inspect metadata, and read text only when it has a candidate document.&lt;/p&gt;

&lt;p&gt;The text-reading tool returns chunks with continuation tokens. That is important for long documents and large repositories. The agent should not request a full document when the metadata already answers the question.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/OpenDMA/llama-index-opendma/blob/main/docs/tutorials/05_agent_tools.md" rel="noopener noreferrer"&gt;tool-calling agent tutorial&lt;/a&gt; shows this with an agent that first locates a document, inspects metadata, and only reads text when needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the surface
&lt;/h2&gt;

&lt;p&gt;Start with &lt;code&gt;AlfrescoReader&lt;/code&gt; if you need semantic search over a controlled indexed corpus.&lt;/p&gt;

&lt;p&gt;Start with &lt;code&gt;AlfrescoRetriever&lt;/code&gt; if keeping search live in Alfresco is more important than vector similarity.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;AlfrescoToolSpec&lt;/code&gt; when the agent needs repository navigation, metadata inspection, and selective reading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try the examples yourself&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The complete runnable examples for &lt;code&gt;AlfrescoReader&lt;/code&gt;, &lt;code&gt;AlfrescoRetriever&lt;/code&gt;, and &lt;code&gt;AlfrescoToolSpec&lt;/code&gt; are available in the &lt;a href="https://github.com/xaldon/opendma-demo-alfresco-llamaindex-basics" rel="noopener noreferrer"&gt;GitHub sample project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Clone the repository to experiment with the different integration approaches in your own environment.&lt;/p&gt;

&lt;p&gt;For complete runnable flows, see the existing tutorials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenDMA: &lt;a href="https://opendma.org/" rel="noopener noreferrer"&gt;https://opendma.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;LlamaIndex OpenDMA integration: &lt;a href="https://github.com/OpenDMA/llama-index-opendma" rel="noopener noreferrer"&gt;https://github.com/OpenDMA/llama-index-opendma&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/OpenDMA/llama-index-opendma/blob/main/docs/tutorials/01_basic_rag.md" rel="noopener noreferrer"&gt;Basic RAG&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/OpenDMA/llama-index-opendma/blob/main/docs/tutorials/02_metadata_aware_retrieval.md" rel="noopener noreferrer"&gt;Metadata-Aware Retrieval&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/OpenDMA/llama-index-opendma/blob/main/docs/tutorials/04_agentic_rag_retriever.md" rel="noopener noreferrer"&gt;Agentic RAG with OpenDMA Retriever&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/OpenDMA/llama-index-opendma/blob/main/docs/tutorials/05_agent_tools.md" rel="noopener noreferrer"&gt;Tool-Calling Agent with ToolSpec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://xaldon.com/products/opendma-adaptors" rel="noopener noreferrer"&gt;Adaptors&lt;/a&gt; and &lt;a href="https://xaldon.com/products/eci-server" rel="noopener noreferrer"&gt;ECI Server&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In all three cases, the LlamaIndex code works against OpenDMA instead of becoming a custom Alfresco REST integration.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>alfresco</category>
      <category>llamaindex</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Connecting LangChain to Alfresco Without Writing Another REST Loader</title>
      <dc:creator>Stefan Kopf</dc:creator>
      <pubDate>Thu, 15 Oct 2026 08:45:29 +0000</pubDate>
      <link>https://dev.to/stefankopfxaldon/connecting-langchain-to-alfresco-without-writing-another-rest-loader-355</link>
      <guid>https://dev.to/stefankopfxaldon/connecting-langchain-to-alfresco-without-writing-another-rest-loader-355</guid>
      <description>&lt;p&gt;You already have a LangChain application. The content you need is in Alfresco Content Services.&lt;/p&gt;

&lt;p&gt;At first, it is tempting to write a small loader around the Alfresco REST API: search a couple of nodes, download the content, extract text, add the chunks to a vector store.&lt;/p&gt;

&lt;p&gt;That works for the first demo. Then the edge cases arrive.&lt;/p&gt;

&lt;p&gt;You need the path, the site, the content type, custom properties, aspects, checked-out state, versions, permissions, and security markings. For some information, like Type and Aspect definitions, there is no matching Alfresco REST API. Your production repository may also contain custom or exotic file formats for which there is no useful Python implementation. Alfresco Content Services may already be configured to handle those formats, but your LangChain application does not know that.&lt;/p&gt;

&lt;p&gt;This is where a simple REST loader quickly turns into a rabbit hole.&lt;/p&gt;

&lt;p&gt;OpenDMA provides another boundary. The Alfresco adaptor maps Alfresco into the OpenDMA model. The LangChain integration talks to the OpenDMA REST endpoint and returns normal LangChain &lt;code&gt;Document&lt;/code&gt; objects. The same application code can then use the OpenDMA model instead of spreading Alfresco REST details across the RAG pipeline.&lt;/p&gt;

&lt;p&gt;Unlike CMIS, OpenDMA is not only an abstraction layer. It is a framework for putting functionality between the repository and the application: auditing, identity translation, content transformations, SIEM integration, and repository federation belong in that layer, not in every LangChain script.&lt;/p&gt;

&lt;p&gt;The examples below assume that an OpenDMA endpoint for Alfresco is already available at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:7070/opendma/alf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the LangChain OpenDMA Integration with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install langchain-opendma
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The examples use &lt;code&gt;admin/admin&lt;/code&gt; only to keep the snippets short.&lt;/p&gt;

&lt;p&gt;A runnable version of the examples is available here: &lt;a href="https://github.com/xaldon/opendma-demo-alfresco-langchain-basics" rel="noopener noreferrer"&gt;https://github.com/xaldon/opendma-demo-alfresco-langchain-basics&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Load Alfresco Content Into LangChain
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;AlfrescoLoader&lt;/code&gt; when you want to ingest Alfresco content into a LangChain application, for example to build a vector store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_opendma&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AlfrescoLoader&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_opendma.content_handlers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;UnstructuredLoaderContentHandler&lt;/span&gt;

&lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UnstructuredLoaderContentHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;chunking_strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;by_title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_characters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;new_after_n_chars&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;combine_text_under_n_chars&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AlfrescoLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:7070/opendma/alf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;repository_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alfresco&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sites&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;swsdp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;content_handlers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each returned LangChain &lt;code&gt;Document&lt;/code&gt; contains extracted text in &lt;code&gt;page_content&lt;/code&gt; and OpenDMA metadata in &lt;code&gt;metadata&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opendma:Title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_content&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;AlfrescoLoader&lt;/code&gt; is a convenience loader on top of the generic &lt;code&gt;OpenDMALoader&lt;/code&gt;. It adds Alfresco defaults and Alfresco-specific concepts like &lt;code&gt;sites&lt;/code&gt;. With &lt;code&gt;sites=["swsdp"]&lt;/code&gt;, the loader searches the Alfresco site with that short name and loads the document nodes below the matching site folder.&lt;/p&gt;

&lt;p&gt;The content handler controls how binary content becomes text. The default handler processes plain text. For Office documents, PDFs, HTML, and similar formats, use an Unstructured or Docling handler.&lt;/p&gt;

&lt;p&gt;When the content handler chunks a document, one Alfresco document can become multiple LangChain &lt;code&gt;Document&lt;/code&gt; objects. This is the usual shape for vector-store ingestion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.vectorstores&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;InMemoryVectorStore&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAIEmbeddings&lt;/span&gt;

&lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAIEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text-embedding-3-large&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vector_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InMemoryVectorStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;vector_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In production, the indexing job normally runs for new and changed documents and writes to a persistent vector store. The important part is not the vector store used in this snippet. The important part is that the documents still carry Alfresco and OpenDMA metadata after loading.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/xaldon/opendma-demo-alfresco-langchain-basics" rel="noopener noreferrer"&gt;sample repository&lt;/a&gt; contains a small CLI version of this flow. It loads one Alfresco site, builds an in-memory vector store, and then lets you ask questions against it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep The Metadata
&lt;/h2&gt;

&lt;p&gt;The metadata is not decorative.&lt;/p&gt;

&lt;p&gt;After loading content from Alfresco nodes, the returned documents can contain metadata such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opendma:Title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opendma:CheckedOut&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alfresco:Site&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alfresco:Path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alfresco:cm:name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alfresco:cm:creator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alfresco:cm:accessed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This matters as soon as the repository stops looking like a small demo. A real Alfresco system can contain meeting notes for many teams, several generations of project documents, contracts, specifications, scanned content, and old versions. A plain similarity search across everything can return text chunks that are close to the question but from the wrong site or the wrong context.&lt;/p&gt;

&lt;p&gt;You can use the metadata for filtering:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vector_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;similarity_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;state of localisation of the new website design&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alfresco:Site&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;swsdp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opendma:CheckedOut&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This article is not about metadata-aware retrieval as a complete pattern. That deserves its own article. The basic point is simpler: preserve repository metadata from the start. If your loader throws it away, you have already removed information that the retrieval step will need later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search Alfresco Live With &lt;code&gt;AlfrescoRetriever&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;A vector store is not always the right first choice.&lt;/p&gt;

&lt;p&gt;Building one means extracting content, chunking it, calculating embeddings, storing the chunks, keeping them up to date, and securing a second copy of repository text. That can be useful, but it is not free.&lt;/p&gt;

&lt;p&gt;There is also a retrieval quality issue in the other direction. Similarity search is good when the wording of the user question differs from the wording of the document. It is less appropriate when exact terms matter. Product codes, customer numbers, invoice IDs, claim numbers, or part numbers should not be "semantically similar". They should match.&lt;/p&gt;

&lt;p&gt;For these cases, use &lt;code&gt;AlfrescoRetriever&lt;/code&gt; to run a live full-text search through OpenDMA:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_opendma&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AlfrescoRetriever&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_opendma.content_handlers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;UnstructuredLoaderContentHandler&lt;/span&gt;

&lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UnstructuredLoaderContentHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;chunking_strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;by_title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_characters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;new_after_n_chars&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;combine_text_under_n_chars&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;retriever&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AlfrescoRetriever&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:7070/opendma/alf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;repository_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alfresco&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sites&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;swsdp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;content_handlers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;retriever&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;meeting notes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;AlfrescoRetriever&lt;/code&gt; implements LangChain's retriever API. The input string is converted into an Alfresco AFTS full-text query, including escaping of special characters. If &lt;code&gt;sites&lt;/code&gt; is set, the query is restricted to those Alfresco sites.&lt;/p&gt;

&lt;p&gt;The result is still a list of LangChain &lt;code&gt;Document&lt;/code&gt; objects.&lt;/p&gt;

&lt;p&gt;Because the search runs through Alfresco Content Services via the OpenDMA endpoint, repository access control, governance rules, and security markings remain in the live retrieval path. This is different from querying a separate vector store that contains copied text.&lt;/p&gt;

&lt;p&gt;For some applications, this is enough. For others, live full-text search and vector search can both be useful, depending on the question.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/xaldon/opendma-demo-alfresco-langchain-basics" rel="noopener noreferrer"&gt;sample repository&lt;/a&gt; also contains the same idea as a small live-search command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give An Agent Alfresco Tools
&lt;/h2&gt;

&lt;p&gt;Sometimes the agent should not only retrieve chunks. It needs to inspect the repository.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which Alfresco sites exist?&lt;/li&gt;
&lt;li&gt;Which documents are in this folder?&lt;/li&gt;
&lt;li&gt;What type and aspects does this object have?&lt;/li&gt;
&lt;li&gt;Is the answer already in metadata?&lt;/li&gt;
&lt;li&gt;Which document text should be read?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For that, use &lt;code&gt;AlfrescoToolkit&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.chat_models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;init_chat_model&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_opendma&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AlfrescoToolkit&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_opendma.content_handlers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DoclingLoaderContentHandler&lt;/span&gt;

&lt;span class="n"&gt;toolkit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AlfrescoToolkit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:7070/opendma/alf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;repository_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alfresco&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content_handlers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;DoclingLoaderContentHandler&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;init_chat_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai:gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;toolkit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_tools&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You answer questions using documents stored in Alfresco. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Use repository tools to find documents and inspect metadata before &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reading document text.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The toolkit exposes tools such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;alfresco_list_sites&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;opendma_list_children&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;opendma_get_metadata&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;opendma_describe_class&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;opendma_search&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;opendma_read_text&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives the agent a different way to work. It can list sites, browse folders, inspect metadata, search, and read selected text chunks.&lt;/p&gt;

&lt;p&gt;Reading document text is more expensive than listing children or reading metadata. A good agent prompt should make that visible. Often the agent should first locate candidate documents, inspect their metadata, and only then read text.&lt;/p&gt;

&lt;p&gt;The toolkit example in the &lt;a href="https://github.com/xaldon/opendma-demo-alfresco-langchain-basics" rel="noopener noreferrer"&gt;sample repository&lt;/a&gt; runs this as an interactive agent.&lt;/p&gt;

&lt;p&gt;This is still the basic toolkit usage. More elaborate agentic RAG flows, where the agent decides how often to search and how to assess retrieved context, are a separate topic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Start With cmislib Or Direct REST?
&lt;/h2&gt;

&lt;p&gt;CMIS used to be the obvious abstraction to mention around ECM systems. For new LangChain work, I would not start there.&lt;/p&gt;

&lt;p&gt;Apache Chemistry's cmislib project is retired. Its documentation is visibly stale. It may still be useful in old systems, but it is not a good foundation for a new LangChain integration.&lt;/p&gt;

&lt;p&gt;Calling Alfresco REST directly is different. It is often the right choice for a small script with a narrow job. The problem starts when that small script becomes the content layer for an LLM application.&lt;/p&gt;

&lt;p&gt;Then you need to model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;metadata&lt;/li&gt;
&lt;li&gt;Type and Aspect definitions&lt;/li&gt;
&lt;li&gt;paths and sites&lt;/li&gt;
&lt;li&gt;versions and checked-out state&lt;/li&gt;
&lt;li&gt;permissions and security markings&lt;/li&gt;
&lt;li&gt;content conversion&lt;/li&gt;
&lt;li&gt;query escaping&lt;/li&gt;
&lt;li&gt;errors and partial failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For custom and exotic file formats, you may also end up rebuilding in Python what Alfresco Content Services is already configured to handle.&lt;/p&gt;

&lt;p&gt;OpenDMA keeps the Alfresco-specific work in the adaptor and integration layer. The LangChain application sees &lt;code&gt;Document&lt;/code&gt; objects, retrievers, and tools.&lt;/p&gt;

&lt;p&gt;OpenDMA also models renditions. That matters because production Alfresco repositories often already know how to turn custom formats into PDF or extractable text. A LangChain application should not become the place where all of that repository-specific conversion logic is rebuilt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enterprise Notes
&lt;/h2&gt;

&lt;p&gt;The OpenDMA endpoint is also the place where enterprise concerns can be handled without putting them into every LangChain application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;auditing repository requests made by an agent&lt;/li&gt;
&lt;li&gt;identity forwarding or token-based sessions&lt;/li&gt;
&lt;li&gt;identity translation&lt;/li&gt;
&lt;li&gt;RBAC&lt;/li&gt;
&lt;li&gt;SIEM integration&lt;/li&gt;
&lt;li&gt;federation across multiple ECM systems&lt;/li&gt;
&lt;li&gt;repository-side rendition support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are adaptor, middleware, and deployment concerns. The LangChain code should stay small.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which OpenDMA Integration Surface To Use
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;AlfrescoLoader&lt;/code&gt; when you want to ingest Alfresco content into a vector store or another knowledge base.&lt;/p&gt;

&lt;p&gt;Keep the Alfresco metadata in the LangChain documents from the start. Later filtering and metadata-aware retrieval depend on it.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;AlfrescoRetriever&lt;/code&gt; when live Alfresco full-text search is preferable to a second indexed copy of repository content.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;AlfrescoToolkit&lt;/code&gt; when an agent needs to navigate Alfresco, inspect metadata, search, and read selected text chunks.&lt;/p&gt;

&lt;p&gt;Further reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runnable sample project: &lt;a href="https://github.com/xaldon/opendma-demo-alfresco-langchain-basics" rel="noopener noreferrer"&gt;https://github.com/xaldon/opendma-demo-alfresco-langchain-basics&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OpenDMA: &lt;a href="https://opendma.org/" rel="noopener noreferrer"&gt;https://opendma.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;LangChain OpenDMA integration: &lt;a href="https://github.com/OpenDMA/langchain-opendma" rel="noopener noreferrer"&gt;https://github.com/OpenDMA/langchain-opendma&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tutorials: &lt;a href="https://github.com/OpenDMA/langchain-opendma/tree/main/docs/tutorials" rel="noopener noreferrer"&gt;https://github.com/OpenDMA/langchain-opendma/tree/main/docs/tutorials&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Loader documentation: &lt;a href="https://github.com/OpenDMA/langchain-opendma/blob/main/docs/Loader.md" rel="noopener noreferrer"&gt;https://github.com/OpenDMA/langchain-opendma/blob/main/docs/Loader.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Retriever documentation: &lt;a href="https://github.com/OpenDMA/langchain-opendma/blob/main/docs/Retriever.md" rel="noopener noreferrer"&gt;https://github.com/OpenDMA/langchain-opendma/blob/main/docs/Retriever.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Toolkit documentation: &lt;a href="https://github.com/OpenDMA/langchain-opendma/blob/main/docs/Toolkit.md" rel="noopener noreferrer"&gt;https://github.com/OpenDMA/langchain-opendma/blob/main/docs/Toolkit.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;site.docs.alfresco.com&lt;/li&gt;
&lt;li&gt;site.docs.langchain.com&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://xaldon.com/products/opendma-adaptors" rel="noopener noreferrer"&gt;Adaptors&lt;/a&gt; and &lt;a href="https://xaldon.com/products/eci-server" rel="noopener noreferrer"&gt;ECI Server&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>alfresco</category>
      <category>langchain</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
