Skip to content

Commit 95eb747

Browse files
Add ReAct (reasoning and acting) agent strategy
1 parent 80edb30 commit 95eb747

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed

agents/agents-ext/src/commonMain/kotlin/ai/koog/agents/ext/agent/AIAgentStrategies.kt

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package ai.koog.agents.ext.agent
22

33
import ai.koog.agents.core.agent.entity.AIAgentStrategy
4+
import ai.koog.agents.core.agent.entity.createStorageKey
45
import ai.koog.agents.core.dsl.builder.forwardTo
56
import ai.koog.agents.core.dsl.builder.strategy
67
import ai.koog.agents.core.dsl.extension.*
8+
import ai.koog.agents.core.environment.ReceivedToolResult
9+
import ai.koog.agents.core.environment.result
710
import ai.koog.prompt.message.Message
811

912
/**
@@ -42,3 +45,107 @@ public fun chatAgentStrategy(): AIAgentStrategy<String, String> = strategy("chat
4245
edge(nodeSendToolResult forwardTo nodeFinish onToolCall { tc -> tc.tool == "__exit__" } transformed { "Chat finished" })
4346
edge(nodeSendToolResult forwardTo nodeExecuteTool onToolCall { true })
4447
}
48+
49+
/**
50+
* Creates a ReAct AI agent strategy that alternates between reasoning and execution stages
51+
* to dynamically process tasks and request outputs from an LLM.
52+
*
53+
* @param reasoningInterval Specifies the interval for reasoning steps.
54+
* @return An instance of [AIAgentStrategy] that defines the ReAct strategy.
55+
*
56+
*
57+
* +-------+ +---------------+ +---------------+ +--------+
58+
* | Start | ----------> | CallLLMReason | ----------> | CallLLMAction | ----------> | Finish |
59+
* +-------+ +---------------+ +---------------+ +--------+
60+
* ^ | Finished? Yes
61+
* | | No
62+
* | v
63+
* +-----------------------+
64+
* | ExecuteTool |
65+
* +-----------------------+
66+
*
67+
* Example execution flow of a banking agent with ReAct strategy:
68+
*
69+
* 1. Start: User asks "How much did I spend last month?"
70+
*
71+
* 2. Reasoning Phase:
72+
* CallLLMReason: "I need to follow these steps:
73+
* 1. Get all transactions from last month
74+
* 2. Filter out deposits (positive amounts)
75+
* 3. Calculate total spending"
76+
*
77+
* 3. Action & Execution Phase 1:
78+
* CallLLMAction: {tool: "get_transactions", args: {startDate: "2025-05-19", endDate: "2025-06-18"}}
79+
* ExecuteTool Result: [
80+
* {date: "2025-05-25", amount: -100.00, description: "Grocery Store"},
81+
* {date: "2025-05-31", amount: +1000.00, description: "Salary Deposit"},
82+
* {date: "2025-06-10", amount: -500.00, description: "Rent Payment"},
83+
* {date: "2025-06-13", amount: -200.00, description: "Utilities"}
84+
* ]
85+
*
86+
* 4. Reasoning Phase:
87+
* CallLLMReason: "I have the transactions. Now I need to:
88+
* 1. Remove the salary deposit of +1000.00
89+
* 2. Sum up the remaining transactions"
90+
*
91+
* 5. Action & Execution Phase 2:
92+
* CallLLMAction: {tool: "calculate_sum", args: {amounts: [-100.00, -500.00, -200.00]}}
93+
* ExecuteTool Result: -800.00
94+
*
95+
* 6. Final Response:
96+
* Assistant: "You spent $800.00 last month on groceries, rent, and utilities."
97+
*
98+
* 7. Finish: Execution complete
99+
*/
100+
public fun reActStrategy(reasoningInterval: Int = 1, name: String = "re_act"): AIAgentStrategy<String, String> = strategy(name) {
101+
require(reasoningInterval > 0) { "Reasoning interval must be greater than 0" }
102+
val reasoningStepKey = createStorageKey<Int>("reasoning_step")
103+
val nodeSetup by node<String, String> {
104+
storage.set(reasoningStepKey, 0)
105+
it
106+
}
107+
val nodeCallLLM by node<Unit, Message.Response> {
108+
llm.writeSession {
109+
requestLLM()
110+
}
111+
}
112+
val nodeExecuteTool by nodeExecuteTool()
113+
114+
val reasoningPrompt = "Please give your thoughts about the task and plan the next steps."
115+
val nodeCallLLMReasonInput by node<String, Unit> { stageInput ->
116+
llm.writeSession {
117+
updatePrompt {
118+
user(stageInput)
119+
user(reasoningPrompt)
120+
}
121+
122+
requestLLMWithoutTools()
123+
}
124+
}
125+
val nodeCallLLMReason by node<ReceivedToolResult, Unit> { result ->
126+
val reasoningStep = storage.getValue(reasoningStepKey)
127+
llm.writeSession {
128+
updatePrompt {
129+
tool {
130+
result(result)
131+
}
132+
}
133+
134+
if (reasoningStep % reasoningInterval == 0) {
135+
updatePrompt {
136+
user(reasoningPrompt)
137+
}
138+
requestLLMWithoutTools()
139+
}
140+
}
141+
storage.set(reasoningStepKey, reasoningStep + 1)
142+
}
143+
144+
edge(nodeStart forwardTo nodeSetup)
145+
edge(nodeSetup forwardTo nodeCallLLMReasonInput)
146+
edge(nodeCallLLMReasonInput forwardTo nodeCallLLM)
147+
edge(nodeCallLLM forwardTo nodeExecuteTool onToolCall { true })
148+
edge(nodeCallLLM forwardTo nodeFinish onAssistantMessage { true })
149+
edge(nodeExecuteTool forwardTo nodeCallLLMReason)
150+
edge(nodeCallLLMReason forwardTo nodeCallLLM)
151+
}

0 commit comments

Comments
 (0)