Skip to content
Open
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
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@ All notable changes to Raven are documented here.

### Added

- The reasoning effort can be set **per conversation**: the *Reasoning
effort* row of the WebUI's model picker, beside the sub-agent tier, and
`/effort <minimal|low|medium|high|default>` in the TUI (`/effort` alone
reports, `default` returns to the global setting). The pick outranks
`agents.defaults.reasoningEffort` and the conversation's mode, is kept on the
conversation's own record so it survives a gateway restart and comes back on
resume, lands on the next turn, and moves Raven's own model calls only.
New RPC `session.set_effort` (report, set or clear; every answer names what
the default is on this install and the words), and `session.info` carries
`reasoning_effort` for the status bar. The using-raven guide has a section on
it. Clearing `agents.defaults.reasoningEffort` (*Provider default*) reaches a
running gateway's next turn as well: the turn sends the effort in force on
every call, the provider's own default included, instead of leaving a
cleared key to the copy the provider took at boot. A fork keeps its
parent's pick with its model; the agent's own `raven_config` door accepts
null for `agents.defaults.reasoningEffort` as the settings page does; and
inside a TUI direct chat with an instance `/effort` is refused rather than
moving the main conversation while saying "this conversation".

- The settings dialog's **Skills** and **Plugins** pages have an **Add**
button. Neither page can install anything -- a skill is fetched from Skill
Hub by `use_skill` and an MCP server is connected from the PlugHub catalogue
Expand Down Expand Up @@ -483,6 +502,12 @@ All notable changes to Raven are documented here.

### Fixed

- The settings page showed the reasoning effort as **Low** when the key was
not set at all, while the provider's own default was what ran. The unset
state is now its own rung, *Provider default*, which also clears the key
(`agents.defaults.reasoningEffort` accepts null) rather than leaving a word
the install never chose.

- On native Windows a DAG node can read files by reference: `{{ ref:... }}`,
`{{ ref_path:... }}` and a `{"file": ...}` input naming a file in the
session's working directory or its sub-agent history. They were resolved by
Expand Down
26 changes: 26 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,32 @@ _Avoid_: putting the scope of the control on the rungs. Each row says only what
distinguishes it; that raven's own effort is unchanged is stated once by whichever surface
draws the control.

**Session Reasoning Effort** (`agent/session_effort.py`; `session.set_effort` over RPC, `/effort` in
the TUI, the effort row of the WebUI's model picker):
The reasoning effort one session picked for its own model calls, kept on the session's
metadata beside `model` (`reasoning_effort`; null once cleared, since a metadata patch sets
keys and never removes one) rather than on the loop's in-memory policy, so it outlives the
gateway and a resumed conversation runs at what it was left at, and a fork carries it with
the model (`FORK_CARRIES` in `session/manager.py`). `_run_agent_loop` is handed
it by the turn entrance that holds the record, the way the model is, and ranks it: the
configured `agents.defaults.reasoningEffort` underneath, a Session Mode's own
`reasoningEffort` over that, this pick over both, a hook's rollback override on top. The
ranking is `session_effort.in_force`, one rule with one home: the turn runs at it
(`session_reasoning_effort` on the loop) and `session.info` / `session.set_effort` report
it, and the turn sends the word in force on every call, None included, so a default cleared
on a running gateway lands on its next turn -- the provider's own copy of the key, frozen at
boot, is never consulted. The
words are `REASONING_EFFORTS` (`config/schema.py`), shared with the console's checker for
`agents.defaults.reasoningEffort` and with the agent's own `raven_config` door -- a key that
is clearable on both (null is the provider's own default) and that the settings page draws,
when unset, as a rung of its own rather than as `low`. Lands on the next turn, like a tier;
reaches no sub-agent, unlike one -- a sub-agent is still told the configured default
(`RAVEN_PARENT_REASONING_EFFORT`), and the TUI's `/effort` is refused inside a direct chat
with an instance, since the pick is the main conversation's and an instance has none.
_Avoid_: reading the pick off `SessionPolicy`. A mode switch replaces the whole policy and a
restart empties it; the record is the pick's one home, and the policy carries only the
mode's own effort.

**Subagent** (`agent/subagent/`):
A background agent task spawned by `SubagentManager`. Runs with its own tool set; its result
re-enters the session as a `SUBAGENT`-origin `TurnRequest` via Spine submit. Bounded by
Expand Down
1 change: 1 addition & 0 deletions docs-site/docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ on a specific command. From a source checkout, prefix commands with `uv run`.
| Task | Read |
| --- | --- |
| Choose a conversation or one-shot workflow | [Using Raven](using-raven.md) |
| Set how much the model thinks, globally or per conversation | [Using Raven](using-raven.md#set-the-reasoning-effort) |
| Configure shipped or external agents | [Agent Integrations](agent-integrations.md) |
| Continue or steer a specialist task | [Working with sub-agents](agent-collaboration.md) |
| Share browser work with the agent | [Browser collaboration](browser-collaboration.md) |
Expand Down
1 change: 1 addition & 0 deletions docs-site/docs/commands.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
| 任务 | 阅读 |
| --- | --- |
| 选择对话或单次工作流 | [使用 Raven](using-raven.md) |
| 设置模型的思考深度(全局或单个对话) | [使用 Raven](using-raven.md#set-the-reasoning-effort) |
| 配置随 Raven 发布或外部 Agent | [Agent 集成](agent-integrations.md) |
| 继续或引导专用任务 | [与子 Agent 协作](agent-collaboration.md) |
| 与 Agent 共享浏览器工作 | [浏览器协作](browser-collaboration.md) |
Expand Down
27 changes: 27 additions & 0 deletions docs-site/docs/using-raven.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,33 @@ Use the UI's conversation picker for interactive sessions. A sub-agent
`instance` continues that child's conversation; it is not the host session id
or a DAG node id. See [DAG Orchestration](orchestration.md).

## Set the reasoning effort

The reasoning effort is how much the model thinks before each answer. The
words are `minimal`, `low`, `medium` and `high`. Each provider maps them to its
own request (Anthropic's adaptive effort and thinking budgets, the
`reasoning_effort` field of OpenAI-style APIs), and a model with no thinking to
move ignores them. Left unset, the provider's own default applies.

Two places set it:

- **For every conversation**: in the WebUI, open Settings, then *Parameters* on
the *Chat* row, then *Reasoning effort*; or set
`agents.defaults.reasoningEffort` in `~/.raven/config.json`. The first rung,
*Provider default*, clears the key.
- **For one conversation**: the *Reasoning effort* row of the model picker in
the WebUI (open it from the model chip beside the composer), or `/effort
<word>` in the TUI. `/effort` alone reports the effort in force, and
`/effort default` returns the conversation to the global setting. Inside a
direct chat with a sub-agent instance the command is refused: the pick is
the main conversation's, and an instance has none of its own.

A pick made for one conversation outranks the global setting and the
conversation's mode. It is kept on the conversation's own record, so it survives
a gateway restart and comes back when the conversation is resumed, and it lands
on the next turn. It moves Raven's own model calls only; sub-agents keep the
configured default.

## Add capability deliberately

Use a skill for reusable instructions, MCP for external tools, and a plugin for
Expand Down
20 changes: 20 additions & 0 deletions docs-site/docs/using-raven.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,26 @@ raven agent --continue -m "Expand the previous summary; do not modify files"
交互式会话通过 UI 的对话选择器切换。子 Agent 的 `instance` 续接子 Agent 对话,
不是宿主 session id 或 DAG node id,详见[DAG 编排](orchestration.md)。

## 设置思考深度 { #set-the-reasoning-effort }

思考深度是模型每次回答前思考多少。可选的词是 `minimal`、`low`、`medium`、`high`。
各厂商把它们映射成自己的请求参数(Anthropic 的自适应思考强度与思考预算,OpenAI 风格
接口的 `reasoning_effort` 字段),没有思考能力的模型会忽略它。不设置时使用厂商自己的默认值。

两处可以设置:

- **对所有对话**:在 WebUI 打开设置页,在「主对话」一行点「参数」,再选「思考深度」;
或在 `~/.raven/config.json` 里设置 `agents.defaults.reasoningEffort`。第一档
「厂商默认」会清掉这个键。
- **只对当前对话**:WebUI 模型选择器(点输入框旁的模型标签打开)里的「思考深度」行,
或 TUI 里的 `/effort <词>`。单独输入 `/effort` 查看当前生效的档位,`/effort default`
让这个对话重新跟随全局设置。在与某个子智能体实例直聊时这条命令会被拒绝:这个选择
属于主对话,实例自己没有思考深度可设。

单个对话的选择优先于全局设置和该对话的模式。它记录在对话自己的档案里,网关重启后仍在,
恢复对话时一并带回,从下一轮开始生效。它只影响 Raven 自己的模型调用;子智能体仍按全局
默认值运行。

## 有目的地增加能力 { #add-capability-deliberately }

可复用指令用 skill,外部工具用 MCP,运行时贡献用 plugin。需要独立身份和执行配置
Expand Down
58 changes: 56 additions & 2 deletions i18n/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,16 @@
"help": "查看或设置推理强度(对运行中的 agent 生效)"
}
},
"effort": {
"en": {
"name": "effort",
"help": "show or set how much the model thinks for this conversation [minimal|low|medium|high|default]"
},
"zh": {
"name": "思考深度",
"help": "查看或设置本会话的思考深度 [minimal|low|medium|high|default]"
}
},
"fast": {
"zh": {
"name": "快速模式",
Expand Down Expand Up @@ -6158,8 +6168,8 @@
"zh": "模式"
},
"gui.tier.scope": {
"en": "Reasoning depth, sub-agent mode and the rest follow this setting. This conversation only.",
"zh": "模型思考深度、子智能体运行强度等都跟着这一档走。只作用于本会话。"
"en": "The tier the sub-agents this conversation dispatches run at. This conversation only.",
"zh": "派出的子智能体按这一档运行。只作用于本会话。"
},
"gui.tier.failed": {
"en": "Could not switch to {name}",
Expand All @@ -6173,6 +6183,46 @@
"en": "This is a deployment's own mode, not a built-in tier, so it is not passed on to sub-agents.",
"zh": "这是部署自带的模式,不是内置档位,因此不会下发给子智能体。"
},
"gui.effort.title": {
"en": "Reasoning effort",
"zh": "思考深度"
},
"gui.effort.default": {
"en": "Default",
"zh": "默认"
},
"gui.effort.minimal": {
"en": "Minimal",
"zh": "最少"
},
"gui.effort.low": {
"en": "Low",
"zh": "低"
},
"gui.effort.medium": {
"en": "Medium",
"zh": "中"
},
"gui.effort.high": {
"en": "High",
"zh": "高"
},
"gui.effort.scope": {
"en": "How much the model thinks before each answer, for this conversation only. Sub-agents are not affected.",
"zh": "模型每次回答前思考多少,只作用于本会话,不影响子智能体。"
},
"gui.effort.now": {
"en": "Default follows the global setting, which is {name} right now.",
"zh": "「默认」跟随设置页里的全局值,当前是「{name}」。"
},
"gui.effort.provider": {
"en": "the provider's own default",
"zh": "厂商默认"
},
"gui.effort.failed": {
"en": "Could not set the reasoning effort to {name}",
"zh": "思考深度切到「{name}」没有成功"
},
"gui.imodel.title": {
"en": "Model",
"zh": "模型"
Expand Down Expand Up @@ -8581,6 +8631,10 @@
"en": "Reasoning effort",
"zh": "思考深度"
},
"gui.settings.roles.effort_default": {
"en": "Provider default",
"zh": "厂商默认"
},
"gui.settings.roles.effort_minimal": {
"en": "Minimal",
"zh": "最少"
Expand Down
11 changes: 6 additions & 5 deletions raven/agent/loop/organ_glue.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ async def _report_skill_read(self, session_key: str, tool_name: str, args: dict[
# CancelledError is not an Exception and still propagates.
_WATCH_JUDGEMENT_TIMEOUT_S: float = 60.0

async def _judge_watch_work(self, message: str, effort_kwargs: dict[str, str]) -> Any:
async def _judge_watch_work(self, message: str, effort_kwargs: dict[str, str | None]) -> Any:
"""The turn's watch-work verdict, a quiet no when the model does not answer."""
import asyncio

Expand Down Expand Up @@ -582,10 +582,11 @@ async def _note_watch_work(
) -> str:
"""The steering line this look earned, or "". The CALLER places it.

``reasoning_effort`` is the turn's pinned effort: the judgement this
``reasoning_effort`` is the effort the turn runs at: the judgement this
helper may pay for is a model call of the turn like any other, so it
asks for the session's effort too. ``None`` passes nothing and the
provider's configured default stands.
asks for the same one, sent the same way -- None included, which is
the provider's own default rather than the copy the provider took at
boot (``_run_agent_loop``'s ``gen_overrides`` says why).

Returned separately rather than appended to ``result`` because of where
the result goes next: every tool result is fenced as untrusted data
Expand All @@ -611,7 +612,7 @@ async def _note_watch_work(
reasoning, and ignored, while a fact arriving in a tool result was acted
on.
"""
effort_kwargs: dict[str, str] = {} if reasoning_effort is None else {"reasoning_effort": reasoning_effort}
effort_kwargs: dict[str, str | None] = {"reasoning_effort": reasoning_effort}
# Only a real hand-off answers the question this line asks for the rest
# of the turn. The first cut treated every spawn as one and silenced the
# nudges on refusal too -- at exactly the moment the model chose to run
Expand Down
Loading
Loading