,

适合普通程序员的AI大模型开发框架

liangdabiao

·

,

·

项目背景:

作为最普通的程序员一员,同时知道未来的开发必然是AI大模型相关开发为主,怎样入局?查看了市面多数开源项目,觉得大部分项目要么过度封装和复杂,要么就是困难和不灵活。普通程序员很难二开和可控利用。所以产生以下项目simple_ai_toolset:https://github.com/liangdabiao/simple_ai_toolset 。 以下介绍它是什么,有什么用,未来计划和开发方向。

功能介绍:

simple_ai_toolset :AI大模型的基本开发框架,适合普通后端程序员,功能类似coze,包括:fastapi后端接口,搜索引擎对接,RAG文档解析和向量化,RPA和爬虫,自定义agent,对接第三方数据接口,mongodb数据库,控制json返回,多模态理解和生成等等。有新的AI功能,会马上跟随潮流,添加上去。

AI大模型应用开发理念:

1, 理解复杂,处理复杂,生成复杂

2, 尽量把不确定的AI黑盒子转为更加确定性的可控的流程和结果

3, 大模型应用原型开发是容易的,但是真实可用的AI应用其实需要程序员深入开发,本框架就是这个目的

技术介绍:

1,后端用fastapi

FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API。它基于标准 Python 类型提示,并且非常适合初学者和大型项目。以下是 FastAPI 的一些主要特点:

  1. 快速:FastAPI 基于标准 Python 类型提示,可以自动推断数据类型,从而实现高性能。
  2. 简洁:代码简洁,易于理解和维护。
  3. 功能丰富:提供了丰富的功能,如数据验证、序列化和认证等。
  4. 异步支持:支持异步请求处理,可以提高应用的并发处理能力。
  5. 交互式文档:自动生成交互式 API 文档,方便开发者和用户使用。
  6. 类型检查:利用 Python 类型提示,可以在运行时进行类型检查,减少错误。
  7. 依赖注入:支持依赖注入,方便管理依赖关系。
  8. 数据库集成:可以轻松集成数据库,如 SQLAlchemy、Django ORM 等。

FastAPI 特别适合用于构建高性能的微服务和 Web 应用,并且可以与现代前端框架(如 React、Vue.js 等)无缝集成。

项目取用 python的 fastapi作为框架,我觉得这是最适合AI大模型开发的框架,同时也是最适合现代后端开发的框架。

考虑到AI资源有限,必须在框架增加保护和限制,所以加上使用slowapi对FastApi的接口进行限速:https://github.com/laurents/slowapi

考虑到程序员开发和利用大模型,需要把大模型的不确定性转为可控,所以利用了pydantic库来进行大模型输出的可控。利用pydantic可以指定一个 JSON 对象中必须包含哪些属性,这些属性的数据类型是什么,是否有默认值,以及其他一些约束条件。

考虑到大模型应用开发的工具库是有通用情况,所以框架集成了Image图片处理和生成,各种language大模型的集成(如openai,gemini等),Embedding向量数据库集成,prompts提示词工具,agent工具,读取解析各类文件工具,json数据可控工具,第三方数据api对接工具,数据库和缓存工具,搜索引擎内容工具等等。方便程序员快速利用和开发。

2,Embedding向量和RAG

RAG技术包括三个核心部分:索引(Indexing)、检索(Retrieval)和生成(Generation)

索引(Indexing),取用各大模型商提供的embeddings技术,把知识数据转化为向量数据。索引的一个关键部分是把知识整理好,本项目利用了llama_parse (https://cloud.llamaindex.ai/parse),强大的工具把各种文档的知识整理成markdown形式方便AI大模型学习和索引。同时也推荐doc2x和OmniParse等强大工具。

检索(Retrieval),本项目已经内置案例,本地文件保存和检索向量,还有pinecone和chromadb 等主流向量数据库。 chunking分段也有内置自定义函数,和利用llama_index的成熟方案。按自己实际情况选择,都有案例展示。chunking分段的可控也是RAG成功的重要部分,本项目有提供方法。

生成(Generation),常规的RAG对于我们程序员是很难可控的,输入输出都不可控,所以很难把AI大模型的技术整合在原有系统,所以本项目的重点是把它变成可控,生成(Generation)部分,代码例子如下:

# 定义qa prompt
    qa_prompt_tmpl_str = (
        "## 角色\n"
        "请你扮演中国软件水平考试高级辅导专家,负责用户发送的概念讲解和发送的题目解答。\n"

        "## 技能\n"
        "### 技能1:概念讲解\n"
        "当我发送一些软考概念题目的上下文信息。\n"
        "Step1:根据概括帮我讲解一下相关内容,讲解时尽量通俗易懂,并给出恰当的例子,优先使用 markdown 表格的形式来呈现\n"
        "Step2:出 2 道相关的选择题,在出完题目的最后给出答案和对答案的详细讲解。\n"

        "输出格式为:\n"
        "=====\n"

        "# 一、AI 讲解\n"
        "<概念讲解>\n"

        "# 二、AI 出题\n"
        "### (1)题目\n"
        "<出对应的2道选择题>\n"
        "### (2)答案和解析\n"
        "<所有选择题的答案和解释,每个答案和对应解释放在一起>\n"

        "=====\n"

        "## 要求\n"
        "1 必须使用中文回答我\n"
        "2 解答时,尽量使用通俗易懂的语言\n"
        "3 讲解时,如果有可能尽量给出相关例子\n"
        "4 讲解时,优先考虑使用 markdown 表格的方式呈现,如果出现不同层级的概念,可以将不同层级的概念用不同的表格表示\n"
        "5 给出答案和解析时,每道题的答案和解释要在一起给出,答案的解释需要详尽\n"
        "上下文信息如下。\n"
        "---------------------\n"
        "{context_str}\n"
        "---------------------\n"
        "请根据上下文信息而不是先验知识来回答以下的查询。回复格式:markdown 。 "
        "作为一个中国软考考试专家人工智能助手,你的回答要尽可能严谨。\n"
        "Query: 关于 {query_str}\n"
        "Answer: "
    )
    qa_prompt_tmpl = PromptTemplate(qa_prompt_tmpl_str)
    # 更新查询引擎中的prompt template
    query_engine.update_prompts(
        {"response_synthesizer:text_qa_template": qa_prompt_tmpl,
         "response_synthesizer:refine_template": refine_prompt_tmpl}
    )
  
    response = query_engine.query(question)

利用了自定义提示语等方法,把RAG的可控性大大增加。

3,Agent智能体代理

agent智能体必然会成为程序员开发的主要工作,因为这里太多模糊,定制,复杂,同时价值巨大。复杂性带来工作,所以必须非常重视这一块,所以本框架提供了最实用的智能体,同时做到最简化和最可控,这样才能整合在项目开发之中。利用了ReAct思想,让智能体自己选择合适的tool工具,反思和行动,然后一步一步自动完成和实现工作。

ReAct prompt 提示词如下:

prompt_template = """
                You run in a loop of Thought, Action, PAUSE, Action_Response.
                At the end of the loop you output an Answer.

                Use Thought to understand the question you have been asked.
                Use Action to run one of the actions available to you - then return PAUSE.
                Action_Response will be the result of running those actions.

                Your available actions are:

                {actions_list}

                To use an action, please use the following format:

                Thought: Do I need to use a tool? Yes

                Action:

                {{
                    "function_name": tool_name,
                    "function_params": {{
                        "param": "value"
                    }}
                }}

                Action_Response: the result of the action"""

智能体利用工具例子:

"""   ai agent tester """
@router.get("/ai_agent_lego")
async def ai_agent_lego(question: str, request: Request):
    
    llm_instance = LLM.create(provider=LLMProvider.OPENAI, model_name="gpt-4o")
    # Create an agent instance
    agent = Agent(LLMProvider.OPENAI, model_name="gpt-4o")

    #add tools
    agent.add_tool(tool_brick4_search)
    agent.add_tool(tool_smzdm)
    agent.add_tool(tool_brick4)

    user_query = question

    # Generate a response
    final_result = agent.generate_response(user_query)
    print(final_result)

测试过很多,效果是很不错的,可控的利用好智能体,满足程序员开发AI大模型应用的很重要部分。

框架使用的例子

1,可控的RAG应用:

    documents = SimpleDirectoryReader(input_files=['./data/abc.txt']).load_data()
        print(documents)

        index = VectorStoreIndex.from_documents(
            documents, storage_context=storage_context
        )

    query_engine = index.as_query_engine()
    # 可以指定固定回复格式 参考:https://segmentfault.com/a/1190000044329812
    # query_engine = index.as_query_engine(output_cls=BlogTitles) 

    # 定义qa prompt
    qa_prompt_tmpl_str = (
        "上下文信息如下。\n"
        "---------------------\n"
        "{context_str}\n"
        "---------------------\n"
        "请根据上下文信息而不是先验知识来回答以下的查询。回复格式:请务必把上下文的图片image jpg放在回答的底部作为参考。"
        "作为一个油画艺术人工智能助手,你的回答要尽可能严谨。\n"
        "Query: {query_str}\n"
        "Answer: "
    )
    qa_prompt_tmpl = PromptTemplate(qa_prompt_tmpl_str)

    # 定义refine prompt
    refine_prompt_tmpl_str = (
        "原始查询如下:{query_str}"
        "我们提供了现有答案:{existing_answer}"
        "我们有机会通过下面的更多上下文来完善现有答案(仅在需要时)。"
        "------------"
        "{context_msg}"
        "------------"
        "考虑到新的上下文,优化原始答案以更好地回答查询。 如果上下文没有用,请返回原始答案。"
        "Refined Answer:"
    )
    refine_prompt_tmpl = PromptTemplate(refine_prompt_tmpl_str)

    # 更新查询引擎中的prompt template
    query_engine.update_prompts(
        {"response_synthesizer:text_qa_template": qa_prompt_tmpl,
         "response_synthesizer:refine_template": refine_prompt_tmpl}
    )

    response = query_engine.query(question)

利用langchain,等框架往往都是黑盒子,很难可控的控制输入输出,本框架提供了一些方法让事情变得可控,适合程序员使用。太强大太通用,对于程序员来说不够,还不如可控有用,因为AI需要整合在项目,必须可控的输入输出。 RAG也是如此的可控的进入业务流程。

2,智能体问答助手

实现让智能体作为大模型的问答助手,自行判断用户的意图,自行调用不同的接口api和服务,来得到知识,准确的回答用户问题。以下是例子,实现一个积木问答机器人,用户可以问 积木新闻,搜索积木,积木新品,积木优惠活动,积木拼搭技术,等等,然后智能体自行判断调用哪个 积木函数,得到相关知识,多次调用,提供更好的问答。

本框架和langchain的不同agent使用在于,本框架的agent完全是透明的,可控的,代码和提示词都是自行修改,这样才能方便程序员实现可控的编程和整合。

"""   多tools的智能体 """
#Define a brick4 search tool  定义工具,依靠注释让agent自动判断调用哪个tool
def tool_brick4_search(keyword: str):
    """
    Search content from a given Keyword.
    Parameters: keyword (str)
    """
    llm_instance = LLM.create(provider=LLMProvider.OPENAI, model_name="gpt-4o") 
    search_query_encoded = quote_plus(keyword)
    sjina = str("http://brick4.com/get/set?filter_brandorder=0&filter_order=1&brandorder=1&page=1&s="+search_query_encoded)


"""   ai agent tester """
@router.get("/ai_agent_lego")
async def ai_agent_lego(question: str, request: Request):
# Create an agent instance
    agent = Agent(LLMProvider.OPENAI, model_name="gpt-4o")

    #add tools
    agent.add_tool(tool_brick4_search)
    agent.add_tool(tool_smzdm)
    agent.add_tool(tool_brick4)

    user_query = question

    # Generate a response
    final_result = agent.generate_response(user_query)

3, 写作机器人

当我们把所有tools都变成可控,输入输出可控,那么我们也可以轻松实现类似coze等的控制流,利用程序判断流程,轻松实现流程化的AI调用和整合。coze等总是报错,总是遇到小意外就出错停止反馈和回答了,这种可用性很难达到程序员的需求,所以必须自行实现和控制流程判断,处理异常情况等等。

""" 积木写作日报,  模仿hackernew 推送和新闻头条 """
@router.get("/ai_write_lego")
async def ai_write_lego(request: Request):
    
    llm_instance = LLM.create(provider=LLMProvider.OPENAI, model_name="moonshot-v1-128k")
    
    tool_brick_news_str = tool_brick_news()
    main_points_1 = llm_instance.generate_response(prompt=f"从以下[积木情报]中提取关键信息,格式为:(标题,日期时间,链接,图片),上下文是: {tool_brick_news_str}")
    tool_brick_news2_str = tool_brick_news2()
    main_points_1b = llm_instance.generate_response(prompt=f"从以下[积木情报]中提取关键信息,格式为:(标题,日期时间,链接,图片),上下文是: {tool_brick_news2_str}")
   
    tool_smzdm_str = tool_smzdm()
    main_points_2 = llm_instance.generate_response(prompt=f"从以下[积木优惠]中提取关键信息,格式为:(标题,价格,折扣,评价,平台,图片),上下文是: {tool_smzdm_str}")
    tool_brick4_str = tool_brick4()
    main_points_3 = llm_instance.generate_response(prompt=f"从以下[积木新品]中提取关键信息,格式为:(名称,价格,上市日期,平台,图片): {tool_brick4_str}")
    all_str ="\n\n # 今天积木情报: \n\n" + main_points_1 + "\n\n  " + main_points_1b + "\n\n # 今天积木优惠: \n\n" + main_points_2 + "\n\n # 今天积木新品: \n\n" + main_points_3
    
    main_points_best = llm_instance.generate_response(prompt=f"你是一个积木专栏作家,请给上下文内容一个总结,用中文回答,上下文如下: {all_str}")
    print(main_points_best)
    print(all_str)

总结:

本框架的目的就是为了:

1,利用好大模型技术带来的新作用,让程序员从处理信息转为处理知识,这是新的复杂性,带来很多新工作

2,可控性是整合各系统和流程的关键,同时智能的知识处理是整个多个复杂系统的粘合剂

3,本框架已经整合了很多有用的工具,节省大家开发时间。

4,本框架参考了多个开源项目,特别致谢 learnwithhasan.com

欢迎点赞,项目地址: https://github.com/liangdabiao/simple_ai_toolset