# Structured deliverable (/use-cases/deep-research/structured-deliverable)



Make the final pipeline step return a typed decision that downstream code can validate and use. Define the call, conviction, allocation, rationale, and citations in a Pydantic schema, then pass it as the final agent's `output_schema`.

These are composition examples adapted from the [Investment Team application](https://github.com/agno-agi/investment-team). Follow its [clone, database, credentials, and research-loading setup](https://github.com/agno-agi/investment-team#quick-start) for a complete application. Its [`agents/`](https://github.com/agno-agi/investment-team/tree/main/agents), [`teams/`](https://github.com/agno-agi/investment-team/tree/main/teams), and [`workflows/`](https://github.com/agno-agi/investment-team/tree/main/workflows) define the components referenced here; [`agents/settings.py`](https://github.com/agno-agi/investment-team/blob/main/agents/settings.py) supplies shared knowledge and paths.

Import those components into your application module, or define your own before composing them. The application uses Gemini; the OpenAI variants on these pages require `uv pip install "agno[openai]"` and `OPENAI_API_KEY` as well. Shared knowledge/storage still needs the application's database and embedding-provider setup. Prose context, analyst-output variables, and local archives must be supplied by your application.

```python
from typing import List, Literal

from agno.agent import Agent
from agno.models.openai import OpenAIResponses
from pydantic import BaseModel, Field


class Decision(BaseModel):
    call: Literal["BUY", "HOLD", "PASS"] = Field(..., description="The committee decision")
    conviction: Literal["low", "medium", "high"] = Field(..., description="Confidence in the call")
    allocation_usd: float = Field(..., description="Dollar allocation, 0 if not BUY")
    rationale: str = Field(..., description="Why, referencing the analyst inputs")
    citations: List[str] = Field(..., description="Sources and prior memos used")


chair = Agent(
    name="Committee Chair",
    model=OpenAIResponses(id="gpt-5.5"),
    output_schema=Decision,
    instructions=(
        "Synthesize the analyst inputs into a decision. Every BUY needs a "
        "dollar amount. Every decision must reference at least one risk."
    ),
)


def briefing(*analyst_outputs: str) -> str:
    return "Analyst inputs:\n\n" + "\n\n".join(analyst_outputs)


result = chair.run(briefing(market, fundamentals, technicals, risk)).content
# Decision(call='BUY', conviction='high', allocation_usd=2_000_000.0,
#          rationale='Momentum and fundamentals align; sized within the
#                      sector cap the Risk Officer set.',
#          citations=['memo:NVDA-2024Q3', 'research:semiconductors'])
```

`output_schema=Decision` requests a parsed `Decision`. If output parsing fails, `content` can remain a string. Check that the run completed and `isinstance(run.content, Decision)` before reading fields or acting on the result.

The chair weighs the specialists' inputs and commits to a call. Its configuration omits tools and supplies a briefing as context. That does not guarantee that every conclusion is supported by the briefing; validate claims against the supplied evidence.

## Decision and memo [#decision-and-memo]

A research system usually produces both a machine-actionable decision and a human-readable memo.

| Artifact | Form                           | Consumer                                 |
| -------- | ------------------------------ | ---------------------------------------- |
| Decision | Typed object (`output_schema`) | Downstream automation, dashboards, audit |
| Memo     | Markdown written to disk       | Humans, the next review's context        |

The memo is written by a dedicated agent with file tools and a fixed template, then archived. The next review reads it back as [prior work](/use-cases/deep-research/grounding-research). The decision is the row you store and act on.

## Required decision fields [#required-decision-fields]

| Field        | Purpose                                                  |
| ------------ | -------------------------------------------------------- |
| `conviction` | Lets you threshold: act on high, queue medium for review |
| `rationale`  | Records the reasoning trail for review and audit         |
| `citations`  | Carries source identifiers for downstream verification   |

The schema requires a `citations` field but allows an empty list and does not check source identifiers. Before a downstream action, application code should require the necessary citations, resolve them against the actual research, and enforce its allocation and action thresholds.

## Add approval for consequential actions [#add-approval-for-consequential-actions]

When a decision triggers a real action, such as moving capital or publishing a number, add human approval before the action executes. See [human approval](/hitl/overview).

## Next steps [#next-steps]

| Task                             | Guide                                                                     |
| -------------------------------- | ------------------------------------------------------------------------- |
| Carry the memo into the next run | [Grounding research](/use-cases/deep-research/grounding-research)         |
| Make decisions improve over time | [Institutional learning](/use-cases/deep-research/institutional-learning) |
| Serve the decision to a surface  | [Serve and embed](/use-cases/deep-research/serve-and-embed)               |

## Developer Resources [#developer-resources]

* [Structured output](/input-output/structured-output/agent)
* [Workflows cookbook](https://github.com/agno-agi/agno/tree/main/cookbook/04_workflows)
* [Human approval](/hitl/overview)
