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'
|
||||
system_prompt_file: str = ''
|
||||
tools: list = []
|
||||
auto_rag: bool = True # set False on knowledge-only agents to prevent self-calls
|
||||
|
||||
def __init__(self, odoo, llm, peer_bus=None):
|
||||
self._odoo = odoo
|
||||
@@ -92,6 +93,34 @@ class BaseAgent(ABC):
|
||||
validate_agent_tools(self.tools, self.name)
|
||||
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:
|
||||
self._directive = directive
|
||||
self._gathered = {}
|
||||
@@ -99,6 +128,10 @@ class BaseAgent(ABC):
|
||||
t0 = time.monotonic()
|
||||
try:
|
||||
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()
|
||||
await self._gather(plan)
|
||||
reasoning = await self._reason()
|
||||
@@ -164,6 +197,19 @@ class BaseAgent(ABC):
|
||||
|
||||
async def _loop(self, messages, tools=None, max_iter=10) -> str:
|
||||
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
|
||||
for iteration in range(max_iter):
|
||||
resp = await self._llm.submit(current, tools=active_tools, caller=self.name)
|
||||
|
||||
@@ -30,6 +30,7 @@ class OdooDocAgent(BaseAgent):
|
||||
required_odoo_module = 'base'
|
||||
system_prompt_file = ''
|
||||
tools = []
|
||||
auto_rag = False # prevent self-referential PeerBus calls
|
||||
|
||||
async def _plan(self) -> dict:
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user