Skip to content

feat: add EoG (Explanations over Graphs) agent pattern#993

Open
UIPath-Harshit wants to merge 4 commits into
mainfrom
worktree-agent-a1cd372f
Open

feat: add EoG (Explanations over Graphs) agent pattern#993
UIPath-Harshit wants to merge 4 commits into
mainfrom
worktree-agent-a1cd372f

Conversation

@UIPath-Harshit

Copy link
Copy Markdown
Contributor

Summary

  • Introduces EoG as a new agent type alongside ReAct for ontology-grounded graph investigation
  • Deterministic controller traverses an ontology graph, invoking bounded LLM inference per node
  • Belief propagation along edges with convergence on a minimal explanatory frontier
  • REST client for ontology-runtime (discover, invoke functions, SPARQL)
  • 44 tests covering types, client, nodes, and graph integration

Test plan

  • uv run pytest tests/agent/eog/ -v — 44 tests pass
  • uv run ruff check src/uipath_langchain/agent/eog/ — lint clean
  • E2E with live ontology-runtime (requires local stack)

🤖 Generated with Claude Code

Introduces a new agent type alongside ReAct for ontology-grounded
graph investigation. EoG uses a deterministic controller to traverse
an ontology graph, invoking bounded LLM inference at each node,
propagating beliefs along edges, and converging on a minimal
explanatory frontier.

- agent/eog/types.py: EoGState, Belief, LedgerEntry, InvestigationConfig
- agent/eog/ontology_client.py: async REST client for ontology-runtime
- agent/eog/nodes.py: 7 graph nodes (bootstrap, pop, fetch, policy, update, propagate, frontier)
- agent/eog/agent.py: create_eog_agent() graph builder
- tests/agent/eog/: 44 tests covering types, client, nodes, and integration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 15, 2026 16:39

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new EoG (Explanations over Graphs) agent pattern to the UiPath LangChain integration, including a LangGraph-based controller, an ontology-runtime REST client, and supporting state/types plus test coverage.

Changes:

  • Introduces EoG state/types (beliefs, ledger, edges, config) and LangGraph state reducers.
  • Adds an async OntologyClient for ontology discovery, function invocation, and SPARQL.
  • Implements EoG node functions and a create_eog_agent graph builder, with a new test suite validating behavior.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/uipath_langchain/agent/eog/types.py Defines EoG state models and reducer-annotated fields for LangGraph merging.
src/uipath_langchain/agent/eog/ontology_client.py Adds async REST client to talk to ontology-runtime endpoints.
src/uipath_langchain/agent/eog/nodes.py Implements bootstrap/pop/context/policy/update/propagate/frontier nodes for the EoG traversal.
src/uipath_langchain/agent/eog/agent.py Builds the LangGraph StateGraph wiring EoG nodes together.
src/uipath_langchain/agent/eog/__init__.py Exposes the EoG public API surface (create_eog_agent, types, client).
tests/agent/eog/test_types.py Unit tests for new EoG types and reducer behavior.
tests/agent/eog/test_ontology_client.py Unit tests for OntologyClient request construction and error handling.
tests/agent/eog/test_nodes.py Unit tests for node logic (bootstrap, policy parsing, propagation, frontier).
tests/agent/eog/test_agent.py Integration-style tests that compile/run the EoG graph with mocks.
tests/agent/eog/__init__.py Adds package init for the new EoG test folder.

f"{self._api_base}/ontology/{ontology}"
f"/functions/{fn_name}/invoke"
)
body: dict[str, Any] = {"params": params} if params else {}
Comment thread src/uipath_langchain/agent/eog/nodes.py Outdated
Comment on lines +150 to +157
for fn in functions:
fn_name = fn.get("name", "")
fn_desc = fn.get("description", "")
if entity_id.lower() in fn_desc.lower() or not fn_desc:
try:
result = await client.invoke_function(
ontology, fn_name, {"entity_id": entity_id}
)
Comment on lines +31 to +33
def _make_mock_client(self: OntologyClient) -> httpx.AsyncClient:
return httpx.AsyncClient(transport=httpx.MockTransport(handler))

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0da98e7c7b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/uipath_langchain/agent/eog/nodes.py Outdated
Comment on lines +300 to +301
for prop in propagations:
target = prop.get("entity", "")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate propagation entries before iterating

When the model returns valid JSON but propagations is null, an object, a string, or a list containing non-object items, this loop will either fail iteration or call .get on a non-dict and abort the graph after the policy node. Since the LLM output is only json.loads-checked, malformed-but-valid JSON can crash investigations instead of falling back to no propagations; validate/normalize this field before iterating.

Useful? React with 👍 / 👎.

Comment thread src/uipath_langchain/agent/eog/nodes.py Outdated
Comment on lines +320 to +325
if (
target_flips < max_flips
and target not in state.active_set
and target not in new_active
):
new_active.append(target)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restrict propagation targets to ontology nodes

Every LLM-supplied target that passes the flip checks is appended to active_set, without verifying that it exists in the discovered ontology or is connected to current_entity. In a graph-grounded investigation, a hallucinated target will become an entity visit and fetch_context will invoke ontology functions for that arbitrary ID, polluting the frontier and spending budget on off-graph nodes; constrain activations to known/adjacent ontology nodes before enqueueing them.

Useful? React with 👍 / 👎.

UIPath-Harshit and others added 3 commits July 17, 2026 06:59
The EoG agent was missing the dependency graph that the paper's algorithm
requires. Without it, propagation was LLM-controlled (defeating the
deterministic controller premise), context contract couldn't find neighbors,
and frontier was a filter instead of a minimality computation.

This commit adds:

graph_topology.py — OWL Functional Notation parser + OntologyGraph
  - Parses classes → nodes, object properties → typed directed edges
  - Adjacency indexes: neighbors(), outgoing(), incoming(), edges_of()
  - Function-to-entity mapping via param names + SPARQL statement scanning
  - Entity ID → type resolution via prefix conventions
  - Serialization round-trip for LangGraph state storage

ontology_client.py — fetch_graph() method
  - Downloads schema.ofn artifact + function list
  - Parses OWL into OntologyGraph with adjacency built

nodes.py — fixed all nodes to use graph topology:
  - bootstrap: calls fetch_graph() instead of discover()
  - fetch_context: uses graph edges for 1-hop neighbors, binds function
    params correctly using entity type + ID conventions
  - policy: enforces label vocabulary, strips markdown fences
  - update: applies damping (force Defer when flip_count > max_flips)
  - propagate: follows GRAPH EDGES not LLM suggestions. Only propagates
    when belief actually changed. Messages include relationship label.
  - frontier: computes irreducibility (Source→DerivedEffect edges remove
    the DerivedEffect from the frontier)

60 tests passing (was 44, added 16 for graph topology).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OFN parsing alone couldn't bind function params like exceptionId to
ToleranceException — the naming convention (toleranceExceptionId)
didn't match. Now fetch_graph() also downloads mapping.yarrrml.yml
and parses subject templates to extract the authoritative key property
per entity (e.g., ToleranceException → exceptionId, PurchaseOrder → poId).

Verified E2E: coded agent now produces 5 frontier findings (was 0
without YARRRML keys). All 7 entity types get correct function dispatch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Documents all known limitations: regex-based OFN parser (POC only),
YARRRML key binding dependency, hardcoded ID prefix conventions,
namespace constraint, Ontop transitive property gap, and the path
to production (server-side graph endpoint).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@0xr3ngar
0xr3ngar self-requested a review July 17, 2026 07:59

@0xr3ngar 0xr3ngar left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leaving this comment, so people don't merge until I can take a look

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants