fix: auto-activate registered agents with descriptive capabilities
The master agent was routing expense/receipt requests to finance_agent instead of expenses_agent because only DB-registered agents appeared in get_active_agents(). This adds auto-activation of all in-memory registered agents with precise capability summaries so the LLM picks the right specialist. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,21 @@ import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Default capability descriptions used when the Odoo DB registry has no entry
|
||||
# for an agent. These drive the master-agent's intent classification, so they
|
||||
# must be specific enough for the LLM to pick the right agent.
|
||||
_DEFAULT_CAPABILITIES = {
|
||||
'expenses_agent': 'Employee expense reports and receipts — create, submit, and approve hr.expense records',
|
||||
'finance_agent': 'Financial reporting — P&L, balance sheet, cash flow, budgets',
|
||||
'accounting_agent': 'Accounting — journal entries, invoices, vendor bills, payments, reconciliation',
|
||||
'crm_agent': 'CRM — leads, opportunities, pipeline stages, customer interactions',
|
||||
'sales_agent': 'Sales — orders, quotations, customers, pricelists',
|
||||
'project_agent': 'Projects — tasks, timesheets, milestones, deadlines',
|
||||
'elearning_agent': 'eLearning — courses, slides, attendees, certifications',
|
||||
'employees_agent': 'HR — employees, contracts, attendance, leave requests',
|
||||
'odoo_doc_agent': 'Odoo 18 documentation and workflow guidance (internal use)',
|
||||
}
|
||||
|
||||
|
||||
class AgentRegistry:
|
||||
def __init__(self):
|
||||
@@ -17,17 +32,34 @@ class AgentRegistry:
|
||||
[['active', '=', True]],
|
||||
['agent_name', 'domain', 'backend'])
|
||||
self._active = {r['agent_name'] for r in rows}
|
||||
self._capabilities = {r['agent_name']: r.get('domain', '') for r in rows}
|
||||
logger.info('AgentRegistry loaded %d agents: %s', len(self._active), list(self._active))
|
||||
# Use the DB domain field when present, fall back to defaults
|
||||
for r in rows:
|
||||
key = r['agent_name']
|
||||
self._capabilities[key] = r.get('domain') or _DEFAULT_CAPABILITIES.get(key, key)
|
||||
logger.info('AgentRegistry loaded %d agents from DB: %s', len(self._active), list(self._active))
|
||||
except Exception as exc:
|
||||
logger.error('AgentRegistry.load_from_odoo failed: %s', exc)
|
||||
|
||||
def register(self, agent_key, agent_instance):
|
||||
self._agents[agent_key] = agent_instance
|
||||
# Auto-activate every registered agent so it appears in the master
|
||||
# prompt even when the Odoo DB registry table has no row for it.
|
||||
self._active.add(agent_key)
|
||||
if not self._capabilities.get(agent_key):
|
||||
self._capabilities[agent_key] = _DEFAULT_CAPABILITIES.get(
|
||||
agent_key,
|
||||
agent_key.replace('_agent', '').replace('_', ' ') + ' management',
|
||||
)
|
||||
logger.debug('AgentRegistry: registered %s (active=%d)', agent_key, len(self._active))
|
||||
|
||||
async def get_active_agents(self):
|
||||
return [{'agent_key': k, 'capabilities_summary': self._capabilities.get(k, '')}
|
||||
for k in self._active]
|
||||
return [
|
||||
{'agent_key': k, 'capabilities_summary': self._capabilities.get(k, '')}
|
||||
for k in self._active
|
||||
if k in self._agents # only expose agents that have a live instance
|
||||
]
|
||||
|
||||
async def get_all(self):
|
||||
"""Return all registered agents with active status and capabilities."""
|
||||
return [
|
||||
{
|
||||
'agent_key': k,
|
||||
@@ -45,8 +77,5 @@ class AgentRegistry:
|
||||
self._active = set(active_keys)
|
||||
logger.info('AgentRegistry synced: active=%s', active_keys)
|
||||
|
||||
def register(self, agent_key, agent_instance):
|
||||
self._agents[agent_key] = agent_instance
|
||||
|
||||
def get_agent_instance(self, agent_key):
|
||||
return self._agents.get(agent_key)
|
||||
|
||||
Reference in New Issue
Block a user