feat: auto-inject Odoo workflow context into every agent execution
BaseAgent._lookup_odoo_context() calls odoo_doc_agent via PeerBus before _plan() runs on every directive. The RAG answer is stored in self._gathered['odoo_context'] and injected into every _loop() LLM call so agents reason with correct Odoo 18 workflow steps automatically. No changes required to individual agents. odoo_doc_agent opts out via auto_rag=False to prevent self-referential calls. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -81,6 +81,7 @@ class BaseAgent(ABC):
|
|||||||
required_odoo_module: str = 'base'
|
required_odoo_module: str = 'base'
|
||||||
system_prompt_file: str = ''
|
system_prompt_file: str = ''
|
||||||
tools: list = []
|
tools: list = []
|
||||||
|
auto_rag: bool = True # set False on knowledge-only agents to prevent self-calls
|
||||||
|
|
||||||
def __init__(self, odoo, llm, peer_bus=None):
|
def __init__(self, odoo, llm, peer_bus=None):
|
||||||
self._odoo = odoo
|
self._odoo = odoo
|
||||||
@@ -92,6 +93,34 @@ class BaseAgent(ABC):
|
|||||||
validate_agent_tools(self.tools, self.name)
|
validate_agent_tools(self.tools, self.name)
|
||||||
self._validator = ToolCallValidator(self.tools)
|
self._validator = ToolCallValidator(self.tools)
|
||||||
|
|
||||||
|
async def _lookup_odoo_context(self) -> str:
|
||||||
|
"""
|
||||||
|
Auto-fetch Odoo 18 workflow guidance from odoo_doc_agent via PeerBus.
|
||||||
|
Result is stored in self._gathered['odoo_context'] and injected into
|
||||||
|
every _loop() LLM call so agents reason with correct Odoo steps.
|
||||||
|
Returns empty string if unavailable (non-fatal).
|
||||||
|
"""
|
||||||
|
if not self.auto_rag or not self._peer_bus or not self._directive:
|
||||||
|
return ''
|
||||||
|
module = self.domain if self.domain not in ('base', 'documentation') else None
|
||||||
|
try:
|
||||||
|
response = await self._peer_bus.request(
|
||||||
|
from_agent=self.name,
|
||||||
|
to_agent='odoo_doc_agent',
|
||||||
|
request_type='query_docs',
|
||||||
|
params={
|
||||||
|
'question': self._directive.task,
|
||||||
|
'module': module,
|
||||||
|
'top_k': 4,
|
||||||
|
},
|
||||||
|
reason='auto workflow guidance lookup',
|
||||||
|
)
|
||||||
|
if response.available and response.success:
|
||||||
|
return response.data.get('answer', '')
|
||||||
|
except Exception as exc:
|
||||||
|
logger.debug('agent=%s auto-RAG skipped: %s', self.name, exc)
|
||||||
|
return ''
|
||||||
|
|
||||||
async def execute(self, directive: AgentDirective) -> AgentReport:
|
async def execute(self, directive: AgentDirective) -> AgentReport:
|
||||||
self._directive = directive
|
self._directive = directive
|
||||||
self._gathered = {}
|
self._gathered = {}
|
||||||
@@ -99,6 +128,10 @@ class BaseAgent(ABC):
|
|||||||
t0 = time.monotonic()
|
t0 = time.monotonic()
|
||||||
try:
|
try:
|
||||||
await self._receive(directive)
|
await self._receive(directive)
|
||||||
|
odoo_ctx = await self._lookup_odoo_context()
|
||||||
|
if odoo_ctx:
|
||||||
|
self._gathered['odoo_context'] = odoo_ctx
|
||||||
|
logger.info('agent=%s odoo_context=%d chars injected', self.name, len(odoo_ctx))
|
||||||
plan = await self._plan()
|
plan = await self._plan()
|
||||||
await self._gather(plan)
|
await self._gather(plan)
|
||||||
reasoning = await self._reason()
|
reasoning = await self._reason()
|
||||||
@@ -164,6 +197,19 @@ class BaseAgent(ABC):
|
|||||||
|
|
||||||
async def _loop(self, messages, tools=None, max_iter=10) -> str:
|
async def _loop(self, messages, tools=None, max_iter=10) -> str:
|
||||||
current = list(messages)
|
current = list(messages)
|
||||||
|
|
||||||
|
# Inject Odoo workflow context retrieved during execute()
|
||||||
|
odoo_ctx = self._gathered.get('odoo_context', '')
|
||||||
|
if odoo_ctx:
|
||||||
|
inject = [
|
||||||
|
{'role': 'user', 'content': f'## Odoo 18 Workflow Reference\n\n{odoo_ctx}'},
|
||||||
|
{'role': 'assistant', 'content': 'Understood. I will follow the documented Odoo 18 workflow steps.'},
|
||||||
|
]
|
||||||
|
if current and current[0].get('role') == 'system':
|
||||||
|
current = [current[0]] + inject + current[1:]
|
||||||
|
else:
|
||||||
|
current = inject + current
|
||||||
|
|
||||||
active_tools = tools or self.tools
|
active_tools = tools or self.tools
|
||||||
for iteration in range(max_iter):
|
for iteration in range(max_iter):
|
||||||
resp = await self._llm.submit(current, tools=active_tools, caller=self.name)
|
resp = await self._llm.submit(current, tools=active_tools, caller=self.name)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class OdooDocAgent(BaseAgent):
|
|||||||
required_odoo_module = 'base'
|
required_odoo_module = 'base'
|
||||||
system_prompt_file = ''
|
system_prompt_file = ''
|
||||||
tools = []
|
tools = []
|
||||||
|
auto_rag = False # prevent self-referential PeerBus calls
|
||||||
|
|
||||||
async def _plan(self) -> dict:
|
async def _plan(self) -> dict:
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user