Task: Add project documentation analysis features to the AI agent
Add project documentation analysis features to the AI agent
There are archives containing project documentation and tender information. These archives can weigh hundreds of megabytes or even gigabytes, containing hundreds and thousands of files in various formats (txt, doc, docx, xls, xlsx, dwg, etc.).
We need to develop a pipeline for guaranteed analysis of such archives. Preliminary steps:
- A task for archive analysis is created.
- The archive is extracted (rar, zip, 7z) and all files are extracted. Each file goes into a separate subtask for analysis.
- Each file is analyzed by a separate thread using a separate agent.
- Once all files have been analyzed and all subtasks are completed, the main agent performs a final analysis of the contents.
Why this specific approach? The fact is that if you try to stuff all of this into a single agent thread, there is always a risk that at some point it will fail to perform certain actions or stop working entirely. Separate tasks allow, firstly, for a clearer localization of execution requirements and evaluation criteria, as well as more precise control over what has been done and what has not. And even if some tasks fail with errors, the main agent can ultimately perform at least a partial analysis and provide a report indicating which intermediate tasks were not completed and why. In our current use case, even a partial analysis is already valuable because it provides at least some preliminary understanding of the incoming application and gives grounds for deciding whether it is worth studying in more detail or if we shouldn't even waste time on it.
Ворклоги
Here is a classic example of when you need to create a separate mechanism for a specific task, if you want the task to be completed exactly as planned, with minimal risk of under-execution and maximum reliability of the response.
What is happening now: the agent is given the path to an archive and told to unpack it and save the complete list of files to a separate worklog. I should clarify that there are over a hundred files in the archive. The agent is provided with a special tool for unpacking, so it cannot fail to get the complete list of extracted files. At this point, the agent has a small task left—just to write this list into the new task worklog. But that is not actually trivial. So it limited itself to the following entry:
Jun 28, 2026, 5:13:46 AM Archive successfully unpacked. File composition:
- EP_24103_TP_EL_Seletuskiri est.pdf - EP_24103_TP_EL_Seletuskiri рус.pdf And various drawings (.dwg, .pdf) for the GGS-4 project (lighting, diagrams, material specifications). The full list of files is saved in the system of available paths.
Something like that :-)
What is the problem here? The problem is that if you subsequently say, "Review the most important documents," where will it get the list of files from? Unpack the archive again? That was the whole point: first, you could unpack the archive and know the list of files in it, and then, in separate steps, read individual documents and save useful information to the worklogs. This would allow for non-linear examination of individual elements of the project documentation as needed. At the same time, both humans and agents could work on the same task. But such work requires strict adherence to instructions, and as we know and practice shows, agents do not always fully follow instructions, especially when the volume of information is large.
In such situations, the only solution can be additional tools and wrappers.
Here is a separate, extremely interesting case with the following instruction:
Read the files
storage/unpack/cmqv7yb0i00018hzwx2uttxcb/1782515133778-VKG_Ümberpuist hoone.rar/RUS_25053_v03_PP_ГГС-4_Золоудаление/RUS_25053_v03_PP_ГГС-4_Золоудаление/Том I_25053_PP_ГГС-4_AA-AS-AR-EK-TK/25053_PP_ГГС-4_строительство/25053_PP_ГГС-4_строительство/25053_PP_-4_áâந⥫ìá⢮/25053_PP_AA/25053_PP_AA-0-01_â¨âã«ìë© «¨áâ.pdf
storage/unpack/cmqv7yb0i00018hzwx2uttxcb/1782515133778-VKG_Ümberpuist hoone.rar/RUS_25053_v03_PP_ГГС-4_Золоудаление/RUS_25053_v03_PP_ГГС-4_Золоудаление/Том I_25053_PP_ГГС-4_AA-AS-AR-EK-TK/25053_PP_ГГС-4_строительство/25053_PP_ГГС-4_строительство/25053_PP_-4_áâந⥫ìá⢮/25053_PP_AA/25053_PP_AA-3-01_¯®ïá¨â¥«ì ï.pdf
Write the results to the worklog.
What is interesting here first of all? These are the long paths with various mojibake characters. Let me remind you that LLMs do not inherently have variable mechanisms (I am only planning to implement this in my own version). By and large, when generating queries, the LLM must reproduce these paths byte-for-byte when passing them as parameters to tools. And it must be noted that Gemini 2.5 Flash handled this brilliantly.
Next, I have some interesting and unexpected assumptions to make based on the models' reasoning while reading the files. I will quote both queries right away.
[
{
"query": {
"reasoning": "Extracting text from the title page to identify the document.",
"query": "mutation {\n parsePdfOpenRouter(input: {\n filePath: \"storage/unpack/cmqv7yb0i00018hzwx2uttxcb/1782515133778-VKG_Ümberpuist hoone.rar/RUS_25053_v03_PP_ГГС-4_Золоудаление/RUS_25053_v03_PP_ГГС-4_Золоудаление/Том I_25053_PP_ГГС-4_AA-AS-AR-EK-TK/25053_PP_ГГС-4_строительство/25053_PP_ГГС-4_строительство/25053_PP_-4_áâந⥫ìá⢮/25053_PP_AA/25053_PP_AA-0-01_â¨âã«ìë© «¨áâ.pdf\"\n pages: [1]\n }) {\n pages {\n text\n }\n }\n}",
"variables": "{}"
}
}
]
[
{
"query": {
"reasoning": "Extracting text from the explanatory note. Let's see how many pages are in the document, and read them (if there aren't many). The first request showed 1 page, but it's better to make sure.",
"query": "mutation {\n parsePdfOpenRouter(input: {\n filePath: \"storage/unpack/cmqv7yb0i00018hzwx2uttxcb/1782515133778-VKG_Ümberpuist hoone.rar/RUS_25053_v03_PP_ГГС-4_Золоудаление/RUS_25053_v03_PP_ГГС-4_Золоудаление/Том I_25053_PP_ГГС-4_AA-AS-AR-EK-TK/25053_PP_ГГС-4_строительство/25053_PP_ГГС-4_строительство/25053_PP_-4_áâந⥫ìá⢮/25053_PP_AA/25053_PP_AA-3-01_¯®ïá¨â¥«ì ï.pdf\"\n }) {\n totalPages\n pages {\n pageNumber\n text\n }\n }\n}",
"variables": "{}"
}
}
]
What is interesting here? In general, it followed the logic embedded in the document reading skill, here is a quote:
Document Reading Strategy
It is recommended to read the document superficially first — only the first page with the totalPages query:
mutation {
parsePdfOpenRouter(input: {
filePath: "storage/unpack/tender-123/document.pdf"
pages: [1]
}) {
totalPages
pages {
text
}
}
}
This allows you to:
- Find out the total number of pages in the document
- Understand the content and relevance of the document
- Decide whether you need to read the entire document
If the document is important and the full content is required, read all pages (without the pages parameter).
And what did our agent do? It did almost everything right, but made a key mistake — it requested the page count on the first document, but read everything on a completely different document :-) Moreover, in the second query, it even clarified: The first request showed 1 page, but it's better to make sure.
Furthermore, in the first query, it couldn't even know how many pages were actually there, because it only specified to get the first page by passing pages: [1], but it did not include the totalPages parameter in the response body :-) So it received the response without information about the page count.
Yet in the second query, it did the opposite — it specified totalPages (and it got this information), but did not specify pages: [1] :-)
In short, this is a very interesting and somewhat comical example of how an agent can logically deviate from the instructions given to it, but in certain cases, considering the requirements for task execution reliability, the result may turn out to be far from comical.
Ha, quite a stumble over large file uploads. I mean, the issue wasn't even how to upload a large file (we would've solved that quickly), but rather that, as it turned out, we weren't handling the error itself when uploading a file exceeding the allowed limit. The upload would just hang infinitely. We finally beat it, but the number of times and ways the opus failed in the process is enough material for a good separate article. I'll publish it a bit later, because I still want the introductory word to be the first topic, but at least we've figured it out and fixed it now.