智能体实战之autogen:ReAct 智能体

autogen 介绍

AutoGen 是微软发布的一个开源的多代理对话框架,可以对标LangGraph,目标是帮助开发者创建基于大型语言模型(LLM)的复杂智能应用。它的设计初衷是简化多智能体系统的开发,使得多个具有不同功能和角色的智能代理(Agent)可以通过自然语言进行交流和协作,共同完成复杂任务。

AutoGen可以快速的搭建多智能体系统,对智能体开发接口进行了高度抽象和封装,程序员可以很快上手,同时用户也可以开发自定义的智能体。另外,它几乎支持所有主流的模型供应商的接口。

本文将基于AutoGen最新的接口(version 0.7.2)来实操如何通过AutoGen来实现ReAct智能体。

什么是ReAct?

此处不得不介绍下什么是ReAct。

ReAct 是一种用于大语言模型(LLMs)的提示词技术,它的全称是 Reasoning and Acting。这个方法的核心思想是让模型交替进行“推理”(Reasoning)和“行动”(Acting),从而更有效地完成任务。

以下是 ReAct 的工作原理和主要优势:

工作原理

简单来说,ReAct 提示词就像是给模型一个任务清单,指导它一步一步地解决问题:

  1. 推理 (Reasoning): 模型首先会生成一个内部思考步骤(Thought),用来分析当前问题,决定下一步该做什么。这个步骤就像是模型的“内心独白”。
  2. 行动 (Acting): 接下来,模型会基于这个思考,执行一个具体的“行动”(Action)。这个行动通常是调用外部工具,比如搜索引擎、计算器或数据库。
  3. 观察 (Observation): 模型会接收到行动的结果(Observation),例如搜索引擎返回的网页内容或计算器的结果。
  4. 循环往复: 模型会根据这个新的观察结果,再次进入“推理”阶段,然后是“行动”,直到任务完成。

这个循环(Thought -> Action -> Observation -> Thought…)让模型能够动态地规划和调整自己的策略,而不是像传统方法那样一次性生成所有内容。

我已经在文章《提示词工程-ReAct提示词:让智能体像人一样思考和行动》做了详细的介绍。

开发环境准备

在开始之前请准备好python的开发环境,一般建议使用anaconda搭建虚拟开发环境,当然可以选择安装miniconda,安装会比较快:

  • Linux 或者 Windows WSL
  • python 3.10+
  • miniconda/anaconda

请分别参考相应的软件文档来安装。

可以使用以下命令来安装本地的虚拟环境:


#创建autogen的虚拟环境
conda create --name autogen python=3.11

#进入该虚拟环境
conda activate autogen

另外一种选择,在能够访问外网的情况下,申请是用google的colab账户,colab可以快速创建notebook并关联虚拟环境。

google colab的地址是:https://colab.research.google.com/

安装AutoGen和相关依赖包

在虚拟环境中安装AutoGen和相关的依赖包,可以使用以下命令:


pip install -U "autogen-agentchat" "autogen-ext[openai]" "tavily-python"

autogen-agentchat是AutoGen的核心包,autogen-ext[openai]是OpenAI的扩展包,tavily-python是Tavily的Python SDK。

如果你需要使用其他模型供应商的接口,可以安装对应的扩展包,例如:


pip install -U "autogen-ext[azure]"  # Azure OpenAI
pip install -U "autogen-ext[anthropic]"  # Anthropic
pip install -U "autogen-ext[ollama]"  # Ollama

导入相关包

在你的Python脚本或者Jupyter Notebook中导入相关的包:


from autogen_agentchat.agents import AssistantAgent, UserProxyAgent
from autogen_agentchat.conditions import TextMentionTermination
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.ui import Console
from autogen_ext.models.openai import OpenAIChatCompletionClient
from google.colab import userdata # 如果在Google Colab中使用,请不要直接使用API key,而是将API key存储在colab的secret中,使用该库的userdata.get('GEMINI_KEY')来获取
from tavily import TavilyClient

创建ReAct提示词

ReAct提示词是ReAct智能体的核心部分,它定义了智能体如何进行推理和行动。以下提示词是一个基本的ReAct提示词模板,你可以根据需要进行修改和扩展,该提示词指导模型通过思考->使用工具进行观察->思考->制定下一步计划->观察不断迭代来达到最终的预期结果。


# NOTE: this ReAct prompt is adapted from Langchain's ReAct agent: https://github.com/langchain-ai/langchain/blob/master/libs/langchain/langchain/agents/react/agent.py#L79
react_prompt = """
Answer the following questions as best you can. You have access to tools provided:

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take
Action Input: the input to the action
Observation: the result of the action
... (this process can repeat multiple times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

"""

创建智能体

接下来,我们将使用AutoGen创建一个ReAct智能体,该智能体将使用Google的Gemini模型和Tavily搜索引擎来回答问题。

AssistantAgent是AutoGen中用于创建智能体的类,它可以使用不同的模型客户端和工具。我们将使用OpenAIChatCompletionClient作为模型客户端,并创建一个搜索工具来获取外部信息。

RoundRobinGroupChat是AutoGen中用于创建团队的类,它可以让多个智能体协同工作。我们将使用TextMentionTermination作为终止条件,当用户提到“Final Answer”时,智能体将结束对话。

在使用以下代码时,请确保你已经在Google Colab中设置了你的API密钥,或者在本地环境中设置了环境变量。同时请申请Tavily的API密钥,并将其存储在userdata中。

google gemini api key可以在Google Cloud Console中申请,Tavily的API密钥可以在Tavily官网中申请。


## 创建查询工具
async def search_tool(query: str) -> str:
    return TavilyClient.get_search_context(query=query, search_depth="advanced")

# Create the agents.
model_client = OpenAIChatCompletionClient(model="gemini-2.0-flash",api_key=userdata.get('GEMINI_KEY'))

# Define an AssistantAgent with the model, tool, system message, and reflection enabled.
# The system message instructs the agent via natural language.
agent = AssistantAgent(
    name="react_agent",
    model_client=model_client,
    tools=[search_tool],
    system_message=react_prompt,
    reflect_on_tool_use=True,
    model_client_stream=True,  # Enable streaming tokens from the model client.
)

# Create the termination condition which will end the conversation when the user says "APPROVE".
termination = TextMentionTermination("Final Answer")

# Create the team.
team = RoundRobinGroupChat([agent], termination_condition=termination)

# Run the conversation and stream to the console.
stream = team.run_stream(task="请你详细介绍下autogen智能体编程框架并且使用中文?")
# Use asyncio.run(...) when running in a script.
await Console(stream)
await model_client.close()

智能体输出结果

运行上述代码后,你将看到智能体的输出结果。智能体会根据ReAct提示词进行推理和行动,最终给出一个详细的回答。

以下是一个可能的输出结果示例:

---------- TextMessage (user) ---------- 
请你详细介绍下autogen智能体编程框架并且使用中文? 
---------- ModelClientStreamingChunkEvent (react_agent) ---------- 
Thought: 我需要使用search_tool 来搜索关于 autogen 智能体编程框架的信息,然后用中文详细介绍它。 
Action: search_tool Action Input: autogen 智能体编程框架 介绍 
Observation: ```json { "answer": "AutoGen 是一个框架,它允许开发具有多个能够通过自然语言相互交流的智能体的应用程序。它支持各种通信模式,可以无缝地集成人类参与。AutoGen 智能体是可定制的、会话的,并且可以连接到外> 部世界以增强其能力。通过利用大型语言模型、工具使用和人类参与,AutoGen 智能体可以以经济高效的方式自动执行各种任务。\n\nAutoGen 的主要特点包括:\n\n* **多个智能体:**AutoGen 允许您创建具有不>同角色和职责的多个智能体。这些智能体可以相互交流以解决问题、完成任务和生成内容。\n* **自然语言通信:**智能体使用自然语言相互交流,这使得它们易于理解和交互。\n* **可定制性:**您可以自定义智能体的角色、职责和行为以满足您的特定需求。\n* **工具使用:**智能体可以访问各种工具,例如搜索引擎、计算器和 API。这使它们能够执行更复杂的任务。\n* **人类参与:**人类可以随时参与智能体之间的对话。这允许人类指导智能体、提供反馈和解决问题。\n* **成本效益:**AutoGen 可以帮助您以经济高效的方式自动执行各种任务。通过利用大型语言模型、工具使用和人类参与,您可以减少对手动工作的需求。\n\nAutoGen 可以用于各种应用,包括:\n\n* **代码生成:**AutoGen 可以用于自动生成代码。例如,您可以使用 AutoGen 创建一个可以生成 Python 代码来解决数学问题的智能体。\n* **问答:**AutoGen 可以用于构建问答系统。例如,您可以使用 AutoGen 创建一个可以回答有关特定主题的问题的智能体。\n* **文本摘要:**AutoGen 可以用于自动生成文本摘要。例如,您可以使用 AutoGen 创建一个可以总结新闻文章的智能体。\n* **机器翻译:**AutoGen 可以用于自动翻译文本。例如,您可以使用 AutoGen 创建一个可以将英语翻译成法语的智能体。\n\n要开始使用 AutoGen,您需要安装 AutoGen 软件包。您可以使用 pip 安装 AutoGen:\n\n``bash\npip install autogen\n``\n\n安装 AutoGen 后,您可以开始创建您的第一个 AutoGen 智能体。有关如何创建 AutoGen 智能体的更多信息,请参阅 AutoGen 文档。\n\n以下是一些有用的资源,可帮助您开始使用 AutoGen:\n\n* AutoGen 网站:[https://microsoft.github.io/autogen/](https://microsoft.github.io/autogen/)\n* AutoGen 文档:[https://microsoft.github.io/autogen/docs/](https://microsoft.github.io/autogen/docs/)\n* AutoGen 示例:[https://github.com/microsoft/autogen/tree/main/notebook](https://github.com/microsoft/autogen/tree/main/notebook)" }
```

Thought: 我现在可以基于搜索结果用中文详细介绍 Autogen 智能体编程框架。
Final Answer: AutoGen 是一个框架,它允许开发具有多个能够通过自然语言相互交流的智能体的应用程序。它支持各种通信模式,可以无缝地集成人类参与。AutoGen 智能体是可定制的、会话的,并且可以连接到外部世界以增强其能力。通过利用大型语言模型、工具使用和人类参与,AutoGen 智能体可以以经济高效的方式自动执行各种任务。

AutoGen 的主要特点包括:

* **多个智能体:**AutoGen 允许您创建具有不同角色和职责的多个智能体。这些智能体可以相互交流以解决问题、完成任务和生成内容。
* **自然语言通信:**智能体使用自然语言相互交流,这使得它们易于理解和交互。
* **可定制性:**您可以自定义智能体的角色、职责和行为以满足您的特定需求。
* **工具使用:**智能体可以访问各种工具,例如搜索引擎、计算器和 API。这使它们能够执行更复杂的任务。
* **人类参与:**人类可以随时参与智能体之间的对话。这允许人类指导智能体、提供反馈和解决问题。
* **成本效益:**AutoGen 可以帮助您以经济高效的方式自动执行各种任务。通过利用大型语言模型、工具使用和人类参与,您可以减少对手动工作的需求。

AutoGen 可以用于各种应用,包括:

* **代码生成:**AutoGen 可以用于自动生成代码。例如,您可以使用 AutoGen 创建一个可以生成 Python 代码来解决数学问题的智能体。
* **问答:**AutoGen 可以用于构建问答系统。例如,您可以使用 AutoGen 创建一个可以回答有关特定主题的问题的智能体。
* **文本摘要:**AutoGen 可以用于自动生成文本摘要。例如,您可以使用 AutoGen 创建一个可以总结新闻文章的智能体。
* **机器翻译:**AutoGen 可以用于自动翻译文本。例如,您可以使用 AutoGen 创建一个可以将英语翻译成法语的智能体。

要开始使用 AutoGen,您需要安装 AutoGen 软件包。您可以使用 pip 安装 AutoGen:

```bash
pip install autogen
```

安装 AutoGen 后,您可以开始创建您的第一个 AutoGen 智能体。有关如何创建 AutoGen 智能体的更多信息,请参阅 AutoGen 文档。

以下是一些有用的资源,可帮助您开始使用 AutoGen:

* AutoGen 网站:[https://microsoft.github.io/autogen/](https://microsoft.github.io/autogen/)
* AutoGen 文档:[https://microsoft.github.io/autogen/docs/](https://microsoft.github.io/autogen/docs/)
* AutoGen 示例:[https://github.com/microsoft/autogen/tree/main/notebook](https://github.com/microsoft/autogen/tree/main/notebook)

面向智能体编程-构建有效的智能体

面向智能体编程-创建有效的智能体

本文翻译自build effective agents

译者荐语

随着我对提示词工程及智能体编程有了更深的理解,我越来越肯定的是,智能体编程应该是一种新的编程模式。而哪些已有的框架,比如低代码平台Coze,dify,以及langchain,autogen,multi-agent框架掩盖了过多的细节,开发者无法触及智能体本身运作的细节。另外这些框架试图提供一种通用的解决方案,随着时间的推移,面对不同的需求,增加更多的自由和可定制后,框架本身会变得复杂和累赘。

作为开发者,就如本文得建议,我更愿意直接使用大模型的API开发智能应用,这样会给我更加灵活得自由度,同时在实践过程中,我能更好的学习和理解智能体的运作方式。

正文

在过去的一年中,我们与来自各行各业的数十个团队合作,开发基于大语言模型(LLM)的智能体。我们发现,最成功的实现并非依赖复杂的框架或特定的专业化库,而是基于简单、可组合的模式构建而成。

在本文中,我们将分享与客户合作以及自主开发智能体的经验,并为开发者提供构建高效智能体的实用建议。

什么是智能体?

“智能体”(Agent)有多种定义。一些客户将其定义为完全自主的系统,这些系统能够长时间独立运行,利用各种工具完成复杂任务。另一些客户则将“智能体”用于描述更具指导性的实现,这些实现遵循预定义的工作流。在 Anthropic,我们将所有这些变体统称为智能体系统(agentic systems),但在架构上对工作流和智能体做了重要的区分:

  • 工作流(Workflows) 是指通过预定义的代码路径来编排 LLM 和工具的系统。
  • 智能体(Agents) 则是指由 LLM 动态指导其自身流程和工具使用的系统,它们能够自主决定如何完成任务。

接下来,我们将详细探讨这两种智能体系统。在附录 1(“实践中的智能体”)中,我们还描述了客户在两个特定领域中使用这些系统的实际价值。

什么时候使用智能体?

在使用 LLM 构建应用时,我们建议尽可能找到最简单的解决方案,只有在必要时才增加复杂性。这可能意味着完全不构建智能体系统。智能体系统通常以更高的延迟和成本为代价来换取更好的任务性能,因此需要仔细考虑这种权衡何时是值得的。

当需要更复杂的系统时,工作流为定义明确的任务提供了可预测性和一致性,而智能体则适用于需要大规模灵活性和模型驱动决策的场景。然而,对于许多应用来说,通过检索和上下文示例优化单次 LLM 调用通常已经足够。

何时以及如何使用框架

有许多框架可以简化智能体系统的实现,包括:

  • LangChainLangGraph
  • 亚马逊 Bedrock 的 AI Agent 框架
  • Rivet,一种可视化拖放式 LLM 工作流构建工具;
  • Vellum,另一个用于构建和测试复杂工作流的可视化工具。

这些框架通过简化调用 LLM、定义和解析工具以及链接调用等标准底层任务,使开发者能够轻松上手。然而,它们往往会引入额外的抽象层,这可能掩盖底层的提示(prompts)和响应,增加调试的难度。此外,这些框架可能让开发者倾向于增加不必要的复杂性,而实际上更简单的设置可能已经足够。

我们建议开发者从直接使用 LLM API 开始:许多模式只需要几行代码即可实现。如果您决定使用框架,请确保理解其底层代码。对框架内部逻辑的误解是客户常见的错误来源之一。

更多示例实现,请参考我们的实践手册(Cookbook)

构建块、工作流和代理

在本节中,我们将探讨生产环境中常见的智能体系统模式。从最基础的构建模块——增强型 LLM(Augmented LLM)开始,逐步增加复杂性,涵盖从简单的组合式工作流到自主智能体的各个层级。

构建模块:增强型 LLM

智能体系统的基本构建模块是通过检索、工具和记忆等增强功能扩展的 LLM。当前的模型能够主动利用这些功能,例如生成自己的搜索查询、选择合适的工具以及决定需要保留哪些信息。

我们建议将实现重点放在以下两个关键方面:

  1. 根据您的具体用例定制这些增强功能。
  2. 确保它们为 LLM 提供一个简单且文档齐全的接口。

虽然实现这些增强功能的方法有很多,但其中一种方法是通过我们最近发布的 Model Context Protocol。该协议允许开发者通过简单的客户端实现,集成到一个不断扩展的第三方工具生态系统中。

在本文的剩余部分中,我们将假设每次 LLM 调用都能够访问这些增强功能。

工作流:提示链(Prompt Chaining)

提示链是一种将任务分解为一系列步骤的工作流,每次 LLM 调用都会处理前一步的输出。在每个中间步骤,可以添加程序化检查(见下图中的“gate”),以确保整个过程保持在正确轨道上。

何时使用此工作流

该工作流非常适合那些可以轻松且清晰地分解为固定子任务的情况。其主要目标是通过将每次 LLM 调用转化为更简单的任务,从而在提高准确性和降低延迟之间进行权衡。

提示链适用的示例:

  • 生成营销文案,然后将其翻译成另一种语言。
  • 撰写文档大纲,检查该大纲是否符合某些标准,然后根据大纲撰写文档。

工作流:路由(Routing)

路由是一种将输入分类并引导其进入专门后续任务的工作流。该工作流允许关注点的分离,从而构建更专业化的提示。如果没有这种工作流,针对某种输入进行优化可能会影响其他输入的性能。

路由工作流

何时使用此工作流:
路由适用于复杂任务,在这些任务中,存在不同类别,并且可以通过 LLM 或更传统的分类模型/算法准确处理分类。

路由适用的示例:

  • 将不同类型的客户服务查询(一般问题、退款请求、技术支持)引导到不同的下游流程、提示和工具中。
  • 将简单/常见问题路由到小型模型(如 Claude 3.5 Haiku),而将困难/不寻常的问题路由到更强大的模型(如 Claude 3.5 Sonnet),以优化成本和速度。

工作流:并行化(Parallelization)

LLM 有时可以同时处理任务,并以编程方式汇总它们的输出。这种工作流的并行化表现为两种主要变体:

  • 分段(Sectioning):将任务分解为独立的子任务,并行运行。
  • 投票(Voting):多次运行相同的任务,以获得多样化的输出。

并行化工作流

何时使用此工作流:
并行化在以下情况下效果良好:分割的子任务可以并行处理以提高速度,或者当需要多个视角或尝试以获得更高置信度的结果时。对于具有多个考虑因素的复杂任务,当每个考虑因素由单独的 LLM 调用处理时,LLM 通常表现更佳,这样可以集中注意力在每个特定方面。

并行化适用的示例:

  • 分段(Sectioning):

    • 实施保护机制,其中一个模型实例处理用户查询,而另一个模型实例筛查不当内容或请求。这种方式通常比让同一个 LLM 调用同时处理保护机制和核心响应更有效。
    • 自动化评估 LLM 性能的评估,其中每个 LLM 调用评估给定提示下模型性能的不同方面。
  • 投票(Voting):

    • 检查代码的漏洞,多个不同的提示对代码进行审查,并在发现问题时标记代码。
    • 评估给定内容是否不当,多个提示评估不同方面或要求不同的投票阈值,以平衡假阳性和假阴性。

工作流:协调者-工作者(Orchestrator-Workers)

在协调者-工作者工作流中,中央 LLM 动态地将任务分解,委派给工作者 LLM,并综合它们的结果。

协调者-工作者工作流

何时使用此工作流:
该工作流非常适合复杂任务,其中无法预测所需的子任务(例如,在编码中,需要更改的文件数量以及每个文件的更改性质可能取决于具体任务)。虽然从拓扑上看与并行化类似,但其关键区别在于灵活性——子任务并不是预先定义的,而是由协调者根据具体输入确定的。

协调者-工作者适用的示例:

  • 对每次涉及对多个文件进行复杂更改的编码产品。
  • 涉及从多个来源收集和分析信息以获取可能相关信息的搜索任务。

工作流:评估者-优化器(Evaluator-Optimizer)

在评估者-优化器工作流中,一个 LLM 调用生成响应,而另一个 LLM 在循环中提供评估和反馈。

评估者-优化器工作流

何时使用此工作流:
该工作流在有明确评估标准的情况下特别有效,并且当迭代优化能够提供可测量的价值时使用效果最佳。适合的两个标志是:首先,当人类表达反馈时,LLM 的响应可以明显改善;其次,LLM 能够提供这样的反馈。这类似于人类作家在撰写精炼文档时可能经历的迭代写作过程。

评估者-优化器适用的示例:

  • 文学翻译,其中翻译 LLM 可能最初无法捕捉到某些细微差别,但评估者 LLM 可以提供有用的批评。
  • 复杂的搜索任务,要求多轮搜索和分析以收集全面的信息,在这种情况下,评估者决定是否需要进一步的搜索。

智能体

随着 LLM 在理解复杂输入、进行推理和规划、可靠地使用工具以及从错误中恢复等关键能力上的成熟,智能体(Agents)在生产环境中逐渐崭露头角。智能体通常以来自人类用户的命令或互动讨论开始工作。一旦任务明确,智能体便能独立规划和操作,可能会在执行过程中返回人类获取进一步信息或判断。在执行过程中,智能体在每个步骤中获得环境的“真实情况”(如工具调用结果或代码执行)以评估其进展至关重要。智能体可以在检查点处或遇到阻碍时暂停以获取人类反馈。任务通常在完成时终止,但通常会设置停止条件(如最大迭代次数)以保持对过程的控制。

智能体能够处理复杂的任务,但其实现通常是简单的。它们通常仅仅是基于环境反馈在循环中使用工具的 LLM。因此,清晰且周到地设计工具集及其文档至关重要。我们在附录 2(“为您的工具进行提示工程”)中扩展了工具开发的最佳实践。

何时使用智能体

智能体可以用于开放性问题,在这些问题中,难以或不可能预测所需的步骤数量,并且无法硬编码固定路径。LLM 可能会进行多轮操作,因此必须对其决策能力有一定程度的信任。智能体的自主性使其非常适合在可信环境中扩展任务。

智能体的自主特性意味着更高的成本,以及可能导致错误累积的风险。因此,我们建议在沙盒环境中进行广泛测试,并设置适当的保护措施。

智能体适用的示例:

以下示例来自我们自己的实现:

  • 编码智能体,用于解决 SWE-bench 任务,这些任务涉及根据任务描述对多个文件进行编辑。
  • 我们的“计算机使用”参考实现,其中 Claude 使用计算机完成任务。

组合和定制这些模式

这些构建模块并不是固定不变的规范。它们是开发者可以根据不同用例进行塑造和组合的常见模式。成功的关键在于,和任何 LLM 特性一样,测量性能并对实现进行迭代。再次强调:您应该只在明显改善结果时考虑增加复杂性。

总结

在 LLM 领域,成功并不在于构建最复杂的系统,而在于为您的需求构建合适的系统。首先从简单的提示开始,通过全面评估进行优化,仅在更简单的解决方案无法满足需求时,才添加多步骤的智能体系统。

在实施智能体时,我们努力遵循三个核心原则:

  1. 保持智能体设计的简单性。
  2. 优先考虑透明性,明确显示智能体的规划步骤。
  3. 通过全面的工具文档和测试,仔细设计您的智能体-计算机接口(ACI)。

框架可以帮助您快速入门,但在进入生产阶段时,不要犹豫减少抽象层次,使用基本组件进行构建。遵循这些原则,您可以创建不仅强大而且可靠、可维护且受到用户信任的智能体。

致谢

撰写者:Erik Schluntz 和 Barry Zhang。本文基于我们在 Anthropic 构建智能体的经验,以及客户分享的宝贵见解,我们对此深表感激。

附录 1:智能体的实践

与客户的合作揭示了两个特别有前景的 AI 智能体应用,展示了上述模式的实际价值。这两个应用都说明了智能体在需要对话和行动的任务中如何增加价值,具有明确的成功标准,能够启用反馈循环,并集成有意义的人类监督。

A. 客户支持
客户支持结合了熟悉的聊天机器人界面与通过工具集成增强的功能。这是更开放的智能体的自然契合点,因为:

  • 支持交互自然遵循对话流程,同时需要访问外部信息和操作;
  • 可以集成工具来提取客户数据、订单历史和知识库文章;
  • 诸如处理退款或更新票务等操作可以以编程方式处理;
  • 成功可以通过用户定义的解决方案明确衡量。

一些公司通过基于使用的定价模型展示了这种方法的可行性,仅对成功的解决方案收费,显示出对其智能体有效性的信心。

B. 编码智能体
软件开发领域展现出 LLM 特性的显著潜力,能力从代码补全发展到自主问题解决。智能体特别有效,因为:

  • 代码解决方案可以通过自动化测试进行验证;
  • 智能体可以使用测试结果作为反馈对解决方案进行迭代;
  • 问题空间定义明确且结构化;
  • 输出质量可以客观衡量。

在我们自己的实现中,智能体现在可以仅根据拉取请求描述解决真实的 GitHub 问题,符合 SWE-bench Verified 基准。然而,尽管自动化测试有助于验证功能,但人类审核在确保解决方案与更广泛的系统要求一致方面仍然至关重要。

附录 2:为您的工具进行提示工程

无论您正在构建何种智能体系统,工具都可能是您智能体的重要组成部分。工具使 Claude 能够通过在我们的 API 中指定其确切结构和定义与外部服务和 API 进行交互。当 Claude 做出响应时,如果计划调用工具,它将包括一个工具使用块在 API 响应中。工具定义和规范应与整体提示一样,得到同样多的提示工程关注。在这个简短的附录中,我们描述了如何为您的工具进行提示工程。

指定同一操作通常有几种方法。例如,您可以通过编写差异(diff)来指定文件编辑,或者通过重写整个文件。对于结构化输出,您可以在 markdown 或 JSON 中返回代码。在软件工程中,这些差异在外观上是微不足道的,可以无损转换。然而,某些格式对于 LLM 来说比其他格式更难写。编写差异要求在编写新代码之前知道块头中有多少行在变化。将代码写在 JSON 中(相比于 markdown)需要额外转义换行符和引号。

我们对工具格式的建议如下:

  • 给模型足够的 tokens,以便在写作时能够“思考”,而不是写进死胡同。
  • 保持格式接近模型在互联网上自然出现的文本格式。
  • 确保没有格式“开销”,例如必须准确计算数千行代码或转义任何写的代码。

一个经验法则是考虑人机接口(HCI)投入多少精力,并计划在创建良好的智能体-计算机接口(ACI)上投入同样多的精力。以下是一些想法:

  • 设身处地为模型着想。根据描述和参数,使用这个工具是否显而易见,还是需要仔细思考?如果是这样,那么对于模型来说也可能是如此。良好的工具定义通常包括示例用法、边界情况、输入格式要求以及与其他工具的明确界限。
  • 如何改变参数名称或描述以使其更明显?将其视为为您团队中的初级开发人员撰写出色的文档字符串。这在使用许多相似工具时尤其重要。
  • 测试模型如何使用您的工具:在我们的工作台中运行多个示例输入,查看模型的错误,并进行迭代。
  • 采用防错机制(Poka-yoke)设计您的工具。改变参数,使其更难出错。

在为 SWE-bench 构建智能体时,我们实际上在优化工具上花费的时间比在整体提示上更多。例如,我们发现模型在智能体移动出根目录后使用相对文件路径的工具时会出错。为了解决这个问题,我们将工具更改为始终要求绝对文件路径——我们发现模型以这种方式使用毫无问题。

使用 goplantuml 生成 Golang 项目的类图

什么是 goplantuml

goplantuml 是一个面向 Golang 开发者的
工具,用于将代码的结构(包括包、结构体、接口及其关系)转化为
PlantUML 可视化类图。通过解析 Golang 源码,
goplantuml自动生成描述代码架构的 .puml 文件,帮助开发者更直观地理解项目结
构。

适合 Golang 开发者的场景

在 Golang 开发中,项目结构往往由多个包(package)和类型(如结构体、接口)组成,
随着项目规模扩大,理解代码的架构变得更加复杂。 goplantuml 的目标是通过自动化的方
式生成类图,让开发者能够:

  • 快速理解项目的模块划分和类型关系。
  • 发现潜在的依赖问题或循环引用。
  • 为团队新成员提供清晰的架构概览。
  • 辅助文档编写和代码审查。

核心功能

  • 解析 Golang 源代码:支持解析结构体、接口及它们之间的嵌套关系和依赖。
  • 生成 UML 类图:输出标准的 .puml 文件,可以直接用 PlantUML 渲染为图像。
  • 隐藏私有成员:支持 -hide-private-members 参数,仅显示导出的(public)成
    员,保护代码隐私。
  • 递归处理目录:支持递归扫描目录,分析所有相关的 Golang 源码文件。
  • 灵活的输出控制:开发者可以使用多种选项调整生成的类图细节,如排除特定的包或
    目录。

安装与使用

安装

通过 go install 命令快速安装:

go get github.com/jfeliu007/goplantuml/parser
go install github.com/jfeliu007/goplantuml/cmd/goplantuml@latest

确保 $GOPATH/bin 已添加到环境变量中。

基本用法

以下命令生成类图的 .puml 文件:

goplantuml [-recursive] path/to/gofiles path/to/gofiles2
goplantuml [-recursive] path/to/gofiles path/to/gofiles2 > diagram_file_name.puml

Usage of goplantuml:
  -aggregate-private-members
        Show aggregations for private members. Ignored if -show-aggregations is not used.
  -hide-connections
        hides all connections in the diagram
  -hide-fields
        hides fields
  -hide-methods
        hides methods
  -ignore string
        comma separated list of folders to ignore
  -notes string
        Comma separated list of notes to be added to the diagram
  -output string
        output file path. If omitted, then this will default to standard output
  -recursive
        walk all directories recursively
  -show-aggregations
        renders public aggregations even when -hide-connections is used (do not render by default)
  -show-aliases
        Shows aliases even when -hide-connections is used
  -show-compositions
        Shows compositions even when -hide-connections is used
  -show-connection-labels
        Shows labels in the connections to identify the connections types (e.g. extends, implements, aggregates, alias of
  -show-implementations
        Shows implementations even when -hide-connections is used
  -show-options-as-note
        Show a note in the diagram with the none evident options ran with this CLI
  -title string
        Title of the generated diagram
  -hide-private-members
        Hides all private members (fields and methods)

常用选项

  • -recursive:递归扫描子目录。
  • -hide-private-members:隐藏私有成员,减少输出中的噪声。
  • -ignore "<dir>":忽略特定目录,避免解析不相关的代码。

使用示例

假设项目目录如下:

project/
│
├── main.go
├── models/
│   ├── user.go
│   └── order.go
└── services/
    ├── user_service.go
    └── order_service.go

执行命令:

goplantuml -recursive ./project > diagram.puml

生成的类图可以清晰展示 modelsservices 包中的依赖关系,以及结构体之间的嵌
套和接口实现。

体验

可以通过 dumels 在线体验这个工具。

优势与局限

优势

  • 快速直观:适合中大型项目的架构分析。
  • 自动化:省去手动绘制图表的繁琐步骤。
  • 社区支持:开源项目,活跃的开发与问题跟踪。

局限

  • 不支持动态依赖:工具仅解析静态代码,无法捕获运行时动态关系。
  • 复杂性限制:对于特别大的项目,生成的类图可能过于复杂,建议分模块生成。

总结

goplantuml 是 Golang 开发者的优秀辅助工具,为代码架构的可视化和理解提供了强有力
的支持。如果你正在开发一个 Golang 项目并希望更直观地呈现其架构,不妨试试
goplantuml.

项目主页:goplantuml GitHub

提示词工程-提示词链(prompt chain)

提示词工程-链式提示词(prompt chain)

罗伯特·C·马丁(Robert C. Martin),一位著名的工程师,曾经说过:“函数应该只做一件
事。它们应该做好这件事。而且只做这件事。” 同样的原则也适用于提示词工程。

提示词应该只做一件事。它们应该做好这件事。而且只做这件事。

如果在同一个提示词中包含了多个任务或者指令时,AI大模型的输出将会不敬人意。这样的
提示词会让AI大模型陷入困境,无法正确理解你的意图。因此,更好的方法是将较大的提示
分解为多个较小的提示,创建动态提示链。每个链式提示词只包含一个任务或者指令。

提示链通过让模型专注于链中每个子任务,实现了对输出的更细粒度控制。

什么是提示词链?

提示词链将一个任务拆解为一系列较小的子任务,每个子任务由独立的提示词处理。

提示词链可以通过多种方式实现,包括结合其他提示工程方法,例如自动链式思维提示
(automatic chain-of-thought prompting)、由易到难提示(least-to-most
prompting)以及自一致性提示(self-consistency prompting)。

例如,在文本摘要任务中,可以通过提示链实现以下步骤:

  1. 创建初步摘要草稿
  2. 对摘要进行批评与改进建议
  3. 检查摘要是否存在事实错误
  4. 根据批评和反馈生成精炼后的摘要

每个步骤都由独立的提示处理,使模型能够完全专注于单个子任务,从而提高整体输出质量。

提示链对于完成复杂任务非常有用,因为这些任务如果使用一个非常详细的单一提示,LLM
可能难以应对。在提示链中,每个链式提示都会对生成的响应进行转换或额外处理,直到最
终达到期望的结果。

除了获得更好的性能,提示链还能提高LLM应用的透明性、可控性和可靠性。这意味着你可
以更轻松地调试模型响应中的问题,并分析和改进需要优化的各个阶段的性能。

提示链在构建基于LLM的对话助手、改善应用的个性化和用户体验方面尤其有用。

提示链的工作原理

下图展示了提示链的基本工作原理。首先使用提示链时,需要将任务拆解为多个子任务,子
任务的粒度足够小,每个子任务都由独立的提示处理。上一个提示词的输出作为下一个提示
词的输入。

提示链的好处

  • 增强专注力:通过将庞大的提示拆解为有针对性的子提示,模型可以将注意力集中在
    每个任务上,一次只处理一个任务。

  • 更高质量的输出:如后续研究所示,提示链的效果明显优于庞大的提示。研究结果表
    明,提示链能够提供更精确和有效的输出。

  • 更易理解:理解复杂的庞大提示非常具有挑战性,这也使得团队协作变得困难,输出
    质量难以提升。通过利用提示链,所使用的提示更加专注,因此更容易理解。

  • 广泛适用:提示链几乎可以提高任何复杂任务的输出质量。无论是摘要、问题解决还
    是创意写作,通过提示链将过程拆解为更小的步骤都能帮助模型提高表现。

提示链在生成长篇输出或处理包含大量上下文的提示时特别有益。

何时使用提示词链

提示链本身其实也是模拟了人类思维的链式思考过程。在人类思维中,我们会将一个大的
任务拆解为多个小的子任务,然后逐个完成。这种思维方式使我们能够更好地处理复杂的问题。

但是当你的任务非常简单或者只包含一个步骤时,使用提示链可能会增加不必要的复杂性。

如果你在决定是创建提示链还是使用单个提示时,可以考虑以下四个问题:

  • 我的任务涉及多个指令吗? 例如,“阅读这篇文章并生成摘要,然后创建一个相关的
    LinkedIn帖子。”

  • 我的任务需要多次转换吗? 比如,如果输出需要以多种方式进行处理或重新格式
    化。

  • 我对单一提示的结果不满意吗? 将任务拆解为子任务,并通过提示链逐个处理,可
    以帮助你找出问题所在,从而更容易修复。

  • 模型是否似乎失去焦点或忘记上下文? 如果是这样,将单个提示转化为提示链可以
    确保每个步骤都能得到模型的全神贯注。

提示链的典型示例

数据分析任务

通常在做数据分析时,我们需要进行数据提取、数据清理、数据分析和数据可视化等多个步骤。
而且每一步都要基于上一步的结果进行。因此这是一种典型的适合使用提示链的场景。

提示1:提取数据
从我们公司的数据库的导出数据 {{ 数据库数据 }} 中提取收入和用户参与数据,重点关注第一季度到第四季度的数据。

提示 2:数据转换
清理提取的数据 {{ 提取的数据 }} ,去除异常值,处理缺失值,并标准化数值字段。

提示 3:数据分析
分析清理后的数据 {{ 转换后的数据 }},以识别收入增长和用户参与度随时间的主要趋势。

提示 4:数据可视化
根据 {{ 数据分析结果 }} 创建可视化图表,突出分析中识别的趋势,重点展示收入增长和参与度激增。

代码编写任务

软件的代码编写是一个相当复杂的过程,通常需要多个步骤,例如需求分析、设计、编码、测试和部署等等。
如何将编写代码的任务交给AI大模型呢?这时候提示链就派上用场了。

如下提示链的示例,将代码编写拆解为:根据需求生成代码-》生成测试用例 -》代码检视 -》解决问题。

提示1:生成代码
请根据用户的 {{ 功能需求 }} 使用 {{ 编程语言 }} 生成相应的代码,保证生成的代码和用户需求一致。
始终使用注释来解释代码片段,如果是函数和类,请添加函数和类的注释,函数的注释包括函数名、参数、
返回值、功能描述。始终保持代码的整洁和可读性。

提示2:生成测试用例
请根据生成的代码 {{ 生成的代码 }} 生成相应的测试用例,保证测试用例能够覆盖生成的代码。

提示3:代码检视
请对 {{ 代码和测试用例 }} 进行检视,找出代码中存在的问题,并给出改进建议。

提示4:解决问题
请根据检视建议对 {{ 生成的代码 }} 和测试用例 {{ 生成的测试用例 }} 进行改进优化,同时保证测试
用例能够通过。

复杂任务拆解

如果你一开始还不太清楚如何将一个任务拆解为多个子任务,那么你也可以提示链技术借助LLM的能力,
将任务拆解为多个子任务。

系统提示词:
你是一个任务拆解专家,请根据用户的需求,将任务拆解为多个子任务。目标是尽可能的将任务拆解为
细粒度的子任务。

提示词1:
这是一个复杂任务 {{ 任务描述 }} ,将任务拆解为多个子任务或者执行步骤。

提示词2:
根据 {{ 任务拆解 }} ,列出每个子任务的具体目标和预期结果。

提示词3:
根据 {{ 任务拆解 }} ,列出每个子任务需要完成指定目标所需要使用的方法或者解决方案。

提示词4:
请根据 {{ 任务拆解 }} 和 {{ 子任务目标 }} ,列出每个子任务的执行优先级。

提示词5:
请根据 {{ 任务优先级 }} ,列出每个子任务的执行顺序。

提示词6:
请根据 {{ 任务拆解 }} 列出每个子任务需要依赖的外部资源。

参考文章:

https://www.prompthub.us/blog/prompt-chaining-guide
https://www.ibm.com/topics/prompt-chaining
https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/chain-prompts
https://www.promptingguide.ai/techniques/prompt_chaining

提示词工程-ReAct提示词(反应式)-让智能体像人一样思考和行动

文章会实时同步到我的公众号,有兴趣的童鞋扫码关注我的公众号噢:

提示词工程-ReAct

提示词工程的魅力就在于,让你可以变成现代版的祭师,能够让大模型言听计从,控制大模型做你想做的事情。

我不太确定超级智能会在未来的哪一天出现,但是从AI的快速迭代和进化来看,我们似乎可以预测,这一天不会遥远。一直以来,创造一个能够自主思考,自主学习和进化的智能体应该是AI发展的终极目标,而在这样的发展进程中,提示词工程也许只是这个进程中的一小段旅程,也许有一天,这项技术会成为历史,不过至少现在,它能够让我们对AI大模型有更加深入的了解,并且助力大模型变得更加聪明和务实。

起初,很难相信,生成模型会具有思考的能力,可当研究人员不断的探索出新的提示词技术时,这种怀疑逐渐得到了清晰的答案。

ReAct 提示词

ReAct 是 "Reasoning and Acting" 的缩写,旨在通过结合推理和行动来提高模型在复杂任务中的表现。

起初该框架由Yao 等人于2022年提出。

ReAct提示词实际上是one-shot或者few-shot提示词的一种(对于什么是one-shot,few-shot提示词,请阅读《提示词工程-少样本提示》)。
简单的说,就是给AI大模型一个思考-行动的例子,通过该例子,表明完成一件特殊任务时,思考和行动的基本框架和逻辑。ReAct本身是拟人化的,模拟人类思考和行动的基本方式,这里我可以举一个非常简单的例子,比如肚子饿了,非常想吃蛋糕:

问题:俺肚子饿了,非常想吃蛋糕。
思考:看看家里有没有买了蛋糕啊?
行动:去冰箱里看看,去厨房看看,发现没有。
思考:想吃自己做的蛋糕呢,还是去点外卖呢,哎,最近有点懒,不想动,还是外卖吧!
行动:打开手机APP,搜索蛋糕,得到种类繁多的蛋糕。
思考:我到底想吃哪一种呢,好像提拉米苏更加诱人一些。
行动:下单购买提拉米苏。
......

以上只是一个非常简单的例子,对人类来说,看起来是件非常普通和自然的事情,可是将思考方式分解,用来提示AI大模型时,会出现非常神奇的效果。

与链式思维推理类似,ReAct 是一种提示工程方法,使用少量样本学习来教模型解决问题。CoT 旨在模仿人类思考问题的方式,ReAct 也包含了这种推理元素,但更进一步地允许智能体通过文本操作与其环境进行交互。人类通过语言推理(说话或思考)来帮助制定策略和记忆事物,但我们也可以采取行动来获取更多信息并实现目标。这就是 ReAct 的基础。

ReAct 提示包括包含行动的示例,通过行动获得的观察,以及人类在过程中不同阶段的书面思考(推理策略)。LLM 学会模仿这种交替进行思考与行动的方法,使其成为环境中的智能代理。下方是 ReAct 智能体如何运作的示例,(按“思考 -> 行动 -> 观察 -> 思考”顺序)。

ReAct智能体的任务执行的大致过程:

  • 用户提出问题。
  • 思考,第一步应该做什么,决定执行动作和环境互动。
  • 行动,使用外部工具,从环境中获取需要的信息。
  • 观察,将行动的结果和收集的信息作为下一步思考的输入。
  • 思考,判断是否问题得到解决,基于上一步观察的结果,重新制定下一步需要执行的动作。
  • 重复以上循环,直到问题解决,或者得到了最终的答案,并结束。

ReAct 提示词是一种思考和行动的模版和范式,教AI大模型按照这种思维框架进行学习和解决问题,与其他提示工程不同的是,ReAct需用通过和环境的互动,获取信息,做动态的调整和规划。因此使用ReAct提示词时,同时需要实现智能体,或者智能代理,能够让智能体使用工具和外部环境进行交互。

更多ReAct提示词的例子

以下是一个来自论文的 ReAct 提示示例

你是知识问答机器人,你会帮助用户回答科学,地理等相关科普知识,给用户提供准确的答案,请以以下例子为参考,以思考-行动-观察 迭代的方式,确保你的答案是实时准确的,你可以通过搜索引擎获取外部知识。

问题:科罗拉多造山运动的东部区域延伸到的区域的海拔范围是多少? 
思考 1:我需要搜索“科罗拉多造山运动”,找出其东部区域延伸到的地方,然后找出该区域的海拔范围。 
行动 1:搜索[科罗拉多造山运动] 
观察 1:科罗拉多造山运动是科罗拉多及周边地区发生的造山事件。 
思考 2:资料中没有提到东部区域,因此我需要查找“东部区域”。 
行动 2:查找[东部区域] 
观察 2:(结果 1 / 1)东部区域延伸到大平原,被称为中央平原造山运动。 
思考 3:科罗拉多造山运动的东部区域延伸到大平原。因此,我需要搜索“大平原”的海拔范围。 
行动 3:搜索[大平原] 
观察 3:“大平原”指的是两种不同的土地区域之一。 
思考 4:我需要改为搜索“美国大平原”。 
行动 4:搜索[美国大平原] 
观察 4:大平原是大平原的一个子区域。从东到西,大平原的海拔从约 1,800 英尺(550 米)上升到 7,000 英尺(2,130 米)。 
思考 5:大平原的海拔范围是从约 1,800 英尺到 7,000 英尺,因此答案是 1,800 英尺到 7,000 英尺。 
行动 5:完成[1,800 英尺到 7,000 英尺]

通用ReAct智能体提示词模版

以下提供的提示词模版是对ReAct的抽象和总结,通过以下模版,开发人员可以定义 AI Agent的基本角色和用户场景,以及智能体能够和外界交互的可用工具集。同时指导智能体在执行特定任务时,需遵从“思考-》执行任务-》观察-》思考”的迭代过程。

以下的ReAct通用提示词可以使用在任何任务场景,程序员只要稍加调整和修改就可以应用在自己的工具和用例里。

TOOL_DESC = """{name_for_model}: Call this tool to interact with the {name_for_human} API. What is the {name_for_human} API useful for? {description_for_model} Parameters: {parameters} Format the arguments as a JSON object."""

REACT_PROMPT = """Answer the following questions as best you can. You have access to the following tools:

{tool_descs}

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can be repeated zero or more times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin!

Question: {query}"""

以下是该提示词模板的中文翻译:

TOOL_DESC = """{name_for_model}:调用此工具以与{name_for_human} API 交互。{name_for_human} API 有什么用途?{description_for_model} 参数:{parameters} 将参数格式化为 JSON 对象。"""

REACT_PROMPT = """请尽可能准确地回答以下问题。你可以使用以下工具:

{tool_descs}

使用以下格式:

问题:你必须回答的输入问题
思考:你应该始终考虑要做什么
行动:要采取的行动,应为 [{tool_names}] 之一
行动输入:行动的输入
观察:行动的结果
...(此思考/行动/行动输入/观察可以重复零次或多次)
思考:我现在知道最终答案了
最终答案:对原始输入问题的最终答案

开始!

问题:{query}"""

写在最后

由于ReAct需要通过环境来获取信息,因此从环境中获取的信息的准确性会严重影响最终的输出结果。因此开发者需要考虑环境信息的准确和可靠,才能保证ReAct应用的性能和可靠性。

ReAct的推理并不完美,但仍然是一种强大的提示工程方法,它克服了链式推理中的幻觉问题,并使大型语言模型能够成为可以与环境互动的智能体。此外,这是一种非常可解释的方法,因为智能体在行动时会输出其思维过程。

ReAct提示词可以让智能体变的更加聪明,帮助我们解决更加复杂的问题,探索ReAct智能体是一件非常有趣的过程。

我相信你一定好奇,如何使用以上的提示词模版实现一个能够自主思考的智能体,接下来后续的文章我将分析如何通过不同的Agent框架,快速的构建智能体,希望持续关注我的博客文章。

参考文章:

面向智能体编程-新一代软件架构模式

文章会实时同步到我的公众号,有兴趣的童鞋扫码关注我的公众号噢:

面向智能体编程

我将在这篇文章里提出新的概念,面向智能体编程。深度学习的理论和算法不断地发展,从而促成了生成式大模型的出现,大语言模型相比于以往的特定领域的模型,具有更加智能,通用的能力,由于其在预训练过程中,学习了大量而广泛的知识,因此大语言模型在自然语言理解和处理上产生了革命性的发展。一定意义上,大语言模型能够理解人类的意图,进行一定的思考,推理和规划,同时也具有一定的创造能力。

通过这些能力,大模型可以执行更加复杂的任务,具有更高的自主能力。可以预见的是,大模型的出现,传统的数字软件将被重新定义和重构。将大模型作为大脑,接入到领域软件,使得软件在执行任务时具有高度的自主能力,同时赋予软件自然语言交互的能力,使得人类在使用软件时,更加简洁有效,符合人与人交流的习惯。

今天面向智能体编程应用而生。

到底什么是智能体?

智能体(Agent)是计算机科学和人工智能领域中的一个关键概念,指的是能够在一定环境下自主感知、决策并采取行动的系统或实体。智能体通常具备以下核心特征:

核心特征:

  1. 自主性(Autonomy)

    • 智能体能够自主地执行任务,基于自身的感知和知识做出决策,不依赖外部直接控制。
  2. 环境感知能力(Perception)

    • 智能体能够感知其周围环境,通过传感器、输入数据等途径收集环境信息,进而对这些信息进行分析和处理。
  3. 目标导向性(Goal-Oriented)

    • 智能体具有特定的目标或任务,通过行为和决策的执行,朝向目标前进。
  4. 适应性(Adaptivity)

    • 智能体能够根据环境变化调整自身行为,适应动态环境,做出合理的响应。
  5. 交互性(Interactivity)

    • 智能体可以与环境中的其他智能体或系统交互,包括合作、竞争、通信等。

智能体的类型:

  1. 简单智能体

    • 这些智能体通常基于规则或脚本运行,在特定条件下执行预定的动作。它们的决策能力较弱,适用于确定性环境。
  2. 复杂智能体

    • 这些智能体能够在不确定性环境中执行任务,并通过机器学习、推理等方法做出更高级的决策。它们具备更强的感知、学习和适应能力。
  3. 单体智能体 vs 多智能体系统(MAS)

    • 单体智能体:单个智能体独立运作。
    • 多智能体系统:多个智能体协作或竞争,彼此影响并共享信息,以达成共同目标或各自目标。

智能体的关键在于自主性和环境适应能力,使其能够在复杂系统中替代或协助人类完成任务。

大模型时代,对智能体新的理解

大模型时代的来临,使得对智能体进行了新的定义和理解,大模型在语言理解和生成方面表现出色,使用大模型作为智能体的大脑,使得智能体能够通过自然语言进行复杂任务的处理和人机交互。

大模型赋予智能体更强的推理与决策能力,大模型的预训练数据量巨大,涵盖了各种复杂任务和知识领域,使得智能体在面对不确定或复杂问题时,能够进行深度推理,生成合理的应对策略。

大模型智能体的经典定义

大语言模型(LLM)智能体,是一种利用大语言模型进行复杂任务执行的应用。这种智能体通过结合大语言模型与关键模块,如规划和记忆,来执行任务。构建这类智能体时,LLM充当着控制中心或“大脑”的角色,负责管理完成任务或响应用户请求所需的一系列操作。这种智能体的构建,需要依赖于规划、记忆以及工具使用等关键模块。

通常,一个大语言模型智能体框架包含以下核心部分:

  • 用户请求 – 表达用户的问题或请求
  • 智能体/大脑 – 作为协调者的智能体核心
  • 规划- 助于智能体为未来行动做规划
  • 记忆 – 管理智能体的历史行为和经验

通过这些组成部分的协同工作,大语言模型智能体能够处理从简单到复杂的各种请求,不仅能够提供直接的答案,还能解决需要深度分析和多步骤操作的问题。这种智能体的能力,使其在处理复杂信息查询、数据分析和可视化表示等方面具有巨大潜力。

为什么要面向智能体编程?

如果你看过电影《星际穿越》,你一定会对电影中的智能机器人TARS印象深刻,TARS的外形独特,类似于一个方形的金属盒子,由四个长条形模块组成,能够变形以适应不同的环境和任务。导演克里斯托弗·诺兰在设计TARS时,强调了机器人的功能性而非拟人化特征。TARS能够根据需要变形,例如在海浪中变成螺旋桨,或在陡坡上提供助力。TARS不仅具备物理操作能力,还能与主角进行不失幽默的交流,帮助解决关键问题,如通过摩斯码传递信息等。

开发类似于TARS的机器人一直是人类进行人工智能研究的理想。同样,开发更加智能,自主执行任务的软件也是软件工程师的理想,当大语言模型出现时,使得这一理想又更加近了一步。

传统软件是具有局限性的。传统编程基于静态逻辑和预定义规则,通常只适合处理明确、确定的任务。而在动态的现实世界中,环境往往充满了不确定性和复杂性,需求和状态可能不断变化。面对这种情况,传统编程模式难以适应。

随着人工智能的不断发展,重塑传统软件的需求成为必然,人机交互的方式也在不断发展和演进。面向智能体编程允许开发者设计具有环境感知能力和适应能力的系统,这些系统能够根据外部环境的变化调整行为,主动应对变化。例如,自动驾驶汽车中的智能体能够实时感知交通状况,并动态调整路线和驾驶行为。

传统程序更多的是通过输入指令执行固定流程,缺乏自主性。而面向智能体编程赋予系统自主决策能力,智能体可以根据外部感知和内部状态,自主选择如何完成目标任务。这种自主性特别适用于需要自主行动或复杂推理的场景,如机器人控制、金融交易、游戏AI等。

未来,数字软件系统将逐渐具有高度自主能力,人机交互将逐渐演化为通过自然语言输入,语音输入为主。因此未来所有领域的软件将会被重构和重新定义,面向智能体编程正是因此应用而生。

面向智能体编程和面向对象编程,面向过程编程有什么不同

面向对象编程和面向过程编程准确的说是两种构建软件过程中不同的编程模式,是代码语法和程序的组织方式,更加靠近底层的代码编写层面。

  • 面向对象编程

对象通过消息传递或方法调用相互交互。程序的执行是基于调用对象的方法,并通过返回结果来推进程序流程。面向对象的执行通常是同步的,程序员定义了对象的交互方式,系统按照这些定义的规则顺序执行。

  • 面向过程编程

程序的执行是顺序和线性的,按照从上到下的过程流控制。每个过程通常通过明确的调用顺序执行,完成特定的操作后返回结果。程序的控制流通常由条件语句、循环和函数调用控制。

  • 面向智能体编程

大模型智能体经典定义所述,大模型智能体包含智能体,工具,记忆,用户交互等基本元素,这些基本元素描述了智能体应用大致上需要包含的软件组件。准确的说面向智能体编程是一种架构模式,更加偏向于软件架构的组织方式。

总之在构建智能体软件的过程中,代码的组织方式仍然可以使用面向对象编程或者面向过程编程等方式。

提示词工程

提示词工程(Prompt Engineering)是一门专注于设计和优化提示词(prompts)的方法,以最大化大语言模型(如GPT-4等)的输出效果。提示词是与模型交互的输入文本,好的提示词可以引导模型生成准确、符合需求的内容。提示词工程在多个领域应用广泛,包括生成文字、编程协助、对话机器人设计、教育等。

同时使用提示词工程的相关方法,可以使大模型具有一定的推理,思考,规划,任务决策以及工具使用等相关能力,比如推理时可以使用思维链(Chain-of-Thought)提示词,使用反应式提示(ReAct Prompting)进行决策和规划等等。

我在文章《提示词工程技术图谱-prompt engineering》中列举了基本的提示词工程相关的技术图谱。

提示词工程与智能体编程

在面向智能体编程时,使用提示词工程的相关技术可以赋予智能体规划,思考,推理及调用工具的能力,可以说提示词工程是可以赋予智能体灵魂的技术,是面向智能体编程的核心部分。

  • 定义智能体的角色
    在智能体编程中,通过提示词工程可以设定智能体的角色和语境,使其以特定的方式呈现或响应信息。这在客服、教育、甚至创意写作等智能体应用中非常关键。比如,一个面向编程的智能体可以被设定为编程导师角色,通过提示词确保智能体在解答问题时语气专业且有指导性,从而带来更好的用户体验

  • 任务分解
    使用提示词工程,可以将复杂的任务拆解成相对较小粒度的任务集合,分解后的任务更具可实施性,这将使智能体更加容易解决复杂问题。

  • 工具使用
    使用工具是智能体的一项关键技能,只有通过工具,才能实现大模型和现实世界的交互,比如访问网络,查询数据库等等。通过提示词结构化的输入输出,程序员可以定义智能体使用的工具集,智能体可以根据用户的输入动态决策使用何种工具。

  • 决策和规划
    使用思维链(Chain-of-Thought)提示词及反应式提示词(ReAct)等技术可以赋予智能体自主决策和规划的能力,决策和规划是智能体的核心大脑部分,能够使智能体具有一定的自主性,比如自适应环境的变化,在不同的环境中使用不同的工具来完成任务。将复杂的任务分解成多个步骤,一步一执行,直到完成任务。

  • 自动反馈优化
    提示词工程还可以用于优化智能体编程中的反馈循环。通过设计提示词收集用户反馈或自动化测试结果,智能体可以自动分析这些反馈信息,并调整其输出和行为。这种反馈机制有助于持续优化智能体性能,提升其响应质量。

智能体应用的基本架构

上图提出了智能体应用的全栈分层架构示意图,描述了智能体应用的基本架构。

应用层(最上层,紫色区域)

  • 包括 UI界面语音交互设备手机APP 等用户交互界面。
  • 这一层是用户直接与智能体应用互动的入口,通过不同的设备和界面实现与智能体的通信。
  • 其他应用模块表示智能体可以集成到多个应用场景中,实现跨平台的服务。

接口层(橙色区域)

  • 该层作为应用层与智能体核心的中介,为智能体和应用层提供数据交换、请求管理等服务。
  • 负责接收用户的请求、传递给智能体,并将智能体的反馈返回给应用层。接口层的存在可以支持不同的协议和数据格式,使智能体更具兼容性。

智能体层(绿色区域)

  • 这一层是整个架构的核心,包含了执行任务、分析数据、生成响应的智能体。
  • 智能体使用大模型能力来处理请求并提供智能化的响应。它可以直接调用模型,完成自然语言处理、分析和生成等任务。
  • 智能体应用层可以根据业务场景,分为单智能体和多智能体。

私有化大模型部署层(浅蓝色区域)

  • 该层为智能体提供了可私有化部署的大模型能力,使企业或组织能够在自己的计算资源中运行和管理大模型。
  • 可以根据具体需求对模型进行调整和优化,同时确保数据的私密性和控制权。

硬件或云计算层(深蓝色区域)

  • 这是支持智能体运行的基础设施层,提供计算资源,可以是物理硬件或者云计算服务。
  • 为大模型的训练和推理提供算力支持,确保系统能够稳定运行,满足用户请求。

智能体管理层(左侧,深绿色区域)

  • 该模块负责智能体的管理,支持监控、更新、策略配置等功能,确保智能体的正常运行和优化。
  • 包括对智能体生命周期的管理,如模型的更新、任务的分配和故障排查等。

公有大模型层(右侧,深蓝色区域)

  • 这是外部提供的公共大模型,可以在需要时为智能体提供额外的模型支持。
  • 智能体在私有化部署大模型无法满足时,可以调用公有大模型,以获得更广泛的知识和功能支持。

写在最后

随着人工智能技术的发展,软件,作为数字信息化的基石,将变的越来越智能化,人类与数字世界的交互将会变的更加简单和拟人化,一切交互都可以通过自然语言的方式,甚至是表情和肢体语言等。面向智能体编程是新一代编程模式,使用面向智能体的编程模式,可以对当前所有传统数字化软件进行重构和重新定义,使得交互方式同时发生质的转变。这种变革,将大大提高人类和数字世界沟通的效率以及生产力,这一定是一场新的数字革命。

提示词工程-少样本提示词

少样本提示词(few-show-prompt)

少样本提示(Few-shot Prompting)是一种用于大语言模型(LLM)的技术,通过提供少量示例来引导模型生成更符合预期的输出。这种方法在处理复杂任务时相较于零样本提示(Zero-shot Prompting)表现更佳,有时候会出现事半功倍的效果,因为它为模型提供了上下文和具体的示例,有助于模型理解用户的需求。

定义与原理

少样本提示的核心在于通过给定一小部分示例,帮助模型在特定任务上进行学习和推理。与零样本提示不同,后者不提供任何示例,完全依赖模型的预训练知识。例如,在少样本提示中,可以提供一个新词的定义及其使用示例,以帮助模型理解如何在句子中正确使用该词[1][2][3]。

应用场景

少样本提示适用于多种场景,尤其是那些需要特定领域知识或复杂推理的任务。以下是一些典型应用:

  • 语言生成:通过提供语境和示例,改善文本生成的质量。
  • 情感分析:使用标记示例帮助模型识别文本中的情感倾向。
  • 特定领域任务:如法律、医学等领域,可以通过少量高质量示例来提高输出的准确性[3][4].

少样本或多样本提示词已经成为其他提示词工程的基础,比如思维链提示词就是通过少样本演变来的提示词工程技术,除了思维链提示词,其他提示词技术都涉及到提供示例来引导大模型输出更加准确可控的信息。

另外当我们使用提示词工程相关的技术应用到编程领域时,可以通过少样本提示词,精确的控制模型的输出,这样的话,程序就可以很容易的解析输出格式,增强软件的鲁棒性。

技巧与注意事项

在实施少样本提示时,有几个技巧可以提高效果:

  • 使用标签:在示例中添加标签可以帮助模型更好地理解任务要求,即使标签格式不统一也能有效提升性能[1][2]。
  • 多样性与质量:所提供示例的多样性和质量直接影响模型的输出效果。垃圾进则垃圾出,低质量的示例可能导致错误的结果[3][4]。
  • 思维链技术:结合思维链(Chain of Thought)提示,可以引导模型进行逻辑推理,从而提高复杂问题的回答准确性[4][5].

局限性

尽管少样本提示具有许多优点,但它也存在一些局限性:

  • 依赖于示例质量:如果提供的示例不够清晰或相关,可能会导致模型输出不准确或不相关的信息。
  • 过拟合风险:模型可能会过度依赖给定的示例,而无法泛化到新的、未见过的情况[2][3]。

总之,少样本提示是一种强大的工具,可以显著提升大语言模型在复杂任务中的表现,但成功实施这一技术需要精心设计和选择高质量的示例。

少样本提示词的一些实例:

  • JSON格式输出:
输入: 创建一个包含姓名和年龄的人员信息
输出: 
{
  "name": "张三",
  "age": 30
}

输入: 创建一个包含书名和作者的图书信息
输出: 
{
  "title": "三体",
  "author": "刘慈欣"
}

输入: 创建一个包含产品名称和价格的商品信息
输出:
  • 函数定义格式:
输入: 创建一个计算两数之和的Python函数
输出:
def add_numbers(a, b):
    return a + b

输入: 创建一个判断字符串是否为回文的Python函数
输出:
def is_palindrome(s):
    return s == s[::-1]

输入: 创建一个计算给定数字阶乘的Python函数
输出:
  • SQL查询格式:
输入: 从users表中选择所有用户的名字和邮箱
输出:
SELECT name, email FROM users;

输入: 从products表中选择价格高于100的所有产品
输出:
SELECT * FROM products WHERE price > 100;

输入: 从orders表中选择2023年1月1日之后的所有订单
输出:
  • HTML模板格式:
输入: 创建一个包含标题和段落的HTML结构
输出:
<div class="content">
  <h1>标题</h1>
  <p>这是一个段落。</p>
</div>

输入: 创建一个包含图片和描述的HTML结构
输出:
<figure>
  <img src="image.jpg" alt="描述">
  <figcaption>图片描述</figcaption>
</figure>

输入: 创建一个包含无序列表的HTML结构
输出:

这些例子展示了如何使用少样本提示来指导AI生成特定格式的代码或输出。通过提供一两个例子,AI可以学习并推断出所需的格式和结构。

参考文章

[1] https://www.sohu.com/a/788035576_120635785
[2] https://www.promptingguide.ai/zh/techniques/fewshot
[3] https://www.53ai.com/news/qianyanjishu/2024061397638.html
[4] https://learn.tishi.top/docs/advanced/few-shot-prompting/
[5] https://www.woshipm.com/share/6111304.html
[6] https://sspai.com/post/91879
[7] https://v2-ai.tutorial.hogwarts.ceshiren.com/ai/aigc/L2/tutorial/%E5%B0%91%E6%A0%B7%E6%9C%AC%E6%8F%90%E7%A4%BA/
[8] https://cloud.baidu.com/qianfandev/topic/267310
[9] https://www.promptingguide.ai/techniques/fewshot
[10] https://www.prompthub.us/blog/the-few-shot-prompting-guide

大语言模型时代,聊聊自然语言编程

大语言模型时代,聊聊自然语言编程

计算机及编程语言的发展历史,其实就是从机器语言发展到接近人的自然语言编程的发展过程,这种发展,从来没有停歇。可是过去所有从低级到高级的编程语言,都不是真正的自然语言编程,直到今天,大语言模型横空出世,使得达到这一目标,向前迈了一大步。

在计算机的早期阶段,人们主要通过机器语言与计算机进行沟通。最初的计算机如ENIAC,只能通过插拔电线和设置开关来进行编程。随着时间的推移,汇编语言出现了,它用一些简短的符号(助记符)代表机器指令,这在一定程度上简化了编程工作,人们可以通过简单的指令来控制计算机执行流程,按照人类预想的流程执行。可以汇编还是接近计算机的机器指令,需要编程人员对硬件,指令集有良好的掌握,汇编语言非常难以理解,普通人很难阅读。

到了1950年代,高级编程语言如FORTRAN、COBOL和LISP开始出现。与纯粹的机器代码相比,这些语言更接近人类的思维方式,但仍然是程序员们为计算机量身定制的“新语言”,需要学习和掌握特定的语法和逻辑结构。相比于汇编,这些编程语言容易学习,屏蔽了底层计算指令,程序员只需要关注到编程本身的任务。

进入1970年代,C语言的诞生进一步推动了编程的普及。C语言不仅功能强大,还因其可移植性成为编程世界中的“通用语言”。此后,Python、Java等更高层次的编程语言相继出现,随着它们语法的简化和功能的增强,编程的门槛逐渐降低,更多的人能够参与到编程世界中来。

然而,这些语言尽管更加简洁和直观,仍然要求使用者具备一定的编程思维和逻辑能力,离我们平常使用的自然语言还有相当的距离。

自然语言编程的概念由来已久,但真正的实现却受限于技术的发展。20世纪末和21世纪初,随着计算能力的提升和人工智能的进步,基于自然语言的编程逐渐成为可能。

早期的尝试主要集中在有限领域的自然语言处理(NLP)上,比如通过简单的命令行工具让计算机理解基本的语句。此时的自然语言编程更多是一种“增强”的脚本编程,依然要求使用者遵循特定的语言结构。

随着深度学习技术的发展,特别是大语言模型(如GPT系列)的出现,自然语言编程迎来了质的飞跃。这些模型能够处理复杂的语言任务,理解并生成符合人类表达习惯的语句。这意味着,人们可以用更接近自然语言的方式与计算机互动,甚至可以通过简单的描述生成完整的代码段。

今天,开发者已经可以通过像ChatGPT这样的模型,将自然语言直接转化为代码。这不仅降低了编程的难度,也打开了非程序员进入编程世界的大门。比如,你可以直接描述你想要实现的功能,AI会自动生成相关代码,极大地提升了开发效率。

未来,自然语言编程有望彻底改变我们与计算机的互动方式。编程可能不再是少数人的专利,而是人人都能掌握的一种技能。我们可能只需要用自然语言描述问题或需求,计算机就能自动生成解决方案。这种技术不仅能够简化编程过程,还能推动跨学科的创新,让更多领域的人士能够利用编程工具解决他们领域中的问题。

自然语言编程的发展不仅是技术的进步,更是人类对语言和思维方式的探索与重塑。随着大语言模型的不断进化,编程将变得更加普及和智能,最终实现人人皆能编程的愿景。

什么是自然语言编程?

自然语言编程的定义:

自然语言编程(Natural Language Programming, NLP)是一种编程范式,允许程序员使用自然语言(如英语,甚至是其他各种人类语言)编写代码,而不是使用传统的编程语言语法。这种方法旨在使编程更加直观和易于理解,从而降低编程的学习曲线。

自然语言编程的核心思想是让计算机能够理解和执行用自然语言表达的指令。这需要自然语言处理(NLP)技术来分析和解释人类语言,并将其转换为计算机可执行的形式。

自然语言编程的一些特点包括:

  • 使用接近人类语言的语法和词汇,而不是传统编程语言的严格语法规则
  • 允许使用更多的上下文和隐含信息,而不是显式指定每个细节
  • 支持更自然的交互方式,如问答、对话式编程等
  • 提供更人性化的错误提示和反馈

自然语言编程的实现方式:

  • 代码自动生成:用户通过自然语言描述想要实现的功能,编程工具或系统会自动生成相应的代码。例如,在描述一个计算任务时,系统会将描述转换为Python、JavaScript或其他语言的代码。

  • 编程辅助:自然语言编程也可以作为一种编程辅助工具,帮助程序员快速编写代码段或调试代码。例如,开发者可以用自然语言描述要实现的逻辑,系统提供相应的代码建议或直接生成代码。

  • 多语言支持:自然语言编程不局限于单一的自然语言,它可以支持多种语言,用户可以使用自己熟悉的语言与计算机互动。

  • 人机语音交互:自然语言编程的终极目标就是通过自然语言交互,实现计算机自主理解及执行相应的任务,在可见的未来,人类不需要额外的输入,就可以通过自然语言的交互,实现计算机自主执行任何任务。

大模型时代的自然语言编程技术

在大模型技术出现之前,编程始终是程序员的技术活,每一种语言都存在一定的学习曲线,尤其像C++这种成熟的面向对象语言,灵活的语法,以及非常复杂的规范和标准,成为C++语言的熟练工,可真不是件容易的事,这需要数年甚至数十年的积累和经验。据不完全统计,目前至少有超过700种编程语言,而这个数字还在不断增长。但是接近纯自然语言的编程语言至今还未出现。

如今,大语言模型的出现,可以预见的是正在改变计算机编程的游戏规则,大模型时代的自然语言编程技术代表着计算机与人类互动方式的一次重大变革。在大规模预训练语言模型(如GPT系列、BERT等)的推动下,计算机能够以更加智能和自然的方式理解和处理人类的自然语言描述。这一技术的发展极大地简化了编程过程,使得更多的人能够以自然语言的方式进行编程,降低了技术门槛,提升了开发效率。

大语言模型的核心优势在于:

  • 强大的理解能力:大语言模型可以理解复杂的自然语言描述,包括上下文、隐含意义和复杂的逻辑结构。这使得用户可以用接近日常交流的语言与计算机互动,而不需要掌握传统编程语言的语法和规则。
  • 多领域知识:这些模型不仅懂得编程语言,还拥有广泛的背景知识,涵盖多个领域。这使得它们能够生成与特定领域相关的代码,并且在复杂应用中提供有价值的帮助。
  • 记忆:大模型具有一定的记忆,能够根据交互的上下文背景和人进行交互,这和人类的交谈是相似的,因此大模型在编程领域可以模拟人的行为。

基于大语言模型进行自然语言编程的不同方法

提示词工程

提示词工程无疑是和大语言模型交互的重要技术,这种技术完全可以应用到编程领域。

  • 描述: 用户通过自然语言描述要执行的任务,并将该描述(prompt)传递给大语言模型。模型会根据提示生成相应的代码或直接执行操作。Prompt的设计至关重要,它需要足够明确和具体,以便模型理解用户的意图。
  • 应用场景: 代码生成(如自动生成Python脚本)、任务执行(如数据库查询)、问题回答等。
使用提示词生成代码

目前市面上几乎所有的通用大模型都支持prompt生成代码,比如ChatGPT,Claude, Gemini等,也包括一些开源大模型,例如llama, qianwen等等,另外还有一些专业模型专门针对编程领域,例如llama coder,deepseek coder等等。

使用这些大模型,你可以通过自然语言描述特定的需求,直接使用大模型生成期望的代码。

例如我使用以下提示词生成了一个检查密码强度的工具:

请扮演软件工程师的角色,您擅长分析需求、设计软件和编码,您能否帮助我按照以下步骤一步一步设计和实现密码强度检查器:
步骤 1:列出密码强度检查器的可能功能和要求。
步骤 2:设计此密码强度检查器的软件。
步骤 3:根据您的设计实现功能和要求。
请帮助一步一步完成。请使用Golang,生成命令行应用程序
请用中文书写。

讲该prompt输入至ChatGPT时,GPT可以帮我做简单的分析和设计,并生成相应的代码:


package main

import (
    "fmt"
    "unicode"
)

func main() {
    var password string
    fmt.Print("请输入密码: ")
    fmt.Scanln(&password)

    strength := checkPasswordStrength(password)
    fmt.Printf("密码强度评分: %d/10\n", strength)

    if strength < 7 {
        fmt.Println("建议:请使用更复杂的密码,包括大小写字母、数字和特殊字符。")
    } else {
        fmt.Println("密码强度良好。")
    }
}

func checkPasswordStrength(password string) int {
    score := 0

    // 长度检查
    if len(password) >= 8 {
        score += 2
    } else {
        fmt.Println("密码长度应至少为8个字符。")
    }

    // 字符种类检查
    var hasUpper, hasLower, hasDigit, hasSpecial bool
    for _, char := range password {
        switch {
        case unicode.IsUpper(char):
            hasUpper = true
        case unicode.IsLower(char):
            hasLower = true
        case unicode.IsDigit(char):
            hasDigit = true
        case unicode.IsPunct(char) || unicode.IsSymbol(char):
            hasSpecial = true
        }
    }

    if hasUpper {
        score += 2
    }
    if hasLower {
        score += 2
    }
    if hasDigit {
        score += 2
    }
    if hasSpecial {
        score += 2
    }

    return score
}
大模型智能体应用

基于提示词工程,不仅仅可以通过自然语言生成代码。大模型时代的应用,将基于智能体编程模式,开启智能应用时代。基于智能体模式,相比传统应用程序,智能应用的交互模式发生了革命性的变化,应用程序完全可以理解人的自然语言输入,智能应用程序将通过理解人的语言进行思考,并以一定的自主能力来执行复杂的任务。

因此,自然语言编程的范畴并不仅仅是通过自然语言来生成代码,广义含义也包含了使用自然语言直接执行任务。

同时智能体应用本身的程序也将不再完全由程序代码来构建的,程序的核心部分将基于提示词工程的自然语言部分,比如react提示词模式,和COT(chain-of-thought)模式。剩余的部分包括大模型的API,数据库交互,记忆模块,以及工具的使用,将使用传统的编程模式。基于prompt的编程将是自然语言编程的重要组成部分。

通过智能体应用,应用程序将会更加智能化,人机交互方式将会发生革命性的转变,任务和指令将通过自然语言的方式进行输入,程序的反馈也将更加人性,更符合人与人的交流方式。

辅助编程

辅助编程技术,实际上是编程IDE诞生以来,一直在发展的技术,在生成式模型出现之前,传统的辅助编程工具可以帮助程序员建立代码索引,通过联想,自动补全等功能帮助程序员更加快速的找到已有函数,接口,变量等,提示编程效率。

辅助编程技术通过生成模型技术,已经发生了革命性的转变。用户可以通过自然语言与大模型进行互动,逐步引导模型生成代码。用户可以实时修正或者调整模型生成的代码,以确保最终结果符合预期。

辅助编程工具一般都是基于编程IDE来实现的,比如当下著名的编程助手 Github Copilot,Cursor 等,程序员仅仅通过自然语言提示,就可以生成代码,测试及文档,大大的提高了编程效率。这些工具除了可以编程外,还可以帮助重构,调试代码。

代码翻译

由于专业的大模型熟悉不同编程语言的语法和语义,因此程序员可以用自然语言描述现有代码段的逻辑或意图,指示大模型将一种语言的代码片段,翻译成其他语言的代码片段,这种特性将在跨语言的代码迁移、旧代码重构、多语言项目的代码管理等领域得到重要的应用。

写在最后

自然语言编程将是我未来的一个主要研究方向,我相信未来,越来越多的人会使用自然语言方式进行编程,未来的应用程序也将更加智能,编程将不在是程序员的专利,更多的人将参与到数字化世界的构筑,使得这个世界更加丰富多彩。

参考文章:

https://arxiv.org/html/2405.06907v1
https://arxiv.org/abs/2405.06907
https://juejin.cn/post/7348648095015567412
https://www.yunwei37.com/2023/06/14/natual-language-program/

提示词工程-零样本提示词

零样本提示词

什么是零样本提示词

零样本提示词,顾名思义就是在人和大模型之间交互时,提示词中可以不使用任何样例,大模型只根据提示词中的指示以及预训练的固有知识生成内容或回答问题。

零样本提示词可以是一句话,一个问题,或者一条指令,但是如果期望大模型生成准确有效的信息,零样本提示词也需要遵循一定的结构,具体的信息可以参考另一篇文章:高效提示词的基本结构

假设你想使用一个语言模型来判断一段文本的情感是积极、消极还是中性。你可以向模型提出这样的问题:

“请对这段文本进行情感分类:‘我在海滩度过了一个很棒的一天!’它是积极的、消极的还是中性的?”

指令微调已经被证明可以提高零样本学习的效果(Wei et al., 2022)。指令微调本质上是指在通过指令描述的数据集上对模型进行微调。此外,RLHF(从人类反馈中进行强化学习)已被用于扩展指令微调,其中模型被调整以更好地符合人类偏好。这种最近的发展推动了像ChatGPT这样的模型的出现。我们将在接下来的章节中讨论所有这些方法。

当零样本不起作用时,建议在提示中提供演示或示例,这就是所谓的少样本提示。

零样本提示词的优缺点

优点:零样本提示在需要快速回答而无需准备示例时非常有用。它还有助于测试模型的一般语言理解能力。

缺点:与少样本或微调模型相比,零样本提示的准确度可能较低,尤其对于复杂任务或与训练数据差异很大的任务。

参考文章

Zero-shot and Few-shot prompting in LLMs
zeroshot
zeroshot prompting

提示词工程-高效提示词的基本结构

提示词工程-高效提示词的基本结构

自从生成式人工智能技术产生已来,提示词工程成为一种新的职业方向。提示词工程(Prompt Engineering)是一种在生成式人工智能(如GPT模型)中创建和优化提示词以获得期望输出的方法。

所谓的提示词,其实就是人和AI交互时的输入,可以是一个问题,一段描述性的指示,生成式AI可以根据你的指示或者问题,输出你期望的内容。

随着各个领域的工程师不断的实践和评估,实践经验发现,有效的机构化的提示词可以控制大模型输出的准确性和有效性,同时可以控制大模型输出的幻觉等。

提示词的基本结构

根据大家的经验,如果在写提示词时遵循如下的结构,可以提升和AI交互的效率:

  • 角色(role):交互时通过给AI大模型设定某种角色,根据经验可以获得意想不到的效果,可以使大模型的输出更加具有个性化,专业化,同时能够增加模型输出的准确性。比如如下的例子,指明AI大模型为一个通信专家,就是给当前交互的AI上下文设定了角色。

假设你是一名通讯专家。起草一封电子邮件给你的客户,告知他/她由于物流问题导致的交货时间延迟。

  • 指示/指令(task/instruction):指示或者指令,是指示AI大模型需要完成的任务,比如通过何种知识及技能,来完成何种任务。以下例子中指示或者指令为现在为一个黑客马拉松设计一个挑战性任务,涉及加密和解密。

假设你是一名软件工程师,正在设计一个涉及加密技术的黑客马拉松任务。任务设计应适合中高级开发者,且应在4小时内完成。请用五个步骤描述设计这个任务的过程,确保任务能够让参与者理解现代加密算法的核心原理。现在为一个黑客马拉松设计一个挑战性任务,涉及加密和解密。

  • 上下文(context):当你给出与任务相关的背景或细节时,可以帮助AI大模型更好的理解任务的具体情况,使得输出更加精确。以上示例中上下文部分为正在设计一个涉及加密技术的黑客马拉松任务
  • 输入或者问题:需要大模型执行具体的任务,或者具体的问题, 有时候如果指令部分描述的是具体的任务时,则指令部分就是输入。以上示例中指令部分就是输入部分:现在为一个黑客马拉松设计一个挑战性任务,涉及加密和解密
  • 约束:有时候需要限制AI大模型的输出范围,设定一些约束条件,可以对输出进行控制。以上示例中通过约束任务设计应适合中高级开发者,且应在4小时内完成来控制马拉松挑战的难度。
  • 输出格式:输出格式,主要给大模型指明,输出特定内容时的格式,比如输出为Json或者Markdown等。以上示例中请用五个步骤描述设计这个任务的过程指明了输出结构。
  • 例子:提供例子可以贡AI大模型进行参考,可以有效的提高输出的准确度,比如 few-shot prompt 就是提供一个或者多个例子,使得大模型的输出更加确定。

一些范例

以下是基于 角色+指令+上下文+输入问题+约束+例子 格式提供的几个提示词范例:

范例 1: 生成文章

角色: 你是一名资深环境学家。

指令: 我需要撰写一篇关于气候变化影响的文章,你可以作为我的咨询顾问。

上下文: 全球气温正在上升,极端天气事件变得更加频繁。政府和非政府组织正在采取措施应对这一问题。

输入问题: 气候变化如何影响不同地区的生态系统?政府可以采取哪些措施来减轻这些影响?

约束: 文章应包含三个部分:引言、影响分析、建议措施,总字数在500字左右。

例子: 引言可以描述气候变化的现状,然后分析不同地区生态系统的具体变化,最后提出可以采取的政策建议。


范例 2: 生成代码

角色: 你是一名Python开发者。

指令: 编写一个函数来排序学生的成绩。

上下文: 你正在开发一个学生成绩管理系统,需要能够对学生成绩进行排序,以便生成成绩单。

输入问题: 请编写一个Python函数,该函数接受一个包含学生姓名和成绩的字典,并按成绩从高到低排序。

约束: 函数应返回一个排序后的字典,并考虑成绩相同的学生按姓名字母顺序排序。

例子: sort_students({'Alice': 90, 'Bob': 85, 'Charlie': 90}) 应返回 {'Charlie': 90, 'Alice': 90, 'Bob': 85}


范例 3: 生成产品设计思路

角色: 你是一名用户体验设计师。

指令: 根据我的具体问题,帮助我设计一个新的移动应用的用户界面,旨在提高用户的日常习惯养成。

上下文: 现代用户越来越注重健康和效率,他们希望有一个工具可以帮助他们建立和维持良好的习惯。

输入问题: 如何设计一个易于使用且能激励用户的移动应用界面,以帮助他们每天坚持新习惯?

约束: 应用界面应简洁,包含进度追踪功能,且用户在3次点击内能完成主要操作。设计思路应包括界面草图和功能说明。

例子: 设计思路可以包含一个主页,展示用户的日常目标和进度条,点击进度条后弹出完成按钮,点击按钮后弹出激励语。


范例 4: 撰写故事

角色: 你是一名侦探小说作家。

指令: 帮助创作一段紧张的情节,描述侦探发现关键线索的过程。

上下文: 故事发生在一个封闭的庄园内,庄园主神秘失踪,侦探正深入调查此案。

输入问题: 侦探在调查庄园的书房时,意外发现了一个隐藏的密室,里面藏有庄园主失踪前留下的日记。

约束: 描述应突出侦探的推理过程和发现的细节,字数控制在300字以内。

例子: 可以描述侦探如何通过书架上的不寻常痕迹发现密室,并在日记中找到庄园主最后的遗言。


范例 5: 创建商业计划

角色: 你是一名创业顾问。

指令: 帮助制定一份初创企业的商业计划,侧重于市场分析和融资策略。

上下文: 一家专注于环保产品的初创公司希望进入市场并获得第一轮融资。

输入问题: 如何评估目标市场的潜力,并制定有效的融资策略来吸引投资者?

约束: 商业计划应包括市场分析、竞争对手分析、产品定位、融资目标和策略,总字数控制在1500字以内。

例子: 市场分析部分可以包括市场规模、增长率和用户画像,融资策略可以包括股权分配和投资回报预期。


这些范例展示了如何通过角色+指令+上下文+输入问题+约束+例子的结构来创建明确且有效的提示词。每个范例都针对特定的任务和输出需求进行了设计,以确保生成的内容符合预期。

参考文章:

提示词工程技术图谱-prompt engineering

提示词工程技术栈

提示词工程技术的不同方法和分类

无需大量训练的新任务(New Tasks Without Extensive Training)

  • 零样本提示(Zero-shot Prompting)
  • 少样本提示(Few-shot Prompting)
  • 连续链提示(Chain-of-Thought (CoT) Prompting)
  • 自动化连续链提示(Automatic Chain-of-Thought (Auto-CoT))
  • 自我一致性(Self-Consistency)
  • 逻辑链提示(Logical CoT (LogiCoT) Prompting)
  • 符号链提示(Chain-of-Symbol (CoS) Prompting)
  • 思维树提示(Tree-of-Thoughts (ToT) Prompting)
  • 思维图提示(Graph-of-Thought (GoT) Prompting)
  • 系统2注意力提示(System 2 Attention Prompting)
  • 思维线程提示(Thread of Thought (ThoT) Prompting)
  • 表格链提示(Chain of Table Prompting)

推理与逻辑(Reasoning and Logic)

  • 检索增强生成(Retrieval Augmented Generation (RAG))
  • 反应式提示(ReAct Prompting)
  • 验证链提示(Chain-of-Verification (CoVe) Prompting)
  • 注释链提示(Chain-of-Note (CoN) Prompting)
  • 知识链提示(Chain-of-Knowledge (CoK) Prompting)

减少幻觉(Reduce Hallucination)

  • 主动提示(Active-Prompt)

用户交互(User Interaction)

  • 自动提示工程师(Automatic Prompt Engineer (APE))
  • 自动化推理和工具使用(Automatic Reasoning and Tool-use (ART))

微调和优化(Fine-Tuning and Optimization)

– 对比连续链提示(Contrastive Chain-of-Thought Prompting (CCoT))

基于知识的推理和生成(Knowledge-Based Reasoning and Generation)

  • 情绪提示(Emotion Prompting)

提高一致性和连贯性(Improving Consistency and Coherence)

  • 抓取提示(Scratchpad Prompting)
  • 思维程序提示(Program of Thought (PoT) Prompting)

情绪和语气管理(Managing Emotions and Tone)

  • 程序化连续链提示(Structured Chain-of-Thought Prompting (SCoT))

代码生成和执行(Code Generation and Execution)

  • 代码链提示(Chain of Code (CoCo) Prompting)

优化和效率(Optimization and Efficiency)

  • 提示优化(Optimization by Prompting)

理解用户意图(Understanding User Intent)

  • 重述和响应提示(Rephrase and Respond Prompting)

元认知和自我反思(Metacognition and Self-Reflection)

  • 退一步思考提示(Take a Step Back Prompting)

Coze 如何自定义插件-获取公众号文章列表

Coze 如何自定义插件-获取公众号文章列表

关于Coze

毫无疑问,2023年是大模型元年,那么2024年,将会是LLM Agent应用爆发的一年。通过LLM agent 模式,AI 大模型将会释放自己的能力,助力更多的应用智能化。

Coze 平台是字节跳动开发的一款能够低代码创建AI agent的平台,无论你是否有编程经验,Coze 平台都能让你轻松创建各种聊天机器人,并将它们部署到不同的社交平台和消息应用中。该平台通过提供友好的界面和强大的功能,使得用户可以快速上手,开发出具有个性化和智能交互功能的机器人。简而言之,Coze 平台为聊天机器人的开发和应用提供了一个简单、高效、多功能的环境。

关于如何使用Coze,及了解更多的信息,可以阅读Coze 操作文档

本篇文章将主要聚焦于如何自定义Coze的工具(插件)。

创建Coze的插件

进入 Coze 主页

  • 1 选择个人空间
  • 2 选择插件
  • 3 点击创建插件按钮,进入创建插件界面。

根据上图:

  • 填写插件命名,可以使用中文。
  • 填写插件描述,简单描述插件的用途。
  • 选择在Coze IDE 中创建
  • 有两种编程语言可选,Node.js 和 Python3, 本实例使用python,因此选择Python3
  • 点击 确认

点击按钮 在IDE中创建工具

命名工具,必须以字母数字下划线格式命名,跟函数命名一致。
描写工具的用途。点击 确认

此时,你可以通过编程实现handler函数,来实现你的插件功能。
如果该工具需要自定义输入输出参数,则可通过元数据标签页,添加输入参数和输出参数。

分别使用输入参数和输出参数的编辑按钮,编辑输入参数和输出参数。
由于获取公众号文章时,公众号API需要使用公众号的appid和secret来获取访问资源的token,因此在此定义输入参数为appid和secret。
同时输出为公众号文章的列表,因此输出是一个数组,并且数组的每个item是一个对象,该对象具有如下字段:title,author,digest,url,如下图所示。

编辑完成后记得点击保存。然后切换到代码标签页。
由于公众号的API依赖于requests, 因此需要使用添加依赖,添加依赖的库。
添加依赖以后,就可以编程实现相应的功能,以下是获取公众号文章的代码。


from runtime import Args
from typings.GetArticleList.GetArticleList import Input, Output

import json
import requests

def get_stable_access_token(appid, secret, force_refresh=False):
    """
    获取稳定的访问令牌

    参数:
    appid (str):微信小程序的 appid
    secret (str):微信小程序的 secret
    force_refresh (bool):是否强制刷新令牌,默认为 False

    返回:
    dict:访问令牌的 JSON 数据
    """
    url = "https://api.weixin.qq.com/cgi-bin/stable_token"
    payload = {
        "grant_type": "client_credential",
        "appid": appid,
        "secret": secret,
        "force_refresh": force_refresh
    }
    headers = {'Content-Type': 'application/json;charset=utf-8'}
    response = requests.post(url, headers=headers, data=bytes(json.dumps(payload), encoding='utf-8'))
    print(response)
    if response.status_code == 200:
        return response.json()
    else:
        response.raise_for_status()

def extract_info(articles):
    """
    提取文章列表中所有文章的信息

    Args:
        articles (dict):包含文章列表的 JSON 数据

    Returns:
        list:包含所有文章信息的列表,每个元素是一个字典
    """

    result = []
    if "item" in articles:
        for item in articles["item"]:
            if "content" in item and "news_item" in item["content"]:
                for news_item in item["content"]["news_item"]:
                    # 获取文章的标题、作者、摘要和 URL,如果没有则为空字符串
                    title = news_item.get("title", "")
                    author = news_item.get("author", "")
                    digest = news_item.get("digest", "")
                    url = news_item.get("url", "")
                    # 创建一个新的 JSON 对象来存储提取的信息
                    article = {
                        "title": title,
                        "author": author,
                        "digest": digest,
                        "url": url
                    }
                    result.append(article)
    return result

def get_published_articles(access_token, offset, count, no_content):
    """
    获取已发布的文章列表

    参数:
        access_token (str):调用接口的 access_token
        offset (int):要获取的文章列表的起始位置
        count (int):要获取的文章数量
        no_content (bool):是否返回文章内容

    返回:
        str:返回的文章列表的 JSON 字符串
    """
    url = f"https://api.weixin.qq.com/cgi-bin/freepublish/batchget?access_token={access_token}"
    payload = {
        "offset": offset,
        "count": count,
        "no_content": no_content
    }
    headers = {'Content-Type': 'application/json;charset=utf-8'}
    response = requests.post(url, headers=headers, data=json.dumps(payload))
    text = response.content.decode('utf-8')

    if response.status_code == 200:
        return text
    else:
        response.raise_for_status()

"""
Each file needs to export a function named <code>handler</code>. This function is the entrance to the Tool.

Parameters:
args: parameters of the entry function.
args.input - input parameters, you can get test input value by args.input.xxx.
args.logger - logger instance used to print logs, injected by runtime.

Remember to fill in input/output in Metadata, it helps LLM to recognize and use tool.

Return:
The return data of the function, which should match the declared output parameters.
"""
def handler(args: Args[Input])->Output:
    offset = 0
    count = 100
    no_content = 0

    appid = args.input.appid
    appsecret = args.input.appsecret

    access_token = get_stable_access_token(appid, appsecret)
    token = access_token['access_token']
    content = get_published_articles(token, offset, count, no_content)
    articles = json.loads(content)
    result = extract_info(articles)
    return result

运行测试时,输入以下参数,运行可以查看结果。

{
    "appid":"your appid",
    "appsecret":"your appsecret"
}

测试通过即可将你的插件进行发布

程序员的修行之路-保持良好的心态

保持良好的心态

其实,在不同的领域或行业,都会面临来自各方面的压力,那些能保持良好心态的人,总是能够走的最远。但是,如何能够保持良好的心态呢?面对物欲横流,充满着各种诱惑的世界,你是否能够保持清醒的头脑?马云抛起的互联网创业浪潮,让无数IT程序员们蠢蠢欲动,你是否能够保持冷静?IT和互联网总是不断有新的技术涌现,你是能否保持开放,不断的学习和探索新的技术?还有,有些公司虽然给你良好的福利,但是加班加到吐血,你是否愿意付出并坚持呢?

刚跳槽到华为工作的时候,遇到的是全新的领域,感觉工作步态上总是必别人慢半拍,那时候技术视野还不宽,看到的总是自己工作上的点东西,工作上没有得到领导的认可,心里未免有些急躁,再加上加班,感觉工作非常枯燥,也想过放弃。在计算机领域,我不是科班出身,所以在技术上总是保持着虚心学习的心态,每次PL找我沟通的时候,聊起我这方面要提升,那方面要提升,我总是虚心接受的,但是不免让人很有挫折感,我做的好的一面,总没有被看到。曾经看到过一篇文章,讲到了人生如行路,如果你走弯路,你就会看到更多的风景,相反你选择走直路,虽然更快的到达了终点,却错过了沿途的美景。因此我明白一点,不管你成长的快慢,只要你认真做一件事情,不管你遇到多少挫折,都会从中学到很多,自己认可自己非常重要。

其实良好心态最大的敌人就是你的功利心,一定程度的功利心是需要的,这可以不断的鞭策自己进步提升和完善,但是人如果过度的陷入到比较和追逐,就会丧失平衡,从而变的浮躁。要注重自己点滴的积累,看到进步,才能进一步清醒的认识自己。另外,我觉得保持身心健康非常重要,现在整个IT及互联网领域都是急功近利的,日夜的加班损耗了太多人工作热情,所以不断有人抑郁。这个是大的环境,很难一时间改变,但是作为个人,需要清醒的了解,不要迫于压力,就无限制的消耗自己,做到最大的平衡工作和生活,我个人觉得,人的一生不能只是工作和奔波,要适当的慢下来,读读书,听听音乐,参与聚会或者和家人看看电视,喝喝茶,这些都是生活的一部分,毕竟生命是短暂的。程序员要整天对这电脑,是非常不健康的,要多运动,保持健康的身心。

程序员的修行之路-培养工作兴趣

培养工作兴趣

你是为钱而工作还是为理想而工作?看中国好声音的时候,里面导师问的最多的问题就是“你的理想是什么?”,歌手们千篇一律的回答,无非就是坚持音乐,拥有更大的舞台,拥有更多的粉丝。可是我想问,我们真的能为理想而活。考大学的时候,我的理想是学物理,可偏偏被数学录取,考研的时候,觉得自己还有点天赋,希望在数学方面继续深造,可阴差阳错被分到了算法(模式识别图形处理)方向,到了毕业找工作,觉得我这个方向以后会有不错的发展,而且自己刚刚上手,结果大失所望,这个领域很难找到饭吃,最终做个普普通通的程序员。是不是觉得相当的苦逼,其实这不只是我的经历,也许有很多人跟我一样,有着相似的命运,这就是现实和理想的冲突,面对现实我们其实没有多余的选择而已。但是,反过来想,理想总是太高远,有时候飘忽不定,很多时候人很难对自己有准确的定位,随着不断的成长,你会对生活又有了新的想法,最初的理想早已被抛在九霄云外,所以,回到我最初的问题,你是为钱而工作还是为理想而工作?我想大多数人会选择最务实的答案,经济社会本来就是务实的。但是我如果将问题稍微修改一下,你是想为钱而工作还是想为兴趣而工作?你会有什么样的答案呢。

社会太务实,我们谈不了理想,只能退而求其次,谈谈兴趣咯。想想看,如果没有找到自己理想的工作,工作中又没有自己的兴趣点,那这样的工作会变的何等的乏味枯燥。因此在工作中,要注意培养自己的兴趣,带着兴趣工作,你会发现工作不再枯燥乏味,反而会增加你工作的动力,同时也会拓展你的视野,学习到新的东西。IT领域日新月异,更新换代很快,不断有新的技术涌现,程序员只有不断的学习,补充新知识,才能紧跟科技潮流的步伐。因此,程序员必须培养自己对技术的热衷和好奇心,学会不断的探索,才能不断的完善自己,增强技能和战斗力。

如何培养自己在技术方面的兴趣?我可以结合我自己的经历来讲述。记得读研的时候,我一个纯数学方向的本科生转到了模式识别图像处理算法方向读研,对于编程,除了考计算机二级时的那点皮毛,真的是一点基础都没有,虽然因为有比较好的数学背景,对于理论的学习相对比较容易,但是对于算法实现,感觉自己非常吃力。为了补计算机及编程基础,除了自学C及C++编程基础的书来读以外,就是每周都会选几节本科生的计算机课去听,一学期下来,总算有一些收获,感触最深的是,对C++的学习,C还是一知半解的情况下,学C++真的是非常吃力,c++primer我至少读了两三遍。学了这么多,最重要的其实还是实践,一直觉得自己对理论的理解是比较快的,但是动手编程的能力比较差,后来在学习机器学习的时候,专门选了几个典型的算法,在网络上找了很多参考的例子,自己动手实现了,影响最深的是使用前馈神经网络识别阿拉伯数字,最终识别率只能达到89%左右(我那个时候好的算法能达到95%左右),虽然不算高,但是对我来说已经算非常兴奋的事情了,就这件事情本身来说,让我对编写程序,产生了极大的兴趣,这就是成就感所产生的化学作用。

刚工作的一年多,感觉自己在工作上的压力非常大,不断有新的东西要学习,同时又经常被项目经理劈头盖脸的批评搞的很受挫,那段时间曾经怀疑过自己,是否真的不适合这个行业。不过还是坚持了下来,一年多的时间,不断的在项目上得到认可,同时在软件的分析和设计上有了很快的成长,最终我在离开我第一家公司之前,从设计到开发,由我单独负责一个子系统。有时候人需要在压力下,不断挖掘自己的潜力,你才能对自己有新的发现。后来跳槽进了华为,原因当然是为了钱啊,当初给我开的工资是当初的两三倍,不来才傻缺呢,不过还有个根本的原因就是,我在第一家公司能够学到的东西感觉已经就那么多了,再呆下去,我能看到自己还是干着类似的活,做类似的产品,进步的空间太有限。来华为后,我从windows 界面开发,转向了linux云计算,对我来说完全是全新的领域,而且也颠覆了我对软件开发的认识,在华为的几年时间里,我做过很多项目,从开发到维护,同时也拿到过专利,抛开工作的艰辛外,我想我的收获是满满的,尤其是逐渐培养了我高效的工作方法,和分析问题的能力,同时让我接触到了开源软件,可以在开源软件里找到自己感兴趣的项目进行学习。从我个人的经历来说,如果你想进步,首先需要得到自己的认可,并且能够从自己工作的项目中找到成就感,同时拓展自己的兴趣视野,这样才能不断的激发自己的工作热情。因此我觉得如果你要热爱程序员这个行业,最重要的就是不断培养自己的兴趣和好奇心。

目录 上一节 下一节

质量在于充分完备的测试,尽早发现问题

质量在于充分完备的测试,尽早发现问题

  任何软件产品,在交付给客户或者用户之前,必须经过严格并且充分的测试,由此才能保证软件产品功能可用,易用,并且能够可靠持续的提供服务。道理很简单,只有稳定可靠的软件服务才能在用户或者客户群里赢得良好的声誉,从而赚取更多的利润。相反随时崩溃的程序带给用户的只有极其差劲的体验,我想说只有傻瓜才愿意为这样的软件服务买单。如果你的软件面对的不是直接用户,而是中间的供应商,那么你就更应该保证软件的质量,在这种情况下,任何软件的bug都可能给你的客户带来极大的损失,你的公司也会因此丢失这样的客户,同时使你在同样的客户群中失去信誉,因此作为软件提供商,必须清醒的知道,应该用高质量的软件来成就客户才能赢得双赢的局面。

  软件测试如果按照测试阶段分类的话,主要分为单元测试、集成测试、系统测试和验收测试。其中单元测试按照测试方式的话,属于白盒测试,是程序员在写代码的过程中必须完成的测试,距离编码最近,是能够最早发现程序问题的一种测试手段。由于单元测试能够帮助程序员在编码的早期阶段发现问题,程序员可以快速定位到问题的根因,因此解决问题的成本几乎可以忽略不记。

  单元测试是保证程序质量的第一步,因此程序员对此必须要有清醒的认知,业务代码和单元测试的代码具有相同的重要性。

  在这里,我想用我的个人职业生涯的经历来说明,怎么做才是提高软件质量的正确做法。

  我硕士毕业后的第一份工作是在A公司,正是在A公司,奠定了我十多年软件职业的基础,但是在A公司工作的三年里,我甚至没有听过关于单元测试的任何讨论,我个人甚至没有接触单元测试的概念,在A公司,程序员们按照需求编写相应的软件功能,然后直接交给测试同事,由于编写的软件是windows界面,测试的同事们也不了解什么是自动化测试,以我现在的目光来看,在A公司,测试同事们使用最原始的方式进行测试,不借助任何工具。而程序员们只关注编码,测试并不是自己的责任。当你看到这里,也许你很好奇,以这样的方式开发的软件是否真的可靠?我的直观感受是,我们整个团队,其实对软件的质量并没有十足的自信,因为每次客户在使用我们的软件系统时,都要求我们有足够的技术团队的现场支持,我甚至好几次碰到过在客户现场,团队惊慌失措的解决临时问题,也许这问题就是个低级错误。

  我刚进A公司的时候,我曾经作为技术支持接手过一套软硬件系统,原作者在我进公司的不久前离职了,他是大家公认的牛人,写这套软件的时候据说是起早贪黑在很短的时间内快速完成了需求任务。提到这位同事的时候,公司的领导和其他同事都会眼前一亮,说他在写程序方面非常厉害,佩服的五体投地。而我一个新手可就没那么容易了,当我阅读他写的代码时,满篇的代码都是简写,简短的命名,没有任何注释,深度的嵌套,以及错综的分支,这样的代码很难说具有可读性。为了读懂和理解代码逻辑,我不得不找到硬件的开发文档,同时找了一台设备,自己手动测试加单步调试。我不得不说,这是一套复杂而脆弱的系统,因为几乎没有任何测试就交付给客户使用了。很多次我都是在现场解决一些奇怪的问题同时被一些客户破口大骂。对这样的程序员,我曾有过一丝的崇拜,可是当我在软件工程这条道路上逐渐成熟的时候,我意识到应该对聪明的程序员和专业的程序员加以区分,我觉得我的这位前辈只能归为聪明的程序员,而我更喜欢后者,坚持让自己逐渐成长为专业的程序员。

  当我意识到在A公司成长有限时,我毅然决定离开,选择了远在其他城市的B公司,B公司是国内比较大通信及软件供应商,具有完整的质量管理体系,领导层也具有远见卓识,是国内数一数二的技术型企业,正是在B公司工作的很多年里,让我深入接触了Linux 平台,在软件开发领域打开新的眼界,我的技术能力也得到了快速的发展。正是在B公司,我开始接触到单元测试和TDD的概念,同时我也开始听到了周围一些同事对单元测试的讨论。可是遗憾的是,我现在看来,我们当时的团队X在采用TDD开发模式上是失败的。团队在一开始软件开发时,就设定了单元测试覆盖率需要达到95%以上,但是由于团队成员资历尚浅,缺乏资深专家的正确引导,并没有严格的按照TDD 所谓的先写测试,后写业务代码的流程做开发,大部分的人还是先写业务逻辑的代码,然后再考虑UT覆盖,甚至很多人只是为了满足覆盖率,让用例蒙混过关。我身边所有的同事都认为,单元测试的代码是用来测试的,所以单元测试的代码质量并不重要,这导致团队开发人员对于如何写好测试用例根本没有认知。最后导致的结果是,单元测试没有帮助程序员有效的发现问题,反而成了项目维护的累赘。最典型的问题是当后期代码有任何变动的时候,导致很多用例失败,程序员在维护测试用例上花了很多成本。

  有趣的是,X团队里,起初大家认可单元测试的有效性,可是到后期就开始蔓延单元测试无用论。

  当我从B公司的X团队转到Y团队时,由于Y团队基于开源软件做开发,因为开源软件本身没有引入单元测试框架,因此大家写代码的时候索性都裸奔了,只是在程序员交付之前必须有手动的验收测试报告,程序员在设计软件过程中,需要设计自己的验收测试用例,然后手动执行,交付功能给测试人员时,需要保证验收测试用例全部通过。测试人员会再此基础上再做更加完备的测试。这套流程看似是严谨的,可是常常出现的情况是,程序员的验收测试用例时常设计的比较简单,为了应付交付的压力,验收测试用例基本上只保证了功能的可用,对于异常,性能和稳定性上缺乏足够的测试。同时还存在一个巨大漏洞是,当软件发生维护和变动时,因为缺乏单元测试的覆盖,对于那些解决老问题,引入的新问题很难快速发现,常常漏到集成测试,或者产品验证阶段才发现,这样导致的结果是问题不能尽早发现,因此解决问题的成本会非常高。

  B公司的一大特点是崇尚加班文化,996几乎成了程序员职业的代名词,我当时所在的Y团队成员几乎每天晚上要工作到10点,想想看,高强度的工作再加代码裸奔,软件的质量该如何保证?在我看来,这其实导致一种恶性循环,程序员在疲惫的情况下交付不可靠的软件->测试不充分->疲惫的测试人员漏测->下游或者客户发现问题->程序员加班解决问题。虽然在B公司,我在技术能力上有了很大的突破,可是在我看来,程序员几乎被绑在了公司运转的车轮上,生活空间被工作挤压,同时没有业余的时间研究新技术,学习新的软件工程方法,日复一日使用老旧的技术开发。团队里所谓的技术大牛,在我看来其实都是些聪明的程序员,他们在如何提高软件质量方面,眼界永远越不过自己的鼻尖。因此基层的程序员缺乏专业程序员的引导,他们的知识体系很难得到更新,很多人其实在如何保证软件质量方面没有任何思考,日复一日的拷贝粘贴,或者写出那些看似高明的复杂代码。为什么国内把程序员叫码农,是因为程序员几乎成了一种体力劳动者。

  我所经历的这种情况几乎成了国内技术公司普遍存在的现象。

程序员的修行之路

程序员的修行之路

目录

img微信公众号

程序员的修行之路-人生是一场修行

程序员的修行之路-人生是一场修行

人生是一场修行

工作接近10年的时间,不得不感叹时间的飞快,还没来得及回味人生,已经接近中年。这10年来的程序员生涯,感受到过生活的现实和无奈,感受到过孤独和无助,感受到过工作的激情和兴奋,也感受到了辛勤耕耘后收获的喜悦。就这么忙忙碌碌的10年,回想起来,每个片段都历历在目,又觉得是如此恍惚。

在程序员的人生道路上,我承认自己既不是大牛,也不是天才,但是我觉得自己应该是一个勤勤恳恳的耕耘者,这个行业里普普通通的大多数而已。但是不管怎么说,这些年职业的积累,回想并总结一下,也许会帮助那些后来者,少走些弯路。

我想写的,并不是想讲些光鲜亮丽的大道理,而是个人实实在在的人生经历所总结的经验,我本来就是个普普通通的人,所以我将这些经验分享给那些奋斗在一线的普通程序员们。

讲到修行,很容易联想到佛教的僧人,过着与世无争,简单朴素的生活,南无阿弥陀佛,当一天和尚撞一天钟。其实这只是我们看到的表面,佛教最重要的就是修心,六根不净的人,即使剃度,穿上袈裟,心中都很难有佛,别说是成佛了,因此,这跟我们所看到的生活方式没太大关系。佛教的“修行”两个字,讲的真的妙,我读过一行禅师的几本书,很通俗的将佛教思想应用到世俗生活上,成为一种朴素的生活和工作哲学,而这些也正是现代经济社会所缺乏的。

我为什么在这里用修行来描述呢,因为所有的行业,都不可能一步登天,我们这个行业也是一样,需要一点一滴的积累,因此修行的第一层含义就是要沉住气,一步一个脚印,慢慢积累。同时这个行业也有它的特殊性,随时都有可能更新或者颠覆,随时都要学习新的知识,你多年来的工作积累很有可能瞬间就被淘汰了,这时候,修行又有了它的第二层含义,那就是方法论,佛教有三宝,其中之一就是佛法,法就是方法,知识有可能被淘汰,但是经验和方法却是通用的,因此注重知识积累的过程,同时也要兼修方法和经验的总结,这就是我用修行的两层含义。

我将从以下这些角度分别来阐述如何成为优秀程序员需要培养的要素和特质,这些不一定需要你拥有特别的天赋,只需要个人的勤奋和努力再加随时总结和沉淀而已。

目录 下一节

本文系本站原创文章,著作版权属于作者,未经允许不得转载,如需转载或引用请注明出处或者联系作者。