AI Agent Note (智能体创建)
2025-12-05 00:00:00 # AI

之前实现了大模型对工具的调用,现在实现初始化一个智能体,然后对工具进行调用。

这里有一个问题,智能体是基于大模型对工具进行调用,这二者有什么区别?

这是一个非常关键、也是很多人容易混淆的问题。大模型调用工具智能体调用工具看起来都能触发外部函数/接口,但它们的行为模型、执行流程、决策机制和适用场景完全不同。

LLM 只做一次决策:选哪个工具 + 给参数 → 工具执行 → 直接返回结果。它本身不会思考“下一步该怎么办”。

Agent 是可以持续推理、规划、循环执行的系统,它会根据工具结果重新思考、再调用工具、多轮执行并最终形成任务完成闭环。

理论先摆在这,,,下面创建一个智能体,然后实现调用工具进行数学计算,最后与前面大模型调用工具进行对比。

智能体创建

初始化智能体

1
2
3
4
5
6
7
8
from app.bailian.commom import create_calc_tools, llm, chat_prompt_template

agent = initialize_agent(
tools=create_calc_tools(),
llm=llm,
agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
verbose=True,
)

这里初始化一个名为agent的智能体,有4个参数:

  • tools:可以理解为一个工具集

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    class AddInputArgs(BaseModel):
    a: int = Field(description="第一个数")
    b: int = Field(description="第二个数")
    @tool(
    description="两个数相加",
    args_schema=AddInputArgs,
    return_direct=False
    )
    def add(a, b):
    print("调用了函数add:")
    return a + b
    @tool(
    description="两个数相减",
    args_schema=AddInputArgs,
    return_direct=False
    )
    def jian(a, b):
    print("调用了函数jian:")
    return a - b
    @tool(
    description="两个数相乘",
    args_schema=AddInputArgs,
    return_direct=False
    )
    def cheng(a, b):
    print("调用了函数cheng:")
    return a * b
    def create_calc_tools():
    return [add, jian, cheng]
  • llm:所调用的大模型

  • agent:所用到的agent类型

  • verbose:输出调试信息

这里关于AgentType可以看一下它的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@deprecated(
"0.1.0",
message=AGENT_DEPRECATION_WARNING,
removal="1.0",
)
class AgentType(str, Enum):
"""An enum for agent types.

See documentation: https://python.langchain.com/api_reference/langchain/agents/langchain.agents.agent_types.AgentType.html
"""

ZERO_SHOT_REACT_DESCRIPTION = "zero-shot-react-description"
"""A zero shot agent that does a reasoning step before acting."""

REACT_DOCSTORE = "react-docstore"
"""A zero shot agent that does a reasoning step before acting.

This agent has access to a document store that allows it to look up
relevant information to answering the question.
"""

SELF_ASK_WITH_SEARCH = "self-ask-with-search"
"""An agent that breaks down a complex question into a series of simpler questions.

This agent uses a search tool to look up answers to the simpler questions
in order to answer the original complex question.
"""
CONVERSATIONAL_REACT_DESCRIPTION = "conversational-react-description"
CHAT_ZERO_SHOT_REACT_DESCRIPTION = "chat-zero-shot-react-description"
"""A zero shot agent that does a reasoning step before acting.

This agent is designed to be used in conjunction
"""

CHAT_CONVERSATIONAL_REACT_DESCRIPTION = "chat-conversational-react-description"

STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION = (
"structured-chat-zero-shot-react-description"
)
"""An zero-shot react agent optimized for chat models.

This agent is capable of invoking tools that have multiple inputs.
"""

OPENAI_FUNCTIONS = "openai-functions"
"""An agent optimized for using open AI functions."""

OPENAI_MULTI_FUNCTIONS = "openai-multi-functions"

写提示词

1
2
3
4
5
6
7
8
9
prompt = chat_prompt_template.format_messages(
role="计算",
domain="使用工具进行数学计算",
question=f'''
请阅读下面的问题,并返回一个严格的json对象,不要使用markdown代码块包裹。
格式要求:{format_instructions}
问题:帮我求 2 × (100 - 50)
'''
)

这里还是用的之前的template,传入三个变量:roledomainquestion

langchainparsers来控制输出格式

1
2
3
4
5
6
7
8
9
10
11
class Output(BaseModel):
args: str = Field("输入的参数:")
result: str = Field("返回的结果")


parser = JsonOutputParser(pydantic_object=Output)
format_instructions = parser.get_format_instructions()

resp = agent.invoke(prompt)

print(resp['output'])

结果如下👇🏻

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
The output should be formatted as a JSON instance that conforms to the JSON schema below.

As an example, for the schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}
the object {"foo": ["bar", "baz"]} is a well-formatted instance of the schema. The object {"properties": {"foo": ["bar", "baz"]}} is not well-formatted.

Here is the output schema:
```
{"properties": {"args": {"default": "输入的参数:", "title": "Args", "type": "string"}, "result": {"default": "返回的结果", "title": "Result", "type": "string"}}}
```


> Entering new AgentExecutor chain...
Thought: 我需要先计算括号内的值,然后再乘以2。
Action:
```
{
"action": "jian",
"action_input": {
"a": 100,
"b": 50
}
}
```
调用了函数jian

Observation: 50
Thought:现在我需要将括号内的结果乘以2。
Action:
```
{
"action": "cheng",
"action_input": {
"a": 2,
"b": 50
}
}
```调用了函数cheng

Observation: 100
Thought:我现在可以返回最终答案了。
Action:
```
{
"action": "Final Answer",
"action_input": "{\"args\": \"2 × (100 - 50)\", \"result\": \"100\"}"
}
```

> Finished chain.
{"args": "2 × (100 - 50)", "result": "100"}

进程已结束,退出代码为 0

可以看到,这里智能体先进行了规划

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Thought: 我需要先计算括号内的值,然后再乘以2。
Action:
```
{
"action": "jian",
"action_input": {
"a": 100,
"b": 50
}
}
```
调用了函数jian

Observation: 50

得到了第一步的计算结果,这里并没有完成,而是循环执行,进行了下一步的调用

1
2
3
4
5
6
7
8
9
10
11
12
13
Thought:现在我需要将括号内的结果乘以2。
Action:
```
{
"action": "cheng",
"action_input": {
"a": 2,
"b": 50
}
}
```调用了函数cheng

Observation: 100

最后又进行了一步思考,然后返回了答案

1
2
3
4
5
6
7
8
9
10
11
Thought:我现在可以返回最终答案了。
Action:
```
{
"action": "Final Answer",
"action_input": "{\"args\": \"2 × (100 - 50)\", \"result\": \"100\"}"
}
```

> Finished chain.
{"args": "2 × (100 - 50)", "result": "100"}

回到前面,对比大模型调用,用同样的问题,让大模型来完成任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from app.bailian.commom import create_calc_tools, add, jian, cheng
from app.bailian.commom import chat_prompt_template, llm


calc_tool = create_calc_tools()
llm_with_tools = llm.bind_tools(calc_tool)

chain = chat_prompt_template | llm_with_tools

resp = chain.invoke(input={
"role": "计算",
"domain": "数学计算",
"question": "帮我求 2 × (100 - 50)"
})

print(resp)

resp的内容如下:

1
2
content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_8a45115ab4ea4cf0837b91', 'function': {'arguments': '{"a": 100, "b": 50}', 'name': 'jian'}, 'type': 'function'}, {'index': 1, 'id': 'call_75d1788ce3544703aaae25', 'function': {'arguments': '{"a": 2, "b": "jian(100, 50)"}', 'name': 'cheng'}, 'type': 'function'}]} response_metadata={'finish_reason': 'tool_calls', 'model_name': 'qwen-max-latest'} id='run--e4cfd825-384d-43dd-a06d-f8539a252cd9-0' tool_calls=[{'name': 'jian', 'args': {'a': 100, 'b': 50}, 'id': 'call_8a45115ab4ea4cf0837b91', 'type': 'tool_call'}, {'name': 'cheng', 'args': {'a': 2, 'b': 'jian(100, 50)'}, 'id': 'call_75d1788ce3544703aaae25', 'type': 'tool_call'}]
{'name': 'jian', 'args': {'a': 100, 'b': 50}, 'id': 'call_8a45115ab4ea4cf0837b91', 'type': 'tool_call'}

可以看到,在tool_calls列表中第二次调用工具传入的参数bjian(100, 50),就可以推测出大模型内部的计划是

1
2
步骤1:调用 jian(100, 50)
步骤2:调用 cheng(2, result_of_step1)

这就是大模型根据prompt仅生成结构化函数调用计划,不会执行,也不会知道第一次调用返回了多少。

通过这个简单的Demo,就可以看出来大模型调用工具和智能体调用工具的区别了。

上一页
2025-12-05 00:00:00 # AI