传统 Agent 的架构困境
所有大模型的 Agent 能力都面临一个根本性矛盾:
Chain-of-Thought(CoT)推理模式: 能进行多步复杂推理,但推理过程是封闭的,无法中断去调用外部工具。
Function Calling(工具调用)模式: 能执行外部操作,但每次调用都会打断推理链路,导致上下文丢失和推理碎片化。
这种架构分离导致的结果是:要么闭卷推理(无实时数据支撑),要么开卷查询(缺乏深度分析)。
DeepSeek 3.2 的技术突破
核心创新:推理过程中的动态工具调用
DeepSeek 3.2 实现了全球首个"Reasoning-in-Action"范式:
架构对比:
arduino
传统 ReAct:Thought → Action → Observation → Thought...
(推理与行动交替,上下文割裂)
V3.2 新范式:Continuous Reasoning [Tool Call ⊂ Reasoning Process]
(工具调用内嵌于连续推理流)
技术实现:
- reasoning_content 传递机制:保持完整推理链路的上下文连贯性
- 自适应工具决策:模型在推理中自主判断工具调用时机和参数
- 双模式支持:思考模式(复杂任务)+ 非思考模式(快速响应)
这种架构让模型具备了真正的"元认知"能力:知道自己的知识边界,主动寻求外部信息补充。
详细信息可以查看官方文档,里面提供了python实现代码,因为我更喜欢ts,所以下面改写一段ts版的:
ts
import OpenAI from 'openai'
import type {
ChatCompletionMessageParam,
ChatCompletionTool,
} from 'openai/resources/chat/completions'
// Get environment variables
const apiKey = process.env.DEEPSEEK_API_KEY
const baseURL = process.env.DEEPSEEK_BASE_URL
const client = new OpenAI({
apiKey: apiKey,
baseURL: baseURL,
})
// The definition of the tools
const tools: ChatCompletionTool[] = [
{
type: 'function',
function: {
name: 'get_date',
description: 'Get the current date',
parameters: { type: 'object', properties: {} },
},
},
{
type: 'function',
function: {
name: 'get_weather',
description:
'Get weather of a location, the user should supply the location and date.',
parameters: {
type: 'object',
properties: {
location: { type: 'string', description: 'The city name' },
date: {
type: 'string',
description: 'The date in format YYYY-mm-dd',
},
},
required: ['location', 'date'],
},
},
},
]
// The mocked version of the tool calls
// In Python `**kwargs` unpacks the dict, in JS we receive a single object argument
function get_date_mock(): string {
return '2025-12-01'
}
function get_weather_mock(args: { location: string; date: string }): string {
return 'Cloudy 7~13°C'
}
const TOOL_CALL_MAP: Record<string, Function> = {
get_date: get_date_mock,
get_weather: get_weather_mock,
}
// Function to clear reasoning_content to save bandwidth
// Note: modifying objects in the array by reference
function clear_reasoning_content(messages: any[]) {
for (const message of messages) {
if (message.reasoning_content) {
message.reasoning_content = null
}
}
}
async function run_turn(turn: number, messages: any[]) {
let sub_turn = 1
while (true) {
// DeepSeek specific: extra_body for thinking
const response = await client.chat.completions.create({
model: 'deepseek-chat',
messages: messages as ChatCompletionMessageParam[],
tools: tools,
// 注意!!!这里不需要extra_body包多一层,因为这里会将其他属性全部丢到body里面
thinking: { type: 'enabled' },
} as any) // Cast to any to allow extra_body if types are strict
const message = response.choices[0].message
messages.push(message)
// Access properties (casting to any to access 'reasoning_content' which is not in standard types)
const reasoning_content = (message as any).reasoning_content
const content = message.content
const tool_calls = message.tool_calls
console.log(
`Turn ${turn}.${sub_turn}\nreasoning_content=${reasoning_content}\ncontent=${content}\ntool_calls=${JSON.stringify(
tool_calls
)}`
)
// If there are no tool calls, stop the loop
if (!tool_calls || tool_calls.length === 0) {
break
}
for (const tool of tool_calls) {
const tool_function = TOOL_CALL_MAP[tool.function.name]
// In JS, we parse the JSON string to an object and pass it to the function
const args = JSON.parse(tool.function.arguments)
const tool_result = tool_function(args)
console.log(`tool result for ${tool.function.name}: ${tool_result}\n`)
messages.push({
role: 'tool',
tool_call_id: tool.id,
content: tool_result,
})
}
sub_turn += 1
}
}
async function main() {
// The user starts a question
let turn = 1
const messages: any[] = [
{
role: 'user',
content: "How's the weather in Hangzhou Tomorrow",
},
]
await run_turn(turn, messages)
// The user starts a new question
turn = 2
messages.push({
role: 'user',
content: "How's the weather in Hangzhou Tomorrow",
})
// Clear reasoning_content in history
clear_reasoning_content(messages)
await run_turn(turn, messages)
}
// Execute logic
main().catch(console.error)
输出:
vbnet
Turn 1.1
reasoning_content=The user wants to know the weather in Hangzhou tomorrow. I need to get the date for tomorrow first, then use that to get the weather. Let me start by getting the current date.
content=
tool_calls=[{"index":0,"id":"call_00_jv8TcuSwxu1c5nzVMf0NrVrN","type":"function","function":{"name":"get_date","arguments":"{}"}}]
tool result for get_date: 2025-12-01
Turn 1.2
reasoning_content=Today is December 1, 2025. Tomorrow would be December 2, 2025. Now I can get the weather for Hangzhou for that date. Let me call get_weather with location "Hangzhou" and date "2025-12-02".
content=
tool_calls=[{"index":0,"id":"call_00_qdEqnDe5ulkeSe8orl44rAvN","type":"function","function":{"name":"get_weather","arguments":"{\"location\": \"Hangzhou\", \"date\": \"2025-12-02\"}"}}]
tool result for get_weather: Cloudy 7~13°C
Turn 1.3
reasoning_content=I have the weather information: "Cloudy 7~13°C". I should provide this to the user in a friendly manner. I'll also mention that it's for tomorrow. Let me craft a response.
content=Tomorrow (December 2, 2025) in Hangzhou, the weather will be **cloudy** with temperatures ranging from **7°C to 13°C**.
tool_calls=undefined
Turn 2.1
reasoning_content=The user is asking again about Hangzhou's weather tomorrow. I already provided the answer for December 2, 2025. I should check if there's any reason they might be asking again - perhaps they want more details or there's a misunderstanding about the date. Since today is December 1, 2025 (as per the get_date result), tomorrow is indeed December 2, 2025. I already have the weather information: cloudy, 7~13°C. I should just repeat the answer, perhaps with a slightly different phrasing. No need to call the function again since the date hasn't changed. I'll respond with the same information.
content=The weather in Hangzhou tomorrow (December 2, 2025) will be **cloudy** with temperatures between **7°C and 13°C**.
tool_calls=undefined
看得出来和py的运行结果一样。支持推理过程进行函数调用之后,就可以不用再搓一个ReAct模型的Agent,让模型原生支持ReAct。