[Payables Agent] Agent-driven line matching - #7546
Conversation
Add IPrepareDraftGuard interface to allow skipping the AL-based PrepareDraft pipeline when matching is handled by the agent. - New IPrepareDraftGuard interface with SkipPrepareDraft() method - Default implementation returns false (AL pipeline runs normally) - Guard check at top of PrepareDraft procedure - Added to E-Doc. Proc. Customizations enum with DefaultImplementation - Extended app.json idRanges (6243-6249) New Historical Purchase Lines page and smart data loader: - Priority-based loader: same-vendor first, then cross-vendor - Matching by exact product code, exact description, LLM similar descriptions - Per-line results (different lines show different historical matches) - Capped at 5,000 records New line-level navigation actions on Draft Subform (Scope = Repeater): - Item References, Text-to-Account Mappings, Historical Purchase Lines, Chart of Accounts, Deferral Templates - EnsureEDocumentPurchaseHeader guard for agent session compatibility - Allocation Account No. field on Historical Lines page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
b52fc8c to
ca778d8
Compare
There was a problem hiding this comment.
AL Documentation Audit
Documentation gaps were detected in the following apps:
- E-Document-Core: 29% documentation coverage
To generate documentation, run /al-docs init or /al-docs update using GitHub Copilot CLI or Claude Code.
This review is for awareness to help keep documentation in sync with code changes. It is okay to dismiss this request.
Add IPrepareDraftGuard interface to allow skipping the AL-based PrepareDraft pipeline when matching is handled by the agent. - New IPrepareDraftGuard interface with SkipPrepareDraft() method - Default implementation returns false (AL pipeline runs normally) - Guard check at top of PrepareDraft procedure - Added to E-Doc. Proc. Customizations enum with DefaultImplementation - Extended app.json idRanges (6243-6249) New Historical Purchase Lines page and smart data loader: - Priority-based loader: same-vendor first, then cross-vendor - Matching by exact product code, exact description, LLM similar descriptions - Per-line results (different lines show different historical matches) - Capped at 5,000 records New line-level navigation actions on Draft Subform (Scope = Repeater): - Item References, Text-to-Account Mappings, Historical Purchase Lines, Chart of Accounts, Deferral Templates - EnsureEDocumentPurchaseHeader guard for agent session compatibility - Allocation Account No. field on Historical Lines page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…les-agent-line-matching
…les-agent-line-matching
…//github.com/microsoft/BCApps into features/607916-payables-agent-line-matching
|
…les-agent-line-matching
Adds an 'Items' action on the draft subform that opens the Item List (page 31) filtered to non-blocked items. This supports the Payables Agent's new Items matching source (Source E) where the agent can look up items by name/description when no Item Reference exists for the vendor. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
…arePurchaseEDocDraft The IPrepareDraftGuard interface dispatch pattern is replaced with a direct FeatureConfiguration.GetConfiguration() check inside PreparePurchaseEDocDraft, mirroring how MLLM extraction (EDocPDFFileFormat) works. Changes: - PreparePurchaseEDocDraft.Codeunit.al: replace interface dispatch with inline ECS check (PAAgentDrivenLineMatching = 'agent_driven') - EDocProcCustomizations.Enum.al: remove IPrepareDraftGuard from implements clause and DefaultImplementation - Delete EDocDefPrepDraftGuard.Codeunit.al (default always-false guard) - Delete IPrepareDraftGuard.Interface.al - app.json: remove id 6243 from range (was EDocDefPrepDraftGuard) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…/607916-payables-agent-line-matching
- Update agent instructions with full line matching guidance (6 sources, collect-then-synthesize, HITL confidence, reason field) - Update PAEDocPurchaseDraftSubform page customization to expose all 6 source actions (Items, ItemRef, HistoricalLines, TTA, GLAccount, Deferral) - Add 6 new PA profile page customizations: PAItems, PAChartOfAccounts, PADeferralTemplateList, PAHistPurchaseLines, PAItemReferenceEntries, PATextToAccountMapping Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 6 new page customizations to PayablesAgent.Profile that support agent-driven line matching source navigation: - PA Item Reference Entries - PA Text-to-Account Mapping - PA Chart of Accounts - PA Hist. Purchase Lines - PA Deferral Template List - PA Items Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…les-agent-line-matching
…les-agent-line-matching
…les-agent-line-matching
…hing flag Select the Payables Agent instructions prompt based on the same ECS feature flag that gates AL PrepareDraft line matching: - Control (PAAgentDrivenLineMatching != agent_driven): original prompt (PayablesAgent-AgentInstructions.md, restored to main) + AL PrepareDraft. - Treatment (agent_driven): new agent-driven prompt (PayablesAgent-AgentInstructions-AgentDriven.md), AL PrepareDraft skipped. SetAgentInstructions now reads Feature Configuration and loads the matching prompt resource. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aeb37867-e4e2-4aa3-ac17-0817b685dab1
…gentDrivenLineMatching flag The five lookup actions added for agent-driven line matching (Text-to-Account Mappings, Historical Purchase Lines, Chart of Accounts, Items, Deferral Templates) on the E-Doc. Purchase Draft Subform are now shown only when the PAAgentDrivenLineMatching ECS feature is in the agent_driven treatment, matching the prompt and PrepareDraft gating. OnOpenPage reads Feature Configuration and drives each action's Visible via AgentDrivenLineMatchingEnabled. Note: the Payables Agent profile customization statically forces these actions visible (profile customizations cannot hold conditional logic), so the agent still sees them; the agent's behavior remains gated by the prompt. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aeb37867-e4e2-4aa3-ac17-0817b685dab1
…les-agent-line-matching
…ension keys - LogError (0000SEO) now emits GetLastErrorText(true) only, dropping the vendor number and the unsanitized error text so no vendor-identifying or customer-bearing content is sent to telemetry. Removes the now-unused HistoricalDataLoadFailedErr label. - Rename 0000SEN custom-dimension keys to space-free PascalCase (RecordsLoaded, VendorMatchingScope, MaxRecordsLimit, LimitReached) for a stable telemetry schema. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aeb37867-e4e2-4aa3-ac17-0817b685dab1
…oncile When the best-effort instruction reconciliation TryFunction fails, capture the redacted GetLastErrorText(true) into the 0000SEL telemetry custom dimensions so Key Vault, prompt-loading, or instruction-update failures remain diagnosable in production, then remove the transient dimension before the subsequent event. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aeb37867-e4e2-4aa3-ac17-0817b685dab1
Agentic PR Review - Round 4Recommendation: Request ChangesWhat this PR doesFive commits landed since round 3. They add usage and error telemetry to the historical-line loader (wrapped in a [TryFunction] so a load failure no longer silently aborts the action), sanitize AI-generated filter terms before passing them to SetFilter (removes AL filter metacharacters & | ( ) < > = ? @ * . ' " %), add a TestField guard on the vendor number before invoking the loader action, replace a per-row database re-read in OnAfterGetRecord with a direct Rec assignment, add a prompt-injection hardening note to the agent instructions, and wrap EnsureAgentInstructionsMatchConfiguration in a best-effort [TryFunction] so a Key Vault failure during instruction reconciliation does not abort e-document import. All five changes are correct and improve the reliability and security of the agent-driven path. However, the standing S2 blocker is still in the diff: PreparePurchaseEDocDraft.Codeunit.al exits immediately (before calling PrepareDraftHelper.PrepareDraft) when PAAgentDrivenLineMatching = agent_driven, skipping VAT product posting group resolution and description enrichment for every draft line. Round 4 commits do not touch that file. Status of previous suggestions
New observations (commits since round 3)None — all five commits address prior feedback or add supporting diagnostics. No new issues found. Risk assessment and necessityRisk: S2 remains the financial-data integrity blocker. On any treatment tenant where PAAgentDrivenLineMatching = agent_driven, the early exit in PreparePurchaseEDocDraft.PrepareDraft means every e-document draft is created without the PrepareDraftHelper.PrepareDraft step that resolves VAT product posting groups and enriches line descriptions. The agent prompt does not fill VAT fields, so VAT amounts can be blank or wrong on every finalized purchase invoice in agent-driven mode. S4 compounds this: because there are no automated tests for the agent-driven path, this failure mode has no regression coverage. The round 4 changes do not increase or decrease this risk. The new telemetry, filter sanitization, vendor guard, and prompt-injection note are all positive additions with no regression surface. Necessity: Unchanged from round 1. The feature goal (agent-driven line matching for Payables) is valid and the scope is appropriate. Resolving S2 before enabling the treatment on any tenant is the remaining gate.
|
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aeb37867-e4e2-4aa3-ac17-0817b685dab1
Agentic PR Review - Round 5Recommendation: Request ChangesWhat this PR doesSince round 4, the PR branch only merged the latest The feature goal is still coherent: Payables Agent gets an ECS-gated, agent-driven line matching flow with new lookup pages and a historical purchase line tool. The remaining blocker is unchanged: the Status of previous suggestions
New observations (commits since round 4)None. The only new branch commit is a merge from Risk assessment and necessityRisk: S2 is still the blocking data-integrity risk. In treatment tenants, the early exit can leave purchase draft lines without existing prepare-draft outputs such as VAT product posting group resolution and related enrichment, and the agent-driven prompt does not fill those fields. The current GitHub build is also red because fail-on-new-warning validation now reports warnings, so the PR is not in a merge-ready validation state. Necessity: The slice remains valid and useful. The feature should preserve the existing non-matching purchase draft preparation guarantees while moving only line-account matching to the agent-driven flow.
|
…les-agent-line-matching
Jose is on holiday, asking other team members to review in his place.
…les-agent-line-matching
…les-agent-line-matching
| /// similar descriptions), then any remaining lines for the same vendor. | ||
| /// The search is scoped to the draft's vendor; no cross-vendor history is loaded. | ||
| /// </summary> | ||
| procedure LoadHistoricalLines(var TempPurchInvLine: Record "Purch. Inv. Line" temporary; VendorNo: Code[20]; ProductCode: Text[100]; Description: Text[100]) |
There was a problem hiding this comment.
The new internal codeunit 'E-Doc. Hist. Line Data Loader' exposes LoadHistoricalLines and MaxHistoricalRecords with default public access even though both are only used inside the E-Document app. That turns implementation-detail routines into callable contracts for friend apps. Mark the cross-object entry point 'internal' and keep the helper 'local' unless you intend to support them as stable APIs.
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4
Avoid refreshing agent instructions from the e-document import callback, and rely on the Agent Framework security prompt instead of duplicating prompt hardening. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0fedf3ac-fbae-4f75-9c22-f5b7dfadda9b
ac6ed4e
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0fedf3ac-fbae-4f75-9c22-f5b7dfadda9b
| PrepareDraftHelper: Codeunit "EDoc Prepare Purch. Draft"; | ||
|
|
||
| procedure PrepareDraft(EDocument: Record "E-Document"; EDocImportParameters: Record "E-Doc. Import Parameters"): Enum "E-Document Type" | ||
| var |
There was a problem hiding this comment.
The tenant-level experiment key 'PAAgentDrivenLineMatching' and its treatment value 'agent_driven' are declared as separate, independently-maintained Label literals in two different apps: src/Apps/W1/EDocument/App/src/Processing/Import/PrepareDraft/PreparePurchaseEDocDraft.Codeunit.al (AgentDrivenLineMatchingTok/AgentDrivenTreatmentTok) and src/Apps/W1/PayablesAgent/app/Setup/PayablesAgentSetup.Codeunit.al (same token names/values, used both to select the prompt variant and to build the config-hash fingerprint). Both call sites must agree on the exact key and treatment string for the feature to behave consistently (EDocument decides whether to skip its own draft-prep logic based on the same flag PayablesAgent uses to pick the prompt and to detect instruction drift). Because the string is duplicated rather than shared from one owning location, a future rename or value change in one app silently desynchronizes from the other with no compile-time signal, causing PrepareDraft's branch and the agent's active prompt/config-hash to diverge. Recommend hoisting the experiment key (and its treatment value) into a single shared/public constant or procedure that both apps reference, or having EDocument call a published PayablesAgent API for the check instead of re-declaring the literal.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
|
The new local procedure EnsureEDocumentPurchaseHeader raises a plain Error when the purchase header for the current e-document line cannot be found. This is an internal invariant failure (the row the page is showing has no corresponding header) that the user cannot correct, so it should be classified as ErrorType::Internal rather than a client-facing validation error. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4 |
| if TotalLoaded >= MaxHistoricalRecords() then | ||
| exit; | ||
|
|
||
| PurchInvLine.Reset(); |
There was a problem hiding this comment.
The historical-line loader (E-Doc. Hist. Line Data Loader) builds several non-primary-key 'Purch. Inv. Line' scans filtered by vendor/date/type/description and caps the result at 5000 rows, but never selects a key matching the actual access pattern (e.g. vendor + posting date) before FindSet(). Without SetCurrentKey aligned to the filters, the engine may scan a wider, arbitrarily ordered slice and the 5000-row cap can be reached before the most relevant historical lines are found.
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.33.4
Summary
Adds agent-driven line matching to the Payables Agent. When the ECS feature flag
PAAgentDrivenLineMatchingis set toagent_driven, the agent resolves each draft purchase line itself by navigating BC pages, instead of relying on the ALPrepareDraftmatching pipeline. All new behavior is gated behind that flag, so default (non-treatment) behavior is unchanged.The feature spans two apps: the E-Document app (new navigation surfaces + historical-line data loader) and the Payables Agent app (new agent instructions prompt, page customizations, and prompt gating).
Feature gating
A single ECS flag, read via
Feature Configuration.GetConfiguration('PAAgentDrivenLineMatching'), controls three things when it equalsagent_driven:Payables Agent Setup, codeunit 3307) — loads the newPayablesAgent-AgentInstructions-AgentDriven.mdinstructions instead of the standard prompt.Prepare Purchase E-Doc. Draft, codeunit 6125) — bypasses the ALPrepareDrafthelper so the agent drives matching rather than the built-in pipeline.E-Doc. Purchase Draft Subform, page 6183) — the new line lookup actions are shown only when the flag is enabled.E-Document app changes
E-Doc. Purchase Draft Subform(page 6183) — five new line-scoped (Scope = Repeater) lookup actions, flag-gated: Text-to-Account Mappings, Historical Purchase Lines, Chart of Accounts, Items, and Deferral Templates. The existing Item References action is also scoped to the repeater. Adds anEnsureEDocumentPurchaseHeaderhelper.E-Doc. Hist. Line Data Loader(codeunit 6244, new) — loads up to 5,000 posted purchase invoice lines from the last year into a temporary table, ordered by relevance: same-vendor matches first, then cross-vendor matches, then remaining same-vendor and cross-vendor fill. Matching is by exact product code, exact description, and LLM-generated similar descriptions (E-Doc. Similar Descriptions). Allocation-account lines display the allocation account name.E-Doc. Historical Lines List(page 6186, new) — read-only temporary list page presenting the loaded historical lines.6244-6249for the new objects.Payables Agent app changes
PayablesAgent-AgentInstructions-AgentDriven.md(new) — agent-driven instructions implementing a collect-then-synthesize line-matching strategy across six sources (Item References, Text-to-Account Mappings, Historical Purchase Lines, Chart of Accounts, Items, Deferral Templates), including a product-identifier rule, recency weighting, conflict detection, and an updated sitemap.ClearActions/ClearLayout, no insert/modify/delete) exposing only the fields the agent needs: Chart of Accounts, Deferral Template List, Hist. Purchase Lines, Item Reference Entries, Items, and Text-to-Account Mapping.PA EDoc Purchase Draft Subformcustomization — exposes new line fields (Line Type, No., Item Reference No., Deferral Code, Unit of Measure) and the six new line actions.Payables Agentprofile — registers the six new page customizations.Related NAV PR
NAV PR #250770 -- [Payables Agent] Agent-driven line matching — test scenarios (Slice #607916)
Related Work Items
AB#607916
Test plan
agent_driven, agent-driven prompt loads, PrepareDraft is skipped, and line actions are visible