阅读官方文档了解基本概念和逻辑:http://doc-bot.tmall.com/docs/doc.htm?treeId=393&articleId=106714&docType=1
文档中的示例是以自己创建的实体来示范的,如果我们需要创建一个“找工作”的意图,那么不可能把所有的工种一一添加到实体中,必须在语料中使用 sys:any 这个系统实体来匹配工种。例如:
添加语料:我想找会计的工作。把会计标注为 sys:any,参数名称可以取为 job,必选,追问语句:请说出您要找的工作类型。这样,Web Hook 就能接收到参数 job 了,而且值可以为用户说的任意工种。
同样的,我们可以再添加一个“租房子”的意图。
有了两个或多个意图后,必须选择其中一个为默认意图。如果我们的意图功能有同等的重要性,那么可以再添加一个入口意图,引导用户自己选择需要使用的意图,实现类似 10086 的层级选项。
添加一个“想干嘛”的意图并设为默认,不管是否添加语料或参数,意图完成时最终还是会调用 Web Hook,那么我们就在 Web Hook 中返回引导语句,如:您要找工作还是租房子呢?
然后在“找工作”意图中添加语料:我要找工作。在“租房子”意图中添加语料:我要租房子。顺利引导用户进入相应意图。
为了避免对话中断,需要在“想干嘛”意图的 Web Hook 中将 resultType 设为 ASK_INF。
AliGenie 的语料匹配过程并非是:
先匹配语料填充参数,再通过追问语句引导匹配其它语料填充剩余必选参数,最后调用 Web Hook。
我觉得更应该是:
先匹配语料填充参数,再通过追问语句引导单个填充剩余必选参数,最后调用 Web Hook。
也就是说追问语句应尽量引导用户回答单个实体值(如在查天气示例中:“明天”、“北京”,加一些助词之类的也是没问题的,毕竟是人工智能嘛),而不是引导用户去匹配其它语料。
当我们成功完成一次找工作的检索后,如果用户想继续询问其它岗位,需要再重新匹配一次语料。添加连续对话语料可以实现更简洁的对话场景:
连续对话语料:厨师呢
其中厨师标注为 job
可以实现如下对话:
我要找工作
请说您要找的工作类型
驾驶员
没有找到关于 驾驶员 的招聘信息。
司机呢
为您找到 司机 的招聘信息。 xxxxxxxx
如果没有这个连续对话语料,那么最后一句的回答是:对不起,我不明白你的意思
为了提高用户体验,当成功返回用户所要找的工作信息时追问“重听还是下一条?”,这时我在连续对话语料或普通语料中添加“重听”,会丢失 job 参数,直接返回了 job 参数的追问语句,无法调用 Web Hook,期待大家指点迷津。
更多需要注意的地方:
修改配置(意图、实体等信息)后,如果在线测试未即时生效,可能有缓存或 bug,第二天再继续测试,当然也不排除配置有误,应仔细排查。
文档中心底部可以直接向提交问题,官方会给予回复(不保证),及时性令人捉急。
用户的语句是按:连续对话语料 -> 普通语料 -> 意图 -> 技能 -> 平台 的顺序来匹配的。也就是说,如果上一步是在处理意图 A 的逻辑,那么下一步会优先匹配意图 A 的连续对话语料和普通语料,如果未匹配,但匹配了意图 B 的语料,那么照样会执行意图 B 的逻辑。如果是真机测试,那么甚至是跳出技能来匹配官方技能(如唱一首歌)。
AliGenie 会在匹配语料之前对用户的话进行建模,如果添加了语料“我要找工作”,实际测试中,有时候能匹配语句“我想找工作”,有时候却不能,有时候甚至连“我要找工作”都不能匹配,具体的匹配逻辑令人无法捉摸,可能这样更像是“人工智能”吧。
真机测试时最好在手机上开启“天猫精灵”App,时刻观察天猫精灵识别到的内容,以减少我们对 AliGenie 处理逻辑的误判。
sys:time 可以匹配:今天、明天、后天、大后天等,但不能匹配“昨天”,神马情况?
AliGenie 目前还不完善,特别是添加修改语料时经常出错莫名错误和乱码,期待阿里能尽快完善,能让更多的开发者参与到其中来,共同推动 AI 发展。
1.1 四大特性
MVC
<!doctype html>
<html ng-app>
<body>
<div ng-controller="HelloAngular">
<p>{{greeting.text}}, Angular</p>
</div>
<script src="js/angular-1.3.0.js"></script>
<script>
function HelloAngular($scope) {
$scope.greeting = {
text: 'Hello'
};
}
</script>
</body>
</html>
模块化与依赖注入
<!doctype html>
<html ng-app="app">
<body>
<div ng-controller="HelloAngular">
<p>{{greeting.text}}, Angular</p>
</div>
<script src="js/angular-1.3.0.js"></script>
<script>
var myModule = angular.module("app", []);
myModule.controller("HelloAngular", ['$scope',
function($scope) {
$scope.greeting = {
text: 'Hello'
};
}
]);
</script>
</body>
</html>
双向数据绑定
<!doctype html>
<html ng-app>
<body>
<input ng-model="greeting.text" />
<p>{{greeting.text}},AngularJS</p>
<script src="js/angular-1.3.0.js"></script>
</body>
</html>
指令
<!doctype html>
<html ng-app="app">
<body>
<hello></hello>
<script src="js/angular-1.3.0.js"></script>
<script>
var myModule = angular.module("app", []);
myModule.directive("hello", function () {
return {
restrict: 'E',
template: '<div>Hi everyone!</div>',
replace: true
}
});
</script>
</body>
</html>
1.2 开发环境
node.js | JS 运行环境 |
Bower | 插件管理(相当于 NuGet) |
Grunt | 代码混淆、合并工具 |
Sublime | 代码编辑器(小巧),Zen Coding 快速编写 HTML/CSS 代码 |
WebStorm | 代码编辑器(强大) |
git 小乌龟 | 版本管理 |
2.1 MVC
Controller 使用要点:
不要复用 Controller
不要操作 DOM
不要做数据格式化,ng 有表单控件
不要做数据过滤,ng 有 $filter
$scope 作用域
<!doctype html>
<html ng-app>
<head>
<style>
.show-scope-demo .ng-scope, .show-scope-demo .ng-scope { border: 1px solid red; margin: 3px; }
</style>
</head>
<body>
<div class="show-scope-demo">
<div ng-controller="GreetCtrl">
Hello {{name}}!
</div>
<div ng-controller="ListCtrl">
<ol>
<li ng-repeat="name in names">
{{name}} from {{department}}
</li>
</ol>
</div>
</div>
<script src="js/angular-1.3.0.js"></script>
<script>
function GreetCtrl($scope, $rootScope) {
$scope.name = 'World';
// 在根作用域定义了 department,在 ListCtrl 控制器中也能用
$rootScope.department = 'Angular';
}
function ListCtrl($scope) {
$scope.names = ['Igor', 'Misko', 'Vojta'];
}
</script>
</body>
</html>
<!doctype html>
<html ng-app>
<head>
<style>
.show-scope-demo .ng-scope, .show-scope-demo .ng-scope { border: 1px solid red; margin: 3px; }
</style>
</head>
<body>
<div ng-controller="EventController">
Root scope
<tt>MyEvent</tt> count: {{count}}
<ul>
<li ng-repeat="i in [1]" ng-controller="EventController">
<button ng-click="$emit('MyEvent')">
$emit('MyEvent')
</button>
<button ng-click="$broadcast('MyEvent')">
$broadcast('MyEvent')
</button>
<br>
Middle scope
<tt>MyEvent</tt> count: {{count}}
<ul>
<li ng-repeat="item in [1, 2]" ng-controller="EventController">
Leaf scope
<tt>MyEvent</tt> count: {{count}}
</li>
</ul>
</li>
</ul>
</div>
<script src="js/angular-1.3.0.js"></script>
<script>
function EventController($scope) {
$scope.count = 0;
$scope.$on('MyEvent', function () {
$scope.count++;
});
}
</script>
</body>
</html>
$emit:向上传播
$broadcast:向下传播
ng-repeat="i in [1, 2]":循环固定项
$scope 是一个树形结构,与 DOM 平行
子 $scope 对象会继承父 $scope 上的属性和方法
每个 Angular 应用只有一个根 $scope,一般位于 ng-app 上
$scope 可以传播事件,类似 DOM 事件,可向上可向下
2.2 路由、模块、依赖注入
var bookStoreApp = angular.module('bookStoreApp', [
'ngRoute', 'ngAnimate', 'bookStoreCtrls', 'bookStoreFilters',
'bookStoreServices', 'bookStoreDirectives'
]);
bookStoreApp.config(function($routeProvider) {
$routeProvider.when('/hello', {
templateUrl: 'tpls/hello.html',
controller: 'HelloCtrl'
}).when('/list',{
templateUrl:'tpls/bookList.html',
controller:'BookListCtrl'
}).otherwise({
redirectTo: '/hello'
})
});
2.3 双向数据绑定
绑定方式1:<p>{{greeting.text}}, Angular</p>
绑定方式2:<p><span ng-bind="greeting.text"></span>, Angular</p>
方式1 在加载 angular.js 之前会将 {{***}} 显示于界面,因此在 index.html 页面建议用方式2,通过模板加载的页面上的绑定可以使用方式1。
<!doctype html>
<html ng-app="UserInfoModule">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="css/bootstrap-3.0.0/css/bootstrap.css">
</head>
<body>
<div class="panel panel-primary">
<div class="panel-heading">
<div class="panel-title">双向数据绑定</div>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-12">
<form class="form-horizontal" role="form" ng-controller="UserInfoCtrl">
<div class="form-group">
<label class="col-md-2 control-label">
邮箱:
</label>
<div class="col-md-10">
<input type="email" class="form-control" placeholder="推荐使用126邮箱" ng-model="userInfo.email">
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">
密码:
</label>
<div class="col-md-10">
<input type="password" class="form-control" placeholder="只能是数字、字母、下划线" ng-model="userInfo.password">
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="userInfo.autoLogin">自动登录
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button class="btn btn-default" ng-click="getFormData()">获取Form表单的值</button>
<button class="btn btn-default" ng-click="setFormData()">设置Form表单的值</button>
<button class="btn btn-default" ng-click="resetForm()">重置表单</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<script src="js/angular-1.3.0.js"></script>
<script>
var userInfoModule = angular.module('UserInfoModule', []);
userInfoModule.controller('UserInfoCtrl', ['$scope',
function ($scope) {
$scope.userInfo = {
email: "253445528@qq.com",
password: "253445528",
autoLogin: true
};
$scope.getFormData = function () {
console.log($scope.userInfo);
};
$scope.setFormData = function () {
$scope.userInfo = {
email: 'damoqiongqiu@126.com',
password: 'damoqiongqiu',
autoLogin: false
}
};
$scope.resetForm = function () {
$scope.userInfo = {
email: "253445528@qq.com",
password: "253445528",
autoLogin: true
};
}
}
]);
</script>
</body>
</html>
注意 ng-click 的用法。
ng-class 用法:
ng-class='{bg-danger: isError, bg-success: isSuccess}'
表示当 isError 为 true 时应用 bg-danger 样式,当 isSuccess 为 true 时应用 bg-success 样式。
ng-show / ng-hide 用法:
ng-show='isShow'
表示当 isShow 为 true 时显示,否则隐藏。
2.4 指令(Directive)重要!
路由
Angular 自带的 angular.route.js 不能实现嵌套路由,推荐使用 UI-Router,方便将页面分割成多个 ui-view 实现局部更新。
angular.module("app", []).directive("directiveName", function () {
return {
//通过设置项来定义
};
});
设置项:
restrict: 'AEMC', // A 属性(默认),E 元素,M 注释,C 样式
template: '<div>xxx</div>', // 替代的 HTML
replace: true, // 替代
transclude: true, // 嵌套,需要 template 配合,例如:
templace: '<div>xxx <div ng-transclude></div></div>', // 原来指令标签中的内容会被放置于 ng-transclude 所在 div 中。
templaceUrl: 'xxx.html', // 使用独立 html 的模板内容
compile: function() { }, // 对模板自身进行转换
link: function() {}, // 操作 DOM、绑定事件监听器
可以在页面中使用 ui-sref 或控制器中 $state.go 来实现跳转(页面更新)
如何传递参数:
一个简单的示例:在一个文章列表页面和一个文章详情页面中实现跳转,需要传递参数文章 id
在路由配置 state 中加入:params: { "id": null }
在列表行中绑定 id,格式如:<a ui-sref="app.blog.item({id:row.id})"><span>Edit</span></a>
在详情页控制器中接收参数:$stateParams,即可用 $stateParams.id 来获取文章 id 值
如果需要同时更改地址栏 url,并且能够使用该 url 直接打开该文章,那么修改 state 中的 url 为 /item?id,体现为 #/app/blog/item?id=9,也可以用冒号 /item:id,体现为 #/app/blog/item9
scope 的绑定策略:
@:把当前属性作为字符串传递。你还可以绑定来自外层 scope 的值,在属性值中插入 {{ }} 即可
=:与父 scope 中的属性进行双向绑定
&:传递一个来自父 scope 的函数,稍后调用
ERP 类型的系统必备 UI 组件:MiniUI、Ext JS
互联网/电商型系统必备 UI 组件:KISSY
其它常用组件:AngularUI
2.5 Service 与 Provider
使用 $http.get 获取 JSON 数据
<!doctype html>
<html ng-app="app">
<head>
<meta charset="UTF-8">
<script src="framework/1.3.0.14/angular.js"></script>
</head>
<body>
<div ng-controller="LoadDataCtrl">
<ul>
<li ng-repeat="user in users">
{{user.name}}
</li>
</ul>
</div>
<script>
var app = angular.module("app", []);
app.controller('LoadDataCtrl', ['$scope', '$http', function ($scope, $http) {
$http({
method: 'GET',
url: 'data.json'
}).success(function (data, status, headers, config) {
console.log("success...");
console.log(data);
$scope.users = data;
}).error(function (data, status, headers, config) {
console.log("error...");
})
}]);
</script>
</body>
</html>
data.json:
[
{ "name": "a" },
{ "name": "b" },
{ "name": "c" }
]
自定义 Service
<!doctype html>
<html ng-app="app">
<head>
<meta charset="UTF-8">
<script src="framework/1.3.0.14/angular.js"></script>
</head>
<body>
<div ng-controller="ServiceController">
<label>用户名:</label>
<input type="text" ng-model="username" placeholder="请输入用户名" />
<ul ng-show="username">
<li ng-repeat="user in users">
{{user.name}}
</li>
</ul>
</div>
<script>
var app = angular.module("app", []);
app.factory('userListService', ['$http', function ($http) {
var doRequest = function (username, path) {
return $http({ method: 'GET', url: 'data.json' });
};
return {
userList: function (username) {
return doRequest(username, 'userList');
}
}
}]);
app.controller('ServiceController', ['$scope', '$timeout', 'userListService', function ($scope, $timeout, userListService) {
var timeout;
$scope.$watch('username', function (newUserName) {
if (newUserName) {
if (timeout) {
$timeout.cancel(timeout);
}
timeout = $timeout(function () {
userListService.userList(newUserName)
.success(function (data, status) {
$scope.users = data;
});
}, 350);
}
});
}]);
</script>
</body>
</html>
知识点:
$timeout 延时,防抖动
userListService 服务调用 $http 服务获取数据,返回一个对象
自定义 Service 命名不要以“$”开头,可以像内置 Service 一样注入的,注入时必须放在内置 Service 之后
Service 是单例的
Service 只要声明,不需要实例化
Service 在整个应用的生命周期中存在,可以用来共享数据
$filter
currency / date / filter / json / limitTo / lowercase / number / orderBy / uppercase
data 例:
{{ 1304375948024 | data }}
{{ 1304375948024 | data:"MM/dd/yyyy @ h:mma" }}
{{ 1304375948024 | data:"yyyy-MM-dd hh:mm:ss" }}
效果:
May 3, 2011
05/03/2011 @ 6:39AM
2011-05-03 06:39:08
自定义 filter:
<!doctype html>
<html ng-app="app">
<head>
<meta charset="UTF-8">
<script src="framework/1.3.0.14/angular.js"></script>
</head>
<body>
{{'xoyozo'|filter1}}
<script>
var app = angular.module("app", []);
app.filter('filter1', function () {
return function (item) {
return item + 'o(u_u)o';
}
});
</script>
</body>
</html>
其它常用 Service:内置共 24 个
$compile 编译服务
$filter 数据格式化工具,内置了 8 个
$interval
$timeout
$locale
$location
$log
$parse
$http 封装了 ajax
2.6 综合实例 BookStore
ASP.NET MVC 项目发布到 IIS 上,出现服务器错误“403 - 禁止访问:访问被拒绝”,排除权限设置问题后,原因在于 MVC 的 URL 通常没有扩展名,IIS 并未将其交由 ASP.NET 托管处理。
经过各种找资料,各种尝试后,最终确定:项目本身没有问题,本地运行正常,发布到另一台服务器上正常。
删除服务器 IIS 上的该网站和应用程序池,重新创建网站,恢复正常。原因未知。
官网示例 | 国内示例 | ||
Metronic | 收费 | 最新 | |
Unify | 收费 | ||
Angulr | 收费 | Angular HTML | 本站 Angular v2.2.0 本站 HTML v2.2.0 |
AdminLTE | 免费开源 | 最新 | 本站 v2.4.2 |
Color Admin | 收费 | 最新 | |
更多 …… |
浏览示例前,将以下域名的重定向加入到 hosts 中可以加快页面的打开速度:
127.0.0.1 fonts.googleapis.com
127.0.0.1 ajax.googleapis.com
127.0.0.1 player.vimeo.com
127.0.0.1 www.vimeo.com
先保存一次
Ctrl + A 全选
Ctrl + U 取消组合对象
菜单 - 编辑 - 全选 - 文本
Ctrl + Q 转换为曲线
菜单 - 文本 - 文本统计信息
看到 0 就成功了
记得“另存为”,千万不要直接“保存”,否则无法再次编辑。
可以改为乘以一个数后取整,再除以这个数,例如,将浮点数 f 精确到百分位:
f.toFixed(2)
替换为:
Math.round(f * 100) / 100
但需要注意一个细节,toFixed() 是四舍六入五成双,Math.round 是四舍五入。
在 Global.asax 的 Application_Start() 方法中添加以下代码,作用是在应用程序启动时预先访问一遍所有动态页面,办法虽土,但很有效。
本代码适合 Web Forms 项目,不适合 MVC 项目;修改代码中的 domain 值为真实的网站域名
代码整理中……
在 IIS 中设置应用程序池最长时效或永不过期,则效果更佳。
在应用程序池中选中网站对应的应用程序池,在右侧“操作”窗口中选择“正在回收...”
取消“固定间隔”框中的所有选项,确定。
实践证明,近 200 个动态页面一次性访问需要耗时近 10 分钟,发布后 10 分钟内无法正常浏览网站是同样是无法忍受的。因此更改方案为:
做一个 Winform 应用程序来定时访问这些页面,30 秒一个,一个半小时能完成一个循环,对正常浏览的影响非常小。
在 MySQL 中,int 的取值范围是 [-2147483648, 2147483647],占用 4 个字节。
int(M) 中 M 的默认值为 11,该值不影响取值范围和占用字节,仅表示最大显示宽度。
以某 int 字段存储的记录值为 2147483647 为例:
类型为 int(1) 时,SELECT 结果为 214
类型为 int(2) 时,SELECT 结果为 2147
……
类型为 int(7) 时,SELECT 结果为 214748364
类型为 int(8) ~ int(11) 时,SELECT 结果为 2147483647
测试结果跟网上的说法不同
如果添加了 zerofill 属性,当然是填充零的效果,仍以上述值为例:
类型为 int(1) 时,SELECT 结果为 214
类型为 int(2) 时,SELECT 结果为 2147
……
类型为 int(7) 时,SELECT 结果为 214748364
类型为 int(8) ~ int(10) 时,SELECT 结果为 2147483647
类型为 int(11) 时,SELECT 结果为 02147483647
结论,既然 M 值不影响取值范围和占用字节,那么何必去改它呢。除非有特殊业务需求,否则很容易引起逻辑混乱,特别是当你误认为它是用来限定取值范围或节省存储空间的时候。
发布选项 \ 项目类型 | Web 窗体网站 | Web 应用程序 (Web 窗体) | Web 应用程序 (MVC) | ASP.NET Core Web 应用程序 |
在发布期间预编译 Precompile during publishing | 若勾选,将 .cs 文件编译为 .dll | 无论是否勾选,都将 .cs 文件编译为 .dll | ||
允许更新预编译站点 Allow precompiled site to be updatable | 若不允许,则会将 .aspx 等页面也一同编译,并以内容“这是预编译工具生成的标记文件,不应删除!”代替 |
未进行完整的测试和分析,总结有误请指正。