项目背景:
作为最普通的程序员一员,同时知道未来的开发必然是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 的一些主要特点:
- 快速:FastAPI 基于标准 Python 类型提示,可以自动推断数据类型,从而实现高性能。
- 简洁:代码简洁,易于理解和维护。
- 功能丰富:提供了丰富的功能,如数据验证、序列化和认证等。
- 异步支持:支持异步请求处理,可以提高应用的并发处理能力。
- 交互式文档:自动生成交互式 API 文档,方便开发者和用户使用。
- 类型检查:利用 Python 类型提示,可以在运行时进行类型检查,减少错误。
- 依赖注入:支持依赖注入,方便管理依赖关系。
- 数据库集成:可以轻松集成数据库,如 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