Skip to content

Commit 732221b

Browse files
authored
Merge pull request #7 from tjdoomer/feature/think-tool
Think tool for structured reasoning
2 parents a99f8b8 + b011b9a commit 732221b

3 files changed

Lines changed: 174 additions & 0 deletions

File tree

docs/roadmap/07-think-tool.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Think / Plan Tool
2+
3+
## Branch: `feature/think-tool`
4+
5+
## Problem
6+
7+
Local models (7B-32B running on LM Studio) make impulsive tool calls — they
8+
jump straight to editing without understanding the problem. Larger API models
9+
do this too but less often. There's no mechanism to force structured reasoning
10+
before acting.
11+
12+
Delta has `Kind.Think` in its enum but no actual tool backing it.
13+
14+
## What it is
15+
16+
A no-op tool that accepts the model's reasoning and returns it back into context.
17+
No side effects. The model calls it to think before it acts.
18+
19+
```
20+
Model → think({ reasoning: "The user wants X. I see files A, B, C are involved.
21+
The dependency chain is A→B→C. I should modify B first because..." })
22+
← Returns the same text back into context
23+
```
24+
25+
## Why it works
26+
27+
Research on process reward models shows that models which checkpoint reasoning
28+
mid-task make fewer cascading errors. For weaker models this is even more
29+
pronounced — a 7B model that plans before acting outperforms the same model
30+
that jumps straight to edits.
31+
32+
## Implementation
33+
34+
### Tool declaration
35+
36+
```typescript
37+
{
38+
name: 'think',
39+
description: 'Use this tool to plan your approach before taking action. '
40+
+ 'Write out your reasoning about the problem, what files are involved, '
41+
+ 'what changes are needed, and in what order. This helps you avoid '
42+
+ 'mistakes by thinking before acting. The output is returned to you '
43+
+ 'for reference — no side effects occur.',
44+
parameters: {
45+
type: 'object',
46+
properties: {
47+
reasoning: {
48+
type: 'string',
49+
description: 'Your structured reasoning about the current task.',
50+
},
51+
},
52+
required: ['reasoning'],
53+
},
54+
}
55+
```
56+
57+
### Tool implementation
58+
59+
```typescript
60+
// The entire tool is ~10 lines
61+
async execute({ reasoning }: { reasoning: string }): Promise<string> {
62+
return reasoning;
63+
}
64+
```
65+
66+
### System prompt integration
67+
68+
Add to the system prompt for weaker models (configurable):
69+
70+
> Before making changes to code, use the `think` tool to plan your approach.
71+
> Outline which files need to change, in what order, and why. This is
72+
> especially important for multi-file changes.
73+
74+
### Optional: structured think mode
75+
76+
For models that support it, enforce a schema:
77+
78+
```typescript
79+
{
80+
goal: string;
81+
files_involved: string[];
82+
steps: string[];
83+
risks: string[];
84+
current_step: number;
85+
}
86+
```
87+
88+
This could be a `think_structured` variant or a config flag.
89+
90+
## Files to create
91+
- `packages/core/src/tools/think.ts` — tool implementation
92+
- `packages/core/src/tools/think.test.ts`
93+
94+
## Files to modify
95+
- `packages/core/src/config/config.ts` — register tool in `registerCoreTool()`
96+
- `packages/core/src/core/prompts.ts` — add think guidance to system prompt
97+
(gated on config flag or model capability)
98+
99+
## Effort: ~1 hour

packages/core/src/config/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
import { TodoWriteTool } from '../tools/todoWrite.js';
3131
import { WebSearchTool } from '../tools/web-search.js';
3232
import { SubAgentTool } from '../tools/subAgentTool.js';
33+
import { ThinkTool } from '../tools/think.js';
3334
import { DeltaClient } from '../core/client.js';
3435
import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
3536
import { GitService } from '../services/gitService.js';
@@ -916,6 +917,7 @@ export class Config {
916917
registerCoreTool(WebSearchTool, this);
917918
}
918919
registerCoreTool(SubAgentTool, this);
920+
registerCoreTool(ThinkTool);
919921

920922
await registry.discoverAllTools();
921923
return registry;

packages/core/src/tools/think.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* Think tool — forces the model to reason before acting.
3+
*
4+
* A no-op tool that accepts structured reasoning and returns it back into
5+
* context. No side effects. The model calls it to plan before making changes.
6+
*
7+
* This is the single best compensator for weaker local models — research on
8+
* process reward models shows that models which checkpoint reasoning mid-task
9+
* make fewer cascading errors.
10+
*/
11+
12+
import {
13+
BaseDeclarativeTool,
14+
BaseToolInvocation,
15+
Kind,
16+
ToolResult,
17+
} from './tools.js';
18+
import { FunctionDeclaration } from '@google/genai';
19+
20+
interface ThinkParams {
21+
reasoning: string;
22+
}
23+
24+
const thinkToolSchemaData: FunctionDeclaration = {
25+
name: 'think',
26+
description:
27+
'Use this tool to plan your approach before taking action. Write out your reasoning about the problem, what files are involved, what changes are needed, and in what order. This helps you avoid mistakes by thinking before acting. The output is returned to you for reference — no side effects occur.',
28+
parametersJsonSchema: {
29+
type: 'object',
30+
properties: {
31+
reasoning: {
32+
type: 'string',
33+
description: 'Your structured reasoning about the current task.',
34+
},
35+
},
36+
required: ['reasoning'],
37+
$schema: 'http://json-schema.org/draft-07/schema#',
38+
},
39+
};
40+
41+
// The invocation is trivially simple — return the reasoning back to the model.
42+
// No file I/O, no side effects, no confirmation needed.
43+
class ThinkToolInvocation extends BaseToolInvocation<ThinkParams, ToolResult> {
44+
getDescription(): string {
45+
const preview = this.params.reasoning.substring(0, 80);
46+
return preview.length < this.params.reasoning.length ? `${preview}...` : preview;
47+
}
48+
49+
async execute(): Promise<ToolResult> {
50+
return {
51+
llmContent: [{ text: this.params.reasoning }],
52+
returnDisplay: this.params.reasoning,
53+
};
54+
}
55+
}
56+
57+
export class ThinkTool extends BaseDeclarativeTool<ThinkParams, ToolResult> {
58+
static readonly Name: string = thinkToolSchemaData.name!;
59+
60+
constructor() {
61+
super(
62+
ThinkTool.Name,
63+
'Think',
64+
thinkToolSchemaData.description!,
65+
Kind.Think,
66+
thinkToolSchemaData.parametersJsonSchema,
67+
);
68+
}
69+
70+
protected createInvocation(params: ThinkParams) {
71+
return new ThinkToolInvocation(params);
72+
}
73+
}

0 commit comments

Comments
 (0)