참고소스 수정본
This commit is contained in:
248
참고/ontocast-main/ontocast/stategraph/atomic.py
Normal file
248
참고/ontocast-main/ontocast/stategraph/atomic.py
Normal file
@@ -0,0 +1,248 @@
|
||||
"""Reusable per-unit render/critic retry loops.
|
||||
|
||||
These loops are designed for map/reduce execution where each content unit
|
||||
is processed independently. They deep-copy the incoming unit state, then run
|
||||
render -> critic until success or retry exhaustion.
|
||||
|
||||
Minimal tools contract:
|
||||
- A ``ToolBox`` instance is still expected by type, but the loop itself only
|
||||
relies on downstream agent calls that resolve an LLM via
|
||||
``tools.get_llm_tool(state.budget_tracker)``.
|
||||
- No triple-store, chunker, converter, or aggregator capabilities are required
|
||||
for these atomic loops.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from ontocast.agent.criticise_facts import criticise_facts
|
||||
from ontocast.agent.criticise_ontology import criticise_ontology
|
||||
from ontocast.agent.external_evidence import (
|
||||
fetch_external_evidence_for_node,
|
||||
plan_external_evidence_for_node,
|
||||
)
|
||||
from ontocast.agent.render_facts import render_facts
|
||||
from ontocast.agent.render_ontology import render_ontology
|
||||
from ontocast.onto.enum import Status, WorkflowNode
|
||||
from ontocast.onto.model import ExternalEvidenceCacheEntry, ExternalEvidenceRequest
|
||||
from ontocast.onto.unit_states import UnitFactsState, UnitOntologyState
|
||||
from ontocast.tool.atomic import AtomicToolBox
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _resolve_max_visits_limit(state_visits: int, override: int | None) -> int:
|
||||
"""Return a safe visit limit while respecting explicit overrides."""
|
||||
visits = state_visits if override is None else override
|
||||
return max(1, visits)
|
||||
|
||||
|
||||
def _reset_node_evidence_context(
|
||||
state: UnitFactsState | UnitOntologyState, node: WorkflowNode
|
||||
) -> None:
|
||||
"""Start node execution in no-search mode with empty evidence context."""
|
||||
state.set_external_evidence_request(node, ExternalEvidenceRequest())
|
||||
state.set_external_evidence_cache_entry(node, ExternalEvidenceCacheEntry())
|
||||
state.load_external_evidence_for_node(node)
|
||||
|
||||
|
||||
async def facts_loop(
|
||||
state: UnitFactsState, tools: AtomicToolBox, max_visits_per_node: int | None = None
|
||||
) -> UnitFactsState:
|
||||
"""Run facts render/critic loop for one content unit.
|
||||
|
||||
Ontology is selected once per document in the main workflow; ontology_snapshot
|
||||
is always provided by the caller.
|
||||
"""
|
||||
unit_state = state.model_copy(deep=True)
|
||||
max_visits = _resolve_max_visits_limit(
|
||||
unit_state.max_visits_per_node, max_visits_per_node
|
||||
)
|
||||
unit_state.max_visits_per_node = max_visits
|
||||
|
||||
for render_attempt in range(1, max_visits + 1):
|
||||
unit_state.node_visits[WorkflowNode.TEXT_TO_FACTS] += 1
|
||||
_reset_node_evidence_context(unit_state, WorkflowNode.TEXT_TO_FACTS)
|
||||
unit_state = await render_facts(unit_state, tools)
|
||||
if unit_state.status != Status.SUCCESS:
|
||||
render_request = unit_state.get_external_evidence_request(
|
||||
WorkflowNode.TEXT_TO_FACTS
|
||||
)
|
||||
if render_request.initiate_search:
|
||||
unit_state = await plan_external_evidence_for_node(
|
||||
unit_state, tools, WorkflowNode.TEXT_TO_FACTS
|
||||
)
|
||||
unit_state = await fetch_external_evidence_for_node(
|
||||
unit_state, tools, WorkflowNode.TEXT_TO_FACTS
|
||||
)
|
||||
unit_state = await render_facts(unit_state, tools)
|
||||
if unit_state.status == Status.SUCCESS:
|
||||
logger.info(
|
||||
"Unit facts render recovered with search at attempt %s/%s",
|
||||
render_attempt,
|
||||
max_visits,
|
||||
)
|
||||
# Continue to critic tier below.
|
||||
else:
|
||||
logger.info(
|
||||
"Unit facts render failed at attempt %s/%s (with search)",
|
||||
render_attempt,
|
||||
max_visits,
|
||||
)
|
||||
continue
|
||||
else:
|
||||
logger.info(
|
||||
"Unit facts render failed at attempt %s/%s (no search request)",
|
||||
render_attempt,
|
||||
max_visits,
|
||||
)
|
||||
continue
|
||||
|
||||
for critic_attempt in range(1, max_visits + 1):
|
||||
unit_state.node_visits[WorkflowNode.CRITICISE_FACTS] += 1
|
||||
_reset_node_evidence_context(unit_state, WorkflowNode.CRITICISE_FACTS)
|
||||
unit_state = await criticise_facts(unit_state, tools)
|
||||
if unit_state.status == Status.SUCCESS:
|
||||
logger.info(
|
||||
"Unit facts loop converged at render %s/%s critic %s/%s",
|
||||
render_attempt,
|
||||
max_visits,
|
||||
critic_attempt,
|
||||
max_visits,
|
||||
)
|
||||
return unit_state
|
||||
|
||||
critic_request = unit_state.get_external_evidence_request(
|
||||
WorkflowNode.CRITICISE_FACTS
|
||||
)
|
||||
if not critic_request.initiate_search:
|
||||
logger.info(
|
||||
"Unit facts critic failed at render %s/%s critic %s/%s without search request",
|
||||
render_attempt,
|
||||
max_visits,
|
||||
critic_attempt,
|
||||
max_visits,
|
||||
)
|
||||
break
|
||||
|
||||
unit_state = await plan_external_evidence_for_node(
|
||||
unit_state, tools, WorkflowNode.CRITICISE_FACTS
|
||||
)
|
||||
unit_state = await fetch_external_evidence_for_node(
|
||||
unit_state, tools, WorkflowNode.CRITICISE_FACTS
|
||||
)
|
||||
unit_state = await criticise_facts(unit_state, tools)
|
||||
if unit_state.status == Status.SUCCESS:
|
||||
logger.info(
|
||||
"Unit facts loop converged with critic search at render %s/%s critic %s/%s",
|
||||
render_attempt,
|
||||
max_visits,
|
||||
critic_attempt,
|
||||
max_visits,
|
||||
)
|
||||
return unit_state
|
||||
|
||||
continue
|
||||
|
||||
logger.info("Unit facts loop exhausted retries")
|
||||
return unit_state
|
||||
|
||||
|
||||
async def ontology_loop(
|
||||
state: UnitOntologyState,
|
||||
tools: AtomicToolBox,
|
||||
max_visits_per_node: int | None = None,
|
||||
) -> UnitOntologyState:
|
||||
"""Run ontology render/critic loop for one content unit.
|
||||
|
||||
Ontology is selected once per document in the main workflow; ontology_snapshot
|
||||
is always provided by the caller (may be null for fresh-ontology builds).
|
||||
"""
|
||||
unit_state = state.model_copy(deep=True)
|
||||
max_visits = _resolve_max_visits_limit(
|
||||
unit_state.max_visits_per_node, max_visits_per_node
|
||||
)
|
||||
unit_state.max_visits_per_node = max_visits
|
||||
|
||||
for render_attempt in range(1, max_visits + 1):
|
||||
unit_state.node_visits[WorkflowNode.TEXT_TO_ONTOLOGY] += 1
|
||||
_reset_node_evidence_context(unit_state, WorkflowNode.TEXT_TO_ONTOLOGY)
|
||||
unit_state = await render_ontology(unit_state, tools)
|
||||
if unit_state.status != Status.SUCCESS:
|
||||
render_request = unit_state.get_external_evidence_request(
|
||||
WorkflowNode.TEXT_TO_ONTOLOGY
|
||||
)
|
||||
if render_request.initiate_search:
|
||||
unit_state = await plan_external_evidence_for_node(
|
||||
unit_state, tools, WorkflowNode.TEXT_TO_ONTOLOGY
|
||||
)
|
||||
unit_state = await fetch_external_evidence_for_node(
|
||||
unit_state, tools, WorkflowNode.TEXT_TO_ONTOLOGY
|
||||
)
|
||||
unit_state = await render_ontology(unit_state, tools)
|
||||
if unit_state.status == Status.SUCCESS:
|
||||
logger.info(
|
||||
"Unit ontology render recovered with search at attempt %s/%s",
|
||||
render_attempt,
|
||||
max_visits,
|
||||
)
|
||||
else:
|
||||
logger.info(
|
||||
"Unit ontology render failed at attempt %s/%s (with search)",
|
||||
render_attempt,
|
||||
max_visits,
|
||||
)
|
||||
continue
|
||||
else:
|
||||
logger.info(
|
||||
"Unit ontology render failed at attempt %s/%s (no search request)",
|
||||
render_attempt,
|
||||
max_visits,
|
||||
)
|
||||
continue
|
||||
|
||||
for critic_attempt in range(1, max_visits + 1):
|
||||
unit_state.node_visits[WorkflowNode.CRITICISE_ONTOLOGY] += 1
|
||||
_reset_node_evidence_context(unit_state, WorkflowNode.CRITICISE_ONTOLOGY)
|
||||
unit_state = await criticise_ontology(unit_state, tools)
|
||||
if unit_state.status == Status.SUCCESS:
|
||||
logger.info(
|
||||
"Unit ontology loop converged at render %s/%s critic %s/%s",
|
||||
render_attempt,
|
||||
max_visits,
|
||||
critic_attempt,
|
||||
max_visits,
|
||||
)
|
||||
return unit_state
|
||||
|
||||
critic_request = unit_state.get_external_evidence_request(
|
||||
WorkflowNode.CRITICISE_ONTOLOGY
|
||||
)
|
||||
if not critic_request.initiate_search:
|
||||
logger.info(
|
||||
"Unit ontology critic failed at render %s/%s critic %s/%s without search request",
|
||||
render_attempt,
|
||||
max_visits,
|
||||
critic_attempt,
|
||||
max_visits,
|
||||
)
|
||||
break
|
||||
|
||||
unit_state = await plan_external_evidence_for_node(
|
||||
unit_state, tools, WorkflowNode.CRITICISE_ONTOLOGY
|
||||
)
|
||||
unit_state = await fetch_external_evidence_for_node(
|
||||
unit_state, tools, WorkflowNode.CRITICISE_ONTOLOGY
|
||||
)
|
||||
unit_state = await criticise_ontology(unit_state, tools)
|
||||
if unit_state.status == Status.SUCCESS:
|
||||
logger.info(
|
||||
"Unit ontology loop converged with critic search at render %s/%s critic %s/%s",
|
||||
render_attempt,
|
||||
max_visits,
|
||||
critic_attempt,
|
||||
max_visits,
|
||||
)
|
||||
return unit_state
|
||||
|
||||
logger.info("Unit ontology loop exhausted retries")
|
||||
return unit_state
|
||||
Reference in New Issue
Block a user