Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 26 additions & 10 deletions implementations/boc_rate_decisions/analyst_agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@


def _build_boc_analyst_instruction() -> str:
"""Build the BoC analyst instruction, embedding the output schema from the class.
"""Build the tool-free BoC analyst instruction (payload signals only).

Web-search guidance is appended by :func:`build_boc_news_config` — never
reference a tool here that ``build_boc_basic_config`` does not attach.

Using a function instead of a static string ensures the ``## Output
schema`` block is always in sync with ``CategoricalAgentForecastOutput``
Expand Down Expand Up @@ -116,23 +119,36 @@ def _build_boc_analyst_instruction() -> str:
"strongly shape which tail outcome is plausible.\n"
"4. Use ONLY information available on or before `as_of`. Do not use "
"knowledge of what the Bank actually decided on or after "
"`announcement_date`, even if you remember it. If `search_web` returns a "
"result beginning with `[SEARCH_VERIFICATION_FAILED]`, treat it as no "
"verified news for that query — proceed on the other signals in your "
"payload and note the gap, never filling it from your own background "
"knowledge.\n"
"`announcement_date`, even if you remember it. Reason only from the "
"payload (and any tools listed in later sections of this instruction — "
"if none are listed, you have no tools).\n"
"5. Document your reasoning in `reasoning` and list the decisive inputs "
"in `key_signals` — these are compared against the Bank's own published "
"rationale by a downstream evaluator, so be specific.\n\n"
"## Output schema\n\n"
"Call `set_model_response` with a `json_response` string matching "
"**exactly**:\n\n"
"```json\n" + schema + "\n```\n"
"If a `set_model_response` tool is available, call it with a "
"`json_response` string matching **exactly**:\n\n"
"```json\n" + schema + "\n```\n\n"
"Otherwise return that JSON directly as plain text with no preamble.\n"
)


_BOC_ANALYST_INSTRUCTION = _build_boc_analyst_instruction()

# Appended only by configs that enable ContextRetrievalConfig (news).
_BOC_CONTEXT_RETRIEVAL_SUPPLEMENT = """

## Context retrieval

Call ``search_web`` to gather recent BoC communications and macro-news context \
BEFORE producing your distribution. Pass ``cutoff_date`` equal to the payload's \
``as_of`` date.

If ``search_web`` returns a result beginning with ``[SEARCH_VERIFICATION_FAILED]``, \
treat it as no verified news for that query — proceed on the other signals in your \
payload and note the gap, never filling it from your own background knowledge.
"""


# ---------------------------------------------------------------------------
# Context retrieval instruction (sub-agent) — seam for the report-grounded variant
Expand Down Expand Up @@ -335,7 +351,7 @@ def build_boc_news_config(
return AgentConfig(
name="boc_analyst_news",
model=model,
instruction=_BOC_ANALYST_INSTRUCTION,
instruction=_BOC_ANALYST_INSTRUCTION + _BOC_CONTEXT_RETRIEVAL_SUPPLEMENT,
context_retrieval=ContextRetrievalConfig(
enabled=True,
instruction=_BOC_CONTEXT_RETRIEVAL_INSTRUCTION,
Expand Down
66 changes: 43 additions & 23 deletions implementations/energy_oil_forecasting/analyst_agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,11 @@


def _build_wti_analyst_instruction() -> str:
"""Build the WTI analyst instruction, embedding the output schema from the class.
"""Build the tool-free WTI analyst instruction (price history only).

Tool-specific guidance (web search, code exec, forecast tool) is appended
by the config factories that enable those capabilities — never reference a
tool here that ``build_wti_basic_config`` does not attach.

Using a function instead of a static string ensures the ``## Output schema``
block is always in sync with ``ContinuousAgentForecastOutput`` —
Expand All @@ -111,37 +115,49 @@ def _build_wti_analyst_instruction() -> str:
"2. Use exactly the quantile levels from `standard_quantiles` — no additions, no omissions.\n"
"3. `point_forecast` must exactly equal the 0.50 quantile value.\n"
"4. Quantile values must be strictly non-decreasing as quantile levels increase.\n"
"5. Document your reasoning in the `rationale` fields.\n"
"6. When tools are enabled, conclude with `set_model_response` to return the structured forecast.\n\n"
"5. Document your reasoning in the `rationale` fields.\n\n"
"## Output schema\n\n"
"Call `set_model_response` with a `json_response` string matching **exactly**:\n\n"
"If a `set_model_response` tool is available, call it with a "
"`json_response` string matching **exactly**:\n\n"
"```json\n" + schema + "\n```\n\n"
"Otherwise return that JSON directly as plain text with no preamble.\n\n"
'Critical: use `"horizon"` (integer, not `"horizon_days"`). '
'`"quantiles"` is a **list** of `{"quantile": <level>, "value": <price>}` '
"objects — not a dict. Omit any field not shown above.\n\n"
"## Analysis discipline\n\n"
"When context retrieval is available, call ``search_web`` to gather market "
"intelligence BEFORE producing forecasts.\n\n"
"Call ``search_web`` with ``query`` and ``cutoff_date`` (set to the ``as_of`` "
"date from the payload). The ``cutoff_date`` MUST always equal ``as_of`` — "
"this is the temporal fence that prevents post-origin information from "
"contaminating historical backtests.\n\n"
"If ``search_web`` returns a result beginning with "
"``[SEARCH_VERIFICATION_FAILED]``, treat it as no verified news context for "
"that query. Do not use your own background knowledge to fill the gap or "
"speculate about what the news might have said — proceed with price-history "
"and other available signals only, and note the gap in your rationale.\n\n"
"Recommended queries (call ``search_web`` once per topic):\n"
'- ``search_web(query="WTI crude oil price trend and OPEC+ supply decisions", cutoff_date=<as_of>)``\n'
'- ``search_web(query="Persian Gulf geopolitical risk shipping lane disruptions", cutoff_date=<as_of>)``\n'
'- ``search_web(query="US Strategic Petroleum Reserve policy and global demand outlook", cutoff_date=<as_of>)``\n\n'
"Document your key assumptions (OPEC+ policy, shipping lane risk, inventory "
"levels, macro demand) in the `rationale` fields of your forecast output."
"levels, macro demand) in the `rationale` fields of your forecast output. "
"Reason only from the payload (and any tools listed in later sections of "
"this instruction — if none are listed, you have no tools)."
)


_WTI_ANALYST_INSTRUCTION = _build_wti_analyst_instruction()

# Appended only by configs that enable ContextRetrievalConfig (news / code / tool).
_CONTEXT_RETRIEVAL_SUPPLEMENT = """

## Context retrieval

Call ``search_web`` to gather market intelligence BEFORE producing forecasts.

Call ``search_web`` with ``query`` and ``cutoff_date`` (set to the ``as_of`` \
date from the payload). The ``cutoff_date`` MUST always equal ``as_of`` — \
this is the temporal fence that prevents post-origin information from \
contaminating historical backtests.

If ``search_web`` returns a result beginning with \
``[SEARCH_VERIFICATION_FAILED]``, treat it as no verified news context for \
that query. Do not use your own background knowledge to fill the gap or \
speculate about what the news might have said — proceed with price-history \
and other available signals only, and note the gap in your rationale.

Recommended queries (call ``search_web`` once per topic):
- ``search_web(query="WTI crude oil price trend and OPEC+ supply decisions", cutoff_date=<as_of>)``
- ``search_web(query="Persian Gulf geopolitical risk shipping lane disruptions", cutoff_date=<as_of>)``
- ``search_web(query="US Strategic Petroleum Reserve policy and global demand outlook", cutoff_date=<as_of>)``
"""

# ---------------------------------------------------------------------------
# Context retrieval instruction (sub-agent)
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -457,7 +473,7 @@ def build_wti_news_config(
return AgentConfig(
name="wti_analyst_news",
model=model,
instruction=_WTI_ANALYST_INSTRUCTION,
instruction=_WTI_ANALYST_INSTRUCTION + _CONTEXT_RETRIEVAL_SUPPLEMENT,
context_retrieval=ContextRetrievalConfig(
enabled=True,
instruction=_WTI_CONTEXT_RETRIEVAL_INSTRUCTION,
Expand Down Expand Up @@ -518,7 +534,9 @@ def build_wti_code_exec_config(
return AgentConfig(
name="wti_analyst_code",
model=model,
instruction=_WTI_ANALYST_INSTRUCTION + _CODE_EXEC_SKILLS_SUPPLEMENT,
instruction=(
_WTI_ANALYST_INSTRUCTION + _CONTEXT_RETRIEVAL_SUPPLEMENT + _CODE_EXEC_SKILLS_SUPPLEMENT
),
max_output_tokens=max_output_tokens,
context_retrieval=ContextRetrievalConfig(
enabled=True,
Expand Down Expand Up @@ -593,7 +611,9 @@ def build_wti_tool_config(
return AgentConfig(
name="wti_analyst_tool",
model=model,
instruction=_WTI_ANALYST_INSTRUCTION + _FORECAST_TOOL_SUPPLEMENT,
instruction=(
_WTI_ANALYST_INSTRUCTION + _CONTEXT_RETRIEVAL_SUPPLEMENT + _FORECAST_TOOL_SUPPLEMENT
),
context_retrieval=ContextRetrievalConfig(
enabled=True,
instruction=_WTI_CONTEXT_RETRIEVAL_INSTRUCTION,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ from pydantic import BaseModel


def _build_boc_analyst_instruction() -> str:
"""Build the BoC analyst instruction, embedding the output schema from the class.
"""Build the tool-free BoC analyst instruction (payload signals only).

Web-search guidance is appended by :func:`build_boc_news_config` — never
reference a tool here that ``build_boc_basic_config`` does not attach.

Using a function instead of a static string ensures the ``## Output
schema`` block is always in sync with ``CategoricalAgentForecastOutput``
Expand Down Expand Up @@ -121,19 +124,36 @@ def _build_boc_analyst_instruction() -> str:
"strongly shape which tail outcome is plausible.\n"
"4. Use ONLY information available on or before `as_of`. Do not use "
"knowledge of what the Bank actually decided on or after "
"`announcement_date`, even if you remember it.\n"
"`announcement_date`, even if you remember it. Reason only from the "
"payload (and any tools listed in later sections of this instruction — "
"if none are listed, you have no tools).\n"
"5. Document your reasoning in `reasoning` and list the decisive inputs "
"in `key_signals` — these are compared against the Bank's own published "
"rationale by a downstream evaluator, so be specific.\n\n"
"## Output schema\n\n"
"Call `set_model_response` with a `json_response` string matching "
"**exactly**:\n\n"
"```json\n" + schema + "\n```\n"
"If a `set_model_response` tool is available, call it with a "
"`json_response` string matching **exactly**:\n\n"
"```json\n" + schema + "\n```\n\n"
"Otherwise return that JSON directly as plain text with no preamble.\n"
)


_BOC_ANALYST_INSTRUCTION = _build_boc_analyst_instruction()

# Appended only by configs that enable ContextRetrievalConfig (news).
_BOC_CONTEXT_RETRIEVAL_SUPPLEMENT = """

## Context retrieval

Call ``search_web`` to gather recent BoC communications and macro-news context \
BEFORE producing your distribution. Pass ``cutoff_date`` equal to the payload's \
``as_of`` date.

If ``search_web`` returns a result beginning with ``[SEARCH_VERIFICATION_FAILED]``, \
treat it as no verified news for that query — proceed on the other signals in your \
payload and note the gap, never filling it from your own background knowledge.
"""


# ---------------------------------------------------------------------------
# Context retrieval instruction (sub-agent) — seam for the report-grounded variant
Expand All @@ -152,7 +172,16 @@ markdown summary (3-5 paragraphs) covering relevant aspects of:

Ground your summary in the search results you actually retrieve. \
When a cutoff date is specified, do not report or speculate about events \
that occurred after that date.\
that occurred after that date.

Before finalizing your summary, reason step by step: (1) for each candidate \
fact, judge its actual recency from the substance of the result itself, \
never from a source's claimed publish date or byline timestamp — those are \
frequently stale or updated after original publication; (2) discard \
anything you cannot confidently place before the cutoff date; (3) only then \
write your summary. Do not supplement the search results with your own \
background/training knowledge — if the results are insufficient, say so \
explicitly rather than filling gaps from memory.\
"""


Expand Down Expand Up @@ -327,7 +356,7 @@ def build_boc_news_config(
return AgentConfig(
name="boc_analyst_news",
model=model,
instruction=_BOC_ANALYST_INSTRUCTION,
instruction=_BOC_ANALYST_INSTRUCTION + _BOC_CONTEXT_RETRIEVAL_SUPPLEMENT,
context_retrieval=ContextRetrievalConfig(
enabled=True,
instruction=_BOC_CONTEXT_RETRIEVAL_INSTRUCTION,
Expand Down
Loading
Loading