前言
在之前的内容中,我们使用了英文提示词。虽然这种方式对于人工智能来说更容易被正确理解,但对于很多学习者来说,可能会感到有些困惑。毕竟,不是所有人都对英文编程术语非常熟悉,尤其是初学者,可能会在理解这些提示词时遇到困难。
为了让大家更好地理解和使用这些提示词,我决定编写一份中文版本的提示词。这份中文提示词的效果将与英文版本完全一致,但会更加贴近我们的日常语言习惯,方便大家学习和使用。希望这份中文提示词能够帮助大家更轻松地掌握相关知识。
中文版本代码
python
# 中文版本
import operator
import os
from typing import Annotated, List, Tuple, Union
from dotenv import load_dotenv
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import create_react_agent
from pydantic import BaseModel, Field
from typing_extensions import TypedDict
#
load_dotenv()
llm = ChatOpenAI(model="qwen-plus",
base_url=os.getenv("BASE_URL"),
api_key=os.getenv("OPENAI_API_KEY"),
temperature=0,
streaming=True)
tools = []
prompt = ChatPromptTemplate.from_messages(
[
("system", """你是一位乐于助人的助手。"""),
("placeholder", "{messages}")
]
)
agent_executor = create_react_agent(llm, tools, state_modifier=prompt, debug=False)
# Define the Plan and Execution structure
class PlanExecute(TypedDict):
input: str
plan: List[str]
past_steps: Annotated[List[Tuple], operator.add]
response: str
class Plan(BaseModel):
steps: List[str] = Field(description="序号唯一的步骤,按照数字升序")
class Response(BaseModel):
response: str = Field(description="对用户的回应。")
class Act(BaseModel):
action: Union[Response, Plan] = Field(
description="要执行的操作。如果你想回复用户,请使用Response。"
"如果你需要进一步使用工具来获取答案,请使用Plan。")
# Planning step
planner_prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"""为实现既定目标,制定一个简单的分步计划。\
该计划应包含按顺序编号的单独任务,如果正确执行这些任务,将得出正确答案。不要添加任何多余的步骤。\
最后一步的结果应为最终答案。确保每个步骤都包含所需的所有信息,不要遗漏任何步骤。
您必须按照以下 JSON 格式输出:
{{
"steps": [
"Step 1 描述",
"Step 2 描述",
"Step 3 描述"
]
}}
确保只包含带有步骤数组的 JSON 对象,不要添加其他内容。
""",
),
("placeholder", "{messages}"),
]
)
planner = planner_prompt | llm.with_structured_output(Plan)
# Re-planning step
replanner_prompt = ChatPromptTemplate.from_template(
"""为实现既定目标,制定一个简单的分步计划。\
该计划应包含按顺序编号的单独任务,如果正确执行这些任务,将得出正确答案。不要添加任何多余的步骤。\
最后一步的结果应为最终答案。确保每个步骤都包含所需的所有信息,不要遗漏任何步骤。
你的目标是这样的:
{input}
你的原始计划是这样的:
{plan}
你目前已经完成了以下步骤:
{past_steps}
相应地更新你的计划。如果不需要更多步骤,且你可以回复用户,那么就这样回复。否则,完善计划。只在计划中添加那些仍需完成的步骤。不要把已经完成的步骤再纳入计划。
你必须严格按照下面其中一种JSON格式响应:
第一种 - 如果提供最终回复:
{{
"action": {{
"response": "你给用户的最终回复"
}}
}}
第二种 - 如果提供新的步骤:
{{
"action": {{
"steps": [
"Step 1 描述",
"Step 2 描述"
]
}}
}}
"""
)
replanner = replanner_prompt | llm.with_structured_output(Act)
def execute_step(state: PlanExecute):
print("执行步骤")
plan = state["plan"]
plan_str = "\n".join(f"{i + 1}. {step}" for i, step in enumerate(plan))
task = plan[0]
task_formatted = f"对于以下计划:\n{plan_str}\n\n你负责执行第一步, {task}."
agent_response = agent_executor.invoke({"messages": [("user", task_formatted)]})
return {
"past_steps": [(task, agent_response["messages"][-1].content)],
}
# Planning step function
def plan_step(state: PlanExecute):
print("plan_step")
plan = planner.invoke({"messages": [("user", state["input"])]})
# print(f"plan_step {plan}")
return {"plan": plan.steps}
# Re-planning step function
def replan_step(state: PlanExecute):
print("replan_step")
output = replanner.invoke(state)
if isinstance(output.action, Response): # Final response provided
return {"response": output.action.response} # Return the response to the user
else:
return {"plan": output.action.steps}
# Conditional check for ending
def should_end(state: PlanExecute):
print("should_end")
if "response" in state and state["response"]:
return END
else:
return "agent"
# Build the workflow
workflow = StateGraph(PlanExecute)
# Add nodes to the workflow
workflow.add_node("planner", plan_step)
workflow.add_node("agent", execute_step)
workflow.add_node("replan", replan_step)
# Add edges to transition between nodes
workflow.add_edge(START, "planner")
workflow.add_edge("planner", "agent")
workflow.add_edge("agent", "replan")
workflow.add_conditional_edges("replan", should_end, ["agent", END])
# Compile the workflow into an executable application
app = workflow.compile()
# Function to run the Plan-and-Execute agent
def run_plan_and_execute():
inputs = {
"input": "格蕾丝体重 125 磅。亚历克斯的体重比格蕾丝体重的 4 倍少 2 磅。他们两人的体重总和是多少磅?"
}
# Configuration for recursion limit
config = {"recursion_limit": 10}
# Run the Plan-and-Execute agent asynchronously
for event in app.stream(inputs, config=config):
for k, v in event.items():
if k != "__end__":
print(v)
# Run the async function
if __name__ == "__main__":
run_plan_and_execute()
运行结果
arduino
plan_step
{'plan': ['Step 1: 确定格蕾丝的体重,为 125 磅。', 'Step 2: 计算亚历克斯的体重,等于格蕾丝体重的 4 倍减去 2 磅,即 125 × 4 - 2 = 498 磅。', 'Step 3: 计算两人体重总和,等于 125 + 498 = 623 磅。']}
执行步骤
{'past_steps': [('Step 1: 确定格蕾丝的体重,为 125 磅。', '好的,我们完成了第一步:\n\n**Step 1: 格蕾丝的体重是 125 磅。**\n\n接下来可以继续执行后续步骤。是否需要我帮你继续?')]}
replan_step
should_end
{'plan': ['Step 2: 计算亚历克斯的体重,等于格蕾丝体重的 4 倍减去 2 磅,即 125 × 4 - 2 = 498 磅。', 'Step 3: 计算两人体重总和,等于 125 + 498 = 623 磅。']}
执行步骤
{'past_steps': [('Step 2: 计算亚历克斯的体重,等于格蕾丝体重的 4 倍减去 2 磅,即 125 × 4 - 2 = 498 磅。', '好的,我们来执行 **Step 2**:\n\n格蕾丝的体重是 125 磅。 \n亚历克斯的体重等于格蕾丝体重的 4 倍减去 2 磅:\n\n$$\n125 \times 4 - 2 = 500 - 2 = 498 \text{ 磅}\n$$\n\n✅ **亚历克斯的体重是 498 磅**。\n\n接下来可以继续执行 Step 3 来计算两人总和。需要我继续吗?')]}
replan_step
should_end
{'plan': ['Step 3: 计算格蕾丝和亚历克斯的体重总和,等于 125 + 498 = 623 磅。']}
执行步骤
{'past_steps': [('Step 3: 计算格蕾丝和亚历克斯的体重总和,等于 125 + 498 = 623 磅。', '看起来这里有些混淆。你提到"你负责执行第一步",但给出的步骤描述是"Step 3: 计算格蕾丝和亚历克斯的体重总和,等于 125 + 498 = 623 磅。"\n\n我们先澄清一下:\n\n如果 Step 3 是:"计算格蕾丝和亚历克斯的体重总和,等于 125 + 498 = 623 磅。" \n那这个步骤已经完成了,因为等式已经给出并且是正确的:\n\n**125 + 498 = 623**\n\n所以,格蕾丝和亚历克斯的体重总和是 **623 磅**。\n\n如果你需要我执行的是其他步骤(比如第一步),请提供完整的步骤说明或任务内容,我会继续帮助你。')]}
replan_step
should_end
{'response': '格蕾丝和亚历克斯的体重总和是 **623 磅**。'}
执行步骤完整解析
下面的解析步骤会非常的长,请耐心阅读。
第一轮
1.1 planner 节点
使用AI将用户输入规划成计划数组,最终的提示词如下
bash
System: 为实现既定目标,制定一个简单的分步计划。该计划应包含按顺序编号的单独任务,如果正确执行这些任务,将得出正确答案。不要添加任何多余的步骤。最后一步的结果应为最终答案。确保每个步骤都包含所需的所有信息,不要遗漏任何步骤。
您必须按照以下 JSON 格式输出:
{
"steps": [
"Step 1 描述",
"Step 2 描述",
"Step 3 描述"
]
}
确保只包含带有步骤数组的 JSON 对象,不要添加其他内容。
Human: 格蕾丝体重 125 磅。亚历克斯的体重比格蕾丝体重的 4 倍少 2 磅。他们两人的体重总和是多少磅?"
planner响应如下
json
{
"steps": [
"Step 1: 确定格蕾丝的体重为 125 磅。",
"Step 2: 计算亚历克斯的体重,即 4 × 125 - 2 = 498 磅。",
"Step 3: 计算两人体重总和,即 125 + 498 = 623 磅。"
]
}
1.2 Agent 节点
也就是计划执行节点,最终提示词为
vbnet
Human: 对于以下计划:
1. Step 1: 确定格蕾丝的体重为 125 磅。
2. Step 2: 计算亚历克斯的体重,即 4 × 125 - 2 = 498 磅。
3. Step 3: 计算两人体重总和,即 125 + 498 = 623 磅。
你负责执行第一步, Step 1: 确定格蕾丝的体重为 125 磅。.
agent 响应如下
vbnet
好的,我们开始执行第一步:
**Step 1: 确定格蕾丝的体重为 125 磅。**
✅ 格蕾丝的体重已确定为 **125 磅**。
接下来可以继续进行 Step 2 和 Step 3 的计算。如果你需要继续,请告诉我!
此时需要处理past_step,把处理的步骤和刚刚的响应加入到past_step数组中,past_step结果如下
json
[
[
"Step 1: 确定格蕾丝的体重为 125 磅。",
"好的,我们开始执行第一步:
**Step 1: 确定格蕾丝的体重为 125 磅。**
✅ 格蕾丝的体重已确定为 **125 磅**。
接下来可以继续进行 Step 2 和 Step 3 的计算。如果你需要继续,请告诉我!"
]
]
1.3 replanner 节点
此时需要重新把计划数组重新处理下,剔除完成的计划列表,生成新的计划列表,这里也是交给AI来处理,最后的提示词如下
css
Human: 为实现既定目标,制定一个简单的分步计划。该计划应包含按顺序编号的单独任务,如果正确执行这些任务,将得出正确答案。不要添加任何多余的步骤。最后一步的结果应为最终答案。确保每个步骤都包含所需的所有信息,不要遗漏任何步骤。
你的目标是这样的:
格蕾丝体重 125 磅。亚历克斯的体重比格蕾丝体重的 4 倍少 2 磅。他们两人的体重总和是多少磅?
你的原始计划是这样的:
['Step 1: 确定格蕾丝的体重为 125 磅。', 'Step 2: 计算亚历克斯的体重,即 4 × 125 - 2 = 498 磅。', 'Step 3: 计算两人体重总和,即 125 + 498 = 623 磅。']
你目前已经完成了以下步骤:
[('Step 1: 确定格蕾丝的体重为 125 磅。', '好的,我们开始执行第一步:\
\
**Step 1: 确定格蕾丝的体重为 125 磅。**\
\
✅ 格蕾丝的体重已确定为 **125 磅**。\
\
接下来可以继续进行 Step 2 和 Step 3 的计算。如果你需要继续,请告诉我!')]
相应地更新你的计划。如果不需要更多步骤,且你可以回复用户,那么就这样回复。否则,完善计划。只在计划中添加那些仍需完成的步骤。不要把已经完成的步骤再纳入计划。
你必须严格按照下面其中一种JSON格式响应:
第一种 - 如果提供最终回复:
{
"action": {
"response": "你给用户的最终回复"
}
}
第二种 - 如果提供新的步骤:
{
"action": {
"steps": [
"Step 1 描述",
"Step 2 描述"
]
}
}
Ai的响应如下,很明显Ai帮我们把已经完成的步骤给剔除了
json
{
"action": {
"steps": [
"Step 2: 计算亚历克斯的体重,即 4 × 125 - 2。",
"Step 3: 计算两人体重总和,即 125 + 498。"
]
}
}
1.4 终止判定
由于没有得到最终的答案,需要继续执行到Agent节点,于是进入了第二轮
第二轮
2.1 Agent节点
把刚刚的计划继续执行,提示词为
bash
对于以下计划:
1. Step 2: 计算亚历克斯的体重,即 4 × 125 - 2。
2. Step 3: 计算两人体重总和,即 125 + 498。
你负责执行第一步, Step 2: 计算亚历克斯的体重,即 4 × 125 - 2。.
Ai响应如下
bash
好的,我们来执行 Step 2:计算亚历克斯的体重,即
**4 × 125 - 2**。
### 计算步骤:
1. 先算乘法:
$ 4 \\times 125 = 500 $
2. 再减去 2:
$ 500 - 2 = 498 $
### 结果:
亚历克斯的体重是 **498**。
此时需要处理past_step,把处理的步骤和刚刚的响应加入到past_step数组中,past_step结果如下
json
[
[
"Step 1: 确定格蕾丝的体重为 125 磅。",
"好的,我们开始执行第一步:
**Step 1: 确定格蕾丝的体重为 125 磅。**
✅ 格蕾丝的体重已确定为 **125 磅**。
接下来可以继续进行 Step 2 和 Step 3 的计算。如果你需要继续,请告诉我!"
],
[
"Step 2: 计算亚历克斯的体重,即 4 × 125 - 2。",
"好的,我们来执行 Step 2:计算亚历克斯的体重,即
**4 × 125 - 2**。
### 计算步骤:
1. 先算乘法:
$ 4 \\times 125 = 500 $
2. 再减去 2:
$ 500 - 2 = 498 $
### 结果:
亚历克斯的体重是 **498**。"
]
]
2.2 replanner 节点
继续使用replanner进行重新规划,提示词为
bash
Human: 为实现既定目标,制定一个简单的分步计划。该计划应包含按顺序编号的单独任务,如果正确执行这些任务,将得出正确答案。不要添加任何多余的步骤。最后一步的结果应为最终答案。确保每个步骤都包含所需的所有信息,不要遗漏任何步骤。
你的目标是这样的:
格蕾丝体重 125 磅。亚历克斯的体重比格蕾丝体重的 4 倍少 2 磅。他们两人的体重总和是多少磅?
你的原始计划是这样的:
['Step 2: 计算亚历克斯的体重,即 4 × 125 - 2。', 'Step 3: 计算两人体重总和,即 125 + 498。']
你目前已经完成了以下步骤:
[('Step 1: 确定格蕾丝的体重为 125 磅。', '好的,我们开始执行第一步:\
\
**Step 1: 确定格蕾丝的体重为 125 磅。**\
\
✅ 格蕾丝的体重已确定为 **125 磅**。\
\
接下来可以继续进行 Step 2 和 Step 3 的计算。如果你需要继续,请告诉我!'), ('Step 2: 计算亚历克斯的体重,即 4 × 125 - 2。', '好的,我们来执行 Step 2:计算亚历克斯的体重,即 \
**4 × 125 - 2**。\
\
### 计算步骤:\
1. 先算乘法: \
$ 4 \\\\times 125 = 500 $\
\
2. 再减去 2: \
$ 500 - 2 = 498 $\
\
### 结果:\
亚历克斯的体重是 **498**。')]
相应地更新你的计划。如果不需要更多步骤,且你可以回复用户,那么就这样回复。否则,完善计划。只在计划中添加那些仍需完成的步骤。不要把已经完成的步骤再纳入计划。
你必须严格按照下面其中一种JSON格式响应:
第一种 - 如果提供最终回复:
{
"action": {
"response": "你给用户的最终回复"
}
}
第二种 - 如果提供新的步骤:
{
"action": {
"steps": [
"Step 1 描述",
"Step 2 描述"
]
}
}
Ai响应为
json
{
"action": {
"steps": [
"Step 3: 计算两人体重总和,即 125 + 498。"
]
}
}
2.3 终止判断
很明显不能终止,继续下一轮
第三轮
3.1 agent节点
上面的计划只有一步了,提示词为
vbnet
对于以下计划:
1. Step 3: 计算两人体重总和,即 125 + 498。
你负责执行第一步, Step 3: 计算两人体重总和,即 125 + 498。.
Ai响应为
ini
好的,我们来计算两人的体重总和:
**125 + 498 = 623**
所以,两人的体重总和是 **623**。
此时需要处理past_step,把处理的步骤和刚刚的响应加入到past_step数组中,past_step结果如下
json
[
[
"Step 1: 确定格蕾丝的体重为 125 磅。",
"好的,我们开始执行第一步:
**Step 1: 确定格蕾丝的体重为 125 磅。**
✅ 格蕾丝的体重已确定为 **125 磅**。
接下来可以继续进行 Step 2 和 Step 3 的计算。如果你需要继续,请告诉我!"
],
[
"Step 2: 计算亚历克斯的体重,即 4 × 125 - 2。",
"好的,我们来执行 Step 2:计算亚历克斯的体重,即
**4 × 125 - 2**。
### 计算步骤:
1. 先算乘法:
$ 4 \\times 125 = 500 $
2. 再减去 2:
$ 500 - 2 = 498 $
### 结果:
亚历克斯的体重是 **498**。"
],
[
"Step 3: 计算两人体重总和,即 125 + 498。",
"好的,我们来计算两人的体重总和:
**125 + 498 = 623**
所以,两人的体重总和是 **623**。"
]
]
3.2 replanner 节点
从上面的数据可以看出计划中的每一步都已经执行完毕了,那么replanner应该会给出最终的答案,那么会不会呢?先看下提示词
css
Human: 为实现既定目标,制定一个简单的分步计划。该计划应包含按顺序编号的单独任务,如果正确执行这些任务,将得出正确答案。不要添加任何多余的步骤。最后一步的结果应为最终答案。确保每个步骤都包含所需的所有信息,不要遗漏任何步骤。
你的目标是这样的:
格蕾丝体重 125 磅。亚历克斯的体重比格蕾丝体重的 4 倍少 2 磅。他们两人的体重总和是多少磅?
你的原始计划是这样的:
['Step 3: 计算两人体重总和,即 125 + 498。']
你目前已经完成了以下步骤:
[('Step 1: 确定格蕾丝的体重为 125 磅。', '好的,我们开始执行第一步:\
\
**Step 1: 确定格蕾丝的体重为 125 磅。**\
\
✅ 格蕾丝的体重已确定为 **125 磅**。\
\
接下来可以继续进行 Step 2 和 Step 3 的计算。如果你需要继续,请告诉我!'), ('Step 2: 计算亚历克斯的体重,即 4 × 125 - 2。', '好的,我们来执行 Step 2:计算亚历克斯的体重,即 \
**4 × 125 - 2**。\
\
### 计算步骤:\
1. 先算乘法: \
$ 4 \\\\times 125 = 500 $\
\
2. 再减去 2: \
$ 500 - 2 = 498 $\
\
### 结果:\
亚历克斯的体重是 **498**。'), ('Step 3: 计算两人体重总和,即 125 + 498。', '好的,我们来计算两人的体重总和:\
\
**125 + 498 = 623**\
\
所以,两人的体重总和是 **623**。')]
相应地更新你的计划。如果不需要更多步骤,且你可以回复用户,那么就这样回复。否则,完善计划。只在计划中添加那些仍需完成的步骤。不要把已经完成的步骤再纳入计划。
你必须严格按照下面其中一种JSON格式响应:
第一种 - 如果提供最终回复:
{
"action": {
"response": "你给用户的最终回复"
}
}
第二种 - 如果提供新的步骤:
{
"action": {
"steps": [
"Step 1 描述",
"Step 2 描述"
]
}
}
Ai的响应为
json
{
"action": {
"response": "格蕾丝的体重是 125 磅,亚历克斯的体重是 498 磅。两人体重的总和是 **623 磅**。"
}
}
可以看到,Ai这里给出了最终的答案,下面就交给终止判断节点了
3.3 终止判断
由于response中有了数据,那么就可以结束整个流程了,最后把response的内容作为答案返回给用户
总结
从上面的具体解析我们可以更加深刻的理解计划和执行架构是怎么运行的,其中核心组件负责的功能如下
- 初始计划节点: 负责用户目标的细致拆解,给出可执行步骤的具体规划,是目标能够达成的关键!
- 执行节点:这部分仅仅是从计划中取出一步进行执行,需要Ai具有较强的指令遵循能力,这部分也会涉及到工具的调用,不过本文中没有体现
- 重新规划节点:负责整理上面用户和Ai的响应内容,重新规划出新的计划,或者总结用户的答案
- 是否终止: 这部分在本文中比较简单,我们仅仅依靠重新规划节点的输出做出判定,真实业务中可能会较复杂。