Smarty实现页面静态化(生成HTML)的方法 / 张生荣

mikel阅读(396)

来源: Smarty实现页面静态化(生成HTML)的方法 / 张生荣

本文实例讲述了Smarty实现页面静态化(生成HTML)的方法。分享给大家供大家参考,具体如下:

为了减少数据库读取次数,某些内容不经常被更改的页面,比如文章详细页面需要做成HTML静态页面。

在使用Smarty的情况下,也可以实现页面静态化。下面先简单说一下使用Smarty时通常动态读取的做法。

一般分这几步:

1、通过URL传递一个参数(ID);

2、然后根据此ID查询数据库;

3、取得数据后根据需要修改显示内容;

4、assign需要显示的数据;

5、display模板文件。

Smarty静态化过程只需要在上述过程中添加两个步骤:

第一:在1之前使用 ob_start() 打开缓冲区。

第二:在5之后使用 ob_get_contents() 获取内存未输出内容,然后使用fwrite()将内容写入目标html文件。

根据上述描述,此过程是在网站前台实现的,而内容管理(添加、修改、删除)通常是在后台进行,为了能有效

利用上述过程,可以使用一点小手段,那就是Header()。具体过程是这样的:在添加、修改程序完成之后,使用

Header() (当然还有其它方式)跳到前台读取,这样可以实现页面HTML化,然后在生成html后再跳回后台管理侧,而这两个跳转

过程是不可见的。

  1. <?php
  2. $cachefile=“./cache/demo.html”;//把缓存文件放到一个cache文件夹里
  3. $cachetime=20;
  4. if (!file_exists($cachefile ) || filemtime($cachefile)+$cachetime < time()) //判断是否存在和过期时间
  5. {
  6. ob_start();//输出控制
  7. echo ‘<table border=”1″ width=”800″ align=”center”>’;
  8. echo ‘<caption><h1>user</h1></caption>’;
  9. echo ‘<tr>’;
  10. echo “<td>11111</td>”;
  11. echo “<td>22222</td>”;
  12. echo ‘</tr>’;
  13. echo ‘<tr>’;
  14. echo “<td>11111</td>”;
  15. echo “<td>22222</td>”;
  16. echo ‘</tr>’;
  17. echo ‘</table>’;
  18. $html=ob_get_contents();
  19. file_put_contents($cachefile, $html);//输出到缓存文件
  20. ob_end_flush();//输出并关闭缓冲区
  21. }
  22. else{
  23. echo ‘ceshi’;
  24. include $cachefile;
  25. }
  26. ?>

更多关于Smarty相关内容感兴趣的读者可查看本站专题:《smarty模板入门基础教程》、《PHP模板技术总结》、《PHP基于pdo操作数据库技巧总结》、《PHP运算与运算符用法总结》、《PHP网络编程技巧总结》、《PHP基本语法入门教程》、《php面向对象程序设计入门教程》、《php字符串(string)用法总结》、《php+mySQL数据库操作入门教程》及《php常见数据库操作技巧汇总》

希望本文所述对大家基于smarty模板的PHP程序设计有所帮助。

使用PHP Smarty静态化,使用smarty模板或缓存实现页面静态化-CSDN博客

mikel阅读(371)

来源: 使用PHP Smarty静态化,使用smarty模板或缓存实现页面静态化-CSDN博客

第一种:利用模板。目前PHP的模板可以说是很多了,有功能强大的smarty,还有简单易用的smarttemplate等。它们每一种模板,都有一个获取输出内容的函数。我们生成静态页面的方法,就是利用了这个函数。用这个方法的优点是,代码比较清晰,可读性好。

这里我用smarty做例子,说明如何生成静态页

require(‘smarty/Smarty.class.php’);

$t = new Smarty;

$t->assign(“title”,”Hello World!”);

$content = $t->fetch(“templates/index.htm”);

//这里的 fetch() 就是获取输出内容的函数,现在$content变量里面,就是要显示的内容了

$fp = fopen(“archives/2005/05/19/0001.html”, “w”);

fwrite($fp, $content);

fclose($fp);

第二种方法:利用ob系列的函数。这里用到的函数主要是 ob_start(), ob_end_flush(), ob_get_content(),其中ob_start()是打开浏览器缓冲区的意思,打开缓冲后,所有来自PHP程序的非文件头信息均不会发送,而是保存在内部缓冲区,直到你使用了ob_end_flush().而这里最重要的一个函数,就是ob_get_contents(),这个函数的作用是获取缓冲区的内容,相当于上面的那个fetch(),道理一样的。代码:

ob_start();

echo “Hello World!”;

$content = ob_get_contents();//取得php页面输出的全部内容

$fp = fopen(“archives/2005/05/19/0001.html”, “w”);

fwrite($fp, $content);

fclose($fp);

我选用的第2种方法 也就是用ob系列的函数

我刚开始看这个的时候有点不太明白 后来才知道ob是output buffering的意思 也就是输出缓存

当你准备输出的时候 所有的数据都保存在ob里面 服务器解析完php以后 把所有要输出到客户端的html代码都存放在ob里面 如果我们要输出html静态页面 只要把缓存取出来写入一个html页面即可

所以原理其实是很简单的

这里用到了几个函数 由于我初学php 很多函数我还不了解 所以这里也说明一下 希望可以帮助大家

ob_start():开始“捕捉”缓存 也就是从这里开始 打开浏览器的缓存

ob_end_flush():关闭浏览器缓存

ob_get_content():读取缓存内容

fopen(“文件路径”,”打开模式”)打开文件 这个函数的打开模式有好几种 下面介绍几种主要的模式:

‘r’ 只读方式打开,将文件指针指向文件头。??

‘r+’ 读写方式打开,将文件指针指向文件头。??

‘w’ 写入方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。??

‘w+’ 读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。??

fwrite(“文件名称”,”写入内容”) 写入文件

fclose() 关闭文件

由于我要转换的html文件非常多 可能有几百个 所以这里不能静态指定fopen的路径 大家可以设置一个路径变量 里面可以保存用户传来的id等信息 方便进行html文件命名 下面是我结合上次php读取xml数据的一个简单例子

程序代码

ob_start();????????????//打开浏览器缓存

//下面是读取xml数据

$parser = xml_parser_create(); //创建一个parser编辑器

xml_set_element_handler($parser, “startElement”, “endElement”);//设立标签触发时的相应函数 这里分别为startElement和endElenment

xml_set_character_data_handler($parser, “characterData”);//设立数据读取时的相应函数

$xml_file=”1.xml”;//指定所要读取的xml文件,可以是url

$filehandler = fopen($xml_file, “r”);//打开文件

while ($data = fread($filehandler, 4096))

{

xml_parse($parser, $data, feof($filehandler));

}//每次取出4096个字节进行处理

fclose($filehandler);

xml_parser_free($parser);//关闭和释放parser解析器

$name=false;

$position=false;

function startElement($parser_instance, $element_name, $attrs) //起始标签事件的函数

{

global $name,$position;??

if($element_name==”NAME”)

{

$name=true;

$position=false;

echo “名字:”;

}

if($element_name==”POSITION”)

{$name=false;

$position=true;

echo “职位:”;

}

}

function characterData($parser_instance, $xml_data) //读取数据时的函数

{

??global $name,$position;

??if($position)

echo $xml_data.”

“;

if($name)

echo $xml_data.”

“;

}

function endElement($parser_instance, $element_name) //结束标签事件的函数

{

global $name,$position;

$name=false;

$position=false;

}

?>

//xml数据读取完毕

$htmlname=$id.”.html”;??????????????????????????//$id可以自己定义 这里代表用户传来的id

$htmlpath=”archives/”.$htmlname;?????? //设置路径变量

$content = ob_get_contents();??????????????//取得php页面输出的全部内容

$fp = fopen($htmlpath, “w”);

fwrite($fp, $content);

fclose($fp);

?>

ThinkPHP 的页面静态化功能的实现_thinkphp 静态化-CSDN博客

mikel阅读(329)

来源: ThinkPHP 的页面静态化功能的实现_thinkphp 静态化-CSDN博客

**
常说的页面静态化分为两种,一种是伪静态,即url 重写,一种是真静态化。

上一篇讲到了ThinkPHP自带的buildHtml()方法生成静态页面,在ThinkPHP的官方文档也没具体讲到此方法,而文档却很具体的讲了静态缓存技术,看来TP官方还是建议使用静态缓存。

一. 网站不分手机版和电脑版,只有一套前台模板

(1)在根目录下的程序入口文件index.php中加下面这行:

define(‘HTML_PATH’, ‘./HTML/’);//生成静态页面的文件位置

(2)在项目的配置文件config.php中进行如下配置:
‘HTML_CACHE_ON’ => true, // 开启静态缓存
‘HTML_CACHE_TIME’ => 604800, // 全局静态缓存有效期(秒)(3600247)
‘HTML_FILE_SUFFIX’=> ‘.html’, // 设置静态缓存文件后缀
‘HTML_CACHE_RULES’=> array( // 定义静态缓存规则
// 定义格式1 数组方式
// 定义格式2 字符串方式
//后一个参数是静态缓存有效期,单位为秒。如果不定义,则会获取配置参数HTML_CACHE_TIME 的设置值,如果定义为0则表示永久缓存。
‘Index:index’ =>array(’{:controller}{:action}’),
‘Index:articalList’ =>array(’{:controller}
{:action}/{id}{p}list’),
‘Index:articalInfo’ =>array(’{:controller}{:action}/{id}’),
‘Index:productList’ =>array(’{:controller}
{:action}/{id}{p}list’),
‘Index:productInfo’ =>array(’{:controller}{:action}/{id}’),
//‘Index:staticcachec’ =>array(’{:module}/{:controller}/{:action}/{id}’,30),
//‘Index:staticcachec’ =>array(’{:module}/{:controller}/{:action}
{id}’,30),
)
{:module} 、{:controller} 和{:action} 分别表示当前模块名、控制器名和操作名,{id} 其实等效于 {$_GET.id},{p}则表示分页。在{}之外的字符作为字符串对待,如果包含有”/”,会自动创建目录。
在前台第一次访问某个模块的某个控制器的某个方法时,就会在指定的目录按照指定的文件名格式生成静态HTML文件。如图:

此时访问网站虽然URL没有变,但访问到的就是静态化之后的页面。亲测有效,但是此法也有不足:

如果系统分电脑版和手机版两套模板,那么生成的静态页面只有一套,手机先访问就生成手机的模板,再用电脑访问的界面就会混乱。

但如若网站只有一套模板(比如电脑版)那么到此就够了。如果手机电脑两套模板,解决方法如下

二. 网站分手机版和电脑版两套模板

(1)在根目录下的程序入口文件index.php中进行如下配置:

define(‘HTML_PATH’, ‘./HTML/’);//生成静态页面的文件位置

//如果是手机访问,设置手机缓存目录
if(isMobile()){
KaTeX parse error: Expected ‘EOF’, got ‘}’ at position 37: …] = ‘mobile/’; }̲elseif(isset(_SESSION[‘theme_path’])){
unset($_SESSION[‘theme_path’]);
}

//判断是否是手机访问
function isMobile(){
if(isset(KaTeX parse error: Double subscript at position 16: _SERVER[‘HTTP_X_̲WAP_PROFILE’]))…_SERVER[‘HTTP_VIA’]))return stristr(S E R V E R [ ′ H T T P V I A ′ ] , &quot; w a p &quot; ) ? t r u e : f a l s e ; i f ( i s s e t ( _SERVER[&#x27;HTTP_VIA&#x27;], &quot;wap&quot;) ? true : false; if(isset(SERVER[HTTPVIA],wap)?true:false;if(isset(_SERVER[‘HTTP_USER_AGENT’])){$clientkeywords = array (‘nokia’,‘sony’,‘ericsson’,‘mot’,‘samsung’,‘htc’,‘sgh’,‘lg’,‘sharp’,‘sie-’,‘philips’,‘panasonic’,‘alcatel’,‘lenovo’,‘iphone’,‘ipod’,‘blackberry’,‘meizu’,‘Android’,‘netfront’,‘symbian’,‘ucweb’,‘windowsce’,‘palm’,‘operamini’,‘operamobi’,‘openwave’,‘nexusone’,‘cldc’,‘midp’,‘wap’,‘mobile’);if (preg_match(“/(” . implode(’|’, c l i e n t k e y w o r d s ) . &quot; ) / i &quot; , s t r t o l o w e r ( clientkeywords) . &quot;)/i&quot;, strtolower(clientkeywords).)/i,strtolower(_SERVER[‘HTTP_USER_AGENT’])))return true;}
if(isset(KaTeX parse error: Expected ‘}’, got ‘EOF’ at end of input: …])){if((strpos(_SERVER[‘HTTP_ACCEPT’], ‘vnd.wap.wml’) !== false) && (strpos(S E R V E R [ ′ H T T P A C C E P T ′ ] , ′ t e x t / h t m l ′ ) = = = f a l s e ∣ ∣ ( s t r p o s ( _SERVER[&#x27;HTTP_ACCEPT&#x27;], &#x27;text/html&#x27;) === false || (strpos(SERVER[HTTPACCEPT],text/html)===false(strpos(_SERVER[‘HTTP_ACCEPT’], ‘vnd.wap.wml’) < strpos($_SERVER[‘HTTP_ACCEPT’], ‘text/html’))))return true;}
return false;
}

(2)在项目的配置文件config.php中进行如下配置:
//设置静态缓存
‘HTML_CACHE_ON’ => true, // 开启静态缓存
‘HTML_CACHE_TIME’ => 604800, // 全局静态缓存有效期(秒)(3600247)
‘HTML_FILE_SUFFIX’=> ‘.html’, // 设置静态缓存文件后缀
‘HTML_CACHE_RULES’=> array( // 定义静态缓存规则
// 定义格式2 字符串方式
‘Index:index’ =>array(’{KaTeX parse error: Expected ‘EOF’, got ‘}’ at position 20: …SION.theme_path}̲/{:controller}_…SESSION.theme_path}/{:controller}{:action}/{id}_{p}list’),
‘Index:articalInfo’ =>array(’{KaTeX parse error: Expected ‘EOF’, got ‘}’ at position 20: …SION.theme_path}̲/{:controller}_…SESSION.theme_path}/{:controller}{:action}/{id}
{p}_list’),
//‘Index:productInfo’ =>array(’{$SESSION.theme_path}/{:controller}{:action}/{id}’),
)

那么手机访问的时候,会先以缓存的方式生成一个名字为theme_path内容为mobile/的字符串缓存,然后在HTML/mobile/目录下生成相应的手机模板静态文件;如果再用电脑访问,那么会先清除名字为theme_path的字符串缓存内容,那么再生成静态文件的时候就在HTML/目录下了。

作者:江南极客
来源:CSDN
原文:https://blog.csdn.net/sinat_35861727/article/details/54971805
版权声明:本文为博主原创文章,转载请附上博文链接!**

TinkPHP6-tp6实现全站静态化方法-ThinkPHP-萝卜网络博客

mikel阅读(339)

来源: TinkPHP6-tp6实现全站静态化方法-ThinkPHP-萝卜网络博客

一,前言

模板完全静态化,也就是通过模板完全生成纯静态的网页,相比动态页面和伪静态页面更安全更利于SEO访问更快。

二,实现思路

1,根据模块/控制器_MD5(参数)动态递归创建目录

2,file_exists判断生成的静态页是否存在,是否过期,存在并且未过期则重定向到静态网页

3,不存在或者文件已过期,则file_put_contents($file,$content)函数生成静态页面

三,编码

1,基类中的生成前与生成后的方法

class Common extends BaseController
{
    //静态模板生成目录
    protected $staticHtmlDir = "";
    //静态文件
    protected $staticHtmlFile = "";             

    //判断是否存在静态
    public function beforeBuild($param = []) {
        //生成静态
        $this->staticHtmlDir = "html".DS.$this->request->controller().DS;
        //参数md5
        $param = md5(json_encode($param));
        $this->staticHtmlFile = $this->staticHtmlDir .$this->request->action() . '_'  . $param .'.html';
        
        //目录不存在,则创建
        if(!file_exists($this->staticHtmlDir)){
            mkdir($this->staticHtmlDir);
        }

        //静态文件存在,并且没有过期
        if(file_exists($this->staticHtmlFile) && filectime($this->staticHtmlFile)>=time()-60*60*24*5) {
            header("Location:/" . $this->staticHtmlFile);
            exit();
        }

    }

    //开始生成静态文件
    public function afterBuild($html) {
        if(!empty($this->staticHtmlFile) && !empty($html)) {
            if(file_exists($this->staticHtmlFile)) {
                \unlink($this->staticHtmlFile);
            }
            if(file_put_contents($this->staticHtmlFile,$html)) {
                header("Location:/" . $this->staticHtmlFile);
                exit();
            }
        }
    }

}

2,控制器中的使用

public function getList($id = '')
{
    //判断静态界面是否存在
    $this->beforeBuild(array($id));

    // do someing
    $name = "测试静态化";
    $html = View::fetch('/get_list',['name'=>$name]);

    //生成静态界面
    $this->afterBuild($html);

}

一套以用户体验出发的.NET8 Web开源框架 - 追逐时光者 - 博客园

mikel阅读(553)

来源: 一套以用户体验出发的.NET8 Web开源框架 – 追逐时光者 – 博客园

前言

今天大姚给大家分享一套以用户体验出发的.NET8 Web开源框架:YiFramework。

项目介绍

YiFramework是一个基于.NET8 + Abp.vNext + SQLSugar 的DDD领域驱动设计后端开源框架,前端使用Vue3,项目架构模式三层架构\DDD领域驱动设计,内置RBAC权限管理、BBS论坛社区系统 以用户体验出发。架构干净整洁、采用微软风格原生框架封装。适合小中大型项目上线、.NET8学习、Abp.vNext学习、SQLsugar学习 、项目二次开发。

项目特点

框架简单易用,框架不以打包形式引用,而是直接以项目附带源码给出,自由度拉满,遵循MIT License协议,允许随意修改(请注明来源即可)。

核心技术

后端

.NET8、Abp.vNext、Jwt、Serilog、Autofac、Mapster、SQLsugarCore、Quartz.Net等。

前端

vue3、axios、echarts、element-plus、vue-router、vite、pinia等。

运维

nginx、gitlab、Jenkins、harbor。

业务支持模块

RABC权限管理系统

用户管理、角色管理、菜单管理、部门管理、岗位管理、字典管理、参数管理、用户在线、登录日志、定时任务、服务监控等功能模块。

BBS社区论坛系统

文章功能、板块功能、主题功能、个人中心、授权中心、权限管理。

演示截图

 

项目源码地址

更多项目实用功能和特性欢迎前往项目开源地址查看👀,别忘了给项目一个Star支持💖。

优秀项目和框架精选

该项目已收录到C#/.NET/.NET Core优秀项目和框架精选中,关注优秀项目和框架精选能让你及时了解C#、.NET和.NET Core领域的最新动态和最佳实践,提高开发工作效率和质量。坑已挖,欢迎大家踊跃提交PR推荐或自荐(让优秀的项目和框架不被埋没🤞)。

大模型应用开发初探 : 手搓一个简易Agent - EdisonZhou - 博客园

mikel阅读(384)

来源: 大模型应用开发初探 : 手搓一个简易Agent – EdisonZhou – 博客园

大家好,我是Edison。

今天是中秋节前最后一个工作日,加油挺住,马上就放假了!

近期我一直在学习和了解LLM的相关知识,听到大家都在谈论AI Agent,说它是接下来几年大模型应用开发的新范式,那么什么是AI Agent,如何快速开发一个AI Agent呢?

AI Agent:可以帮你执行任务的助手

学术界和工业界对术语“AI Agent”提出了各种定义。其中,OpenAI将AI Agent定义为“以大语言模型为大脑驱动的系统,具备自主理解、感知、规划、记忆和使用工具的能力,能够自动化执行完成复杂任务的系统。”

说人话就是:大多数时候你给它一个最终你想要达成的目标,它能直接交付结果,过程你啥都不用管

NOTE:如果说人和动物的区别是人会使用各种工具,那么Agent和大模型的区别亦然。

我们可以把Agent与LLM形象地比作生物体与其大脑,Agent有手有脚,可以自己干活自己执行,而LLM呢,就是它的大脑。比如,如果你使用LLM大模型,它可能只能给你输出一份食谱,告诉你需要哪些食材和步骤来制作。但如果你使用Agent,它可能就是不仅提供食谱和步骤,还会根据你的需求,帮你选择合适的食材甚至自动下单购买,监控烹饪过程,确保食物口感,最终为你呈上一份佳肴。

AI Agent如何工作?

AI Agent的架构是其智能行为的基础,它通常包括感知、规划、记忆、工具使用和行动等关键组件,这些组件协同工作以实现高效的智能行为。

AI Agent的工作流程其实就是一个连续的循环过程

它从感知环境开始,经过信息处理、规划和决策,然后执行行动。最后,根据执行结果和环境反馈进行调整,以优化未来的行动和决策。

通过这种结构化和层次化的方式,AI Agent能够有效地处理信息,做出决策,并在复杂环境中执行任务。

如何开发AI Agent?

目前业界开发AI Agent主要有两种模式:

一种是基于Python或C#等编程语言,结合LangChain或Semantic Kernel等大模型应用开发框架,集成某个大模型API 和 企业内部的业务API能力,来完成具体领域的Agent。

另一种是基于Coze、Dify、AutoGen等Agent开发管理平台,拖过拖拉拽的方式快速生成一个Agent,与其说是开发,不如说是Workflow一样的配置。当然,也需要给这些平台注册封装好的企业内部API平台提供的能力供配置好的Agent去实现工具调用。

使用Semantic Kernel开发AI Agent

这里我们快速使用Semantic Kernel开发一个简易的WorkOrder Agent(MES工单助手),重点关注如何给LLM添加Function Calling能力,直观了解Agent规划任务 和 执行任务 的效果,而至于其他更加具体的,等待后续了解深入后再交流,这里我们就先来个感性认识即可。

以终为始,先看效果吧:

(1)没有实现Function Calling的效果,它就只是个Chatbot

(2)实现了Function Calling的效果,它就可以称为Agent

可以看到,我的需求其实包含两个步骤:第一步是更新工单的Quantity,第二步是查询更新后的工单信息。而这两个步骤我们假设其实都是需要去调用MES WorkOrderService API才能获得的,而这就需要我们给LLM加入Function Calling的能力,当然LLM自己得知道如何规划执行的步骤,哪个步骤先执行,哪个后执行。

示例代码的结构如下所示:

关键部分代码:

(1)Shared

OpenAiConfiguration.cs

复制代码
public class OpenAiConfiguration
{
    public string Provider { get; set; }
    public string ModelId { get; set; }
    public string EndPoint { get; set; }
    public string ApiKey { get; set; }

    public OpenAiConfiguration(string modelId, string endPoint, string apiKey)
    {
        Provider = ConfigConstants.LLMProviders.OpenAI; // Default OpenAI-Compatible LLM API Provider
        ModelId = modelId;
        EndPoint = endPoint;
        ApiKey = apiKey;
    }

    public OpenAiConfiguration(string provider, string modelId, string endPoint, string apiKey)
    {
        Provider = provider;
        ModelId = modelId;
        EndPoint = endPoint;
        ApiKey = apiKey;
    }
}
复制代码

CustomLLMApiHandler.cs

复制代码
public class CustomLLMApiHandler : HttpClientHandler
{
    private readonly string _openAiProvider;
    private readonly string _openAiBaseAddress;

    public CustomLLMApiHandler(string openAiProvider, string openAiBaseAddress)
    {
        _openAiProvider = openAiProvider;
        _openAiBaseAddress = openAiBaseAddress;
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        UriBuilder uriBuilder;
        Uri uri = new Uri(_openAiBaseAddress);
        switch (request.RequestUri?.LocalPath)
        {
            case "/v1/chat/completions":
                switch (_openAiProvider)
                {
                    case ConfigConstants.LLMProviders.ZhiPuAI:
                        uriBuilder = new UriBuilder(request.RequestUri)
                        {
                            Scheme = "https",
                            Host = uri.Host,
                            Path = ConfigConstants.LLMApiPaths.ZhiPuAIChatCompletions,
                        };
                        request.RequestUri = uriBuilder.Uri;
                        break;
                    default: // Default: OpenAI-Compatible API Providers
                        uriBuilder = new UriBuilder(request.RequestUri)
                        {
                            Scheme = "https",
                            Host = uri.Host,
                            Path = ConfigConstants.LLMApiPaths.OpenAIChatCompletions,
                        };
                        request.RequestUri = uriBuilder.Uri;
                        break;
                }
                break;
        }

        HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
        return response;
    }
}
复制代码

(2)WorkOrderService

这里我直接模拟的API的逻辑,你可以使用HttpClient去实际调用某个API。

复制代码
public class WorkOrderService
{
    private static List<WorkOrder> workOrders = new List<WorkOrder>
            {
                new WorkOrder { WorkOrderName = "9050100", ProductName = "A5E900100", ProductVersion = "001 / AB", Quantity = 100, Status = "Ready" },
                new WorkOrder { WorkOrderName = "9050101", ProductName = "A5E900101", ProductVersion = "001 / AB", Quantity = 200, Status = "Ready" },
                new WorkOrder { WorkOrderName = "9050102", ProductName = "A5E900102", ProductVersion = "001 / AB", Quantity = 300, Status = "InProcess" },
                new WorkOrder { WorkOrderName = "9050103", ProductName = "A5E900103", ProductVersion = "001 / AB", Quantity = 400, Status = "InProcess" },
                new WorkOrder { WorkOrderName = "9050104", ProductName = "A5E900104", ProductVersion = "001 / AB", Quantity = 500, Status = "Completed" }
            };

    public WorkOrder GetWorkOrderInfo(string orderName)
    {
        return workOrders.Find(o => o.WorkOrderName == orderName);
    }

    public string UpdateWorkOrderStatus(string orderName, string newStatus)
    {
        var workOrder = this.GetWorkOrderInfo(orderName);
        if (workOrder == null)
            return "Operate Failed : The work order is not existing!";
        // Update status if it is valid
        workOrder.Status = newStatus;
        return "Operate Succeed!";
    }

    public string ReduceWorkOrderQuantity(string orderName, int newQuantity)
    {
        var workOrder = this.GetWorkOrderInfo(orderName);
        if (workOrder == null)
            return "Operate Failed : The work order is not existing!";

        // Some business checking logic like this
        if (workOrder.Status == "Completed")
            return "Operate Failed : The work order is completed, can not be reduced!";
        if (newQuantity <= 1 || newQuantity >= workOrder.Quantity)
            return "Operate Failed : The new quantity is invalid!";
        // Update quantity if it is valid
        workOrder.Quantity = newQuantity;
        return "Operate Succeed!";
    }
}
复制代码

(3)Form

appsetting.json

复制代码
{
  "LLM_API_PROVIDER": "ZhiPuAI",
  "LLM_API_MODEL": "glm-4",
  "LLM_API_BASE_URL": "https://open.bigmodel.cn",
  "LLM_API_KEY": "***********" // Update this value to yours
}
复制代码

AgentForm.cs

初始化Kernel

复制代码
 private Kernel _kernel = null;
 private OpenAIPromptExecutionSettings _settings = null;
 private IChatCompletionService _chatCompletion = null;
 private ChatHistory _chatHistory = null;

private void ChatForm_Load(object sender, EventArgs e)
    {
        var configuration = new ConfigurationBuilder().AddJsonFile($"appsettings.ZhiPu.json");
        var config = configuration.Build();
        var openAiConfiguration = new OpenAiConfiguration(
            config.GetSection("LLM_API_PROVIDER").Value,
            config.GetSection("LLM_API_MODEL").Value,
            config.GetSection("LLM_API_BASE_URL").Value,
            config.GetSection("LLM_API_KEY").Value);
        var openAiClient = new HttpClient(new CustomLlmApiHandler(openAiConfiguration.Provider, openAiConfiguration.EndPoint));
        _kernel = Kernel.CreateBuilder()
            .AddOpenAIChatCompletion(openAiConfiguration.ModelId, openAiConfiguration.ApiKey, httpClient: openAiClient)
            .Build();

        _chatCompletion = _kernel.GetRequiredService<IChatCompletionService>();

        _chatHistory = new ChatHistory();
        _chatHistory.AddSystemMessage("You are one WorkOrder Assistant.");
    }
复制代码

注册Functions

复制代码
_kernel.Plugins.Add(KernelPluginFactory.CreateFromFunctions("WorkOrderHelperPlugin",
    new List<KernelFunction>
    {
                    _kernel.CreateFunctionFromMethod((string orderName) =>
                    {
                        var workOrderRepository = new WorkOrderService();
                        return workOrderRepository.GetWorkOrderInfo(orderName);
                    }, "GetWorkOrderInfo", "Get WorkOrder's Detail Information"),
                    _kernel.CreateFunctionFromMethod((string orderName, int newQuantity) =>
                    {
                        var workOrderRepository = new WorkOrderService();
                        return workOrderRepository.ReduceWorkOrderQuantity(orderName, newQuantity);
                    }, "ReduceWorkOrderQuantity", "Reduce WorkOrder's Quantity to new Quantity"),
                    _kernel.CreateFunctionFromMethod((string orderName, string newStatus) =>
                    {
                        var workOrderRepository = new WorkOrderService();
                        return workOrderRepository.UpdateWorkOrderStatus(orderName, newStatus);
                    }, "UpdateWorkOrderStatus", "Update WorkOrder's Status to new Status")
     }
));
复制代码

开启自动调用Function,告诉大模型可以自行决定调用相关Functions,而且大模型会自行决定根据什么顺序来调用,这就是大模型作为Agent大脑的规划能力。当然,我们还可以通过定制化Planner来增强agent的规划能力,这个就留到后面再分享:

_settings = new OpenAIPromptExecutionSettings
{
   ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};

发送用户的提示词给大模型:

复制代码
_chatHistory.AddUserMessage(tbxPrompt.Text);
        ChatMessageContent chatResponse = null;
        tbxResponse.Clear();

if (cbxUseFunctionCalling.Checked)
{
            Task.Run(() =>
            {
                ShowProcessMessage("AI is handling your request now...");
                chatResponse = _chatCompletion.GetChatMessageContentAsync(_chatHistory, _settings, _kernel)
                    .GetAwaiter()
                    .GetResult();
                UpdateResponseContent(chatResponse.ToString());
                ShowProcessMessage("AI Response:");
            });
        }
else
{
            Task.Run(() =>
            {
                ShowProcessMessage("AI is handling your request now...");
                chatResponse = _chatCompletion.GetChatMessageContentAsync(_chatHistory, null, _kernel)
                    .GetAwaiter()
                    .GetResult();
                UpdateResponseContent(chatResponse.ToString());
                ShowProcessMessage("AI Response:");
            });
}
复制代码

小结

本文简单介绍了AI Agent的基本概念 和 工作方式,目前主要有两种开发Agent的模式,一种是高代码手搓,另一种是低代码拖拉拽。

最后,本文通过C# + Semantic Kernel + 智谱GLM-4模型 演示了如何快速开发一个简易的AI Agent,虽然它只是个Demo,但希望对你快速了解Agent有所帮助!

示例源码

本文示例:https://github.com/Coder-EdisonZhou/EDT.Agent.Demos

本文大模型:智谱 GLM-4 模型

推荐学习

Microsoft Learn, 《Semantic Kernel 学习之路

大模型应用开发初探 : 基于Coze创建Agent - EdisonZhou - 博客园

mikel阅读(614)

来源: 大模型应用开发初探 : 基于Coze创建Agent – EdisonZhou – 博客园

大家好,我是Edison。

最近学习了一门课程《AI Agent入门实战》,了解了如何在Coze平台上创建AI Agent,发现它对我们个人(C端用户)而言十分有用,分享给你一下。

Coze是什么?

Coze(扣子)是字节跳动公司开发的新一代AI应用开发平台,使用这个AI应用开发平台,无论你是否有编码基础,都可以快速搭建基于大语言模型的各类AI Bot,还可以将Bot发布到其他渠道。对于一个AI Agent而言,最重要的能力就是任务规划、调用工具、知识库 和 记忆能力,而这些能力在Coze中你都不需要关注,已经封装好了提供给你,对你而言就是透明的。

如上图所示,我们可以对我们要做的AI Agent先设立人设,然后给它注册想要调用的工具或工作流,还可以给它注册一个内部知识库(文档/图片/表格等),如果想要记忆能力甚至可以直接给它添加一个数据库供其使用,最后再通过调试模块进行测试,一个针对AI Agent的“宇宙最强IDE“也不过如此。
目前,Coze有两个版本:

(1)基础版:面向尝鲜体验的个人和企业开发者,全部功能免费使用,但有一定的限量额度,超过后不可再使用,需切换专业版后继续使用。

(2)专业版:面向对稳定性和用量有更高需求的专业开发者,支持更高团队空间容量和免费知识库容量,付费功能保障专业级 SLA,不限制调用请求频率和总量,费用按实际用量计算。

这里,我用的是基础版,主要是尝尝鲜,做了几个DEMO体验下效果,用到的模型主要是豆包的Function Call模型。未来,我们可能会主要尝试企业内部搭建的FastGPT或Dify,又或者是微软的AutoGen。

下面,主要通过我做的这几个DEMO一起来看看Coze提供的一些关键能力。

强大的工作流配置

我通过Coze创建了一个城市天气助手的Bot,使用了Coze提供的工作流能力,如下图所示,这是一个获取天气预报并解析的工作流:

可以看到,通过一个简单的工作流,我们就快速调用了大模型 和 插件(墨迹天气)的能力,而这些操作在传统的编码场景下,都需要程序员单独来处理,现在则是0代码纯配置就可以了。

基于这个工作流,我再把人设和回复的逻辑配置一下提示词,就可以完成一个Bot的创建。值得一提的是,针对你的提示词,Coze提供了一个优化的功能,可以按照最佳实践将你的提示词做一个优化,这真的是一个很实用的功能。

最后实现的效果如下图所示:

强大的图像流配置

我通过Coze创建了一个产品图背景替换助手的Bot,用到了Coze提供的另一个强大技能:图像流。这也是一个工作流,但是其用到了专门针对图像处理的处理节点,例如图像生成、背景替换、画质提升等等。这些功能对于有做社交媒体运营的朋友,应该挺有帮助的。

最后的效果如下图所示:我把原图 和 想要替换的背景描述给它,它给我输出了一张还算不错的背景替换图。

快捷的知识库应用

Coze支持不同格式的知识库,例如文本类型(如txt, pdf, doc等)、表格类型(如xls等)以及 照片类型(如png, jpg等)。

比如,我创建了一个MongoDB知识助手的Bot,就导入了一些MongoDB的体系课程的pdf文档:

最终的效果如下图所示:

又如,我创建了一个产品图查询助手的Bot,可以基于我导入的产品图资料库,让我可以快速的查找到对应的产品图。

效果如下图所示:

再如,假设我是一个在线课堂的老板,我将课程订单表(Excel)导入到知识库中,通过对人设和回复逻辑的设置,就可以实现一个快速查询的功能:

透明的记忆能力

假设我是一个在线课堂的老板,我可以用Coze创建一个在线客服,让它和客户对话,并试图引导用户留下姓名和联系方式,这就需要一个类似于数据库的记忆能力。

这样配置后,一旦客户在对话中留下联系方式,我们的Bot就会自动将其存入预先设置的数据库中:

其他能力

对于客服类Bot,语音能力是非常重要的,在Coze中可以支持语音通话,还有多种口音供选择,个人觉得这是很方便的一个支持能力。

发布到订阅号

Coze可以支持发布到多个平台,未来可能真的会有Agent Store的概念。不过,我目前最喜欢的还是可以直接发布到微信订阅号,这样大家在给我回复时,不只是有冷冰冰的自动回复,而是有情绪价值的回复,for all of you!

小结

本文简单介绍了Coze(扣子)这个AI应用开发平台的主要功能,通过我所学习实践的一些DEMO来了解一下在AI Agent开发中涉及到一些核心概念如工作流、图像流、记忆能力、知识库等等,相信会对大家在今后的AI Agent开发实践中有所帮助。

推荐学习

周文洋,《AI Agent入门实战》

Log Parser 2.2 + Log Parser Lizard GUI 分析IIS日志示例_log parser lizard gui:-CSDN博客

mikel阅读(326)

来源: Log Parser 2.2 + Log Parser Lizard GUI 分析IIS日志示例_log parser lizard gui:-CSDN博客

Log Parser 日志分析工具,用命令行操作,可以分析 IIS logs,event logs,active directory,log4net,file system,t-SQL

Log Parser Lizard 以可视化界面操作,使用类似SQL的语法查询

 

下载地址:

Log Parser 2.2 :
http://www.microsoft.com/en-us/download/details.aspx?id=24659

Log Parser Lizard GUI :
http://www.lizard-labs.com/log_parser_lizard_buy.aspx

 

或者两个打包好的资源下载:

LogParser2.2+LogParserLizard

 

安装完成后:

 

 

 

LogParser 查询方法:

 

编写SQL脚本保存为: SlowPage.sql

–C:\inetpub\SlowPage.sql
Select Top 20
LogRow as [Line Number],
date as [Date],
time as [Time],
c-ip as [Client-IP],
s-ip as [Server IP],
s-port as [Server Port],
cs-method as [Request Verb],
cs-uri-stem as [Request URI],
sc-bytes as [Bytes sent],
sc-status as [Status],
sc-substatus as [Sub-status],
sc-win32-status as [Win 32 Status],
time-taken as [Time Taken]
From
C:\inetpub\logs\LogFiles\W3SVC2\u_ex*.log
Order by time-taken desc

打开 LogParser 2.2 命令行工具,执行脚本:
LOGPARSER -i:IISW3C file:C:\inetpub\SlowPage.sql -o:DataGrid -q:off

 

 

Log Parser Lizard 查询方法:

打开 Log Parser Lizard 可视化界面,如下图,将sql命令复制到 查询[query] 窗口中,在点击生成[generate]。

 

 

 

 

还可以以各种图形的方式显示出来,更方便、直观 了解分析情况:

 

 

 

在Log Parser 2.2 安装路径下,有很多自带的sql语句:

 

 

若不清楚列名称,可以先全部查询所有列,再选择有用的列进行分析:

 

 

更多的使用方法,可以参考帮助文档,有很多例子:

 

————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/kk185800961/article/details/43964209

推荐 | 10个好用的Web日志安全分析工具 - Bypass - 博客园

mikel阅读(555)

来源: 推荐 | 10个好用的Web日志安全分析工具 – Bypass – 博客园

经常听到有朋友问,有没有比较好用的web日志安全分析工具?
首先,我们应该清楚,日志文件不但可以帮助我们溯源,找到入侵者攻击路径,而且在平常的运维中,日志也可以反应出很多的安全攻击行为。

一款简单好用的Web日志分析工具,可以大大提升效率,目前业内日志分析工具比较多,今天推荐十个比较好用的Web日志安全分析工具。

1、360星图
一款非常好用的网站访问日志分析工具,可以有效识别Web漏洞攻击、CC攻击、恶意爬虫扫描、异常访问等行为。一键自动化分析,输出安全分析报告,支持iis/apache/nginx日志,支持自定义格式。
下载地址:https://wangzhan.qianxin.com/activity/xingtu


2、LogForensics
TSRC提供的一款日志分析工具,可从单一可疑线索作为调查起点,遍历所有可疑url(CGI)和来源IP。
相关下载地址:https://security.tencent.com/index.php/opensource/detail/15


3、GoAccess
一款可视化 Web 日志分析工具,通过Web 浏览器或者 nix 系统下的终端程序即可访问。能为系统管理员提供快速且有价值的 HTTP 统计,并以在线可视化服务器的方式呈现。
官网地址:https://www.goaccess.cc/



4、AWStats

一款功能强大的开源日志分析系统,可以图形方式生成高级Web,流媒体,ftp或邮件服务器统计信息。
官网地址:http://www.awstats.org/


5、Logstalgia
一款非常炫酷且可视化日志分析工具,可以直观的展示CC攻击和网站的日志分析,并以可视化的3D效果展示出来。
下载地址:http://www.softpedia.com/get/Internet/Servers/Server-Tools/Logstalgia.shtml



6、FinderWeb
程序员的看日志利器,支持,tail, less, grep,支持超大的文本文件,从几M到几十G的日志文件都流畅自如。
下载使用:http://www.finderweb.net/download.html


7、web-log-parser
一款开源的分析web日志工具,采用python语言开发,具有灵活的日志格式配置。
github项目地址:https://github.com/JeffXue/web-log-parser

8、ELK
开源实时日志分析的ELK平台,由ElasticSearch、Logstash和Kiabana三个开源项目组成,在企业级日志管理平台中十分常见。
下载使用:https://www.elastic.co/cn/elastic-stack

9、Splunk
一款顶级的日志分析软件,如果你经常用 grep、awk、sed、sort、uniq、tail、head 来分析日志,那么你可以很容易地过渡到Splunk。
下载地址:https://www.splunk.com/zh-hans_cn/download/splunk-enterprise.html



10、IBM QRadar
Qradar有一个免费的社区版本,功能上和商用版本差别不大,适合小规模日志和流量分析使用。
下载地址:https://developer.ibm.com/qradar/ce/

vue中子组件的methods中获取到props中的值方法(vue 学习笔记)_vue 获取props的值-CSDN博客

mikel阅读(387)

来源: vue中子组件的methods中获取到props中的值方法(vue 学习笔记)_vue 获取props的值-CSDN博客

说一下为什么会有这个需求:如果不需要处理props的值直接用,是不用这么麻烦的,
直接在页面上用{{name}}就好了。在做项目的时候由于要对props的值做处理后才能使用,当我在methods、mounted 用this.xxx,发现取出来的值是空对象,有点疑惑
于是就上网查了原来:
通过这个方法传过来的值,在子组件mounted函数中如果直接去获取的话有可能会获取不到。因为此时父组件的值还没有传过来,但是如果你是写在methods中的方法中获取值的话,此时值已经传过来了,可以直接使用this.name获取到。 如果你想要在子组件的mounted()中获取传值的话最好做个延迟或者是监听,但是又有一点,为啥我在页面中使用{{name}}没有问题呢! 这就是vue的好处它是等到这个name值有变化的时候就会更新的。所以才会看到显示。(其实先是空然后值传过来了,就会加载上去。)
https://segmentfault.com/q/1010000014590428 (原文链接)
解决方法,直接上代码了:
父组件代码:
<template>
// 将photoList 传给子组件
  <div class=”home”><photo-view :photoList=”photoList”></photo-view></div>
</template>
<script>
import PhotoView from ‘./components/PhotoView’
import axios from ‘axios’
export default {
  name: ‘Home’,
  components: {
    PhotoView
  },
  data () {
    return {
      photoList: []
    }
  },
  mounted () {
    this.getPhotoInfo()
    this.getPushText()
  },
  methods: {
    getPhotoInfo () {
    // 获取后台数据通过getPhotoInfoSucc得到photoList
      axios.get(‘./mock/photo.json’)
        .then(this.getPhotoInfoSucc)
    },
    getPhotoInfoSucc (res) {
      res = res.data
      if (res.data) {
        const data = res.data
        this.photoList = data
        console.log(data)
      }
    }
  }
}
</script>
子组件代码:
<template>
</template>
<script>
export default {
  name: ‘PhotoView’,
  props: {
    photoList: Array
  },
  data () {
    return {
      newPhotoList: [],
    }
  },
  //监听photoList的值,当它由空转变时就会触发,这时候就能取到了,拿到值后要传值到data的newPhotoList中
  watch: {
    photoList: function (newData) {
      this.newPhotoList = newData
      this.getPhotoList(this.newPhotoList)
    }
  },
  methods: {
    getPhotoList (res) {
    console.log(this.newPhotoList)
  }
}
</script>
这里是参考https://www.jianshu.com/p/cdc90de5b1cf
个人学习笔记,如有误,请指教,谢谢
————————————————
                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/baidu_38942289/article/details/115552354